all: updated FSF address in GPL text
[sip-router] / modules / ims_isc / mark.c
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 "../../str.h"
48 #include "../../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  */
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  */
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  */
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  */
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  */
200 inline 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  */
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  */
276 inline 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  */
314 int isc_mark_write_psu(struct sip_msg *msg, isc_mark *mark) {
315     struct lump *l = msg->add_rm;
316     int hlen;
317     char * hstr = NULL;
318     const str *regstate, *sescase;
319
320     switch(mark->direction) {
321     case IFC_ORIGINATING_SESSION:
322         regstate = &regstate_reg;
323         sescase = &sescase_orig;
324         break;
325     case IFC_TERMINATING_SESSION:
326         regstate = &regstate_reg;
327         sescase = &sescase_term;
328         break;
329     case IFC_TERMINATING_UNREGISTERED:
330         regstate = &regstate_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 }