Browse code

- Requests after a DNS failover are constructed from the outgoing message buffer of the failed branch instead of from the incomming request. - reparse_on_dns_failover module parameter is introduced. Closes SER-300

Miklos Tirpak authored on 10/03/2008 14:09:01
Showing 8 changed files
... ...
@@ -477,4 +477,42 @@ modparam("tm", "cancel_b_method", 1)
477 477
 	</example>
478 478
     </section>
479 479
 
480
+    <section id="reparse_on_dns_failover">
481
+	<title><varname>reparse_on_dns_failover</varname> (integer)</title>
482
+	<para>
483
+		If set to 1, the SIP message after a DNS failover is constructed
484
+		from the outgoing message buffer of the failed branch instead of
485
+		from the received request.
486
+	</para>
487
+	<para>
488
+		It must be set if multiple branches are installed, the SIP message is
489
+		modified differently in them, and at least one of them can result
490
+		in DNS failover. If the parameter is not set the per-branch modifications
491
+		are lost after the failover.
492
+	</para>
493
+	<para>
494
+		Note: If the parameter is set, branch route block and TMCB_REQUEST_FWDED
495
+		callback are not called in case of the failover.
496
+	</para>
497
+	<para>
498
+		Disadvantage: only the via header is replaced in the message buffer, so
499
+		the outgoing socket address is not corrected in any other part of the message.
500
+		It is dangerous on multihomed hosts: when the new SIP request after
501
+		the DNS failover is sent via different interface than the first request,
502
+		the message can contain incorrect ip address in the Record-Route header
503
+		for instance.
504
+	</para>
505
+	<para>
506
+		Default value is 1.
507
+	</para>
508
+	<example>
509
+	    <title>Set <varname>reparse_on_dns_failover</varname> parameter</title>
510
+	    <programlisting>
511
+...
512
+modparam("tm", "reparse_on_dns_failover", 0)
513
+...
514
+	    </programlisting>
515
+       </example>
516
+    </section>
517
+
480 518
 </section>
... ...
@@ -255,3 +255,27 @@ char *lw_next_line(char *buf, char *buf_end)
255 255
 
256 256
 	return c;
257 257
 }
258
+
259
+#ifdef USE_DNS_FAILOVER
260
+/* returns the pointer to the first VIA header */
261
+char *lw_find_via(char *buf, char *buf_end)
262
+{
263
+	char		*p;
264
+	unsigned int	val;
265
+
266
+	/* skip the first line */
267
+	p = eat_line(buf, buf_end - buf);
268
+
269
+	while (buf_end - p > 4) {
270
+		val = LOWER_DWORD(READ(p));
271
+		if ((val == _via1_) || (val == _via2_)
272
+		|| ((LOWER_BYTE(*p) == 'v')		/* compact header */
273
+			&& ((*(p+1) == ' ') || (*(p+1) == ':')) )
274
+				) return p;
275
+
276
+		p = lw_next_line(p, buf_end);
277
+	}
278
+	/* not found */
279
+	return 0;
280
+}
281
+#endif
... ...
@@ -43,4 +43,9 @@ char *lw_get_hf_name(char *begin, char *end,
43 43
 /* returns a pointer to the next line */
44 44
 char *lw_next_line(char *buf, char *buf_end);
45 45
 
46
+#ifdef USE_DNS_FAILOVER
47
+/* returns the pointer to the first VIA header */
48
+char *lw_find_via(char *buf, char *buf_end);
49
+#endif
50
+
46 51
 #endif /* _LW_PARSER_H */
... ...
@@ -72,6 +72,11 @@
72 72
  *              t_relay_cancel() introduced -- can be used to relay CANCELs
73 73
  *              at the beginning of the script. (Miklos)
74 74
  * 2007-06-04  running transaction are canceled hop by hop (andrei)
75
+ *  2007-08-37  In case of DNS failover the new SIP message is constructed
76
+ *              from the message buffer of the failed branch instead of
77
+ *              applying the lumps again, because the per-branch lumps are not saved,
78
+ *              thus, are not available. Set reparse_on_dns_failover to 0 to
79
+ *              revert the change. (Miklos)
75 80
  */
76 81
 
77 82
 #include "defs.h"
... ...
@@ -103,6 +108,8 @@
103 108
 #ifdef USE_DNS_FAILOVER
104 109
 #include "../../dns_cache.h"
105 110
 #include "../../cfg_core.h" /* cfg_get(core, core_cfg, use_dns_failover) */
111
+#include "../../msg_translator.h"
112
+#include "lw_parser.h"
106 113
 #endif
107 114
 #ifdef USE_DST_BLACKLIST
108 115
 #include "../../dst_blacklist.h"
... ...
@@ -235,6 +242,82 @@ error01:
235 242
 	return shbuf;
236 243
 }
237 244
 
245
+#ifdef USE_DNS_FAILOVER
246
+/* Similar to print_uac_request(), but this function uses the outgoing message buffer of
247
+   the failed branch to construt the new message in case of DNS failover.
248
+
249
+   WARNING: only the first VIA header is replaced in the buffer, the rest
250
+   of the message is untuched, thus, the send socket is corrected only in the VIA HF.
251
+*/
252
+static char *print_uac_request_from_buf( struct cell *t, struct sip_msg *i_req,
253
+	int branch, str *uri, unsigned int *len, struct dest_info* dst,
254
+	char *buf, short buf_len)
255
+{
256
+	char *shbuf;
257
+	str branch_str;
258
+	char *via, *old_via_begin, *old_via_end;
259
+	unsigned int via_len;
260
+
261
+	shbuf=0;
262
+
263
+	/* ... we calculate branch ... */	
264
+	if (!t_calc_branch(t, branch, i_req->add_to_branch_s,
265
+			&i_req->add_to_branch_len ))
266
+	{
267
+		LOG(L_ERR, "ERROR: print_uac_request_from_buf: branch computation failed\n");
268
+		goto error00;
269
+	}
270
+	branch_str.s = i_req->add_to_branch_s;
271
+	branch_str.len = i_req->add_to_branch_len;
272
+
273
+	/* find the beginning of the first via header in the buffer */
274
+	old_via_begin = lw_find_via(buf, buf+buf_len);
275
+	if (!old_via_begin) {
276
+		LOG(L_ERR, "ERROR: print_uac_request_from_buf: beginning of via header not found\n");
277
+		goto error00;
278
+	}
279
+	/* find the end of the first via header in the buffer */
280
+	old_via_end = lw_next_line(old_via_begin, buf+buf_len);
281
+	if (!old_via_end) {
282
+		LOG(L_ERR, "ERROR: print_uac_request_from_buf: end of via header not found\n");
283
+		goto error00;
284
+	}
285
+
286
+	/* create the new VIA HF */
287
+	via = create_via_hf(&via_len, i_req, dst, &branch_str);
288
+	if (!via) {
289
+		LOG(L_ERR, "ERROR: print_uac_request_from_buf: via building failed\n");
290
+		goto error00;
291
+	}
292
+
293
+	/* allocate memory for the new buffer */
294
+	*len = buf_len + via_len - (old_via_end - old_via_begin);
295
+	shbuf=(char *)shm_malloc(*len);
296
+	if (!shbuf) {
297
+		ser_error=E_OUT_OF_MEM;
298
+		LOG(L_ERR, "ERROR: print_uac_request_from_buf: no shmem\n");
299
+		goto error01;
300
+	}
301
+
302
+	/* construct the new buffer */
303
+	memcpy(shbuf, buf, old_via_begin-buf);
304
+	memcpy(shbuf+(old_via_begin-buf), via, via_len);
305
+	memcpy(shbuf+(old_via_begin-buf)+via_len, old_via_end, (buf+buf_len)-old_via_end);
306
+
307
+#ifdef DBG_MSG_QA
308
+	if (shbuf[*len-1]==0) {
309
+		LOG(L_ERR, "ERROR: print_uac_request_from_buf: sanity check failed\n");
310
+		abort();
311
+	}
312
+#endif
313
+
314
+error01:
315
+	pkg_free(via);
316
+error00:
317
+	return shbuf;
318
+}
319
+#endif
320
+
238 321
 /* introduce a new uac, which is blind -- it only creates the
239 322
    data structures and starts FR timer, but that's it; it does
240 323
    not print messages and send anything anywhere; that is good
... ...
@@ -375,6 +458,76 @@ error:
375 458
 
376 459
 
377 460
 #ifdef USE_DNS_FAILOVER
461
+/* Similar to add_uac(), but this function uses the outgoing message buffer of
462
+   the failed branch to construt the new message in case of DNS failover.
463
+*/
464
+static int add_uac_from_buf( struct cell *t, struct sip_msg *request, str *uri, int proto,
465
+			char *buf, short buf_len)
466
+{
467
+
468
+	int ret;
469
+	unsigned short branch;
470
+	char *shbuf;
471
+	unsigned int len;
472
+
473
+	branch=t->nr_of_outgoings;
474
+	if (branch==MAX_BRANCHES) {
475
+		LOG(L_ERR, "ERROR: add_uac_from_buf: maximum number of branches exceeded\n");
476
+		ret=ser_error=E_TOO_MANY_BRANCHES;
477
+		goto error;
478
+	}
479
+
480
+	/* check existing buffer -- rewriting should never occur */
481
+	if (t->uac[branch].request.buffer) {
482
+		LOG(L_CRIT, "ERROR: add_uac_from_buf: buffer rewrite attempt\n");
483
+		ret=ser_error=E_BUG;
484
+		goto error;
485
+	}
486
+
487
+	if (uri2dst(&t->uac[branch].dns_h, &t->uac[branch].request.dst,
488
+				request, uri, proto) == 0)
489
+	{
490
+		ret=ser_error=E_BAD_ADDRESS;
491
+		goto error;
492
+	}
493
+	
494
+	/* check if send_sock is ok */
495
+	if (t->uac[branch].request.dst.send_sock==0) {
496
+		LOG(L_ERR, "ERROR: add_uac_from_buf: can't fwd to af %d, proto %d "
497
+			" (no corresponding listening socket)\n",
498
+			t->uac[branch].request.dst.to.s.sa_family, 
499
+			t->uac[branch].request.dst.proto );
500
+		ret=ser_error=E_NO_SOCKET;
501
+		goto error;
502
+	}
503
+
504
+	/* now message printing starts ... */
505
+	shbuf=print_uac_request_from_buf( t, request, branch, uri, 
506
+							&len, &t->uac[branch].request.dst,
507
+							buf, buf_len);
508
+	if (!shbuf) {
509
+		ret=ser_error=E_OUT_OF_MEM;
510
+		goto error;
511
+	}
512
+
513
+	/* things went well, move ahead and install new buffer! */
514
+	t->uac[branch].request.buffer=shbuf;
515
+	t->uac[branch].request.buffer_len=len;
516
+	t->uac[branch].uri.s=t->uac[branch].request.buffer+
517
+		request->first_line.u.request.method.len+1;
518
+	t->uac[branch].uri.len=uri->len;
519
+	membar_write(); /* to allow lockless ops (e.g. which_cancel()) we want
520
+					   to be sure everything above is fully written before
521
+					   updating branches no. */
522
+	t->nr_of_outgoings=(branch+1);
523
+
524
+	/* done! */
525
+	ret=branch;
526
+		
527
+error:
528
+	return ret;
529
+}
530
+
378 531
 /* introduce a new uac to transaction, based on old_uac and a possible
379 532
  *  new ip address (if the dns name resolves to more ips). If no more
380 533
  *   ips are found => returns -1.
... ...
@@ -420,12 +573,23 @@ int add_uac_dns_fallback( struct cell *t, struct sip_msg* msg,
420 573
 			/* copy the dns handle into the new uac */
421 574
 			dns_srv_handle_cpy(&t->uac[t->nr_of_outgoings].dns_h,
422 575
 								&old_uac->dns_h);
423
-			/* add_uac will use dns_h => next_hop will be ignored.
424
-			 * Unfortunately we can't reuse the old buffer, the branch id
425
-			 *  must be changed and the send_socket might be different =>
426
-			 *  re-create the whole uac */
427
-			ret=add_uac(t,  msg, &old_uac->uri, 0, 0, 
576
+
577
+			if (cfg_get(tm, tm_cfg, reparse_on_dns_failover))
578
+				/* Reuse the old buffer and only replace the via header.
579
+				 * The drowback is that the send_socket is not corrected
580
+				 * in the rest of the message, only in the VIA HF (Miklos) */
581
+				ret=add_uac_from_buf(t,  msg, &old_uac->uri, 
582
+							old_uac->request.dst.proto,
583
+							old_uac->request.buffer,
584
+							old_uac->request.buffer_len);
585
+			else
586
+				/* add_uac will use dns_h => next_hop will be ignored.
587
+				 * Unfortunately we can't reuse the old buffer, the branch id
588
+				 *  must be changed and the send_socket might be different =>
589
+				 *  re-create the whole uac */
590
+				ret=add_uac(t,  msg, &old_uac->uri, 0, 0, 
428 591
 							old_uac->request.dst.proto);
592
+
429 593
 			if (ret<0){
430 594
 				/* failed, delete the copied dns_h */
431 595
 				dns_srv_handle_put(&t->uac[t->nr_of_outgoings].dns_h);
... ...
@@ -1099,3 +1263,16 @@ int t_replicate(struct sip_msg *p_msg,  struct proxy_l *proxy, int proto )
1099 1263
 	*/
1100 1264
 	return t_relay_to(p_msg, proxy, proto, 1 /* replicate */);
1101 1265
 }
1266
+
1267
+/* fixup function for reparse_on_dns_failover modparam */
1268
+int reparse_on_dns_failover_fixup(void *handle, str *name, void **val)
1269
+{
1270
+#ifdef USE_DNS_FAILOVER
1271
+	if ((int)(long)(*val) && mhomed) {
1272
+		LOG(L_WARN, "WARNING: reparse_on_dns_failover_fixup:"
1273
+		"reparse_on_dns_failover is enabled on a "
1274
+		"multihomed host -- check the readme of tm module!\n");
1275
+	}
1276
+#endif
1277
+	return 0;
1278
+}
... ...
@@ -73,6 +73,7 @@ int t_send_branch( struct cell *t, int branch, struct sip_msg* p_msg ,
73 73
 					struct proxy_l * proxy, int lock_replies);
74 74
 int t_relay_cancel(struct sip_msg* p_msg);
75 75
 
76
+int reparse_on_dns_failover_fixup(void *handle, str *name, void **val);
76 77
 
77 78
 #endif
78 79
 
... ...
@@ -106,6 +106,7 @@
106 106
 #include "../../route_struct.h"
107 107
 #include "../../route.h"
108 108
 #include "../../cfg/cfg.h"
109
+#include "../../globals.h"
109 110
 
110 111
 #include "config.h"
111 112
 #include "sip_msg.h"
... ...
@@ -381,6 +382,7 @@ static param_export_t params[]={
381 382
 	{"blst_methods_add",    PARAM_INT, &default_tm_cfg.tm_blst_methods_add   },
382 383
 	{"blst_methods_lookup", PARAM_INT, &default_tm_cfg.tm_blst_methods_lookup},
383 384
 	{"cancel_b_method",     PARAM_INT, &default_tm_cfg.cancel_b_flags},
385
+	{"reparse_on_dns_failover", PARAM_INT, &default_tm_cfg.reparse_on_dns_failover},
384 386
 	{0,0,0}
385 387
 };
386 388
 
... ...
@@ -612,6 +614,14 @@ static int mod_init(void)
612 614
 		return -1;
613 615
 	}
614 616
 
617
+#ifdef USE_DNS_FAILOVER
618
+	if (default_tm_cfg.reparse_on_dns_failover && mhomed) {
619
+		LOG(L_WARN, "WARNING: mod_init: "
620
+			"reparse_on_dns_failover is enabled on a "
621
+			"multihomed host -- check the readme of tm module!\n");
622
+	}
623
+#endif
624
+
615 625
 	/* declare the configuration */
616 626
 	if (cfg_declare("tm", tm_cfg_def, &default_tm_cfg, cfg_size(tm),
617 627
 			 &tm_cfg)) {
... ...
@@ -59,6 +59,8 @@
59 59
  * 2006-04-20  build_req_from_sip_req, via_builder and lump_* functions
60 60
  *              use now struct dest_info; lumps & via comp param support
61 61
  *              (rfc3486) (andrei)
62
+ * 2007-08-31  id_builder() and via_builder() are grouped into one function:
63
+ *             create_via_hf() -- tm module needs them as well (Miklos)
62 64
  *
63 65
  */
64 66
 /* Via special params:
... ...
@@ -1427,20 +1429,8 @@ char * build_req_buf_from_sip_req( struct sip_msg* msg,
1427 1429
 	struct lump* anchor;
1428 1430
 	struct lump* via_insert_param;
1429 1431
 	str branch;
1430
-	str extra_params;
1431
-	struct hostport hp;
1432 1432
 
1433
-#ifdef USE_TCP
1434
-	char* id_buf;
1435
-	unsigned int id_len;
1436
-
1437
-
1438
-	id_buf=0;
1439
-	id_len=0;
1440
-#endif
1441 1433
 	via_insert_param=0;
1442
-	extra_params.len=0;
1443
-	extra_params.s=0;
1444 1434
 	uri_len=0;
1445 1435
 	buf=msg->buf;
1446 1436
 	len=msg->len;
... ...
@@ -1451,25 +1441,6 @@ char * build_req_buf_from_sip_req( struct sip_msg* msg,
1451 1441
 	rport_buf=0;
1452 1442
 	line_buf=0;
1453 1443
 
1454
-#ifdef USE_TCP
1455
-	/* add id if tcp */
1456
-	if (msg->rcv.proto==PROTO_TCP
1457
-#ifdef USE_TLS
1458
-			|| msg->rcv.proto==PROTO_TLS
1459
-#endif
1460
-			){
1461
-		if  ((id_buf=id_builder(msg, &id_len))==0){
1462
-			LOG(L_ERR, "ERROR: build_req_buf_from_sip_req:"
1463
-							" id_builder failed\n");
1464
-			goto error00; /* we don't need to free anything,
1465
-			                 nothing alloc'ed yet*/
1466
-		}
1467
-		DBG("build_req_from_req: id added: <%.*s>, rcv proto=%d\n",
1468
-				(int)id_len, id_buf, msg->rcv.proto);
1469
-		extra_params.s=id_buf;
1470
-		extra_params.len=id_len;
1471
-	}
1472
-#endif
1473 1444
 	     /* Calculate message body difference and adjust
1474 1445
 	      * Content-Length
1475 1446
 	      */
... ...
@@ -1480,11 +1451,11 @@ char * build_req_buf_from_sip_req( struct sip_msg* msg,
1480 1451
 		goto error00;
1481 1452
 	}
1482 1453
 
1454
+	/* create a the via header */
1483 1455
 	branch.s=msg->add_to_branch_s;
1484 1456
 	branch.len=msg->add_to_branch_len;
1485
-	set_hostport(&hp, msg);
1486
-	line_buf = via_builder( &via_len, send_info, &branch,
1487
-							extra_params.len?&extra_params:0, &hp);
1457
+
1458
+	line_buf = create_via_hf( &via_len, msg, send_info, &branch);
1488 1459
 	if (!line_buf){
1489 1460
 		LOG(L_ERR,"ERROR: build_req_buf_from_sip_req: no via received!\n");
1490 1461
 		goto error00;
... ...
@@ -1615,11 +1586,6 @@ char * build_req_buf_from_sip_req( struct sip_msg* msg,
1615 1586
 #endif
1616 1587
 
1617 1588
 	*returned_len=new_len;
1618
-	/* cleanup */
1619
-#ifdef USE_TCP
1620
-	if (id_buf) pkg_free(id_buf); /* it's not in a lump => we don't need it
1621
-									 anymore */
1622
-#endif
1623 1589
 	return new_buf;
1624 1590
 
1625 1591
 error01:
... ...
@@ -1629,9 +1595,6 @@ error02:
1629 1595
 error03:
1630 1596
 	if (rport_buf) pkg_free(rport_buf);
1631 1597
 error00:
1632
-#ifdef USE_TCP
1633
-	if (id_buf) pkg_free(id_buf);
1634
-#endif
1635 1598
 	*returned_len=0;
1636 1599
 	return 0;
1637 1600
 }
... ...
@@ -2189,6 +2152,60 @@ char* via_builder( unsigned int *len,
2189 2152
 	return line_buf;
2190 2153
 }
2191 2154
 
2155
+/* creates a via header honoring the protocol of the incomming socket
2156
+ * msg is an optional parameter */
2157
+char* create_via_hf( unsigned int *len,
2158
+	struct sip_msg *msg,
2159
+	struct dest_info* send_info /* where to send the reply */,
2160
+	str* branch)
2161
+{
2162
+	char* via;
2163
+	str extra_params;
2164
+	struct hostport hp;
2165
+#ifdef USE_TCP
2166
+	char* id_buf;
2167
+	unsigned int id_len;
2168
+
2169
+
2170
+	id_buf=0;
2171
+	id_len=0;
2172
+#endif
2173
+	extra_params.len=0;
2174
+	extra_params.s=0;
2175
+
2176
+
2177
+#ifdef USE_TCP
2178
+	/* add id if tcp */
2179
+	if (msg
2180
+	&& ((msg->rcv.proto==PROTO_TCP)
2181
+#ifdef USE_TLS
2182
+			|| (msg->rcv.proto==PROTO_TLS)
2183
+#endif
2184
+			)){
2185
+		if  ((id_buf=id_builder(msg, &id_len))==0){
2186
+			LOG(L_ERR, "ERROR: create_via_hf:"
2187
+							" id_builder failed\n");
2188
+			return 0; /* we don't need to free anything,
2189
+			                 nothing alloc'ed yet*/
2190
+		}
2191
+		DBG("create_via_hf: id added: <%.*s>, rcv proto=%d\n",
2192
+				(int)id_len, id_buf, msg->rcv.proto);
2193
+		extra_params.s=id_buf;
2194
+		extra_params.len=id_len;
2195
+	}
2196
+#endif
2197
+
2198
+	set_hostport(&hp, msg);
2199
+	via = via_builder( len, send_info, branch,
2200
+							extra_params.len?&extra_params:0, &hp);
2201
+
2202
+#ifdef USE_TCP
2203
+	/* we do not need id_buf any more, the id is already in the new via header */
2204
+	if (id_buf) pkg_free(id_buf);
2205
+#endif
2206
+	return via;
2207
+}
2208
+
2192 2209
 /* builds a char* buffer from message headers without body
2193 2210
  * first line is excluded in case of skip_first_line=1
2194 2211
  * error is set -1 if the memory allocation failes
... ...
@@ -105,6 +105,12 @@ char* via_builder( unsigned int *len,
105 105
 	struct dest_info* send_info,
106 106
 	str *branch, str* extra_params, struct hostport *hp );
107 107
 
108
+/* creates a via header honoring the protocol of the incomming socket
109
+ * msg is an optional parameter */
110
+char* create_via_hf( unsigned int *len,
111
+	struct sip_msg *msg,
112
+	struct dest_info* send_info /* where to send the reply */,
113
+	str* branch);
108 114
 
109 115
 int branch_builder( unsigned int hash_index, 
110 116
 	/* only either parameter useful */