diff --git a/modules/nathelper/README b/modules/nathelper/README index d4f5b2b8739..4b21b90bd6c 100644 --- a/modules/nathelper/README +++ b/modules/nathelper/README @@ -43,6 +43,8 @@ nathelper Module 1.5.3. add_rcv_param([flag]), 1.5.4. fix_nated_register() 1.5.5. nat_uac_test(flags) + 1.5.6. add_contact_alias([ip_addr, port, proto]) + 1.5.7. handle_ruri_alias() 1.6. Exported MI Functions @@ -91,7 +93,9 @@ nathelper Module 1.22. add_rcv_paramer usage 1.23. fix_nated_register usage 1.24. nat_uac_test usage - 1.25. nh_enable_ping usage + 1.25. add_contact_alias usage + 1.26. handle_ruri_alias usage + 1.27. nh_enable_ping usage Chapter 1. Admin Guide @@ -595,6 +599,46 @@ if (nat_uac_test("private-contact,private-sdp")) xlog("SIP message is NAT'ed (Call-ID: $ci)\n"); ... +1.5.6. add_contact_alias([ip_addr, port, proto]) + + Adds a standards-compliant “;alias=” parameter to the Contact URI + containing the signalling source IP, port and transport protocol + when they differ from the advertised Contact address. + + Meaning of the parameters is as follows: + * ip_addr (string, optional) - IP to encode inside the alias. If + omitted, the message source IP is used. + * port (string, optional) - port to encode inside the alias. If + omitted, the message source port is used. + * proto (string, optional) - transport protocol to encode inside + the alias (for example “udp”, “tcp”, “tls”, “sctp”, “ws” or + “wss”). If omitted, the transport used to receive the message is + used. + + This function can be used from REQUEST_ROUTE, ONREPLY_ROUTE, + BRANCH_ROUTE and FAILURE_ROUTE. + + Example 1.25. add_contact_alias usage +... + if (!add_contact_alias("$si", "$sp", "tcp")) { + xlog("[NAT] cannot create alias\n"); + } +... + +1.5.7. handle_ruri_alias() + + If the Request URI contains an “alias” parameter, the destination + URI is set based on its encoded IP, port and transport, and the + parameter is removed from the URI. + + This function can be used from REQUEST_ROUTE, BRANCH_ROUTE and + LOCAL_ROUTE. + + Example 1.26. handle_ruri_alias usage +... + handle_ruri_alias(); +... + 1.6. Exported MI Functions 1.6.1. nh_enable_ping @@ -607,7 +651,7 @@ if (nat_uac_test("private-contact,private-sdp")) parameter value greater than 0 or disables natping if parameter value is 0. - Example 1.25. nh_enable_ping usage + Example 1.27. nh_enable_ping usage ... $ opensips-cli -x mi nh_enable_ping Status:: 1 diff --git a/modules/nathelper/doc/nathelper_admin.xml b/modules/nathelper/doc/nathelper_admin.xml index a24295bb1a6..35878be4035 100644 --- a/modules/nathelper/doc/nathelper_admin.xml +++ b/modules/nathelper/doc/nathelper_admin.xml @@ -833,6 +833,55 @@ fix_nated_register(); if (nat_uac_test("private-contact,private-sdp")) xlog("SIP message is NAT'ed (Call-ID: $ci)\n"); ... + + + +
+ + <function moreinfo="none">add_contact_alias([ip_addr, port, proto])</function> + + + Adds an “alias” URI parameter to the Contact address that encodes the + signalling source IP, port and transport protocol when they differ from + the advertised Contact address. + + + ip_addr (string, optional) - IP to encode + inside the alias. If omitted, the source IP of the SIP message is used. + port (string, optional) - port to encode inside + the alias. If omitted, the source port of the SIP message is used. + proto (string, optional) - transport protocol to + encode inside the alias (e.g. “udp”, “tcp”, “tls”, “sctp”, “ws” or “wss”). If omitted, + the transport used to receive the message is used. + + This function can be used from REQUEST_ROUTE, ONREPLY_ROUTE, BRANCH_ROUTE and + FAILURE_ROUTE. + + <function>add_contact_alias</function> usage + +... +if (!add_contact_alias("$si", "$sp", "tcp")) { + xlog("[NAT] cannot create alias\n"); +} +... + + +
+
+ + <function moreinfo="none">handle_ruri_alias()</function> + + + Parses an “alias” parameter from the Request URI, sets the destination URI based on its + contents and removes the parameter from the Request URI. + + This function can be used from REQUEST_ROUTE, BRANCH_ROUTE and LOCAL_ROUTE. + + <function>handle_ruri_alias</function> usage + +... +handle_ruri_alias(); +...
diff --git a/modules/nathelper/nathelper.c b/modules/nathelper/nathelper.c index 030d10107bf..d8b7c631a9b 100644 --- a/modules/nathelper/nathelper.c +++ b/modules/nathelper/nathelper.c @@ -58,14 +58,19 @@ #endif #include #include +#include #include "../../data_lump.h" #include "../../data_lump_rpl.h" #include "../../forward.h" #include "../../timer.h" #include "../../msg_translator.h" +#include "../../parser/msg_parser.h" #include "../../socket_info.h" +#include "../../ip_addr.h" +#include "../../str.h" #include "../../mod_fix.h" +#include "../../resolve.h" #include "../../sr_module.h" #include "../../lib/csv.h" #include "../../parser/parse_uri.h" @@ -113,6 +118,9 @@ static int sipping_latency_flag = -1; /* by the code imported by sip_pinger*/ static int fixup_flags_uac_test(void** param); static int fixup_flags_sdp(void** param); +static int add_contact_alias_0_f(struct sip_msg *, char *, char *); +static int add_contact_alias_3_f(struct sip_msg *, str *, str *, str *); +static int handle_ruri_alias_f(struct sip_msg *, char *, char *); static int nat_uac_test_f(struct sip_msg* msg, void *tests); static int fix_nated_contact_f(struct sip_msg* msg, str *params); @@ -120,6 +128,10 @@ static int fix_nated_sdp_f(struct sip_msg* msg, void *flags, str *ip, str *new_sdp_lines); static int fix_nated_register_f(struct sip_msg *, char *, char *); static int add_rcv_param_f(struct sip_msg* msg, int *flag); +static int fixup_add_contact_alias(void **param, int param_no); +static int fixup_add_contact_alias_ip(void **param); +static int fixup_add_contact_alias_port(void **param); +static int fixup_add_contact_alias_proto(void **param); static int get_oldip_fields_value(modparam_t type, void* val); static void nh_timer(unsigned int, void *); @@ -224,6 +236,18 @@ static const cmd_export_t cmds[] = { {"fix_nated_contact", (cmd_function)fix_nated_contact_f, { {CMD_PARAM_STR|CMD_PARAM_OPT,0,0}, {0,0,0}}, REQUEST_ROUTE|ONREPLY_ROUTE|BRANCH_ROUTE|LOCAL_ROUTE}, + {"add_contact_alias", (cmd_function)add_contact_alias_0_f, { + {0,0,0}}, + REQUEST_ROUTE|ONREPLY_ROUTE|BRANCH_ROUTE|LOCAL_ROUTE}, + {"add_contact_alias", (cmd_function)add_contact_alias_3_f, { + {CMD_PARAM_STR,fixup_add_contact_alias_ip,0}, + {CMD_PARAM_STR,fixup_add_contact_alias_port,0}, + {CMD_PARAM_STR,fixup_add_contact_alias_proto,0}, + {0,0,0}}, + REQUEST_ROUTE|ONREPLY_ROUTE|BRANCH_ROUTE|FAILURE_ROUTE}, + {"handle_ruri_alias", (cmd_function)handle_ruri_alias_f, { + {0,0,0}}, + REQUEST_ROUTE|BRANCH_ROUTE|LOCAL_ROUTE}, {"fix_nated_sdp", (cmd_function)fix_nated_sdp_f, { {CMD_PARAM_STR,fixup_flags_sdp,0}, {CMD_PARAM_STR|CMD_PARAM_OPT,0,0}, @@ -849,7 +873,31 @@ contact_rport(struct sip_msg* msg) } return 0; +} + +static int fixup_add_contact_alias(void **param, int param_no) +{ + if (param_no < 1 || param_no > 3) { + LM_ERR("invalid parameter number <%d>\n", param_no); + return -1; + } + + return 0; +} + +static int fixup_add_contact_alias_ip(void **param) +{ + return fixup_add_contact_alias(param, 1); +} + +static int fixup_add_contact_alias_port(void **param) +{ + return fixup_add_contact_alias(param, 2); +} +static int fixup_add_contact_alias_proto(void **param) +{ + return fixup_add_contact_alias(param, 3); } static str nat_uac_test_flag_names[] = @@ -1590,8 +1638,424 @@ add_rcv_param_f(struct sip_msg* msg, int *flag) return 1; } +#define SALIAS ";alias=" +#define SALIAS_LEN (sizeof(SALIAS) - 1) +#define ALIAS "alias=" +#define ALIAS_LEN (sizeof(ALIAS) - 1) + +static inline void append_str_buf(char **dst, const char *src, int len) +{ + memcpy(*dst, src, len); + *dst += len; +} + +static inline void append_chr_buf(char **dst, char chr) +{ + **dst = chr; + (*dst)++; +} + +static int proto_type_from_str(str *proto) +{ + int parsed_proto = PROTO_OTHER; + + if (!proto || !proto->s) + return PROTO_OTHER; + + if (parse_proto((unsigned char *)proto->s, proto->len, &parsed_proto) < 0) + return PROTO_OTHER; + + return parsed_proto; +} + +static int proto_type_to_str(int proto, str *out) +{ + char *p; + + p = proto2a(proto); + if (p == NULL) + return -1; + + out->s = p; + out->len = strlen(p); + + return 0; +} + +/* + * Adds ;alias=ip~port~proto param to contact uri containing received ip, + * port, and transport proto if contact uri ip and port do not match + * received ip and port. + */ +static int +add_contact_alias_0_f(struct sip_msg* msg, char* str1, char* str2) +{ + int len, param_len, ip_len; + contact_t *c; + struct lump *anchor; + struct sip_uri uri; + struct hdr_field *hdr = NULL; + struct ip_addr *ip; + const char *ip_buf; + char *bracket, *lt, *param, *at, *port, *start; + + /* Do nothing if Contact header does not exist */ + if (!msg->contact) { + if (parse_headers(msg, HDR_CONTACT_F, 0) == -1) { + LM_ERR("while parsing headers\n"); + return -1; + } + if (!msg->contact) { + LM_DBG("no contact header\n"); + return -1; + } + } + if (get_contact_uri(msg, &uri, &c, &hdr) == -1) { + LM_ERR("failed to get contact uri\n"); + return -1; + } + + /* Compare source ip and port against contact uri */ + if (((ip = str2ip(&(uri.host))) == NULL) && + ((ip = str2ip6(&(uri.host))) == NULL)) { + LM_DBG("contact uri host is not an ip address\n"); + } else { + if (ip_addr_cmp(ip, &(msg->rcv.src_ip)) && + ((msg->rcv.src_port == uri.port_no) || + ((uri.port.len == 0) && (msg->rcv.src_port == 5060)))) { + LM_DBG("no need to add alias param\n"); + return -1; + } + } + + /* Check if function has been called already */ + if ((c->uri.s < msg->buf) || (c->uri.s > (msg->buf + msg->len))) { + LM_ERR("you can't call add_contact_alias twice, check your config!\n"); + return -1; + } + + /* Check if Contact URI needs to be enclosed in <>s */ + lt = param = NULL; + bracket = memchr(hdr->body.s, '<', hdr->body.len); + if (bracket == NULL) { + /* add opening < */ + lt = (char*)pkg_malloc(1); + if (!lt) { + LM_ERR("no pkg memory left for lt sign\n"); + goto err; + } + *lt = '<'; + anchor = anchor_lump(msg, hdr->body.s - msg->buf, 0); + if (anchor == NULL) { + LM_ERR("anchor_lump for beginning of contact body failed\n"); + goto err; + } + if (insert_new_lump_before(anchor, lt, 1, 0) == 0) { + LM_ERR("insert_new_lump_before for \"<\" failed\n"); + goto err; + } + } + + /* Create ;alias param */ + param_len = SALIAS_LEN + ((msg->rcv.src_ip.af == AF_INET6) ? 2 : 0) + + IP_ADDR_MAX_STR_SIZE + 1 /* ~ */ + 5 /* port */ + + 1 /* ~ */ + 1 /* proto */ + 1 /* > */; + param = (char*)pkg_malloc(param_len); + if (!param) { + LM_ERR("no pkg memory left for alias param\n"); + goto err; + } + at = param; + /* ip address */ + append_str_buf(&at, SALIAS, SALIAS_LEN); + if (msg->rcv.src_ip.af == AF_INET6) + append_chr_buf(&at, '['); + ip_buf = ip_addr2a(&(msg->rcv.src_ip)); + if (ip_buf == NULL) { + LM_ERR("failed to copy source ip\n"); + goto err; + } + ip_len = strlen(ip_buf); + append_str_buf(&at, ip_buf, ip_len); + if (msg->rcv.src_ip.af == AF_INET6) + append_chr_buf(&at, ']'); + /* port */ + append_chr_buf(&at, '~'); + port = int2str(msg->rcv.src_port, &len); + append_str_buf(&at, port, len); + /* proto */ + append_chr_buf(&at, '~'); + if ((msg->rcv.proto < PROTO_UDP) || (msg->rcv.proto > PROTO_WSS)) { + LM_ERR("invalid transport protocol\n"); + goto err; + } + append_chr_buf(&at, msg->rcv.proto + '0'); + /* closing > */ + if (bracket == NULL) { + append_chr_buf(&at, '>'); + } + param_len = at - param; + + /* Add ;alias param */ + LM_DBG("adding param <%.*s>\n", param_len, param); + if (uri.port.len > 0) { + start = uri.port.s + uri.port.len; + } else { + start = uri.host.s + uri.host.len; + } + anchor = anchor_lump(msg, start - msg->buf, 0); + if (anchor == NULL) { + LM_ERR("anchor_lump for ;alias param failed\n"); + goto err; + } + if (insert_new_lump_after(anchor, param, param_len, 0) == 0) { + LM_ERR("insert_new_lump_after for ;alias param failed\n"); + goto err; + } + return 1; + +err: + if (lt) pkg_free(lt); + if (param) pkg_free(param); + return -1; +} + +/* + * Adds ;alias=ip~port~proto param to contact uri containing ip, port, + * and encoded proto given as parameters. + */ +static int +add_contact_alias_3_f(struct sip_msg* msg, str* ip, str* port, str* proto) +{ + int param_len, parsed_proto; + unsigned int tmp; + contact_t *c; + struct lump *anchor; + struct sip_uri uri; + struct hdr_field *hdr = NULL; + char *bracket, *lt, *param, *at, *start; + + /* Do nothing if Contact header does not exist */ + if (!msg->contact) { + if (parse_headers(msg, HDR_CONTACT_F, 0) == -1) { + LM_ERR("while parsing headers\n"); + return -1; + } + if (!msg->contact) { + LM_DBG("no contact header\n"); + return -1; + } + } + if (get_contact_uri(msg, &uri, &c, &hdr) == -1) { + LM_ERR("failed to get contact uri\n"); + return -1; + } + + /* Get and check param values */ + if (!ip || (ip->len == 0) || + ((str2ip(ip) == NULL) && (str2ip6(ip) == NULL))) { + LM_ERR("ip param value is not valid IP address\n"); + return -1; + } + if (!port || (str2int(port, &tmp) == -1) || (tmp == 0) || (tmp > 65535)) { + LM_ERR("port param value is not valid port\n"); + return -1; + } + parsed_proto = proto_type_from_str(proto); + if (parsed_proto == PROTO_OTHER) { + LM_ERR("proto param value is not a known protocol\n"); + return -1; + } + + /* Check if function has been called already */ + if ((c->uri.s < msg->buf) || (c->uri.s > (msg->buf + msg->len))) { + LM_ERR("you can't call alias_contact twice, check your config!\n"); + return -1; + } + + /* Check if Contact URI needs to be enclosed in <>s */ + lt = param = NULL; + bracket = memchr(hdr->body.s, '<', hdr->body.len); + if (bracket == NULL) { + /* add opening < */ + lt = (char*)pkg_malloc(1); + if (!lt) { + LM_ERR("no pkg memory left for lt sign\n"); + goto err; + } + *lt = '<'; + anchor = anchor_lump(msg, hdr->body.s - msg->buf, 0); + if (anchor == NULL) { + LM_ERR("anchor_lump for beginning of contact body failed\n"); + goto err; + } + if (insert_new_lump_before(anchor, lt, 1, 0) == 0) { + LM_ERR("insert_new_lump_before for \"<\" failed\n"); + goto err; + } + } + + /* Create ;alias param */ + param_len = SALIAS_LEN + ip->len + 1 /* ~ */ + port->len + 1 /* ~ */ + + 1 /* proto */; + if (bracket == NULL) + param_len += 1; /* closing > */ + param = (char*)pkg_malloc(param_len); + if (!param) { + LM_ERR("no pkg memory left for alias param\n"); + goto err; + } + at = param; + /* ip address */ + append_str_buf(&at, SALIAS, SALIAS_LEN); + append_str_buf(&at, ip->s, ip->len); + /* port */ + append_chr_buf(&at, '~'); + append_str_buf(&at, port->s, port->len); + /* proto */ + append_chr_buf(&at, '~'); + append_chr_buf(&at, parsed_proto + '0'); + /* closing > */ + if (bracket == NULL) { + append_chr_buf(&at, '>'); + } + param_len = at - param; + + /* Add ;alias param */ + LM_DBG("adding param <%.*s>\n", param_len, param); + if (uri.port.len > 0) { + start = uri.port.s + uri.port.len; + } else { + start = uri.host.s + uri.host.len; + } + anchor = anchor_lump(msg, start - msg->buf, 0); + if (anchor == NULL) { + LM_ERR("anchor_lump for ;alias param failed\n"); + goto err; + } + if (insert_new_lump_after(anchor, param, param_len, 0) == 0) { + LM_ERR("insert_new_lump_after for ;alias param failed\n"); + goto err; + } + return 1; + +err: + if (lt) pkg_free(lt); + if (param) pkg_free(param); + return -1; +} + +/* + * Checks if r-uri has alias param and if so, removes it and sets $du + * based on its value. + */ +static int +handle_ruri_alias_f(struct sip_msg* msg, char* str1, char* str2) +{ + str uri, proto; + char buf[MAX_URI_SIZE], *val, *sep, *at, *next, *cur_uri, *rest, + *port, *trans; + unsigned int len, rest_len, val_len, alias_len, proto_type, cur_uri_len, + ip_port_len; + + if (parse_sip_msg_uri(msg) < 0) { + LM_ERR("while parsing Request-URI\n"); + return -1; + } + rest = msg->parsed_uri.params.s; + rest_len = msg->parsed_uri.params.len; + if (rest_len == 0) { + LM_DBG("no params\n"); + return -1; + } + while (rest_len >= ALIAS_LEN) { + if (strncmp(rest, ALIAS, ALIAS_LEN) == 0) break; + sep = memchr(rest, 59 /* ; */, rest_len); + if (sep == NULL) { + LM_DBG("no alias param\n"); + return -1; + } else { + rest_len = rest_len - (sep - rest + 1); + rest = sep + 1; + } + } + + if (rest_len < ALIAS_LEN) { + LM_DBG("no alias param\n"); + return -1; + } + /* set dst uri based on alias param value */ + val = rest + ALIAS_LEN; + val_len = rest_len - ALIAS_LEN; + port = memchr(val, 126 /* ~ */, val_len); + if (port == NULL) { + LM_ERR("no '~' in alias param value\n"); + return -1; + } + *(port++) = ':'; + trans = memchr(port, 126 /* ~ */, val_len - (port - val)); + if (trans == NULL) { + LM_ERR("no second '~' in alias param value\n"); + return -1; + } + at = &(buf[0]); + append_str_buf(&at, "sip:", 4); + ip_port_len = trans - val; + alias_len = SALIAS_LEN + ip_port_len + 2 /* ~n */; + memcpy(at, val, ip_port_len); + at = at + ip_port_len; + trans = trans + 1; + if ((ip_port_len + 2 > val_len) || (*trans == ';') || (*trans == '?')) { + LM_ERR("no proto in alias param\n"); + return -1; + } + proto_type = *trans - 48 /* char 0 */; + if (proto_type != PROTO_UDP) { + if (proto_type_to_str(proto_type, &proto) < 0 || proto.len == 0) { + LM_ERR("unknown proto in alias param\n"); + return -1; + } + append_str_buf(&at, ";transport=", 11); + memcpy(at, proto.s, proto.len); + at = at + proto.len; + } + next = trans + 1; + if ((ip_port_len + 2 < val_len) && (*next != ';') && (*next != '?')) { + LM_ERR("invalid alias param value\n"); + return -1; + } + uri.s = &(buf[0]); + uri.len = at - &(buf[0]); + LM_DBG("setting dst_uri to <%.*s>\n", uri.len, uri.s); + if (set_dst_uri(msg, &uri) == -1) { + LM_ERR("failed to set dst uri\n"); + return -1; + } + /* remove alias param */ + if (msg->new_uri.s) { + cur_uri = msg->new_uri.s; + cur_uri_len = msg->new_uri.len; + } else { + cur_uri = msg->first_line.u.request.uri.s; + cur_uri_len = msg->first_line.u.request.uri.len; + } + at = &(buf[0]); + len = rest - 1 /* ; */ - cur_uri; + memcpy(at, cur_uri, len); + at = at + len; + len = cur_uri_len - alias_len - len; + memcpy(at, rest + alias_len - 1, len); + uri.s = &(buf[0]); + uri.len = cur_uri_len - alias_len; + LM_DBG("rewriting r-uri to <%.*s>\n", uri.len, uri.s); + if (set_ruri(msg, &uri) < 0) { + LM_ERR("failed to set r-uri\n"); + return -1; + } + return 1; +} /*