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 103
 #ifdef USE_DNS_FAILOVER
104 104
 #include "../../dns_cache.h"
105 105
 #include "../../cfg_core.h" /* cfg_get(core, core_cfg, use_dns_failover) */
106
+#include "../../msg_translator.h"
107
+#include "lw_parser.h"
106 108
 #endif
107 109
 #ifdef USE_DST_BLACKLIST
108 110
 #include "../../dst_blacklist.h"
... ...
@@ -235,6 +242,82 @@ error01:
235 235
 	return shbuf;
236 236
 }
237 237
 
238
+#ifdef USE_DNS_FAILOVER
239
+/* Similar to print_uac_request(), but this function uses the outgoing message buffer of
240
+   the failed branch to construt the new message in case of DNS failover.
241
+
242
+   WARNING: only the first VIA header is replaced in the buffer, the rest
243
+   of the message is untuched, thus, the send socket is corrected only in the VIA HF.
244
+*/
245
+static char *print_uac_request_from_buf( struct cell *t, struct sip_msg *i_req,
246
+	int branch, str *uri, unsigned int *len, struct dest_info* dst,
247
+	char *buf, short buf_len)
248
+{
249
+	char *shbuf;
250
+	str branch_str;
251
+	char *via, *old_via_begin, *old_via_end;
252
+	unsigned int via_len;
253
+
254
+	shbuf=0;
255
+
256
+	/* ... we calculate branch ... */	
257
+	if (!t_calc_branch(t, branch, i_req->add_to_branch_s,
258
+			&i_req->add_to_branch_len ))
259
+	{
260
+		LOG(L_ERR, "ERROR: print_uac_request_from_buf: branch computation failed\n");
261
+		goto error00;
262
+	}
263
+	branch_str.s = i_req->add_to_branch_s;
264
+	branch_str.len = i_req->add_to_branch_len;
265
+
266
+	/* find the beginning of the first via header in the buffer */
267
+	old_via_begin = lw_find_via(buf, buf+buf_len);
268
+	if (!old_via_begin) {
269
+		LOG(L_ERR, "ERROR: print_uac_request_from_buf: beginning of via header not found\n");
270
+		goto error00;
271
+	}
272
+	/* find the end of the first via header in the buffer */
273
+	old_via_end = lw_next_line(old_via_begin, buf+buf_len);
274
+	if (!old_via_end) {
275
+		LOG(L_ERR, "ERROR: print_uac_request_from_buf: end of via header not found\n");
276
+		goto error00;
277
+	}
278
+
279
+	/* create the new VIA HF */
280
+	via = create_via_hf(&via_len, i_req, dst, &branch_str);
281
+	if (!via) {
282
+		LOG(L_ERR, "ERROR: print_uac_request_from_buf: via building failed\n");
283
+		goto error00;
284
+	}
285
+
286
+	/* allocate memory for the new buffer */
287
+	*len = buf_len + via_len - (old_via_end - old_via_begin);
288
+	shbuf=(char *)shm_malloc(*len);
289
+	if (!shbuf) {
290
+		ser_error=E_OUT_OF_MEM;
291
+		LOG(L_ERR, "ERROR: print_uac_request_from_buf: no shmem\n");
292
+		goto error01;
293
+	}
294
+
295
+	/* construct the new buffer */
296
+	memcpy(shbuf, buf, old_via_begin-buf);
297
+	memcpy(shbuf+(old_via_begin-buf), via, via_len);
298
+	memcpy(shbuf+(old_via_begin-buf)+via_len, old_via_end, (buf+buf_len)-old_via_end);
299
+
300
+#ifdef DBG_MSG_QA
301
+	if (shbuf[*len-1]==0) {
302
+		LOG(L_ERR, "ERROR: print_uac_request_from_buf: sanity check failed\n");
303
+		abort();
304
+	}
305
+#endif
306
+
307
+error01:
308
+	pkg_free(via);
309
+error00:
310
+	return shbuf;
311
+}
312
+#endif
313
+
238 314
 /* introduce a new uac, which is blind -- it only creates the
239 315
    data structures and starts FR timer, but that's it; it does
240 316
    not print messages and send anything anywhere; that is good
... ...
@@ -375,6 +458,76 @@ error:
375 375
 
376 376
 
377 377
 #ifdef USE_DNS_FAILOVER
378
+/* Similar to add_uac(), but this function uses the outgoing message buffer of
379
+   the failed branch to construt the new message in case of DNS failover.
380
+*/
381
+static int add_uac_from_buf( struct cell *t, struct sip_msg *request, str *uri, int proto,
382
+			char *buf, short buf_len)
383
+{
384
+
385
+	int ret;
386
+	unsigned short branch;
387
+	char *shbuf;
388
+	unsigned int len;
389
+
390
+	branch=t->nr_of_outgoings;
391
+	if (branch==MAX_BRANCHES) {
392
+		LOG(L_ERR, "ERROR: add_uac_from_buf: maximum number of branches exceeded\n");
393
+		ret=ser_error=E_TOO_MANY_BRANCHES;
394
+		goto error;
395
+	}
396
+
397
+	/* check existing buffer -- rewriting should never occur */
398
+	if (t->uac[branch].request.buffer) {
399
+		LOG(L_CRIT, "ERROR: add_uac_from_buf: buffer rewrite attempt\n");
400
+		ret=ser_error=E_BUG;
401
+		goto error;
402
+	}
403
+
404
+	if (uri2dst(&t->uac[branch].dns_h, &t->uac[branch].request.dst,
405
+				request, uri, proto) == 0)
406
+	{
407
+		ret=ser_error=E_BAD_ADDRESS;
408
+		goto error;
409
+	}
410
+	
411
+	/* check if send_sock is ok */
412
+	if (t->uac[branch].request.dst.send_sock==0) {
413
+		LOG(L_ERR, "ERROR: add_uac_from_buf: can't fwd to af %d, proto %d "
414
+			" (no corresponding listening socket)\n",
415
+			t->uac[branch].request.dst.to.s.sa_family, 
416
+			t->uac[branch].request.dst.proto );
417
+		ret=ser_error=E_NO_SOCKET;
418
+		goto error;
419
+	}
420
+
421
+	/* now message printing starts ... */
422
+	shbuf=print_uac_request_from_buf( t, request, branch, uri, 
423
+							&len, &t->uac[branch].request.dst,
424
+							buf, buf_len);
425
+	if (!shbuf) {
426
+		ret=ser_error=E_OUT_OF_MEM;
427
+		goto error;
428
+	}
429
+
430
+	/* things went well, move ahead and install new buffer! */
431
+	t->uac[branch].request.buffer=shbuf;
432
+	t->uac[branch].request.buffer_len=len;
433
+	t->uac[branch].uri.s=t->uac[branch].request.buffer+
434
+		request->first_line.u.request.method.len+1;
435
+	t->uac[branch].uri.len=uri->len;
436
+	membar_write(); /* to allow lockless ops (e.g. which_cancel()) we want
437
+					   to be sure everything above is fully written before
438
+					   updating branches no. */
439
+	t->nr_of_outgoings=(branch+1);
440
+
441
+	/* done! */
442
+	ret=branch;
443
+		
444
+error:
445
+	return ret;
446
+}
447
+
378 448
 /* introduce a new uac to transaction, based on old_uac and a possible
379 449
  *  new ip address (if the dns name resolves to more ips). If no more
380 450
  *   ips are found => returns -1.
... ...
@@ -420,12 +573,23 @@ int add_uac_dns_fallback( struct cell *t, struct sip_msg* msg,
420 420
 			/* copy the dns handle into the new uac */
421 421
 			dns_srv_handle_cpy(&t->uac[t->nr_of_outgoings].dns_h,
422 422
 								&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, 
423
+
424
+			if (cfg_get(tm, tm_cfg, reparse_on_dns_failover))
425
+				/* Reuse the old buffer and only replace the via header.
426
+				 * The drowback is that the send_socket is not corrected
427
+				 * in the rest of the message, only in the VIA HF (Miklos) */
428
+				ret=add_uac_from_buf(t,  msg, &old_uac->uri, 
429
+							old_uac->request.dst.proto,
430
+							old_uac->request.buffer,
431
+							old_uac->request.buffer_len);
432
+			else
433
+				/* add_uac will use dns_h => next_hop will be ignored.
434
+				 * Unfortunately we can't reuse the old buffer, the branch id
435
+				 *  must be changed and the send_socket might be different =>
436
+				 *  re-create the whole uac */
437
+				ret=add_uac(t,  msg, &old_uac->uri, 0, 0, 
428 438
 							old_uac->request.dst.proto);
439
+
429 440
 			if (ret<0){
430 441
 				/* failed, delete the copied dns_h */
431 442
 				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 1099
 	*/
1100 1100
 	return t_relay_to(p_msg, proxy, proto, 1 /* replicate */);
1101 1101
 }
1102
+
1103
+/* fixup function for reparse_on_dns_failover modparam */
1104
+int reparse_on_dns_failover_fixup(void *handle, str *name, void **val)
1105
+{
1106
+#ifdef USE_DNS_FAILOVER
1107
+	if ((int)(long)(*val) && mhomed) {
1108
+		LOG(L_WARN, "WARNING: reparse_on_dns_failover_fixup:"
1109
+		"reparse_on_dns_failover is enabled on a "
1110
+		"multihomed host -- check the readme of tm module!\n");
1111
+	}
1112
+#endif
1113
+	return 0;
1114
+}
... ...
@@ -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 381
 	{"blst_methods_add",    PARAM_INT, &default_tm_cfg.tm_blst_methods_add   },
382 382
 	{"blst_methods_lookup", PARAM_INT, &default_tm_cfg.tm_blst_methods_lookup},
383 383
 	{"cancel_b_method",     PARAM_INT, &default_tm_cfg.cancel_b_flags},
384
+	{"reparse_on_dns_failover", PARAM_INT, &default_tm_cfg.reparse_on_dns_failover},
384 385
 	{0,0,0}
385 386
 };
386 387
 
... ...
@@ -612,6 +614,14 @@ static int mod_init(void)
612 612
 		return -1;
613 613
 	}
614 614
 
615
+#ifdef USE_DNS_FAILOVER
616
+	if (default_tm_cfg.reparse_on_dns_failover && mhomed) {
617
+		LOG(L_WARN, "WARNING: mod_init: "
618
+			"reparse_on_dns_failover is enabled on a "
619
+			"multihomed host -- check the readme of tm module!\n");
620
+	}
621
+#endif
622
+
615 623
 	/* declare the configuration */
616 624
 	if (cfg_declare("tm", tm_cfg_def, &default_tm_cfg, cfg_size(tm),
617 625
 			 &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 1427
 	struct lump* anchor;
1428 1428
 	struct lump* via_insert_param;
1429 1429
 	str branch;
1430
-	str extra_params;
1431
-	struct hostport hp;
1432 1430
 
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 1431
 	via_insert_param=0;
1442
-	extra_params.len=0;
1443
-	extra_params.s=0;
1444 1432
 	uri_len=0;
1445 1433
 	buf=msg->buf;
1446 1434
 	len=msg->len;
... ...
@@ -1451,25 +1441,6 @@ char * build_req_buf_from_sip_req( struct sip_msg* msg,
1451 1451
 	rport_buf=0;
1452 1452
 	line_buf=0;
1453 1453
 
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 1454
 	     /* Calculate message body difference and adjust
1474 1455
 	      * Content-Length
1475 1456
 	      */
... ...
@@ -1480,11 +1451,11 @@ char * build_req_buf_from_sip_req( struct sip_msg* msg,
1480 1480
 		goto error00;
1481 1481
 	}
1482 1482
 
1483
+	/* create a the via header */
1483 1484
 	branch.s=msg->add_to_branch_s;
1484 1485
 	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);
1486
+
1487
+	line_buf = create_via_hf( &via_len, msg, send_info, &branch);
1488 1488
 	if (!line_buf){
1489 1489
 		LOG(L_ERR,"ERROR: build_req_buf_from_sip_req: no via received!\n");
1490 1490
 		goto error00;
... ...
@@ -1615,11 +1586,6 @@ char * build_req_buf_from_sip_req( struct sip_msg* msg,
1615 1615
 #endif
1616 1616
 
1617 1617
 	*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 1618
 	return new_buf;
1624 1619
 
1625 1620
 error01:
... ...
@@ -1629,9 +1595,6 @@ error02:
1629 1629
 error03:
1630 1630
 	if (rport_buf) pkg_free(rport_buf);
1631 1631
 error00:
1632
-#ifdef USE_TCP
1633
-	if (id_buf) pkg_free(id_buf);
1634
-#endif
1635 1632
 	*returned_len=0;
1636 1633
 	return 0;
1637 1634
 }
... ...
@@ -2189,6 +2152,60 @@ char* via_builder( unsigned int *len,
2189 2189
 	return line_buf;
2190 2190
 }
2191 2191
 
2192
+/* creates a via header honoring the protocol of the incomming socket
2193
+ * msg is an optional parameter */
2194
+char* create_via_hf( unsigned int *len,
2195
+	struct sip_msg *msg,
2196
+	struct dest_info* send_info /* where to send the reply */,
2197
+	str* branch)
2198
+{
2199
+	char* via;
2200
+	str extra_params;
2201
+	struct hostport hp;
2202
+#ifdef USE_TCP
2203
+	char* id_buf;
2204
+	unsigned int id_len;
2205
+
2206
+
2207
+	id_buf=0;
2208
+	id_len=0;
2209
+#endif
2210
+	extra_params.len=0;
2211
+	extra_params.s=0;
2212
+
2213
+
2214
+#ifdef USE_TCP
2215
+	/* add id if tcp */
2216
+	if (msg
2217
+	&& ((msg->rcv.proto==PROTO_TCP)
2218
+#ifdef USE_TLS
2219
+			|| (msg->rcv.proto==PROTO_TLS)
2220
+#endif
2221
+			)){
2222
+		if  ((id_buf=id_builder(msg, &id_len))==0){
2223
+			LOG(L_ERR, "ERROR: create_via_hf:"
2224
+							" id_builder failed\n");
2225
+			return 0; /* we don't need to free anything,
2226
+			                 nothing alloc'ed yet*/
2227
+		}
2228
+		DBG("create_via_hf: id added: <%.*s>, rcv proto=%d\n",
2229
+				(int)id_len, id_buf, msg->rcv.proto);
2230
+		extra_params.s=id_buf;
2231
+		extra_params.len=id_len;
2232
+	}
2233
+#endif
2234
+
2235
+	set_hostport(&hp, msg);
2236
+	via = via_builder( len, send_info, branch,
2237
+							extra_params.len?&extra_params:0, &hp);
2238
+
2239
+#ifdef USE_TCP
2240
+	/* we do not need id_buf any more, the id is already in the new via header */
2241
+	if (id_buf) pkg_free(id_buf);
2242
+#endif
2243
+	return via;
2244
+}
2245
+
2192 2246
 /* builds a char* buffer from message headers without body
2193 2247
  * first line is excluded in case of skip_first_line=1
2194 2248
  * 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 */