dect
/
asterisk
Archived
13
0
Fork 0

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: http://svn.digium.com/svn/asterisk/trunk@304246 f38db490-d61c-443f-a65b-d21fe96a405b
This commit is contained in:
mnicholson 2011-01-26 20:44:47 +00:00
parent 891ff5a82b
commit 43274c4f8f
6 changed files with 321 additions and 91 deletions

View File

@ -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;
}

View File

@ -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.
* \brief Parse a Via header
*
* \note This will modify the string
* 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
*/
void get_viabranch(char *via, char **sent_by, char **branch);
struct sip_via *parse_via(const char *header);
/*
* \brief Free parsed Via data.
*/
void free_via(struct sip_via *v);
#endif

View File

@ -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

View File

@ -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);
}
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;
}
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;
}
/* seperate the first via-parm */
via = strsep(&via, ",");
/* chop off sent-protocol */
via = ast_skip_blanks(via);
strsep(&via, " \t\r\n");
if (ast_strlen_zero(via)) {
return;
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);
if (via) {
via = ast_skip_blanks(via);
}
/* chop off sent-by */
via = ast_skip_blanks(via);
*sent_by = strsep(&via, "; \t\r\n");
if (ast_strlen_zero(via)) {
return;
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);
}
/* 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);
/* 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);
/* chop off the branch parameter */
*branch = strsep(&via, "; \t\r\n");
/* 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",
.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 t2 = {
.in = "SIP/2.0/UDP host:port;branch=thebranch",
.expected_branch = "thebranch",
.expected_sent_by = "host:port"
.in = "SIP/2.0/UDP host:port",
.expected_protocol = "SIP/2.0/UDP",
.expected_sent_by = "host:port",
.expected_branch = "",
};
struct testdata t3 = {
.in = "SIP/2.0/UDP host:port",
.expected_branch = "",
.expected_sent_by = "host:port"
.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)

View File

@ -385,6 +385,20 @@ int ast_sockaddr_is_ipv4(const struct ast_sockaddr *addr);
*/
int ast_sockaddr_is_ipv4_mapped(const struct ast_sockaddr *addr);
/*!
* \since 1.10
*
* \brief
* Determine if an IPv4 address is a multicast address
*
* \parm addr the address to check
*
* This function checks if an address is in the 224.0.0.0/4 network block.
*
* \return non-zero if this is a multicast address
*/
int ast_sockaddr_is_ipv4_multicast(const struct ast_sockaddr *addr);
/*!
* \since 1.8
*

View File

@ -381,6 +381,11 @@ int ast_sockaddr_is_ipv4_mapped(const struct ast_sockaddr *addr)
return addr->len && IN6_IS_ADDR_V4MAPPED(&sin6->sin6_addr);
}
int ast_sockaddr_is_ipv4_multicast(const struct ast_sockaddr *addr)
{
return ((ast_sockaddr_ipv4(addr) & 0xf0000000) == 0xe0000000);
}
int ast_sockaddr_is_ipv6(const struct ast_sockaddr *addr)
{
return addr->ss.ss_family == AF_INET6 &&