|
1 /* |
|
2 * $Id: lcr_mod.c 6015 2009-08-22 21:45:06Z bogdan_iancu $ |
|
3 * |
|
4 * Least Cost Routing module (also implements sequential forking) |
|
5 * |
|
6 * Copyright (C) 2005 Juha Heinanen |
|
7 * Copyright (C) 2006 Voice Sistem SRL |
|
8 * |
|
9 * This file is part of opensips, a free SIP server. |
|
10 * |
|
11 * opensips is free software; you can redistribute it and/or modify |
|
12 * it under the terms of the GNU General Public License as published by |
|
13 * the Free Software Foundation; either version 2 of the License, or |
|
14 * (at your option) any later version |
|
15 * |
|
16 * opensips is distributed in the hope that it will be useful, |
|
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|
19 * GNU General Public License for more details. |
|
20 * |
|
21 * You should have received a copy of the GNU General Public License |
|
22 * along with this program; if not, write to the Free Software |
|
23 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
|
24 * |
|
25 * History: |
|
26 * ------- |
|
27 * 2005-02-14: Introduced lcr module (jh) |
|
28 * 2005-02-20: Added sequential forking functions (jh) |
|
29 * 2005-02-25: Added support for int AVP names, combined addr and port |
|
30 * AVPs (jh) |
|
31 * 2005-07-28: Added support for gw URI scheme and transport, |
|
32 * backport from ser (kd) |
|
33 * 2005-08-20: Added support for gw prefixes (jh) |
|
34 * 2005-09-03: Request-URI user part can be modified between load_gws() |
|
35 * and first next_gw() calls. |
|
36 */ |
|
37 |
|
38 #include <stdio.h> |
|
39 #include <stdlib.h> |
|
40 #include <string.h> |
|
41 #include <sys/types.h> |
|
42 #include <sys/socket.h> |
|
43 #include <netinet/in.h> |
|
44 #include <arpa/inet.h> |
|
45 #include <regex.h> |
|
46 #include "../../sr_module.h" |
|
47 #include "../../dprint.h" |
|
48 #include "../../ut.h" |
|
49 #include "../../error.h" |
|
50 #include "../../mem/mem.h" |
|
51 #include "../../mem/shm_mem.h" |
|
52 #include "../../db/db.h" |
|
53 #include "../../usr_avp.h" |
|
54 #include "../../parser/parse_uri.h" |
|
55 #include "../../parser/parse_from.h" |
|
56 #include "../../parser/msg_parser.h" |
|
57 #include "../../action.h" |
|
58 #include "../../qvalue.h" |
|
59 #include "../../dset.h" |
|
60 #include "../../ip_addr.h" |
|
61 #include "../../resolve.h" |
|
62 #include "../../mi/mi.h" |
|
63 #include "../../mod_fix.h" |
|
64 #include "../../socket_info.h" |
|
65 #include "../../pvar.h" |
|
66 #include "../../mod_fix.h" |
|
67 #include "mi.h" |
|
68 |
|
69 |
|
70 |
|
71 /* |
|
72 * Version of gw and lcr tables required by the module, |
|
73 * increment this value if you change the table in |
|
74 * an backwards incompatible way |
|
75 */ |
|
76 #define GW_TABLE_VERSION 8 |
|
77 #define LCR_TABLE_VERSION 3 |
|
78 |
|
79 /* usr_avp flag for sequential forking */ |
|
80 #define Q_FLAG (1<<2) |
|
81 |
|
82 static void destroy(void); /* Module destroy function */ |
|
83 static int mi_child_init(void); |
|
84 static int mod_init(void); /* Module initialization function */ |
|
85 static int fixstringloadgws(void **param, int param_count); |
|
86 |
|
87 int reload_gws ( void ); |
|
88 |
|
89 #define GW_TABLE "gw" |
|
90 |
|
91 #define GW_NAME_COL "gw_name" |
|
92 |
|
93 #define GRP_ID_COL "grp_id" |
|
94 |
|
95 #define IP_ADDR_COL "ip_addr" |
|
96 |
|
97 #define PORT_COL "port" |
|
98 |
|
99 #define URI_SCHEME_COL "uri_scheme" |
|
100 |
|
101 #define TRANSPORT_COL "transport" |
|
102 |
|
103 #define STRIP_COL "strip" |
|
104 |
|
105 #define TAG_COL "tag" |
|
106 |
|
107 #define FLAGS_COL "flags" |
|
108 |
|
109 #define LCR_TABLE "lcr" |
|
110 |
|
111 #define PREFIX_COL "prefix" |
|
112 |
|
113 #define FROM_URI_COL "from_uri" |
|
114 |
|
115 #define PRIORITY_COL "priority" |
|
116 |
|
117 #define MAX_NO_OF_GWS 32 |
|
118 #define MAX_NO_OF_LCRS 256 |
|
119 #define MAX_PREFIX_LEN 256 |
|
120 #define MAX_TAG_LEN 16 |
|
121 #define MAX_FROM_URI_LEN 256 |
|
122 |
|
123 /* Default module parameter values */ |
|
124 #define DEF_FR_INV_TIMER 90 |
|
125 #define DEF_FR_INV_TIMER_NEXT 30 |
|
126 #define DEF_PREFIX_MODE 0 |
|
127 |
|
128 /* |
|
129 * Type definitions |
|
130 */ |
|
131 |
|
132 typedef enum sip_protos uri_transport; |
|
133 |
|
134 struct gw_info { |
|
135 unsigned int ip_addr; |
|
136 unsigned int port; |
|
137 unsigned int grp_id; |
|
138 uri_type scheme; |
|
139 uri_transport transport; |
|
140 unsigned int strip; |
|
141 char tag[MAX_TAG_LEN + 1]; |
|
142 unsigned short tag_len; |
|
143 unsigned int flags; |
|
144 }; |
|
145 |
|
146 struct lcr_info { |
|
147 char prefix[MAX_PREFIX_LEN + 1]; |
|
148 unsigned short prefix_len; |
|
149 char from_uri[MAX_FROM_URI_LEN + 1]; |
|
150 unsigned short from_uri_len; |
|
151 unsigned int grp_id; |
|
152 unsigned short priority; |
|
153 unsigned short end_record; |
|
154 }; |
|
155 |
|
156 struct prefix_regex { |
|
157 regex_t re; |
|
158 short int valid; |
|
159 }; |
|
160 |
|
161 struct from_uri_regex { |
|
162 regex_t re; |
|
163 short int valid; |
|
164 }; |
|
165 |
|
166 struct mi { |
|
167 int gw_index; |
|
168 int route_index; |
|
169 int randomizer; |
|
170 }; |
|
171 |
|
172 |
|
173 /* |
|
174 * Database variables |
|
175 */ |
|
176 static db_con_t* db_handle = 0; /* Database connection handle */ |
|
177 static db_func_t lcr_dbf; |
|
178 |
|
179 /* |
|
180 * Module parameter variables |
|
181 */ |
|
182 |
|
183 /* database */ |
|
184 static str db_url = str_init(DEFAULT_RODB_URL); |
|
185 static str gw_table = str_init(GW_TABLE); |
|
186 static str gw_name_col = str_init(GW_NAME_COL); |
|
187 static str grp_id_col = str_init(GRP_ID_COL); |
|
188 static str ip_addr_col = str_init(IP_ADDR_COL); |
|
189 static str port_col = str_init(PORT_COL); |
|
190 static str uri_scheme_col = str_init(URI_SCHEME_COL); |
|
191 static str transport_col = str_init(TRANSPORT_COL); |
|
192 static str strip_col = str_init(STRIP_COL); |
|
193 static str tag_col = str_init(TAG_COL); |
|
194 static str flags_col = str_init(FLAGS_COL); |
|
195 static str lcr_table = str_init(LCR_TABLE); |
|
196 static str prefix_col = str_init(PREFIX_COL); |
|
197 static str from_uri_col = str_init(FROM_URI_COL); |
|
198 static str priority_col = str_init(PRIORITY_COL); |
|
199 |
|
200 /* timer */ |
|
201 int fr_inv_timer = DEF_FR_INV_TIMER; |
|
202 int fr_inv_timer_next = DEF_FR_INV_TIMER_NEXT; |
|
203 |
|
204 /* avps */ |
|
205 static char *fr_inv_timer_avp_param = NULL; |
|
206 static char *gw_uri_avp_param = NULL; |
|
207 static char *ruri_user_avp_param = NULL; |
|
208 static char *contact_avp_param = NULL; |
|
209 static char *rpid_avp_param = NULL; |
|
210 static char *flags_avp_param = NULL; |
|
211 |
|
212 /* prefix mode */ |
|
213 int prefix_mode_param = DEF_PREFIX_MODE; |
|
214 |
|
215 /* |
|
216 * Other module types and variables |
|
217 */ |
|
218 |
|
219 struct contact { |
|
220 str uri; |
|
221 qvalue_t q; |
|
222 str dst_uri; |
|
223 str path; |
|
224 unsigned int flags; |
|
225 struct socket_info* sock; |
|
226 unsigned short q_flag; |
|
227 struct contact *next; |
|
228 }; |
|
229 |
|
230 static int fr_inv_timer_avp_type; |
|
231 static int_str fr_inv_timer_avp; |
|
232 static int gw_uri_avp_type; |
|
233 static int_str gw_uri_avp; |
|
234 static int ruri_user_avp_type; |
|
235 static int_str ruri_user_avp; |
|
236 static int contact_avp_type; |
|
237 static int_str contact_avp; |
|
238 static int rpid_avp_type; |
|
239 static int_str rpid_avp; |
|
240 static int flags_avp_type; |
|
241 static int_str flags_avp; |
|
242 |
|
243 struct gw_info **gws; /* Pointer to current gw table pointer */ |
|
244 struct gw_info *gws_1; /* Pointer to gw table 1 */ |
|
245 struct gw_info *gws_2; /* Pointer to gw table 2 */ |
|
246 |
|
247 struct lcr_info **lcrs; /* Pointer to current lcr table pointer */ |
|
248 struct lcr_info *lcrs_1; /* Pointer to lcr table 1 */ |
|
249 struct lcr_info *lcrs_2; /* Pointer to lcr table 2 */ |
|
250 |
|
251 unsigned int *lcrs_ws_reload_counter; |
|
252 unsigned int reload_counter; |
|
253 |
|
254 struct prefix_regex prefix_reg[MAX_NO_OF_LCRS]; |
|
255 struct from_uri_regex from_uri_reg[MAX_NO_OF_LCRS]; |
|
256 |
|
257 /* |
|
258 * Module functions that are defined later |
|
259 */ |
|
260 static int load_gws_0(struct sip_msg* _m, char* _s1, char* _s2); |
|
261 static int load_gws_1(struct sip_msg* _m, char* _s1, char* _s2); |
|
262 static int load_gws_from_grp(struct sip_msg* _m, char* _s1, char* _s2); |
|
263 static int next_gw(struct sip_msg* _m, char* _s1, char* _s2); |
|
264 static int from_gw_0(struct sip_msg* _m, char* _s1, char* _s2); |
|
265 static int from_gw_1(struct sip_msg* _m, char* _s1, char* _s2); |
|
266 static int from_gw_grp(struct sip_msg* _m, char* _s1, char* _s2); |
|
267 static int to_gw(struct sip_msg* _m, char* _s1, char* _s2); |
|
268 static int to_gw_grp(struct sip_msg* _m, char* _s1, char* _s2); |
|
269 static int load_contacts (struct sip_msg*, char*, char*); |
|
270 static int next_contacts (struct sip_msg*, char*, char*); |
|
271 |
|
272 |
|
273 /* |
|
274 * Exported functions |
|
275 */ |
|
276 static cmd_export_t cmds[] = { |
|
277 {"load_gws", (cmd_function)load_gws_0, 0, 0, 0, REQUEST_ROUTE}, |
|
278 {"load_gws", (cmd_function)load_gws_1, 1, fixup_pvar_null, |
|
279 fixup_free_pvar_null, REQUEST_ROUTE}, |
|
280 {"load_gws_from_grp", (cmd_function)load_gws_from_grp, 1, |
|
281 fixstringloadgws, 0, REQUEST_ROUTE}, |
|
282 {"next_gw", (cmd_function)next_gw, 0, 0, 0, |
|
283 REQUEST_ROUTE | FAILURE_ROUTE}, |
|
284 {"from_gw", (cmd_function)from_gw_0, 0, 0, 0, |
|
285 REQUEST_ROUTE | FAILURE_ROUTE | ONREPLY_ROUTE}, |
|
286 {"from_gw", (cmd_function)from_gw_1, 1, fixup_pvar_null, |
|
287 fixup_free_pvar_null, REQUEST_ROUTE | FAILURE_ROUTE | ONREPLY_ROUTE}, |
|
288 {"from_gw_grp", (cmd_function)from_gw_grp, 1, fixup_uint_null, 0, |
|
289 REQUEST_ROUTE | FAILURE_ROUTE | ONREPLY_ROUTE}, |
|
290 {"to_gw", (cmd_function)to_gw, 0, 0, 0, |
|
291 REQUEST_ROUTE | FAILURE_ROUTE}, |
|
292 {"to_gw", (cmd_function)to_gw_grp, 1, fixup_uint_null, 0, |
|
293 REQUEST_ROUTE | FAILURE_ROUTE}, |
|
294 {"load_contacts", (cmd_function)load_contacts, 0, 0, 0, |
|
295 REQUEST_ROUTE}, |
|
296 {"next_contacts", (cmd_function)next_contacts, 0, 0, 0, |
|
297 REQUEST_ROUTE | FAILURE_ROUTE}, |
|
298 {0, 0, 0, 0, 0, 0} |
|
299 }; |
|
300 |
|
301 |
|
302 /* |
|
303 * Exported parameters |
|
304 */ |
|
305 static param_export_t params[] = { |
|
306 {"db_url", STR_PARAM, &db_url.s }, |
|
307 {"gw_table", STR_PARAM, &gw_table.s }, |
|
308 {"gw_name_column", STR_PARAM, &gw_name_col.s }, |
|
309 {"grp_id_column", STR_PARAM, &grp_id_col.s }, |
|
310 {"ip_addr_column", STR_PARAM, &ip_addr_col.s }, |
|
311 {"port_column", STR_PARAM, &port_col.s }, |
|
312 {"uri_scheme_column", STR_PARAM, &uri_scheme_col.s }, |
|
313 {"transport_column", STR_PARAM, &transport_col.s }, |
|
314 {"strip_column", STR_PARAM, &strip_col.s }, |
|
315 {"tag_column", STR_PARAM, &tag_col.s }, |
|
316 {"flags_column", STR_PARAM, &flags_col.s }, |
|
317 {"lcr_table", STR_PARAM, &lcr_table.s }, |
|
318 {"prefix_column", STR_PARAM, &prefix_col.s }, |
|
319 {"from_uri_column", STR_PARAM, &from_uri_col.s }, |
|
320 {"priority_column", STR_PARAM, &priority_col.s }, |
|
321 {"fr_inv_timer_avp", STR_PARAM, &fr_inv_timer_avp_param }, |
|
322 {"gw_uri_avp", STR_PARAM, &gw_uri_avp_param }, |
|
323 {"ruri_user_avp", STR_PARAM, &ruri_user_avp_param }, |
|
324 {"contact_avp", STR_PARAM, &contact_avp_param }, |
|
325 {"rpid_avp", STR_PARAM, &rpid_avp_param }, |
|
326 {"flags_avp", STR_PARAM, &flags_avp_param }, |
|
327 {"fr_inv_timer", INT_PARAM, &fr_inv_timer }, |
|
328 {"fr_inv_timer_next", INT_PARAM, &fr_inv_timer_next }, |
|
329 {"prefix_mode", INT_PARAM, &prefix_mode_param }, |
|
330 {0, 0, 0} |
|
331 }; |
|
332 |
|
333 |
|
334 /* |
|
335 * Exported MI functions |
|
336 */ |
|
337 static mi_export_t mi_cmds[] = { |
|
338 { MI_LCR_RELOAD, mi_lcr_reload, MI_NO_INPUT_FLAG, 0, mi_child_init }, |
|
339 { MI_LCR_DUMP, mi_lcr_dump, MI_NO_INPUT_FLAG, 0, 0 }, |
|
340 { 0, 0, 0, 0 ,0} |
|
341 }; |
|
342 |
|
343 |
|
344 /* |
|
345 * Module interface |
|
346 */ |
|
347 struct module_exports exports = { |
|
348 "lcr", |
|
349 MODULE_VERSION, |
|
350 DEFAULT_DLFLAGS, /* dlopen flags */ |
|
351 cmds, /* Exported functions */ |
|
352 params, /* Exported parameters */ |
|
353 0, /* exported statistics */ |
|
354 mi_cmds, /* exported MI functions */ |
|
355 0, /* exported pseudo-variables */ |
|
356 0, /* extra processes */ |
|
357 mod_init, /* module initialization function */ |
|
358 0, /* response function */ |
|
359 destroy, /* destroy function */ |
|
360 0 /* child initialization function */ |
|
361 }; |
|
362 |
|
363 |
|
364 static int lcr_db_init(const str* db_url) |
|
365 { |
|
366 if (lcr_dbf.init==0){ |
|
367 LM_CRIT("Null lcr_dbf\n"); |
|
368 goto error; |
|
369 } |
|
370 db_handle=lcr_dbf.init(db_url); |
|
371 if (db_handle==0){ |
|
372 LM_ERR("Unable to connect to the database\n"); |
|
373 goto error; |
|
374 } |
|
375 return 0; |
|
376 error: |
|
377 return -1; |
|
378 } |
|
379 |
|
380 |
|
381 |
|
382 static int lcr_db_bind(const str* db_url) |
|
383 { |
|
384 if (db_bind_mod(db_url, &lcr_dbf)<0){ |
|
385 LM_ERR("Unable to bind to the database module\n"); |
|
386 return -1; |
|
387 } |
|
388 |
|
389 if (!DB_CAPABILITY(lcr_dbf, DB_CAP_QUERY)) { |
|
390 LM_ERR("Database module does not implement 'query' function\n"); |
|
391 return -1; |
|
392 } |
|
393 |
|
394 return 0; |
|
395 } |
|
396 |
|
397 |
|
398 static void lcr_db_close(void) |
|
399 { |
|
400 if (db_handle && lcr_dbf.close){ |
|
401 lcr_dbf.close(db_handle); |
|
402 db_handle=0; |
|
403 } |
|
404 } |
|
405 |
|
406 |
|
407 static int mi_child_init(void) |
|
408 { |
|
409 return lcr_db_init(&db_url); |
|
410 } |
|
411 |
|
412 |
|
413 /* |
|
414 * Module initialization function that is called before the main process forks |
|
415 */ |
|
416 static int mod_init(void) |
|
417 { |
|
418 int i; |
|
419 pv_spec_t avp_spec; |
|
420 str s; |
|
421 unsigned short avp_flags; |
|
422 |
|
423 LM_DBG("Initializing\n"); |
|
424 |
|
425 /* Update length of module variables */ |
|
426 db_url.len = strlen(db_url.s); |
|
427 gw_table.len = strlen(gw_table.s); |
|
428 gw_name_col.len = strlen(gw_name_col.s); |
|
429 grp_id_col.len = strlen(grp_id_col.s); |
|
430 ip_addr_col.len = strlen(ip_addr_col.s); |
|
431 port_col.len = strlen(port_col.s); |
|
432 uri_scheme_col.len = strlen(uri_scheme_col.s); |
|
433 transport_col.len = strlen(transport_col.s); |
|
434 strip_col.len = strlen(strip_col.s); |
|
435 tag_col.len = strlen(tag_col.s); |
|
436 flags_col.len = strlen(flags_col.s); |
|
437 lcr_table.len = strlen(lcr_table.s); |
|
438 prefix_col.len = strlen(prefix_col.s); |
|
439 from_uri_col.len = strlen(from_uri_col.s); |
|
440 priority_col.len = strlen(priority_col.s); |
|
441 |
|
442 /* Bind database */ |
|
443 if (lcr_db_bind(&db_url)) { |
|
444 LM_ERR("No database module found\n"); |
|
445 return -1; |
|
446 } |
|
447 |
|
448 /* Check value of prefix_mode */ |
|
449 if ((prefix_mode_param != 0) && (prefix_mode_param != 1)) { |
|
450 LM_ERR("Invalid prefix_mode value <%d>\n", prefix_mode_param); |
|
451 return -1; |
|
452 } |
|
453 |
|
454 /* Process AVP params */ |
|
455 if (fr_inv_timer_avp_param && *fr_inv_timer_avp_param) { |
|
456 s.s = fr_inv_timer_avp_param; s.len = strlen(s.s); |
|
457 if (pv_parse_spec(&s, &avp_spec)==0 |
|
458 || avp_spec.type!=PVT_AVP) { |
|
459 LM_ERR("Malformed or non AVP definition <%s>\n", |
|
460 fr_inv_timer_avp_param); |
|
461 return -1; |
|
462 } |
|
463 |
|
464 if(pv_get_avp_name(0, &(avp_spec.pvp), &fr_inv_timer_avp, &avp_flags)!=0) { |
|
465 LM_ERR("Invalid AVP definition <%s>\n", fr_inv_timer_avp_param); |
|
466 return -1; |
|
467 } |
|
468 fr_inv_timer_avp_type = avp_flags; |
|
469 } else { |
|
470 LM_ERR("AVP fr_inv_timer_avp has not been defined\n"); |
|
471 return -1; |
|
472 } |
|
473 |
|
474 if (gw_uri_avp_param && *gw_uri_avp_param) { |
|
475 s.s = gw_uri_avp_param; s.len = strlen(s.s); |
|
476 if (pv_parse_spec(&s, &avp_spec)==0 |
|
477 || avp_spec.type!=PVT_AVP) { |
|
478 LM_ERR("Malformed or non AVP definition <%s>\n", gw_uri_avp_param); |
|
479 return -1; |
|
480 } |
|
481 |
|
482 if(pv_get_avp_name(0, &(avp_spec.pvp), &gw_uri_avp, &avp_flags)!=0) { |
|
483 LM_ERR("Invalid AVP definition <%s>\n", gw_uri_avp_param); |
|
484 return -1; |
|
485 } |
|
486 gw_uri_avp_type = avp_flags; |
|
487 } else { |
|
488 LM_ERR("AVP gw_uri_avp has not been defined\n"); |
|
489 return -1; |
|
490 } |
|
491 |
|
492 if (ruri_user_avp_param && *ruri_user_avp_param) { |
|
493 s.s = ruri_user_avp_param; s.len = strlen(s.s); |
|
494 if (pv_parse_spec(&s, &avp_spec)==0 |
|
495 || avp_spec.type!=PVT_AVP) { |
|
496 LM_ERR("Malformed or non AVP definition <%s>\n", |
|
497 ruri_user_avp_param); |
|
498 return -1; |
|
499 } |
|
500 |
|
501 if(pv_get_avp_name(0, &(avp_spec.pvp), &ruri_user_avp, &avp_flags)!=0) { |
|
502 LM_ERR("Invalid AVP definition <%s>\n", ruri_user_avp_param); |
|
503 return -1; |
|
504 } |
|
505 ruri_user_avp_type = avp_flags; |
|
506 } else { |
|
507 LM_ERR("AVP ruri_user_avp has not been defined\n"); |
|
508 return -1; |
|
509 } |
|
510 |
|
511 if (contact_avp_param && *contact_avp_param) { |
|
512 s.s = contact_avp_param; s.len = strlen(s.s); |
|
513 if (pv_parse_spec(&s, &avp_spec)==0 |
|
514 || avp_spec.type!=PVT_AVP) { |
|
515 LM_ERR("Malformed or non AVP definition <%s>\n", |
|
516 contact_avp_param); |
|
517 return -1; |
|
518 } |
|
519 |
|
520 if(pv_get_avp_name(0, &(avp_spec.pvp), &contact_avp, &avp_flags)!=0) { |
|
521 LM_ERR("Invalid AVP definition <%s>\n", contact_avp_param); |
|
522 return -1; |
|
523 } |
|
524 contact_avp_type = avp_flags; |
|
525 } else { |
|
526 LM_ERR("AVP contact_avp has not been defined\n"); |
|
527 return -1; |
|
528 } |
|
529 |
|
530 if (rpid_avp_param && *rpid_avp_param) { |
|
531 s.s = rpid_avp_param; s.len = strlen(s.s); |
|
532 if (pv_parse_spec(&s, &avp_spec)==0 |
|
533 || avp_spec.type!=PVT_AVP) { |
|
534 LM_ERR("Malformed or non AVP definition <%s>\n", rpid_avp_param); |
|
535 return -1; |
|
536 } |
|
537 |
|
538 if(pv_get_avp_name(0, &(avp_spec.pvp), &rpid_avp, &avp_flags)!=0) { |
|
539 LM_ERR("Invalid AVP definition <%s>\n", rpid_avp_param); |
|
540 return -1; |
|
541 } |
|
542 rpid_avp_type = avp_flags; |
|
543 } else { |
|
544 LM_ERR("AVP rpid_avp has not been defined\n"); |
|
545 return -1; |
|
546 } |
|
547 |
|
548 if (flags_avp_param && *flags_avp_param) { |
|
549 s.s = flags_avp_param; s.len = strlen(s.s); |
|
550 if (pv_parse_spec(&s, &avp_spec)==0 |
|
551 || avp_spec.type!=PVT_AVP) { |
|
552 LM_ERR("Malformed or non AVP definition <%s>\n", flags_avp_param); |
|
553 return -1; |
|
554 } |
|
555 |
|
556 if(pv_get_avp_name(0, &(avp_spec.pvp), &flags_avp, &avp_flags)!=0) { |
|
557 LM_ERR("Invalid AVP definition <%s>\n", flags_avp_param); |
|
558 return -1; |
|
559 } |
|
560 flags_avp_type = avp_flags; |
|
561 } else { |
|
562 LM_ERR("AVP flags_avp has not been defined\n"); |
|
563 return -1; |
|
564 } |
|
565 |
|
566 /* Check table version */ |
|
567 db_con_t* dbh; |
|
568 if (lcr_dbf.init==0){ |
|
569 LM_CRIT("Unbound database\n"); |
|
570 return -1; |
|
571 } |
|
572 dbh=lcr_dbf.init(&db_url); |
|
573 if (dbh==0){ |
|
574 LM_ERR("Unable to open database connection\n"); |
|
575 return -1; |
|
576 } |
|
577 if((db_check_table_version(&lcr_dbf, dbh, &gw_table, GW_TABLE_VERSION) < 0) || |
|
578 (db_check_table_version(&lcr_dbf, dbh, &lcr_table, LCR_TABLE_VERSION) < 0)) { |
|
579 LM_ERR("error during table version check.\n"); |
|
580 lcr_dbf.close(dbh); |
|
581 goto err; |
|
582 } |
|
583 lcr_dbf.close(dbh); |
|
584 |
|
585 /* Initializing gw tables and gw table pointer variable */ |
|
586 gws_1 = (struct gw_info *)shm_malloc(sizeof(struct gw_info) * |
|
587 (MAX_NO_OF_GWS + 1)); |
|
588 if (gws_1 == 0) { |
|
589 LM_ERR("No memory for gw table\n"); |
|
590 goto err; |
|
591 } |
|
592 gws_2 = (struct gw_info *)shm_malloc(sizeof(struct gw_info) * |
|
593 (MAX_NO_OF_GWS + 1)); |
|
594 if (gws_2 == 0) { |
|
595 LM_ERR("No memory for gw table\n"); |
|
596 goto err; |
|
597 } |
|
598 for (i = 0; i < MAX_NO_OF_GWS + 1; i++) { |
|
599 gws_1[i].ip_addr = gws_2[i].ip_addr = 0; |
|
600 } |
|
601 gws = (struct gw_info **)shm_malloc(sizeof(struct gw_info *)); |
|
602 if (gws == 0) { |
|
603 LM_ERR("No memory for gw table pointer\n"); |
|
604 } |
|
605 *gws = gws_1; |
|
606 |
|
607 /* Initializing lcr tables and lcr table pointer variable */ |
|
608 lcrs_1 = (struct lcr_info *)shm_malloc(sizeof(struct lcr_info) * |
|
609 (MAX_NO_OF_LCRS + 1)); |
|
610 if (lcrs_1 == 0) { |
|
611 LM_ERR("No memory for lcr table\n"); |
|
612 goto err; |
|
613 } |
|
614 lcrs_2 = (struct lcr_info *)shm_malloc(sizeof(struct lcr_info) * |
|
615 (MAX_NO_OF_LCRS + 1)); |
|
616 if (lcrs_2 == 0) { |
|
617 LM_ERR("No memory for lcr table\n"); |
|
618 goto err; |
|
619 } |
|
620 for (i = 0; i < MAX_NO_OF_LCRS + 1; i++) { |
|
621 lcrs_1[i].end_record = lcrs_2[i].end_record = 0; |
|
622 } |
|
623 lcrs = (struct lcr_info **)shm_malloc(sizeof(struct lcr_info *)); |
|
624 if (lcrs == 0) { |
|
625 LM_ERR("No memory for lcr table pointer\n"); |
|
626 goto err; |
|
627 } |
|
628 *lcrs = lcrs_1; |
|
629 |
|
630 lcrs_ws_reload_counter = (unsigned int *)shm_malloc(sizeof(unsigned int)); |
|
631 if (lcrs_ws_reload_counter == 0) { |
|
632 LM_ERR("No memory for reload counter\n"); |
|
633 goto err; |
|
634 } |
|
635 *lcrs_ws_reload_counter = reload_counter = 0; |
|
636 |
|
637 memset(prefix_reg, 0, sizeof(struct prefix_regex) * MAX_NO_OF_LCRS); |
|
638 memset(from_uri_reg, 0, sizeof(struct from_uri_regex) * MAX_NO_OF_LCRS); |
|
639 |
|
640 /* First reload */ |
|
641 if (reload_gws() == -1) { |
|
642 LM_CRIT("Failed to reload gateways and routes\n"); |
|
643 goto err; |
|
644 } |
|
645 |
|
646 return 0; |
|
647 |
|
648 err: |
|
649 return -1; |
|
650 } |
|
651 |
|
652 |
|
653 static void destroy(void) |
|
654 { |
|
655 lcr_db_close(); |
|
656 } |
|
657 |
|
658 /* |
|
659 * Sort lcr records by prefix_len and priority. |
|
660 */ |
|
661 static int comp_lcrs(const void *m1, const void *m2) |
|
662 { |
|
663 int result = -1; |
|
664 |
|
665 struct mi *mi1 = (struct mi *) m1; |
|
666 struct mi *mi2 = (struct mi *) m2; |
|
667 |
|
668 struct lcr_info lcr_record1 = (*lcrs)[mi1->route_index]; |
|
669 struct lcr_info lcr_record2 = (*lcrs)[mi2->route_index]; |
|
670 |
|
671 if (prefix_mode_param == 0) { |
|
672 /* Sort by prefix. */ |
|
673 if (lcr_record1.prefix_len > lcr_record2.prefix_len) { |
|
674 result = 1; |
|
675 } else if (lcr_record1.prefix_len == lcr_record2.prefix_len) { |
|
676 /* Sort by priority. */ |
|
677 if (lcr_record1.priority < lcr_record2.priority) { |
|
678 result = 1; |
|
679 } else if (lcr_record1.priority == lcr_record2.priority) { |
|
680 /* Nothing to do. */ |
|
681 result = 0; |
|
682 } |
|
683 } |
|
684 } else { |
|
685 if (lcr_record1.priority < lcr_record2.priority) { |
|
686 result = 1; |
|
687 } else if (lcr_record1.priority == lcr_record2.priority) { |
|
688 /* Nothing to do. */ |
|
689 result = 0; |
|
690 } |
|
691 } |
|
692 |
|
693 return result; |
|
694 } |
|
695 |
|
696 /* |
|
697 * Sort lcr records by rand table. |
|
698 */ |
|
699 static int rand_lcrs(const void *m1, const void *m2) |
|
700 { |
|
701 int result = -1; |
|
702 |
|
703 struct mi mi1 = *((struct mi *) m1); |
|
704 struct mi mi2 = *((struct mi *) m2); |
|
705 |
|
706 if (mi1.randomizer > mi2.randomizer) { |
|
707 result = 1; |
|
708 } else if (mi1.randomizer == mi2.randomizer) { |
|
709 result = 0; |
|
710 } |
|
711 |
|
712 return result; |
|
713 } |
|
714 |
|
715 /* |
|
716 * regcomp each prefix. |
|
717 */ |
|
718 static int load_prefix_regex(void) |
|
719 { |
|
720 int i, status, result = 0; |
|
721 |
|
722 for (i = 0; i < MAX_NO_OF_LCRS; i++) { |
|
723 if ((*lcrs)[i].end_record != 0) { |
|
724 break; |
|
725 } |
|
726 if (prefix_reg[i].valid) { |
|
727 regfree(&(prefix_reg[i].re)); |
|
728 prefix_reg[i].valid = 0; |
|
729 } |
|
730 memset(&(prefix_reg[i].re), 0, sizeof(regex_t)); |
|
731 if ((status=regcomp(&(prefix_reg[i].re),(*lcrs)[i].prefix,0))!=0){ |
|
732 LM_ERR("bad prefix re <%s>, regcomp returned %d (check regex.h)\n", |
|
733 (*lcrs)[i].prefix, status); |
|
734 result = -1; |
|
735 break; |
|
736 } |
|
737 prefix_reg[i].valid = 1; |
|
738 } |
|
739 |
|
740 return result; |
|
741 } |
|
742 |
|
743 /* |
|
744 * regcomp each from_uri. |
|
745 */ |
|
746 static int load_from_uri_regex(void) |
|
747 { |
|
748 int i, status, result = 0; |
|
749 |
|
750 for (i = 0; i < MAX_NO_OF_LCRS; i++) { |
|
751 if ((*lcrs)[i].end_record != 0) { |
|
752 break; |
|
753 } |
|
754 if (from_uri_reg[i].valid) { |
|
755 regfree(&(from_uri_reg[i].re)); |
|
756 from_uri_reg[i].valid = 0; |
|
757 } |
|
758 memset(&(from_uri_reg[i].re), 0, sizeof(regex_t)); |
|
759 if ((status=regcomp(&(from_uri_reg[i].re),(*lcrs)[i].from_uri,0))!=0){ |
|
760 LM_ERR("Bad from_uri re <%s>, regcomp returned %d (check regex.h)\n", |
|
761 (*lcrs)[i].from_uri, status); |
|
762 result = -1; |
|
763 break; |
|
764 } |
|
765 from_uri_reg[i].valid = 1; |
|
766 } |
|
767 |
|
768 if (result != -1) { |
|
769 reload_counter = *lcrs_ws_reload_counter; |
|
770 } |
|
771 return result; |
|
772 } |
|
773 |
|
774 static int load_all_regex(void) |
|
775 { |
|
776 int result =0; |
|
777 |
|
778 if (prefix_mode_param != 0) { |
|
779 result = load_prefix_regex(); |
|
780 } |
|
781 |
|
782 if (result == 0) { |
|
783 result = load_from_uri_regex(); |
|
784 } else { |
|
785 LM_ERR("Unable to load prefix regex\n"); |
|
786 } |
|
787 |
|
788 if (result == 0) { |
|
789 reload_counter = *lcrs_ws_reload_counter; |
|
790 } else { |
|
791 LM_ERR("Unable to load from_uri regex\n"); |
|
792 } |
|
793 |
|
794 return result; |
|
795 } |
|
796 |
|
797 /* |
|
798 * Reload gws to unused gw table and lcrs to unused lcr table, and, when done |
|
799 * make unused gw and lcr table the one in use. |
|
800 */ |
|
801 int reload_gws(void) |
|
802 { |
|
803 unsigned int i, port, strip, tag_len, prefix_len, from_uri_len, |
|
804 grp_id, priority; |
|
805 struct in_addr ip_addr; |
|
806 unsigned int flags; |
|
807 uri_type scheme; |
|
808 uri_transport transport; |
|
809 db_con_t* dbh; |
|
810 char *tag, *prefix, *from_uri; |
|
811 db_res_t* res = NULL; |
|
812 db_row_t* row; |
|
813 db_key_t gw_cols[8]; |
|
814 db_key_t lcr_cols[4]; |
|
815 |
|
816 gw_cols[0] = &ip_addr_col; |
|
817 gw_cols[1] = &port_col; |
|
818 gw_cols[2] = &uri_scheme_col; |
|
819 gw_cols[3] = &transport_col; |
|
820 gw_cols[4] = &strip_col; |
|
821 gw_cols[5] = &tag_col; |
|
822 /* FIXME: is this ok if we have different names for grp_id |
|
823 in the two tables? (ge vw lcr) */ |
|
824 gw_cols[6] = &grp_id_col; |
|
825 gw_cols[7] = &flags_col; |
|
826 |
|
827 lcr_cols[0] = &prefix_col; |
|
828 lcr_cols[1] = &from_uri_col; |
|
829 /* FIXME: is this ok if we have different names for grp_id |
|
830 in the two tables? (ge vw lcr) */ |
|
831 lcr_cols[2] = &grp_id_col; |
|
832 lcr_cols[3] = &priority_col; |
|
833 |
|
834 if (lcr_dbf.init==0){ |
|
835 LM_CRIT("Unbound database\n"); |
|
836 return -1; |
|
837 } |
|
838 dbh=lcr_dbf.init(&db_url); |
|
839 if (dbh==0){ |
|
840 LM_ERR("Unable to open database connection\n"); |
|
841 return -1; |
|
842 } |
|
843 |
|
844 if (lcr_dbf.use_table(dbh, &gw_table) < 0) { |
|
845 LM_ERR("Error while trying to use gw table\n"); |
|
846 return -1; |
|
847 } |
|
848 |
|
849 if (lcr_dbf.query(dbh, NULL, 0, NULL, gw_cols, 0, 8, 0, &res) < 0) { |
|
850 LM_ERR("Failed to query gw data\n"); |
|
851 lcr_dbf.close(dbh); |
|
852 return -1; |
|
853 } |
|
854 |
|
855 if (RES_ROW_N(res) + 1 > MAX_NO_OF_GWS) { |
|
856 LM_ERR("Too many gateways\n"); |
|
857 lcr_dbf.free_result(dbh, res); |
|
858 lcr_dbf.close(dbh); |
|
859 return -1; |
|
860 } |
|
861 |
|
862 for (i = 0; i < RES_ROW_N(res); i++) { |
|
863 row = RES_ROWS(res) + i; |
|
864 if (!((VAL_TYPE(ROW_VALUES(row)) == DB_STRING) && |
|
865 !VAL_NULL(ROW_VALUES(row)) && |
|
866 inet_aton((char *)VAL_STRING(ROW_VALUES(row)), &ip_addr) != 0)) { |
|
867 LM_ERR("Invalid IP address of gw <%s>\n", |
|
868 (char *)VAL_STRING(ROW_VALUES(row))); |
|
869 lcr_dbf.free_result(dbh, res); |
|
870 lcr_dbf.close(dbh); |
|
871 return -1; |
|
872 } |
|
873 if (VAL_NULL(ROW_VALUES(row) + 1) == 1) { |
|
874 port = 0; |
|
875 } else { |
|
876 port = (unsigned int)VAL_INT(ROW_VALUES(row) + 1); |
|
877 } |
|
878 if (port > 65536) { |
|
879 LM_ERR("Port of gw is too large <%u>\n", port); |
|
880 lcr_dbf.free_result(dbh, res); |
|
881 lcr_dbf.close(dbh); |
|
882 return -1; |
|
883 } |
|
884 if (VAL_NULL(ROW_VALUES(row) + 2) == 1) { |
|
885 scheme = SIP_URI_T; |
|
886 } else { |
|
887 scheme = (uri_type)VAL_INT(ROW_VALUES(row) + 2); |
|
888 if ((scheme != SIP_URI_T) && (scheme != SIPS_URI_T)) { |
|
889 LM_ERR("Unknown or unsupported URI scheme <%u>\n", |
|
890 (unsigned int)scheme); |
|
891 lcr_dbf.free_result(dbh, res); |
|
892 lcr_dbf.close(dbh); |
|
893 return -1; |
|
894 } |
|
895 } |
|
896 if (VAL_NULL(ROW_VALUES(row) + 3) == 1) { |
|
897 transport = PROTO_NONE; |
|
898 } else { |
|
899 transport = (uri_transport)VAL_INT(ROW_VALUES(row) + 3); |
|
900 if ((transport != PROTO_UDP) && (transport != PROTO_TCP) && |
|
901 (transport != PROTO_TLS)) { |
|
902 LM_ERR("Unknown or unsupported transport <%u>\n", |
|
903 (unsigned int)transport); |
|
904 lcr_dbf.free_result(dbh, res); |
|
905 lcr_dbf.close(dbh); |
|
906 return -1; |
|
907 } |
|
908 } |
|
909 if (VAL_NULL(ROW_VALUES(row) + 4) == 1) { |
|
910 strip = 0; |
|
911 } else { |
|
912 strip = (unsigned int)VAL_INT(ROW_VALUES(row) + 4); |
|
913 } |
|
914 if (VAL_NULL(ROW_VALUES(row) + 5) == 1) { |
|
915 tag_len = 0; |
|
916 tag = (char *)0; |
|
917 } else { |
|
918 tag = (char *)VAL_STRING(ROW_VALUES(row) + 5); |
|
919 tag_len = strlen(tag); |
|
920 if (tag_len > MAX_TAG_LEN) { |
|
921 LM_ERR("Too long gw tag <%u>\n", tag_len); |
|
922 lcr_dbf.free_result(dbh, res); |
|
923 lcr_dbf.close(dbh); |
|
924 return -1; |
|
925 } |
|
926 } |
|
927 if (VAL_NULL(ROW_VALUES(row) + 6) == 1) { |
|
928 grp_id = 0; |
|
929 } else { |
|
930 grp_id = VAL_INT(ROW_VALUES(row) + 6); |
|
931 } |
|
932 if (!VAL_NULL(ROW_VALUES(row) + 7) && |
|
933 (VAL_TYPE(ROW_VALUES(row) + 7) == DB_INT)) { |
|
934 flags = (unsigned int)VAL_INT(ROW_VALUES(row) + 7); |
|
935 } else { |
|
936 LM_ERR("Attribute flags is NULL or non-int\n"); |
|
937 lcr_dbf.free_result(dbh, res); |
|
938 lcr_dbf.close(dbh); |
|
939 return -1; |
|
940 } |
|
941 if (*gws == gws_1) { |
|
942 gws_2[i].ip_addr = (unsigned int)ip_addr.s_addr; |
|
943 gws_2[i].port = port; |
|
944 gws_2[i].grp_id = grp_id; |
|
945 gws_2[i].scheme = scheme; |
|
946 gws_2[i].transport = transport; |
|
947 gws_2[i].flags = flags; |
|
948 gws_2[i].strip = strip; |
|
949 gws_2[i].tag_len = tag_len; |
|
950 if (tag_len) |
|
951 memcpy(&(gws_2[i].tag[0]), tag, tag_len); |
|
952 } else { |
|
953 gws_1[i].ip_addr = (unsigned int)ip_addr.s_addr; |
|
954 gws_1[i].port = port; |
|
955 gws_1[i].grp_id = grp_id; |
|
956 gws_1[i].scheme = scheme; |
|
957 gws_1[i].transport = transport; |
|
958 gws_1[i].flags = flags; |
|
959 gws_1[i].strip = strip; |
|
960 gws_1[i].tag_len = tag_len; |
|
961 if (tag_len) |
|
962 memcpy(&(gws_1[i].tag[0]), tag, tag_len); |
|
963 } |
|
964 } |
|
965 |
|
966 lcr_dbf.free_result(dbh, res); |
|
967 |
|
968 if (*gws == gws_1) { |
|
969 gws_2[i].ip_addr = 0; |
|
970 *gws = gws_2; |
|
971 } else { |
|
972 gws_1[i].ip_addr = 0; |
|
973 *gws = gws_1; |
|
974 } |
|
975 |
|
976 |
|
977 if (lcr_dbf.use_table(dbh, &lcr_table) < 0) { |
|
978 LM_ERR("Error while trying to use lcr table\n"); |
|
979 return -1; |
|
980 } |
|
981 |
|
982 if (lcr_dbf.query(dbh, NULL, 0, NULL, lcr_cols, 0, 4, 0, &res) < 0) { |
|
983 LM_ERR("Failed to query lcr data\n"); |
|
984 lcr_dbf.close(dbh); |
|
985 return -1; |
|
986 } |
|
987 |
|
988 if (RES_ROW_N(res) + 1 > MAX_NO_OF_LCRS) { |
|
989 LM_ERR("Too many lcr entries <%d>\n", RES_ROW_N(res)); |
|
990 lcr_dbf.free_result(dbh, res); |
|
991 lcr_dbf.close(dbh); |
|
992 return -1; |
|
993 } |
|
994 for (i = 0; i < RES_ROW_N(res); i++) { |
|
995 row = RES_ROWS(res) + i; |
|
996 if (VAL_NULL(ROW_VALUES(row)) == 1) { |
|
997 prefix_len = 0; |
|
998 prefix = 0; |
|
999 } else { |
|
1000 prefix = (char *)VAL_STRING(ROW_VALUES(row)); |
|
1001 prefix_len = strlen(prefix); |
|
1002 if (prefix_len > MAX_PREFIX_LEN) { |
|
1003 LM_ERR("Too long lcr prefix <%u>\n", prefix_len); |
|
1004 lcr_dbf.free_result(dbh, res); |
|
1005 lcr_dbf.close(dbh); |
|
1006 return -1; |
|
1007 } |
|
1008 } |
|
1009 if (VAL_NULL(ROW_VALUES(row) + 1) == 1) { |
|
1010 from_uri_len = 0; |
|
1011 from_uri = 0; |
|
1012 } else { |
|
1013 from_uri = (char *)VAL_STRING(ROW_VALUES(row) + 1); |
|
1014 from_uri_len = strlen(from_uri); |
|
1015 if (from_uri_len > MAX_FROM_URI_LEN) { |
|
1016 LM_ERR("Too long from_uri <%u>\n", from_uri_len); |
|
1017 lcr_dbf.free_result(dbh, res); |
|
1018 lcr_dbf.close(dbh); |
|
1019 return -1; |
|
1020 } |
|
1021 } |
|
1022 if (VAL_NULL(ROW_VALUES(row) + 2) == 1) { |
|
1023 LM_ERR("Route grp_id is NULL\n"); |
|
1024 lcr_dbf.free_result(dbh, res); |
|
1025 lcr_dbf.close(dbh); |
|
1026 return -1; |
|
1027 } |
|
1028 grp_id = (unsigned int)VAL_INT(ROW_VALUES(row) + 2); |
|
1029 if (VAL_NULL(ROW_VALUES(row) + 3) == 1) { |
|
1030 LM_ERR("Route priority is NULL\n"); |
|
1031 lcr_dbf.free_result(dbh, res); |
|
1032 lcr_dbf.close(dbh); |
|
1033 return -1; |
|
1034 } |
|
1035 priority = (unsigned int)VAL_INT(ROW_VALUES(row) + 3); |
|
1036 |
|
1037 if (*lcrs == lcrs_1) { |
|
1038 lcrs_2[i].prefix_len = prefix_len; |
|
1039 if (prefix_len) |
|
1040 memcpy(&(lcrs_2[i].prefix[0]), prefix, prefix_len); |
|
1041 lcrs_2[i].from_uri_len = from_uri_len; |
|
1042 if (from_uri_len) { |
|
1043 memcpy(&(lcrs_2[i].from_uri[0]), from_uri, from_uri_len); |
|
1044 lcrs_2[i].from_uri[from_uri_len] = '\0'; |
|
1045 } |
|
1046 lcrs_2[i].grp_id = grp_id; |
|
1047 lcrs_2[i].priority = priority; |
|
1048 lcrs_2[i].end_record = 0; |
|
1049 } else { |
|
1050 lcrs_1[i].prefix_len = prefix_len; |
|
1051 if (prefix_len) |
|
1052 memcpy(&(lcrs_1[i].prefix[0]), prefix, prefix_len); |
|
1053 lcrs_1[i].from_uri_len = from_uri_len; |
|
1054 if (from_uri_len) { |
|
1055 memcpy(&(lcrs_1[i].from_uri[0]), from_uri, from_uri_len); |
|
1056 lcrs_1[i].from_uri[from_uri_len] = '\0'; |
|
1057 } |
|
1058 lcrs_1[i].grp_id = grp_id; |
|
1059 lcrs_1[i].priority = priority; |
|
1060 lcrs_1[i].end_record = 0; |
|
1061 } |
|
1062 } |
|
1063 |
|
1064 lcr_dbf.free_result(dbh, res); |
|
1065 lcr_dbf.close(dbh); |
|
1066 |
|
1067 if (*lcrs == lcrs_1) { |
|
1068 lcrs_2[i].end_record = 1; |
|
1069 *lcrs = lcrs_2; |
|
1070 } else { |
|
1071 lcrs_1[i].end_record = 1; |
|
1072 *lcrs = lcrs_1; |
|
1073 } |
|
1074 |
|
1075 (*lcrs_ws_reload_counter)++; |
|
1076 if (0 != load_all_regex()) { |
|
1077 |
|
1078 return -1; |
|
1079 } |
|
1080 |
|
1081 return 1; |
|
1082 } |
|
1083 |
|
1084 |
|
1085 int mi_print_gws(struct mi_node* rpl) |
|
1086 { |
|
1087 unsigned int i; |
|
1088 struct mi_attr* attr; |
|
1089 uri_transport transport; |
|
1090 char *transp; |
|
1091 struct mi_node* node; |
|
1092 struct ip_addr address; |
|
1093 char* p; |
|
1094 int len; |
|
1095 |
|
1096 for (i = 0; i < MAX_NO_OF_GWS; i++) { |
|
1097 |
|
1098 if ((*gws)[i].ip_addr == 0) |
|
1099 break; |
|
1100 |
|
1101 node= add_mi_node_child(rpl,0 ,"GW", 2, 0, 0); |
|
1102 if(node == NULL) |
|
1103 return -1; |
|
1104 |
|
1105 p = int2str((unsigned long)(*gws)[i].grp_id, &len ); |
|
1106 attr = add_mi_attr(node, MI_DUP_VALUE, "GRP_ID", 6, p, len ); |
|
1107 if(attr == NULL) |
|
1108 return -1; |
|
1109 |
|
1110 transport = (*gws)[i].transport; |
|
1111 if (transport == PROTO_UDP) |
|
1112 transp= ";transport=udp"; |
|
1113 else if (transport == PROTO_TCP) |
|
1114 transp= ";transport=tcp"; |
|
1115 else if (transport == PROTO_TLS) |
|
1116 transp= ";transport=tls"; |
|
1117 else |
|
1118 transp= ""; |
|
1119 |
|
1120 address.af = AF_INET; |
|
1121 address.len = 4; |
|
1122 address.u.addr32[0] = (*gws)[i].ip_addr; |
|
1123 attr= addf_mi_attr(node,0 ,"URI", 3,"%s:%s:%d%s", |
|
1124 ((*gws)[i].scheme == SIP_URI_T)?"sip":"sips", |
|
1125 ip_addr2a(&address), |
|
1126 ((*gws)[i].port == 0)?5060:(*gws)[i].port,transp); |
|
1127 if(attr == NULL) |
|
1128 return -1; |
|
1129 |
|
1130 p = int2str((unsigned long)(*gws)[i].strip, &len ); |
|
1131 attr = add_mi_attr(node, MI_DUP_VALUE, "STRIP", 5, p, len); |
|
1132 if(attr == NULL) |
|
1133 return -1; |
|
1134 |
|
1135 attr = add_mi_attr(node, MI_DUP_VALUE, "TAG", 3, |
|
1136 (*gws)[i].tag, (*gws)[i].tag_len ); |
|
1137 if(attr == NULL) |
|
1138 return -1; |
|
1139 |
|
1140 p = int2str((unsigned long)(*gws)[i].flags, &len ); |
|
1141 attr = add_mi_attr(node, MI_DUP_VALUE, "FLAGS", 5, p, len); |
|
1142 if(attr == NULL) |
|
1143 return -1; |
|
1144 } |
|
1145 |
|
1146 for (i = 0; i < MAX_NO_OF_LCRS; i++) { |
|
1147 if ((*lcrs)[i].end_record != 0) |
|
1148 break; |
|
1149 |
|
1150 node= add_mi_node_child(rpl, 0, "RULE", 4, 0, 0); |
|
1151 attr = add_mi_attr(node, 0, "PREFIX", 6, (*lcrs)[i].prefix, |
|
1152 (*lcrs)[i].prefix_len ); |
|
1153 if(attr== 0) |
|
1154 return -1; |
|
1155 |
|
1156 attr = add_mi_attr(node, 0, "FROM_URI", 8, (*lcrs)[i].from_uri, |
|
1157 (*lcrs)[i].from_uri_len ); |
|
1158 if(attr== 0) |
|
1159 return -1; |
|
1160 |
|
1161 p = int2str((unsigned long)(*lcrs)[i].grp_id, &len ); |
|
1162 attr = add_mi_attr(node, MI_DUP_VALUE, "GRP_ID", 6, p, len ); |
|
1163 if(attr == NULL) |
|
1164 return -1; |
|
1165 |
|
1166 p = int2str((unsigned long)(*lcrs)[i].priority, &len ); |
|
1167 attr = add_mi_attr(node, MI_DUP_VALUE, "PRIORITY", 8, p, len ); |
|
1168 if(attr == NULL) |
|
1169 return -1; |
|
1170 |
|
1171 } |
|
1172 |
|
1173 return 0; |
|
1174 } |
|
1175 |
|
1176 |
|
1177 /* |
|
1178 * Load info of matching GWs from database to gw_uri AVPs |
|
1179 */ |
|
1180 static int do_load_gws(struct sip_msg* _m, str *_from_uri, int _grp_id) |
|
1181 { |
|
1182 str ruri_user, from_uri, value; |
|
1183 char from_uri_str[MAX_FROM_URI_LEN + 1]; |
|
1184 char ruri[MAX_URI_SIZE]; |
|
1185 unsigned int i, j, k, index, addr, port, strip, gw_index, |
|
1186 duplicated_gw, flags, have_rpid_avp; |
|
1187 uri_type scheme; |
|
1188 uri_transport transport; |
|
1189 struct ip_addr address; |
|
1190 str addr_str, port_str; |
|
1191 char *at, *tag, *strip_string, *flags_string; |
|
1192 struct usr_avp *avp; |
|
1193 int_str val; |
|
1194 struct mi matched_gws[MAX_NO_OF_GWS + 1]; |
|
1195 unsigned short tag_len, prefix_len, priority; |
|
1196 int randomizer_start, randomizer_end, randomizer_flag, |
|
1197 strip_len, flags_len; |
|
1198 struct lcr_info lcr_rec; |
|
1199 |
|
1200 /* Find Request-URI user */ |
|
1201 if ((parse_sip_msg_uri(_m) < 0) || (!_m->parsed_uri.user.s)) { |
|
1202 LM_ERR("Error while parsing R-URI\n"); |
|
1203 return -1; |
|
1204 } |
|
1205 ruri_user = _m->parsed_uri.user; |
|
1206 |
|
1207 if (_from_uri) { |
|
1208 /* take caller uri from _from_uri argument */ |
|
1209 from_uri = *_from_uri; |
|
1210 } else { |
|
1211 /* take caller uri from RPID or From URI */ |
|
1212 have_rpid_avp = 0; |
|
1213 avp = search_first_avp(rpid_avp_type, rpid_avp, &val, 0); |
|
1214 if (avp != NULL) { |
|
1215 /* Get URI user from RPID if not empty */ |
|
1216 if (avp->flags & AVP_VAL_STR) { |
|
1217 if (val.s.s && val.s.len) { |
|
1218 from_uri = val.s; |
|
1219 have_rpid_avp = 1; |
|
1220 } |
|
1221 } else { |
|
1222 from_uri.s = int2str(val.n, &from_uri.len); |
|
1223 have_rpid_avp = 1; |
|
1224 } |
|
1225 } |
|
1226 if (!have_rpid_avp) { |
|
1227 /* Get URI from From URI */ |
|
1228 if ((!_m->from) && (parse_headers(_m, HDR_FROM_F, 0) == -1)) { |
|
1229 LM_ERR("Error while parsing headers\n"); |
|
1230 return -1; |
|
1231 } |
|
1232 if (!_m->from) { |
|
1233 LM_ERR("From header field not found\n"); |
|
1234 return -1; |
|
1235 } |
|
1236 if ((!(_m->from)->parsed) && (parse_from_header(_m) < 0)) { |
|
1237 LM_ERR("Error while parsing From header\n"); |
|
1238 return -1; |
|
1239 } |
|
1240 from_uri = get_from(_m)->uri; |
|
1241 } |
|
1242 } |
|
1243 if (from_uri.len <= MAX_FROM_URI_LEN) { |
|
1244 strncpy(from_uri_str, from_uri.s, from_uri.len); |
|
1245 from_uri_str[from_uri.len] = '\0'; |
|
1246 } else { |
|
1247 LM_ERR("From URI is too long <%u>\n", from_uri.len); |
|
1248 return -1; |
|
1249 } |
|
1250 |
|
1251 /* |
|
1252 * Check if the gws and lcrs were reloaded |
|
1253 */ |
|
1254 if (reload_counter != *lcrs_ws_reload_counter) { |
|
1255 if (load_all_regex() != 0) { |
|
1256 return -1; |
|
1257 } |
|
1258 } |
|
1259 |
|
1260 /* |
|
1261 * Let's match the gws: |
|
1262 * 1. prefix matching |
|
1263 * 2. from_uri matching |
|
1264 * 3. _grp_id matching |
|
1265 * |
|
1266 * Note: A gateway must be in the list _only_ once. |
|
1267 */ |
|
1268 gw_index = 0; |
|
1269 duplicated_gw = 0; |
|
1270 for (i = 0; i < MAX_NO_OF_LCRS; i++) { |
|
1271 lcr_rec = (*lcrs)[i]; |
|
1272 if (lcr_rec.end_record != 0) { |
|
1273 break; |
|
1274 } |
|
1275 if ( ((prefix_mode_param == 0) && (lcr_rec.prefix_len <= ruri_user.len) && |
|
1276 (strncmp(lcr_rec.prefix, ruri_user.s, lcr_rec.prefix_len)==0)) || |
|
1277 ( (prefix_mode_param != 0) && ( (lcr_rec.prefix_len == 0) || |
|
1278 (prefix_reg[i].valid && |
|
1279 (regexec(&(prefix_reg[i].re), ruri_user.s, 0, |
|
1280 (regmatch_t *)NULL, 0) == 0)) ) ) ) { |
|
1281 /* 1. Prefix matching is done */ |
|
1282 if ((lcr_rec.from_uri_len == 0) || |
|
1283 (from_uri_reg[i].valid && |
|
1284 (regexec(&(from_uri_reg[i].re), from_uri_str, 0, |
|
1285 (regmatch_t *)NULL, 0) == 0))) { |
|
1286 /* 2. from_uri matching is done */ |
|
1287 for (j = 0; j < MAX_NO_OF_GWS; j++) { |
|
1288 if ((*gws)[j].ip_addr == 0) { |
|
1289 break; |
|
1290 } |
|
1291 if (lcr_rec.grp_id == (*gws)[j].grp_id && |
|
1292 (_grp_id < 0 || (*gws)[j].grp_id == _grp_id)) { |
|
1293 /* 3. _grp_id matching is done */ |
|
1294 for (k = 0; k < gw_index; k++) { |
|
1295 if ((*gws)[j].ip_addr == |
|
1296 (*gws)[matched_gws[k].gw_index].ip_addr) { |
|
1297 /* Found the same gw in the list */ |
|
1298 /* Let's keep the one with higher */ |
|
1299 /* match on prefix len */ |
|
1300 LM_DBG("Duplicate gw for index" |
|
1301 " %d [%d,%d] and current [%d,%d] \n", |
|
1302 k, matched_gws[k].route_index, |
|
1303 matched_gws[k].route_index, i, j); |
|
1304 duplicated_gw = 1; |
|
1305 if (lcr_rec.prefix_len > |
|
1306 (*lcrs)[matched_gws[k].route_index].prefix_len) { |
|
1307 /* Replace the old entry with the new one */ |
|
1308 LM_DBG("Replace [%d,%d]" |
|
1309 " with [%d,%d] on index %d:" |
|
1310 " prefix reason %d>%d\n", |
|
1311 matched_gws[k].route_index, |
|
1312 matched_gws[k].gw_index, i, j, k, |
|
1313 lcr_rec.prefix_len, |
|
1314 (*lcrs)[matched_gws[k].route_index].prefix_len); |
|
1315 matched_gws[k].route_index = i; |
|
1316 matched_gws[k].gw_index = j; |
|
1317 /* Stop searching in the matched_gws list */ |
|
1318 break; |
|
1319 } else if (lcr_rec.prefix_len == |
|
1320 (*lcrs)[matched_gws[k].route_index].prefix_len) { |
|
1321 if (lcr_rec.priority > |
|
1322 (*lcrs)[matched_gws[k].route_index].priority) { |
|
1323 /* Replace the old entry with the new one */ |
|
1324 LM_DBG("Replace [%d,%d] with" |
|
1325 " [%d,%d] on index %d:" |
|
1326 " priority reason %d>%d\n", |
|
1327 matched_gws[k].route_index, |
|
1328 matched_gws[k].gw_index, |
|
1329 i, j, k, lcr_rec.priority, |
|
1330 (*lcrs)[matched_gws[k].route_index].priority); |
|
1331 matched_gws[k].route_index = i; |
|
1332 matched_gws[k].gw_index = j; |
|
1333 /* Stop searching in the matched_gws list */ |
|
1334 break; |
|
1335 } |
|
1336 } |
|
1337 } |
|
1338 } |
|
1339 if (duplicated_gw == 0) { |
|
1340 /* This is a new gw */ |
|
1341 matched_gws[gw_index].route_index = i; |
|
1342 matched_gws[gw_index].gw_index = j; |
|
1343 LM_DBG("Added matched_gws[%d]=[%d,%d]\n", |
|
1344 gw_index, i, j); |
|
1345 gw_index++; |
|
1346 } else { |
|
1347 duplicated_gw = 0; |
|
1348 } |
|
1349 } |
|
1350 } |
|
1351 } |
|
1352 } |
|
1353 } |
|
1354 matched_gws[gw_index].route_index = -1; |
|
1355 matched_gws[gw_index].gw_index = -1; |
|
1356 |
|
1357 /* |
|
1358 * Sort the gateways based on: |
|
1359 * 1. prefix len |
|
1360 * 2. priority |
|
1361 */ |
|
1362 qsort(matched_gws, gw_index, sizeof(struct mi), comp_lcrs); |
|
1363 randomizer_start = 0; |
|
1364 |
|
1365 /* Randomizing the gateways with same prefix_len and same priority */ |
|
1366 randomizer_flag = 0; |
|
1367 prefix_len = (*lcrs)[matched_gws[0].route_index].prefix_len; |
|
1368 priority = (*lcrs)[matched_gws[0].route_index].priority; |
|
1369 for (i = 1; i < gw_index; i++) { |
|
1370 if ( prefix_len == (*lcrs)[matched_gws[i].route_index].prefix_len && |
|
1371 priority == (*lcrs)[matched_gws[i].route_index].priority) { |
|
1372 /* we have a match */ |
|
1373 if (randomizer_flag == 0) { |
|
1374 randomizer_flag = 1; |
|
1375 randomizer_start = i - 1; |
|
1376 } |
|
1377 matched_gws[i - 1].randomizer = rand(); |
|
1378 } |
|
1379 else { |
|
1380 if (randomizer_flag == 1) { |
|
1381 randomizer_end = i - 1; |
|
1382 randomizer_flag = 0; |
|
1383 qsort(&matched_gws[randomizer_start], |
|
1384 randomizer_end - randomizer_start + 1, |
|
1385 sizeof(struct mi), rand_lcrs); |
|
1386 } |
|
1387 prefix_len = (*lcrs)[matched_gws[i].route_index].prefix_len; |
|
1388 priority = (*lcrs)[matched_gws[i].route_index].priority; |
|
1389 } |
|
1390 } |
|
1391 if (randomizer_flag == 1) { |
|
1392 randomizer_end = gw_index - 1; |
|
1393 matched_gws[i - 1].randomizer = rand(); |
|
1394 qsort(&matched_gws[randomizer_start], |
|
1395 randomizer_end - randomizer_start + 1, |
|
1396 sizeof(struct mi), rand_lcrs); |
|
1397 } |
|
1398 |
|
1399 for (i = 0; i < MAX_NO_OF_GWS; i++) { |
|
1400 index = matched_gws[i].gw_index; |
|
1401 if (index == -1) { |
|
1402 break; |
|
1403 } |
|
1404 addr = (*gws)[index].ip_addr; |
|
1405 port = (*gws)[index].port; |
|
1406 scheme = (*gws)[index].scheme; |
|
1407 transport = (*gws)[index].transport; |
|
1408 flags = (*gws)[index].flags; |
|
1409 strip = (*gws)[index].strip; |
|
1410 if (strip > ruri_user.len) { |
|
1411 LM_ERR("Strip count of gw is too large <%u>\n", strip); |
|
1412 goto skip; |
|
1413 } |
|
1414 tag_len = (*gws)[index].tag_len; |
|
1415 tag = (*gws)[index].tag; |
|
1416 if (6 + tag_len + 40 /* flags + strip */ + 1 + 15 + 1 + 5 + 1 + 14 > |
|
1417 MAX_URI_SIZE) { |
|
1418 LM_ERR("Request URI would be too long\n"); |
|
1419 goto skip; |
|
1420 } |
|
1421 at = (char *)&(ruri[0]); |
|
1422 flags_string = int2str(flags, &flags_len); |
|
1423 memcpy(at, flags_string, flags_len); |
|
1424 at = at + flags_len; |
|
1425 if (scheme == SIP_URI_T) { |
|
1426 memcpy(at, "sip:", 4); at = at + 4; |
|
1427 } else if (scheme == SIPS_URI_T) { |
|
1428 memcpy(at, "sips:", 5); at = at + 5; |
|
1429 } else { |
|
1430 LM_ERR("Unknown or unsupported URI scheme <%u>\n", |
|
1431 (unsigned int)scheme); |
|
1432 goto skip; |
|
1433 } |
|
1434 if (tag_len) { |
|
1435 memcpy(at, tag, tag_len); at = at + tag_len; |
|
1436 } |
|
1437 /* Add strip in this form |number. |
|
1438 * For example: |3 means strip first 3 characters. |
|
1439 */ |
|
1440 *at = '|'; at = at + 1; |
|
1441 strip_string = int2str(strip, &strip_len); |
|
1442 memcpy(at, strip_string, strip_len); |
|
1443 at = at + strip_len; |
|
1444 *at = '@'; at = at + 1; |
|
1445 address.af = AF_INET; |
|
1446 address.len = 4; |
|
1447 address.u.addr32[0] = addr; |
|
1448 addr_str.s = ip_addr2a(&address); |
|
1449 addr_str.len = strlen(addr_str.s); |
|
1450 memcpy(at, addr_str.s, addr_str.len); at = at + addr_str.len; |
|
1451 if (port != 0) { |
|
1452 if (port > 65536) { |
|
1453 LM_ERR("Port of GW is too large <%u>\n", port); |
|
1454 goto skip; |
|
1455 } |
|
1456 *at = ':'; at = at + 1; |
|
1457 port_str.s = int2str(port, &port_str.len); |
|
1458 memcpy(at, port_str.s, port_str.len); at = at + port_str.len; |
|
1459 } |
|
1460 if (transport != PROTO_NONE) { |
|
1461 memcpy(at, ";transport=", 11); at = at + 11; |
|
1462 if (transport == PROTO_UDP) { |
|
1463 memcpy(at, "udp", 3); at = at + 3; |
|
1464 } else if (transport == PROTO_TCP) { |
|
1465 memcpy(at, "tcp", 3); at = at + 3; |
|
1466 } else if (transport == PROTO_TLS) { |
|
1467 memcpy(at, "tls", 3); at = at + 3; |
|
1468 } else { |
|
1469 LM_ERR("Unknown or unsupported transport <%u>\n", |
|
1470 (unsigned int)transport); |
|
1471 goto skip; |
|
1472 } |
|
1473 } |
|
1474 value.s = (char *)&(ruri[0]); |
|
1475 value.len = at - value.s; |
|
1476 val.s = value; |
|
1477 add_avp(gw_uri_avp_type|AVP_VAL_STR, gw_uri_avp, val); |
|
1478 LM_DBG("Added gw_uri_avp <%.*s>\n", value.len, value.s); |
|
1479 skip: |
|
1480 continue; |
|
1481 } |
|
1482 |
|
1483 return 1; |
|
1484 } |
|
1485 |
|
1486 /* |
|
1487 * Load info of matching GWs from database to gw_uri AVPs |
|
1488 * taking into account the given group id. Caller URI is taken |
|
1489 * from request. |
|
1490 */ |
|
1491 static int load_gws_from_grp(struct sip_msg* _m, char* _s1, char* _s2) |
|
1492 { |
|
1493 str grp_s; |
|
1494 unsigned int grp_id; |
|
1495 |
|
1496 if(((pv_elem_p)_s1)->spec.getf!=NULL) |
|
1497 { |
|
1498 if(pv_printf_s(_m, (pv_elem_p)_s1, &grp_s)!=0) |
|
1499 return -1; |
|
1500 if(str2int(&grp_s, &grp_id)!=0) |
|
1501 return -1; |
|
1502 } else { |
|
1503 grp_id = ((pv_elem_p)_s1)->spec.pvp.pvn.u.isname.name.n; |
|
1504 } |
|
1505 if (grp_id > 0) return do_load_gws(_m, (str *)0, (int)grp_id); |
|
1506 else return -1; |
|
1507 } |
|
1508 |
|
1509 |
|
1510 /* |
|
1511 * Load info of matching GWs from database to gw_uri AVPs. |
|
1512 * Caller URI is taken from request. |
|
1513 */ |
|
1514 static int load_gws_0(struct sip_msg* _m, char* _s1, char* _s2) |
|
1515 { |
|
1516 return do_load_gws(_m, (str *)0, -1); |
|
1517 } |
|
1518 |
|
1519 |
|
1520 /* |
|
1521 * Load info of matching GWs from database to gw_uri AVPs. |
|
1522 * Caller URI is taken from pseudo variable argument. |
|
1523 */ |
|
1524 static int load_gws_1(struct sip_msg* _m, char* _sp, char* _s2) |
|
1525 { |
|
1526 pv_spec_t *sp; |
|
1527 pv_value_t pv_val; |
|
1528 sp = (pv_spec_t *)_sp; |
|
1529 |
|
1530 if (sp && (pv_get_spec_value(_m, sp, &pv_val) == 0)) { |
|
1531 if (pv_val.flags & PV_VAL_STR) { |
|
1532 if (pv_val.rs.len == 0 || pv_val.rs.s == NULL) { |
|
1533 LM_DBG("missing from uri\n"); |
|
1534 return -1; |
|
1535 } |
|
1536 return do_load_gws(_m, &(pv_val.rs), -1); |
|
1537 } else { |
|
1538 LM_DBG("pseudo variable value is not string\n"); |
|
1539 return -1; |
|
1540 } |
|
1541 } else { |
|
1542 LM_DBG("cannot get pseudo variable value\n"); |
|
1543 return -1; |
|
1544 } |
|
1545 } |
|
1546 |
|
1547 |
|
1548 /* |
|
1549 * Rewrites scheme, host, port, and transport parts of R-URI based on first |
|
1550 * gw_uri AVP value, which is then destroyed. Also saves R-URI user to |
|
1551 * ruri_user AVP for later use in failure route block. |
|
1552 * If called from failure route block, appends a new branch to request |
|
1553 * where scheme, host, port, and transport of URI are taken from the first |
|
1554 * gw_uri AVP value, which is then destroyed. URI user is taken from |
|
1555 * ruri_user AVP value saved earlier. |
|
1556 * Returns 1 upon success and -1 upon failure. |
|
1557 */ |
|
1558 static int next_gw(struct sip_msg* _m, char* _s1, char* _s2) |
|
1559 { |
|
1560 int_str gw_uri_val, ruri_user_val, val; |
|
1561 struct usr_avp *gu_avp, *ru_avp; |
|
1562 int rval; |
|
1563 str new_ruri; |
|
1564 char *at, *at_char, *strip_char, *endptr; |
|
1565 unsigned int strip; |
|
1566 |
|
1567 gu_avp = search_first_avp(gw_uri_avp_type, gw_uri_avp, &gw_uri_val, 0); |
|
1568 if (!gu_avp) return -1; |
|
1569 |
|
1570 /* Set flags_avp from integer at the beginning of of gw_uri */ |
|
1571 val.n = (int)strtoul(gw_uri_val.s.s, &at, 0); |
|
1572 add_avp(flags_avp_type, flags_avp, val); |
|
1573 LM_DBG("Added flags_avp <%u>\n", (unsigned int)val.n); |
|
1574 |
|
1575 gw_uri_val.s.len = gw_uri_val.s.len - (at - gw_uri_val.s.s); |
|
1576 gw_uri_val.s.s = at; |
|
1577 |
|
1578 /* Create new Request-URI taking URI user from ruri_user AVP |
|
1579 and other parts of from gateway URI AVP. */ |
|
1580 ru_avp = search_first_avp(ruri_user_avp_type, ruri_user_avp, |
|
1581 &ruri_user_val, 0); |
|
1582 if (!ru_avp) { |
|
1583 LM_DBG("ruri_user AVP no yet set -> use RURI\n"); |
|
1584 /* parse RURI and ger username */ |
|
1585 if (parse_sip_msg_uri(_m) < 0) { |
|
1586 LM_ERR("Parsing of R-URI failed\n"); |
|
1587 return -1; |
|
1588 } |
|
1589 ruri_user_val.s = _m->parsed_uri.user; |
|
1590 /* Save Request-URI user for use in FAILURE_ROUTE */ |
|
1591 val.s = _m->parsed_uri.user; |
|
1592 add_avp(ruri_user_avp_type|AVP_VAL_STR, ruri_user_avp, val); |
|
1593 LM_DBG("Added ruri_user_avp <%.*s>\n", val.s.len, val.s.s); |
|
1594 } |
|
1595 |
|
1596 new_ruri.s = pkg_malloc(gw_uri_val.s.len + ruri_user_val.s.len); |
|
1597 if (!new_ruri.s) { |
|
1598 LM_ERR("No memory for new R-URI.\n"); |
|
1599 return -1; |
|
1600 } |
|
1601 at_char = memchr(gw_uri_val.s.s, '@', gw_uri_val.s.len); |
|
1602 if (!at_char) { |
|
1603 pkg_free(new_ruri.s); |
|
1604 LM_ERR("No @ in gateway URI <%.*s>\n", |
|
1605 gw_uri_val.s.len, gw_uri_val.s.s); |
|
1606 return -1; |
|
1607 } |
|
1608 strip_char = memchr(gw_uri_val.s.s, '|', gw_uri_val.s.len); |
|
1609 if (!strip_char || strip_char + 1 >= at_char) { |
|
1610 pkg_free(new_ruri.s); |
|
1611 LM_ERR("No strip char | and at least one " |
|
1612 "char before @ in gateway URI <%.*s>\n", |
|
1613 gw_uri_val.s.len, gw_uri_val.s.s); |
|
1614 return -1; |
|
1615 } |
|
1616 at = new_ruri.s; |
|
1617 memcpy(at, gw_uri_val.s.s, strip_char - gw_uri_val.s.s); |
|
1618 at = at + (strip_char - gw_uri_val.s.s); |
|
1619 strip = strtol(strip_char + 1, &endptr, 10); |
|
1620 if (endptr != at_char) { |
|
1621 pkg_free(new_ruri.s); |
|
1622 LM_ERR("Non-digit char between | and @ chars in gw URI <%.*s>\n", |
|
1623 gw_uri_val.s.len, gw_uri_val.s.s); |
|
1624 return -1; |
|
1625 } |
|
1626 if (ruri_user_val.s.len - strip > 0) { |
|
1627 memcpy(at, ruri_user_val.s.s + strip, |
|
1628 ruri_user_val.s.len - strip); |
|
1629 at = at + ruri_user_val.s.len - strip; |
|
1630 } |
|
1631 if (*(at - 1) != ':') { |
|
1632 memcpy(at, at_char, gw_uri_val.s.len - (at_char - gw_uri_val.s.s)); |
|
1633 at = at + gw_uri_val.s.len - (at_char - gw_uri_val.s.s); |
|
1634 } else { |
|
1635 memcpy(at, at_char + 1, gw_uri_val.s.len - |
|
1636 (at_char + 1 - gw_uri_val.s.s)); |
|
1637 at = at + gw_uri_val.s.len - (at_char + 1 - gw_uri_val.s.s); |
|
1638 } |
|
1639 new_ruri.len = at - new_ruri.s; |
|
1640 |
|
1641 /* set new RURI */ |
|
1642 rval = set_ruri( _m, &new_ruri); |
|
1643 pkg_free(new_ruri.s); |
|
1644 destroy_avp(gu_avp); |
|
1645 if (rval!=0) { |
|
1646 LM_ERR("failed to set new RURI\n"); |
|
1647 return -1; |
|
1648 } |
|
1649 |
|
1650 return 1; |
|
1651 } |
|
1652 |
|
1653 |
|
1654 /* |
|
1655 * Checks if request comes from a gateway |
|
1656 */ |
|
1657 static int do_from_gw(struct sip_msg* _m, pv_spec_t *addr_sp, int grp_id) |
|
1658 { |
|
1659 int i; |
|
1660 unsigned int src_addr; |
|
1661 pv_value_t pv_val; |
|
1662 struct ip_addr *ip; |
|
1663 int_str val; |
|
1664 |
|
1665 if (addr_sp && (pv_get_spec_value(_m, addr_sp, &pv_val) == 0)) { |
|
1666 if (pv_val.flags & PV_VAL_INT) { |
|
1667 src_addr = pv_val.ri; |
|
1668 } else if (pv_val.flags & PV_VAL_STR) { |
|
1669 if ( (ip=str2ip( &pv_val.rs)) == NULL) { |
|
1670 LM_ERR("failed to convert IP address string to in_addr\n"); |
|
1671 return -1; |
|
1672 } else { |
|
1673 src_addr = ip->u.addr32[0]; |
|
1674 } |
|
1675 } else { |
|
1676 LM_ERR("IP address PV empty value\n"); |
|
1677 return -1; |
|
1678 } |
|
1679 } else { |
|
1680 src_addr = _m->rcv.src_ip.u.addr32[0]; |
|
1681 } |
|
1682 |
|
1683 for (i = 0; i < MAX_NO_OF_GWS; i++) { |
|
1684 if ((*gws)[i].ip_addr == 0) { |
|
1685 return -1; |
|
1686 } |
|
1687 if ((*gws)[i].ip_addr == src_addr && |
|
1688 (grp_id < 0 || (*gws)[i].grp_id == grp_id)) { |
|
1689 LM_DBG("Request came from gw\n"); |
|
1690 val.n = (int)(*gws)[i].flags; |
|
1691 add_avp(flags_avp_type, flags_avp, val); |
|
1692 LM_DBG("Added flags_avp <%u>\n", (unsigned int)val.n); |
|
1693 return 1; |
|
1694 } |
|
1695 } |
|
1696 |
|
1697 LM_DBG("Request did not come from gw\n"); |
|
1698 return -1; |
|
1699 } |
|
1700 |
|
1701 |
|
1702 /* |
|
1703 * Checks if request comes from a gateway, taking source address from request |
|
1704 * and taking into account the group id. |
|
1705 */ |
|
1706 static int from_gw_grp(struct sip_msg* _m, char* _grp_id, char* _s2) |
|
1707 { |
|
1708 return do_from_gw(_m, (pv_spec_t *)0, (int)(long)_grp_id); |
|
1709 } |
|
1710 |
|
1711 |
|
1712 /* |
|
1713 * Checks if request comes from a gateway, taking src_address from request |
|
1714 * and ignoring group id. |
|
1715 */ |
|
1716 static int from_gw_0(struct sip_msg* _m, char* _s1, char* _s2) |
|
1717 { |
|
1718 return do_from_gw(_m, (pv_spec_t *)0, -1); |
|
1719 } |
|
1720 |
|
1721 |
|
1722 /* |
|
1723 * Checks if request comes from a gateway, taking source address from pw |
|
1724 * and ignoring group id. |
|
1725 */ |
|
1726 static int from_gw_1(struct sip_msg* _m, char* _addr_sp, char* _s2) |
|
1727 { |
|
1728 return do_from_gw(_m, (pv_spec_t *)_addr_sp, -1); |
|
1729 } |
|
1730 |
|
1731 |
|
1732 /* |
|
1733 * Checks if in-dialog request goes to gateway |
|
1734 */ |
|
1735 static int do_to_gw(struct sip_msg* _m, int grp_id) |
|
1736 { |
|
1737 char host[16]; |
|
1738 struct in_addr addr; |
|
1739 unsigned int i; |
|
1740 |
|
1741 if((_m->parsed_uri_ok == 0) && (parse_sip_msg_uri(_m) < 0)) { |
|
1742 LM_ERR("Error while parsing the R-URI\n"); |
|
1743 return -1; |
|
1744 } |
|
1745 |
|
1746 if (_m->parsed_uri.host.len > 15) { |
|
1747 return -1; |
|
1748 } |
|
1749 memcpy(host, _m->parsed_uri.host.s, _m->parsed_uri.host.len); |
|
1750 host[_m->parsed_uri.host.len] = 0; |
|
1751 |
|
1752 if (!inet_aton(host, &addr)) { |
|
1753 return -1; |
|
1754 } |
|
1755 |
|
1756 for (i = 0; i < MAX_NO_OF_GWS; i++) { |
|
1757 if ((*gws)[i].ip_addr == 0) { |
|
1758 return -1; |
|
1759 } |
|
1760 if ((*gws)[i].ip_addr == addr.s_addr && |
|
1761 (grp_id < 0 || (*gws)[i].grp_id == grp_id)) { |
|
1762 return 1; |
|
1763 } |
|
1764 } |
|
1765 |
|
1766 return -1; |
|
1767 } |
|
1768 |
|
1769 |
|
1770 /* |
|
1771 * Checks if in-dialog request goes to gateway, taking |
|
1772 * into account the group id. |
|
1773 */ |
|
1774 static int to_gw_grp(struct sip_msg* _m, char* _s1, char* _s2) |
|
1775 { |
|
1776 int grp_id; |
|
1777 |
|
1778 grp_id = (int)(long)_s1; |
|
1779 return do_to_gw(_m, grp_id); |
|
1780 } |
|
1781 |
|
1782 |
|
1783 /* |
|
1784 * Checks if in-dialog request goes to gateway, ignoring |
|
1785 * the group id. |
|
1786 */ |
|
1787 static int to_gw(struct sip_msg* _m, char* _s1, char* _s2) |
|
1788 { |
|
1789 return do_to_gw(_m, -1); |
|
1790 } |
|
1791 |
|
1792 |
|
1793 /* |
|
1794 * Frees contact list used by load_contacts function |
|
1795 */ |
|
1796 static inline void free_contact_list(struct contact *curr) { |
|
1797 struct contact *prev; |
|
1798 while (curr) { |
|
1799 prev = curr; |
|
1800 curr = curr->next; |
|
1801 pkg_free(prev); |
|
1802 } |
|
1803 } |
|
1804 |
|
1805 /* Encode branch info from contact struct to str */ |
|
1806 static inline int encode_branch_info(str *info, struct contact *con) |
|
1807 { |
|
1808 char *at, *s; |
|
1809 int len; |
|
1810 |
|
1811 info->len = con->uri.len + con->dst_uri.len + |
|
1812 con->path.len + MAX_SOCKET_STR + INT2STR_MAX_LEN + 5; |
|
1813 info->s = pkg_malloc(info->len); |
|
1814 if (!info->s) { |
|
1815 LM_ERR("No memory left for branch info\n"); |
|
1816 return 0; |
|
1817 } |
|
1818 at = info->s; |
|
1819 memcpy(at, con->uri.s, con->uri.len); |
|
1820 at = at + con->uri.len; |
|
1821 *at = '\n'; |
|
1822 at++; |
|
1823 memcpy(at, con->dst_uri.s, con->dst_uri.len); |
|
1824 at = at + con->dst_uri.len; |
|
1825 *at = '\n'; |
|
1826 at++; |
|
1827 memcpy(at, con->path.s, con->path.len); |
|
1828 at = at + con->path.len; |
|
1829 *at = '\n'; |
|
1830 at++; |
|
1831 if (con->sock) { |
|
1832 len = MAX_SOCKET_STR; |
|
1833 if (!socket2str(con->sock, at, &len, 1)) { |
|
1834 LM_ERR("Failed to convert socket to str\n"); |
|
1835 return 0; |
|
1836 } |
|
1837 } else { |
|
1838 len = 0; |
|
1839 } |
|
1840 at = at + len; |
|
1841 *at = '\n'; |
|
1842 at++; |
|
1843 s = int2str(con->flags, &len); |
|
1844 memcpy(at, s, len); |
|
1845 at = at + len; |
|
1846 *at = '\n'; |
|
1847 info->len = at - info->s + 1; |
|
1848 |
|
1849 return 1; |
|
1850 } |
|
1851 |
|
1852 |
|
1853 /* Encode branch info from str */ |
|
1854 static inline int decode_branch_info(char *info, str *uri, str *dst, str *path, |
|
1855 struct socket_info **sock, unsigned int *flags) |
|
1856 { |
|
1857 str s, host; |
|
1858 int port, proto; |
|
1859 char *pos, *at; |
|
1860 |
|
1861 pos = strchr(info, '\n'); |
|
1862 uri->len = pos - info; |
|
1863 if (uri->len) { |
|
1864 uri->s = info; |
|
1865 } else { |
|
1866 uri->s = 0; |
|
1867 } |
|
1868 at = pos + 1; |
|
1869 |
|
1870 pos = strchr(at, '\n'); |
|
1871 dst->len = pos - at; |
|
1872 if (dst->len) { |
|
1873 dst->s = at; |
|
1874 } else { |
|
1875 dst->s = 0; |
|
1876 } |
|
1877 at = pos + 1; |
|
1878 |
|
1879 pos = strchr(at, '\n'); |
|
1880 path->len = pos - at; |
|
1881 if (path->len) { |
|
1882 path->s = at; |
|
1883 } else { |
|
1884 path->s = 0; |
|
1885 } |
|
1886 at = pos + 1; |
|
1887 |
|
1888 pos = strchr(at, '\n'); |
|
1889 s.len = pos - at; |
|
1890 if (s.len) { |
|
1891 s.s = at; |
|
1892 if (parse_phostport(s.s, s.len, &host.s, &host.len, |
|
1893 &port, &proto) != 0) { |
|
1894 LM_ERR("Parsing of socket info <%.*s> failed\n", s.len, s.s); |
|
1895 return 0; |
|
1896 } |
|
1897 *sock = grep_sock_info(&host, (unsigned short)port, |
|
1898 (unsigned short)proto); |
|
1899 if (*sock == 0) { |
|
1900 LM_ERR("Invalid socket <%.*s>\n", s.len, s.s); |
|
1901 return 0; |
|
1902 } |
|
1903 } else { |
|
1904 *sock = 0; |
|
1905 } |
|
1906 at = pos + 1; |
|
1907 |
|
1908 pos = strchr(at, '\n'); |
|
1909 s.len = pos - at; |
|
1910 if (s.len) { |
|
1911 s.s = at; |
|
1912 if (str2int(&s, flags) != 0) { |
|
1913 LM_ERR("Failed to decode flags <%.*s>\n", s.len, s.s); |
|
1914 return 0; |
|
1915 } |
|
1916 } else { |
|
1917 *flags = 0; |
|
1918 } |
|
1919 |
|
1920 return 1; |
|
1921 } |
|
1922 |
|
1923 |
|
1924 /* |
|
1925 * Loads contacts in destination set into "lcr_contact" AVP in reverse |
|
1926 * priority order and associated each contact with Q_FLAG telling if |
|
1927 * contact is the last one in its priority class. Finally, removes |
|
1928 * all branches from destination set. |
|
1929 */ |
|
1930 static int load_contacts(struct sip_msg* msg, char* key, char* value) |
|
1931 { |
|
1932 str uri, dst_uri, path, branch_info, *ruri; |
|
1933 qvalue_t q, ruri_q; |
|
1934 struct contact *contacts, *next, *prev, *curr; |
|
1935 int_str val; |
|
1936 int idx; |
|
1937 struct socket_info* sock; |
|
1938 unsigned int flags; |
|
1939 |
|
1940 /* Check if anything needs to be done */ |
|
1941 if (nr_branches == 0) { |
|
1942 LM_DBG("Nothing to do - no branches!\n"); |
|
1943 return 1; |
|
1944 } |
|
1945 |
|
1946 ruri = GET_RURI(msg); |
|
1947 if (!ruri) { |
|
1948 LM_ERR("No Request-URI found\n"); |
|
1949 return -1; |
|
1950 } |
|
1951 ruri_q = get_ruri_q(); |
|
1952 |
|
1953 for(idx = 0; (uri.s = get_branch(idx, &uri.len, &q, 0, 0, 0, 0)) != 0; |
|
1954 idx++) { |
|
1955 if (q != ruri_q) { |
|
1956 goto rest; |
|
1957 } |
|
1958 } |
|
1959 LM_DBG("Nothing to do - all contacts have same q!\n"); |
|
1960 return 1; |
|
1961 |
|
1962 rest: |
|
1963 /* Insert Request-URI branch to contact list */ |
|
1964 contacts = (struct contact *)pkg_malloc(sizeof(struct contact)); |
|
1965 if (!contacts) { |
|
1966 LM_ERR("No memory for contact info\n"); |
|
1967 return -1; |
|
1968 } |
|
1969 contacts->uri.s = ruri->s; |
|
1970 contacts->uri.len = ruri->len; |
|
1971 contacts->q = ruri_q; |
|
1972 contacts->dst_uri = msg->dst_uri; |
|
1973 contacts->sock = msg->force_send_socket; |
|
1974 contacts->flags = getb0flags(); |
|
1975 contacts->path = msg->path_vec; |
|
1976 contacts->next = (struct contact *)0; |
|
1977 |
|
1978 /* Insert branches to contact list in increasing q order */ |
|
1979 for(idx = 0; |
|
1980 (uri.s = get_branch(idx,&uri.len,&q,&dst_uri,&path,&flags,&sock)) |
|
1981 != 0; |
|
1982 idx++ ) { |
|
1983 next = (struct contact *)pkg_malloc(sizeof(struct contact)); |
|
1984 if (!next) { |
|
1985 LM_ERR("No memory for contact info\n"); |
|
1986 free_contact_list(contacts); |
|
1987 return -1; |
|
1988 } |
|
1989 next->uri = uri; |
|
1990 next->q = q; |
|
1991 next->dst_uri = dst_uri; |
|
1992 next->path = path; |
|
1993 next->flags = flags; |
|
1994 next->sock = sock; |
|
1995 next->next = (struct contact *)0; |
|
1996 prev = (struct contact *)0; |
|
1997 curr = contacts; |
|
1998 while (curr && (curr->q < q)) { |
|
1999 prev = curr; |
|
2000 curr = curr->next; |
|
2001 } |
|
2002 if (!curr) { |
|
2003 next->next = (struct contact *)0; |
|
2004 prev->next = next; |
|
2005 } else { |
|
2006 next->next = curr; |
|
2007 if (prev) { |
|
2008 prev->next = next; |
|
2009 } else { |
|
2010 contacts = next; |
|
2011 } |
|
2012 } |
|
2013 } |
|
2014 |
|
2015 /* Assign values for q_flags */ |
|
2016 curr = contacts; |
|
2017 curr->q_flag = 0; |
|
2018 while (curr->next) { |
|
2019 if (curr->q < curr->next->q) { |
|
2020 curr->next->q_flag = Q_FLAG; |
|
2021 } else { |
|
2022 curr->next->q_flag = 0; |
|
2023 } |
|
2024 curr = curr->next; |
|
2025 } |
|
2026 |
|
2027 /* Add contacts to "contacts" AVP */ |
|
2028 curr = contacts; |
|
2029 while (curr) { |
|
2030 if (encode_branch_info(&branch_info, curr) == 0) { |
|
2031 LM_ERR("Encoding of branch info failed\n"); |
|
2032 free_contact_list(contacts); |
|
2033 if (branch_info.s) pkg_free(branch_info.s); |
|
2034 return -1; |
|
2035 } |
|
2036 val.s = branch_info; |
|
2037 add_avp(contact_avp_type|AVP_VAL_STR|(curr->q_flag), |
|
2038 contact_avp, val); |
|
2039 pkg_free(branch_info.s); |
|
2040 LM_DBG("Loaded contact <%.*s> with q_flag <%d>\n", |
|
2041 val.s.len, val.s.s, curr->q_flag); |
|
2042 curr = curr->next; |
|
2043 } |
|
2044 |
|
2045 /* Clear all branches */ |
|
2046 clear_branches(); |
|
2047 |
|
2048 /* Free contact list */ |
|
2049 free_contact_list(contacts); |
|
2050 |
|
2051 return 1; |
|
2052 } |
|
2053 |
|
2054 |
|
2055 /* |
|
2056 * Adds to request a destination set that includes all highest priority |
|
2057 * class contacts in "lcr_contact" AVP. If called from a route block, |
|
2058 * rewrites the request uri with first contact and adds the remaining |
|
2059 * contacts as branches. If called from failure route block, adds all |
|
2060 * contacts as branches. Removes added contacts from "lcr_contact" AVP. |
|
2061 */ |
|
2062 static int next_contacts(struct sip_msg* msg, char* key, char* value) |
|
2063 { |
|
2064 struct usr_avp *avp, *prev; |
|
2065 int_str val; |
|
2066 str uri, dst, path; |
|
2067 struct socket_info *sock; |
|
2068 unsigned int flags; |
|
2069 |
|
2070 /* Find first lcr_contact_avp value */ |
|
2071 avp = search_first_avp(contact_avp_type, contact_avp, &val, 0); |
|
2072 if (!avp) { |
|
2073 LM_DBG("No AVPs -- we are done!\n"); |
|
2074 return -1; |
|
2075 } |
|
2076 |
|
2077 LM_DBG("Next contact is <%s>\n", val.s.s); |
|
2078 |
|
2079 if (decode_branch_info(val.s.s, &uri, &dst, &path, &sock, &flags)== 0) { |
|
2080 LM_ERR("Decoding of branch info <%.*s> failed\n", |
|
2081 val.s.len, val.s.s); |
|
2082 destroy_avp(avp); |
|
2083 return -1; |
|
2084 } |
|
2085 |
|
2086 set_ruri(msg, &uri); |
|
2087 set_dst_uri(msg, &dst); |
|
2088 set_path_vector(msg, &path); |
|
2089 msg->force_send_socket = sock; |
|
2090 setb0flags(flags); |
|
2091 |
|
2092 if (avp->flags & Q_FLAG) { |
|
2093 destroy_avp(avp); |
|
2094 if (route_type == REQUEST_ROUTE) { |
|
2095 /* Set fr_inv_timer */ |
|
2096 val.n = fr_inv_timer_next; |
|
2097 if (add_avp(fr_inv_timer_avp_type, fr_inv_timer_avp, val) != 0) { |
|
2098 LM_ERR("Setting of fr_inv_timer_avp failed\n"); |
|
2099 return -1; |
|
2100 } |
|
2101 } |
|
2102 return 1; |
|
2103 } |
|
2104 |
|
2105 /* Append branches until out of branches or Q_FLAG is set */ |
|
2106 prev = avp; |
|
2107 while ((avp = search_next_avp(avp, &val))) { |
|
2108 destroy_avp(prev); |
|
2109 |
|
2110 LM_DBG("Next contact is <%s>\n", val.s.s); |
|
2111 |
|
2112 if (decode_branch_info(val.s.s, &uri, &dst, &path, &sock, &flags)== 0){ |
|
2113 LM_ERR("Decoding of branch info <%.*s> failed\n", |
|
2114 val.s.len, val.s.s); |
|
2115 destroy_avp(avp); |
|
2116 return -1; |
|
2117 } |
|
2118 |
|
2119 if (append_branch(msg, &uri, &dst, &path, 0, flags, sock) != 1) { |
|
2120 LM_ERR("Appending branch failed\n"); |
|
2121 destroy_avp(avp); |
|
2122 return -1; |
|
2123 } |
|
2124 |
|
2125 if (avp->flags & Q_FLAG) { |
|
2126 destroy_avp(avp); |
|
2127 if (route_type == REQUEST_ROUTE) { |
|
2128 val.n = fr_inv_timer_next; |
|
2129 if (add_avp(fr_inv_timer_avp_type, fr_inv_timer_avp, val)!= 0){ |
|
2130 LM_ERR("Setting of fr_inv_timer_avp failed\n"); |
|
2131 return -1; |
|
2132 } |
|
2133 } |
|
2134 return 1; |
|
2135 } |
|
2136 prev = avp; |
|
2137 } |
|
2138 |
|
2139 /* Restore fr_inv_timer */ |
|
2140 val.n = fr_inv_timer; |
|
2141 if (add_avp(fr_inv_timer_avp_type, fr_inv_timer_avp, val) != 0) { |
|
2142 LM_ERR("Setting of fr_inv_timer_avp failed\n"); |
|
2143 return -1; |
|
2144 } |
|
2145 |
|
2146 return 1; |
|
2147 } |
|
2148 |
|
2149 |
|
2150 /* |
|
2151 * Convert string parameter to integer for functions that expect an integer. |
|
2152 * Taken from sl module. |
|
2153 */ |
|
2154 static int fixstringloadgws(void **param, int param_count) |
|
2155 { |
|
2156 pv_elem_t *model=NULL; |
|
2157 str s; |
|
2158 |
|
2159 /* convert to str */ |
|
2160 s.s = (char*)*param; |
|
2161 s.len = strlen(s.s); |
|
2162 |
|
2163 model=NULL; |
|
2164 if (param_count==1) { |
|
2165 if(s.len==0) { |
|
2166 LM_ERR("No param <%d>!\n", param_count); |
|
2167 return -1; |
|
2168 } |
|
2169 |
|
2170 if(pv_parse_format(&s,&model)<0 || model==NULL) { |
|
2171 LM_ERR("Wrong format <%s> for param <%d>!\n", s.s, param_count); |
|
2172 return -1; |
|
2173 } |
|
2174 if(model->spec.getf==NULL) { |
|
2175 if(param_count==1) { |
|
2176 if(str2int(&s, (unsigned int*)&model->spec.pvp.pvn.u.isname.name.n)!=0) { |
|
2177 LM_ERR("Wrong value <%s> for param <%d>!\n", |
|
2178 s.s, param_count); |
|
2179 return -1; |
|
2180 } |
|
2181 } |
|
2182 } |
|
2183 *param = (void*)model; |
|
2184 } |
|
2185 |
|
2186 return 0; |
|
2187 } |