diff options
author | mnicholson <mnicholson@f38db490-d61c-443f-a65b-d21fe96a405b> | 2011-01-26 20:38:22 +0000 |
---|---|---|
committer | mnicholson <mnicholson@f38db490-d61c-443f-a65b-d21fe96a405b> | 2011-01-26 20:38:22 +0000 |
commit | 40f25655422ee64a2720869a47a8a8727dfbb6e3 (patch) | |
tree | 4f7f7be4ea82e280735a7213a1164dc6cbaaeff7 /channels | |
parent | f16dff006a478351bcf57f00e3ed42130ce0d468 (diff) |
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/branches/1.4@304241 f38db490-d61c-443f-a65b-d21fe96a405b
Diffstat (limited to 'channels')
-rw-r--r-- | channels/chan_sip.c | 194 |
1 files changed, 194 insertions, 0 deletions
diff --git a/channels/chan_sip.c b/channels/chan_sip.c index 55cb54a82..c04e09e84 100644 --- a/channels/chan_sip.c +++ b/channels/chan_sip.c @@ -690,6 +690,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 Modes for SIP domain handling in the PBX */ enum domain_mode { SIP_DOMAIN_AUTO, /*!< This domain is auto-configured */ @@ -1586,6 +1597,8 @@ static int get_destination(struct sip_pvt *p, struct sip_request *oreq); static int get_msg_text(char *buf, int len, struct sip_request *req); static void free_old_route(struct sip_route *route); static int transmit_state_notify(struct sip_pvt *p, int state, int full, int timeout); +static struct sip_via *parse_via(const char *header); +static void free_via(struct sip_via *v); /*--- Constructing requests and responses */ static void initialize_initreq(struct sip_pvt *p, struct sip_request *req); @@ -4948,6 +4961,177 @@ static struct sip_pvt *sip_alloc(ast_string_field callid, struct sockaddr_in *si return p; } +static void free_via(struct sip_via *v) +{ + if (!v) { + return; + } + + if (v->via) { + ast_free(v->via); + } + + ast_free(v); +} + +/*! + * \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 + */ +static 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)) { + 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 */ + 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 */ + 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; +} + +/*! + * \brief Check if an ip is an multicast IP. + * \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 + */ +static int addr_is_multicast(struct in_addr *addr) +{ + return ((addr->s_addr & 0xf0000000) == 0xe0000000); +} + +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) { + struct hostent *hp; + struct ast_hostent ahp; + + hp = ast_gethostbyname(via->maddr, &ahp); + if (hp == NULL) { + 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; + } + + p->sa.sin_family = AF_INET; + memcpy(&p->sa.sin_addr, hp->h_addr, sizeof(p->sa.sin_addr)); + + if (via->port) { + p->sa.sin_port = via->port; + } else { + p->sa.sin_port = STANDARD_SIP_PORT; + } + + if (addr_is_multicast(&p->sa.sin_addr)) { + setsockopt(sipsock, IPPROTO_IP, IP_MULTICAST_TTL, &via->ttl, sizeof(via->ttl)); + } + } + + free_via(via); + return 0; +} + /*! \brief Connect incoming SIP message to current dialog or create new dialog structure Called by handle_request, sipsock_read */ static struct sip_pvt *find_call(struct sip_request *req, struct sockaddr_in *sin, const int intended_method) @@ -6592,6 +6776,16 @@ static int respprep(struct sip_request *resp, struct sip_pvt *p, const char *msg } else if (!ast_strlen_zero(p->our_contact) && resp_needs_contact(msg, p->method)) { add_header(resp, "Contact", p->our_contact); } + + /* 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; } |