From 356fba18ac5eef25e67a2357b4cdcd9255810094 Mon Sep 17 00:00:00 2001 From: Wolfgang Kampichler Date: Mon, 15 Mar 2021 14:18:57 +0100 Subject: [PATCH] lost: new features, attributes and a new function to dereference location - features: LoST redirect, dynamic HELD url resolving (#2574), LoST NAPTR, POST request to dereference loation - attributes: reponse_time (-1:emergencyDispatch, 0:emergencyRouting, >0[ms]); post_request (POST method to dereference location #2641); recursion (yes/no); location_profile (PIDF/LO profile selection: first/last/geo/civic); verbose (detailed LoST response as log INFO); geoheader_type (filter schema: any/cid/http/https); geoheader_order (first/last) - function: lost_held_dereference (specific function to dereference location using POST method); attributes are url (r), resp.-time (r), resp.-type (r), pidf (r/w) and error (r/w) - general: The extension of the module allows dynamic querying of LIS/HELD and LOST services via NAPTR lookup. In the case of LOST, a redirect response is evaluated. In case a lost_held_request (used to connect to a LIS via POST or GET) is passed with an empty string ("") for the connection parameter, then P-A-I or From header value hostnames are used for NAPTR lookup to get a corresponding service. --- src/modules/lost/functions.c | 902 ++++++++++++++++++++++--------- src/modules/lost/functions.h | 3 + src/modules/lost/lost.c | 88 +++- src/modules/lost/naptr.c | 255 +++++++++ src/modules/lost/naptr.h | 38 ++ src/modules/lost/pidf.c | 5 +- src/modules/lost/response.c | 991 +++++++++++++++++++++++++++++++++++ src/modules/lost/response.h | 131 +++++ src/modules/lost/utilities.c | 447 +++++++++++++--- src/modules/lost/utilities.h | 27 +- 10 files changed, 2542 insertions(+), 345 deletions(-) create mode 100644 src/modules/lost/naptr.c create mode 100644 src/modules/lost/naptr.h create mode 100644 src/modules/lost/response.c create mode 100644 src/modules/lost/response.h diff --git a/src/modules/lost/functions.c b/src/modules/lost/functions.c index 7e62a8dd7a3..f11c1545879 100644 --- a/src/modules/lost/functions.c +++ b/src/modules/lost/functions.c @@ -1,7 +1,7 @@ /* * lost module functions * - * Copyright (C) 2020 Wolfgang Kampichler + * Copyright (C) 2021 Wolfgang Kampichler * DEC112, FREQUENTIS AG * * This file is part of Kamailio, a free SIP server. @@ -44,6 +44,8 @@ #include "pidf.h" #include "utilities.h" +#include "response.h" +#include "naptr.h" #define LOST_SUCCESS 200 #define LOST_CLIENT_ERROR 400 @@ -52,12 +54,23 @@ #define HELD_DEFAULT_TYPE "geodetic locationURI" #define HELD_DEFAULT_TYPE_LEN (sizeof(HELD_DEFAULT_TYPE) - 1) +#define NAPTR_LOST_SERVICE_HTTP "LoST:http" +#define NAPTR_LOST_SERVICE_HTTPS "LoST:https" +#define NAPTR_LIS_SERVICE_HELD "LIS:HELD" + +#define ACCEPT_HDR \ + "Accept: " \ + "application/pidf+xml,application/" \ + "held+xml;q=0.5" + extern httpc_api_t httpapi; extern int lost_geoloc_type; extern int lost_geoloc_order; +extern int lost_verbose; extern int held_resp_time; extern int held_exact_type; +extern int held_post_req; extern str held_loc_type; char mtheld[] = "application/held+xml;charset=utf-8"; @@ -138,6 +151,7 @@ char *lost_held_type(char *type, int *exact, int *lgth) err: LM_ERR("no more private memory\n"); + /* clean up */ if(ret != NULL) { pkg_free(ret); } @@ -166,36 +180,61 @@ int lost_held_function(struct sip_msg *_m, char *_con, char *_pidf, char *_url, xmlNodePtr root = NULL; xmlNodePtr cur_node = NULL; - str did = {NULL, 0}; - str que = {NULL, 0}; - str con = {NULL, 0}; - str geo = {NULL, 0}; - str err = {NULL, 0}; - str res = {NULL, 0}; - str idhdr = {NULL, 0}; - str pidfuri = {NULL, 0}; - str rtype = {HELD_DEFAULT_TYPE, HELD_DEFAULT_TYPE_LEN}; - - int curlres = 0; + str geo = STR_NULL; /* return value geolocation uri */ + str res = STR_NULL; /* return value pidf */ + str err = STR_NULL; /* return value error */ + + str url = STR_NULL; + str did = STR_NULL; + str que = STR_NULL; + str con = STR_NULL; + str host = STR_NULL; + str name = STR_NULL; + str idhdr = STR_NULL; + str pidfurl = STR_NULL; + + static str rtype = STR_STATIC_INIT(HELD_DEFAULT_TYPE); + static str sheld = STR_STATIC_INIT(NAPTR_LIS_SERVICE_HELD); + + char ustr[MAX_URI_SIZE]; + char istr[NI_MAXHOST]; + char *ipstr = NULL; + char *lisurl = NULL; + char *heldreq = NULL; + + int len = 0; + int curl = 0; + int flag = 0; + int naptr = 0; int presence = 0; - if(_con == NULL || _pidf == NULL || _url == NULL || _err == NULL) { + if(_pidf == NULL || _url == NULL || _err == NULL) { LM_ERR("invalid parameter\n"); goto err; } + /* module parameter */ if(held_loc_type.len > 0) { rtype.s = held_loc_type.s; rtype.len = held_loc_type.len; } /* connection from parameter */ - if(fixup_get_svalue(_m, (gparam_p)_con, &con) != 0) { - LM_ERR("cannot get connection string\n"); - goto err; + if(_con) { + if(get_str_fparam(&con, _m, (gparam_p)_con) != 0) { + LM_ERR("cannot get connection string\n"); + goto err; + } + /* check if connection exists */ + if(con.s != NULL && con.len > 0) { + if(httpapi.http_connection_exists(&con) == 0) { + LM_ERR("connection: [%s] does not exist\n", con.s); + goto err; + } + } } /* id from parameter */ if(_id) { - if(fixup_get_svalue(_m, (gparam_p)_id, &did) != 0) { + if(get_str_fparam(&did, _m, (gparam_p)_id) != 0) { LM_ERR("cannot get device id\n"); goto err; } @@ -225,30 +264,15 @@ int lost_held_function(struct sip_msg *_m, char *_con, char *_pidf, char *_url, did.len = idhdr.len; } LM_INFO("### HELD id [%.*s]\n", did.len, did.s); - - /* check if connection exists */ - if(httpapi.http_connection_exists(&con) == 0) { - LM_ERR("connection: [%s] does not exist\n", con.s); - lost_free_string(&idhdr); - goto err; - } - /* assemble locationRequest */ held = lost_new_held(did, rtype, held_resp_time, held_exact_type); - if(held == NULL) { LM_ERR("held object allocation failed\n"); - lost_free_string(&idhdr); + lost_free_string(&idhdr); /* clean up */ goto err; } que.s = lost_held_location_request(held, &que.len); - - /* free memory */ - did.s = NULL; - did.len = 0; - lost_free_held(held); - lost_free_string(&idhdr); - + lost_free_held(&held); /* clean up */ if(que.len == 0) { LM_ERR("held request document error\n"); que.s = NULL; @@ -258,17 +282,95 @@ int lost_held_function(struct sip_msg *_m, char *_con, char *_pidf, char *_url, LM_DBG("held location request: [%s]\n", que.s); /* send locationRequest to location server - HTTP POST */ - curlres = httpapi.http_connect(_m, &con, NULL, &res, mtheld, &que); + if(con.s != NULL && con.len > 0) { + + LM_DBG("using connection [%.*s]\n", con.len, con.s); + + /* send via connection */ + curl = httpapi.http_connect(_m, &con, NULL, &res, mtheld, &que); + } else { + /* we have no connection ... do a NAPTR lookup */ + if(lost_parse_host(did.s, &host, &flag) > 0) { + + LM_DBG("no conn. trying NATPR lookup [%.*s]\n", host.len, host.s); + + /* remove '[' and ']' from string (IPv6) */ + if(flag == AF_INET6) { + host.s++; + host.len = host.len - 2; + } + /* is it a name or ip ... check nameinfo (reverse lookup) */ + len = 0; + ipstr = lost_copy_string(host, &len); + if(len > 0) { + name.s = &(istr[0]); + name.len = NI_MAXHOST; + if(lost_get_nameinfo(ipstr, &name, flag) > 0) { + + LM_DBG("ip [%s] to name [%.*s]\n", ipstr, name.len, name.s); + + /* change ip string to name */ + host.s = name.s; + host.len = name.len; + } else { + + /* keep string */ + LM_DBG("no nameinfo for [%s]\n", ipstr); + } + pkg_free(ipstr); /* clean up */ + } + url.s = &(ustr[0]); + url.len = MAX_URI_SIZE; + if((naptr = lost_naptr_lookup(host, &sheld, &url)) == 0) { + LM_ERR("NAPTR failed on [%.*s]\n", host.len, host.s); + lost_free_string(&que); /* clean up */ + lost_free_string(&idhdr); + goto err; + } + } else { + LM_ERR("failed to get location service for [%.*s]\n", did.len, + did.s); + lost_free_string(&que); /* clean up */ + lost_free_string(&idhdr); + goto err; + } + + LM_DBG("NATPR lookup returned [%.*s]\n", url.len, url.s); + + /* curl doesn't like str */ + len = 0; + lisurl = lost_copy_string(url, &len); + /* send to service */ + if(lisurl != NULL && len > 0) { + curl = httpapi.http_client_query_c( + _m, lisurl, &res, que.s, mtheld, ACCEPT_HDR); + pkg_free(lisurl); /*clean up */ + } else { + goto err; + } + } /* only HTTP 2xx responses are accepted */ - if(curlres >= 300 || curlres < 100) { - LM_ERR("[%.*s] failed with error: %d\n", con.len, con.s, curlres); + if(curl >= 300 || curl < 100) { + if(con.s != NULL && con.len > 0) { + LM_ERR("POST [%.*s] failed with error: %d\n", con.len, con.s, curl); + } else { + LM_ERR("POST [%.*s] failed with error: %d\n", url.len, url.s, curl); + } lost_free_string(&res); goto err; } + if(con.s != NULL && con.len > 0) { - LM_DBG("[%.*s] returned: %d\n", con.len, con.s, curlres); + LM_DBG("[%.*s] returned: %d\n", con.len, con.s, curl); - /* free memory */ + } else { + + LM_DBG("[%.*s] returned: %d\n", url.len, url.s, curl); + } + did.s = NULL; + did.len = 0; + /* clean up */ + lost_free_string(&idhdr); lost_free_string(&que); /* read and parse the returned xml */ doc = xmlReadMemory(res.s, res.len, 0, NULL, @@ -288,7 +390,7 @@ int lost_held_function(struct sip_msg *_m, char *_con, char *_pidf, char *_url, LM_ERR("empty xml document\n"); goto err; } - /* check the root element ... shall be locationResponse, or errors */ + /* check the root element ... shall be locationResponse, or error */ if(xmlStrcmp(root->name, (const xmlChar *)"locationResponse") == 0) { LM_DBG("HELD location response [%.*s]\n", res.len, res.s); @@ -320,46 +422,59 @@ int lost_held_function(struct sip_msg *_m, char *_con, char *_pidf, char *_url, } } } - /* if we do not have a presence node but a location URI */ /* dereference pidf.lo at location server via HTTP GET */ - if((presence == 0) && (geo.len > 0)) { - + if((presence == 0) && (geo.s != NULL && geo.len > 0)) { LM_INFO("presence node not found in HELD response, trying URI " "...\n"); - - curlres = - httpapi.http_client_query(_m, geo.s, &pidfuri, NULL, NULL); + if(held_post_req == 0) { + curl = httpapi.http_client_query_c( + _m, geo.s, &pidfurl, NULL, mtheld, ACCEPT_HDR); + } else { + len = 0; + heldreq = lost_held_post_request(&len, 0, NULL); + if(len > 0) { + + LM_DBG("held POST request: [%.*s]\n", len, heldreq); + + curl = httpapi.http_client_query_c( + _m, geo.s, &pidfurl, heldreq, mtheld, ACCEPT_HDR); + pkg_free(heldreq); /* clean up */ + } else { + LM_ERR("could not create POST request\n"); + lost_free_string(&pidfurl); /* clean up */ + goto err; + } + } /* only HTTP 2xx responses are accepted */ - if(curlres >= 300 || curlres < 100) { - LM_ERR("dereferencing location failed: %d\n", curlres); - /* free memory */ - lost_free_string(&pidfuri); + if(curl >= 300 || curl < 100) { + LM_ERR("GET [%.*s] failed with error: %d\n", pidfurl.len, + pidfurl.s, curl); + /* clean up */ + lost_free_string(&pidfurl); goto err; } - - if(pidfuri.len == 0) { - + if(pidfurl.len == 0) { LM_WARN("HELD location request failed [%.*s]\n", geo.len, geo.s); - } else { - LM_DBG("HELD location response [%.*s]\n", pidfuri.len, - pidfuri.s); + LM_DBG("HELD location response [%.*s]\n", pidfurl.len, + pidfurl.s); - res.s = pidfuri.s; - res.len = pidfuri.len; + res.s = pidfurl.s; + res.len = pidfurl.len; } } + /* error received */ } else if(xmlStrcmp(root->name, (const xmlChar *)"error") == 0) { LM_DBG("HELD error response [%.*s]\n", res.len, res.s); - /* get the error patterm */ + /* get the error property */ err.s = lost_get_property(root, (char *)"code", &err.len); if(err.len == 0) { - LM_ERR("error - code property not found: [%.*s]\n", res.len, res.s); + LM_ERR("error - property not found: [%.*s]\n", res.len, res.s); goto err; } LM_WARN("locationRequest error response: [%.*s]\n", err.len, err.s); @@ -367,6 +482,8 @@ int lost_held_function(struct sip_msg *_m, char *_con, char *_pidf, char *_url, LM_ERR("root element is not valid: [%.*s]\n", res.len, res.s); goto err; } + + /* clean up */ xmlFreeDoc(doc); doc = NULL; @@ -405,6 +522,229 @@ int lost_held_function(struct sip_msg *_m, char *_con, char *_pidf, char *_url, return LOST_CLIENT_ERROR; } + +/* + * lost_held_dereference(msg, url, pidf, err, rtime, rtype) + * assembles and runs HELD locationRequest (POST), returns result as pidf + */ +int lost_held_dereference(struct sip_msg *_m, char *_url, char *_pidf, + char *_err, char *_rtime, char *_rtype) +{ + pv_spec_t *pspidf; + pv_spec_t *pserr; + + pv_value_t pvpidf; + pv_value_t pverr; + + xmlDocPtr doc = NULL; + xmlNodePtr root = NULL; + + str url = STR_NULL; + str rtm = STR_NULL; + str rtp = STR_NULL; + + str res = STR_NULL; /* return value location response */ + str err = STR_NULL; /* return value error */ + + char *ptr = NULL; + char *lisurl = NULL; + char *heldreq = NULL; + char *rtype = NULL; + + long ltime = 0; + + int len = 0; + int curl = 0; + int exact = 0; + + if(_url == NULL || _rtime == NULL || _pidf == NULL || _rtype == NULL + || _err == NULL) { + LM_ERR("invalid parameter\n"); + goto err; + } + + /* dereference url from parameter */ + if(_url) { + if(get_str_fparam(&url, _m, (gparam_p)_url) != 0) { + LM_ERR("cannot get dereference url\n"); + goto err; + } + if(url.len == 0) { + LM_ERR("no dereference url found\n"); + goto err; + } + } + + /* response time from parameter */ + if(_rtime) { + if(get_str_fparam(&rtm, _m, (gparam_p)_rtime) != 0) { + LM_ERR("cannot get response time\n"); + goto err; + } + if(rtm.len == 0) { + LM_WARN("no response time found\n"); + ltime = 0; + } else { + ltime = strtol(rtm.s, &ptr, 10); + } + } + + /* response type from parameter */ + if(_rtype) { + if(get_str_fparam(&rtp, _m, (gparam_p)_rtype) != 0) { + LM_ERR("cannot get response type\n"); + goto err; + } + if(rtp.len == 0) { + LM_WARN("no response type found\n"); + rtype = NULL; + } else { + len = 0; + /* response type string sanity check */ + rtype = lost_held_type(rtp.s, &exact, &len); + if(len == 0) { + LM_WARN("cannot normalize [%.*s]\n", rtp.len, rtp.s); + rtype = NULL; + } + } + } + + /* default responseTime: emergencyRouting */ + heldreq = lost_held_post_request(&len, 0, rtype); + + /* responseTime: milliseconds */ + if((ltime > 0) && (strlen(ptr) == 0)) { + heldreq = lost_held_post_request(&len, ltime, rtype); + } + + /* responseTime: emergencyRouting|emergencyDispatch */ + if((ltime == 0) && (strlen(ptr) > 0)) { + if(strncasecmp(ptr, HELD_ED, strlen(HELD_ED)) == 0) { + heldreq = lost_held_post_request(&len, -1, rtype); + } + if(strncasecmp(ptr, HELD_ER, strlen(HELD_ER)) == 0) { + heldreq = lost_held_post_request(&len, 0, rtype); + } + } + + /* clean up */ + if(rtype != NULL) { + pkg_free(rtype); + } + + if(heldreq != NULL && len == 0) { + LM_ERR("could not create POST request\n"); + goto err; + } + + LM_DBG("POST request: [%.*s]\n", len, heldreq); + + /* curl doesn't like str */ + len = 0; + lisurl = lost_copy_string(url, &len); + if(lisurl != NULL && len > 0) { + + LM_DBG("POST url: [%.*s]\n", len, lisurl); + + curl = httpapi.http_client_query_c( + _m, lisurl, &res, heldreq, mtheld, ACCEPT_HDR); + pkg_free(lisurl); /* clean up */ + pkg_free(heldreq); + } else { + LM_ERR("could not copy POST url\n"); + pkg_free(heldreq); /* clean up */ + goto err; + } + + /* only HTTP 2xx responses are accepted */ + if(curl >= 300 || curl < 100) { + LM_ERR("POST [%.*s] failed with error: %d\n", url.len, url.s, curl); + goto err; + } + if(res.s != NULL && res.len > 0) { + + LM_DBG("LbR pidf-lo: [%.*s]\n", res.len, res.s); + + } else { + LM_ERR("dereferencing location failed\n"); + goto err; + } + + /* read and parse the returned xml */ + doc = xmlReadMemory(res.s, res.len, 0, NULL, + XML_PARSE_NOBLANKS | XML_PARSE_NONET | XML_PARSE_NOCDATA); + if(doc == NULL) { + LM_WARN("invalid xml document: [%.*s]\n", res.len, res.s); + doc = xmlRecoverMemory(res.s, res.len); + if(doc == NULL) { + LM_ERR("xml document recovery failed on: [%.*s]\n", res.len, res.s); + goto err; + } + + LM_DBG("xml document recovered\n"); + } + root = xmlDocGetRootElement(doc); + if(root == NULL) { + LM_ERR("empty xml document\n"); + goto err; + } + + /* check the root element ... shall be locationResponse, or error */ + if(xmlStrcmp(root->name, (const xmlChar *)"presence") == 0) { + + LM_DBG("HELD location response [%.*s]\n", res.len, res.s); + + /* error received */ + } else if(xmlStrcmp(root->name, (const xmlChar *)"error") == 0) { + + LM_DBG("HELD error response [%.*s]\n", res.len, res.s); + + /* get the error property */ + err.s = lost_get_property(root, (char *)"code", &err.len); + if(err.len == 0) { + LM_ERR("error - property not found: [%.*s]\n", res.len, res.s); + goto err; + } + LM_WARN("locationRequest error response: [%.*s]\n", err.len, err.s); + } else { + LM_ERR("root element is not valid: [%.*s]\n", res.len, res.s); + goto err; + } + + /* clean up */ + xmlFreeDoc(doc); + doc = NULL; + + /* set writeable pvars */ + pvpidf.rs = res; + pvpidf.rs.s = res.s; + pvpidf.rs.len = res.len; + + pvpidf.flags = PV_VAL_STR; + pspidf = (pv_spec_t *)_pidf; + pspidf->setf(_m, &pspidf->pvp, (int)EQ_T, &pvpidf); + + pverr.rs = err; + pverr.rs.s = err.s; + pverr.rs.len = err.len; + + pverr.flags = PV_VAL_STR; + pserr = (pv_spec_t *)_err; + pserr->setf(_m, &pserr->pvp, (int)EQ_T, &pverr); + + return (err.len > 0) ? LOST_SERVER_ERROR : LOST_SUCCESS; + +err: + if(doc != NULL) { + xmlFreeDoc(doc); + } + if(res.s != NULL && res.len > 0) { + lost_free_string(&res); + } + + return LOST_CLIENT_ERROR; +} + /* * lost_function(msg, con, pidf, uri, name, err, pidf, urn) * assembles and runs LOST findService request, parses results @@ -422,108 +762,119 @@ int lost_function(struct sip_msg *_m, char *_con, char *_uri, char *_name, p_loc_t loc = NULL; p_geolist_t geolist = NULL; - int geotype; - - str url = {NULL, 0}; - str uri = {NULL, 0}; - str urn = {NULL, 0}; - str err = {NULL, 0}; - str req = {NULL, 0}; - str con = {NULL, 0}; - str ret = {NULL, 0}; - str name = {NULL, 0}; - str pidf = {NULL, 0}; - str geohdr = {NULL, 0}; - str pidfhdr = {NULL, 0}; + p_fsr_t fsrdata = NULL; + + str name = STR_NULL; /* return value displayName */ + str uri = STR_NULL; /* return value uri */ + str err = STR_NULL; /* return value error */ + + str tmp = STR_NULL; + str url = STR_NULL; + str urn = STR_NULL; + str req = STR_NULL; + str con = STR_NULL; + str ret = STR_NULL; + str pidf = STR_NULL; + str oldurl = STR_NULL; + str losturl = STR_NULL; + + static str shttp = STR_STATIC_INIT(NAPTR_LOST_SERVICE_HTTP); + static str shttps = STR_STATIC_INIT(NAPTR_LOST_SERVICE_HTTPS); struct msg_start *fl; + + char ustr[MAX_URI_SIZE]; char *search = NULL; char *geoval = NULL; - int curlres = 0; - int geoitems = 0; + char *urlrep = NULL; + char *heldreq = NULL; - xmlDocPtr doc = NULL; - xmlNodePtr root = NULL; + int geotype = 0; + int redirect = 0; + int curl = 0; + int len = 0; + int naptr = 0; + int geoitems = 0; if(_con == NULL || _uri == NULL || _name == NULL || _err == NULL) { LM_ERR("invalid parameter\n"); goto err; } - if(fixup_get_svalue(_m, (gparam_p)_con, &con) != 0) { - LM_ERR("cannot get connection string\n"); - goto err; + /* connection from parameter */ + if(_con) { + if(get_str_fparam(&con, _m, (gparam_p)_con) != 0) { + LM_ERR("cannot get connection string\n"); + goto err; + } + /* check if connection exists */ + if(con.s != NULL && con.len > 0) { + if(httpapi.http_connection_exists(&con) == 0) { + LM_WARN("connection: [%.*s] does not exist\n", con.len, con.s); + /* check if NAPTR lookup works with connection parameter */ + losturl.s = &(ustr[0]); + losturl.len = MAX_URI_SIZE; + if((naptr = lost_naptr_lookup(con, &shttps, &losturl)) == 0) { + naptr = lost_naptr_lookup(con, &shttp, &losturl); + } + if(naptr == 0) { + LM_ERR("NAPTR failed on [%.*s]\n", con.len, con.s); + goto err; + } + } + } } /* urn from parameter */ if(_urn) { - if(fixup_get_svalue(_m, (gparam_p)_urn, &urn) != 0) { - LM_ERR("cannot get service urn\n"); + if(get_str_fparam(&urn, _m, (gparam_p)_urn) != 0) { + LM_ERR("cannot get service urn parameter\n"); goto err; } } /* urn from request line */ if(urn.len == 0) { - LM_WARN("no sevice urn parameter, trying request line ...\n"); + + LM_DBG("no service urn parameter, trying request line ...\n"); + fl = &(_m->first_line); urn.len = fl->u.request.uri.len; urn.s = fl->u.request.uri.s; } /* check urn scheme */ - if(urn.len > 3) { - search = urn.s; - if(((*(search + 0) == 'u') || (*(search + 0) == 'U')) - && ((*(search + 1) == 'r') || (*(search + 1) == 'R')) - && ((*(search + 2) == 'n') || (*(search + 2) == 'N')) - && (*(search + 3) == ':')) { - LM_INFO("### LOST urn\t[%.*s]\n", urn.len, urn.s); - } else { - LM_ERR("service urn not found\n"); - goto err; - } + search = urn.s; + if(is_urn(search) > 0) { + LM_INFO("### LOST urn\t[%.*s]\n", urn.len, urn.s); } else { LM_ERR("service urn not found\n"); goto err; } /* pidf from parameter */ if(_pidf) { - if(fixup_get_svalue(_m, (gparam_p)_pidf, &pidf) != 0) { - LM_WARN("cannot get pidf-lo parameter\n"); + if(get_str_fparam(&pidf, _m, (gparam_p)_pidf) != 0) { + LM_ERR("cannot get pidf parameter\n"); } else { - LM_DBG("parsing pidf-lo from paramenter\n"); + LM_DBG("parsing pidf parameter ...\n"); - if(pidf.len > 0) { + if(pidf.s != NULL && pidf.len > 0) { - LM_DBG("pidf-lo: [%.*s]\n", pidf.len, pidf.s); + LM_DBG("pidf: [%.*s]\n", pidf.len, pidf.s); - /* parse the pidf-lo */ + /* parse the pidf and get loc object */ loc = lost_parse_pidf(pidf, urn); - /* free memory */ - pidf.s = NULL; - pidf.len = 0; - } else { - LM_WARN("no valid pidf parameter ...\n"); } } } - - /* no pidf-lo so far ... check geolocation header */ + /* neither valifd pidf parameter nor loc ... check geolocation header */ if(loc == NULL) { - LM_DBG("looking for geolocation header ...\n"); + /* parse Geolocation header */ - geohdr.s = lost_get_geolocation_header(_m, &geohdr.len); - if(geohdr.len == 0) { - LM_ERR("geolocation header not found\n"); - goto err; - } + LM_DBG("parsing geolocation header ...\n"); - LM_DBG("geolocation header found\n"); + geolist = lost_get_geolocation_header(_m, &geoitems); - /* parse Geolocation header */ - geolist = lost_new_geoheader_list(geohdr, &geoitems); if(geoitems == 0) { - LM_ERR("invalid geolocation header\n"); - lost_free_string(&geohdr); + LM_ERR("geolocation header not found\n"); goto err; } @@ -535,7 +886,6 @@ int lost_function(struct sip_msg *_m, char *_con, char *_uri, char *_name, lost_reverse_geoheader_list(&geolist); } - switch(lost_geoloc_type) { case ANY: /* type: 0 */ geoval = lost_get_geoheader_value(geolist, ANY, &geotype); @@ -580,36 +930,26 @@ int lost_function(struct sip_msg *_m, char *_con, char *_uri, char *_name, break; } - if(geoval == NULL) { LM_ERR("invalid geolocation header\n"); - /* free memory */ - lost_delete_geoheader_list(geolist); - lost_free_string(&geohdr); goto err; } - LM_INFO("### LOST loc\t[%s]\n", geoval); - + /* clean up */ + pidf.s = NULL; + pidf.len = 0; /* use location by value */ if(geotype == CID) { - /* get body part - filter=>content-indirection */ pidf.s = get_body_part_by_filter(_m, 0, 0, geoval, NULL, &pidf.len); - if(pidf.len > 0) { + if(pidf.s != NULL && pidf.len > 0) { LM_DBG("LbV pidf-lo: [%.*s]\n", pidf.len, pidf.s); - /* parse the pidf-lo */ - loc = lost_parse_pidf(pidf, urn); - /* free memory */ - pidf.s = NULL; - pidf.len = 0; } else { LM_WARN("no multipart body found\n"); } } - /* use location by reference */ if((geotype == HTTPS) || (geotype == HTTP)) { url.s = geoval; @@ -617,60 +957,67 @@ int lost_function(struct sip_msg *_m, char *_con, char *_uri, char *_name, /* ! dereference pidf.lo at location server - HTTP GET */ /* ! requires hack in http_client module */ /* ! functions.c => http_client_query => query_params.oneline = 0; */ - curlres = - httpapi.http_client_query(_m, url.s, &pidfhdr, NULL, NULL); - /* free memory */ - url.s = NULL; - url.len = 0; + if(held_post_req == 0) { + curl = httpapi.http_client_query_c( + _m, url.s, &ret, NULL, mtheld, ACCEPT_HDR); + } else { + len = 0; + heldreq = lost_held_post_request(&len, 0, NULL); + if(len > 0) { + + LM_DBG("POST request: [%.*s]\n", len, heldreq); + + curl = httpapi.http_client_query_c( + _m, url.s, &ret, heldreq, mtheld, ACCEPT_HDR); + pkg_free(heldreq); /* clean up */ + } else { + LM_ERR("could not create POST request\n"); + goto err; + } + } /* only HTTP 2xx responses are accepted */ - if(curlres >= 300 || curlres < 100) { - LM_ERR("http GET failed with error: %d\n", curlres); - /* free memory */ - lost_delete_geoheader_list(geolist); - lost_free_string(&pidfhdr); - lost_free_string(&geohdr); + if(curl >= 300 || curl < 100) { + if(held_post_req == 0) { + LM_ERR("GET [%.*s] failed with error: %d\n", url.len, url.s, + curl); + } else { + LM_ERR("POST [%.*s] failed with error: %d\n", url.len, + url.s, curl); + } + /* clean up */ + lost_free_string(&ret); goto err; } - - pidf.s = pidfhdr.s; - pidf.len = pidfhdr.len; - - if(pidf.len > 0) { + url.s = NULL; + url.len = 0; + pidf.s = ret.s; + pidf.len = ret.len; + if(pidf.s != NULL && pidf.len > 0) { LM_DBG("LbR pidf-lo: [%.*s]\n", pidf.len, pidf.s); - /* parse the pidf-lo */ - loc = lost_parse_pidf(pidf, urn); - /* free memory */ - pidf.s = NULL; - pidf.len = 0; } else { LM_WARN("dereferencing location failed\n"); } } - /* free memory */ - lost_delete_geoheader_list(geolist); - lost_free_string(&geohdr); - lost_free_string(&pidfhdr); + /* clean up */ + lost_free_geoheader_list(&geolist); + lost_free_string(&ret); } - - if(loc == NULL) { + if(pidf.s == NULL && pidf.len == 0) { LM_ERR("location object not found\n"); goto err; } - - /* check if connection exits */ - if(httpapi.http_connection_exists(&con) == 0) { - LM_ERR("connection: [%.*s] does not exist\n", con.len, con.s); + /* parse the pidf and get loc object */ + loc = lost_parse_pidf(pidf, urn); + if(loc == NULL) { + LM_ERR("parsing pidf failed\n"); goto err; } /* assemble findService request */ req.s = lost_find_service_request(loc, &req.len); - /* free memory */ - lost_free_loc(loc); - loc = NULL; - - if(req.len == 0) { + lost_free_loc(&loc); /* clean up */ + if(req.s == NULL && req.len == 0) { LM_ERR("lost request failed\n"); goto err; } @@ -678,18 +1025,28 @@ int lost_function(struct sip_msg *_m, char *_con, char *_uri, char *_name, LM_DBG("findService request: [%.*s]\n", req.len, req.s); /* send findService request to mapping server - HTTP POST */ - curlres = httpapi.http_connect(_m, &con, NULL, &ret, mtlost, &req); + if(naptr) { + /* copy url */ + len = 0; + urlrep = lost_copy_string(url, &len); + if(len > 0) { + /* send request */ + curl = httpapi.http_client_query(_m, urlrep, &ret, req.s, mtlost); + pkg_free(urlrep); /*clean up */ + } else { + goto err; + } + } else { + curl = httpapi.http_connect(_m, &con, NULL, &ret, mtlost, &req); + } /* only HTTP 2xx responses are accepted */ - if(curlres >= 300 || curlres < 100) { - LM_ERR("[%.*s] failed with error: %d\n", con.len, con.s, curlres); + if(curl >= 300 || curl < 100) { + LM_ERR("POST [%.*s] failed with error: %d\n", con.len, con.s, curl); lost_free_string(&ret); goto err; } - LM_DBG("[%.*s] returned: %d\n", con.len, con.s, curlres); - - /* free memory */ - lost_free_string(&req); + LM_DBG("[%.*s] returned: %d\n", con.len, con.s, curl); if(ret.len == 0) { LM_ERR("findService request failed\n"); @@ -698,70 +1055,128 @@ int lost_function(struct sip_msg *_m, char *_con, char *_uri, char *_name, LM_DBG("findService response: [%.*s]\n", ret.len, ret.s); - /* read and parse the returned xml */ - doc = xmlReadMemory(ret.s, ret.len, 0, 0, - XML_PARSE_NOBLANKS | XML_PARSE_NONET | XML_PARSE_NOCDATA); - - if(doc == NULL) { - LM_ERR("invalid xml document: [%.*s]\n", ret.len, ret.s); - doc = xmlRecoverMemory(ret.s, ret.len); - if(doc == NULL) { - LM_ERR("xml document recovery failed on: [%.*s]\n", ret.len, ret.s); - goto err; - } - - LM_DBG("xml document recovered\n"); - } - root = xmlDocGetRootElement(doc); - if(root == NULL) { - LM_ERR("empty xml document: [%.*s]\n", ret.len, ret.s); - /* free memory */ - lost_free_string(&ret); - goto err; - } - /* check the root element, shall be findServiceResponse, or errors */ - if((!xmlStrcmp(root->name, (const xmlChar *)"findServiceResponse"))) { - /* get the uri element */ - uri.s = lost_get_content(root, uri_element, &uri.len); - if(uri.len == 0) { - LM_ERR("uri element not found: [%.*s]\n", ret.len, ret.s); - /* free memory */ - lost_free_string(&ret); - goto err; + /* at least parse one request */ + redirect = 1; + while(redirect) { + fsrdata = lost_parse_findServiceResponse(ret); + if (lost_verbose == 1) { + lost_print_findServiceResponse(fsrdata); } - LM_INFO("### LOST uri\t[%.*s]\n", uri.len, uri.s); - /* get the displayName element */ - name.s = lost_get_content(root, name_element, &name.len); - if(name.len == 0) { - LM_ERR("displayName element not found: [%.*s]\n", ret.len, ret.s); - /* free memory */ - lost_free_string(&ret); - goto err; - } - LM_INFO("### LOST din\t[%.*s]\n", name.len, name.s); - } else if((!xmlStrcmp(root->name, (const xmlChar *)"errors"))) { - - LM_DBG("findService error response received\n"); - - /* get the error patterm */ - err.s = lost_get_childname(root, errors_element, &err.len); - LM_DBG("findService error response: [%.*s]\n", err.len, err.s); - if(err.len == 0) { - LM_ERR("error pattern element not found: [%.*s]\n", ret.len, ret.s); - /* free memory */ - lost_free_string(&ret); - goto err; + switch(fsrdata->category) { + case RESPONSE: + if(fsrdata->uri != NULL) { + /* get the first uri element */ + if((tmp.s = fsrdata->uri->value) != NULL) { + tmp.len = strlen(fsrdata->uri->value); + uri.s = lost_copy_string(tmp, &uri.len); + } + } else { + LM_ERR("uri not found: [%.*s]\n", ret.len, ret.s); + goto err; + } + if(fsrdata->mapping != NULL) { + /* get the displayName element */ + if((tmp.s = fsrdata->mapping->name->text) != NULL) { + tmp.len = strlen(fsrdata->mapping->name->text); + name.s = lost_copy_string(tmp, &name.len); + } + } else { + LM_ERR("name not found: [%.*s]\n", ret.len, ret.s); + goto err; + } + /* we are done */ + redirect = 0; + break; + case ERROR: + /* get the errors element */ + if(fsrdata->errors != NULL) { + if((tmp.s = fsrdata->errors->issue->type) != NULL) { + tmp.len = strlen(fsrdata->errors->issue->type); + err.s = lost_copy_string(tmp, &err.len); + } + /* clean up */ + tmp.s = NULL; + tmp.len = 0; + } else { + LM_ERR("errors not found: [%.*s]\n", ret.len, ret.s); + goto err; + } + /* we are done */ + redirect = 0; + break; + case REDIRECT: + /* get the target element */ + if(fsrdata->redirect != NULL) { + if((tmp.s = fsrdata->redirect->target) != NULL) { + tmp.len = strlen(fsrdata->redirect->target); + url.s = &(ustr[0]); + url.len = MAX_URI_SIZE; + if((naptr = lost_naptr_lookup(tmp, &shttps, &url)) + == 0) { + naptr = lost_naptr_lookup(tmp, &shttp, &url); + } + if(naptr == 0) { + LM_ERR("NAPTR failed on [%.*s]\n", tmp.len, tmp.s); + goto err; + } + /* clean up */ + tmp.s = NULL; + tmp.len = 0; + /* check loop */ + if(oldurl.s != NULL && oldurl.len > 0) { + if(str_strcasecmp(&url, &oldurl) == 0) { + LM_ERR("loop detected: " + "[%.*s]<-->[%.*s]\n", + oldurl.len, oldurl.s, url.len, url.s); + goto err; + } + } + /* remember the redirect target */ + oldurl.s = lost_copy_string(url, &oldurl.len); + /* clean up */ + lost_free_findServiceResponse(&fsrdata); + lost_free_string(&ret); + /* copy url */ + len = 0; + urlrep = lost_copy_string(url, &len); + if(len > 0) { + /* send request */ + curl = httpapi.http_client_query( + _m, urlrep, &ret, req.s, mtlost); + url.s = NULL; + url.len = 0; + pkg_free(urlrep); /*clean up */ + /* only HTTP 2xx responses are accepted */ + if(curl >= 300 || curl < 100) { + LM_ERR("POST [%s] failed with error: %d\n", + urlrep, curl); + goto err; + } + } else { + goto err; + } + /* once more ... we got a redirect */ + redirect = 1; + } + } else { + LM_ERR("redirect element not found: [%.*s]\n", ret.len, + ret.s); + goto err; + } + break; + case OTHER: + default: + LM_ERR("pidf is not valid: [%.*s]\n", ret.len, ret.s); + goto err; + break; } - LM_WARN("findService error response: [%.*s]\n", err.len, err.s); - } else { - LM_ERR("root element is not valid: [%.*s]\n", ret.len, ret.s); - /* free memory */ - lost_free_string(&ret); - goto err; } - /* free memory */ + /* clean up */ + lost_free_findServiceResponse(&fsrdata); lost_free_string(&ret); + lost_free_string(&req); + lost_free_string(&oldurl); /* set writable pvars */ pvname.rs = name; @@ -791,13 +1206,18 @@ int lost_function(struct sip_msg *_m, char *_con, char *_uri, char *_name, return (err.len > 0) ? LOST_SERVER_ERROR : LOST_SUCCESS; err: - /* free memory */ - if(doc != NULL) { - xmlFreeDoc(doc); - doc = NULL; + /* clean up */ + lost_free_findServiceResponse(&fsrdata); + lost_free_geoheader_list(&geolist); + lost_free_loc(&loc); + if(oldurl.s != NULL && oldurl.len > 0) { + lost_free_string(&oldurl); + } + if(ret.s != NULL && ret.len > 0) { + lost_free_string(&ret); } - if(loc != NULL) { - lost_free_loc(loc); + if(req.s != NULL && req.len > 0) { + lost_free_string(&req); } return LOST_CLIENT_ERROR; diff --git a/src/modules/lost/functions.h b/src/modules/lost/functions.h index ee41b934277..0e56f4dbc91 100755 --- a/src/modules/lost/functions.h +++ b/src/modules/lost/functions.h @@ -34,6 +34,9 @@ char *lost_held_type(char *, int *, int *); +int lost_held_dereference( + struct sip_msg *, char *, char *, char *, char *, char *); + int lost_held_function( struct sip_msg *, char *, char *, char *, char *, char *); diff --git a/src/modules/lost/lost.c b/src/modules/lost/lost.c index a2dd2498ad6..ab6ef510e66 100644 --- a/src/modules/lost/lost.c +++ b/src/modules/lost/lost.c @@ -1,7 +1,7 @@ /* * lost module * - * Copyright (C) 2020 Wolfgang Kampichler + * Copyright (C) 2021 Wolfgang Kampichler * DEC112, FREQUENTIS AG * * This file is part of Kamailio, a free SIP server. @@ -53,10 +53,18 @@ httpc_api_t httpapi; int lost_geoloc_type = 0; /* lost: Geolocation header value order: first (0) or last (1) (default: 0) */ int lost_geoloc_order = 0; -/* held request: response time (default: 0 = no timeout) */ +/* lost: Recursion allowed: yes (1) or no (0) (default: 1 = allowed) */ +int lost_recursion = 1; +/* lost geo profile: first (0), last (1), geo (2) or civic (3) (default: 0) */ +int lost_profile = 0; +/* lost verbose report: no (0), yes (1) (default: 0) */ +int lost_verbose = 0; +/* held request: response time (default: 0 = "emergencyRouting") */ int held_resp_time = 0; /* held request: exact is true (1) or false (0) (default: false) */ int held_exact_type = 0; +/* held request: POST to deref. location: yes (1) or no (0) (default: 0 = no) */ +int held_post_req = 0; /* held request: location type */ str held_loc_type = STR_NULL; @@ -70,6 +78,8 @@ static int fixup_lost_held_query(void **param, int param_no); static int fixup_free_lost_held_query(void **param, int param_no); static int fixup_lost_held_query_id(void **param, int param_no); static int fixup_free_lost_held_query_id(void **param, int param_no); +static int fixup_lost_held_deref(void **param, int param_no); +static int fixup_free_lost_held_deref(void **param, int param_no); static int fixup_lost_query(void **param, int param_no); static int fixup_free_lost_query(void **param, int param_no); @@ -81,6 +91,8 @@ static int w_lost_held_query( struct sip_msg *_m, char *_con, char *_pidf, char *_url, char *_err); static int w_lost_held_query_id(struct sip_msg *_m, char *_con, char *_id, char *_pidf, char *_url, char *_err); +static int w_lost_held_deref(struct sip_msg *_m, char *_url, char *_rtime, + char *_rtype, char *_pidf, char *_err); static int w_lost_query( struct sip_msg *_m, char *_con, char *_uri, char *_name, char *_err); static int w_lost_query_all(struct sip_msg *_m, char *_con, char *_pidf, @@ -94,6 +106,9 @@ static cmd_export_t cmds[] = { {"lost_held_query", (cmd_function)w_lost_held_query_id, 5, fixup_lost_held_query_id, fixup_free_lost_held_query_id, REQUEST_ROUTE | FAILURE_ROUTE | BRANCH_ROUTE}, + {"lost_held_dereference", (cmd_function)w_lost_held_deref, 5, + fixup_lost_held_deref, fixup_free_lost_held_deref, + REQUEST_ROUTE | FAILURE_ROUTE | BRANCH_ROUTE}, {"lost_query", (cmd_function)w_lost_query, 4, fixup_lost_query, fixup_free_lost_query, REQUEST_ROUTE | FAILURE_ROUTE | BRANCH_ROUTE}, @@ -105,7 +120,11 @@ static cmd_export_t cmds[] = { /* Exported parameters */ static param_export_t params[] = {{"exact_type", PARAM_INT, &held_exact_type}, {"response_time", PARAM_INT, &held_resp_time}, + {"post_request", PARAM_INT, &held_post_req}, {"location_type", PARAM_STR, &held_loc_type}, + {"recursion", PARAM_INT, &lost_recursion}, + {"location_profile", PARAM_INT, &lost_profile}, + {"verbose", PARAM_INT, &lost_verbose}, {"geoheader_type", PARAM_INT, &lost_geoloc_type}, {"geoheader_order", PARAM_INT, &lost_geoloc_order}, {0, 0, 0}}; @@ -155,7 +174,10 @@ static int child_init(int rank) static void destroy(void) { - pkg_free(held_loc_type.s); + if(held_loc_type.s != NULL && held_loc_type.len > 0) { + pkg_free(held_loc_type.s); + held_loc_type.len = 0; + } /* do nothing */ } @@ -200,15 +222,12 @@ static int fixup_free_lost_held_query(void **param, int param_no) } /* - * Fix 5 lost_held_query_id params: con (string/pvar) id (string that may contain - * pvars) and pidf, url, err (writable pvar). + * Fix 5 lost_held_query_id params: con (string/pvar) id (string that may + * contain pvars) and pidf, url, err (writable pvar). */ static int fixup_lost_held_query_id(void **param, int param_no) { - if(param_no == 1) { - return fixup_spve_null(param, 1); - } - if(param_no == 2) { + if((param_no == 1) || (param_no == 2)) { return fixup_spve_null(param, 1); } if((param_no == 3) || (param_no == 4) || (param_no == 5)) { @@ -231,13 +250,49 @@ static int fixup_lost_held_query_id(void **param, int param_no) */ static int fixup_free_lost_held_query_id(void **param, int param_no) { - if(param_no == 1) { + if((param_no == 1) || (param_no == 2)) { return fixup_free_spve_null(param, 1); } - if(param_no == 2) { + if((param_no == 3) || (param_no == 4) || (param_no == 5)) { + return fixup_free_pvar_null(param, 1); + } + LM_ERR("invalid parameter number <%d>\n", param_no); + return -1; +} + +/* + * Fix 5 lost_held_dereference params: url (string/pvar), rtime (string/pvar), + * rtype (string/pvar) and pidf, err (writable pvar). + */ +static int fixup_lost_held_deref(void **param, int param_no) +{ + if((param_no == 1) || (param_no == 2) || (param_no == 3)) { + return fixup_spve_null(param, 1); + } + if((param_no == 4) || (param_no == 5)) { + if(fixup_pvar_null(param, 1) != 0) { + LM_ERR("failed to fixup result pvar\n"); + return -1; + } + if(((pv_spec_t *)(*param))->setf == NULL) { + LM_ERR("result pvar is not writable\n"); + return -1; + } + return 0; + } + LM_ERR("invalid parameter number <%d>\n", param_no); + return -1; +} + +/* + * Free lost_held_dereference params. + */ +static int fixup_free_lost_held_deref(void **param, int param_no) +{ + if((param_no == 1) || (param_no == 2) || (param_no == 3)) { return fixup_free_spve_null(param, 1); } - if((param_no == 3) || (param_no == 4) || (param_no == 5)) { + if((param_no == 4) || (param_no == 5)) { return fixup_free_pvar_null(param, 1); } LM_ERR("invalid parameter number <%d>\n", param_no); @@ -346,6 +401,15 @@ static int w_lost_held_query_id(struct sip_msg *_m, char *_con, char *_id, return lost_held_function(_m, _con, _pidf, _url, _err, _id); } +/* + * Wrapper for lost_held_dereference + */ +static int w_lost_held_deref(struct sip_msg *_m, char *_url, char *_rtime, + char *_rtype, char *_pidf, char *_err) +{ + return lost_held_dereference(_m, _url, _pidf, _err, _rtime, _rtype); +} + /* * Wrapper for lost_query w/o pudf, urn */ diff --git a/src/modules/lost/naptr.c b/src/modules/lost/naptr.c new file mode 100644 index 00000000000..86e89e3330c --- /dev/null +++ b/src/modules/lost/naptr.c @@ -0,0 +1,255 @@ +/* + * lost module naptr functions + * thankfully taken over from the enum module + * + * Copyright (C) 2021 Wolfgang Kampichler + * DEC112, FREQUENTIS AG + * + * This file is part of Kamailio, a free SIP server. + * + * Kamailio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version + * + * Kamailio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +/*! + * \file + * \brief Kamailio lost :: naptr + * \ingroup lost + * Module: \ref lost + */ + +#include "../../core/resolve.h" +#include "../../core/strutils.h" +#include "../../core/qvalue.h" + +/* Checks if NAPTR record has flag u and its services field + * LoST:https or LOST:http + * LIS:HELD + */ +static inline int service_match(struct naptr_rdata *naptr, str *service) +{ + if(service->len == 0) { + return 0; + } + + /* LoST:https or LOST:http or LIS:HELD */ + if(naptr->flags_len == 1) { + return (((naptr->flags[0] == 'u') || (naptr->flags[0] == 'U')) + && (naptr->services_len == service->len) + && (strncasecmp(naptr->services, service->s, + service->len) == 0)); + } + + /* LIS:HELD domain */ + if(naptr->flags_len == 0) { + return ((naptr->services_len == service->len) + && (strncasecmp(naptr->services, service->s, + service->len) == 0)); + } + + /* no matching service found */ + return 0; +} + +/* Parse NAPTR regexp field of the form !pattern!replacement! and return its + * components in pattern and replacement parameters. Regexp field starts at + * address first and is len characters long. + */ +static inline int parse_naptr_regexp( + char *first, int len, str *pattern, str *replacement) +{ + char *second, *third; + + if(len > 0) { + if(*first == '!') { + second = (char *)memchr((void *)(first + 1), '!', len - 1); + if(second) { + len = len - (second - first + 1); + if(len > 0) { + third = memchr(second + 1, '!', len); + if(third) { + pattern->len = second - first - 1; + pattern->s = first + 1; + replacement->len = third - second - 1; + replacement->s = second + 1; + return 1; + } else { + LM_ERR("Third ! missing from regexp\n"); + return -1; + } + } else { + LM_ERR("Third ! missing from regexp\n"); + return -2; + } + } else { + LM_ERR("Second ! missing from regexp\n"); + return -3; + } + } else { + LM_ERR("First ! missing from regexp\n"); + return -4; + } + } else { + LM_ERR("Regexp missing\n"); + return -5; + } +} + +/* + * Tests if one result record is "greater" that the other. Non-NAPTR records + * greater that NAPTR record. An invalid NAPTR record is greater than a + * valid one. Valid NAPTR records are compared based on their + * (order,preference). + */ +static inline int naptr_greater(struct rdata *a, struct rdata *b) +{ + struct naptr_rdata *na, *nb; + + if(a->type != T_NAPTR) + return 1; + if(b->type != T_NAPTR) + return 0; + + na = (struct naptr_rdata *)a->rdata; + if(na == 0) + return 1; + + nb = (struct naptr_rdata *)b->rdata; + if(nb == 0) + return 0; + + return (((na->order) << 16) + na->pref) > (((nb->order) << 16) + nb->pref); +} + +/* + * Bubble sorts result record list according to naptr (order,preference). + */ +static inline void naptr_sort(struct rdata **head) +{ + struct rdata *p, *q, *r, *s, *temp, *start; + + /* r precedes p and s points to the node up to which comparisons + are to be made */ + + s = NULL; + start = *head; + while(s != start->next) { + r = p = start; + q = p->next; + while(p != s) { + if(naptr_greater(p, q)) { + if(p == start) { + temp = q->next; + q->next = p; + p->next = temp; + start = q; + r = q; + } else { + temp = q->next; + q->next = p; + p->next = temp; + r->next = q; + r = q; + } + } else { + r = p; + p = p->next; + } + q = p->next; + if(q == s) + s = p; + } + } + *head = start; +} + +/* + * NAPTR lookup on hostname & service, returns result as string + */ +int lost_naptr_lookup(str hostname, str *service, str *result) +{ + struct rdata *head; + struct rdata *l; + struct naptr_rdata *naptr; + + str pattern, replacement; + + head = get_record(hostname.s, T_NAPTR, RES_ONLY_TYPE); + + if(head == 0) { + LM_DBG("no NAPTR record found for [%.*s]\n", hostname.len, hostname.s); + return 0; + } + + naptr_sort(&head); + + /* we have the naptr records, loop and find an srv record with */ + /* same ip address as source ip address, if we do then true is returned */ + + for(l = head; l; l = l->next) { + + if(l->type != T_NAPTR) + continue; /*should never happen*/ + naptr = (struct naptr_rdata *)l->rdata; + + if(naptr == 0) { + LM_ERR("no rdata in DNS response\n"); + free_rdata_list(head); + return 0; + } + + LM_DBG("NAPTR query on %.*s: order %u, pref %u, flen %u, flags " + "'%.*s', slen %u, services '%.*s', rlen %u, " + "regexp '%.*s'\n", + hostname.len, hostname.s, naptr->order, naptr->pref, + naptr->flags_len, (int)(naptr->flags_len), ZSW(naptr->flags), + naptr->services_len, (int)(naptr->services_len), + ZSW(naptr->services), naptr->regexp_len, + (int)(naptr->regexp_len), ZSW(naptr->regexp)); + + if(service_match(naptr, service) == 0) { + continue; + } + + if(parse_naptr_regexp(&(naptr->regexp[0]), naptr->regexp_len, &pattern, + &replacement) < 0) { + free_rdata_list(head); /*clean up*/ + LM_ERR("parsing of NAPTR regexp failed\n"); + return 0; + } + /* Avoid making copies of pattern and replacement */ + pattern.s[pattern.len] = (char)0; + replacement.s[replacement.len] = (char)0; + /* replace hostname */ + if(reg_replace(pattern.s, replacement.s, &(hostname.s[0]), + result) < 0) { + pattern.s[pattern.len] = '!'; + replacement.s[replacement.len] = '!'; + LM_ERR("regexp replace failed\n"); + free_rdata_list(head); /*clean up*/ + return 0; + } else { + + LM_DBG("resulted in replacement: '%.*s'\n", result->len, + ZSW(result->s)); + + free_rdata_list(head); /*clean up*/ + return 1; + } + } + + /* must not have found the record */ + return 0; +} \ No newline at end of file diff --git a/src/modules/lost/naptr.h b/src/modules/lost/naptr.h new file mode 100644 index 00000000000..c55a684ff61 --- /dev/null +++ b/src/modules/lost/naptr.h @@ -0,0 +1,38 @@ +/* + * lost module naptr functions + * + * Copyright (C) 2021 Wolfgang Kampichler + * DEC112, FREQUENTIS AG + * + * This file is part of Kamailio, a free SIP server. + * + * Kamailio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version + * + * Kamailio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +/*! + * \file + * \brief Kamailio lost :: naptr + * \ingroup lost + * Module: \ref lost + */ + +#ifndef LOST_NAPTR_H +#define LOST_NAPTR_H + +/* NAPTR lookup and host string replacement */ +int lost_naptr_lookup(str, str *, str *); + +#endif \ No newline at end of file diff --git a/src/modules/lost/pidf.c b/src/modules/lost/pidf.c index 927d39fbe51..73ba530a75a 100644 --- a/src/modules/lost/pidf.c +++ b/src/modules/lost/pidf.c @@ -123,7 +123,10 @@ char *xmlNodeGetNodeContentByName( xmlNodePtr xmlDocGetNodeByName(xmlDocPtr doc, const char *name, const char *ns) { xmlNodePtr cur = doc->children; - return xmlNodeGetNodeByName(cur, name, ns); + if(cur) + return xmlNodeGetNodeByName(cur, name, ns); + else + return NULL; } char *xmlDocGetNodeContentByName( diff --git a/src/modules/lost/response.c b/src/modules/lost/response.c new file mode 100644 index 00000000000..d4284a36ccf --- /dev/null +++ b/src/modules/lost/response.c @@ -0,0 +1,991 @@ +/* + * lost module LoST response parsing functions + * + * Copyright (C) 2021 Wolfgang Kampichler + * DEC112, FREQUENTIS AG + * + * This file is part of Kamailio, a free SIP server. + * + * Kamailio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version + * + * Kamailio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +/*! + * \file + * \brief Kamailio lost :: response + * \ingroup lost + * Module: \ref lost + */ + +#include +#include +#include +#include +#include + +#include "../../core/parser/msg_parser.h" +#include "../../core/parser/parse_content.h" +#include "../../core/parser/parse_uri.h" +#include "../../core/parser/parse_from.h" +#include "../../core/parser/parse_ppi_pai.h" +#include "../../core/dprint.h" +#include "../../core/mem/mem.h" +#include "../../core/mem/shm_mem.h" +#include "../../core/rand/kam_rand.h" + +#include +#include +#include +#include + +#include "pidf.h" +#include "utilities.h" +#include "response.h" + +/* + * is_http_laquot(search) + * return 1 if true else 0 + */ +int is_http_laquot(char *search) +{ + if(search == NULL) { + return 0; + } + if(strlen(search) < strlen("category = OTHER; + res->mapping = NULL; + res->path = NULL; + res->warnings = NULL; + res->errors = NULL; + res->redirect = NULL; + res->uri = NULL; + + LM_DBG("### reponse data initialized\n"); + + return res; +} + +/* + * lost_new_response_type(void) + * creates a new response type object in private memory and returns a pointer + */ +p_type_t lost_new_response_type(void) +{ + p_type_t res; + + res = (p_type_t)pkg_malloc(sizeof(s_type_t)); + if(res == NULL) { + LM_ERR("no more private memory\n"); + return NULL; + } + res->type = NULL; + res->target = NULL; + res->source = NULL; + + res->info = (p_info_t)pkg_malloc(sizeof(s_info_t)); + if(res->info == NULL) { + LM_ERR("no more private memory\n"); + } else { + res->info->text = NULL; + res->info->lang = NULL; + } + + LM_DBG("### type data initialized\n"); + + return res; +} + +/* + * lost_new_response_issues(void) + * creates a new issues object in private memory and returns a pointer + */ +p_issue_t lost_new_response_issues(void) +{ + p_issue_t res = NULL; + + res = (p_issue_t)pkg_malloc(sizeof(s_issue_t)); + if(res == NULL) { + LM_ERR("no more private memory\n"); + return NULL; + } + res->issue = NULL; + res->next = NULL; + + LM_DBG("### issues data initialized\n"); + + return res; +} + +/* + * lost_new_response_data(void) + * creates a new response data object in private memory and returns a pointer + */ +p_data_t lost_new_response_data(void) +{ + p_data_t res; + + res = (p_data_t)pkg_malloc(sizeof(s_data_t)); + if(res == NULL) { + LM_ERR("no more private memory\n"); + return NULL; + } + res->expires = NULL; + res->updated = NULL; + res->source = NULL; + res->sourceid = NULL; + res->urn = NULL; + res->name = NULL; + res->number = NULL; + + LM_DBG("### mapping data initialized\n"); + + return res; +} + +/* + * lost_new_response_list(void) + * creates a new response list object in private memory and returns a pointer + */ +p_list_t lost_new_response_list(void) +{ + p_list_t list; + + list = (p_list_t)pkg_malloc(sizeof(s_list_t)); + if(list == NULL) { + LM_ERR("no more private memory\n"); + return NULL; + } + + list->value = NULL; + list->next = NULL; + + LM_DBG("### list data initialized\n"); + + return list; +} + +/* + * lost_reverse_response_list(list) + * reverses list order of a list object + */ +void lost_reverse_response_list(p_list_t *head) +{ + p_list_t prev = NULL; + p_list_t next = NULL; + p_list_t current = *head; + + while(current != NULL) { + next = current->next; + current->next = prev; + prev = current; + current = next; + } + *head = prev; +} + +/* + * lost_delete_response_list(list) + * removes response list from private memory + */ +void lost_delete_response_list(p_list_t *list) +{ + p_list_t cur; + + if(*list == NULL) + return; + + while((cur = *list) != NULL) { + *list = cur->next; + if(cur->value != NULL) { + pkg_free(cur->value); + } + pkg_free(cur); + } + + *list = NULL; + + LM_DBG("### list data deleted\n"); + + return; +} + +/* + * lost_delete_response_msg(msg) + * removes response info from private memory + */ +void lost_delete_response_info(p_info_t *info) +{ + p_info_t ptr; + + if(*info == NULL) + return; + + ptr = *info; + + if(ptr->text != NULL) { + pkg_free(ptr->text); + } + if(ptr->lang != NULL) { + pkg_free(ptr->lang); + } + + pkg_free(ptr); + *info = NULL; + + LM_DBG("### info data deleted\n"); + + return; +} + +/* + * lost_delete_response_msg(type) + * removes response type from private memory + */ +void lost_delete_response_type(p_type_t *type) +{ + p_type_t ptr; + + if(*type == NULL) + return; + + ptr = *type; + + if(ptr->type != NULL) { + pkg_free(ptr->type); + } + if(ptr->target != NULL) { + pkg_free(ptr->target); + } + if(ptr->source != NULL) { + pkg_free(ptr->source); + } + if(ptr->info != NULL) { + lost_delete_response_info(&ptr->info); + } + + pkg_free(ptr); + *type = NULL; + + LM_DBG("### type data deleted\n"); + + return; +} + +/* + * lost_delete_response_issue(list) + * removes response issue object from private memory + */ +void lost_delete_response_issues(p_issue_t *list) +{ + p_issue_t cur; + + while((cur = *list) != NULL) { + *list = cur->next; + if(cur->issue != NULL) { + lost_delete_response_type(&cur->issue); + } + pkg_free(cur); + } + + *list = NULL; + + LM_DBG("### issue data deleted\n"); + + return; +} + +/* + * lost_delete_response_issue(mapping) + * removes respone data object from private memory + */ +void lost_delete_response_data(p_data_t *m) +{ + p_data_t ptr; + + if(*m == NULL) + return; + + ptr = *m; + + if(ptr->expires != NULL) { + pkg_free(ptr->expires); + } + if(ptr->updated != NULL) { + pkg_free(ptr->updated); + } + if(ptr->source != NULL) { + pkg_free(ptr->source); + } + if(ptr->sourceid != NULL) { + pkg_free(ptr->sourceid); + } + if(ptr->urn != NULL) { + pkg_free(ptr->urn); + } + if(ptr->name != NULL) { + lost_delete_response_info(&ptr->name); + } + if(ptr->number != NULL) { + pkg_free(ptr->number); + } + + pkg_free(ptr); + *m = NULL; + + LM_DBG("### mapping data deleted\n"); + + return; +} + +/* + * lost_free_findServiceResponse(response) + * removes findServiceResponse object from private memory + */ +void lost_free_findServiceResponse(p_fsr_t *res) +{ + p_fsr_t ptr; + + if(*res == NULL) + return; + + ptr = *res; + + if(ptr->mapping != NULL) { + lost_delete_response_data(&ptr->mapping); + } + if(ptr->path != NULL) { + lost_delete_response_list(&ptr->path); + } + if(ptr->warnings != NULL) { + lost_delete_response_issues(&ptr->warnings); + } + if(ptr->errors != NULL) { + lost_delete_response_issues(&ptr->errors); + } + if(ptr->redirect != NULL) { + lost_delete_response_type(&ptr->redirect); + } + if(ptr->uri != NULL) { + lost_delete_response_list(&ptr->uri); + } + + pkg_free(ptr); + *res = NULL; + + LM_DBG("### findServiceResponse deleted\n"); + + return; +} + +/* + * lost_get_response_issue(node) + * parses response issue (errors, warnings) and writes + * results to issue object + */ +p_issue_t lost_get_response_issues(xmlNodePtr node) +{ + xmlNodePtr cur = NULL; + + p_issue_t list = NULL; + p_issue_t new = NULL; + p_type_t issue = NULL; + + str tmp = STR_NULL; + + int len = 0; + + if(node == NULL) { + return NULL; + ; + } + + LM_DBG("### LOST\t%s\n", node->name); + + cur = node->children; + while(cur) { + if(cur->type == XML_ELEMENT_NODE) { + /* get a new list element */ + new = lost_new_response_issues(); + if(new == NULL) { + return NULL; + } + /* parse the element */ + issue = lost_new_response_type(); + if(issue == NULL) { + LM_ERR("no more private memory\n"); + pkg_free(list); + pkg_free(new); + return NULL; + } + issue->source = lost_get_property(cur->parent, MAPP_PROP_SRC, &len); + tmp.s = (char *)cur->name; + tmp.len = strlen((char *)cur->name); + if(tmp.len > 0 && tmp.s != NULL) { + issue->type = lost_copy_string(tmp, &len); + } + if(len > 0) { + + LM_DBG("###\t[%s]\n", issue->type); + + if(issue->info != NULL) { + issue->info->text = lost_get_property(cur, PROP_MSG, &len); + issue->info->lang = lost_get_property(cur, PROP_LANG, &len); + } + /* append to list */ + new->issue = issue; + new->next = list; + list = new; + } else { + lost_delete_response_issues(&new); /* clean up */ + } + /* get next element */ + cur = cur->next; + } + } + + return list; +} + +/* + * lost_get_response_list(node, name, property) + * parses response list and writes results to list object + */ +p_list_t lost_get_response_list( + xmlNodePtr node, const char *name, const char *prop) +{ + xmlNodePtr cur = NULL; + + p_list_t list = NULL; + p_list_t new = NULL; + + str tmp = STR_NULL; + int len = 0; + + if(node == NULL) { + return list; + } + + LM_DBG("### LOST\t%s\n", node->name); + + for(cur = node; cur; cur = cur->next) { + if(cur->type == XML_ELEMENT_NODE) { + if(!xmlStrcasecmp(cur->name, (unsigned char *)name)) { + new = lost_new_response_list(); + if(new != NULL) { + if(prop) { + tmp.s = lost_get_property(cur, prop, &tmp.len); + } else { + tmp.s = lost_get_content(cur, name, &tmp.len); + } + if(tmp.len > 0 && tmp.s != NULL) { + new->value = lost_copy_string(tmp, &len); + + LM_DBG("###\t[%s]\n", new->value); + + new->next = list; + list = new; + lost_free_string(&tmp); /* clean up */ + } else { + lost_delete_response_list(&new); /* clean up */ + } + } + } + } else { + /* not an uri element */ + break; + } + } + + return list; +} + +/* + * lost_get_response_element(node, name) + * parses response element and returns a char pointer + */ +char *lost_get_response_element(xmlNodePtr node, const char *name) +{ + char *ret = NULL; + int len = 0; + + if(node == NULL) { + return ret; + } + + LM_DBG("### LOST %s\n", node->name); + + ret = lost_get_content(node, name, &len); + + LM_DBG("###\t[%.*s]\n", len, ret); + + return ret; +} + +/* + * lost_get_response_type(node, name) + * parses response type and writes results to type object + */ +p_type_t lost_get_response_type(xmlNodePtr node, const char *name) +{ + p_type_t res = NULL; + + str tmp = STR_NULL; + + int len = 0; + + if(node == NULL) { + return res; + } + + LM_DBG("### LOST %s\n", node->name); + + tmp.s = lost_get_childname(node, name, &tmp.len); + if(tmp.len > 0 && tmp.s != NULL) { + res = lost_new_response_type(); + if(res != NULL) { + res->type = lost_copy_string(tmp, &len); + if(len > 0) { + + LM_DBG("###\t[%s]\n", res->type); + } + if(res->info != NULL) { + res->info->text = + lost_get_property(node->children, PROP_MSG, &len); + res->info->lang = + lost_get_property(node->children, PROP_LANG, &len); + } + } + lost_free_string(&tmp); /* clean up */ + } + + return res; +} + +/* + * lost_get_response_info(node, name, property) + * parses response info (text, language) and writes results to info object + */ +p_info_t lost_get_response_info( + xmlNodePtr node, const char *name, const char *prop) +{ + p_info_t res = NULL; + + str tmp = STR_NULL; + + int len = 0; + + if(node == NULL) { + return res; + } + + LM_DBG("### LOST %s\n", node->name); + + res = (p_info_t)pkg_malloc(sizeof(s_info_t)); + if(res == NULL) { + LM_ERR("no more private memory\n"); + return NULL; + } else { + res->text = NULL; + res->lang = NULL; + if(prop) { + tmp.s = lost_get_property(node, PROP_MSG, &tmp.len); + } else { + tmp.s = lost_get_content(node, name, &tmp.len); + } + if(tmp.len > 0 && tmp.s != NULL) { + res->text = lost_copy_string(tmp, &len); + if(len > 0) { + + LM_DBG("###\t\t[%s]\n", res->text); + } + lost_free_string(&tmp); /* clean up */ + } + res->lang = lost_get_property(node, PROP_LANG, &len); + + LM_DBG("###\t\t[%s]\n", res->lang); + } + + return res; +} + +/* + * lost_print_findServiceResponse(response) + * prints/logs response elements + */ +void lost_print_findServiceResponse(p_fsr_t res) +{ + if(res == NULL) { + return; + } + p_data_t m = NULL; + p_type_t r = NULL; + p_issue_t e = NULL; + p_issue_t w = NULL; + p_type_t t = NULL; + p_list_t list = NULL; + + switch(res->category) { + case RESPONSE: + if((m = res->mapping) != NULL) { + LM_INFO("### LOST %s:\t[%s]\n", MAPP_PROP_EXP, m->expires); + LM_INFO("### LOST %s:\t[%s]\n", MAPP_PROP_LUP, m->updated); + LM_INFO("### LOST %s:\t[%s]\n", MAPP_PROP_SRC, m->source); + LM_INFO("### LOST %s:\t[%s]\n", MAPP_PROP_SID, m->sourceid); + if(m->name) { + LM_INFO("### LOST %s:\t[%s (%s)]\n", DISPNAME_NODE, + m->name->text, m->name->lang); + } + LM_INFO("### LOST %s:\t[%s]\n", SERVICENR_NODE, m->number); + LM_INFO("### LOST %s:\t[%s]\n", SERVICE_NODE, m->urn); + if((list = res->uri) != NULL) { + while(list) { + LM_INFO("### LOST %s:\t[%s]\n", MAPP_NODE_URI, + list->value); + list = list->next; + } + } + if((list = res->path) != NULL) { + while(list) { + LM_INFO("### LOST %s:\t[%s]\n", PATH_NODE_VIA, + list->value); + list = list->next; + } + } + if((w = res->warnings) != NULL) { + while(w) { + t = w->issue; + LM_INFO("###! LOST %s reported ...\n", t->source); + LM_INFO("###! LOST %s:\n", t->type); + if(t->info) { + LM_INFO("###! LOST\t[%s (%s)]\n", t->info->text, + t->info->lang); + } + w = w->next; + } + } + } + break; + case REDIRECT: + if((r = res->redirect) != NULL) { + LM_INFO("### LOST %s:\t[%s]\n", RED_PROP_TAR, r->target); + LM_INFO("### LOST %s:\t[%s]\n", RED_PROP_SRC, r->source); + if(r->info) { + LM_INFO("### LOST %s:\t[%s (%s)]\n", PROP_MSG, + r->info->text, r->info->lang); + } + } + break; + case ERROR: + if((e = res->errors) != NULL) { + while(e) { + t = e->issue; + LM_INFO("###! LOST %s reported ...\n", t->source); + LM_INFO("###! LOST %s:\n", t->type); + if(t->info) { + LM_INFO("###! LOST\t[%s (%s)]\n", t->info->text, + t->info->lang); + } + e = e->next; + } + } + break; + case OTHER: + default: + break; + } + + return; +} + +/* + * lost_parse_findServiceResponse(str) + * read and parse the findServiceResponse xml string + */ +p_fsr_t lost_parse_findServiceResponse(str ret) +{ + xmlDocPtr doc = NULL; + xmlNodePtr root = NULL; + xmlNodePtr node = NULL; + + p_fsr_t res = NULL; + p_data_t m = NULL; + p_type_t r = NULL; + + int len = 0; + + doc = xmlReadMemory(ret.s, ret.len, 0, 0, + XML_PARSE_NOBLANKS | XML_PARSE_NONET | XML_PARSE_NOCDATA); + + if(doc == NULL) { + LM_ERR("invalid xml document: [%.*s]\n", ret.len, ret.s); + doc = xmlRecoverMemory(ret.s, ret.len); + if(doc == NULL) { + LM_ERR("xml document recovery failed on: [%.*s]\n", ret.len, ret.s); + return NULL; + } + + LM_DBG("xml document recovered\n"); + } + root = xmlDocGetRootElement(doc); + if(root == NULL) { + LM_ERR("empty xml document: [%.*s]\n", ret.len, ret.s); + xmlFreeDoc(doc); /* clean up */ + return NULL; + } + + LM_DBG("### %s received\n", root->name); + + res = lost_new_response(); + if(res == NULL) { + LM_ERR("no more private memory\n"); + xmlFreeDoc(doc); /* clean up */ + return NULL; + } + /* check the root element ... */ + /* findServiceResponse */ + if(!xmlStrcmp(root->name, (unsigned char *)ROOT_NODE)) { + m = lost_new_response_data(); + if(m == NULL) { + LM_ERR("no more private memory\n"); + /* clean up */ + xmlFreeDoc(doc); + lost_free_findServiceResponse(&res); + return NULL; + } + /* get mapping properties */ + node = xmlDocGetNodeByName(doc, MAPP_NODE, NULL); + m->expires = lost_get_property(node, MAPP_PROP_EXP, &len); + m->updated = lost_get_property(node, MAPP_PROP_LUP, &len); + m->source = lost_get_property(node, MAPP_PROP_SRC, &len); + m->sourceid = lost_get_property(node, MAPP_PROP_SID, &len); + node = NULL; + /* get the displayName element */ + node = xmlDocGetNodeByName(doc, DISPNAME_NODE, NULL); + m->name = lost_get_response_info(node, DISPNAME_NODE, NULL); + /* get the serviceNumber element */ + m->number = lost_get_response_element(root, SERVICENR_NODE); + /* get the service element */ + m->urn = lost_get_response_element(root, SERVICE_NODE); + node = NULL; + /* get the via path (list) */ + node = xmlDocGetNodeByName(doc, PATH_NODE_VIA, NULL); + res->path = lost_get_response_list(node, PATH_NODE_VIA, MAPP_PROP_SRC); + lost_reverse_response_list(&res->path); + node = NULL; + /* get the uri element list */ + node = xmlDocGetNodeByName(doc, MAPP_NODE_URI, NULL); + res->uri = lost_get_response_list(node, MAPP_NODE_URI, NULL); + lost_reverse_response_list(&res->uri); + node = NULL; + /* get the warnings element */ + node = xmlDocGetNodeByName(doc, WARNINGS_NODE, NULL); + res->warnings = lost_get_response_issues(node); + /* return */ + res->mapping = m; + res->category = RESPONSE; + /* check the root element ... */ + /* errors */ + } else if(!xmlStrcmp(root->name, (unsigned char *)ERRORS_NODE)) { + res->errors = lost_get_response_issues(root); + /* return */ + res->category = ERROR; + /* check the root element ... */ + /* redirect */ + } else if(!xmlStrcmp(root->name, (unsigned char *)RED_NODE)) { + r = lost_new_response_type(); + if(r == NULL) { + LM_ERR("no more private memory\n"); + xmlFreeDoc(doc); /* clean up */ + lost_free_findServiceResponse(&res); + return NULL; + } + r->source = lost_get_property(root, RED_PROP_SRC, &len); + r->target = lost_get_property(root, RED_PROP_TAR, &len); + if(r->info != NULL) { + r->info->text = lost_get_property(root, RED_PROP_MSG, &len); + r->info->lang = lost_get_property(root, PROP_LANG, &len); + } + /* return */ + res->redirect = r; + res->category = REDIRECT; + /* check the root element ... */ + /* unknown */ + } else { + LM_ERR("root element is not valid: [%.*s]\n", ret.len, ret.s); + res->category = OTHER; + } + + xmlFreeDoc(doc); /* clean up */ + + return res; +} \ No newline at end of file diff --git a/src/modules/lost/response.h b/src/modules/lost/response.h new file mode 100644 index 00000000000..fc9e958b514 --- /dev/null +++ b/src/modules/lost/response.h @@ -0,0 +1,131 @@ +/* + * lost module LoST response parsing functions + * + * Copyright (C) 2021 Wolfgang Kampichler + * DEC112, FREQUENTIS AG + * + * This file is part of Kamailio, a free SIP server. + * + * Kamailio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version + * + * Kamailio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +/*! + * \file + * \brief Kamailio lost :: response + * \ingroup lost + * Module: \ref lost + */ + +#ifndef LOST_RESPONSE_H +#define LOST_RESPONSE_H + +#define PROP_LANG (const char *)"lang" +#define PROP_MSG (const char *)"message" + +#define ROOT_NODE (const char *)"findServiceResponse" +#define DISPNAME_NODE (const char *)"displayName" +#define SERVICE_NODE (const char *)"service" +#define SERVICENR_NODE (const char *)"serviceNumber" +#define WARNINGS_NODE (const char *)"warnings" +#define PATH_NODE (const char *)"via" +#define PATH_NODE_VIA (const char *)"via" +#define MAPP_NODE (const char *)"mapping" +#define MAPP_NODE_URI (const char *)"uri" +#define MAPP_PROP_EXP (const char *)"expires" +#define MAPP_PROP_LUP (const char *)"lastUpdated" +#define MAPP_PROP_SRC (const char *)"source" +#define MAPP_PROP_SID (const char *)"sourceId" + +#define RED_NODE (const char *)"redirect" +#define RED_PROP_TAR (const char *)"target" +#define RED_PROP_SRC (const char *)"source" +#define RED_PROP_MSG (const char *)"message" + +#define ERRORS_NODE (const char *)"errors" + +typedef struct LIST +{ + char *value; + struct LIST *next; +} s_list_t, *p_list_t; + +typedef struct INFO +{ + char *text; + char *lang; +} s_info_t, *p_info_t; + +typedef struct DATA +{ + char *expires; + char *updated; + char *source; + char *sourceid; + char *urn; + char *number; + p_info_t name; +} s_data_t, *p_data_t; + +typedef struct TYPE +{ + char *type; + char *target; + char *source; + p_info_t info; +} s_type_t, *p_type_t; + +typedef struct ISSUE +{ + p_type_t issue; + struct ISSUE *next; +} s_issue_t, *p_issue_t; + +typedef enum CAT +{ + RESPONSE, + ERROR, + REDIRECT, + OTHER = -1 +} cat_t; + +typedef struct FSR +{ + cat_t category; + p_data_t mapping; + p_issue_t warnings; + p_issue_t errors; + p_type_t redirect; + p_list_t path; + p_list_t uri; +} s_fsr_t, *p_fsr_t; + +/* read and parse response data */ +p_fsr_t lost_parse_findServiceResponse(str); +/* print the response */ +void lost_print_findServiceResponse(p_fsr_t); +/* remove response data from memory */ +void lost_free_findServiceResponse(p_fsr_t *); + +/* uri scheme parsing */ +int is_urn(char *); +int is_cid(char *); +int is_http(char *); +int is_https(char *); +int is_cid_laquot(char *); +int is_http_laquot(char *); +int is_https_laquot(char *); + +#endif diff --git a/src/modules/lost/utilities.c b/src/modules/lost/utilities.c index fca503cbf65..8f56a618164 100644 --- a/src/modules/lost/utilities.c +++ b/src/modules/lost/utilities.c @@ -1,7 +1,7 @@ /* * lost module utility functions * - * Copyright (C) 2020 Wolfgang Kampichler + * Copyright (C) 2021 Wolfgang Kampichler * DEC112, FREQUENTIS AG * * This file is part of Kamailio, a free SIP server. @@ -34,6 +34,7 @@ #include #include #include +#include #include "../../core/parser/msg_parser.h" #include "../../core/parser/parse_content.h" @@ -48,6 +49,9 @@ #include "pidf.h" #include "utilities.h" +extern int lost_recursion; +extern int lost_profile; + /* * lost_trim_content(dest, lgth) * removes whitespace that my occur in a content of an xml element @@ -56,6 +60,11 @@ char *lost_trim_content(char *str, int *lgth) { char *end; + *lgth = 0; + + if(str == NULL) + return NULL; + while(isspace(*str)) str++; @@ -134,8 +143,8 @@ p_loc_t lost_new_loc(str rurn) ptr->xpath = NULL; ptr->profile = NULL; ptr->radius = 0; - ptr->recursive = LOST_RECURSION_TRUE; /* set recursion to true */ - ptr->boundary = 0; /* set boundary to reference */ + ptr->recursive = lost_recursion; /* set recursion to param */ + ptr->boundary = LOST_BOUNDARY_FALSE; /* set boundary to reference */ return ptr; @@ -197,10 +206,19 @@ p_held_t lost_new_held(str s_uri, str s_type, int time, int exact) * lost_free_loc(ptr) * frees a location object */ -void lost_free_loc(p_loc_t ptr) +void lost_free_loc(p_loc_t *loc) { - pkg_free(ptr->identity); - pkg_free(ptr->urn); + p_loc_t ptr; + + if(*loc == NULL) + return; + + ptr = *loc; + + if(ptr->identity) + pkg_free(ptr->identity); + if(ptr->urn) + pkg_free(ptr->urn); if(ptr->xpath) pkg_free(ptr->xpath); if(ptr->geodetic) @@ -213,20 +231,55 @@ void lost_free_loc(p_loc_t ptr) pkg_free(ptr->profile); pkg_free(ptr); - ptr = NULL; + *loc = NULL; } /* * lost_free_loc(ptr) * frees a held location request object */ -void lost_free_held(p_held_t ptr) +void lost_free_held(p_held_t *held) { - pkg_free(ptr->identity); - pkg_free(ptr->type); + p_held_t ptr; + + if(held == NULL) + return; + + ptr = *held; + + if(ptr->identity) + pkg_free(ptr->identity); + if(ptr->type) + pkg_free(ptr->type); pkg_free(ptr); - ptr = NULL; + *held = NULL; +} + +/* + * lost_copy_string(str, int*) { + * copies a string and returns string allocated in private memory + */ +char *lost_copy_string(str src, int *lgth) +{ + char *res = NULL; + + if(src.s == NULL && src.len == 0) { + *lgth = 0; + return NULL; + } + res = (char *)pkg_malloc((src.len + 1) * sizeof(char)); + if(res == NULL) { + LM_ERR("no more private memory\n"); + *lgth = 0; + } else { + memset(res, 0, src.len + 1); + memcpy(res, src.s, src.len + 1); + res[src.len] = '\0'; + *lgth = (int)strlen(res); + } + + return res; } /* @@ -235,23 +288,33 @@ void lost_free_held(p_held_t ptr) */ void lost_free_string(str *string) { - str ptr = *string; + str ptr = STR_NULL; - if(ptr.s) { + if(string->s == NULL) + return; + + ptr = *string; + + if(ptr.s != NULL && ptr.len > 0) { pkg_free(ptr.s); - ptr.s = NULL; - ptr.len = 0; } + + string->s = NULL; + string->len = 0; + + return; } /* * lost_get_content(node, name, lgth) - * gets a nodes "name" content and returns string allocated in private memory + * gets a nodes "name" content, removes whitespace and returns string + * allocated in private memory */ char *lost_get_content(xmlNodePtr node, const char *name, int *lgth) { xmlNodePtr cur = node; char *content = NULL; + char *trimmed = NULL; char *cnt = NULL; int len; @@ -262,7 +325,7 @@ char *lost_get_content(xmlNodePtr node, const char *name, int *lgth) LM_ERR("could not get XML node content\n"); return cnt; } else { - len = strlen(content); + trimmed = lost_trim_content(content, &len); cnt = (char *)pkg_malloc((len + 1) * sizeof(char)); if(cnt == NULL) { LM_ERR("no more private memory\n"); @@ -270,7 +333,7 @@ char *lost_get_content(xmlNodePtr node, const char *name, int *lgth) return cnt; } memset(cnt, 0, len + 1); - memcpy(cnt, content, len); + memcpy(cnt, trimmed, len); cnt[len] = '\0'; } @@ -325,7 +388,10 @@ char *lost_get_childname(xmlNodePtr node, const char *name, int *lgth) xmlNodePtr cur = node; xmlNodePtr parent = NULL; xmlNodePtr child = NULL; + char *cnt = NULL; + char *trimmed = NULL; + int len; *lgth = 0; @@ -335,22 +401,19 @@ char *lost_get_childname(xmlNodePtr node, const char *name, int *lgth) LM_ERR("xmlNodeGetNodeByName() failed\n"); return cnt; } - child = parent->children; if(child == NULL) { LM_ERR("%s has no children '%s'\n", parent->name, name); return cnt; } - - len = strlen((char *)child->name); + trimmed = lost_trim_content((char *)child->name, &len); cnt = (char *)pkg_malloc((len + 1) * sizeof(char)); if(cnt == NULL) { LM_ERR("no more private memory\n"); return cnt; } - memset(cnt, 0, len + 1); - memcpy(cnt, child->name, len); + memcpy(cnt, trimmed, len); cnt[len] = '\0'; *lgth = strlen(cnt); @@ -359,20 +422,19 @@ char *lost_get_childname(xmlNodePtr node, const char *name, int *lgth) } /* - * lost_get_geolocation_header(msg, lgth) - * gets the Geolocation header value and returns string allocated in - * private memory + * lost_get_geolocation_header(msg, hdr) + * gets the Geolocation header value and returns 1 on success */ -char *lost_get_geolocation_header(struct sip_msg *msg, int *lgth) +p_geolist_t lost_get_geolocation_header(struct sip_msg *msg, int *items) { struct hdr_field *hf; - char *res = NULL; - - *lgth = 0; + str hdr = STR_NULL; + p_geolist_t list = NULL; if(parse_headers(msg, HDR_EOH_F, 0) == -1) { - LM_ERR("failed to parse geolocation header\n"); - return res; + LM_ERR("failed to parse SIP headers\n"); + *items = 0; + return list; } for(hf = msg->headers; hf; hf = hf->next) { @@ -382,26 +444,14 @@ char *lost_get_geolocation_header(struct sip_msg *msg, int *lgth) if(strncasecmp(hf->name.s, LOST_GEOLOC_HEADER, LOST_GEOLOC_HEADER_SIZE) == 0) { - res = (char *)pkg_malloc((hf->body.len + 1) * sizeof(char)); - if(res == NULL) { - LM_ERR("no more private memory\n"); - return res; - } else { - memset(res, 0, hf->body.len + 1); - memcpy(res, hf->body.s, hf->body.len + 1); - res[hf->body.len] = '\0'; + LM_DBG("found geolocation header [%.*s]\n", hdr.len, hdr.s); - *lgth = strlen(res); - } - } else { - LM_ERR("header '%.*s' length %d\n", hf->body.len, hf->body.s, - hf->body.len); + *items += lost_new_geoheader_list(&list, hdr); } - break; } } - return res; + return list; } /* @@ -472,6 +522,103 @@ char *lost_get_pai_header(struct sip_msg *msg, int *lgth) return res; } +/* + * lost_parse_host(uri, string) + * parses host (ipv4, ipv6 or name) from uri and returns string + */ +int lost_parse_host(const char *uri, str *host, int *flag) +{ + char *search = (char *)uri; + char *end; + + int len = 0; + int ip6 = 0; + + while((len < strlen(uri)) && (*search++ != '@')) { + len++; + } + + if(len == strlen(uri)) { + return 0; + } + + if(*(search) == 0) + return 0; + + if(*(search) == '[') { + ip6 = 1; + } + + end = search; + + if(ip6) { + while((len < strlen(uri)) && (*end++ != ']')) { + len++; + } + if(len == strlen(uri)) { + return 0; + } + } else { + while(len < strlen(uri)) { + if((*end == ':') || (*end == '>')) { + break; + } + end++; + len++; + } + } + + if(*(search) == 0) + return 0; + + host->s = search; + host->len = end - search; + + if(ip6) { + *flag = AF_INET; + } else { + *flag = AF_INET; + } + + return 1; +} + +/* + * lost_get_nameinfo(ip, name, flag) + * translates socket address to service name (flag: IPv4 or IPv6) + */ +int lost_get_nameinfo(char *ip, str *name, int flag) +{ + struct sockaddr_in sa4; + struct sockaddr_in6 sa6; + + if(flag == AF_INET) { + bzero(&sa4, sizeof(sa4)); + sa4.sin_family = flag; + if(inet_pton(flag, ip, &sa4.sin_addr) <= 0) + return 0; + if(getnameinfo((struct sockaddr *)&sa4, sizeof(sa4), name->s, name->len, + NULL, 0, NI_NAMEREQD)) + return 0; + + return 1; + } + + if(flag == AF_INET6) { + bzero(&sa6, sizeof(sa6)); + sa6.sin6_family = flag; + if(inet_pton(flag, ip, &sa6.sin6_addr) <= 0) + return 0; + if(getnameinfo((struct sockaddr *)&sa6, sizeof(sa6), name->s, name->len, + NULL, 0, NI_NAMEREQD)) + return 0; + + return 1; + } + + return 0; +} + /* * lost_get_from_header(msg, lgth) * gets the From header value and returns string allocated in @@ -513,16 +660,15 @@ char *lost_get_from_header(struct sip_msg *msg, int *lgth) } /* - * lost_delete_geoheader_list(list) + * lost_free_geoheader_list(list) * removes geoheader list from private memory */ -void lost_delete_geoheader_list(p_geolist_t list) +void lost_free_geoheader_list(p_geolist_t *list) { - p_geolist_t curr; - while((curr = list) != NULL) { - list = curr->next; + while((curr = *list) != NULL) { + *list = curr->next; if(curr->value != NULL) { pkg_free(curr->value); } @@ -532,7 +678,7 @@ void lost_delete_geoheader_list(p_geolist_t list) pkg_free(curr); } - list = NULL; + *list = NULL; return; } @@ -543,7 +689,6 @@ void lost_delete_geoheader_list(p_geolist_t list) */ char *lost_get_geoheader_value(p_geolist_t list, geotype_t type, int *rtype) { - p_geolist_t head = list; char *value = NULL; @@ -570,14 +715,12 @@ char *lost_get_geoheader_value(p_geolist_t list, geotype_t type, int *rtype) return value; } - /* * lost_reverse_geoheader_list(list) * reverses list order */ void lost_reverse_geoheader_list(p_geolist_t *head) { - p_geolist_t prev = NULL; p_geolist_t next = NULL; p_geolist_t current = *head; @@ -598,7 +741,6 @@ void lost_reverse_geoheader_list(p_geolist_t *head) */ char *lost_copy_geoheader_value(char *src, int len) { - char *res = NULL; res = (char *)pkg_malloc((len + 1) * sizeof(char)); @@ -619,9 +761,8 @@ char *lost_copy_geoheader_value(char *src, int len) * searches and parses Geolocation header and returns a list * allocated in private memory and an item count */ -p_geolist_t lost_new_geoheader_list(str hdr, int *items) +int lost_new_geoheader_list(p_geolist_t *list, str hdr) { - char *search = NULL; char *cidptr = NULL; char *urlptr = NULL; @@ -631,7 +772,6 @@ p_geolist_t lost_new_geoheader_list(str hdr, int *items) int len = 0; int i = 0; - p_geolist_t list = NULL; p_geolist_t new = NULL; LM_DBG("parsing geolocation header value ...\n"); @@ -669,8 +809,8 @@ p_geolist_t lost_new_geoheader_list(str hdr, int *items) new->value = lost_copy_geoheader_value(cidptr, len + 1); new->param = NULL; new->type = CID; - new->next = list; - list = new; + new->next = *list; + *list = new; count++; LM_DBG("adding cid [%s]\n", new->value); @@ -678,6 +818,7 @@ p_geolist_t lost_new_geoheader_list(str hdr, int *items) } else { LM_WARN("invalid value: [%.*s]\n", hdr.len, hdr.s); } + *cidptr = COLON; } } @@ -722,8 +863,8 @@ p_geolist_t lost_new_geoheader_list(str hdr, int *items) new->type = HTTPS; } - new->next = list; - list = new; + new->next = *list; + *list = new; count++; } } else { @@ -734,9 +875,7 @@ p_geolist_t lost_new_geoheader_list(str hdr, int *items) search++; } - *items = count; - - return list; + return count; } /* @@ -790,8 +929,7 @@ p_loc_t lost_parse_pidf(str pidf, str urn) pidf.len, pidf.s); goto err; } - - /* free memory */ + /* clean up */ xmlFreeDoc(doc); doc = NULL; @@ -799,14 +937,13 @@ p_loc_t lost_parse_pidf(str pidf, str urn) err: LM_ERR("pidflo parsing error\n"); - /* free memory */ + /* clean up */ + lost_free_loc(&loc); if(doc != NULL) { xmlFreeDoc(doc); doc = NULL; } - if(loc != NULL) { - lost_free_loc(loc); - } + return NULL; } @@ -915,6 +1052,9 @@ int lost_xpath_location(xmlDocPtr doc, char *path, p_loc_t loc) int len = 0; int i = 0; int nok = -1; + int select = -1; + int selgeo = -1; + int selciv = -1; xpath = (xmlChar *)path; /* get location via xpath expression */ @@ -947,14 +1087,17 @@ int lost_xpath_location(xmlDocPtr doc, char *path, p_loc_t loc) cname = BAD_CAST cur->name; if(xmlStrcasecmp(cname, s_point) == 0) { s_profile = LOST_PRO_GEO2D; + selgeo = i; break; } if(xmlStrcasecmp(cname, s_circle) == 0) { s_profile = LOST_PRO_GEO2D; + selgeo = i; break; } if(xmlStrcasecmp(cname, s_civic) == 0) { s_profile = LOST_PRO_CIVIC; + selciv = i; break; } /* nothing found ... try next DOM level */ @@ -1005,8 +1148,42 @@ int lost_xpath_location(xmlDocPtr doc, char *path, p_loc_t loc) xmlXPathFreeObject(result); return -1; } - /* take the first location-info element only */ - if(i == 0) { + /* take the first location-info element */ + if(lost_profile == 0) { + + LM_DBG("first location-info element [%d]\n", i); + + select = 0; + /* take the last location-info element */ + } else if(lost_profile == 1) { + + LM_DBG("last location-info element [%d]\n", i); + + select = size - 1; + /* take the first geodetic location-info element */ + } else if(lost_profile == 2) { + + LM_DBG("first geodetic location-info element [%d]\n", i); + + select = selgeo; + /* take the first civic location-info element */ + } else if(lost_profile == 3) { + + LM_DBG("first civic location-info element [%d]\n", i); + + select = selciv; + } + + /* nothing found */ + if((select < 0) && (i == size - 1)) { + LM_ERR("could not find proper location-info element\n"); + xmlFree(xmlbuff); + xmlFreeDoc(new); + xmlXPathFreeObject(result); + return -1; + } + + if(i == select) { /* return the current profile */ loc->profile = (char *)pkg_malloc(strlen(s_profile) + 1); if(loc->profile == NULL) { @@ -1078,7 +1255,6 @@ int lost_xpath_location(xmlDocPtr doc, char *path, p_loc_t loc) */ int lost_parse_location_info(xmlNodePtr root, p_loc_t loc) { - if(lost_xpath_location(root->doc, LOST_XPATH_GP, loc) == 0) { return 0; } @@ -1093,7 +1269,104 @@ int lost_parse_location_info(xmlNodePtr root, p_loc_t loc) } /* - * lost_held_location_request(id, lgth, responsetime, exact, type) + * lost_held_post_request(lgth) + * assembles and returns locationRequest string (allocated in private memory) + */ +char *lost_held_post_request(int *lgth, long rtime, char *type) +{ + int buffersize = 0; + + char buf[BUFSIZE]; + char *doc = NULL; + + xmlChar *xmlbuff = NULL; + xmlDocPtr request = NULL; + + xmlNodePtr ptrLocationRequest = NULL; + xmlNodePtr ptrLocationType = NULL; + + xmlKeepBlanksDefault(1); + *lgth = 0; + + /* +https://tools.ietf.org/html/rfc6753 + + + + + geodetic civic + + +*/ + + /* create request */ + request = xmlNewDoc(BAD_CAST "1.0"); + if(request == NULL) { + LM_ERR("locationRequest xmlNewDoc() failed\n"); + return doc; + } + /* locationRequest - element */ + ptrLocationRequest = xmlNewNode(NULL, BAD_CAST "locationRequest"); + if(ptrLocationRequest == NULL) { + LM_ERR("locationRequest xmlNewNode() failed\n"); + xmlFreeDoc(request); + return doc; + } + + xmlDocSetRootElement(request, ptrLocationRequest); + /* properties */ + xmlNewProp(ptrLocationRequest, BAD_CAST "xmlns", + BAD_CAST "urn:ietf:params:xml:ns:geopriv:held"); + /* responseTime - element (default: emergencyRouting) */ + if(rtime > 0) { + /* set responseTime "") */ + snprintf(buf, BUFSIZE, "%ld", rtime); + xmlNewProp(ptrLocationRequest, BAD_CAST HELD_RT, BAD_CAST buf); + } else if(rtime == -1) { + /* set responseTime "emergencyDispatch" */ + xmlNewProp(ptrLocationRequest, BAD_CAST HELD_RT, BAD_CAST HELD_ED); + } else { + /* set responseTime "emergencyRouting" */ + xmlNewProp(ptrLocationRequest, BAD_CAST HELD_RT, BAD_CAST HELD_ER); + } + /* locationType - element (optional) */ + ptrLocationType = + xmlNewChild(ptrLocationRequest, NULL, BAD_CAST "locationType", + type == NULL ? BAD_CAST "geodetic civic" : BAD_CAST type); + /* properties */ + xmlNewProp(ptrLocationType, BAD_CAST "exact", BAD_CAST "false"); + + xmlDocDumpFormatMemory(request, &xmlbuff, &buffersize, 0); + if(xmlbuff == NULL) { + LM_ERR("locationRequest xmlDocDumpFormatMemory() failed\n"); + xmlFreeDoc(request); + return doc; + } + + doc = (char *)pkg_malloc((buffersize + 1) * sizeof(char)); + if(doc == NULL) { + LM_ERR("no more private memory\n"); + xmlFree(xmlbuff); + xmlFreeDoc(request); + return doc; + } + + memset(doc, 0, buffersize + 1); + memcpy(doc, (char *)xmlbuff, buffersize); + doc[buffersize] = '\0'; + + *lgth = strlen(doc); + + xmlFree(xmlbuff); + xmlFreeDoc(request); + + return doc; +} + +/* + * lost_held_location_request(held, lgth) * assembles and returns locationRequest string (allocated in private memory) */ char *lost_held_location_request(p_held_t held, int *lgth) @@ -1118,7 +1391,8 @@ char *lost_held_location_request(p_held_t held, int *lgth) https://tools.ietf.org/html/rfc5985 - + geodetic locationURI sip:user@example.net @@ -1133,7 +1407,7 @@ char *lost_held_location_request(p_held_t held, int *lgth) return doc; } /* locationRequest - element */ - ptrLocationRequest = xmlNewNode(NULL, BAD_CAST "locationRequest"); + ptrLocationRequest = xmlNewNode(NULL, BAD_CAST HELD_LR); if(ptrLocationRequest == NULL) { LM_ERR("locationRequest xmlNewNode() failed\n"); xmlFreeDoc(request); @@ -1143,14 +1417,21 @@ char *lost_held_location_request(p_held_t held, int *lgth) /* properties */ xmlNewProp(ptrLocationRequest, BAD_CAST "xmlns", BAD_CAST "urn:ietf:params:xml:ns:geopriv:held"); - /* responseTime - element (optional) */ + /* responseTime - element (default: emergencyRouting) */ if(held->time > 0) { + /* set responseTime "") */ snprintf(buf, BUFSIZE, "%d", held->time); - xmlNewProp(ptrLocationRequest, BAD_CAST "responseTime", BAD_CAST buf); + xmlNewProp(ptrLocationRequest, BAD_CAST HELD_RT, BAD_CAST buf); + } else if(held->time == -1) { + /* set responseTime "emergencyDispatch" */ + xmlNewProp(ptrLocationRequest, BAD_CAST HELD_RT, BAD_CAST HELD_ED); + } else { + /* set responseTime "emergencyRouting" */ + xmlNewProp(ptrLocationRequest, BAD_CAST HELD_RT, BAD_CAST HELD_ER); } /* locationType - element (optional) */ - ptrLocationType = xmlNewChild(ptrLocationRequest, NULL, - BAD_CAST "locationType", BAD_CAST held->type); + ptrLocationType = xmlNewChild( + ptrLocationRequest, NULL, BAD_CAST HELD_LT, BAD_CAST held->type); /* properties */ xmlNewProp(ptrLocationType, BAD_CAST "exact", (held->exact == HELD_EXACT_TRUE) ? BAD_CAST "true" diff --git a/src/modules/lost/utilities.h b/src/modules/lost/utilities.h index a77bd83af18..19dc185f94a 100644 --- a/src/modules/lost/utilities.h +++ b/src/modules/lost/utilities.h @@ -1,7 +1,7 @@ /* * lost module utility functions * - * Copyright (C) 2020 Wolfgang Kampichler + * Copyright (C) 2021 Wolfgang Kampichler * DEC112, FREQUENTIS AG * * This file is part of Kamailio, a free SIP server. @@ -33,14 +33,15 @@ #define LOST_UTILITIES_H #define LAQUOT '<' +#define COLON ':' #define LOST_GEOLOC_HEADER "Geolocation: " #define LOST_GEOLOC_HEADER_SIZE strlen(LOST_GEOLOC_HEADER) #define LOST_PAI_HEADER "P-Asserted-Identity: " #define LOST_PAI_HEADER_SIZE strlen(LOST_PAI_HEADER) -#define LOST_RECURSION_TRUE 1 -#define LOST_RECURSION_FALSE 0 +#define LOST_BOUNDARY_TRUE 1 +#define LOST_BOUNDARY_FALSE 0 #define LOST_XPATH_DPTH 3 #define LOST_XPATH_GP "//gp:location-info/*" @@ -66,6 +67,12 @@ #define HELD_TYPE_URI "locationURI" #define HELD_TYPE_SEP " " +#define HELD_LR "locationRequest" +#define HELD_LT "locationType" +#define HELD_RT "responseTime" +#define HELD_ED "emergencyDispatch" +#define HELD_ER "emergencyRouting" + #define HELD_EXACT_TRUE 1 #define HELD_EXACT_FALSE 0 @@ -112,31 +119,35 @@ typedef struct GEOLIST } s_geolist_t, *p_geolist_t; void lost_rand_str(char *, size_t); -void lost_free_loc(p_loc_t); -void lost_free_held(p_held_t); +void lost_free_loc(p_loc_t *); +void lost_free_held(p_held_t *); void lost_free_string(str *); +void lost_free_geoheader_list(p_geolist_t *); void lost_reverse_geoheader_list(p_geolist_t *); -void lost_delete_geoheader_list(p_geolist_t); int lost_parse_location_info(xmlNodePtr, p_loc_t); int lost_xpath_location(xmlDocPtr, char *, p_loc_t); int lost_parse_geo(xmlNodePtr, p_loc_t); +int lost_parse_host(const char *, str *, int *); +int lost_new_geoheader_list(p_geolist_t *, str); +int lost_get_nameinfo(char *, str *, int); char *lost_find_service_request(p_loc_t, int *); char *lost_held_location_request(p_held_t, int *); +char *lost_held_post_request(int *, long, char *); char *lost_get_content(xmlNodePtr, const char *, int *); char *lost_get_property(xmlNodePtr, const char *, int *); -char *lost_get_geolocation_header(struct sip_msg *, int *); char *lost_get_from_header(struct sip_msg *, int *); char *lost_get_pai_header(struct sip_msg *, int *); char *lost_get_childname(xmlNodePtr, const char *, int *); char *lost_trim_content(char *, int *); char *lost_copy_geoheader_value(char *, int); char *lost_get_geoheader_value(p_geolist_t, geotype_t, int *); +char *lost_copy_string(str, int *); p_loc_t lost_new_loc(str); p_loc_t lost_parse_pidf(str, str); p_held_t lost_new_held(str, str, int, int); -p_geolist_t lost_new_geoheader_list(str, int *); +p_geolist_t lost_get_geolocation_header(struct sip_msg *, int *); #endif