1 /*
2 * $Id$
3 *
4 * Copyright (C) 2012 Smile Communications, jason.penton@smilecoms.com
5 * Copyright (C) 2012 Smile Communications, richard.good@smilecoms.com
6 *
7 * The initial version of this code was written by Dragos Vingarzan
8 * (dragos(dot)vingarzan(at)fokus(dot)fraunhofer(dot)de and the
9 * Fruanhofer Institute. It was and still is maintained in a separate
10 * branch of the original SER. We are therefore migrating it to
11 * Kamailio/SR and look forward to maintaining it from here on out.
12 * 2011/2012 Smile Communications, Pty. Ltd.
13 * ported/maintained/improved by
14 * Jason Penton (jason(dot)penton(at)smilecoms.com and
15 * Richard Good (richard(dot)good(at)smilecoms.com) as part of an
16 * effort to add full IMS support to Kamailio/SR using a new and
17 * improved architecture
18 *
19 * NB: Alot of this code was originally part of OpenIMSCore,
20 * FhG Fokus.
21 * Copyright (C) 2004-2006 FhG Fokus
22 * Thanks for great work! This is an effort to
23 * break apart the various CSCF functions into logically separate
24 * components. We hope this will drive wider use. We also feel
25 * that in this way the architecture is more complete and thereby easier
26 * to manage in the Kamailio/SR environment
27 *
28 * This file is part of Kamailio, a free SIP server.
29 *
30 * Kamailio is free software; you can redistribute it and/or modify
31 * it under the terms of the GNU General Public License as published by
32 * the Free Software Foundation; either version 2 of the License, or
33 * (at your option) any later version
34 *
35 * Kamailio is distributed in the hope that it will be useful,
36 * but WITHOUT ANY WARRANTY; without even the implied warranty of
37 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
38 * GNU General Public License for more details.
39 *
40 * You should have received a copy of the GNU General Public License
41 * along with this program; if not, write to the Free Software
42 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
43 *
44 */
45
46 #include "checker.h"
47
48 /**
49 * Check if a Service Point Trigger for Header matches the SDP body
50 * @param spt - the service point trigger
51 * @param headers - the headers of the message
52 * @returns - 1 on success, 0 on failure
53 */
isc_check_headers(ims_spt * spt,struct hdr_field * headers)54 static int isc_check_headers(ims_spt *spt, struct hdr_field *headers) {
55 struct hdr_field *i;
56 char c, ch;
57 char buf[256];
58 regex_t header_comp, content_comp;
59 i = headers;
60
61 if (spt->sip_header.header.len >= sizeof(buf)) {
62 LM_ERR("Header name \"%.*s\" is to long to be processed (max %d bytes)\n", spt->sip_header.header.len, spt->sip_header.header.s, (int) (sizeof(buf) - 1));
63 return FALSE;
64 }
65 if (spt->sip_header.content.len >= sizeof(buf)) {
66 LM_ERR("Header content \"%.*s\" is to long to be processed (max %d bytes)\n", spt->sip_header.content.len, spt->sip_header.content.s, (int) (sizeof(buf) - 1));
67 return FALSE;
68 }
69
70 /* compile the regex for header name */
71 memcpy(buf, spt->sip_header.header.s, spt->sip_header.header.len);
72 buf[spt->sip_header.header.len] = 0;
73 if (regcomp(&(header_comp), buf, REG_ICASE | REG_EXTENDED) != 0) {
74 LM_ERR("Error compiling the following regexp for header name: %.*s\n", spt->sip_header.header.len, spt->sip_header.header.s);
75 return FALSE;
76 }
77
78 /* compile the regex for content */
79 memcpy(buf, spt->sip_header.content.s, spt->sip_header.content.len);
80 buf[spt->sip_header.content.len] = 0;
81 if(regcomp(&(content_comp), buf, REG_ICASE | REG_EXTENDED) != 0) {
82 LM_ERR("Error compiling the following regexp for header content: %.*s\n", spt->sip_header.content.len, spt->sip_header.content.s);
83 regfree(&(header_comp));
84 return FALSE;
85 }
86
87 LM_DBG("isc_check_headers: Looking for Header[%.*s(%d)] %.*s \n",
88 spt->sip_header.header.len, spt->sip_header.header.s, spt->sip_header.type, spt->sip_header.content.len, spt->sip_header.content.s);
89 while (i != NULL) {
90 ch = i->name.s[i->name.len];
91 i->name.s[i->name.len] = 0;
92
93 if ((spt->sip_header.type > 0 && spt->sip_header.type == i->type) || //matches known type
94 (regexec(&(header_comp), i->name.s, 0, NULL, 0) == 0) //or matches the name
95 ) {
96
97 i->name.s[i->name.len] = ch;
98 LM_DBG("isc_check_headers: Found Header[%.*s(%d)] %.*s \n",
99 i->name.len, i->name.s, i->type, i->body.len, i->body.s);
100 //if the header should be absent but found it
101
102 if (spt->sip_header.content.s == NULL)
103 if (spt->condition_negated) {
104 regfree(&(header_comp));
105 regfree(&(content_comp));
106 return FALSE;
107 }
108
109 //check regex
110 c = i->body.s[i->body.len];
111 i->body.s[i->body.len] = 0;
112
113 if (regexec(&(content_comp), i->body.s, 0, NULL, 0) == 0) //regex match
114 {
115 regfree(&(header_comp));
116 regfree(&(content_comp));
117 i->body.s[i->body.len] = c;
118 return TRUE;
119 }
120
121 i->body.s[i->body.len] = c;
122 } else
123 i->name.s[i->name.len] = ch;
124 i = i->next;
125 }
126
127 regfree(&(header_comp));
128 regfree(&(content_comp));
129 return FALSE;
130 }
131
132 static str sdp = { "application/sdp", 15 };
133
134 /**
135 * Check if a Service Point Trigger for Session Description matches the SDP body
136 * @param spt - the service point trigger
137 * @param msg - the message
138 * @returns - 1 on success, 0 on failure
139 */
isc_check_session_desc(ims_spt * spt,struct sip_msg * msg)140 static int isc_check_session_desc(ims_spt *spt, struct sip_msg *msg) {
141 int len;
142 char *body, c;
143 char *x;
144 regex_t comp;
145
146 if (msg->content_type == NULL)
147 return FALSE;
148 if (strncasecmp(msg->content_type->body.s, sdp.s,
149 msg->content_type->body.len) != 0)
150 return FALSE;
151 LM_DBG("ifc_check_session_desc: Found Content-Type == appliction/sdp\n");
152 //check for sdp line
153 body = get_body(msg);
154 if (body == 0)
155 return FALSE;
156 if (msg->content_length->parsed == NULL) {
157 parse_content_length(msg->content_length->body.s,
158 msg->content_length->body.s + msg->content_length->body.len,
159 &len);
160 msg->content_length->parsed = (void*) (long) len;
161 } else
162 len = (long) msg->content_length->parsed;
163
164 c = body[len];
165 body[len] = 0;
166 x = pkg_malloc(spt->session_desc.line.len + 2 + spt->session_desc.content.len);
167 sprintf(x, "%.*s=%.*s", spt->session_desc.line.len,
168 spt->session_desc.line.s, spt->session_desc.content.len,
169 spt->session_desc.content.s);
170 /* compile the whole regexp */
171 regcomp(&(comp), x, REG_ICASE | REG_EXTENDED);
172 if (regexec(&(comp), body, 0, NULL, 0) == 0) //regex match
173 {
174 body[len] = c;
175 LM_DBG("ifc_check_session_desc: Found Session Desc. > %s\n", body);
176 pkg_free(x);
177 return TRUE;
178 }
179 body[len] = c;
180 pkg_free(x);
181 return FALSE;
182 }
183
184 /**
185 * Check if a Service Point Trigger for RURI matches the RURI of a message
186 * @param spt - the service point trigger
187 * @param msg - the message
188 * @returns - 1 on success, 0 on failure
189 */
isc_check_ruri(ims_spt * spt,struct sip_msg * msg)190 static int isc_check_ruri(ims_spt *spt, struct sip_msg *msg) {
191 char buf[256];
192 char buf2[256];
193 regex_t comp;
194
195 if (spt->request_uri.len >= sizeof(buf)) {
196 LM_ERR("RURI regexp \"%.*s\" is too long to be compiled (max %d bytes)\n", spt->request_uri.len, spt->request_uri.s, (int) (sizeof(buf) - 1));
197 return FALSE;
198 }
199
200 if (msg->first_line.u.request.uri.len >= sizeof(buf2)) {
201 LM_ERR("RURI \"%.*s\" is too long to be processed (max %d bytes)\n", msg->first_line.u.request.uri.len, msg->first_line.u.request.uri.s, (int) (sizeof(buf2) - 1));
202 return FALSE;
203 }
204
205 /* compile the regex for content */
206 memcpy(buf, spt->request_uri.s, spt->request_uri.len);
207 buf[spt->request_uri.len] = 0;
208 if (regcomp(&(comp), buf, REG_ICASE | REG_EXTENDED) != 0) {
209 LM_ERR("Error compiling the following regexp for RURI content: %.*s\n", spt->request_uri.len, spt->request_uri.s);
210 return FALSE;
211 }
212
213 memcpy(buf2, msg->first_line.u.request.uri.s, msg->first_line.u.request.uri.len);
214 buf2[msg->first_line.u.request.uri.len] = 0;
215 if (regexec(&(comp), buf2, 0, NULL, 0) == 0) //regex match
216 {
217 regfree(&(comp));
218 return TRUE;
219 }
220 regfree(&(comp));
221 return FALSE;
222 }
223
224
225
226 /**
227 * Check if a Service Point Trigger matches a message
228 * @param spt - the service point trigger
229 * @param msg - the message
230 * @param direction - if filter criteria is for originating/terminating/terminating_unregistered
231 * @param registration_type - if the message is initial/re/de registration
232 * @returns - 1 on success, 0 on failure
233 */
isc_check_spt(ims_spt * spt,struct sip_msg * msg,char direction,char registration_type)234 static int isc_check_spt(ims_spt *spt, struct sip_msg *msg, char direction,
235 char registration_type) {
236 int r = FALSE;
237 switch (spt->type) {
238 case IFC_REQUEST_URI:
239 LM_DBG("ifc_check_spt: SPT type %d -> RequestURI == %.*s ?\n",
240 spt->type, spt->request_uri.len, spt->request_uri.s);
241 LM_DBG("ifc_check_spt: Found Request URI %.*s \n",
242 msg->first_line.u.request.uri.len, msg->first_line.u.request.uri.s);
243 r = isc_check_ruri(spt, msg);
244 break;
245 case IFC_METHOD:
246 LM_DBG("ifc_check_spt: SPT type %d -> Method == %.*s ?\n",
247 spt->type, spt->method.len, spt->method.s);
248 LM_DBG("ifc_check_spt: Found method %.*s \n",
249 msg->first_line.u.request.method.len, msg->first_line.u.request.method.s);
250 r = (strncasecmp(spt->method.s, msg->first_line.u.request.method.s,
251 spt->method.len) == 0);
252 if (r && spt->method.len == 8
253 && strncasecmp(spt->method.s, "REGISTER", 8) == 0
254 && !(spt->registration_type == 0
255 || (registration_type & spt->registration_type)))
256 r = 0;
257 break;
258 case IFC_SIP_HEADER:
259 LM_DBG("ifc_check_spt: SPT type %d -> Header[%.*s] %%= %.*s ?\n",
260 spt->type, spt->sip_header.header.len, spt->sip_header.header.s, spt->sip_header.content.len, spt->sip_header.content.s);
261 if (parse_headers(msg, HDR_EOH_F, 0) != 0) {
262 LM_ERR("ifc_checker: can't parse all headers\n");
263 r = FALSE;
264 } else
265 r = isc_check_headers(spt, msg->headers);
266 break;
267 case IFC_SESSION_CASE:
268 LM_DBG("ifc_check_spt: SPT type %d -> Session Case == %d ?\n",
269 spt->type, spt->session_case);
270 LM_DBG("ifc_check_spt: Found session_case %d \n",
271 direction);
272 r = (direction == spt->session_case);
273 break;
274 case IFC_SESSION_DESC:
275 LM_DBG("ifc_check_spt: SPT type %d -> Session Desc.[%.*s] %%= %.*s ?\n",
276 spt->type, spt->session_desc.line.len, spt->session_desc.line.s, spt->session_desc.content.len, spt->session_desc.content.s);
277
278 if (parse_headers(msg, HDR_CONTENTTYPE_F | HDR_CONTENTLENGTH_F, 0) != 0) {
279 LM_ERR("ifc_checker: can't parse all headers \n");
280 r = FALSE;
281 }
282 r = isc_check_session_desc(spt, msg);
283 break;
284 default:
285 LM_ERR("ifc_checker: unknown spt type %d \n", spt->type);
286 return FALSE;
287 }
288 if (spt->condition_negated)
289 return !r;
290 else
291 return r;
292 }
293
294 /**
295 * Check if an entire filter criteria matches a message
296 * @param fc - the filter criteria
297 * @param msg - the message
298 * @param direction - if filter criteria is for originating/terminating/terminating_unregistered
299 * @param registration_type - if the message is initial/re/de registration
300 * @returns - 1 on success, 0 on failure
301 */
isc_check_filter_criteria(ims_filter_criteria * fc,struct sip_msg * msg,char direction,char registration_type)302 static int isc_check_filter_criteria(ims_filter_criteria *fc,
303 struct sip_msg *msg, char direction, char registration_type) {
304
305 int i, partial, total, inside, outside, group;
306 ims_trigger_point *t;
307 t = fc->trigger_point;
308
309 /* If the trigger is missing -> always fwd */
310 if (t == NULL)
311 return TRUE;
312 /* This shouldn't happen */
313 if (msg == NULL)
314 return FALSE;
315
316 if (t->condition_type_cnf == IFC_CNF) { //CNF
317 inside = TRUE;
318 outside = FALSE;
319 partial = FALSE;
320 total = TRUE;
321 } else { //DNF
322 inside = FALSE;
323 outside = TRUE;
324 partial = TRUE;
325 total = FALSE;
326 }
327 LM_DBG("ifc_checker_trigger: Starting expression check: \n");
328 group = t->spt[0].group;
329 for (i = 0; i < t->spt_cnt; i++) {
330 if (group != t->spt[i].group) { //jump to other group
331 total = t->condition_type_cnf == IFC_CNF ?
332 total && partial : total || partial;
333 if (total == outside) {
334 LM_DBG("ifc_checker_trigger: Total compromised, aborting...\n");
335 return outside; // will never match from now on, so get out
336 }
337
338 group = t->spt[i].group;
339 partial = isc_check_spt(t->spt + i, msg, direction,
340 registration_type);
341 LM_DBG("ifc_checker_trigger: - group %d => %d. \n", group, partial);
342 } else { //in same group
343 partial = t->condition_type_cnf == IFC_CNF ? partial || isc_check_spt(t->spt + i, msg, direction, registration_type) : partial
344 && isc_check_spt(t->spt + i, msg, direction, registration_type);
345 }
346
347 if (partial == inside) { // can't change partial from now, so next group
348 LM_DBG("ifc_checker_trigger: - group compromised, skipping to next group\n");
349 while (i + 1 < t->spt_cnt && t->spt[i + 1].group == group)
350 i++;
351 continue;
352 }
353 }
354 total = t->condition_type_cnf == IFC_CNF ?
355 total && partial : total || partial;
356 LM_DBG("ifc_checker_trigger: Check finished => %d\n", total);
357 return total;
358 }
359
360 /**
361 * Create a new matching instance
362 * @param fc - filter criteria that match
363 * @param index - index of the filter that matches
364 * @returns the new isc_match* structure or NULL on error
365 */
isc_new_match(ims_filter_criteria * fc,int index)366 static inline isc_match* isc_new_match(ims_filter_criteria *fc, int index) {
367 isc_match *r = 0;
368
369 r = pkg_malloc(sizeof (isc_match));
370 if (!r) {
371 LM_ERR("isc_new_match(): error allocating %lx bytes\n", sizeof (isc_match));
372 return 0;
373 }
374 memset(r, 0, sizeof(isc_match));
375 if (fc->application_server.server_name.len) {
376 r->server_name.s = pkg_malloc(fc->application_server.server_name.len);
377 if (!r->server_name.s) {
378 LM_ERR("isc_new_match(): error allocating %d bytes\n",
379 fc->application_server.server_name.len);
380 pkg_free(r);
381 return 0;
382 }
383 r->server_name.len = fc->application_server.server_name.len;
384 memcpy(r->server_name.s, fc->application_server.server_name.s,
385 fc->application_server.server_name.len);
386 }
387 r->default_handling = fc->application_server.default_handling;
388 if (fc->application_server.service_info.len) {
389 r->service_info.s = pkg_malloc(fc->application_server.service_info.len);
390 if (!r->service_info.s) {
391 LM_ERR("isc_new_match(): error allocating %d bytes\n",
392 fc->application_server.service_info.len);
393 if (r->server_name.s) {
394 pkg_free(r->server_name.s);
395 }
396 pkg_free(r);
397 return 0;
398 }
399 r->service_info.len = fc->application_server.service_info.len;
400 memcpy(r->service_info.s, fc->application_server.service_info.s,
401 fc->application_server.service_info.len);
402 }
403 r->index = index;
404 r->include_register_request = fc->application_server.include_register_request;
405 r->include_register_response = fc->application_server.include_register_response;
406 return r;
407 }
408
409 /**
410 * Find the next match and fill up the ifc_match structure with the position of the match
411 * @param uri - URI of the user for which to apply the IFC
412 * @param direction - direction of the session
413 * @param skip - how many IFCs to skip because already matched
414 * @param msg - the SIP initial request to check on
415 * @return - TRUE if found, FALSE if none found, end of search space
416 */
isc_checker_find(str uri,char direction,int skip,struct sip_msg * msg,int registered,udomain_t * d)417 isc_match* isc_checker_find(str uri, char direction, int skip,
418 struct sip_msg *msg, int registered, udomain_t *d) {
419 int expires;
420 char registration_type;
421 int i, j, k, cnt, si, sj, next;
422
423 impurecord_t *p;
424 int ret;
425
426 ims_service_profile *sp;
427 ims_filter_criteria *fc;
428 isc_match *r;
429
430 if (skip == 0)
431 LM_DBG("isc_checker_find: starting search\n");
432 else
433 LM_DBG("isc_checker_find: resuming search from %d\n", skip);
434
435 expires = cscf_get_expires(msg);
436 if (!registered)
437 registration_type = IFC_INITIAL_REGISTRATION;
438 else if (expires > 0)
439 registration_type = IFC_RE_REGISTRATION;
440 else
441 registration_type = IFC_DE_REGISTRATION;
442
443 isc_ulb.lock_udomain(d, &uri);
444
445 //need to get the urecord
446 if ((ret = isc_ulb.get_impurecord(d, &uri, &p)) != 0) {
447 isc_ulb.unlock_udomain(d, &uri);
448 LM_ERR("Failure getting IMPU record for [%.*s] - ISC checker find METHOD: [%.*s]\n", uri.len, uri.s, msg->first_line.u.request.method.len, msg->first_line.u.request.method.s);
449 return 0;
450 };
451
452 LM_DBG("isc_checker_find(): got a r_public for the user %.*s\n",
453 uri.len, uri.s);
454 if (!p->s) {
455 LM_DBG("isc_checker_find() : got an user without a subscription\n");
456 //need to free the record somewhere
457 //isc_ulb.release_impurecord(p);
458 //need to do an unlock on the domain somewhere
459 isc_ulb.unlock_udomain(d, &uri);
460 return 0;
461 }
462
463 /* find the starting fc as the skip-th one*/
464 cnt = 0;
465 si = 0;
466 sj = 0;
467
468 LM_DBG("About to try p->s->service_profiles_cnt!! #profiles is %d\n",
469 p->s->service_profiles_cnt);
470 while (si < p->s->service_profiles_cnt) {
471 LM_DBG("About to try p->s->service_profiles[a].filter_criterai_cnt\n");
472 next = cnt + p->s->service_profiles[si].filter_criteria_cnt;
473 if (cnt <= skip && skip < next) {
474 sj = skip - cnt;
475 cnt += sj;
476 break;
477 }
478 cnt = next;
479 si++;
480 }
481
482 LM_DBG("DEBUG ISC: SECOND TIME About to try p->s->service_profiles_cnt!!\n");
483 /* iterate through the rest and check for matches */
484 i = si;
485 while (i < p->s->service_profiles_cnt) {
486 LM_DBG("DEBUG ISC : About to try p->s->service_profiles\n");
487 sp = p->s->service_profiles + i;
488 k = 0;
489 LM_DBG("DEBUG ISC : About to try public identities\n");
490 for (j = 0; j < sp->public_identities_cnt; j++) {
491
492 LM_DBG("DEBUG ISC : About to try WPSI\n");
493 if (p->s->wpsi) {
494 // here i should regexec again!
495 // to check this , but anyway if i already got p
496 // from the get_r_public , that is already checked...
497 // or not if there is no wildcardPSI but ... then ...
498 //isc_check_wpsi_match();
499 k = 1;
500 break;
501
502 } else {
503 if (sp->public_identities[j].public_identity.len == uri.len
504 && strncasecmp(
505 sp->public_identities[j].public_identity.s,
506 uri.s, uri.len) == 0) {
507 k = 1;
508 break;
509 }
510 }
511 }
512
513 if (!k) {/* this sp is not for this id */
514
515 cnt += sp->filter_criteria_cnt;
516 } else {
517
518 for (j = sj; j < sp->filter_criteria_cnt; j++) {
519 fc = sp->filter_criteria + j;
520 if (fc->profile_part_indicator) {
521 if (((registered == IMS_USER_REGISTERED)
522 && (*fc->profile_part_indicator))
523 || ((registered == IMS_USER_UNREGISTERED)
524 && !(*fc->profile_part_indicator))) {
525 LM_DBG("isc_checker_find: this one is not good... ppindicator wrong \n");
526 cnt++;
527 continue;
528 }
529 }
530
531 if (isc_check_filter_criteria(fc, msg, direction, registration_type)) {
532 LM_DBG("isc_checker_find: MATCH -> %.*s (%.*s) handling %d \n",
533 fc->application_server.server_name.len, fc->application_server.server_name.s, fc->application_server.service_info.len, fc->application_server.service_info.s, fc->application_server.default_handling);
534 r = isc_new_match(fc, cnt);
535
536 //need to free the record somewhere
537 //isc_ulb.release_urecord(p);
538 //need to do an unlock on the domain somewhere
539 isc_ulb.unlock_udomain(d, &uri);
540
541 return r;
542 } else {
543 cnt++;
544 continue;
545 }
546 }
547 }
548 i++;
549 sj = 0;
550 }
551 //need to free the record somewhere
552 // isc_ulb.release_urecord(p);
553 //need to do an unlock on the domain somewhere
554 isc_ulb.unlock_udomain(d, &uri);
555
556 return 0;
557 }
558
559 /**
560 * Free up all memory taken by a isc_match.
561 * @param m - match to deallocate
562 */
isc_free_match(isc_match * m)563 void isc_free_match(isc_match *m) {
564 if (m) {
565 if (m->server_name.s)
566 pkg_free(m->server_name.s);
567 if (m->service_info.s)
568 pkg_free(m->service_info.s);
569 pkg_free(m);
570 }
571 LM_DBG("isc_match_free: match position freed\n");
572 }
573 /**
574 * Find if user is registered or not => TRUE/FALSE.
575 * This uses the S-CSCF registrar to get the state.
576 * @param uri - uri of the user to check
577 * @returns the reg_state
578 */
isc_is_registered(str * uri,udomain_t * d)579 int isc_is_registered(str *uri, udomain_t *d) {
580 int result = 0;
581 int ret = 0;
582 impurecord_t *p;
583
584 isc_ulb.lock_udomain(d, uri);
585
586 LM_DBG("Searching in usrloc\n");
587 //need to get the urecord
588 if ((ret = isc_ulb.get_impurecord(d, uri, &p)) != 0) {
589 LM_DBG("no record exists for [%.*s]\n", uri->len, uri->s);
590 isc_ulb.unlock_udomain(d, uri);
591 return result;
592 }
593
594 LM_DBG("Finished searching usrloc\n");
595 result = p->reg_state;
596 isc_ulb.unlock_udomain(d, uri);
597
598 return result;
599 }
600
601