From 462f62655e6fc7d33b387086123e673e20a7dc5f Mon Sep 17 00:00:00 2001 From: Walter Doekes Date: Mon, 30 Jun 2014 14:12:55 +0200 Subject: [PATCH 1/9] spiral: Add Call-ID mangling support. Dialog module not needed. --- modules/spiral/Makefile | 10 + modules/spiral/README | 107 +++++ modules/spiral/doc/spiral.xml | 45 ++ modules/spiral/doc/spiral_admin.xml | 114 +++++ modules/spiral/spiral.c | 632 ++++++++++++++++++++++++++++ 5 files changed, 908 insertions(+) create mode 100644 modules/spiral/Makefile create mode 100644 modules/spiral/README create mode 100644 modules/spiral/doc/spiral.xml create mode 100644 modules/spiral/doc/spiral_admin.xml create mode 100644 modules/spiral/spiral.c diff --git a/modules/spiral/Makefile b/modules/spiral/Makefile new file mode 100644 index 0000000000..08bd266be6 --- /dev/null +++ b/modules/spiral/Makefile @@ -0,0 +1,10 @@ +# $Id$ +# +# WARNING: do not run this directly, it should be run by the master Makefile + +include ../../Makefile.defs +auto_gen= +NAME=spiral.so +LIBS= + +include ../../Makefile.modules diff --git a/modules/spiral/README b/modules/spiral/README new file mode 100644 index 0000000000..3b6716a404 --- /dev/null +++ b/modules/spiral/README @@ -0,0 +1,107 @@ +spiral Module + +Walter Doekes + + VoIPGRID B.V. + +Edited by + +Walter Doekes + + Copyright © 2014 VoIPGRID B.V. + __________________________________________________________ + + Table of Contents + + 1. Admin Guide + + 1.1. Overview + + 1.1.1. Asterisk PBX chan_sip + 1.1.2. Alternative workarounds + 1.1.3. Caveats + + 1.2. Dependencies + + 1.2.1. OpenSIPS Modules + + 1.3. Exported Parameters + + 1.3.1. callid_mangling_prefix (string) + + List of Examples + + 1.1. Set callid_mangling_prefix parameter + +Chapter 1. Admin Guide + +1.1. Overview + + This module does Call-ID mangling to bypass Asterisk PBX + chan_sip spiral detection. + +1.1.1. Asterisk PBX chan_sip + + Asterisk is a telephony switching and private branch exchange. + Its chan_sip module handles SIP support. That module features + loop/spiral detection solely based on recognising a currently + used Call-ID. + + While that loop detection helps against unwanted loops, it does + complicate creating proper billing records. The detection + merges what should be two calls (customer X calling out and + customer Y getting called) into a single call. + + The workaround is to ensure that the Call-IDs are mangled in + one direction. That way the loop detection is bypassed. + +1.1.2. Alternative workarounds + + A different solution would be to use the b2bua module, or the + callid hiding feature of the dialog module. + + However, both solutions require the dialog module to keep state + of the open dialogs. This module is more lightweight. + +1.1.3. Caveats + + Attempting to combine this with the pua_dialoginfo module may + produce unexpected results. + + The intent of this module is NOT to hide the callid. On the + contrary, by only prefixing it with a magic value, the SIP + messages can be easily matched together. + + If your proxy speaks to a second OpenSIPS server that uses this + module, you'll need to give both a distinct + “callid_mangling_prefix”. This magic string will be considered + when deciding in which manner the Call-ID should be rewritten. + +1.2. Dependencies + +1.2.1. OpenSIPS Modules + + The following modules must be loaded before this module: + * The rr module. Obviously, this proxy should stay in the + path of the dialog to be able to rewrite the Call-IDs. + Spiral uses the ftag= Route parameter to determine the + direction of in-dialog SIP messages. + +1.3. Exported Parameters + +1.3.1. callid_mangling_prefix (string) + + The string to use as a prefix when rewriting Call-IDs. + + Look at the SIP RFC for legal character combinations. RFC4475 + hints which rare characters are still legal. + + The default is “~{}~” since it is not used by the Asterisk PBX + chan_sip generated Call-IDs. And it has been tested to work + with chan_sip. + + Example 1.1. Set callid_mangling_prefix parameter + +... +modparam("spiral", "callid_mangling_prefix", "MAGIC_CALLID-") +... diff --git a/modules/spiral/doc/spiral.xml b/modules/spiral/doc/spiral.xml new file mode 100644 index 0000000000..8cb44916d0 --- /dev/null +++ b/modules/spiral/doc/spiral.xml @@ -0,0 +1,45 @@ + + + + + + +%docentities; + +]> + + + + spiral Module + &osipsname; + + + Walter + Doekes + VoIPGRID B.V. +
+ walter+opensips@wjd.nu +
+
+ + Walter + Doekes +
+ walter+opensips@wjd.nu +
+
+
+ + 2014 + VoIPGRID B.V. + +
+ + + &admin; + &faq; + +
diff --git a/modules/spiral/doc/spiral_admin.xml b/modules/spiral/doc/spiral_admin.xml new file mode 100644 index 0000000000..6c0813ae8e --- /dev/null +++ b/modules/spiral/doc/spiral_admin.xml @@ -0,0 +1,114 @@ + + + + + &adminguide; + +
+ Overview + + This module does Call-ID mangling to bypass Asterisk PBX chan_sip spiral + detection. + +
+ Asterisk PBX chan_sip + + Asterisk is a telephony switching and private branch exchange. + Its chan_sip module handles SIP support. That module features + loop/spiral detection solely based on recognising a currently + used Call-ID. + + + While that loop detection helps against unwanted loops, it + does complicate creating proper billing records. The detection + merges what should be two calls (customer X calling out and + customer Y getting called) into a single call. + + + The workaround is to ensure that the Call-IDs are mangled in + one direction. That way the loop detection is bypassed. + +
+
+ Alternative workarounds + + A different solution would be to use the b2bua module, + or the callid hiding feature of the dialog module. + + + However, both solutions require the dialog module to keep state + of the open dialogs. This module is more lightweight. + +
+
+ Caveats + + Attempting to combine this with the pua_dialoginfo module may + produce unexpected results. + + + The intent of this module is NOT to hide the callid. On the + contrary, by only prefixing it with a magic value, the SIP + messages can be easily matched together. + + + If your proxy speaks to a second &osips; server that uses this + module, you'll need to give both a distinct + callid_mangling_prefix. This magic string will + be considered when deciding in which manner the Call-ID should + be rewritten. + +
+
+ +
+ Dependencies +
+ &osips; Modules + + The following modules must be loaded before this module: + + + + The rr module. Obviously, this proxy should stay in the + path of the dialog to be able to rewrite the Call-IDs. + Spiral uses the ftag= Route parameter to determine the + direction of in-dialog SIP messages. + + + + +
+
+ +
+ Exported Parameters + +
+ <varname>callid_mangling_prefix</varname> (string) + + The string to use as a prefix when rewriting Call-IDs. + + + Look at the SIP RFC for legal character combinations. + RFC4475 hints which rare characters are still legal. + + + The default is ~{}~ since it is not used by the + Asterisk PBX chan_sip generated Call-IDs. And it has been tested + to work with chan_sip. + + + Set <varname>callid_mangling_prefix</varname> parameter + + +... +modparam("spiral", "callid_mangling_prefix", "MAGIC_CALLID-") +... + + + +
+
+ +
diff --git a/modules/spiral/spiral.c b/modules/spiral/spiral.c new file mode 100644 index 0000000000..fa8d794e02 --- /dev/null +++ b/modules/spiral/spiral.c @@ -0,0 +1,632 @@ +/* + * $Id$ + * + * spiral module - support for callid mangling + * + * Copyright (C) 2014 VoIPGRID B.V. + * + * This file is part of opensips, a free SIP server. + * + * opensips 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 + * + * opensips 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * History: + * -------- + * 2014-06-09 initial version (Walter Doekes) + */ + +#include + +#include "../../data_lump.h" +#include "../../mod_fix.h" +#include "../../msg_translator.h" +#include "../../parser/parse_from.h" +#include "../../parser/parse_rr.h" +#include "../../parser/parse_uri.h" +#include "../../script_cb.h" +#include "../../sr_module.h" +#include "../../ut.h" + +#include "../rr/api.h" + +static int mod_init(void); + +static struct rr_binds d_rrb; + +/* spiral callid mangling */ +/* RFC4475: Call-ID: intmeth.word%ZK-!.*_+'@word`~)(><:\/"][?}{ */ +/* NOTE: we cannot chain two of these manglers, or we'll think that we mangled + * it if the previous opensips did. But we can set a different mangling_prefix + * for a second opensips instance. */ +static str callid_mangling_prefix = str_init("~{}~"); /* asterisk chokes on ~%S~ */ + +/* This is added as a temporary header while inside the core. It's + * added in the pre-handler and removed in the post-handler. */ +static const str cookie_header_key = str_init("XX-SPIRAL"); + +static int spiral_pre_raw(str *data); +static int spiral_post_raw(str *data); + +static param_export_t mod_params[] = { + {"callid_mangling_prefix", STR_PARAM, &callid_mangling_prefix.s}, + {0, 0, 0} +}; + +struct module_exports exports= { + "spiral", /* module's name */ + MODULE_VERSION, + DEFAULT_DLFLAGS, /* dlopen flags */ + NULL, /* exported functions */ + mod_params, /* param exports */ + NULL, /* exported statistics */ + NULL, /* exported MI functions */ + NULL, /* exported pseudo-variables */ + 0, /* extra processes */ + mod_init, /* module initialization function */ + NULL, /* reply processing function */ + NULL, /* module unloading/cleanup function */ + NULL /* per-child init function */ +}; + +static int mod_init(void) +{ + LM_INFO("Spiral module - initializing\n"); + + callid_mangling_prefix.len = strlen(callid_mangling_prefix.s); + if (!callid_mangling_prefix.len) { + LM_ERR("invalid value for callid_mangling_prefix\n"); + return -1; + } + + /* Load RR API. Make sure the fromtag "ftag" is appended. */ + if (load_rr_api(&d_rrb) != 0) { + LM_ERR("can't load RR API\n"); + return -1; + } + if (!d_rrb.append_fromtag) { + LM_WARN("explicitly enabling RR append_fromtag\n"); + d_rrb.append_fromtag = 1; + } + + /* Set spiral callid mangling callbacks (pre and post) */ + if (register_raw_processing_cb(spiral_pre_raw, PRE_RAW_PROCESSING) < 0) { + LM_ERR("failed to initialize pre raw support\n"); + return -1; + } + if (register_raw_processing_cb(spiral_post_raw, POST_RAW_PROCESSING) < 0) { + LM_ERR("failed to initialize post raw support\n"); + return -1; + } + + return 0; +} + +static inline int spiral_has_totag(struct sip_msg *msg) +{ + /* If it has a to-tag, this is an in-dialog request */ + if (parse_to_header(msg) < 0) { + LM_ERR("cannot parse TO header\n"); + return -1; + } + if (get_to(msg)->tag_value.len > 0) { + return 1; + } + return 0; +} + +/* Return 1 if the parameter `name` was found, value is placed in `value`. */ +static int spiral_get_param_value(str *in, const str *name, str *value) +{ + param_t *params = NULL; + param_t *p = NULL; + param_hooks_t phooks; + if (parse_params(in, CLASS_ANY, &phooks, ¶ms) < 0) { + return -1; + } + for (p = params; p; p = p->next) { + if (p->name.len == name->len + && strncasecmp(p->name.s, name->s, name->len) == 0) { + *value = p->body; + free_params(params); + return 0; + } + } + + if (params) { + free_params(params); + } + return 1; +} + +/* Return 1 if the packet is from the CALLER to CALLEE. */ +static inline int spiral_direction_downstream(struct sip_msg *msg) +{ + rr_t *rr; + struct sip_uri puri; + const str ftag = {"ftag", 4}; /* hardcoded ftag equal to the RR one */ + str rr_ftag = {0, 0}; + const str *fromtag; + + if (!msg->route) { + LM_DBG("no route header - downstream\n"); + return 1; + } + if (parse_rr(msg->route) < 0) { + LM_ERR("failed to parse RR header\n"); + return -1; + } + + /* We wanted to use d_rrb.is_direction(msg, RR_FLOW_DOWNSTREAM) == 0) + * but unfortunately that silently fails if loose_route() hasn't been + * called yet. Instead we have to replicate its efforts here. */ + if (parse_from_header(msg) < 0) { + LM_ERR("cannot parse FROM header\n"); + return -1; + } + fromtag = &get_from(msg)->tag_value; + if (fromtag->len <= 0) { + LM_ERR("failed to get from header tag\n"); + return -1; + } + rr = (rr_t*)msg->route->parsed; + if (parse_uri(rr->nameaddr.uri.s, rr->nameaddr.uri.len, &puri) < 0) { + LM_ERR("failed to parse the first route URI\n"); + return -1; + } + if (spiral_get_param_value(&puri.params, &ftag, &rr_ftag) != 0) { + return 0; + } + if (fromtag->len == rr_ftag.len + && strncmp(fromtag->s, rr_ftag.s, rr_ftag.len) == 0) + { + LM_DBG("ftag match, is_downstream\n"); + return 1; + } + LM_DBG("ftag mismatch, is_upstream\n"); + return 0; +} + +static int spiral_needs_unmangling(struct sip_msg *msg) +{ + const str *callid; + + if (msg->callid == NULL) { + LM_ERR("callid missing\n"); + return 0; + } + + callid = &msg->callid->body; + if (callid->len > callid_mangling_prefix.len + && memcmp(callid->s, callid_mangling_prefix.s, + callid_mangling_prefix.len) == 0) { + return 1; + } + + return 0; +} + +int spiral_mangle_callid(struct sip_msg *msg) +{ + struct lump *del; + str new_callid; + + if (msg->callid == NULL) { + LM_ERR("no more pkg mem\n"); + return -1; + } + + new_callid.len = msg->callid->body.len + callid_mangling_prefix.len; + new_callid.s = pkg_malloc(new_callid.len); + if (new_callid.s == NULL) { + LM_ERR("failed to allocate callid len\n"); + return -1; + } + + if (new_callid.s == NULL) { + LM_ERR("failed to encode callid\n"); + return -1; + } + + memcpy(new_callid.s, callid_mangling_prefix.s, callid_mangling_prefix.len); + memcpy((new_callid.s + callid_mangling_prefix.len), msg->callid->body.s, + msg->callid->body.len); + + del = del_lump(msg, msg->callid->body.s - msg->buf, msg->callid->body.len, HDR_CALLID_T); + if (del == NULL) { + LM_ERR("failed to delete old callid\n"); + pkg_free(new_callid.s); + return -1; + } + + if (insert_new_lump_after(del, new_callid.s, new_callid.len, HDR_CALLID_T) == NULL) { + LM_ERR("failed to insert new callid\n"); + pkg_free(new_callid.s); + return -1; + } + + return 0; +} + +int spiral_unmangle_callid(struct sip_msg *msg) +{ + struct lump *del; + str new_callid; + int new_size; + + if (msg->callid == NULL) { + LM_ERR("message with no callid\n"); + return -1; + } + + new_size = msg->callid->body.len - callid_mangling_prefix.len; + new_callid.s = pkg_malloc(new_size); + if (new_callid.s == NULL) { + LM_ERR("no more pkg mem\n"); + return -1; + } + + new_callid.len = new_size; + memcpy(new_callid.s, (msg->callid->body.s + callid_mangling_prefix.len), new_callid.len); + + del = del_lump(msg, msg->callid->body.s - msg->buf, msg->callid->body.len, HDR_CALLID_T); + if (del == NULL) { + LM_ERR("failed to delete old callid\n"); + pkg_free(new_callid.s); + return -1; + } + + if (insert_new_lump_after(del, new_callid.s, new_callid.len, HDR_CALLID_T) == NULL) { + LM_ERR("failed to insert new callid\n"); + pkg_free(new_callid.s); + return -1; + } + + return 0; +} + +static int spiral_add_cookie(struct sip_msg *msg, const str *cookie_header_value) +{ + struct lump* anchor; + str h; + + h.len = cookie_header_key.len + 2 + cookie_header_value->len + CRLF_LEN; + h.s = (char*)pkg_malloc(h.len + 1); + if(h.s == 0) { + LM_ERR("no more pkg mem\n"); + return -1; + } + + anchor = anchor_lump(msg, msg->unparsed - msg->buf, 0); + if(anchor == 0) { + LM_ERR("can't get anchor\n"); + pkg_free(h.s); + return -1; + } + + memcpy(h.s, cookie_header_key.s, cookie_header_key.len); + memcpy(h.s + cookie_header_key.len, ": ", 2); + memcpy(h.s + cookie_header_key.len + 2, cookie_header_value->s, cookie_header_value->len); + memcpy(h.s + cookie_header_key.len + 2 + cookie_header_value->len, CRLF, CRLF_LEN); + h.s[h.len] = '\0'; + + if (insert_new_lump_before(anchor, h.s, h.len, 0) == 0) + { + LM_ERR("can't insert lump\n"); + pkg_free(h.s); + return -1; + } + LM_DBG("spiral: added cookie header %.*s [%.*s]\n", + cookie_header_key.len, cookie_header_key.s, h.len, h.s); + return 0; +} + +static void spiral_del_cookie(struct sip_msg *msg) +{ + struct hdr_field *hf; + struct lump* l; + + for (hf = msg->headers; hf; hf = hf->next) + { + /* strncasecmp not needed, we put the cookie there with the + * same case */ + if (hf->name.len == cookie_header_key.len + && strncmp(hf->name.s, cookie_header_key.s, + cookie_header_key.len) == 0) + { + l = del_lump(msg, hf->name.s-msg->buf, hf->len, 0); + if (l == 0) { + LM_ERR("spiral: unable to delete cookie header\n"); + } + return; + } + } +} + +int spiral_get_cookie(struct sip_msg *msg, str *dest) +{ + struct hdr_field *hf; + + for (hf = msg->headers; hf; hf = hf->next) + { + /* strncasecmp not needed, we put the cookie there with the + * same case */ + if (hf->name.len == cookie_header_key.len + && strncmp(hf->name.s, cookie_header_key.s, + cookie_header_key.len) == 0) { + /* Copy at most the input length. */ + if (dest->len > hf->body.len) + dest->len = hf->body.len; + memcpy(dest->s, hf->body.s, dest->len); + LM_DBG("spiral: got cookie header %.*s [%.*s]\n", cookie_header_key.len, + cookie_header_key.s, dest->len, dest->s); + return 1; + } + } + + LM_DBG("spiral: no cookie header %.*s found\n", + cookie_header_key.len, cookie_header_key.s); + return 0; +} + +static inline char *spiral_rebuild_req(struct sip_msg *msg, int *len) +{ + char *ret = build_req_buf_from_sip_req(msg, (unsigned int*)len, NULL, + PROTO_NONE, MSG_TRANS_NOVIA_FLAG); + return ret; +} + +static inline char *spiral_rebuild_rpl(struct sip_msg *msg, int *len) +{ + char *ret = build_res_buf_from_sip_res(msg, (unsigned int*)len, NULL, + MSG_TRANS_NOVIA_FLAG); + return ret; +} + +int spiral_parse_msg(struct sip_msg *msg) +{ + if (parse_msg(msg->buf, msg->len, msg) != 0) { + LM_ERR("invalid SIP msg\n"); + return -1; + } + if (parse_headers(msg, HDR_EOH_F, 0) < 0) { + LM_ERR("failed to parse SIP headers\n"); + return -1; + } +#if 0 + /* 2nd via parsing here so we can do msg.via2 checking later. */ + if (parse_headers(msg, HDR_VIA2_F, 0) == -1 + || (msg->via2 == 0) || (msg->via2->error != PARSE_OK)) { + LM_DBG("no second via in this message\n"); + } +#endif + return 0; +} + +#define MSG_SKIP_BITMASK (METHOD_REGISTER|METHOD_PUBLISH|METHOD_OPTIONS| \ + METHOD_NOTIFY|METHOD_SUBSCRIBE) +int spiral_skip_msg(struct sip_msg *msg) +{ + if (msg->cseq == NULL || get_cseq(msg) == NULL) { + LM_ERR("failed to parse CSEQ header\n"); + return -1; /* error */ + } + if ((get_cseq(msg)->method_id) & MSG_SKIP_BITMASK) { + LM_DBG("skipping method %d for spiral callid mangling\n", + get_cseq(msg)->method_id); + return 1; /* skip */ + } + return 0; +} +#undef MSG_SKIP_BITMASK + +/* Call flow diagram: + * + * > INVITE >>> where=post, action=mangle (downstream) + * <<<<<< 200 < where=pre, action=unmangle (upstream) + * > ACK >>>>>> where=post, action=mangle (downstream) + * + * <<<<<< BYE < where=pre, action=unmangle (upstream) + * > 200 >>>>>> where=post, action=mangle (downstream) + * + * Summarizing: + * + * - In pre, we only do unmangling. We can ignore the RR headers and + * simply use the "magic" callid prefix to determine whether we + * need to do any unmangling. + * - In post, we only do mangling. Here we use the RR ftag to check + * in which direction this call was started. If ftag==from-tag then + */ + +static int spiral_pre_raw(str *data) +{ + struct sip_msg msg = { 0, }; + str cookie = {"..", 2}; + int in_dialog; + int is_downstream; + + msg.buf = data->s; + msg.len = data->len; + + if (spiral_parse_msg(&msg) != 0) { + goto done; + } + if (spiral_skip_msg(&msg)) { + goto done; + } + +#if 0 + if (msg.first_line.type == SIP_REQUEST) { + LM_DBG("%.*s %.*s %.*s: R", msg.callid->body.len, msg.callid->body.s, + get_cseq(&msg)->number.len, get_cseq(&msg)->number.s, + get_cseq(&msg)->method.len, get_cseq(&msg)->method.s); + } else { + LM_DBG("%.*s %.*s %.*s: %.*s", msg.callid->body.len, msg.callid->body.s, + get_cseq(&msg)->number.len, get_cseq(&msg)->number.s, + get_cseq(&msg)->method.len, get_cseq(&msg)->method.s, + msg.first_line.u.reply.status.len, msg.first_line.u.reply.status.s); + } +#endif + + /* ... */ + if (msg.first_line.type == SIP_REQUEST) { + in_dialog = spiral_has_totag(&msg); + if (in_dialog) { + is_downstream = spiral_direction_downstream(&msg); + if (is_downstream < 0) { + LM_ERR("unable to detect direction\n"); + goto error; + } + cookie.s = is_downstream ? "dc" : "uc"; /* downstream, continue */ + } else { + /* For in-dialog requests we don't need to touch RR. */ + cookie.s = "di"; /* downstream, initial */ + } + + /* Do any unmangling, if needed. If not, we do mangling + * on the output side. */ + if (in_dialog && !is_downstream) { + if (!spiral_needs_unmangling(&msg)) { + LM_ERR("expected the need for %.*s request callid %.*s " + "unmangling!\n", + msg.first_line.u.request.method.len, + msg.first_line.u.request.method.s, + msg.callid->body.len, msg.callid->body.s); + goto error; + } + if (spiral_unmangle_callid(&msg) != 0) + goto error; + } + + } else /*if (msg.first_line.type == SIP_REPLY)*/ { +#if 0 + /* We could check msg.via2 == 0 here to skip handling + * replies to locally generated requests. But that + * does not seem to have any added value. */ + if (msg.via2 == 0 && (get_cseq(&msg)->method_id) & (METHOD_CANCEL)) { + goto done; + } +#endif + + if (spiral_needs_unmangling(&msg)) { + cookie.s = "uc"; /* upstream, continue */ + if (spiral_unmangle_callid(&msg) != 0) + goto error; + } else { + cookie.s = "dc"; /* downstream, continue */ + /* Do mangling on the output side. */ + } + } + + spiral_add_cookie(&msg, &cookie); + if (msg.first_line.type == SIP_REQUEST) { + data->s = spiral_rebuild_req(&msg, &data->len); + } else /*if (msg.first_line.type == SIP_REPLY)*/ { + data->s = spiral_rebuild_rpl(&msg, &data->len); + } + +done: + free_sip_msg(&msg); + return 0; + +error: + free_sip_msg(&msg); + return -1; +} + +static int spiral_post_raw(str *data) +{ + struct sip_msg msg = { 0, }; + char cookiebuf[3] = ".."; /* writable buffer for cookie */ + str cookie = {cookiebuf, 2}; + int in_dialog; + int is_downstream; + int is_local; + + msg.buf = data->s; + msg.len = data->len; + + if (spiral_parse_msg(&msg) != 0) { + goto done; + } + if (spiral_skip_msg(&msg)) { + goto done; + } + + if (spiral_get_cookie(&msg, &cookie)) { + spiral_del_cookie(&msg); + } + is_local = (cookie.s[0] == '.'); /* upstream nor downstream */ + + if (msg.first_line.type == SIP_REQUEST) { + /* Locally generated requests */ + if (is_local) { + /* ACK and CANCEL go downstream + * (only followups to initial requests, e2e ack is not is_local) */ + if(get_cseq(&msg)->method_id == METHOD_ACK + || get_cseq(&msg)->method_id == METHOD_CANCEL) { + if (spiral_mangle_callid(&msg) != 0) + goto error; + } else { + /* All other local stuff goes upstream + * (no cookies were set/removed, nothing to do) */ + goto done; + } + + /* Non-local requests */ + } else { + in_dialog = spiral_has_totag(&msg); + if (in_dialog) { + is_downstream = (cookie.s[0] == 'd'); /* upstream/downstream */ + if (is_downstream) { + if (spiral_mangle_callid(&msg) != 0) + goto error; + } + } else { + /* Initial request is downstream */ + if (spiral_mangle_callid(&msg) != 0) + goto error; + } + } + + } else /*if (msg.first_line.type == SIP_REPLY)*/ { + /* Locally generated response */ + if (is_local) { + /* This happens for hop-by-hop replies (100) and + * e.g. 401/407 that we send. These are directed + * upstream, so we don't need to mangle them. */ + goto done; + } + + /* Non-local responses */ + is_downstream = (cookie.s[0] == 'd'); /* upstream/downstream */ + if (is_downstream) { + if (spiral_mangle_callid(&msg) != 0) + goto error; + } + } + + if (msg.first_line.type == SIP_REQUEST) { + data->s = spiral_rebuild_req(&msg, &data->len); + } else /*if (msg.first_line.type == SIP_REPLY)*/ { + data->s = spiral_rebuild_rpl(&msg, &data->len); + } + +done: + free_sip_msg(&msg); + return 0; + +error: + free_sip_msg(&msg); + return -1; +} From f83505d0ef9ec47639d017fd4cc134a9aacd6c07 Mon Sep 17 00:00:00 2001 From: Walter Doekes Date: Tue, 1 Jul 2014 12:32:13 +0200 Subject: [PATCH 2/9] spiral: Replace strncmp with memcmp when appropriate. --- modules/spiral/spiral.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/modules/spiral/spiral.c b/modules/spiral/spiral.c index fa8d794e02..dee212bb67 100644 --- a/modules/spiral/spiral.c +++ b/modules/spiral/spiral.c @@ -188,7 +188,7 @@ static inline int spiral_direction_downstream(struct sip_msg *msg) return 0; } if (fromtag->len == rr_ftag.len - && strncmp(fromtag->s, rr_ftag.s, rr_ftag.len) == 0) + && memcmp(fromtag->s, rr_ftag.s, rr_ftag.len) == 0) { LM_DBG("ftag match, is_downstream\n"); return 1; @@ -341,7 +341,7 @@ static void spiral_del_cookie(struct sip_msg *msg) /* strncasecmp not needed, we put the cookie there with the * same case */ if (hf->name.len == cookie_header_key.len - && strncmp(hf->name.s, cookie_header_key.s, + && memcmp(hf->name.s, cookie_header_key.s, cookie_header_key.len) == 0) { l = del_lump(msg, hf->name.s-msg->buf, hf->len, 0); @@ -362,7 +362,7 @@ int spiral_get_cookie(struct sip_msg *msg, str *dest) /* strncasecmp not needed, we put the cookie there with the * same case */ if (hf->name.len == cookie_header_key.len - && strncmp(hf->name.s, cookie_header_key.s, + && memcmp(hf->name.s, cookie_header_key.s, cookie_header_key.len) == 0) { /* Copy at most the input length. */ if (dest->len > hf->body.len) From 3b8e75b6d3dcebebedc6f626d36c5350fa874c2c Mon Sep 17 00:00:00 2001 From: Walter Doekes Date: Mon, 18 Aug 2014 15:41:18 +0200 Subject: [PATCH 3/9] spiral: Work with the new module dependency specification interface. --- modules/spiral/spiral.c | 25 ++++++++++++++++++++++++- 1 file changed, 24 insertions(+), 1 deletion(-) diff --git a/modules/spiral/spiral.c b/modules/spiral/spiral.c index dee212bb67..99d9ceb95e 100644 --- a/modules/spiral/spiral.c +++ b/modules/spiral/spiral.c @@ -28,6 +28,11 @@ #include +#include "../../sr_module.h" +#ifndef MAX_MOD_DEPS +# define PRE_1_12_MODULE_DEPS +#endif + #include "../../data_lump.h" #include "../../mod_fix.h" #include "../../msg_translator.h" @@ -35,7 +40,6 @@ #include "../../parser/parse_rr.h" #include "../../parser/parse_uri.h" #include "../../script_cb.h" -#include "../../sr_module.h" #include "../../ut.h" #include "../rr/api.h" @@ -63,10 +67,29 @@ static param_export_t mod_params[] = { {0, 0, 0} }; +#ifndef PRE_1_12_MODULE_DEPS +static dep_export_t deps = { + { /* OpenSIPS module dependencies */ + { MOD_TYPE_DEFAULT, "rr", DEP_ABORT }, + { MOD_TYPE_DEFAULT, "tm", DEP_ABORT }, + { MOD_TYPE_NULL, NULL, 0 }, + }, + { /* modparam dependencies */ + { NULL, NULL }, + }, +}; +#endif + struct module_exports exports= { "spiral", /* module's name */ +#ifndef PRE_1_12_MODULE_DEPS + MOD_TYPE_DEFAULT,/* class of this module */ +#endif MODULE_VERSION, DEFAULT_DLFLAGS, /* dlopen flags */ +#ifndef PRE_1_12_MODULE_DEPS + &deps, /* OpenSIPS module dependencies */ +#endif NULL, /* exported functions */ mod_params, /* param exports */ NULL, /* exported statistics */ From 1483299ee6d478ab5ed44f56924a8a807802bf20 Mon Sep 17 00:00:00 2001 From: Walter Doekes Date: Mon, 18 Aug 2014 15:44:30 +0200 Subject: [PATCH 4/9] spiral: Add sipp tests. --- modules/spiral/test-alice-5061.xml | 143 +++++++++++++++++++++++++++ modules/spiral/test-bob-5062.xml | 151 +++++++++++++++++++++++++++++ modules/spiral/test-opensips.cfg | 60 ++++++++++++ modules/spiral/test.txt | 8 ++ 4 files changed, 362 insertions(+) create mode 100644 modules/spiral/test-alice-5061.xml create mode 100644 modules/spiral/test-bob-5062.xml create mode 100644 modules/spiral/test-opensips.cfg create mode 100644 modules/spiral/test.txt diff --git a/modules/spiral/test-alice-5061.xml b/modules/spiral/test-alice-5061.xml new file mode 100644 index 0000000000..49735700bd --- /dev/null +++ b/modules/spiral/test-alice-5061.xml @@ -0,0 +1,143 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/modules/spiral/test-bob-5062.xml b/modules/spiral/test-bob-5062.xml new file mode 100644 index 0000000000..2d70102512 --- /dev/null +++ b/modules/spiral/test-bob-5062.xml @@ -0,0 +1,151 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Call-ID: [call_id] + CSeq: [cseq] ACK + Max-Forwards: 70 + Content-Length: 0 + + ]]> + + + + + + + [last_Call-ID:] + [last_CSeq:] + Content-Length: 0 + + ]]> + + + + + diff --git a/modules/spiral/test-opensips.cfg b/modules/spiral/test-opensips.cfg new file mode 100644 index 0000000000..ebf453357d --- /dev/null +++ b/modules/spiral/test-opensips.cfg @@ -0,0 +1,60 @@ +# vim: syn=ser ts=8 sw=4 sts=4 et ai: +listen = udp:127.0.0.1:5060 +mpath = "/usr/lib64/opensips/modules" +loadmodule "rr.so" +loadmodule "sipmsgops.so" +loadmodule "sl.so" +loadmodule "spiral.so" +loadmodule "tm.so" +loadmodule "uri.so" + +route { + # in-dialog + if (has_totag()) { + loose_route(); + + if (uri == myself) { + if (is_method("ACK")) { + if (t_check_trans()) { + t_relay(); + } + exit; + } + sl_send_reply("500", "Not Implemented"); + exit; + } + + t_relay(); + exit; + } + + # cancel + if (is_method("CANCEL")) { + if (t_check_trans()) { + t_relay(); + } + exit; + } + + # not invite? + if (!is_method("INVITE")) { + sl_send_reply("500", "Not Implemented"); + exit; + } + + # ignore preloaded routes, add rr + remove_hf("Route"); + if (is_method("INVITE")) { + record_route(); + } + + # check destination + if ($rU == "alice") { + t_relay("udp:127.0.0.1:5061"); + } else if ($rU == "bob") { + t_relay("udp:127.0.0.1:5062"); + } else { + t_reply("404", "Unknown Recipient"); + } + exit; +} diff --git a/modules/spiral/test.txt b/modules/spiral/test.txt new file mode 100644 index 0000000000..6d08ca2c63 --- /dev/null +++ b/modules/spiral/test.txt @@ -0,0 +1,8 @@ +TESTING + +pkill opensips; sleep 1; opensips -f ./test-opensips.cfg +sudo ngrep -qtldlo -Wbyline udp and '(port 5060 or port 5061 or port 5062)' + +sipp -m 1 -sf test-alice-5061.xml -p 5061 127.0.0.1 + +sipp -m 1 -sf test-bob-5062.xml -p 5062 127.0.0.1 From a708f5fa26221a077f5f241ae7e222330c286822 Mon Sep 17 00:00:00 2001 From: Walter Doekes Date: Mon, 18 Aug 2014 17:19:24 +0200 Subject: [PATCH 5/9] spiral: Add via cookies to tackle the failing cases. --- modules/spiral/spiral.c | 243 +++++++++++++++++++++++++++------------- 1 file changed, 168 insertions(+), 75 deletions(-) diff --git a/modules/spiral/spiral.c b/modules/spiral/spiral.c index 99d9ceb95e..45b2ffcb60 100644 --- a/modules/spiral/spiral.c +++ b/modules/spiral/spiral.c @@ -151,34 +151,34 @@ static inline int spiral_has_totag(struct sip_msg *msg) /* Return 1 if the parameter `name` was found, value is placed in `value`. */ static int spiral_get_param_value(str *in, const str *name, str *value) { - param_t *params = NULL; - param_t *p = NULL; - param_hooks_t phooks; - if (parse_params(in, CLASS_ANY, &phooks, ¶ms) < 0) { - return -1; - } - for (p = params; p; p = p->next) { - if (p->name.len == name->len - && strncasecmp(p->name.s, name->s, name->len) == 0) { - *value = p->body; - free_params(params); - return 0; - } - } - - if (params) { + param_t *params = NULL; + param_t *p = NULL; + param_hooks_t phooks; + if (parse_params(in, CLASS_ANY, &phooks, ¶ms) < 0) { + return -1; + } + for (p = params; p; p = p->next) { + if (p->name.len == name->len && + strncasecmp(p->name.s, name->s, name->len) == 0) { + *value = p->body; + free_params(params); + return 0; + } + } + + if (params) { free_params(params); } - return 1; + return 1; } /* Return 1 if the packet is from the CALLER to CALLEE. */ static inline int spiral_direction_downstream(struct sip_msg *msg) { - rr_t *rr; - struct sip_uri puri; - const str ftag = {"ftag", 4}; /* hardcoded ftag equal to the RR one */ - str rr_ftag = {0, 0}; + rr_t *rr; + struct sip_uri puri; + const str ftag = {"ftag", 4}; /* hardcoded ftag equal to the RR one */ + str rr_ftag = {0, 0}; const str *fromtag; if (!msg->route) { @@ -210,9 +210,8 @@ static inline int spiral_direction_downstream(struct sip_msg *msg) if (spiral_get_param_value(&puri.params, &ftag, &rr_ftag) != 0) { return 0; } - if (fromtag->len == rr_ftag.len - && memcmp(fromtag->s, rr_ftag.s, rr_ftag.len) == 0) - { + if (fromtag->len == rr_ftag.len && + memcmp(fromtag->s, rr_ftag.s, rr_ftag.len) == 0) { LM_DBG("ftag match, is_downstream\n"); return 1; } @@ -230,8 +229,8 @@ static int spiral_needs_unmangling(struct sip_msg *msg) } callid = &msg->callid->body; - if (callid->len > callid_mangling_prefix.len - && memcmp(callid->s, callid_mangling_prefix.s, + if (callid->len > callid_mangling_prefix.len && + memcmp(callid->s, callid_mangling_prefix.s, callid_mangling_prefix.len) == 0) { return 1; } @@ -321,18 +320,20 @@ int spiral_unmangle_callid(struct sip_msg *msg) static int spiral_add_cookie(struct sip_msg *msg, const str *cookie_header_value) { struct lump* anchor; - str h; + int pos; + str h, v; + /* Add header cookie */ h.len = cookie_header_key.len + 2 + cookie_header_value->len + CRLF_LEN; h.s = (char*)pkg_malloc(h.len + 1); - if(h.s == 0) { + if (h.s == 0) { LM_ERR("no more pkg mem\n"); return -1; } anchor = anchor_lump(msg, msg->unparsed - msg->buf, 0); - if(anchor == 0) { - LM_ERR("can't get anchor\n"); + if (anchor == 0) { + LM_ERR("can't get header anchor\n"); pkg_free(h.s); return -1; } @@ -343,35 +344,90 @@ static int spiral_add_cookie(struct sip_msg *msg, const str *cookie_header_value memcpy(h.s + cookie_header_key.len + 2 + cookie_header_value->len, CRLF, CRLF_LEN); h.s[h.len] = '\0'; - if (insert_new_lump_before(anchor, h.s, h.len, 0) == 0) - { - LM_ERR("can't insert lump\n"); + if (insert_new_lump_before(anchor, h.s, h.len, 0) == 0) { + LM_ERR("can't insert header lump\n"); + pkg_free(h.s); + return -1; + } + + /* Add Via cookie as well */ + if (msg->via1->params.s) { + pos = msg->via1->params.s - msg->via1->hdr.s - 1; + } else { + pos = msg->via1->host.s - msg->via1->hdr.s + msg->via1->host.len; + if (msg->via1->port != 0) + pos += msg->via1->port_str.len + 1; /* +1 for ':' */ + } + anchor = anchor_lump(msg, msg->via1->hdr.s - msg->buf + pos, 0); + if (anchor == 0) { + LM_ERR("can't get Via anchor\n"); + return -1; + } + + v.len = 1 + cookie_header_key.len + 1 + cookie_header_value->len; + v.s = (char*)pkg_malloc(v.len + 1); + if (v.s == 0) { + LM_ERR("no more pkg mem\n"); + return -1; + } + v.s[0] = ';'; + memcpy(v.s + 1, cookie_header_key.s, cookie_header_key.len); + v.s[cookie_header_key.len + 1] = '='; + memcpy(v.s + cookie_header_key.len + 2, cookie_header_value->s, cookie_header_value->len); + v.s[v.len] = '\0'; + + if (insert_new_lump_after(anchor, v.s, v.len, 0) == 0) { + LM_ERR("can't insert Via lump\n"); pkg_free(h.s); return -1; } - LM_DBG("spiral: added cookie header %.*s [%.*s]\n", + + LM_DBG("spiral: added cookie in header/Via %.*s [%.*s]\n", cookie_header_key.len, cookie_header_key.s, h.len, h.s); return 0; } static void spiral_del_cookie(struct sip_msg *msg) { + struct lump* anchor; struct hdr_field *hf; - struct lump* l; + struct via_param *p; - for (hf = msg->headers; hf; hf = hf->next) - { + /* Remove cooke from headers */ + for (hf = msg->headers; hf; hf = hf->next) { /* strncasecmp not needed, we put the cookie there with the * same case */ - if (hf->name.len == cookie_header_key.len - && memcmp(hf->name.s, cookie_header_key.s, - cookie_header_key.len) == 0) - { - l = del_lump(msg, hf->name.s-msg->buf, hf->len, 0); - if (l == 0) { - LM_ERR("spiral: unable to delete cookie header\n"); + if (hf->name.len == cookie_header_key.len && + memcmp(hf->name.s, cookie_header_key.s, + cookie_header_key.len) == 0) { + anchor = del_lump(msg, hf->name.s-msg->buf, hf->len, 0); + if (anchor == 0) { + LM_ERR("spiral: unable to delete header cookie\n"); + } + break; + } + } + + /* Remove cookie from Via as well */ + if (msg->via1 == NULL) { + LM_DBG("no via header\n"); /* XXX: when does this happen? */ + } + + /* For requests, it is in via2, for replies it is in via1 */ + if (msg->first_line.type == SIP_REQUEST) { + p = msg->via2->param_lst; + } else { + p = msg->via1->param_lst; + } + for (; p; p = p->next) { + if (p->name.len == cookie_header_key.len && + memcmp(p->name.s, cookie_header_key.s, + cookie_header_key.len) == 0) { + anchor = del_lump(msg, p->start-msg->buf - 1, p->size + 1, 0); + if (anchor == 0) { + LM_ERR("spiral: unable to delete Via cookie\n"); } - return; + break; } } } @@ -379,26 +435,46 @@ static void spiral_del_cookie(struct sip_msg *msg) int spiral_get_cookie(struct sip_msg *msg, str *dest) { struct hdr_field *hf; + struct via_param *p; - for (hf = msg->headers; hf; hf = hf->next) - { + /* Get header cookie */ + for (hf = msg->headers; hf; hf = hf->next) { /* strncasecmp not needed, we put the cookie there with the * same case */ - if (hf->name.len == cookie_header_key.len - && memcmp(hf->name.s, cookie_header_key.s, + if (hf->name.len == cookie_header_key.len && + memcmp(hf->name.s, cookie_header_key.s, cookie_header_key.len) == 0) { /* Copy at most the input length. */ - if (dest->len > hf->body.len) - dest->len = hf->body.len; - memcpy(dest->s, hf->body.s, dest->len); - LM_DBG("spiral: got cookie header %.*s [%.*s]\n", cookie_header_key.len, + if (dest->len > hf->body.len + 1) + dest->len = hf->body.len + 1; + dest->s[0] = 'h'; + memcpy(dest->s + 1, hf->body.s, dest->len); + LM_DBG("spiral: got header cookie %.*s [%.*s]\n", cookie_header_key.len, cookie_header_key.s, dest->len, dest->s); return 1; } } + LM_DBG("spiral: no header cookie %.*s found\n", + cookie_header_key.len, cookie_header_key.s); - LM_DBG("spiral: no cookie header %.*s found\n", + /* Get Via cookie if no header cookie can be found */ + for (p = msg->via1->param_lst; p; p = p->next) { + if (p->name.len == cookie_header_key.len && + memcmp(p->name.s, cookie_header_key.s, + cookie_header_key.len) == 0) { + /* Copy at most the input length. */ + if (dest->len > p->value.len + 1) + dest->len = p->value.len + 1; + dest->s[0] = 'v'; + memcpy(dest->s + 1, p->value.s, dest->len + 1); + LM_DBG("spiral: got Via cookie %.*s [%.*s]\n", cookie_header_key.len, + cookie_header_key.s, dest->len, dest->s); + return 1; + } + } + LM_DBG("spiral: no Via cookie %.*s found\n", cookie_header_key.len, cookie_header_key.s); + return 0; } @@ -428,8 +504,8 @@ int spiral_parse_msg(struct sip_msg *msg) } #if 0 /* 2nd via parsing here so we can do msg.via2 checking later. */ - if (parse_headers(msg, HDR_VIA2_F, 0) == -1 - || (msg->via2 == 0) || (msg->via2->error != PARSE_OK)) { + if (parse_headers(msg, HDR_VIA2_F, 0) == -1 || + (msg->via2 == 0) || (msg->via2->error != PARSE_OK)) { LM_DBG("no second via in this message\n"); } #endif @@ -570,11 +646,12 @@ static int spiral_pre_raw(str *data) static int spiral_post_raw(str *data) { struct sip_msg msg = { 0, }; - char cookiebuf[3] = ".."; /* writable buffer for cookie */ - str cookie = {cookiebuf, 2}; - int in_dialog; - int is_downstream; - int is_local; + /* The cookiebuf will hold three characters: + * [.hv] unset, Header, Via + * [.du] unset, Downstream, Upstream + * [.ic] unset, Initial, Continue */ + char cookiebuf[3] = "..."; + str cookie = {cookiebuf, 3}; msg.buf = data->s; msg.len = data->len; @@ -589,15 +666,18 @@ static int spiral_post_raw(str *data) if (spiral_get_cookie(&msg, &cookie)) { spiral_del_cookie(&msg); } - is_local = (cookie.s[0] == '.'); /* upstream nor downstream */ if (msg.first_line.type == SIP_REQUEST) { + int in_dialog; + int is_downstream; + int is_local = (cookie.s[1] == '.'); /* upstream nor downstream */ + /* Locally generated requests */ if (is_local) { /* ACK and CANCEL go downstream * (only followups to initial requests, e2e ack is not is_local) */ - if(get_cseq(&msg)->method_id == METHOD_ACK - || get_cseq(&msg)->method_id == METHOD_CANCEL) { + if (get_cseq(&msg)->method_id == METHOD_ACK || + get_cseq(&msg)->method_id == METHOD_CANCEL) { if (spiral_mangle_callid(&msg) != 0) goto error; } else { @@ -610,7 +690,7 @@ static int spiral_post_raw(str *data) } else { in_dialog = spiral_has_totag(&msg); if (in_dialog) { - is_downstream = (cookie.s[0] == 'd'); /* upstream/downstream */ + is_downstream = (cookie.s[1] == 'd'); /* upstream/downstream */ if (is_downstream) { if (spiral_mangle_callid(&msg) != 0) goto error; @@ -623,19 +703,32 @@ static int spiral_post_raw(str *data) } } else /*if (msg.first_line.type == SIP_REPLY)*/ { + int is_downstream; + int is_local = (cookie.s[0] == '.' || cookie.s[0] == 'v'); + /* Locally generated response */ if (is_local) { - /* This happens for hop-by-hop replies (100) and - * e.g. 401/407 that we send. These are directed - * upstream, so we don't need to mangle them. */ - goto done; - } - + if (cookie.s[1] == '.') { + /* No cookies at all? This is something + * unrelated to us. Don't touch. */ + goto done; + } + if (cookie.s[1] == 'u') { + /* The transaction was upstream, this is e.g. a + * provisional reply back down. */ + if (spiral_mangle_callid(&msg) != 0) + goto error; + } else { + /* The transaction was downstream and this is + * e.g. a generated 100/401/407 that we reply. */ + } /* Non-local responses */ - is_downstream = (cookie.s[0] == 'd'); /* upstream/downstream */ - if (is_downstream) { - if (spiral_mangle_callid(&msg) != 0) - goto error; + } else { + is_downstream = (cookie.s[1] == 'd'); /* upstream/downstream */ + if (is_downstream) { + if (spiral_mangle_callid(&msg) != 0) + goto error; + } } } From 9129c20a9775693dff22b4c7f28ebfe9a9a1a36d Mon Sep 17 00:00:00 2001 From: Walter Doekes Date: Mon, 18 Aug 2014 17:31:05 +0200 Subject: [PATCH 6/9] spiral: Improve tests. Check 404 too. --- modules/spiral/test-alice-5061.xml | 58 ++++++++++++++++++++++++------ modules/spiral/test-bob-5062.xml | 3 +- modules/spiral/test-opensips.cfg | 9 ++--- 3 files changed, 53 insertions(+), 17 deletions(-) diff --git a/modules/spiral/test-alice-5061.xml b/modules/spiral/test-alice-5061.xml index 49735700bd..35543deecd 100644 --- a/modules/spiral/test-alice-5061.xml +++ b/modules/spiral/test-alice-5061.xml @@ -9,6 +9,7 @@ - alice INVITEs bob, bob picks up - bob reINVITEs alice, alice OKs - alice hangs up + - alice INVITEs charlie, gets 404 Usage: sipp -m 1 -sf test-alice-5061.xml -p 5061 127.0.0.1 @@ -59,17 +60,7 @@ Call-ID: [call_id] CSeq: [cseq] ACK Max-Forwards: 70 - Content-Type: application/sdp - Content-Length: [len] - - v=0 - o=alice 1 1 IN IP[local_ip_type] [local_ip] - s=- - c=IN IP[media_ip_type] [media_ip] - t=0 0 - m=audio [media_port] RTP/AVP 8 0 - a=rtpmap:8 PCMA/8000 - a=rtpmap:0 PCMU/8000 + Content-Length: 0 ]]> @@ -140,4 +131,49 @@ + + + + + + + + + + + diff --git a/modules/spiral/test-bob-5062.xml b/modules/spiral/test-bob-5062.xml index 2d70102512..f0a8d38d17 100644 --- a/modules/spiral/test-bob-5062.xml +++ b/modules/spiral/test-bob-5062.xml @@ -119,7 +119,7 @@ [routes] [last_From:] [last_To:] - Contact: + Contact: sip:bob@[local_ip]:[local_port] Call-ID: [call_id] CSeq: [cseq] ACK Max-Forwards: 70 @@ -138,7 +138,6 @@ [last_Record-Route:] [last_From:] [last_To:] - Contact: [last_Call-ID:] [last_CSeq:] Content-Length: 0 diff --git a/modules/spiral/test-opensips.cfg b/modules/spiral/test-opensips.cfg index ebf453357d..8555d4965b 100644 --- a/modules/spiral/test-opensips.cfg +++ b/modules/spiral/test-opensips.cfg @@ -36,6 +36,9 @@ route { exit; } + # retransmit replies and handle non-e2e ACK + t_check_trans(); + # not invite? if (!is_method("INVITE")) { sl_send_reply("500", "Not Implemented"); @@ -44,9 +47,7 @@ route { # ignore preloaded routes, add rr remove_hf("Route"); - if (is_method("INVITE")) { - record_route(); - } + record_route(); # check destination if ($rU == "alice") { @@ -54,7 +55,7 @@ route { } else if ($rU == "bob") { t_relay("udp:127.0.0.1:5062"); } else { + t_reply("100", "Trying"); t_reply("404", "Unknown Recipient"); } - exit; } From 61c56a7d59283796505994c7683e3b4a3fd2c625 Mon Sep 17 00:00:00 2001 From: Walter Doekes Date: Mon, 18 Aug 2014 17:49:20 +0200 Subject: [PATCH 7/9] spiral: Improve tests by adding a CANCEL test case. --- modules/spiral/test-alice-5061.xml | 69 +++++++++++++++++++++++++++- modules/spiral/test-bob-5062.xml | 72 ++++++++++++++++++++++++++++++ 2 files changed, 140 insertions(+), 1 deletion(-) diff --git a/modules/spiral/test-alice-5061.xml b/modules/spiral/test-alice-5061.xml index 35543deecd..063229347b 100644 --- a/modules/spiral/test-alice-5061.xml +++ b/modules/spiral/test-alice-5061.xml @@ -165,7 +165,7 @@ + + + + + + + + + + + + + + + + + + + + diff --git a/modules/spiral/test-bob-5062.xml b/modules/spiral/test-bob-5062.xml index f0a8d38d17..cd45843d88 100644 --- a/modules/spiral/test-bob-5062.xml +++ b/modules/spiral/test-bob-5062.xml @@ -145,6 +145,78 @@ ]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + From e4be2cc6b014db9b860489fa680841d4d214110f Mon Sep 17 00:00:00 2001 From: Walter Doekes Date: Tue, 19 Aug 2014 12:06:45 +0200 Subject: [PATCH 8/9] msg_translator: Passing MSG_TRANS_NOVIA_FLAG means don't touch Via. Before this, if you passed the MSG_TRANS_NOVIA_FLAG flag to build_res_buf_from_sip_res, the msg->via1->next would get checked but not used. Now it's not touched at all. (cherry picked from d8abf71ed74a739749ab34e0e823ac2fe9c90200) --- msg_translator.c | 32 ++++++++++++++++++-------------- 1 file changed, 18 insertions(+), 14 deletions(-) diff --git a/msg_translator.c b/msg_translator.c index 367c8a13d7..2b12e9d3e9 100644 --- a/msg_translator.c +++ b/msg_translator.c @@ -1584,21 +1584,13 @@ char * build_req_buf_from_sip_req( struct sip_msg* msg, char * build_res_buf_from_sip_res( struct sip_msg* msg, unsigned int *returned_len, struct socket_info *sock,int flags) { - unsigned int new_len, via_len, body_delta, len; + unsigned int new_len, body_delta, len; char *new_buf, *buf; - unsigned offset, s_offset, via_offset; + unsigned int offset, s_offset; buf=msg->buf; len=msg->len; new_buf=0; - /* we must remove the first via */ - if (msg->via1->next) { - via_len=msg->via1->bsize; - via_offset=msg->h_via1->body.s-buf; - } else { - via_len=msg->h_via1->len; - via_offset=msg->h_via1->name.s-buf; - } /* Calculate message body difference and adjust * Content-Length @@ -1610,10 +1602,22 @@ char * build_res_buf_from_sip_res( struct sip_msg* msg, goto error; } - /* remove the first via*/ - if (!(flags&MSG_TRANS_NOVIA_FLAG) && del_lump( msg, via_offset, via_len, HDR_VIA_T)==0){ - LM_ERR("failed to remove first via\n"); - goto error; + /* remove the first via */ + if (!(flags & MSG_TRANS_NOVIA_FLAG)) { + unsigned int via_len, via_offset; + + if (msg->via1->next) { + via_len = msg->via1->bsize; + via_offset = msg->h_via1->body.s-buf; + } else { + via_len = msg->h_via1->len; + via_offset = msg->h_via1->name.s-buf; + } + + if (del_lump(msg, via_offset, via_len, HDR_VIA_T) == 0) { + LM_ERR("failed to remove first via\n"); + goto error; + } } new_len=len+body_delta+lumps_len(msg, msg->add_rm, sock); From b3c3a3c445615646c66e4be6a2e76808d3205266 Mon Sep 17 00:00:00 2001 From: Walter Doekes Date: Tue, 19 Aug 2014 14:26:20 +0200 Subject: [PATCH 9/9] spiral: Fix CANCEL behaviour (and CANCEL test). --- modules/spiral/spiral.c | 11 ++++++++--- modules/spiral/test-alice-5061.xml | 9 ++++++++- modules/spiral/test-bob-5062.xml | 13 +++++++++---- 3 files changed, 25 insertions(+), 8 deletions(-) diff --git a/modules/spiral/spiral.c b/modules/spiral/spiral.c index 45b2ffcb60..cc37a8ccee 100644 --- a/modules/spiral/spiral.c +++ b/modules/spiral/spiral.c @@ -410,11 +410,17 @@ static void spiral_del_cookie(struct sip_msg *msg) /* Remove cookie from Via as well */ if (msg->via1 == NULL) { - LM_DBG("no via header\n"); /* XXX: when does this happen? */ + /* If the 487 to a CANCELed call is misconstructed with + * a single Via, that Via gets stripped and now we end + * up here without any Via at all. Don't die. */ + LM_DBG("spiral: no Via header to remove cookie from\n"); + return; } /* For requests, it is in via2, for replies it is in via1 */ if (msg->first_line.type == SIP_REQUEST) { + /* There must be a via2, if there wasn't a via1, we + * wouldn't be here. And we add the via2. */ p = msg->via2->param_lst; } else { p = msg->via1->param_lst; @@ -676,8 +682,7 @@ static int spiral_post_raw(str *data) if (is_local) { /* ACK and CANCEL go downstream * (only followups to initial requests, e2e ack is not is_local) */ - if (get_cseq(&msg)->method_id == METHOD_ACK || - get_cseq(&msg)->method_id == METHOD_CANCEL) { + if (get_cseq(&msg)->method_id & (METHOD_ACK|METHOD_CANCEL)) { if (spiral_mangle_callid(&msg) != 0) goto error; } else { diff --git a/modules/spiral/test-alice-5061.xml b/modules/spiral/test-alice-5061.xml index 063229347b..addeb58ab0 100644 --- a/modules/spiral/test-alice-5061.xml +++ b/modules/spiral/test-alice-5061.xml @@ -9,11 +9,18 @@ - alice INVITEs bob, bob picks up - bob reINVITEs alice, alice OKs - alice hangs up + - alice INVITEs charlie, gets 404 + - alice INVITEs bob, gets 180 + - alice CANCELs, gets 487 + Usage: sipp -m 1 -sf test-alice-5061.xml -p 5061 127.0.0.1 + (Note: we require a post 3.4 version of sipp that has get_peer_tag + patched to properly read tags from addr-spec (no name-addr) From.) + --> @@ -232,7 +239,7 @@ - - + + @@ -145,7 +145,12 @@ ]]> - + + + + + +