/* The oSIP library implements the Session Initiation Protocol (SIP -rfc3261-) Copyright (C) 2001-2020 Aymeric MOIZARD amoizard@antisip.com This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include #include "parser.h" static void osip_util_replace_all_lws(char *sip_message); static int osip_message_set__header(osip_message_t *sip, const char *hname, const char *hvalue); static int msg_headers_parse(osip_message_t *sip, const char *start_of_header, const char **body); static int msg_osip_body_parse(osip_message_t *sip, const char *start_of_buf, const char **next_body, size_t length); static int __osip_message_startline_parsereq(osip_message_t *dest, const char *buf, const char **headers) { const char *p1; const char *p2; char *requesturi; int i; dest->sip_method = NULL; dest->status_code = 0; dest->reason_phrase = NULL; *headers = buf; /* The first token is the method name: */ p2 = strchr(buf, ' '); if (p2 == NULL) return OSIP_SYNTAXERROR; if (*(p2 + 1) == '\0' || *(p2 + 2) == '\0') return OSIP_SYNTAXERROR; if (p2 - buf == 0) { OSIP_TRACE(osip_trace(__FILE__, __LINE__, OSIP_ERROR, NULL, "No space allowed here\n")); return OSIP_SYNTAXERROR; } dest->sip_method = (char *) osip_malloc(p2 - buf + 1); if (dest->sip_method == NULL) return OSIP_NOMEM; osip_strncpy(dest->sip_method, buf, p2 - buf); /* The second token is a sip-url or a uri: */ p1 = strchr(p2 + 2, ' '); /* no space allowed inside sip-url */ if (p1 == NULL) { OSIP_TRACE(osip_trace(__FILE__, __LINE__, OSIP_ERROR, NULL, "Uncompliant request-uri\n")); osip_free(dest->sip_method); dest->sip_method = NULL; return OSIP_SYNTAXERROR; } if (p1 - p2 < 2) { osip_free(dest->sip_method); dest->sip_method = NULL; return OSIP_SYNTAXERROR; } requesturi = (char *) osip_malloc(p1 - p2); if (requesturi == NULL) { osip_free(dest->sip_method); dest->sip_method = NULL; return OSIP_NOMEM; } osip_clrncpy(requesturi, p2 + 1, (p1 - p2 - 1)); i = osip_uri_init(&(dest->req_uri)); if (i != 0) { osip_free(requesturi); requesturi = NULL; osip_free(dest->sip_method); dest->sip_method = NULL; return OSIP_NOMEM; } i = osip_uri_parse(dest->req_uri, requesturi); osip_free(requesturi); if (i != 0) { osip_free(dest->sip_method); dest->sip_method = NULL; osip_uri_free(dest->req_uri); dest->req_uri = NULL; return OSIP_SYNTAXERROR; } /* find the the version and the beginning of headers */ { const char *hp = p1; hp++; /* skip space */ if (*hp == '\0' || *(hp + 1) == '\0' || *(hp + 2) == '\0' || *(hp + 3) == '\0' || *(hp + 4) == '\0' || *(hp + 5) == '\0' || *(hp + 6) == '\0') { OSIP_TRACE(osip_trace(__FILE__, __LINE__, OSIP_ERROR, NULL, "Uncomplete request line\n")); osip_free(dest->sip_method); dest->sip_method = NULL; osip_uri_free(dest->req_uri); dest->req_uri = NULL; return OSIP_SYNTAXERROR; } if (((hp[0] != 'S') && (hp[0] != 's')) || ((hp[1] != 'I') && (hp[1] != 'i')) || ((hp[2] != 'P') && (hp[2] != 'p')) || (hp[3] != '/')) { OSIP_TRACE(osip_trace(__FILE__, __LINE__, OSIP_ERROR, NULL, "No crlf found/No SIP/2.0 found\n")); osip_free(dest->sip_method); dest->sip_method = NULL; osip_uri_free(dest->req_uri); dest->req_uri = NULL; return OSIP_SYNTAXERROR; } hp = hp + 4; /* SIP/ */ while ((*hp != '\r') && (*hp != '\n')) { if (*hp) { if ((*hp >= '0') && (*hp <= '9')) hp++; else if (*hp == '.') hp++; else { OSIP_TRACE(osip_trace(__FILE__, __LINE__, OSIP_ERROR, NULL, "incorrect sip version string\n")); osip_free(dest->sip_method); dest->sip_method = NULL; osip_uri_free(dest->req_uri); dest->req_uri = NULL; return OSIP_SYNTAXERROR; } } else { OSIP_TRACE(osip_trace(__FILE__, __LINE__, OSIP_ERROR, NULL, "No crlf found\n")); osip_free(dest->sip_method); dest->sip_method = NULL; osip_uri_free(dest->req_uri); dest->req_uri = NULL; return OSIP_SYNTAXERROR; } } if (hp - p1 < 2) { osip_free(dest->sip_method); dest->sip_method = NULL; osip_uri_free(dest->req_uri); dest->req_uri = NULL; return OSIP_SYNTAXERROR; } dest->sip_version = (char *) osip_malloc(hp - p1); if (dest->sip_version == NULL) { osip_free(dest->sip_method); dest->sip_method = NULL; osip_uri_free(dest->req_uri); dest->req_uri = NULL; return OSIP_NOMEM; } osip_strncpy(dest->sip_version, p1 + 1, (hp - p1 - 1)); if (0 != osip_strcasecmp(dest->sip_version, "SIP/2.0")) { OSIP_TRACE(osip_trace(__FILE__, __LINE__, OSIP_WARNING, NULL, "Wrong version number\n")); } hp++; if ((*hp) && ('\r' == hp[-1]) && ('\n' == hp[0])) hp++; (*headers) = hp; } return OSIP_SUCCESS; } static int __osip_message_startline_parseresp(osip_message_t *dest, const char *buf, const char **headers) { const char *statuscode; const char *reasonphrase; dest->req_uri = NULL; dest->sip_method = NULL; *headers = buf; statuscode = strchr(buf, ' '); /* search for first SPACE */ if (statuscode == NULL) return OSIP_SYNTAXERROR; if (statuscode - (*headers) < 7) /* must be at least "SIP" "/" 1*DIGIT "." 1*DIGIT */ return OSIP_SYNTAXERROR; dest->sip_version = (char *) osip_malloc(statuscode - (*headers) + 1); if (dest->sip_version == NULL) return OSIP_NOMEM; osip_strncpy(dest->sip_version, *headers, statuscode - (*headers)); reasonphrase = strchr(statuscode + 1, ' '); if (reasonphrase == NULL) { osip_free(dest->sip_version); dest->sip_version = NULL; return OSIP_SYNTAXERROR; } /* dest->status_code = (char *) osip_malloc (reasonphrase - statuscode); */ /* osip_strncpy (dest->status_code, statuscode + 1, reasonphrase - statuscode - 1); */ if (sscanf(statuscode + 1, "%d", &dest->status_code) != 1) { /* Non-numeric status code */ return OSIP_SYNTAXERROR; } if (dest->status_code == 0) return OSIP_SYNTAXERROR; { const char *hp = reasonphrase; while ((*hp != '\r') && (*hp != '\n')) { if (*hp) hp++; else { OSIP_TRACE(osip_trace(__FILE__, __LINE__, OSIP_ERROR, NULL, "No crlf found\n")); return OSIP_SYNTAXERROR; } } dest->reason_phrase = (char *) osip_malloc(hp - reasonphrase); if (dest->reason_phrase == NULL) { osip_free(dest->sip_version); dest->sip_version = NULL; return OSIP_NOMEM; } osip_strncpy(dest->reason_phrase, reasonphrase + 1, hp - reasonphrase - 1); hp++; if ((*hp) && ('\r' == hp[-1]) && ('\n' == hp[0])) hp++; (*headers) = hp; } return OSIP_SUCCESS; } static int __osip_message_startline_parse(osip_message_t *dest, const char *buf, const char **headers) { if (0 == strncmp((const char *) buf, (const char *) "SIP/", 4)) return __osip_message_startline_parseresp(dest, buf, headers); else return __osip_message_startline_parsereq(dest, buf, headers); } int __osip_find_next_occurence(const char *str, const char *buf, const char **index_of_str, const char *end_of_buf) { size_t slen; *index_of_str = NULL; /* AMD fix */ if (str == NULL || buf == NULL) return OSIP_BADPARAMETER; slen = strlen(str); while (slen < (size_t)(end_of_buf - buf)) { if (!memcmp(str, buf, slen)) { *index_of_str = buf; return OSIP_SUCCESS; } ++buf; } return OSIP_SYNTAXERROR; } /* This method replace all LWS with SP located before the initial CRLFCRLF found or the end of the string. */ static void osip_util_replace_all_lws(char *sip_message) { /* const char *end_of_message; */ char *tmp; if (sip_message == NULL) return; /* end_of_message = sip_message + strlen (sip_message); */ tmp = sip_message; for (; tmp[0] != '\0'; tmp++) { if (('\0' == tmp[0]) || ('\0' == tmp[1]) || ('\0' == tmp[2]) || ('\0' == tmp[3])) return; if ((('\r' == tmp[0]) && ('\n' == tmp[1]) && ('\r' == tmp[2]) && ('\n' == tmp[3])) || (('\r' == tmp[0]) && ('\r' == tmp[1])) || (('\n' == tmp[0]) && ('\n' == tmp[1]))) return; /* end of message */ if ((('\r' == tmp[0]) && ('\n' == tmp[1]) && ((' ' == tmp[2]) || ('\t' == tmp[2]))) || (('\r' == tmp[0]) && ((' ' == tmp[1]) || ('\t' == tmp[1]))) || (('\n' == tmp[0]) && ((' ' == tmp[1]) || ('\t' == tmp[1])))) { /* replace line end and TAB symbols by SP */ tmp[0] = ' '; tmp[1] = ' '; tmp = tmp + 2; /* replace all following TAB symbols */ for (; ('\t' == tmp[0] || ' ' == tmp[0]);) { tmp[0] = ' '; tmp++; } if (tmp[0] == '\0') /* fixed Janv 13 2020: Heap-buffer-overflow with a final LWS without nothing after */ return; } } } int __osip_find_next_crlf(const char *start_of_header, const char **end_of_header) { const char *soh = start_of_header; *end_of_header = NULL; /* AMD fix */ while (('\r' != *soh) && ('\n' != *soh)) { if (*soh) soh++; else { OSIP_TRACE(osip_trace(__FILE__, __LINE__, OSIP_ERROR, NULL, "Final CRLF is missing\n")); return OSIP_SYNTAXERROR; } } if (('\r' == soh[0]) && ('\n' == soh[1])) /* case 1: CRLF is the separator case 2 or 3: CR or LF is the separator */ soh = soh + 1; /* VERIFY if TMP is the end of header or LWS. */ /* LWS are extra SP, HT, CR and LF contained in headers. */ if ((' ' == soh[1]) || ('\t' == soh[1])) { /* From now on, incoming message that potentially contains LWS must be processed with -> void osip_util_replace_all_lws(char *) This is because the parser methods does not support detection of LWS inside. */ OSIP_TRACE(osip_trace(__FILE__, __LINE__, OSIP_BUG, NULL, "Message that contains LWS must be processed with osip_util_replace_all_lws(char *tmp) before being parsed.\n")); return -2; } *end_of_header = soh + 1; return OSIP_SUCCESS; } int __osip_find_next_crlfcrlf(const char *start_of_part, const char **end_of_part) { const char *start_of_line; const char *end_of_line; int i; start_of_line = start_of_part; for (;;) { i = __osip_find_next_crlf(start_of_line, &end_of_line); if (i == -2) { } else if (i != 0) { /* error case??? no end of mesage found */ OSIP_TRACE(osip_trace(__FILE__, __LINE__, OSIP_ERROR, NULL, "Final CRLF is missing\n")); return i; } if ('\0' == end_of_line[0]) { /* error case??? no end of message found */ OSIP_TRACE(osip_trace(__FILE__, __LINE__, OSIP_ERROR, NULL, "Final CRLF is missing\n")); return OSIP_SYNTAXERROR; } else if ('\r' == end_of_line[0]) { if ('\n' == end_of_line[1]) end_of_line++; *end_of_part = end_of_line + 1; return OSIP_SUCCESS; } else if ('\n' == end_of_line[0]) { *end_of_part = end_of_line + 1; return OSIP_SUCCESS; } start_of_line = end_of_line; } } static int osip_message_set__header(osip_message_t *sip, const char *hname, const char *hvalue) { int my_index; if (hname == NULL) return OSIP_SYNTAXERROR; /* some headers are analysed completely */ /* this method is used for selective parsing */ my_index = __osip_message_is_known_header(hname); if (my_index >= 0) { /* ok */ int ret; ret = __osip_message_call_method(my_index, sip, hvalue); if (ret != 0) return ret; return OSIP_SUCCESS; } /* unknownheader */ if (osip_message_set_header(sip, hname, hvalue) != 0) { OSIP_TRACE(osip_trace(__FILE__, __LINE__, OSIP_WARNING, NULL, "Could not set unknown header\n")); return OSIP_SUCCESS; } return OSIP_SUCCESS; } int osip_message_set_multiple_header(osip_message_t *sip, char *hname, char *hvalue) { int i; char *ptr, *p; /* current location of the search */ char *comma; /* This is the separator we are elooking for */ char *beg; /* beg of a header */ char *end; /* end of a header */ int inquotes, inuri; /* state for inside/outside of double-qoutes or URI */ /* Find header based upon lowercase comparison */ osip_tolower(hname); if (hvalue == NULL) { i = osip_message_set__header(sip, hname, hvalue); if (i != 0) return i; return OSIP_SUCCESS; } ptr = hvalue; comma = strchr(ptr, ','); /* if there is a COMMA, we check if the header is allowed on multiple line from an internal list: those headers are defined in rfc to support the following format. header = "header-name" HCOLON header-value *(COMMA header-value) We cannot guess for any other headers and thus, we will handle other headers as one header. */ if (comma == NULL || __osip_message_is_header_comma_separated(hname) != OSIP_SUCCESS) { i = osip_message_set__header(sip, hname, hvalue); if (i != 0) return i; return OSIP_SUCCESS; } beg = hvalue; inquotes = 0; inuri = 0; /* Seach for a comma that is not within quotes or a URI */ for (;; ptr++) { switch (*ptr) { case '"': /* Check that the '"' is not escaped */ for (i = 0, p = ptr - 1; p >= beg && *p == '\\'; p--, i++) ; if (i % 2 == 0) inquotes = !inquotes; /* the '"' was not escaped */ break; case '<': if (!inquotes) { if (!inuri) { if ((osip_strncasecmp(ptr + 1, "sip:", 4) == 0 || osip_strncasecmp(ptr + 1, "sips:", 5) == 0 || osip_strncasecmp(ptr + 1, "http:", 5) == 0 || osip_strncasecmp(ptr + 1, "https:", 6) == 0 || osip_strncasecmp(ptr + 1, "tel:", 4) == 0) && strchr(ptr, '>')) inuri = 1; } /* else { if we found such sequence: "" It might be a valid header containing data and not URIs. Thus, we ignore inuri } */ } break; case '>': if (!inquotes) { if (inuri) inuri = 0; } break; case '\0': /* we discard any validation we tried: no valid uri detected */ inquotes = 0; inuri = 0; // (keep next comment to avoid fall through warning) // fall through case ',': if (!inquotes && !inuri) { char *avalue; if (beg[0] == '\0') return OSIP_SUCCESS; /* empty header */ end = ptr; if (end - beg + 1 < 2) { beg = end + 1; break; /* skip empty header */ } avalue = (char *) osip_malloc(end - beg + 1); if (avalue == NULL) return OSIP_NOMEM; osip_clrncpy(avalue, beg, end - beg); /* really store the header in the sip structure */ i = osip_message_set__header(sip, hname, avalue); osip_free(avalue); if (i != 0) return i; beg = end + 1; } if (*ptr == '\0') return OSIP_SUCCESS; break; default: break; } } } /* set all headers */ static int msg_headers_parse(osip_message_t *sip, const char *start_of_header, const char **body) { const char *colon_index; /* index of ':' */ char *hname; char *hvalue; const char *end_of_header; int i; for (;;) { if (start_of_header[0] == '\0') { /* final CRLF is missing */ OSIP_TRACE(osip_trace(__FILE__, __LINE__, OSIP_INFO1, NULL, "SIP message does not end with CRLFCRLF\n")); return OSIP_SUCCESS; } i = __osip_find_next_crlf(start_of_header, &end_of_header); if (i == -2) { } else if (i != 0) { OSIP_TRACE(osip_trace(__FILE__, __LINE__, OSIP_ERROR, NULL, "End of header Not found\n")); return i; /* this is an error case! */ } /* the list of headers MUST always end with */ /* CRLFCRLF (also CRCR and LFLF are allowed) */ if ((start_of_header[0] == '\r') || (start_of_header[0] == '\n')) { *body = start_of_header; return OSIP_SUCCESS; /* end of header found */ } /* find the header name */ colon_index = strchr(start_of_header, ':'); if (colon_index == NULL) { OSIP_TRACE(osip_trace(__FILE__, __LINE__, OSIP_ERROR, NULL, "End of header Not found\n")); return OSIP_SYNTAXERROR; /* this is also an error case */ } if (colon_index - start_of_header + 1 < 2) return OSIP_SYNTAXERROR; if (end_of_header <= colon_index) { OSIP_TRACE(osip_trace(__FILE__, __LINE__, OSIP_ERROR, NULL, "Malformed message\n")); return OSIP_SYNTAXERROR; } hname = (char *) osip_malloc(colon_index - start_of_header + 1); if (hname == NULL) return OSIP_NOMEM; osip_clrncpy(hname, start_of_header, colon_index - start_of_header); { const char *end; /* END of header is (end_of_header-2) if header separation is CRLF */ /* END of header is (end_of_header-1) if header separation is CR or LF */ if ((end_of_header[-2] == '\r') || (end_of_header[-2] == '\n')) end = end_of_header - 2; else end = end_of_header - 1; if ((end) -colon_index < 2) hvalue = NULL; /* some headers (subject) can be empty */ else { hvalue = (char *) osip_malloc((end) -colon_index + 1); if (hvalue == NULL) { osip_free(hname); return OSIP_NOMEM; } osip_clrncpy(hvalue, colon_index + 1, (end) -colon_index - 1); } } /* hvalue MAY contains multiple value. In this case, they */ /* are separated by commas. But, a comma may be part of a */ /* quoted-string ("here, and there" is an example where the */ /* comma is not a separator!) */ i = osip_message_set_multiple_header(sip, hname, hvalue); osip_free(hname); if (hvalue != NULL) osip_free(hvalue); if (i != 0) { OSIP_TRACE(osip_trace(__FILE__, __LINE__, OSIP_ERROR, NULL, "End of header Not found\n")); return OSIP_SYNTAXERROR; } /* continue on the next header */ start_of_header = end_of_header; } /* Unreachable code OSIP_TRACE (osip_trace (__FILE__, __LINE__, OSIP_BUG, NULL, "This code cannot be reached\n")); */ return OSIP_SYNTAXERROR; } static int msg_osip_body_parse(osip_message_t *sip, const char *start_of_buf, const char **next_body, size_t length) { const char *start_of_body; const char *end_of_body; const char *end_of_buf; char *tmp; int i; char *sep_boundary; size_t len_sep_boundary; osip_generic_param_t *ct_param; if (sip->content_type == NULL || sip->content_type->type == NULL || sip->content_type->subtype == NULL) return OSIP_SUCCESS; /* no body is attached */ if (0 != osip_strcasecmp(sip->content_type->type, "multipart")) { size_t osip_body_len; if (start_of_buf[0] == '\0') return OSIP_SYNTAXERROR; /* final CRLF is missing */ /* get rid of the first CRLF */ if ('\r' == start_of_buf[0]) { if ('\n' == start_of_buf[1]) start_of_body = start_of_buf + 2; else start_of_body = start_of_buf + 1; } else if ('\n' == start_of_buf[0]) start_of_body = start_of_buf + 1; else return OSIP_SYNTAXERROR; /* message does not end with CRLFCRLF, CRCR or LFLF */ /* update length (without CRLFCRLF */ length = length - (start_of_body - start_of_buf); /* fixed 24 08 2004 */ if (length <= 0) return OSIP_SYNTAXERROR; if (sip->content_length != NULL) osip_body_len = osip_atoi(sip->content_length->value); else { /* if content_length does not exist, set it. */ char tmp[16]; osip_body_len = length; sprintf(tmp, "%i", (int) osip_body_len); i = osip_message_set_content_length(sip, tmp); if (i != 0) return i; } if (length < osip_body_len) { OSIP_TRACE(osip_trace(__FILE__, __LINE__, OSIP_ERROR, NULL, "Message was not receieved enterely. length=%i osip_body_len=%i\n", (int) length, (int) osip_body_len)); return OSIP_SYNTAXERROR; } end_of_body = start_of_body + osip_body_len; tmp = osip_malloc(end_of_body - start_of_body + 2); if (tmp == NULL) return OSIP_NOMEM; memcpy(tmp, start_of_body, end_of_body - start_of_body); tmp[end_of_body - start_of_body] = '\0'; i = osip_message_set_body(sip, tmp, end_of_body - start_of_body); osip_free(tmp); if (i != 0) return i; return OSIP_SUCCESS; } /* find the boundary */ i = osip_generic_param_get_byname(&sip->content_type->gen_params, "boundary", &ct_param); if (i != 0) return i; if (ct_param == NULL) return OSIP_SYNTAXERROR; if (ct_param->gvalue == NULL) return OSIP_SYNTAXERROR; /* No boundary but multiple headers??? */ { const char *boundary_prefix = "\n--"; size_t len = strlen(ct_param->gvalue); sep_boundary = (char *) osip_malloc(len + 4); if (sep_boundary == NULL) return OSIP_NOMEM; strcpy(sep_boundary, boundary_prefix); if (ct_param->gvalue[0] == '"' && ct_param->gvalue[len - 1] == '"') strncat(sep_boundary, ct_param->gvalue + 1, len - 2); else strncat(sep_boundary, ct_param->gvalue, len); } len_sep_boundary = strlen(sep_boundary); *next_body = NULL; start_of_body = start_of_buf; end_of_buf = start_of_buf + length; for (;;) { size_t body_len = 0; i = __osip_find_next_occurence(sep_boundary, start_of_body, &start_of_body, end_of_buf); if (i != 0) { osip_free(sep_boundary); return i; } i = __osip_find_next_occurence(sep_boundary, start_of_body + len_sep_boundary, &end_of_body, end_of_buf); if (i != 0) { osip_free(sep_boundary); return i; } /* this is the real beginning of body */ start_of_body = start_of_body + len_sep_boundary + 1; if ('\n' == start_of_body[0] || '\r' == start_of_body[0]) start_of_body++; /* if message body is empty or contains a single CR/LF */ if (end_of_body <= start_of_body) { osip_free(sep_boundary); return OSIP_SYNTAXERROR; } body_len = end_of_body - start_of_body; /* Skip CR before end boundary. */ if (*(end_of_body - 1) == '\r') body_len--; tmp = osip_malloc(body_len + 2); if (tmp == NULL) { osip_free(sep_boundary); return OSIP_NOMEM; } memcpy(tmp, start_of_body, body_len); tmp[body_len] = '\0'; i = osip_message_set_body_mime(sip, tmp, body_len); osip_free(tmp); if (i != 0) { osip_free(sep_boundary); return i; } if (strncmp(end_of_body + len_sep_boundary, "--", 2) == 0) { /* end of all bodies */ *next_body = end_of_body; osip_free(sep_boundary); return OSIP_SUCCESS; } /* continue on the next body */ start_of_body = end_of_body; } /* Unreachable code */ /* osip_free (sep_boundary); */ return OSIP_SYNTAXERROR; } /* osip_message_t *sip is filled while analysing buf */ static int _osip_message_parse(osip_message_t *sip, const char *buf, size_t length, int sipfrag) { int i; const char *next_header_index; char *tmp; char *beg; tmp = osip_malloc(length + 2); if (tmp == NULL) { OSIP_TRACE(osip_trace(__FILE__, __LINE__, OSIP_ERROR, NULL, "Could not allocate memory.\n")); return OSIP_NOMEM; } beg = tmp; memcpy(tmp, buf, length); /* may contain binary data */ tmp[length] = '\0'; /* skip initial \r\n */ tmp += strspn(tmp, "\r\n"); osip_util_replace_all_lws(tmp); /* parse request or status line */ i = __osip_message_startline_parse(sip, tmp, &next_header_index); if (i != 0 && !sipfrag) { OSIP_TRACE(osip_trace(__FILE__, __LINE__, OSIP_ERROR, NULL, "Could not parse start line of message.\n")); osip_free(beg); return i; } tmp = (char *) next_header_index; /* parse headers */ i = msg_headers_parse(sip, tmp, &next_header_index); if (i != 0) { OSIP_TRACE(osip_trace(__FILE__, __LINE__, OSIP_ERROR, NULL, "error in msg_headers_parse()\n")); osip_free(beg); return i; } tmp = (char *) next_header_index; if (sip->content_length != NULL && sip->content_length->value == NULL) { /* empty content_length header */ osip_content_length_free(sip->content_length); sip->content_length = NULL; } if (sip->content_length != NULL && sip->content_length->value != NULL && atoi(sip->content_length->value) > 0) { /* body exist */ } else if (sip->content_length == NULL && '\r' == next_header_index[0] && '\n' == next_header_index[1] && length - (tmp - beg) - (2) > 0) { /* body exist */ } else if (sip->content_length == NULL && '\n' == next_header_index[0] && length - (tmp - beg) - (1) > 0) { /* body exist */ } else { if (sip->content_length == NULL) osip_message_set_content_length(sip, "0"); osip_free(beg); return OSIP_SUCCESS; /* no body found */ } i = msg_osip_body_parse(sip, tmp, &next_header_index, length - (tmp - beg)); osip_free(beg); if (i != 0) { OSIP_TRACE(osip_trace(__FILE__, __LINE__, OSIP_ERROR, NULL, "error in msg_osip_body_parse()\n")); return i; } /* this is mandatory in the oSIP stack */ if (sip->content_length == NULL) osip_message_set_content_length(sip, "0"); return OSIP_SUCCESS; } int osip_message_parse(osip_message_t *sip, const char *buf, size_t length) { return _osip_message_parse(sip, buf, length, 0); } int osip_message_parse_sipfrag(osip_message_t *sip, const char *buf, size_t length) { return _osip_message_parse(sip, buf, length, 1); } /* This method just add a received parameter in the Via as requested by rfc3261 */ int osip_message_fix_last_via_header(osip_message_t *request, const char *ip_addr, int port) { osip_generic_param_t *rport; osip_via_t *via; /* get Top most Via header: */ if (request == NULL) return OSIP_BADPARAMETER; if (MSG_IS_RESPONSE(request)) return OSIP_SUCCESS; /* Don't fix Via header */ via = osip_list_get(&request->vias, 0); if (via == NULL || via->host == NULL) /* Hey, we could build it? */ return OSIP_BADPARAMETER; osip_via_param_get_byname(via, "rport", &rport); if (rport != NULL) { if (rport->gvalue == NULL) { rport->gvalue = (char *) osip_malloc(9); if (rport->gvalue == NULL) return OSIP_NOMEM; #if !defined __PALMOS__ && (defined WIN32 || defined _WIN32_WCE) _snprintf(rport->gvalue, 8, "%i", port); #else snprintf(rport->gvalue, 8, "%i", port); #endif } /* else bug? */ } /* only add the received parameter if the 'sent-by' value does not contains this ip address */ if (0 == strcmp(via->host, ip_addr)) /* don't need the received parameter */ return OSIP_SUCCESS; osip_via_set_received(via, osip_strdup(ip_addr)); return OSIP_SUCCESS; } const char *osip_message_get_reason(int replycode) { struct code_to_reason { int code; const char *reason; }; static const struct code_to_reason reasons1xx[] = { {100, "Trying"}, {180, "Ringing"}, {181, "Call Is Being Forwarded"}, {182, "Queued"}, {183, "Session Progress"}, {199, "Early Dialog Terminated"}, }; static const struct code_to_reason reasons2xx[] = { {200, "OK"}, {202, "Accepted"}, {204, "No Notification"}, }; static const struct code_to_reason reasons3xx[] = { {300, "Multiple Choices"}, {301, "Moved Permanently"}, {302, "Moved Temporarily"}, {305, "Use Proxy"}, {380, "Alternative Service"}, }; static const struct code_to_reason reasons4xx[] = { {400, "Bad Request"}, {401, "Unauthorized"}, {402, "Payment Required"}, {403, "Forbidden"}, {404, "Not Found"}, {405, "Method Not Allowed"}, {406, "Not Acceptable"}, {407, "Proxy Authentication Required"}, {408, "Request Timeout"}, {409, "Conflict"}, {410, "Gone"}, {411, "Length Required"}, {412, "Conditional Request Failed"}, {413, "Request Entity Too Large"}, {414, "Request-URI Too Long"}, {415, "Unsupported Media Type"}, {416, "Unsupported URI Scheme"}, {417, "Unknown Resource-Priority"}, {420, "Bad Extension"}, {421, "Extension Required"}, {422, "Session Interval Too Small"}, {423, "Interval Too Brief"}, {424, "Bad Location Information"}, {428, "Use Identity Header"}, {429, "Provide Referrer Identity"}, {430, "Flow Failed"}, {433, "Anonymity Disallowed"}, {436, "Bad Identity Info"}, {437, "Unsupported Credential"}, {438, "Invalid Identity Header"}, {439, "First Hop Lacks Outbound Support"}, {440, "Max-Breadth Exceeded"}, {469, "Bad Info Package"}, {470, "Consent Needed"}, {480, "Temporarily Unavailable"}, {481, "Call/Transaction Does Not Exist"}, {482, "Loop Detected"}, {483, "Too Many Hops"}, {484, "Address Incomplete"}, {485, "Ambiguous"}, {486, "Busy Here"}, {487, "Request Terminated"}, {488, "Not Acceptable Here"}, {489, "Bad Event"}, {491, "Request Pending"}, {493, "Undecipherable"}, {494, "Security Agreement Required"}, }; static const struct code_to_reason reasons5xx[] = { {500, "Server Internal Error"}, {501, "Not Implemented"}, {502, "Bad Gateway"}, {503, "Service Unavailable"}, {504, "Server Time-out"}, {505, "Version Not Supported"}, {513, "Message Too Large"}, {580, "Precondition Failure"}, }; static const struct code_to_reason reasons6xx[] = {{600, "Busy Everywhere"}, {603, "Decline"}, {604, "Does Not Exist Anywhere"}, {606, "Not Acceptable"}, {607, "Unwanted"}, {687, "Dialog Terminated"}}; const struct code_to_reason *reasons; int len, i; switch (replycode / 100) { case 1: reasons = reasons1xx; len = sizeof(reasons1xx) / sizeof(*reasons); break; case 2: reasons = reasons2xx; len = sizeof(reasons2xx) / sizeof(*reasons); break; case 3: reasons = reasons3xx; len = sizeof(reasons3xx) / sizeof(*reasons); break; case 4: reasons = reasons4xx; len = sizeof(reasons4xx) / sizeof(*reasons); break; case 5: reasons = reasons5xx; len = sizeof(reasons5xx) / sizeof(*reasons); break; case 6: reasons = reasons6xx; len = sizeof(reasons6xx) / sizeof(*reasons); break; default: return NULL; } for (i = 0; i < len; i++) if (reasons[i].code == replycode) return reasons[i].reason; /* Not found. */ return NULL; }