Browse code

- dev version increased - applied patch from Carsten Bock - closes #1689036 - set of enhancement to dispatcher module - hash over the authentication-username - ds_is_from_list() to find if source ip a destination list - set a gateway to a probing mode (after a definable threshhold) by calling ds_mark_dst("p"). If a gateway/proxy fails several times (per default three times), it will be removed from the routing and be probed: The Proxy will send an OPTIONS-Request to the proxy/gateway and if it receives a "successful" reply (200 OK or 501 Not implemented) it will be automatically reactivated. We accept "501 Not implemented" as well, because some Cisco gateways do not support the OPTIONS-Request and reply with a "501 Not implemented".

git-svn-id: https://openser.svn.sourceforge.net/svnroot/openser/trunk@2177 689a6050-402a-0410-94f2-e92a70836424

Daniel-Constantin Mierla authored on 09/05/2007 14:54:25
Showing 6 changed files
... ...
@@ -9,6 +9,8 @@ Edited by
9 9
 
10 10
 Daniel-Constantin Mierla
11 11
 
12
+Carsten Bock
13
+
12 14
    Copyright � 2004 FhG FOKUS
13 15
 
14 16
    Copyright � 2005 Voice-System.RO
... ...
@@ -32,6 +34,10 @@ Daniel-Constantin Mierla
32 34
               1.3.5. dst_avp (str)
33 35
               1.3.6. grp_avp (str)
34 36
               1.3.7. cnt_avp (str)
37
+              1.3.8. ds_ping_method (string)
38
+              1.3.9. ds_ping_from (string)
39
+              1.3.10. ds_ping_interval (int)
40
+              1.3.11. ds_probing_threshhold (int)
35 41
 
36 42
         1.4. Exported Functions
37 43
 
... ...
@@ -40,6 +46,9 @@ Daniel-Constantin Mierla
40 46
               1.4.3. ds_next_dst()
41 47
               1.4.4. ds_next_domain()
42 48
               1.4.5. ds_mark_dst()
49
+              1.4.6. ds_mark_dst("s")
50
+              1.4.7. ds_is_from_list()
51
+              1.4.8. ds_is_from_list("group")
43 52
 
44 53
         1.5. Exported MI Functions
45 54
 
... ...
@@ -62,9 +71,13 @@ Daniel-Constantin Mierla
62 71
    1-5. Set the "dst_avp" parameter
63 72
    1-6. Set the "grp_avp" parameter
64 73
    1-7. Set the "cnt_avp" parameter
65
-   1-8. ds_select_dst usage
66
-   1-9. dispatcher list file
67
-   1-10. OpenSER config script - sample dispatcher usage
74
+   1-8. Set the "ds_ping_method" parameter
75
+   1-9. Set the "ds_ping_from" parameter
76
+   1-10. Set the "ds_ping_interval" parameter
77
+   1-11. Set the "ds_probing_threshhold" parameter
78
+   1-12. ds_select_dst usage
79
+   1-13. dispatcher list file
80
+   1-14. OpenSER config script - sample dispatcher usage
68 81
      _________________________________________________________
69 82
 
70 83
 Chapter 1. User's Guide
... ...
@@ -86,7 +99,7 @@ Chapter 1. User's Guide
86 99
 
87 100
    The following modules must be loaded before this module:
88 101
 
89
-     * none.
102
+     * TM - only if active recovery of failed hosts is required.
90 103
      _________________________________________________________
91 104
 
92 105
 1.2.2. External libraries or applications
... ...
@@ -226,6 +239,69 @@ modparam("dispatcher", "force_dst", 1)
226 239
 
227 240
      _________________________________________________________
228 241
 
242
+1.3.8. ds_ping_method (string)
243
+
244
+   With this Method you can define, with which method you want to
245
+   probe the failed gateways. This method is only available, if
246
+   compiled with the probing of failed gateways enabled.
247
+
248
+   Default value is "OPTIONS". 
249
+
250
+   Example 1-8. Set the "ds_ping_method" parameter
251
+ ...
252
+ modparam("dispatcher", "ds_ping_method", "INFO")
253
+ ...
254
+
255
+     _________________________________________________________
256
+
257
+1.3.9. ds_ping_from (string)
258
+
259
+   With this Method you can define the "From:"-Line for the
260
+   request, sent to the failed gateways. This method is only
261
+   available, if compiled with the probing of failed gateways
262
+   enabled.
263
+
264
+   Default value is "sip:dispatcher@localhost". 
265
+
266
+   Example 1-9. Set the "ds_ping_from" parameter
267
+ ...
268
+ modparam("dispatcher", "ds_ping_from", "sip:proxy@sip.somehost.com")
269
+ ...
270
+
271
+     _________________________________________________________
272
+
273
+1.3.10. ds_ping_interval (int)
274
+
275
+   With this Method you can define the interval for sending a
276
+   request to a failed gateway. This parameter is only used, when
277
+   the TM-Module is loaded. If set to "0", the pinging of failed
278
+   requests is disabled.
279
+
280
+   Default value is "10". 
281
+
282
+   Example 1-10. Set the "ds_ping_interval" parameter
283
+ ...
284
+ modparam("dispatcher", "ds_ping_interval", 30)
285
+ ...
286
+
287
+     _________________________________________________________
288
+
289
+1.3.11. ds_probing_threshhold (int)
290
+
291
+   If you want to set a gateway into probing mode, you will need
292
+   a specific number of requests until it will change from
293
+   "active" to probing. The number of attempts can be set with
294
+   this parameter.
295
+
296
+   Default value is "3". 
297
+
298
+   Example 1-11. Set the "ds_probing_threshhold" parameter
299
+ ...
300
+ modparam("dispatcher", "ds_probing_threshhold", 10)
301
+ ...
302
+
303
+     _________________________________________________________
304
+
229 305
 1.4. Exported Functions
230 306
 
231 307
 1.4.1. ds_select_dst(set, alg)
... ...
@@ -243,6 +319,9 @@ modparam("dispatcher", "force_dst", 1)
243 319
           + "2" - hash over to uri.
244 320
           + "3" - hash over request-uri.
245 321
           + "4" - round-robin (next destination).
322
+          + "5" - hash over authorization-username
323
+            (Proxy-Authorization or "normal" authorization). If
324
+            no username is found, round robin is used.
246 325
           + "X" - if the algorithm is not implemented, the first
247 326
             entry in set is chosen.
248 327
 
... ...
@@ -253,7 +332,7 @@ modparam("dispatcher", "force_dst", 1)
253 332
 
254 333
    This function can be used from REQUEST_ROUTE.
255 334
 
256
-   Example 1-8. ds_select_dst usage
335
+   Example 1-12. ds_select_dst usage
257 336
 ...
258 337
 ds_select_dst("1", "0");
259 338
 ...
... ...
@@ -300,6 +379,48 @@ ds_select_dst("1", "0");
300 379
    This function can be used from FAILURE_ROUTE.
301 380
      _________________________________________________________
302 381
 
382
+1.4.6. ds_mark_dst("s")
383
+
384
+   Mark the last used address from destination set as inactive
385
+   ("i"/"I"/"0"), active ("a"/"A"/"1") or probing ("p"/"P"/"2").
386
+   With this function, an automatic detection of failed gateways
387
+   can be implemented. When an address is marked as inactive or
388
+   probing, it will be ignored by 'ds_select_dst' and
389
+   'ds_select_domain'.
390
+
391
+   possible parameters:
392
+
393
+     * "i", "I" or "0" - the last destination should be set to
394
+       inactive and will be ignored in future requests.
395
+     * "a", "A" or "1" - the last destination should be set to
396
+       active.
397
+     * "p", "P" or "2" - the last destination will be set to
398
+       probing. Note: You will need to call this function
399
+       "threshhold"-times, before it will be actually set to
400
+       probing.
401
+
402
+   This function can be used from FAILURE_ROUTE.
403
+     _________________________________________________________
404
+
405
+1.4.7. ds_is_from_list()
406
+
407
+   This function returns true, if the current request comes from
408
+   a host from the dispatcher-list; otherwise false.
409
+
410
+   This function can be used from REQUEST_ROUTE, FAILURE_ROUTE,
411
+   BRANCH_ROUTE and ONREPLY_ROUTE.
412
+     _________________________________________________________
413
+
414
+1.4.8. ds_is_from_list("group")
415
+
416
+   This function returns true, if the current request comes from
417
+   a host in the given group of the dispatcher-list; otherwise
418
+   false.
419
+
420
+   This function can be used from REQUEST_ROUTE, FAILURE_ROUTE,
421
+   BRANCH_ROUTE and ONREPLY_ROUTE.
422
+     _________________________________________________________
423
+
303 424
 1.5. Exported MI Functions
304 425
 
305 426
 1.5.1. ds_set_state
... ...
@@ -314,6 +435,7 @@ ds_select_dst("1", "0");
314 435
      * _state_ : state of the destination address
315 436
           + "a": active
316 437
           + "i": inactive
438
+          + "p": probing
317 439
      * _group_: destination group id
318 440
      * _address_: address of the destination in the _group_
319 441
 
... ...
@@ -347,7 +469,7 @@ ds_select_dst("1", "0");
347 469
    integer value. Destination address must be a valid SIP URI.
348 470
    Empty lines or lines starting with "#" are ignored.
349 471
 
350
-   Example 1-9. dispatcher list file
472
+   Example 1-13. dispatcher list file
351 473
 ...
352 474
 # $Id$
353 475
 # dispatcher destination sets
... ...
@@ -368,7 +490,7 @@ ds_select_dst("1", "0");
368 490
 
369 491
    Next picture displays a sample usage of dispatcher.
370 492
 
371
-   Example 1-10. OpenSER config script - sample dispatcher usage
493
+   Example 1-14. OpenSER config script - sample dispatcher usage
372 494
 ...
373 495
 # $Id$
374 496
 # sample config file for dispatcher module
... ...
@@ -27,7 +27,14 @@
27 27
  * 2004-07-31  first version, by daniel
28 28
  * 2005-04-22  added ruri  & to_uri hashing (andrei)
29 29
  * 2005-12-10  added failover support via avp (daniel)
30
- * 
30
+ * 2006-08-15  added support for authorization username hashing (carsten)
31
+ * 2007-01-11  Added a function to check if a specific gateway is in a
32
+ * group (carsten)
33
+ * 2007-01-12  Added a threshhold for automatic deactivation (carsten)
34
+ * 2007-02-09  Added active probing of failed destinations and automatic
35
+ * re-enabling of destinations (carsten)
36
+ * 2007-05-08  Ported the changes to SVN-Trunk, renamed ds_is_domain to
37
+ * ds_is_from_list and modified the function to work with IPv6 adresses.
31 38
  */
32 39
 
33 40
 #include <stdio.h>
... ...
@@ -45,6 +52,9 @@
45 52
 #include "../../parser/parse_from.h"
46 53
 #include "../../usr_avp.h"
47 54
 #include "../../mi/mi.h"
55
+#include "../../parser/digest/digest.h"
56
+#include "../../resolve.h"
57
+#include "../tm/tm_load.h"
48 58
 
49 59
 #include "dispatch.h"
50 60
 
... ...
@@ -60,6 +70,10 @@ typedef struct _ds_dest
60 70
 {
61 71
 	str uri;
62 72
 	int flags;
73
+	struct ip_addr ip_address; /* IP-Address of the entry */
74
+    unsigned short int port; /* Port of the request URI */
75
+    int failure_count;
76
+    	
63 77
 	struct _ds_dest *next;
64 78
 } ds_dest_t, *ds_dest_p;
65 79
 
... ...
@@ -92,6 +106,10 @@ int ds_load_list(char *lfile)
92 106
 	ds_dest_p dp = NULL, dp0 = NULL;
93 107
 	ds_set_p  sp = NULL, sp0 = NULL;
94 108
 	ds_setidx_p si = NULL, si0 = NULL;
109
+
110
+	/* For DNS-Lookups */
111
+	char *hn;
112
+	struct hostent* he;
95 113
 	
96 114
 	if(lfile==NULL || strlen(lfile)<=0)
97 115
 	{
... ...
@@ -221,6 +239,33 @@ int ds_load_list(char *lfile)
221 239
 		dp->uri.s[uri.len]='\0';
222 240
 		dp->uri.len = uri.len;
223 241
 
242
+		/* The Hostname needs to be \0 terminated for resolvehost, so we
243
+		 * make a copy here. */
244
+		hn = (char*)pkg_malloc(puri.host.len + 1);
245
+		if(hn==NULL)
246
+		{
247
+			LOG(L_ERR, "DISPATCHER:ds_load_list: no more memory!!\n");
248
+			pkg_free(hn);
249
+			goto error;
250
+		}
251
+		strncpy(hn, puri.host.s, puri.host.len);
252
+		hn[puri.host.len]='\0';
253
+		
254
+		/* Do a DNS-Lookup for the Host-Name: */
255
+		he=resolvehost(hn, 1);
256
+		if (he==0){
257
+			LOG(L_ERR, "DISPATCHER:ds_load_list: could not resolve %.*s\n",
258
+					puri.host.len, puri.host.s);
259
+			pkg_free(hn);
260
+			goto error;
261
+		}
262
+		/* Free the hostname */
263
+		pkg_free(hn);
264
+		hostent2ip_addr(&dp->ip_address, he, 0);
265
+		
266
+		/* Copy the Port out of the URI: */
267
+		dp->port = puri.port_no;		
268
+
224 269
 		dp->next = sp->dlist;
225 270
 		sp->dlist = dp;
226 271
 		DBG("DISPATCHER:ds_load_list: dest [%d/%d/%d] <%.*s>\n", sp->index,
... ...
@@ -609,6 +654,65 @@ int ds_hash_ruri(struct sip_msg *msg, unsigned int *hash)
609 654
 	return 0;
610 655
 }
611 656
 
657
+int ds_hash_authusername(struct sip_msg *msg, unsigned int *hash)
658
+{
659
+	/* Header, which contains the authorization */
660
+	struct hdr_field* h = 0;
661
+	/* The Username */
662
+	str username = {0, 0};
663
+	/* The Credentials from this request */
664
+	auth_body_t* cred;
665
+	
666
+	if(msg==NULL || hash == NULL)
667
+	{
668
+		LOG(L_ERR, "DISPATCHER:ds_hash_authusername: bad parameters\n");
669
+		return -1;
670
+	}
671
+	if (parse_headers(msg, HDR_PROXYAUTH_F, 0) == -1)
672
+	{
673
+		LOG(L_ERR, "DISPATCHER:ds_hash_authusername: Error Parsing headers!\n");
674
+		return -1;
675
+	}
676
+	if (msg->proxy_auth && !msg->proxy_auth->parsed)
677
+		parse_credentials(msg->proxy_auth);
678
+	if (msg->proxy_auth && msg->proxy_auth->parsed) {
679
+		h = msg->proxy_auth;
680
+	}
681
+	if (!h)
682
+	{
683
+		if (parse_headers(msg, HDR_AUTHORIZATION_F, 0) == -1)
684
+		{
685
+			LOG(L_ERR, "DISPATCHER:ds_hash_authusername: Error Parsing headers!\n");
686
+			return -1;
687
+		}
688
+		if (msg->authorization && !msg->authorization->parsed)
689
+			parse_credentials(msg->authorization);
690
+		if (msg->authorization && msg->authorization->parsed) {
691
+			h = msg->authorization;
692
+		}
693
+	}
694
+	if (!h)
695
+	{
696
+		LOG(L_DBG, "DISPATCHER:ds_hash_authusername: No Authorization-Header!\n");
697
+		return 1;
698
+	}
699
+
700
+	cred=(auth_body_t*)(h->parsed);
701
+	if (!cred || !cred->digest.username.user.len)
702
+	{
703
+		LOG(L_ERR, "DISPATCHER:ds_hash_authusername: No Authorization-Username or Credentials!\n");
704
+		return 1;
705
+	}
706
+	
707
+	username.s = cred->digest.username.user.s;
708
+	username.len = cred->digest.username.user.len;
709
+
710
+	trim(&username);
711
+	
712
+	*hash = ds_get_hash(&username, NULL);
713
+	
714
+	return 0;
715
+}
612 716
 
613 717
 static inline int ds_get_index(int group, int *index)
614 718
 {
... ...
@@ -772,6 +876,25 @@ int ds_select_dst(struct sip_msg *msg, int set, int alg, int mode)
772 876
 			hash = _ds_list[idx].last;
773 877
 			_ds_list[idx].last = (_ds_list[idx].last+1) % _ds_list[idx].nr;
774 878
 		break;
879
+		case 5:
880
+			i = ds_hash_authusername(msg, &hash);
881
+			switch (i)
882
+			{
883
+				case 0:
884
+					/* Authorization-Header found: Nothing to be done here */
885
+				break;
886
+				case 1:
887
+					/* No Authorization found: Use round robin */
888
+					hash = _ds_list[idx].last;
889
+					_ds_list[idx].last = (_ds_list[idx].last+1) % _ds_list[idx].nr;
890
+				break;
891
+				default:
892
+					LOG(L_ERR, "DISPATCHER:ds_select_dst: can't get"
893
+							" authorization hash\n");
894
+					return -1;
895
+				break;
896
+			}
897
+		break;
775 898
 		default:
776 899
 			LOG(L_WARN,
777 900
 					"WARNING: ds_select_dst: algo %d not implemented"
... ...
@@ -786,7 +909,7 @@ int ds_select_dst(struct sip_msg *msg, int set, int alg, int mode)
786 909
 	else
787 910
 		hash = hash%_ds_list[idx].nr;
788 911
 	i=hash;
789
-	while(_ds_list[idx].dlist[i].flags & DS_INACTIVE_DST)
912
+	while ((_ds_list[idx].dlist[i].flags & DS_INACTIVE_DST) || (_ds_list[idx].dlist[i].flags & DS_PROBING_DST))
790 913
 	{
791 914
 		if(ds_use_default!=0)
792 915
 			i = (i+1)%(_ds_list[idx].nr-1);
... ...
@@ -938,11 +1061,19 @@ int ds_mark_dst(struct sip_msg *msg, int mode)
938 1061
 	if(prev_avp==NULL || !(prev_avp->flags&AVP_VAL_STR))
939 1062
 		return -1; /* dst avp deleted -- strange */
940 1063
 	
941
-	if(mode==1)
942
-		ret = ds_set_state(group, &avp_value.s, DS_INACTIVE_DST, 0);
943
-	else
1064
+	if(mode==1) {
1065
+		ret = ds_set_state(group, &avp_value.s, 
1066
+				DS_INACTIVE_DST|DS_PROBING_DST, 0);
1067
+	} else if(mode==2) {
1068
+		ret = ds_set_state(group, &avp_value.s, DS_PROBING_DST, 1);
1069
+		if (ret == 0) ret = ds_set_state(group, &avp_value.s,
1070
+				DS_INACTIVE_DST, 0);
1071
+	} else {
944 1072
 		ret = ds_set_state(group, &avp_value.s, DS_INACTIVE_DST, 1);
945
-
1073
+		if (ret == 0) ret = ds_set_state(group, &avp_value.s,
1074
+				DS_PROBING_DST, 0);
1075
+	}
1076
+	
946 1077
 	DBG("DISPATCHER:ds_mark_dst: mode [%d] grp [%d] dst [%.*s]\n",
947 1078
 			mode, group, avp_value.s.len, avp_value.s.s);
948 1079
 	
... ...
@@ -973,6 +1104,25 @@ int ds_set_state(int group, str *address, int state, int type)
973 1104
 				&& strncasecmp(_ds_list[idx].dlist[i].uri.s, address->s,
974 1105
 					address->len)==0)
975 1106
 		{
1107
+			/* remove the Probing/Inactive-State? Set the fail-count to 0. */
1108
+			if (state == DS_PROBING_DST) {
1109
+				if (type) {
1110
+					_ds_list[idx].dlist[i].failure_count++;
1111
+					/* Fire only, if the Threshold is reached. */
1112
+					if (_ds_list[idx].dlist[i].failure_count 
1113
+							< probing_threshhold) return 0;
1114
+					if (_ds_list[idx].dlist[i].failure_count
1115
+							> probing_threshhold) 
1116
+						_ds_list[idx].dlist[i].failure_count
1117
+							= probing_threshhold;				
1118
+				}
1119
+			}
1120
+			/* Reset the Failure-Counter */
1121
+			if ((state & DS_RESET_FAIL_DST) > 0) {
1122
+				_ds_list[idx].dlist[i].failure_count = 0;
1123
+				state &= ~DS_RESET_FAIL_DST;
1124
+			}
1125
+			
976 1126
 			if(type)
977 1127
 				_ds_list[idx].dlist[i].flags |= state;
978 1128
 			else
... ...
@@ -1002,8 +1152,23 @@ int ds_print_list(FILE *fout)
1002 1152
 		fprintf(fout, "\n set #%d\n", _ds_list[i].id);
1003 1153
 		for(j=0; j<_ds_list[i].nr; j++)
1004 1154
 		{
1005
-			fprintf(fout, "    %c   %.*s\n",
1006
-				(_ds_list[i].dlist[j].flags&DS_INACTIVE_DST)?'I':'A',
1155
+			if (_ds_list[i].dlist[j].flags&DS_INACTIVE_DST)
1156
+				fprintf(fout, "    Disabled         ");
1157
+			else if (_ds_list[i].dlist[j].flags&DS_PROBING_DST)
1158
+				fprintf(fout, "    Probing          ");
1159
+			else {
1160
+				fprintf(fout, "    Active");
1161
+				/* Optional: Print the tries for this host. */
1162
+				if (_ds_list[i].dlist[j].failure_count > 0) {
1163
+					fprintf(fout, " (Fail %d/%d)", 
1164
+							_ds_list[i].dlist[j].failure_count,
1165
+							probing_threshhold);
1166
+				} else {
1167
+					fprintf(fout, "           ");
1168
+				}
1169
+			}
1170
+			
1171
+			fprintf(fout, "   %.*s\n",
1007 1172
 				_ds_list[i].dlist[j].uri.len, _ds_list[i].dlist[j].uri.s);
1008 1173
 		}
1009 1174
 	}
... ...
@@ -1011,6 +1176,25 @@ int ds_print_list(FILE *fout)
1011 1176
 }
1012 1177
 
1013 1178
 
1179
+/* Checks, if the request (sip_msg *_m) comes from a host in a group
1180
+ * (group-id or -1 for all groups)
1181
+ */
1182
+int ds_is_from_list(struct sip_msg *_m, int group) {
1183
+    int i, j;
1184
+	for(i=0; i<_ds_list_nr; i++) {
1185
+		if ((group == -1) || (group == _ds_list[i].id)) {
1186
+			for(j=0; j<_ds_list[i].nr; j++) {
1187
+				if (ip_addr_cmp(&_m->rcv.src_ip,
1188
+							&_ds_list[i].dlist[j].ip_address)
1189
+					&& (_m->rcv.src_port == _ds_list[i].dlist[j].port)) {
1190
+					return 1;
1191
+				}
1192
+			}
1193
+		}
1194
+	}
1195
+	return -1;
1196
+}
1197
+
1014 1198
 int ds_print_mi_list(struct mi_node* rpl)
1015 1199
 {
1016 1200
 	int i, j, len;
... ...
@@ -1046,7 +1230,10 @@ int ds_print_mi_list(struct mi_node* rpl)
1046 1230
 			if(node == NULL)
1047 1231
 				return -1;
1048 1232
 
1049
-			c =  (_ds_list[i].dlist[j].flags&DS_INACTIVE_DST)?'I':'A';
1233
+			if (_ds_list[i].dlist[j].flags & DS_INACTIVE_DST) c = 'I';
1234
+			else if (_ds_list[i].dlist[j].flags & DS_PROBING_DST) c = 'P';
1235
+			else c = 'A';
1236
+			
1050 1237
 			attr = add_mi_attr (node, MI_DUP_VALUE, "flag",4, &c, 1);
1051 1238
 			if(attr == 0)
1052 1239
 				return -1;
... ...
@@ -1056,3 +1243,141 @@ int ds_print_mi_list(struct mi_node* rpl)
1056 1243
 
1057 1244
 	return 0;
1058 1245
 }
1246
+
1247
+/**
1248
+ * Callback-Function for the OPTIONS-Request
1249
+ * This Function is called, as soon as the Transaction is finished
1250
+ * (e. g. a Response came in, the timeout was hit, ...)
1251
+ * 
1252
+ */ 
1253
+static void ds_options_callback( struct cell *t, int type, struct tmcb_params *ps )
1254
+{
1255
+	int group = 0;
1256
+	str uri = {0, 0};
1257
+	/* The Param does contain the group, in which the failed host
1258
+	 * can be found.*/
1259
+	if (!*ps->param) {
1260
+		LOG(L_DBG, "No parameter provided, OPTIONS-Request was finished"
1261
+				" with code %d\n", ps->code);
1262
+		return;
1263
+	}
1264
+	/* The param is a (void*) Pointer, so we need to dereference it and
1265
+	 *  cast it to an int. */
1266
+	group = (int)(*ps->param);
1267
+	/* The SIP-URI is taken from the Transaction.
1268
+	 * Remove the "To: " (s+4) and the trailing new-line (s - 4 (To: )
1269
+	 * - 2 (\r\n)). */
1270
+	uri.s = t->to.s + 4;
1271
+	uri.len = t->to.len - 6;
1272
+	LOG(L_DBG,
1273
+			"OPTIONS-Request was finished with code %d (to %.*s, group %d)\n",
1274
+			ps->code, uri.len, uri.s, group);
1275
+	/* ps->code contains the result-code of the request.
1276
+	 * 
1277
+	 * We accept both a "200 OK", "501 Not supported" and "403" as an
1278
+	 * successful reply.
1279
+	 *   501: Cisco-Gateways reply with a "501 Not supported" to the request.
1280
+	 *   403: Aastra-Gateways reply with a "403" to the request. */
1281
+	if ((ps->code == 200) || (ps->code == 501) || (ps->code == 403)) {
1282
+		/* Set the according entry back to "Active":
1283
+		 *  remove the Probing/Inactive Flag and reset the failure counter. */
1284
+		if (ds_set_state(group, &uri,
1285
+					DS_INACTIVE_DST|DS_PROBING_DST|DS_RESET_FAIL_DST, 0) != 0) {
1286
+			LOG(L_ERR, "Setting the state failed (%.*s, group %d)\n", 
1287
+				uri.len, uri.s,
1288
+				group);
1289
+		}
1290
+	}
1291
+
1292
+	return;
1293
+}
1294
+
1295
+/*
1296
+ * Timer for checking inactive destinations
1297
+ * 
1298
+ * This timer is regularly fired.
1299
+ */
1300
+void ds_check_timer(unsigned int ticks, void* param) {
1301
+	int i, j;
1302
+	struct socket_info* send_sock = 0;
1303
+	struct sip_uri curi;
1304
+	union sockaddr_union to;
1305
+	struct hostent* he;
1306
+	unsigned short proto;
1307
+	dlg_t * dialog;	
1308
+	
1309
+	/* Check for the list. */
1310
+	if(_ds_list==NULL || _ds_list_nr<=0)
1311
+	{
1312
+		LOG(L_ERR, "DISPATCHER:ds_print_list: the list is null\n");
1313
+		return;
1314
+	}
1315
+	
1316
+	/* Iterate over the groups and the entries of each group: */
1317
+	for(i=0; i<_ds_list_nr; i++) {
1318
+		for(j=0; j<_ds_list[i].nr; j++) {
1319
+			/* If the Flag of the entry has "Probing set, send a probe:	*/
1320
+			if ((_ds_list[i].dlist[j].flags&DS_PROBING_DST) > 0) {
1321
+				LOG(L_DBG, "Probing set #%d, URI %.*s\n", _ds_list[i].id,
1322
+						_ds_list[i].dlist[j].uri.len,
1323
+						_ds_list[i].dlist[j].uri.s);
1324
+				
1325
+				/* Parse the URI of the destination, we want to probe (we need
1326
+				 * more information about the destination) */
1327
+				if (parse_uri(_ds_list[i].dlist[j].uri.s,
1328
+							_ds_list[i].dlist[j].uri.len, &curi) < 0) {
1329
+					LOG(L_ERR, "dispatcher: ds_check_timer: Unable to parse"
1330
+							" URI (%.*s)\n", _ds_list[i].dlist[j].uri.len,
1331
+							_ds_list[i].dlist[j].uri.s);
1332
+					continue;
1333
+				}
1334
+				
1335
+				/* If the port is not set, we use the default-SIP-Port.*/
1336
+				if (curi.port_no == 0)
1337
+					curi.port_no = SIP_PORT;
1338
+
1339
+				/* Resolve the Hostnamme */
1340
+				proto = curi.proto;
1341
+				he = sip_resolvehost(&curi.host, &curi.port_no, &proto, 
1342
+						(curi.type==SIPS_URI_T)?1:0 , 0);
1343
+				if (he == NULL){
1344
+					LOG(L_ERR,
1345
+						"dispatcher: ds_check_timer: can't resolve_host\n");
1346
+					continue;
1347
+				}
1348
+				
1349
+				/* Determine the sending-socket for sending the OPTIONS-Request
1350
+				 * Maybe, later, provide the possibility to use a specific
1351
+				 * socket */
1352
+				hostent2su(&to, he, 0, curi.port_no);
1353
+				if (send_sock==0) {
1354
+					/* send_sock=force_socket ? force_socket : 
1355
+						get_send_socket(0, &to, PROTO_UDP); */
1356
+					send_sock=get_send_socket(0, &to, PROTO_UDP);
1357
+				}
1358
+				if (send_sock == NULL) {
1359
+					LOG(L_ERR,
1360
+					"dispatcher: ds_check_timer: can't get sending socket\n");
1361
+					continue;
1362
+				}
1363
+				
1364
+				/* Execute the Dialog using the "req_outside"-Method of the
1365
+				 * TM-Module.
1366
+				 * req_outside(str* m, str* t, str* f, str* h, str* b,
1367
+				 *		dlg_t** d, transaction_cb c, void* cp); */
1368
+				if (tmb.t_request_outside(&ping_method,
1369
+							&_ds_list[i].dlist[j].uri,
1370
+							&ping_from,
1371
+							NULL,
1372
+							NULL,
1373
+							&dialog,
1374
+							ds_options_callback,
1375
+							(void*)_ds_list[i].id) < 0) {
1376
+					LOG(L_ERR,
1377
+					"dispatcher: ds_check_timer: unable to execute dialog\n");
1378
+				}
1379
+			}
1380
+		}
1381
+	}
1382
+}
1383
+
... ...
@@ -24,6 +24,12 @@
24 24
  * History
25 25
  * -------
26 26
  * 2004-07-31  first version, by daniel
27
+ * 2007-01-11  Added a function to check if a specific gateway is in
28
+ *		a group (carsten)
29
+ * 2007-02-09  Added active probing of failed destinations and automatic
30
+ *		re-enabling of destinations
31
+ * 2007-05-08  Ported the changes to SVN-Trunk and renamed ds_is_domain
32
+ *		to ds_is_from_list.
27 33
  */
28 34
 
29 35
 #ifndef _DISPATCH_H_
... ...
@@ -32,11 +38,16 @@
32 38
 #include <stdio.h>
33 39
 #include "../../items.h"
34 40
 #include "../../parser/msg_parser.h"
41
+#include "../tm/tm_load.h"
35 42
 
36 43
 
37 44
 #define DS_HASH_USER_ONLY	1  /* use only the uri user part for hashing */
38 45
 #define DS_FAILOVER_ON		2  /* store the other dest in avps */
46
+
39 47
 #define DS_INACTIVE_DST		1  /* inactive destination */
48
+#define DS_PROBING_DST		2  /* checking destination */
49
+#define DS_RESET_FAIL_DST	4  /* Reset-Failure-Counter */
50
+
40 51
 
41 52
 typedef struct _ds_param
42 53
 {
... ...
@@ -57,6 +68,12 @@ extern unsigned short grp_avp_type;
57 68
 extern int_str cnt_avp_name;
58 69
 extern unsigned short cnt_avp_type;
59 70
 
71
+/* Structure containing pointers to TM-functions */
72
+struct tm_binds tmb;
73
+extern str ping_method;
74
+extern str ping_from;
75
+extern int probing_threshhold; /* number of failed requests, before a destination is taken into probing */ 
76
+
60 77
 int ds_load_list(char *lfile);
61 78
 int ds_destroy_list();
62 79
 int ds_select_dst(struct sip_msg *msg, int set, int alg, int mode);
... ...
@@ -66,5 +83,11 @@ int ds_mark_dst(struct sip_msg *msg, int mode);
66 83
 int ds_print_list(FILE *fout);
67 84
 int ds_print_mi_list(struct mi_node* rpl);
68 85
 
86
+int ds_is_from_list(struct sip_msg *_m, int group);
87
+/*
88
+ * Timer for checking inactive destinations
89
+ */
90
+void ds_check_timer(unsigned int ticks, void* param);
91
+
69 92
 #endif
70 93
 
... ...
@@ -25,7 +25,12 @@
25 25
  * History
26 26
  * -------
27 27
  * 2004-07-31  first version, by daniel
28
- * 
28
+ * 2007-01-11  Added a function to check if a specific gateway is in a group
29
+ *				(carsten - Carsten Bock, BASIS AudioNet GmbH)
30
+ * 2007-02-09  Added active probing of failed destinations and automatic
31
+ *				re-enabling of destinations (carsten)
32
+ * 2007-05-08  Ported the changes to SVN-Trunk and renamed ds_is_domain
33
+ *				to ds_is_from_list.  (carsten)
29 34
  */
30 35
 
31 36
 #include <stdio.h>
... ...
@@ -61,6 +66,11 @@ unsigned short grp_avp_type;
61 66
 int_str cnt_avp_name;
62 67
 unsigned short cnt_avp_type;
63 68
 
69
+int probing_threshhold = 3; /* number of failed requests, before a destination
70
+							   is taken into probing */
71
+str ping_method = {"OPTIONS",7};
72
+str ping_from = {"sip:dispatcher@localhost", 24};
73
+static int ds_ping_interval = 30;
64 74
 
65 75
 /** module functions */
66 76
 static int mod_init(void);
... ...
@@ -73,6 +83,10 @@ static int w_ds_next_domain(struct sip_msg*, char*, char*);
73 83
 static int w_ds_mark_dst0(struct sip_msg*, char*, char*);
74 84
 static int w_ds_mark_dst1(struct sip_msg*, char*, char*);
75 85
 
86
+static int w_ds_is_from_list0(struct sip_msg*, char*, char*);
87
+static int w_ds_is_from_list1(struct sip_msg*, char*, char*);
88
+static int fixstring2int(void **, int);
89
+
76 90
 void destroy(void);
77 91
 
78 92
 static int ds_fixup(void** param, int param_no);
... ...
@@ -88,6 +102,8 @@ static cmd_export_t cmds[]={
88 102
 	{"ds_next_domain",   w_ds_next_domain,   0, ds_warn_fixup, FAILURE_ROUTE},
89 103
 	{"ds_mark_dst",      w_ds_mark_dst0,     0, ds_warn_fixup, FAILURE_ROUTE},
90 104
 	{"ds_mark_dst",      w_ds_mark_dst1,     1, ds_warn_fixup, FAILURE_ROUTE},
105
+	{"ds_is_from_list",  w_ds_is_from_list0, 0, 0, REQUEST_ROUTE|FAILURE_ROUTE|ONREPLY_ROUTE|BRANCH_ROUTE},
106
+	{"ds_is_from_list",  w_ds_is_from_list1, 1, fixstring2int, REQUEST_ROUTE|FAILURE_ROUTE|ONREPLY_ROUTE|BRANCH_ROUTE},
91 107
 	{0,0,0,0,0}
92 108
 };
93 109
 
... ...
@@ -100,6 +116,10 @@ static param_export_t params[]={
100 116
 	{"dst_avp",        STR_PARAM, &dst_avp_param},
101 117
 	{"grp_avp",        STR_PARAM, &grp_avp_param},
102 118
 	{"cnt_avp",        STR_PARAM, &cnt_avp_param},
119
+	{"ds_probing_threshhold", INT_PARAM, &probing_threshhold},
120
+	{"ds_ping_method",     STR_PARAM, &ping_method},
121
+	{"ds_ping_from",       STR_PARAM, &ping_from},
122
+	{"ds_ping_interval",   INT_PARAM, &ds_ping_interval},
103 123
 	{0,0,0}
104 124
 };
105 125
 
... ...
@@ -197,6 +217,35 @@ static int mod_init(void)
197 217
 		cnt_avp_name.n = 0;
198 218
 		cnt_avp_type = 0;
199 219
 	}
220
+	
221
+	if (ping_from.s) ping_from.len = strlen(ping_from.s);
222
+	if (ping_method.s) ping_method.len = strlen(ping_method.s);
223
+
224
+	/* Only, if the Probing-Timer is enabled the TM-API needs to be loaded: */
225
+	if (ds_ping_interval > 0) {
226
+		/*****************************************************
227
+		 * TM-Bindings
228
+	  	 *****************************************************/
229
+		load_tm_f load_tm;
230
+		load_tm=(load_tm_f)find_export("load_tm", 0, 0);
231
+	
232
+		/* import the TM auto-loading function */
233
+		if (load_tm) {
234
+			/* let the auto-loading function load all TM stuff */
235
+			if (load_tm( &tmb ) == -1) {
236
+				LOG(L_ERR, "DISPATCHER:mod_init:ERROR -- could not load"
237
+						" the TM-functions\n");
238
+				return -1;
239
+			}
240
+			/*****************************************************
241
+			 * Register the PING-Timer
242
+	    	 *****************************************************/
243
+			register_timer(ds_check_timer, NULL, ds_ping_interval);
244
+		} else {
245
+			LOG(L_WARN, "DISPATCHER:mod_init:WARNING -- could not bind to"
246
+					" the TM-Module, automatic re-activation disabled.\n");
247
+		}
248
+	}
200 249
 
201 250
 	return 0;
202 251
 }
... ...
@@ -312,6 +361,8 @@ static int w_ds_mark_dst1(struct sip_msg *msg, char *str1, char *str2)
312 361
 {
313 362
 	if(str1 && (str1[0]=='i' || str1[0]=='I' || str1[0]=='0'))
314 363
 		return ds_mark_dst(msg, 0);
364
+	else if(str1 && (str1[0]=='p' || str1[0]=='P' || str1[0]=='2'))
365
+		return ds_mark_dst(msg, 2);
315 366
 	else
316 367
 		return ds_mark_dst(msg, 1);
317 368
 }
... ...
@@ -461,3 +512,44 @@ struct mi_root* ds_mi_list(struct mi_root* cmd_tree, void* param)
461 512
 	return rpl_tree;
462 513
 }
463 514
 
515
+
516
+/**
517
+ *
518
+ */
519
+static int w_ds_is_from_list0(struct sip_msg *msg, char *str1, char *str2)
520
+{
521
+	return ds_is_from_list(msg, -1);
522
+}
523
+
524
+
525
+/**
526
+ *
527
+ */
528
+static int w_ds_is_from_list1(struct sip_msg *msg, char *set, char *str2)
529
+{
530
+	return ds_is_from_list(msg, (int)set);
531
+}
532
+
533
+/* 
534
+ * Convert string parameter to integer for functions that expect an integer.
535
+ * Taken from mediaproxy/LCR module.
536
+ */
537
+static int fixstring2int(void **param, int param_count)
538
+{
539
+	unsigned long number;
540
+	int err;
541
+
542
+	if (param_count == 1) {
543
+		number = str2s(*param, strlen(*param), &err);
544
+		if (err == 0) {
545
+			pkg_free(*param);
546
+			*param = (void*)number;
547
+			return 0;
548
+		} else {
549
+			LOG(L_ERR, "dispatcher/fixstring2int(): ERROR: bad number `%s'\n",
550
+				(char*)(*param));
551
+			return E_CFG;
552
+		}
553
+	}
554
+	return 0;
555
+}
... ...
@@ -33,6 +33,14 @@
33 33
 		<email>daniel@voice-system.ro</email>
34 34
 		</address>
35 35
 	    </editor>
36
+            <editor>
37
+                <firstname>Carsten</firstname>
38
+                <surname>Bock</surname>
39
+                <affiliation><orgname>BASIS AudioNet GmbH</orgname></affiliation>
40
+                <address>
41
+                <email>bock@basis-audionet.de</email>
42
+                </address>
43
+            </editor>
36 44
 	</authorgroup>
37 45
 	<copyright>
38 46
 	    <year>2004</year>
... ...
@@ -33,7 +33,7 @@
33 33
 			<itemizedlist>
34 34
 			<listitem>
35 35
 			<para>
36
-				<emphasis>none</emphasis>.
36
+				<emphasis>TM - only if active recovery of failed hosts is required</emphasis>.
37 37
 			</para>
38 38
 			</listitem>
39 39
 			</itemizedlist>
... ...
@@ -230,6 +230,90 @@ modparam("dispatcher", "force_dst", 1)
230 230
  </programlisting>
231 231
  		</example>
232 232
  	</section>
233
+ 	<section>
234
+ 		<title><varname>ds_ping_method</varname> (string)</title>
235
+ 		<para>
236
+ 		With this Method you can define, with which method you want to probe the failed gateways.
237
+ 		This method is only available, if compiled with the probing of failed gateways enabled. 		
238
+ 		</para>
239
+ 		<para>
240
+ 		<emphasis>
241
+ 			Default value is <quote>OPTIONS</quote>.
242
+ 		</emphasis>
243
+ 		</para>
244
+ 		<example>
245
+ 		<title>Set the <quote>ds_ping_method</quote> parameter</title>
246
+ <programlisting format="linespecific">
247
+ ...
248
+ modparam("dispatcher", "ds_ping_method", "INFO")
249
+ ...
250
+ </programlisting>
251
+ 		</example>
252
+	</section> 	
253
+ 	<section>
254
+ 		<title><varname>ds_ping_from</varname> (string)</title>
255
+ 		<para>
256
+ 		With this Method you can define the "From:"-Line for the request, sent to the failed gateways. 		
257
+ 		This method is only available, if compiled with the probing of failed gateways enabled.
258
+ 		</para>
259
+ 		<para>
260
+ 		<emphasis>
261
+ 			Default value is <quote>sip:dispatcher@localhost</quote>.
262
+ 		</emphasis>
263
+ 		</para>
264
+ 		<example>
265
+ 		<title>Set the <quote>ds_ping_from</quote> parameter</title>
266
+ <programlisting format="linespecific">
267
+ ...
268
+ modparam("dispatcher", "ds_ping_from", "sip:proxy@sip.somehost.com")
269
+ ...
270
+ </programlisting>
271
+ 		</example>
272
+	</section> 	
273
+
274
+ 	<section>
275
+ 		<title><varname>ds_ping_interval</varname> (int)</title>
276
+ 		<para>
277
+ 		With this Method you can define the interval for sending a request to a failed gateway.
278
+ 		This parameter is only used, when the TM-Module is loaded.
279
+		If set to <quote>0</quote>, the pinging of failed requests is disabled.
280
+ 		</para>
281
+ 		<para>
282
+ 		<emphasis>
283
+ 			Default value is <quote>10</quote>.
284
+ 		</emphasis>
285
+ 		</para>
286
+ 		<example>
287
+ 		<title>Set the <quote>ds_ping_interval</quote> parameter</title>
288
+ <programlisting format="linespecific">
289
+ ...
290
+ modparam("dispatcher", "ds_ping_interval", 30)
291
+ ...
292
+ </programlisting>
293
+ 		</example>
294
+	</section> 	
295
+	
296
+ 	<section>
297
+ 		<title><varname>ds_probing_threshhold</varname> (int)</title>
298
+ 		<para>
299
+		If you want to set a gateway into probing mode, you will need a specific number of requests until it will change from "active" to probing.
300
+		The number of attempts can be set with this parameter.
301
+ 		</para>
302
+ 		<para>
303
+ 		<emphasis>
304
+ 			Default value is <quote>3</quote>.
305
+ 		</emphasis>
306
+ 		</para>
307
+ 		<example>
308
+ 		<title>Set the <quote>ds_probing_threshhold</quote> parameter</title>
309
+ <programlisting format="linespecific">
310
+ ...
311
+ modparam("dispatcher", "ds_probing_threshhold", 10)
312
+ ...
313
+ </programlisting>
314
+ 		</example>
315
+	</section>
316
+
233 317
 	</section>
234 318
 
235 319
 	<section>
... ...
@@ -280,6 +364,11 @@ modparam("dispatcher", "force_dst", 1)
280 364
 				<quote>4</quote> - round-robin (next destination).
281 365
 				</para>
282 366
 			</listitem>
367
+			<listitem>
368
+				<para>
369
+				<quote>5</quote> - hash over authorization-username (Proxy-Authorization or "normal" authorization). If no username is found, round robin is used.
370
+				</para>
371
+			</listitem>
283 372
 			<listitem>
284 373
 				<para>
285 374
 				<quote>X</quote> - if the algorithm is not implemented, the
... ...
@@ -364,6 +453,53 @@ ds_select_dst("1", "0");
364 453
 		This function can be used from FAILURE_ROUTE.
365 454
 		</para>
366 455
  	</section>
456
+  	<section>
457
+ 		<title>
458
+ 		<function moreinfo="none">ds_mark_dst("s")</function>
459
+ 		</title>
460
+ 		<para>
461
+ 		Mark the last used address from destination set as inactive ("i"/"I"/"0"), active ("a"/"A"/"1") or probing ("p"/"P"/"2").
462
+ 		With this function, an automatic detection of failed gateways can be implemented. When an address is marked as
463
+		inactive or probing, it will be ignored by 'ds_select_dst' and 'ds_select_domain'.
464
+ 		</para>
465
+		<para>possible parameters:</para>
466
+		<itemizedlist>
467
+		<listitem>
468
+			<para><emphasis>"i", "I" or "0"</emphasis> - the last destination should be set to inactive and will be ignored in future requests.</para>
469
+		</listitem>
470
+		<listitem>
471
+			<para><emphasis>"a", "A" or "1"</emphasis> - the last destination should be set to active.</para>
472
+		</listitem>
473
+		<listitem>
474
+			<para><emphasis>"p", "P" or "2"</emphasis> - the last destination will be set to probing. Note: You will need to call this function "threshhold"-times, before it will be actually set to probing.</para>
475
+		</listitem>
476
+		</itemizedlist>
477
+		<para>
478
+		This function can be used from FAILURE_ROUTE.
479
+		</para>
480
+ 	</section>
481
+  	<section>
482
+ 		<title>
483
+ 		<function moreinfo="none">ds_is_from_list()</function>
484
+ 		</title>
485
+ 		<para>
486
+ 		This function returns true, if the current request comes from a host from the dispatcher-list; otherwise false.
487
+ 		</para>
488
+		<para>
489
+		This function can be used from REQUEST_ROUTE, FAILURE_ROUTE, BRANCH_ROUTE and ONREPLY_ROUTE.
490
+		</para>
491
+ 	</section>
492
+  	<section>
493
+ 		<title>
494
+ 		<function moreinfo="none">ds_is_from_list("group")</function>
495
+ 		</title>
496
+ 		<para>
497
+ 		This function returns true, if the current request comes from a host in the given group of the dispatcher-list; otherwise false.
498
+ 		</para>
499
+		<para>
500
+		This function can be used from REQUEST_ROUTE, FAILURE_ROUTE, BRANCH_ROUTE and ONREPLY_ROUTE.
501
+		</para>
502
+ 	</section>
367 503
 	</section>
368 504
 
369 505
 	<section>
... ...
@@ -385,6 +521,7 @@ ds_select_dst("1", "0");
385 521
 			      <itemizedlist>
386 522
 	                 <listitem><para> <quote>a</quote>: active </para></listitem> 
387 523
 			         <listitem><para> <quote>i</quote>: inactive </para></listitem>	
524
+				<listitem><para> <quote>p</quote>: probing </para></listitem>
388 525
 				  </itemizedlist>
389 526
 			</listitem>	  
390 527