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 = &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 }
364