/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (the "License").
* You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* or http://www.opensolaris.org/os/licensing.
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright 2007 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#pragma ident "%Z%%M% %I% %E% SMI"
#include <stdlib.h>
#include <assert.h>
#include <errno.h>
#include <strings.h>
#include <ctype.h>
#include <sip.h>
#include "sip_miscdefs.h"
#include "sip_msg.h"
#include "sip_parse_uri.h"
/*
* atoi function from a header
*/
int
sip_atoi(_sip_header_t *sip_header, int *num)
{
boolean_t num_found = B_FALSE;
*num = 0;
while (sip_header->sip_hdr_current < sip_header->sip_hdr_end) {
if (isspace(*sip_header->sip_hdr_current)) {
sip_header->sip_hdr_current++;
if (num_found)
break;
} else if (isdigit(*sip_header->sip_hdr_current)) {
*num = (*num * 10) +
(*sip_header->sip_hdr_current - '0');
num_found = B_TRUE;
sip_header->sip_hdr_current++;
} else {
break;
}
}
if (!num_found)
return (EINVAL);
return (0);
}
/*
* Find the 'token'
*/
int
sip_find_token(_sip_header_t *sip_header, char token)
{
while (sip_header->sip_hdr_current < sip_header->sip_hdr_end) {
if (token != SIP_COMMA &&
*sip_header->sip_hdr_current == SIP_COMMA) {
sip_header->sip_hdr_current--;
return (1);
}
if (*sip_header->sip_hdr_current++ == token) {
/*
* sip_hdr_current points to the char
* after the token
*/
return (0);
}
}
return (1);
}
/*
* Find a carriage-return
*/
int
sip_find_cr(_sip_header_t *sip_header)
{
sip_header->sip_hdr_current = sip_header->sip_hdr_end;
while (*sip_header->sip_hdr_current-- != '\n') {
if (sip_header->sip_hdr_current == sip_header->sip_hdr_start)
return (1);
}
return (0);
}
/*
* Find one of the separator provided, i.e. separator_1st or separator_2nd or
* separator_3rd.
*/
int
sip_find_separator(_sip_header_t *sip_header, char separator_1st,
char separator_2nd, char separator_3rd, boolean_t ignore_space)
{
assert(separator_1st != (char)NULL || separator_2nd != (char)NULL);
while (sip_header->sip_hdr_current < sip_header->sip_hdr_end) {
if (ignore_space && (*sip_header->sip_hdr_current == SIP_SP)) {
sip_header->sip_hdr_current++;
continue;
}
if (isspace(*sip_header->sip_hdr_current) ||
(separator_1st != (char)NULL &&
(*sip_header->sip_hdr_current == separator_1st)) ||
(separator_2nd != (char)NULL &&
(*sip_header->sip_hdr_current == separator_2nd)) ||
(separator_3rd != (char)NULL &&
(*sip_header->sip_hdr_current == separator_3rd))) {
return (0);
}
/*
* If we have escape character, go to the next char
*/
if (*sip_header->sip_hdr_current == '\\')
sip_header->sip_hdr_current++;
sip_header->sip_hdr_current++;
}
return (1);
}
/*
* Return when we hit a white space
*/
int
sip_find_white_space(_sip_header_t *sip_header)
{
while (sip_header->sip_hdr_current < sip_header->sip_hdr_end) {
if (isspace(*sip_header->sip_hdr_current))
return (0);
sip_header->sip_hdr_current++;
}
return (1);
}
/*
* Skip to the next non-whitespace
*/
int
sip_skip_white_space(_sip_header_t *sip_header)
{
while (sip_header->sip_hdr_current < sip_header->sip_hdr_end) {
if (!isspace(*sip_header->sip_hdr_current))
return (0);
sip_header->sip_hdr_current++;
}
return (1);
}
/*
* Skip to the non-white space in the reverse direction
*/
int
sip_reverse_skip_white_space(_sip_header_t *sip_header)
{
while (sip_header->sip_hdr_current >= sip_header->sip_hdr_start) {
if (!isspace(*sip_header->sip_hdr_current))
return (0);
sip_header->sip_hdr_current--;
}
return (1);
}
/*
* get to the first non space after ':'
*/
int
sip_parse_goto_values(_sip_header_t *sip_header)
{
if (sip_find_token(sip_header, SIP_HCOLON) != 0)
return (1);
if (sip_skip_white_space(sip_header) != 0)
return (1);
return (0);
}
/*
* Skip the current value.
*/
int
sip_goto_next_value(_sip_header_t *sip_header)
{
boolean_t quoted = B_FALSE;
while (sip_header->sip_hdr_current < sip_header->sip_hdr_end) {
if (*sip_header->sip_hdr_current == SIP_QUOTE) {
if (quoted)
quoted = B_FALSE;
else
quoted = B_TRUE;
} else if (!quoted &&
*sip_header->sip_hdr_current == SIP_COMMA) {
/*
* value ends before the COMMA
*/
sip_header->sip_hdr_current--;
return (0);
}
sip_header->sip_hdr_current++;
}
if (quoted)
return (1);
return (0);
}
/*
* Parse the header into parameter list. Parameters start with a ';'
*/
int
sip_parse_params(_sip_header_t *sip_header, sip_param_t **parsed_list)
{
sip_param_t *param = NULL;
sip_param_t *new_param;
char *tmp_ptr;
if (parsed_list == NULL)
return (0);
*parsed_list = NULL;
for (;;) {
boolean_t quoted_name = B_FALSE;
/*
* First check if there are any params
*/
if (sip_skip_white_space(sip_header) != 0)
return (0);
if (*sip_header->sip_hdr_current != SIP_SEMI)
return (0);
sip_header->sip_hdr_current++;
new_param = calloc(1, sizeof (sip_param_t));
if (new_param == NULL)
return (ENOMEM);
if (param != NULL)
param->param_next = new_param;
else
*parsed_list = new_param;
param = new_param;
/*
* Let's get to the start of the param name
*/
if (sip_skip_white_space(sip_header) != 0)
return (EPROTO);
/*
* start of param name
*/
tmp_ptr = sip_header->sip_hdr_current;
param->param_name.sip_str_ptr = tmp_ptr;
if (sip_find_separator(sip_header, SIP_EQUAL, SIP_SEMI,
SIP_COMMA, B_FALSE) != 0) {
param->param_name.sip_str_len =
sip_header->sip_hdr_current - tmp_ptr;
param->param_value.sip_str_ptr = NULL;
param->param_value.sip_str_len = 0;
return (0);
}
/*
* End of param name
*/
param->param_name.sip_str_len =
sip_header->sip_hdr_current - tmp_ptr;
if (sip_skip_white_space(sip_header) != 0 ||
*sip_header->sip_hdr_current == SIP_COMMA) {
param->param_value.sip_str_ptr = NULL;
param->param_value.sip_str_len = 0;
return (0);
}
if (*sip_header->sip_hdr_current == SIP_SEMI) {
param->param_value.sip_str_ptr = NULL;
param->param_value.sip_str_len = 0;
continue;
}
assert(*sip_header->sip_hdr_current == SIP_EQUAL);
/*
* We are at EQUAL, lets go beyond that
*/
sip_header->sip_hdr_current++;
if (sip_skip_white_space(sip_header) != 0)
return (EPROTO);
if (*sip_header->sip_hdr_current == SIP_QUOTE) {
sip_header->sip_hdr_current++;
quoted_name = B_TRUE;
}
/*
* start of param value
*/
param->param_value.sip_str_ptr = sip_header->sip_hdr_current;
tmp_ptr = sip_header->sip_hdr_current;
if (quoted_name && sip_find_token(sip_header, SIP_QUOTE) != 0) {
return (EPROTO);
} else if (sip_find_separator(sip_header, SIP_SEMI, SIP_COMMA,
(char)NULL, B_FALSE) != 0) {
return (EPROTO);
}
param->param_value.sip_str_len = sip_header->sip_hdr_current -
tmp_ptr;
if (quoted_name)
param->param_value.sip_str_len--;
}
}
/*
* a header that only has "header_name : " is an empty header
* ":" must exist
* sip_hdr_current resets to sip_hdr_start before exit
*/
boolean_t
sip_is_empty_hdr(_sip_header_t *sip_header)
{
if (sip_find_token(sip_header, SIP_HCOLON) != 0) {
sip_header->sip_hdr_current = sip_header->sip_hdr_start;
return (B_FALSE);
}
if (sip_skip_white_space(sip_header) == 0) {
sip_header->sip_hdr_current = sip_header->sip_hdr_start;
return (B_FALSE);
}
sip_header->sip_hdr_current = sip_header->sip_hdr_start;
return (B_TRUE);
}
/*
* Parsing an empty header, i.e. only has a ":"
*/
int
sip_parse_hdr_empty(_sip_header_t *hdr, sip_parsed_header_t **phdr)
{
sip_parsed_header_t *parsed_header;
if (hdr == NULL || phdr == NULL)
return (EINVAL);
/*
* check if already parsed
*/
if (hdr->sip_hdr_parsed != NULL) {
*phdr = hdr->sip_hdr_parsed;
return (0);
}
*phdr = NULL;
parsed_header = calloc(1, sizeof (sip_parsed_header_t));
if (parsed_header == NULL)
return (ENOMEM);
parsed_header->sip_header = hdr;
parsed_header->value = NULL;
*phdr = parsed_header;
return (0);
}
/*
* validate uri str and parse uri using uri_parse()
*/
static void
sip_parse_uri_str(sip_str_t *sip_str, sip_hdr_value_t *value)
{
int error;
/*
* Parse uri
*/
if (sip_str->sip_str_len > 0) {
value->sip_value_parsed_uri = sip_parse_uri(sip_str, &error);
if (value->sip_value_parsed_uri == NULL)
return;
if (error != 0 ||
value->sip_value_parsed_uri->sip_uri_errflags != 0) {
value->sip_value_state = SIP_VALUE_BAD;
}
}
}
/*
* Some basic common checks before parsing the headers
*/
int
sip_prim_parsers(_sip_header_t *sip_header, sip_parsed_header_t **header)
{
if (sip_header == NULL || header == NULL)
return (EINVAL);
/*
* check if already parsed
*/
if (sip_header->sip_hdr_parsed != NULL) {
*header = sip_header->sip_hdr_parsed;
return (0);
}
*header = NULL;
assert(sip_header->sip_hdr_start == sip_header->sip_hdr_current);
if (sip_parse_goto_values(sip_header) != 0)
return (EPROTO);
return (0);
}
/*
* Parse SIP/2.0 string
*/
int
sip_get_protocol_version(_sip_header_t *sip_header,
sip_proto_version_t *sip_proto_version)
{
if (sip_skip_white_space(sip_header) != 0)
return (1);
if (strncasecmp(sip_header->sip_hdr_current, SIP, strlen(SIP)) == 0) {
sip_proto_version->name.sip_str_ptr =
sip_header->sip_hdr_current;
sip_proto_version->name.sip_str_len = strlen(SIP);
if (sip_find_token(sip_header, SIP_SLASH) != 0)
return (1);
if (sip_skip_white_space(sip_header) != 0)
return (1);
sip_proto_version->version.sip_str_ptr =
sip_header->sip_hdr_current;
while (isdigit(*sip_header->sip_hdr_current)) {
sip_header->sip_hdr_current++;
if (sip_header->sip_hdr_current >=
sip_header->sip_hdr_end) {
return (1);
}
}
if (*sip_header->sip_hdr_current != SIP_PERIOD)
return (1);
sip_header->sip_hdr_current++;
if (!isdigit(*sip_header->sip_hdr_current))
return (1);
while (isdigit(*sip_header->sip_hdr_current)) {
sip_header->sip_hdr_current++;
if (sip_header->sip_hdr_current >=
sip_header->sip_hdr_end) {
return (1);
}
}
sip_proto_version->version.sip_str_len =
sip_header->sip_hdr_current -
sip_proto_version->version.sip_str_ptr;
return (0);
}
return (1);
}
/*
* parser1 parses hdr format
* header_name: val1[; par1=pval1;par2=pval2 ..][, val2[;parlist..] ]
* val can be str1/str2 or str
* headers: Accept, Accept-Encode, Accept-lang, Allow, Content-disp,
* Content-Encode, Content-Lang, In-reply-to,
* Priority, Require, Supported, Unsupported
* Allow-Events, Event, Subscription-State
*/
int
sip_parse_hdr_parser1(_sip_header_t *hdr, sip_parsed_header_t **phdr, char sep)
{
sip_parsed_header_t *parsed_header;
int ret;
sip_hdr_value_t *value = NULL;
sip_hdr_value_t *last_value = NULL;
if ((ret = sip_prim_parsers(hdr, phdr)) != 0)
return (ret);
/*
* check if previously parsed
*/
if (*phdr != NULL) {
hdr->sip_hdr_parsed = *phdr;
return (0);
}
parsed_header = calloc(1, sizeof (sip_parsed_header_t));
if (parsed_header == NULL)
return (ENOMEM);
parsed_header->sip_parsed_header_version = SIP_PARSED_HEADER_VERSION_1;
parsed_header->sip_header = hdr;
while (hdr->sip_hdr_current < hdr->sip_hdr_end) {
value = calloc(1, sizeof (sip_hdr_value_t));
if (value == NULL) {
sip_free_phdr(parsed_header);
return (ENOMEM);
}
if (last_value != NULL)
last_value->sip_next_value = value;
else
parsed_header->value = (sip_value_t *)value;
value->sip_value_start = hdr->sip_hdr_current;
value->sip_value_header = parsed_header;
if (sip_find_separator(hdr, sep, SIP_COMMA, SIP_SEMI,
B_FALSE) == 0) {
char c = *hdr->sip_hdr_current;
if (isspace(c) && sep == (char)NULL) {
value->str_val_ptr = value->sip_value_start;
value->str_val_len = hdr->sip_hdr_current -
value->sip_value_start;
/*
* nothing at the end except space
*/
if (sip_skip_white_space(hdr) != 0) {
value->sip_value_end =
hdr->sip_hdr_current;
goto end;
}
/*
* white space skipped
*/
c = *(hdr->sip_hdr_current);
}
/*
* only one string until COMMA, use sip_str_t
*/
if (c == SIP_COMMA) {
char *t = hdr->sip_hdr_current;
hdr->sip_hdr_current--;
(void) sip_reverse_skip_white_space(hdr);
value->str_val_ptr = value->sip_value_start;
value->str_val_len = hdr->sip_hdr_current -
value->sip_value_start + 1;
hdr->sip_hdr_current = t;
goto get_next_val;
}
/*
* two strings, use sip_2strs_t
*/
if ((sep != (char)NULL) && (c == sep)) {
value->strs1_val_ptr = value->sip_value_start;
value->strs1_val_len = hdr->sip_hdr_current -
value->sip_value_start;
value->strs2_val_ptr =
(++hdr->sip_hdr_current);
if (sip_find_separator(hdr, SIP_SEMI, SIP_COMMA,
(char)NULL, B_FALSE) == 0) {
char t = *(hdr->sip_hdr_current);
value->strs2_val_len =
hdr->sip_hdr_current -
value->strs2_val_ptr;
/*
* if COMMA, no param list, get next val
* if SEMI, need to set params list
*/
if (t == SIP_COMMA)
goto get_next_val;
} else { /* the last part */
value->strs2_val_len =
hdr->sip_hdr_current -
value->strs2_val_ptr;
value->sip_value_end =
hdr->sip_hdr_current;
goto end;
}
} else if (sep != (char)NULL) {
value->sip_value_state = SIP_VALUE_BAD;
goto get_next_val;
}
/*
* c == SEMI, value contains single string
* only one string until SEMI, use sip_str_t
*/
if (c == SIP_SEMI) {
char *t = hdr->sip_hdr_current;
hdr->sip_hdr_current--;
/*
* get rid of SP at end of value field
*/
(void) sip_reverse_skip_white_space(hdr);
value->str_val_ptr = value->sip_value_start;
value->str_val_len = hdr->sip_hdr_current -
value->str_val_ptr + 1;
hdr->sip_hdr_current = t;
}
/*
* if SEMI exists in the value, set params list
* two situations, there is or not SLASH before SEMI
*/
ret = sip_parse_params(hdr, &value->sip_param_list);
if (ret == EPROTO) {
value->sip_value_state = SIP_VALUE_BAD;
} else if (ret != 0) {
sip_free_phdr(parsed_header);
return (ret);
}
goto get_next_val;
} else {
value->str_val_ptr = value->sip_value_start;
value->str_val_len = hdr->sip_hdr_current -
value->sip_value_start;
value->sip_value_end = hdr->sip_hdr_current;
goto end;
}
get_next_val:
if (sip_find_token(hdr, SIP_COMMA) != 0) {
value->sip_value_end = hdr->sip_hdr_current;
break;
}
value->sip_value_end = hdr->sip_hdr_current - 1;
last_value = value;
(void) sip_skip_white_space(hdr);
}
end:
*phdr = parsed_header;
hdr->sip_hdr_parsed = *phdr;
return (0);
}
/*
* header_name: int
* headers: Expires, Min-Expires
*/
/* ARGSUSED */
int
sip_parse_hdr_parser2(_sip_header_t *hdr, sip_parsed_header_t **phdr,
int val_type)
{
sip_parsed_header_t *parsed_header;
int ret = 0;
sip_hdr_value_t *value = NULL;
if ((ret = sip_prim_parsers(hdr, phdr)) != 0)
return (ret);
/*
* check if previously parsed
*/
if (*phdr != NULL) {
hdr->sip_hdr_parsed = *phdr;
return (0);
}
parsed_header = calloc(1, sizeof (sip_parsed_header_t));
if (parsed_header == NULL)
return (ENOMEM);
parsed_header->sip_parsed_header_version = SIP_PARSED_HEADER_VERSION_1;
parsed_header->sip_header = hdr;
value = calloc(1, sizeof (sip_hdr_value_t));
if (value == NULL) {
sip_free_phdr(parsed_header);
return (ENOMEM);
}
parsed_header->value = (sip_value_t *)value;
value->sip_value_start = hdr->sip_hdr_current;
value->sip_value_header = parsed_header;
ret = sip_atoi(hdr, &value->int_val);
if (ret != 0) {
value->int_val = 0;
value->sip_value_state = SIP_VALUE_BAD;
}
value->sip_value_end = hdr->sip_hdr_current - 1;
*phdr = parsed_header;
hdr->sip_hdr_parsed = *phdr;
return (0);
}
/*
* parser3 parses hdr format
* header_name: <val1>[, <val2>]
* Alert-Info, Call-Info, Error-Info, reply-to
*/
int
sip_parse_hdr_parser3(_sip_header_t *hdr, sip_parsed_header_t **phdr, int type,
boolean_t parse_uri)
{
sip_parsed_header_t *parsed_header;
sip_hdr_value_t *value = NULL;
sip_hdr_value_t *last_value = NULL;
int ret;
if ((ret = sip_prim_parsers(hdr, phdr)) != 0)
return (ret);
/*
* check if previously parsed
*/
if (*phdr != NULL) {
hdr->sip_hdr_parsed = *phdr;
return (0);
}
parsed_header = calloc(1, sizeof (sip_parsed_header_t));
if (parsed_header == NULL)
return (ENOMEM);
parsed_header->sip_parsed_header_version = SIP_PARSED_HEADER_VERSION_1;
parsed_header->sip_header = hdr;
while (hdr->sip_hdr_current < hdr->sip_hdr_end) {
int r;
value = calloc(1, sizeof (sip_hdr_value_t));
if (value == NULL) {
sip_free_phdr(parsed_header);
return (ENOMEM);
}
if (last_value != NULL)
last_value->sip_next_value = value;
else
parsed_header->value = (sip_value_t *)value;
value->sip_value_start = hdr->sip_hdr_current;
value->sip_value_header = parsed_header;
if (type == SIP_STRS_VAL) {
if (sip_find_token(hdr, SIP_LAQUOT) == 0) {
char *cur;
/*
* record the position after LAQUOT
*/
cur = hdr->sip_hdr_current;
/*
* get display name and store in str1
*/
hdr->sip_hdr_current = value->sip_value_start;
if (*(hdr->sip_hdr_current) != SIP_LAQUOT) {
/*
* record start pos of display name
*/
char *tmp = hdr->sip_hdr_current;
if (*hdr->sip_hdr_current ==
SIP_QUOTE) {
hdr->sip_hdr_current++;
tmp++;
if (sip_find_token(hdr,
SIP_QUOTE) != 0) {
value->sip_value_state =
SIP_VALUE_BAD;
goto get_next_val;
}
hdr->sip_hdr_current -= 2;
} else {
hdr->sip_hdr_current = cur - 2;
(void)
sip_reverse_skip_white_space
(hdr);
}
value->strs1_val_ptr = tmp;
value->strs1_val_len =
hdr->sip_hdr_current - tmp + 1;
} else {
value->strs1_val_ptr = NULL;
value->strs1_val_len = 0;
}
/*
* set current to the char after LAQUOT
*/
hdr->sip_hdr_current = cur;
value->strs2_val_ptr = hdr->sip_hdr_current;
if (sip_find_token(hdr, SIP_RAQUOT)) {
/*
* no RAQUOT
*/
value->strs1_val_ptr = NULL;
value->strs1_val_len = 0;
value->strs2_val_ptr = NULL;
value->strs2_val_len = 0;
value->sip_value_state = SIP_VALUE_BAD;
goto get_next_val;
}
value->strs2_val_len = hdr->sip_hdr_current -
value->strs2_val_ptr - 1;
} else {
char *cur;
/*
* No display name - Only URI.
*/
value->strs1_val_ptr = NULL;
value->strs1_val_len = 0;
cur = value->sip_value_start;
hdr->sip_hdr_current = cur;
if (sip_find_separator(hdr, SIP_COMMA,
(char)NULL, (char)NULL, B_FALSE) != 0) {
value->strs2_val_ptr = cur;
value->strs2_val_len =
hdr->sip_hdr_current -
value->strs2_val_ptr - 1;
} else if (*hdr->sip_hdr_current == SIP_SP) {
value->strs2_val_ptr = cur;
cur = hdr->sip_hdr_current - 1;
if (sip_skip_white_space(hdr) != 0) {
value->strs2_val_len = cur -
value->strs2_val_ptr - 1;
} else if (*hdr->sip_hdr_current ==
SIP_COMMA) {
value->strs2_val_len = cur -
value->strs2_val_ptr - 1;
} else {
value->sip_value_state =
SIP_VALUE_BAD;
goto get_next_val;
}
} else {
value->strs2_val_ptr = cur;
value->strs2_val_len =
hdr->sip_hdr_current -
value->strs2_val_ptr;
}
}
if (parse_uri)
sip_parse_uri_str(&value->strs_s2, value);
}
if (type == SIP_STR_VAL) {
/*
* alert-info, error-info, call-info
*/
if (sip_find_token(hdr, SIP_LAQUOT) == 0) {
value->str_val_ptr = hdr->sip_hdr_current;
if (sip_find_token(hdr, SIP_RAQUOT) == 0) {
value->str_val_len =
hdr->sip_hdr_current -
value->str_val_ptr - 1;
} else {
value->str_val_ptr = NULL;
value->str_val_len = 0;
value->sip_value_state = SIP_VALUE_BAD;
goto get_next_val;
}
hdr->sip_hdr_current--;
} else {
value->str_val_ptr = NULL;
value->str_val_len = 0;
value->sip_value_state = SIP_VALUE_BAD;
goto get_next_val;
}
if (parse_uri)
sip_parse_uri_str(&value->str_val, value);
}
r = sip_find_separator(hdr, SIP_COMMA, SIP_SEMI, (char)NULL,
B_FALSE);
if (r != 0) {
value->sip_value_end = hdr->sip_hdr_current;
goto end;
}
if (*hdr->sip_hdr_current == SIP_SEMI) {
(void) sip_parse_params(hdr,
&(value->sip_param_list));
goto get_next_val;
}
if (*hdr->sip_hdr_current == SIP_COMMA) {
hdr->sip_hdr_current--;
goto get_next_val;
}
get_next_val:
if (sip_find_token(hdr, SIP_COMMA) != 0) {
value->sip_value_end = hdr->sip_hdr_current;
break;
}
value->sip_value_end = hdr->sip_hdr_current - 1;
last_value = value;
(void) sip_skip_white_space(hdr);
}
end:
*phdr = parsed_header;
hdr->sip_hdr_parsed = *phdr;
return (0);
}
/*
* parser4 parses hdr format, the whole field is one single str
* header: Subject, MIME-Version, Organization, Server, User-Agent
*/
int
sip_parse_hdr_parser4(_sip_header_t *hdr, sip_parsed_header_t **phdr)
{
sip_parsed_header_t *parsed_header;
sip_hdr_value_t *value = NULL;
int ret;
if ((ret = sip_prim_parsers(hdr, phdr)) != 0)
return (ret);
/*
* check if previously parsed
*/
if (*phdr != NULL) {
hdr->sip_hdr_parsed = *phdr;
return (0);
}
parsed_header = calloc(1, sizeof (sip_parsed_header_t));
if (parsed_header == NULL)
return (ENOMEM);
parsed_header->sip_parsed_header_version = SIP_PARSED_HEADER_VERSION_1;
parsed_header->sip_header = hdr;
value = calloc(1, sizeof (sip_hdr_value_t));
if (value == NULL) {
sip_free_phdr(parsed_header);
return (ENOMEM);
}
parsed_header->value = (sip_value_t *)value;
value->sip_value_start = hdr->sip_hdr_current;
value->sip_value_header = parsed_header;
value->str_val_ptr = hdr->sip_hdr_current;
/*
* get rid of CRLF at end
*/
value->str_val_len = hdr->sip_hdr_end - value->str_val_ptr - 2;
value->sip_value_end = hdr->sip_hdr_end;
*phdr = parsed_header;
hdr->sip_hdr_parsed = *phdr;
return (0);
}
int
sip_parse_hdr_parser5(_sip_header_t *hdr, sip_parsed_header_t **phdr,
boolean_t parse_uri)
{
sip_parsed_header_t *parsed_header;
sip_hdr_value_t *value = NULL;
sip_param_t *tmp_param;
boolean_t first_param = B_TRUE;
int ret;
if ((ret = sip_prim_parsers(hdr, phdr)) != 0)
return (ret);
/*
* check if previously parsed
*/
if (*phdr != NULL) {
hdr->sip_hdr_parsed = *phdr;
return (0);
}
parsed_header = calloc(1, sizeof (sip_parsed_header_t));
if (parsed_header == NULL)
return (ENOMEM);
parsed_header->sip_parsed_header_version = SIP_PARSED_HEADER_VERSION_1;
parsed_header->sip_header = hdr;
value = calloc(1, sizeof (sip_hdr_value_t));
if (value == NULL) {
sip_free_phdr(parsed_header);
return (ENOMEM);
}
parsed_header->value = (sip_value_t *)value;
value->sip_value_start = hdr->sip_hdr_current;
value->auth_scheme_ptr = value->sip_value_start;
value->sip_value_header = parsed_header;
/*
* get auth_scheme
*/
if (sip_find_white_space(hdr)) {
value->sip_value_state = SIP_VALUE_BAD;
return (EINVAL);
}
value->auth_scheme_len = hdr->sip_hdr_current - value->auth_scheme_ptr;
tmp_param = value->auth_param;
/*
* parse auth_param
*/
for (;;) {
char *tmp_cur;
boolean_t quoted_name = B_FALSE;
char quoted_char = (char)0;
sip_param_t *new_param;
boolean_t pval_is_uri = B_FALSE;
if (sip_skip_white_space(hdr) != 0) {
value->sip_value_state = SIP_VALUE_BAD;
return (EPROTO);
}
tmp_cur = hdr->sip_hdr_current;
new_param = calloc(1, sizeof (sip_param_t));
if (new_param == NULL)
return (ENOMEM);
if (first_param == B_FALSE)
tmp_param->param_next = new_param;
else
value->auth_param = new_param;
tmp_param = new_param;
tmp_param->param_name.sip_str_ptr = tmp_cur;
if (sip_find_separator(hdr, SIP_EQUAL, SIP_COMMA, (char)NULL,
B_FALSE) != 0) {
tmp_param->param_name.sip_str_len =
hdr->sip_hdr_current - tmp_cur;
tmp_param->param_value.sip_str_ptr = NULL;
tmp_param->param_value.sip_str_len = 0;
value->sip_value_end = hdr->sip_hdr_current;
goto end;
}
/*
* End of param name
*/
tmp_param->param_name.sip_str_len = hdr->sip_hdr_current -
tmp_cur;
if (sip_skip_white_space(hdr) != 0 ||
*hdr->sip_hdr_current == SIP_COMMA) {
tmp_param->param_value.sip_str_ptr = NULL;
tmp_param->param_value.sip_str_len = 0;
continue;
}
/*
* We are at EQUAL
*/
hdr->sip_hdr_current++;
if (sip_skip_white_space(hdr) != 0) {
value->sip_value_state = SIP_VALUE_BAD;
free(tmp_param);
return (EPROTO);
}
if (*hdr->sip_hdr_current == SIP_QUOTE ||
*hdr->sip_hdr_current == SIP_LAQUOT) {
if (*hdr->sip_hdr_current == SIP_QUOTE)
quoted_char = SIP_QUOTE;
else {
quoted_char = SIP_RAQUOT;
pval_is_uri = B_TRUE;
}
hdr->sip_hdr_current++;
quoted_name = B_TRUE;
}
/*
* start of param value
*/
tmp_cur = hdr->sip_hdr_current;
tmp_param->param_value.sip_str_ptr = tmp_cur;
if (quoted_name) {
if (sip_find_token(hdr, quoted_char) != 0) {
value->sip_value_state = SIP_VALUE_BAD;
free(tmp_param);
return (EPROTO);
}
tmp_param->param_value.sip_str_len =
hdr->sip_hdr_current - tmp_cur - 1;
}
if (sip_find_token(hdr, SIP_COMMA) != 0) {
value->sip_value_end = hdr->sip_hdr_current;
goto end;
} else {
if (!quoted_name) {
char *t = hdr->sip_hdr_current;
hdr->sip_hdr_current--;
(void) sip_reverse_skip_white_space(hdr);
tmp_param->param_value.sip_str_len =
hdr->sip_hdr_current - tmp_cur;
hdr->sip_hdr_current = t;
}
}
if (first_param == B_TRUE)
first_param = B_FALSE;
/*
* Parse uri
*/
if (pval_is_uri && parse_uri)
sip_parse_uri_str(&tmp_param->param_value, value);
}
end:
*phdr = parsed_header;
hdr->sip_hdr_parsed = *phdr;
return (0);
}
/*
* Return the URI in the request startline
*/
static int
_sip_get_request_uri(_sip_header_t *sip_header, sip_message_type_t *msg_info)
{
int size = 0;
char *start_ptr;
if (sip_skip_white_space(sip_header) != 0)
return (EINVAL);
start_ptr = sip_header->sip_hdr_current;
while (!isspace(*sip_header->sip_hdr_current)) {
if (sip_header->sip_hdr_current >= sip_header->sip_hdr_end)
return (EINVAL);
sip_header->sip_hdr_current++;
}
size = sip_header->sip_hdr_current - start_ptr;
msg_info->U.sip_request.sip_request_uri.sip_str_ptr = start_ptr;
msg_info->U.sip_request.sip_request_uri.sip_str_len = size;
if (size > 0) { /* Parse uri */
int error;
msg_info->U.sip_request.sip_parse_uri = sip_parse_uri(
&msg_info->U.sip_request.sip_request_uri, &error);
if (msg_info->U.sip_request.sip_parse_uri == NULL)
return (error);
}
return (0);
}
/*
* Parse the start line into request/response
*/
int
sip_parse_first_line(_sip_header_t *sip_header, sip_message_type_t **msg_info)
{
sip_message_type_t *sip_msg_info;
boolean_t sip_is_request = B_TRUE;
int ret;
if (sip_header == NULL || msg_info == NULL)
return (EINVAL);
if (sip_skip_white_space(sip_header) != 0)
return (EPROTO);
/*
* There is nothing, return
*/
if (sip_header->sip_hdr_current + strlen(SIP_VERSION) >=
sip_header->sip_hdr_end) {
return (EPROTO);
}
#ifdef __solaris__
assert(mutex_held(&sip_header->sip_hdr_sipmsg->sip_msg_mutex));
#endif
sip_msg_info = malloc(sizeof (sip_message_type_t));
if (sip_msg_info == NULL)
return (ENOMEM);
/*
* let's see if it's a request or a response
*/
ret = sip_get_protocol_version(sip_header,
&sip_msg_info->sip_proto_version);
if (ret == 0) {
sip_is_request = B_FALSE;
} else if (ret == 2) {
free(sip_msg_info);
return (EPROTO);
}
if (sip_skip_white_space(sip_header) != 0) {
free(sip_msg_info);
return (EPROTO);
}
if (!sip_is_request) {
/*
* check for status code.
*/
if (sip_skip_white_space(sip_header) != 0) {
free(sip_msg_info);
return (EPROTO);
}
if (sip_header->sip_hdr_current + SIP_SIZE_OF_STATUS_CODE >=
sip_header->sip_hdr_end) {
free(sip_msg_info);
return (EPROTO);
}
if (sip_atoi(sip_header,
&sip_msg_info->U.sip_response.sip_response_code)) {
free(sip_msg_info);
return (EPROTO);
}
if (sip_msg_info->U.sip_response.sip_response_code < 100 ||
sip_msg_info->U.sip_response.sip_response_code > 700) {
free(sip_msg_info);
return (EPROTO);
}
/*
* get reason phrase.
*/
if (sip_skip_white_space(sip_header) != 0) {
sip_msg_info->sip_resp_phrase_len = 0;
sip_msg_info->sip_resp_phrase_ptr = NULL;
} else {
sip_msg_info->sip_resp_phrase_ptr =
sip_header->sip_hdr_current;
if (sip_find_cr(sip_header) != 0) {
free(sip_msg_info);
return (EPROTO);
}
sip_msg_info->sip_resp_phrase_len =
sip_header->sip_hdr_current -
sip_msg_info->sip_resp_phrase_ptr;
}
sip_msg_info->is_request = B_FALSE;
} else {
int i;
/*
* It's a request.
*/
sip_msg_info->is_request = B_TRUE;
for (i = 1; i < MAX_SIP_METHODS; i++) {
if (strncmp(sip_methods[i].name,
sip_header->sip_hdr_current,
sip_methods[i].len) == 0) {
sip_msg_info->sip_req_method = i;
sip_header->sip_hdr_current +=
sip_methods[i].len;
if (!isspace(*sip_header->sip_hdr_current++) ||
!isalpha(*sip_header->sip_hdr_current)) {
free(sip_msg_info);
return (EPROTO);
}
if ((ret = _sip_get_request_uri(sip_header,
sip_msg_info)) != 0) {
free(sip_msg_info);
return (ret);
}
/*
* Get SIP version
*/
ret = sip_get_protocol_version(sip_header,
&sip_msg_info->sip_proto_version);
if (ret != 0) {
free(sip_msg_info);
return (EPROTO);
}
goto done;
}
}
free(sip_msg_info);
return (EPROTO);
}
done:
sip_msg_info->sip_next = *msg_info;
*msg_info = sip_msg_info;
return (0);
}