diff options
author | Matthew Nicholson <mnicholson@digium.com> | 2011-01-26 20:44:47 +0000 |
---|---|---|
committer | Matthew Nicholson <mnicholson@digium.com> | 2011-01-26 20:44:47 +0000 |
commit | 48a9694ed0772d09229919031effbb56ed253be2 (patch) | |
tree | 700b71361d4a128f480482d2b4d272b3b81bdfa2 /channels | |
parent | 8db5da18cff776045ddb8d00e1d4d161b39aa299 (diff) |
Merged revisions 304245 via svnmerge from
https://origsvn.digium.com/svn/asterisk/branches/1.8
................
r304245 | mnicholson | 2011-01-26 14:43:27 -0600 (Wed, 26 Jan 2011) | 20 lines
Merged revisions 304244 via svnmerge from
https://origsvn.digium.com/svn/asterisk/branches/1.6.2
................
r304244 | mnicholson | 2011-01-26 14:42:16 -0600 (Wed, 26 Jan 2011) | 13 lines
Merged revisions 304241 via svnmerge from
https://origsvn.digium.com/svn/asterisk/branches/1.4
........
r304241 | mnicholson | 2011-01-26 14:38:22 -0600 (Wed, 26 Jan 2011) | 6 lines
This patch modifies chan_sip to route responses to the address the request came from. It also modifies chan_sip to respect the maddr parameter in the Via header.
ABE-2664
Review: https://reviewboard.asterisk.org/r/1059/
........
................
................
git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@304246 65c4cc65-6c06-0410-ace0-fbb531ad65f3
Diffstat (limited to 'channels')
-rw-r--r-- | channels/chan_sip.c | 70 | ||||
-rw-r--r-- | channels/sip/include/reqresp_parser.h | 42 | ||||
-rw-r--r-- | channels/sip/include/sip.h | 11 | ||||
-rw-r--r-- | channels/sip/reqresp_parser.c | 276 |
4 files changed, 305 insertions, 94 deletions
diff --git a/channels/chan_sip.c b/channels/chan_sip.c index 183b2da35..d0c0fffa3 100644 --- a/channels/chan_sip.c +++ b/channels/chan_sip.c @@ -7153,17 +7153,20 @@ struct sip_pvt *sip_alloc(ast_string_field callid, struct ast_sockaddr *addr, /* If this dialog is created as a result of a request or response, lets store * some information about it in the dialog. */ if (req) { - char *sent_by, *branch; + struct sip_via *via; const char *cseq = get_header(req, "Cseq"); unsigned int seqno; /* get branch parameter from initial Request that started this dialog */ - get_viabranch(ast_strdupa(get_header(req, "Via")), &sent_by, &branch); - /* only store the branch if it begins with the magic prefix "z9hG4bK", otherwise - * it is not useful to us to have it */ - if (!ast_strlen_zero(branch) && !strncasecmp(branch, "z9hG4bK", 7)) { - ast_string_field_set(p, initviabranch, branch); - ast_string_field_set(p, initviasentby, sent_by); + via = parse_via(get_header(req, "Via")); + if (via) { + /* only store the branch if it begins with the magic prefix "z9hG4bK", otherwise + * it is not useful to us to have it */ + if (!ast_strlen_zero(via->branch) && !strncasecmp(via->branch, "z9hG4bK", 7)) { + ast_string_field_set(p, initviabranch, via->branch); + ast_string_field_set(p, initviasentby, via->sent_by); + } + free_via(via); } /* Store initial incoming cseq. An error in sscanf here is ignored. There is no approperiate @@ -7275,6 +7278,38 @@ struct sip_pvt *sip_alloc(ast_string_field callid, struct ast_sockaddr *addr, return p; } +static int process_via(struct sip_pvt *p, const struct sip_request *req) +{ + struct sip_via *via = parse_via(get_header(req, "Via")); + + if (!via) { + ast_log(LOG_ERROR, "error processing via header\n"); + return -1; + } + + if (via->maddr) { + if (ast_sockaddr_resolve_first(&p->sa, via->maddr, PARSE_PORT_FORBID)) { + ast_log(LOG_WARNING, "Can't find address for maddr '%s'\n", via->maddr); + ast_log(LOG_ERROR, "error processing via header\n"); + free_via(via); + return -1; + } + + if (via->port) { + ast_sockaddr_set_port(&p->sa, via->port); + } else { + ast_sockaddr_set_port(&p->sa, STANDARD_SIP_PORT); + } + + if (ast_sockaddr_is_ipv4_multicast(&p->sa)) { + setsockopt(sipsock, IPPROTO_IP, IP_MULTICAST_TTL, &via->ttl, sizeof(via->ttl)); + } + } + + free_via(via); + return 0; +} + /* \brief arguments used for Request/Response to matching */ struct match_req_args { int method; @@ -7572,6 +7607,7 @@ static struct sip_pvt *find_call(struct sip_request *req, struct ast_sockaddr *a dialog_find_multiple, &tmp_dialog, "pedantic ao2_find in dialogs"); + struct sip_via *via = NULL; args.method = req->method; args.callid = NULL; /* we already matched this. */ @@ -7580,7 +7616,11 @@ static struct sip_pvt *find_call(struct sip_request *req, struct ast_sockaddr *a args.seqno = seqno; /* get via header information. */ args.ruri = REQ_OFFSET_TO_STR(req, rlPart2); - get_viabranch(ast_strdupa(get_header(req, "Via")), (char **) &args.viasentby, (char **) &args.viabranch); + via = parse_via(get_header(req, "Via")); + if (via) { + args.viasentby = via->sent_by; + args.viabranch = via->branch; + } /* determine if this is a Request with authentication credentials. */ if (!ast_strlen_zero(get_header(req, "Authorization")) || !ast_strlen_zero(get_header(req, "Proxy-Authorization"))) { @@ -7604,6 +7644,7 @@ static struct sip_pvt *find_call(struct sip_request *req, struct ast_sockaddr *a sip_pvt_lock(sip_pvt_ptr); ao2_iterator_destroy(iterator); dialog_unref(fork_pvt, "unref fork_pvt"); + free_via(via); return sip_pvt_ptr; /* return pvt with ref */ case SIP_REQ_LOOP_DETECTED: /* This is likely a forked Request that somehow resulted in us receiving multiple parts of the fork. @@ -7612,6 +7653,7 @@ static struct sip_pvt *find_call(struct sip_request *req, struct ast_sockaddr *a dialog_unref(sip_pvt_ptr, "pvt did not match incoming SIP msg, unref from search."); ao2_iterator_destroy(iterator); dialog_unref(fork_pvt, "unref fork_pvt"); + free_via(via); return NULL; case SIP_REQ_FORKED: dialog_unref(fork_pvt, "throwing way pvt to fork off of."); @@ -7633,10 +7675,13 @@ static struct sip_pvt *find_call(struct sip_request *req, struct ast_sockaddr *a if (fork_pvt->method == SIP_INVITE) { forked_invite_init(req, args.totag, fork_pvt, addr); dialog_unref(fork_pvt, "throwing way old forked pvt"); + free_via(via); return NULL; } fork_pvt = dialog_unref(fork_pvt, "throwing way pvt to fork off of"); } + + free_via(via); } /* end of pedantic mode Request/Reponse to Dialog matching */ /* See if the method is capable of creating a dialog */ @@ -9686,6 +9731,15 @@ static int respprep(struct sip_request *resp, struct sip_pvt *p, const char *msg ast_string_field_set(p, url, NULL); } + /* default to routing the response to the address where the request + * came from. Since we don't have a transport layer, we do this here. + */ + p->sa = p->recv; + + if (process_via(p, req)) { + ast_log(LOG_WARNING, "error processing via header, will send response to originating address\n"); + } + return 0; } diff --git a/channels/sip/include/reqresp_parser.h b/channels/sip/include/reqresp_parser.h index 8c8c59ad6..bb1831431 100644 --- a/channels/sip/include/reqresp_parser.h +++ b/channels/sip/include/reqresp_parser.h @@ -166,9 +166,43 @@ int sip_reqresp_parser_init(void); void sip_reqresp_parser_exit(void); /*! - * \brief Parse the VIA header into it's parts. - * - * \note This will modify the string + * \brief Parse a Via header + * + * This function parses the Via header and processes it according to section + * 18.2 of RFC 3261 and RFC 3581. Since we don't have a transport layer, we + * only care about the maddr and ttl parms. The received and rport params are + * not parsed. + * + * \note This function fails to parse some odd combinations of SWS in parameter + * lists. + * + * \code + * VIA syntax. RFC 3261 section 25.1 + * Via = ( "Via" / "v" ) HCOLON via-parm *(COMMA via-parm) + * via-parm = sent-protocol LWS sent-by *( SEMI via-params ) + * via-params = via-ttl / via-maddr + * / via-received / via-branch + * / via-extension + * via-ttl = "ttl" EQUAL ttl + * via-maddr = "maddr" EQUAL host + * via-received = "received" EQUAL (IPv4address / IPv6address) + * via-branch = "branch" EQUAL token + * via-extension = generic-param + * sent-protocol = protocol-name SLASH protocol-version + * SLASH transport + * protocol-name = "SIP" / token + * protocol-version = token + * transport = "UDP" / "TCP" / "TLS" / "SCTP" + * / other-transport + * sent-by = host [ COLON port ] + * ttl = 1*3DIGIT ; 0 to 255 + * \endcode + */ +struct sip_via *parse_via(const char *header); + +/* + * \brief Free parsed Via data. */ -void get_viabranch(char *via, char **sent_by, char **branch); +void free_via(struct sip_via *v); + #endif diff --git a/channels/sip/include/sip.h b/channels/sip/include/sip.h index 385d9663b..57c155e14 100644 --- a/channels/sip/include/sip.h +++ b/channels/sip/include/sip.h @@ -789,6 +789,17 @@ struct sip_route { char hop[0]; }; +/*! \brief Structure to store Via information */ +struct sip_via { + char *via; + const char *protocol; + const char *sent_by; + const char *branch; + const char *maddr; + unsigned int port; + unsigned char ttl; +}; + /*! \brief Domain data structure. \note In the future, we will connect this to a configuration tree specific for this domain diff --git a/channels/sip/reqresp_parser.c b/channels/sip/reqresp_parser.c index aaf1f56c7..ecea91f0c 100644 --- a/channels/sip/reqresp_parser.c +++ b/channels/sip/reqresp_parser.c @@ -2243,123 +2243,159 @@ AST_TEST_DEFINE(sip_uri_cmp_test) return test_res; } -void get_viabranch(char *via, char **sent_by, char **branch) +void free_via(struct sip_via *v) { - char *tmp; - - if (sent_by) { - *sent_by = NULL; - } - if (branch) { - *branch = NULL; - } - if (ast_strlen_zero(via)) { + if (!v) { return; } - via = ast_skip_blanks(via); - /* - * VIA syntax. RFC 3261 section 6.40.5 - * Via = ( "Via" | "v") ":" 1#( sent-protocol sent-by *( ";" via-params ) [ comment ] ) - * via-params = via-hidden | via-ttl | via-maddr | via-received | via-branch - * via-hidden = "hidden" - * via-ttl = "ttl" "=" ttl - * via-maddr = "maddr" "=" maddr - * via-received = "received" "=" host - * via-branch = "branch" "=" token - * sent-protocol = protocol-name "/" protocol-version "/" transport - * protocol-name = "SIP" | token - * protocol-version = token - * transport = "UDP" | "TCP" | token - * sent-by = ( host [ ":" port ] ) | ( concealed-host ) - * concealed-host = token - * ttl = 1*3DIGIT ; 0 to 255 - */ - /* chop off ("Via:" | "v:") if present */ - if (!strncasecmp(via, "Via:", 4)) { - via += 4; - } else if (!strncasecmp(via, "v:", 2)) { - via += 2; + if (v->via) { + ast_free(v->via); } - if (ast_strlen_zero(via)) { - return; + + ast_free(v); +} + +struct sip_via *parse_via(const char *header) +{ + struct sip_via *v = ast_calloc(1, sizeof(*v)); + char *via, *parm; + + if (!v) { + return NULL; } - /* chop off sent-protocol */ - via = ast_skip_blanks(via); - strsep(&via, " \t\r\n"); + v->via = ast_strdup(header); + v->ttl = 1; + + via = v->via; + if (ast_strlen_zero(via)) { - return; + ast_log(LOG_ERROR, "received request without a Via header\n"); + free_via(v); + return NULL; } - /* chop off sent-by */ - via = ast_skip_blanks(via); - *sent_by = strsep(&via, "; \t\r\n"); - if (ast_strlen_zero(via)) { - return; + /* seperate the first via-parm */ + via = strsep(&via, ","); + + /* chop off sent-protocol */ + v->protocol = strsep(&via, " \t\r\n"); + if (ast_strlen_zero(v->protocol)) { + ast_log(LOG_ERROR, "missing sent-protocol in Via header\n"); + free_via(v); + return NULL; } + v->protocol = ast_skip_blanks(v->protocol); - /* now see if there is a branch parameter in there */ - if (!ast_strlen_zero(via) && (tmp = strstr(via, "branch="))) { - /* find the branch ID */ - via = ast_skip_blanks(tmp + 7); + if (via) { + via = ast_skip_blanks(via); + } - /* chop off the branch parameter */ - *branch = strsep(&via, "; \t\r\n"); + /* chop off sent-by */ + v->sent_by = strsep(&via, "; \t\r\n"); + if (ast_strlen_zero(v->sent_by)) { + ast_log(LOG_ERROR, "missing sent-by in Via header\n"); + free_via(v); + return NULL; + } + v->sent_by = ast_skip_blanks(v->sent_by); + + /* store the port */ + if ((parm = strchr(v->sent_by, ':'))) { + char *endptr; + + v->port = strtol(++parm, &endptr, 10); + } + + /* evaluate any via-parms */ + while ((parm = strsep(&via, "; \t\r\n"))) { + char *c; + if ((c = strstr(parm, "maddr="))) { + v->maddr = ast_skip_blanks(c + sizeof("maddr=") - 1); + } else if ((c = strstr(parm, "branch="))) { + v->branch = ast_skip_blanks(c + sizeof("branch=") - 1); + } else if ((c = strstr(parm, "ttl="))) { + char *endptr; + c = ast_skip_blanks(c + sizeof("ttl=") - 1); + v->ttl = strtol(c, &endptr, 10); + + /* make sure we got a valid ttl value */ + if (c == endptr) { + v->ttl = 1; + } + } } + + return v; } -AST_TEST_DEFINE(get_viabranch_test) +AST_TEST_DEFINE(parse_via_test) { int res = AST_TEST_PASS; int i = 1; - char *sent_by, *branch; + struct sip_via *via; struct testdata { char *in; + char *expected_protocol; char *expected_branch; char *expected_sent_by; + char *expected_maddr; + unsigned int expected_port; + unsigned char expected_ttl; + int expected_null; AST_LIST_ENTRY(testdata) list; }; struct testdata *testdataptr; static AST_LIST_HEAD_NOLOCK(testdataliststruct, testdata) testdatalist; struct testdata t1 = { - .in = "Via: SIP/2.0/UDP host:port;branch=thebranch", - .expected_branch = "thebranch", - .expected_sent_by = "host:port" - }; - struct testdata t2 = { .in = "SIP/2.0/UDP host:port;branch=thebranch", + .expected_protocol = "SIP/2.0/UDP", + .expected_sent_by = "host:port", .expected_branch = "thebranch", - .expected_sent_by = "host:port" }; - struct testdata t3 = { + struct testdata t2 = { .in = "SIP/2.0/UDP host:port", + .expected_protocol = "SIP/2.0/UDP", + .expected_sent_by = "host:port", .expected_branch = "", - .expected_sent_by = "host:port" + }; + struct testdata t3 = { + .in = "SIP/2.0/UDP", + .expected_null = 1, }; struct testdata t4 = { - .in = "BLAH/BLAH/BLAH host:port ; branch= thebranch ;;;;;;;", - .expected_branch = "thebranch", - .expected_sent_by = "host:port" + .in = "BLAH/BLAH/BLAH host:port;branch=", + .expected_protocol = "BLAH/BLAH/BLAH", + .expected_sent_by = "host:port", + .expected_branch = "", }; struct testdata t5 = { - .in = "v: BLAH/BLAH/BLAH", - .expected_branch = "", - .expected_sent_by = "" + .in = "SIP/2.0/UDP host:5060;branch=thebranch;maddr=224.0.0.1;ttl=1", + .expected_protocol = "SIP/2.0/UDP", + .expected_sent_by = "host:5060", + .expected_port = 5060, + .expected_branch = "thebranch", + .expected_maddr = "224.0.0.1", + .expected_ttl = 1, }; struct testdata t6 = { - .in = "BLAH/BLAH/BLAH host:port;branch=", - .expected_branch = "", - .expected_sent_by = "host:port" + .in = "SIP/2.0/UDP host:5060;\n branch=thebranch;\r\n maddr=224.0.0.1; ttl=1", + .expected_protocol = "SIP/2.0/UDP", + .expected_sent_by = "host:5060", + .expected_port = 5060, + .expected_branch = "thebranch", + .expected_maddr = "224.0.0.1", + .expected_ttl = 1, }; switch (cmd) { case TEST_INIT: - info->name = "get_viabranch_test"; + info->name = "parse_via_test"; info->category = "/channels/chan_sip/"; - info->summary = "Tests getting sent-by and branch parameter from via"; + info->summary = "Tests parsing the Via header"; info->description = - "Runs through various test situations in which a sent-by and" - " branch parameter must be extracted from a VIA header"; + "Runs through various test situations in which various " + " parameters parameter must be extracted from a VIA header"; return AST_TEST_NOT_RUN; case TEST_EXECUTE: break; @@ -2374,15 +2410,91 @@ AST_TEST_DEFINE(get_viabranch_test) AST_LIST_TRAVERSE(&testdatalist, testdataptr, list) { - get_viabranch(ast_strdupa(testdataptr->in), &sent_by, &branch); - if ((ast_strlen_zero(sent_by) && !ast_strlen_zero(testdataptr->expected_sent_by)) || - (ast_strlen_zero(branch) && !ast_strlen_zero(testdataptr->expected_branch)) || - (!ast_strlen_zero(sent_by) && strcmp(sent_by, testdataptr->expected_sent_by)) || - (!ast_strlen_zero(branch) && strcmp(branch, testdataptr->expected_branch))) { - ast_test_status_update(test, "TEST#%d FAILED: VIA = \"%s\" parsed sent-by = \"%s\" parsed branch = \"%s\"\n", - i, testdataptr->in, sent_by, branch); + via = parse_via(testdataptr->in); + if (!via) { + if (!testdataptr->expected_null) { + ast_test_status_update(test, "TEST#%d FAILED: VIA = \"%s\"\n" + "failed to parse header\n", + i, testdataptr->in); + res = AST_TEST_FAIL; + } + i++; + continue; + } + + if (testdataptr->expected_null) { + ast_test_status_update(test, "TEST#%d FAILED: VIA = \"%s\"\n" + "successfully parased invalid via header\n", + i, testdataptr->in); + res = AST_TEST_FAIL; + free_via(via); + i++; + continue; + } + + if ((ast_strlen_zero(via->protocol) && !ast_strlen_zero(testdataptr->expected_protocol)) + || (!ast_strlen_zero(via->protocol) && strcmp(via->protocol, testdataptr->expected_protocol))) { + + ast_test_status_update(test, "TEST#%d FAILED: VIA = \"%s\"\n" + "parsed protocol = \"%s\"\n" + "expected = \"%s\"\n" + "failed to parse protocol\n", + i, testdataptr->in, via->protocol, testdataptr->expected_protocol); + res = AST_TEST_FAIL; + } + + if ((ast_strlen_zero(via->sent_by) && !ast_strlen_zero(testdataptr->expected_sent_by)) + || (!ast_strlen_zero(via->sent_by) && strcmp(via->sent_by, testdataptr->expected_sent_by))) { + + ast_test_status_update(test, "TEST#%d FAILED: VIA = \"%s\"\n" + "parsed sent_by = \"%s\"\n" + "expected = \"%s\"\n" + "failed to parse sent-by\n", + i, testdataptr->in, via->sent_by, testdataptr->expected_sent_by); + res = AST_TEST_FAIL; + } + + if (testdataptr->expected_port && testdataptr->expected_port != via->port) { + ast_test_status_update(test, "TEST#%d FAILED: VIA = \"%s\"\n" + "parsed port = \"%d\"\n" + "expected = \"%d\"\n" + "failed to parse port\n", + i, testdataptr->in, via->port, testdataptr->expected_port); + res = AST_TEST_FAIL; + } + + if ((ast_strlen_zero(via->branch) && !ast_strlen_zero(testdataptr->expected_branch)) + || (!ast_strlen_zero(via->branch) && strcmp(via->branch, testdataptr->expected_branch))) { + + ast_test_status_update(test, "TEST#%d FAILED: VIA = \"%s\"\n" + "parsed branch = \"%s\"\n" + "expected = \"%s\"\n" + "failed to parse branch\n", + i, testdataptr->in, via->branch, testdataptr->expected_branch); res = AST_TEST_FAIL; } + + if ((ast_strlen_zero(via->maddr) && !ast_strlen_zero(testdataptr->expected_maddr)) + || (!ast_strlen_zero(via->maddr) && strcmp(via->maddr, testdataptr->expected_maddr))) { + + ast_test_status_update(test, "TEST#%d FAILED: VIA = \"%s\"\n" + "parsed maddr = \"%s\"\n" + "expected = \"%s\"\n" + "failed to parse maddr\n", + i, testdataptr->in, via->maddr, testdataptr->expected_maddr); + res = AST_TEST_FAIL; + } + + if (testdataptr->expected_ttl && testdataptr->expected_ttl != via->ttl) { + ast_test_status_update(test, "TEST#%d FAILED: VIA = \"%s\"\n" + "parsed ttl = \"%d\"\n" + "expected = \"%d\"\n" + "failed to parse ttl\n", + i, testdataptr->in, via->ttl, testdataptr->expected_ttl); + res = AST_TEST_FAIL; + } + + free_via(via); i++; } return res; @@ -2399,7 +2511,7 @@ void sip_request_parser_register_tests(void) AST_TEST_REGISTER(parse_contact_header_test); AST_TEST_REGISTER(sip_parse_options_test); AST_TEST_REGISTER(sip_uri_cmp_test); - AST_TEST_REGISTER(get_viabranch_test); + AST_TEST_REGISTER(parse_via_test); } void sip_request_parser_unregister_tests(void) { @@ -2412,7 +2524,7 @@ void sip_request_parser_unregister_tests(void) AST_TEST_UNREGISTER(parse_contact_header_test); AST_TEST_UNREGISTER(sip_parse_options_test); AST_TEST_UNREGISTER(sip_uri_cmp_test); - AST_TEST_UNREGISTER(get_viabranch_test); + AST_TEST_UNREGISTER(parse_via_test); } int sip_reqresp_parser_init(void) |