lost: adds HELD (RFC6155) and LOST (RFC5222) queries for location-based routing
[kamailio] / src / modules / lost / utilities.c
1 /*\r
2  * lost module utility functions\r
3  *\r
4  * Copyright (C) 2019 Wolfgang Kampichler\r
5  * DEC112, FREQUENTIS AG\r
6  *\r
7  * This file is part of Kamailio, a free SIP server.\r
8  *\r
9  * Kamailio is free software; you can redistribute it and/or modify\r
10  * it under the terms of the GNU General Public License as published by\r
11  * the Free Software Foundation; either version 2 of the License, or\r
12  * (at your option) any later version\r
13  *\r
14  * Kamailio is distributed in the hope that it will be useful,\r
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
17  * GNU General Public License for more details.\r
18  *\r
19  * You should have received a copy of the GNU General Public License\r
20  * along with this program; if not, write to the Free Software\r
21  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA\r
22  *\r
23  */\r
24 \r
25 /*!\r
26  * \file\r
27  * \brief Kamailio lost :: utilities\r
28  * \ingroup lost\r
29  * Module: \ref lost\r
30  */\r
31 \r
32 #include <stdio.h>\r
33 #include <string.h>\r
34 #include <stdlib.h>\r
35 #include <ctype.h>\r
36 #include <time.h>\r
37 \r
38 #include <libxml/xmlmemory.h>\r
39 #include <libxml/parser.h>\r
40 #include "../../core/parser/msg_parser.h"\r
41 #include "../../core/parser/parse_content.h"\r
42 #include "../../core/parser/parse_uri.h"\r
43 #include "../../core/parser/parse_from.h"\r
44 #include "../../core/parser/parse_ppi_pai.h"\r
45 #include "../../core/dprint.h"\r
46 #include "../../core/mem/mem.h"\r
47 #include "../../core/mem/shm_mem.h"\r
48 \r
49 #include "pidf.h"\r
50 #include "utilities.h"\r
51 \r
52 /*\r
53  * lost_trim_content(dest, lgth)\r
54  * removes whitespace that my occur in a content of an xml element\r
55  */\r
56 char *lost_trim_content(char *str, int *lgth)\r
57 {\r
58         char *end;\r
59 \r
60         while(isspace(*str))\r
61                 str++;\r
62 \r
63         if(*str == 0)\r
64                 return NULL;\r
65 \r
66         end = str + strlen(str) - 1;\r
67 \r
68         while(end > str && isspace(*end))\r
69                 end--;\r
70 \r
71         *(end + 1) = '\0';\r
72 \r
73         *lgth = (end + 1) - str;\r
74 \r
75         return str;\r
76 }\r
77 \r
78 /*\r
79  * lost_rand_str(dest, length)\r
80  * creates a random string used as temporary id in a findService request\r
81  */\r
82 void lost_rand_str(char *dest, size_t lgth)\r
83 {\r
84         size_t index;\r
85         char charset[] = "0123456789"\r
86                                          "abcdefghijklmnopqrstuvwxyz"\r
87                                          "ABCDEFGHIJKLMNOPQRSTUVWXYZ";\r
88         srand(time(NULL));\r
89         while(lgth-- > 0) {\r
90                 index = (double)rand() / RAND_MAX * (sizeof charset - 1);\r
91                 *dest++ = charset[index];\r
92         }\r
93         *dest = '\0';\r
94 }\r
95 \r
96 /*\r
97  * lost_free_loc(ptr)\r
98  * freess a location object\r
99  */\r
100 void lost_free_loc(p_loc_t ptr)\r
101 {\r
102         pkg_free(ptr->identity);\r
103         pkg_free(ptr->urn);\r
104         pkg_free(ptr->longitude);\r
105         pkg_free(ptr->latitude);\r
106         pkg_free(ptr);\r
107 }\r
108 \r
109 /*\r
110  * lost_new_loc(urn)\r
111  * creates a new location object in private memory and returns a pointer\r
112  */\r
113 p_loc_t lost_new_loc(str rurn)\r
114 {\r
115         s_loc_t *ptr;\r
116         char *id;\r
117         char *urn;\r
118 \r
119         ptr = (s_loc_t *)pkg_malloc(sizeof(s_loc_t));\r
120         if(ptr == NULL) {\r
121                 LM_ERR("no more private memory\n");\r
122         }\r
123 \r
124         id = (char *)pkg_malloc(RANDSTRSIZE * sizeof(char) + 1);\r
125         if(id == NULL) {\r
126                 LM_ERR("no more private memory\n");\r
127         }\r
128 \r
129         urn = (char *)pkg_malloc(rurn.len + 1);\r
130         if(urn == NULL) {\r
131                 LM_ERR("no more private memory\n");\r
132         }\r
133 \r
134         memset(urn, 0, rurn.len + 1);\r
135         memcpy(urn, rurn.s, rurn.len);\r
136         urn[rurn.len] = '\0';\r
137 \r
138         lost_rand_str(id, RANDSTRSIZE);\r
139 \r
140         ptr->identity = id;\r
141         ptr->urn = urn;\r
142         ptr->longitude = NULL;\r
143         ptr->latitude = NULL;\r
144         ptr->radius = 0;\r
145         ptr->recursive = 0;\r
146 \r
147         return ptr;\r
148 }\r
149 \r
150 /*\r
151  * lost_get_content(node, name, lgth)\r
152  * gets a nodes "name" content and returns string allocated in private memory\r
153  */\r
154 char *lost_get_content(xmlNodePtr node, const char *name, int *lgth)\r
155 {\r
156         xmlNodePtr cur = node;\r
157         char *content;\r
158         char *cnt = NULL;\r
159 \r
160         int len;\r
161 \r
162         *lgth = 0;\r
163         content = xmlNodeGetNodeContentByName(cur, name, NULL);\r
164         len = strlen(content);\r
165 \r
166         cnt = (char *)pkg_malloc((len + 1) * sizeof(char));\r
167         if(cnt == NULL) {\r
168                 LM_ERR("No more private memory\n");\r
169         }\r
170 \r
171         memset(cnt, 0, len + 1);\r
172         memcpy(cnt, content, len);\r
173         cnt[len] = '\0';\r
174 \r
175         *lgth = strlen(cnt);\r
176 \r
177         xmlFree(content);\r
178 \r
179         return cnt;\r
180 }\r
181 \r
182 /*\r
183  * lost_get_property(node, name, lgth)\r
184  * gets a nodes property "name" and returns string allocated in private memory\r
185  */\r
186 char *lost_get_property(xmlNodePtr node, const char *name, int *lgth)\r
187 {\r
188         xmlNodePtr cur = node;\r
189         char *content;\r
190         char *cnt = NULL;\r
191 \r
192         int len;\r
193 \r
194         *lgth = 0;\r
195         content = xmlNodeGetAttrContentByName(cur, name);\r
196         len = strlen(content);\r
197 \r
198         cnt = (char *)pkg_malloc((len + 1) * sizeof(char));\r
199         if(cnt == NULL) {\r
200                 LM_ERR("No more private memory\n");\r
201         }\r
202 \r
203         memset(cnt, 0, len + 1);\r
204         memcpy(cnt, content, len);\r
205         cnt[len] = '\0';\r
206 \r
207         *lgth = strlen(cnt);\r
208 \r
209         xmlFree(content);\r
210 \r
211         return cnt;\r
212 }\r
213 \r
214 /*\r
215  * lost_get_childname(name, lgth)\r
216  * gets a nodes child name and returns string allocated in private memory\r
217  */\r
218 char *lost_get_childname(xmlNodePtr node, const char *name, int *lgth)\r
219 {\r
220         xmlNodePtr cur = node;\r
221         xmlNodePtr parent = NULL;\r
222         xmlNodePtr child = NULL;\r
223 \r
224         char *cnt = NULL;\r
225         int len;\r
226 \r
227         *lgth = 0;\r
228 \r
229         parent = xmlNodeGetNodeByName(cur, name, NULL);\r
230         child = parent->children;\r
231 \r
232         if(child) {\r
233                 len = strlen((char *)child->name);\r
234 \r
235                 cnt = (char *)pkg_malloc((len + 1) * sizeof(char));\r
236                 if(cnt == NULL) {\r
237                         LM_ERR("no more private memory\n");\r
238                 }\r
239 \r
240                 memset(cnt, 0, len + 1);\r
241                 memcpy(cnt, child->name, len);\r
242                 cnt[len] = '\0';\r
243 \r
244                 *lgth = strlen(cnt);\r
245         }\r
246         return cnt;\r
247 }\r
248 \r
249 /*\r
250  * lost_get_geolocation_header(msg, lgth)\r
251  * gets the Geolocation header value and returns string allocated in\r
252  * private memory\r
253  */\r
254 char *lost_get_geolocation_header(struct sip_msg *msg, int *lgth)\r
255 {\r
256         struct hdr_field *hf;\r
257         char *res = NULL;\r
258 \r
259         *lgth = 0;\r
260 \r
261         parse_headers(msg, HDR_EOH_F, 0);\r
262 \r
263         for(hf = msg->headers; hf; hf = hf->next) {\r
264                 if((hf->type == HDR_OTHER_T)\r
265                                 && (hf->name.len == LOST_GEOLOC_HEADER_SIZE - 2)) {\r
266                         /* possible hit */\r
267                         if(strncasecmp(\r
268                                            hf->name.s, LOST_GEOLOC_HEADER, LOST_GEOLOC_HEADER_SIZE)\r
269                                         == 0) {\r
270 \r
271                                 res = (char *)pkg_malloc((hf->body.len + 1) * sizeof(char));\r
272                                 if(res == NULL) {\r
273                                         LM_ERR("no more private memory\n");\r
274                                 } else {\r
275                                         memset(res, 0, hf->body.len + 1);\r
276                                         memcpy(res, hf->body.s, hf->body.len + 1);\r
277                                         res[hf->body.len] = '\0';\r
278 \r
279                                         *lgth = strlen(res);\r
280                                 }\r
281                         } else {\r
282                                 LM_ERR("header '%.*s' length %d\n", hf->body.len, hf->body.s,\r
283                                                 hf->body.len);\r
284                         }\r
285                         break;\r
286                 }\r
287         }\r
288         return res;\r
289 }\r
290 \r
291 /*\r
292  * lost_get_pai_header(msg, lgth)\r
293  * gets the P-A-I header value and returns string allocated in\r
294  * private memory\r
295  */\r
296 char *lost_get_pai_header(struct sip_msg *msg, int *lgth)\r
297 {\r
298         struct hdr_field *hf;\r
299         char *res = NULL;\r
300 \r
301         *lgth = 0;\r
302 \r
303         parse_headers(msg, HDR_PAI_F, 0);\r
304 \r
305         for(hf = msg->headers; hf; hf = hf->next) {\r
306                 if((hf->type == HDR_PAI_T)\r
307                                 && (hf->name.len == LOST_PAI_HEADER_SIZE - 2)) {\r
308                         /* possible hit */\r
309                         if(strncasecmp(hf->name.s, LOST_PAI_HEADER, LOST_PAI_HEADER_SIZE)\r
310                                         == 0) {\r
311                                 res = (char *)pkg_malloc((hf->body.len + 1) * sizeof(char));\r
312                                 if(res == NULL) {\r
313                                         LM_ERR("no more private memory\n");\r
314                                 } else {\r
315 \r
316                                         memset(res, 0, hf->body.len + 1);\r
317                                         memcpy(res, hf->body.s, hf->body.len + 1);\r
318                                         res[hf->body.len] = '\0';\r
319 \r
320                                         *lgth = strlen(res);\r
321                                 }\r
322                         } else {\r
323                                 LM_ERR("header '%.*s' length %d\n", hf->body.len, hf->body.s,\r
324                                                 hf->body.len);\r
325                         }\r
326                         break;\r
327                 }\r
328         }\r
329         return res;\r
330 }\r
331 \r
332 /*\r
333  * lost_get_from_header(msg, lgth)\r
334  * gets the From header value and returns string allocated in\r
335  * private memory\r
336  */\r
337 char *lost_get_from_header(struct sip_msg *msg, int *lgth)\r
338 {\r
339         to_body_t *f_body;\r
340         char *res = NULL;\r
341 \r
342         *lgth = 0;\r
343 \r
344         parse_headers(msg, HDR_FROM_F, 0);\r
345 \r
346         if(msg->from == NULL || get_from(msg) == NULL) {\r
347                 LM_ERR("From header not found\n");\r
348                 return res;\r
349         }\r
350         f_body = get_from(msg);\r
351         res = (char *)pkg_malloc((f_body->uri.len + 1) * sizeof(char));\r
352         if(res == NULL) {\r
353                 LM_ERR("no more private memory\n");\r
354         } else {\r
355                 memset(res, 0, f_body->uri.len + 1);\r
356                 memcpy(res, f_body->uri.s, f_body->uri.len + 1);\r
357                 res[f_body->uri.len] = '\0';\r
358 \r
359                 *lgth = strlen(res);\r
360         }\r
361         return res;\r
362 }\r
363 \r
364 \r
365 /*\r
366  * lost_parse_location_info(node, loc)\r
367  * parses locationResponse and writes results to location object\r
368  */\r
369 int lost_parse_location_info(xmlNodePtr node, p_loc_t loc)\r
370 {\r
371         char bufLat[BUFSIZE];\r
372         char bufLon[BUFSIZE];\r
373         int iRadius;\r
374         char *content = NULL;\r
375         int ret = -1;\r
376 \r
377         xmlNodePtr cur = node;\r
378 \r
379         content = xmlNodeGetNodeContentByName(cur, "pos", NULL);\r
380         if(content) {\r
381                 sscanf(content, "%s %s", bufLat, bufLon);\r
382 \r
383                 loc->latitude = (char *)pkg_malloc(strlen((char *)bufLat) + 1);\r
384                 snprintf(loc->latitude, strlen((char *)bufLat) + 1, "%s",\r
385                                 (char *)bufLat);\r
386 \r
387                 loc->longitude = (char *)pkg_malloc(strlen((char *)bufLon) + 1);\r
388                 snprintf(loc->longitude, strlen((char *)bufLon) + 1, "%s",\r
389                                 (char *)bufLon);\r
390 \r
391                 loc->radius = 0;\r
392                 ret = 0;\r
393         }\r
394 \r
395         content = xmlNodeGetNodeContentByName(cur, "radius", NULL);\r
396         if(content) {\r
397                 iRadius = 0;\r
398 \r
399                 sscanf(content, "%d", &iRadius);\r
400                 loc->radius = iRadius;\r
401                 ret = 0;\r
402         }\r
403 \r
404         if(ret < 0) {\r
405                 LM_ERR("could not parse location information\n");\r
406         }\r
407         return ret;\r
408 }\r
409 \r
410 /*\r
411  * lost_held_location_request(id, lgth)\r
412  * assembles and returns locationRequest string (allocated in private memory)\r
413  */\r
414 char *lost_held_location_request(char *id, int *lgth)\r
415 {\r
416         int buffersize = 0;\r
417 \r
418         char buf[BUFSIZE];\r
419         char *doc = NULL;\r
420 \r
421         xmlChar *xmlbuff = NULL;\r
422         xmlDocPtr request = NULL;\r
423 \r
424         xmlNodePtr ptrLocationRequest = NULL;\r
425         xmlNodePtr ptrLocationType = NULL;\r
426         xmlNodePtr ptrDevice = NULL;\r
427 \r
428         xmlKeepBlanksDefault(1);\r
429         *lgth = 0;\r
430 \r
431         /*\r
432 https://tools.ietf.org/html/rfc6155\r
433 \r
434 <?xml version="1.0" encoding="UTF-8"?>\r
435 <locationRequest xmlns="urn:ietf:params:xml:ns:geopriv:held" responseTime="8">\r
436     <locationType exact="true">geodetic locationURI</locationType>\r
437     <device xmlns="urn:ietf:params:xml:ns:geopriv:held:id">\r
438         <uri>sip:user@example.net</uri>\r
439     </device>\r
440 </locationRequest>\r
441 */\r
442 \r
443         /* create request */\r
444         request = xmlNewDoc(BAD_CAST "1.0");\r
445         /* locationRequest - element */\r
446         ptrLocationRequest = xmlNewNode(NULL, BAD_CAST "locationRequest");\r
447         xmlDocSetRootElement(request, ptrLocationRequest);\r
448         /* properties */\r
449         xmlNewProp(ptrLocationRequest, BAD_CAST "xmlns",\r
450                         BAD_CAST "urn:ietf:params:xml:ns:geopriv:held");\r
451         xmlNewProp(ptrLocationRequest, BAD_CAST "responseTime", BAD_CAST "8");\r
452         /* locationType - element */\r
453         ptrLocationType = xmlNewChild(ptrLocationRequest, NULL,\r
454                         BAD_CAST "locationType", BAD_CAST "geodetic locationURI");\r
455         /* properties */\r
456         xmlNewProp(ptrLocationType, BAD_CAST "exact", BAD_CAST "false");\r
457         /* device - element */\r
458         ptrDevice = xmlNewChild(ptrLocationRequest, NULL, BAD_CAST "device", NULL);\r
459         /* properties */\r
460         xmlNewProp(ptrDevice, BAD_CAST "xmlns",\r
461                         BAD_CAST "urn:ietf:params:xml:ns:geopriv:held:id");\r
462         /* uri - element */\r
463         snprintf(buf, BUFSIZE, "%s", id);\r
464         xmlNewChild(ptrDevice, NULL, BAD_CAST "uri", BAD_CAST buf);\r
465 \r
466         xmlDocDumpFormatMemory(request, &xmlbuff, &buffersize, 0);\r
467 \r
468         doc = (char *)pkg_malloc((buffersize + 1) * sizeof(char));\r
469         if(doc == NULL) {\r
470                 LM_ERR("no more private memory\n");\r
471         }\r
472 \r
473         memset(doc, 0, buffersize + 1);\r
474         memcpy(doc, (char *)xmlbuff, buffersize);\r
475         doc[buffersize] = '\0';\r
476 \r
477         *lgth = strlen(doc);\r
478 \r
479         xmlFree(xmlbuff);\r
480         xmlFreeDoc(request);\r
481 \r
482         return doc;\r
483 }\r
484 \r
485 /*\r
486  * lost_find_service_request(loc, lgth)\r
487  * assembles and returns findService request string (allocated in private memory)\r
488  */\r
489 char *lost_find_service_request(p_loc_t loc, int *lgth)\r
490 {\r
491         int buffersize = 0;\r
492 \r
493         char buf[BUFSIZE];\r
494         char *doc = NULL;\r
495 \r
496         xmlChar *xmlbuff = NULL;\r
497         xmlDocPtr request = NULL;\r
498 \r
499         xmlNodePtr ptrFindService = NULL;\r
500         xmlNodePtr ptrLocation = NULL;\r
501         xmlNodePtr ptrPoint = NULL;\r
502         xmlNodePtr ptrCircle = NULL;\r
503         xmlNodePtr ptrRadius = NULL;\r
504 \r
505         xmlKeepBlanksDefault(1);\r
506         *lgth = 0;\r
507 \r
508         /*\r
509 https://tools.ietf.org/html/rfc5222\r
510 \r
511 <?xml version="1.0" encoding="UTF-8"?>\r
512 <findService\r
513  xmlns="urn:ietf:params:xml:ns:lost1"\r
514  xmlns:p2="http://www.opengis.net/gml"\r
515  serviceBoundary="value"\r
516  recursive="true">\r
517     <location id="6020688f1ce1896d" profile="geodetic-2d">\r
518         <p2:Point id="point1" srsName="urn:ogc:def:crs:EPSG::4326">\r
519             <p2:pos>37.775 -122.422</p2:pos>\r
520         </p2:Point>\r
521     </location>\r
522     <service>urn:service:sos.police</service>\r
523 </findService>\r
524  */\r
525         /* create request */\r
526         request = xmlNewDoc(BAD_CAST "1.0");\r
527         /* findService - element */\r
528         ptrFindService = xmlNewNode(NULL, BAD_CAST "findService");\r
529         xmlDocSetRootElement(request, ptrFindService);\r
530         /* set properties */\r
531         xmlNewProp(ptrFindService, BAD_CAST "xmlns",\r
532                         BAD_CAST "urn:ietf:params:xml:ns:lost1");\r
533         xmlNewProp(ptrFindService, BAD_CAST "xmlns:p2",\r
534                         BAD_CAST "http://www.opengis.net/gml");\r
535         xmlNewProp(\r
536                         ptrFindService, BAD_CAST "serviceBoundary", BAD_CAST "reference");\r
537         xmlNewProp(ptrFindService, BAD_CAST "recursive", BAD_CAST "true");\r
538         /* location - element */\r
539         ptrLocation = xmlNewChild(ptrFindService, NULL, BAD_CAST "location", NULL);\r
540         xmlNewProp(ptrLocation, BAD_CAST "id", BAD_CAST loc->identity);\r
541         xmlNewProp(ptrLocation, BAD_CAST "profile", BAD_CAST "geodetic-2d");\r
542         /* set pos */\r
543         snprintf(buf, BUFSIZE, "%s %s", loc->latitude, loc->longitude);\r
544         /* Point */\r
545         if(loc->radius == 0) {\r
546                 ptrPoint = xmlNewChild(ptrLocation, NULL, BAD_CAST "Point", NULL);\r
547                 xmlNewProp(ptrPoint, BAD_CAST "xmlns",\r
548                                 BAD_CAST "http://www.opengis.net/gml");\r
549                 xmlNewProp(ptrPoint, BAD_CAST "srsName",\r
550                                 BAD_CAST "urn:ogc:def:crs:EPSG::4326");\r
551                 /* pos */\r
552                 xmlNewChild(ptrPoint, NULL, BAD_CAST "pos", BAD_CAST buf);\r
553         } else {\r
554                 /* circle - Point */\r
555                 ptrCircle = xmlNewChild(ptrLocation, NULL, BAD_CAST "gs:Circle", NULL);\r
556                 xmlNewProp(ptrCircle, BAD_CAST "xmlns:gml",\r
557                                 BAD_CAST "http://www.opengis.net/gml");\r
558                 xmlNewProp(ptrCircle, BAD_CAST "xmlns:gs",\r
559                                 BAD_CAST "http://www.opengis.net/pidflo/1.0");\r
560                 xmlNewProp(ptrCircle, BAD_CAST "srsName",\r
561                                 BAD_CAST "urn:ogc:def:crs:EPSG::4326");\r
562                 /* pos */\r
563                 xmlNewChild(ptrCircle, NULL, BAD_CAST "gml:pos", BAD_CAST buf);\r
564                 /* circle - radius */\r
565                 snprintf(buf, BUFSIZE, "%d", loc->radius);\r
566                 ptrRadius = xmlNewChild(\r
567                                 ptrCircle, NULL, BAD_CAST "gs:radius", BAD_CAST buf);\r
568                 xmlNewProp(ptrRadius, BAD_CAST "uom",\r
569                                 BAD_CAST "urn:ogc:def:uom:EPSG::9001");\r
570         }\r
571         /* service - element */\r
572         snprintf(buf, BUFSIZE, "%s", loc->urn);\r
573         xmlNewChild(ptrFindService, NULL, BAD_CAST "service", BAD_CAST buf);\r
574 \r
575         xmlDocDumpFormatMemory(request, &xmlbuff, &buffersize, 0);\r
576 \r
577         doc = (char *)pkg_malloc((buffersize + 1) * sizeof(char));\r
578         if(doc == NULL) {\r
579                 LM_ERR("no more private memory\n");\r
580         }\r
581 \r
582         memset(doc, 0, buffersize + 1);\r
583         memcpy(doc, (char *)xmlbuff, buffersize);\r
584         doc[buffersize] = '\0';\r
585 \r
586         *lgth = strlen(doc);\r
587 \r
588         xmlFree(xmlbuff);\r
589         xmlFreeDoc(request);\r
590 \r
591         return doc;\r
592 }