1 /*
2 * Copyright (C) 2006 Otmar Lendl & Klaus Darilion
3 *
4 * Based on the ENUM and domain module.
5 *
6 * This file is part of Kamailio, a free SIP server.
7 *
8 * Kamailio is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version
12 *
13 * Kamailio is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
21 *
22 * History:
23 * --------
24 * 2006-04-20 Initial Version
25 * 2006-09-08 Updated to -02 version, added support for D2P+SIP:std
26 */
27
28
29 /*!
30 * \file
31 * \brief Domain Policy related functions
32 */
33
34
35 #include "domainpolicy_mod.h"
36 #include "domainpolicy.h"
37 #include "../../lib/srdb1/db.h"
38 #include "../../core/parser/parse_uri.h"
39 #include "../../core/parser/parse_from.h"
40 #include "../../core/ut.h"
41 #include "../../core/dset.h"
42 #include "../../core/route.h"
43 #include "../../core/ip_addr.h"
44 #include "../../core/socket_info.h"
45
46 #include "../../core/resolve.h"
47 #include "../../core/strutils.h"
48
49 #define IS_D2PNAPTR(naptr) ((naptr->services_len >= 7) && (!strncasecmp("D2P+SIP", naptr->services, 7)))
50
51 static db1_con_t* db_handle=0;
52 static db_func_t domainpolicy_dbf;
53
54 /*
55 * some helper structs + functions to help build up the AVPs.
56 * We can't immediately store them in AVPs as a later non-matched
57 * rule can result in junking all the AVPs added up to that moment.
58 *
59 * Thus we store them temporarily in an avp_stack.
60 */
61 #define AVPMAXSIZE 120
62 #define AVPSTACKSIZE 32
63
64 struct avp {
65 char att[AVPMAXSIZE];
66 char val[AVPMAXSIZE];
67 };
68
69 struct avp_stack {
70 int succeeded;
71 int i;
72 struct avp avp[AVPSTACKSIZE];
73 };
74
75 /*
76 * Push avp-pair on stack.
77 *
78 * return 0 on failure.
79 */
stack_push(struct avp_stack * stack,char * att,char * val)80 static int stack_push(struct avp_stack *stack, char *att, char *val) {
81 int i;
82 if (stack->i >= (AVPSTACKSIZE-1)) {
83 LM_ERR("exceeded stack size.!\n");
84 return(0);
85 }
86
87 i = (stack->i)++;
88 strncpy(stack->avp[i].att, att, AVPMAXSIZE - 1);
89 strncpy(stack->avp[i].val, val, AVPMAXSIZE - 1);
90
91 stack->succeeded = 1;
92
93 return(1);
94 }
95
96
stack_reset(struct avp_stack * stack)97 static void stack_reset(struct avp_stack *stack) {
98 stack->i = 0;
99 stack->succeeded = 0;
100 }
101
stack_succeeded(struct avp_stack * stack)102 static int stack_succeeded(struct avp_stack *stack) {
103 return(stack->succeeded);
104 }
105
stack_to_avp(struct avp_stack * stack)106 static void stack_to_avp(struct avp_stack *stack) {
107 int j;
108 int_str avp_att;
109 int_str avp_val;
110 unsigned int intval;
111
112 intval=2;
113
114 for(j=0; j< stack->i; j++) {
115 /* AVP names can be integer or string based */
116 LM_DBG("process AVP: name='%s' value='%s'\n",
117 stack->avp[j].att, stack->avp[j].val);
118
119 /* if the second character is a ':', ignore the prefix
120 * this allows specifying the name with i:... or s:... too
121 * Note: the first character is ignored!!!
122 */
123 if ( stack->avp[j].att[0] && stack->avp[j].att[1]==':' ) {
124
125 switch (stack->avp[j].att[0]) {
126 case 'i':
127 case 'I':
128 intval = 1;
129 break;
130 case 's':
131 case 'S':
132 intval = 0;
133 break;
134 default:
135 LM_ERR("invalid type '%c'\n",stack->avp[j].att[0]);
136 continue;
137 }
138 avp_att.s.s = (char *) &(stack->avp[j].att[2]);
139 } else {
140 avp_att.s.s = stack->avp[j].att;
141 }
142 avp_att.s.len = strlen(avp_att.s.s);
143 if (!avp_att.s.len) {
144 LM_ERR("empty AVP name string!\n");
145 continue;
146 }
147
148 avp_val.s.s = stack->avp[j].val;
149 avp_val.s.len = strlen(avp_val.s.s);
150
151 if (intval==1) {
152 /* integer type explicitely forced with i: */
153 if (str2int(&(avp_att.s), &intval) == 0) {
154 /* integer named AVP */
155 if (!intval) {
156 LM_ERR("nameless integer AVP!\n");
157 continue;
158 }
159 avp_att.n = intval;
160 LM_DBG("create integer named AVP <i:%d>\n", avp_att.n);
161 add_avp(AVP_VAL_STR, avp_att, avp_val);
162 continue;
163 } else {
164 LM_ERR("integer AVP is not an integer!\n");
165 continue;
166 }
167 }
168
169 if (intval==2) {
170 /* string type undefined */
171 /* convert name into integer. if it succeeds then it is
172 * an integer named AVP. If it fails, then it is a string
173 * named AVP
174 */
175 if (str2int(&(avp_att.s), &intval) == 0) {
176 /* integer named AVP */
177 if (!intval) {
178 LM_ERR("nameless integer AVP!\n");
179 continue;
180 }
181 avp_att.n = intval;
182 LM_DBG("create integer named AVP <i:%d>\n", avp_att.n);
183 add_avp(AVP_VAL_STR, avp_att, avp_val);
184 continue;
185 } else {
186 LM_DBG("create string named AVP <s:%.*s>\n",
187 avp_att.s.len, ZSW(avp_att.s.s));
188 add_avp(AVP_NAME_STR | AVP_VAL_STR, avp_att, avp_val);
189 continue;
190 }
191 }
192
193 /* intval==0, string type explicitely forced with s: */
194 LM_DBG("create string named AVP <s:%.*s>\n",
195 avp_att.s.len, ZSW(avp_att.s.s));
196 add_avp(AVP_NAME_STR | AVP_VAL_STR, avp_att, avp_val);
197 }
198 }
199
200 /* helper db functions*/
201
202 /*!
203 * \brief Bind the database interface
204 * \param db_url database url
205 * \return -1 on failure, 0 on success
206 */
domainpolicy_db_bind(const str * db_url)207 int domainpolicy_db_bind(const str* db_url)
208 {
209 if (db_bind_mod(db_url, &domainpolicy_dbf )) {
210 LM_CRIT("cannot bind to database module! "
211 "Did you forget to load a database module ?\n");
212 return -1;
213 }
214 return 0;
215 }
216
217
218 /*!
219 * \brief Initialize the database connection
220 * \param db_url database url
221 * \return -1 on failure, 0 on success
222 */
domainpolicy_db_init(const str * db_url)223 int domainpolicy_db_init(const str* db_url)
224 {
225 if (domainpolicy_dbf.init==0){
226 LM_CRIT("unbound database module\n");
227 goto error;
228 }
229 db_handle=domainpolicy_dbf.init(db_url);
230 if (db_handle==0){
231 LM_CRIT("cannot initialize database connection\n");
232 goto error;
233 }
234 return 0;
235 error:
236 return -1;
237 }
238
239
240 /*!
241 * \brief Close the database connection
242 */
domainpolicy_db_close(void)243 void domainpolicy_db_close(void)
244 {
245 if (db_handle && domainpolicy_dbf.close){
246 domainpolicy_dbf.close(db_handle);
247 db_handle=0;
248 }
249 }
250
251
252 /*!
253 * \brief Check the database table version
254 * \param db_url database URL
255 * \return -1 on failure, 0 on success
256 */
domainpolicy_db_ver(const str * db_url)257 int domainpolicy_db_ver(const str* db_url)
258 {
259 db1_con_t* dbh;
260
261 if (domainpolicy_dbf.init==0){
262 LM_CRIT("unbound database\n");
263 return -1;
264 }
265 dbh=domainpolicy_dbf.init(db_url);
266 if (dbh==0){
267 LM_CRIT("null database handler\n");
268 return -1;
269 }
270 if (db_check_table_version(&domainpolicy_dbf, dbh, &domainpolicy_table, DOMAINPOLICY_TABLE_VERSION) < 0) {
271 DB_TABLE_VERSION_ERROR(domainpolicy_table);
272 domainpolicy_dbf.close(dbh);
273 dbh=0;
274 return -1;
275 }
276 domainpolicy_dbf.close(dbh);
277 dbh=0;
278 return 0;
279 }
280
281 /***************************/
282 /*
283 *
284 * code from enum.c
285 *
286 * should be moved to some DDDS support module instead of code-duplication
287 *
288 *
289 */
290
291 /* Parse NAPTR regexp field of the form !pattern!replacement! and return its
292 * components in pattern and replacement parameters. Regexp field starts at
293 * address first and is len characters long.
294 */
parse_naptr_regexp(char * first,int len,str * pattern,str * replacement)295 static inline int parse_naptr_regexp(char* first, int len, str* pattern,
296 str* replacement)
297 {
298 char *second, *third;
299
300 if (len > 0) {
301 if (*first == '!') {
302 second = (char *)memchr((void *)(first + 1), '!', len - 1);
303 if (second) {
304 len = len - (second - first + 1);
305 if (len > 0) {
306 third = memchr(second + 1, '!', len);
307 if (third) {
308 pattern->len = second - first - 1;
309 pattern->s = first + 1;
310 replacement->len = third - second - 1;
311 replacement->s = second + 1;
312 return 1;
313 } else {
314 LM_ERR("third ! missing from regexp\n");
315 return -1;
316 }
317 } else {
318 LM_ERR("third ! missing from regexp\n");
319 return -2;
320 }
321 } else {
322 LM_ERR("second ! missing from regexp\n");
323 return -3;
324 }
325 } else {
326 LM_ERR("first ! missing from regexp\n");
327 return -4;
328 }
329 } else {
330 LM_ERR("regexp missing\n");
331 return -5;
332 }
333 }
334
335
336 /*
337 * Tests if one result record is "greater" that the other. Non-NAPTR records
338 * greater that NAPTR record. An invalid NAPTR record is greater than a
339 * valid one. Valid NAPTR records are compared based on their
340 * (order,preference).
341 *
342 * Naptrs without D2P+SIP service field are greater.
343 *
344 */
naptr_greater(struct rdata * a,struct rdata * b)345 static inline int naptr_greater(struct rdata* a, struct rdata* b)
346 {
347 struct naptr_rdata *na, *nb;
348
349 if (a->type != T_NAPTR) return 1;
350 if (b->type != T_NAPTR) return 0;
351
352 na = (struct naptr_rdata*)a->rdata;
353 if (na == 0) return 1;
354
355 nb = (struct naptr_rdata*)b->rdata;
356 if (nb == 0) return 0;
357
358 if (!IS_D2PNAPTR(na))
359 return 1;
360
361 if (!IS_D2PNAPTR(nb))
362 return 0;
363
364
365 return (((na->order) << 16) + na->pref) >
366 (((nb->order) << 16) + nb->pref);
367 }
368
369
370 /*
371 * Bubble sorts result record list according to naptr (order,preference).
372 */
naptr_sort(struct rdata ** head)373 static inline void naptr_sort(struct rdata** head)
374 {
375 struct rdata *p, *q, *r, *s, *temp, *start;
376
377 /* r precedes p and s points to the node up to which comparisons
378 are to be made */
379
380 s = NULL;
381 start = *head;
382 while ( s != start -> next ) {
383 r = p = start ;
384 q = p -> next ;
385 while ( p != s ) {
386 if ( naptr_greater(p, q) ) {
387 if ( p == start ) {
388 temp = q -> next ;
389 q -> next = p ;
390 p -> next = temp ;
391 start = q ;
392 r = q ;
393 } else {
394 temp = q -> next ;
395 q -> next = p ;
396 p -> next = temp ;
397 r -> next = q ;
398 r = q ;
399 }
400 } else {
401 r = p ;
402 p = p -> next ;
403 }
404 q = p -> next ;
405 if ( q == s ) s = p ;
406 }
407 }
408 *head = start;
409 }
410
411 /*
412 * input: rule straight from the DDDS + avp-stack.
413 *
414 * output: adds found rules to the stack and return
415 * 1 on success
416 * 0 on failure
417 */
check_rule(str * rule,char * service,int service_len,struct avp_stack * stack)418 static int check_rule(str *rule, char *service, int service_len, struct avp_stack *stack) {
419
420 /* for the select */
421 db_key_t keys[2];
422 db_val_t vals[2];
423 db_key_t cols[4];
424 db1_res_t* res;
425 db_row_t* row;
426 db_val_t* val;
427 int i;
428 char *type;
429 int type_len;
430
431 LM_INFO("checking for '%.*s'.\n", rule->len, ZSW(rule->s));
432
433 if ((service_len != 11) || (strncasecmp("d2p+sip:fed", service, 11) &&
434 strncasecmp("d2p+sip:std", service, 11) && strncasecmp("d2p+sip:dom", service, 11))) {
435 LM_ERR("can only cope with d2p+sip:fed, d2p+sip:std,and d2p+sip:dom "
436 "for now (and not %.*s).\n", service_len, service);
437 return(0);
438 }
439
440 type = service + 8;
441 type_len = service_len - 8;
442
443 if (domainpolicy_dbf.use_table(db_handle, &domainpolicy_table) < 0) {
444 LM_ERR("failed to domainpolicy table\n");
445 return -1;
446 }
447
448 keys[0]=&domainpolicy_col_rule;
449 keys[1]=&domainpolicy_col_type;
450 cols[0]=&domainpolicy_col_rule;
451 cols[1]=&domainpolicy_col_type;
452 cols[2]=&domainpolicy_col_att;
453 cols[3]=&domainpolicy_col_val;
454
455 VAL_TYPE(&vals[0]) = DB1_STR;
456 VAL_NULL(&vals[0]) = 0;
457 VAL_STR(&vals[0]).s = rule->s;
458 VAL_STR(&vals[0]).len = rule->len;
459
460 VAL_TYPE(&vals[1]) = DB1_STR;
461 VAL_NULL(&vals[1]) = 0;
462 VAL_STR(&vals[1]).s = type;
463 VAL_STR(&vals[1]).len = type_len;
464
465 /*
466 * SELECT rule, att, val from domainpolicy where rule = "..."
467 */
468
469 if (domainpolicy_dbf.query(db_handle, keys, 0, vals, cols, 2, 4, 0, &res) < 0
470 ) {
471 LM_ERR("querying database\n");
472 return -1;
473 }
474
475 LM_INFO("querying database OK\n");
476
477 if (RES_ROW_N(res) == 0) {
478 LM_DBG("rule '%.*s' is not know.\n",
479 rule->len, ZSW(rule->s));
480 domainpolicy_dbf.free_result(db_handle, res);
481 return 0;
482 } else {
483 LM_DBG("rule '%.*s' is known\n", rule->len, ZSW(rule->s));
484
485 row = RES_ROWS(res);
486
487 for(i = 0; i < RES_ROW_N(res); i++) {
488 if (ROW_N(row + i) != 4) {
489 LM_ERR("unexpected cell count\n");
490 return(-1);
491 }
492
493 val = ROW_VALUES(row + i);
494
495 if ((VAL_TYPE(val) != DB1_STRING) ||
496 (VAL_TYPE(val+1) != DB1_STRING) ||
497 (VAL_TYPE(val+2) != DB1_STRING) ||
498 (VAL_TYPE(val+3) != DB1_STRING)) {
499 LM_ERR("unexpected cell types\n");
500 return(-1);
501 }
502
503 if (VAL_NULL(val+2) || VAL_NULL(val+3)) {
504 LM_INFO("db returned NULL values. Fine with us.\n");
505 continue;
506 }
507
508 LM_INFO("DB returned %s/%s \n",VAL_STRING(val+2),VAL_STRING(val+3));
509
510
511 if (!stack_push(stack, (char *) VAL_STRING(val+2),
512 (char *) VAL_STRING(val+3))) {
513 return(-1);
514 }
515 }
516 domainpolicy_dbf.free_result(db_handle, res);
517 return 1;
518 }
519 }
520
dp_can_connect_str(str * domain,int rec_level)521 int dp_can_connect_str(str *domain, int rec_level) {
522 struct rdata* head;
523 struct rdata* l;
524 struct naptr_rdata* naptr;
525 struct naptr_rdata* next_naptr;
526 int ret;
527 str newdomain;
528 char uri[MAX_URI_SIZE];
529 struct avp_stack stack;
530 int last_order = -1;
531 int failed = 0;
532 int found_anything = 0;
533
534 str pattern, replacement, result;
535
536 stack_reset(&stack);
537 /* If we're in a recursive call, set the domain-replacement */
538 if ( rec_level > 0 ) {
539 stack_push(&stack, domain_replacement_name.s.s, domain->s);
540 stack.succeeded = 0;
541 }
542
543 if (rec_level > MAX_DDDS_RECURSIONS) {
544 LM_ERR("too many indirect NAPTRs. Aborting at %.*s.\n", domain->len,
545 ZSW(domain->s));
546 return(DP_DDDS_RET_DNSERROR);
547 }
548
549 LM_INFO("looking up Domain itself: %.*s\n",domain->len, ZSW(domain->s));
550 ret = check_rule(domain,"D2P+sip:dom", 11, &stack);
551
552 if (ret == 1) {
553 LM_INFO("found a match on domain itself\n");
554 stack_to_avp(&stack);
555 return(DP_DDDS_RET_POSITIVE);
556 } else if (ret == 0) {
557 LM_INFO("no match on domain itself.\n");
558 stack_reset(&stack);
559 /* If we're in a recursive call, set the domain-replacement */
560 if ( rec_level > 0 ) {
561 stack_push(&stack, domain_replacement_name.s.s, (char *) domain->s);
562 stack.succeeded = 0;
563 }
564 } else {
565 return(DP_DDDS_RET_DNSERROR); /* actually: DB error */
566 }
567
568 LM_INFO("doing DDDS with %.*s\n",domain->len, ZSW(domain->s));
569 head = get_record(domain->s, T_NAPTR, RES_ONLY_TYPE);
570 if (head == 0) {
571 LM_NOTICE("no NAPTR record found for %.*s.\n",
572 domain->len, ZSW(domain->s));
573 return(DP_DDDS_RET_NOTFOUND);
574 }
575
576 LM_DBG("found the following NAPTRs: \n");
577 for (l = head; l; l = l->next) {
578 if (l->type != T_NAPTR) {
579 LM_DBG("found non-NAPTR record.\n");
580 continue; /*should never happen*/
581 }
582 naptr = (struct naptr_rdata*)l->rdata;
583 if (naptr == 0) {
584 LM_CRIT("null rdata\n");
585 continue;
586 }
587 LM_DBG("order %u, pref %u, flen %u, flags '%.*s', slen %u, "
588 "services '%.*s', rlen %u, regexp '%.*s', repl '%s'\n",
589 naptr->order, naptr->pref,
590 naptr->flags_len, (int)(naptr->flags_len), ZSW(naptr->flags),
591 naptr->services_len,
592 (int)(naptr->services_len), ZSW(naptr->services), naptr->regexp_len,
593 (int)(naptr->regexp_len), ZSW(naptr->regexp),
594 ZSW(naptr->repl)
595 );
596 }
597
598
599 LM_DBG("sorting...\n");
600 naptr_sort(&head);
601
602 for (l = head; l; l = l->next) {
603
604 if (l->type != T_NAPTR) continue; /*should never happen*/
605 naptr = (struct naptr_rdata*)l->rdata;
606 if (naptr == 0) {
607 LM_CRIT("null rdata\n");
608 continue;
609 }
610
611 LM_DBG("considering order %u, pref %u, flen %u, flags '%.*s', slen %u, "
612 "services '%.*s', rlen %u, regexp '%.*s', repl '%s'\n",
613 naptr->order, naptr->pref,
614 naptr->flags_len, (int)(naptr->flags_len), ZSW(naptr->flags),
615 naptr->services_len,
616 (int)(naptr->services_len), ZSW(naptr->services), naptr->regexp_len,
617 (int)(naptr->regexp_len), ZSW(naptr->regexp),
618 ZSW(naptr->repl)
619 );
620
621 /*
622 * New order? then we check whether the had success during the last one.
623 * If yes, we can leave the loop.
624 */
625 if (last_order != naptr->order) {
626 last_order = naptr->order;
627 failed = 0;
628
629 if (stack_succeeded(&stack)) {
630 LM_INFO("we don't need to consider further orders "
631 "(starting with %d).\n",last_order);
632 break;
633 }
634 } else if (failed) {
635 LM_INFO("order %d has already failed.\n",last_order);
636 continue;
637 }
638
639
640 /*
641 * NAPTRs we don't care about
642 */
643 if (!IS_D2PNAPTR(naptr))
644 continue;
645
646 /*
647 * once we've been here, don't return DP_DDDS_RET_NOTFOUND
648 */
649 found_anything = 1;
650
651 next_naptr = NULL;
652 if (l->next && (l->next->type == T_NAPTR)) {
653 next_naptr = (struct naptr_rdata*)l->next->rdata;
654 }
655
656 /*
657 * Non-terminal?
658 */
659 if ((naptr->services_len == 7) && !strncasecmp("D2P+SIP", naptr->services,7) && (naptr->flags_len == 0)){
660 LM_INFO("found non-terminal NAPTR\n");
661
662 /*
663 * This needs to be the only record with this order.
664 */
665 if (next_naptr && (next_naptr->order == naptr->order) && IS_D2PNAPTR(next_naptr)) {
666 LM_ERR("non-terminal NAPTR needs to be the only one "
667 "with this order %.*s.\n", domain->len, ZSW(domain->s));
668
669 return(DP_DDDS_RET_DNSERROR);
670 }
671
672 newdomain.s = naptr->repl;
673 newdomain.len = strlen(naptr->repl);
674
675 ret = dp_can_connect_str(&newdomain, rec_level + 1);
676
677 if (ret == DP_DDDS_RET_POSITIVE) /* succeeded, we're done. */
678 return(ret);
679
680 if (ret == DP_DDDS_RET_NEGATIVE) /* found rules, did not work */
681 continue; /* look for more rules */
682
683 if (ret == DP_DDDS_RET_DNSERROR) /* errors during lookup */
684 return(ret); /* report them */
685
686 if (ret == DP_DDDS_RET_NOTFOUND) /* no entries in linked domain? */
687 return(ret); /* ok, fine. go with that */
688
689 continue; /* not reached */
690 }
691
692 /*
693 * wrong kind of terminal
694 */
695 if ((naptr->flags_len != 1) || (tolower(naptr->flags[0]) != 'u')) {
696 LM_ERR("terminal NAPTR needs flag = 'u' and not '%.*s'.\n",
697 (int)naptr->flags_len, ZSW(naptr->flags));
698 /*
699 * It's not that clear what we should do now: Ignore this records or regard it as failed.
700 * We go with "ignore" for now.
701 */
702 continue;
703 }
704
705 if (parse_naptr_regexp(&(naptr->regexp[0]), naptr->regexp_len,
706 &pattern, &replacement) < 0) {
707 LM_ERR("parsing of NAPTR regexp failed\n");
708 continue;
709 }
710 result.s = &(uri[0]);
711 result.len = MAX_URI_SIZE;
712
713 /* Avoid making copies of pattern and replacement */
714 pattern.s[pattern.len] = (char)0;
715 replacement.s[replacement.len] = (char)0;
716 if (reg_replace(pattern.s, replacement.s, domain->s,
717 &result) < 0) {
718 pattern.s[pattern.len] = '!';
719 replacement.s[replacement.len] = '!';
720 LM_ERR("regexp replace failed\n");
721 continue;
722 }
723 LM_INFO("resulted in replacement: '%.*s'\n", result.len, ZSW(result.s));
724 pattern.s[pattern.len] = '!';
725 replacement.s[replacement.len] = '!';
726
727 ret = check_rule(&result,naptr->services,naptr->services_len, &stack);
728
729 if (ret == 1) {
730 LM_INFO("positive return\n");
731 } else if (ret == 0) {
732 LM_INFO("check_rule failed.\n");
733 stack_reset(&stack);
734 /* If we're in a recursive call, set the domain-replacement */
735 if ( rec_level > 0 ) {
736 stack_push(&stack, domain_replacement_name.s.s, (char *) domain->s);
737 stack.succeeded = 0;
738 }
739 failed = 1;
740 } else {
741 return(DP_DDDS_RET_DNSERROR);
742 }
743 }
744
745 if (stack_succeeded(&stack)) {
746 LM_INFO("calling stack_to_avp.\n");
747 stack_to_avp(&stack);
748 return(DP_DDDS_RET_POSITIVE);
749 }
750
751 LM_INFO("returning %d.\n",
752 (found_anything ? DP_DDDS_RET_NEGATIVE : DP_DDDS_RET_NOTFOUND));
753 return( found_anything ? DP_DDDS_RET_NEGATIVE : DP_DDDS_RET_NOTFOUND );
754 }
755
756
757 /*!
758 * \brief Check if host in Request URI has DP-DDDS NAPTRs and if we can connect to them
759 * \param _msg SIP message
760 * \param _s1 unused
761 * \param _s2 unused
762 * \return negative on failure, positive on success
763 */
dp_can_connect(struct sip_msg * _msg,char * _s1,char * _s2)764 int dp_can_connect(struct sip_msg* _msg, char* _s1, char* _s2) {
765
766 static char domainname[MAX_DOMAIN_SIZE];
767 str domain;
768 int ret;
769
770 if (!is_route_type(REQUEST_ROUTE)) {
771 LM_ERR("unsupported route type\n");
772 return -1;
773 }
774
775 if (parse_sip_msg_uri(_msg) < 0) {
776 LM_ERR("failed to parse R-URI\n");
777 return -1;
778 }
779
780 if (_msg->parsed_uri.host.len >= MAX_DOMAIN_SIZE) {
781 LM_ERR("domain buffer to small\n");
782 return -1;
783 }
784
785 /* copy domain into static buffer as later we sometimes need \0
786 * terminated strings
787 */
788 domain.s = (char *) &(domainname[0]);
789 domain.len = _msg->parsed_uri.host.len;
790 memcpy(domain.s, _msg->parsed_uri.host.s, domain.len);
791 domainname[domain.len] = '\0';
792
793 LM_DBG("domain is %.*s.\n", domain.len, ZSW(domain.s));
794
795 ret = dp_can_connect_str(&domain,0);
796 LM_DBG("returning %d.\n", ret);
797 return(ret);
798 }
799
800
801 /*!
802 * \brief Apply DP-DDDS policy to current SIP message
803 *
804 * Apply DP-DDDS policy to current SIP message. This means
805 * build a new destination URI from the policy AVP and export it
806 * as AVP. Then in kamailio.cfg this new target AVP can be pushed
807 * into the destination URI $duri
808 * \param _msg SIP message
809 * \param _s1 unused
810 * \param _s2 unused
811 * \return negative on failure, positive on succes
812 */
dp_apply_policy(struct sip_msg * _msg,char * _s1,char * _s2)813 int dp_apply_policy(struct sip_msg* _msg, char* _s1, char* _s2) {
814
815 str *domain;
816 int_str val;
817 struct usr_avp *avp;
818
819 char duri[MAX_URI_SIZE];
820 str duri_str;
821 int len, didsomething;
822 char *at; /* pointer to current location inside duri */
823
824 str host;
825 int port, proto;
826 struct socket_info* si;
827
828 if (!is_route_type(REQUEST_ROUTE)) {
829 LM_ERR("unsupported route type\n");
830 return -1;
831 }
832
833 /*
834 * set the send_socket
835 */
836
837 /* search for send_socket AVP */
838 avp = search_first_avp(send_socket_avp_name_str, send_socket_name, &val, 0);
839 if (avp) {
840 if ( !(avp->flags&AVP_VAL_STR) || !val.s.s || !val.s.len) {
841 LM_ERR("empty or non-string send_socket_avp, "
842 "return with error ...\n");
843 return -1;
844 }
845 LM_DBG("send_socket_avp found = '%.*s'\n", val.s.len, ZSW(val.s.s));
846 /* parse phostport - AVP str val is asciiz */
847 /* FIXME: This code relies on the fact that the string value of an AVP
848 * is zero terminated, which may or may not be true in the future */
849 if (parse_phostport(val.s.s, &(host.s), &(host.len), &port, &proto)) {
850 LM_ERR("could not parse send_socket, return with error ...\n");
851 return -1;
852 }
853 si = grep_sock_info( &host, (unsigned short) port, (unsigned short) proto);
854 if (si) {
855 set_force_socket(_msg, si);
856 } else {
857 LM_WARN("could not find socket for"
858 "send_socket '%.*s'\n", val.s.len, ZSW(val.s.s));
859 }
860 } else {
861 LM_DBG("send_socket_avp not found\n");
862 }
863
864 /*
865 * set the destination URI
866 */
867
868 didsomething = 0; /* if no AVP is set, there is no need to set the DURI in the end */
869
870 if (parse_sip_msg_uri(_msg) < 0) {
871 LM_ERR("failed to parse R-URI\n");
872 return -1;
873 }
874
875 at = (char *)&(duri[0]);
876 len = 0;
877 if ( (len + 4) > MAX_URI_SIZE) {
878 LM_ERR("duri buffer to small to add uri schema\n");
879 return -1;
880 }
881 memcpy(at, "sip:", 4); at = at + 4; len = len + 4;
882
883 domain = &(_msg->parsed_uri.host);
884 LM_DBG("domain is %.*s.\n", domain->len, ZSW(domain->s));
885
886 /* search for prefix and add it to duri buffer */
887 avp = search_first_avp(domain_prefix_avp_name_str, domain_prefix_name, &val, 0);
888 if (avp) {
889 if ( !(avp->flags&AVP_VAL_STR) || !val.s.s || !val.s.len) {
890 LM_ERR("empty or non-string domain_prefix_avp, return with error ...\n");
891 return -1;
892 }
893 LM_DBG("domain_prefix_avp found = '%.*s'\n", val.s.len, ZSW(val.s.s));
894 if ( (len + val.s.len +1) > MAX_URI_SIZE) {
895 LM_ERR("duri buffer to small to add domain prefix\n");
896 return -1;
897 }
898 memcpy(at, val.s.s, val.s.len); at = at + val.s.len;
899 *at = '.'; at = at + 1; /* add . as delimiter between prefix and domain */
900 didsomething = 1;
901 } else {
902 LM_DBG("domain_prefix_avp not found\n");
903 }
904
905
906 /* add domain to duri buffer */
907 avp = search_first_avp(domain_replacement_avp_name_str, domain_replacement_name, &val, 0);
908 if (avp) {
909 if ( !(avp->flags&AVP_VAL_STR) || !val.s.s || !val.s.len) {
910 LM_ERR("empty or non-string domain_replacement_avp, return with"
911 "error ...\n");
912 return -1;
913 }
914 LM_DBG("domain_replacement_avp found='%.*s'\n",val.s.len, ZSW(val.s.s));
915 if ( (len + val.s.len +1) > MAX_URI_SIZE) {
916 LM_ERR("duri buffer to small to add domain replacement\n");
917 return -1;
918 }
919 memcpy(at, val.s.s, val.s.len); at = at + val.s.len;
920 didsomething = 1;
921 } else {
922 LM_DBG("domain_replacement_avp not found, using original domain '"
923 "%.*s'\n",domain->len, domain->s);
924 if ( (len + domain->len) > MAX_URI_SIZE) {
925 LM_ERR("duri buffer to small to add domain\n");
926 return -1;
927 }
928 memcpy(at, domain->s, domain->len); at = at + domain->len;
929 }
930
931 /* search for suffix and add it to duri buffer */
932 avp = search_first_avp(domain_suffix_avp_name_str, domain_suffix_name, &val, 0);
933 if (avp) {
934 if ( !(avp->flags&AVP_VAL_STR) || !val.s.s || !val.s.len) {
935 LM_ERR("empty or non-string domain_suffix_avp,return with error .."
936 "\n");
937 return -1;
938 }
939 LM_DBG("domain_suffix_avp found = '%.*s'\n", val.s.len, ZSW(val.s.s));
940 if ( (len + val.s.len + 1) > MAX_URI_SIZE) {
941 LM_ERR("duri buffer to small to add domain suffix\n");
942 return -1;
943 }
944 *at = '.'; at = at + 1; /* add . as delimiter between domain and suffix */
945 memcpy(at, val.s.s, val.s.len); at = at + val.s.len;
946 didsomething = 1;
947 } else {
948 LM_DBG("domain_suffix_avp not found\n");
949 }
950
951 /* search for port override and add it to duri buffer */
952 avp = search_first_avp(port_override_avp_name_str, port_override_name, &val, 0);
953 if (avp) {
954 if ( !(avp->flags&AVP_VAL_STR) || !val.s.s || !val.s.len) {
955 LM_ERR("empty or non-string port_override_avp, return with error ...\n");
956 return -1;
957 }
958 LM_DBG("port_override_avp found = '%.*s'\n", val.s.len, ZSW(val.s.s));
959 /* We do not check if the port is valid */
960 if ( (len + val.s.len + 1) > MAX_URI_SIZE) {
961 LM_ERR("duri buffer to small to add domain suffix\n");
962 return -1;
963 }
964 *at = ':'; at = at + 1; /* add : as delimiter between domain and port */
965 memcpy(at, val.s.s, val.s.len); at = at + val.s.len;
966 didsomething = 1;
967 } else {
968 LM_DBG("port_override_avp not found, using original port\n");
969 if (_msg->parsed_uri.port.len) {
970 LM_DBG("port found in RURI, reusing it for DURI\n");
971 if ( (len + _msg->parsed_uri.port.len + 1) > MAX_URI_SIZE) {
972 LM_ERR("duri buffer to small to copy port\n");
973 return -1;
974 }
975 *at = ':'; at = at + 1;
976 /* add : as delimiter between domain and port */
977 memcpy(at, _msg->parsed_uri.port.s, _msg->parsed_uri.port.len);
978 at = at + _msg->parsed_uri.port.len;
979 } else {
980 LM_DBG("port not found in RURI, no need to copy it to DURI\n");
981 }
982 }
983
984 /* search for transport override and add it to duri buffer */
985 avp = search_first_avp(transport_override_avp_name_str, transport_override_name, &val, 0);
986 if (avp) {
987 if ( !(avp->flags&AVP_VAL_STR) || !val.s.s || !val.s.len) {
988 LM_ERR("empty or non-string transport_override_avp, "
989 "return with error ...\n");
990 return -1;
991 }
992 LM_DBG("transport_override_avp found='%.*s'\n",val.s.len, ZSW(val.s.s));
993
994 if ( (len + val.s.len + 11) > MAX_URI_SIZE) {
995 LM_ERR("duri buffer to small to add transport override\n");
996 return -1;
997 }
998 /* add : as transport parameter to duri; NOTE: no checks if transport parameter is valid */
999 memcpy(at, ";transport=", 11); at = at + 11;
1000 memcpy(at, val.s.s, val.s.len); at = at + val.s.len;
1001 didsomething = 1;
1002 } else {
1003 LM_DBG("transport_override_avp not found, using original transport\n");
1004 if (_msg->parsed_uri.transport.len) {
1005 LM_DBG("transport found in RURI, reusing it for DURI\n");
1006 if ( (len + _msg->parsed_uri.transport.len + 1) > MAX_URI_SIZE) {
1007 LM_ERR("duri buffer to small to copy transport\n");
1008 return -1;
1009 }
1010 *at = ';'; at = at + 1; /* add : as delimiter between domain and port */
1011 memcpy(at, _msg->parsed_uri.transport.s, _msg->parsed_uri.transport.len); at = at + _msg->parsed_uri.transport.len;
1012 } else {
1013 LM_DBG("transport not found in RURI, no need to copy it to DURI\n");
1014 }
1015 }
1016
1017 /* write new target DURI into DURI */
1018 if (didsomething == 0) {
1019 LM_DBG("no domainpolicy AVP set, no need to push new DURI\n");
1020 return 2;
1021 }
1022 duri_str.s = (char *)&(duri[0]);
1023 duri_str.len = at - duri_str.s;
1024 LM_DBG("new DURI is '%.*s'\n",duri_str.len, ZSW(duri_str.s));
1025 if(set_dst_uri(_msg, &duri_str)<0) {
1026 LM_ERR("failed to se dst uri\n");
1027 return -1;
1028 }
1029 /* dst_uri changes, so it makes sense to re-use the current uri for
1030 forking */
1031 ruri_mark_new(); /* re-use uri for serial forking */
1032
1033 return 1;
1034 }
1035
1036