diff --git a/channels/chan_sip.c b/channels/chan_sip.c index 91c208b3a9..9d03955dc0 100644 --- a/channels/chan_sip.c +++ b/channels/chan_sip.c @@ -519,11 +519,13 @@ struct sip_request { int len; /*!< Length */ int headers; /*!< # of SIP Headers */ int method; /*!< Method of this request */ - int lines; /*!< SDP Content */ + int lines; /*!< Body Content */ unsigned int flags; /*!< SIP_PKT Flags for this packet */ char *header[SIP_MAX_HEADERS]; char *line[SIP_MAX_LINES]; char data[SIP_MAX_PACKET]; + unsigned int sdp_start; /*!< the line number where the SDP begins */ + unsigned int sdp_end; /*!< the line number where the SDP ends */ }; /* @@ -1091,9 +1093,9 @@ static struct sip_pvt *find_call(struct sip_request *req, struct sockaddr_in *si /*--- Codec handling / SDP */ static void try_suggested_sip_codec(struct sip_pvt *p); -static const char *get_sdp_by_line(const char* line, const char *name, int nameLen); static const char* get_sdp_iterate(int* start, struct sip_request *req, const char *name); static const char *get_sdp(struct sip_request *req, const char *name); +static int find_sdp(struct sip_request *req); static int process_sdp(struct sip_pvt *p, struct sip_request *req); static void add_codec_to_sdp(const struct sip_pvt *p, int codec, int sample_rate, char **m_buf, size_t *m_size, char **a_buf, size_t *a_size, @@ -3270,37 +3272,55 @@ static struct ast_channel *sip_new(struct sip_pvt *i, int state, const char *tit } /*! \brief Reads one line of SIP message body */ -static const char *get_sdp_by_line(const char* line, const char *name, int nameLen) +static char *get_body_by_line(const char *line, const char *name, int nameLen) { if (strncasecmp(line, name, nameLen) == 0 && line[nameLen] == '=') return ast_skip_blanks(line + nameLen + 1); + return ""; } -/*! \brief get_sdp_iterate: lookup 'name' in the request starting +/*! \brief Lookup 'name' in the SDP starting * at the 'start' line. Returns the matching line, and 'start' * is updated with the next line number. */ -static const char* get_sdp_iterate(int* start, struct sip_request *req, const char *name) +static const char *get_sdp_iterate(int *start, struct sip_request *req, const char *name) { int len = strlen(name); - while (*start < req->lines) { - const char *r = get_sdp_by_line(req->line[(*start)++], name, len); + while (*start < req->sdp_end) { + const char *r = get_body_by_line(req->line[(*start)++], name, len); if (r[0] != '\0') return r; } + return ""; } -/*! \brief get_sdp: Gets all kind of SIP message bodies, including SDP, - but the name wrongly applies _only_ sdp */ +/*! \brief Get a line from an SDP message body */ static const char *get_sdp(struct sip_request *req, const char *name) { int dummy = 0; + return get_sdp_iterate(&dummy, req, name); } +/*! \brief Get a specific line from the message body */ +static char *get_body(struct sip_request *req, char *name) +{ + int x; + int len = strlen(name); + char *r; + + for (x = 0; x < req->lines; x++) { + r = get_body_by_line(req->line[x], name, len); + if (r[0] != '\0') + return r; + } + + return ""; +} + /*! \brief Find compressed SIP alias */ static const char *find_alias(const char *name, const char *_default) { @@ -3329,9 +3349,11 @@ static const char *find_alias(const char *name, const char *_default) { "Session-Expires", "x" }, }; int x; + for (x=0; xsdp_start and req->sdp_end to indicate where the SDP + lives in the message body. +*/ +static int find_sdp(struct sip_request *req) +{ + const char *content_type; + const char *search; + char *boundary; + unsigned int x; + + content_type = get_header(req, "Content-Type"); + + /* if the body contains only SDP, this is easy */ + if (!strcasecmp(content_type, "application/sdp")) { + req->sdp_start = 0; + req->sdp_end = req->lines; + return 1; + } + + /* if it's not multipart/mixed, there cannot be an SDP */ + if (strncasecmp(content_type, "multipart/mixed", 15)) + return 0; + + /* if there is no boundary marker, it's invalid */ + if (!(search = strcasestr(content_type, ";boundary="))) + return 0; + + search += 10; + + if (ast_strlen_zero(search)) + return 0; + + /* make a duplicate of the string, with two extra characters + at the beginning */ + boundary = ast_strdupa(search - 2); + boundary[0] = boundary[1] = '-'; + + /* search for the boundary marker, but stop when there are not enough + lines left for it, the Content-Type header and at least one line of + body */ + for (x = 0; x < (req->lines - 2); x++) { + if (!strncasecmp(req->line[x], boundary, strlen(boundary)) && + !strcasecmp(req->line[x + 1], "Content-Type: application/sdp")) { + req->sdp_start = x + 2; + /* search for the end of the body part */ + for ( ; x < req->lines; x++) { + if (!strncasecmp(req->line[x], boundary, strlen(boundary))) + break; + } + req->sdp_end = x; + return 1; + } + } + + return 0; +} + +/*! \brief Process SIP SDP and activate RTP channels---*/ static int process_sdp(struct sip_pvt *p, struct sip_request *req) { const char *m; @@ -3894,13 +3978,8 @@ static int process_sdp(struct sip_pvt *p, struct sip_request *req) time(&p->lastrtprx); time(&p->lastrtptx); - /* Get codec and RTP info from SDP */ - if (strcasecmp(get_header(req, "Content-Type"), "application/sdp")) { - ast_log(LOG_NOTICE, "Content is '%s', not 'application/sdp'\n", get_header(req, "Content-Type")); - return -1; - } m = get_sdp(req, "m"); - destiterator = 0; + destiterator = req->sdp_start; c = get_sdp_iterate(&destiterator, req, "c"); if (ast_strlen_zero(m) || ast_strlen_zero(c)) { ast_log(LOG_WARNING, "Insufficient information for SDP (m = '%s', c = '%s')\n", m, c); @@ -3916,7 +3995,7 @@ static int process_sdp(struct sip_pvt *p, struct sip_request *req) ast_log(LOG_WARNING, "Unable to lookup host in c= line, '%s'\n", c); return -1; } - iterator = 0; + iterator = req->sdp_start; ast_set_flag(&p->flags[0], SIP_NOVIDEO); while ((m = get_sdp_iterate(&iterator, req, "m"))[0] != '\0') { int found = 0; @@ -4015,7 +4094,7 @@ static int process_sdp(struct sip_pvt *p, struct sip_request *req) /* Next, scan through each "a=rtpmap:" line, noting each * specified RTP payload type (with corresponding MIME subtype): */ - iterator = 0; + iterator = req->sdp_start; while ((a = get_sdp_iterate(&iterator, req, "a"))[0] != '\0') { char* mimeSubtype = ast_strdupa(a); /* ensures we have enough space */ if (!strcasecmp(a, "sendonly")) { @@ -9142,7 +9221,7 @@ static void handle_request_info(struct sip_pvt *p, struct sip_request *req) !strcasecmp(c, "application/vnd.nortelnetworks.digits")) { /* Try getting the "signal=" part */ - if (ast_strlen_zero(c = get_sdp(req, "Signal")) && ast_strlen_zero(c = get_sdp(req, "d"))) { + if (ast_strlen_zero(c = get_body(req, "Signal")) && ast_strlen_zero(c = get_body(req, "d"))) { ast_log(LOG_WARNING, "Unable to retrieve DTMF signal from INFO message from %s\n", p->callid); transmit_response(p, "200 OK", req); /* Should return error */ return; @@ -10002,7 +10081,7 @@ static void handle_response_invite(struct sip_pvt *p, int resp, char *rest, stru if (p->owner->_state != AST_STATE_UP) ast_setstate(p->owner, AST_STATE_RINGING); } - if (!strcasecmp(get_header(req, "Content-Type"), "application/sdp")) { + if (find_sdp(req)) { process_sdp(p, req); if (!ast_test_flag(req, SIP_PKT_IGNORE) && p->owner) { /* Queue a progress frame only if we have SDP in 180 */ @@ -10014,7 +10093,7 @@ static void handle_response_invite(struct sip_pvt *p, int resp, char *rest, stru if (!ast_test_flag(req, SIP_PKT_IGNORE)) sip_cancel_destroy(p); /* Ignore 183 Session progress without SDP */ - if (!strcasecmp(get_header(req, "Content-Type"), "application/sdp")) { + if (find_sdp(req)) { process_sdp(p, req); if (!ast_test_flag(req, SIP_PKT_IGNORE) && p->owner) { /* Queue a progress frame */ @@ -10026,7 +10105,7 @@ static void handle_response_invite(struct sip_pvt *p, int resp, char *rest, stru if (!ast_test_flag(req, SIP_PKT_IGNORE)) sip_cancel_destroy(p); p->authtries = 0; - if (!strcasecmp(get_header(req, "Content-Type"), "application/sdp")) + if (find_sdp(req)) process_sdp(p, req); /* Parse contact header for continued conversation */ @@ -10594,7 +10673,7 @@ static void handle_response(struct sip_pvt *p, int resp, char *rest, struct sip_ } else if ((resp >= 100) && (resp < 200)) { if (sipmethod == SIP_INVITE) { sip_cancel_destroy(p); - if (!ast_strlen_zero(get_header(req, "Content-Type"))) + if (find_sdp(req)) process_sdp(p, req); if (p->owner) { /* Queue a progress frame */ @@ -11155,7 +11234,7 @@ static int handle_request_invite(struct sip_pvt *p, struct sip_request *req, int parse_ok_contact(p, req); } else { /* Re-invite on existing call */ /* Handle SDP here if we already have an owner */ - if (!strcasecmp(get_header(req, "Content-Type"), "application/sdp")) { + if (find_sdp(req)) { if (process_sdp(p, req)) { transmit_response(p, "488 Not acceptable here", req); if (!p->lastinvite) @@ -11186,7 +11265,7 @@ static int handle_request_invite(struct sip_pvt *p, struct sip_request *req, int } /* We have a succesful authentication, process the SDP portion if there is one */ - if (!strcasecmp(get_header(req, "Content-Type"), "application/sdp") ) { + if (find_sdp(req)) { if (process_sdp(p, req)) { /* Unacceptable codecs */ transmit_response_reliable(p, "488 Not acceptable here", req); @@ -11986,7 +12065,7 @@ static int handle_request(struct sip_pvt *p, struct sip_request *req, struct soc if (seqno == p->pendinginvite) { p->pendinginvite = 0; __sip_ack(p, seqno, FLAG_RESPONSE, 0, FALSE); - if (!ast_strlen_zero(get_header(req, "Content-Type"))) { + if (find_sdp(req)) { if (process_sdp(p, req)) return -1; }