Convert the character pointers in a sip_request to be pointer offsets
authorMark Michelson <mmichelson@digium.com>
Tue, 20 Jan 2009 20:10:31 +0000 (20:10 +0000)
committerMark Michelson <mmichelson@digium.com>
Tue, 20 Jan 2009 20:10:31 +0000 (20:10 +0000)
When an ast_str expands to hold more data, any pointers that were pointing
to the data prior to the expansion will be pointing at invalid memory. This
change makes such pointers used in chan_sip.c instead be offsets from the
beginning of the string so that the same math may be applied no matter where
in memory the string resides.

To help ease this transition, a macro called REQ_OFFSET_TO_STR has been added
to chan_sip.c so that given a sip_request and an offset, the string at that
offset is returned.

(closes issue #14220)
Reported by: riksta
Tested by: putnopvut

Review http://reviewboard.digium.com/r/126/

git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@169557 65c4cc65-6c06-0410-ace0-fbb531ad65f3

channels/chan_sip.c

index f97bd99..23a150d 100644 (file)
@@ -1187,8 +1187,8 @@ struct sip_socket {
  * \endverbatim
  */
 struct sip_request {
-       char *rlPart1;          /*!< SIP Method Name or "SIP/2.0" protocol version */
-       char *rlPart2;          /*!< The Request URI or Response Status */
+       ptrdiff_t rlPart1;              /*!< Offset of the SIP Method Name or "SIP/2.0" protocol version */
+       ptrdiff_t rlPart2;              /*!< Offset of the Request URI or Response Status */
        int len;                /*!< bytes used in data[], excluding trailing null terminator. Rarely used. */
        int headers;            /*!< # of SIP Headers */
        int method;             /*!< Method of this request */
@@ -1198,14 +1198,25 @@ struct sip_request {
        char debug;             /*!< print extra debugging if non zero */
        char has_to_tag;        /*!< non-zero if packet has To: tag */
        char ignore;            /*!< if non-zero This is a re-transmit, ignore it */
-       char *header[SIP_MAX_HEADERS];
-       char *line[SIP_MAX_LINES];
+       /* Array of offsets into the request string of each SIP header*/
+       ptrdiff_t header[SIP_MAX_HEADERS];
+       /* Array of offsets into the request string of each SDP line*/
+       ptrdiff_t line[SIP_MAX_LINES];
        struct ast_str *data;   
        /* XXX Do we need to unref socket.ser when the request goes away? */
        struct sip_socket socket;       /*!< The socket used for this request */
        AST_LIST_ENTRY(sip_request) next;
 };
 
+/* \brief given a sip_request and an offset, return the char * that resides there
+ *
+ * It used to be that rlPart1, rlPart2, and the header and line arrays were character
+ * pointers. They are now offsets into the ast_str portion of the sip_request structure.
+ * To avoid adding a bunch of redundant pointer arithmetic to the code, this macro is
+ * provided to retrieve the string at a particular offset within the request's buffer
+ */
+#define REQ_OFFSET_TO_STR(req,offset) (ast_str_buffer((req)->data) + ((req)->offset))
+
 /*! \brief structure used in transfers */
 struct sip_dual {
        struct ast_channel *chan1;      /*!< First channel involved */
@@ -2745,7 +2756,7 @@ static void *_sip_tcp_helper_thread(struct sip_pvt *pvt, struct ast_tcptls_sessi
                }
 
                /* Read in headers one line at a time */
-               while (req.len < 4 || strncmp((char *)&req.data->str + req.len - 4, "\r\n\r\n", 4)) {
+               while (req.len < 4 || strncmp(REQ_OFFSET_TO_STR(&req, len - 4), "\r\n\r\n", 4)) {
                        ast_mutex_lock(&tcptls_session->lock);
                        if (!fgets(buf, sizeof(buf), tcptls_session->f)) {
                                ast_mutex_unlock(&tcptls_session->lock);
@@ -3758,7 +3769,7 @@ static void add_blank(struct sip_request *req)
        if (!req->lines) {
                /* Add extra empty return. add_header() reserves 4 bytes so cannot be truncated */
                ast_str_append(&req->data, 0, "\r\n");
-               req->len = req->data->used;
+               req->len = ast_str_strlen(req->data);
        }
 }
 
@@ -3777,10 +3788,10 @@ static int send_response(struct sip_pvt *p, struct sip_request *req, enum xmitty
                        ntohs(dst->sin_port), req->data->str);
        }
        if (p->do_history) {
-               struct sip_request tmp = { .rlPart1 = NULL, };
+               struct sip_request tmp = { .rlPart1 = 0, };
                parse_copy(&tmp, req);
                append_history(p, reliable ? "TxRespRel" : "TxResp", "%s / %s - %s", tmp.data->str, get_header(&tmp, "CSeq"), 
-                       (tmp.method == SIP_RESPONSE || tmp.method == SIP_UNKNOWN) ? tmp.rlPart2 : sip_methods[tmp.method].text);
+                       (tmp.method == SIP_RESPONSE || tmp.method == SIP_UNKNOWN) ? REQ_OFFSET_TO_STR(&tmp, rlPart2) : sip_methods[tmp.method].text);
                ast_free(tmp.data);
        }
        res = (reliable) ?
@@ -3813,7 +3824,7 @@ static int send_request(struct sip_pvt *p, struct sip_request *req, enum xmittyp
                        ast_verbose("%sTransmitting (no NAT) to %s:%d:\n%s\n---\n", reliable ? "Reliably " : "", ast_inet_ntoa(p->sa.sin_addr), ntohs(p->sa.sin_port), req->data->str);
        }
        if (p->do_history) {
-               struct sip_request tmp = { .rlPart1 = NULL, };
+               struct sip_request tmp = { .rlPart1 = 0, };
                parse_copy(&tmp, req);
                append_history(p, reliable ? "TxReqRel" : "TxReq", "%s / %s - %s", tmp.data->str, get_header(&tmp, "CSeq"), sip_methods[tmp.method].text);
                ast_free(tmp.data);
@@ -6241,7 +6252,7 @@ static const char *get_sdp_iterate(int *start, struct sip_request *req, const ch
        int len = strlen(name);
 
        while (*start < req->sdp_end) {
-               const char *r = get_body_by_line(req->line[(*start)++], name, len, '=');
+               const char *r = get_body_by_line(REQ_OFFSET_TO_STR(req, line[(*start)++]), name, len, '=');
                if (r[0] != '\0')
                        return r;
        }
@@ -6265,7 +6276,7 @@ static char *get_body(struct sip_request *req, char *name, char delimiter)
        char *r;
 
        for (x = 0; x < req->lines; x++) {
-               r = get_body_by_line(req->line[x], name, len, delimiter);
+               r = get_body_by_line(REQ_OFFSET_TO_STR(req, line[x]), name, len, delimiter);
                if (r[0] != '\0')
                        return r;
        }
@@ -6327,9 +6338,10 @@ static const char *__get_header(const struct sip_request *req, const char *name,
         */
        for (pass = 0; name && pass < 2;pass++) {
                int x, len = strlen(name);
-               for (x=*start; x<req->headers; x++) {
-                       if (!strncasecmp(req->header[x], name, len)) {
-                               char *r = req->header[x] + len; /* skip name */
+               for (x = *start; x < req->headers; x++) {
+                       char *header = REQ_OFFSET_TO_STR(req, header[x]);
+                       if (!strncasecmp(header, name, len)) {
+                               char *r = header + len; /* skip name */
                                if (sip_cfg.pedanticsipchecking)
                                        r = ast_skip_blanks(r);
 
@@ -7074,33 +7086,38 @@ static int lws2sws(char *msgbuf, int len)
 */
 static int parse_request(struct sip_request *req)
 {
-       char *c = req->data->str, **dst = req->header;
+       char *c = req->data->str;
+       ptrdiff_t *dst = req->header;
        int i = 0, lim = SIP_MAX_HEADERS - 1;
        unsigned int skipping_headers = 0;
+       ptrdiff_t current_header_offset = 0;
+       char *previous_header = "";
 
-       req->header[0] = c;
+       req->header[0] = 0;
        req->headers = -1;      /* mark that we are working on the header */
        for (; *c; c++) {
                if (*c == '\r') {               /* remove \r */
                        *c = '\0';
                } else if (*c == '\n') {        /* end of this line */
                        *c = '\0';
+                       current_header_offset = (c + 1) - req->data->str;
+                       previous_header = req->data->str + dst[i];
                        if (skipping_headers) {
                                /* check to see if this line is blank; if so, turn off
                                   the skipping flag, so the next line will be processed
                                   as a body line */
-                               if (ast_strlen_zero(dst[i])) {
+                               if (ast_strlen_zero(previous_header)) {
                                        skipping_headers = 0;
                                }
-                               dst[i] = c + 1; /* record start of next line */
+                               dst[i] = current_header_offset; /* record start of next line */
                                continue;
                        }
                        if (sipdebug) {
                                ast_debug(4, "%7s %2d [%3d]: %s\n",
                                          req->headers < 0 ? "Header" : "Body",
-                                         i, (int) strlen(dst[i]), dst[i]);
+                                         i, (int) strlen(previous_header), previous_header);
                        }
-                       if (ast_strlen_zero(dst[i]) && req->headers < 0) {
+                       if (ast_strlen_zero(previous_header) && req->headers < 0) {
                                req->headers = i;       /* record number of header lines */
                                dst = req->line;        /* start working on the body */
                                i = 0;
@@ -7121,20 +7138,21 @@ static int parse_request(struct sip_request *req)
                                        }
                                }
                        }
-                       dst[i] = c + 1; /* record start of next line */
+                       dst[i] = current_header_offset; /* record start of next line */
                }
-        }
+       }
 
        /* Check for last header or body line without CRLF. The RFC for SDP requires CRLF,
           but since some devices send without, we'll be generous in what we accept. However,
           if we've already reached the maximum number of lines for portion of the message
           we were parsing, we can't accept any more, so just ignore it.
        */
-       if ((i < lim) && !ast_strlen_zero(dst[i])) {
+       previous_header = req->data->str + dst[i];
+       if ((i < lim) && !ast_strlen_zero(previous_header)) {
                if (sipdebug) {
                        ast_debug(4, "%7s %2d [%3d]: %s\n",
                                  req->headers < 0 ? "Header" : "Body",
-                                 i, (int) strlen(dst[i]), dst[i]);
+                                 i, (int) strlen(previous_header), previous_header );
                }
                i++;
        }
@@ -7145,7 +7163,8 @@ static int parse_request(struct sip_request *req)
        } else {                        /* no body */
                req->headers = i;
                req->lines = 0;
-               req->line[0] = "";
+               /* req->data->used will be a NULL byte */
+               req->line[0] = ast_str_strlen(req->data);
        }
 
        if (*c) {
@@ -7230,25 +7249,26 @@ static int find_sdp(struct sip_request *req)
        /* search for the boundary marker, the empty line delimiting headers from
           sdp part and the end boundry if it exists */
 
-       for (x = 0; x < (req->lines ); x++) {
-               if(!strncasecmp(req->line[x], boundary, strlen(boundary))){
-                       if(found_application_sdp && found_end_of_headers){
+       for (x = 0; x < (req->lines); x++) {
+               char *line = REQ_OFFSET_TO_STR(req, line[x]);
+               if (!strncasecmp(line, boundary, strlen(boundary))){
+                       if (found_application_sdp && found_end_of_headers) {
                                req->sdp_end = x-1;
                                return 1;
                        }
                        found_application_sdp = FALSE;
                }
-               if(!strcasecmp(req->line[x], "Content-Type: application/sdp"))
+               if (!strcasecmp(line, "Content-Type: application/sdp"))
                        found_application_sdp = TRUE;
                
-               if (ast_strlen_zero(req->line[x])) {
-                       if(found_application_sdp && !found_end_of_headers){
+               if (ast_strlen_zero(line)) {
+                       if (found_application_sdp && !found_end_of_headers){
                                req->sdp_start = x;
                                found_end_of_headers = TRUE;
                        }
                }
        }
-       if(found_application_sdp && found_end_of_headers) {
+       if (found_application_sdp && found_end_of_headers) {
                req->sdp_end = x;
                return TRUE;
        }
@@ -8059,9 +8079,9 @@ static int add_header(struct sip_request *req, const char *var, const char *valu
        }
 
        ast_str_append(&req->data, 0, "%s: %s\r\n", var, value);
-       req->header[req->headers] = req->data->str + req->len;
+       req->header[req->headers] = req->len;
 
-       req->len += strlen(req->header[req->headers]);
+       req->len = ast_str_strlen(req->data);
        req->headers++;
 
        return 0;       
@@ -8086,9 +8106,9 @@ static int add_line(struct sip_request *req, const char *line)
        if (!req->lines)
                /* Add extra empty return */
                req->len += ast_str_append(&req->data, 0, "\r\n");
-       req->line[req->lines] = req->data->str + req->len;
+       req->line[req->lines] = req->len;
        ast_str_append(&req->data, 0, "%s", line);
-       req->len += strlen(req->line[req->lines]);
+       req->len = ast_str_strlen(req->data);
        req->lines++;
        return 0;       
 }
@@ -8304,9 +8324,9 @@ static int init_resp(struct sip_request *resp, const char *msg)
        resp->method = SIP_RESPONSE;
        if (!(resp->data = ast_str_create(SIP_MIN_PACKET)))
                return -1;
-       resp->header[0] = resp->data->str;
+       resp->header[0] = 0;
        ast_str_set(&resp->data, 0, "SIP/2.0 %s\r\n", msg);
-       resp->len = strlen(resp->header[0]);
+       resp->len = resp->data->used;
        resp->headers++;
        return 0;
 }
@@ -8319,9 +8339,9 @@ static int init_req(struct sip_request *req, int sipmethod, const char *recip)
        if (!(req->data = ast_str_create(SIP_MIN_PACKET)))
                return -1;
        req->method = sipmethod;
-       req->header[0] = req->data->str;
+       req->header[0] = 0;
        ast_str_set(&req->data, 0, "%s %s SIP/2.0\r\n", sip_methods[sipmethod].text, recip);
-       req->len = strlen(req->header[0]);
+       req->len = ast_str_strlen(req->data);
        req->headers++;
        return 0;
 }
@@ -8428,14 +8448,14 @@ static int reqprep(struct sip_request *req, struct sip_pvt *p, int sipmethod, in
        }
        
        if (sipmethod == SIP_CANCEL)
-               c = p->initreq.rlPart2; /* Use original URI */
+               c = REQ_OFFSET_TO_STR(&p->initreq, rlPart2);    /* Use original URI */
        else if (sipmethod == SIP_ACK) {
                /* Use URI from Contact: in 200 OK (if INVITE) 
                (we only have the contacturi on INVITEs) */
                if (!ast_strlen_zero(p->okcontacturi))
                        c = is_strict ? p->route->hop : p->okcontacturi;
                else
-                       c = p->initreq.rlPart2;
+                       c = REQ_OFFSET_TO_STR(&p->initreq, rlPart2);
        } else if (!ast_strlen_zero(p->okcontacturi)) 
                c = is_strict ? p->route->hop : p->okcontacturi; /* Use for BYE or REINVITE */
        else if (!ast_strlen_zero(p->uri)) 
@@ -9364,8 +9384,6 @@ static int transmit_response_with_t38_sdp(struct sip_pvt *p, char *msg, struct s
 /*! \brief copy SIP request (mostly used to save request for responses) */
 static void copy_request(struct sip_request *dst, const struct sip_request *src)
 {
-       long offset;
-       int x;
        struct ast_str *duplicate = dst->data;
 
        /* First copy stuff */
@@ -9385,17 +9403,6 @@ static void copy_request(struct sip_request *dst, const struct sip_request *src)
                
        memcpy(dst->data->str, src->data->str, src->data->used + 1);
        dst->data->used = src->data->used;
-       offset = ((void *)dst->data->str) - ((void *)src->data->str);
-       /* Now fix pointer arithmetic */
-       for (x = 0; x < src->headers; x++)
-               dst->header[x] += offset;
-       for (x = 0; x < src->lines; x++)
-               dst->line[x] += offset;
-       /* On some occasions this function is called without parse_request being called first so lets not create an invalid pointer */
-       if (src->rlPart1)
-               dst->rlPart1 += offset;
-       if (src->rlPart2)
-               dst->rlPart2 += offset;
 }
 
 /*! \brief Used for 200 OK and 183 early media 
@@ -9427,11 +9434,13 @@ static int transmit_response_with_sdp(struct sip_pvt *p, const char *msg, const
 /*! \brief Parse first line of incoming SIP request */
 static int determine_firstline_parts(struct sip_request *req) 
 {
-       char *e = ast_skip_blanks(req->header[0]);      /* there shouldn't be any */
+       char *e = ast_skip_blanks(req->data->str);      /* there shouldn't be any */
+       char *local_rlPart1;
 
        if (!*e)
                return -1;
-       req->rlPart1 = e;       /* method or protocol */
+       req->rlPart1 = e - req->data->str;      /* method or protocol */
+       local_rlPart1 = e;
        e = ast_skip_nonblanks(e);
        if (*e)
                *e++ = '\0';
@@ -9441,10 +9450,10 @@ static int determine_firstline_parts(struct sip_request *req)
                return -1;
        ast_trim_blanks(e);
 
-       if (!strcasecmp(req->rlPart1, "SIP/2.0") ) { /* We have a response */
+       if (!strcasecmp(local_rlPart1, "SIP/2.0") ) { /* We have a response */
                if (strlen(e) < 3)      /* status code is 3 digits */
                        return -1;
-               req->rlPart2 = e;
+               req->rlPart2 = e - req->data->str;
        } else { /* We have a request */
                if ( *e == '<' ) { /* XXX the spec says it must not be in <> ! */
                        ast_debug(3, "Oops. Bogus uri in <> %s\n", e);
@@ -9452,7 +9461,7 @@ static int determine_firstline_parts(struct sip_request *req)
                        if (!*e)
                                return -1; 
                }
-               req->rlPart2 = e;       /* URI */
+               req->rlPart2 = e - req->data->str;      /* URI */
                e = ast_skip_nonblanks(e);
                if (*e)
                        *e++ = '\0';
@@ -12064,7 +12073,7 @@ static int get_destination(struct sip_pvt *p, struct sip_request *oreq)
 
        /* Find the request URI */
        if (req->rlPart2)
-               ast_copy_string(tmp, req->rlPart2, sizeof(tmp));
+               ast_copy_string(tmp, REQ_OFFSET_TO_STR(req, rlPart2), sizeof(tmp));
        
        if (sip_cfg.pedanticsipchecking)
                ast_uri_decode(tmp);
@@ -13001,12 +13010,14 @@ static int get_msg_text(char *buf, int len, struct sip_request *req, int addnewl
        int y;
 
        buf[0] = '\0';
+       /*XXX isn't strlen(buf) going to always be 0? */
        y = len - strlen(buf) - 5;
        if (y < 0)
                y = 0;
-       for (x=0; x < req->lines; x++) {
-               strncat(buf, req->line[x], y); /* safe */
-               y -= strlen(req->line[x]) + 1;
+       for (x = 0; x < req->lines; x++) {
+               char *line = REQ_OFFSET_TO_STR(req, line[x]);
+               strncat(buf, line, y); /* safe */
+               y -= strlen(line) + 1;
                if (y < 0)
                        y = 0;
                if (y != 0 && addnewline)
@@ -18461,10 +18472,12 @@ static int handle_request_invite(struct sip_pvt *p, struct sip_request *req, int
                /* If pedantic is on, we need to check the tags. If they're different, this is
                in fact a forked call through a SIP proxy somewhere. */
                int different;
+               char *initial_rlPart2 = REQ_OFFSET_TO_STR(&p->initreq, rlPart2);
+               char *this_rlPart2 = REQ_OFFSET_TO_STR(req, rlPart2);
                if (sip_cfg.pedanticsipchecking)
-                       different = sip_uri_cmp(p->initreq.rlPart2, req->rlPart2);
+                       different = sip_uri_cmp(initial_rlPart2, this_rlPart2);
                else
-                       different = strcmp(p->initreq.rlPart2, req->rlPart2);
+                       different = strcmp(initial_rlPart2, this_rlPart2);
                if (!different) {
                        transmit_response(p, "482 Loop Detected", req);
                        p->invitestate = INV_COMPLETED;
@@ -18478,12 +18491,12 @@ static int handle_request_invite(struct sip_pvt *p, struct sip_request *req, int
                         * \todo XXX This needs to be reviewed.  YOu don't change the request URI really, you route the packet
                         * correctly instead...
                         */
-                       char *uri = ast_strdupa(req->rlPart2);
+                       char *uri = ast_strdupa(this_rlPart2);
                        char *at = strchr(uri, '@');
                        char *peerorhost;
                        struct sip_pkt *pkt = NULL;
                        if (option_debug > 2) {
-                               ast_log(LOG_DEBUG, "Potential spiral detected. Original RURI was %s, new RURI is %s\n", p->initreq.rlPart2, req->rlPart2);
+                               ast_log(LOG_DEBUG, "Potential spiral detected. Original RURI was %s, new RURI is %s\n", initial_rlPart2, this_rlPart2);
                        }
                        if (at) {
                                *at = '\0';
@@ -20255,7 +20268,7 @@ static int handle_incoming(struct sip_pvt *p, struct sip_request *req, struct so
 
        /* Get Method and Cseq */
        cseq = get_header(req, "Cseq");
-       cmd = req->header[0];
+       cmd = REQ_OFFSET_TO_STR(req, header[0]);
 
        /* Must have Cseq */
        if (ast_strlen_zero(cmd) || ast_strlen_zero(cseq)) {
@@ -20274,8 +20287,8 @@ static int handle_incoming(struct sip_pvt *p, struct sip_request *req, struct so
        }
        /* Get the command XXX */
 
-       cmd = req->rlPart1;
-       e = req->rlPart2;
+       cmd = REQ_OFFSET_TO_STR(req, rlPart1);
+       e = REQ_OFFSET_TO_STR(req, rlPart2);
 
        /* Save useragent of the client */
        useragent = get_header(req, "User-Agent");
@@ -20622,7 +20635,7 @@ static int handle_request_do(struct sip_request *req, struct sockaddr_in *sin)
                ast_str_reset(req->data); /* nulling this out is NOT a good idea here. */
                return 1;
        }
-       req->method = find_sip_method(req->rlPart1);
+       req->method = find_sip_method(REQ_OFFSET_TO_STR(req, rlPart1));
 
        if (req->debug)
                ast_verbose("--- (%d headers %d lines)%s ---\n", req->headers, req->lines, (req->headers + req->lines == 0) ? " Nat keepalive" : "");
@@ -20662,7 +20675,7 @@ static int handle_request_do(struct sip_request *req, struct sockaddr_in *sin)
        p->recv = *sin;
 
        if (p->do_history) /* This is a request or response, note what it was for */
-               append_history(p, "Rx", "%s / %s / %s", req->data->str, get_header(req, "CSeq"), req->rlPart2);
+               append_history(p, "Rx", "%s / %s / %s", req->data->str, get_header(req, "CSeq"), REQ_OFFSET_TO_STR(req, rlPart2));
 
        if (!lockretry) {
                if (!queue_request(p, req)) {