Browse code

When building the route set of ACKs for local UACs, only the reply is now evaluated; this is insufficient, since replies to in-dialog requests normally miss the route set. The patch fixes that: evals INVITE, if this was in-dialog; otherwise, the reply (since the req. doesn't have yet complete route set).

Add support for the SASI if'ace, adding the implemenation for two more TM API
functions:
- t_get_canceled_ident(): returns the hash coordinates (bucket/index) of the
transaction the currently processed CANCEL is targeting
- ack_local_uac(): allow generating the ACKs for 2xx'ed locally originated
INVITEs - new headers and body can now also be appended to it.

Fully closes #SER-346.

Bogdan Pintea authored on 05/01/2009 21:26:44
Showing 12 changed files
... ...
@@ -456,6 +456,8 @@ endif
456 456
 #		core that the DNS servers are down. No DNS query is performed
457 457
 #		when the servers are unreachable, and even expired resource
458 458
 #		records are used from the cache. (requires external watchdog)
459
+# -DWITH_AS_SUPPORT
460
+#		adds support for Application Server interface
459 461
 # Sometimes is needes correct non-quoted $OS. HACK: gcc translates known OS to number ('linux'), so there is added underscore
460 462
 
461 463
 DEFS= $(extra_defs) \
... ...
@@ -476,6 +478,7 @@ DEFS= $(extra_defs) \
476 478
 	 -DUSE_DNS_FAILOVER \
477 479
 	 -DUSE_DST_BLACKLIST \
478 480
 	 -DUSE_NAPTR \
481
+	 -DWITH_AS_SUPPORT \
479 482
 	 -DDBG_QM_MALLOC \
480 483
 	 #-DUSE_DNS_CACHE_STATS \
481 484
 	 #-DUSE_DST_BLACKLIST_STATS \
... ...
@@ -77,7 +77,13 @@ modules:
77 77
  - blst      - new module containing script blacklist manipulations functions
78 78
                (the source of a message can be blacklisted, removed from the
79 79
                 blacklist or checked for presence in the blacklist).
80
- - tm        - matching of E2E ACKs no longer requires full From HF identity,
80
+ - tm        - added API function t_get_canceled_ident(): returns the hash 
81
+               coordinates (bucket/index) of the transaction the currently 
82
+               processed CANCEL is targeting. Requires AS support enabled.
83
+             - added API function ack_local_uac(): allow generating the ACKs 
84
+               for 2xx'ed locally originated INVITEs - new headers and body can
85
+               now also be appended to it. Requires AS support enabled.
86
+             - matching of E2E ACKs no longer requires full From HF identity,
81 87
                but rather only tag equality (this behaviour can be changed by
82 88
                defining TM_E2E_ACK_CHECK_FROM_URI)
83 89
              - added t_reset_fr(), t_reset_retr(), t_reset_max_lifetime()
... ...
@@ -215,11 +215,17 @@ static inline int str_duplicate(str* _d, str* _s)
215 215
 
216 216
 /*
217 217
  * Calculate dialog hooks
218
+ * @return:
219
+ *  negative : error
220
+ *  0 : no routes present
221
+ *  F_RB_NH_LOOSE : routes present, next hop is loose router
222
+ *  F_RB_NH_STRICT: next hop is strict.
218 223
  */
219 224
 static inline int calculate_hooks(dlg_t* _d)
220 225
 {
221 226
 	str* uri;
222 227
 	struct sip_uri puri;
228
+	int nhop;
223 229
 
224 230
 	/* we might re-calc. some existing hooks =>
225 231
 	 * reset all the hooks to 0 */
... ...
@@ -236,6 +242,7 @@ static inline int calculate_hooks(dlg_t* _d)
236 242
 			else _d->hooks.request_uri = &_d->rem_uri;
237 243
 			_d->hooks.next_hop = &_d->route_set->nameaddr.uri;
238 244
 			_d->hooks.first_route = _d->route_set;
245
+			nhop = F_RB_NH_LOOSE;
239 246
 		} else {
240 247
 			_d->hooks.request_uri = &_d->route_set->nameaddr.uri;
241 248
 			_d->hooks.next_hop = _d->hooks.request_uri;
... ...
@@ -244,6 +251,7 @@ static inline int calculate_hooks(dlg_t* _d)
244 251
 				_d->hooks.last_route = &_d->rem_target;
245 252
 			else 
246 253
 				_d->hooks.last_route = NULL; /* ? */
254
+			nhop = F_RB_NH_STRICT;
247 255
 		}
248 256
 	} else {
249 257
 		if (_d->rem_target.s) _d->hooks.request_uri = &_d->rem_target;
... ...
@@ -252,12 +260,14 @@ static inline int calculate_hooks(dlg_t* _d)
252 260
 		if (_d->dst_uri.s) _d->hooks.next_hop = &_d->dst_uri;
253 261
 		else _d->hooks.next_hop = _d->hooks.request_uri;
254 262
 
263
+		nhop = 0;
255 264
 		/*
256
-		 * the routes in the hooks need to be reset because if the route_set was dropped somewhere else
257
-		 * then these will remain set without the actual routes existing any more
265
+		 * the routes in the hooks need to be reset because if the route_set 
266
+		 * was dropped somewhere else then these will remain set without the
267
+		 * actual routes existing any more
258 268
 		 */
259 269
 		_d->hooks.first_route = 0;
260
-		_d->hooks.last_route = 0; 
270
+		_d->hooks.last_route = 0;
261 271
 	}
262 272
 
263 273
 	if ((_d->hooks.request_uri) && (_d->hooks.request_uri->s) && (_d->hooks.request_uri->len)) {
... ...
@@ -273,7 +283,7 @@ static inline int calculate_hooks(dlg_t* _d)
273 283
 		get_raw_uri(_d->hooks.next_hop);
274 284
 	}
275 285
 
276
-	return 0;
286
+	return nhop;
277 287
 }
278 288
 
279 289
 /*
... ...
@@ -738,7 +748,8 @@ static inline int dlg_confirmed_resp_uac(dlg_t* _d, struct sip_msg* _m,
738 748
 			if (str_duplicate(&_d->rem_target, &contact) < 0) return -4;
739 749
 		}
740 750
 
741
-		calculate_hooks(_d);
751
+		if (calculate_hooks(_d) < 0)
752
+			return -1;
742 753
 	}
743 754
 
744 755
 	return 0;
... ...
@@ -1078,7 +1089,8 @@ int dlg_request_uas(dlg_t* _d, struct sip_msg* _m, target_refresh_t is_target_re
1078 1089
 			if (str_duplicate(&_d->rem_target, &contact) < 0) return -6;
1079 1090
 		}
1080 1091
 
1081
-		calculate_hooks(_d);
1092
+		if (calculate_hooks(_d) < 0)
1093
+			return -1;
1082 1094
 		
1083 1095
 	}
1084 1096
 
... ...
@@ -1248,7 +1260,7 @@ int set_dlg_target(dlg_t* _d, str* _ruri, str* _duri) {
1248 1260
 		if (str_duplicate(&_d->dst_uri, _duri)) return -1;
1249 1261
 	}
1250 1262
 
1251
-	if (calculate_hooks(_d)) {
1263
+	if (calculate_hooks(_d) < 0) {
1252 1264
 		LOG(L_ERR, "set_dlg_target(): Error while calculating hooks\n");
1253 1265
 		return -1;
1254 1266
 	}
... ...
@@ -65,6 +65,7 @@
65 65
 #include "h_table.h"
66 66
 #include "fix_lumps.h" /* free_via_clen_lump */
67 67
 #include "timer.h"
68
+#include "uac.h" /* free_local_ack */
68 69
 
69 70
 
70 71
 static enum kill_reason kr;
... ...
@@ -174,6 +175,9 @@ void free_cell( struct cell* dead_cell )
174 175
 #endif
175 176
 	}
176 177
 
178
+	if (dead_cell->uac[0].local_ack)
179
+		free_local_ack_unsafe(dead_cell->uac[0].local_ack);
180
+
177 181
 	/* collected to tags */
178 182
 	tt=dead_cell->fwded_totags;
179 183
 	while(tt) {
... ...
@@ -143,6 +143,10 @@ enum kill_reason { REQ_FWDED=1, REQ_RPLD=2, REQ_RLSD=4, REQ_EXIST=8,
143 143
 #define F_RB_REPLIED	0x20 /* reply received */
144 144
 #define F_RB_CANCELED	0x40 /* rb/branch canceled */
145 145
 #define F_RB_DEL_TIMER	0x80 /* timer should be deleted if active */
146
+#define F_RB_NH_LOOSE	0x100 /* next hop is a loose router */
147
+#define F_RB_NH_STRICT	0x200 /* next hop is a strict router */
148
+/* must detect when neither loose nor strict flag is set -> two flags.
149
+ * alternatively, 1x flag for strict/loose and 1x for loose|strict set/not */
146 150
 
147 151
 
148 152
 /* if canceled or intended to be canceled, return true */
... ...
@@ -154,10 +158,10 @@ typedef struct retr_buf
154 158
 	short activ_type;
155 159
 	/* set to status code if the buffer is a reply,
156 160
 	0 if request or -1 if local CANCEL */
157
-	volatile unsigned char flags; /* DISABLED, T2 */
161
+	volatile unsigned short flags; /* DISABLED, T2 */
158 162
 	volatile unsigned char t_active; /* timer active */
159 163
 	unsigned short branch; /* no more then 65k branches :-) */
160
-	short   buffer_len;
164
+	short buffer_len;
161 165
 	char *buffer;
162 166
 	/*the cell that contains this retrans_buff*/
163 167
 	struct cell* my_T;
... ...
@@ -207,6 +211,16 @@ typedef struct ua_client
207 211
 	str              uri;
208 212
 	/* if we don't store, we at least want to know the status */
209 213
 	int             last_received;
214
+#ifdef WITH_AS_SUPPORT
215
+	/**
216
+	 * Resent for every rcvd 2xx reply.
217
+	 * This member's as an alternative to passing the reply to the AS, 
218
+	 * every time a reply for local request is rcvd.
219
+	 * Member can not be union'ed with local_cancel, since CANCEL can happen
220
+	 * concurrently with a 2xx reply (to generate an ACK).
221
+	 */
222
+	struct retr_buf *local_ack;
223
+#endif
210 224
 }ua_client_type;
211 225
 
212 226
 
... ...
@@ -237,6 +251,10 @@ struct totag_elem {
237 251
 #define T_IN_AGONY (1<<5) /* set if waiting to die (delete timer)
238 252
                              TODO: replace it with del on unref */
239 253
 #define T_AUTO_INV_100 (1<<6) /* send an 100 reply automatically  to inv. */
254
+#ifdef WITH_AS_SUPPORT
255
+	/* don't generate automatically an ACK for local transaction */
256
+#	define T_NO_AUTO_ACK	(1<<7)
257
+#endif
240 258
 #define T_DONT_FORK   (T_CANCELED|T_6xx)
241 259
 
242 260
 /* unsigned short should be enough for a retr. timer: max. 65535 ticks =>
... ...
@@ -441,6 +459,9 @@ inline static void insert_into_hash_table_unsafe( struct cell * p_cell,
441 459
 													unsigned int hash )
442 460
 {
443 461
 	p_cell->label = _tm_table->entries[hash].next_label++;
462
+#ifdef EXTRA_DEBUG
463
+	DEBUG("cell label: %u\n", p_cell->label);
464
+#endif
444 465
 	p_cell->hash_index=hash;
445 466
 	/* insert at the beginning */
446 467
 	clist_insert(&_tm_table->entries[hash], p_cell, next_c, prev_c);
... ...
@@ -76,12 +76,15 @@ struct cell;
76 76
 #define TMCB_DESTROY_N          15  /* called on transaction destroy */
77 77
 #define TMCB_E2ECANCEL_IN_N     16
78 78
 #define TMCB_E2EACK_RETR_IN_N   17
79
+#ifdef WITH_AS_SUPPORT
80
+#define TMCB_DONT_ACK_N         18 /* TM shoudn't ACK a local UAC  */
81
+#endif
79 82
 #ifdef TMCB_ONSEND
80
-#define TMCB_REQUEST_SENT_N     18
81
-#define TMCB_RESPONSE_SENT_N    19
82
-#define TMCB_MAX_N              19
83
+#define TMCB_REQUEST_SENT_N     19
84
+#define TMCB_RESPONSE_SENT_N    20
85
+#define TMCB_MAX_N              20
83 86
 #else
84
-#define TMCB_MAX_N              17
87
+#define TMCB_MAX_N              18
85 88
 #endif
86 89
 
87 90
 
... ...
@@ -103,6 +106,9 @@ struct cell;
103 106
 #define TMCB_DESTROY          (1<<TMCB_DESTROY_N)
104 107
 #define TMCB_E2ECANCEL_IN     (1<<TMCB_E2ECANCEL_IN_N)
105 108
 #define TMCB_E2EACK_RETR_IN   (1<<TMCB_E2EACK_RETR_IN_N)
109
+#ifdef WITH_AS_SUPPORT
110
+#define TMCB_DONT_ACK         (1<<TMCB_DONT_ACK_N)
111
+#endif
106 112
 #ifdef TMCB_ONSEND
107 113
 #define TMCB_REQUEST_SENT      (1<<TMCB_REQUEST_SENT_N)
108 114
 #define TMCB_RESPONSE_SENT     (1<<TMCB_RESPONSE_SENT_N)
... ...
@@ -313,6 +319,14 @@ struct cell;
313 319
  *  the cell* parameter (t) and the tmcb are set to 0. Only the param is
314 320
  *  is filled inside TMCB. For dialogs callbacks t is also 0.
315 321
  *
322
+ *
323
+ * TMCB_DONT_ACK (requires AS support) -- for localy generated INVITEs, TM 
324
+ * automatically generates an ACK for the received 2xx replies. But, if this 
325
+ * flag is passed to TM when creating the initial UAC request, this won't
326
+ * happen anymore: the ACK generation must be triggered from outside, using
327
+ * TM's interface.
328
+ * While this isn't exactly a callback type, it is used as part of the flags
329
+ * mask when registering callbacks.
316 330
 
317 331
 	the callback's param MUST be in shared memory and will
318 332
 	NOT be freed by TM; you must do it yourself from the
... ...
@@ -1500,6 +1500,31 @@ int t_get_trans_ident(struct sip_msg* p_msg, unsigned int* hash_index, unsigned
1500 1500
     return 1;
1501 1501
 }
1502 1502
 
1503
+#ifdef WITH_AS_SUPPORT
1504
+/**
1505
+ * Returns the hash coordinates of the transaction current CANCEL is targeting.
1506
+ */
1507
+int t_get_canceled_ident(struct sip_msg* msg, unsigned int* hash_index, 
1508
+		unsigned int* label)
1509
+{
1510
+	struct cell *orig;
1511
+	if (msg->REQ_METHOD != METHOD_CANCEL) {
1512
+		WARN("looking up original transaction for non-CANCEL method (%d).\n",
1513
+				msg->REQ_METHOD);
1514
+		return -1;
1515
+	}
1516
+	orig = t_lookupOriginalT(msg);
1517
+	if ((orig == T_NULL_CELL) || (orig == T_UNDEFINED))
1518
+		return -1;
1519
+	*hash_index = orig->hash_index;
1520
+	*label = orig->label;
1521
+	DEBUG("original T found @%p, %d:%d.\n", orig, *hash_index, *label);
1522
+	/* TODO: why's w_t_lookup_cancel setting T to 'undefined'?? */
1523
+	UNREF(orig);
1524
+	return 1;
1525
+}
1526
+#endif /* WITH_AS_SUPPORT */
1527
+
1503 1528
 int t_lookup_ident(struct cell ** trans, unsigned int hash_index, 
1504 1529
 					unsigned int label)
1505 1530
 {
... ...
@@ -1512,7 +1537,11 @@ int t_lookup_ident(struct cell ** trans, unsigned int hash_index,
1512 1537
 	}
1513 1538
 	
1514 1539
 	LOCK_HASH(hash_index);
1515
-	
1540
+
1541
+#ifndef E2E_CANCEL_HOP_BY_HOP
1542
+#warning "t_lookup_ident() can only reliably match INVITE transactions in " \
1543
+		"E2E_CANCEL_HOP_BY_HOP mode"
1544
+#endif
1516 1545
 	hash_bucket=&(get_tm_table()->entries[hash_index]);
1517 1546
 	/* all the transactions from the entry are compared */
1518 1547
 	clist_foreach(hash_bucket, p_cell, next_c){
... ...
@@ -50,7 +50,9 @@
50 50
 
51 51
 #include "defs.h"
52 52
 
53
-
53
+#ifdef EXTRA_DEBUG
54
+#include <assert.h>
55
+#endif
54 56
 #include "../../comp_defs.h"
55 57
 #include "../../hash_func.h"
56 58
 #include "../../globals.h"
... ...
@@ -375,10 +377,13 @@ error:
375 377
 }
376 378
 
377 379
 
378
-struct rte {
380
+typedef struct rte {
379 381
 	rr_t* ptr;
382
+	/* 'ptr' above doesn't point to a mem chunk linked to a sip_msg, so it
383
+	 * won't be free'd along with it => it must be free'd "manually" */
384
+	int free_rr;
380 385
 	struct rte* next;
381
-};
386
+} rte_t;
382 387
 
383 388
   	 
384 389
 static inline void free_rte_list(struct rte* list)
... ...
@@ -388,74 +393,13 @@ static inline void free_rte_list(struct rte* list)
388 393
 	while(list) {
389 394
 		ptr = list;
390 395
 		list = list->next;
396
+		if (ptr->free_rr)
397
+			free_rr(&ptr->ptr);
391 398
 		pkg_free(ptr);
392 399
 	}
393 400
 }
394 401
 
395 402
 
396
-static inline int process_routeset(struct sip_msg* msg, str* contact, struct rte** list, str* ruri, str* next_hop)
397
-{
398
-	struct hdr_field* ptr;
399
-	rr_t* p;
400
-	struct rte* t, *head;
401
-	struct sip_uri puri;
402
-	
403
-	ptr = msg->record_route;
404
-	head = 0;
405
-	while(ptr) {
406
-		if (ptr->type == HDR_RECORDROUTE_T) {
407
-			if (parse_rr(ptr) < 0) {
408
-				LOG(L_ERR, "process_routeset: Error while parsing Record-Route header\n");
409
-				return -1;
410
-			}
411
-			
412
-			p = (rr_t*)ptr->parsed;
413
-			while(p) {
414
-				t = (struct rte*)pkg_malloc(sizeof(struct rte));
415
-				if (!t) {
416
-					LOG(L_ERR, "process_routeset: No memory left\n");
417
-					free_rte_list(head);
418
-					return -1;
419
-				}
420
-				t->ptr = p;
421
-				t->next = head;
422
-				head = t;
423
-				p = p->next;
424
-			}
425
-		}
426
-		ptr = ptr->next;
427
-	}
428
-	
429
-	if (head) {
430
-		if (parse_uri(head->ptr->nameaddr.uri.s, head->ptr->nameaddr.uri.len, &puri) == -1) {
431
-			LOG(L_ERR, "process_routeset: Error while parsing URI\n");
432
-			free_rte_list(head);
433
-			return -1;
434
-		}
435
-		
436
-		if (puri.lr.s) {
437
-			     /* Next hop is loose router */
438
-			*ruri = *contact;
439
-			*next_hop = head->ptr->nameaddr.uri;
440
-		} else {
441
-			     /* Next hop is strict router */
442
-			*ruri = head->ptr->nameaddr.uri;
443
-			*next_hop = *ruri;
444
-			t = head;
445
-			head = head->next;
446
-			pkg_free(t);
447
-		}
448
-	} else {
449
-		     /* No routes */
450
-		*ruri = *contact;
451
-		*next_hop = *contact;
452
-	}
453
-	
454
-	*list = head;
455
-	return 0;
456
-}
457
-
458
-
459 403
 static inline int calc_routeset_len(struct rte* list, str* contact)
460 404
 {
461 405
 	struct rte* ptr;
... ...
@@ -547,7 +491,429 @@ static inline int get_contact_uri(struct sip_msg* msg, str* uri)
547 491
 	return 0;
548 492
 }
549 493
 
494
+/**
495
+ * Extract route set from the message (out of Record-Route, if reply, OR
496
+ * Route, if request).
497
+ * The route set is returned into the "UAC-format" (keep order for Rs, reverse
498
+ * RRs).
499
+ */
500
+static inline int get_uac_rs(sip_msg_t *msg, int is_req, struct rte **rtset)
501
+{
502
+	struct hdr_field* ptr;
503
+	rr_t *p, *new_p;
504
+	struct rte *t, *head, *old_head;
505
+
506
+	head = 0;
507
+	for (ptr = is_req ? msg->route : msg->record_route; ptr; ptr = ptr->next) {
508
+		switch (ptr->type) {
509
+			case HDR_RECORDROUTE_T:
510
+				if (is_req)
511
+					continue;
512
+				break;
513
+			case HDR_ROUTE_T:
514
+				if (! is_req)
515
+					continue;
516
+				break;
517
+			default:
518
+				continue;
519
+		}
520
+		if (parse_rr(ptr) < 0) {
521
+			ERR("failed to parse Record-/Route HF (%d).\n", ptr->type);
522
+			goto err;
523
+		}
524
+			
525
+		p = (rr_t*)ptr->parsed;
526
+		while(p) {
527
+			if (! (t = (struct rte*)pkg_malloc(sizeof(struct rte)))) {
528
+				ERR("out of pkg mem (asked for: %zd).\n", sizeof(struct rte));
529
+				goto err;
530
+			}
531
+			if (is_req) {
532
+				/* in case of requests, the sip_msg structure is free'd before
533
+				 * rte list is evaluated => must do a copy of it */
534
+				if (duplicate_rr(&new_p, p) < 0) {
535
+					pkg_free(t);
536
+					ERR("failed to duplicate RR");
537
+					goto err;
538
+				}
539
+				t->ptr = new_p;
540
+			} else {
541
+				t->ptr = p;
542
+			}
543
+			t->free_rr = is_req;
544
+			t->next = head;
545
+			head = t;
546
+			p = p->next;
547
+		}
548
+	}
550 549
 
550
+	if (is_req) {
551
+		/* harvesting the R/RR HF above inserts at head, which suites RRs (as
552
+		 * they must be reversed, anyway), but not Rs => reverse once more */
553
+		old_head = head;
554
+		head = 0;
555
+		while (old_head) {
556
+			t = old_head;
557
+			old_head = old_head->next;
558
+			t->next = head;
559
+			head = t;
560
+		}
561
+	}
562
+
563
+	*rtset = head;
564
+	return 0;
565
+err:
566
+	free_rte_list(head);
567
+	return -1;
568
+}
569
+
570
+
571
+static inline unsigned short uri2port(const struct sip_uri *puri)
572
+{
573
+	if (puri->port.s) {
574
+		return puri->port_no;
575
+	} else switch (puri->type) {
576
+		case SIP_URI_T:
577
+		case TEL_URI_T:
578
+			if (puri->transport_val.len == sizeof("TLS") - 1) {
579
+				unsigned trans;
580
+				trans = puri->transport_val.s[0] | 0x20; trans <<= 8;
581
+				trans |= puri->transport_val.s[1] | 0x20; trans <<= 8;
582
+				trans |= puri->transport_val.s[2] | 0x20;
583
+				if (trans == 0x746C73) /* t l s */
584
+					return SIPS_PORT;
585
+			}
586
+			return SIP_PORT;
587
+		case SIPS_URI_T:
588
+		case TELS_URI_T:
589
+			return SIPS_PORT;
590
+		default:
591
+			BUG("unexpected URI type %d.\n", puri->type);
592
+	}
593
+	return 0;
594
+}
595
+
596
+/**
597
+ * Evaluate if next hop is a strict or loose router, by looking at the
598
+ * retr. buffer of the original INVITE.
599
+ * Assumes:
600
+ * 	orig_inv is a parsed SIP message;
601
+ * 	rtset is not NULL.
602
+ * @return:
603
+ * 	F_RB_NH_LOOSE : next hop was loose router;
604
+ * 	F_RB_NH_STRICT: nh is strict;
605
+ * 	0 on error.
606
+ */
607
+static unsigned long nhop_type(sip_msg_t *orig_inv, rte_t *rtset,
608
+		const struct dest_info *dst_inv, str *contact)
609
+{
610
+	struct sip_uri puri, topr_uri, lastr_uri, inv_ruri, cont_uri;
611
+	struct ip_addr *uri_ia;
612
+	union sockaddr_union uri_sau;
613
+	unsigned int uri_port, dst_port, inv_port, cont_port, lastr_port;
614
+	rte_t *last_r;
615
+#ifdef TM_LOC_ACK_DO_REV_DNS
616
+	struct ip_addr ia;
617
+	struct hostent *he;
618
+	char **alias;
619
+#endif
620
+
621
+#define PARSE_URI(_str_, _uri_) \
622
+	do { \
623
+		/* parse_uri() 0z the puri */ \
624
+		if (parse_uri((_str_)->s, \
625
+				(_str_)->len, _uri_) < 0) { \
626
+			ERR("failed to parse route body '%.*s'.\n", STR_FMT(_str_)); \
627
+			return 0; \
628
+		} \
629
+	} while (0)
630
+
631
+#define HAS_LR(_rte_) \
632
+	({ \
633
+		PARSE_URI(&(_rte_)->ptr->nameaddr.uri, &puri); \
634
+		puri.lr.s; \
635
+	})
636
+
637
+#define URI_PORT(_puri_, _port) \
638
+	do { \
639
+		if (! (_port = uri2port(_puri_))) \
640
+			return 0; \
641
+	} while (0)
642
+
643
+	/* examine the easy/fast & positive cases foremost */
644
+
645
+	/* [1] check if 1st route lacks ;lr */
646
+	DEBUG("checking lack of ';lr' in 1st route.\n");
647
+	if (! HAS_LR(rtset))
648
+		return F_RB_NH_STRICT;
649
+	topr_uri = puri; /* save 1st route's URI */
650
+
651
+	/* [2] check if last route shows ;lr */
652
+	DEBUG("checking presence of ';lr' in last route.\n");
653
+	for (last_r = rtset; last_r->next; last_r = last_r->next)
654
+		/* scroll down to last route */
655
+		;
656
+	if (HAS_LR(last_r))
657
+		return F_RB_NH_LOOSE;
658
+
659
+	/* [3] 1st route has ;lr -> check if the destination of original INV
660
+	 * equals the address provided by this route; if does -> loose */
661
+	DEBUG("checking INVITE's destination against its first route.\n");
662
+	URI_PORT(&topr_uri, uri_port);
663
+	if (! (dst_port = su_getport((void *)&dst_inv->to)))
664
+		return 0; /* not really expected */
665
+	if (dst_port != uri_port)
666
+		return F_RB_NH_STRICT;
667
+	/* if 1st route contains an IP address, comparing it against .dst */
668
+	if ((uri_ia = str2ip(&topr_uri.host))
669
+#ifdef USE_IPV6
670
+			|| (uri_ia = str2ip6(&topr_uri.host))
671
+#endif
672
+			) {
673
+		/* we have an IP address in route -> comparison can go swiftly */
674
+		if (init_su(&uri_sau, uri_ia, uri_port) < 0)
675
+			return 0; /* not really expected */
676
+		if (su_cmp(&uri_sau, (void *)&dst_inv->to))
677
+			/* ;lr and sent there */
678
+			return F_RB_NH_LOOSE;
679
+		else
680
+			/* ;lr and NOT sent there (probably sent to RURI address) */
681
+			return F_RB_NH_STRICT;
682
+	} else {
683
+		/*if 1st route contains a name, rev resolve the .dst and compare*/
684
+		INFO("Failed to decode string '%.*s' in route set element as IP "
685
+				"address. Trying name resolution.\n",STR_FMT(&topr_uri.host));
686
+
687
+	/* TODO: alternatively, rev name and compare against dest. IP.  */
688
+#ifdef TM_LOC_ACK_DO_REV_DNS
689
+		ia.af = 0;
690
+		su2ip_addr(&ia, (void *)&dst_inv->to);
691
+		if (! ia.af)
692
+			return 0; /* not really expected */
693
+		if ((he = rev_resolvehost(&ia))) {
694
+			if ((strlen(he->h_name) == topr_uri.host.len) &&
695
+					(memcmp(he->h_name, topr_uri.host.s, 
696
+							topr_uri.host.len) == 0))
697
+				return F_RB_NH_LOOSE;
698
+			for (alias = he->h_aliases; *alias; alias ++)
699
+				if ((strlen(*alias) == topr_uri.host.len) &&
700
+						(memcmp(*alias, topr_uri.host.s, 
701
+								topr_uri.host.len) == 0))
702
+					return F_RB_NH_LOOSE;
703
+			return F_RB_NH_STRICT;
704
+		} else {
705
+			INFO("failed to resolve address '%s' to a name.\n", 
706
+					ip_addr2a(&ia));
707
+		}
708
+#endif
709
+	}
710
+
711
+	WARN("failed to establish with certainty the type of next hop; trying an"
712
+			" educated guess.\n");
713
+
714
+	/* [4] compare (possibly updated) remote target to original RURI; if
715
+	 * equal, a strict router's address wasn't filled in as RURI -> loose */
716
+	DEBUG("checking remote target against INVITE's RURI.\n");
717
+	PARSE_URI(contact, &cont_uri);
718
+	PARSE_URI(GET_RURI(orig_inv), &inv_ruri);
719
+	URI_PORT(&cont_uri, cont_port);
720
+	URI_PORT(&inv_ruri, inv_port);
721
+	if ((cont_port == inv_port) && (cont_uri.host.len == inv_ruri.host.len) &&
722
+			(memcmp(cont_uri.host.s, inv_ruri.host.s, cont_uri.host.len) == 0))
723
+		return F_RB_NH_LOOSE;
724
+
725
+	/* [5] compare (possibly updated) remote target to last route; if equal, 
726
+	 * strict router's address might have been filled as RURI and remote
727
+	 * target appended to route set -> strict */
728
+	DEBUG("checking remote target against INVITE's last route.\n");
729
+	PARSE_URI(&last_r->ptr->nameaddr.uri, &lastr_uri);
730
+	URI_PORT(&lastr_uri, lastr_port);
731
+	if ((cont_port == lastr_port) && 
732
+			(cont_uri.host.len == lastr_uri.host.len) &&
733
+			(memcmp(cont_uri.host.s, lastr_uri.host.s, 
734
+					lastr_uri.host.len) == 0))
735
+		return F_RB_NH_STRICT;
736
+
737
+	WARN("failed to establish the type of next hop; assuming loose router.\n");
738
+	return F_RB_NH_LOOSE;
739
+
740
+#undef PARSE_URI
741
+#undef HAS_LR
742
+#undef URI_PORT
743
+}
744
+
745
+/**
746
+ * Evaluates the routing elements in locally originated request or reply to
747
+ * locally originated request.
748
+ * If original INVITE was in-dialog (had to-tag), it uses the
749
+ * routes present there (b/c the 2xx for it does not have a RR set, normally).
750
+ * Otherwise, use the reply (b/c the INVITE does not have yet the complete 
751
+ * route set).
752
+ *
753
+ * @return: negative for failure; out params:
754
+ *  - list: route set;
755
+ *  - ruri: RURI to be used in ACK;
756
+ *  - nexthop: where to first send the ACK.
757
+ *
758
+ *  NOTE: assumes rpl's parsed to EOF!
759
+ *
760
+ */
761
+static int eval_uac_routing(sip_msg_t *rpl, const struct retr_buf *inv_rb, 
762
+		str* contact, struct rte **list, str *ruri, str *next_hop)
763
+{
764
+	sip_msg_t orig_inv, *sipmsg; /* reparse original INVITE */
765
+	rte_t *t, *prev_t, *rtset = NULL;
766
+	int is_req;
767
+	struct sip_uri puri;
768
+	static size_t chklen;
769
+	int ret = -1;
770
+	
771
+	/* parse the retr. buffer */
772
+	memset(&orig_inv, 0, sizeof(struct sip_msg));
773
+	orig_inv.buf = inv_rb->buffer;
774
+	orig_inv.len = inv_rb->buffer_len;
775
+	DEBUG("reparsing retransmission buffer of original INVITE:\n%.*s\n",
776
+			orig_inv.len, orig_inv.buf);
777
+	if (parse_msg(orig_inv.buf, orig_inv.len, &orig_inv) != 0) {
778
+		ERR("failed to parse retr buffer (weird!): \n%.*s\n", orig_inv.len,
779
+				orig_inv.buf);
780
+		return -1;
781
+	}
782
+
783
+	/* check if we need to look at request or reply */
784
+	if ((parse_headers(&orig_inv, HDR_TO_F, 0) < 0) || (! orig_inv.to)) {
785
+		/* the bug is at message assembly */
786
+		BUG("failed to parse INVITE retr. buffer and/or extract 'To' HF:"
787
+				"\n%.*s\n", orig_inv.len, orig_inv.buf);
788
+		goto end;
789
+	}
790
+	if (((struct to_body *)orig_inv.to->parsed)->tag_value.len) {
791
+		DEBUG("building ACK for in-dialog INVITE (using RS in orig. INV.)\n");
792
+		if (parse_headers(&orig_inv, HDR_EOH_F, 0) < 0) {
793
+			BUG("failed to parse INVITE retr. buffer to EOH:"
794
+					"\n%.*s\n", orig_inv.len, orig_inv.buf);
795
+			goto end;
796
+		}
797
+		sipmsg = &orig_inv;
798
+		is_req = 1;
799
+	} else {
800
+		DEBUG("building ACK for out-of-dialog INVITE (using RS in RR set).\n");
801
+		sipmsg = rpl;
802
+		is_req = 0;
803
+	}
804
+
805
+	/* extract the route set */
806
+	if (get_uac_rs(sipmsg, is_req, &rtset) < 0) {
807
+		ERR("failed to extract route set.\n");
808
+		goto end;
809
+	}
810
+
811
+	if (! rtset) { /* No routes */
812
+		*ruri = *contact;
813
+		*next_hop = *contact;
814
+	} else if (! is_req) { /* out of dialog req. */
815
+		if (parse_uri(rtset->ptr->nameaddr.uri.s, rtset->ptr->nameaddr.uri.len,
816
+				&puri) < 0) {
817
+			ERR("failed to parse first route in set.\n");
818
+			goto end;
819
+		}
820
+		
821
+		if (puri.lr.s) { /* Next hop is loose router */
822
+			*ruri = *contact;
823
+			*next_hop = rtset->ptr->nameaddr.uri;
824
+		} else { /* Next hop is strict router */
825
+			*ruri = rtset->ptr->nameaddr.uri;
826
+			*next_hop = *ruri;
827
+			/* consume first route, b/c it will be put in RURI */
828
+			t = rtset;
829
+			rtset = rtset->next;
830
+			pkg_free(t);
831
+		}
832
+	} else {
833
+		unsigned long route_flags = inv_rb->flags;
834
+		DEBUG("UAC rb flags: 0x%x.\n", (unsigned int)route_flags);
835
+eval_flags:
836
+		switch (route_flags & (F_RB_NH_LOOSE|F_RB_NH_STRICT)) {
837
+		case 0:
838
+			WARN("calculate_hooks() not called when built the local UAC of "
839
+					"in-dialog request, or called with empty route set.\n");
840
+			/* try to figure out what kind of hop is the next one
841
+			 * (strict/loose) by reading the original invite */
842
+			if ((route_flags = nhop_type(&orig_inv, rtset, &inv_rb->dst, 
843
+					contact))) {
844
+				DEBUG("original request's next hop type evaluated to: 0x%x.\n",
845
+						(unsigned int)route_flags);
846
+				goto eval_flags;
847
+			} else {
848
+				ERR("failed to establish what kind of router the next "
849
+						"hop is.\n");
850
+				goto end;
851
+			}
852
+			break;
853
+		case F_RB_NH_LOOSE:
854
+			*ruri = *contact;
855
+			*next_hop = rtset->ptr->nameaddr.uri;
856
+			break;
857
+		case F_RB_NH_STRICT:
858
+			/* find ptr to last route body that contains the (possibly) old 
859
+			 * remote target 
860
+			 */
861
+			for (t = rtset, prev_t = t; t->next; prev_t = t, t = t->next)
862
+				;
863
+			if ((t->ptr->len == contact->len) && 
864
+					(memcmp(t->ptr->nameaddr.name.s, contact->s, 
865
+							contact->len) == 0)){
866
+				/* the remote target didn't update -> keep the whole route set,
867
+				 * including the last entry */
868
+				/* do nothing */
869
+			} else {
870
+				/* trash last entry and replace with new remote target */
871
+				free_rte_list(t);
872
+				/* compact the rr_t struct along with rte. this way, free'ing
873
+				 * it can be done along with rte chunk, independent of Route
874
+				 * header parser's allocator (using pkg/shm) */
875
+				chklen = sizeof(struct rte) + sizeof(rr_t);
876
+				if (! (t = (struct rte *)pkg_malloc(chklen))) {
877
+					ERR("out of pkg memory (%zd required)\n", chklen);
878
+					goto end;
879
+				}
880
+				/* this way, .free_rr is also set to 0 (!!!) */
881
+				memset(t, 0, chklen); 
882
+				((rr_t *)&t[1])->nameaddr.name = *contact;
883
+				((rr_t *)&t[1])->len = contact->len;
884
+				/* chain the new route elem in set */
885
+				if (prev_t == rtset)
886
+				 	/*there is only one elem in route set: the remote target*/
887
+					rtset = t;
888
+				else
889
+					prev_t->next = t;
890
+			}
891
+
892
+			*ruri = *GET_RURI(&orig_inv); /* reuse original RURI */
893
+			*next_hop = *ruri;
894
+			break;
895
+		default:
896
+			/* probably a mem corruption */
897
+			BUG("next hop of original request marked as both loose and strict"
898
+					" router (buffer: %.*s).\n", inv_rb->buffer_len, 
899
+					inv_rb->buffer);
900
+#ifdef EXTRA_DEBUG
901
+			abort();
902
+#else
903
+			goto end;
904
+#endif
905
+		}
906
+	}
907
+
908
+	*list = rtset;
909
+	/* all went well */
910
+	ret = 0;
911
+end:
912
+	free_sip_msg(&orig_inv);
913
+	if (ret < 0)
914
+		free_rte_list(rtset);
915
+	return ret;
916
+}
551 917
 
552 918
      /*
553 919
       * The function creates an ACK to 200 OK. Route set will be created
... ...
@@ -556,8 +922,8 @@ static inline int get_contact_uri(struct sip_msg* msg, str* uri)
556 922
 	  * generates local ACK to 200 OK (on behalf of applications using uac)
557 923
       */
558 924
 char *build_dlg_ack(struct sip_msg* rpl, struct cell *Trans, 
559
-					unsigned int branch, str* to, unsigned int *len,
560
-					struct dest_info* dst)
925
+					unsigned int branch, str *hdrs, str *body,
926
+					unsigned int *len, struct dest_info* dst)
561 927
 {
562 928
 	char *req_buf, *p, *via;
563 929
 	unsigned int via_len;
... ...
@@ -568,18 +934,47 @@ char *build_dlg_ack(struct sip_msg* rpl, struct cell *Trans,
568 934
 	struct rte* list;
569 935
 	str contact, ruri, *cont;
570 936
 	str next_hop;
937
+	str body_len;
938
+	str _to, *to = &_to;
571 939
 #ifdef USE_DNS_FAILOVER
572 940
 	struct dns_srv_handle dns_h;
573 941
 #endif
942
+#ifdef WITH_AS_SUPPORT
943
+	/* With AS support, TM allows for external modules to generate building of
944
+	 * the ACK; in this case, the ACK's retransmission buffer is built once
945
+	 * and kept in memory (to help when retransmitted 2xx are received and ACK
946
+	 * must be resent).
947
+	 * Allocation of the string raw buffer that holds the ACK is piggy-backed
948
+	 * with allocation of the retransmission buffer (since both have the same
949
+	 * life-cycle): both the string buffer and retransm. buffer are placed 
950
+	 * into the same allocated chunk of memory (retr. buffer first, string 
951
+	 * buffer follows).In this case, the 'len' param is used as in-out 
952
+	 * parameter: 'in' to give the extra space needed by the retr. buffer,
953
+	 * 'out' to return the lenght of the allocated string buffer.
954
+	 */
955
+	unsigned offset = *len;
956
+#endif
574 957
 	
575
-	if (get_contact_uri(rpl, &contact) < 0) {
958
+	if (parse_headers(rpl, HDR_EOH_F, 0) == -1 || !rpl->to) {
959
+		ERR("Error while parsing headers.\n");
576 960
 		return 0;
961
+	} else {
962
+		_to.s = rpl->to->name.s;
963
+		_to.len = rpl->to->len;
577 964
 	}
578 965
 	
579
-	if (process_routeset(rpl, &contact, &list, &ruri, &next_hop) < 0) {
966
+	if (get_contact_uri(rpl, &contact) < 0) {
580 967
 		return 0;
581 968
 	}
582 969
 	
970
+	if (eval_uac_routing(rpl, &Trans->uac[branch].request, &contact, 
971
+			&list, &ruri, &next_hop) < 0) {
972
+		ERR("failed to evaluate routing elements.\n");
973
+		return 0;
974
+	}
975
+	DEBUG("ACK RURI: `%.*s', NH: `%.*s'.\n", STR_FMT(&ruri), 
976
+			STR_FMT(&next_hop));
977
+
583 978
 	if ((contact.s != ruri.s) || (contact.len != ruri.len)) {
584 979
 		     /* contact != ruri means that the next
585 980
 		      * hop is a strict router, cont will be non-zero
... ...
@@ -643,12 +1038,29 @@ char *build_dlg_ack(struct sip_msg* rpl, struct cell *Trans,
643 1038
 	
644 1039
 	     /* User Agent */
645 1040
 	if (server_signature) *len += USER_AGENT_LEN + CRLF_LEN;
1041
+		/* extra headers */
1042
+	if (hdrs)
1043
+		*len += hdrs->len;
1044
+		/* body */
1045
+	if (body) {
1046
+		body_len.s = int2str(body->len, &body_len.len);
1047
+		*len += body->len;
1048
+	} else {
1049
+		body_len.len = 0;
1050
+		body_len.s = NULL; /*4gcc*/
1051
+		*len += 1; /* for the (Cont-Len:) `0' */
1052
+	}
646 1053
 	     /* Content Length, EoM */
647
-	*len += CONTENT_LENGTH_LEN + 1 + CRLF_LEN + CRLF_LEN;
648
-	
1054
+	*len += CONTENT_LENGTH_LEN + body_len.len + CRLF_LEN + CRLF_LEN;
1055
+
1056
+#if WITH_AS_SUPPORT
1057
+	req_buf = shm_malloc(offset + *len + 1);
1058
+	req_buf += offset;
1059
+#else
649 1060
 	req_buf = shm_malloc(*len + 1);
1061
+#endif
650 1062
 	if (!req_buf) {
651
-		LOG(L_ERR, "build_dlg_ack: Cannot allocate memory\n");
1063
+		ERR("Cannot allocate memory (%u+1)\n", *len);
652 1064
 		goto error01;
653 1065
 	}
654 1066
 	p = req_buf;
... ...
@@ -679,8 +1091,23 @@ char *build_dlg_ack(struct sip_msg* rpl, struct cell *Trans,
679 1091
 		append_mem_block(p, USER_AGENT CRLF, USER_AGENT_LEN + CRLF_LEN);
680 1092
 	}
681 1093
 	
682
-	     /* Content Length, EoM */
683
-	append_mem_block(p, CONTENT_LENGTH "0" CRLF CRLF, CONTENT_LENGTH_LEN + 1 + CRLF_LEN + CRLF_LEN);
1094
+	/* extra headers */
1095
+	if (hdrs)
1096
+		append_mem_block(p, hdrs->s, hdrs->len);
1097
+	
1098
+	     /* Content Length, EoH, (body) */
1099
+	if (body) {
1100
+		append_mem_block(p, CONTENT_LENGTH, CONTENT_LENGTH_LEN);
1101
+		append_mem_block(p, body_len.s, body_len.len);
1102
+		append_mem_block(p, /*end crr. header*/CRLF /*EoH*/CRLF, CRLF_LEN + 
1103
+				CRLF_LEN);
1104
+		append_mem_block(p, body->s, body->len);
1105
+	} else {
1106
+		append_mem_block(p, CONTENT_LENGTH "0" CRLF CRLF, 
1107
+				CONTENT_LENGTH_LEN + 1 + CRLF_LEN + CRLF_LEN);
1108
+	}
1109
+
1110
+	/* EoM */
684 1111
 	*p = 0;
685 1112
 	
686 1113
 	pkg_free(via);
... ...
@@ -692,7 +1119,7 @@ char *build_dlg_ack(struct sip_msg* rpl, struct cell *Trans,
692 1119
  error:
693 1120
 	free_rte_list(list);
694 1121
 	return 0;
695
-  	 }
1122
+}
696 1123
 
697 1124
 
698 1125
 /*
... ...
@@ -959,7 +1386,7 @@ char* build_uac_req(str* method, str* headers, str* body, dlg_t* dialog, int bra
959 1386
      	if (body) memapp(w, body->s, body->len);
960 1387
 
961 1388
 #ifdef EXTRA_DEBUG
962
-	if (w-buf != *len ) abort();
1389
+	assert(w-buf == *len);
963 1390
 #endif
964 1391
 
965 1392
 	pkg_free(via.s);
... ...
@@ -72,8 +72,8 @@ char *build_uac_request(  str msg_type, str dst, str from,
72 72
  * local ACK to 200 OK (on behalf of applications using uac
73 73
  */
74 74
 char *build_dlg_ack(struct sip_msg* rpl, struct cell *Trans, 
75
-					unsigned int branch, str* to, unsigned int *len,
76
-					struct dest_info *dst);
75
+					unsigned int branch, str *hdrs, str *body,
76
+					unsigned int *len, struct dest_info* dst);
77 77
 
78 78
 
79 79
 /*
... ...
@@ -93,7 +93,9 @@
93 93
  */
94 94
 
95 95
 
96
-
96
+#ifdef EXTRA_DEBUG
97
+#include <assert.h>
98
+#endif
97 99
 #include "../../comp_defs.h"
98 100
 #include "../../hash_func.h"
99 101
 #include "../../dprint.h"
... ...
@@ -368,15 +370,43 @@ static char *build_local_ack(struct sip_msg* rpl, struct cell *trans,
368 370
 								int branch, unsigned int *ret_len,
369 371
 								struct dest_info*  dst)
370 372
 {
371
-	str to;
372
-	if (parse_headers(rpl, HDR_EOH_F, 0) == -1 || !rpl->to) {
373
-		LOG(L_ERR, "ERROR: build_local_ack: Error while parsing headers\n");
374
-		return 0;
373
+#ifdef WITH_AS_SUPPORT
374
+	struct retr_buf *local_ack, *old_lack;
375
+
376
+	/* do we have the ACK cache, previously build? */
377
+	if ((local_ack = trans->uac[0].local_ack) && local_ack->buffer_len) {
378
+		DEBUG("reusing ACK retr. buffer.\n");
379
+		*ret_len = local_ack->buffer_len;
380
+		*dst = local_ack->dst;
381
+		return local_ack->buffer;
382
+	}
383
+
384
+	/* the ACK will be built (and cached) by the AS (ack_local_uac()) */
385
+	if (trans->flags & T_NO_AUTO_ACK) 
386
+		return NULL;
387
+
388
+	if (! (local_ack = local_ack_rb(rpl, trans, branch, /*hdrs*/NULL, 
389
+			/*body*/NULL))) {
390
+		ERR("failed to build local ACK retransmission buffer (T@%p).\n",trans);
391
+		return NULL;
375 392
 	}
376 393
 
377
-	to.s = rpl->to->name.s;
378
-	to.len = rpl->to->len;
379
-	return build_dlg_ack(rpl, trans, branch, &to, ret_len, dst);
394
+	/* set the new buffer, but only if not already set (concurrent 2xx) */
395
+	if ((old_lack = (struct retr_buf *)atomic_cmpxchg_long(
396
+			(void *)&trans->uac[0].local_ack, 0, (long)local_ack))) {
397
+		/* buffer already set: trash current and use the winning one */
398
+		INFO("concurrent 2xx to local INVITE detected (T@%p).\n", trans);
399
+		free_local_ack(local_ack);
400
+		local_ack = old_lack;
401
+	}
402
+	
403
+	*ret_len = local_ack->buffer_len;
404
+	*dst = local_ack->dst;
405
+	return local_ack->buffer;
406
+#else /* ! WITH_AS_SUPPORT */
407
+	return build_dlg_ack(rpl, trans, branch, /*hdrs*/NULL, /*body*/NULL, 
408
+			ret_len, dst);
409
+#endif /* WITH_AS_SUPPORT */
380 410
 }
381 411
 
382 412
 
... ...
@@ -1125,9 +1155,16 @@ static enum rps t_should_relay_response( struct cell *Trans , int new_code,
1125 1155
 
1126 1156
 	/* not >=300 ... it must be 2xx or provisional 1xx */
1127 1157
 	if (new_code>=100) {
1158
+#ifdef WITH_AS_SUPPORT
1159
+			/* need a copy of the message for ACK generation */
1160
+			*should_store = (inv_through && is_local(Trans) && 
1161
+					(Trans->uac[branch].last_received < 200) &&
1162
+					(Trans->flags & T_NO_AUTO_ACK)) ? 1 : 0;
1163
+#else
1164
+		*should_store=0;
1165
+#endif
1128 1166
 		/* 1xx and 2xx except 100 will be relayed */
1129 1167
 		Trans->uac[branch].last_received=new_code;
1130
-		*should_store=0;
1131 1168
 		*should_relay= new_code==100? -1 : branch;
1132 1169
 		if (new_code>=200 ) {
1133 1170
 			which_cancel( Trans, cancel_bitmap );
... ...
@@ -1818,7 +1855,9 @@ int reply_received( struct sip_msg  *p_msg )
1818 1855
 													&onsend_params);
1819 1856
 					}
1820 1857
 #endif
1858
+#ifndef WITH_AS_SUPPORT
1821 1859
 					shm_free(ack);
1860
+#endif
1822 1861
 				}
1823 1862
 			}
1824 1863
 		}
... ...
@@ -194,6 +194,7 @@ static inline int t_uac_prepare(uac_req_t *uac_r,
194 194
 #ifdef USE_DNS_FAILOVER
195 195
 	struct dns_srv_handle dns_h;
196 196
 #endif
197
+	long nhtype;
197 198
 
198 199
 	ret=-1;
199 200
 	hi=0; /* make gcc happy */
... ...
@@ -203,7 +204,8 @@ static inline int t_uac_prepare(uac_req_t *uac_r,
203 204
 	/*** added by dcm 
204 205
 	 * - needed by external ua to send a request within a dlg
205 206
 	 */
206
-	if (w_calculate_hooks(uac_r->dialog)<0 && !uac_r->dialog->hooks.next_hop)
207
+	if ((nhtype = w_calculate_hooks(uac_r->dialog)) < 0)
208
+		/* if err's returned, the message is incorrect */
207 209
 		goto error2;
208 210
 
209 211
 	if (!uac_r->dialog->loc_seq.is_set) {
... ...
@@ -257,6 +259,10 @@ static inline int t_uac_prepare(uac_req_t *uac_r,
257 259
 		new_cell->flags |= T_IS_INVITE_FLAG;
258 260
 		new_cell->flags|=T_AUTO_INV_100 &
259 261
 				(!cfg_get(tm, tm_cfg, tm_auto_inv_100) -1);
262
+#ifdef WITH_AS_SUPPORT
263
+		if (uac_r->cb_flags & TMCB_DONT_ACK)
264
+			new_cell->flags |= T_NO_AUTO_ACK;
265
+#endif
260 266
 		lifetime=cfg_get(tm, tm_cfg, tm_max_inv_lifetime);
261 267
 	}else
262 268
 		lifetime=cfg_get(tm, tm_cfg, tm_max_noninv_lifetime);
... ...
@@ -282,8 +288,8 @@ static inline int t_uac_prepare(uac_req_t *uac_r,
282 288
 	set_kr(REQ_FWDED);
283 289
 
284 290
 	request = &new_cell->uac[0].request;
285
-	
286 291
 	request->dst = dst;
292
+	request->flags |= nhtype;
287 293
 
288 294
 	if (!is_ack) {
289 295
 #ifdef TM_DEL_UNREF
... ...
@@ -402,18 +408,7 @@ void send_prepared_request(struct retr_buf *request)
402 408
  */
403 409
 int t_uac(uac_req_t *uac_r)
404 410
 {
405
-	struct retr_buf *request;
406
-	struct cell *cell;
407
-	int ret;
408
-	int is_ack;
409
-
410
-	ret = t_uac_prepare(uac_r, &request, &cell);
411
-	if (ret < 0) return ret;
412
-	is_ack = (uac_r->method->len == 3) && (memcmp("ACK", uac_r->method->s, 3)==0) ? 1 : 0;
413
-	send_prepared_request_impl(request, !is_ack /* retransmit */);
414
-	if (cell && is_ack)
415
-		free_cell(cell);
416
-	return ret;
411
+	return t_uac_with_ids(uac_r, NULL, NULL);
417 412
 }
418 413
 
419 414
 /*
... ...
@@ -445,6 +440,140 @@ int t_uac_with_ids(uac_req_t *uac_r,
445 440
 	return ret;
446 441
 }
447 442
 
443
+#ifdef WITH_AS_SUPPORT
444
+struct retr_buf *local_ack_rb(sip_msg_t *rpl_2xx, struct cell *trans,
445
+					unsigned int branch, str *hdrs, str *body)
446
+{
447
+	struct retr_buf *lack;
448
+	unsigned int buf_len;
449
+	char *buffer;
450
+	struct dest_info dst;
451
+
452
+	buf_len = (unsigned)sizeof(struct retr_buf);
453
+	if (! (buffer = build_dlg_ack(rpl_2xx, trans, branch, hdrs, body, 
454
+			&buf_len, &dst))) {
455
+		return 0;
456
+	} else {
457
+		/* 'buffer' now points into a contiguous chunk of memory with enough
458
+		 * room to hold both the retr. buffer and the string raw buffer: it
459
+		 * points to the begining of the string buffer; we iterate back to get
460
+		 * the begining of the space for the retr. buffer. */
461
+		lack = &((struct retr_buf *)buffer)[-1];
462
+		lack->buffer = buffer;
463
+		lack->buffer_len = buf_len;
464
+		lack->dst = dst;
465
+	}
466
+
467
+	/* TODO: need next 2? */
468
+	lack->activ_type = TYPE_LOCAL_ACK;
469
+	lack->my_T = trans;
470
+
471
+	return lack;
472
+}
473
+
474
+void free_local_ack(struct retr_buf *lack)
475
+{
476
+	shm_free(lack);
477
+}
478
+
479
+void free_local_ack_unsafe(struct retr_buf *lack)
480
+{
481
+	shm_free_unsafe(lack);
482
+}
483
+
484
+/**
485
+ * @return: 
486
+ * 	0: success
487
+ * 	-1: internal error
488
+ * 	-2: insane call :)
489
+ */
490
+int ack_local_uac(struct cell *trans, str *hdrs, str *body)
491
+{
492
+	struct retr_buf *local_ack, *old_lack;
493
+	int ret;
494
+
495
+	/* sanity checks */
496
+
497
+#ifdef EXTRA_DEBUG
498
+	if (! trans) {
499
+		BUG("no transaction to ACK.\n");
500
+		abort();
501
+	}
502
+#endif
503
+
504
+#define RET_INVALID \
505
+		ret = -2; \
506
+		goto fin
507
+
508
+	if (! is_local(trans)) {
509
+		ERR("trying to ACK non local transaction (T@%p).\n", trans);
510
+		RET_INVALID;
511
+	}
512
+	if (! is_invite(trans)) {
513
+		ERR("trying to ACK non INVITE local transaction (T@%p).\n", trans);
514
+		RET_INVALID;
515
+	}
516
+	if (! trans->uac[0].reply) {
517
+		ERR("trying to ACK un-completed INVITE transaction (T@%p).\n", trans);
518
+		RET_INVALID;
519
+	}
520
+
521
+	if (! (trans->flags & T_NO_AUTO_ACK)) {
522
+		ERR("trying to ACK an auto-ACK transaction (T@%p).\n", trans);
523
+		RET_INVALID;
524
+	}
525
+	if (trans->uac[0].local_ack) {
526
+		ERR("trying to rebuild ACK retransmission buffer (T@%p).\n", trans);
527
+		RET_INVALID;
528
+	}
529
+
530
+	/* looks sane: build the retransmission buffer */
531
+
532
+	if (! (local_ack = local_ack_rb(trans->uac[0].reply, trans, /*branch*/0, 
533
+			hdrs, body))) {
534
+		ERR("failed to build ACK retransmission buffer");
535
+		RET_INVALID;
536
+	} else {
537
+		/* set the new buffer, but only if not already set (conc. invok.) */
538
+		if ((old_lack = (struct retr_buf *)atomic_cmpxchg_long(
539
+				(void *)&trans->uac[0].local_ack, 0, (long)local_ack))) {
540
+			/* buffer already set: deny current attempt */
541
+			ERR("concurrent ACKing for local INVITE detected (T@%p).\n",trans);
542
+			free_local_ack(local_ack);
543
+			RET_INVALID;
544
+		}
545
+	}
546
+
547
+	if (msg_send(&local_ack->dst, local_ack->buffer, local_ack->buffer_len)<0){
548
+		/* hopefully will succeed on next 2xx retransmission */
549
+		ERR("failed to send local ACK (T@%p).\n", trans);
550
+		ret = -1;
551
+		goto fin;
552
+	}
553
+#ifdef	TMCB_ONSEND
554
+	else {
555
+		run_onsend_callbacks2(TMCB_REQUEST_SENT, &trans->uac[0]->request, 
556
+				local_ack->buffer, local_ack->buffer_len, &local_ack->dst,
557
+				TYPE_LOCAL_ACK);
558
+	}
559
+#endif
560
+
561
+	ret = 0;
562
+fin:
563
+	/* TODO: ugly! */
564
+	/* FIXME: the T had been obtain by t_lookup_ident()'ing for it, so, it is
565
+	 * ref-counted. The t_unref() can not be used, as it requests a valid SIP
566
+	 * message (all available might be the reply, but if AS goes wrong and
567
+	 * tries to ACK before the final reply is received, we still have to
568
+	 * lookup the T to find this out). */
569
+	UNREF( trans );
570
+	return ret;
571
+
572
+#undef RET_INVALID
573
+}
574
+#endif /* WITH_AS_SUPPORT */
575
+
576
+
448 577
 /*
449 578
  * Send a message within a dialog
450 579
  */
... ...
@@ -82,6 +82,9 @@ typedef int (*req_t)(uac_req_t *uac_r, str* ruri, str* to, str* from, str *next_
82 82
 typedef int (*t_uac_t)(uac_req_t *uac_r);
83 83
 typedef int (*t_uac_with_ids_t)(uac_req_t *uac_r,
84 84
 		unsigned int *ret_index, unsigned int *ret_label);
85
+#ifdef WITH_AS_SUPPORT
86
+typedef int (*ack_local_uac_f)(struct cell *trans, str *hdrs, str *body);
87
+#endif
85 88
 typedef int (*prepare_request_within_f)(uac_req_t *uac_r,
86 89
 		struct retr_buf **dst_req);
87 90
 typedef void (*send_prepared_request_f)(struct retr_buf *request_dst);
... ...
@@ -121,6 +124,19 @@ int req_within(uac_req_t *uac_r);
121 124
  */
122 125
 int req_outside(uac_req_t *uac_r, str* to, str* from);
123 126
 
127
+
128
+#ifdef WITH_AS_SUPPORT
129
+struct retr_buf *local_ack_rb(sip_msg_t *rpl_2xx, struct cell *trans,
130
+					unsigned int branch, str *hdrs, str *body);
131
+void free_local_ack(struct retr_buf *lack);
132
+void free_local_ack_unsafe(struct retr_buf *lack);
133
+
134
+/**
135
+ * ACK an existing local INVITE transaction...
136
+ */
137
+int ack_local_uac(struct cell *trans, str *hdrs, str *body);
138
+#endif
139
+
124 140
 /*
125 141
  * Send a transactional request, no dialogs involved
126 142
  */