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 "mark.h"
47 #include "../../core/str.h"
48 #include "../../core/data_lump.h"
49
50 const str psu_hdr_s = str_init("P-Served-User: <%.*s>;sescase=%.*s;regstate=%.*s\r\n");
51 const str sescase_orig = str_init("orig");
52 const str sescase_term = str_init("term");
53 const str regstate_reg = str_init("reg");
54 const str regstate_unreg = str_init("unreg");
55
56 extern int add_p_served_user;
57
58 /** base16 char constants */
59 char *hexchars = "0123456789abcdef";
60 /**
61 * Converts a binary encoded value to its base16 representation.
62 * @param from - buffer containing the input data
63 * @param len - the size of from
64 * @param to - the output buffer !!! must have at least len*2 allocated memory
65 * @returns the written length
66 */
bin_to_base16(char * from,int len,char * to)67 int bin_to_base16(char *from, int len, char *to) {
68 int i, j;
69 for (i = 0, j = 0; i < len; i++, j += 2) {
70 to[j] = hexchars[(((unsigned char) from[i]) >> 4) & 0x0F];
71 to[j + 1] = hexchars[(((unsigned char) from[i])) & 0x0F];
72 }
73 return 2 * len;
74 }
75
76 /** char to hex convertor */
77 #define HEX_VAL(c) ( (c<='9')?(c-'0'):(c-'A'+10) )
78
79 /**
80 * Retrieves the mark from message.
81 * - the marking should be in a header like described before
82 * @param msg - SIP mesage to mark
83 * @param mark - mark to load into
84 * @returns 1 if found, 0 if not
85 */
isc_mark_get_from_msg(struct sip_msg * msg,isc_mark * mark)86 int isc_mark_get_from_msg(struct sip_msg *msg, isc_mark *mark) {
87 struct hdr_field *hdr;
88 rr_t *rr;
89 str x;
90 LM_DBG("isc_mark_get_from_msg: Trying to get the mark from the message \n");
91
92 memset(mark, 0, sizeof(isc_mark));
93
94 parse_headers(msg, HDR_EOH_F, 0);
95 hdr = msg->headers;
96 while (hdr) {
97 if (hdr->type == HDR_ROUTE_T) {
98 if (!hdr->parsed) {
99 if (parse_rr(hdr) < 0) {
100 LM_ERR("isc_mark_get_from_msg: Error while parsing Route HF\n");
101 hdr = hdr->next;
102 continue;
103 }
104 }
105 rr = (rr_t*) hdr->parsed;
106 while (rr) {
107 x = rr->nameaddr.uri;
108 if (x.len >= ISC_MARK_USERNAME_LEN + 1 + isc_my_uri.len
109 && strncasecmp(x.s, ISC_MARK_USERNAME,
110 ISC_MARK_USERNAME_LEN) == 0
111 && strncasecmp(x.s + ISC_MARK_USERNAME_LEN + 1,
112 isc_my_uri.s, isc_my_uri.len) == 0) {
113 LM_DBG("isc_mark_get_from_msg: Found <%.*s>\n", x.len, x.s);
114 isc_mark_get(x, mark);
115 return 1;
116 }
117 rr = rr->next;
118 }
119 }
120 hdr = hdr->next;
121 }
122 return 0;
123 }
124
125 /**
126 * Load the mark from a string.
127 * @param x - string with the mark, as found in the Route header
128 * @param mark - mark to load into
129 */
isc_mark_get(str x,isc_mark * mark)130 void isc_mark_get(str x, isc_mark *mark) {
131 int i, j, k;
132 str aor_hex = { 0, 0 };
133 if (mark->aor.s)
134 pkg_free(mark->aor.s);
135 mark->aor = aor_hex;
136 for (i = 0; i < x.len && x.s[i] != ';'; i++)
137 ;
138 while (i < x.len) {
139 if (x.s[i + 1] == '=') {
140 k = 0;
141 for (j = i + 2; j < x.len && x.s[j] != ';'; j++)
142 k = k * 10 + (x.s[j] - '0');
143 switch (x.s[i]) {
144 case 's':
145 mark->skip = k;
146 break;
147 case 'h':
148 mark->handling = k;
149 break;
150 case 'd':
151 mark->direction = k;
152 break;
153 case 'a':
154 aor_hex.s = x.s + i + 2;
155 aor_hex.len = 0;
156 for (j = i + 2; j < x.len && x.s[j] != ';'; j++)
157 aor_hex.len++;
158 mark->aor.len = aor_hex.len / 2;
159 mark->aor.s = pkg_malloc(mark->aor.len);
160 if (!mark->aor.s) {
161 LM_ERR("isc_mark_get: Error allocating %d bytes\n", mark->aor.len);
162 mark->aor.len = 0;
163 } else {
164 mark->aor.len = base16_to_bin(aor_hex.s, aor_hex.len, mark->aor.s);
165 }
166 break;
167 default:
168 LM_ERR("isc_mark_get: unknown parameter found: %c !\n", x.s[i]);
169 }
170 i = j + 1;
171 } else
172 i++;
173 }
174 }
175
176 /** from base16 char to int */
177 #define HEX_DIGIT(x) \
178 ((x>='0'&&x<='9')?x-'0':((x>='a'&&x<='f')?x-'a'+10:((x>='A'&&x<='F')?x-'A'+10:0)))
179 /**
180 * Converts a hex encoded value to its binary value
181 * @param from - buffer containing the input data
182 * @param len - the size of from
183 * @param to - the output buffer !!! must have at least len/2 allocated memory
184 * @returns the written length
185 */
base16_to_bin(char * from,int len,char * to)186 int base16_to_bin(char *from, int len, char *to) {
187 int i, j;
188 for (i = 0, j = 0; j < len; i++, j += 2) {
189 to[i] = (unsigned char) (HEX_DIGIT(from[j]) << 4 | HEX_DIGIT(from[j+1]));
190 }
191 return i;
192 }
193
194 /**
195 * Deletes the previous marking attempts (lumps).
196 *
197 * @param msg - SIP mesage to mark
198 * @returns 1 on success
199 */
isc_mark_drop_route(struct sip_msg * msg)200 int isc_mark_drop_route(struct sip_msg *msg) {
201 struct lump* lmp, *tmp;
202
203 parse_headers(msg, HDR_EOH_F, 0);
204
205 anchor_lump(msg, msg->headers->name.s - msg->buf, 0, 0);
206
207 LM_DBG("ifc_mark_drop_route: Start --------- \n");
208 lmp = msg->add_rm;
209 while (lmp) {
210 tmp = lmp->before;
211 if (tmp && tmp->op == LUMP_ADD && tmp->u.value
212 && strstr(tmp->u.value, ISC_MARK_USERNAME)) {
213 LM_DBG("ifc_mark_drop_route: Found lump %s ... dropping\n", tmp->u.value);
214 //tmp->op=LUMP_NOP;
215 tmp->len = 0;
216 /*lmp->before = tmp->before;
217 free_lump(tmp); */
218 }
219 lmp = lmp->next;
220 }
221 LM_DBG("ifc_mark_drop_route: ---------- End \n");
222
223 return 1;
224 }
225
226 /**
227 * Mark the message with the given mark.
228 * - old marking attempts are deleted
229 * - marking is performed by inserting the following header
230 * @param msg - SIP mesage to mark
231 * @param match - the current IFC match
232 * @param mark - pointer to the mark
233 * @returns 1 on success or 0 on failure
234 */
isc_mark_set(struct sip_msg * msg,isc_match * match,isc_mark * mark)235 int isc_mark_set(struct sip_msg *msg, isc_match *match, isc_mark *mark) {
236 str route = { 0, 0 };
237 str as = { 0, 0 };
238 char chr_mark[256];
239 char aor_hex[256];
240 int len;
241
242 /* Drop all the old Header Lump "Route: <as>, <my>" */
243 isc_mark_drop_route(msg);
244
245 len = bin_to_base16(mark->aor.s, mark->aor.len, aor_hex);
246 /* Create the Marking */
247 sprintf(chr_mark, "%s@%.*s;lr;s=%d;h=%d;d=%d;a=%.*s", ISC_MARK_USERNAME,
248 isc_my_uri.len, isc_my_uri.s, mark->skip, mark->handling,
249 mark->direction, len, aor_hex);
250 /* Add it in a lump */
251 route.s = chr_mark;
252 route.len = strlen(chr_mark);
253 if (match)
254 as = match->server_name;
255 isc_mark_write_route(msg, &as, &route);
256 if (add_p_served_user) {
257 isc_mark_write_psu(msg, mark);
258 }
259 LM_DBG("isc_mark_set: NEW mark <%s>\n", chr_mark);
260
261 return 1;
262 }
263
264 /**
265 * Inserts the Route header for marking, before first header.
266 * - the marking will be in a header like below
267 * - if the "as" parameter is empty: \code Route: <[iscmark]> \endcode
268 * - else: \code Route: <sip:as@asdomain.net;lr>, <[iscmark]> \endcode
269 *
270 *
271 * @param msg - SIP mesage to mark
272 * @param as - SIP addres of the application server to forward to
273 * @param iscmark - the mark to write
274 * @returns 1 on success, else 0
275 */
isc_mark_write_route(struct sip_msg * msg,str * as,str * iscmark)276 int isc_mark_write_route(struct sip_msg *msg, str *as, str *iscmark) {
277 struct hdr_field *first;
278 struct lump* anchor;
279 str route;
280
281 parse_headers(msg, HDR_EOH_F, 0);
282 first = msg->headers;
283 if (as && as->len) {
284 route.s = pkg_malloc(21+as->len+iscmark->len);
285 sprintf(route.s, "Route: <%.*s;lr>, <%.*s>\r\n", as->len, as->s,
286 iscmark->len, iscmark->s);
287 } else {
288 route.s = pkg_malloc(18+iscmark->len);
289 sprintf(route.s, "Route: <%.*s>\r\n", iscmark->len, iscmark->s);
290 }
291
292 route.len = strlen(route.s);
293 LM_DBG("isc_mark_write_route: <%.*s>\n", route.len, route.s);
294
295 anchor = anchor_lump(msg, first->name.s - msg->buf, 0, HDR_ROUTE_T);
296 if (anchor == NULL) {
297 LM_ERR("isc_mark_write_route: anchor_lump failed\n");
298 return 0;
299 }
300
301 if (!insert_new_lump_before(anchor, route.s, route.len, HDR_ROUTE_T)) {
302 LM_ERR("isc_mark_write_route: error creating lump for header_mark\n");
303 }
304 return 1;
305 }
306
307 /**
308 * Inserts the P-Served-User header on a SIP message
309 * as specified in RFC 5502
310 * @param msg - SIP message
311 * @param mark - the mark containing all required information
312 * @returns 1 on success, else 0
313 */
isc_mark_write_psu(struct sip_msg * msg,isc_mark * mark)314 int isc_mark_write_psu(struct sip_msg *msg, isc_mark *mark) {
315 struct lump *l = msg->add_rm;
316 size_t hlen;
317 char * hstr = NULL;
318 const str *regstate, *sescase;
319
320 switch(mark->direction) {
321 case IFC_ORIGINATING_SESSION:
322 regstate = ®state_reg;
323 sescase = &sescase_orig;
324 break;
325 case IFC_TERMINATING_SESSION:
326 regstate = ®state_reg;
327 sescase = &sescase_term;
328 break;
329 case IFC_TERMINATING_UNREGISTERED:
330 regstate = ®state_unreg;
331 sescase = &sescase_term;
332 break;
333 default:
334 LM_ERR("isc_mark_write_psu: unknown direction: %d\n", mark->direction);
335 return 0;
336 }
337
338 hlen = psu_hdr_s.len - /* 3 "%.*s" */ 12 + mark->aor.len + regstate->len + sescase->len + 1;
339 hstr = pkg_malloc(hlen);
340 if (hstr == NULL) {
341 LM_ERR("isc_mark_write_psu: could not allocate %zu bytes\n", hlen);
342 return 0;
343 }
344
345 int ret = snprintf(hstr, hlen, psu_hdr_s.s,
346 mark->aor.len, mark->aor.s,
347 sescase->len, sescase->s,
348 regstate->len, regstate->s);
349 if (ret >= hlen) {
350 LM_ERR("isc_mark_write_psu: invalid string buffer size: %zu, required: %d\n", hlen, ret);
351 pkg_free(hstr);
352 return 0;
353 }
354
355 LM_DBG("isc_mark_write_psu: %.*s\n", (int)hlen - 3 /* don't print \r\n\0 */, hstr);
356 if (append_new_lump(&l, hstr, hlen - 1, HDR_OTHER_T) == 0) {
357 LM_ERR("isc_mark_write_psu: append_new_lump(%p, \"%.*s\\\r\\n\", %zu, 0) failed\n", &l, (int)hlen - 3 /* don't print \r\n\0 */, hstr, hlen - 1);
358 pkg_free(hstr);
359 return 0;
360 }
361 /* hstr will be deallocated when msg will be destroyed */
362 return 1;
363 }
364