You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
kamailio/src/modules/lost/functions.c

1325 lines
33 KiB

/*
* lost module functions
*
* Copyright (C) 2022 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 :: functions
* \ingroup lost
* Module: \ref lost
*/
/*****************/
#include "../../modules/http_client/curl_api.h"
#include "../../core/mod_fix.h"
#include "../../core/pvar.h"
#include "../../core/route_struct.h"
#include "../../core/ut.h"
#include "../../core/trim.h"
#include "../../core/mem/mem.h"
#include "../../core/parser/msg_parser.h"
#include "../../core/parser/parse_body.h"
#include "../../core/lvalue.h"
#include "pidf.h"
#include "utilities.h"
#include "response.h"
#include "naptr.h"
#define LOST_SUCCESS 200
#define LOST_CLIENT_ERROR 400
#define LOST_SERVER_ERROR 500
#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";
char mtlost[] = "application/lost+xml;charset=utf-8";
char uri_element[] = "uri";
char name_element[] = "displayName";
char errors_element[] = "errors";
/*
* lost_held_type(type, exact, lgth)
* verifies module params and returns valid HELD loaction type
* allocated in private memory
*/
char *lost_held_type(char *type, int *exact, int *lgth)
{
char *ret = NULL;
char *tmp = NULL;
int len = 0;
ret = (char *)pkg_malloc(1);
if(ret == NULL)
goto err;
memset(ret, 0, 1);
*lgth = 0;
if(strstr(type, HELD_TYPE_ANY)) {
len = strlen(ret) + strlen(HELD_TYPE_ANY) + 1;
tmp = pkg_realloc(ret, len);
if(tmp == NULL)
goto err;
ret = tmp;
strcat(ret, HELD_TYPE_ANY);
*exact = 0;
} else {
if(strstr(type, HELD_TYPE_CIV)) {
len = strlen(ret) + strlen(HELD_TYPE_CIV) + 1;
tmp = pkg_realloc(ret, len);
if(tmp == NULL)
goto err;
ret = tmp;
strcat(ret, HELD_TYPE_CIV);
}
if(strstr(type, HELD_TYPE_GEO)) {
if(strlen(ret) > 1) {
len = strlen(ret) + strlen(HELD_TYPE_SEP) + 1;
tmp = pkg_realloc(ret, len);
if(tmp == NULL)
goto err;
ret = tmp;
strcat(ret, HELD_TYPE_SEP);
}
len = strlen(ret) + strlen(HELD_TYPE_GEO) + 1;
tmp = pkg_realloc(ret, len);
if(tmp == NULL)
goto err;
ret = tmp;
strcat(ret, HELD_TYPE_GEO);
}
if(strstr(type, HELD_TYPE_URI)) {
if(strlen(ret) > 1) {
len = strlen(ret) + strlen(HELD_TYPE_SEP) + 1;
tmp = pkg_realloc(ret, len);
if(tmp == NULL)
goto err;
ret = tmp;
strcat(ret, HELD_TYPE_SEP);
}
len = strlen(ret) + strlen(HELD_TYPE_URI) + 1;
tmp = pkg_realloc(ret, len);
if(tmp == NULL)
goto err;
ret = tmp;
strcat(ret, HELD_TYPE_URI);
}
}
*lgth = strlen(ret);
return ret;
err:
PKG_MEM_ERROR;
/* clean up */
if(ret != NULL) {
pkg_free(ret);
ret = NULL;
}
*lgth = 0;
return NULL;
}
/*
* lost_function_held(msg, con, pidf, url, err, id)
* assembles and runs HELD locationRequest, parses results
*/
int lost_held_function(struct sip_msg *_m, char *_con, char *_pidf, char *_url,
char *_err, char *_id)
{
pv_spec_t *pspidf;
pv_spec_t *psurl;
pv_spec_t *pserr;
pv_value_t pvpidf;
pv_value_t pvurl;
pv_value_t pverr;
p_lost_held_t held = NULL;
xmlDocPtr doc = NULL;
xmlNodePtr root = NULL;
xmlNodePtr cur_node = NULL;
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;
int res_error = 0;
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(_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(get_str_fparam(&did, _m, (gparam_p)_id) != 0) {
LM_ERR("cannot get device id\n");
goto err;
}
if(did.len == 0) {
LM_ERR("no device id found\n");
goto err;
}
} else {
LM_DBG("parsing P-A-I header\n");
/* id from P-A-I header */
idhdr.s = lost_get_pai_header(_m, &idhdr.len);
if(idhdr.len == 0) {
LM_WARN("P-A-I header not found, trying From header ...\n");
LM_DBG("parsing From header\n");
/* id from From header */
idhdr.s = lost_get_from_header(_m, &idhdr.len);
if(idhdr.len == 0) {
LM_ERR("no device id found\n");
goto err;
}
}
did.s = idhdr.s;
did.len = idhdr.len;
}
LM_INFO("### HELD id [%.*s]\n", did.len, did.s);
/* assemble locationRequest */
held = lost_new_held(did, rtype, held_resp_time, held_exact_type);
if(held == NULL) {
LM_ERR("held object allocation failed\n");
goto err;
}
que.s = lost_held_location_request(held, &que.len);
lost_free_held(&held); /* clean up */
if(que.len == 0) {
LM_ERR("held request document error\n");
que.s = NULL;
goto err;
}
LM_DBG("held location request: [%s]\n", que.s);
/* send locationRequest to location server - HTTP POST */
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(ipstr != NULL) {
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 */
ipstr = NULL;
} else {
LM_ERR("could not copy host info\n");
}
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);
goto err;
}
} else {
LM_ERR("failed to get location service for [%.*s]\n", did.len,
did.s);
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);
if(lisurl == NULL) {
LM_ERR("could not copy POST url\n");
goto err;
}
/* send to service */
curl = httpapi.http_client_query_c(
_m, lisurl, &res, que.s, mtheld, ACCEPT_HDR);
pkg_free(lisurl); /*clean up */
lisurl = NULL;
}
/* only HTTP 2xx responses are accepted */
if(curl >= 300 || curl < 100) {
if(con.s != NULL && con.len > 0) {
LM_ERR("[%.*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);
}
goto err;
}
if(con.s != NULL && con.len > 0) {
LM_DBG("[%.*s] returned: %d\n", con.len, con.s, curl);
} 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,
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 *)"locationResponse") == 0) {
LM_DBG("HELD location response [%.*s]\n", res.len, res.s);
for(cur_node = root->children; cur_node; cur_node = cur_node->next) {
if(cur_node->type == XML_ELEMENT_NODE) {
if(xmlStrcmp(cur_node->name, (const xmlChar *)"locationUriSet")
== 0) {
LM_DBG("*** node '%s' found\n", cur_node->name);
/* get the locationUri element */
geo.s = lost_get_content(
root, (char *)HELD_TYPE_URI, &geo.len);
if(geo.len == 0) {
LM_WARN("%s element not found\n", HELD_TYPE_URI);
geo.s = NULL;
} else {
geo.s = lost_trim_content(geo.s, &geo.len);
}
}
if(xmlStrcmp(cur_node->name, (const xmlChar *)"presence")
== 0) {
LM_DBG("*** node '%s' found\n", cur_node->name);
/* response contains presence node */
presence = 1;
}
}
}
/* 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.s != NULL && geo.len > 0)) {
LM_INFO("presence node not found in HELD response, trying URI "
"...\n");
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(heldreq == NULL) {
LM_ERR("could not create POST request\n");
goto err;
}
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 */
heldreq = NULL;
}
/* only HTTP 2xx responses are accepted */
if(curl >= 300 || curl < 100) {
LM_ERR("GET [%.*s] failed with error: %d\n", pidfurl.len,
pidfurl.s, curl);
goto err;
}
if(pidfurl.len == 0) {
LM_WARN("HELD location request failed [%.*s]\n", geo.len,
geo.s);
} else {
LM_DBG("HELD location response [%.*s]\n", pidfurl.len,
pidfurl.s);
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 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);
lost_free_string(&res); /* clean up */
pvurl.rs = geo;
pvurl.rs.s = geo.s;
pvurl.rs.len = geo.len;
pvurl.flags = PV_VAL_STR;
psurl = (pv_spec_t *)_url;
psurl->setf(_m, &psurl->pvp, (int)EQ_T, &pvurl);
lost_free_string(&geo); /* clean up */
/* return error code in case of response error */
if(err.len > 0) {
res_error = 1;
}
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);
lost_free_string(&err); /* clean up */
return (res_error > 0) ? LOST_SERVER_ERROR : LOST_SUCCESS;
err:
/* clean up pointer */
lost_free_string(&que);
lost_free_string(&idhdr);
lost_free_string(&pidfurl);
/* clean up xml */
if(doc != NULL) {
xmlFreeDoc(doc);
}
/* clean up string */
if(res.s != NULL && res.len > 0) {
lost_free_string(&res);
}
if(geo.s != NULL && geo.len > 0) {
lost_free_string(&geo);
}
if(err.s != NULL && err.len > 0) {
lost_free_string(&err);
}
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;
long rtime = 0;
int len = 0;
int curl = 0;
int exact = 0;
int ret = LOST_SUCCESS;
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) {
/* default: rtime = 0 */
LM_WARN("no response time found\n");
} else {
ltime = strtol(rtm.s, &ptr, 10);
/* look for a number ... */
if((ltime > 0) && (strlen(ptr) == 0)) {
/* responseTime: milliseconds */
rtime = ltime;
/* or a string */
} else if((ltime == 0) && (strlen(ptr) > 0)) {
if(strncasecmp(ptr, HELD_ED, strlen(HELD_ED)) == 0) {
/* responseTime: emergencyDispatch */
rtime = -1;
} else if(strncasecmp(ptr, HELD_ER, strlen(HELD_ER)) == 0) {
/* responseTime: emergencyRouting */
rtime = 0;
}
}
}
}
/* 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);
/* default value will be used if nothing was returned */
if(rtype == NULL) {
LM_WARN("cannot normalize [%.*s]\n", rtp.len, rtp.s);
}
}
}
/* get the HELD request body */
heldreq = lost_held_post_request(&len, rtime, rtype);
/* clean up */
if(rtype != NULL) {
pkg_free(rtype);
rtype = NULL;
}
if(heldreq == NULL) {
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) {
LM_ERR("could not copy POST url\n");
pkg_free(heldreq); /* clean up */
heldreq = NULL;
goto err;
}
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 */
lisurl = NULL;
pkg_free(heldreq);
heldreq = NULL;
/* 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 root element ... shall be presence|locationResponse, or error */
if((!xmlStrcmp(root->name, (const xmlChar *)"presence"))
|| (!xmlStrcmp(root->name, (const xmlChar *)"locationResponse"))) {
LM_DBG("HELD location response [%.*s]\n", res.len, res.s);
/* check content and set response code
* + 0 nothing found: return 200
* + 1 reference found: return 201
* + 2 value found: return 202
* + 3 value and reference found: return 203
*/
ret += lost_check_HeldResponse(root);
/* 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);
lost_free_string(&res); /* clean up */
/* return error code in case of response error */
if(err.len > 0) {
ret = LOST_SERVER_ERROR;
}
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);
lost_free_string(&err); /* clean up */
return ret;
err:
/* clean up xml */
if(doc != NULL) {
xmlFreeDoc(doc);
}
/* clean up string */
if(res.s != NULL && res.len > 0) {
lost_free_string(&res);
}
if(err.s != NULL && err.len > 0) {
lost_free_string(&err);
}
return LOST_CLIENT_ERROR;
}
/*
* lost_function(msg, con, pidf, uri, name, err, pidf, urn)
* assembles and runs LOST findService request, parses results
*/
int lost_function(struct sip_msg *_m, char *_con, char *_uri, char *_name,
char *_err, char *_pidf, char *_urn)
{
pv_spec_t *psname;
pv_spec_t *psuri;
pv_spec_t *pserr;
pv_value_t pvname;
pv_value_t pvuri;
pv_value_t pverr;
p_lost_loc_t loc = NULL;
p_lost_geolist_t geolist = NULL;
p_lost_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;
char *urlrep = NULL;
char *heldreq = NULL;
int geotype = 0;
int redirect = 0;
int curl = 0;
int len = 0;
int naptr = 0;
int geoitems = 0;
int res_error = 0;
if(_con == NULL || _uri == NULL || _name == NULL || _err == NULL) {
LM_ERR("invalid parameter\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(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_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 */
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(get_str_fparam(&pidf, _m, (gparam_p)_pidf) != 0) {
LM_ERR("cannot get pidf parameter\n");
} else {
LM_DBG("parsing pidf parameter ...\n");
if(pidf.s != NULL && pidf.len > 0) {
LM_DBG("pidf: [%.*s]\n", pidf.len, pidf.s);
/* parse the pidf and get loc object */
loc = lost_parse_pidf(pidf, urn);
}
}
}
/* neither valid pidf parameter nor loc ... check geolocation header */
if(loc == NULL) {
/* parse Geolocation header */
LM_DBG("parsing geolocation header ...\n");
geolist = lost_get_geolocation_header(_m, &geoitems);
if(geoitems == 0) {
LM_ERR("geolocation header not found\n");
goto err;
}
LM_DBG("number of location URIs: %d\n", geoitems);
if(lost_geoloc_order == 0) {
LM_DBG("reversing location URI sequence\n");
lost_reverse_geoheader_list(&geolist);
}
switch(lost_geoloc_type) {
case ANY: /* type: 0 */
geoval = lost_get_geoheader_value(geolist, ANY, &geotype);
LM_DBG("geolocation header field (any): %s\n", geoval);
break;
case CID: /* type: 1 */
geoval = lost_get_geoheader_value(geolist, CID, &geotype);
LM_DBG("geolocation header field (LbV): %s\n", geoval);
break;
case HTTP: /* type: 2 */
geoval = lost_get_geoheader_value(geolist, HTTP, &geotype);
/* fallback to https */
if(geoval == NULL) {
LM_WARN("no valid http URL ... trying https\n");
geoval = lost_get_geoheader_value(geolist, HTTPS, &geotype);
}
LM_DBG("geolocation header field (LbR): %s\n", geoval);
break;
case HTTPS: /* type: 3 */
/* prefer https */
geoval = lost_get_geoheader_value(geolist, HTTPS, &geotype);
/* fallback to http */
if(geoval == NULL) {
LM_WARN("no valid https URL ... trying http\n");
geoval = lost_get_geoheader_value(geolist, HTTP, &geotype);
}
LM_DBG("geolocation header field (LbR): %s\n", geoval);
break;
default:
LM_WARN("unknown module parameter value\n");
geoval = lost_get_geoheader_value(geolist, UNKNOWN, &geotype);
LM_DBG("geolocation header field (any): %s\n", geoval);
break;
}
if(geoval == NULL) {
LM_ERR("invalid geolocation header\n");
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.s != NULL && pidf.len > 0) {
LM_DBG("LbV pidf-lo: [%.*s]\n", pidf.len, pidf.s);
} else {
LM_WARN("no multipart body found\n");
}
}
/* use location by reference */
if((geotype == HTTPS) || (geotype == HTTP)) {
url.s = geoval;
url.len = strlen(geoval);
/* ! dereference pidf.lo at location server - HTTP GET */
/* ! requires hack in http_client module */
/* ! functions.c => http_client_query => query_params.oneline = 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(heldreq == NULL) {
LM_ERR("could not create POST request\n");
goto err;
}
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 */
heldreq = NULL;
}
/* only HTTP 2xx responses are accepted */
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;
}
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);
} else {
LM_WARN("dereferencing location failed\n");
}
}
/* clean up */
lost_free_geoheader_list(&geolist);
lost_free_string(&ret);
if(pidf.s == NULL && pidf.len == 0) {
LM_ERR("location object not found\n");
goto err;
}
/* parse the pidf and get loc object */
loc = lost_parse_pidf(pidf, urn);
}
/* pidf parsing failed ... return */
if(loc == NULL) {
LM_ERR("parsing pidf failed\n");
goto err;
}
/* assemble findService request */
req.s = lost_find_service_request(loc, &req.len);
lost_free_loc(&loc); /* clean up */
if(req.s == NULL && req.len == 0) {
LM_ERR("lost request failed\n");
goto err;
}
LM_DBG("findService request: [%.*s]\n", req.len, req.s);
/* send findService request to mapping server - HTTP POST */
if(naptr) {
/* copy url */
len = 0;
urlrep = lost_copy_string(url, &len);
if(urlrep == NULL) {
LM_ERR("could not copy POST url\n");
goto err;
}
/* send request */
curl = httpapi.http_client_query(_m, urlrep, &ret, req.s, mtlost);
pkg_free(urlrep); /* clean up */
urlrep = NULL;
} else {
curl = httpapi.http_connect(_m, &con, NULL, &ret, mtlost, &req);
}
/* only HTTP 2xx responses are accepted */
if(curl >= 300 || curl < 100) {
if(naptr) {
LM_ERR("POST [%.*s] failed with error: %d\n", url.len, url.s, curl);
} else {
LM_ERR("[%.*s] failed with error: %d\n", con.len, con.s, curl);
}
lost_free_string(&ret);
goto err;
}
if(naptr) {
LM_DBG("[%.*s] returned: %d\n", url.len, url.s, curl);
} else {
LM_DBG("[%.*s] returned: %d\n", con.len, con.s, curl);
}
if(ret.len == 0) {
LM_ERR("findService request failed\n");
goto err;
}
LM_DBG("findService response: [%.*s]\n", ret.len, ret.s);
/* at least parse one request */
redirect = 1;
while(redirect) {
fsrdata = lost_parse_findServiceResponse(ret);
if (lost_verbose == 1) {
lost_print_findServiceResponse(fsrdata);
}
switch(fsrdata->category) {
case RESPONSE:
if(fsrdata->uri != NULL) {
/* get the first sips uri element ... */
if(lost_search_response_list(&fsrdata->uri, &tmp.s, SIPS_S) > 0) {
tmp.len = strlen(tmp.s);
/* or the first sip uri element ... */
} else if(lost_search_response_list(&fsrdata->uri, &tmp.s, SIP_S) > 0) {
tmp.len = strlen(tmp.s);
/* or return error if nothing found */
} else {
LM_ERR("sip/sips uri not found: [%.*s]\n", ret.len, ret.s);
goto err;
}
/* copy uri string */
if(pkg_str_dup(&uri, &tmp) < 0) {
LM_ERR("could not copy: [%.*s]\n", tmp.len, tmp.s);
goto err;
}
} else {
LM_ERR("uri element 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);
if(pkg_str_dup(&name, &tmp) < 0) {
LM_ERR("could not copy: [%.*s]\n", tmp.len, tmp.s);
goto err;
}
}
} 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);
if(pkg_str_dup(&err, &tmp) < 0) {
LM_ERR("could not copy: [%.*s]\n", tmp.len, tmp.s);
goto err;
}
}
/* 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;
/* get url string via NAPTR */
naptr = lost_naptr_lookup(tmp, &shttps, &url);
if(naptr == 0) {
/* fallback to http */
naptr = lost_naptr_lookup(tmp, &shttp, &url);
}
/* nothing found ... return */
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 */
if(pkg_str_dup(&oldurl, &url) < 0) {
LM_ERR("could not copy: [%.*s]\n", url.len, url.s);
goto err;
}
/* clean up */
lost_free_findServiceResponse(&fsrdata);
lost_free_string(&ret);
/* copy url */
len = 0;
urlrep = lost_copy_string(url, &len);
if(urlrep == NULL) {
LM_ERR("could not copy POST url\n");
goto err;
}
/* send request */
curl = httpapi.http_client_query(
_m, urlrep, &ret, req.s, mtlost);
pkg_free(urlrep); /*clean up */
urlrep = NULL;
/* 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;
}
/* reset url string */
url.s = NULL;
url.len = 0;
/* 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;
}
}
/* clean up */
lost_free_findServiceResponse(&fsrdata);
lost_free_string(&ret);
lost_free_string(&req);
lost_free_string(&oldurl);
/* set writable pvars */
pvname.rs = name;
pvname.rs.s = name.s;
pvname.rs.len = name.len;
pvname.flags = PV_VAL_STR;
psname = (pv_spec_t *)_name;
psname->setf(_m, &psname->pvp, (int)EQ_T, &pvname);
lost_free_string(&name); /* clean up */
pvuri.rs = uri;
pvuri.rs.s = uri.s;
pvuri.rs.len = uri.len;
pvuri.flags = PV_VAL_STR;
psuri = (pv_spec_t *)_uri;
psuri->setf(_m, &psuri->pvp, (int)EQ_T, &pvuri);
lost_free_string(&uri); /* clean up */
/* return error code in case of response error */
if(err.len > 0) {
res_error = 1;
}
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);
lost_free_string(&err); /* clean up */
return (res_error > 0) ? LOST_SERVER_ERROR : LOST_SUCCESS;
err:
/* clean up */
lost_free_findServiceResponse(&fsrdata);
lost_free_geoheader_list(&geolist);
lost_free_loc(&loc);
/* clean up string */
if(oldurl.s != NULL && oldurl.len > 0) {
lost_free_string(&oldurl);
}
if(ret.s != NULL && ret.len > 0) {
lost_free_string(&ret);
}
if(req.s != NULL && req.len > 0) {
lost_free_string(&req);
}
if(name.s != NULL && name.len > 0) {
lost_free_string(&name);
}
if(uri.s != NULL && uri.len > 0) {
lost_free_string(&uri);
}
if(err.s != NULL && err.len > 0) {
lost_free_string(&err);
}
return LOST_CLIENT_ERROR;
}