summaryrefslogtreecommitdiff
path: root/usr/src/lib/libsip/common/sip_parse_hdrs.c
diff options
context:
space:
mode:
Diffstat (limited to 'usr/src/lib/libsip/common/sip_parse_hdrs.c')
-rw-r--r--usr/src/lib/libsip/common/sip_parse_hdrs.c1678
1 files changed, 1678 insertions, 0 deletions
diff --git a/usr/src/lib/libsip/common/sip_parse_hdrs.c b/usr/src/lib/libsip/common/sip_parse_hdrs.c
new file mode 100644
index 0000000000..fc021678d8
--- /dev/null
+++ b/usr/src/lib/libsip/common/sip_parse_hdrs.c
@@ -0,0 +1,1678 @@
+/*
+ * 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 2006 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include "sip_parse_uri.h"
+#include "sip_msg.h"
+#include "sip_miscdefs.h"
+#include "sip_parse_generic.h"
+
+/*
+ * Accept = "Accept" HCOLON [ accept-range *(COMMA accept-range) ]
+ * accept-range = media-range *(SEMI accept-param)
+ * media-range = ("* / *" | (m-type SLASH "*") | (m-type SLASH m-subtype))
+ * *(SEMI m-param)
+ * accept-param = ("q" EQUAL qvalue) | generic-param
+ * qvalue = ("0" ["." 0*3DIGIT]) | ("1" ["." 0*3DIGIT])
+ * generic-param = token [ EQUAL gen-value]
+ * gen-value = token | host | quoted-str
+ */
+int
+sip_parse_acpt_header(_sip_header_t *sip_header, sip_parsed_header_t **header)
+{
+ if (sip_is_empty_hdr(sip_header))
+ return (sip_parse_hdr_empty(sip_header, header));
+ return (sip_parse_hdr_parser1(sip_header, header, SIP_SLASH));
+}
+
+/*
+ * Accept-Encoding = "Accept-Encoding" ":" 1#(codings [ ";" "q" "=" qval])
+ * codings = (content-coding | "*")
+ * content-coding = token
+ */
+int
+sip_parse_acpt_encode_header(_sip_header_t *sip_header,
+ sip_parsed_header_t **header)
+{
+ return (sip_parse_hdr_parser1(sip_header, header, (char)NULL));
+}
+
+/*
+ * Accept-Language = "Accept-Language" ":" [ lang * (COMMA lang) ]
+ * lang = lang-range *(SEMI accept-param)
+ * lang-range = ((1*8ALPHA * ("-" 1*8ALPHA)) | "*"
+ */
+int
+sip_parse_acpt_lang_header(_sip_header_t *sip_header,
+ sip_parsed_header_t **header)
+{
+ if (sip_is_empty_hdr(sip_header))
+ return (sip_parse_hdr_empty(sip_header, header));
+ return (sip_parse_hdr_parser1(sip_header, header, (char)NULL));
+}
+
+/*
+ * Alert-Info = "Alert-Info" ":" alert-param *(COMMA alert-param)
+ * alert-param = LAQUOT absoluteURI RAQUOT * (SEMI generic-param)
+ */
+int
+sip_parse_alert_header(_sip_header_t *sip_header, sip_parsed_header_t **header)
+{
+ return (sip_parse_hdr_parser3(sip_header, header, SIP_STR_VAL, B_TRUE));
+}
+
+/*
+ * Allow = "Allow" ":" method-name1[, method-name2..]
+ */
+int
+sip_parse_allow_header(_sip_header_t *hdr, sip_parsed_header_t **phdr)
+{
+ sip_parsed_header_t *parsed_header;
+ sip_hdr_value_t *value = NULL;
+ sip_hdr_value_t *last_value = NULL;
+ int len;
+ int i;
+ int ret;
+ boolean_t multi_value = B_FALSE;
+
+ if ((ret = sip_prim_parsers(hdr, phdr)) != 0)
+ return (ret);
+
+ if (*phdr != NULL)
+ 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, SIP_COMMA, (char)NULL,
+ (char)NULL) == 0) {
+ multi_value = B_TRUE;
+ }
+
+ len = hdr->sip_hdr_current - value->sip_value_start;
+ for (i = 1; i < MAX_SIP_METHODS; i++) {
+ if (strncmp(sip_methods[i].name, value->sip_value_start,
+ len) == 0) {
+ break;
+ }
+ }
+ if (i >= MAX_SIP_METHODS) {
+ value->int_val = 0;
+ value->sip_value_state = SIP_VALUE_BAD;
+ if (multi_value)
+ goto next_val;
+ else
+ goto end;
+ }
+ value->int_val = i;
+ if (!multi_value)
+ goto end;
+ next_val:
+ if (sip_find_token(hdr, SIP_COMMA) != 0)
+ break;
+ value->sip_value_end = hdr->sip_hdr_current - 1;
+ last_value = value;
+ (void) sip_skip_white_space(hdr);
+ }
+
+end:
+ *phdr = parsed_header;
+ return (0);
+}
+
+
+/*
+ * Call-Info = "Call-Info" HCOLON info * (COMMA info)
+ * info = LAQUOT absoluteURI RAQUOT * (SEMI info-param)
+ * info-param = ("purpose" EQUAL ("icon" | "info" | "card" | token)) |
+ * generic-param
+ */
+int
+sip_parse_callinfo_header(_sip_header_t *sip_header,
+ sip_parsed_header_t **header)
+{
+ return (sip_parse_hdr_parser3(sip_header, header, SIP_STR_VAL, B_TRUE));
+}
+
+/*
+ * Content-Disposition = "Content-Disposition" HCOLON disp-type *
+ * (SEMI disp-param)
+ * disp-type = "render" | "session" | "icon" | "alert" | disp-ext-token
+ * disp-param = handling-param | generic-param
+ * handling-param = "handling" EQUAL("optional" | "required" | other-handling)
+ * other-handling = token
+ * disp-ext-token = token
+ *
+ */
+int
+sip_parse_contentdis_header(_sip_header_t *sip_header,
+ sip_parsed_header_t **header)
+{
+ return (sip_parse_hdr_parser1(sip_header, header, (char)NULL));
+}
+
+/*
+ * Content-Encoding = ("Content-Encoding" | "e") HCOLON content-coding *
+ * (COMMA content-coding)
+ */
+int
+sip_parse_contentencode_header(_sip_header_t *sip_header,
+ sip_parsed_header_t **header)
+{
+ return (sip_parse_hdr_parser1(sip_header, header, (char)NULL));
+}
+
+/*
+ * Content-Language = ("Content-Language" | "l") HCOLON lang-tag *
+ * (COMMA lang-tag)
+ * lang-tag = primary-tag *("-" subtag)
+ * prmary-tag = 1*8ALPHA
+ * subtag = 1*8ALPHA
+ */
+int
+sip_parse_contentlang_header(_sip_header_t *sip_header,
+ sip_parsed_header_t **header)
+{
+ return (sip_parse_hdr_parser1(sip_header, header, (char)NULL));
+}
+
+/*
+ * Date = "Date" HCOLON SIPdate
+ * SIPdate = wkday "," SP date1 SP time SP "GMT"
+ * date1 = 2DIGIT SP mnth SP 4DIGIT; day month year
+ * time = 2DIGIT ":" 2DIGIT ":" 2DIGIT
+ * wkday = "Mon" | "Tue" | "Wed" | "Thu" | "Fri" | "Sat" | "Sun"
+ * month = "Jan" | "Feb" etc
+ */
+int
+sip_parse_date_header(_sip_header_t *sip_header, sip_parsed_header_t **header)
+{
+ sip_parsed_header_t *parsed_header;
+ int r;
+ sip_hdr_value_t *value = NULL;
+
+ if ((r = sip_prim_parsers(sip_header, header)) != 0)
+ return (r);
+
+ if (*header != NULL)
+ 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 = sip_header;
+
+ 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 = sip_header->sip_hdr_current;
+ value->sip_value_header = parsed_header;
+ value->date_wd_ptr = sip_header->sip_hdr_current;
+ if (sip_find_token(sip_header, SIP_COMMA) == 0) {
+ value->date_wd_len = sip_header->sip_hdr_current -
+ value->date_wd_ptr - 1;
+ sip_header->sip_hdr_current++;
+ if (sip_skip_white_space(sip_header) != 0) {
+ value->sip_value_state = SIP_VALUE_BAD;
+ return (EPROTO);
+ }
+ } else {
+ value->sip_value_state = SIP_VALUE_BAD;
+ return (EPROTO);
+ }
+
+ if (sip_skip_white_space(sip_header) != 0) {
+ value->sip_value_state = SIP_VALUE_BAD;
+ return (EPROTO);
+ }
+ r = sip_atoi(sip_header, &value->date_d);
+ if (r != 0 || value->date_d < 0 || value->date_d > 31) {
+ value->sip_value_state = SIP_VALUE_BAD;
+ return (EPROTO);
+ }
+ if (sip_skip_white_space(sip_header) != 0) {
+ value->sip_value_state = SIP_VALUE_BAD;
+ return (EPROTO);
+ }
+ value->date_m_ptr = sip_header->sip_hdr_current;
+ if (sip_find_token(sip_header, SIP_SP) == 0) {
+ value->date_m_len = sip_header->sip_hdr_current -
+ value->date_m_ptr - 1;
+ } else {
+ value->sip_value_state = SIP_VALUE_BAD;
+ return (EPROTO);
+ }
+
+ r = sip_atoi(sip_header, &value->date_y);
+ if (r != 0 || value->date_y < 0) {
+ value->sip_value_state = SIP_VALUE_BAD;
+ return (EPROTO);
+ }
+ if (sip_skip_white_space(sip_header) != 0) {
+ value->sip_value_state = SIP_VALUE_BAD;
+ return (EPROTO);
+ }
+ value->date_t_ptr = sip_header->sip_hdr_current;
+ if (sip_find_token(sip_header, SIP_SP) == 0) {
+ value->date_t_len = sip_header->sip_hdr_current -
+ value->date_t_ptr - 1;
+ } else {
+ value->sip_value_state = SIP_VALUE_BAD;
+ return (EPROTO);
+ }
+
+ value->date_tz_ptr = sip_header->sip_hdr_current;
+ /*
+ * minus 2 to get rid of the CRLF
+ */
+ value->date_tz_len = sip_header->sip_hdr_end -
+ sip_header->sip_hdr_current - 2;
+
+ *header = parsed_header;
+
+ sip_header->sip_hdr_parsed = *header;
+ return (0);
+}
+
+/*
+ * Error-Info = "Error-Info" HCOLON error-uri *(COMMA error-uri)
+ * error-uri = LAQUOT absoluteURI RAQUOT *(SEMI generic-param)
+ */
+int
+sip_parse_errorinfo_header(_sip_header_t *sip_header,
+ sip_parsed_header_t **header)
+{
+ return (sip_parse_hdr_parser3(sip_header, header, SIP_STR_VAL, B_TRUE));
+}
+
+/*
+ * Expires = "Expires" HCOLON delta-seconds
+ */
+int
+sip_parse_expire_header(_sip_header_t *sip_header,
+ sip_parsed_header_t **header)
+{
+ return (sip_parse_hdr_parser2(sip_header, header, SIP_INT_VAL));
+}
+
+/*
+ * In-Reply-To = "In-Reply-To" HCOLON callid *(COMMA callid)
+ */
+int
+sip_parse_inreplyto_header(_sip_header_t *sip_header,
+ sip_parsed_header_t **header)
+{
+ return (sip_parse_hdr_parser1(sip_header, header, (char)NULL));
+}
+
+/*
+ * RSeq = "RSeq" HCOLON response-num
+ */
+int
+sip_parse_rseq(_sip_header_t *sip_header, sip_parsed_header_t **header)
+{
+ int r;
+ sip_hdr_value_t *rseq_value;
+
+ r = sip_parse_hdr_parser2(sip_header, header, SIP_INT_VAL);
+ /*
+ * Additionally, a value of 0 is bad_value
+ */
+ if (sip_header->sip_hdr_parsed != NULL &&
+ sip_header->sip_hdr_parsed->value != NULL) {
+ rseq_value = (sip_hdr_value_t *)
+ sip_header->sip_hdr_parsed->value;
+ if (rseq_value->int_val == 0)
+ rseq_value->sip_value_state = SIP_VALUE_BAD;
+ }
+ return (r);
+}
+
+/*
+ * Min-Expires = "Min-Expires" HCOLON delta-seconds
+ */
+int
+sip_parse_minexpire_header(_sip_header_t *sip_header,
+ sip_parsed_header_t **header)
+{
+ return (sip_parse_hdr_parser2(sip_header, header, SIP_INT_VAL));
+}
+
+/*
+ * MIME-Version = "MIME-Version" HCOLON 1*DIGIT "." 1*DIGIT
+ */
+int
+sip_parse_mimeversion_header(_sip_header_t *sip_header,
+ sip_parsed_header_t **header)
+{
+ return (sip_parse_hdr_parser4(sip_header, header));
+}
+
+/*
+ * Organization = "Organization" HCOLON [TEXT-UTF8-TRIM]
+ */
+int
+sip_parse_org_header(_sip_header_t *sip_header,
+ sip_parsed_header_t **header)
+{
+ if (sip_is_empty_hdr(sip_header))
+ return (sip_parse_hdr_empty(sip_header, header));
+ return (sip_parse_hdr_parser4(sip_header, header));
+}
+
+/*
+ * Priority = "Priority" HCOLON priority-val
+ * priority-val = "emergency" | "urgent" | "normal" | "non-urgent" | other
+ * other = token
+ */
+int
+sip_parse_priority_header(_sip_header_t *sip_header,
+ sip_parsed_header_t **header)
+{
+ return (sip_parse_hdr_parser4(sip_header, header));
+}
+
+/*
+ * Reply-To = "Reply-To" HCOLON rplyto-spec
+ * rplyto-spec = (name-addr | addr-spec) *(SEMI rplyto-param)
+ * rplyto-param = generic-param
+ * name-addr = [ display-name ] LAQUOT addr-spec RAQUOT
+ * addr-spec = SIP-URI | SIPS-URI | absolute URI
+ */
+int
+sip_parse_replyto_header(_sip_header_t *sip_header,
+ sip_parsed_header_t **header)
+{
+ return (sip_parse_hdr_parser3(sip_header, header, SIP_STRS_VAL,
+ B_TRUE));
+}
+
+/*
+ * PRIVACY = "Privacy" HCOLON priv-value *(COMMA priv-value)
+ * priv-value = "header" / "session" / "user" / "none" / "critical"
+ * / token / id
+ */
+int
+sip_parse_privacy_header(_sip_header_t *sip_header,
+ sip_parsed_header_t **header)
+{
+ return (sip_parse_hdr_parser1(sip_header, header, (char)NULL));
+}
+
+
+/*
+ * Require = "Require" HCOLON option-tag * (COMMA option-tag)
+ */
+int
+sip_parse_require_header(_sip_header_t *sip_header,
+ sip_parsed_header_t **header)
+{
+ return (sip_parse_hdr_parser1(sip_header, header, (char)NULL));
+}
+
+/*
+ * Retry-After = "Retry-After" HCOLON delta-seconds [ comment ] *
+ * (SEMI retry-param)
+ * retry-param = "duration" EQUAL delta-seconds
+ */
+int
+sip_parse_retryaft_header(_sip_header_t *sip_header,
+ sip_parsed_header_t **header)
+{
+ sip_parsed_header_t *parsed_header;
+ sip_hdr_value_t *value = NULL;
+ int ret;
+
+ if ((ret = sip_prim_parsers(sip_header, header)) != 0)
+ return (ret);
+
+ if (*header != NULL)
+ 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 = sip_header;
+
+ 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 = sip_header->sip_hdr_current;
+ value->sip_value_header = parsed_header;
+
+ ret = sip_atoi(sip_header, &(value->intstr_int));
+ if (ret != 0)
+ value->sip_value_state = SIP_VALUE_BAD;
+ if (sip_find_token(sip_header, SIP_LPAR) == 0) {
+ value->intstr_str_ptr = sip_header->sip_hdr_current;
+ if (sip_find_token(sip_header, SIP_RPAR) == 0) {
+ value->intstr_str_len =
+ sip_header->sip_hdr_current -
+ value->intstr_str_ptr - 1;
+ if (sip_find_token(sip_header, SIP_SEMI) == 0) {
+ sip_header->sip_hdr_current--;
+ (void) sip_parse_params(sip_header,
+ &(value->sip_param_list));
+ }
+ } else {
+ value->sip_value_state = SIP_VALUE_BAD;
+ return (EPROTO);
+ }
+ } else {
+ value->intstr_str_ptr = NULL;
+ value->intstr_str_len = 0;
+
+ /*
+ * from value start, search if parameter list
+ */
+ sip_header->sip_hdr_current = value->sip_value_start;
+ if (sip_find_token(sip_header, SIP_SEMI) == 0) {
+ sip_header->sip_hdr_current--;
+ (void) sip_parse_params(sip_header,
+ &(value->sip_param_list));
+ }
+ }
+
+ *header = parsed_header;
+ sip_header->sip_hdr_parsed = *header;
+ return (0);
+}
+
+/*
+ * Server = "Server" HCOLON servel-val *(LWS server-val)
+ * servel-val = product|comment
+ * product = token [SLASH version]
+ * version = token
+ * Treated as one single string
+ */
+int
+sip_parse_server_header(_sip_header_t *sip_header, sip_parsed_header_t **header)
+{
+ return (sip_parse_hdr_parser4(sip_header, header));
+}
+
+/*
+ * Subject = ("Subject" | "s")HCOLON [TEXT-UTF8-TRIM]
+ */
+int
+sip_parse_subject_header(_sip_header_t *sip_header,
+ sip_parsed_header_t **header)
+{
+ if (sip_is_empty_hdr(sip_header))
+ return (sip_parse_hdr_empty(sip_header, header));
+ return (sip_parse_hdr_parser4(sip_header, header));
+}
+
+/*
+ * Supported = ("Supported" | "k") HCOLON [option-tag * (COMMA option-tag) ]
+ */
+int
+sip_parse_support_header(_sip_header_t *sip_header,
+ sip_parsed_header_t **header)
+{
+ if (sip_is_empty_hdr(sip_header))
+ return (sip_parse_hdr_empty(sip_header, header));
+ return (sip_parse_hdr_parser1(sip_header, header, (char)NULL));
+}
+
+/*
+ * Timestamp = "Timestamp" HCOLON 1*DIGIT ["." *(DIGIT)] [LWS delay]
+ */
+int
+sip_parse_timestamp_header(_sip_header_t *sip_header,
+ sip_parsed_header_t **header)
+{
+ sip_parsed_header_t *parsed_header;
+ sip_hdr_value_t *value = NULL;
+ int ret;
+
+ if ((ret = sip_prim_parsers(sip_header, header)) != 0)
+ return (ret);
+
+ if (*header != NULL)
+ 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 = sip_header;
+
+ 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 = sip_header->sip_hdr_current;
+ value->sip_value_header = parsed_header;
+
+ if (sip_skip_white_space(sip_header) != 0) {
+ value->sip_value_state = SIP_VALUE_BAD;
+ return (EPROTO);
+ }
+ value->strs1_val_ptr = sip_header->sip_hdr_current;
+
+ if (sip_find_white_space(sip_header) == 0) {
+ /*
+ * timestamp and delay, timestamp in str1, delay in str2
+ */
+ value->strs1_val_len = sip_header->sip_hdr_current -
+ value->strs1_val_ptr;
+ (void) sip_skip_white_space(sip_header);
+
+ value->strs2_val_ptr = sip_header->sip_hdr_current;
+ if (sip_find_cr(sip_header) != 0) {
+ value->sip_value_state = SIP_VALUE_BAD;
+ return (EPROTO);
+ }
+ if (sip_header->sip_hdr_current < value->strs2_val_ptr) {
+ value->strs2_val_ptr = NULL;
+ value->strs2_val_len = 0;
+ } else {
+ value->strs2_val_len = sip_header->sip_hdr_current -
+ value->strs2_val_ptr;
+ }
+ } else {
+ /*
+ * no delay information
+ */
+ value->strs1_val_len = sip_header->sip_hdr_current
+ - value->strs1_val_ptr;
+ value->strs2_val_ptr = NULL;
+ value->strs2_val_len = 0;
+ }
+
+ *header = parsed_header;
+ sip_header->sip_hdr_parsed = *header;
+
+ return (0);
+}
+/*
+ * Unsupported = "Unsupported" HCOLON option-tag * (COMMA option-tag)
+ */
+int
+sip_parse_usupport_header(_sip_header_t *sip_header,
+ sip_parsed_header_t **header)
+{
+ return (sip_parse_hdr_parser1(sip_header, header, (char)NULL));
+}
+
+/*
+ * User-Agent = "User-Agent" HCOLON server-val * (LWS server-val)
+ * servel-val = product |comment
+ * product = token [SLASH version]
+ * version = token
+ */
+int
+sip_parse_useragt_header(_sip_header_t *sip_header,
+ sip_parsed_header_t **header)
+{
+ return (sip_parse_hdr_parser4(sip_header, header));
+}
+
+/*
+ * Warning = "Warning" HCOLON warning-value *(COMMA warning-value)
+ * warning-value = warn-code SP warn-agent SP warn-text
+ * warn-code = 3DIGIT
+ * warn-agent = hostport | pseudonym ;
+ * the name or pseudonym of the server adding;
+ * the Warning header, for use in debugging
+ * warn-text = quoted-string
+ * pseudonym = token
+ */
+int
+sip_parse_warn_header(_sip_header_t *sip_header, sip_parsed_header_t **header)
+{
+ 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(sip_header, header)) != 0)
+ return (ret);
+
+ if (*header != NULL)
+ 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 = sip_header;
+
+ while (sip_header->sip_hdr_current < sip_header->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 = sip_header->sip_hdr_current;
+ value->sip_value_header = parsed_header;
+
+ ret = sip_atoi(sip_header, &value->warn_code);
+ if (ret != 0 || value->warn_code < 100 ||
+ value->warn_code > 999) {
+ value->sip_value_state = SIP_VALUE_BAD;
+ goto get_next_val;
+ }
+ if (sip_skip_white_space(sip_header) != 0) {
+ value->sip_value_state = SIP_VALUE_BAD;
+ goto get_next_val;
+ }
+ value->warn_agt_ptr = sip_header->sip_hdr_current;
+
+ if (sip_find_token(sip_header, SIP_QUOTE) == 0) {
+ /*
+ * get warning agent
+ */
+ sip_header->sip_hdr_current--;
+ (void) sip_reverse_skip_white_space(sip_header);
+ value->warn_agt_len = sip_header->sip_hdr_current -
+ value->warn_agt_ptr - 1;
+ if (value->warn_agt_len <= 0) {
+ value->warn_agt_ptr = NULL;
+ value->sip_value_state = SIP_VALUE_BAD;
+ }
+
+ /*
+ * We will have a SIP_QUOTE here
+ */
+ (void) sip_find_token(sip_header, SIP_QUOTE);
+
+ value->warn_text_ptr = sip_header->sip_hdr_current;
+ if (sip_find_token(sip_header, SIP_QUOTE) == 0) {
+ value->warn_text_len =
+ sip_header->sip_hdr_current -
+ value->warn_text_ptr - 1;
+ } else {
+ value->sip_value_state = SIP_VALUE_BAD;
+ goto get_next_val;
+ }
+ } else
+ /*
+ * warning text must present
+ */
+ value->sip_value_state = SIP_VALUE_BAD;
+
+get_next_val:
+ if (sip_find_token(sip_header, SIP_COMMA) != 0)
+ break;
+ value->sip_value_end = sip_header->sip_hdr_current - 1;
+ last_value = value;
+ (void) sip_skip_white_space(sip_header);
+ }
+
+ *header = parsed_header;
+
+ sip_header->sip_hdr_parsed = *header;
+ return (0);
+}
+
+/*
+ * Parse RAck header
+ * "RAck" HCOLON response-num LWS CSeq-num LWS Method
+ * response-num = 1*DIGIT
+ * CSeq-num = 1*DIGIT
+ */
+int
+sip_parse_rack(_sip_header_t *sip_header, sip_parsed_header_t **header)
+{
+ sip_parsed_header_t *parsed_header;
+ sip_hdr_value_t *rack_value;
+ int len;
+ char *tmp_ptr;
+ int i;
+ int ret;
+
+ if ((ret = sip_prim_parsers(sip_header, header)) != 0)
+ return (ret);
+
+ if (*header != NULL)
+ 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 = sip_header;
+
+ parsed_header->value = calloc(1, sizeof (sip_hdr_value_t));
+ if (parsed_header->value == NULL) {
+ free(parsed_header);
+ return (ENOMEM);
+ }
+ rack_value = (sip_hdr_value_t *)parsed_header->value;
+ rack_value->sip_value_version = SIP_VALUE_VERSION_1;
+ rack_value->sip_value_start = sip_header->sip_hdr_current;
+ rack_value->sip_value_header = parsed_header;
+ if (sip_atoi(sip_header, &rack_value->rack_resp) ||
+ rack_value->rack_resp == 0) {
+ rack_value->sip_value_state = SIP_VALUE_BAD;
+ rack_value->sip_value_end = sip_header->sip_hdr_end - 2;
+ goto rack_parse_done;
+ }
+ rack_value->sip_value_header = parsed_header;
+ /*
+ * Get cseq.
+ */
+ if (sip_skip_white_space(sip_header) != 0) {
+ rack_value->sip_value_state = SIP_VALUE_BAD;
+ rack_value->sip_value_end = sip_header->sip_hdr_end - 2;
+ goto rack_parse_done;
+ }
+ if (sip_atoi(sip_header, &rack_value->rack_cseq)) {
+ rack_value->sip_value_state = SIP_VALUE_BAD;
+ rack_value->sip_value_end = sip_header->sip_hdr_end - 2;
+ goto rack_parse_done;
+ }
+ /*
+ * Get method.
+ */
+ if (sip_skip_white_space(sip_header) != 0) {
+ rack_value->sip_value_state = SIP_VALUE_BAD;
+ rack_value->sip_value_end = sip_header->sip_hdr_end - 2;
+ goto rack_parse_done;
+ }
+
+ tmp_ptr = sip_header->sip_hdr_current;
+ if (sip_find_white_space(sip_header)) {
+ rack_value->sip_value_state = SIP_VALUE_BAD;
+ rack_value->sip_value_end = sip_header->sip_hdr_end - 2;
+ goto rack_parse_done;
+ }
+
+ len = sip_header->sip_hdr_current - tmp_ptr;
+
+ for (i = 1; i < MAX_SIP_METHODS; i++) {
+ if (strncmp(sip_methods[i].name, tmp_ptr, len) == 0)
+ break;
+ }
+
+ if (i >= MAX_SIP_METHODS) {
+ rack_value->sip_value_state = SIP_VALUE_BAD;
+ rack_value->sip_value_end = sip_header->sip_hdr_end - 2;
+ goto rack_parse_done;
+ }
+
+ rack_value->rack_method = i;
+ rack_value->sip_value_end = sip_header->sip_hdr_current;
+
+rack_parse_done:
+ sip_header->sip_hdr_parsed = parsed_header;
+
+ *header = parsed_header;
+ return (0);
+}
+
+/*
+ * Allow = "Allow" HCOLON [Method *(COMMA Method)]
+ */
+int
+sip_parse_allow_events_header(_sip_header_t *sip_header,
+ sip_parsed_header_t **header)
+{
+ return (sip_parse_hdr_parser1(sip_header, header, (char)NULL));
+}
+
+/*
+ * Event = ( "Event" / "o" ) HCOLON event-type
+ * *( SEMI event-param )
+ * event-type = event-package *( "." event-template )
+ * event-package = token-nodot
+ * event-template = token-nodot
+ * token-nodot = 1*( alphanum / "-" / "!" / "%" / "*"
+ * / "_" / "+" / "`" / "'" / "~" )
+ * event-param = generic-param / ( "id" EQUAL token )
+ */
+int
+sip_parse_event_header(_sip_header_t *sip_header, sip_parsed_header_t **header)
+{
+ return (sip_parse_hdr_parser1(sip_header, header, (char)NULL));
+}
+
+/*
+ * Subscription-State = "Subscription-State" HCOLON substate-value
+ * *( SEMI subexp-params )
+ * substate-value = "active" / "pending" / "terminated"
+ * / extension-substate
+ * extension-substate = token
+ * subexp-params = ("reason" EQUAL event-reason-value)
+ * / ("expires" EQUAL delta-seconds)*
+ * / ("retry-after" EQUAL delta-seconds)
+ * / generic-param
+ * event-reason-value = "deactivated"
+ * / "probation"
+ * / "rejected"
+ * / "timeout"
+ * / "giveup"
+ * / "noresource"
+ * / event-reason-extension
+ * event-reason-extension = token
+ */
+int
+sip_parse_substate_header(_sip_header_t *sip_header,
+ sip_parsed_header_t **header)
+{
+ return (sip_parse_hdr_parser1(sip_header, header, (char)NULL));
+}
+
+/*
+ * Authorization = "Authorization" HCOLON credentials
+ * credentials = ("Digest" LWS digest-response)
+ * / other-response
+ * digest-response = dig-resp *(COMMA dig-resp)
+ * dig-resp = username / realm / nonce / digest-uri
+ * / dresponse / algorithm / cnonce
+ * / opaque / message-qop
+ * / nonce-count / auth-param
+ * username = "username" EQUAL username-value
+ * username-value = quoted-string
+ * digest-uri = "uri" EQUAL LDQUOT digest-uri-value RDQUOT
+ * digest-uri-value = rquest-uri ; Equal to request-uri as specified
+ * by HTTP/1.1
+ * message-qop = "qop" EQUAL qop-value
+ * cnonce = "cnonce" EQUAL cnonce-value
+ * cnonce-value = nonce-value
+ * nonce-count = "nc" EQUAL nc-value
+ * nc-value = 8LHEX
+ * dresponse = "response" EQUAL request-digest
+ * request-digest = LDQUOT 32LHEX RDQUOT
+ * auth-param = auth-param-name EQUAL
+ * ( token / quoted-string )
+ * auth-param-name = token
+ * other-response = auth-scheme LWS auth-param
+ * *(COMMA auth-param)
+ * auth-scheme = token
+ */
+int
+sip_parse_author_header(_sip_header_t *sip_header,
+ sip_parsed_header_t **header)
+{
+ return (sip_parse_hdr_parser5(sip_header, header, B_TRUE));
+}
+
+/*
+ * Authentication-Info = "Authentication-Info" HCOLON ainfo
+ * *(COMMA ainfo)
+ * ainfo = nextnonce / message-qop
+ * / response-auth / cnonce
+ * / nonce-count
+ * nextnonce = "nextnonce" EQUAL nonce-value
+ * response-auth = "rspauth" EQUAL response-digest
+ * response-digest = LDQUOT *LHEX RDQUOT
+ *
+ */
+int
+sip_parse_ainfo_header(_sip_header_t *sip_header,
+ sip_parsed_header_t **header)
+{
+ return (sip_parse_hdr_parser1(sip_header, header, (char)NULL));
+}
+
+/*
+ * Proxy-Authenticate = "Proxy-Authenticate" HCOLON challenge
+ * challenge = ("Digest" LWS digest-cln *(COMMA digest-cln))
+ * / other-challenge
+ * other-challenge = auth-scheme LWS auth-param
+ * *(COMMA auth-param)
+ * digest-cln = realm / domain / nonce
+ * / opaque / stale / algorithm
+ * / qop-options / auth-param
+ * realm = "realm" EQUAL realm-value
+ * realm-value = quoted-string
+ * domain = "domain" EQUAL LDQUOT URI
+ * *( 1*SP URI ) RDQUOT
+ * URI = absoluteURI / abs-path
+ * nonce = "nonce" EQUAL nonce-value
+ * nonce-value = quoted-string
+ * opaque = "opaque" EQUAL quoted-string
+ * stale = "stale" EQUAL ( "true" / "false" )
+ * algorithm = "algorithm" EQUAL ( "MD5" / "MD5-sess"
+ * / token )
+ * qop-options = "qop" EQUAL LDQUOT qop-value
+ * *("," qop-value) RDQUOT
+ * qop-value = "auth" / "auth-int" / token
+ *
+ */
+int
+sip_parse_pauthen_header(_sip_header_t *sip_header,
+ sip_parsed_header_t **header)
+{
+ return (sip_parse_hdr_parser5(sip_header, header, B_TRUE));
+}
+
+/*
+ * Proxy-Authorization = "Proxy-Authorization" HCOLON credentials
+ */
+int
+sip_parse_pauthor_header(_sip_header_t *sip_header,
+ sip_parsed_header_t **header)
+{
+ return (sip_parse_hdr_parser5(sip_header, header, B_TRUE));
+}
+
+/*
+ * Proxy-Require = "Proxy-Require" HCOLON option-tag
+ * *(COMMA option-tag)
+ * option-tag = token
+ */
+int
+sip_parse_preq_header(_sip_header_t *sip_header,
+ sip_parsed_header_t **header)
+{
+ return (sip_parse_hdr_parser1(sip_header, header, (char)NULL));
+}
+
+/*
+ * WWW-Authenticate = "WWW-Authenticate" HCOLON challenge
+ * extension-header = header-name HCOLON header-value
+ * header-name = token
+ * header-value = *(TEXT-UTF8char / UTF8-CONT / LWS)
+ * message-body = *OCTET
+ *
+ */
+int
+sip_parse_wauthen_header(_sip_header_t *sip_header,
+ sip_parsed_header_t **header)
+{
+ return (sip_parse_hdr_parser5(sip_header, header, B_TRUE));
+}
+
+/*
+ * Call-ID = ( "Call-ID" / "i" ) HCOLON callid
+ */
+int
+sip_parse_cid_header(_sip_header_t *sip_header, sip_parsed_header_t **header)
+{
+ return (sip_parse_hdr_parser4(sip_header, header));
+}
+
+/*
+ * CSeq = "CSeq" HCOLON 1*DIGIT LWS Method
+ */
+int
+sip_parse_cseq_header(_sip_header_t *sip_header, sip_parsed_header_t **header)
+{
+ sip_parsed_header_t *parsed_header;
+ sip_hdr_value_t *cseq_value;
+ int len;
+ char *tmp_ptr;
+ int i;
+ int ret;
+
+ if ((ret = sip_prim_parsers(sip_header, header)) != 0)
+ return (ret);
+
+ if (*header != NULL)
+ 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 = sip_header;
+
+ parsed_header->value = calloc(1, sizeof (sip_hdr_value_t));
+ if (parsed_header->value == NULL) {
+ free(parsed_header);
+ return (ENOMEM);
+ }
+ cseq_value = (sip_hdr_value_t *)parsed_header->value;
+ cseq_value->sip_value_version = SIP_VALUE_VERSION_1;
+ cseq_value->sip_value_start = sip_header->sip_hdr_current;
+ if (sip_atoi(sip_header, &cseq_value->cseq_num)) {
+ cseq_value->sip_value_state = SIP_VALUE_BAD;
+ cseq_value->sip_value_end = sip_header->sip_hdr_end - 2;
+ goto cseq_parse_done;
+ }
+ cseq_value->sip_value_header = parsed_header;
+ /*
+ * Get method.
+ */
+ if (sip_skip_white_space(sip_header) != 0) {
+ cseq_value->sip_value_state = SIP_VALUE_BAD;
+ cseq_value->sip_value_end = sip_header->sip_hdr_end - 2;
+ goto cseq_parse_done;
+ }
+
+ tmp_ptr = sip_header->sip_hdr_current;
+
+ if (sip_find_white_space(sip_header)) {
+ cseq_value->sip_value_state = SIP_VALUE_BAD;
+ cseq_value->sip_value_end = sip_header->sip_hdr_current;
+ goto cseq_parse_done;
+ }
+
+ len = sip_header->sip_hdr_current - tmp_ptr;
+
+ for (i = 1; i < MAX_SIP_METHODS; i++) {
+ if (strncmp(sip_methods[i].name, tmp_ptr, len) == 0)
+ break;
+ }
+
+ if (i >= MAX_SIP_METHODS) {
+ cseq_value->sip_value_state = SIP_VALUE_BAD;
+ cseq_value->sip_value_end = sip_header->sip_hdr_current;
+ goto cseq_parse_done;
+ }
+
+ cseq_value->cseq_method = i;
+ cseq_value->sip_value_end = sip_header->sip_hdr_current;
+cseq_parse_done:
+
+ sip_header->sip_hdr_parsed = parsed_header;
+
+ *header = parsed_header;
+ return (0);
+}
+
+
+/*
+ * 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
+ *
+ * There can be multiple via headers we always append the header.
+ */
+int
+sip_parse_via_header(_sip_header_t *sip_header, sip_parsed_header_t **header)
+{
+ 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(sip_header, header)) != 0)
+ return (ret);
+
+ if (*header != NULL)
+ 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 = sip_header;
+
+ while (sip_header->sip_hdr_current < sip_header->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_version = SIP_VALUE_VERSION_1;
+ value->sip_value_start = sip_header->sip_hdr_current;
+ value->sip_value_header = parsed_header;
+ value->via_protocol_name.sip_str_ptr =
+ sip_header->sip_hdr_current;
+
+ /*
+ * Check to see if there is a version number
+ */
+ if (sip_get_protocol_version(sip_header,
+ &value->via_protocol) != 0) {
+ if (sip_goto_next_value(sip_header) != 0) {
+ sip_free_phdr(parsed_header);
+ return (EPROTO);
+ }
+ value->sip_value_state = SIP_VALUE_BAD;
+ goto get_next_via_value;
+ }
+
+ if (sip_find_token(sip_header, SIP_SLASH) != 0) {
+ if (sip_goto_next_value(sip_header) != 0) {
+ sip_free_phdr(parsed_header);
+ return (EPROTO);
+ }
+ value->sip_value_state = SIP_VALUE_BAD;
+ goto get_next_via_value;
+ }
+
+ if (sip_skip_white_space(sip_header) != 0) {
+ if (sip_goto_next_value(sip_header) != 0) {
+ sip_free_phdr(parsed_header);
+ return (EPROTO);
+ }
+ value->sip_value_state = SIP_VALUE_BAD;
+ goto get_next_via_value;
+ }
+
+ value->via_protocol_transport.sip_str_ptr =
+ sip_header->sip_hdr_current;
+ if (sip_find_white_space(sip_header) != 0) {
+ if (sip_goto_next_value(sip_header) != 0) {
+ sip_free_phdr(parsed_header);
+ return (EPROTO);
+ }
+ value->sip_value_state = SIP_VALUE_BAD;
+ goto get_next_via_value;
+ }
+
+ value->via_protocol_transport.sip_str_len =
+ sip_header->sip_hdr_current -
+ value->via_protocol_transport.sip_str_ptr;
+
+ if (sip_skip_white_space(sip_header) != 0) {
+ if (sip_goto_next_value(sip_header) != 0) {
+ sip_free_phdr(parsed_header);
+ return (EPROTO);
+ }
+ value->sip_value_state = SIP_VALUE_BAD;
+ goto get_next_via_value;
+ }
+
+ value->via_sent_by_host.sip_str_ptr =
+ sip_header->sip_hdr_current;
+ if (*sip_header->sip_hdr_current == '[') {
+ if (sip_find_token(sip_header, ']')) {
+ if (sip_goto_next_value(sip_header) != 0) {
+ sip_free_phdr(parsed_header);
+ return (EPROTO);
+ }
+ value->sip_value_state = SIP_VALUE_BAD;
+ goto get_next_via_value;
+ }
+ } else if (sip_find_separator(sip_header, SIP_SEMI, SIP_COMMA,
+ SIP_HCOLON)) {
+ if (sip_goto_next_value(sip_header) != 0) {
+ sip_free_phdr(parsed_header);
+ return (EPROTO);
+ }
+ value->sip_value_state = SIP_VALUE_BAD;
+ goto get_next_via_value;
+ }
+ value->via_sent_by_host.sip_str_len =
+ sip_header->sip_hdr_current -
+ value->via_sent_by_host.sip_str_ptr;
+
+ if (sip_skip_white_space(sip_header) != 0) {
+ if (sip_goto_next_value(sip_header) != 0) {
+ sip_free_phdr(parsed_header);
+ return (EPROTO);
+ }
+ value->sip_value_state = SIP_VALUE_BAD;
+ goto get_next_via_value;
+ }
+
+ if (*sip_header->sip_hdr_current == SIP_HCOLON) {
+ sip_header->sip_hdr_current++;
+ /*
+ * We have a port number
+ */
+ if (sip_atoi(sip_header, &value->via_sent_by_port) !=
+ 0) {
+ if (sip_goto_next_value(sip_header) != 0) {
+ sip_free_phdr(parsed_header);
+ return (EPROTO);
+ }
+ value->sip_value_state = SIP_VALUE_BAD;
+ goto get_next_via_value;
+ }
+
+ }
+
+ /*
+ * Do some sanity checking.
+ * This should be replaced by a v4/v6 address check.
+ */
+ if (value->via_sent_by_host.sip_str_len == 0 ||
+ (!isalnum(*value->via_sent_by_host.sip_str_ptr) &&
+ *value->via_sent_by_host.sip_str_ptr != '[')) {
+ if (sip_goto_next_value(sip_header) != 0) {
+ sip_free_phdr(parsed_header);
+ return (EPROTO);
+ }
+ value->sip_value_state = SIP_VALUE_BAD;
+ goto get_next_via_value;
+ }
+
+ ret = sip_parse_params(sip_header, &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);
+ }
+get_next_via_value:
+ value->sip_value_end = sip_header->sip_hdr_current;
+
+ if (sip_find_token(sip_header, SIP_COMMA) != 0)
+ break;
+ last_value = value;
+ (void) sip_skip_white_space(sip_header);
+ }
+
+ sip_header->sip_hdr_parsed = parsed_header;
+
+ *header = parsed_header;
+ return (0);
+}
+
+/*
+ * Max-Forwards = "Max-Forwards" HCOLON 1*DIGIT
+ */
+int
+sip_parse_maxf_header(_sip_header_t *sip_header, sip_parsed_header_t **header)
+{
+ return (sip_parse_hdr_parser2(sip_header, header, SIP_INT_VAL));
+}
+
+/*
+ * Content-Type = ( "Content-Type" / "c" ) HCOLON media-type
+ * media-type = m-type SLASH m-subtype *(SEMI m-parameter)
+ * m-type = discrete-type / composite-type
+ * discrete-type = "text" / "image" / "audio" / "video"
+ * / "application" / extension-token
+ * composite-type = "message" / "multipart" / extension-token
+ * extension-token = ietf-token / x-token
+ * ietf-token = token
+ * x-token = "x-" token
+ * m-subtype = extension-token / iana-token
+ * iana-token = token
+ * m-parameter = m-attribute EQUAL m-value
+ * m-attribute = token
+ * m-value = token / quoted-string
+ */
+int
+sip_parse_ctype_header(_sip_header_t *sip_header, sip_parsed_header_t **header)
+{
+ return (sip_parse_hdr_parser1(sip_header, header, SIP_SLASH));
+}
+
+/*
+ * Content-Length = ( "Content-Length" / "l" ) HCOLON 1*DIGIT
+ */
+int
+sip_parse_clen_header(_sip_header_t *sip_header, sip_parsed_header_t **header)
+{
+ return (sip_parse_hdr_parser2(sip_header, header, SIP_INT_VAL));
+}
+
+/*
+ * Generic parser for Contact, From, To, Route and Record-Route headers
+ *
+ * Contact = ("Contact" / "m" ) HCOLON
+ * ( STAR / (contact-param *(COMMA contact-param)))
+ * contact-param = (name-addr / addr-spec) *(SEMI contact-params)
+ * name-addr = [ display-name ] LAQUOT addr-spec RAQUOT
+ * addr-spec = SIP-URI / SIPS-URI / absoluteURI
+ * display-name = *(token LWS)/ quoted-string
+ * contact-params = c-p-q / c-p-expires
+ * / contact-extension
+ *
+ * From = ( "From" / "f" ) HCOLON from-spec
+ * from-spec = ( name-addr / addr-spec )
+ * *( SEMI from-param )
+ * from-param = tag-param / generic-param
+ * tag-param = "tag" EQUAL token
+ *
+ * To = ( "To" / "t" ) HCOLON ( name-addr
+ * / addr-spec ) *( SEMI to-param )
+ * to-param = tag-param / generic-param
+ *
+ * Route = "Route" HCOLON route-param *(COMMA route-param)
+ * route-param = name-addr *( SEMI rr-param )
+ *
+ * Record-Route = "Record-Route" HCOLON rec-route *(COMMA rec-route)
+ * rec-route = name-addr *( SEMI rr-param )
+ * rr-param = generic-param
+ *
+ * We could have multiple values for these headers. For the ones that have
+ * a display name we will have a LAQUOT/RAQUOT. If we encounter an error
+ * when parsing a value, we mark the value as bad and start paring the
+ * next value, if present. Before we start parsing the next value, we
+ * check for any parameters, if present.
+ */
+int
+sip_parse_cftr_header(_sip_header_t *sip_header, sip_parsed_header_t **header)
+{
+ sip_parsed_header_t *parsed_header;
+ char *tmp_ptr;
+ char *tmp_ptr_2;
+ int ret;
+ sip_hdr_value_t *value = NULL;
+ sip_hdr_value_t *last_value = NULL;
+
+ if ((ret = sip_prim_parsers(sip_header, header)) != 0)
+ return (ret);
+
+ if (*header != NULL)
+ 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 = sip_header;
+ while (sip_header->sip_hdr_current < sip_header->sip_hdr_end) {
+ boolean_t quoted_name = B_FALSE;
+
+ value = calloc(1, sizeof (sip_hdr_value_t));
+ if (value == NULL) {
+ sip_free_cftr_header(parsed_header);
+ return (ENOMEM);
+ }
+ if (last_value != NULL)
+ last_value->sip_next_value = value;
+ else
+ parsed_header->value = (sip_value_t *)value;
+ if (*sip_header->sip_hdr_current == SIP_QUOTE) {
+ sip_header->sip_hdr_current++;
+ quoted_name = B_TRUE;
+ }
+ value->sip_value_version = SIP_VALUE_VERSION_1;
+ value->sip_value_start = sip_header->sip_hdr_current;
+ value->sip_value_header = parsed_header;
+ /*
+ * let's see if there is a display name
+ */
+ if (*sip_header->sip_hdr_current != SIP_LAQUOT) {
+
+ tmp_ptr = sip_header->sip_hdr_current;
+ /*
+ * According to 20.10 '<' may not have a leading
+ * space.
+ */
+ if (quoted_name &&
+ sip_find_token(sip_header, SIP_QUOTE) != 0) {
+ if (sip_goto_next_value(sip_header) != 0) {
+ sip_free_cftr_header(parsed_header);
+ return (EPROTO);
+ }
+ value->sip_value_state = SIP_VALUE_BAD;
+ goto get_next_cftr_value;
+ } else if (sip_find_separator(sip_header, SIP_SEMI,
+ SIP_LAQUOT, SIP_COMMA) != 0) {
+ /*
+ * only a uri.
+ */
+ value->cftr_uri.sip_str_ptr = tmp_ptr;
+ value->cftr_uri.sip_str_len =
+ sip_header->sip_hdr_current - tmp_ptr;
+ /*
+ * It's an error not to have a uri.
+ */
+ if (value->cftr_uri.sip_str_len == 0) {
+ if (sip_goto_next_value(sip_header) !=
+ 0) {
+ sip_free_cftr_header(
+ parsed_header);
+ return (EPROTO);
+ }
+ value->sip_value_state = SIP_VALUE_BAD;
+ goto get_next_cftr_value;
+ }
+ continue;
+ }
+
+ tmp_ptr_2 = sip_header->sip_hdr_current;
+ if (*sip_header->sip_hdr_current == SIP_SP) {
+ if (sip_skip_white_space(sip_header) != 0) {
+ /*
+ * only a uri.
+ */
+ value->cftr_uri.sip_str_ptr = tmp_ptr;
+ value->cftr_uri.sip_str_len =
+ tmp_ptr_2 - tmp_ptr;
+ /*
+ * It's an error not to have a uri.
+ */
+ if (value->cftr_uri.sip_str_len == 0) {
+ if (sip_goto_next_value(
+ sip_header) != 0) {
+ sip_free_cftr_header(
+ parsed_header);
+ return (EPROTO);
+ }
+ value->sip_value_state =
+ SIP_VALUE_BAD;
+ goto get_next_cftr_value;
+ }
+ continue;
+ }
+ }
+
+ if (*sip_header->sip_hdr_current != SIP_LAQUOT) {
+ /*
+ * No display name here.
+ */
+ value->cftr_uri.sip_str_ptr = tmp_ptr;
+ value->cftr_uri.sip_str_len = tmp_ptr_2 -
+ tmp_ptr;
+ /*
+ * It's an error not to have a uri.
+ */
+ if (value->cftr_uri.sip_str_len == 0) {
+ if (sip_goto_next_value(sip_header) !=
+ 0) {
+ sip_free_cftr_header(
+ parsed_header);
+ return (EPROTO);
+ }
+ value->sip_value_state = SIP_VALUE_BAD;
+ goto get_next_cftr_value;
+ }
+ goto get_params;
+ }
+
+ value->cftr_name = malloc(sizeof (sip_str_t));
+ if (value->cftr_name == NULL) {
+ sip_free_cftr_header(parsed_header);
+ return (ENOMEM);
+ }
+ value->cftr_name->sip_str_ptr = tmp_ptr;
+ value->cftr_name->sip_str_len = tmp_ptr_2 - tmp_ptr;
+ if (quoted_name)
+ value->cftr_name->sip_str_len--;
+ }
+
+ if (sip_find_token(sip_header, SIP_LAQUOT) != 0) {
+ if (sip_goto_next_value(sip_header) != 0) {
+ sip_free_cftr_header(parsed_header);
+ return (EPROTO);
+ }
+ value->sip_value_state = SIP_VALUE_BAD;
+ goto get_next_cftr_value;
+ }
+
+ if (*sip_header->sip_hdr_current == SIP_SP) {
+ if (sip_skip_white_space(sip_header) != 0) {
+ if (sip_goto_next_value(sip_header) != 0) {
+ sip_free_cftr_header(parsed_header);
+ return (EPROTO);
+ }
+ value->sip_value_state = SIP_VALUE_BAD;
+ goto get_next_cftr_value;
+ }
+ }
+
+ tmp_ptr = sip_header->sip_hdr_current;
+
+ if (sip_find_separator(sip_header, SIP_RAQUOT, (char)NULL,
+ (char)NULL)) {
+ if (sip_goto_next_value(sip_header) != 0) {
+ sip_free_cftr_header(parsed_header);
+ return (EPROTO);
+ }
+ value->sip_value_state = SIP_VALUE_BAD;
+ goto get_next_cftr_value;
+ }
+
+ value->cftr_uri.sip_str_ptr = tmp_ptr;
+ value->cftr_uri.sip_str_len =
+ sip_header->sip_hdr_current - tmp_ptr;
+
+ if (sip_find_token(sip_header, SIP_RAQUOT) != 0) {
+ if (sip_goto_next_value(sip_header) != 0) {
+ sip_free_cftr_header(parsed_header);
+ return (EINVAL);
+ }
+ value->sip_value_state = SIP_VALUE_BAD;
+ goto get_next_cftr_value;
+ }
+
+ if (value->cftr_uri.sip_str_len <= strlen("<>")) {
+ if (sip_goto_next_value(sip_header) != 0) {
+ sip_free_cftr_header(parsed_header);
+ return (EPROTO);
+ }
+ value->sip_value_state = SIP_VALUE_BAD;
+ goto get_next_cftr_value;
+ }
+
+get_params:
+ ret = sip_parse_params(sip_header, &value->sip_param_list);
+ if (ret == EPROTO) {
+ value->sip_value_state = SIP_VALUE_BAD;
+ } else if (ret != 0) {
+ sip_free_cftr_header(parsed_header);
+ return (ret);
+ }
+get_next_cftr_value:
+ value->sip_value_end = sip_header->sip_hdr_current;
+
+ /*
+ * Parse uri
+ */
+ if (value->cftr_uri.sip_str_len > 0) {
+ int error;
+
+ value->sip_value_parsed_uri = sip_parse_uri(
+ &value->cftr_uri, &error);
+ if (value->sip_value_parsed_uri == NULL) {
+ sip_free_cftr_header(parsed_header);
+ return (ENOMEM);
+ }
+ if (error != 0 ||
+ ((_sip_uri_t *)value->sip_value_parsed_uri)->
+ sip_uri_errflags != 0) {
+ value->sip_value_state = SIP_VALUE_BAD;
+ }
+ }
+
+ (void) sip_find_token(sip_header, SIP_COMMA);
+ last_value = value;
+ (void) sip_skip_white_space(sip_header);
+ }
+
+ sip_header->sip_hdr_parsed = parsed_header;
+
+ *header = parsed_header;
+ return (0);
+}
+
+/*
+ * PAssertedID = "P-Asserted-Identity" HCOLON PAssertedID-value
+ * *(COMMA PAssertedID-value)
+ * PAssertedID-value = name-addr / addr-spec
+ */
+int
+sip_parse_passertedid(_sip_header_t *sip_header, sip_parsed_header_t **header)
+{
+ return (sip_parse_hdr_parser3(sip_header, header, SIP_STRS_VAL,
+ B_TRUE));
+}
+
+/*
+ * PPreferredID = "P-Preferred-Identity" HCOLON PPreferredID-value
+ * *(COMMA PAssertedID-value)
+ * PPreferredID-value = name-addr / addr-spec
+ */
+int
+sip_parse_ppreferredid(_sip_header_t *sip_header, sip_parsed_header_t **header)
+{
+ return (sip_parse_hdr_parser3(sip_header, header, SIP_STRS_VAL,
+ B_TRUE));
+}
+
+
+/*
+ * We don't do anything for a header we don't understand
+ */
+/* ARGSUSED */
+int
+sip_parse_unknown_header(_sip_header_t *sip_header,
+ sip_parsed_header_t **header)
+{
+ return (EINVAL);
+}