<!-- Kamailio Pull Request Template -->
<!-- IMPORTANT: - for detailed contributing guidelines, read: https://github.com/kamailio/kamailio/blob/master/.github/CONTRIBUTING.md - pull requests must be done to master branch, unless they are backports of fixes from master branch to a stable branch - backports to stable branches must be done with 'git cherry-pick -x ...' - code is contributed under BSD for core and main components (tm, sl, auth, tls) - code is contributed GPLv2 or a compatible license for the other components - GPL code is contributed with OpenSSL licensing exception -->
#### Pre-Submission Checklist <!-- Go over all points below, and after creating the PR, tick all the checkboxes that apply --> <!-- All points should be verified, otherwise, read the CONTRIBUTING guidelines from above--> <!-- If you're unsure about any of these, don't hesitate to ask on sr-dev mailing list --> - [x] Commit message has the format required by CONTRIBUTING guide - [x] Commits are split per component (core, individual modules, libs, utils, ...) - [x] Each component has a single commit (if not, squash them into one commit) - [x] No commits to README files for modules (changes must be done to docbook files in `doc/` subfolder, the README file is autogenerated)
#### Type Of Change - [x] Small bug fix (non-breaking change which fixes an issue) - [x] New feature (non-breaking change which adds new functionality) - [ ] Breaking change (fix or feature that would change existing functionality)
#### Checklist: <!-- Go over all points below, and after creating the PR, tick the checkboxes that apply --> - [ ] PR should be backported to stable branches - [x] Tested changes locally - [x] Related to issue #2574 and #2641
#### Description <!-- Describe your changes in detail --> 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 (1:yes/0:no); location_profile (PIDF/LO profile selection: 0:first/1:last/2:geo/3:civic); verbose 0/1 (detailed LoST response as log INFO); geoheader_type (filter schema: 0:any/1:cid/2:http/3:https); geoheader_order (0:first/1:last) - function: lost_held_dereference (specific function to dereference location using POST method); attributes are url (r;string), resp.-time (r;string), resp.-type (r;string), pidf (r/w;string) and error (r/w;string) - 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.
http_client: http_client_request (api) content-type header support - new api call including a content-type argument implemented as new function - required to resolve geolocation url using POST (#2641) - Note: not sure if this is the most elegant solution, but it helps to avoid backward compatibility issues
You can view, comment on, or merge this pull request online at:
https://github.com/kamailio/kamailio/pull/2675
-- Commit Summary --
* http_client: http_client_request (api) content-type header support * lost: new features, attributes and a new function to dereference location
-- File Changes --
M src/modules/http_client/curl_api.c (1) M src/modules/http_client/curl_api.h (3) M src/modules/http_client/functions.c (68) M src/modules/http_client/functions.h (14) M src/modules/lost/functions.c (902) M src/modules/lost/functions.h (3) M src/modules/lost/lost.c (88) A src/modules/lost/naptr.c (255) A src/modules/lost/naptr.h (38) M src/modules/lost/pidf.c (5) A src/modules/lost/response.c (991) A src/modules/lost/response.h (131) M src/modules/lost/utilities.c (447) M src/modules/lost/utilities.h (27)
-- Patch Links --
https://github.com/kamailio/kamailio/pull/2675.patch https://github.com/kamailio/kamailio/pull/2675.diff
Please don't create that much duplicated code. Make the old API function call the new as a wrapper with a null argument for content type header. That way we keep backwards compatibility and can add new functions.
Please also add new tests to the API test module.
@wkampich pushed 1 commit.
ffb0a8624af952e619ddc5f6fa29c6655f17d4c9 lost: bug-fix due to a code formatting error
Please don't create that much duplicated code. Make the old API function call the new as a wrapper with a null argument for content type header. That way we keep backwards compatibility and can add new functions.
Please also add new tests to the API test module.
Hi Olle, i think you are referring to the http_client part of this pull request? Which one in particular, not sure if you know that you can also add comments to individual functions in the review mode.
Please don't create that much duplicated code. Make the old API function call the new as a wrapper with a null argument for content type header. That way we keep backwards compatibility and can add new functions. Please also add new tests to the API test module.
Hi Olle, i think you are referring to the http_client part of this pull request? Which one in particular, not sure if you know that you can also add comments to individual functions in the review mode.
Hi Olle, Henning, I think I understood Olle's comment - I am about to implement the suggestion ...
@wkampich pushed 3 commits.
2f48d0cdc84eaa583a2f2b6837e6251e62abc901 lost: DOM level count fix 5f50cc0ec7017a325d25249e6e66e6a2feb4dd08 http_client: duplicated code removed 25378c0388bb4365e8d4f1a4e874381fe339cd3b lost: README update
Code duplication was removed for http client module. I guess this can be merged, if no other opinions against.
All good. Thanks for fixing!
@henningw commented on this pull request.
Thanks for the update. I just did a quick review, focus on the usual string handling and memory allocation topics.
pkg_free(ptr); - ptr = NULL; + *held = NULL; +} + +/* + * lost_copy_string(str, int*) {
Have you considered using the existing ut.h pkg_str_dup(..) function?
/* 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);
Its necessary to make a copy here, can not the host.s be used?
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);
Its necessary to make a copy here, can not the url.s be used?
+ /* 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);
same as above
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 */ - curlres = httpapi.http_connect(_m, &con, NULL, &ret, mtlost, &req); + if(naptr) { + /* copy url */ + len = 0; + urlrep = lost_copy_string(url, &len);
same
- /* 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);
if you need a str anyway, you could use pkg_str_dup
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);
if you need a str anyway, you could use pkg_str_dup
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);
if you need a str anyway, you could use pkg_str_dup
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);
if you need a str anyway, you can use pkg_str_dup
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);
same
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);
this will allocate pkg memory to heldreq
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);
and this again another chunk of pkg mem to heldreq, but only one is freed later one?
}
+ } + } + + /* 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);
see above
+ /* 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);
see above
@@ -0,0 +1,255 @@
+/* + * lost module naptr functions + * thankfully taken over from the enum module
There are several functions (parse_naptr_regexp, naptr_greater, naptr_sort..) copied from the enum module. We could consider moving them to the core. If we want to keep them, the copyright from the author should be present here as well.
+#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" +
The section below defines several data structures. In order to prevent overlapping with existing data structures in modules and other libraries you probably want to add a prefix (e.g. lost_list_t, lost_data_t etc..).
Moving code to the core from existing module must ensure it can be relocated under BSD license.
Regarding the cloning of some str values, they seem to be required due to used functions that require zero-terminated strings, like ipstr that is passed to lost_get_nameinfo(), which is using libc inet_pton().
Good point @miconda regarding the zero termination, then of course one needs to do a copy. The license topic is of course valid as well, the original author of the enum code is Juha.
@wkampich commented on this pull request.
pkg_free(ptr); - ptr = NULL; + *held = NULL; +} + +/* + * lost_copy_string(str, int*) {
yes true `pkg_str_dup(..)` would be an option; basically I only use the function when a pointer to a string is necessary and requires zero termination
@wkampich commented on this pull request.
/* 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);
the `inet_..()` function called in a next step requires a zero terminated string
@wkampich commented on this pull request.
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);
curl requires a zero terminated string as url, which is not added by the http_client API query function `http_client_query_c`
@wkampich commented on this pull request.
+ /* 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);
curl requires a zero terminated string as url, which is not added by the http_client API query function `http_client_query_c`
@wkampich commented on this pull request.
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 */ - curlres = httpapi.http_connect(_m, &con, NULL, &ret, mtlost, &req); + if(naptr) { + /* copy url */ + len = 0; + urlrep = lost_copy_string(url, &len);
curl requires a zero terminated string as url, which is not added by the http_client API query function `http_client_query_c`
@wkampich commented on this pull request.
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);
curl requires a zero terminated string as url, which is not added by the http_client API query function `http_client_query_c`
@wkampich commented on this pull request.
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);
yes, I should definitely correct that - thanks for pointing it out
@wkampich commented on this pull request.
- /* 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);
thanks, have incorporated it
@wkampich commented on this pull request.
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);
thanks, have incorporated it
@wkampich commented on this pull request.
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);
thanks, have incorporated it
@wkampich commented on this pull request.
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);
thanks, have incorporated it
@wkampich: lost_copy_string() is not a significant amount of code to replace with pkg_str_dup(), the signature is different as well -- overall, it could be good, but not a must. If you fix the leak, the PR should be ready to merge.
@wkampich commented on this pull request.
+#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" +
done
@wkampich pushed 1 commit.
b458bbcba00cd6678617f004bbdeff2a1b0c524d lost: memory leak fix and code refactoring
@sergey-safarov commented on this pull request.
</itemizedlist>
+ <para> + The return value is 200 on success, 400 if an internal error occured, or 500 if an + error code is returned in the HELD response. + </para> + <para> + This function can be used from REQUEST_ROUTE, + ONREPLY_ROUTE, FAILURE_ROUTE, and BRANCH_ROUTE. + </para> + <example> + <title><function>lost_held_dereference()</function> usage</title> + <programlisting format="linespecific"> +... +# HELD location dereference +if ($hdr(Geolocation)=~"^<http.*$") { + $var(url) = $(hdr(Geolocation){s.select,0,;});
I can suggest use ``` $var(url) = @hf_value.geolocation[1].uri; ```
tested this config file ``` listen=udp:127.0.0.1:5060 loadmodule "xlog.so" loadmodule "pv.so" loadmodule "http_client.so" loadmodule "lost.so" loadmodule "textopsx.so"
modparam("lost", "exact_type", 1)
request_route { $var(geo_uri) = @hf_value.geolocation[1].uri; if ($(var(geo_uri){s.select,0,:}) =~ "http|https") { $var(res) = lost_held_dereference("$var(geo_uri)", "emergencyDispatch", "civic geodetic", "$var(pidf)", "$var(err)"); xlog("L_INFO", "HELD location dereference: Result code $var(res)\n$var(pidf)"); } } ```
During URL dereferencing send this request ``` POST /webhook HTTP/1.1 Host: 217.12.247.98 User-Agent: kamailio (5.5.0-pre0 (x86_64/linux)) Content-Type: application/held+xml;charset=utf-8 Accept: application/pidf+xml,application/held+xml;q=0.5 Content-Length: 192
<?xml version="1.0"?> <locationRequest xmlns="urn:ietf:params:xml:ns:geopriv:held" responseTime="emergencyDispatch"><locationType exact="false">civic geodetic</locationType></locationRequest> ```
According to config file `modparam("lost", "exact_type", 1)` but in real request send `locationType exact="false"`
@sergey-safarov commented on this pull request.
@@ -168,7 +176,59 @@
<title>Set <varname>location_type</varname> parameter</title> <programlisting format="linespecific"> ... - modparam("lost", "location_type, "civic geodetic locationURI") + modparam("lost", "location_type", "civic geodetic locationURI") + ... + </programlisting> + </example> + </section> + <section id="lost.p.post_request"> + <title><varname>post_request</varname> (int)</title> + <para> + Dereferencing the location can be done using either the HTTP GET or POST method. This parameter
"This parameter" duplicated here.
Tested config ``` listen=udp:127.0.0.1:5060 loadmodule "xlog.so" loadmodule "pv.so" loadmodule "http_client.so" loadmodule "lost.so" loadmodule "textopsx.so"
modparam("lost", "exact_type", 1) modparam("lost", "post_request", 1)
request_route { $var(id) = "sip:alice@ca.nga911.com"; $var(res) = lost_held_query("", "$var(id)" , "$var(pidf)", "$var(url)", "$var(err)"); xlog("L_INFO", "HELD locationRequest: Result code $var(res)\nUrl: $var(url)\n$var(pidf)"); } ``` works as expected.
@miconda commented on this pull request.
</itemizedlist>
+ <para> + The return value is 200 on success, 400 if an internal error occured, or 500 if an + error code is returned in the HELD response. + </para> + <para> + This function can be used from REQUEST_ROUTE, + ONREPLY_ROUTE, FAILURE_ROUTE, and BRANCH_ROUTE. + </para> + <example> + <title><function>lost_held_dereference()</function> usage</title> + <programlisting format="linespecific"> +... +# HELD location dereference +if ($hdr(Geolocation)=~"^<http.*$") { + $var(url) = $(hdr(Geolocation){s.select,0,;});
Note that `@...` were SER specific variables, still available, but not with an active maintainer. I would rather recommend using `$...` instead of `@...`, so better examples with $hdr(...) when gives what's needed.
tested config ``` listen=udp:127.0.0.1:5060 loadmodule "xlog.so" loadmodule "pv.so" loadmodule "http_client.so" loadmodule "lost.so" loadmodule "textopsx.so"
modparam("lost", "exact_type", 1) modparam("lost", "post_request", 1) modparam("http_client", "httpcon", "lostsrv=>http://psap.or.nga911.com/api/lost");
request_route { $var(res) = lost_query("lostsrv", "$var(uri)", "$var(name)", "$var(err)"); xlog("L_INFO", "LOST findService: Result code $var(res)\nUri: $var(uri)\nName: $var(name)\n"); } ```
Test with one `Geolocation` header. LOST request send with HTTP headers like ``` POST /api/lost HTTP/1.1 Host: api.example.com User-Agent: kamailio (5.5.0-pre0 (x86_64/linux)) Accept: */* Content-Type: application/lost+xml;charset=utf-8 Content-Length: 526 ```
Here may be updated ``` Accept: application/lost+xml ```
Use case when the call contains two `Geolocation` headers.
Example ``` Geolocation: http://example.org/held-link Geolocation: cid:7607331117@10.3.90.20 ```
Technically such a call contains Geolocacation by_value and does not require deference first Geolocation header by_reference.
From my point of view.
When used `emergencyRouting` routing need to prefer Geolocation by_value. This alow delivered call with minimal delays but maybe with low accuracy. When used `emergencyDispatch` routing needs to prefer Geolocation by_reference. This alow send HELD request to a mobile carrier and get latest mobile device location.
Technically on Kamailio maybe used `emergencyRouting` and on `PSAP` used `emergencyDispatch`.
So as idea, in lost request check the number of present Geolocation headers, if present more than one header: 1. and used `emergencyRouting` then try search `by_value` Geolocation; 2. and used `emergencyDispatch` then try to search and dereference `by_reference` Geolocation.
Use case when the call contains two `Geolocation` headers. Related `geoheader_type` param.
Example
Geolocation: <http://example.org/held-link> Geolocation: <cid:7607331117@10.3.90.20>
Technically such a call contains Geolocacation by_value and does not require deference first Geolocation header by_reference.
From my point of view. When used `emergencyRouting` routing need to prefer Geolocation by_value. This alow delivered call with minimal delays but maybe with low accuracy. When used `emergencyDispatch` routing needs to prefer Geolocation by_reference. This alow send HELD request to a mobile carrier and get latest mobile device location.
Technically on Kamailio maybe used `emergencyRouting` and on `PSAP` used `emergencyDispatch`.
So as idea, in lost request check the number of present Geolocation headers, if present more than one header:
1. and used `emergencyRouting` then try search `by_value` Geolocation; 2. and used `emergencyDispatch` then try to search and dereference `by_reference` Geolocation.
@sergey-safarov: basically that is what `lost_query` (lost_function) does. You may set the param `geoheader_type` to get only cid values (LocByValue) - in case there are several Geoloc header providing a cid you may either pick the first or the last one (`geoheader_order`).
@wkampich commented on this pull request.
</itemizedlist>
+ <para> + The return value is 200 on success, 400 if an internal error occured, or 500 if an + error code is returned in the HELD response. + </para> + <para> + This function can be used from REQUEST_ROUTE, + ONREPLY_ROUTE, FAILURE_ROUTE, and BRANCH_ROUTE. + </para> + <example> + <title><function>lost_held_dereference()</function> usage</title> + <programlisting format="linespecific"> +... +# HELD location dereference +if ($hdr(Geolocation)=~"^<http.*$") { + $var(url) = $(hdr(Geolocation){s.select,0,;});
@sergey-safarov: you may use the following to dereference multiple header fields: ``` ## response time in ms: 20000, or 'emergencyRouting' or 'emergencyDispatch' #!substdef "!MY_LOC_TIME!emergencyDispatch!g" ## location type: 'any', 'civic', 'geodetic', or 'civic geodetic' #!substdef "!MY_LOC_TYPE!any!g" ``` ... ``` if(is_present_hf("Geolocation")) { $var(count) = 0; while($var(count) < $hdrc(Geolocation)) { $var(geo) = $(hdr(Geolocation)[$var(count)]); if ($var(geo)=~"^<http.*$") { $var(url) = $(var(geo){s.select,0,;}); xlog("L_INFO", "### HELD dereferencing location at: $(var(url){s.unbracket})\n"); $var(res) = lost_held_dereference("$(var(url){s.unbracket})", "MY_LOC_TIME", "MY_LOC_TYPE", "$var(pidf)", "$var(err)"); if ($var(res) == 200) { xlog("L_INFO", "### HELD dereference request returned loc:\n$var(pidf)\n"); } else { xlog("L_INFO", "### held request failed with $var(err)\n"); } } } } ```
tested this config file
listen=udp:127.0.0.1:5060 loadmodule "xlog.so" loadmodule "pv.so" loadmodule "http_client.so" loadmodule "lost.so" loadmodule "textopsx.so" modparam("lost", "exact_type", 1) request_route { $var(geo_uri) = @hf_value.geolocation[1].uri; if ($(var(geo_uri){s.select,0,:}) =~ "http|https") { $var(res) = lost_held_dereference("$var(geo_uri)", "emergencyDispatch", "civic geodetic", "$var(pidf)", "$var(err)"); xlog("L_INFO", "HELD location dereference: Result code $var(res)\n$var(pidf)"); } }
During URL dereferencing send this request
POST /webhook HTTP/1.1 Host: 217.12.247.98 User-Agent: kamailio (5.5.0-pre0 (x86_64/linux)) Content-Type: application/held+xml;charset=utf-8 Accept: application/pidf+xml,application/held+xml;q=0.5 Content-Length: 192 <?xml version="1.0"?> <locationRequest xmlns="urn:ietf:params:xml:ns:geopriv:held" responseTime="emergencyDispatch"><locationType exact="false">civic geodetic</locationType></locationRequest>
According to config file `modparam("lost", "exact_type", 1)` but in real request send `locationType exact="false"`
@sergey-safarov: the derefencing function only supports "false" (does not consider the module parameter ... I'll add this info to doc)
tested config
listen=udp:127.0.0.1:5060 loadmodule "xlog.so" loadmodule "pv.so" loadmodule "http_client.so" loadmodule "lost.so" loadmodule "textopsx.so" modparam("lost", "exact_type", 1) modparam("lost", "post_request", 1) modparam("http_client", "httpcon", "lostsrv=>http://api.example.com/api/lost"); request_route { $var(res) = lost_query("lostsrv", "$var(uri)", "$var(name)", "$var(err)"); xlog("L_INFO", "LOST findService: Result code $var(res)\nUri: $var(uri)\nName: $var(name)\n"); }
Test with one `Geolocation` header. LOST request send with HTTP headers like
POST /api/lost HTTP/1.1 Host: api.example.com User-Agent: kamailio (5.5.0-pre0 (x86_64/linux)) Accept: */* Content-Type: application/lost+xml;charset=utf-8 Content-Length: 526
Here may be updated
Accept: application/lost+xml
@sergey-safarov: that looks like another http_client API fix as the API call does not support to set a specific header.
Also in Kamailio console, I can see this error message when `Geolocation` passed as a value
1(82) ERROR: <core> [core/parser/parse_body.c:510]: part_multipart_headers_cmp(): error code: "-9" error text: "We reached the end of the buffer".
Looks as here here "ERROR" level misslead. My test SIPp script attached.
[test_call.xml.gz](https://github.com/kamailio/kamailio/files/6255191/test_call.xml.gz)
The script may be started using the command
sipp -m 1 -sf test_call.xml -p 2345 127.0.0.1
@sergey-safarov: I see the same ERROR, but I suspect it is more of a WARNING
@wkampich pushed 1 commit.
01fa6d962258a7ea82e6d3fe2d56f98b94806a31 lost: removed some typos and added explanatory text to doc
Regarding:
``` 1(82) ERROR: <core> [core/parser/parse_body.c:510]: part_multipart_headers_cmp(): error code: "-9" error text: "We reached the end of the buffer". ```
Maybe there are white spaces at the end of body that fit inside the Content-Length in the sipp scenario (try to print the $mb enclosed in some delimiters like [ ] to see it completely. If proves to be a problem, then an issue can be created for this case with a minimal config to reproduce and the sipp scenario for it.
Then related this PR, while not familiar with all its aspects, it seems some of the questions are actually about further improvements (e.g., handling two Geolocation headers). They do not have to be implemented as part of this PR, can be done separately. If no problems need to be solved on this PR, then it should be merged in order to get more chances to be tested before branching 5.5.
Regarding:
1(82) ERROR: <core> [core/parser/parse_body.c:510]: part_multipart_headers_cmp(): error code: "-9" error text: "We reached the end of the buffer".
Maybe there are white spaces at the end of body that fit inside the Content-Length in the sipp scenario (try to print the $mb enclosed in some delimiters like [ ] to see it completely. If proves to be a problem, then an issue can be created for this case with a minimal config to reproduce and the sipp scenario for it.
Then related this PR, while not familiar with all its aspects, it seems some of the questions are actually about further improvements (e.g., handling two Geolocation headers). They do not have to be implemented as part of this PR, can be done separately. If no problems need to be solved on this PR, then it should be merged in order to get more chances to be tested before branching 5.5.
I did check parse_body.c and it seems that part_multipart_headers_cmp logs an ERROR in case it does not find a matching header, which is the case when comparing the first part (application/sdp) of the multipart body (see the example xml). So the error happens any time when comparing sup-parts that do not match. In the example provided the second part contains the `cid` and the function returns the body - I assume if it there is another sub-part before the pidf-lo, we should see another ERROR log. I would propose to log the following as INFO: ` error = -9; error_msg = "unsuccessfully reached the end of the buffer"; ` If you agree I'll commit a fix as part of this PR, the result of a local test as below:
``` 2021-04-07T11:45:17.418411261+02:00 9(16) INFO: {1 1 INVITE 1-1@172.16.16.45} lost [functions.c:844]: lost_function(): ### LOST urn [urn:service:sos] 2021-04-07T11:45:17.418721057+02:00 9(16) INFO: {1 1 INVITE 1-1@172.16.16.45} lost [functions.c:936]: lost_function(): ### LOST loc [7607331117@10.3.90.20] 2021-04-07T11:45:17.418780681+02:00 9(16) INFO: {1 1 INVITE 1-1@172.16.16.45} <core> [core/parser/parse_body.c:511]: part_multipart_headers_cmp(): result code: "-9" text: "unsuccessfully reached the end of the buffer". ```
If it is a valid multipart body, with a header that may not exist, then the log message should be DEBUG. You can make the patch if that's the case.
@wkampich pushed 1 commit.
106b8a659bd4a53e8a6872e9db8a9af0f12ff70a core: parse_body.c/part_multipart_headers_cmp() changed log output from ERR to DBG in case a header does not exist
If there are no further comments, I will merge the pull request today (late afternoon). Just a note about "handling more than one `Geolocation` header": The LoST module creates a list of `Geolocation` headers (be it multiple values in a header or single headers) and can decide which header type to use to retrieve the location for a LoST request based on certain module parameters. This is done via module functions and does not require any additional steps in `kamailio.cfg`. The new module function `lost_held_dereference` can be used when Kamailio is configured as PSAP endpoint, but was intended more for testing purposes. To get the correct `Geolocation` header value (URL) for this function some additional steps in `kamailio.cfg` are required.
Thank you for your contributions, Wolfgang!
@wkampich Thanks for the update. I think the comments regarding the missing original copyright notice in the natptr.[c,h] files and also the response.h naming are not resolved yet. The copyright notice should be added, I can also do it later if you prefer this. Regarding the naming comment, I just wanted to let you know that there might be a risk for overlapping data structures, as the s_info_t, s_data_t, cat_t etc.. are pretty generic and might be also defined in another library or module. The typedefs like LIST, INFO, DATA etc.. are in C also globally visible. For this reasons modules usually use the module prefix as part of the typedef, e.g.:
* typedef enum rms_action_type * typedef struct _sr_lua_env * typedef struct ipsec_pcscf_api
Several of this typedefs seems to be also not used in your code. But if you think its no big deal for your module, fine.
Hell @wkampich let me review the change about ERROR waring. I want to try to understand in detail why the error generated. Maybe I can suggest another approach. Please give today and tommorow.
Hello, @wkampich Let me review the change about ERROR waring. I want to try to understand in detail why the error generated. Maybe I can suggest another approach. Please give today and tomorrow.
@sergey-safarov: yes, ok for me - just a hint: the problem is not in the multipart header parsing but in the function that searches for specific header values in several body parts - parsing (successfully) happens beforehand. Just add the following to your sipp example (xml) and you get the error (or debug message) twice:
``` ... a=maxptime:20
--35129 Content-Type: plain/text
HELLO
--35129 Content-Type: application/pidf+xml Content-Id: 7607331117@10.3.90.20 ... ```
@wkampich Thanks for the update. I think the comments regarding the missing original copyright notice in the natptr.[c,h] files and also the response.h naming are not resolved yet. The copyright notice should be added, I can also do it later if you prefer this. Regarding the naming comment, I just wanted to let you know that there might be a risk for overlapping data structures, as the s_info_t, s_data_t, cat_t etc.. are pretty generic and might be also defined in another library or module. The typedefs like LIST, INFO, DATA etc.. are in C also globally visible. For this reasons modules usually use the module prefix as part of the typedef, e.g.:
* typedef enum rms_action_type * typedef struct _sr_lua_env * typedef struct ipsec_pcscf_api
Several of this typedefs seems to be also not used in your code. But if you think its no big deal for your module, fine.
@henningw: Thanks for the reminder - I'll include your naming and copyright suggestions in a new commit.
@wkampich, @sergey-safarov: the error message from part_multipart_headers_cmp() does not seem related to the code added in this PR, so, even if it is an issue, it is not required to be fixed by this PR. If proves to be a bug, an issue can be opened, or even better, a new PR can be made to fix it.
@wkampich pushed 1 commit.
5f1959cd42ff925009dd9e0fdd87f9d0487ec4fe lost: typedef naming changed and copyright added
yes, have checked "ERROR" generation. This happens because `part_multipart_headers_cmp()` try search content by "Content-Id". The first content element (SDP in the example) does not have "Content-Id". Function reaches the end of the content element and generates the error.
Then `part_multipart_headers_cmp()` try search "Content-Id" in second element and fount it and then returns result.
Looks as current `part_multipart_headers_cmp()` implementation handles case when content element do not has "Content-Id" header as error. Looks as provided fix correct for this case.
yes, have checked "ERROR" generation. This happens because `part_multipart_headers_cmp()` try search content by "Content-Id". The first content element (SDP in the example) does not have "Content-Id". Function reaches the end of the content element and generates the error.
Then `part_multipart_headers_cmp()` try search "Content-Id" in second element and fount it and then returns result.
Looks as current `part_multipart_headers_cmp()` implementation handles case when content element do not has "Content-Id" header as error. Looks as provided fix correct for this case.
Ok, thanks for testing. @miconda How shall I proceed? Merge the PR with or without the patch (commit 106b8a659bd4a53e8a6872e9db8a9af0f12ff70a)?
Merge it with the patch, it is only changing the printing of the log message in that case and should be legit that the header is missing, not being mandatory.
Merged #2675 into master.