changeset 2882:5f4abbf1f03e

PSARC 2006/402 SIP Library Integration 6461142 Integrate SIP in Solaris
author vi117747
date Sat, 07 Oct 2006 14:26:26 -0700
parents ea6360e7e1c5
children 2a5ad54a050b
files usr/src/Makefile.lint usr/src/lib/Makefile usr/src/lib/libsip/Makefile usr/src/lib/libsip/Makefile.com usr/src/lib/libsip/amd64/Makefile usr/src/lib/libsip/common/llib-lsip usr/src/lib/libsip/common/mapfile-vers usr/src/lib/libsip/common/sip.h usr/src/lib/libsip/common/sip_add_hdrs.c usr/src/lib/libsip/common/sip_dialog.c usr/src/lib/libsip/common/sip_dialog.h usr/src/lib/libsip/common/sip_dialog_ui.c usr/src/lib/libsip/common/sip_gids.c usr/src/lib/libsip/common/sip_hash.c usr/src/lib/libsip/common/sip_hash.h usr/src/lib/libsip/common/sip_hdrs_ui.c usr/src/lib/libsip/common/sip_headers.c usr/src/lib/libsip/common/sip_itf.c usr/src/lib/libsip/common/sip_miscdefs.h usr/src/lib/libsip/common/sip_msg.c usr/src/lib/libsip/common/sip_msg.h usr/src/lib/libsip/common/sip_parse_generic.c usr/src/lib/libsip/common/sip_parse_generic.h usr/src/lib/libsip/common/sip_parse_hdrs.c usr/src/lib/libsip/common/sip_parse_uri.c usr/src/lib/libsip/common/sip_parse_uri.h usr/src/lib/libsip/common/sip_reass.c usr/src/lib/libsip/common/sip_timeout.c usr/src/lib/libsip/common/sip_ui.c usr/src/lib/libsip/common/sip_uri_ui.c usr/src/lib/libsip/common/sip_xaction.c usr/src/lib/libsip/common/sip_xaction.h usr/src/lib/libsip/common/sip_xaction_state_mc.c usr/src/lib/libsip/common/sip_xaction_ui.c usr/src/lib/libsip/i386/Makefile usr/src/lib/libsip/sparc/Makefile usr/src/lib/libsip/sparcv9/Makefile usr/src/pkgdefs/SUNWarc/prototype_com usr/src/pkgdefs/SUNWarc/prototype_i386 usr/src/pkgdefs/SUNWarc/prototype_sparc usr/src/pkgdefs/SUNWcsl/prototype_com usr/src/pkgdefs/SUNWcsl/prototype_i386 usr/src/pkgdefs/SUNWcsl/prototype_sparc usr/src/pkgdefs/SUNWhea/prototype_com
diffstat 44 files changed, 20409 insertions(+), 0 deletions(-) [+]
line wrap: on
line diff
--- a/usr/src/Makefile.lint	Sat Oct 07 13:37:05 2006 -0700
+++ b/usr/src/Makefile.lint	Sat Oct 07 14:26:26 2006 -0700
@@ -357,6 +357,7 @@
 	lib/libsec \
 	lib/libsecdb \
 	lib/libsendfile \
+	lib/libsip \
 	lib/libsldap \
 	lib/libslp \
 	lib/libsmbios \
--- a/usr/src/lib/Makefile	Sat Oct 07 13:37:05 2006 -0700
+++ b/usr/src/lib/Makefile	Sat Oct 07 14:26:26 2006 -0700
@@ -73,6 +73,7 @@
 	librpcsvc			\
 	libsocket		.WAIT	\
 	libsctp			\
+	libsip			\
 	libresolv			\
 	libresolv2		.WAIT	\
 	libw			.WAIT	\
@@ -347,6 +348,7 @@
 	libproc		\
 	librcm		\
 	libscf		\
+	libsip		\
 	libsmbios	\
 	librestart	\
 	librpcsvc	\
@@ -466,6 +468,7 @@
 libsasl:	libgss libsocket pkcs11 libmd
 sasl_plugins:	pkcs11 libgss libsocket libsasl
 libsctp:	libsocket
+libsip:		libmd5
 libsocket:	libnsl
 libldap5:	libsasl libsocket libnsl libmd
 libsldap:	libldap5 libtsol
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/lib/libsip/Makefile	Sat Oct 07 14:26:26 2006 -0700
@@ -0,0 +1,53 @@
+#
+# 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.
+#
+# ident	"%Z%%M%	%I%	%E% SMI"
+#
+
+include ../Makefile.lib
+
+HDRS = sip.h
+HDRDIR = common
+SUBDIRS = $(MACH)
+$(BUILD64)SUBDIRS += $(MACH64)
+
+all	:= TARGET= all
+clean	:= TARGET= clean
+clobber	:= TARGET= clobber
+install	:= TARGET= install
+lint	:= TARGET= lint
+
+.KEEP_STATE:
+
+all clean clobber install lint: $(SUBDIRS)
+
+install_h:	$(ROOTHDRS)
+
+check:	$(CHECKHDRS)
+
+$(SUBDIRS): FRC
+	@cd $@; pwd; $(MAKE) $(TARGET)
+
+FRC:
+
+include ../Makefile.targ
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/lib/libsip/Makefile.com	Sat Oct 07 14:26:26 2006 -0700
@@ -0,0 +1,51 @@
+#
+# 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.
+#
+# ident	"%Z%%M%	%I%	%E% SMI"
+#
+
+LIBRARY = libsip.a
+VERS    = .1
+OBJECTS = sip_headers.o sip_msg.o sip_gids.o \
+	sip_timeout.o sip_xaction_state_mc.o sip_xaction.o \
+	sip_hash.o sip_itf.o sip_ui.o sip_reass.o sip_dialog.o \
+	sip_dialog_ui.o sip_xaction_ui.o sip_parse_generic.o \
+	sip_parse_uri.o sip_uri_ui.o sip_parse_hdrs.o \
+	sip_add_hdrs.o sip_hdrs_ui.o
+
+include ../../Makefile.lib
+
+SRCDIR =	../common
+LIBS =		$(DYNLIB) $(LINTLIB)
+$(LINTLIB) :=	SRCS = $(SRCDIR)/$(LINTSRC)
+LDLIBS +=	-lmd5 -lc
+
+CFLAGS +=	-v -DOS='"solaris"' -D__OS_solaris
+
+.KEEP_STATE:
+
+all:		$(LIBS)
+
+lint:		lintcheck
+
+include ../../Makefile.targ
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/lib/libsip/amd64/Makefile	Sat Oct 07 14:26:26 2006 -0700
@@ -0,0 +1,30 @@
+#
+# 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.
+#
+# ident	"%Z%%M%	%I%	%E% SMI"
+#
+
+include ../Makefile.com
+include ../../Makefile.lib.64
+
+install: all $(ROOTLIBS64) $(ROOTLINKS64)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/lib/libsip/common/llib-lsip	Sat Oct 07 14:26:26 2006 -0700
@@ -0,0 +1,31 @@
+/*
+ * 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"
+
+/* LINTLIBRARY */
+/* PROTOLIB1 */
+#include <sip.h>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/lib/libsip/common/mapfile-vers	Sat Oct 07 14:26:26 2006 -0700
@@ -0,0 +1,266 @@
+#
+# 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.
+#
+# ident	"%Z%%M%	%I%	%E% SMI"
+#
+
+SUNW_1.1 {
+    global:
+	sip_init_conn_object;
+	sip_clear_stale_data;
+	sip_conn_destroyed;
+	sip_stack_init;
+	sip_sendmsg;
+	sip_process_new_packet;
+	sip_sent_by_to_str;
+	sip_register_sent_by;
+	sip_unregister_sent_by;
+	sip_unregister_all_sent_by;
+	sip_guid;
+	sip_branchid;
+	sip_get_cseq;
+	sip_get_rseq;
+	sip_new_msg;
+	sip_free_msg;
+	sip_hold_msg;
+	sip_get_msg_len;
+	sip_clone_msg;
+	sip_msg_to_str;
+	sip_get_header;
+	sip_add_header;
+	sip_delete_header;
+	sip_delete_header_by_name;
+	sip_copy_header;
+	sip_copy_header_by_name;
+	sip_copy_all_headers;
+	sip_hdr_to_str;
+	sip_get_header_value;
+	sip_get_next_value;
+	sip_delete_value;
+	sip_is_param_present;
+	sip_get_param_value;
+	sip_get_params;
+	sip_add_param;
+	sip_get_trans;
+	sip_get_trans_branchid;
+	sip_get_trans_method;
+	sip_get_trans_state;
+	sip_get_trans_resp_msg;
+	sip_get_trans_orig_msg;
+	sip_get_trans_conn_obj;
+	sip_hold_trans;
+	sip_release_trans;
+	sip_create_dialog_req;
+	sip_hold_dialog;
+	sip_release_dialog;
+	sip_delete_dialog;
+	sip_get_dialog_method;
+	sip_get_dialog_state;
+	sip_get_dialog_callid;
+	sip_get_dialog_local_tag;
+	sip_get_dialog_remote_tag;
+	sip_get_dialog_local_uri;
+	sip_get_dialog_remote_uri;
+	sip_get_dialog_remote_target_uri;
+	sip_get_dialog_route_set;
+	sip_is_dialog_secure;
+	sip_get_dialog_local_cseq;
+	sip_get_dialog_remote_cseq;
+	sip_get_dialog_type;
+	sip_get_request_uri;
+	sip_get_uri_parsed;
+	sip_parse_uri;
+	sip_free_parsed_uri;
+	sip_is_sipuri;
+	sip_get_uri_scheme;
+	sip_get_uri_user;
+	sip_get_uri_password;
+	sip_get_uri_host;
+	sip_get_uri_port;
+	sip_get_uri_params;
+	sip_get_uri_headers;
+	sip_get_uri_opaque;
+	sip_get_uri_query;
+	sip_get_uri_path;
+	sip_get_uri_regname;
+	sip_is_uri_teluser;
+	sip_get_uri_errflags;
+	sip_uri_errflags_to_str;
+	sip_copy_start_line;
+	sip_delete_start_line;
+	sip_reqline_to_str;
+	sip_respline_to_str;
+	sip_add_request_line;
+	sip_add_response_line;
+	sip_msg_is_request;
+	sip_msg_is_response;
+	sip_get_request_method;
+	sip_get_request_uri_str;
+	sip_get_resp_desc;
+	sip_get_response_code;
+	sip_get_response_phrase;
+	sip_create_response;
+	sip_get_sip_version;
+	sip_create_OKack;
+	sip_add_from;
+	sip_get_from_uri_str;
+	sip_get_from_display_name;
+	sip_get_from_tag;
+	sip_add_to;
+	sip_get_to_uri_str;
+	sip_get_to_display_name;
+	sip_get_to_tag;
+	sip_add_contact;
+	sip_get_contact_uri_str;
+	sip_get_contact_display_name;
+	sip_get_num_via;
+	sip_get_branchid;
+	sip_add_via;
+	sip_add_branchid_to_via;
+	sip_get_via_sent_by_host;
+	sip_get_via_sent_by_port;
+	sip_get_via_sent_protocol_version;
+	sip_get_via_sent_protocol_name;
+	sip_get_via_sent_transport;
+	sip_add_callid;
+	sip_get_callid;
+	sip_add_cseq;
+	sip_get_callseq_num;
+	sip_get_callseq_method;
+	sip_add_maxforward;
+	sip_get_maxforward;
+	sip_get_content_length;
+	sip_add_content;
+	sip_get_content;
+	sip_add_content_type;
+	sip_get_content_type;
+	sip_get_content_sub_type;
+	sip_add_route;
+	sip_get_route_uri_str;
+	sip_get_route_display_name;
+	sip_add_record_route;
+	sip_add_allow_events;
+	sip_get_allow_events;
+	sip_add_event;
+	sip_get_event;
+	sip_add_substate;
+	sip_get_substate;
+	sip_add_accept;
+	sip_get_accept_type;
+	sip_get_accept_sub_type;
+	sip_add_accept_enc;
+	sip_get_accept_enc;
+	sip_add_accept_lang;
+	sip_get_accept_lang;
+	sip_add_alert_info;
+	sip_get_alert_info_uri;
+	sip_add_allow;
+	sip_get_allow_method;
+	sip_add_call_info;
+	sip_get_call_info_uri;
+	sip_add_content_disp;
+	sip_get_content_disp;
+	sip_add_content_enc;
+	sip_get_content_enc;
+	sip_add_content_lang;
+	sip_get_content_lang;
+	sip_add_date;
+	sip_get_date_time;
+	sip_get_date_day;
+	sip_get_date_month;
+	sip_get_date_year;
+	sip_get_date_wkday;
+	sip_get_date_timezone;
+	sip_add_error_info;
+	sip_get_error_info_uri;
+	sip_add_expires;
+	sip_get_expires;
+	sip_add_in_reply_to;
+	sip_get_in_reply_to;
+	sip_add_min_expires;
+	sip_get_min_expires;
+	sip_add_mime_version;
+	sip_get_mime_version;
+	sip_add_org;
+	sip_get_org;
+	sip_add_priority;
+	sip_get_priority;
+	sip_add_reply_to;
+	sip_get_replyto_display_name;
+	sip_get_replyto_uri_str;
+	sip_add_require;
+	sip_get_require;
+	sip_add_retry_after;
+	sip_get_retry_after_time;
+	sip_get_retry_after_cmts;
+	sip_add_server;
+	sip_get_server;
+	sip_add_subject;
+	sip_get_subject;
+	sip_add_supported;
+	sip_get_supported;
+	sip_add_tstamp;
+	sip_get_tstamp_value;
+	sip_get_tstamp_delay;
+	sip_add_unsupported;
+	sip_get_unsupported;
+	sip_add_user_agent;
+	sip_get_user_agent;
+	sip_add_warning;
+	sip_get_warning_code;
+	sip_get_warning_agent;
+	sip_get_warning_text;
+	sip_add_author;
+	sip_get_author_scheme;
+	sip_get_author_param;
+	sip_add_authen_info;
+	sip_get_authen_info;
+	sip_add_proxy_authen;
+	sip_get_proxy_authen_scheme;
+	sip_get_proxy_authen_param;
+	sip_add_proxy_author;
+	sip_get_proxy_author_scheme;
+	sip_get_proxy_author_param;
+	sip_add_proxy_require;
+	sip_get_proxy_require;
+	sip_add_www_authen;
+	sip_get_www_authen_scheme;
+	sip_get_www_authen_param;
+	sip_add_privacy;
+	sip_get_priv_value;
+	sip_add_passertedid;
+	sip_get_passertedid_display_name;
+	sip_get_passertedid_uri_str;
+	sip_add_ppreferredid;
+	sip_get_ppreferredid_display_name;
+	sip_get_ppreferredid_uri_str;
+	sip_add_rack;
+	sip_get_rack_resp_num;
+	sip_get_rack_cseq_num;
+	sip_get_rack_method;
+	sip_add_rseq;
+	sip_get_rseq_resp_num;
+    local:
+	*;
+};
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/lib/libsip/common/sip.h	Sat Oct 07 14:26:26 2006 -0700
@@ -0,0 +1,720 @@
+/*
+ * 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.
+ */
+
+#ifndef	_SIP_H
+#define	_SIP_H
+
+#pragma ident	"%Z%%M%	%I%	%E% SMI"
+
+#ifdef	__cplusplus
+extern "C" {
+#endif
+
+#define	_XPG4_2
+#define	__EXTENSIONS__
+
+#ifndef	DEBUG
+#define	NDEBUG	1
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <ctype.h>
+#include <string.h>
+#include <strings.h>
+#include <assert.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <pthread.h>
+#include <errno.h>
+#include <unistd.h>
+#include <signal.h>
+
+/* Send a SIP message statefully */
+#define	SIP_SEND_STATEFUL	0x0001
+
+/* Enable multiple dialogs if a request is forked */
+#define	SIP_DIALOG_ON_FORK	0x0010
+
+#define	SIP_CRLF 		"\r\n"
+#define	SKIP_CRLF(msg_ptr)	(msg_ptr = msg_ptr + 2)
+#define	SIP_VERSION		"SIP/2.0"
+#define	SIP			"SIP"
+
+/* SIP headers */
+#define	SIP_TO				"TO"
+#define	SIP_FROM			"FROM"
+#define	SIP_TAG				"TAG="
+#define	SIP_CONTENT_LENGTH		"CONTENT-LENGTH"
+#define	SIP_CONTENT_TYPE		"CONTENT-TYPE"
+#define	SIP_CALL_ID			"CALL-ID"
+#define	SIP_CSEQ			"CSEQ"
+#define	SIP_MAX_FORWARDS		"MAX-FORWARDS"
+#define	SIP_CONTACT			"CONTACT"
+#define	SIP_VIA				"Via"
+#define	SIP_RECORD_ROUTE		"RECORD-ROUTE"
+#define	SIP_ROUTE			"ROUTE"
+#define	SIP_ACCEPT			"ACCEPT"
+#define	SIP_ACCEPT_ENCODE		"ACCEPT-ENCODING"
+#define	SIP_ACCEPT_LANG			"ACCEPT-LANGUAGE"
+#define	SIP_ALERT_INFO			"ALERT-INFO"
+#define	SIP_ALLOW			"ALLOW"
+#define	SIP_CALL_INFO			"CALL-INFO"
+#define	SIP_CONTENT_DIS			"CONTENT-DISPOSITION"
+#define	SIP_CONTENT_ENCODE		"CONTENT-ENCODING"
+#define	SIP_CONTENT_LANG		"CONTENT-LANGUAGE"
+#define	SIP_DATE			"DATE"
+#define	SIP_ERROR_INFO			"ERROR-INFO"
+#define	SIP_EXPIRE			"EXPIRES"
+#define	SIP_IN_REPLY_TO			"IN-REPLY-TO"
+#define	SIP_MIN_EXPIRE			"MIN-EXPIRES"
+#define	SIP_MIME_VERSION		"MIME-VERSION"
+#define	SIP_ORGANIZATION		"ORGANIZATION"
+#define	SIP_PRIORITY			"PRIORITY"
+#define	SIP_REQUIRE			"REQUIRE"
+#define	SIP_REPLYTO			"REPLY-TO"
+#define	SIP_RETRY_AFTER			"RETRY-AFTER"
+#define	SIP_SERVER			"SERVER"
+#define	SIP_SUBJECT			"SUBJECT"
+#define	SIP_SUPPORT			"SUPPORTED"
+#define	SIP_TIMESTAMP			"TIMESTAMP"
+#define	SIP_UNSUPPORT			"UNSUPPORTED"
+#define	SIP_USER_AGENT			"USER-AGENT"
+#define	SIP_WARNING			"WARNING"
+#define	SIP_ALLOW_EVENTS		"ALLOW-EVENTS"
+#define	SIP_EVENT			"EVENT"
+#define	SIP_SUBSCRIPTION_STATE		"SUBSCRIPTION-STATE"
+#define	SIP_WWW_AUTHEN			"WWW-AUTHENTICATE"
+#define	SIP_AUTHOR			"AUTHORIZATION"
+#define	SIP_AUTHEN_INFO			"AUTHENTICATION-INFO"
+#define	SIP_PROXY_AUTHOR		"PROXY-AUTHORIZATION"
+#define	SIP_PROXY_AUTHEN		"PROXY-AUTHENTICATE"
+#define	SIP_PROXY_REQ			"PROXY-REQUIRE"
+#define	SIP_PASSERTEDID			"P-ASSERTED-IDENTITY"
+#define	SIP_PPREFERREDID		"P-PREFERRED-IDENTITY"
+#define	SIP_PRIVACY			"PRIVACY"
+#define	SIP_RACK			"RACK"
+#define	SIP_RSEQ			"RSEQ"
+
+/* SIP Response Codes */
+
+/* 1XX - Provisional */
+#define	SIP_TRYING			100
+#define	SIP_RINGING			180
+#define	SIP_CALL_IS_BEING_FORWARDED 	181
+#define	SIP_QUEUED			182
+#define	SIP_SESSION_PROGRESS		183
+
+/* 2XX - Success */
+#define	SIP_OK				200
+#define	SIP_ACCEPTED			202
+
+/* 3XX - Redirection */
+#define	SIP_MULTIPLE_CHOICES		300
+#define	SIP_MOVED_PERMANENTLY		301
+#define	SIP_MOVED_TEMPORARILY		302
+#define	SIP_USE_PROXY			303
+#define	SIP_ALTERNATIVE_SERVICE		304
+
+/* 4XX - Request Failed */
+#define	SIP_BAD_REQUEST			400
+#define	SIP_UNAUTHORIZED		401
+#define	SIP_PAYMENT_REQUIRED		402
+#define	SIP_FORBIDDEN			403
+#define	SIP_NOT_FOUND			404
+#define	SIP_METHOD_NOT_ALLOWED		405
+#define	SIP_NOT_ACCEPTABLE		406
+#define	SIP_PROXY_AUTH_REQUIRED		407
+#define	SIP_REQUEST_TIMEOUT		408
+#define	SIP_GONE			410
+#define	SIP_REQUEST_ENTITY_2_LARGE	413
+#define	SIP_REQUEST_URI_2_LONG		414
+#define	SIP_UNSUPPORTED_MEDIA_TYPE	415
+#define	SIP_UNSUPPORTED_URI_SCHEME	416
+#define	SIP_BAD_EXTENSION		420
+#define	SIP_EXTENSION_REQUIRED		421
+#define	SIP_INTERVAL_2_BRIEF		423
+#define	SIP_TEMPORARILY_UNAVAIL		480
+#define	SIP_CALL_NON_EXISTANT		481
+#define	SIP_LOOP_DETECTED		482
+#define	SIP_TOO_MANY_HOOPS		483
+#define	SIP_ADDRESS_INCOMPLETE		484
+#define	SIP_AMBIGUOUS			485
+#define	SIP_BUSY_HERE			486
+#define	SIP_REQUEST_TERMINATED		487
+#define	SIP_NOT_ACCEPTABLE_HERE		488
+#define	SIP_BAD_EVENT			489
+#define	SIP_REQUEST_PENDING		491
+#define	SIP_UNDECIPHERABLE		493
+
+/* 5XX - Server Failure */
+#define	SIP_SERVER_INTERNAL_ERROR	500
+#define	SIP_NOT_IMPLEMENTED		501
+#define	SIP_BAD_GATEWAY			502
+#define	SIP_SERVICE_UNAVAILABLE		503
+#define	SIP_SERVER_TIMEOUT		504
+#define	SIP_VERSION_NOT_SUPPORTED	505
+#define	SIP_MESSAGE_2_LARGE		513
+
+/* 6XX - Global Failures */
+#define	SIP_BUSY_EVERYWHERE		600
+#define	SIP_DECLINE			601
+#define	SIP_DOES_NOT_EXIST_ANYWHERE 	604
+#define	SIP_NOT_ACCEPTABLE_ANYWHERE	606
+
+/* Response error types */
+#define	SIP_PROVISIONAL_RESP(resp)	((resp) >= 100 && (resp) < 200)
+#define	SIP_FINAL_RESP(resp)		((resp) >= 200 && (resp) < 700)
+#define	SIP_OK_RESP(resp)		((resp) >= 200 && (resp) < 300)
+#define	SIP_NONOK_FINAL_RESP(resp)	((resp) >= 300 && (resp) < 700)
+#define	SIP_REDIRECT_RESP(resp)		((resp) >= 300 && (resp) < 400)
+#define	SIP_REQFAIL_RESP(resp)		((resp) >= 400 && (resp) < 500)
+#define	SIP_SRVFAIL_RESP(resp)		((resp) >= 500 && (resp) < 600)
+#define	SIP_GLOBFAIL_RESP(resp)		((resp) >= 600 && (resp) < 700)
+
+/* Types of transactions */
+#define	SIP_CLIENT_TRANSACTION		1
+#define	SIP_SERVER_TRANSACTION		2
+
+/* Transaction states */
+#define	SIP_NEW_TRANSACTION		0
+
+/* Client Transaction States */
+#define	SIP_CLNT_CALLING		1
+#define	SIP_CLNT_INV_PROCEEDING 	2
+#define	SIP_CLNT_INV_TERMINATED 	3
+#define	SIP_CLNT_INV_COMPLETED		4
+#define	SIP_CLNT_TRYING		5
+#define	SIP_CLNT_NONINV_PROCEEDING 	6
+#define	SIP_CLNT_NONINV_TERMINATED 	7
+#define	SIP_CLNT_NONINV_COMPLETED	8
+
+/* Server Transaction States */
+#define	SIP_SRV_INV_PROCEEDING		9
+#define	SIP_SRV_INV_COMPLETED		10
+#define	SIP_SRV_CONFIRMED		11
+#define	SIP_SRV_INV_TERMINATED		12
+#define	SIP_SRV_TRYING			13
+#define	SIP_SRV_NONINV_PROCEEDING	14
+#define	SIP_SRV_NONINV_COMPLETED	15
+#define	SIP_SRV_NONINV_TERMINATED	16
+
+/* Dialog types */
+#define	SIP_UAC_DIALOG			1
+#define	SIP_UAS_DIALOG			2
+
+/* Dialog state */
+typedef enum dialog_state {
+	SIP_DLG_NEW = 0,	/* New dialog, no reply received yet */
+	SIP_DLG_EARLY,		/* Early dialog, provisional reply received */
+	SIP_DLG_CONFIRMED,	/* Confirmed dialog, 2xx reply received */
+	SIP_DLG_DESTROYED	/* Destroyed dialog */
+} dialog_state_t;
+
+/* SIP URI parse errors */
+#define	SIP_URIERR_SCHEME	0x00000001 /* invalid URL SCHEME name */
+#define	SIP_URIERR_USER		0x00000002 /* invalid user name */
+#define	SIP_URIERR_PASS		0x00000004 /* invalid password  */
+#define	SIP_URIERR_HOST		0x00000008 /* invalid domain name */
+#define	SIP_URIERR_PORT		0x00000010 /* invalid port number */
+#define	SIP_URIERR_PARAM	0x00000020 /* parameter specific error */
+#define	SIP_URIERR_HEADER	0x00000040 /* headers specific error */
+#define	SIP_URIERR_OPAQUE	0x00000080 /* opaque specific error */
+#define	SIP_URIERR_QUERY	0x00000100 /* query specific error */
+#define	SIP_URIERR_PATH		0x00000200 /* path specific error */
+#define	SIP_URIERR_REGNAME	0x00000400 /* reg-name specific error */
+#define	SIP_URIERR_NOURI	0x00000800 /* No URI */
+#define	SIP_URIERR_MEMORY	0x00001000 /* out of memory */
+
+#ifdef		__linux__
+#define		B_FALSE		0
+#define		B_TRUE		1
+
+typedef int		boolean_t;
+typedef unsigned char	uchar_t;
+typedef unsigned int	uint_t;
+typedef unsigned int	uint32_t;
+#endif
+
+typedef struct sip_message	*sip_msg_t;
+typedef struct sip_header	*sip_header_t;
+typedef struct sip_value	*sip_header_value_t;
+typedef struct sip_dialog	*sip_dialog_t;
+typedef struct sip_uri		*sip_uri_t;
+typedef struct sip_conn_object	*sip_conn_object_t;
+typedef	struct sip_xaction	*sip_transaction_t;
+
+typedef struct sip_str {
+	char	*sip_str_ptr;
+	int	sip_str_len;
+}sip_str_t;
+
+
+/* SIP parameter */
+typedef struct sip_param {
+	sip_str_t	param_name;
+	sip_str_t	param_value;
+	struct sip_param *param_next;
+}sip_param_t;
+
+
+/* Parsed header structure */
+typedef struct sip_parsed_header {
+	int		 sip_parsed_header_version;
+	struct sip_value *value;
+	sip_header_t	sip_header;
+}sip_parsed_header_t;
+
+#define	SIP_PARSED_HEADER_VERSION_1	1
+
+/* Value states */
+typedef enum {
+	SIP_VALUE_ACTIVE = 0,
+	SIP_VALUE_BAD,
+	SIP_VALUE_DELETED
+}sip_value_state_t;
+
+/* SIP header value */
+typedef struct sip_value {
+	int			sip_value_version;
+	void			*next;
+	sip_param_t		*param_list;
+	sip_value_state_t	value_state; /* Active/Deleted */
+	sip_parsed_header_t	*parsed_header;
+	char			*value_start;
+	char			*value_end;
+	sip_str_t		*sip_value_uri_str;
+	sip_uri_t		sip_value_parse_uri;
+}sip_value_t;
+
+#define	SIP_VALUE_VERSION_1	1
+
+typedef struct sip_header_general {
+	char			*sip_hdr_start;
+	char			*sip_hdr_end;
+	char			*sip_hdr_current;
+	sip_parsed_header_t	*sip_hdr_parsed;
+}sip_hdr_general_t;
+
+/* SIP methods */
+typedef enum {
+	UNKNOWN = 0,
+	INVITE,
+	ACK,
+	OPTIONS,
+	BYE,
+	CANCEL,
+	REGISTER,
+	REFER,
+	INFO,
+	SUBSCRIBE,
+	NOTIFY,
+	PRACK
+}sip_method_t;
+
+#define	MAX_SIP_METHODS	12
+
+typedef struct sip_methods {
+	char *name;	/* Name of the method */
+	int  len;	/* Length for comparison */
+}sip_methods_t;
+
+extern sip_methods_t sip_methods[];
+
+/* SIP header function table */
+typedef struct header_function_table {
+	char		*header_name;
+	char		*header_short_name;
+	int		(*header_parse_func)(struct sip_header *,
+			    struct sip_parsed_header **);
+	boolean_t	(*header_check_compliance)(struct sip_parsed_header *);
+	boolean_t	(*header_is_equal)(struct sip_parsed_header *,
+			    struct sip_parsed_header *);
+	void		(*header_free)(struct sip_parsed_header *);
+}sip_header_function_t;
+
+/* Connection Manager interface */
+typedef struct sip_io_pointers_s {
+	int	(*sip_conn_send)(const sip_conn_object_t, char *, int);
+	void	(*sip_hold_conn_object)(sip_conn_object_t);
+	void	(*sip_rel_conn_object)(sip_conn_object_t);
+	boolean_t	(*sip_conn_is_stream)(sip_conn_object_t);
+	boolean_t	(*sip_conn_is_reliable)(sip_conn_object_t);
+	int 	(*sip_conn_remote_address)(sip_conn_object_t, struct sockaddr *,
+		    socklen_t *);
+	int 	(*sip_conn_local_address)(sip_conn_object_t, struct sockaddr *,
+		    socklen_t *);
+	int	(*sip_conn_transport)(sip_conn_object_t);
+	int	(*sip_conn_timer1)(sip_conn_object_t);
+	int	(*sip_conn_timer2)(sip_conn_object_t);
+	int	(*sip_conn_timer4)(sip_conn_object_t);
+	int	(*sip_conn_timerd)(sip_conn_object_t);
+}sip_io_pointers_t;
+
+/* Upper layer registerations */
+typedef struct sip_ulp_pointers_s {
+	void		(*sip_ulp_recv)(const sip_conn_object_t,
+			    sip_msg_t, const sip_dialog_t);
+	uint_t		(*sip_ulp_timeout)(void *, void (*func)(void *),
+			    struct timeval *);
+	boolean_t	(*sip_ulp_untimeout)(uint_t);
+	int		(*sip_ulp_trans_error)(sip_transaction_t, int, void *);
+	void		(*sip_ulp_dlg_del)(sip_dialog_t, sip_msg_t, void *);
+	void		(*sip_ulp_trans_state_cb)(sip_transaction_t,
+			    sip_msg_t, int, int);
+	void		(*sip_ulp_dlg_state_cb)(sip_dialog_t, sip_msg_t, int,
+			    int);
+}sip_ulp_pointers_t;
+
+/* SIP stack initialization structure */
+typedef struct sip_stack_init_s {
+	int			sip_version;
+	int			sip_stack_flags;
+	sip_io_pointers_t	*sip_io_pointers;
+	sip_ulp_pointers_t	*sip_ulp_pointers;
+	sip_header_function_t	*sip_function_table;
+}sip_stack_init_t;
+
+/* SIP stack version */
+#define	SIP_STACK_VERSION		1
+
+/* Flags for sip_stack_flags */
+#define	SIP_STACK_DIALOGS		0x0001
+
+extern int		sip_init_conn_object(sip_conn_object_t);
+extern void		sip_clear_stale_data(sip_conn_object_t);
+extern void		sip_conn_destroyed(sip_conn_object_t);
+
+extern int		(*sip_stack_send)(const sip_conn_object_t, char *, int);
+extern void		(*sip_refhold_conn)(sip_conn_object_t);
+extern void		(*sip_refrele_conn)(sip_conn_object_t);
+extern boolean_t	(*sip_is_conn_stream)(sip_conn_object_t);
+extern boolean_t	(*sip_is_conn_reliable)(sip_conn_object_t);
+extern int 		(*sip_conn_rem_addr)(sip_conn_object_t,
+			    struct sockaddr *, socklen_t *);
+extern int		(*sip_conn_local_addr)(sip_conn_object_t,
+			    struct sockaddr *, socklen_t *);
+extern int		(*sip_conn_transport)(sip_conn_object_t);
+extern int		(*sip_conn_timer1)(sip_conn_object_t);
+extern int		(*sip_conn_timer2)(sip_conn_object_t);
+extern int		(*sip_conn_timer4)(sip_conn_object_t);
+extern int		(*sip_conn_timerd)(sip_conn_object_t);
+
+extern uint_t		(*sip_stack_timeout)(void *, void (*func)(void *),
+			    struct timeval *);
+extern boolean_t	(*sip_stack_untimeout)(uint_t);
+
+extern sip_msg_t	sip_new_msg();
+extern void		sip_free_msg(sip_msg_t);
+extern void		sip_hold_msg(sip_msg_t);
+extern int		sip_stack_init(sip_stack_init_t *);
+extern int		sip_sendmsg(sip_conn_object_t, sip_msg_t, sip_dialog_t,
+			    uint32_t);
+extern void		sip_process_new_packet(sip_conn_object_t, void *,
+			    size_t);
+extern char 		*sip_guid();
+extern char		*sip_sent_by_to_str(int *);
+extern int		sip_register_sent_by(char *);
+extern void		sip_unregister_sent_by(char *);
+extern void		sip_unregister_all_sent_by();
+extern char 		*sip_branchid(sip_msg_t);
+extern uint32_t		sip_get_cseq();
+extern uint32_t		sip_get_rseq();
+extern int		sip_get_num_via(sip_msg_t, int *);
+
+extern int 		sip_add_from(sip_msg_t, char *, char *, char *,
+			    boolean_t, char *);
+extern int 		sip_add_to(sip_msg_t, char *, char *, char *,
+			    boolean_t, char *);
+extern int 		sip_add_response_line(sip_msg_t, int, char *);
+extern int 		sip_add_request_line(sip_msg_t, sip_method_t, char *);
+extern int 		sip_add_via(sip_msg_t, char *, char *, int, char *);
+extern int 		sip_add_maxforward(sip_msg_t, uint_t);
+extern int 		sip_add_callid(sip_msg_t, char *);
+extern int 		sip_add_cseq(sip_msg_t, sip_method_t, uint32_t);
+extern int 		sip_add_content_type(sip_msg_t, char *, char *);
+extern int 		sip_add_content(sip_msg_t, char *);
+extern int 		sip_add_contact(sip_msg_t, char *, char *, boolean_t,
+			    char *);
+extern int 		sip_add_route(sip_msg_t, char *, char *, char *);
+extern int 		sip_add_record_route(sip_msg_t, char *, char *, char *);
+extern int 		sip_add_branchid_to_via(sip_msg_t, char *);
+extern int 		sip_add_accept(sip_msg_t, char *, char *, char *,
+			    char *);
+extern int		sip_add_author(sip_msg_t, char *,  char *);
+extern int		sip_add_authen_info(sip_msg_t, char *);
+extern int		sip_add_proxy_authen(sip_msg_t, char *,  char *);
+extern int		sip_add_proxy_author(sip_msg_t, char *, char *);
+extern int		sip_add_proxy_require(sip_msg_t, char *);
+extern int		sip_add_www_authen(sip_msg_t, char *, char *);
+extern int		sip_add_accept_enc(sip_msg_t, char *, char *);
+extern int		sip_add_accept_lang(sip_msg_t, char *, char *);
+extern int		sip_add_alert_info(sip_msg_t, char *, char *);
+extern int		sip_add_allow(sip_msg_t, sip_method_t);
+extern int		sip_add_call_info(sip_msg_t, char *, char *);
+extern int		sip_add_content_disp(sip_msg_t, char *, char *);
+extern int		sip_add_content_enc(sip_msg_t, char *);
+extern int		sip_add_content_lang(sip_msg_t, char *);
+extern int		sip_add_date(sip_msg_t, char *);
+extern int		sip_add_error_info(sip_msg_t, char *, char *);
+extern int		sip_add_expires(sip_msg_t, int);
+extern int		sip_add_in_reply_to(sip_msg_t, char *);
+extern int		sip_add_mime_version(sip_msg_t, char *);
+extern int		sip_add_min_expires(sip_msg_t, int);
+extern int		sip_add_org(sip_msg_t, char *);
+extern int		sip_add_priority(sip_msg_t, char *);
+extern int		sip_add_reply_to(sip_msg_t, char *, char *, char *,
+			    boolean_t);
+extern int		sip_add_require(sip_msg_t, char *);
+extern int		sip_add_retry_after(sip_msg_t, int, char *, char *);
+extern int		sip_add_server(sip_msg_t, char *);
+extern int		sip_add_subject(sip_msg_t, char *);
+extern int		sip_add_supported(sip_msg_t, char *);
+extern int		sip_add_tstamp(sip_msg_t, char *, char *);
+extern int		sip_add_unsupported(sip_msg_t, char *);
+extern int		sip_add_user_agent(sip_msg_t, char *);
+extern int		sip_add_warning(sip_msg_t, int, char *, char *);
+extern int		sip_add_allow_events(sip_msg_t, char *);
+extern int		sip_add_event(sip_msg_t, char *, char *);
+extern int		sip_add_substate(sip_msg_t, char *, char *);
+extern int		sip_add_privacy(sip_msg_t, char *);
+extern int		sip_add_passertedid(sip_msg_t, char *, char *,
+			    boolean_t);
+extern int		sip_add_ppreferredid(sip_msg_t, char *, char *,
+			    boolean_t);
+extern int		sip_add_rack(sip_msg_t, int, int, sip_method_t);
+extern int		sip_add_rseq(sip_msg_t, int);
+extern const sip_str_t *sip_get_author_scheme(sip_msg_t, int *);
+extern const sip_str_t *sip_get_author_param(sip_msg_t, char *, int *);
+extern const sip_str_t *sip_get_authen_info(sip_header_value_t, int *);
+extern const sip_str_t *sip_get_proxy_authen_scheme(sip_msg_t, int *);
+extern const sip_str_t *sip_get_proxy_authen_param(sip_msg_t, char *, int *);
+extern const sip_str_t *sip_get_proxy_author_scheme(sip_msg_t, int *);
+extern const sip_str_t *sip_get_proxy_author_param(sip_msg_t, char *, int *);
+extern const sip_str_t *sip_get_proxy_require(sip_header_value_t, int *);
+extern const sip_str_t *sip_get_www_authen_scheme(sip_msg_t, int *);
+extern const sip_str_t *sip_get_www_authen_param(sip_msg_t, char *, int *);
+extern const sip_str_t	*sip_get_allow_events(sip_header_value_t, int *);
+extern const sip_str_t	*sip_get_event(sip_msg_t, int *);
+extern const sip_str_t	*sip_get_substate(sip_msg_t, int *);
+extern const sip_str_t	*sip_get_accept_type(sip_header_value_t, int *);
+extern const sip_str_t	*sip_get_accept_sub_type(sip_header_value_t, int *);
+extern const sip_str_t	*sip_get_accept_enc(sip_header_value_t, int *);
+extern const sip_str_t	*sip_get_accept_lang(sip_header_value_t, int *);
+extern const sip_str_t	*sip_get_alert_info_uri(sip_header_value_t, int *);
+extern sip_method_t	sip_get_allow_method(sip_header_value_t, int *);
+extern int		sip_get_min_expires(sip_msg_t, int *);
+extern const sip_str_t	*sip_get_mime_version(sip_msg_t, int *);
+extern const sip_str_t	*sip_get_org(sip_msg_t, int *);
+extern const sip_str_t	*sip_get_priority(sip_msg_t, int *);
+extern const sip_str_t	*sip_get_replyto_display_name(sip_msg_t, int *);
+extern const sip_str_t	*sip_get_replyto_uri_str(sip_msg_t, int *);
+extern const sip_str_t	*sip_get_date_time(sip_msg_t, int *);
+extern int		sip_get_date_day(sip_msg_t, int *);
+extern const sip_str_t	*sip_get_date_month(sip_msg_t, int *);
+extern const sip_str_t	*sip_get_date_wkday(sip_msg_t, int *);
+extern int		sip_get_date_year(sip_msg_t, int *);
+extern const sip_str_t	*sip_get_date_timezone(sip_msg_t, int *);
+extern const sip_str_t	*sip_get_content_disp(sip_msg_t, int *);
+extern const sip_str_t	*sip_get_content_lang(sip_header_value_t, int *);
+extern const sip_str_t	*sip_get_content_enc(sip_header_value_t, int *);
+extern const sip_str_t	*sip_get_error_info_uri(sip_header_value_t, int *);
+extern int		sip_get_expires(sip_msg_t, int *);
+extern const sip_str_t	*sip_get_require(sip_header_value_t, int *);
+extern const sip_str_t	*sip_get_subject(sip_msg_t, int *);
+extern const sip_str_t	*sip_get_supported(sip_header_value_t, int *);
+extern const sip_str_t	*sip_get_tstamp_delay(sip_msg_t, int *);
+extern const sip_str_t	*sip_get_tstamp_value(sip_msg_t, int *);
+extern const sip_str_t	*sip_get_unsupported(sip_header_value_t, int *);
+extern const sip_str_t	*sip_get_server(sip_msg_t, int *);
+extern const sip_str_t	*sip_get_user_agent(sip_msg_t, int *);
+extern int		sip_get_warning_code(sip_header_value_t, int *);
+extern const sip_str_t	*sip_get_warning_agent(sip_header_value_t, int *);
+extern const sip_str_t	*sip_get_warning_text(sip_header_value_t, int *);
+extern const sip_str_t	*sip_get_call_info_uri(sip_header_value_t, int *);
+extern const sip_str_t	*sip_get_in_reply_to(sip_header_value_t, int *);
+extern int		sip_get_retry_after_time(sip_msg_t, int *);
+extern const sip_str_t	*sip_get_retry_after_cmts(sip_msg_t, int *);
+extern const sip_str_t	*sip_get_passertedid_display_name(sip_header_value_t,
+			    int *);
+extern const sip_str_t	*sip_get_passertedid_uri_str(sip_header_value_t,
+			    int *);
+extern const sip_str_t	*sip_get_ppreferredid_display_name(sip_header_value_t,
+			    int *);
+extern const sip_str_t	*sip_get_ppreferredid_uri_str(sip_header_value_t,
+			    int *);
+extern const sip_str_t	*sip_get_priv_value(sip_header_value_t, int *);
+extern int		sip_get_rack_resp_num(sip_msg_t, int *);
+extern int		sip_get_rack_cseq_num(sip_msg_t, int *);
+extern sip_method_t	sip_get_rack_method(sip_msg_t, int *);
+extern int		sip_get_rseq_resp_num(sip_msg_t, int *);
+
+extern int		sip_copy_start_line(sip_msg_t, sip_msg_t);
+extern int		sip_delete_start_line(sip_msg_t sip_msg);
+extern int		sip_copy_header(sip_msg_t, sip_header_t, char *);
+extern int		sip_copy_header_by_name(sip_msg_t, sip_msg_t, char *,
+			    char *);
+extern int		sip_copy_all_headers(sip_msg_t, sip_msg_t);
+extern int		sip_delete_header_by_name(sip_msg_t, char *);
+extern int		sip_add_header(sip_msg_t, char *);
+extern sip_header_t	sip_add_param(sip_header_t, char *, int *);
+extern int		sip_delete_header(sip_header_t);
+extern int		sip_delete_value(sip_header_t, sip_header_value_t);
+extern sip_msg_t	sip_clone_msg(const sip_msg_t);
+extern sip_msg_t	sip_create_response(const sip_msg_t, int, char *,
+			    char *, char *);
+extern int		sip_create_OKack(const sip_msg_t, sip_msg_t, char *,
+			    char *, int, char *);
+extern char 		*sip_get_resp_desc(int);
+extern char		*sip_get_branchid(const sip_msg_t, int *);
+
+extern const struct sip_header	*sip_get_header(sip_msg_t, char *, sip_header_t,
+				    int *);
+extern const struct sip_value	*sip_get_header_value(
+				    const struct sip_header *, int *);
+extern const struct sip_value	*sip_get_next_value(sip_header_value_t, int *);
+extern const sip_str_t		*sip_get_param_value(sip_header_value_t,
+				    char *, int *);
+extern const sip_param_t	*sip_get_params(sip_header_value_t, int *);
+extern boolean_t		sip_is_param_present(const sip_param_t *,
+				    char *, int);
+
+extern char		*sip_msg_to_str(sip_msg_t, int *);
+extern char		*sip_hdr_to_str(sip_header_t, int *);
+extern char		*sip_reqline_to_str(sip_msg_t, int *);
+extern char		*sip_respline_to_str(sip_msg_t, int *);
+extern boolean_t	sip_msg_is_request(const sip_msg_t, int *);
+extern boolean_t	sip_msg_is_response(const sip_msg_t, int *);
+extern sip_method_t	sip_get_request_method(const sip_msg_t, int *);
+extern const sip_str_t	*sip_get_request_uri_str(sip_msg_t, int *);
+extern int		sip_get_response_code(sip_msg_t, int *);
+extern const sip_str_t	*sip_get_response_phrase(sip_msg_t, int *);
+extern const sip_str_t	*sip_get_sip_version(sip_msg_t, int *);
+extern int		sip_get_msg_len(sip_msg_t, int *);
+extern const sip_str_t	*sip_get_route_uri_str(sip_header_value_t, int *);
+extern const sip_str_t	*sip_get_route_display_name(sip_header_value_t, int *);
+extern const sip_str_t	*sip_get_contact_uri_str(sip_header_value_t, int *);
+extern const sip_str_t	*sip_get_contact_display_name(sip_header_value_t,
+			    int *);
+extern const sip_str_t	*sip_get_from_uri_str(sip_msg_t, int *);
+extern const sip_str_t	*sip_get_from_display_name(sip_msg_t, int *);
+extern const sip_str_t	*sip_get_from_tag(sip_msg_t, int *);
+extern const sip_str_t	*sip_get_to_uri_str(sip_msg_t, int *);
+extern const sip_str_t	*sip_get_to_display_name(sip_msg_t, int *);
+extern const sip_str_t	*sip_get_to_tag(sip_msg_t, int *);
+extern const sip_str_t	*sip_get_callid(sip_msg_t, int *);
+extern int		sip_get_callseq_num(sip_msg_t, int *);
+extern sip_method_t	sip_get_callseq_method(sip_msg_t, int *);
+extern const sip_str_t	*sip_get_via_sent_by_host(sip_header_value_t, int *);
+extern int		sip_get_via_sent_by_port(sip_header_value_t, int *);
+extern const sip_str_t	*sip_get_via_sent_protocol_version(sip_header_value_t,
+			    int *);
+extern const sip_str_t	*sip_get_via_sent_protocol_name(sip_header_value_t,
+			    int *);
+extern const sip_str_t	*sip_get_via_sent_transport(sip_header_value_t,
+			    int *);
+extern int 		sip_get_maxforward(sip_msg_t, int *);
+extern int 		sip_get_content_length(sip_msg_t, int *);
+extern const sip_str_t	*sip_get_content_type(sip_msg_t, int *);
+extern const sip_str_t	*sip_get_content_sub_type(sip_msg_t, int *);
+extern char		*sip_get_content(sip_msg_t, int *);
+extern sip_msg_t	sip_create_dialog_req(sip_method_t, sip_dialog_t,
+			    char *, char *, int, char *, uint32_t, int);
+
+extern int			sip_get_dialog_state(sip_dialog_t, int *);
+extern int			sip_get_dialog_method(sip_dialog_t, int *);
+extern const sip_str_t		*sip_get_dialog_callid(sip_dialog_t, int *);
+extern const sip_str_t		*sip_get_dialog_local_tag(sip_dialog_t, int *);
+extern const sip_str_t		*sip_get_dialog_remote_tag(sip_dialog_t, int *);
+extern const struct sip_uri	*sip_get_dialog_local_uri(sip_dialog_t, int *);
+extern const struct sip_uri	*sip_get_dialog_remote_uri(sip_dialog_t, int *);
+extern const struct sip_uri	*sip_get_dialog_remote_target_uri(sip_dialog_t,
+				    int *);
+extern const sip_str_t		*sip_get_dialog_route_set(sip_dialog_t, int *);
+extern boolean_t		sip_is_dialog_secure(sip_dialog_t, int *);
+extern uint32_t			sip_get_dialog_local_cseq(sip_dialog_t, int *);
+extern uint32_t			sip_get_dialog_remote_cseq(sip_dialog_t, int *);
+extern int			sip_get_dialog_type(sip_dialog_t dialog, int *);
+
+extern void			sip_hold_dialog(sip_dialog_t);
+extern void			sip_release_dialog(sip_dialog_t);
+extern void			sip_delete_dialog(sip_dialog_t);
+
+extern sip_uri_t		sip_parse_uri(sip_str_t *, int *);
+extern void			sip_free_parsed_uri(sip_uri_t);
+extern boolean_t		sip_is_sipuri(const struct sip_uri *);
+extern const sip_str_t		*sip_get_uri_scheme(const struct sip_uri *,
+				    int *);
+extern const sip_str_t		*sip_get_uri_user(const struct sip_uri *,
+				    int *);
+extern const sip_str_t		*sip_get_uri_password(const struct sip_uri *,
+				    int *);
+extern const sip_str_t		*sip_get_uri_host(const struct sip_uri *,
+				    int *);
+extern int			sip_get_uri_port(const struct sip_uri *,
+				    int *error);
+extern const sip_param_t	*sip_get_uri_params(const struct sip_uri *,
+				    int *);
+extern const sip_str_t		*sip_get_uri_headers(const struct sip_uri *,
+				    int *);
+extern const sip_str_t		*sip_get_uri_opaque(const struct sip_uri *,
+				    int *);
+extern const sip_str_t		*sip_get_uri_query(const struct sip_uri *,
+				    int *);
+extern const sip_str_t		*sip_get_uri_path(const struct sip_uri *,
+				    int *);
+extern const sip_str_t		*sip_get_uri_regname(const struct sip_uri *,
+				    int *);
+extern boolean_t		sip_is_uri_teluser(const struct sip_uri *);
+extern int			sip_get_uri_errflags(const struct sip_uri *,
+				    int *);
+extern char			*sip_uri_errflags_to_str(int);
+
+extern const struct sip_uri	*sip_get_request_uri(sip_msg_t, int *);
+extern const struct sip_uri	*sip_get_uri_parsed(sip_header_value_t, int *);
+
+/* Transaction functions */
+extern const struct sip_xaction	*sip_get_trans(sip_msg_t, int, int *);
+extern char 			*sip_get_trans_branchid(sip_transaction_t,
+				    int *);
+extern sip_method_t		sip_get_trans_method(sip_transaction_t,
+				    int *);
+extern int			sip_get_trans_state(sip_transaction_t, int *);
+extern const struct sip_message	*sip_get_trans_resp_msg(sip_transaction_t,
+				    int *);
+extern const struct sip_message	*sip_get_trans_orig_msg(sip_transaction_t,
+				    int *);
+extern void			sip_hold_trans(sip_transaction_t);
+extern void			sip_release_trans(sip_transaction_t);
+extern const struct sip_conn_object	*sip_get_trans_conn_obj(
+					    sip_transaction_t, int *);
+#ifdef	__cplusplus
+}
+#endif
+
+#endif	/* _SIP_H */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/lib/libsip/common/sip_add_hdrs.c	Sat Oct 07 14:26:26 2006 -0700
@@ -0,0 +1,1594 @@
+/*
+ * 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"
+
+/*
+ * Returns number of digits in the given int
+ */
+static int
+sip_num_of_digits(int num)
+{
+	int	num_of_bytes = 0;
+
+	do {
+		num_of_bytes += 1;
+		num = num / 10;
+	} while (num > 0);
+	return (num_of_bytes);
+}
+
+/*
+ * Return the int as a string
+ */
+static char *
+sip_int_to_str(int i)
+{
+	int	count;
+	int	t;
+	int	x;
+	char	*str;
+
+	if (i < 0)
+		return (NULL);
+	/*
+	 * the following two loops convert int i to str
+	 */
+	count = 1;
+	t = i;
+	while ((t = t / 10) != 0) {
+		count++;
+	}
+
+	str = calloc(1, sizeof (char) * count + 1);
+	if (str == NULL)
+		return (NULL);
+	t = i;
+	for (x = 0; x < count; x++) {
+		int a;
+		a = t % 10;
+		str[count - 1 - x] = a + '0';
+		t = t / 10;
+	}
+	str[count] = '\0';
+	return (str);
+}
+
+/*
+ * Add quotes to the give str and return the quoted string
+ */
+static char *
+sip_add_aquot_to_str(char *str, boolean_t *alloc)
+{
+	char 		*new_str;
+	char 		*tmp = str;
+	int		size;
+
+	while (isspace(*tmp))
+		tmp++;
+
+	*alloc = B_FALSE;
+	if (*tmp != SIP_LAQUOT) {
+		size = strlen(str) + 2 * sizeof (char);
+		new_str = calloc(1, size + 1);
+		if (new_str == NULL)
+			return (NULL);
+		new_str[0] = SIP_LAQUOT;
+		new_str[1] = '\0';
+		(void) strncat(new_str, str, strlen(str));
+		(void) strncat(new_str, ">", 1);
+		new_str[size] = '\0';
+		*alloc = B_TRUE;
+		return (new_str);
+	}
+
+	return (str);
+}
+
+/*
+ * Add an empty header
+ */
+static int
+sip_add_empty_hdr(sip_msg_t sip_msg, char *hdr_name)
+{
+	_sip_header_t	*new_header;
+	int 		header_size;
+	_sip_msg_t 	*_sip_msg;
+	int		csize = sizeof (char);
+
+	if (sip_msg == NULL || hdr_name == NULL)
+		return (EINVAL);
+	_sip_msg = (_sip_msg_t *)sip_msg;
+	(void) pthread_mutex_lock(&_sip_msg->sip_msg_mutex);
+	if (_sip_msg->sip_msg_cannot_be_modified) {
+		(void) pthread_mutex_unlock(&_sip_msg->sip_msg_mutex);
+		return (ENOTSUP);
+	}
+
+	header_size = strlen(hdr_name) + SIP_SPACE_LEN + csize;
+
+	new_header = sip_new_header(header_size);
+	if (new_header == NULL) {
+		(void) pthread_mutex_unlock(&_sip_msg->sip_msg_mutex);
+		return (ENOMEM);
+	}
+
+	(void) snprintf(new_header->sip_hdr_start, header_size + 1,
+	    "%s %c",  hdr_name, SIP_HCOLON);
+
+	_sip_add_header(_sip_msg, new_header, B_TRUE, B_FALSE, hdr_name);
+	if (_sip_msg->sip_msg_buf != NULL)
+		_sip_msg->sip_msg_modified = B_TRUE;
+	(void) pthread_mutex_unlock(&_sip_msg->sip_msg_mutex);
+
+	return (0);
+}
+
+/*
+ * Generic function to add a header with two strings to message
+ */
+static int
+sip_add_2strs_to_msg(sip_msg_t sip_msg, char *hdr_name, char *str1,
+    boolean_t qstr1, char *str2, char *plist, char sep)
+{
+	_sip_header_t	*new_header;
+	int 		header_size;
+	_sip_msg_t 	*_sip_msg;
+	int		csize = sizeof (char);
+
+	if (sip_msg == NULL || str1 == NULL || str2 == NULL ||
+	    (str1 != NULL && str1[0] == '\0') ||
+	    (str2 != NULL && str2[0] == '\0')) {
+		return (EINVAL);
+	}
+	_sip_msg = (_sip_msg_t *)sip_msg;
+	(void) pthread_mutex_lock(&_sip_msg->sip_msg_mutex);
+	if (_sip_msg->sip_msg_cannot_be_modified) {
+		(void) pthread_mutex_unlock(&_sip_msg->sip_msg_mutex);
+		return (ENOTSUP);
+	}
+
+	if (plist == NULL) {
+		header_size = strlen(hdr_name) + SIP_SPACE_LEN + csize +
+		    SIP_SPACE_LEN + strlen(str1) + csize + strlen(str2) +
+		    strlen(SIP_CRLF);
+	} else {
+		header_size = strlen(hdr_name) + SIP_SPACE_LEN + csize +
+		    SIP_SPACE_LEN + strlen(str1) + csize + strlen(str2) +
+		    csize + strlen(plist) + strlen(SIP_CRLF);
+	}
+	if (qstr1)
+		header_size += 2 * sizeof (char);
+
+	new_header = sip_new_header(header_size);
+	if (new_header == NULL) {
+		(void) pthread_mutex_unlock(&_sip_msg->sip_msg_mutex);
+		return (ENOMEM);
+	}
+
+	if (plist == NULL) {
+		if (qstr1) {
+			(void) snprintf(new_header->sip_hdr_start,
+			    header_size + 1, "%s %c \"%s\"%c%s%s",
+			    hdr_name, SIP_HCOLON, str1, sep, str2, SIP_CRLF);
+		} else {
+			(void) snprintf(new_header->sip_hdr_start,
+			    header_size + 1, "%s %c %s%c%s%s",
+			    hdr_name, SIP_HCOLON, str1, sep, str2, SIP_CRLF);
+		}
+	} else {
+		if (qstr1) {
+			(void) snprintf(new_header->sip_hdr_start,
+			    header_size + 1,
+			    "%s %c \"%s\"%c%s%c%s%s", hdr_name, SIP_HCOLON,
+			    str1, sep, str2, SIP_SEMI, plist, SIP_CRLF);
+		} else {
+			(void) snprintf(new_header->sip_hdr_start,
+			    header_size + 1, "%s %c %s%c%s%c%s%s",
+			    hdr_name, SIP_HCOLON, str1, sep, str2, SIP_SEMI,
+			    plist, SIP_CRLF);
+		}
+	}
+	_sip_add_header(_sip_msg, new_header, B_TRUE, B_FALSE, NULL);
+	if (_sip_msg->sip_msg_buf != NULL)
+		_sip_msg->sip_msg_modified = B_TRUE;
+	(void) pthread_mutex_unlock(&_sip_msg->sip_msg_mutex);
+
+	return (0);
+}
+
+/*
+ * Generic function to add a header with a string to message
+ */
+static int
+sip_add_str_to_msg(sip_msg_t sip_msg, char *hdr_name, char *str, char *plist,
+    char param_sep)
+{
+	_sip_header_t	*new_header;
+	int 		header_size;
+	_sip_msg_t 	*_sip_msg;
+	int		csize = sizeof (char);
+
+	if (sip_msg == NULL || str == NULL || (str != NULL && str[0] == '\0'))
+		return (EINVAL);
+	_sip_msg = (_sip_msg_t *)sip_msg;
+	(void) pthread_mutex_lock(&_sip_msg->sip_msg_mutex);
+	if (_sip_msg->sip_msg_cannot_be_modified) {
+		(void) pthread_mutex_unlock(&_sip_msg->sip_msg_mutex);
+		return (ENOTSUP);
+	}
+
+	if (plist == NULL) {
+		header_size = strlen(hdr_name) + SIP_SPACE_LEN + csize +
+		    SIP_SPACE_LEN + + strlen(str) + strlen(SIP_CRLF);
+	} else {
+		header_size = strlen(hdr_name) + SIP_SPACE_LEN + csize +
+		    SIP_SPACE_LEN + + strlen(str) + csize + strlen(plist) +
+		    strlen(SIP_CRLF);
+	}
+
+	new_header = sip_new_header(header_size);
+	if (new_header == NULL) {
+		(void) pthread_mutex_unlock(&_sip_msg->sip_msg_mutex);
+		return (ENOMEM);
+	}
+	if (plist == NULL) {
+		(void) snprintf(new_header->sip_hdr_start, header_size + 1,
+		    "%s %c %s%s", hdr_name, SIP_HCOLON, str, SIP_CRLF);
+	} else {
+		(void) snprintf(new_header->sip_hdr_start, header_size + 1,
+		    "%s %c %s%c%s%s", hdr_name, SIP_HCOLON, str, param_sep,
+		    plist, SIP_CRLF);
+	}
+	_sip_add_header(_sip_msg, new_header, B_TRUE, B_FALSE, NULL);
+	if (_sip_msg->sip_msg_buf != NULL)
+		_sip_msg->sip_msg_modified = B_TRUE;
+	(void) pthread_mutex_unlock(&_sip_msg->sip_msg_mutex);
+
+	return (0);
+}
+
+/*
+ * Add an header with an int to sip_msg
+ */
+static int
+sip_add_int_to_msg(sip_msg_t sip_msg, char *hdr_name, int i, char *plist)
+{
+	_sip_header_t	*new_header;
+	int 		header_size;
+	_sip_msg_t 	*_sip_msg;
+	char		*digit_str;
+	int		csize = sizeof (char);
+
+	if (sip_msg == NULL || (hdr_name == NULL))
+		return (EINVAL);
+	_sip_msg = (_sip_msg_t *)sip_msg;
+	(void) pthread_mutex_lock(&_sip_msg->sip_msg_mutex);
+	if (_sip_msg->sip_msg_cannot_be_modified) {
+		(void) pthread_mutex_unlock(&_sip_msg->sip_msg_mutex);
+		return (ENOTSUP);
+	}
+
+	/*
+	 * the following two loops convert int i to str
+	 */
+	digit_str = sip_int_to_str(i);
+	if (digit_str == NULL)
+		return (EINVAL);
+
+	if (plist == NULL) {
+		header_size = strlen(hdr_name) + SIP_SPACE_LEN + csize +
+		    SIP_SPACE_LEN + strlen(digit_str) + strlen(SIP_CRLF);
+	} else {
+		header_size = strlen(hdr_name) + SIP_SPACE_LEN + csize +
+		    SIP_SPACE_LEN + strlen(digit_str) + csize +
+		    strlen(plist) + strlen(SIP_CRLF);
+	}
+
+	new_header = sip_new_header(header_size);
+	if (new_header == NULL) {
+		(void) pthread_mutex_unlock(&_sip_msg->sip_msg_mutex);
+		free(digit_str);
+		return (ENOMEM);
+	}
+
+	if (plist == NULL) {
+		(void) snprintf(new_header->sip_hdr_start, header_size + 1,
+		    "%s %c %s%s", hdr_name, SIP_HCOLON, digit_str, SIP_CRLF);
+	} else {
+		(void) snprintf(new_header->sip_hdr_start, header_size + 1,
+		    "%s %c %s%c%s%s", hdr_name, SIP_HCOLON, digit_str,
+		    SIP_SEMI, plist, SIP_CRLF);
+	}
+	free(digit_str);
+	_sip_add_header(_sip_msg, new_header, B_TRUE, B_FALSE, NULL);
+	if (_sip_msg->sip_msg_buf != NULL)
+		_sip_msg->sip_msg_modified = B_TRUE;
+	(void) pthread_mutex_unlock(&_sip_msg->sip_msg_mutex);
+
+	return (0);
+}
+
+/*
+ * Add a header with an int and string to sip_msg
+ */
+static int
+sip_add_intstr_to_msg(sip_msg_t sip_msg, char *hdr_name, int i, char *s,
+    char *plist)
+{
+	_sip_header_t	*new_header;
+	int 		header_size;
+	_sip_msg_t 	*_sip_msg;
+	char		*digit_str;
+	int		csize = sizeof (char);
+
+	if (sip_msg == NULL || (hdr_name == NULL))
+		return (EINVAL);
+	_sip_msg = (_sip_msg_t *)sip_msg;
+	(void) pthread_mutex_lock(&_sip_msg->sip_msg_mutex);
+	if (_sip_msg->sip_msg_cannot_be_modified) {
+		(void) pthread_mutex_unlock(&_sip_msg->sip_msg_mutex);
+		return (ENOTSUP);
+	}
+
+	/*
+	 * the following two loops convert int i to str
+	 */
+	digit_str = sip_int_to_str(i);
+	if (digit_str == NULL) {
+		(void) pthread_mutex_unlock(&_sip_msg->sip_msg_mutex);
+		return (EINVAL);
+	}
+	if (plist == NULL) {
+		header_size = strlen(hdr_name) + SIP_SPACE_LEN + csize +
+		    SIP_SPACE_LEN + strlen(digit_str) + csize + strlen(s) +
+		    strlen(SIP_CRLF);
+	} else {
+		header_size = strlen(hdr_name) + SIP_SPACE_LEN + csize +
+		    SIP_SPACE_LEN + strlen(digit_str) + csize + strlen(s) +
+		    csize + strlen(plist) + strlen(SIP_CRLF);
+	}
+
+	new_header = sip_new_header(header_size);
+	if (new_header == NULL) {
+		(void) pthread_mutex_unlock(&_sip_msg->sip_msg_mutex);
+		free(digit_str);
+		return (ENOMEM);
+	}
+
+	if (plist == NULL) {
+		(void) snprintf(new_header->sip_hdr_start, header_size + 1,
+		    "%s %c %s %s%s", hdr_name, SIP_HCOLON, digit_str, s,
+		    SIP_CRLF);
+	} else {
+		(void) snprintf(new_header->sip_hdr_start, header_size + 1,
+		    "%s %c %s %s%c%s%s", hdr_name, SIP_HCOLON, digit_str,
+		    s, SIP_SEMI, plist, SIP_CRLF);
+	}
+	free(digit_str);
+	_sip_add_header(_sip_msg, new_header, B_TRUE, B_FALSE, NULL);
+	if (_sip_msg->sip_msg_buf != NULL)
+		_sip_msg->sip_msg_modified = B_TRUE;
+	(void) pthread_mutex_unlock(&_sip_msg->sip_msg_mutex);
+
+	return (0);
+}
+
+/*
+ * Generic function to add Contact, From,  To, Route or Record-Route header
+ */
+static int
+sip_add_name_aspec(sip_msg_t sip_msg, char *display_name, char *uri,
+    char *tags, boolean_t add_aquot, char *header_name, char *params)
+{
+	char		*t = uri;
+	boolean_t	qalloc = B_FALSE;
+	boolean_t	palloc = B_FALSE;
+	int		r;
+
+	if (sip_msg == NULL || uri == NULL || header_name == NULL)
+		return (EINVAL);
+	if (display_name != NULL && !add_aquot)
+		return (EINVAL);
+	if (add_aquot) {
+		t = sip_add_aquot_to_str(uri, &qalloc);
+		if (t == NULL)
+			return (ENOMEM);
+	}
+	if (tags != NULL) {
+		int	plen;
+
+		if (params != NULL)
+			return (EINVAL);
+
+		plen = strlen(SIP_TAG) + strlen(tags) + 1;
+		params = malloc(plen);
+		if (params == NULL)
+			return (ENOMEM);
+		(void) snprintf(params, plen, "%s%s", SIP_TAG, tags);
+		params[plen - 1] = '\0';
+		palloc = B_TRUE;
+	}
+	if (display_name == NULL) {
+		r = sip_add_2strs_to_msg(sip_msg, header_name, " ", B_FALSE,
+		    t, params, SIP_SP);
+	} else {
+		r = sip_add_2strs_to_msg(sip_msg, header_name, display_name,
+		    B_TRUE, t, params, SIP_SP);
+	}
+	if (qalloc)
+		free(t);
+	if (palloc)
+		free(params);
+	return (r);
+}
+
+/*
+ * Accept = "Accept" ":" (media-range [ accept-params ])
+ * media-range = ( "X/X" | (type "/" "*") | (type "/" subtype))*(";" parameter)
+ * accept-params = ";" "q" "=" qvalue *(accept-extension)
+ * accept-extension = ";" token [ "=" (token | quoted-str)
+ *
+ * function take two char ptrs - type and subtype - if any of them is NULL
+ * the corresponding value will be set to "*" in header
+ */
+int
+sip_add_accept(sip_msg_t sip_msg, char *type, char *subtype, char *m_par,
+    char *a_par)
+{
+	int		ret;
+	char		*plist;
+	int		size;
+	boolean_t	alloc = B_FALSE;
+
+	if (type == NULL && subtype == NULL) {
+		ret = sip_add_empty_hdr(sip_msg, SIP_ACCEPT);
+		return (ret);
+	}
+
+	if ((m_par != NULL) && (a_par != NULL)) {
+		size = strlen(m_par) + strlen(a_par) + 2 * sizeof (char);
+		plist = calloc(1, size * sizeof (char));
+		(void) strncpy(plist, m_par, strlen(m_par));
+		(void) strncat(plist, ";", 1);
+		(void) strncat(plist, a_par, strlen(a_par));
+		alloc = B_TRUE;
+	} else if (m_par != NULL) {
+		plist = m_par;
+	} else
+		plist = a_par;
+
+	if ((type != NULL) && (subtype != NULL)) {
+		ret = sip_add_2strs_to_msg(sip_msg, SIP_ACCEPT, type, B_FALSE,
+		    subtype, plist, SIP_SLASH);
+	} else if (type != NULL) {
+		ret = sip_add_2strs_to_msg(sip_msg, SIP_ACCEPT, type, B_FALSE,
+		    "*", plist, SIP_SLASH);
+	} else {
+		ret = EINVAL;
+	}
+
+	if (alloc == B_TRUE)
+		free(plist);
+
+	return (ret);
+}
+
+
+/*
+ * Accept-Encoding = "Accept-Encoding" ":" 1#(codings [ ";" "q" "=" qval])
+ * codings = ( content-coding | "*" )
+ * content-coding   =  token
+ *
+ * function take one char ptr, if NULL value will be set to "*"
+ */
+int
+sip_add_accept_enc(sip_msg_t sip_msg, char *code, char *plist)
+{
+	int ret;
+
+	if (code == NULL) {
+		ret = sip_add_str_to_msg(sip_msg, SIP_ACCEPT_ENCODE, "*", plist,
+		    SIP_SEMI);
+	} else {
+		ret = sip_add_str_to_msg(sip_msg, SIP_ACCEPT_ENCODE, code,
+		    plist, SIP_SEMI);
+	}
+	return (ret);
+}
+
+/*
+ * Accept-Language = "Accept-Language" ":" 1#( language-range [ ";" "q""=" val])
+ * language-range = ( ( 1*8ALPHA *("-" 1*8ALPHA))|"*")
+ */
+int
+sip_add_accept_lang(sip_msg_t sip_msg, char *lang, char *plist)
+{
+	int	ret;
+
+	if (lang == NULL) {
+		ret = sip_add_empty_hdr(sip_msg, SIP_ACCEPT_LANG);
+		return (ret);
+	}
+	ret = sip_add_str_to_msg(sip_msg, SIP_ACCEPT_LANG, lang, plist,
+	    SIP_SEMI);
+	return (ret);
+}
+
+/*
+ * Alert-Info = "Alert-Info" ":" "<" URI ">"
+ */
+int
+sip_add_alert_info(sip_msg_t sip_msg, char *alert, char *plist)
+{
+	int		ret;
+	char		*tmp;
+	boolean_t	alloc;
+
+	if (alert == NULL)
+		return (EINVAL);
+	tmp = sip_add_aquot_to_str(alert, &alloc);
+	if (tmp == NULL)
+		return (ENOMEM);
+	ret = sip_add_str_to_msg(sip_msg, SIP_ALERT_INFO, tmp, plist, SIP_SEMI);
+	if (alloc)
+		free(tmp);
+	return (ret);
+}
+
+/*
+ * Allow = "Allow" ":" method-name1[, method-name2..]
+ * method-name = "INVITE" | "ACK" | "OPTIONS" | "CANCEL" | "BYE"
+ */
+int
+sip_add_allow(sip_msg_t sip_msg, sip_method_t method)
+{
+	int	ret;
+
+	if (method == 0 || method >= MAX_SIP_METHODS)
+		return (EINVAL);
+	ret = sip_add_str_to_msg(sip_msg, SIP_ALLOW, sip_methods[method].name,
+	    NULL, (char)NULL);
+	return (ret);
+}
+
+/*
+ * 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_add_call_info(sip_msg_t sip_msg, char *uri, char *plist)
+{
+	char		*tmp;
+	boolean_t	alloc;
+	int		r;
+
+	if (uri == NULL)
+		return (EINVAL);
+	tmp = sip_add_aquot_to_str(uri, &alloc);
+	if (tmp == NULL)
+		return (ENOMEM);
+	r = sip_add_str_to_msg(sip_msg, SIP_CALL_INFO, tmp, plist, SIP_SEMI);
+	if (alloc)
+		free(tmp);
+	return (r);
+}
+
+/*
+ * Content-Disposition   =  "Content-Disposition" HCOLON
+ *				disp-type *( SEMI disp-param )
+ * disp-type             =  "render" / "session" / "icon" / "alert"
+ *				/ disp-extension-token
+ * disp-param            =  handling-param / generic-param
+ * handling-param        =  "handling" EQUAL
+ *				( "optional" / "required"
+ *				/ other-handling )
+ * other-handling        =  token
+ * disp-extension-token  =  token
+ */
+int
+sip_add_content_disp(sip_msg_t sip_msg, char *dis_type, char *plist)
+{
+	int	ret;
+
+	if (dis_type == NULL)
+		return (EINVAL);
+
+	ret = sip_add_str_to_msg(sip_msg, SIP_CONTENT_DIS, dis_type, plist,
+	    SIP_SEMI);
+	return (ret);
+}
+
+/*
+ * Content-Encoding  =  ( "Content-Encoding" / "e" ) HCOLON
+ *			content-coding *(COMMA content-coding)
+ * content-coding   =  token
+ */
+int
+sip_add_content_enc(sip_msg_t sip_msg, char *code)
+{
+	int	ret;
+
+	if (code == NULL)
+		return (EINVAL);
+
+	ret = sip_add_str_to_msg(sip_msg, SIP_CONTENT_ENCODE, code, NULL,
+	    (char)NULL);
+	return (ret);
+}
+
+/*
+ * Content-Language  =  "Content-Language" HCOLON
+ *			language-tag *(COMMA language-tag)
+ * language-tag      =  primary-tag *( "-" subtag )
+ * primary-tag       =  1*8ALPHA
+ * subtag            =  1*8ALPHA
+ */
+int
+sip_add_content_lang(sip_msg_t sip_msg, char *lang)
+{
+	int	ret;
+
+	if (lang == NULL)
+		return (EINVAL);
+	ret = sip_add_str_to_msg(sip_msg, SIP_CONTENT_LANG, lang, NULL,
+	    (char)NULL);
+	return (ret);
+}
+
+/*
+ * Date          =  "Date" HCOLON SIP-date
+ * SIP-date      =  rfc1123-date
+ * rfc1123-date  =  wkday "," SP date1 SP time SP "GMT"
+ * date1         =  2DIGIT SP month SP 4DIGIT
+ * 			; day month year (e.g., 02 Jun 1982)
+ * time          =  2DIGIT ":" 2DIGIT ":" 2DIGIT
+ *			; 00:00:00 - 23:59:59
+ * wkday         =  "Mon" / "Tue" / "Wed"
+ *			/ "Thu" / "Fri" / "Sat" / "Sun"
+ * month         =  "Jan" / "Feb" / "Mar" / "Apr"
+ *			/ "May" / "Jun" / "Jul" / "Aug"
+ *			/ "Sep" / "Oct" / "Nov" / "Dec"
+ */
+int
+sip_add_date(sip_msg_t sip_msg, char *date)
+{
+	int	ret;
+
+	if (date == NULL)
+		return (EINVAL);
+	ret = sip_add_str_to_msg(sip_msg, SIP_DATE, date, NULL, (char)NULL);
+	return (ret);
+}
+
+/*
+ * Error-Info  =  "Error-Info" HCOLON error-uri *(COMMA error-uri)
+ * error-uri   =  LAQUOT absoluteURI RAQUOT *( SEMI generic-param )
+ */
+int
+sip_add_error_info(sip_msg_t sip_msg, char *uri, char *plist)
+{
+	char		*tmp;
+	boolean_t	alloc;
+	int		r;
+
+	if (uri == NULL)
+		return (EINVAL);
+	tmp = sip_add_aquot_to_str(uri, &alloc);
+	if (tmp == NULL)
+		return (EINVAL);
+
+	r = sip_add_str_to_msg(sip_msg, SIP_ERROR_INFO, tmp, plist, SIP_SEMI);
+	if (alloc)
+		free(tmp);
+	return (r);
+}
+
+/*
+ * Expires     =  "Expires" HCOLON delta-seconds
+ * delta-seconds      =  1*DIGIT
+ */
+int
+sip_add_expires(sip_msg_t sip_msg, int secs)
+{
+	int	ret;
+
+	if (sip_msg == NULL || (int)secs < 0)
+		return (EINVAL);
+
+	ret = sip_add_int_to_msg(sip_msg, SIP_EXPIRE, secs, NULL);
+	return (ret);
+}
+
+/*
+ * In-Reply-To  =  "In-Reply-To" HCOLON callid *(COMMA callid)
+ * callid   =  word [ "@" word ]
+ */
+int
+sip_add_in_reply_to(sip_msg_t sip_msg, char *reply_id)
+{
+	int		r;
+
+	if (reply_id == NULL)
+		return (EINVAL);
+	r = sip_add_str_to_msg(sip_msg, SIP_IN_REPLY_TO, reply_id, NULL,
+	    (char)NULL);
+	return (r);
+}
+
+/*
+ * RSeq          =  "RSeq" HCOLON response-num
+ */
+int
+sip_add_rseq(sip_msg_t sip_msg, int resp_num)
+{
+	int	ret;
+
+	if (sip_msg == NULL || resp_num <= 0)
+		return (EINVAL);
+	ret = sip_add_int_to_msg(sip_msg, SIP_RSEQ, resp_num, NULL);
+	return (ret);
+}
+
+/*
+ * Min-Expires  =  "Min-Expires" HCOLON delta-seconds
+ */
+int
+sip_add_min_expires(sip_msg_t sip_msg, int secs)
+{
+	int	ret;
+
+	if (sip_msg == NULL || (int)secs < 0)
+		return (EINVAL);
+	ret = sip_add_int_to_msg(sip_msg, SIP_MIN_EXPIRE, secs, NULL);
+	return (ret);
+}
+
+/*
+ * MIME-Version  =  "MIME-Version" HCOLON 1*DIGIT "." 1*DIGIT
+ */
+int
+sip_add_mime_version(sip_msg_t sip_msg, char *version)
+{
+	int	ret;
+
+	if (version == NULL)
+		return (EINVAL);
+	ret = sip_add_str_to_msg(sip_msg, SIP_MIME_VERSION, version, NULL,
+	    (char)NULL);
+	return (ret);
+}
+
+/*
+ * Organization  =  "Organization" HCOLON [TEXT-UTF8-TRIM]
+ */
+int
+sip_add_org(sip_msg_t sip_msg, char *org)
+{
+	int	ret;
+
+	if (org == NULL) {
+		ret = sip_add_empty_hdr(sip_msg, SIP_ORGANIZATION);
+	} else {
+		ret = sip_add_str_to_msg(sip_msg, SIP_ORGANIZATION, org, NULL,
+		    (char)NULL);
+	}
+	return (ret);
+}
+
+/*
+ * Priority        =  "Priority" HCOLON priority-value
+ * priority-value  =  "emergency" / "urgent" / "normal"
+ *			/ "non-urgent" / other-priority
+ * other-priority  =  token
+ */
+int
+sip_add_priority(sip_msg_t sip_msg, char *prio)
+{
+	int	ret;
+
+	if (prio == NULL)
+		return (EINVAL);
+	ret = sip_add_str_to_msg(sip_msg, SIP_PRIORITY, prio, NULL, (char)NULL);
+
+	return (ret);
+}
+
+/*
+ * Reply-To      =  "Reply-To" HCOLON rplyto-spec
+ * rplyto-spec   =  ( name-addr / addr-spec )
+ *			*( SEMI rplyto-param )
+ * rplyto-param  =  generic-param
+ */
+int
+sip_add_reply_to(sip_msg_t sip_msg, char *uname, char *addr, char *plist,
+    boolean_t add_aquot)
+{
+	return (sip_add_name_aspec(sip_msg, uname, addr, NULL, add_aquot,
+	    SIP_REPLYTO, plist));
+}
+
+
+/*
+ * Privacy-hdr  =  "Privacy" HCOLON priv-value *(";" priv-value)
+ * priv-value   =   "header" / "session" / "user" / "none" / "critical"
+ *			/ token
+ */
+int
+sip_add_privacy(sip_msg_t sip_msg, char *priv_val)
+{
+	int	ret;
+
+	if (priv_val == NULL)
+		return (EINVAL);
+	ret = sip_add_str_to_msg(sip_msg, SIP_PRIVACY, priv_val, NULL,
+	    (char)NULL);
+	return (ret);
+}
+
+/*
+ * Require       =  "Require" HCOLON option-tag *(COMMA option-tag)
+ * option-tag     =  token
+ */
+int
+sip_add_require(sip_msg_t sip_msg, char *req)
+{
+	int	ret;
+
+	if (req == NULL)
+		return (EINVAL);
+	ret = sip_add_str_to_msg(sip_msg, SIP_REQUIRE, req, NULL, (char)NULL);
+	return (ret);
+}
+
+/*
+ * Retry-After  =  "Retry-After" HCOLON delta-seconds
+ *			[ comment ] *( SEMI retry-param )
+ * retry-param  =  ("duration" EQUAL delta-seconds)
+ *			/ generic-param
+ */
+int
+sip_add_retry_after(sip_msg_t sip_msg, int secs, char *cmt, char *plist)
+{
+	int	r;
+
+	if (secs <= 0)
+		return (EINVAL);
+
+	if (cmt == NULL) {
+		r = sip_add_int_to_msg(sip_msg, SIP_RETRY_AFTER, secs, plist);
+		return (r);
+	}
+
+	r = sip_add_intstr_to_msg(sip_msg, SIP_RETRY_AFTER, secs, cmt, plist);
+	return (r);
+}
+
+/*
+ * Server           =  "Server" HCOLON server-val *(LWS server-val)
+ * server-val       =  product / comment
+ * product          =  token [SLASH product-version]
+ * product-version  =  token
+ */
+int
+sip_add_server(sip_msg_t sip_msg, char *svr)
+{
+	int	ret;
+
+	if (svr == NULL)
+		return (EINVAL);
+	ret = sip_add_str_to_msg(sip_msg, SIP_SERVER, svr, NULL, (char)NULL);
+	return (ret);
+}
+
+/*
+ * Subject  =  ( "Subject" / "s" ) HCOLON [TEXT-UTF8-TRIM]
+ */
+int
+sip_add_subject(sip_msg_t sip_msg, char *subject)
+{
+	int	ret;
+
+	if (subject == NULL) {
+		ret = sip_add_empty_hdr(sip_msg, SIP_SUBJECT);
+	} else {
+		ret = sip_add_str_to_msg(sip_msg, SIP_SUBJECT, subject, NULL,
+		    (char)NULL);
+	}
+	return (ret);
+}
+
+/*
+ * Supported  =  ( "Supported" / "k" ) HCOLON
+ *		[option-tag *(COMMA option-tag)]
+ */
+int
+sip_add_supported(sip_msg_t sip_msg, char *support)
+{
+	int	ret;
+
+	if (support == NULL) {
+		ret = sip_add_empty_hdr(sip_msg, SIP_SUPPORT);
+	} else {
+		ret = sip_add_str_to_msg(sip_msg, SIP_SUPPORT, support, NULL,
+		    (char)NULL);
+	}
+	return (ret);
+}
+
+/*
+ * Timestamp  =  "Timestamp" HCOLON 1*(DIGIT)
+ *		[ "." *(DIGIT) ] [ LWS delay ]
+ * delay      =  *(DIGIT) [ "." *(DIGIT) ]
+ */
+int
+sip_add_tstamp(sip_msg_t sip_msg, char *time, char *delay)
+{
+	int	ret;
+
+	if (delay == NULL) {
+		ret = sip_add_str_to_msg(sip_msg, SIP_TIMESTAMP, time, NULL,
+		    (char)NULL);
+	} else {
+		ret = sip_add_2strs_to_msg(sip_msg, SIP_TIMESTAMP, time,
+		    B_FALSE, delay, NULL, ' ');
+	}
+	return (ret);
+}
+
+/*
+ * Unsupported  =  "Unsupported" HCOLON option-tag *(COMMA option-tag)
+ */
+int
+sip_add_unsupported(sip_msg_t sip_msg, char *unsupport)
+{
+	int	ret;
+
+	if (unsupport == NULL)
+		return (EINVAL);
+	ret = sip_add_str_to_msg(sip_msg, SIP_UNSUPPORT, unsupport, NULL,
+	    (char)NULL);
+	return (ret);
+}
+
+/*
+ * User-Agent  =  "User-Agent" HCOLON server-val *(LWS server-val)
+ */
+int
+sip_add_user_agent(sip_msg_t sip_msg, char *usr)
+{
+	int	r;
+
+	if (usr == NULL)
+		return (EINVAL);
+	r = sip_add_str_to_msg(sip_msg, SIP_USER_AGENT, usr, NULL, (char)NULL);
+	return (r);
+}
+
+/*
+ * 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_add_warning(sip_msg_t sip_msg, int code, char *addr, char *msg)
+{
+	_sip_header_t	*new_header;
+	int 		header_size;
+	_sip_msg_t 	*_sip_msg;
+	char		*hdr_name = SIP_WARNING;
+
+	if (sip_msg == NULL || addr == NULL || msg == NULL ||
+	    addr[0] == '\0' || msg == '\0' || code < 100 || code > 999) {
+		return (EINVAL);
+	}
+
+	_sip_msg = (_sip_msg_t *)sip_msg;
+	(void) pthread_mutex_lock(&_sip_msg->sip_msg_mutex);
+	if (_sip_msg->sip_msg_cannot_be_modified) {
+		(void) pthread_mutex_unlock(&_sip_msg->sip_msg_mutex);
+		return (ENOTSUP);
+	}
+
+	header_size = strlen(hdr_name) + SIP_SPACE_LEN + sizeof (char) +
+	    SIP_SPACE_LEN + sip_num_of_digits(code) + SIP_SPACE_LEN +
+	    strlen(addr) + SIP_SPACE_LEN + sizeof (char) + strlen(msg) +
+	    sizeof (char) + strlen(SIP_CRLF);
+
+	new_header = sip_new_header(header_size);
+	if (new_header == NULL) {
+		(void) pthread_mutex_unlock(&_sip_msg->sip_msg_mutex);
+		return (ENOMEM);
+	}
+
+	(void) snprintf(new_header->sip_hdr_start, header_size + 1,
+	    "%s %c %d %s \"%s\"%s", hdr_name, SIP_HCOLON, code, addr,
+	    msg, SIP_CRLF);
+	_sip_add_header(_sip_msg, new_header, B_TRUE, B_FALSE, NULL);
+	if (_sip_msg->sip_msg_buf != NULL)
+		_sip_msg->sip_msg_modified = B_TRUE;
+	(void) pthread_mutex_unlock(&_sip_msg->sip_msg_mutex);
+
+	return (0);
+}
+
+/*
+ * RAck          =  "RAck" HCOLON response-num LWS CSeq-num LWS Method
+ * response-num  =  1*DIGIT
+ * CSeq-num      =  1*DIGIT
+ */
+int
+sip_add_rack(sip_msg_t sip_msg, int resp_num, int cseq, sip_method_t method)
+{
+	_sip_header_t	*new_header;
+	int 		header_size;
+	_sip_msg_t 	*_sip_msg;
+	char		*hdr_name = SIP_RACK;
+
+	if (sip_msg == NULL || resp_num <= 0 || cseq < 0 || method <= 0 ||
+	    method >= MAX_SIP_METHODS) {
+		return (EINVAL);
+	}
+
+	_sip_msg = (_sip_msg_t *)sip_msg;
+	(void) pthread_mutex_lock(&_sip_msg->sip_msg_mutex);
+	if (_sip_msg->sip_msg_cannot_be_modified) {
+		(void) pthread_mutex_unlock(&_sip_msg->sip_msg_mutex);
+		return (ENOTSUP);
+	}
+
+	header_size = strlen(hdr_name) + SIP_SPACE_LEN + sizeof (char) +
+	    SIP_SPACE_LEN + sip_num_of_digits(resp_num) + SIP_SPACE_LEN +
+	    sip_num_of_digits(cseq) + SIP_SPACE_LEN +
+	    strlen(sip_methods[method].name) + strlen(SIP_CRLF);
+
+	new_header = sip_new_header(header_size);
+	if (new_header == NULL) {
+		(void) pthread_mutex_unlock(&_sip_msg->sip_msg_mutex);
+		return (ENOMEM);
+	}
+
+	(void) snprintf(new_header->sip_hdr_start, header_size + 1,
+	    "%s %c %d %d %s%s", hdr_name, SIP_HCOLON, resp_num, cseq,
+	    sip_methods[method].name, SIP_CRLF);
+
+	_sip_add_header(_sip_msg, new_header, B_TRUE, B_FALSE, NULL);
+	if (_sip_msg->sip_msg_buf != NULL)
+		_sip_msg->sip_msg_modified = B_TRUE;
+	(void) pthread_mutex_unlock(&_sip_msg->sip_msg_mutex);
+
+	return (0);
+
+}
+
+/*
+ * Allow-Events =  ( "Allow-Events" / "u" ) HCOLON event-type
+ *			*(COMMA event-type)
+ */
+int
+sip_add_allow_events(sip_msg_t sip_msg, char *t_event)
+{
+	return (sip_add_str_to_msg(sip_msg, SIP_ALLOW_EVENTS, t_event, NULL,
+	    (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_add_event(sip_msg_t sip_msg, char *t_event, char *plist)
+{
+	return (sip_add_str_to_msg(sip_msg, SIP_EVENT, t_event, plist,
+	    SIP_SEMI));
+}
+
+/*
+ * 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_add_substate(sip_msg_t sip_msg, char *sub, char *plist)
+{
+	return (sip_add_str_to_msg(sip_msg, SIP_SUBSCRIPTION_STATE, sub, plist,
+	    SIP_SEMI));
+}
+
+/*
+ * 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_add_author(sip_msg_t sip_msg, char *scheme, char *param)
+{
+	return (sip_add_str_to_msg(sip_msg, SIP_AUTHOR, scheme, param, SIP_SP));
+}
+
+/*
+ * 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_add_authen_info(sip_msg_t sip_msg, char *ainfo)
+{
+	return (sip_add_str_to_msg(sip_msg, SIP_AUTHEN_INFO, ainfo, NULL,
+	    (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_add_proxy_authen(sip_msg_t sip_msg, char *pascheme, char *paparam)
+{
+	return (sip_add_str_to_msg(sip_msg, SIP_PROXY_AUTHEN, pascheme, paparam,
+	    SIP_SP));
+}
+
+/*
+ * Proxy-Authorization  =  "Proxy-Authorization" HCOLON credentials
+ */
+int
+sip_add_proxy_author(sip_msg_t sip_msg, char *paschem, char *paparam)
+{
+	return (sip_add_str_to_msg(sip_msg, SIP_PROXY_AUTHOR, paschem, paparam,
+	    SIP_SP));
+}
+
+/*
+ * Proxy-Require  =  "Proxy-Require" HCOLON option-tag
+ *			*(COMMA option-tag)
+ * option-tag     =  token
+ */
+int
+sip_add_proxy_require(sip_msg_t sip_msg, char *opt)
+{
+	return (sip_add_str_to_msg(sip_msg, SIP_PROXY_REQ, opt, NULL,
+	    (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_add_www_authen(sip_msg_t sip_msg, char *wascheme, char *waparam)
+{
+	return (sip_add_str_to_msg(sip_msg, SIP_WWW_AUTHEN, wascheme, waparam,
+	    SIP_SP));
+}
+
+/*
+ * Call-ID  =  ( "Call-ID" / "i" ) HCOLON callid
+ */
+int
+sip_add_callid(sip_msg_t sip_msg, char *callid)
+{
+	int		ret;
+	boolean_t	allocd = B_FALSE;
+
+	if (sip_msg == NULL || (callid != NULL && callid[0] == '\0'))
+		return (EINVAL);
+	if (callid == NULL) {
+		callid = (char *)sip_guid();
+		if (callid == NULL)
+			return (ENOMEM);
+		allocd = B_TRUE;
+	}
+	ret = sip_add_str_to_msg(sip_msg, SIP_CALL_ID, callid, NULL,
+	    (char)NULL);
+	if (allocd)
+		free(callid);
+	return (ret);
+}
+
+/*
+ * CSeq  =  "CSeq" HCOLON 1*DIGIT LWS Method
+ */
+int
+sip_add_cseq(sip_msg_t sip_msg, sip_method_t method, uint32_t cseq)
+{
+	int	r;
+
+	if (sip_msg == NULL || (int)cseq < 0 || method == 0 ||
+	    method >= MAX_SIP_METHODS) {
+		return (EINVAL);
+	}
+	r = sip_add_intstr_to_msg(sip_msg, SIP_CSEQ, cseq,
+	    sip_methods[method].name, NULL);
+	return (r);
+}
+
+/*
+ * 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
+ */
+_sip_header_t *
+sip_create_via_hdr(char *sent_protocol_transport, char *sent_by_host,
+    int sent_by_port, char *via_params)
+{
+	_sip_header_t	*new_header;
+	int		header_size;
+	int		count;
+
+	header_size = strlen(SIP_VIA) + SIP_SPACE_LEN + sizeof (char) +
+	    SIP_SPACE_LEN + strlen(SIP_VERSION) + sizeof (char) +
+	    strlen(sent_protocol_transport) + SIP_SPACE_LEN +
+	    strlen(sent_by_host) + strlen(SIP_CRLF);
+
+	if (sent_by_port > 0) {
+		header_size += SIP_SPACE_LEN + sizeof (char) + SIP_SPACE_LEN +
+		    sip_num_of_digits(sent_by_port);
+	}
+
+	if (via_params != NULL) {
+		header_size += SIP_SPACE_LEN + sizeof (char) +
+		    strlen(via_params);
+	}
+	new_header = sip_new_header(header_size);
+	if (new_header->sip_hdr_start == NULL)
+		return (NULL);
+	count = snprintf(new_header->sip_hdr_current, header_size + 1,
+	    "%s %c %s/%s %s",
+	    SIP_VIA, SIP_HCOLON, SIP_VERSION, sent_protocol_transport,
+	    sent_by_host);
+	new_header->sip_hdr_current += count;
+	header_size -= count;
+
+	if (sent_by_port > 0) {
+		count = snprintf(new_header->sip_hdr_current, header_size + 1,
+		    " %c %d", SIP_HCOLON, sent_by_port);
+		new_header->sip_hdr_current += count;
+		header_size -= count;
+	}
+
+	if (via_params != NULL) {
+		count = snprintf(new_header->sip_hdr_current, header_size + 1,
+		    " %c%s", SIP_SEMI, via_params);
+		new_header->sip_hdr_current += count;
+		header_size -= count;
+	}
+
+	(void) snprintf(new_header->sip_hdr_current, header_size + 1,
+	    "%s", SIP_CRLF);
+	return (new_header);
+}
+
+/*
+ * There can be multiple via headers we always append the header.
+ * We expect the via params to be a semi-colon separated list of parameters.
+ * We will add a semi-clone, before adding the list to the header.
+ */
+int
+sip_add_via(sip_msg_t sip_msg, char *sent_protocol_transport,
+    char *sent_by_host, int sent_by_port, char *via_params)
+{
+	_sip_header_t	*new_header;
+	_sip_msg_t	*_sip_msg;
+
+	if (sip_msg == NULL || sent_protocol_transport == NULL ||
+	    sent_by_host == NULL || sent_by_port < 0) {
+		return (EINVAL);
+	}
+
+	_sip_msg = (_sip_msg_t *)sip_msg;
+	(void) pthread_mutex_lock(&_sip_msg->sip_msg_mutex);
+	if (_sip_msg->sip_msg_cannot_be_modified) {
+		(void) pthread_mutex_unlock(&_sip_msg->sip_msg_mutex);
+		return (ENOTSUP);
+	}
+
+	new_header = sip_create_via_hdr(sent_protocol_transport, sent_by_host,
+	    sent_by_port, via_params);
+	if (new_header == NULL) {
+		(void) pthread_mutex_unlock(&_sip_msg->sip_msg_mutex);
+		return (ENOMEM);
+	}
+	_sip_add_header(_sip_msg, new_header, B_TRUE, B_FALSE, NULL);
+	if (_sip_msg->sip_msg_buf != NULL)
+		_sip_msg->sip_msg_modified = B_TRUE;
+	(void) pthread_mutex_unlock(&_sip_msg->sip_msg_mutex);
+	return (0);
+}
+
+/*
+ * Max-Forwards  =  "Max-Forwards" HCOLON 1*DIGIT
+ */
+int
+sip_add_maxforward(sip_msg_t sip_msg, uint_t maxforward)
+{
+	if (sip_msg == NULL || (int)maxforward < 0)
+		return (EINVAL);
+	return (sip_add_int_to_msg(sip_msg, SIP_MAX_FORWARDS, maxforward,
+	    NULL));
+}
+
+/*
+ * 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_add_content_type(sip_msg_t sip_msg, char *type, char *subtype)
+{
+	if (sip_msg == NULL || type == NULL || subtype == NULL)
+		return (EINVAL);
+	return (sip_add_2strs_to_msg(sip_msg, SIP_CONTENT_TYPE, type, B_FALSE,
+	    subtype, NULL, SIP_SLASH));
+}
+
+/*
+ * Content-Length  =  ( "Content-Length" / "l" ) HCOLON 1*DIGIT
+ */
+int
+sip_add_content_length(_sip_msg_t *_sip_msg, int length)
+{
+	_sip_header_t	*new_header;
+	int 		header_size;
+
+	if (_sip_msg == NULL || length < 0)
+		return (EINVAL);
+	(void) pthread_mutex_lock(&_sip_msg->sip_msg_mutex);
+	if (_sip_msg->sip_msg_cannot_be_modified) {
+		(void) pthread_mutex_unlock(&_sip_msg->sip_msg_mutex);
+		return (ENOTSUP);
+	}
+
+	header_size = strlen(SIP_CONTENT_LENGTH) + SIP_SPACE_LEN +
+	    sizeof (char) + SIP_SPACE_LEN + sip_num_of_digits(length) +
+	    strlen(SIP_CRLF) + strlen(SIP_CRLF);
+
+	new_header = sip_new_header(header_size);
+	if (new_header == NULL) {
+		(void) pthread_mutex_unlock(&_sip_msg->sip_msg_mutex);
+		return (ENOMEM);
+	}
+	(void) snprintf(new_header->sip_hdr_start, header_size + 1,
+	    "%s %c %u%s%s", SIP_CONTENT_LENGTH, SIP_HCOLON, length,
+	    SIP_CRLF, SIP_CRLF);
+
+	_sip_add_header(_sip_msg, new_header, B_TRUE, B_FALSE, NULL);
+	if (_sip_msg->sip_msg_buf != NULL)
+		_sip_msg->sip_msg_modified = B_TRUE;
+	(void) pthread_mutex_unlock(&_sip_msg->sip_msg_mutex);
+	return (0);
+}
+
+
+/*
+ * 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
+ */
+int
+sip_add_contact(sip_msg_t sip_msg, char *display_name, char *contact_uri,
+    boolean_t add_aquot, char *contact_params)
+{
+	return (sip_add_name_aspec(sip_msg, display_name, contact_uri, NULL,
+	    add_aquot, SIP_CONTACT, contact_params));
+}
+
+/*
+ * 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
+ *
+ * Since there can be more than one tags, fromtags is a semi colon separated
+ * list of tags.
+ */
+int
+sip_add_from(sip_msg_t sip_msg, char *display_name, char *from_uri,
+    char *fromtags, boolean_t add_aquot, char *from_params)
+{
+	return (sip_add_name_aspec(sip_msg, display_name, from_uri, fromtags,
+	    add_aquot, SIP_FROM, from_params));
+}
+
+/*
+ * To =  ( "To" / "t" ) HCOLON ( name-addr
+ *	/ addr-spec ) *( SEMI to-param )
+ * to-param  =  tag-param / generic-param
+ */
+int
+sip_add_to(sip_msg_t sip_msg, char *display_name, char *to_uri,
+    char *totags, boolean_t add_aquot, char *to_params)
+{
+	return (sip_add_name_aspec(sip_msg, display_name, to_uri, totags,
+	    add_aquot, SIP_TO, to_params));
+}
+
+/*
+ * Route        =  "Route" HCOLON route-param *(COMMA route-param)
+ * route-param  =  name-addr *( SEMI rr-param )
+ */
+int
+sip_add_route(sip_msg_t sip_msg, char *display_name, char *uri,
+    char *route_params)
+{
+	return (sip_add_name_aspec(sip_msg, display_name, uri, NULL, B_TRUE,
+	    SIP_ROUTE, route_params));
+}
+
+/*
+ * Record-Route  =  "Record-Route" HCOLON rec-route *(COMMA rec-route)
+ * rec-route     =  name-addr *( SEMI rr-param )
+ * rr-param      =  generic-param
+ */
+int
+sip_add_record_route(sip_msg_t sip_msg, char *display_name, char *uri,
+    char *route_params)
+{
+	return (sip_add_name_aspec(sip_msg, display_name, uri, NULL, B_TRUE,
+	    SIP_RECORD_ROUTE, route_params));
+}
+
+
+/*
+ * PAssertedID = "P-Asserted-Identity" HCOLON PAssertedID-value
+ *			*(COMMA PAssertedID-value)
+ * PAssertedID-value = name-addr / addr-spec
+ */
+int
+sip_add_passertedid(sip_msg_t sip_msg, char *display_name, char *addr,
+    boolean_t add_aquot)
+{
+	return (sip_add_name_aspec(sip_msg, display_name, addr, NULL, add_aquot,
+	    SIP_PASSERTEDID, NULL));
+}
+
+/*
+ * PPreferredID = "P-Preferred-Identity" HCOLON PPreferredID-value
+ *			*(COMMA PPreferredID-value)
+ * PPreferredID-value = name-addr / addr-spec
+ */
+int
+sip_add_ppreferredid(sip_msg_t sip_msg, char *display_name, char *addr,
+    boolean_t add_aquot)
+{
+	return (sip_add_name_aspec(sip_msg, display_name, addr, NULL, add_aquot,
+	    SIP_PPREFERREDID, NULL));
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/lib/libsip/common/sip_dialog.c	Sat Oct 07 14:26:26 2006 -0700
@@ -0,0 +1,1669 @@
+/*
+ * 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_hash.h"
+#include "sip_miscdefs.h"
+#include "sip_dialog.h"
+#include "sip_parse_generic.h"
+
+#define	SIP_DLG_XCHG_FROM	0
+#define	SIP_DLG_XCHG_TO		1
+
+/*
+ * Dialog state change callback function
+ */
+void (*sip_dlg_ulp_state_cb)(sip_dialog_t, sip_msg_t, int, int) = NULL;
+void (*sip_ulp_dlg_del_cb)(sip_dialog_t, sip_msg_t, void *) = NULL;
+
+boolean_t	sip_incomplete_dialog(sip_dialog_t);
+
+/*
+ * Exchange From/To header
+ */
+_sip_header_t *sip_dlg_xchg_from_to(sip_msg_t, int);
+
+/*
+ * Complete dialog hash table
+ */
+sip_hash_t sip_dialog_hash[SIP_HASH_SZ];
+
+/*
+ * Partial dialog hash table
+ */
+sip_hash_t sip_dialog_phash[SIP_HASH_SZ];
+
+/*
+ * Route set structure
+ */
+typedef struct sip_dlg_route_set_s  {
+	char		*sip_dlg_route;
+	sip_str_t	sip_dlg_ruri;
+	boolean_t	sip_dlg_route_lr;
+	struct sip_dlg_route_set_s *sip_dlg_route_next;
+}sip_dlg_route_set_t;
+
+sip_dialog_t		sip_seed_dialog(sip_conn_object_t, _sip_msg_t *,
+			    boolean_t, int);
+sip_dialog_t		sip_complete_dialog(_sip_msg_t *, _sip_dialog_t *);
+int			sip_dialog_process(_sip_msg_t *, sip_dialog_t *);
+void			sip_dialog_delete(_sip_dialog_t *);
+void			sip_dialog_init();
+sip_dialog_t		sip_dialog_find(_sip_msg_t *);
+boolean_t		sip_dialog_match(void *, void *);
+boolean_t		sip_dialog_free(void *, void *, int *);
+sip_dialog_t		sip_update_dialog(sip_dialog_t, _sip_msg_t *);
+char			*sip_dialog_req_uri(sip_dialog_t);
+
+static void		sip_release_dialog_res(_sip_dialog_t *);
+void			sip_dlg_self_destruct(void *);
+static int		sip_dialog_get_route_set(_sip_dialog_t *, _sip_msg_t *,
+			    int);
+static void		sip_dialog_free_rset(sip_dlg_route_set_t *);
+
+/*
+ * Timer object for partial dialogs
+ */
+typedef struct sip_dialog_timer_obj_s {
+	_sip_dialog_t	*dialog;
+	void		(*func)(sip_dialog_t, sip_msg_t, void *);
+} sip_dialog_timer_obj_t;
+
+/*
+ * To avoid duplication all over the place
+ */
+static void
+sip_release_dialog_res(_sip_dialog_t *dialog)
+{
+
+	assert(dialog->sip_dlg_ref_cnt == 0);
+	if (SIP_IS_TIMER_RUNNING(dialog->sip_dlg_timer))
+		SIP_CANCEL_TIMER(dialog->sip_dlg_timer);
+	if (dialog->sip_dlg_call_id != NULL)
+		sip_free_header(dialog->sip_dlg_call_id);
+	if (dialog->sip_dlg_local_uri_tag != NULL)
+		sip_free_header(dialog->sip_dlg_local_uri_tag);
+	if (dialog->sip_dlg_remote_uri_tag != NULL)
+		sip_free_header(dialog->sip_dlg_remote_uri_tag);
+	if (dialog->sip_dlg_remote_target != NULL)
+		sip_free_header(dialog->sip_dlg_remote_target);
+	if (dialog->sip_dlg_route_set != NULL)
+		sip_free_header(dialog->sip_dlg_route_set);
+	if (dialog->sip_dlg_event != NULL)
+		sip_free_header(dialog->sip_dlg_event);
+	if (dialog->sip_dlg_req_uri.sip_str_ptr != NULL) {
+		free(dialog->sip_dlg_req_uri.sip_str_ptr);
+		dialog->sip_dlg_req_uri.sip_str_ptr = NULL;
+		dialog->sip_dlg_req_uri.sip_str_len = 0;
+	}
+	if (dialog->sip_dlg_rset.sip_str_ptr != NULL) {
+		free(dialog->sip_dlg_rset.sip_str_ptr);
+		dialog->sip_dlg_rset.sip_str_len = 0;
+		dialog->sip_dlg_rset.sip_str_ptr = NULL;
+	}
+	(void) pthread_mutex_destroy(&dialog->sip_dlg_mutex);
+	free(dialog);
+}
+
+/*
+ * Get the route information from the 'value' and add it to the route
+ * set.
+ */
+static sip_dlg_route_set_t *
+sip_add_route_to_set(sip_hdr_value_t *value)
+{
+	int			vlen = 0;
+	sip_dlg_route_set_t	*rset;
+	char			*crlf;
+	const sip_param_t	*uri_param;
+	int			error;
+
+	rset = calloc(1, sizeof (*rset));
+	if (rset == NULL)
+		return (NULL);
+	rset->sip_dlg_route_next = NULL;
+	vlen = value->sip_value_end - value->sip_value_start;
+
+	/*
+	 * check for CRLF
+	 */
+	crlf = value->sip_value_end - strlen(SIP_CRLF);
+	while (crlf != NULL && strncmp(crlf, SIP_CRLF, strlen(SIP_CRLF)) == 0) {
+		vlen -= strlen(SIP_CRLF);
+		crlf -= strlen(SIP_CRLF);
+	}
+	rset->sip_dlg_route = calloc(1, vlen + 1);
+	if (rset->sip_dlg_route == NULL) {
+		free(rset);
+		return (NULL);
+	}
+	/*
+	 * loose routing
+	 */
+	rset->sip_dlg_route_lr = B_FALSE;
+	(void) strncpy(rset->sip_dlg_route, value->sip_value_start, vlen);
+	rset->sip_dlg_ruri.sip_str_ptr = rset->sip_dlg_route +
+	    (value->cftr_uri.sip_str_ptr - value->sip_value_start);
+	rset->sip_dlg_ruri.sip_str_len = value->cftr_uri.sip_str_len;
+	rset->sip_dlg_route[vlen] = '\0';
+
+	assert(value->sip_value_parsed_uri != NULL);
+	/*
+	 * Check if the 'lr' param is present for this route.
+	 */
+	uri_param = sip_get_uri_params(value->sip_value_parsed_uri, &error);
+	if (error != 0) {
+		free(rset->sip_dlg_route);
+		free(rset);
+		return (NULL);
+	}
+	if (uri_param != NULL) {
+		rset->sip_dlg_route_lr = sip_is_param_present(uri_param, "lr",
+		    strlen("lr"));
+	}
+	return (rset);
+}
+
+/*
+ * Depending on the route-set, determine the request URI.
+ */
+char *
+sip_dialog_req_uri(sip_dialog_t dialog)
+{
+	const sip_str_t		*req_uri;
+	char			*uri;
+	_sip_dialog_t		*_dialog;
+
+	_dialog = (_sip_dialog_t *)dialog;
+	if (_dialog->sip_dlg_route_set == NULL ||
+	    _dialog->sip_dlg_req_uri.sip_str_ptr == NULL) {
+		const struct sip_value	*val;
+
+		val = sip_get_header_value(_dialog->sip_dlg_remote_target,
+		    NULL);
+		if (val == NULL)
+			return (NULL);
+		req_uri = &((sip_hdr_value_t *)val)->cftr_uri;
+	} else {
+		req_uri = &_dialog->sip_dlg_req_uri;
+	}
+	uri = (char *)malloc(req_uri->sip_str_len + 1);
+	if (uri == NULL)
+		return (NULL);
+	(void) strncpy(uri, req_uri->sip_str_ptr, req_uri->sip_str_len);
+	uri[req_uri->sip_str_len] = '\0';
+
+	return (uri);
+}
+
+/*
+ * Free the route set.
+ */
+void
+sip_dialog_free_rset(sip_dlg_route_set_t *rset)
+{
+	sip_dlg_route_set_t	*next;
+
+	while (rset != NULL) {
+		next = rset->sip_dlg_route_next;
+		rset->sip_dlg_route_next = NULL;
+		free(rset->sip_dlg_route);
+		free(rset);
+		rset = next;
+	}
+}
+
+/*
+ * Recompute route-set
+ */
+static int
+sip_dlg_recompute_rset(_sip_dialog_t *dialog, _sip_msg_t *sip_msg, int what)
+{
+	int ret;
+
+	if (dialog->sip_dlg_route_set != NULL) {
+		sip_free_header(dialog->sip_dlg_route_set);
+		dialog->sip_dlg_route_set = NULL;
+	}
+	if (dialog->sip_dlg_req_uri.sip_str_ptr != NULL) {
+		free(dialog->sip_dlg_req_uri.sip_str_ptr);
+		dialog->sip_dlg_req_uri.sip_str_ptr = NULL;
+		dialog->sip_dlg_req_uri.sip_str_len = 0;
+	}
+	if (dialog->sip_dlg_rset.sip_str_ptr != NULL) {
+		free(dialog->sip_dlg_rset.sip_str_ptr);
+		dialog->sip_dlg_rset.sip_str_ptr = NULL;
+		dialog->sip_dlg_rset.sip_str_len = 0;
+	}
+	ret = sip_dialog_get_route_set(dialog, sip_msg, what);
+	return (ret);
+}
+
+/*
+ * If the route set is empty, the UAC MUST place the remote target URI
+ * into the Request-URI.  The UAC MUST NOT add a Route header field to
+ * the request.
+ *
+ * If the route set is not empty, and the first URI in the route set
+ * contains the lr parameter (see Section 19.1.1), the UAC MUST place
+ * the remote target URI into the Request-URI and MUST include a Route
+ * header field containing the route set values in order, including all
+ * parameters.
+ *
+ * If the route set is not empty, and its first URI does not contain the
+ * lr parameter, the UAC MUST place the first URI from the route set
+ * into the Request-URI, stripping any parameters that are not allowed
+ * in a Request-URI.  The UAC MUST add a Route header field containing
+ * the remainder of the route set values in order, including all
+ * parameters.  The UAC MUST then place the remote target URI into the
+ * Route header field as the last value.
+ */
+int
+sip_dialog_set_route_hdr(_sip_dialog_t *dialog, sip_dlg_route_set_t *rset_head,
+    int rcnt, int rlen)
+{
+	size_t			rset_len;
+	_sip_header_t		*rhdr;
+	char			*rset;
+	char			*rp;
+	char			*rsp;
+	int			count;
+	sip_dlg_route_set_t	*route;
+	boolean_t		first = B_TRUE;
+	const sip_str_t		*to_uri;
+	char			*uri = NULL;
+	int			rspl;
+	int			rpl;
+
+	assert(rcnt > 0);
+
+	dialog->sip_dlg_rset.sip_str_len = rlen + rcnt - 1;
+	dialog->sip_dlg_rset.sip_str_ptr = malloc(rlen + rcnt);
+	if (dialog->sip_dlg_rset.sip_str_ptr == NULL)
+		return (ENOMEM);
+	rsp = dialog->sip_dlg_rset.sip_str_ptr;
+	rspl = rlen + rcnt;
+	route = rset_head;
+	rset_len = rlen;
+	if (!route->sip_dlg_route_lr) {
+		const struct sip_value	*val;
+
+		val = sip_get_header_value(dialog->sip_dlg_remote_target, NULL);
+		to_uri = &((sip_hdr_value_t *)val)->cftr_uri;
+		uri = (char *)malloc(to_uri->sip_str_len + 1);
+		if (uri == NULL) {
+			free(dialog->sip_dlg_rset.sip_str_ptr);
+			dialog->sip_dlg_rset.sip_str_len = 0;
+			dialog->sip_dlg_rset.sip_str_ptr = NULL;
+			return (ENOMEM);
+		}
+		(void) strncpy(uri, to_uri->sip_str_ptr, to_uri->sip_str_len);
+		uri[to_uri->sip_str_len] = '\0';
+		rset_len = rlen - strlen(route->sip_dlg_route) + strlen(uri) +
+		    SIP_SPACE_LEN + sizeof (char) + SIP_SPACE_LEN +
+		    sizeof (char);
+		count = snprintf(rsp, rspl, "%s", route->sip_dlg_route);
+		dialog->sip_dlg_req_uri.sip_str_ptr = malloc(
+		    route->sip_dlg_ruri.sip_str_len + 1);
+		if (dialog->sip_dlg_req_uri.sip_str_ptr == NULL) {
+			free(uri);
+			free(dialog->sip_dlg_rset.sip_str_ptr);
+			dialog->sip_dlg_rset.sip_str_len = 0;
+			dialog->sip_dlg_rset.sip_str_ptr = NULL;
+			return (ENOMEM);
+		}
+		(void) strncpy(dialog->sip_dlg_req_uri.sip_str_ptr, rsp +
+		    (route->sip_dlg_ruri.sip_str_ptr - route->sip_dlg_route),
+		    route->sip_dlg_ruri.sip_str_len);
+		dialog->sip_dlg_req_uri.sip_str_ptr[
+		    route->sip_dlg_ruri.sip_str_len] = '\0';
+		dialog->sip_dlg_req_uri.sip_str_len =
+		    route->sip_dlg_ruri.sip_str_len;
+
+		rsp += count;
+		rspl -= count;
+		route = route->sip_dlg_route_next;
+	}
+
+	/*
+	 * rcnt - 1 is for the number of COMMAs
+	 */
+	rset_len += strlen(SIP_ROUTE) + SIP_SPACE_LEN + sizeof (char) +
+	    SIP_SPACE_LEN + rcnt - 1;
+	rset = malloc(rset_len + 1);
+	if (rset == NULL) {
+		free(dialog->sip_dlg_rset.sip_str_ptr);
+		dialog->sip_dlg_rset.sip_str_len = 0;
+		dialog->sip_dlg_rset.sip_str_ptr = NULL;
+		return (ENOMEM);
+	}
+	rhdr = sip_new_header(rset_len + strlen(SIP_CRLF));
+	if (rhdr == NULL) {
+		free(rset);
+		free(dialog->sip_dlg_rset.sip_str_ptr);
+		dialog->sip_dlg_rset.sip_str_len = 0;
+		dialog->sip_dlg_rset.sip_str_ptr = NULL;
+		return (ENOMEM);
+	}
+
+	rp = rset;
+	rpl = rset_len + 1;
+	count = snprintf(rp, rpl, "%s %c ", SIP_ROUTE, SIP_HCOLON);
+	rp += count;
+	rpl -= count;
+
+	while (route != NULL) {
+		if (first) {
+			count = snprintf(rp, rpl, "%s", route->sip_dlg_route);
+			rp += count;
+			rpl -= count;
+			first = B_FALSE;
+			if (uri != NULL) {
+				count = snprintf(rsp, rspl, "%c%s",
+				    SIP_COMMA, route->sip_dlg_route);
+			} else {
+				count = snprintf(rsp, rspl, "%s",
+				    route->sip_dlg_route);
+			}
+			rsp += count;
+			rspl -= count;
+		} else {
+			count = snprintf(rp, rpl, "%c%s", SIP_COMMA,
+			    route->sip_dlg_route);
+			rp += count;
+			rpl -= count;
+			count = snprintf(rsp, rspl, "%c%s", SIP_COMMA,
+			    route->sip_dlg_route);
+			rsp += count;
+			rspl -= count;
+		}
+		route = route->sip_dlg_route_next;
+	}
+	assert(rsp <= dialog->sip_dlg_rset.sip_str_ptr +
+	    dialog->sip_dlg_rset.sip_str_len);
+	dialog->sip_dlg_rset.sip_str_ptr[dialog->sip_dlg_rset.sip_str_len] =
+	    '\0';
+	if (uri != NULL) {
+		if (first) {
+			count = snprintf(rp, rpl, "%c %s %c", SIP_LAQUOT,
+			    uri, SIP_RAQUOT);
+		} else {
+			count = snprintf(rp, rpl, "%c%c %s %c", SIP_COMMA,
+			    SIP_LAQUOT, uri, SIP_RAQUOT);
+		}
+		rp += count;
+		rpl -= count;
+		free(uri);
+	}
+	assert(rp <= rset + rset_len);
+	(void) snprintf(rhdr->sip_hdr_start, rset_len + strlen(SIP_CRLF) + 1,
+	    "%s%s", rset, SIP_CRLF);
+	free(rset);
+	dialog->sip_dlg_route_set = (sip_header_t)rhdr;
+	sip_dialog_free_rset(rset_head);
+	return (0);
+}
+
+/*
+ * UAC Behavior
+ * The route set MUST be set to the list of URIs in the Record-Route
+ * header field from the response, taken in reverse order and preserving
+ * all URI parameters.
+ *
+ * UAS behavior
+ * The route set MUST be set to the list of URIs in the Record-Route
+ * header field from the request, taken in order and preserving all URI
+ * parameters.
+ */
+static int
+sip_dialog_get_route_set(_sip_dialog_t *dialog, _sip_msg_t *sip_msg, int what)
+{
+	sip_header_t		rrhdr;
+	sip_hdr_value_t		*value;
+	int			error;
+	sip_dlg_route_set_t	*rset_head = NULL;
+	sip_dlg_route_set_t	*rset_tail = NULL;
+	sip_dlg_route_set_t	*rset;
+	int			rset_cnt = 0;
+	int			rset_len = 0;
+
+	(void) pthread_mutex_lock(&sip_msg->sip_msg_mutex);
+	rrhdr = sip_search_for_header(sip_msg, SIP_RECORD_ROUTE, NULL);
+	while (rrhdr != NULL) {
+		(void) pthread_mutex_unlock(&sip_msg->sip_msg_mutex);
+		value = (sip_hdr_value_t *)sip_get_header_value(rrhdr, &error);
+		while (value != NULL && error == 0) {
+			char	*crlf;
+
+			if (value->sip_value_state == SIP_VALUE_BAD) {
+				value = (sip_hdr_value_t *)sip_get_next_value(
+				    (sip_header_value_t)value, &error);
+				continue;
+			}
+			rset = sip_add_route_to_set(value);
+			if (rset == NULL)
+				goto r_error;
+			/*
+			 * Add one for COMMA
+			 */
+			rset_cnt++;
+			rset_len += (value->sip_value_end -
+			    value->sip_value_start);
+			/*
+			 * Check for CRLF
+			 */
+			crlf = value->sip_value_end - strlen(SIP_CRLF);
+			while (crlf != NULL &&
+			    strncmp(crlf, SIP_CRLF, strlen(SIP_CRLF)) == 0) {
+				rset_len -= strlen(SIP_CRLF);
+				crlf -= strlen(SIP_CRLF);
+			}
+			if (rset_head == NULL) {
+				assert(rset_tail == NULL);
+				rset_head = rset_tail = rset;
+			} else if (what == SIP_UAS_DIALOG) {
+				rset_tail->sip_dlg_route_next = rset;
+				rset_tail = rset;
+			} else if (what == SIP_UAC_DIALOG) {
+				rset->sip_dlg_route_next = rset_head;
+				rset_head = rset;
+			} else {
+				assert(0);
+			}
+			value = (sip_hdr_value_t *)sip_get_next_value(
+			    (sip_header_value_t)value, &error);
+		}
+		(void) pthread_mutex_lock(&sip_msg->sip_msg_mutex);
+		rrhdr = sip_search_for_header(sip_msg, SIP_RECORD_ROUTE, rrhdr);
+	}
+	(void) pthread_mutex_unlock(&sip_msg->sip_msg_mutex);
+	if (rset_cnt == 0)
+		return (0);
+	if (sip_dialog_set_route_hdr(dialog, rset_head, rset_cnt,
+	    rset_len) != 0) {
+		goto r_error;
+	}
+	return (0);
+r_error:
+	sip_dialog_free_rset(rset_head);
+	return (ENOMEM);
+}
+
+/*
+ * UAS behavior:
+ * The remote sequence number MUST be set to the value of the sequence
+ * number in the CSeq header field of the request.  The local sequence
+ * number MUST be empty.  The call identifier component of the dialog ID
+ * MUST be set to the value of the Call-ID in the request.  The local
+ * tag component of the dialog ID MUST be set to the tag in the To field
+ * in the response to the request (which always includes a tag), and the
+ * remote tag component of the dialog ID MUST be set to the tag from the
+ * From field in the request.  A UAS MUST be prepared to receive a
+ * request without a tag in the From field, in which case the tag is
+ * considered to have a value of null.
+ * The remote URI MUST be set to the URI in the From field, and the
+ * local URI MUST be set to the URI in the To field.
+ * The remote target MUST be set to the URI from the Contact header field
+ * of the request.
+ *
+ * UAC behavior:
+ * The local sequence number MUST be set to the value of the sequence
+ * number in the CSeq header field of the request.  The remote sequence
+ * number MUST be empty (it is established when the remote UA sends a
+ * request within the dialog).  The call identifier component of the
+ * dialog ID MUST be set to the value of the Call-ID in the request.
+ * The local tag component of the dialog ID MUST be set to the tag in
+ * the From field in the request, and the remote tag component of the
+ * dialog ID MUST be set to the tag in the To field of the response.  A
+ * UAC MUST be prepared to receive a response without a tag in the To
+ * field, in which case the tag is considered to have a value of null.
+ * The remote URI MUST be set to the URI in the To field, and the local
+ * URI MUST be set to the URI in the From field.
+ * The remote target MUST be set to the URI from the Contact header field
+ * of the response.
+ */
+
+
+/*
+ * This is the routine that seeds a dialog.
+ */
+sip_dialog_t
+sip_seed_dialog(sip_conn_object_t obj, _sip_msg_t *sip_msg,
+    boolean_t dlg_on_fork, int dlg_type)
+{
+	_sip_dialog_t		*dialog;
+	int			cseq;
+	sip_header_t		fhdr = NULL;
+	sip_header_t		thdr = NULL;
+	sip_header_t		chdr;
+	sip_header_t		cihdr;
+	sip_header_t		evhdr = NULL;
+	const struct sip_value	*value;
+	sip_dialog_timer_obj_t	*tim_obj = NULL;
+	const sip_str_t		*callid;
+	sip_method_t		method;
+	int			timer1 = sip_timer_T1;
+	int			error;
+
+	if (!sip_msg_is_request((sip_msg_t)sip_msg, &error))
+		return (NULL);
+
+	method = sip_get_request_method((sip_msg_t)sip_msg, &error);
+	/*
+	 * Only INVITE and SUBSCRIBE supported
+	 */
+	if (error != 0 || (method != INVITE && method != SUBSCRIBE))
+		return (NULL);
+
+	/*
+	 * A request outside of a dialog MUST NOT contain a To tag
+	 */
+	if (sip_get_to_tag((sip_msg_t)sip_msg, NULL) != NULL)
+		return (NULL);
+
+	if (dlg_type == SIP_UAS_DIALOG) {
+		thdr = sip_dlg_xchg_from_to((sip_msg_t)sip_msg,
+		    SIP_DLG_XCHG_FROM);
+		(void) pthread_mutex_lock(&sip_msg->sip_msg_mutex);
+	} else {
+		(void) pthread_mutex_lock(&sip_msg->sip_msg_mutex);
+		fhdr = sip_search_for_header(sip_msg, SIP_FROM, NULL);
+	}
+	cihdr = sip_search_for_header(sip_msg, SIP_CALL_ID, NULL);
+	chdr = sip_search_for_header(sip_msg, SIP_CONTACT, NULL);
+	if (method == SUBSCRIBE)
+		evhdr = sip_search_for_header(sip_msg, SIP_EVENT, NULL);
+	(void) pthread_mutex_unlock(&sip_msg->sip_msg_mutex);
+	if ((fhdr == NULL && thdr == NULL) || cihdr == NULL || chdr == NULL ||
+	    (method == SUBSCRIBE && evhdr == NULL)) {
+		if (thdr != NULL)
+			sip_free_header(thdr);
+		return (NULL);
+	}
+
+	/*
+	 * Sanity check since we just store the headers in the dialog
+	 */
+	if (sip_get_from_tag((sip_msg_t)sip_msg, NULL) == NULL ||
+	    sip_get_from_uri_str((sip_msg_t)sip_msg, NULL) == NULL ||
+	    ((cseq = sip_get_callseq_num((sip_msg_t)sip_msg, NULL)) == -1) ||
+	    (callid = sip_get_callid((sip_msg_t)sip_msg, NULL)) == NULL ||
+	    sip_get_to_uri_str((sip_msg_t)sip_msg, NULL) == NULL ||
+	    ((value = sip_get_header_value(chdr, NULL)) == NULL) ||
+	    sip_get_contact_uri_str((sip_header_value_t)value, NULL) == NULL) {
+		if (thdr != NULL)
+			sip_free_header(thdr);
+		return (NULL);
+	}
+
+	tim_obj = calloc(1, sizeof (sip_dialog_timer_obj_t));
+	if (tim_obj == NULL) {
+		if (thdr != NULL)
+			sip_free_header(thdr);
+		return (NULL);
+	}
+	dialog = calloc(1, sizeof (_sip_dialog_t));
+	if (dialog == NULL) {
+		if (thdr != NULL)
+			sip_free_header(thdr);
+		return (NULL);
+	}
+	/*
+	 * We will take the TO header with the tag when we complete this
+	 * dialog
+	 */
+	if (dlg_type == SIP_UAS_DIALOG) {
+		dialog->sip_dlg_remote_uri_tag = thdr;
+		/*
+		 * We take the remote target from the incoming request on the
+		 * UAS. For the UAC, we will take it from the response.
+		 */
+		if ((dialog->sip_dlg_remote_target = sip_dup_header(chdr)) ==
+		    NULL) {
+			goto dia_err;
+		}
+	} else {
+		if ((dialog->sip_dlg_local_uri_tag = sip_dup_header(fhdr)) ==
+		    NULL) {
+			goto dia_err;
+		}
+	}
+	if ((dialog->sip_dlg_call_id = sip_dup_header(cihdr)) == NULL)
+		goto dia_err;
+	if (method == SUBSCRIBE) {
+		dialog->sip_dlg_event = sip_dup_header(evhdr);
+		if (dialog->sip_dlg_event == NULL) {
+			goto dia_err;
+		}
+	}
+	dialog->sip_dlg_rset.sip_str_ptr = NULL;
+	dialog->sip_dlg_rset.sip_str_len = 0;
+	dialog->sip_dlg_req_uri.sip_str_ptr = NULL;
+	dialog->sip_dlg_req_uri.sip_str_len = 0;
+	/*
+	 * Get the route set from the request, if present
+	 */
+	if (dlg_type == SIP_UAS_DIALOG &&
+	    sip_dialog_get_route_set(dialog, sip_msg, dlg_type) != 0) {
+		goto dia_err;
+	}
+	if (dlg_type == SIP_UAC_DIALOG)
+		dialog->sip_dlg_local_cseq = cseq;
+	else
+		dialog->sip_dlg_remote_cseq = cseq;
+	dialog->sip_dlg_type = dlg_type;
+	dialog->sip_dlg_on_fork = dlg_on_fork;
+	dialog->sip_dlg_method = method;
+	/*
+	 * Set the partial dialog timer with the INVITE timeout val
+	 */
+	if (sip_conn_timer1 != NULL)
+		timer1 = sip_conn_timer1(obj);
+	SIP_INIT_TIMER(dialog->sip_dlg_timer, 64 * timer1);
+	tim_obj->dialog = dialog;
+	/*
+	 * Since at the client we never pass the partial dialog, we need not
+	 * invoke the callback when the partial dialog self-destructs.
+	 */
+	if (dlg_type == SIP_UAS_DIALOG)
+		tim_obj->func = sip_ulp_dlg_del_cb;
+	SIP_SCHED_TIMER(dialog->sip_dlg_timer, (void *)tim_obj,
+	    sip_dlg_self_destruct);
+	if (!SIP_IS_TIMER_RUNNING(dialog->sip_dlg_timer))
+		goto dia_err;
+	(void) pthread_mutex_init(&dialog->sip_dlg_mutex, NULL);
+
+	if (dlg_type == SIP_UAC_DIALOG) {
+		const sip_str_t	*local_tag;
+
+		local_tag = sip_get_from_tag((sip_msg_t)sip_msg, NULL);
+		assert(local_tag != NULL);
+		sip_md5_hash(local_tag->sip_str_ptr, local_tag->sip_str_len,
+		    callid->sip_str_ptr, callid->sip_str_len,
+		    NULL, 0, NULL, 0, NULL, 0, NULL, 0,
+		    (uchar_t *)dialog->sip_dlg_id);
+
+
+		/*
+		 * Add it to the partial hash table
+		 */
+		if (sip_hash_add(sip_dialog_phash, (void *)dialog,
+		    SIP_DIGEST_TO_HASH(dialog->sip_dlg_id)) != 0) {
+			goto dia_err;
+		}
+	}
+	SIP_DLG_REFCNT_INCR(dialog);
+	return ((sip_dialog_t)dialog);
+dia_err:
+	sip_release_dialog_res(dialog);
+	if (SIP_IS_TIMER_RUNNING(dialog->sip_dlg_timer))
+		SIP_CANCEL_TIMER(dialog->sip_dlg_timer);
+	if (tim_obj != NULL)
+		free(tim_obj);
+	return (NULL);
+}
+
+/*
+ * When creating a dialog from a NOTIFY request, we need to get the FROM
+ * header for the dialog from the TO header of the NOTIFY.
+ */
+_sip_header_t *
+sip_dlg_xchg_from_to(sip_msg_t sip_msg, int what)
+{
+	int			len;
+	_sip_header_t		*newhdr;
+	int			cnt;
+	const struct sip_header	*hdr;
+	int			hdrsize;
+	int			error;
+
+	hdr = sip_get_header(sip_msg, what == SIP_DLG_XCHG_FROM ? SIP_FROM :
+	    SIP_TO, NULL, &error);
+	if (error != 0 || hdr == NULL)
+		return (NULL);
+	if (sip_parse_goto_values((_sip_header_t *)hdr) != 0)
+		return (NULL);
+	len = hdr->sip_hdr_end - hdr->sip_hdr_current;
+	if (what == SIP_DLG_XCHG_FROM) {
+		hdrsize = len + strlen(SIP_TO) + SIP_SPACE_LEN + sizeof (char) +
+		    SIP_SPACE_LEN;
+	} else {
+		hdrsize = len + strlen(SIP_FROM) + SIP_SPACE_LEN +
+		    sizeof (char) + SIP_SPACE_LEN;
+	}
+	newhdr = sip_new_header(hdrsize);
+	if (newhdr == NULL)
+		return (NULL);
+	if (what == SIP_DLG_XCHG_FROM) {
+		cnt = snprintf(newhdr->sip_hdr_current, hdrsize + 1,
+		    "%s %c ", SIP_TO, SIP_HCOLON);
+	} else {
+		cnt = snprintf(newhdr->sip_hdr_current, hdrsize + 1,
+		    "%s %c ", SIP_FROM, SIP_HCOLON);
+	}
+	newhdr->sip_hdr_current += cnt;
+	(void) strncpy(newhdr->sip_hdr_current, hdr->sip_hdr_current, len);
+	newhdr->sip_hdr_current += len;
+	assert(newhdr->sip_hdr_current == newhdr->sip_hdr_end);
+	assert(hdr->sip_header_functions != NULL);
+
+	/*
+	 * FROM and TO have common parsing functions
+	 */
+	newhdr->sip_header_functions = hdr->sip_header_functions;
+	newhdr->sip_hdr_current = newhdr->sip_hdr_start;
+
+	return (newhdr);
+}
+
+/*
+ * This is the response that completes the dialog that was created
+ * in sip_seed_dialog().
+ */
+sip_dialog_t
+sip_complete_dialog(_sip_msg_t *sip_msg, _sip_dialog_t *dialog)
+{
+	_sip_header_t		*thdr;
+	_sip_header_t		*evhdr = NULL;
+	_sip_header_t		*substate = NULL;
+	sip_header_t		chdr = NULL;
+	int			resp_code;
+	const sip_str_t		*ttag;
+	const sip_str_t		*remtag;
+	const sip_str_t		*callid;
+	const struct sip_value 	*val;
+	sip_method_t		method;
+	int			error = 0;
+	int			prev_state;
+	boolean_t		alloc_thdr = B_FALSE;
+
+	if (sip_msg_is_request((sip_msg_t)sip_msg, &error) && error == 0)
+		method = sip_get_request_method((sip_msg_t)sip_msg, &error);
+	else
+		method = sip_get_callseq_method((sip_msg_t)sip_msg, &error);
+	if (error != 0 || dialog == NULL ||
+	    (sip_msg_is_request((sip_msg_t)sip_msg, &error) &&
+	    (dialog->sip_dlg_method == INVITE || method != NOTIFY))) {
+		return (NULL);
+	}
+	if ((dialog->sip_dlg_type == SIP_UAC_DIALOG && method != NOTIFY &&
+	    sip_get_callseq_num((sip_msg_t)sip_msg, NULL) !=
+	    dialog->sip_dlg_local_cseq) ||
+	    (dialog->sip_dlg_type == SIP_UAS_DIALOG && method != NOTIFY &&
+	    sip_get_callseq_num((sip_msg_t)sip_msg, NULL) !=
+	    dialog->sip_dlg_remote_cseq)) {
+		return (NULL);
+	}
+	if (method == NOTIFY) {
+		const sip_str_t	*sstate;
+
+		thdr = sip_dlg_xchg_from_to((sip_msg_t)sip_msg,
+		    SIP_DLG_XCHG_FROM);
+		if (thdr == NULL)
+			return (NULL);
+		alloc_thdr = B_TRUE;
+		(void) pthread_mutex_lock(&sip_msg->sip_msg_mutex);
+		chdr = sip_search_for_header(sip_msg, SIP_CONTACT, NULL);
+		if (chdr == NULL) {
+			(void) pthread_mutex_unlock(&sip_msg->sip_msg_mutex);
+			sip_free_header(thdr);
+			return (NULL);
+		}
+		evhdr = sip_search_for_header(sip_msg, SIP_EVENT, NULL);
+		if (evhdr == NULL) {
+			(void) pthread_mutex_unlock(&sip_msg->sip_msg_mutex);
+			sip_free_header(thdr);
+			return (NULL);
+		}
+		substate = sip_search_for_header(sip_msg,
+		    SIP_SUBSCRIPTION_STATE, NULL);
+		if (substate == NULL) {
+			(void) pthread_mutex_unlock(&sip_msg->sip_msg_mutex);
+			sip_free_header(thdr);
+			return (NULL);
+		}
+		(void) pthread_mutex_unlock(&sip_msg->sip_msg_mutex);
+		sstate = sip_get_substate((sip_msg_t)sip_msg, &error);
+		if (sstate == NULL || error != 0) {
+			sip_free_header(thdr);
+			return (NULL);
+		}
+		if ((sstate->sip_str_len != strlen("pending") &&
+		    sstate->sip_str_len != strlen("active")) ||
+		    ((sstate->sip_str_len == strlen("pending") &&
+		    strncasecmp(sstate->sip_str_ptr, "pending",
+		    strlen("pending")) != 0) ||
+		    (sstate->sip_str_len == strlen("active") &&
+		    strncasecmp(sstate->sip_str_ptr, "active",
+		    strlen("active")) != 0))) {
+			sip_free_header(thdr);
+			return (NULL);
+		}
+		ttag = sip_get_from_tag((sip_msg_t)sip_msg, NULL);
+	} else {
+		if (dialog->sip_dlg_type == SIP_UAS_DIALOG) {
+			thdr = sip_dlg_xchg_from_to((sip_msg_t)sip_msg,
+			    SIP_DLG_XCHG_TO);
+			alloc_thdr = B_TRUE;
+		} else {
+			(void) pthread_mutex_lock(&sip_msg->sip_msg_mutex);
+			thdr = sip_search_for_header(sip_msg, SIP_TO, NULL);
+			if (dialog->sip_dlg_remote_target == NULL) {
+				chdr = sip_search_for_header(sip_msg,
+				    SIP_CONTACT, NULL);
+			}
+			(void) pthread_mutex_unlock(&sip_msg->sip_msg_mutex);
+		}
+		if (thdr == NULL) {
+			(void) pthread_mutex_unlock(&sip_msg->sip_msg_mutex);
+			return (NULL);
+		}
+		ttag = sip_get_to_tag((sip_msg_t)sip_msg, NULL);
+	}
+	if (ttag == NULL) {
+		if (alloc_thdr)
+			sip_free_header(thdr);
+		return (NULL);
+	}
+	prev_state = dialog->sip_dlg_state;
+
+	if (method == NOTIFY) {
+		int			error;
+		const sip_str_t		*dlg_id_val = NULL;
+		const sip_str_t		*event;
+		const sip_str_t		*id_val = NULL;
+		sip_header_value_t	ev_val;
+		sip_hdr_value_t		*dlg_ev_val = NULL;
+
+		event = sip_get_event((sip_msg_t)sip_msg, &error);
+		if (event == NULL || error != 0) {
+			sip_free_header(thdr);
+			return (NULL);
+		}
+		ev_val = (sip_header_value_t)sip_get_header_value(evhdr,
+		    &error);
+		if (ev_val != NULL)
+			id_val = sip_get_param_value(ev_val, "id", &error);
+		if (error == 0) {
+			dlg_ev_val = (sip_hdr_value_t *)sip_get_header_value(
+			    dialog->sip_dlg_event, &error);
+		}
+		if (dlg_ev_val == NULL || error != 0) {
+			sip_free_header(thdr);
+			return (NULL);
+		}
+		dlg_id_val = sip_get_param_value((sip_header_value_t)dlg_ev_val,
+		    "id", &error);
+		if (error != 0 ||
+		    dlg_ev_val->str_val_len != event->sip_str_len ||
+		    strncmp(dlg_ev_val->str_val_ptr, event->sip_str_ptr,
+		    event->sip_str_len != 0)) {
+			sip_free_header(thdr);
+			return (NULL);
+		}
+		if ((dlg_id_val == NULL && id_val != NULL) ||
+		    (dlg_id_val != NULL && id_val == NULL)) {
+			sip_free_header(thdr);
+			return (NULL);
+		} else if (dlg_id_val != NULL && id_val != NULL) {
+			if (dlg_id_val->sip_str_len != id_val->sip_str_len ||
+			    strncasecmp(dlg_id_val->sip_str_ptr,
+			    id_val->sip_str_ptr, dlg_id_val->sip_str_len) !=
+			    0) {
+				sip_free_header(thdr);
+				return (NULL);
+			}
+		}
+		if (dialog->sip_dlg_type == SIP_UAC_DIALOG) {
+			dialog->sip_dlg_remote_uri_tag = thdr;
+			if ((dialog->sip_dlg_remote_target =
+			    sip_dup_header(chdr)) == NULL) {
+				sip_free_header(thdr);
+				return (NULL);
+			}
+		} else {
+			dialog->sip_dlg_local_uri_tag = thdr;
+		}
+		dialog->sip_dlg_state = SIP_DLG_CONFIRMED;
+	} else {
+		resp_code = sip_get_response_code((sip_msg_t)sip_msg, &error);
+		(void) pthread_mutex_lock(&dialog->sip_dlg_mutex);
+		assert(dialog->sip_dlg_state == SIP_DLG_NEW);
+		if (dialog->sip_dlg_remote_target == NULL && chdr != NULL) {
+			assert(dialog->sip_dlg_type == SIP_UAC_DIALOG);
+			if ((dialog->sip_dlg_remote_target =
+			    sip_dup_header(chdr)) == NULL) {
+				(void) pthread_mutex_unlock(
+				    &dialog->sip_dlg_mutex);
+				sip_dialog_terminate(dialog,
+				    (sip_msg_t)sip_msg);
+				if (alloc_thdr)
+					sip_free_header(thdr);
+				return (NULL);
+			}
+			if (sip_dialog_get_route_set(dialog, sip_msg,
+			    dialog->sip_dlg_type) != 0) {
+				(void) pthread_mutex_unlock(
+				    &dialog->sip_dlg_mutex);
+				sip_dialog_terminate(dialog,
+				    (sip_msg_t)sip_msg);
+				if (alloc_thdr)
+					sip_free_header(thdr);
+				return (NULL);
+			}
+		}
+		if (SIP_PROVISIONAL_RESP(resp_code)) {
+			dialog->sip_dlg_state = SIP_DLG_EARLY;
+		} else if (SIP_OK_RESP(resp_code)) {
+			/*
+			 * Per 12.1 the UAS must include the contact header
+			 * for a dialog establishing response, so if we
+			 * don't find one, we terminate it.
+			 */
+			if (dialog->sip_dlg_remote_target == NULL) {
+				(void) pthread_mutex_unlock(
+				    &dialog->sip_dlg_mutex);
+				if (sip_ulp_dlg_del_cb != NULL) {
+					sip_ulp_dlg_del_cb(dialog,
+					    (sip_msg_t)sip_msg, NULL);
+				}
+				sip_dialog_terminate(dialog,
+				    (sip_msg_t)sip_msg);
+				if (alloc_thdr)
+					sip_free_header(thdr);
+				return (NULL);
+			}
+			dialog->sip_dlg_state = SIP_DLG_CONFIRMED;
+		} else {
+			(void) pthread_mutex_unlock(&dialog->sip_dlg_mutex);
+			if (sip_ulp_dlg_del_cb != NULL) {
+				sip_ulp_dlg_del_cb(dialog, (sip_msg_t)sip_msg,
+				    NULL);
+			}
+			sip_dialog_terminate(dialog, (sip_msg_t)sip_msg);
+			if (alloc_thdr)
+				sip_free_header(thdr);
+			return (NULL);
+		}
+		if (dialog->sip_dlg_type == SIP_UAS_DIALOG) {
+			dialog->sip_dlg_local_uri_tag = thdr;
+		} else {
+			if ((dialog->sip_dlg_remote_uri_tag =
+			    sip_dup_header(thdr)) == NULL) {
+				(void) pthread_mutex_unlock(
+				    &dialog->sip_dlg_mutex);
+				sip_dialog_terminate(dialog,
+				    (sip_msg_t)sip_msg);
+				return (NULL);
+			}
+		}
+	}
+
+	/*
+	 * Cancel the partial dialog timer
+	 */
+	if (SIP_IS_TIMER_RUNNING(dialog->sip_dlg_timer))
+		SIP_CANCEL_TIMER(dialog->sip_dlg_timer);
+
+	if (dialog->sip_dlg_type == SIP_UAC_DIALOG) {
+		val =  sip_get_header_value(dialog->sip_dlg_local_uri_tag,
+		    &error);
+	} else {
+		val =  sip_get_header_value(dialog->sip_dlg_remote_uri_tag,
+		    &error);
+	}
+	assert(val != NULL && error == 0);
+	remtag = sip_get_param_value((sip_header_value_t)val, "tag", &error);
+
+	val = sip_get_header_value(dialog->sip_dlg_call_id, &error);
+	callid = &((sip_hdr_value_t *)val)->str_val;
+
+	/*
+	 * Get an ID for this dialog
+	 */
+	if (dialog->sip_dlg_type == SIP_UAC_DIALOG) {
+		sip_md5_hash(remtag->sip_str_ptr, remtag->sip_str_len,
+		    ttag->sip_str_ptr, ttag->sip_str_len,
+		    callid->sip_str_ptr, callid->sip_str_len,
+		    NULL, 0, NULL, 0, NULL, 0, (uchar_t *)dialog->sip_dlg_id);
+	} else {
+		sip_md5_hash(ttag->sip_str_ptr, ttag->sip_str_len,
+		    remtag->sip_str_ptr, remtag->sip_str_len,
+		    callid->sip_str_ptr, callid->sip_str_len,
+		    NULL, 0, NULL, 0, NULL, 0, (uchar_t *)dialog->sip_dlg_id);
+	}
+
+	SIP_DLG_REFCNT_INCR(dialog);
+	(void) pthread_mutex_unlock(&dialog->sip_dlg_mutex);
+
+	/*
+	 * Add it to the hash table
+	 */
+	if (sip_hash_add(sip_dialog_hash, (void *)dialog,
+	    SIP_DIGEST_TO_HASH(dialog->sip_dlg_id)) != 0) {
+		/*
+		 * So that sip_dialog_delete() does not try to remove
+		 * this from the hash table.
+		 */
+		(void) pthread_mutex_lock(&dialog->sip_dlg_mutex);
+		if (dialog->sip_dlg_type == SIP_UAS_DIALOG) {
+			sip_free_header(dialog->sip_dlg_local_uri_tag);
+			dialog->sip_dlg_local_uri_tag = NULL;
+		} else {
+			sip_free_header(dialog->sip_dlg_remote_uri_tag);
+			dialog->sip_dlg_remote_uri_tag = NULL;
+		}
+		(void) pthread_mutex_unlock(&dialog->sip_dlg_mutex);
+		sip_dialog_terminate(dialog, (sip_msg_t)sip_msg);
+		return (NULL);
+	}
+	if (sip_dlg_ulp_state_cb != NULL) {
+		sip_dlg_ulp_state_cb((sip_dialog_t)dialog,
+		    (sip_msg_t)sip_msg, prev_state, dialog->sip_dlg_state);
+	}
+	return ((sip_dialog_t)dialog);
+}
+
+/*
+ * Check if this dialog is a match.
+ */
+boolean_t
+sip_dialog_match(void *obj, void *hindex)
+{
+	_sip_dialog_t	*dialog = (_sip_dialog_t *)obj;
+
+	(void) pthread_mutex_lock(&dialog->sip_dlg_mutex);
+	if (dialog->sip_dlg_state == SIP_DLG_DESTROYED) {
+		(void) pthread_mutex_unlock(&dialog->sip_dlg_mutex);
+		return (B_FALSE);
+	}
+	if (bcmp(dialog->sip_dlg_id, hindex,
+	    sizeof (dialog->sip_dlg_id)) == 0) {
+		SIP_DLG_REFCNT_INCR(dialog);
+		(void) pthread_mutex_unlock(&dialog->sip_dlg_mutex);
+		return (B_TRUE);
+	}
+	(void) pthread_mutex_unlock(&dialog->sip_dlg_mutex);
+	return (B_FALSE);
+}
+
+/*
+ * Don't delete, just take it out of the hash
+ */
+boolean_t
+sip_dialog_dontfree(void *obj, void *hindex, int *found)
+{
+	_sip_dialog_t	*dialog = (_sip_dialog_t *)obj;
+
+	*found = 0;
+	(void) pthread_mutex_lock(&dialog->sip_dlg_mutex);
+	if (bcmp(dialog->sip_dlg_id, hindex, sizeof (dialog->sip_dlg_id))
+	    == 0) {
+		*found = 1;
+		(void) pthread_mutex_unlock(&dialog->sip_dlg_mutex);
+		return (B_TRUE);
+	}
+	(void) pthread_mutex_unlock(&dialog->sip_dlg_mutex);
+	return (B_FALSE);
+}
+
+/*
+ * Free resources associated with the dialog, the object will be removed
+ * from the hash list by sip_hash_delete.
+ */
+boolean_t
+sip_dialog_free(void *obj, void *hindex, int *found)
+{
+	_sip_dialog_t	*dialog = (_sip_dialog_t *)obj;
+
+	*found = 0;
+	(void) pthread_mutex_lock(&dialog->sip_dlg_mutex);
+	if (bcmp(dialog->sip_dlg_id, hindex, sizeof (dialog->sip_dlg_id))
+	    == 0) {
+		*found = 1;
+		assert(dialog->sip_dlg_state == SIP_DLG_DESTROYED);
+		if (dialog->sip_dlg_ref_cnt != 0) {
+			(void) pthread_mutex_unlock(&dialog->sip_dlg_mutex);
+			return (B_FALSE);
+		}
+		sip_release_dialog_res(dialog);
+		return (B_TRUE);
+	}
+	(void) pthread_mutex_unlock(&dialog->sip_dlg_mutex);
+	return (B_FALSE);
+}
+
+/*
+ * The UAS will receive the request from the transaction layer.  If the
+ * request has a tag in the To header field, the UAS core computes the
+ * dialog identifier corresponding to the request and compares it with
+ * existing dialogs.  If there is a match, this is a mid-dialog request.
+ */
+sip_dialog_t
+sip_dialog_find(_sip_msg_t *sip_msg)
+{
+	const sip_str_t	*localtag;
+	const sip_str_t	*remtag;
+	const sip_str_t	*callid;
+	uint16_t	digest[8];
+	_sip_dialog_t	*dialog;
+	boolean_t	is_request;
+	int		error;
+
+	is_request = sip_msg_is_request((sip_msg_t)sip_msg, &error);
+	if (error != 0)
+		return (NULL);
+	if (is_request) {
+		localtag = sip_get_to_tag((sip_msg_t)sip_msg, &error);
+		if (error == 0)
+			remtag = sip_get_from_tag((sip_msg_t)sip_msg, &error);
+	} else {
+		remtag = sip_get_to_tag((sip_msg_t)sip_msg, &error);
+		if (error == 0)
+			localtag = sip_get_from_tag((sip_msg_t)sip_msg, &error);
+	}
+	if (error != 0)
+		return (NULL);
+	callid = sip_get_callid((sip_msg_t)sip_msg, &error);
+	if (error != 0 || remtag == NULL || localtag == NULL ||
+	    callid == NULL) {
+		return (NULL);
+	}
+	sip_md5_hash(localtag->sip_str_ptr, localtag->sip_str_len,
+	    remtag->sip_str_ptr, remtag->sip_str_len,
+	    callid->sip_str_ptr, callid->sip_str_len,
+	    NULL, 0, NULL, 0, NULL, 0, (uchar_t *)digest);
+
+	dialog = (_sip_dialog_t *)sip_hash_find(sip_dialog_hash,
+	    (void *)digest, SIP_DIGEST_TO_HASH(digest), sip_dialog_match);
+	if (dialog == NULL) {
+		sip_md5_hash(localtag->sip_str_ptr, localtag->sip_str_len,
+		    NULL, 0, callid->sip_str_ptr, callid->sip_str_len,
+		    NULL, 0, NULL, 0, NULL, 0, (uchar_t *)digest);
+		dialog = (_sip_dialog_t *)sip_hash_find(sip_dialog_phash,
+		    (void *)digest, SIP_DIGEST_TO_HASH(digest),
+		    sip_dialog_match);
+	}
+	return ((sip_dialog_t)dialog);
+}
+
+/*
+ * We keep this partial dialog for the duration of the INVITE
+ * transaction timeout duration, i.e. Timer B.
+ */
+void
+sip_dlg_self_destruct(void *args)
+{
+	sip_dialog_timer_obj_t	*tim_obj = (sip_dialog_timer_obj_t *)args;
+	_sip_dialog_t		*dialog = (_sip_dialog_t *)tim_obj->dialog;
+	int			index;
+
+	(void) pthread_mutex_lock(&dialog->sip_dlg_mutex);
+	assert(dialog->sip_dlg_state == SIP_DLG_NEW);
+	dialog->sip_dlg_state = SIP_DLG_DESTROYED;
+	if (dialog->sip_dlg_type == SIP_UAC_DIALOG) {
+		index = SIP_DIGEST_TO_HASH(dialog->sip_dlg_id);
+		(void) pthread_mutex_unlock(&dialog->sip_dlg_mutex);
+		sip_hash_delete(sip_dialog_phash, (void *)dialog->sip_dlg_id,
+		    index, sip_dialog_dontfree);
+	} else {
+		(void) pthread_mutex_unlock(&dialog->sip_dlg_mutex);
+	}
+	if (tim_obj->func != NULL)
+		tim_obj->func(dialog, NULL, NULL);
+	free(tim_obj);
+	SIP_DLG_REFCNT_DECR(dialog);
+}
+
+/*
+ * Terminate a dialog
+ */
+void
+sip_dialog_terminate(_sip_dialog_t *dialog, sip_msg_t sip_msg)
+{
+	int	prev_state;
+
+	(void) pthread_mutex_lock(&dialog->sip_dlg_mutex);
+	prev_state = dialog->sip_dlg_state;
+	dialog->sip_dlg_state = SIP_DLG_DESTROYED;
+	(void) pthread_mutex_unlock(&dialog->sip_dlg_mutex);
+	if (sip_dlg_ulp_state_cb != NULL) {
+		sip_dlg_ulp_state_cb((sip_dialog_t)dialog, sip_msg, prev_state,
+		    dialog->sip_dlg_state);
+	}
+	SIP_DLG_REFCNT_DECR(dialog);
+}
+
+/*
+ * Delete a dialog
+ */
+void
+sip_dialog_delete(_sip_dialog_t *dialog)
+{
+	int	index;
+
+	/*
+	 * partial dialog, not in the hash table
+	 */
+	if (dialog->sip_dlg_local_uri_tag == NULL ||
+	    dialog->sip_dlg_remote_uri_tag == NULL) {
+		/*
+		 * Cancel the partial dialog timer
+		 */
+		if (SIP_IS_TIMER_RUNNING(dialog->sip_dlg_timer))
+			SIP_CANCEL_TIMER(dialog->sip_dlg_timer);
+		sip_release_dialog_res(dialog);
+		return;
+	}
+	index = SIP_DIGEST_TO_HASH(dialog->sip_dlg_id);
+	sip_hash_delete(sip_dialog_hash, (void *)dialog->sip_dlg_id, index,
+	    sip_dialog_free);
+}
+
+/*
+ * Get the remote target from the CONTACT header from the 200 OK response
+ */
+static boolean_t
+sip_get_rtarg(_sip_dialog_t *dialog, _sip_msg_t *sip_msg)
+{
+	sip_header_t	chdr;
+
+	if (dialog->sip_dlg_remote_target != NULL)
+		return (B_TRUE);
+
+	(void) pthread_mutex_lock(&sip_msg->sip_msg_mutex);
+	chdr = sip_search_for_header(sip_msg, SIP_CONTACT, NULL);
+	(void) pthread_mutex_unlock(&sip_msg->sip_msg_mutex);
+	if (chdr == NULL)
+		return (B_FALSE);
+	if ((dialog->sip_dlg_remote_target = sip_dup_header(chdr)) == NULL)
+		return (B_FALSE);
+
+	return (B_TRUE);
+}
+
+/*
+ * Process an incoming request/response
+ */
+/* ARGSUSED */
+int
+sip_dialog_process(_sip_msg_t *sip_msg, sip_dialog_t *sip_dialog)
+{
+	boolean_t	request;
+	_sip_dialog_t	*_dialog;
+	int		error;
+
+	request = sip_msg_is_request((sip_msg_t)sip_msg, &error);
+	if (error != 0)
+		return (EINVAL);
+	_dialog = (_sip_dialog_t *)*sip_dialog;
+	if (request) {
+		uint32_t	cseq;
+		sip_method_t	method;
+
+		cseq = sip_get_callseq_num((sip_msg_t)sip_msg, &error);
+		if (error != 0)
+			return (EINVAL);
+		method = sip_get_callseq_method((sip_msg_t)sip_msg, &error);
+		if (error != 0)
+			return (EINVAL);
+		if (sip_get_request_method((sip_msg_t)sip_msg, &error) !=
+		    method) {
+			return (EINVAL);
+		}
+		(void) pthread_mutex_lock(&_dialog->sip_dlg_mutex);
+		/*
+		 * Requests that do not change in any way the state
+		 * of a dialog may be received within a dialog.
+		 * They are processed as if they had been received
+		 * outside the dialog.
+		 * For dialogs that have been established with an
+		 * INVITE, the only target refresh request defined is
+		 * re-INVITE.
+		 */
+		if (_dialog->sip_dlg_method == INVITE &&
+		    method == INVITE && _dialog->sip_dlg_remote_cseq != 0 &&
+		    SIP_CSEQ_LT(cseq, _dialog->sip_dlg_remote_cseq)) {
+			(void) pthread_mutex_unlock(&_dialog->sip_dlg_mutex);
+			return (EPROTO);
+		}
+		/*
+		 * Target-Refresh request
+		 */
+		if (_dialog->sip_dlg_method == INVITE && method == INVITE) {
+			sip_header_t	chdr;
+			sip_header_t	nchdr;
+
+			(void) pthread_mutex_lock(&sip_msg->sip_msg_mutex);
+			chdr = sip_search_for_header(sip_msg, SIP_CONTACT,
+			    NULL);
+			(void) pthread_mutex_unlock(&sip_msg->sip_msg_mutex);
+			if (chdr != NULL &&
+			    (nchdr = sip_dup_header(chdr)) != NULL) {
+				if (_dialog->sip_dlg_remote_target != NULL) {
+					sip_free_header(
+					    _dialog->sip_dlg_remote_target);
+				}
+				_dialog->sip_dlg_remote_target = nchdr;
+			}
+		}
+		_dialog->sip_dlg_remote_cseq = cseq;
+		(void) pthread_mutex_unlock(&_dialog->sip_dlg_mutex);
+	} else {
+		int		resp_code;
+		sip_method_t	method;
+		int		error;
+
+		resp_code = sip_get_response_code((sip_msg_t)sip_msg, &error);
+		if (error == 0) {
+			method = sip_get_callseq_method((sip_msg_t)sip_msg,
+			    &error);
+		}
+		if (error != 0)
+			return (error);
+
+		(void) pthread_mutex_lock(&_dialog->sip_dlg_mutex);
+		assert(_dialog->sip_dlg_state == SIP_DLG_EARLY ||
+		    _dialog->sip_dlg_state == SIP_DLG_CONFIRMED);
+		/*
+		 * Let the user delete the dialog if it is not a 1XX/2XX resp
+		 * for an early INVITE dialog.
+		 */
+		if (SIP_OK_RESP(resp_code)) {
+			if (method == INVITE) {
+				if (!sip_get_rtarg(_dialog, sip_msg)) {
+					(void) pthread_mutex_unlock(
+					    &_dialog->sip_dlg_mutex);
+					if (sip_ulp_dlg_del_cb != NULL) {
+						sip_ulp_dlg_del_cb(
+						    (sip_dialog_t)_dialog,
+						    (sip_msg_t)sip_msg, NULL);
+					}
+					sip_dialog_terminate(_dialog,
+					    (sip_msg_t)sip_msg);
+					return (0);
+				}
+				if (_dialog->sip_dlg_state == SIP_DLG_EARLY) {
+					_dialog->sip_dlg_state =
+					    SIP_DLG_CONFIRMED;
+					(void) pthread_mutex_unlock(
+					    &_dialog->sip_dlg_mutex);
+					(void) sip_dlg_recompute_rset(_dialog,
+					    sip_msg, SIP_UAC_DIALOG);
+					if (sip_dlg_ulp_state_cb != NULL) {
+						sip_dlg_ulp_state_cb(
+						    (sip_dialog_t)_dialog,
+						    sip_msg, SIP_DLG_EARLY,
+						    _dialog->sip_dlg_state);
+					}
+					return (0);
+				}
+			}
+		}
+		(void) pthread_mutex_unlock(&_dialog->sip_dlg_mutex);
+	}
+	return (0);
+}
+
+/*
+ * Copy partial dialog to create a complete dialog
+ */
+_sip_dialog_t *
+sip_copy_partial_dialog(_sip_dialog_t *dialog)
+{
+	_sip_dialog_t	*new_dlg;
+
+	new_dlg =  calloc(1, sizeof (_sip_dialog_t));
+	if (new_dlg == NULL)
+		return (NULL);
+	if (dialog->sip_dlg_req_uri.sip_str_ptr != NULL) {
+		new_dlg->sip_dlg_req_uri.sip_str_ptr =
+		    malloc(dialog->sip_dlg_req_uri.sip_str_len + 1);
+		if (new_dlg->sip_dlg_req_uri.sip_str_ptr == NULL) {
+			free(new_dlg);
+			return (NULL);
+		}
+		(void) strncpy(new_dlg->sip_dlg_req_uri.sip_str_ptr,
+		    dialog->sip_dlg_req_uri.sip_str_ptr,
+		    dialog->sip_dlg_req_uri.sip_str_len);
+		new_dlg->sip_dlg_req_uri.sip_str_ptr[
+		    dialog->sip_dlg_req_uri.sip_str_len] = '\0';
+		new_dlg->sip_dlg_req_uri.sip_str_len =
+		    dialog->sip_dlg_req_uri.sip_str_len;
+	}
+	if (dialog->sip_dlg_route_set != NULL) {
+		assert(dialog->sip_dlg_rset.sip_str_ptr != NULL);
+		new_dlg->sip_dlg_rset.sip_str_ptr =
+		    malloc(dialog->sip_dlg_rset.sip_str_len + 1);
+		if (new_dlg->sip_dlg_rset.sip_str_ptr == NULL) {
+			if (new_dlg->sip_dlg_req_uri.sip_str_ptr != NULL)
+				free(new_dlg->sip_dlg_req_uri.sip_str_ptr);
+			free(new_dlg);
+			return (NULL);
+		}
+		(void) strncpy(new_dlg->sip_dlg_rset.sip_str_ptr,
+		    dialog->sip_dlg_rset.sip_str_ptr,
+		    dialog->sip_dlg_rset.sip_str_len);
+		new_dlg->sip_dlg_rset.sip_str_ptr[
+		    dialog->sip_dlg_rset.sip_str_len] = '\0';
+		new_dlg->sip_dlg_rset.sip_str_len =
+		    dialog->sip_dlg_rset.sip_str_len;
+
+		new_dlg->sip_dlg_route_set =
+		    sip_dup_header(dialog->sip_dlg_route_set);
+		if (new_dlg->sip_dlg_route_set == NULL) {
+			free(new_dlg->sip_dlg_rset.sip_str_ptr);
+			if (new_dlg->sip_dlg_req_uri.sip_str_ptr != NULL)
+				free(new_dlg->sip_dlg_req_uri.sip_str_ptr);
+			free(new_dlg);
+			return (NULL);
+		}
+	}
+	if ((new_dlg->sip_dlg_local_uri_tag =
+	    sip_dup_header(dialog->sip_dlg_local_uri_tag)) == NULL ||
+	    (new_dlg->sip_dlg_remote_target =
+	    sip_dup_header(dialog->sip_dlg_remote_target)) == NULL ||
+	    (new_dlg->sip_dlg_call_id =
+	    sip_dup_header(dialog->sip_dlg_call_id)) == NULL) {
+		sip_release_dialog_res(new_dlg);
+		return (NULL);
+	}
+	if (dialog->sip_dlg_event != NULL) {
+		new_dlg->sip_dlg_event = sip_dup_header(dialog->sip_dlg_event);
+		if (new_dlg->sip_dlg_event == NULL) {
+			sip_release_dialog_res(new_dlg);
+			return (NULL);
+		}
+	}
+	new_dlg->sip_dlg_local_cseq = dialog->sip_dlg_local_cseq;
+	new_dlg->sip_dlg_type = dialog->sip_dlg_type;
+	new_dlg->sip_dlg_on_fork = B_FALSE;
+	(void) pthread_mutex_init(&new_dlg->sip_dlg_mutex, NULL);
+
+	return (new_dlg);
+}
+
+/*
+ * Update the dialog using the response
+ */
+sip_dialog_t
+sip_update_dialog(sip_dialog_t dialog, _sip_msg_t *sip_msg)
+{
+	_sip_dialog_t	*_dialog;
+	boolean_t	isreq;
+	sip_method_t	method;
+	int		resp_code = 0;
+	int		prev_state;
+	boolean_t	decr_ref = B_FALSE;
+	int		error;
+
+	isreq = sip_msg_is_request((sip_msg_t)sip_msg, &error);
+	if (error != 0)
+		return (dialog);
+	_dialog = (_sip_dialog_t *)dialog;
+	(void) pthread_mutex_lock(&_dialog->sip_dlg_mutex);
+	if (isreq) {
+		method = sip_get_request_method((sip_msg_t)sip_msg, &error);
+		if (error != 0 || _dialog->sip_dlg_method != SUBSCRIBE ||
+		    method != NOTIFY) {
+			(void) pthread_mutex_unlock(&_dialog->sip_dlg_mutex);
+			return (dialog);
+		}
+	} else {
+		resp_code = sip_get_response_code((sip_msg_t)sip_msg, &error);
+		if (error != 0) {
+			(void) pthread_mutex_unlock(&_dialog->sip_dlg_mutex);
+			return (dialog);
+		}
+	}
+	prev_state = _dialog->sip_dlg_state;
+	if (_dialog->sip_dlg_state == SIP_DLG_CONFIRMED) {
+		(void) pthread_mutex_unlock(&_dialog->sip_dlg_mutex);
+	} else if (_dialog->sip_dlg_state == SIP_DLG_EARLY) {
+		/*
+		 * Let the user delete the dialog if it is not a 1XX/2XX resp
+		 * for an early dialog.
+		 */
+		assert(!isreq);
+		if (SIP_OK_RESP(resp_code)) {
+			_dialog->sip_dlg_state = SIP_DLG_CONFIRMED;
+			(void) pthread_mutex_unlock(&_dialog->sip_dlg_mutex);
+			(void) sip_dlg_recompute_rset(_dialog, sip_msg,
+			    SIP_UAS_DIALOG);
+			if (sip_dlg_ulp_state_cb != NULL) {
+				sip_dlg_ulp_state_cb(dialog, (sip_msg_t)sip_msg,
+				    prev_state, dialog->sip_dlg_state);
+			}
+		} else {
+			(void) pthread_mutex_unlock(&_dialog->sip_dlg_mutex);
+		}
+	} else if (_dialog->sip_dlg_state == SIP_DLG_NEW) {
+		if (!isreq && _dialog->sip_dlg_method == SUBSCRIBE &&
+		    SIP_PROVISIONAL_RESP(resp_code)) {
+			(void) pthread_mutex_unlock(&_dialog->sip_dlg_mutex);
+			return (dialog);
+		}
+		if (_dialog->sip_dlg_type == SIP_UAC_DIALOG) {
+			_sip_dialog_t	*new_dlg;
+
+			if (_dialog->sip_dlg_on_fork) {
+				new_dlg = sip_copy_partial_dialog(_dialog);
+				if (new_dlg == NULL) {
+					(void) pthread_mutex_unlock(
+					    &_dialog->sip_dlg_mutex);
+					return (dialog);
+				}
+				/*
+				 * This decr/incr dance is because the caller
+				 * has incremented the ref on the partial
+				 * dialog, we release it here and incr the
+				 * ref on the new dialog which will be
+				 * released by the caller.
+				 */
+				(void) pthread_mutex_unlock(
+				    &_dialog->sip_dlg_mutex);
+				SIP_DLG_REFCNT_DECR(_dialog);
+				_dialog = new_dlg;
+				(void) pthread_mutex_lock(
+				    &_dialog->sip_dlg_mutex);
+				SIP_DLG_REFCNT_INCR(_dialog);
+			} else {
+				int	index;
+
+				/*
+				 * take it out of the list so that further
+				 * responses will not result in a dialog.
+				 * We will have an extra refcount when we
+				 * come back from sip_complete_dialog(), i.e.
+				 * one when the partial dialog was created -
+				 * in sip_seed_dialog(), one held by the caller
+				 * and one that will be added by
+				 * sip_complete_dialog(). We need to release
+				 * the one added by the sip_seed_dialog(),
+				 * since the one in sip_complete_dialog()
+				 * is for the same purpose.
+				 */
+				if (SIP_IS_TIMER_RUNNING(
+				    _dialog->sip_dlg_timer)) {
+					SIP_CANCEL_TIMER(
+					    _dialog->sip_dlg_timer);
+				}
+				index = SIP_DIGEST_TO_HASH(dialog->sip_dlg_id);
+				(void) pthread_mutex_unlock(
+				    &_dialog->sip_dlg_mutex);
+				sip_hash_delete(sip_dialog_phash,
+				    (void *)_dialog->sip_dlg_id,
+				    index, sip_dialog_dontfree);
+				(void) pthread_mutex_lock(
+				    &_dialog->sip_dlg_mutex);
+				decr_ref = B_TRUE;
+			}
+		} else {
+			decr_ref = B_TRUE;
+		}
+		(void) pthread_mutex_unlock(&_dialog->sip_dlg_mutex);
+		if ((dialog = sip_complete_dialog(sip_msg, _dialog)) ==
+		    NULL) {
+			return (NULL);
+		}
+		if (decr_ref)
+			SIP_DLG_REFCNT_DECR(_dialog);
+	} else {
+		(void) pthread_mutex_unlock(&_dialog->sip_dlg_mutex);
+	}
+	return (dialog);
+}
+
+/*
+ * Initialize the hash table
+ */
+void
+sip_dialog_init(void (*ulp_dlg_del) (sip_dialog_t, sip_msg_t, void *),
+    void (*ulp_state_cb)(sip_dialog_t, sip_msg_t, int, int))
+{
+	int	cnt;
+
+	for (cnt = 0; cnt < SIP_HASH_SZ; cnt++) {
+		sip_dialog_hash[cnt].hash_count = 0;
+		sip_dialog_hash[cnt].hash_head = NULL;
+		sip_dialog_hash[cnt].hash_tail = NULL;
+		(void) pthread_mutex_init(
+		    &sip_dialog_hash[cnt].sip_hash_mutex, NULL);
+		sip_dialog_phash[cnt].hash_count = 0;
+		sip_dialog_phash[cnt].hash_head = NULL;
+		sip_dialog_phash[cnt].hash_tail = NULL;
+		(void) pthread_mutex_init(
+		    &sip_dialog_phash[cnt].sip_hash_mutex, NULL);
+	}
+	if (ulp_dlg_del != NULL)
+		sip_ulp_dlg_del_cb = ulp_dlg_del;
+
+	if (ulp_state_cb != NULL)
+		sip_dlg_ulp_state_cb = ulp_state_cb;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/lib/libsip/common/sip_dialog.h	Sat Oct 07 14:26:26 2006 -0700
@@ -0,0 +1,102 @@
+/*
+ * 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.
+ */
+
+#ifndef	_SIP_DIALOG_H
+#define	_SIP_DIALOG_H
+
+#pragma ident	"%Z%%M%	%I%	%E% SMI"
+
+#ifdef	__cplusplus
+extern "C" {
+#endif
+
+/*
+ * Dialogs are linked in their own list.
+ */
+
+
+/* This is always done within sip_dlg_mutex */
+#define	SIP_DLG_REFCNT_INCR(dialog)					\
+	(dialog)->sip_dlg_ref_cnt++;
+
+#define	SIP_DLG_REFCNT_DECR(dialog)	 {				\
+	(void) pthread_mutex_lock(&((dialog)->sip_dlg_mutex));		\
+	assert((dialog)->sip_dlg_ref_cnt > 0);				\
+	(dialog)->sip_dlg_ref_cnt--;					\
+	if ((dialog)->sip_dlg_ref_cnt == 0 &&				\
+	    (dialog)->sip_dlg_state == SIP_DLG_DESTROYED) {		\
+		(void) pthread_mutex_unlock(&((dialog)->sip_dlg_mutex)); \
+		sip_dialog_delete(dialog);				\
+	} else {							\
+		(void) pthread_mutex_unlock(&((dialog)->sip_dlg_mutex));\
+	}								\
+}
+
+/* The dialog structure */
+typedef struct sip_dialog
+{
+	_sip_header_t		*sip_dlg_remote_uri_tag;
+	_sip_header_t		*sip_dlg_local_uri_tag;
+	_sip_header_t		*sip_dlg_remote_target;
+	_sip_header_t		*sip_dlg_route_set;
+	_sip_header_t		*sip_dlg_event;
+	sip_str_t		sip_dlg_rset;
+	sip_str_t		sip_dlg_req_uri;
+	_sip_header_t		*sip_dlg_call_id;
+	uint32_t		sip_dlg_local_cseq;
+	uint32_t		sip_dlg_remote_cseq;
+	uint16_t		sip_dlg_id[8];
+	boolean_t		sip_dlg_secure;
+	dialog_state_t		sip_dlg_state;
+	int			sip_dlg_type;	/* CALLEE or CALLER */
+	pthread_mutex_t		sip_dlg_mutex;
+	uint32_t		sip_dlg_ref_cnt;
+	sip_timer_t		sip_dlg_timer;	/* to delete partial dialogs */
+	boolean_t		sip_dlg_on_fork;
+	sip_method_t		sip_dlg_method;
+	void			*sip_dlg_ctxt;	/* currently unused */
+} _sip_dialog_t;
+
+void			sip_dialog_init(void (*sip_ulp_dlg_del)(sip_dialog_t,
+			    sip_msg_t, void *),
+			    void (*ulp_dlg_state)(sip_dialog_t, sip_msg_t,
+			    int, int));
+sip_dialog_t		sip_dialog_create(_sip_msg_t *, _sip_msg_t *, int);
+sip_dialog_t		sip_dialog_find(_sip_msg_t *);
+int			sip_dialog_process(_sip_msg_t *, sip_dialog_t *);
+sip_dialog_t		sip_update_dialog(sip_dialog_t, _sip_msg_t *);
+void			sip_dialog_terminate(sip_dialog_t, sip_msg_t);
+sip_dialog_t		sip_seed_dialog(sip_conn_object_t, _sip_msg_t *,
+			    boolean_t, int);
+char			*sip_dialog_req_uri(sip_dialog_t);
+void			sip_dialog_delete(_sip_dialog_t *);
+extern boolean_t	sip_incomplete_dialog(sip_dialog_t);
+
+#ifdef	__cplusplus
+}
+#endif
+
+#endif	/* _SIP_DIALOG_H */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/lib/libsip/common/sip_dialog_ui.c	Sat Oct 07 14:26:26 2006 -0700
@@ -0,0 +1,531 @@
+/*
+ * 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_dialog.h"
+#include "sip_xaction.h"
+
+/*
+ * Create a request using the state maintained in the dialog.
+ */
+sip_msg_t
+sip_create_dialog_req(sip_method_t method, sip_dialog_t dialog,
+    char *transport, char *sent_by, int sent_by_port, char *via_param,
+    uint32_t maxforward, int cseq)
+{
+	_sip_dialog_t	*_dialog;
+	sip_msg_t	sip_msg;
+	char		*uri;
+	int		oldseq = 0;
+
+	if (!sip_manage_dialog || dialog == NULL || transport == NULL ||
+	    sent_by == NULL) {
+		return (NULL);
+	}
+	if ((sip_msg = sip_new_msg()) == NULL)
+		return (NULL);
+	_dialog = (_sip_dialog_t *)dialog;
+	(void) pthread_mutex_lock(&_dialog->sip_dlg_mutex);
+	/*
+	 * Depending on the route set, if any, the request URI could either
+	 * be the contact URI or the 1st URI from the route set.
+	 */
+	uri = (char *)sip_dialog_req_uri(_dialog);
+	if (uri == NULL)
+		goto err_ret;
+	if (sip_add_request_line(sip_msg, method, uri) != 0) {
+		free(uri);
+		goto err_ret;
+	}
+	free(uri);
+	if (sip_copy_header(sip_msg, _dialog->sip_dlg_local_uri_tag, NULL) != 0)
+		goto err_ret;
+	if (sip_copy_header(sip_msg, _dialog->sip_dlg_remote_uri_tag, NULL) !=
+	    0) {
+		goto err_ret;
+	}
+	if (sip_copy_header(sip_msg, _dialog->sip_dlg_remote_target, NULL) != 0)
+		goto err_ret;
+	if (sip_add_via(sip_msg, transport, sent_by, sent_by_port, via_param) !=
+	    0) {
+		goto err_ret;
+	}
+	if (sip_add_maxforward(sip_msg, maxforward) != 0)
+		goto err_ret;
+	if (sip_copy_header(sip_msg, _dialog->sip_dlg_call_id, NULL) != 0)
+		goto err_ret;
+	if (cseq < 0) {
+		if (_dialog->sip_dlg_local_cseq == 0)
+			_dialog->sip_dlg_local_cseq = 1;
+		oldseq = _dialog->sip_dlg_local_cseq;
+		cseq = ++_dialog->sip_dlg_local_cseq;
+	}
+	if (sip_add_cseq(sip_msg, method, cseq) != 0) {
+		_dialog->sip_dlg_local_cseq = oldseq;
+		goto err_ret;
+	}
+	/*
+	 * The route set, even if empty, overrides any pre-existing route set.
+	 * If the route set is empty, the UAC MUST NOT add a Route header
+	 * field to the request.
+	 */
+	(void) sip_delete_header_by_name(sip_msg, SIP_ROUTE);
+
+	if (_dialog->sip_dlg_route_set != NULL) {
+		if (sip_copy_header(sip_msg, _dialog->sip_dlg_route_set,
+		    NULL) != 0) {
+			_dialog->sip_dlg_local_cseq = oldseq;
+			goto err_ret;
+		}
+	}
+	(void) pthread_mutex_unlock(&_dialog->sip_dlg_mutex);
+	return (sip_msg);
+err_ret:
+	(void) pthread_mutex_unlock(&_dialog->sip_dlg_mutex);
+	sip_free_msg(sip_msg);
+	return (NULL);
+}
+
+/*
+ * Get the Dialog method
+ */
+int
+sip_get_dialog_method(sip_dialog_t dialog, int *error)
+{
+	_sip_dialog_t	*_dialog;
+
+	if (error != NULL)
+		*error = 0;
+	if (!sip_manage_dialog) {
+		if (error != NULL)
+			*error = EINVAL;
+		return (0);
+	}
+	if (dialog == NULL) {
+		if (error != NULL)
+			*error = EINVAL;
+		return (0);
+	}
+	_dialog = (_sip_dialog_t *)dialog;
+	return (_dialog->sip_dlg_method);
+}
+
+/*
+ * Get the Dialog state
+ */
+int
+sip_get_dialog_state(sip_dialog_t dialog, int *error)
+{
+	_sip_dialog_t	*_dialog;
+
+	if (error != NULL)
+		*error = 0;
+	if (!sip_manage_dialog) {
+		if (error != NULL)
+			*error = EINVAL;
+		return (0);
+	}
+	if (dialog == NULL) {
+		if (error != NULL)
+			*error = EINVAL;
+		return (0);
+	}
+	_dialog = (_sip_dialog_t *)dialog;
+	return (_dialog->sip_dlg_state);
+}
+
+/*
+ * Return the dialog callid
+ */
+const sip_str_t *
+sip_get_dialog_callid(sip_dialog_t dialog, int *error)
+{
+	_sip_dialog_t		*_dialog;
+	const struct sip_value	*val;
+	const sip_str_t		*callid = NULL;
+
+	if (error != NULL)
+		*error = 0;
+	if (!sip_manage_dialog || dialog == NULL) {
+		if (error != NULL)
+			*error = EINVAL;
+		return (NULL);
+	}
+	_dialog = (_sip_dialog_t *)dialog;
+	(void) pthread_mutex_lock(&_dialog->sip_dlg_mutex);
+	if (dialog->sip_dlg_call_id != NULL) {
+		val = sip_get_header_value(_dialog->sip_dlg_call_id, error);
+		if (val == NULL) {
+			(void) pthread_mutex_unlock(&_dialog->sip_dlg_mutex);
+			return (NULL);
+		}
+		callid = &((sip_hdr_value_t *)val)->str_val;
+	}
+	(void) pthread_mutex_unlock(&_dialog->sip_dlg_mutex);
+	return (callid);
+}
+
+/*
+ * Return the dialog localtag.
+ */
+const sip_str_t *
+sip_get_dialog_local_tag(sip_dialog_t dialog, int *error)
+{
+	_sip_dialog_t		*_dialog;
+	const sip_str_t		*ltag = NULL;
+	const struct sip_value	*val;
+
+	if (error != NULL)
+		*error = 0;
+	if (!sip_manage_dialog || dialog == NULL) {
+		if (error != NULL)
+			*error = EINVAL;
+		return (NULL);
+	}
+	_dialog = (_sip_dialog_t *)dialog;
+	(void) pthread_mutex_lock(&_dialog->sip_dlg_mutex);
+	if (dialog->sip_dlg_local_uri_tag != NULL) {
+		val = sip_get_header_value(_dialog->sip_dlg_local_uri_tag,
+		    error);
+		if (val == NULL) {
+			(void) pthread_mutex_unlock(&_dialog->sip_dlg_mutex);
+			return (NULL);
+		}
+		ltag = sip_get_param_value((sip_header_value_t)val, "tag",
+		    error);
+	}
+	(void) pthread_mutex_unlock(&_dialog->sip_dlg_mutex);
+	return (ltag);
+}
+
+/*
+ * Return the dialog remotetag
+ */
+const sip_str_t *
+sip_get_dialog_remote_tag(sip_dialog_t dialog, int *error)
+{
+	_sip_dialog_t		*_dialog;
+	const sip_str_t		*ttag = NULL;
+	const struct sip_value	*val;
+
+	if (error != NULL)
+		*error = 0;
+	if (!sip_manage_dialog || dialog == NULL) {
+		if (error != NULL)
+			*error = EINVAL;
+		return (NULL);
+	}
+	_dialog = (_sip_dialog_t *)dialog;
+	(void) pthread_mutex_lock(&_dialog->sip_dlg_mutex);
+	if (dialog->sip_dlg_remote_uri_tag != NULL) {
+		val = sip_get_header_value(_dialog->sip_dlg_remote_uri_tag,
+		    error);
+		if (val == NULL) {
+			(void) pthread_mutex_unlock(&_dialog->sip_dlg_mutex);
+			return (NULL);
+		}
+		ttag = sip_get_param_value((sip_header_value_t)val, "tag",
+		    error);
+	}
+	(void) pthread_mutex_unlock(&_dialog->sip_dlg_mutex);
+
+	return (ttag);
+}
+
+/*
+ * Return the dialog localuri.
+ */
+const struct sip_uri *
+sip_get_dialog_local_uri(sip_dialog_t dialog, int *error)
+{
+	_sip_dialog_t		*_dialog;
+	const _sip_uri_t	*luri = NULL;
+	const struct sip_value	*val;
+
+	if (error != NULL)
+		*error = 0;
+	if (!sip_manage_dialog || dialog == NULL) {
+		if (error != NULL)
+			*error = EINVAL;
+		return (NULL);
+	}
+	_dialog = (_sip_dialog_t *)dialog;
+	(void) pthread_mutex_lock(&_dialog->sip_dlg_mutex);
+	if (dialog->sip_dlg_local_uri_tag != NULL) {
+		val = sip_get_header_value(_dialog->sip_dlg_local_uri_tag,
+		    error);
+		if (val == NULL) {
+			(void) pthread_mutex_unlock(&_dialog->sip_dlg_mutex);
+			return (NULL);
+		}
+		luri = val->sip_value_parse_uri;
+	}
+	(void) pthread_mutex_unlock(&_dialog->sip_dlg_mutex);
+
+	return ((sip_uri_t)luri);
+}
+
+/*
+ * Return the dialog remoteuri.
+ */
+const struct sip_uri *
+sip_get_dialog_remote_uri(sip_dialog_t dialog, int *error)
+{
+	_sip_dialog_t		*_dialog;
+	const _sip_uri_t	*ruri = NULL;
+	const struct sip_value	*val;
+
+	if (error != NULL)
+		*error = 0;
+	if (!sip_manage_dialog || dialog == NULL) {
+		if (error != NULL)
+			*error = EINVAL;
+		return (NULL);
+	}
+	_dialog = (_sip_dialog_t *)dialog;
+	(void) pthread_mutex_lock(&_dialog->sip_dlg_mutex);
+	if (dialog->sip_dlg_remote_uri_tag != NULL) {
+		val = sip_get_header_value(dialog->sip_dlg_remote_uri_tag,
+		    error);
+		if (val == NULL) {
+			(void) pthread_mutex_unlock(&_dialog->sip_dlg_mutex);
+			return (NULL);
+		}
+		ruri = val->sip_value_parse_uri;
+	}
+	(void) pthread_mutex_unlock(&_dialog->sip_dlg_mutex);
+	return ((sip_uri_t)ruri);
+}
+
+/*
+ * Return the dialog remotetarg.
+ */
+const struct sip_uri *
+sip_get_dialog_remote_target_uri(sip_dialog_t dialog, int *error)
+{
+	_sip_dialog_t		*_dialog;
+	const struct sip_uri	*rtarg = NULL;
+	const struct sip_value	*val;
+
+	if (error != NULL)
+		*error = 0;
+	if (!sip_manage_dialog || dialog == NULL) {
+		if (error != NULL)
+			*error = EINVAL;
+		return (NULL);
+	}
+	_dialog = (_sip_dialog_t *)dialog;
+	(void) pthread_mutex_lock(&_dialog->sip_dlg_mutex);
+	if (dialog->sip_dlg_remote_target != NULL) {
+		val = sip_get_header_value(_dialog->sip_dlg_remote_target,
+		    error);
+		if (val == NULL) {
+			(void) pthread_mutex_unlock(&_dialog->sip_dlg_mutex);
+			return (NULL);
+		}
+		rtarg = val->sip_value_parse_uri;
+	}
+	(void) pthread_mutex_unlock(&_dialog->sip_dlg_mutex);
+
+	return ((sip_uri_t)rtarg);
+}
+
+/*
+ * Return the dialog route set
+ */
+const sip_str_t *
+sip_get_dialog_route_set(sip_dialog_t dialog, int *error)
+{
+	_sip_dialog_t		*_dialog;
+
+	if (error != NULL)
+		*error = 0;
+	if (!sip_manage_dialog || dialog == NULL) {
+		if (error != NULL)
+			*error = EINVAL;
+		return (NULL);
+	}
+	_dialog = (_sip_dialog_t *)dialog;
+	if (_dialog->sip_dlg_rset.sip_str_len > 0)
+		return (&_dialog->sip_dlg_rset);
+	return (NULL);
+}
+
+/*
+ * Return the dialog secure
+ */
+boolean_t
+sip_is_dialog_secure(sip_dialog_t dialog, int *error)
+{
+	_sip_dialog_t	*_dialog;
+	boolean_t	issecure;
+
+	if (error != NULL)
+		*error = 0;
+	if (!sip_manage_dialog || dialog == NULL) {
+		if (error != NULL)
+			*error = EINVAL;
+		return (B_FALSE);
+	}
+	_dialog = (_sip_dialog_t *)dialog;
+	(void) pthread_mutex_lock(&_dialog->sip_dlg_mutex);
+	issecure = _dialog->sip_dlg_secure;
+	(void) pthread_mutex_unlock(&_dialog->sip_dlg_mutex);
+	return (issecure);
+}
+
+/*
+ * Return the dialog local cseq
+ */
+uint32_t
+sip_get_dialog_local_cseq(sip_dialog_t dialog, int *error)
+{
+	_sip_dialog_t	*_dialog;
+	uint32_t	cseq;
+
+	if (error != NULL)
+		*error = 0;
+	if (!sip_manage_dialog || dialog == NULL) {
+		if (error != NULL)
+			*error = EINVAL;
+		return (0);
+	}
+	_dialog = (_sip_dialog_t *)dialog;
+	(void) pthread_mutex_lock(&_dialog->sip_dlg_mutex);
+	cseq = _dialog->sip_dlg_local_cseq;
+	(void) pthread_mutex_unlock(&_dialog->sip_dlg_mutex);
+	return (cseq);
+}
+
+/*
+ * Return the dialog remote cseq
+ */
+uint32_t
+sip_get_dialog_remote_cseq(sip_dialog_t dialog, int *error)
+{
+	_sip_dialog_t	*_dialog;
+	uint32_t	cseq;
+
+	if (error != NULL)
+		*error = 0;
+	if (!sip_manage_dialog || dialog == NULL) {
+		if (error != NULL)
+			*error = EINVAL;
+		return (0);
+	}
+	_dialog = (_sip_dialog_t *)dialog;
+	(void) pthread_mutex_lock(&_dialog->sip_dlg_mutex);
+	cseq = _dialog->sip_dlg_remote_cseq;
+	(void) pthread_mutex_unlock(&_dialog->sip_dlg_mutex);
+	return (cseq);
+}
+
+/*
+ * Return the dialog type
+ */
+int
+sip_get_dialog_type(sip_dialog_t dialog, int *error)
+{
+	_sip_dialog_t	*_dialog;
+	int		type;
+
+	if (error != NULL)
+		*error = 0;
+	if (!sip_manage_dialog || dialog == NULL) {
+		if (error != NULL)
+			*error = EINVAL;
+		return (-1);
+	}
+	_dialog = (_sip_dialog_t *)dialog;
+	(void) pthread_mutex_lock(&_dialog->sip_dlg_mutex);
+	type = _dialog->sip_dlg_type;
+	(void) pthread_mutex_unlock(&_dialog->sip_dlg_mutex);
+	return (type);
+}
+
+
+/*
+ * Partial dialog ?
+ */
+boolean_t
+sip_incomplete_dialog(sip_dialog_t dialog)
+{
+	_sip_dialog_t	*_dialog;
+	boolean_t	isnew;
+
+	if (!sip_manage_dialog || dialog == NULL)
+		return (B_FALSE);
+	_dialog = (_sip_dialog_t *)dialog;
+	(void) pthread_mutex_lock(&_dialog->sip_dlg_mutex);
+	isnew = _dialog->sip_dlg_state == SIP_DLG_NEW;
+	(void) pthread_mutex_unlock(&_dialog->sip_dlg_mutex);
+	return (isnew);
+}
+
+/*
+ * Hold dialog
+ */
+void
+sip_hold_dialog(sip_dialog_t dialog)
+{
+	_sip_dialog_t	*_dialog;
+
+	if (!sip_manage_dialog || dialog == NULL)
+		return;
+	_dialog = (_sip_dialog_t *)dialog;
+	(void) pthread_mutex_lock(&_dialog->sip_dlg_mutex);
+	SIP_DLG_REFCNT_INCR(_dialog);
+	(void) pthread_mutex_unlock(&_dialog->sip_dlg_mutex);
+}
+
+/*
+ * Release dialog
+ */
+void
+sip_release_dialog(sip_dialog_t dialog)
+{
+	_sip_dialog_t	*_dialog;
+
+	if (!sip_manage_dialog || dialog == NULL)
+		return;
+	_dialog = (_sip_dialog_t *)dialog;
+	SIP_DLG_REFCNT_DECR(_dialog);
+}
+
+/*
+ * Delete a dialog
+ */
+void
+sip_delete_dialog(sip_dialog_t dialog)
+{
+	if (!sip_manage_dialog || dialog == NULL)
+		return;
+	sip_dialog_terminate(dialog, NULL);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/lib/libsip/common/sip_gids.c	Sat Oct 07 14:26:26 2006 -0700
@@ -0,0 +1,302 @@
+/*
+ * 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 <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <time.h>
+#include <sys/errno.h>
+#ifdef	__linux__
+#include <sasl/sasl.h>
+#include <sasl/saslplug.h>
+#else
+#include <sys/md5.h>
+#endif
+
+#include "sip_parse_uri.h"
+#include "sip_msg.h"
+#include "sip_miscdefs.h"
+
+void	sip_md5_hash(char *, int, char *, int, char *, int,  char *, int,
+	    char *, int, char *, int, uchar_t *);
+
+#define	SIP_RANDOM_LEN	20
+
+/*
+ * Wrapper around /dev/urandom
+ */
+static int
+sip_get_random(char *buf, int buflen)
+{
+	static int devrandom = -1;
+
+	if (devrandom == -1 &&
+	    (devrandom = open("/dev/urandom", O_RDONLY)) == -1) {
+		return (-1);
+	}
+
+	if (read(devrandom, buf, buflen) == -1)
+		return (-1);
+	return (0);
+}
+
+/*
+ * Get MD5 hash of call_id, from_tag, to_tag using key
+ */
+void
+sip_md5_hash(char *str1, int lstr1, char *str2, int lstr2, char *str3,
+    int lstr3, char *str4, int lstr4, char *str5, int lstr5,
+    char *str6, int lstr6, uchar_t *digest)
+{
+	MD5_CTX	ctx;
+
+#ifdef	__linux__
+	_sasl_MD5Init(&ctx);
+
+	_sasl_MD5Update(&ctx, (uchar_t *)&sip_hash_salt, sizeof (uint64_t));
+
+	if (str1 != NULL)
+		_sasl_MD5Update(&ctx, (uchar_t *)str1, lstr1);
+
+	if (str2 != NULL)
+		_sasl_MD5Update(&ctx, (uchar_t *)str2, lstr2);
+
+	if (str3 != NULL)
+		_sasl_MD5Update(&ctx, (uchar_t *)str3, lstr3);
+
+	if (str4 != NULL)
+		_sasl_MD5Update(&ctx, (uchar_t *)str4, lstr4);
+
+	if (str5 != NULL)
+		_sasl_MD5Update(&ctx, (uchar_t *)str5, lstr5);
+
+	if (str6 != NULL)
+		_sasl_MD5Update(&ctx, (uchar_t *)str6, lstr6);
+
+	_sasl_MD5Final(digest, &ctx);
+#else	/* solaris */
+	MD5Init(&ctx);
+
+	MD5Update(&ctx, (uchar_t *)&sip_hash_salt, sizeof (uint64_t));
+
+	if (str1 != NULL)
+		MD5Update(&ctx, (uchar_t *)str1, lstr1);
+
+	if (str2 != NULL)
+		MD5Update(&ctx, (uchar_t *)str2, lstr2);
+
+	if (str3 != NULL)
+		MD5Update(&ctx, (uchar_t *)str3, lstr3);
+
+	if (str4 != NULL)
+		MD5Update(&ctx, (uchar_t *)str4, lstr4);
+
+	if (str5 != NULL)
+		MD5Update(&ctx, (uchar_t *)str5, lstr5);
+
+	if (str6 != NULL)
+		MD5Update(&ctx, (uchar_t *)str6, lstr6);
+
+	MD5Final(digest, &ctx);
+#endif
+}
+
+/*
+ * generate a guid (globally unique id)
+ */
+char *
+sip_guid()
+{
+	int		i;
+	uint8_t		*r;
+	uint32_t 	random;
+	uint32_t	time;
+	char		*guid;
+	int		guidlen;
+#ifdef	__linux__
+	struct timespec	tspec;
+#endif
+
+	guid = (char *)malloc(SIP_RANDOM_LEN + 1);
+	if (guid == NULL)
+		return (NULL);
+	/*
+	 * Get a 32-bit random #
+	 */
+	if (sip_get_random((char *)&random, sizeof (random)) != 0)
+		return (NULL);
+#ifdef	__linux__
+	if (clock_gettime(CLOCK_REALTIME, &tspec) != 0)
+		return (NULL);
+	time = (uint32_t)tspec.tv_nsec;
+#else
+	/*
+	 * Get 32-bits from gethrtime()
+	 */
+	time = (uint32_t)gethrtime();
+#endif
+	(void) snprintf(guid, SIP_RANDOM_LEN + 1, "%u%u", random, time);
+	guidlen = strlen(guid);
+
+	/*
+	 * just throw in some alphabets too
+	 */
+	r = (uint8_t *)malloc(guidlen);
+	if (sip_get_random((char *)r, guidlen) != 0) {
+		free(guid);
+		return (NULL);
+	}
+	for (i = 0; i < guidlen; i++) {
+		if ((r[i] >= 65 && r[i] <= 90) ||
+		    (r[i] >= 97 && r[i] <= 122)) {
+			guid[i] = r[i];
+		}
+	}
+	free(r);
+	return (guid);
+}
+
+/*
+ * Generate  branchid for a transaction
+ */
+char *
+sip_branchid(sip_msg_t sip_msg)
+{
+	char		*guid;
+	char		*branchid;
+	_sip_header_t	*via;
+	unsigned char 	md5_hash[16];
+	_sip_header_t	*to;
+	_sip_header_t	*from;
+	_sip_header_t	*callid;
+	_sip_msg_t	*_sip_msg;
+	int		cseq;
+	MD5_CTX		ctx;
+	size_t		len;
+	int		hdrlen;
+	int		i;
+
+	if (sip_msg == NULL) {
+generate_bid:
+		if ((branchid = (char *)malloc(SIP_BRANCHID_LEN + 1)) == NULL)
+			return (NULL);
+		guid = sip_guid();
+		if (guid == NULL) {
+			free(branchid);
+			return (NULL);
+		}
+		(void) snprintf(branchid, SIP_BRANCHID_LEN + 1, "z9hG4bK%s",
+		    guid);
+		free(guid);
+		return (branchid);
+	}
+	_sip_msg = (_sip_msg_t *)sip_msg;
+	(void) pthread_mutex_lock(&_sip_msg->sip_msg_mutex);
+	via = sip_search_for_header(_sip_msg, SIP_VIA, NULL);
+	if (via == NULL) {
+		(void) pthread_mutex_unlock(&_sip_msg->sip_msg_mutex);
+		goto generate_bid;
+	}
+	to = sip_search_for_header(_sip_msg, SIP_TO, NULL);
+	from = sip_search_for_header(_sip_msg, SIP_FROM, NULL);
+	callid = sip_search_for_header(_sip_msg, SIP_CALL_ID, NULL);
+	(void) pthread_mutex_unlock(&_sip_msg->sip_msg_mutex);
+	cseq = sip_get_callseq_num(_sip_msg, NULL);
+	if (to == NULL || from == NULL || callid == NULL || cseq == -1)
+		return (NULL);
+	if (_sip_msg->sip_msg_req_res == NULL ||
+	    _sip_msg->sip_msg_req_res->U.sip_request.sip_request_uri.
+	    sip_str_ptr == NULL) {
+		return (NULL);
+	}
+	len = 2 * sizeof (md5_hash) + 1;
+	if ((branchid = malloc(len)) == NULL)
+		return (NULL);
+#ifdef	__linux__
+	_sasl_MD5Init(&ctx);
+	hdrlen = via->sip_hdr_end - via->sip_hdr_start;
+	_sasl_MD5Update(&ctx, (uchar_t *)via->sip_hdr_start, hdrlen);
+	hdrlen = to->sip_hdr_end - to->sip_hdr_start;
+	_sasl_MD5Update(&ctx, (uchar_t *)to->sip_hdr_start, hdrlen);
+	hdrlen = from->sip_hdr_end - from->sip_hdr_start;
+	_sasl_MD5Update(&ctx, (uchar_t *)from->sip_hdr_start, hdrlen);
+	hdrlen = callid->sip_hdr_end - callid->sip_hdr_start;
+	_sasl_MD5Update(&ctx, (uchar_t *)callid->sip_hdr_start, hdrlen);
+	_sasl_MD5Update(&ctx, (uchar_t *)_sip_msg->sip_msg_req_res->
+	    U.sip_request.sip_request_uri.sip_str_ptr,
+	    _sip_msg->sip_msg_req_res->U.sip_request.
+	    sip_request_uri.sip_str_len);
+	_sasl_MD5Update(&ctx, (uchar_t *)&cseq, sizeof (int));
+	_sasl_MD5Final(md5_hash, &ctx);
+#else	/* solaris */
+	MD5Init(&ctx);
+	hdrlen = via->sip_hdr_end - via->sip_hdr_start;
+	MD5Update(&ctx, (uchar_t *)via->sip_hdr_start, hdrlen);
+	hdrlen = to->sip_hdr_end - to->sip_hdr_start;
+	MD5Update(&ctx, (uchar_t *)to->sip_hdr_start, hdrlen);
+	hdrlen = from->sip_hdr_end - from->sip_hdr_start;
+	MD5Update(&ctx, (uchar_t *)from->sip_hdr_start, hdrlen);
+	hdrlen = callid->sip_hdr_end - callid->sip_hdr_start;
+	MD5Update(&ctx, (uchar_t *)callid->sip_hdr_start, hdrlen);
+	MD5Update(&ctx, (uchar_t *)_sip_msg->sip_msg_req_res->
+	    U.sip_request.sip_request_uri.sip_str_ptr,
+	    _sip_msg->sip_msg_req_res->U.sip_request.
+	    sip_request_uri.sip_str_len);
+	MD5Update(&ctx, (uchar_t *)&cseq, sizeof (int));
+	MD5Final(md5_hash, &ctx);
+#endif
+	for (i = 0; i < sizeof (md5_hash); i++) {
+		(void) snprintf(&branchid[2 * i], len - (2 * i), "%02x",
+		    md5_hash[i]);
+	}
+	return (branchid);
+}
+
+uint32_t
+sip_get_cseq()
+{
+	time_t	tval;
+
+	tval = time(NULL);
+
+	return ((uint32_t)tval);
+}
+
+uint32_t
+sip_get_rseq()
+{
+	time_t	tval;
+
+	tval = time(NULL);
+
+	return ((uint32_t)tval);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/lib/libsip/common/sip_hash.c	Sat Oct 07 14:26:26 2006 -0700
@@ -0,0 +1,195 @@
+/*
+ * 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 <stdio.h>
+#include <pthread.h>
+#include <sip.h>
+
+#include "sip_hash.h"
+#include "sip_miscdefs.h"
+
+/*
+ * This file implements functions that add, search or remove an object
+ * from the hash table. The object is opaque to the hash functions. To add
+ * an object to the hash table, the caller provides the hash table,
+ * the object and the index into the hash table. To search an object,
+ * the caller provides the hash table, the digest (opaque), the index into
+ * the hash table and the function that does the actual match. Similarly,
+ * for removing an object, the caller provides the hash table, the digest
+ * (opaque), the index into the hash table and the function that does
+ * the acutal deletion of the object - if the deletion is successful,
+ * the object is taken off of the hash table.
+ */
+
+/*
+ * Given an object and the hash index, add it to the given hash table
+ */
+int
+sip_hash_add(sip_hash_t	*sip_hash, void *obj, int hindex)
+{
+	sip_hash_obj_t	*new_obj;
+	sip_hash_t	*hash_entry;
+
+	assert(obj != NULL);
+
+	new_obj = (sip_hash_obj_t *)malloc(sizeof (*new_obj));
+	if (new_obj == NULL)
+		return (-1);
+	new_obj->sip_obj = obj;
+	new_obj->next_obj = NULL;
+	new_obj->prev_obj = NULL;
+	hash_entry = &sip_hash[hindex];
+	(void) pthread_mutex_lock(&hash_entry->sip_hash_mutex);
+	if (hash_entry->hash_count == 0) {
+		assert(hash_entry->hash_head == NULL);
+		assert(hash_entry->hash_tail == NULL);
+		hash_entry->hash_head = new_obj;
+	} else {
+		hash_entry->hash_tail->next_obj = new_obj;
+		new_obj->prev_obj = hash_entry->hash_tail;
+	}
+	hash_entry->hash_tail = new_obj;
+	hash_entry->hash_count++;
+	(void) pthread_mutex_unlock(&hash_entry->sip_hash_mutex);
+	return (0);
+}
+
+/*
+ * Given the hash table, the digest to be searched for,  index into the hash
+ * table and the function to do the actual matching, return the object,
+ * if found.
+ */
+void *
+sip_hash_find(sip_hash_t *sip_hash, void *digest, int hindex,
+    boolean_t (*match_func)(void *, void *))
+{
+	int		count;
+	sip_hash_obj_t	*tmp;
+	sip_hash_t	*hash_entry;
+
+	hash_entry =  &sip_hash[hindex];
+	(void) pthread_mutex_lock(&hash_entry->sip_hash_mutex);
+	tmp = hash_entry->hash_head;
+	for (count = 0; count < hash_entry->hash_count; count++) {
+		if (match_func(tmp->sip_obj, digest)) {
+			(void) pthread_mutex_unlock(
+			    &hash_entry->sip_hash_mutex);
+			return (tmp->sip_obj);
+		}
+		tmp = tmp->next_obj;
+	}
+	(void) pthread_mutex_unlock(&hash_entry->sip_hash_mutex);
+	return (NULL);
+}
+
+/*
+ * Walk the hash table and invoke func on each object. 'arg' is passed
+ * to 'func'
+ */
+void
+sip_walk_hash(sip_hash_t *sip_hash, void (*func)(void *, void *), void *arg)
+{
+	sip_hash_t	*hash_entry;
+	int		count;
+	int		hcount;
+	sip_hash_obj_t	*tmp;
+
+	for (count = 0; count < SIP_HASH_SZ; count++) {
+		hash_entry =  &sip_hash[count];
+		(void) pthread_mutex_lock(&hash_entry->sip_hash_mutex);
+		tmp = hash_entry->hash_head;
+		for (hcount = 0; hcount < hash_entry->hash_count; hcount++) {
+			assert(tmp->sip_obj != NULL);
+			func(tmp->sip_obj, arg);
+			tmp = tmp->next_obj;
+		}
+		(void) pthread_mutex_unlock(&hash_entry->sip_hash_mutex);
+	}
+}
+
+/*
+ * Given the hash table, the digest to be searched for,  the index into the
+ * hash table and the  delete function provided to do the actual deletion,
+ * remove the object from the hash table (i.e. only if the object is deleted).
+ */
+void
+sip_hash_delete(sip_hash_t *sip_hash, void *digest, int hindex,
+    boolean_t (*del_func)(void *, void *, int *))
+{
+	sip_hash_t	*hash_entry;
+	int		count;
+	sip_hash_obj_t	*tmp;
+	int		found;
+
+	hash_entry =  &sip_hash[hindex];
+	(void) pthread_mutex_lock(&hash_entry->sip_hash_mutex);
+	tmp = hash_entry->hash_head;
+	for (count = 0; count < hash_entry->hash_count; count++) {
+		if (del_func(tmp->sip_obj, digest, &found)) {
+			if (tmp == hash_entry->hash_head) {
+				if (tmp->next_obj != NULL) {
+					hash_entry->hash_head = tmp->next_obj;
+					tmp->next_obj->prev_obj = NULL;
+				} else {
+					assert(hash_entry->hash_tail ==
+					    hash_entry->hash_head);
+					hash_entry->hash_head = NULL;
+					hash_entry->hash_tail = NULL;
+				}
+			} else {
+				sip_hash_obj_t	*next = tmp->next_obj;
+
+				if (next != NULL) {
+					tmp->prev_obj->next_obj = next;
+					next->prev_obj = tmp->prev_obj;
+				} else {
+					assert(hash_entry->hash_tail == tmp);
+					tmp->prev_obj->next_obj = NULL;
+					hash_entry->hash_tail =
+					    tmp->prev_obj;
+				}
+			}
+			tmp->prev_obj = NULL;
+			tmp->next_obj = NULL;
+			free(tmp);
+			hash_entry->hash_count--;
+			(void) pthread_mutex_unlock(
+			    &hash_entry->sip_hash_mutex);
+			return;
+		/*
+		 * If we found the object, we are done
+		 */
+		} else if (found == 1) {
+			(void) pthread_mutex_unlock(
+			    &hash_entry->sip_hash_mutex);
+			return;
+		}
+		tmp = tmp->next_obj;
+	}
+	(void) pthread_mutex_unlock(&hash_entry->sip_hash_mutex);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/lib/libsip/common/sip_hash.h	Sat Oct 07 14:26:26 2006 -0700
@@ -0,0 +1,75 @@
+/*
+ * 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.
+ */
+
+#ifndef	_SIP_HASH_H
+#define	_SIP_HASH_H
+
+#pragma ident	"%Z%%M%	%I%	%E% SMI"
+
+#ifdef	__cplusplus
+extern "C" {
+#endif
+
+#include <stdlib.h>
+#include <pthread.h>
+#include <sip.h>
+
+/* A prime number */
+#define	SIP_HASH_SZ	6037
+
+#define	SIP_DIGEST_TO_HASH(digest)					\
+	((digest[0] + digest[1] + digest[2] + digest[3] + digest[4] +	\
+	digest[5] + digest[6] + digest[7]) % SIP_HASH_SZ)
+
+/* An entry in the hash table, sip_obj is opaque */
+typedef struct	sip_hash_obj_s {
+	void			*sip_obj;
+	struct sip_hash_obj_s	*next_obj;
+	struct sip_hash_obj_s	*prev_obj;
+} sip_hash_obj_t;
+
+
+/* A hash list in the table */
+typedef struct sip_hash_s {
+	sip_hash_obj_t	*hash_head;
+	sip_hash_obj_t	*hash_tail;
+	int		hash_count;
+	pthread_mutex_t sip_hash_mutex;
+}sip_hash_t;
+
+int	sip_hash_add(sip_hash_t	*, void *, int);
+void	*sip_hash_find(sip_hash_t *, void *, int,
+	    boolean_t (*)(void *, void *));
+void	sip_walk_hash(sip_hash_t *, void (*)(void *, void *), void *);
+void	sip_hash_delete(sip_hash_t *, void *, int,
+	    boolean_t (*)(void *, void *, int *));
+void	sip_hash_init();
+
+#ifdef	__cplusplus
+}
+#endif
+
+#endif	/* _SIP_HASH_H */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/lib/libsip/common/sip_hdrs_ui.c	Sat Oct 07 14:26:26 2006 -0700
@@ -0,0 +1,1785 @@
+/*
+ * 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"
+
+/*
+ * Generic function to get int or string value from a header
+ */
+static void *
+sip_get_val_from_hdr(sip_hdr_value_t *val, int val_type, boolean_t stype,
+    int *error)
+{
+	if (error != NULL)
+		*error = 0;
+
+	if (val == NULL || val->sip_value_state == SIP_VALUE_DELETED) {
+		if (error != NULL)
+			*error = EINVAL;
+		return (NULL);
+	}
+
+	if (val->sip_value_state == SIP_VALUE_BAD)
+		*error = EPROTO;
+
+	switch (val_type) {
+		case (SIP_INT_VAL):
+			return (&(val->int_val));
+		case (SIP_STR_VAL):
+			return (&(val->str_val));
+		case (SIP_STRS_VAL):
+			if (stype == B_TRUE) {
+				if (val->strs_val.s1.sip_str_ptr != NULL)
+					return (&(val->strs_val.s1));
+				return (NULL);
+			}
+			if (val->strs_val.s2.sip_str_ptr != NULL)
+				return (&(val->strs_val.s2));
+			return (NULL);
+		case (SIP_INTSTR_VAL):
+			if (stype == B_TRUE) {
+				if (val->intstr_str.sip_str_ptr != NULL)
+					return (&(val->intstr_str));
+				else
+					return (NULL);
+			}
+			return (&(val->intstr_int));
+		case (SIP_AUTH_VAL):
+			return (&(val->auth_val));
+	}
+	if (error != NULL && *error == 0)
+		*error = EINVAL;
+	return (NULL);
+}
+
+/*
+ * Generic function to get value from a header given the value type and
+ * the string info (for multi-string values).
+ */
+static void *
+sip_get_val_from_msg(sip_msg_t msg, char *hdr_name, int val_type,
+    boolean_t stype, boolean_t empty_val, int *error)
+{
+	const _sip_header_t	*header;
+	sip_hdr_value_t		*value;
+
+	if (error != NULL)
+		*error = 0;
+	if (msg == NULL) {
+		if (error != NULL)
+			*error = EINVAL;
+		return (NULL);
+	}
+
+	header = sip_get_header(msg, hdr_name, NULL, error);
+	if (header == NULL) {
+		if (error != NULL)
+			*error = EINVAL;
+		return (NULL);
+	}
+
+	value = (sip_hdr_value_t *)sip_get_header_value(header, error);
+	if (value == NULL) {
+		if (error != NULL && empty_val == B_FALSE)
+			*error = EPROTO;
+		return (NULL);
+	}
+	return (sip_get_val_from_hdr(value, val_type, stype, error));
+}
+
+/*
+ * Get the URI from the value
+ */
+const sip_str_t *
+sip_get_cftruri_from_val(sip_header_value_t value, int *error)
+{
+	sip_hdr_value_t	*cftrvalue;
+
+	if (error != NULL)
+		*error = 0;
+
+	if (value == NULL || value->value_state == SIP_VALUE_DELETED) {
+		if (error != NULL)
+			*error = EINVAL;
+		return (NULL);
+	}
+	cftrvalue = (sip_hdr_value_t *)value;
+	/*
+	 * If the value is BAD, update error to reflect it.
+	 */
+	if (error != NULL && value->value_state == SIP_VALUE_BAD)
+		*error = EPROTO;
+	return (&cftrvalue->cftr_uri);
+}
+
+/*
+ * Get display name from the value
+ */
+const sip_str_t *
+sip_get_cftrname_from_val(sip_header_value_t value, int *error)
+{
+	sip_hdr_value_t *cftrvalue;
+
+	if (error != NULL)
+		*error = 0;
+	if (value == NULL || value->value_state == SIP_VALUE_DELETED) {
+		if (error != NULL)
+			*error = EINVAL;
+		return (NULL);
+	}
+	cftrvalue = (sip_hdr_value_t *)value;
+	/*
+	 * If the value is BAD, update error to reflect it.
+	 */
+	if (error != NULL && value->value_state == SIP_VALUE_BAD)
+		*error = EPROTO;
+	return (cftrvalue->cftr_name);
+}
+
+/*
+ * Contact header can have more than one value
+ * so we require a value to be passed in to get a value.
+ */
+const sip_str_t *
+sip_get_contact_uri_str(sip_header_value_t value, int *error)
+{
+	return (sip_get_cftruri_from_val(value, error));
+}
+
+/*
+ * Contact header can have more than one value
+ * so we require a value to be passed in to get a value.
+ */
+const sip_str_t *
+sip_get_contact_display_name(sip_header_value_t value, int *error)
+{
+	return (sip_get_cftrname_from_val(value, error));
+}
+
+/*
+ * Route header can have more than one value
+ * so we require a value to be passed in to get a value.
+ */
+const sip_str_t *
+sip_get_route_uri_str(sip_header_value_t value, int *error)
+{
+	return (sip_get_cftruri_from_val(value, error));
+}
+
+/*
+ * Route header can have more than one value
+ * so we require a value to be passed in to get a value.
+ */
+const sip_str_t *
+sip_get_route_display_name(sip_header_value_t value, int *error)
+{
+	return (sip_get_cftrname_from_val(value, error));
+}
+
+/*
+ * Get URI from the SIP message
+ */
+const sip_str_t *
+sip_get_cftruri_from_msg(sip_msg_t sip_msg, int *error, char *hdrname)
+{
+	const sip_hdr_value_t	*value;
+	const struct sip_header	*header;
+
+	if (error != NULL)
+		*error = 0;
+	if (sip_msg == NULL) {
+		if (error != NULL)
+			*error = EINVAL;
+		return (NULL);
+	}
+
+	header = sip_get_header(sip_msg, hdrname, NULL, error);
+	if (header == NULL) {
+		if (error != NULL)
+			*error = EINVAL;
+		return (NULL);
+	}
+
+	value = (sip_hdr_value_t *)sip_get_header_value(header, error);
+	if (value == NULL) {
+		if (error != NULL)
+			*error = EPROTO;
+		return (NULL);
+	}
+	/*
+	 * If the value is BAD, update error to reflect it.
+	 */
+	if (error != NULL && value->sip_value_state == SIP_VALUE_BAD)
+		*error = EPROTO;
+	return (&value->cftr_uri);
+}
+
+/*
+ * Get display name from the SIP message
+ */
+const sip_str_t *
+sip_get_cftrname_from_msg(sip_msg_t sip_msg, int *error, char *hdrname)
+{
+	const sip_hdr_value_t		*value;
+	const struct  sip_header	*header;
+
+	if (error != NULL)
+		*error = 0;
+	if (sip_msg == NULL) {
+		if (error != NULL)
+			*error = EINVAL;
+		return (NULL);
+	}
+	header = sip_get_header(sip_msg, hdrname, NULL, error);
+	if (header == NULL) {
+		if (error != NULL)
+			*error = EINVAL;
+		return (NULL);
+	}
+
+	value = (sip_hdr_value_t *)sip_get_header_value(header, error);
+	if (value == NULL) {
+		if (error != NULL)
+			*error = EPROTO;
+		return (NULL);
+	}
+	/*
+	 * If the value is BAD, update error to reflect it.
+	 */
+	if (error != NULL && value->sip_value_state == SIP_VALUE_BAD)
+		*error = EPROTO;
+	return (value->cftr_name);
+}
+
+/*
+ * Get FROM URI
+ */
+const sip_str_t *
+sip_get_from_uri_str(sip_msg_t sip_msg, int *error)
+{
+	return (sip_get_cftruri_from_msg(sip_msg, error, SIP_FROM));
+}
+
+/*
+ * Get FROM display name
+ */
+const sip_str_t *
+sip_get_from_display_name(sip_msg_t sip_msg, int *error)
+{
+	return (sip_get_cftrname_from_msg(sip_msg, error, SIP_FROM));
+}
+
+/*
+ * Return the FROM tag
+ */
+const sip_str_t *
+sip_get_from_tag(sip_msg_t sip_msg, int *error)
+{
+	const sip_hdr_value_t	*value;
+	const struct sip_header	*header;
+
+	if (error != NULL)
+		*error = 0;
+	if (sip_msg == NULL) {
+		if (error != NULL)
+			*error = EINVAL;
+		return (NULL);
+	}
+	header = sip_get_header(sip_msg, SIP_FROM, NULL, error);
+	if (header == NULL) {
+		if (error != NULL)
+			*error = EINVAL;
+		return (NULL);
+	}
+
+	value = (sip_hdr_value_t *)sip_get_header_value(header, error);
+	if (value == NULL) {
+		if (error != NULL)
+			*error = EPROTO;
+		return (NULL);
+	}
+	/*
+	 * If the value is BAD, update error to reflect it.
+	 */
+	if (error != NULL && value->sip_value_state == SIP_VALUE_BAD)
+		*error = EPROTO;
+	return (sip_get_param_value((sip_header_value_t)value, "tag", error));
+}
+
+/*
+ * Get TO URI
+ */
+const sip_str_t *
+sip_get_to_uri_str(sip_msg_t sip_msg, int *error)
+{
+	return (sip_get_cftruri_from_msg(sip_msg, error, SIP_TO));
+}
+
+/*
+ * Get TO display name
+ */
+const sip_str_t *
+sip_get_to_display_name(sip_msg_t sip_msg, int *error)
+{
+	return (sip_get_cftrname_from_msg(sip_msg, error, SIP_TO));
+}
+
+/*
+ * Get TO tag
+ */
+const sip_str_t *
+sip_get_to_tag(sip_msg_t sip_msg, int *error)
+{
+	const sip_hdr_value_t	*value;
+	const struct sip_header	*header;
+
+	if (error != NULL)
+		*error = 0;
+	if (sip_msg == NULL) {
+		if (error != NULL)
+			*error = EINVAL;
+		return (NULL);
+	}
+	header = sip_get_header(sip_msg, SIP_TO, NULL, error);
+	if (header == NULL) {
+		if (error != NULL)
+			*error = EINVAL;
+		return (NULL);
+	}
+
+	value = (sip_hdr_value_t *)sip_get_header_value(header, error);
+	if (value == NULL) {
+		if (error != NULL)
+			*error = EPROTO;
+		return (NULL);
+	}
+	/*
+	 * If the value is BAD, update error to reflect it.
+	 */
+	if (error != NULL && value->sip_value_state == SIP_VALUE_BAD)
+		*error = EPROTO;
+	return (sip_get_param_value((sip_header_value_t)value, "tag", error));
+}
+
+/*
+ * Return the Call-Id
+ */
+const sip_str_t *
+sip_get_callid(sip_msg_t sip_msg, int *error)
+{
+	sip_str_t	*r;
+
+	r = (sip_str_t *)sip_get_val_from_msg(sip_msg, SIP_CALL_ID, SIP_STR_VAL,
+	    B_FALSE, B_TRUE, error);
+	return (r);
+}
+
+#define	SIP_CSEQ_NUM	1
+#define	SIP_CSEQ_METHOD	2
+
+/*
+ * Get number/method from the CSEQ header
+ */
+static void *
+sip_get_cseq_val(sip_msg_t msg, int type, int *error)
+{
+	const _sip_header_t	*header;
+	sip_hdr_value_t		*val;
+
+	if (error != NULL)
+		*error = 0;
+
+	if (msg == NULL)  {
+		if (error != NULL)
+			*error = EINVAL;
+		return (NULL);
+	}
+	header = sip_get_header(msg, SIP_CSEQ, NULL, error);
+	if (header == NULL) {
+		if (error != NULL)
+			*error = EINVAL;
+		return (NULL);
+	}
+	val = (sip_hdr_value_t *)sip_get_header_value(header, error);
+	if (val == NULL) {
+		if (error != NULL)
+			*error = EPROTO;
+		return (NULL);
+	}
+	if (error != NULL && val->sip_value.value_state == SIP_VALUE_BAD)
+		*error = EPROTO;
+
+	switch (type) {
+		case SIP_CSEQ_NUM:
+			return (&(val->cseq_num));
+		case SIP_CSEQ_METHOD:
+			return (&(val->cseq_method));
+	}
+	if (error != NULL)
+		*error = EINVAL;
+	return (NULL);
+}
+
+/*
+ * Get CSEQ number
+ */
+int
+sip_get_callseq_num(sip_msg_t sip_msg, int *error)
+{
+	int	*r;
+
+	r = (int *)sip_get_cseq_val(sip_msg, SIP_CSEQ_NUM, error);
+	return (r == NULL ? -1 : *r);
+}
+
+/*
+ * Get CSEQ method
+ */
+sip_method_t
+sip_get_callseq_method(sip_msg_t sip_msg, int *error)
+{
+	sip_method_t	*r;
+
+	r = (sip_method_t *)sip_get_cseq_val(sip_msg, SIP_CSEQ_METHOD, error);
+	return (r == NULL ? -1 : *r);
+}
+
+/*
+ * Via header can have more than one value
+ * so we require a value to be passed in.
+ */
+const sip_str_t *
+sip_get_via_sent_by_host(sip_header_value_t value, int *error)
+{
+	sip_hdr_value_t	*via_value;
+
+	if (error != NULL)
+		*error = 0;
+	if (value == NULL || value->value_state == SIP_VALUE_DELETED) {
+		if (error != NULL)
+			*error = EINVAL;
+		return (NULL);
+	}
+	via_value = (sip_hdr_value_t *)value;
+	if (via_value->sip_value_state == SIP_VALUE_BAD && error != NULL)
+		*error = EPROTO;
+	return (&via_value->via_sent_by_host);
+}
+
+/*
+ * Via header can have more than one value
+ * so we require a value to be passed in.
+ */
+int
+sip_get_via_sent_by_port(sip_header_value_t value, int *error)
+{
+	sip_hdr_value_t	*via_value;
+
+	if (error != NULL)
+		*error = 0;
+	if (value == NULL || value->value_state == SIP_VALUE_DELETED) {
+		if (error != NULL)
+			*error = EINVAL;
+		return (-1);
+	}
+	via_value = (sip_hdr_value_t *)value;
+	if (via_value->sip_value_state == SIP_VALUE_BAD && error != NULL)
+		*error = EPROTO;
+	return (via_value->via_sent_by_port);
+}
+
+/*
+ * Return the protocol version from the VIA value
+ */
+const sip_str_t *
+sip_get_via_sent_protocol_version(sip_header_value_t value, int *error)
+{
+	sip_hdr_value_t *via_value;
+
+	if (value == NULL || value->value_state == SIP_VALUE_DELETED) {
+		if (error != NULL)
+			*error = EINVAL;
+		return (NULL);
+	}
+	via_value = (sip_hdr_value_t *)value;
+	if (via_value->sip_value_state == SIP_VALUE_BAD && error != NULL)
+		*error = EPROTO;
+	return (&via_value->via_protocol_vers);
+}
+
+/*
+ * Return the protocol name
+ */
+const sip_str_t *
+sip_get_via_sent_protocol_name(sip_header_value_t value, int *error)
+{
+	sip_hdr_value_t	*via_value;
+
+	if (error != NULL)
+		*error = 0;
+	if (value == NULL || value->value_state == SIP_VALUE_DELETED) {
+		if (error != NULL)
+			*error = EINVAL;
+		return (NULL);
+	}
+	via_value = (sip_hdr_value_t *)value;
+	if (via_value->sip_value_state == SIP_VALUE_BAD && error != NULL)
+		*error = EPROTO;
+	return (&via_value->via_protocol_name);
+}
+
+/*
+ * Return the transport from the VIA value
+ */
+const sip_str_t *
+sip_get_via_sent_transport(sip_header_value_t value, int *error)
+{
+	sip_hdr_value_t	*via_value;
+
+	if (error != NULL)
+		*error = 0;
+	if (value == NULL || value->value_state == SIP_VALUE_DELETED) {
+		if (error != NULL)
+			*error = EINVAL;
+		return (NULL);
+	}
+	via_value = (sip_hdr_value_t *)value;
+	if (via_value->sip_value_state == SIP_VALUE_BAD && error != NULL)
+		*error = EPROTO;
+	return (&via_value->via_protocol_transport);
+}
+
+/*
+ * get the branch id from the topmost VIA header
+ */
+char *
+sip_get_branchid(sip_msg_t sip_msg, int *error)
+{
+	_sip_header_t		*header;
+	sip_parsed_header_t	*parsed_header;
+	sip_hdr_value_t		*via_value;
+	const sip_str_t		*param_value;
+	char			*bid;
+	_sip_msg_t		*_sip_msg;
+
+	if (error != NULL)
+		*error = 0;
+
+	if (sip_msg == NULL) {
+		if (error != NULL)
+			*error = EINVAL;
+		return (NULL);
+	}
+
+	_sip_msg = (_sip_msg_t *)sip_msg;
+
+	(void) pthread_mutex_lock(&_sip_msg->sip_msg_mutex);
+	header = sip_search_for_header(_sip_msg, SIP_VIA, NULL);
+	if (header == NULL) {
+		if (error != NULL)
+			*error = EINVAL;
+		(void) pthread_mutex_unlock(&_sip_msg->sip_msg_mutex);
+		return (NULL);
+	}
+	if (sip_parse_via_header(header, &parsed_header) != 0) {
+		if (error != NULL)
+			*error = EPROTO;
+		(void) pthread_mutex_unlock(&_sip_msg->sip_msg_mutex);
+		return (NULL);
+	}
+	if (parsed_header == NULL) {
+		if (error != NULL)
+			*error = EPROTO;
+		(void) pthread_mutex_unlock(&_sip_msg->sip_msg_mutex);
+		return (NULL);
+	}
+	via_value = (sip_hdr_value_t *)parsed_header->value;
+	if (via_value == NULL || via_value->sip_value_state == SIP_VALUE_BAD) {
+		if (error != NULL)
+			*error = EPROTO;
+		(void) pthread_mutex_unlock(&_sip_msg->sip_msg_mutex);
+		return (NULL);
+	}
+	param_value = sip_get_param_value((sip_header_value_t)via_value,
+	    "branch", error);
+
+	if (param_value == NULL) {
+		if (error != NULL)
+			*error = EINVAL;
+		(void) pthread_mutex_unlock(&_sip_msg->sip_msg_mutex);
+		return (NULL);
+	}
+
+	bid = (char *)malloc(param_value->sip_str_len + 1);
+	if (bid == NULL) {
+		if (error != NULL)
+			*error = ENOMEM;
+		(void) pthread_mutex_unlock(&_sip_msg->sip_msg_mutex);
+		return (NULL);
+	}
+	(void) strncpy(bid, param_value->sip_str_ptr,
+	    param_value->sip_str_len);
+	bid[param_value->sip_str_len] = '\0';
+	(void) pthread_mutex_unlock(&_sip_msg->sip_msg_mutex);
+	return (bid);
+}
+
+/*
+ * adds branchid to the topmost VIA header, if a branchid already exists,
+ * returns error.
+ */
+int
+sip_add_branchid_to_via(sip_msg_t sip_msg, char *branchid)
+{
+	int		err = 0;
+	char		*param;
+	int		plen;
+	sip_header_t	via_hdr;
+	_sip_msg_t	*_sip_msg;
+
+	if (sip_msg == NULL)
+		return (EINVAL);
+	/*
+	 * If there is already a branchid param, error?
+	 */
+	if (sip_get_branchid(sip_msg, NULL) != NULL)
+		return (EINVAL);
+	_sip_msg = (_sip_msg_t *)sip_msg;
+	(void) pthread_mutex_lock(&_sip_msg->sip_msg_mutex);
+	via_hdr = (sip_header_t)sip_search_for_header(_sip_msg, SIP_VIA, NULL);
+	(void) pthread_mutex_unlock(&_sip_msg->sip_msg_mutex);
+	if (via_hdr == NULL)
+		return (EINVAL);
+	plen = strlen(branchid) + strlen("branch=") + 1;
+	param = malloc(plen);
+	if (param == NULL)
+		return (ENOMEM);
+	(void) snprintf(param, plen, "branch=%s", branchid);
+
+	(void) sip_add_param(via_hdr, param, &err);
+	free(param);
+
+	return (err);
+}
+
+/*
+ * returns the number of VIA headers in the SIP message
+ */
+int
+sip_get_num_via(sip_msg_t sip_msg, int *error)
+{
+	_sip_msg_t	*_sip_msg;
+	sip_header_t	hdr;
+	int		via_cnt = 0;
+
+	if (error != NULL)
+		*error = 0;
+	if (sip_msg == NULL) {
+		if (error != NULL)
+			*error = EINVAL;
+		return (via_cnt);
+	}
+	_sip_msg = (_sip_msg_t *)sip_msg;
+	(void) pthread_mutex_lock(&_sip_msg->sip_msg_mutex);
+	hdr = (sip_header_t)sip_search_for_header(_sip_msg, SIP_VIA, NULL);
+	while (hdr != NULL) {
+		via_cnt++;
+		hdr = (sip_header_t)sip_search_for_header(_sip_msg, SIP_VIA,
+		    hdr);
+	}
+	(void) pthread_mutex_unlock(&_sip_msg->sip_msg_mutex);
+	return (via_cnt);
+}
+
+/*
+ * Return Max-Forward value
+ */
+int
+sip_get_maxforward(sip_msg_t sip_msg, int *error)
+{
+	int	*r;
+
+	r = (int *)sip_get_val_from_msg(sip_msg, SIP_MAX_FORWARDS, SIP_INT_VAL,
+	    B_FALSE, B_FALSE, error);
+	if (r == NULL)
+		return (-1);
+	return (*r);
+}
+
+/*
+ * Get the content type
+ */
+const sip_str_t *
+sip_get_content_type(sip_msg_t sip_msg, int *error)
+{
+	sip_str_t	*r;
+
+	r = (sip_str_t *)sip_get_val_from_msg(sip_msg, SIP_CONTENT_TYPE,
+	    SIP_STRS_VAL, B_TRUE, B_FALSE, error);
+	return (r);
+}
+
+/*
+ * Get the content sub-type
+ */
+const sip_str_t *
+sip_get_content_sub_type(sip_msg_t sip_msg, int *error)
+{
+	sip_str_t	*r;
+
+	r = (sip_str_t *)sip_get_val_from_msg(sip_msg, SIP_CONTENT_TYPE,
+	    SIP_STRS_VAL, B_FALSE, B_FALSE, error);
+	return (r);
+}
+
+/*
+ * Return the content-length value
+ */
+int
+sip_get_content_length(sip_msg_t sip_msg, int *error)
+{
+	int	*r;
+
+	r = (int *)sip_get_val_from_msg(sip_msg, SIP_CONTENT_LENGTH,
+	    SIP_INT_VAL, B_FALSE, B_FALSE, error);
+	if (r == NULL)
+		return (-1);
+	return (*r);
+}
+
+/*
+ * get allow-events
+ */
+const sip_str_t *
+sip_get_allow_events(sip_header_value_t value, int *error)
+{
+	sip_str_t	*r;
+	sip_hdr_value_t	*val = (sip_hdr_value_t *)value;
+
+	r = (sip_str_t *)sip_get_val_from_hdr(val, SIP_STR_VAL, B_TRUE, error);
+	return (r);
+}
+
+/*
+ * get event
+ */
+const sip_str_t *
+sip_get_event(sip_msg_t sip_msg, int *error)
+{
+	sip_str_t	*r;
+
+	r = (sip_str_t *)sip_get_val_from_msg(sip_msg, SIP_EVENT, SIP_STR_VAL,
+	    B_FALSE, B_FALSE, error);
+	return (r);
+}
+
+/*
+ * get subscription state
+ */
+const sip_str_t *
+sip_get_substate(sip_msg_t sip_msg, int *error)
+{
+	sip_str_t	*r;
+
+	r = (sip_str_t *)sip_get_val_from_msg(sip_msg, SIP_SUBSCRIPTION_STATE,
+	    SIP_STR_VAL, B_FALSE, B_FALSE, error);
+	return (r);
+}
+
+/*
+ * get accept type
+ */
+const sip_str_t *
+sip_get_accept_type(sip_header_value_t value, int *error)
+{
+	sip_str_t	*r;
+	sip_hdr_value_t	*val = (sip_hdr_value_t *)value;
+
+	r = (sip_str_t *)sip_get_val_from_hdr(val, SIP_STRS_VAL, B_TRUE, error);
+	return (r);
+}
+
+/*
+ * get accept subtype
+ */
+const sip_str_t *
+sip_get_accept_sub_type(sip_header_value_t value, int *error)
+{
+	sip_str_t	*r;
+	sip_hdr_value_t	*val = (sip_hdr_value_t *)value;
+
+	r = (sip_str_t *)sip_get_val_from_hdr(val, SIP_STRS_VAL, B_FALSE,
+	    error);
+	return (r);
+}
+
+/*
+ * accept-encode can have more than one value
+ */
+const sip_str_t *
+sip_get_accept_enc(sip_header_value_t value, int *error)
+{
+	sip_str_t	*r;
+	sip_hdr_value_t	*val = (sip_hdr_value_t *)value;
+
+	r = (sip_str_t *)sip_get_val_from_hdr(val, SIP_STR_VAL, B_FALSE, error);
+	return (r);
+}
+
+/*
+ * accept-language can have more than one value
+ */
+const sip_str_t *
+sip_get_accept_lang(sip_header_value_t value, int *error)
+{
+	sip_str_t	*r;
+	sip_hdr_value_t	*val = (sip_hdr_value_t *)value;
+
+	r = (sip_str_t *)sip_get_val_from_hdr(val, SIP_STR_VAL, B_FALSE, error);
+	return (r);
+}
+
+/*
+ * get URI from the alert-info header
+ */
+const sip_str_t *
+sip_get_alert_info_uri(sip_header_value_t value, int *error)
+{
+	sip_str_t	*r;
+	sip_hdr_value_t	*val = (sip_hdr_value_t *)value;
+
+	r = (sip_str_t *)sip_get_val_from_hdr(val, SIP_STR_VAL, B_FALSE, error);
+	return (r);
+}
+
+/*
+ * get method from allow header
+ */
+sip_method_t
+sip_get_allow_method(sip_header_value_t value, int *error)
+{
+	int		*r;
+	sip_hdr_value_t	*val = (sip_hdr_value_t *)value;
+
+	r = (int *)sip_get_val_from_hdr(val, SIP_INT_VAL, B_FALSE, error);
+	return (r == NULL ? -1 : (sip_method_t)*r);
+}
+
+/*
+ * get URI from call-info header
+ */
+const sip_str_t *
+sip_get_call_info_uri(sip_header_value_t value, int *error)
+{
+	sip_str_t	*r;
+	sip_hdr_value_t	*val = (sip_hdr_value_t *)value;
+
+	r = (sip_str_t *)sip_get_val_from_hdr(val, SIP_STR_VAL, B_FALSE, error);
+	return (r);
+}
+
+/*
+ * get content-disposition value
+ */
+const sip_str_t *
+sip_get_content_disp(sip_msg_t sip_msg, int *error)
+{
+	sip_str_t	*r;
+
+	r = (sip_str_t *)sip_get_val_from_msg(sip_msg, SIP_CONTENT_DIS,
+	    SIP_STR_VAL, B_FALSE, B_FALSE, error);
+	return (r);
+}
+
+/*
+ * get content-encoding value
+ */
+const sip_str_t *
+sip_get_content_enc(sip_header_value_t value, int *error)
+{
+	sip_str_t	*r;
+	sip_hdr_value_t	*val = (sip_hdr_value_t *)value;
+
+	r = (sip_str_t *)sip_get_val_from_hdr(val, SIP_STR_VAL, B_FALSE, error);
+	return (r);
+}
+
+/*
+ * get content-language value
+ */
+const sip_str_t *
+sip_get_content_lang(sip_header_value_t value, int *error)
+{
+	sip_str_t	*r;
+	sip_hdr_value_t	*val = (sip_hdr_value_t *)value;
+
+	r = (sip_str_t *)sip_get_val_from_hdr(val, SIP_STR_VAL, B_FALSE, error);
+	return (r);
+}
+
+/*
+ * sip_get_date_time, day, wkday, month, year
+ */
+#define	D_TIME		0x01
+#define	D_DAY		0x02
+#define	D_MONTH		0x03
+#define	D_YEAR		0x04
+#define	D_WKDAY		0x05
+#define	D_TIMEZONE	0x06
+
+/*
+ * get date information
+ */
+static void *
+sip_get_date_val(sip_msg_t msg, int type, int *error)
+{
+	const _sip_header_t	*header;
+	sip_hdr_value_t		*val;
+
+	if (error != NULL)
+		*error = 0;
+	if (msg == NULL) {
+		if (error != NULL)
+			*error = EINVAL;
+		return (NULL);
+	}
+	header = sip_get_header(msg, SIP_DATE, NULL, error);
+	if (header == NULL) {
+		if (error != NULL)
+			*error = EINVAL;
+		return (NULL);
+	}
+
+	val = (sip_hdr_value_t *)sip_get_header_value(header, error);
+	if (val == NULL) {
+		if (error != NULL)
+			*error = EPROTO;
+		return (NULL);
+	}
+	if (error != NULL && val->sip_value.value_state == SIP_VALUE_BAD)
+		*error = EPROTO;
+	switch (type) {
+		case (D_TIME):
+			return (&(val->date_t));
+		case (D_DAY):
+			return (&(val->date_d));
+		case (D_MONTH):
+			return (&(val->date_m));
+		case (D_YEAR):
+			return (&(val->date_y));
+		case (D_WKDAY):
+			return (&(val->date_wd));
+		case (D_TIMEZONE):
+			return (&(val->date_tz));
+	}
+	if (error != NULL)
+		*error = EINVAL;
+	return (NULL);
+}
+
+/*
+ * get time value
+ */
+const sip_str_t *
+sip_get_date_time(sip_msg_t sip_msg, int *error)
+{
+	sip_str_t	*r;
+
+	r = (sip_str_t *)sip_get_date_val(sip_msg, D_TIME, error);
+	return (r);
+}
+
+/*
+ * get day
+ */
+int
+sip_get_date_day(sip_msg_t sip_msg, int *error)
+{
+	int	*r = NULL;
+
+	r = sip_get_date_val(sip_msg, D_DAY, error);
+	return (r == NULL ? -1 : *(int *)r);
+}
+
+/*
+ * get month
+ */
+const sip_str_t *
+sip_get_date_month(sip_msg_t sip_msg, int *error)
+{
+	sip_str_t	*r;
+
+	r = (sip_str_t *)sip_get_date_val(sip_msg, D_MONTH, error);
+	return (r);
+}
+
+/*
+ * get year
+ */
+int
+sip_get_date_year(sip_msg_t sip_msg, int *error)
+{
+	int	*r;
+
+	r = (int *)sip_get_date_val(sip_msg, D_YEAR, error);
+	return (r == NULL ? -1 : *r);
+}
+
+/*
+ * get day of the week
+ */
+const sip_str_t *
+sip_get_date_wkday(sip_msg_t sip_msg, int *error)
+{
+	sip_str_t	*r;
+
+	r = (sip_str_t *)sip_get_date_val(sip_msg, D_WKDAY, error);
+	return (r);
+}
+
+/*
+ * get the timezone
+ */
+const sip_str_t *
+sip_get_date_timezone(sip_msg_t sip_msg, int *error)
+{
+	sip_str_t	*r;
+
+	r = (sip_str_t *)sip_get_date_val(sip_msg, D_TIMEZONE, error);
+	return (r);
+}
+
+/*
+ * get error-info URI
+ */
+const sip_str_t *
+sip_get_error_info_uri(sip_header_value_t value, int *error)
+{
+	sip_str_t	*r;
+	sip_hdr_value_t	*val = (sip_hdr_value_t *)value;
+
+	r = (sip_str_t *)sip_get_val_from_hdr(val, SIP_STR_VAL, B_FALSE, error);
+	return (r);
+}
+
+/*
+ * get priv-value from privacy
+ */
+const sip_str_t *
+sip_get_priv_value(sip_header_value_t value, int *error)
+{
+	sip_str_t	*r;
+	sip_hdr_value_t	*val = (sip_hdr_value_t *)value;
+
+	r = (sip_str_t *)sip_get_val_from_hdr(val, SIP_STR_VAL, B_FALSE, error);
+	return (r);
+}
+
+/*
+ * return expires value
+ */
+int
+sip_get_expires(sip_msg_t sip_msg, int *error)
+{
+	int	*r;
+
+	r = (int *)sip_get_val_from_msg(sip_msg, SIP_EXPIRE, SIP_INT_VAL,
+	    B_FALSE, B_FALSE, error);
+	if (r == NULL)
+		return (-1);
+	return (*r);
+}
+
+/*
+ * get reply-to value
+ */
+const sip_str_t *
+sip_get_in_reply_to(sip_header_value_t value, int *error)
+{
+	sip_str_t	*r;
+	sip_hdr_value_t	*val = (sip_hdr_value_t *)value;
+
+	r = (sip_str_t *)sip_get_val_from_hdr(val, SIP_STR_VAL, B_FALSE, error);
+	return (r);
+}
+
+/*
+ * get min-expires value
+ */
+int
+sip_get_min_expires(sip_msg_t sip_msg, int *error)
+{
+	int	*r;
+
+	r = (int *)sip_get_val_from_msg(sip_msg, SIP_MIN_EXPIRE, SIP_INT_VAL,
+	    B_FALSE, B_FALSE, error);
+	if (r == NULL)
+		return (-1);
+	return (*r);
+}
+
+/*
+ * get mime-version
+ */
+const sip_str_t *
+sip_get_mime_version(sip_msg_t sip_msg, int *error)
+{
+	sip_str_t	*r;
+
+	r = (sip_str_t *)sip_get_val_from_msg(sip_msg, SIP_MIME_VERSION,
+	    SIP_STR_VAL, B_FALSE, B_FALSE, error);
+	return (r);
+}
+
+/*
+ * get organization value
+ */
+const sip_str_t *
+sip_get_org(sip_msg_t sip_msg, int *error)
+{
+	sip_str_t	*r;
+
+	r = (sip_str_t *)sip_get_val_from_msg(sip_msg, SIP_ORGANIZATION,
+	    SIP_STR_VAL, B_FALSE, B_TRUE, error);
+	return (r);
+}
+
+/*
+ * get priority value
+ */
+const sip_str_t *
+sip_get_priority(sip_msg_t sip_msg, int *error)
+{
+	sip_str_t	*r;
+
+	r = (sip_str_t *)sip_get_val_from_msg(sip_msg, SIP_PRIORITY,
+	    SIP_STR_VAL, B_FALSE, B_FALSE, error);
+	return (r);
+}
+
+/*
+ * get display name
+ */
+const sip_str_t *
+sip_get_pidentity_display_name(sip_header_value_t value, int *error)
+{
+	sip_str_t	*r;
+	sip_hdr_value_t	*val = (sip_hdr_value_t *)value;
+
+	r = (sip_str_t *)sip_get_val_from_hdr(val, SIP_STRS_VAL, B_TRUE, error);
+
+	return (r);
+}
+
+/*
+ * get URI
+ */
+const sip_str_t *
+sip_get_pidenty_uri_str(sip_header_value_t value, int *error)
+{
+	sip_str_t	*r;
+	sip_hdr_value_t	*val = (sip_hdr_value_t *)value;
+
+	r = (sip_str_t *)sip_get_val_from_hdr(val, SIP_STRS_VAL, B_FALSE,
+	    error);
+
+	return (r);
+}
+
+/*
+ * get display name from passerted-identity header
+ */
+const sip_str_t *
+sip_get_passertedid_display_name(sip_header_value_t value, int *error)
+{
+	return (sip_get_pidentity_display_name(value, error));
+}
+
+/*
+ * get URI from passerted-identity header
+ */
+const sip_str_t *
+sip_get_passertedid_uri_str(sip_header_value_t value, int *error)
+{
+	return (sip_get_pidenty_uri_str(value, error));
+}
+
+/*
+ * get display name from ppreferred-identity header
+ */
+const sip_str_t *
+sip_get_ppreferredid_display_name(sip_header_value_t value, int *error)
+{
+	return (sip_get_pidentity_display_name(value, error));
+}
+
+/*
+ * get URI from ppreferred-identity header
+ */
+const sip_str_t *
+sip_get_ppreferredid_uri_str(sip_header_value_t value, int *error)
+{
+	return (sip_get_pidenty_uri_str(value, error));
+}
+
+#define	SIP_RACK_RESP_NUM	1
+#define	SIP_RACK_CSEQ_NUM	2
+#define	SIP_RACK_METHOD		3
+
+/*
+ * Get rack information
+ */
+static void *
+sip_get_rack_val(sip_msg_t msg, int type, int *error)
+{
+	const _sip_header_t	*header;
+	sip_hdr_value_t		*val;
+
+	if (error != NULL)
+		*error = 0;
+
+	if (msg == NULL)  {
+		if (error != NULL)
+			*error = EINVAL;
+		return (NULL);
+	}
+	header = sip_get_header(msg, SIP_RACK, NULL, error);
+	if (header == NULL) {
+		if (error != NULL)
+			*error = EINVAL;
+		return (NULL);
+	}
+	val = (sip_hdr_value_t *)sip_get_header_value(header, error);
+	if (val == NULL) {
+		if (error != NULL)
+			*error = EPROTO;
+		return (NULL);
+	}
+	if (error != NULL && val->sip_value.value_state == SIP_VALUE_BAD)
+		*error = EPROTO;
+
+	switch (type) {
+		case SIP_RACK_RESP_NUM:
+			return (&(val->rack_resp));
+		case SIP_RACK_CSEQ_NUM:
+			return (&(val->rack_cseq));
+		case SIP_RACK_METHOD:
+			return (&(val->rack_method));
+	}
+	if (error != NULL)
+		*error = EINVAL;
+	return (NULL);
+}
+
+/*
+ * get response number for rack
+ */
+int
+sip_get_rack_resp_num(sip_msg_t sip_msg, int *error)
+{
+	int	*r;
+
+	r = (int *)sip_get_rack_val(sip_msg, SIP_RACK_RESP_NUM, error);
+
+	return (r == NULL ? -1 : *r);
+}
+
+/*
+ * get sequence number for rack
+ */
+int
+sip_get_rack_cseq_num(sip_msg_t sip_msg, int *error)
+{
+	int	*r;
+
+	r = (int *)sip_get_rack_val(sip_msg, SIP_RACK_CSEQ_NUM, error);
+
+	return (r == NULL ? -1 : *r);
+}
+
+/*
+ * get method for rack
+ */
+sip_method_t
+sip_get_rack_method(sip_msg_t sip_msg, int *error)
+{
+	sip_method_t	*r;
+
+	r = (sip_method_t *)sip_get_rack_val(sip_msg, SIP_RACK_METHOD, error);
+
+	return (r == NULL ? -1 : *r);
+}
+
+/*
+ * get response number from rseq
+ */
+int
+sip_get_rseq_resp_num(sip_msg_t sip_msg, int *error)
+{
+	int	*r;
+
+	r = (int *)sip_get_val_from_msg(sip_msg, SIP_RSEQ, SIP_INT_VAL,
+	    B_FALSE, B_FALSE, error);
+
+	return (r == NULL ? -1 : *r);
+}
+
+/*
+ * get reply-to display name
+ */
+const sip_str_t *
+sip_get_replyto_display_name(sip_msg_t sip_msg, int *error)
+{
+	sip_str_t	*r;
+
+	r = (sip_str_t *)sip_get_val_from_msg(sip_msg, SIP_REPLYTO,
+	    SIP_STRS_VAL, B_TRUE, B_FALSE, error);
+	return (r);
+}
+
+/*
+ * get reply-to URI
+ */
+const sip_str_t *
+sip_get_replyto_uri_str(sip_msg_t sip_msg, int *error)
+{
+	sip_str_t	*r;
+
+	r = (sip_str_t *)sip_get_val_from_msg(sip_msg, SIP_REPLYTO,
+	    SIP_STRS_VAL, B_FALSE, B_FALSE, error);
+
+	return (r);
+}
+
+/*
+ * get require value
+ */
+const sip_str_t *
+sip_get_require(sip_header_value_t value, int *error)
+{
+	sip_str_t	*r;
+	sip_hdr_value_t	*val = (sip_hdr_value_t *)value;
+
+	r = (sip_str_t *)sip_get_val_from_hdr(val, SIP_STR_VAL, B_FALSE, error);
+	return (r);
+}
+
+/*
+ * get retry-after time
+ */
+int
+sip_get_retry_after_time(sip_msg_t sip_msg, int *error)
+{
+	int	*t;
+
+	t = (int *)sip_get_val_from_msg(sip_msg, SIP_RETRY_AFTER,
+	    SIP_INTSTR_VAL, B_FALSE, B_FALSE, error);
+	if (t == NULL)
+		return (-1);
+	return (*t);
+}
+
+/*
+ * get retry-after comments
+ */
+const sip_str_t *
+sip_get_retry_after_cmts(sip_msg_t sip_msg, int *error)
+{
+	sip_str_t	*r;
+
+	r = (sip_str_t *)sip_get_val_from_msg(sip_msg, SIP_RETRY_AFTER,
+	    SIP_INTSTR_VAL, B_TRUE, B_FALSE, error);
+	return (r);
+}
+
+/*
+ * get subject
+ */
+const sip_str_t *
+sip_get_subject(sip_msg_t sip_msg, int *error)
+{
+	sip_str_t	*r;
+
+	r = (sip_str_t *)sip_get_val_from_msg(sip_msg, SIP_SUBJECT, SIP_STR_VAL,
+	    B_FALSE, B_TRUE, error);
+	return (r);
+}
+
+/*
+ * get supported
+ */
+const sip_str_t *
+sip_get_supported(sip_header_value_t value, int *error)
+{
+	sip_str_t	*r;
+	sip_hdr_value_t	*val = (sip_hdr_value_t *)value;
+
+	r = (sip_str_t *)sip_get_val_from_hdr(val, SIP_STR_VAL, B_FALSE, error);
+	return (r);
+}
+
+/*
+ * get timestamp delay
+ */
+const sip_str_t *
+sip_get_tstamp_delay(sip_msg_t sip_msg, int *error)
+{
+	sip_str_t	*t;
+
+	t = sip_get_val_from_msg(sip_msg, SIP_TIMESTAMP, SIP_STRS_VAL, B_FALSE,
+	    B_FALSE, error);
+	return (t);
+}
+
+/*
+ * get timestamp
+ */
+const sip_str_t *
+sip_get_tstamp_value(sip_msg_t sip_msg, int *error)
+{
+	sip_str_t	*t;
+
+	t = sip_get_val_from_msg(sip_msg, SIP_TIMESTAMP, SIP_STRS_VAL, B_TRUE,
+	    B_FALSE, error);
+	return (t);
+}
+
+/*
+ * get unsupported value
+ */
+const sip_str_t *
+sip_get_unsupported(sip_header_value_t value, int *error)
+{
+	sip_str_t	*r;
+	sip_hdr_value_t	*val = (sip_hdr_value_t *)value;
+
+	r = (sip_str_t *)sip_get_val_from_hdr(val, SIP_STR_VAL, B_FALSE, error);
+	return (r);
+}
+
+/*
+ * get server value from message
+ */
+const sip_str_t *
+sip_get_server(sip_msg_t sip_msg, int *error)
+{
+	sip_str_t	*r;
+
+	r = (sip_str_t *)sip_get_val_from_msg(sip_msg, SIP_SERVER, SIP_STR_VAL,
+	    B_FALSE, B_FALSE, error);
+	return (r);
+}
+
+/*
+ * get user-agent value
+ */
+const sip_str_t *
+sip_get_user_agent(sip_msg_t sip_msg, int *error)
+{
+	sip_str_t	*r;
+
+	r = sip_get_val_from_msg(sip_msg, SIP_USER_AGENT, SIP_STR_VAL, B_FALSE,
+	    B_FALSE, error);
+	return (r);
+}
+
+#define	W_CODE	0x05
+#define	W_AGENT	0x06
+#define	W_TEXT	0x07
+
+/*
+ * get warning info
+ */
+static void *
+sip_get_warninfo(sip_header_value_t value, int info, int *error)
+{
+	sip_hdr_value_t	*val = (sip_hdr_value_t *)value;
+
+	if (error != NULL)
+		*error = 0;
+
+	if (val == NULL) {
+		if (error != NULL)
+			*error = EINVAL;
+		return (NULL);
+	}
+
+	if (val->sip_value_state == SIP_VALUE_BAD) {
+		*error = EPROTO;
+		return (NULL);
+	}
+
+	switch (info) {
+		case (W_CODE):
+			return (&(val->warn_code));
+		case (W_AGENT):
+			return (&(val->warn_agt));
+		case (W_TEXT):
+			return (&(val->warn_text));
+	}
+	if (error != NULL)
+		*error = EINVAL;
+	return (NULL);
+}
+
+/*
+ * get warning code
+ */
+int
+sip_get_warning_code(sip_header_value_t value, int *error)
+{
+	int	*c;
+
+	if (error != NULL)
+		*error = 0;
+
+	if (value == NULL || value->value_state == SIP_VALUE_DELETED) {
+		if (error != NULL)
+			*error = EINVAL;
+		return (-1);
+	}
+	c = (int *)sip_get_warninfo(value, W_CODE, error);
+	if (c == NULL)
+		return (-1);
+	return (*c);
+}
+
+/*
+ * get warning agent
+ */
+const sip_str_t *
+sip_get_warning_agent(sip_header_value_t value, int *error)
+{
+	sip_str_t	*r;
+
+	if (value == NULL || value->value_state == SIP_VALUE_DELETED) {
+		if (error != NULL)
+			*error = EINVAL;
+		return (NULL);
+	}
+	r = (sip_str_t *)sip_get_warninfo(value, W_AGENT, error);
+	return (r);
+}
+
+/*
+ * get warning text
+ */
+const sip_str_t *
+sip_get_warning_text(sip_header_value_t value, int *error)
+{
+	sip_str_t	*r;
+
+	if (value == NULL || value->value_state == SIP_VALUE_DELETED) {
+		if (error != NULL)
+			*error = EINVAL;
+		return (NULL);
+	}
+	r = (sip_str_t *)sip_get_warninfo(value, W_TEXT, error);
+	return (r);
+}
+
+/*
+ * get authorization scheme
+ */
+const sip_str_t *
+sip_get_author_scheme(sip_msg_t sip_msg, int *error)
+{
+	sip_str_t	*r;
+
+	r = sip_get_val_from_msg(sip_msg, SIP_AUTHOR, SIP_AUTH_VAL, B_FALSE,
+	    B_FALSE, error);
+	return (r);
+}
+
+/*
+ * get authentication parameter
+ */
+static const sip_str_t *
+sip_get_auth_param(sip_msg_t msg, char *hdr_name, char *pname, int *error)
+{
+	const _sip_header_t	*header;
+	sip_hdr_value_t		*value;
+	sip_param_t		*param;
+
+	if (error != NULL)
+		*error = 0;
+
+	if (msg == NULL || pname == NULL || hdr_name == NULL) {
+		if (error != NULL)
+			*error = EINVAL;
+		return (NULL);
+	}
+
+	header = sip_get_header(msg, hdr_name, NULL, error);
+	if (header == NULL) {
+		if (error != NULL)
+			*error = EINVAL;
+		return (NULL);
+	}
+
+	value = (sip_hdr_value_t *)sip_get_header_value(header, error);
+	if (value == NULL) {
+		if (error != NULL)
+			*error = EPROTO;
+		return (NULL);
+	}
+
+	param = sip_get_param_from_list(value->auth_param, pname);
+	if (param != NULL)
+		return (&param->param_value);
+	return (NULL);
+}
+
+/*
+ * get authentication parameter
+ */
+const sip_str_t *
+sip_get_author_param(sip_msg_t sip_msg, char *name, int *error)
+{
+	const sip_str_t	*r;
+
+	r = sip_get_auth_param(sip_msg, SIP_AUTHOR, name, error);
+	return (r);
+}
+
+/*
+ * get authentication info
+ */
+const sip_str_t *
+sip_get_authen_info(sip_header_value_t value, int *error)
+{
+	sip_str_t	*r;
+	sip_hdr_value_t	*val = (sip_hdr_value_t *)value;
+
+	if (error != NULL)
+		*error = 0;
+	if (value == NULL || value->value_state == SIP_VALUE_DELETED) {
+		if (error != NULL)
+			*error = EINVAL;
+		return (NULL);
+	}
+	r = sip_get_val_from_hdr(val, SIP_STR_VAL, B_FALSE, error);
+	return (r);
+}
+
+/*
+ * get proxy-authentication scheme
+ */
+const sip_str_t *
+sip_get_proxy_authen_scheme(sip_msg_t msg, int *error)
+{
+	sip_str_t	*r;
+
+	r = sip_get_val_from_msg(msg, SIP_PROXY_AUTHEN, SIP_AUTH_VAL, B_FALSE,
+	    B_FALSE, error);
+	return (r);
+}
+
+/*
+ * get proxy authentication parameter
+ */
+const sip_str_t *
+sip_get_proxy_authen_param(sip_msg_t sip_msg, char *name, int *error)
+{
+	const sip_str_t	*r;
+
+	r = sip_get_auth_param(sip_msg, SIP_PROXY_AUTHEN, name, error);
+	return (r);
+}
+
+/*
+ * get proxy-authorization scheme
+ */
+const sip_str_t *
+sip_get_proxy_author_scheme(sip_msg_t msg, int *error)
+{
+	sip_str_t	*r;
+
+	r = sip_get_val_from_msg(msg, SIP_PROXY_AUTHOR, SIP_AUTH_VAL, B_FALSE,
+	    B_FALSE, error);
+	return (r);
+}
+
+/*
+ * get proxy-authorization parameter
+ */
+const sip_str_t *
+sip_get_proxy_author_param(sip_msg_t sip_msg, char *name, int *error)
+{
+	const sip_str_t	*r;
+
+	r = sip_get_auth_param(sip_msg, SIP_PROXY_AUTHOR, name, error);
+	return (r);
+}
+
+/*
+ * get proxy-require
+ */
+const sip_str_t *
+sip_get_proxy_require(sip_header_value_t value, int *error)
+{
+	sip_str_t	*r;
+	sip_hdr_value_t	*val = (sip_hdr_value_t *)value;
+
+	if (error != NULL)
+		*error = 0;
+	if (value == NULL || value->value_state == SIP_VALUE_DELETED) {
+		if (error != NULL)
+			*error = EINVAL;
+		return (NULL);
+	}
+	r = sip_get_val_from_hdr(val, SIP_STR_VAL, B_FALSE, error);
+	return (r);
+}
+
+/*
+ * get www-authentication scheme
+ */
+const sip_str_t *
+sip_get_www_authen_scheme(sip_msg_t msg, int *error)
+{
+	sip_str_t	*r;
+
+	r = sip_get_val_from_msg(msg, SIP_WWW_AUTHEN, SIP_AUTH_VAL, B_FALSE,
+	    B_FALSE, error);
+	return (r);
+}
+
+/*
+ * get www-authentication parameter
+ */
+const sip_str_t *
+sip_get_www_authen_param(sip_msg_t sip_msg, char *name, int *error)
+{
+	const sip_str_t	*r;
+
+	r = sip_get_auth_param(sip_msg, SIP_WWW_AUTHEN, name, error);
+	return (r);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/lib/libsip/common/sip_headers.c	Sat Oct 07 14:26:26 2006 -0700
@@ -0,0 +1,1005 @@
+/*
+ * 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"
+
+sip_methods_t sip_methods[MAX_SIP_METHODS] = {
+	{"UNKNOWN", 7},
+	{"INVITE", 6},
+	{"ACK", 3},
+	{"OPTIONS", 7},
+	{"BYE", 3},
+	{"CANCEL", 6},
+	{"REGISTER", 8},
+	{"REFER", 5},
+	{"INFO", 4},
+	{"SUBSCRIBE", 9},
+	{"NOTIFY", 6},
+	{"PRACK", 5}
+};
+
+/*
+ * Built-In Header function table
+ */
+sip_header_function_t sip_header_function_table[] = {
+	{"Unknown", NULL, sip_parse_unknown_header, NULL, NULL, NULL},
+	{"CONTACT", "m", sip_parse_cftr_header, NULL, NULL,
+	sip_free_cftr_header},
+	{"FROM", "F", sip_parse_cftr_header, NULL, NULL, sip_free_cftr_header},
+	{"TO", "T", sip_parse_cftr_header, NULL, NULL, sip_free_cftr_header},
+	{"CONTENT-LENGTH", "l", sip_parse_clen_header, NULL, NULL,
+	sip_free_phdr},
+	{"CONTENT-TYPE", "c", sip_parse_ctype_header, NULL, NULL,
+	sip_free_phdr},
+	{"CALL-ID", "i", sip_parse_cid_header, NULL, NULL, sip_free_phdr},
+	{"CSEQ", NULL, sip_parse_cseq_header, NULL, NULL, sip_free_phdr},
+	{"VIA", "v", sip_parse_via_header, NULL, NULL, sip_free_phdr},
+	{"Max-Forwards", NULL, sip_parse_maxf_header, NULL, NULL,
+	sip_free_phdr},
+	{"RECORD-ROUTE", NULL, sip_parse_cftr_header, NULL, NULL,
+	sip_free_cftr_header},
+	{"ROUTE", NULL, sip_parse_cftr_header, NULL, NULL,
+	sip_free_cftr_header},
+	{"ACCEPT", NULL, sip_parse_acpt_header, NULL, NULL, sip_free_phdr},
+	{"ACCEPT-ENCODING", NULL, sip_parse_acpt_encode_header, NULL, NULL,
+	sip_free_phdr},
+	{"ACCEPT-LANGUAGE", NULL, sip_parse_acpt_lang_header, NULL, NULL,
+	sip_free_phdr},
+	{"ALERT-INFO", NULL, sip_parse_alert_header, NULL, NULL, sip_free_phdr},
+	{"ALLOW", NULL, sip_parse_allow_header, NULL, NULL, sip_free_phdr},
+	{"CALL-INFO", NULL, sip_parse_callinfo_header, NULL, NULL,
+	sip_free_phdr},
+	{"CONTENT-DISPOSITION", NULL, sip_parse_contentdis_header, NULL, NULL,
+	sip_free_phdr},
+	{"CONTENT-ENCODING", "e", sip_parse_contentencode_header, NULL, NULL,
+	sip_free_phdr},
+	{"CONTENT-LANGUAGE", NULL, sip_parse_contentlang_header, NULL, NULL,
+	sip_free_phdr},
+	{"DATE", NULL, sip_parse_date_header, NULL, NULL, sip_free_phdr},
+	{"ERROR-INFO", NULL, sip_parse_errorinfo_header, NULL, NULL,
+	sip_free_phdr},
+	{"EXPIRES", NULL, sip_parse_expire_header, NULL, NULL, sip_free_phdr},
+	{"IN-REPLY-TO", NULL, sip_parse_inreplyto_header, NULL, NULL,
+	sip_free_phdr},
+	{"MIN-EXPIRES", NULL, sip_parse_minexpire_header, NULL, NULL,
+	sip_free_phdr},
+	{"MIME-VERSION", NULL, sip_parse_mimeversion_header, NULL, NULL,
+	sip_free_phdr},
+	{"ORGANIZATION", NULL, sip_parse_org_header, NULL, NULL, sip_free_phdr},
+	{"PRIORITY", NULL, sip_parse_priority_header, NULL, NULL,
+	sip_free_phdr},
+	{"REQUIRE", NULL, sip_parse_require_header, NULL, NULL, sip_free_phdr},
+	{"REPLY-TO", NULL, sip_parse_replyto_header, NULL, NULL, sip_free_phdr},
+	{"RETRY-AFTER", NULL, sip_parse_retryaft_header, NULL, NULL,
+	sip_free_phdr},
+	{"SERVER", NULL, sip_parse_server_header, NULL, NULL, sip_free_phdr},
+	{"SUBJECT", "s", sip_parse_subject_header, NULL, NULL, sip_free_phdr},
+	{"TIMESTAMP", NULL, sip_parse_timestamp_header, NULL, NULL,
+	sip_free_phdr},
+	{"UNSUPPORTED", NULL, sip_parse_usupport_header, NULL, NULL,
+	sip_free_phdr},
+	{"SUPPORTED", "k", sip_parse_support_header, NULL, NULL, sip_free_phdr},
+	{"USER-AGENT", NULL, sip_parse_useragt_header, NULL, NULL,
+	sip_free_phdr},
+	{"WARNING", NULL, sip_parse_warn_header, NULL, NULL, sip_free_phdr},
+	{"ALLOW-EVENTS", "u", sip_parse_allow_events_header, NULL, NULL,
+	sip_free_phdr},
+	{"EVENT", "o", sip_parse_event_header, NULL, NULL, sip_free_phdr},
+	{"SUBSCRIPTION-STATE", NULL, sip_parse_substate_header, NULL, NULL,
+	sip_free_phdr},
+	{"AUTHORIZATION", NULL, sip_parse_author_header, NULL, NULL,
+	sip_free_phdr},
+	{"AUTHENTICATION-INFO", NULL, sip_parse_ainfo_header, NULL, NULL,
+	sip_free_phdr},
+	{"PROXY-AUTHORIZATION", NULL, sip_parse_pauthor_header, NULL, NULL,
+	sip_free_phdr},
+	{"PROXY-AUTHENTICATE", NULL, sip_parse_pauthen_header, NULL, NULL,
+	sip_free_phdr},
+	{"PROXY-REQUIRE", NULL, sip_parse_preq_header, NULL, NULL,
+	sip_free_phdr},
+	{"WWW-AUTHENTICATE", NULL, sip_parse_wauthen_header, NULL, NULL,
+	sip_free_phdr},
+	{"RSEQ", NULL, sip_parse_rseq, NULL, NULL, sip_free_phdr},
+	{"RACK", NULL, sip_parse_rack, NULL, NULL, sip_free_phdr},
+	{"P-ASSERTED-IDENTITY", NULL, sip_parse_passertedid, NULL, NULL,
+	sip_free_phdr},
+	{"P-PREFERRED-IDENTITY", NULL, sip_parse_ppreferredid, NULL, NULL,
+	sip_free_phdr},
+	{"PRIVACY", NULL, sip_parse_privacy_header, NULL, NULL, sip_free_phdr},
+	{NULL, NULL, NULL, NULL, NULL, NULL},
+};
+
+#define	MAX_SIP_HEADERS	\
+	sizeof (sip_header_function_table) / sizeof (sip_header_function_t)
+
+/*
+ * External/application provided function table
+ */
+sip_header_function_t *sip_header_function_table_external = NULL;
+
+/*
+ * Free parameter list
+ */
+static void
+sip_free_params(sip_param_t *param_list)
+{
+	sip_param_t *param, *next_param;
+
+	param = param_list;
+
+	while (param != NULL) {
+		next_param = param->param_next;
+		free(param);
+		param = next_param;
+	}
+}
+
+/*
+ * Common header free routine
+ */
+void
+sip_free_phdr(sip_parsed_header_t *header)
+{
+	sip_hdr_value_t	*value;
+	sip_hdr_value_t	*next_value;
+
+	if (header == NULL)
+		return;
+	value = (sip_hdr_value_t *)header->value;
+	while (value != NULL) {
+		sip_free_params(value->sip_param_list);
+		next_value = value->sip_next_value;
+		free(value);
+		value = next_value;
+	}
+	free(header);
+}
+
+/*
+ * Free Contact/From/To header
+ */
+void
+sip_free_cftr_header(sip_parsed_header_t *header)
+{
+	sip_hdr_value_t	*value;
+	sip_hdr_value_t	*next_value;
+
+	if (header == NULL)
+		return;
+	value = (sip_hdr_value_t *)header->value;
+	while (value != NULL) {
+		next_value = value->sip_next_value;
+		sip_free_params(value->sip_param_list);
+		if (value->cftr_name != NULL)
+			free(value->cftr_name);
+		if (value->sip_value_parsed_uri != NULL) {
+			sip_free_parsed_uri(value->sip_value_parsed_uri);
+			value->sip_value_parsed_uri = NULL;
+		}
+		free(value);
+		value = next_value;
+	}
+	free(header);
+}
+
+/*
+ * Return new header
+ */
+_sip_header_t *
+sip_new_header(int header_size)
+{
+	_sip_header_t *new_header;
+
+	new_header = calloc(1, sizeof (_sip_header_t));
+	if (new_header == NULL)
+		return (NULL);
+
+	/*
+	 * We are using snprintf which adds a null character
+	 * so allocate an extra byte which is not part of
+	 * the message header
+	 */
+	new_header->sip_hdr_start = calloc(1, header_size + 1);
+	if (new_header->sip_hdr_start == NULL) {
+		free(new_header);
+		return (NULL);
+	}
+	new_header->sip_hdr_end = new_header->sip_hdr_start + header_size;
+	new_header->sip_hdr_current = new_header->sip_hdr_start;
+	new_header->sip_hdr_allocated = B_TRUE;
+	return (new_header);
+}
+
+/*
+ * Free the given header
+ */
+void
+sip_free_header(_sip_header_t *sip_header)
+{
+	if (sip_header->sip_hdr_allocated) {
+		assert(sip_header->sip_hdr_start != NULL);
+		free(sip_header->sip_hdr_start);
+	}
+	if (sip_header->sip_hdr_parsed != NULL) {
+		assert(sip_header->sip_header_functions != NULL);
+		if (sip_header->sip_header_functions->header_free != NULL) {
+			sip_header->sip_header_functions->header_free(
+			    sip_header->sip_hdr_parsed);
+		}
+	}
+	free(sip_header);
+}
+
+/*
+ * Return a copy of the header passed in.
+ */
+_sip_header_t *
+sip_dup_header(_sip_header_t *from)
+{
+	size_t		hdr_size;
+	_sip_header_t	*to;
+
+	hdr_size = from->sip_hdr_end - from->sip_hdr_start;
+	to = sip_new_header(hdr_size);
+	if (to == NULL)
+		return (NULL);
+	if (from->sip_header_state == SIP_HEADER_DELETED_VAL) {
+		to->sip_hdr_end = to->sip_hdr_start +
+		    sip_copy_values(to->sip_hdr_start, from);
+	} else {
+		(void) memcpy(to->sip_hdr_start, from->sip_hdr_start, hdr_size);
+		to->sip_hdr_end = to->sip_hdr_start + hdr_size;
+	}
+	to->sip_header_functions = from->sip_header_functions;
+	return (to);
+}
+
+/*
+ * Copy header with extra_param, if any, to sip_msg
+ */
+int
+_sip_copy_header(_sip_msg_t *sip_msg, _sip_header_t *header, char *extra_param,
+    boolean_t skip_crlf)
+{
+	_sip_header_t	*new_header;
+	int		hdrlen;
+	int		extra_len = 0;
+	int		ncrlf = 0;
+	char		*p;
+
+#ifdef	__solaris__
+	assert(mutex_held(&sip_msg->sip_msg_mutex));
+#endif
+	if (extra_param != NULL) {
+		extra_len = SIP_SPACE_LEN + sizeof (char) + SIP_SPACE_LEN +
+		    strlen(extra_param);
+	}
+	/*
+	 * Just take one if there are more, i.e. if this is the last header
+	 * before the content.
+	 */
+	if (skip_crlf) {
+		if (header->sip_hdr_end - strlen(SIP_CRLF) <=
+		    header->sip_hdr_start) {
+			goto proceed;
+		}
+		p = header->sip_hdr_end - strlen(SIP_CRLF);
+		while (strncmp(SIP_CRLF, p, strlen(SIP_CRLF)) == 0) {
+			ncrlf++;
+			if (p - strlen(SIP_CRLF) < header->sip_hdr_start)
+				break;
+			p -= strlen(SIP_CRLF);
+		}
+		/*
+		 * Take one CRLF.
+		 */
+		ncrlf = (ncrlf - 1) * strlen(SIP_CRLF);
+	}
+proceed:
+	hdrlen = header->sip_hdr_end - header->sip_hdr_start - ncrlf;
+	new_header = sip_new_header(hdrlen + extra_len);
+	if (new_header == NULL)
+		return (ENOMEM);
+	if (header->sip_header_state == SIP_HEADER_DELETED_VAL) {
+		int	len;
+
+		len = sip_copy_values(new_header->sip_hdr_start, header);
+		new_header->sip_hdr_end = new_header->sip_hdr_start + len;
+		hdrlen = hdrlen - len + extra_len;
+	} else {
+		(void) memcpy(new_header->sip_hdr_start, header->sip_hdr_start,
+		    hdrlen);
+		new_header->sip_hdr_end = new_header->sip_hdr_start + hdrlen;
+		hdrlen = extra_len;
+	}
+	if (extra_param != NULL) {
+		/*
+		 * Find CR
+		 */
+		if (sip_find_cr(new_header) != 0) {
+			sip_free_header(new_header);
+			return (EINVAL);
+		}
+		hdrlen += new_header->sip_hdr_end - new_header->sip_hdr_current;
+		(void) snprintf(new_header->sip_hdr_current, hdrlen + 1,
+		    " %c %s%s", SIP_SEMI, extra_param, SIP_CRLF);
+	}
+
+	new_header->sip_hdr_end += extra_len;
+	new_header->sip_header_functions = header->sip_header_functions;
+	_sip_add_header(sip_msg, new_header, B_TRUE, B_FALSE, NULL);
+	return (0);
+}
+
+/*
+ * Copy all "header_name" headers from _old_msg to _new_msg
+ */
+int
+_sip_find_and_copy_all_header(_sip_msg_t *_old_msg, _sip_msg_t *_new_msg,
+    char *header_name)
+{
+	_sip_header_t	*header;
+	int		ret = 0;
+
+	if (_old_msg == NULL || _new_msg == NULL)
+		return (EINVAL);
+#ifdef	__solaris__
+	assert(mutex_held(&_old_msg->sip_msg_mutex));
+#endif
+	if (_old_msg != _new_msg)
+		(void) pthread_mutex_lock(&_new_msg->sip_msg_mutex);
+	header = sip_search_for_header(_old_msg, header_name, NULL);
+	while (header != NULL) {
+		ret = _sip_copy_header(_new_msg, header, NULL, B_TRUE);
+		if (ret != 0)
+			break;
+		header = sip_search_for_header(_old_msg, header_name, header);
+	}
+	if (_old_msg != _new_msg)
+		(void) pthread_mutex_unlock(&_new_msg->sip_msg_mutex);
+	return (ret);
+}
+
+/*
+ * Copy header_name from _old_msg to _new_msg with extra_parm.
+ */
+int
+_sip_find_and_copy_header(sip_msg_t _old_msg, sip_msg_t _new_msg,
+    char *header_name, char *extra_param, boolean_t lock_newmsg)
+{
+	_sip_header_t	*header;
+	int		ret;
+
+	if (_old_msg == NULL || _new_msg == NULL)
+		return (EINVAL);
+#ifdef	__solaris__
+	assert(mutex_held(&_old_msg->sip_msg_mutex));
+#endif
+	header = sip_search_for_header(_old_msg, header_name, NULL);
+	if (header == NULL)
+		return (EINVAL);
+	if (lock_newmsg)
+		(void) pthread_mutex_lock(&_new_msg->sip_msg_mutex);
+	ret = _sip_copy_header(_new_msg, header, extra_param, B_TRUE);
+	if (lock_newmsg)
+		(void) pthread_mutex_unlock(&_new_msg->sip_msg_mutex);
+	return (ret);
+}
+
+/*
+ * Copy all headers from old_msg to new_msg
+ */
+int
+sip_copy_all_headers(sip_msg_t old_msg, sip_msg_t new_msg)
+{
+	_sip_header_t	*header;
+	_sip_msg_t	*_old_msg;
+	_sip_msg_t	*_new_msg;
+	int		ret = 0;
+
+	if (old_msg == NULL || new_msg == NULL)
+		return (EINVAL);
+	_old_msg = (_sip_msg_t *)old_msg;
+	_new_msg = (_sip_msg_t *)new_msg;
+
+	(void) pthread_mutex_lock(&_old_msg->sip_msg_mutex);
+	(void) pthread_mutex_lock(&_new_msg->sip_msg_mutex);
+	header = sip_search_for_header(_old_msg, NULL, NULL);
+	while (header != NULL) {
+		ret = _sip_copy_header(_new_msg, header, NULL, B_FALSE);
+		if (ret != 0)
+			goto done;
+		header = sip_search_for_header(_old_msg, NULL, header);
+	}
+done:
+	(void) pthread_mutex_unlock(&_new_msg->sip_msg_mutex);
+	(void) pthread_mutex_unlock(&_old_msg->sip_msg_mutex);
+	return (ret);
+}
+
+/*
+ * Copy start line from msg to sip_msg
+ */
+int
+sip_copy_start_line(sip_msg_t msg, sip_msg_t sip_msg)
+{
+	int		len;
+	_sip_header_t	*new_header;
+	_sip_msg_t	*_old_msg;
+	_sip_msg_t	*_sip_msg;
+
+	if (msg == NULL || sip_msg == NULL)
+		return (EINVAL);
+	_old_msg = (_sip_msg_t *)msg;
+	_sip_msg = (_sip_msg_t *)sip_msg;
+
+	(void) pthread_mutex_lock(&_old_msg->sip_msg_mutex);
+	if (_old_msg->sip_msg_start_line == NULL) {
+		(void) pthread_mutex_unlock(&_old_msg->sip_msg_mutex);
+		return (EINVAL);
+	}
+	len = _old_msg->sip_msg_start_line->sip_hdr_end -
+	    _old_msg->sip_msg_start_line->sip_hdr_start;
+	new_header = sip_new_header(len);
+	if (new_header == NULL) {
+		(void) pthread_mutex_unlock(&_old_msg->sip_msg_mutex);
+		return (ENOMEM);
+	}
+	new_header->sip_hdr_sipmsg = _sip_msg;
+	(void) pthread_mutex_lock(&sip_msg->sip_msg_mutex);
+	_sip_msg->sip_msg_start_line = new_header;
+	_sip_msg->sip_msg_len = len;
+	(void) strncpy(_sip_msg->sip_msg_start_line->sip_hdr_start,
+	    _old_msg->sip_msg_start_line->sip_hdr_start, len);
+	(void) sip_parse_first_line(_sip_msg->sip_msg_start_line,
+	    &_sip_msg->sip_msg_req_res);
+	(void) pthread_mutex_unlock(&_old_msg->sip_msg_mutex);
+	(void) pthread_mutex_unlock(&sip_msg->sip_msg_mutex);
+	return (0);
+}
+
+/*
+ * Delete start line from sip_msg
+ */
+int
+sip_delete_start_line_locked(_sip_msg_t *_sip_msg)
+{
+	_sip_header_t	*header;
+	_sip_header_t	*next_header;
+
+	if (_sip_msg->sip_msg_start_line == NULL)
+		return (EINVAL);
+
+	header = _sip_msg->sip_msg_start_line;
+	while (header != NULL) {
+		next_header = header->sip_hdr_next;
+		_sip_msg->sip_msg_len -= (header->sip_hdr_end -
+		    header->sip_hdr_start);
+		sip_free_header(header);
+		header = next_header;
+	}
+	_sip_msg->sip_msg_start_line = NULL;
+
+	/*
+	 * Also delete the sip_msg_req_res info since we don't have a start
+	 * line.
+	 */
+	while (_sip_msg->sip_msg_req_res != NULL) {
+		sip_message_type_t	*sip_msg_type_ptr;
+
+		sip_msg_type_ptr = _sip_msg->sip_msg_req_res->sip_next;
+		if (_sip_msg->sip_msg_req_res->is_request) {
+			sip_request_t	*reqline;
+
+			reqline = &_sip_msg->sip_msg_req_res->U.sip_request;
+			if (reqline->sip_parse_uri != NULL) {
+				sip_free_parsed_uri(reqline->sip_parse_uri);
+				reqline->sip_parse_uri = NULL;
+			}
+		}
+		free(_sip_msg->sip_msg_req_res);
+		_sip_msg->sip_msg_req_res = sip_msg_type_ptr;
+	}
+	return (0);
+}
+
+
+/*
+ * Delete start line from sip_msg
+ */
+int
+sip_delete_start_line(sip_msg_t sip_msg)
+{
+	_sip_msg_t	*_sip_msg;
+	int		ret;
+
+	if (sip_msg == NULL)
+		return (EINVAL);
+
+	_sip_msg = (_sip_msg_t *)sip_msg;
+	(void) pthread_mutex_lock(&_sip_msg->sip_msg_mutex);
+	ret = sip_delete_start_line_locked(_sip_msg);
+	(void) pthread_mutex_unlock(&_sip_msg->sip_msg_mutex);
+
+	return (ret);
+}
+
+/*
+ * Delete all headers from _sip_msg
+ */
+void
+sip_delete_all_headers(_sip_msg_t *_sip_msg)
+{
+	_sip_header_t *header;
+
+#ifdef	__solaris__
+	assert(mutex_held(&_sip_msg->sip_msg_mutex));
+#endif
+
+	header = _sip_msg->sip_msg_headers_start;
+	while (header != NULL) {
+		_sip_header_t *next_header;
+		next_header = header->sip_hdr_next;
+		sip_free_header(header);
+		header = next_header;
+	}
+	_sip_msg->sip_msg_headers_start = NULL;
+	_sip_msg->sip_msg_headers_end = NULL;
+}
+
+/*
+ * Delete and free the named header. If header_name is null
+ * free all headers.
+ */
+void
+sip_delete_headers(sip_msg_t sip_msg, char *header_name)
+{
+	_sip_header_t *header;
+	_sip_msg_t *_sip_msg;
+
+	_sip_msg = (_sip_msg_t *)sip_msg;
+#ifdef	__solaris__
+	assert(mutex_held(&_sip_msg->sip_msg_mutex));
+#endif
+	header = sip_search_for_header(_sip_msg, header_name, NULL);
+	if (header == NULL)
+		return;
+	while (header != NULL) {
+		if (_sip_msg->sip_msg_headers_start == header) {
+			_sip_msg->sip_msg_headers_start = header->sip_hdr_next;
+		} else {
+			header->sip_hdr_prev->sip_hdr_next =
+			    header->sip_hdr_next;
+		}
+		if (_sip_msg->sip_msg_headers_end == header) {
+			_sip_msg->sip_msg_headers_end = header->sip_hdr_prev;
+		} else {
+			header->sip_hdr_next->sip_hdr_prev =
+			    header->sip_hdr_prev;
+		}
+		sip_free_header(header);
+		if (header_name != NULL)
+			return;
+		else
+			header = sip_search_for_header(_sip_msg, NULL, NULL);
+	}
+}
+
+/*
+ * Add a header to sip_msg. If header_name is provided then the new header
+ * is added before that header, if first is set, or after. If append is
+ * set, then the header is added to the end of the header list.
+ */
+void
+_sip_add_header(_sip_msg_t *sip_msg, _sip_header_t *new_header,
+    boolean_t append, boolean_t first, char *header_name)
+{
+	_sip_header_t	*header = NULL;
+
+	if (sip_msg == NULL || new_header == NULL)
+		return;
+#ifdef	__solaris__
+	assert(mutex_held(&sip_msg->sip_msg_mutex));
+#endif
+	new_header->sip_hdr_sipmsg = sip_msg;
+	if (header_name != NULL) {
+		_sip_header_t	*header_tmp;
+
+		header = sip_search_for_header(sip_msg, header_name, NULL);
+		header_tmp = header;
+		if (!first) {
+			while (header != NULL) {
+				header_tmp = header;
+				header = sip_search_for_header(sip_msg,
+				    header_name, header);
+			}
+		}
+		header = header_tmp;
+		if (header == NULL)
+			append =  B_TRUE;
+	}
+
+	if (header != NULL) {
+		if (append) {
+			new_header->sip_hdr_prev = header;
+			if (sip_msg->sip_msg_headers_end == header) {
+				sip_msg->sip_msg_headers_end = new_header;
+				new_header->sip_hdr_next = NULL;
+			} else {
+				header->sip_hdr_next->sip_hdr_prev = new_header;
+				new_header->sip_hdr_next = header->sip_hdr_next;
+			}
+			header->sip_hdr_next = new_header;
+		} else {
+			new_header->sip_hdr_next = header;
+			if (sip_msg->sip_msg_headers_start == header) {
+				sip_msg->sip_msg_headers_start = new_header;
+				new_header->sip_hdr_prev = NULL;
+			} else {
+				header->sip_hdr_prev->sip_hdr_next = new_header;
+				new_header->sip_hdr_prev = header->sip_hdr_prev;
+			}
+			header->sip_hdr_prev = new_header;
+		}
+	} else {
+		if (append) {
+			if (sip_msg->sip_msg_headers_end != NULL) {
+				sip_msg->sip_msg_headers_end->sip_hdr_next =
+				    new_header;
+			} else {
+				sip_msg->sip_msg_headers_start = new_header;
+			}
+			new_header->sip_hdr_prev =
+			    sip_msg->sip_msg_headers_end;
+			new_header->sip_hdr_next = NULL;
+			sip_msg->sip_msg_headers_end = new_header;
+		} else {
+			if (sip_msg->sip_msg_headers_start != NULL) {
+				sip_msg->sip_msg_headers_start->sip_hdr_prev =
+				    new_header;
+			} else {
+				sip_msg->sip_msg_headers_end = new_header;
+			}
+			new_header->sip_hdr_next =
+			    sip_msg->sip_msg_headers_start;
+			new_header->sip_hdr_prev = NULL;
+			sip_msg->sip_msg_headers_start = new_header;
+		}
+	}
+	sip_msg->sip_msg_len += new_header->sip_hdr_end -
+	    new_header->sip_hdr_start;
+}
+
+/*
+ * Scan through the function table and return the entry for the given header
+ * type.
+ */
+sip_header_function_t *
+_sip_get_header_functions(sip_header_function_t *sip_header_function_table,
+    _sip_header_t *sip_header, char *header_name)
+{
+	int	len;
+	int	i = 0;
+
+	if (sip_header == NULL && header_name == NULL)
+		return (NULL);
+
+	/*
+	 * If header_name is NULL we first have to locate the name
+	 */
+	if (header_name == NULL) {
+		if (sip_skip_white_space(sip_header) != 0) {
+			return (NULL);
+		}
+		header_name = sip_header->sip_hdr_current;
+		if (sip_find_separator(sip_header, SIP_HCOLON, (char)NULL,
+		    (char)NULL) != 0) {
+			return (NULL);
+		}
+		len = sip_header->sip_hdr_current - header_name;
+	} else {
+		len = strlen(header_name);
+	}
+
+	if (len > 0) {
+		while (sip_header_function_table[i].header_name != NULL ||
+		    sip_header_function_table[i].header_short_name != NULL) {
+			if (sip_header_function_table[i].header_name != NULL &&
+			    len ==
+			    strlen(sip_header_function_table[i].header_name)) {
+				if (strncasecmp(header_name,
+				    sip_header_function_table[i].
+				    header_name, len) == 0) {
+					break;
+				}
+			} else if (sip_header_function_table[i].
+			    header_short_name != NULL && len ==
+			    strlen(sip_header_function_table[i].
+			    header_short_name)) {
+				if (strncasecmp(header_name,
+				    sip_header_function_table[i].
+				    header_short_name, len) == 0) {
+					break;
+				}
+			}
+			i++;
+		}
+	}
+
+	if (sip_header != NULL)
+		sip_header->sip_hdr_current = sip_header->sip_hdr_start;
+	if (sip_header_function_table[i].header_name == NULL)
+		return (NULL);
+	return (&sip_header_function_table[i]);
+}
+
+/*
+ * Return the entry from the function table for the given header
+ */
+sip_header_function_t *
+sip_get_header_functions(_sip_header_t *sip_header, char *header_name)
+{
+	sip_header_function_t	*func;
+	sip_header_function_t	*header_f_table = NULL;
+
+	if (sip_header_function_table_external != NULL) {
+		header_f_table = _sip_get_header_functions(
+		    sip_header_function_table_external,
+		    sip_header, header_name);
+		if (header_f_table != NULL)
+			return (header_f_table);
+	}
+	func = _sip_get_header_functions(sip_header_function_table, sip_header,
+	    header_name);
+	return (func);
+}
+
+/*
+ * Search for the header name passed in.
+ */
+_sip_header_t *
+sip_search_for_header(_sip_msg_t *sip_msg, char *header_name,
+    _sip_header_t *old_header)
+{
+	int			len = 0;
+	int			full_len = 0;
+	int			compact_len = 0;
+	_sip_header_t		*header = NULL;
+	char			*compact_name = NULL;
+	char			*full_name = NULL;
+	sip_header_function_t	*header_f_table = NULL;
+
+	if (sip_msg == NULL)
+		return (NULL);
+#ifdef	__solaris__
+	assert(mutex_held(&sip_msg->sip_msg_mutex));
+#endif
+
+	if (header_name != NULL) {
+		header_f_table = sip_get_header_functions(NULL, header_name);
+		if (header_f_table != NULL) {
+			full_name = header_f_table->header_name;
+			compact_name = header_f_table->header_short_name;
+			if (full_name != NULL)
+				full_len = strlen(full_name);
+			if (compact_name != NULL)
+				compact_len = strlen(compact_name);
+		} else {
+			header_f_table = &sip_header_function_table[0];
+			full_name = header_name;
+			full_len  = strlen(full_name);
+		}
+	}
+
+	if (old_header != NULL)
+		header = old_header->sip_hdr_next;
+	else
+		header = sip_msg->sip_msg_headers_start;
+
+	while (header != NULL) {
+
+		if (header->sip_header_state == SIP_HEADER_DELETED) {
+			header = header->sip_hdr_next;
+			continue;
+		}
+
+		if (compact_len == 0 && full_len == 0)
+			break;
+
+		header->sip_hdr_current = header->sip_hdr_start;
+
+		if (sip_skip_white_space(header)) {
+			header = header->sip_hdr_next;
+			continue;
+		}
+
+		len = header->sip_hdr_end - header->sip_hdr_current;
+
+		if (full_name != NULL && (full_len <= len) &&
+		    strncasecmp(header->sip_hdr_current, full_name,
+		    full_len) == 0) {
+			header->sip_hdr_current += full_len;
+			if (sip_skip_white_space(header)) {
+				header = header->sip_hdr_next;
+				continue;
+			}
+
+			if (*header->sip_hdr_current == SIP_HCOLON) {
+				header_name = full_name;
+				break;
+			}
+		}
+
+		if (compact_name != NULL && (compact_len <= len) &&
+		    strncasecmp(header->sip_hdr_current, compact_name,
+		    compact_len) == 0) {
+			header->sip_hdr_current += compact_len;
+			if (sip_skip_white_space(header)) {
+				header = header->sip_hdr_next;
+				continue;
+			}
+			if (*header->sip_hdr_current == SIP_HCOLON) {
+				header_name = compact_name;
+				break;
+			}
+		}
+		header = header->sip_hdr_next;
+	}
+
+	if (header != NULL) {
+		header->sip_hdr_current = header->sip_hdr_start;
+		if (header_f_table == NULL) {
+			header_f_table =
+			    sip_get_header_functions(header, header_name);
+			if (header_f_table == NULL)
+				header_f_table = &sip_header_function_table[0];
+		}
+
+		header->sip_header_functions = header_f_table;
+	}
+	return (header);
+}
+
+/*
+ * Return the start line as a string. Caller frees string
+ */
+char *
+_sip_startline_to_str(_sip_msg_t *sip_msg, int *error)
+{
+	char		*slstr;
+	int		len;
+
+	if (error != NULL)
+		*error = 0;
+
+	if (sip_msg == NULL || sip_msg->sip_msg_start_line == NULL) {
+		if (error != NULL)
+			*error = EINVAL;
+		return (NULL);
+	}
+	(void) pthread_mutex_lock(&sip_msg->sip_msg_mutex);
+	len = sip_msg->sip_msg_start_line->sip_hdr_end -
+	    sip_msg->sip_msg_start_line->sip_hdr_start - 2;
+	if ((slstr = malloc(len + 1)) == NULL) {
+		(void) pthread_mutex_unlock(&sip_msg->sip_msg_mutex);
+		if (error != NULL)
+			*error = ENOMEM;
+		return (NULL);
+	}
+	(void) strncpy(slstr, sip_msg->sip_msg_start_line->sip_hdr_start, len);
+	(void) pthread_mutex_unlock(&sip_msg->sip_msg_mutex);
+	slstr[len] = '\0';
+	return (slstr);
+}
+
+/*
+ * Return the given header as a string. Caller frees string
+ */
+char *
+sip_hdr_to_str(sip_header_t sip_header, int *error)
+{
+	char		*hdrstr;
+	char		*tmpptr;
+	_sip_header_t	*_sip_header;
+	int		len;
+
+	if (error != NULL)
+		*error = 0;
+
+	if (sip_header == NULL) {
+		if (error != NULL)
+			*error = EINVAL;
+		return (NULL);
+	}
+	_sip_header = (_sip_header_t *)sip_header;
+	if (_sip_header->sip_header_state == SIP_HEADER_DELETED) {
+		if (_sip_header->sip_hdr_sipmsg != NULL) {
+			(void) pthread_mutex_unlock(
+			    &_sip_header->sip_hdr_sipmsg->sip_msg_mutex);
+		}
+		if (error != NULL)
+			*error = EINVAL;
+		return (NULL);
+	}
+	if (_sip_header->sip_hdr_sipmsg != NULL) {
+		(void) pthread_mutex_lock(
+		    &_sip_header->sip_hdr_sipmsg->sip_msg_mutex);
+	}
+	len = _sip_header->sip_hdr_end - _sip_header->sip_hdr_start;
+	hdrstr = malloc(len);
+	if (hdrstr == NULL) {
+		if (_sip_header->sip_hdr_sipmsg != NULL) {
+			(void) pthread_mutex_unlock(
+			    &_sip_header->sip_hdr_sipmsg->sip_msg_mutex);
+		}
+		if (error != NULL)
+			*error = ENOMEM;
+		return (NULL);
+	}
+	if (_sip_header->sip_header_state == SIP_HEADER_DELETED_VAL) {
+		len = sip_copy_values(hdrstr, _sip_header);
+	} else {
+		(void) strncpy(hdrstr, _sip_header->sip_hdr_start, len);
+	}
+	if (_sip_header->sip_hdr_sipmsg != NULL) {
+		(void) pthread_mutex_unlock(
+		    &_sip_header->sip_hdr_sipmsg->sip_msg_mutex);
+	}
+	tmpptr = hdrstr + len;
+	while (*tmpptr-- != '\n') {
+		if (tmpptr == _sip_header->sip_hdr_start) {
+			free(hdrstr);
+			if (error != NULL)
+				*error = EINVAL;
+			return (NULL);
+		}
+	}
+	*tmpptr = '\0';
+	return (hdrstr);
+}
+
+/*
+ * Given a param list find the named parameter.
+ * Returns a pointer to the value or NULL.
+ */
+sip_param_t *
+sip_get_param_from_list(sip_param_t *param_list, char *param_name)
+{
+	while (param_list != NULL) {
+		if (param_list->param_name.sip_str_len == strlen(param_name) &&
+		    strncasecmp(param_list->param_name.sip_str_ptr, param_name,
+		    strlen(param_name)) == 0) {
+			return (param_list);
+		}
+		param_list = param_list->param_next;
+	}
+	return (NULL);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/lib/libsip/common/sip_itf.c	Sat Oct 07 14:26:26 2006 -0700
@@ -0,0 +1,642 @@
+/*
+ * 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"
+
+#ifdef	__linux__
+#include <stdarg.h>
+#else
+#include <sys/varargs.h>
+#endif
+#include "sip_parse_uri.h"
+#include "sip_msg.h"
+#include "sip_miscdefs.h"
+#include "sip_xaction.h"
+#include "sip_hash.h"
+#include "sip_dialog.h"
+#include "sip_parse_generic.h"
+
+#define	SIP_MSG_BUF_SZ	100
+
+
+void		(*sip_ulp_recv)(const sip_conn_object_t, sip_msg_t,
+		    const sip_dialog_t) = NULL;
+uint_t		(*sip_stack_timeout)(void *, void (*func)(void *),
+		    struct timeval *) = NULL;
+boolean_t	(*sip_stack_untimeout)(uint_t) = NULL;
+int		(*sip_stack_send)(sip_conn_object_t xonn_object, char *, int) =
+		    NULL;
+void		(*sip_refhold_conn)(sip_conn_object_t) = NULL;
+void		(*sip_refrele_conn)(sip_conn_object_t) = NULL;
+boolean_t	(*sip_is_conn_stream)(sip_conn_object_t) = NULL;
+boolean_t	(*sip_is_conn_reliable)(sip_conn_object_t) = NULL;
+int 		(*sip_conn_rem_addr)(sip_conn_object_t, struct sockaddr *,
+		    socklen_t *) = NULL;
+int		(*sip_conn_local_addr)(sip_conn_object_t, struct sockaddr *,
+		    socklen_t *) = NULL;
+int		(*sip_conn_transport)(sip_conn_object_t) = NULL;
+int		(*sip_conn_timer1)(sip_conn_object_t) = NULL;
+int		(*sip_conn_timer2)(sip_conn_object_t) = NULL;
+int		(*sip_conn_timer4)(sip_conn_object_t) = NULL;
+int		(*sip_conn_timerd)(sip_conn_object_t) = NULL;
+
+boolean_t	sip_manage_dialog = B_FALSE;
+
+uint64_t	sip_hash_salt = 0;
+
+/*
+ * Defaults, overridden by configured values, if any
+ */
+int		sip_timer_T1 = SIP_TIMER_T1;
+int		sip_timer_T2 = SIP_TIMER_T2;
+int		sip_timer_T4 = SIP_TIMER_T4;
+int		sip_timer_TD = 32 * SIP_SECONDS;
+
+/*
+ * list of sent-by values registered by the UA
+ */
+sent_by_list_t	*sip_sent_by = NULL;
+int		sip_sent_by_count = 0;
+pthread_mutex_t	sip_sent_by_lock;
+
+/*
+ * Create and send an error response
+ */
+static void
+sip_send_resp(sip_conn_object_t conn_obj, _sip_msg_t *sip_msg, int resp)
+{
+	_sip_msg_t		*sip_msg_resp;
+
+	sip_msg_resp = (_sip_msg_t *)sip_create_response((sip_msg_t)sip_msg,
+	    resp, sip_get_resp_desc(resp), NULL, NULL);
+	if (sip_msg_resp == NULL) {
+		/*
+		 * Message was too bad to even create a
+		 * response. Just drop the messge.
+		 */
+		return;
+	}
+	/*
+	 * We directly send it to the transport here.
+	 */
+	if (sip_adjust_msgbuf(sip_msg_resp) != 0) {
+		sip_free_msg((sip_msg_t)sip_msg_resp);
+		return;
+	}
+	(void) sip_stack_send(conn_obj, sip_msg_resp->sip_msg_buf,
+	    sip_msg_resp->sip_msg_len);
+	sip_free_msg((sip_msg_t)sip_msg_resp);
+}
+
+/*
+ * Validate some of the common headers
+ */
+boolean_t
+sip_check_common_headers(sip_conn_object_t conn_obj, _sip_msg_t *sip_msg)
+{
+	int	err;
+
+	if (sip_get_to_uri_str((sip_msg_t)sip_msg, &err) == NULL)
+		goto error;
+	if (sip_get_from_uri_str((sip_msg_t)sip_msg, &err) == NULL)
+		goto error;
+	if (sip_get_callseq_num((sip_msg_t)sip_msg, &err) < 0)
+		goto error;
+	if (sip_get_callid((sip_msg_t)sip_msg, &err) == NULL)
+		goto error;
+	return (B_FALSE);
+error:
+	sip_send_resp(conn_obj, sip_msg, SIP_BAD_REQUEST);
+	return (B_TRUE);
+}
+
+/*
+ * setup pointers to where the headers are.
+ */
+static int
+sip_setup_header_pointers(_sip_msg_t *sip_msg)
+{
+	char		*msg;
+	_sip_header_t	*sip_msg_header;
+	char		*end;
+
+	msg = sip_msg->sip_msg_buf;
+	end = sip_msg->sip_msg_buf + sip_msg->sip_msg_len;
+	/*
+	 * Skip while space.
+	 */
+	while (isspace(*msg)) {
+		if (msg < end)
+			msg++;
+		else
+			return (EINVAL);
+	}
+
+	/*
+	 * We consider Request and Response line as a header
+	 */
+	for (;;) {
+		/*
+		 * Skip CRLF
+		 */
+		if (strncmp(SIP_CRLF, msg, strlen(SIP_CRLF)) == 0) {
+			if (sip_msg->sip_msg_headers_end != NULL) {
+				SKIP_CRLF(msg);
+				sip_msg->sip_msg_headers_end->sip_hdr_end = msg;
+			}
+			/*
+			 * Start of a header.
+			 * Check for empty line.
+			 */
+			if (strncmp(SIP_CRLF, msg, strlen(SIP_CRLF)) == 0) {
+				/*
+				 * empty line, start of content.
+				 */
+				SKIP_CRLF(msg);
+				sip_msg->sip_msg_headers_end->sip_hdr_end = msg;
+				break;
+			}
+			/*
+			 * store start of header.
+			 */
+			sip_msg_header = calloc(1, sizeof (_sip_header_t));
+			if (sip_msg_header == NULL)
+				return (EINVAL);
+			sip_msg_header->sip_hdr_start = msg;
+			sip_msg_header->sip_hdr_current = msg;
+			sip_msg_header->sip_hdr_allocated = B_FALSE;
+			sip_msg_header->sip_hdr_prev =
+			    sip_msg->sip_msg_headers_end;
+			sip_msg_header->sip_hdr_next = NULL;
+			sip_msg_header->sip_hdr_sipmsg = sip_msg;
+			sip_msg->sip_msg_headers_end->sip_hdr_next =
+			    sip_msg_header;
+			sip_msg->sip_msg_headers_end = sip_msg_header;
+		} else {
+			if (sip_msg->sip_msg_headers_start == NULL) {
+				/*
+				 * Allocate first header structure.
+				 */
+				sip_msg_header = calloc(1,
+				    sizeof (_sip_header_t));
+				if (sip_msg_header == NULL)
+					return (EINVAL);
+				sip_msg_header->sip_hdr_allocated = B_FALSE;
+				sip_msg_header->sip_hdr_start = msg;
+				sip_msg_header->sip_hdr_current = msg;
+				sip_msg_header->sip_hdr_sipmsg = sip_msg;
+				sip_msg->sip_msg_headers_start = sip_msg_header;
+				sip_msg->sip_msg_headers_end = sip_msg_header;
+			}
+			msg++;
+		}
+		/*
+		 * We have reached the end without hitting the empty line.
+		 */
+		if (msg - sip_msg->sip_msg_buf >= sip_msg->sip_msg_len)
+			return (EINVAL);
+	}
+
+	if (sip_msg->sip_msg_headers_start == NULL)
+		return (EPROTO);
+
+	/*
+	 * Move start line to be a separate line.
+	 */
+	sip_msg->sip_msg_start_line = sip_msg->sip_msg_headers_start;
+	sip_msg->sip_msg_headers_start =
+	    sip_msg->sip_msg_headers_start->sip_hdr_next;
+	sip_msg->sip_msg_start_line->sip_hdr_prev = NULL;
+	sip_msg->sip_msg_start_line->sip_hdr_next = NULL;
+
+	if (sip_msg->sip_msg_headers_start == NULL)
+		return (EINVAL);
+	sip_msg->sip_msg_headers_start->sip_hdr_prev = NULL;
+
+
+	/*
+	 * Deal with content.
+	 */
+	sip_msg->sip_msg_content = calloc(1, sizeof (sip_content_t));
+	sip_msg->sip_msg_content->sip_content_start = msg;
+	sip_msg->sip_msg_content->sip_content_end = sip_msg->sip_msg_buf +
+	    sip_msg->sip_msg_len;
+	sip_msg->sip_msg_content->sip_content_allocated = B_FALSE;
+	sip_msg->sip_msg_content_len =
+	    sip_msg->sip_msg_content->sip_content_end -
+	    sip_msg->sip_msg_content->sip_content_start;
+	return (0);
+}
+
+/*
+ * The send interface to the sip stack. Used by upper layers.
+ */
+int
+sip_sendmsg(sip_conn_object_t obj, sip_msg_t sip_msg, sip_dialog_t dialog,
+    uint32_t flags)
+{
+	sip_xaction_t		*sip_trans = NULL;
+	int			ret = 0;
+	sip_message_type_t	*sip_msg_info;
+	_sip_msg_t 		*_sip_msg;
+	boolean_t		stateful = flags & SIP_SEND_STATEFUL;
+	boolean_t		dlg_on_fork = flags & SIP_DIALOG_ON_FORK;
+
+	sip_refhold_conn(obj);
+
+	_sip_msg = (_sip_msg_t *)sip_msg;
+	if ((ret = sip_adjust_msgbuf(_sip_msg)) != 0) {
+		sip_refrele_conn(obj);
+		return (ret);
+	}
+
+	assert(_sip_msg->sip_msg_req_res != NULL);
+	sip_msg_info = _sip_msg->sip_msg_req_res;
+	/*
+	 * Send it statefully if:
+	 * if stateful is set in 'flags' AND
+	 * this is not an ACK request, if it is a request (should the upper
+	 * layer set stateful in the latter case?, i.e is the check
+	 * necessary here?)
+	 */
+	if (stateful && (!sip_msg_info->is_request ||
+	    sip_msg_info->sip_req_method != ACK)) {
+		sip_trans = (sip_xaction_t *)sip_xaction_get(obj, sip_msg,
+		    B_TRUE, sip_msg_info->is_request ? SIP_CLIENT_TRANSACTION :
+		    SIP_SERVER_TRANSACTION, &ret);
+		if (sip_trans == NULL) {
+			sip_refrele_conn(obj);
+			return (ret);
+		}
+		ret = sip_xaction_output(obj, sip_trans, _sip_msg);
+		SIP_XACTION_REFCNT_DECR(sip_trans);
+		if (ret != 0) {
+			sip_refrele_conn(obj);
+			return (ret);
+		}
+	}
+	/*
+	 * If the appln wants us to create the dialog, create a partial
+	 * dialog at this stage, when we get the response, we will
+	 * complete it.
+	 */
+	if (sip_manage_dialog) {
+		if (sip_msg_info->is_request && dialog == NULL) {
+			dialog = (sip_dialog_t)sip_seed_dialog(obj, sip_msg,
+			    dlg_on_fork, SIP_UAC_DIALOG);
+		} else if (dialog != NULL && (!sip_msg_info->is_request ||
+		    sip_msg_info->sip_req_method == NOTIFY)) {
+			(void) sip_update_dialog(dialog, _sip_msg);
+		}
+	}
+
+	if ((ret = sip_stack_send(obj, _sip_msg->sip_msg_buf,
+	    _sip_msg->sip_msg_len)) != 0) {
+		if (sip_trans != NULL) {
+			sip_xaction_terminate(sip_trans, _sip_msg,
+			    sip_conn_transport(obj));
+		}
+		sip_refrele_conn(obj);
+		return (ret);
+	}
+	sip_refrele_conn(obj);
+	return (ret);
+}
+
+/*
+ * Given a sent-by value check if it is in the registered list. If no values
+ * have been registered, the check passes.
+ */
+static boolean_t
+sip_sent_by_registered(const sip_str_t *sb_val)
+{
+	sent_by_list_t	*sb;
+	int		count = 0;
+
+	(void) pthread_mutex_lock(&sip_sent_by_lock);
+	if (sip_sent_by == NULL) {
+		(void) pthread_mutex_unlock(&sip_sent_by_lock);
+		return (B_TRUE);
+	}
+	sb = sip_sent_by;
+	for (count = 0; count < sip_sent_by_count; count++) {
+		if (strncasecmp(sb->sb_val, sb_val->sip_str_ptr,
+		    sb_val->sip_str_len) == 0) {
+			(void) pthread_mutex_unlock(&sip_sent_by_lock);
+			return (B_TRUE);
+		}
+		sb = sb->sb_next;
+	}
+	(void) pthread_mutex_unlock(&sip_sent_by_lock);
+	return (B_FALSE);
+}
+
+/*
+ * Given a response, check if the sent-by in the VIA header is valid.
+ */
+boolean_t
+sip_valid_sent_by(sip_msg_t sip_msg)
+{
+	sip_header_t		via;
+	sip_header_value_t	value = NULL;
+	int			error;
+	const sip_str_t		*sent_by = NULL;
+
+	via = (sip_header_t)sip_get_header(sip_msg, SIP_VIA,  NULL, &error);
+	if (via == NULL || error != 0)
+		return (B_TRUE);
+	value = (sip_header_value_t)sip_get_header_value(via, &error);
+	if (value == NULL || error != 0)
+		return (B_TRUE);
+	sent_by = sip_get_via_sent_by_host(value, &error);
+	if (sent_by == NULL || error != 0)
+		return (B_TRUE);
+	if (sip_sent_by_registered(sent_by))
+		return (B_TRUE);
+	return (B_FALSE);
+}
+
+
+/*
+ * The receive interface to the transport layer.
+ */
+void
+sip_process_new_packet(sip_conn_object_t conn_object, void *msgstr,
+    size_t msglen)
+{
+	_sip_msg_t		*sip_msg;
+	sip_message_type_t	*sip_msg_info;
+	sip_xaction_t		*sip_trans;
+	sip_dialog_t		dialog = NULL;
+	boolean_t		dialog_created = B_FALSE;
+	int			transport;
+	char			*msgbuf = NULL;
+
+	sip_refhold_conn(conn_object);
+	transport = sip_conn_transport(conn_object);
+	if (transport == IPPROTO_TCP) {
+next_msg:
+		msgstr = (char *)sip_get_tcp_msg(conn_object, (char *)msgstr,
+		    &msglen);
+		if (msgstr == NULL) {
+			sip_refrele_conn(conn_object);
+			return;
+		}
+	} else {
+		msgbuf = (char *)malloc(msglen + 1);
+		if (msgbuf == NULL) {
+			sip_refrele_conn(conn_object);
+			return;
+		}
+		(void) strncpy(msgbuf, msgstr, msglen);
+		msgbuf[msglen] = '\0';
+		msgstr = msgbuf;
+	}
+	sip_msg = (_sip_msg_t *)sip_new_msg();
+	if (sip_msg == NULL) {
+		if (msgbuf != NULL)
+			free(msgbuf);
+		sip_refrele_conn(conn_object);
+		return;
+	}
+	sip_msg->sip_msg_buf = (char *)msgstr;
+	sip_msg->sip_msg_len = msglen;
+	(void) pthread_mutex_lock(&sip_msg->sip_msg_mutex);
+	if (sip_setup_header_pointers(sip_msg) != 0) {
+		(void) pthread_mutex_unlock(&sip_msg->sip_msg_mutex);
+		sip_refrele_conn(conn_object);
+		sip_free_msg((sip_msg_t)sip_msg);
+		return;
+	}
+	if (sip_parse_first_line(sip_msg->sip_msg_start_line,
+	    &sip_msg->sip_msg_req_res)) {
+		(void) pthread_mutex_unlock(&sip_msg->sip_msg_mutex);
+		sip_refrele_conn(conn_object);
+		sip_free_msg((sip_msg_t)sip_msg);
+		return;
+	}
+	sip_msg_info = sip_msg->sip_msg_req_res;
+	(void) pthread_mutex_unlock(&sip_msg->sip_msg_mutex);
+
+	if (sip_check_common_headers(conn_object, sip_msg)) {
+		sip_refrele_conn(conn_object);
+		sip_free_msg((sip_msg_t)sip_msg);
+		return;
+	}
+
+	/*
+	 * Silently discard the response if the top VIA has a sent-by value AND
+	 * the UA has registered sent-by values AND the one in the VIA is
+	 * not part of the registerd sent-by values.
+	 */
+	if (!sip_msg_info->is_request && !sip_valid_sent_by(sip_msg)) {
+		sip_refrele_conn(conn_object);
+		sip_free_msg((sip_msg_t)sip_msg);
+		return;
+
+	}
+	sip_trans = (sip_xaction_t *)sip_xaction_get(conn_object,
+	    (sip_msg_t)sip_msg,
+	    B_FALSE, sip_msg_info->is_request ? SIP_SERVER_TRANSACTION :
+	    SIP_CLIENT_TRANSACTION, NULL);
+	if (sip_trans != NULL) {
+		if (sip_xaction_input(conn_object, sip_trans, &sip_msg) != 0) {
+			SIP_XACTION_REFCNT_DECR(sip_trans);
+			sip_refrele_conn(conn_object);
+			sip_free_msg((sip_msg_t)sip_msg);
+			return;
+		}
+		SIP_XACTION_REFCNT_DECR(sip_trans);
+
+		/*
+		 * msg was retransmission - handled by the transaction
+		 */
+		if (sip_msg == NULL)
+			goto check_next;
+	} else {
+		/*
+		 * If we are getting an INVITE request, let us send a
+		 * 100 TRYING response here, as in 17.2.1:
+		 * "The server transaction MUST generate a 100 (Trying)
+		 * response unless it knows that the TU will generate a
+		 * provisional or final response within 200 ms".
+		 */
+		if (sip_msg_info->is_request &&
+		    sip_msg_info->sip_req_method == INVITE) {
+			sip_send_resp(conn_object, sip_msg, SIP_TRYING);
+		}
+	}
+	if (sip_manage_dialog) {
+		dialog = sip_dialog_find(sip_msg);
+		if (dialog == NULL) {
+			if (sip_msg_info->is_request) {
+				/*
+				 * sip_seed_dialog will check for the
+				 * method in the request
+				 */
+				dialog = (sip_dialog_t)sip_seed_dialog(
+				    conn_object, sip_msg,
+				    B_FALSE, SIP_UAS_DIALOG);
+				dialog_created = B_TRUE;
+			}
+		} else if (sip_incomplete_dialog(dialog)) {
+			if (!sip_msg_info->is_request ||
+			    sip_msg_info->sip_req_method == NOTIFY) {
+				dialog = sip_update_dialog(dialog, sip_msg);
+			}
+		} else if (sip_dialog_process(sip_msg, &dialog) != 0) {
+			if (dialog != NULL)
+				sip_release_dialog(dialog);
+			/*
+			 * cseq number in error, send a
+			 * SIP_SERVER_INTERNAL_ERROR response.
+			 */
+			if (sip_msg_info->is_request) {
+				sip_send_resp(conn_object, sip_msg,
+				    SIP_SERVER_INTERNAL_ERROR);
+			}
+			sip_refrele_conn(conn_object);
+			sip_free_msg((sip_msg_t)sip_msg);
+			return;
+		}
+	}
+	sip_ulp_recv(conn_object, (sip_msg_t)sip_msg, dialog);
+	sip_free_msg((sip_msg_t)sip_msg);
+	if (dialog != NULL && !dialog_created)
+		sip_release_dialog(dialog);
+check_next:
+	/*
+	 * Check if there are more complete messages in the TCP fragment list
+	 * to be consumed
+	 */
+	if (transport == IPPROTO_TCP) {
+		msgstr = NULL;
+		msglen = 0;
+		goto next_msg;
+	}
+	sip_refrele_conn(conn_object);
+}
+
+/*
+ * Initialize the stack. The connection manager functions, upper layer
+ * receive functions are mandatory.
+ */
+int
+sip_stack_init(sip_stack_init_t *stack_val)
+{
+#ifdef	__linux__
+	struct timespec	tspec;
+#endif
+
+	/*
+	 * If the stack has already been configured, return error
+	 */
+	if (sip_stack_send != NULL ||
+	    stack_val->sip_version != SIP_STACK_VERSION) {
+		return (EINVAL);
+	}
+	if (stack_val->sip_io_pointers == NULL ||
+	    stack_val->sip_ulp_pointers == NULL) {
+		return (EINVAL);
+	}
+	sip_ulp_recv = stack_val->sip_ulp_pointers->sip_ulp_recv;
+	sip_manage_dialog = stack_val->sip_stack_flags & SIP_STACK_DIALOGS;
+
+	sip_stack_send = stack_val->sip_io_pointers->sip_conn_send;
+	sip_refhold_conn = stack_val->sip_io_pointers->sip_hold_conn_object;
+	sip_refrele_conn = stack_val->sip_io_pointers->sip_rel_conn_object;
+	sip_is_conn_stream = stack_val->sip_io_pointers->sip_conn_is_stream;
+	sip_is_conn_reliable = stack_val->sip_io_pointers->sip_conn_is_reliable;
+	sip_conn_rem_addr = stack_val->sip_io_pointers->sip_conn_remote_address;
+	sip_conn_local_addr =
+	    stack_val->sip_io_pointers->sip_conn_local_address;
+	sip_conn_transport = stack_val->sip_io_pointers->sip_conn_transport;
+	sip_header_function_table_external = stack_val->sip_function_table;
+
+	if (sip_ulp_recv == NULL || sip_stack_send == NULL ||
+	    sip_refhold_conn == NULL || sip_refrele_conn == NULL ||
+	    sip_is_conn_stream == NULL || sip_is_conn_reliable == NULL ||
+	    sip_conn_rem_addr == NULL || sip_conn_local_addr == NULL ||
+	    sip_conn_transport == NULL) {
+	err_ret:
+		sip_ulp_recv = NULL;
+		sip_stack_send = NULL;
+		sip_refhold_conn = NULL;
+		sip_refrele_conn = NULL;
+		sip_is_conn_stream = NULL;
+		sip_is_conn_reliable = NULL;
+		sip_conn_rem_addr = NULL;
+		sip_conn_local_addr = NULL;
+		sip_conn_transport = NULL;
+		sip_header_function_table_external = NULL;
+		sip_stack_timeout = NULL;
+		sip_stack_untimeout = NULL;
+		return (EINVAL);
+	}
+
+	sip_conn_timer1 = stack_val->sip_io_pointers->sip_conn_timer1;
+	sip_conn_timer2 = stack_val->sip_io_pointers->sip_conn_timer2;
+	sip_conn_timer4 = stack_val->sip_io_pointers->sip_conn_timer4;
+	sip_conn_timerd = stack_val->sip_io_pointers->sip_conn_timerd;
+
+	/*
+	 * Use Appln timeout routines, if provided
+	 */
+	if (stack_val->sip_ulp_pointers->sip_ulp_timeout != NULL) {
+		if (stack_val->sip_ulp_pointers->sip_ulp_untimeout == NULL)
+			goto err_ret;
+		sip_stack_timeout =
+		    stack_val->sip_ulp_pointers->sip_ulp_timeout;
+		sip_stack_untimeout =
+		    stack_val->sip_ulp_pointers->sip_ulp_untimeout;
+	} else {
+		if (stack_val->sip_ulp_pointers->sip_ulp_untimeout != NULL)
+			goto err_ret;
+		sip_timeout_init();
+		sip_stack_timeout = sip_timeout;
+		sip_stack_untimeout = sip_untimeout;
+	}
+
+	/*
+	 * Manage Dialogs?
+	 */
+	if (sip_manage_dialog) {
+		sip_dialog_init(stack_val->sip_ulp_pointers->sip_ulp_dlg_del,
+		    stack_val->sip_ulp_pointers->sip_ulp_dlg_state_cb);
+	}
+	sip_xaction_init(stack_val->sip_ulp_pointers->sip_ulp_trans_error,
+	    stack_val->sip_ulp_pointers->sip_ulp_trans_state_cb);
+
+#ifdef	__linux__
+	if (clock_gettime(CLOCK_REALTIME, &tspec) != 0)
+		goto err_ret;
+	sip_hash_salt = tspec.tv_nsec;
+#else
+	sip_hash_salt = gethrtime();
+#endif
+	(void) pthread_mutex_init(&sip_sent_by_lock, NULL);
+	return (0);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/lib/libsip/common/sip_miscdefs.h	Sat Oct 07 14:26:26 2006 -0700
@@ -0,0 +1,164 @@
+/*
+ * 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.
+ */
+
+#ifndef	_SIP_MISCDEFS_H
+#define	_SIP_MISCDEFS_H
+
+#pragma ident	"%Z%%M%	%I%	%E% SMI"
+
+#ifdef	__cplusplus
+extern "C" {
+#endif
+
+#include <sys/types.h>
+#include <sip.h>
+
+#define	SIP_CR			'\r'
+#define	SIP_SP			' '
+#define	SIP_HCOLON		':'
+#define	SIP_SEMI		';'
+#define	SIP_COMMA		','
+#define	SIP_LAQUOT		'<'
+#define	SIP_RAQUOT		'>'
+#define	SIP_QUOTE		'"'
+#define	SIP_EQUAL		'='
+#define	SIP_SLASH		'/'
+#define	SIP_PERIOD		'.'
+#define	SIP_LPAR		'('
+#define	SIP_RPAR		')'
+
+#define	SIP_BRANCHID_LEN	28	/* incl. the magic cookie */
+#define	SIP_TAG_LEN		20
+#define	SIP_URI_LEN		25
+#define	SIP_DISPLAY_LEN		25
+#define	SIP_DOMAIN_LEN		25
+#define	SIP_MAX_FWDLEN		5
+#define	SIP_TRANSPORT_LEN	5
+#define	SIP_SIZE_OF_STATUS_CODE	3
+#define	SIP_SPACE_LEN		sizeof (char)
+
+#define	SIP_MS			1L
+#define	SIP_SECONDS		(1000 * SIP_MS)
+#define	SIP_MINUTES		(60 * SIP_SECONDS)
+#define	SIP_HOURS   		(60 * SIP_MINUTES)
+
+/* timer granularity is in msecs */
+#define	SIP_TIMER_T1		(1 * SIP_SECONDS)
+#define	SIP_TIMER_T2		(4 * SIP_SECONDS)
+#define	SIP_TIMER_T4		(5 * SIP_SECONDS)
+
+#ifdef		__linux__
+#define		SEC		1
+#define		MILLISEC	1000
+#define		MICROSEC	1000000
+#define		NANOSEC		1000000000
+
+typedef struct timespec	timestruc_t;
+typedef	long long	hrtime_t;
+#endif
+
+extern int	sip_timer_T1;
+extern int	sip_timer_T2;
+extern int	sip_timer_T4;
+extern int	sip_timer_TD;
+
+/* Structure for SIP timers */
+typedef struct sip_timer_s {
+	uint_t		sip_timerid;
+	struct timeval	sip_timeout_val;
+}sip_timer_t;
+
+/* time is in msec */
+#define	SIP_SET_TIMEOUT(timer, time) {					\
+	int	mtime = (time);						\
+									\
+	(timer).sip_timeout_val.tv_sec = mtime / MILLISEC;	\
+	mtime -= (timer).sip_timeout_val.tv_sec * MILLISEC;	\
+	(timer).sip_timeout_val.tv_usec = mtime * MILLISEC;		\
+}
+
+/* time is in msec */
+#define	SIP_INIT_TIMER(timer, time) {				\
+	SIP_SET_TIMEOUT(timer, time);				\
+	(timer).sip_timerid = 0;				\
+}
+
+#define	SIP_SCHED_TIMER(timer, obj, func) {			\
+	(timer).sip_timerid = sip_stack_timeout((void *)(obj),	\
+	    (func), &((timer).sip_timeout_val));			\
+}
+
+#define	SIP_CANCEL_TIMER(timer) {				\
+	if ((timer).sip_timerid != 0) {				\
+		sip_stack_untimeout((timer).sip_timerid);	\
+		(timer).sip_timerid = 0;			\
+	}							\
+}
+
+/* returned time is in msec */
+#define	SIP_GET_TIMEOUT(timer)					\
+	((timer).sip_timeout_val.tv_sec * MILLISEC +		\
+	(timer).sip_timeout_val.tv_usec / MILLISEC)
+
+#define	SIP_IS_TIMER_RUNNING(timer)	((timer).sip_timerid != 0)
+
+/* This is the transaction list */
+typedef struct sip_conn_cache_s {
+	void			*obj;
+	struct sip_conn_cache_s	*next;
+	struct sip_conn_cache_s	*prev;
+} sip_conn_cache_t;
+
+/* TCP fragment entry */
+typedef struct sip_reass_entry_s {
+	char		*sip_reass_msg;
+	int		sip_reass_msglen;
+}sip_reass_entry_t;
+
+/* Library data in stored in connection object */
+typedef struct sip_conn_obj_pvt_s {
+	sip_reass_entry_t	*sip_conn_obj_reass;
+	pthread_mutex_t		sip_conn_obj_reass_lock;
+	sip_conn_cache_t	*sip_conn_obj_cache;
+	pthread_mutex_t		sip_conn_obj_cache_lock;
+} sip_conn_obj_pvt_t;
+
+extern boolean_t sip_manage_dialog;
+
+/* To salt the hash function */
+extern uint64_t	sip_hash_salt;
+
+extern void		sip_timeout_init();
+extern uint_t		sip_timeout(void *, void (*)(void *), struct timeval *);
+extern boolean_t	sip_untimeout(uint_t);
+extern void		sip_md5_hash(char *, int, char *, int, char *, int,
+			    char *, int, char *, int, char *, int, uchar_t *);
+
+#ifdef	__cplusplus
+}
+#endif
+
+#endif	/* _SIP_MISCDEFS_H */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/lib/libsip/common/sip_msg.c	Sat Oct 07 14:26:26 2006 -0700
@@ -0,0 +1,931 @@
+/*
+ * 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"
+
+/*
+ * Response consists of SIP version, response code, response phrase and CRLF.
+ */
+#define	SIP_RESPONSE	"%s %d %s%s"
+
+void sip_free_content(_sip_msg_t *);
+
+/*
+ * Allocate a new sip msg struct.
+ */
+sip_msg_t
+sip_new_msg()
+{
+	_sip_msg_t *sip_msg;
+
+	sip_msg = calloc(1, sizeof (_sip_msg_t));
+	if (sip_msg != NULL) {
+		sip_msg->sip_msg_ref_cnt = 1;
+		(void) pthread_mutex_init(&sip_msg->sip_msg_mutex, NULL);
+	}
+	return ((sip_msg_t)sip_msg);
+}
+
+/*
+ * Free all resources. The lock is taken by SIP_MSG_REFCNT_DECR. The
+ * thread that decrements the last refcount should take care that
+ * the message is not accessible to other threads before doing so.
+ * Else, if the message is still accessible to others, it is
+ * possible that the other thread could be waiting to take the
+ * lock when we proceed to destroy it.
+ */
+void
+sip_destroy_msg(_sip_msg_t *_sip_msg)
+{
+#ifdef	__solaris__
+	assert(mutex_held(&_sip_msg->sip_msg_mutex));
+#endif
+	(void) sip_delete_start_line_locked(_sip_msg);
+	assert(_sip_msg->sip_msg_ref_cnt == 0);
+	sip_delete_all_headers((sip_msg_t)_sip_msg);
+	sip_free_content(_sip_msg);
+	if (_sip_msg->sip_msg_buf != NULL)
+		free(_sip_msg->sip_msg_buf);
+
+	if (_sip_msg->sip_msg_old_buf != NULL)
+		free(_sip_msg->sip_msg_old_buf);
+
+	while (_sip_msg->sip_msg_req_res != NULL) {
+		sip_message_type_t	*sip_msg_type_ptr;
+
+		sip_msg_type_ptr = _sip_msg->sip_msg_req_res->sip_next;
+		if (_sip_msg->sip_msg_req_res->is_request) {
+			sip_request_t	*reqline;
+
+			reqline = &_sip_msg->sip_msg_req_res->U.sip_request;
+			if (reqline->sip_parse_uri != NULL) {
+				sip_free_parsed_uri(reqline->sip_parse_uri);
+				reqline->sip_parse_uri = NULL;
+			}
+		}
+		free(_sip_msg->sip_msg_req_res);
+		_sip_msg->sip_msg_req_res = sip_msg_type_ptr;
+	}
+	(void) pthread_mutex_destroy(&_sip_msg->sip_msg_mutex);
+	free(_sip_msg);
+}
+
+/*
+ * Free a sip msg struct.
+ */
+void
+sip_free_msg(sip_msg_t sip_msg)
+{
+	if (sip_msg == NULL)
+		return;
+
+	SIP_MSG_REFCNT_DECR((_sip_msg_t *)sip_msg);
+}
+
+/*
+ * Hold a sip msg struct.
+ */
+void
+sip_hold_msg(sip_msg_t sip_msg)
+{
+
+	if (sip_msg == NULL)
+		return;
+
+	SIP_MSG_REFCNT_INCR((_sip_msg_t *)sip_msg);
+}
+
+/*
+ * Clone a message
+ */
+sip_msg_t
+sip_clone_msg(sip_msg_t sip_msg)
+{
+	_sip_msg_t	*new_msg;
+	_sip_msg_t	*_sip_msg;
+	sip_content_t	*sip_content;
+	sip_content_t	*msg_content;
+	sip_content_t	*new_content = NULL;
+	int		len;
+
+	if (sip_msg == NULL)
+		return (NULL);
+	new_msg = (_sip_msg_t *)sip_new_msg();
+	if (new_msg == NULL)
+		return (NULL);
+	_sip_msg = (_sip_msg_t *)sip_msg;
+	/*
+	 * Get start line
+	 */
+	if (sip_copy_start_line(_sip_msg, new_msg) != 0) {
+		sip_free_msg((sip_msg_t)new_msg);
+		return (NULL);
+	}
+	if (sip_copy_all_headers(_sip_msg, new_msg) != 0) {
+		sip_free_msg((sip_msg_t)new_msg);
+		return (NULL);
+	}
+	(void) pthread_mutex_lock(&_sip_msg->sip_msg_mutex);
+	sip_content = _sip_msg->sip_msg_content;
+	while (sip_content != NULL) {
+		msg_content = calloc(1, sizeof (sip_content_t));
+		if (msg_content == NULL) {
+			sip_free_msg((sip_msg_t)new_msg);
+			(void) pthread_mutex_unlock(&_sip_msg->sip_msg_mutex);
+			return (NULL);
+		}
+		len = sip_content->sip_content_end -
+		    sip_content->sip_content_start;
+		msg_content->sip_content_start = malloc(len + 1);
+		if (msg_content->sip_content_start == NULL) {
+			sip_free_msg((sip_msg_t)new_msg);
+			(void) pthread_mutex_unlock(&_sip_msg->sip_msg_mutex);
+			return (NULL);
+		}
+		(void) strncpy(msg_content->sip_content_start,
+		    sip_content->sip_content_start, len);
+		msg_content->sip_content_start[len] = '\0';
+		msg_content->sip_content_current =
+		    msg_content->sip_content_start;
+		msg_content->sip_content_end =  msg_content->sip_content_start +
+		    len;
+		msg_content->sip_content_allocated = B_TRUE;
+		new_msg->sip_msg_content_len += len;
+		new_msg->sip_msg_len += len;
+		if (new_msg->sip_msg_content == NULL)
+			new_msg->sip_msg_content = msg_content;
+		else
+			new_content->sip_content_next = msg_content;
+		new_content = msg_content;
+		sip_content = sip_content->sip_content_next;
+	}
+	(void) pthread_mutex_unlock(&_sip_msg->sip_msg_mutex);
+	/*
+	 * Since this is a new message, no threads should be referring
+	 * to this, so it is not necessary to take the lock, however,
+	 * since sip_msg_to_msgbuf() expects the lock to be held, we'll
+	 * take it here.
+	 */
+	(void) pthread_mutex_lock(&new_msg->sip_msg_mutex);
+	new_msg->sip_msg_buf = sip_msg_to_msgbuf((sip_msg_t)new_msg, NULL);
+	if (new_msg->sip_msg_buf == NULL) {
+		(void) pthread_mutex_unlock(&new_msg->sip_msg_mutex);
+		sip_free_msg((sip_msg_t)new_msg);
+		return (NULL);
+	}
+	new_msg->sip_msg_cannot_be_modified = B_TRUE;
+	(void) pthread_mutex_unlock(&new_msg->sip_msg_mutex);
+
+	return ((sip_msg_t)new_msg);
+}
+
+/*
+ * Return the SIP message as a string. Caller frees the string
+ */
+char *
+sip_msg_to_str(sip_msg_t sip_msg, int *error)
+{
+	_sip_msg_t	*msg;
+	char		*msgstr;
+
+	if (sip_msg == NULL) {
+		if (error != NULL)
+			*error = EINVAL;
+		return (NULL);
+	}
+	msg = (_sip_msg_t *)sip_msg;
+	(void) pthread_mutex_lock(&msg->sip_msg_mutex);
+	msgstr = sip_msg_to_msgbuf(msg, error);
+	(void) pthread_mutex_unlock(&msg->sip_msg_mutex);
+	return (msgstr);
+}
+
+/*
+ * Given a message generate a string that includes all the headers and the
+ * content.
+ */
+char *
+sip_msg_to_msgbuf(_sip_msg_t *msg, int *error)
+{
+	_sip_header_t	*header;
+	int		len = 0;
+	char		*p;
+	char		*e;
+	sip_content_t	*sip_content;
+#ifdef	_DEBUG
+	int		tlen = 0;
+	int		clen = 0;
+#endif
+
+	if (error != NULL)
+		*error = 0;
+
+	if (msg == NULL) {
+		if (error != NULL)
+			*error = EINVAL;
+		return (NULL);
+	}
+#ifdef	__solaris__
+	assert(mutex_held(&msg->sip_msg_mutex));
+#endif
+
+	p = (char *)malloc(msg->sip_msg_len + 1);
+	if (p == NULL) {
+		if (error != 0)
+			*error = ENOMEM;
+		return (NULL);
+	}
+	e = p;
+
+	/*
+	 * Get the start line
+	 */
+	if (msg->sip_msg_start_line != NULL) {
+		len = msg->sip_msg_start_line->sip_hdr_end -
+		    msg->sip_msg_start_line->sip_hdr_start;
+		(void) strncpy(e, msg->sip_msg_start_line->sip_hdr_start, len);
+		e += len;
+#ifdef	_DEBUG
+		tlen += len;
+#endif
+	}
+	header = sip_search_for_header(msg, NULL, NULL);
+	while (header != NULL) {
+		if (header->sip_header_state != SIP_HEADER_DELETED) {
+			if (header->sip_header_state ==
+			    SIP_HEADER_DELETED_VAL) {
+				len = sip_copy_values(e, header);
+			} else {
+				len = header->sip_hdr_end -
+				    header->sip_hdr_start;
+				(void) strncpy(e, header->sip_hdr_start, len);
+			}
+#ifdef	_DEBUG
+			tlen += len;
+			assert(tlen <= msg->sip_msg_len);
+#endif
+		}
+		header = sip_search_for_header(msg, NULL, header);
+		e += len;
+	}
+	sip_content = msg->sip_msg_content;
+	while (sip_content != NULL) {
+		len = sip_content->sip_content_end -
+		    sip_content->sip_content_start;
+#ifdef	_DEBUG
+		clen += len;
+		assert(clen <= msg->sip_msg_content_len);
+		tlen += len;
+		assert(tlen <= msg->sip_msg_len);
+#endif
+		(void) strncpy(e, sip_content->sip_content_start, len);
+		e += len;
+		sip_content = sip_content->sip_content_next;
+	}
+	p[msg->sip_msg_len] = '\0';
+	return (p);
+}
+
+/*
+ * This is called just before sending the message to the transport. It
+ * creates the sip_msg_buf from the SIP headers.
+ */
+int
+sip_adjust_msgbuf(_sip_msg_t *msg)
+{
+	_sip_header_t	*header;
+	int		ret;
+#ifdef	_DEBUG
+	int		tlen = 0;
+	int		clen = 0;
+#endif
+
+	if (msg == NULL)
+		return (EINVAL);
+
+	(void) pthread_mutex_lock(&msg->sip_msg_mutex);
+	if ((msg->sip_msg_buf != NULL) && (!msg->sip_msg_modified)) {
+		/*
+		 * We could just be forwarding the message we
+		 * received.
+		 */
+		(void) pthread_mutex_unlock(&msg->sip_msg_mutex);
+		return (0);
+	}
+
+	/*
+	 * We are sending a new message or a message that we received
+	 * but have modified it. We keep the old
+	 * msgbuf till the message is freed as some
+	 * headers still point to it.
+	 */
+
+	assert(msg->sip_msg_old_buf == NULL);
+	msg->sip_msg_old_buf = msg->sip_msg_buf;
+	/*
+	 * We add the content-length header here, if it has not
+	 * already been added.
+	 */
+	header = sip_search_for_header(msg, SIP_CONTENT_LENGTH, NULL);
+	if (header != NULL) {
+		/*
+		 * Mark the previous header as deleted.
+		 */
+		header->sip_header_state = SIP_HEADER_DELETED;
+		header->sip_hdr_sipmsg->sip_msg_len -= header->sip_hdr_end -
+		    header->sip_hdr_start;
+	}
+	(void) pthread_mutex_unlock(&msg->sip_msg_mutex);
+	ret = sip_add_content_length(msg, msg->sip_msg_content_len);
+	if (ret != 0) {
+		(void) pthread_mutex_unlock(&msg->sip_msg_mutex);
+		return (ret);
+	}
+	(void) pthread_mutex_lock(&msg->sip_msg_mutex);
+	msg->sip_msg_modified = B_FALSE;
+
+	msg->sip_msg_buf = sip_msg_to_msgbuf((sip_msg_t)msg, &ret);
+	if (msg->sip_msg_buf == NULL) {
+		(void) pthread_mutex_unlock(&msg->sip_msg_mutex);
+		return (ret);
+	}
+	/*
+	 * Once the message has been sent it can not be modified
+	 * any furthur as we keep a pointer to it for retransmission
+	 */
+	msg->sip_msg_cannot_be_modified = B_TRUE;
+
+	(void) pthread_mutex_unlock(&msg->sip_msg_mutex);
+	return (0);
+}
+
+/*
+ * Copy header values into ptr
+ */
+int
+sip_copy_values(char *ptr, _sip_header_t *header)
+{
+	sip_header_value_t	value;
+	int			tlen = 0;
+	int			len = 0;
+	boolean_t		first = B_TRUE;
+	char			*p = ptr;
+	char			*s;
+	boolean_t		crlf_present = B_FALSE;
+
+	if (sip_parse_goto_values(header) != 0)
+		return (0);
+
+	len = header->sip_hdr_current - header->sip_hdr_start;
+	(void) strncpy(p, header->sip_hdr_start, len);
+	tlen += len;
+	p += len;
+	value = header->sip_hdr_parsed->value;
+	while (value != NULL) {
+		if (value->value_state != SIP_VALUE_DELETED) {
+			crlf_present = B_FALSE;
+			len = value->value_end - value->value_start;
+			if (first) {
+				(void) strncpy(p, value->value_start, len);
+				first = B_FALSE;
+			} else {
+				s = value->value_start;
+				while (*s != SIP_COMMA)
+					s--;
+				len += value->value_start - s;
+				(void) strncpy(p, s, len);
+			}
+			tlen += len;
+			p += len;
+			s = value->value_end;
+			while (s != value->value_start) {
+				if (*s == '\r' && strncmp(s, SIP_CRLF,
+				    strlen(SIP_CRLF)) == 0) {
+					crlf_present = B_TRUE;
+					break;
+				}
+				s--;
+			}
+		} else {
+			if (value->next == NULL && !first && !crlf_present) {
+				s = value->value_end;
+				while (*s != '\r')
+					s--;
+				len = value->value_end - s;
+				(void) strncpy(p, s, len);
+				tlen += len;
+				p += len;
+			}
+		}
+		value = value->next;
+	}
+	return (tlen);
+}
+
+
+/*
+ * Add content (message body) to sip_msg
+ */
+int
+sip_add_content(sip_msg_t sip_msg, char *content)
+{
+	size_t		len;
+	sip_content_t	**loc;
+	sip_content_t	*msg_content;
+	_sip_msg_t	*_sip_msg;
+
+	if (sip_msg == NULL || content == NULL || strlen(content) == 0)
+		return (EINVAL);
+	len = strlen(content);
+	_sip_msg = (_sip_msg_t *)sip_msg;
+	(void) pthread_mutex_lock(&_sip_msg->sip_msg_mutex);
+
+	if (_sip_msg->sip_msg_cannot_be_modified) {
+		(void) pthread_mutex_unlock(&_sip_msg->sip_msg_mutex);
+		return (ENOTSUP);
+	}
+
+	msg_content = calloc(1, sizeof (sip_content_t));
+	if (msg_content == NULL) {
+		(void) pthread_mutex_unlock(&_sip_msg->sip_msg_mutex);
+		return (ENOMEM);
+	}
+	msg_content->sip_content_start = malloc(strlen(content) + 1);
+	if (msg_content->sip_content_start == NULL) {
+		(void) pthread_mutex_unlock(&_sip_msg->sip_msg_mutex);
+		free(msg_content);
+		return (ENOMEM);
+	}
+	(void) strncpy(msg_content->sip_content_start, content,
+	    strlen(content));
+	msg_content->sip_content_start[strlen(content)] = '\0';
+	msg_content->sip_content_current = msg_content->sip_content_start;
+	msg_content->sip_content_end = msg_content->sip_content_start +
+	    strlen(msg_content->sip_content_start);
+	msg_content->sip_content_allocated = B_TRUE;
+
+	loc = &_sip_msg->sip_msg_content;
+	while (*loc != NULL)
+		loc = &((*loc)->sip_content_next);
+	*loc = msg_content;
+
+	_sip_msg->sip_msg_content_len += len;
+	_sip_msg->sip_msg_len += len;
+	if (_sip_msg->sip_msg_buf != NULL)
+		_sip_msg->sip_msg_modified = B_TRUE;
+	(void) pthread_mutex_unlock(&_sip_msg->sip_msg_mutex);
+	return (0);
+}
+
+/*
+ * Free the message content
+ */
+void
+sip_free_content(_sip_msg_t *sip_msg)
+{
+	sip_content_t *content;
+
+	if (sip_msg == NULL)
+		return;
+	content = sip_msg->sip_msg_content;
+	while (content != NULL) {
+		sip_content_t *content_tmp;
+
+		content_tmp = content;
+		content = content->sip_content_next;
+		if (content_tmp->sip_content_allocated)
+			free(content_tmp->sip_content_start);
+		free(content_tmp);
+	}
+	sip_msg->sip_msg_content = NULL;
+}
+
+
+/*
+ * Add a response line to sip_response
+ */
+int
+sip_add_response_line(sip_msg_t sip_response, int response, char *response_code)
+{
+	_sip_header_t	*new_header;
+	int		header_size;
+	_sip_msg_t	*_sip_response;
+	int		ret;
+
+	if (sip_response == NULL || response < 0 || response_code == NULL)
+		return (EINVAL);
+	_sip_response = (_sip_msg_t *)sip_response;
+	(void) pthread_mutex_lock(&_sip_response->sip_msg_mutex);
+	if (_sip_response->sip_msg_cannot_be_modified) {
+		(void) pthread_mutex_unlock(&_sip_response->sip_msg_mutex);
+		return (ENOTSUP);
+	}
+	header_size = strlen(SIP_VERSION) + SIP_SPACE_LEN +
+	    SIP_SIZE_OF_STATUS_CODE + SIP_SPACE_LEN + strlen(response_code) +
+	    strlen(SIP_CRLF);
+
+	new_header = sip_new_header(header_size);
+	if (new_header == NULL) {
+		(void) pthread_mutex_unlock(&_sip_response->sip_msg_mutex);
+		return (ENOMEM);
+	}
+	new_header->sip_hdr_sipmsg = _sip_response;
+
+	(void) snprintf(new_header->sip_hdr_start, header_size + 1,
+	    SIP_RESPONSE, SIP_VERSION, response, response_code, SIP_CRLF);
+
+	new_header->sip_hdr_next = _sip_response->sip_msg_start_line;
+	_sip_response->sip_msg_start_line = new_header;
+	_sip_response->sip_msg_len += header_size;
+	ret = sip_parse_first_line(_sip_response->sip_msg_start_line,
+	    &_sip_response->sip_msg_req_res);
+	if (_sip_response->sip_msg_buf != NULL)
+		_sip_response->sip_msg_modified = B_TRUE;
+	(void) pthread_mutex_unlock(&_sip_response->sip_msg_mutex);
+	return (ret);
+}
+
+/*
+ * create a response based on the sip_request.
+ * Copies Call-ID, CSeq, From, To and Via headers from the request.
+ */
+sip_msg_t
+sip_create_response(sip_msg_t sip_request, int response, char *response_code,
+    char *totag, char *mycontact)
+{
+	_sip_msg_t	*new_msg;
+	_sip_msg_t	*_sip_request;
+	boolean_t	ttag_present;
+
+	if (sip_request == NULL || response_code == NULL)
+		return (NULL);
+
+	ttag_present =  sip_get_to_tag(sip_request, NULL) != NULL;
+
+	new_msg = (_sip_msg_t *)sip_new_msg();
+	if (new_msg == NULL)
+		return (NULL);
+	_sip_request = (_sip_msg_t *)sip_request;
+
+	(void) pthread_mutex_lock(&_sip_request->sip_msg_mutex);
+
+	/*
+	 * Add response line.
+	 */
+	if (sip_add_response_line(new_msg, response, response_code) != 0)
+		goto error;
+
+	/*
+	 * Copy Via headers
+	 */
+	if (_sip_find_and_copy_all_header(_sip_request, new_msg, SIP_VIA) != 0)
+		goto error;
+
+	/*
+	 * Copy From header.
+	 */
+	if (_sip_find_and_copy_header(_sip_request, new_msg, SIP_FROM,
+	    NULL, B_FALSE)) {
+		goto error;
+	}
+	/*
+	 * Copy To header. If To tag is present, copy it, if not then
+	 * add one if the repsonse is not provisional.
+	 */
+	if (ttag_present || (totag == NULL && response == SIP_TRYING)) {
+		if (_sip_find_and_copy_header(_sip_request, new_msg, SIP_TO,
+		    NULL, B_FALSE)) {
+			goto error;
+		}
+	} else {
+		char		*xtra_param;
+		boolean_t	tag_alloc = B_FALSE;
+		int		taglen;
+
+		if (totag == NULL) {
+			totag = sip_guid();
+			if (totag == NULL)
+				goto error;
+			tag_alloc = B_TRUE;
+		}
+		taglen = strlen(SIP_TAG) + strlen(totag) + 1;
+		xtra_param = (char *)malloc(taglen);
+		if (xtra_param == NULL) {
+			if (tag_alloc)
+				free(totag);
+			goto error;
+		}
+		(void) snprintf(xtra_param, taglen, "%s%s", SIP_TAG, totag);
+		if (tag_alloc)
+			free(totag);
+		if (_sip_find_and_copy_header(_sip_request, new_msg,
+		    SIP_TO, xtra_param, B_FALSE)) {
+			free(xtra_param);
+			goto error;
+		}
+		free(xtra_param);
+	}
+
+	/*
+	 * Copy Call-ID header.
+	 */
+	if (_sip_find_and_copy_header(_sip_request, new_msg, SIP_CALL_ID, NULL,
+	    B_FALSE)) {
+		goto error;
+	}
+	/*
+	 * Copy CSEQ header
+	 */
+	if (_sip_find_and_copy_header(_sip_request, new_msg, SIP_CSEQ, NULL,
+	    B_FALSE)) {
+		goto error;
+	}
+	/*
+	 * Copy RECORD-ROUTE header, if present.
+	 */
+	if (sip_search_for_header(_sip_request, SIP_RECORD_ROUTE, NULL) !=
+	    NULL) {
+		if (_sip_find_and_copy_all_header(_sip_request, new_msg,
+		    SIP_RECORD_ROUTE) != 0) {
+			goto error;
+		}
+	}
+	if (mycontact != NULL) {
+		if (sip_add_contact(new_msg, NULL, mycontact, B_FALSE,
+		    NULL) != 0) {
+			goto error;
+		}
+	}
+	(void) pthread_mutex_unlock(&_sip_request->sip_msg_mutex);
+	return ((sip_msg_t)new_msg);
+error:
+	sip_free_msg((sip_msg_t)new_msg);
+	(void) pthread_mutex_unlock(&_sip_request->sip_msg_mutex);
+	return (NULL);
+}
+
+/*
+ * NON OK ACK : MUST contain values for the Call-ID, From, and Request-URI
+ * that are equal to the values of those header fields in the orig request
+ * passed to the transport. The To header field in the ACK MUST equal the To
+ * header field in the response being acknowledged. The ACK MUST contain the
+ * top Via header field of the original request.  The CSeq header field in
+ * the ACK MUST contain the same value for the sequence number as was
+ * present in the original request, but the method parameter MUST be equal
+ * to "ACK".
+ */
+int
+sip_create_nonOKack(sip_msg_t request, sip_msg_t response, sip_msg_t ack_msg)
+{
+	int		seqno;
+	char		*uri;
+	_sip_msg_t	*_request;
+	_sip_msg_t	*_response;
+	_sip_msg_t	*_ack_msg;
+	int		ret;
+
+	if (request == NULL || response == NULL || ack_msg == NULL ||
+	    request == ack_msg) {
+		return (EINVAL);
+	}
+	_request = (_sip_msg_t *)request;
+	_response = (_sip_msg_t *)response;
+	_ack_msg = (_sip_msg_t *)ack_msg;
+
+	(void) pthread_mutex_lock(&_request->sip_msg_mutex);
+	if (_request->sip_msg_req_res == NULL) {
+		if ((ret = sip_parse_first_line(_request->sip_msg_start_line,
+		    &_request->sip_msg_req_res)) != 0) {
+			(void) pthread_mutex_unlock(&_request->sip_msg_mutex);
+			return (ret);
+		}
+	}
+	if (_request->sip_msg_req_res->U.sip_request.sip_request_uri.
+	    sip_str_ptr == NULL) {
+		(void) pthread_mutex_unlock(&_request->sip_msg_mutex);
+		return (EINVAL);
+	}
+	uri = (char *)malloc(_request->sip_msg_req_res->U.sip_request.
+	    sip_request_uri.sip_str_len + 1);
+	if (uri == NULL) {
+		(void) pthread_mutex_unlock(&_request->sip_msg_mutex);
+		return (EINVAL);
+	}
+	(void) strncpy(uri,
+	    _request->sip_msg_req_res->U.sip_request.sip_request_uri.
+	    sip_str_ptr, _request->sip_msg_req_res->U.sip_request.
+	    sip_request_uri.sip_str_len);
+	uri[_request->sip_msg_req_res->U.sip_request.
+	    sip_request_uri.sip_str_len] = '\0';
+	if ((ret = sip_add_request_line(_ack_msg, ACK, uri)) != 0) {
+		(void) pthread_mutex_unlock(&_request->sip_msg_mutex);
+		return (ret);
+	}
+	free(uri);
+	if ((ret = _sip_find_and_copy_header(_request, _ack_msg, SIP_VIA,
+	    NULL, B_TRUE)) != 0) {
+		(void) pthread_mutex_unlock(&_request->sip_msg_mutex);
+		return (ret);
+	}
+	(void) _sip_find_and_copy_header(_request, _ack_msg,
+	    SIP_MAX_FORWARDS, NULL, B_TRUE);
+
+	(void) pthread_mutex_lock(&_response->sip_msg_mutex);
+	if ((ret = _sip_find_and_copy_header(_response, _ack_msg, SIP_TO,
+	    NULL, B_TRUE)) != 0) {
+		(void) pthread_mutex_unlock(&_response->sip_msg_mutex);
+		return (ret);
+	}
+	(void) pthread_mutex_unlock(&_response->sip_msg_mutex);
+	if ((ret = _sip_find_and_copy_header(_request, _ack_msg, SIP_FROM,
+	    NULL, B_TRUE)) != 0) {
+		(void) pthread_mutex_unlock(&_request->sip_msg_mutex);
+		return (ret);
+	}
+	if ((ret = _sip_find_and_copy_header(_request, _ack_msg, SIP_CALL_ID,
+	    NULL, B_TRUE)) != 0) {
+		(void) pthread_mutex_unlock(&_request->sip_msg_mutex);
+		return (ret);
+	}
+	(void) pthread_mutex_unlock(&_request->sip_msg_mutex);
+	seqno = sip_get_callseq_num(_request, &ret);
+	if (ret != 0)
+		return (ret);
+	if ((ret = sip_add_cseq(_ack_msg, ACK, seqno)) != 0)
+		return (ret);
+	if ((ret = sip_adjust_msgbuf(_ack_msg)) != 0)
+		return (ret);
+	return (0);
+}
+
+/*
+ * This is a 2XX ACK, for others ACK is constructed differently,
+ * esp. the branch id is retained.
+ */
+int
+sip_create_OKack(sip_msg_t response, sip_msg_t ack_msg, char *transport,
+    char *sent_by, int sent_by_port, char *via_params)
+{
+	int			seqno;
+	char			*uri;
+	sip_parsed_header_t	*parsed_header;
+	sip_hdr_value_t		*contact_value;
+	_sip_header_t		*header;
+	_sip_msg_t		*_response;
+	_sip_msg_t		*_ack_msg;
+	int			ret;
+
+	if (response == NULL || response == NULL || transport == NULL)
+		return (EINVAL);
+	_response = (_sip_msg_t *)response;
+	_ack_msg = (_sip_msg_t *)ack_msg;
+
+	/*
+	 * Get URI from the response, Contact field
+	 */
+	(void) pthread_mutex_lock(&_response->sip_msg_mutex);
+	if ((header = sip_search_for_header(_response, SIP_CONTACT,
+	    NULL)) == NULL) {
+		(void) pthread_mutex_unlock(&_response->sip_msg_mutex);
+		return (EINVAL);
+	}
+	if ((ret = sip_parse_cftr_header(header, (void *)&parsed_header)) !=
+	    0) {
+		(void) pthread_mutex_unlock(&_response->sip_msg_mutex);
+		return (ret);
+	}
+	contact_value = (sip_hdr_value_t *)parsed_header->value;
+	if (contact_value->cftr_uri.sip_str_ptr == NULL) {
+		(void) pthread_mutex_unlock(&_response->sip_msg_mutex);
+		return (EINVAL);
+	}
+	uri = (char *)malloc(contact_value->cftr_uri.sip_str_len + 1);
+	if (uri == NULL) {
+		(void) pthread_mutex_unlock(&_response->sip_msg_mutex);
+		return (ENOMEM);
+	}
+	(void) strncpy(uri, contact_value->cftr_uri.sip_str_ptr,
+	    contact_value->cftr_uri.sip_str_len);
+	uri[contact_value->cftr_uri.sip_str_len] = '\0';
+	if ((ret = sip_add_request_line(_ack_msg, ACK, uri)) != 0) {
+		(void) pthread_mutex_unlock(&_response->sip_msg_mutex);
+		return (ret);
+	}
+	free(uri);
+	if ((ret = sip_add_via(_ack_msg, transport, sent_by, sent_by_port,
+	    via_params)) != 0) {
+		(void) pthread_mutex_unlock(&_response->sip_msg_mutex);
+		return (ret);
+	}
+
+	if ((ret = _sip_find_and_copy_header(_response, _ack_msg, SIP_TO,
+	    NULL, B_TRUE)) != 0) {
+		(void) pthread_mutex_unlock(&_response->sip_msg_mutex);
+		return (ret);
+	}
+	if ((ret = _sip_find_and_copy_header(_response, _ack_msg, SIP_FROM,
+	    NULL, B_TRUE)) != 0) {
+		(void) pthread_mutex_unlock(&_response->sip_msg_mutex);
+		return (ret);
+	}
+	if ((ret = _sip_find_and_copy_header(_response, _ack_msg, SIP_CALL_ID,
+	    NULL, B_TRUE)) != 0) {
+		(void) pthread_mutex_unlock(&_response->sip_msg_mutex);
+		return (ret);
+	}
+	/*
+	 * Copy Max-Forward if present
+	 */
+	if (sip_search_for_header(_response, SIP_MAX_FORWARDS, NULL) != NULL) {
+		if ((ret = _sip_find_and_copy_header(_response, _ack_msg,
+		    SIP_MAX_FORWARDS, NULL, B_TRUE)) != 0) {
+			(void) pthread_mutex_unlock(&_response->sip_msg_mutex);
+			return (ret);
+		}
+	}
+	(void) pthread_mutex_unlock(&_response->sip_msg_mutex);
+	seqno = sip_get_callseq_num(_response, &ret);
+	if (ret != 0)
+		return (ret);
+	if ((ret = sip_add_cseq(_ack_msg, ACK, seqno)) != 0)
+		return (ret);
+
+	return (0);
+}
+
+/*
+ * Request-Line   =  Method SP Request-URI SP SIP-Version CRLF
+ */
+int
+sip_add_request_line(sip_msg_t sip_request, sip_method_t method,
+    char *request_uri)
+{
+	_sip_header_t	*new_header;
+	int		 header_size;
+	_sip_msg_t	*_sip_request;
+
+	if (method < INVITE || method >= MAX_SIP_METHODS ||
+	    request_uri == NULL || sip_request == NULL) {
+		return (EINVAL);
+	}
+
+	_sip_request = (_sip_msg_t *)sip_request;
+	(void) pthread_mutex_lock(&_sip_request->sip_msg_mutex);
+	if (_sip_request->sip_msg_cannot_be_modified) {
+		(void) pthread_mutex_unlock(&_sip_request->sip_msg_mutex);
+		return (ENOTSUP);
+	}
+
+	header_size = strlen(sip_methods[method].name) + SIP_SPACE_LEN +
+	    strlen(request_uri) + SIP_SPACE_LEN + strlen(SIP_VERSION) +
+	    strlen(SIP_CRLF);
+
+	new_header = sip_new_header(header_size);
+	if (new_header == NULL) {
+		(void) pthread_mutex_unlock(&_sip_request->sip_msg_mutex);
+		return (ENOMEM);
+	}
+	new_header->sip_hdr_sipmsg = _sip_request;
+
+	(void) snprintf(new_header->sip_hdr_start, header_size + 1,
+	    "%s %s %s%s", sip_methods[method].name, request_uri,
+	    SIP_VERSION, SIP_CRLF);
+
+	new_header->sip_hdr_next = _sip_request->sip_msg_start_line;
+	_sip_request->sip_msg_start_line = new_header;
+	_sip_request->sip_msg_len += header_size;
+	(void) sip_parse_first_line(_sip_request->sip_msg_start_line,
+	    &_sip_request->sip_msg_req_res);
+	if (_sip_request->sip_msg_buf != NULL)
+		_sip_request->sip_msg_modified = B_TRUE;
+	(void) pthread_mutex_unlock(&_sip_request->sip_msg_mutex);
+	return (0);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/lib/libsip/common/sip_msg.h	Sat Oct 07 14:26:26 2006 -0700
@@ -0,0 +1,518 @@
+/*
+ * 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.
+ */
+
+#ifndef	_SIP_MSG_H
+#define	_SIP_MSG_H
+
+#pragma ident	"%Z%%M%	%I%	%E% SMI"
+
+#ifdef	__cplusplus
+extern "C" {
+#endif
+
+#include <sip.h>
+
+#ifdef	__solaris__
+extern int mutex_held();
+#endif
+
+extern sip_header_function_t *sip_header_function_table_external;
+
+/* Compare Cseq numbers */
+#define	SIP_CSEQ_LT(a, b)	((int32_t)((a)-(b)) < 0)
+#define	SIP_CSEQ_GT(a, b)	((int32_t)((a)-(b)) > 0)
+#define	SIP_CSEQ_GEQ(a, b)	((int32_t)((a)-(b)) >= 0)
+
+#define	SIP_HEADER_ACTIVE	0x0
+#define	SIP_HEADER_DELETED	0x1
+#define	SIP_HEADER_DELETED_VAL	0x2
+
+/* List of registered sent-by values */
+typedef struct sent_by_list_s {
+	struct sent_by_list_s	*sb_next;
+	struct sent_by_list_s	*sb_prev;
+	char			*sb_val;
+} sent_by_list_t;
+
+extern sent_by_list_t	*sip_sent_by;
+extern int		sip_sent_by_count;
+extern pthread_mutex_t	sip_sent_by_lock;
+
+typedef struct sip_header {
+	sip_hdr_general_t	sip_hdr_general;
+	/* active/deleted or has deleted val */
+	int			sip_header_state;
+	struct sip_header	*sip_hdr_next;
+	struct sip_header	*sip_hdr_prev;
+	struct sip_message	*sip_hdr_sipmsg;
+	/* True if header was allocated */
+	boolean_t		sip_hdr_allocated;
+	sip_header_function_t	*sip_header_functions;
+}_sip_header_t;
+
+/* Structure for the SIP message body */
+typedef struct sip_content {
+	char			*sip_content_start;
+	char			*sip_content_end;
+	char			*sip_content_current;
+	struct sip_content	*sip_content_next;
+	boolean_t		sip_content_allocated;
+}sip_content_t;
+
+
+/* General definitions */
+
+/* Two string values */
+typedef struct sip_2strs {
+	sip_str_t	s1;
+	sip_str_t	s2;
+}sip_2strs_t;
+
+/* An integer and a string value */
+typedef struct sip_intstr {
+	int		i;
+	sip_str_t	s;
+} sip_intstr_t;
+
+/* Warn value */
+typedef struct sip_warn {
+	int		code;
+	sip_str_t	agt;
+	sip_str_t	text;
+} sip_warn_t;
+
+/* Date value */
+typedef struct sip_date {
+	sip_str_t	t;
+	int		d;
+	sip_str_t	m;
+	int		y;
+	sip_str_t	tz;
+	sip_str_t	wd;
+} sip_date_t;
+
+/* Authorization and authentication value */
+typedef struct sip_auth {
+	sip_str_t	scheme;
+	sip_param_t	*param;
+} sip_auth_t;
+
+/* RACK value */
+typedef struct sip_rack {
+	int		rack_resp_num;
+	int		rack_cseq_num;
+	sip_method_t	rack_method;
+}sip_rack_t;
+
+/* Cseq value */
+typedef struct sip_cseq {
+	int		num;
+	sip_method_t	method;
+} sip_cseq_value_t;
+
+/* Value for Contact, From and To header */
+typedef struct cftr_value {
+	sip_str_t	*display_name;
+	sip_str_t	uri;
+} sip_cftr_value_t;
+
+/* SIP name/version/transport value in Via */
+typedef struct sip_proto_version_s {
+	sip_str_t	name;
+	sip_str_t	version;
+	sip_str_t	transport;
+}sip_proto_version_t;
+
+/* Via value */
+typedef struct via_value {
+	sip_proto_version_t 	sent_protocol;
+	sip_str_t		sent_by_host;
+	int			sent_by_port;
+}sip_via_value_t;
+
+typedef struct sip_hdr_value {
+	sip_value_t	sip_value;
+	union {
+		int			i;
+		sip_str_t		str;
+		sip_2strs_t		strs;
+		sip_intstr_t		intstr;
+		sip_warn_t		warn;
+		sip_date_t		date;
+		sip_auth_t		auth;
+		sip_rack_t		rack;
+		sip_cseq_value_t	cseq;
+		sip_cftr_value_t	cftr;
+		sip_via_value_t		via;
+	} hdr_value;
+} sip_hdr_value_t;
+
+/*
+ * NOTE: ALL value structs MUST have sip_value_t as the first field.
+ */
+#define	sip_value_version	sip_value.sip_value_version
+#define	sip_next_value		sip_value.next
+#define	sip_param_list		sip_value.param_list
+#define	sip_value_state 	sip_value.value_state
+#define	sip_value_header 	sip_value.parsed_header
+#define	sip_value_start		sip_value.value_start
+#define	sip_value_end		sip_value.value_end
+#define	sip_value_parsed_uri 	sip_value.sip_value_parse_uri
+
+#define	auth_val		hdr_value.auth
+#define	auth_scheme_ptr		hdr_value.auth.scheme.sip_str_ptr
+#define	auth_scheme_len		hdr_value.auth.scheme.sip_str_len
+#define	auth_param		hdr_value.auth.param
+#define	int_val			hdr_value.i
+#define	str_val			hdr_value.str
+#define	str_val_ptr		hdr_value.str.sip_str_ptr
+#define	str_val_len		hdr_value.str.sip_str_len
+#define	strs_val		hdr_value.strs
+#define	strs_s1			hdr_value.strs.s1
+#define	strs_s2			hdr_value.strs.s2
+#define	strs1_val_ptr		hdr_value.strs.s1.sip_str_ptr
+#define	strs1_val_len		hdr_value.strs.s1.sip_str_len
+#define	strs2_val_ptr		hdr_value.strs.s2.sip_str_ptr
+#define	strs2_val_len		hdr_value.strs.s2.sip_str_len
+#define	intstr_val		hdr_value.intstr
+#define	intstr_int		hdr_value.intstr.i
+#define	intstr_str		hdr_value.intstr.s
+#define	intstr_str_ptr		hdr_value.intstr.s.sip_str_ptr
+#define	intstr_str_len		hdr_value.intstr.s.sip_str_len
+#define	warn_code		hdr_value.warn.code
+#define	warn_agt		hdr_value.warn.agt
+#define	warn_text		hdr_value.warn.text
+#define	warn_agt_ptr		warn_agt.sip_str_ptr
+#define	warn_agt_len		warn_agt.sip_str_len
+#define	warn_text_ptr		warn_text.sip_str_ptr
+#define	warn_text_len		warn_text.sip_str_len
+#define	date_t			hdr_value.date.t
+#define	date_d			hdr_value.date.d
+#define	date_m			hdr_value.date.m
+#define	date_y			hdr_value.date.y
+#define	date_tz			hdr_value.date.tz
+#define	date_wd			hdr_value.date.wd
+#define	date_t_ptr		date_t.sip_str_ptr
+#define	date_t_len		date_t.sip_str_len
+#define	date_m_ptr		date_m.sip_str_ptr
+#define	date_m_len		date_m.sip_str_len
+#define	date_tz_ptr		date_tz.sip_str_ptr
+#define	date_tz_len		date_tz.sip_str_len
+#define	date_wd_ptr		date_wd.sip_str_ptr
+#define	date_wd_len		date_wd.sip_str_len
+#define	rack_resp		hdr_value.rack.rack_resp_num
+#define	rack_cseq		hdr_value.rack.rack_cseq_num
+#define	rack_method		hdr_value.rack.rack_method
+#define	cftr_name		hdr_value.cftr.display_name
+#define	cftr_uri		hdr_value.cftr.uri
+#define	cseq_num		hdr_value.cseq.num
+#define	cseq_method		hdr_value.cseq.method
+#define	via_protocol		hdr_value.via.sent_protocol
+#define	via_protocol_name	hdr_value.via.sent_protocol.name
+#define	via_protocol_vers	hdr_value.via.sent_protocol.version
+#define	via_protocol_transport	hdr_value.via.sent_protocol.transport
+#define	via_sent_by_host	hdr_value.via.sent_by_host
+#define	via_sent_by_port	hdr_value.via.sent_by_port
+
+#define	SIP_INT_VAL		0x01
+#define	SIP_STR_VAL		0x02
+#define	SIP_STRS_VAL		0x03
+#define	SIP_INTSTR_VAL		0x04
+#define	SIP_AUTH_VAL		0x05
+
+/* hdr value contains two string */
+typedef sip_hdr_value_t sip_acpt_value_t;
+typedef sip_hdr_value_t sip_content_type_value_t;
+
+/* hdr value contains one string only */
+typedef sip_hdr_value_t sip_acpt_lang_value_t;
+typedef sip_hdr_value_t sip_acpt_encode_value_t;
+typedef sip_hdr_value_t sip_alert_value_t;
+typedef sip_hdr_value_t sip_cl_info_value_t;
+typedef sip_hdr_value_t sip_ct_disp_value_t;
+typedef sip_hdr_value_t sip_ct_encode_value_t;
+typedef sip_hdr_value_t sip_ct_lang_value_t;
+typedef sip_hdr_value_t sip_irt_value_t;
+typedef sip_hdr_value_t sip_mime_ver_value_t;
+typedef sip_hdr_value_t sip_org_value_t;
+typedef sip_hdr_value_t sip_prio_value_t;
+typedef sip_hdr_value_t sip_reply_value_t;
+typedef sip_hdr_value_t sip_privacy_value_t;
+typedef sip_hdr_value_t sip_ppassertedid_value_t;
+typedef sip_hdr_value_t sip_ppreferredid_value_t;
+typedef sip_hdr_value_t sip_pxy_req_value_t;
+typedef sip_hdr_value_t sip_req_value_t;
+typedef sip_hdr_value_t sip_subject_value_t;
+typedef sip_hdr_value_t sip_svr_value_t;
+typedef sip_hdr_value_t sip_support_value_t;
+typedef sip_hdr_value_t sip_unsupport_value_t;
+typedef sip_hdr_value_t sip_usr_agt_value_t;
+typedef sip_hdr_value_t sip_err_info_value_t;
+typedef sip_hdr_value_t sip_date_value_t;
+typedef sip_hdr_value_t sip_allert_value_t;
+typedef sip_hdr_value_t	sip_callid_value_t;
+
+/* hdr value contain one int only */
+typedef sip_hdr_value_t sip_expr_value_t;
+typedef sip_hdr_value_t sip_min_expr_value_t;
+typedef sip_hdr_value_t sip_retry_value_t;
+typedef sip_hdr_value_t sip_timestamp_value_t;
+typedef sip_hdr_value_t sip_rseq_value_t;
+typedef sip_hdr_value_t sip_content_len_value_t;
+typedef sip_hdr_value_t sip_max_forwards_value_t;
+typedef sip_hdr_value_t sip_allow_value_t;
+
+/* hdr value contain one int, two strings */
+typedef sip_hdr_value_t sip_warn_value_t;
+
+/* hdr field value is a list of param=param_val */
+typedef sip_hdr_value_t	sip_authen_value_t;
+typedef sip_hdr_value_t	sip_authen_info_value_t;
+typedef sip_hdr_value_t	sip_pxy_authen_value_t;
+typedef sip_hdr_value_t	sip_pxy_author_value_t;
+typedef sip_hdr_value_t	sip_3w_authen_value_t;
+
+/* SIP request line structure */
+typedef struct sip_request {
+	sip_method_t	sip_request_method;
+	sip_str_t	sip_request_uri;
+	sip_uri_t	sip_parse_uri;
+} sip_request_t;
+
+/* SIP response line structure */
+typedef struct sip_response {
+	int		sip_response_code;
+	sip_str_t	sip_response_phrase;
+} sip_response_t;
+
+/* SIP message type - request or response */
+typedef struct sip_message_type {
+	boolean_t		is_request;
+	sip_proto_version_t	sip_proto_version;
+	union {
+	sip_request_t		sip_request;
+	sip_response_t		sip_response;
+	} U;
+	/* This is to save old value when we use a recvd message. */
+	struct sip_message_type	*sip_next;
+} sip_message_type_t;
+
+/* Increment reference count on SIP message */
+#define	SIP_MSG_REFCNT_INCR(sip_msg) {				\
+	(void) pthread_mutex_lock(&(sip_msg)->sip_msg_mutex);	\
+	(sip_msg)->sip_msg_ref_cnt++;				\
+	(void) pthread_mutex_unlock(&(sip_msg)->sip_msg_mutex);	\
+}
+
+/* Decrement reference count on SIP message */
+#define	SIP_MSG_REFCNT_DECR(sip_msg) {					\
+	(void) pthread_mutex_lock(&(sip_msg)->sip_msg_mutex);		\
+	assert((sip_msg)->sip_msg_ref_cnt > 0);				\
+	if (--(sip_msg)->sip_msg_ref_cnt == 0) {			\
+		sip_destroy_msg(sip_msg);				\
+	} else {							\
+		(void) pthread_mutex_unlock(&(sip_msg)->sip_msg_mutex);	\
+	}								\
+}
+
+/* SIP message structure */
+typedef struct sip_message {
+	char			*sip_msg_buf;	/* Message */
+	char			*sip_msg_old_buf;
+	boolean_t		sip_msg_modified;
+	boolean_t		sip_msg_cannot_be_modified;
+	int			sip_msg_len;
+	size_t			sip_msg_content_len;	/* content length */
+	sip_content_t		*sip_msg_content;
+	/* All fields synchronizes on this */
+	pthread_mutex_t		sip_msg_mutex;
+	/* doubly linked list of headers */
+	_sip_header_t		*sip_msg_headers_start;
+	_sip_header_t		*sip_msg_headers_end;
+	_sip_header_t		*sip_msg_start_line;
+	sip_message_type_t	*sip_msg_req_res;
+	int			sip_msg_ref_cnt;
+}_sip_msg_t;
+
+extern char		*sip_get_tcp_msg(sip_conn_object_t, char *, size_t *);
+extern char		*sip_msg_to_msgbuf(_sip_msg_t *msg, int *error);
+extern char		*_sip_startline_to_str(_sip_msg_t *sip_msg, int *error);
+extern int		sip_adjust_msgbuf(_sip_msg_t *msg);
+extern void		sip_delete_all_headers(_sip_msg_t *sip_msg);
+extern _sip_header_t	*sip_dup_header(_sip_header_t *from);
+extern int		_sip_copy_header(_sip_msg_t *, _sip_header_t *, char *,
+			    boolean_t);
+extern int		_sip_find_and_copy_header(_sip_msg_t *, _sip_msg_t *,
+			    char *, char *, boolean_t);
+extern int		_sip_find_and_copy_all_header(_sip_msg_t *,
+			    _sip_msg_t *, char *header_name);
+extern _sip_header_t	*sip_search_for_header(_sip_msg_t *, char *,
+			    _sip_header_t *);
+extern void		_sip_add_header(_sip_msg_t *, _sip_header_t *,
+			    boolean_t, boolean_t, char *);
+extern _sip_header_t	*sip_new_header(int);
+extern int		sip_create_nonOKack(sip_msg_t, sip_msg_t, sip_msg_t);
+extern void		sip_destroy_msg(_sip_msg_t *);
+extern void		sip_free_header(_sip_header_t *sip_header);
+extern void		sip_free_phdr(sip_parsed_header_t *);
+extern void		sip_free_cftr_header(sip_parsed_header_t *);
+
+extern int		sip_parse_allow_events_header(_sip_header_t *,
+			    sip_parsed_header_t **);
+extern int		sip_parse_event_header(_sip_header_t *,
+			    sip_parsed_header_t **);
+extern int		sip_parse_substate_header(_sip_header_t *,
+			    sip_parsed_header_t **);
+extern int		sip_parse_acpt_header(_sip_header_t *,
+			    sip_parsed_header_t **);
+extern int		sip_parse_acpt_encode_header(_sip_header_t *,
+			    sip_parsed_header_t **);
+extern int		sip_parse_acpt_lang_header(_sip_header_t *,
+			    sip_parsed_header_t **);
+extern int		sip_parse_alert_header(_sip_header_t *,
+			    sip_parsed_header_t **);
+extern int		sip_parse_allow_header(_sip_header_t *,
+			    sip_parsed_header_t **);
+extern int		sip_parse_useragt_header(_sip_header_t *,
+			    sip_parsed_header_t **);
+extern int		sip_parse_usupport_header(_sip_header_t *,
+			    sip_parsed_header_t **);
+extern int		sip_parse_timestamp_header(_sip_header_t *,
+			    sip_parsed_header_t **);
+extern int		sip_parse_support_header(_sip_header_t *,
+			    sip_parsed_header_t **);
+extern int		sip_parse_subject_header(_sip_header_t *,
+			    sip_parsed_header_t **);
+extern int		sip_parse_server_header(_sip_header_t *,
+			    sip_parsed_header_t **);
+extern int		sip_parse_retryaft_header(_sip_header_t *,
+			    sip_parsed_header_t **);
+extern int		sip_parse_require_header(_sip_header_t *,
+			    sip_parsed_header_t **);
+extern int		sip_parse_replyto_header(_sip_header_t *,
+			    sip_parsed_header_t **);
+extern int		sip_parse_passertedid_header(_sip_header_t *,
+			    sip_parsed_header_t **);
+extern int		sip_parse_ppreferredid_header(_sip_header_t *,
+			    sip_parsed_header_t **);
+extern int		sip_parse_priority_header(_sip_header_t *,
+			    sip_parsed_header_t **);
+extern int		sip_parse_org_header(_sip_header_t *,
+			    sip_parsed_header_t **);
+extern int		sip_parse_mimeversion_header(_sip_header_t *,
+			    sip_parsed_header_t **);
+extern int		sip_parse_minexpire_header(_sip_header_t *,
+			    sip_parsed_header_t **);
+extern int		sip_parse_rseq_header(_sip_header_t *,
+			    sip_parsed_header_t **);
+extern int		sip_parse_inreplyto_header(_sip_header_t *,
+			    sip_parsed_header_t **);
+extern int		sip_parse_privacy_header(_sip_header_t *,
+			    sip_parsed_header_t **);
+extern int		sip_parse_expire_header(_sip_header_t *,
+			    sip_parsed_header_t **);
+extern int		sip_parse_errorinfo_header(_sip_header_t *,
+			    sip_parsed_header_t **);
+extern int		sip_parse_contentlang_header(_sip_header_t *,
+			    sip_parsed_header_t **);
+extern int		sip_parse_contentencode_header(_sip_header_t *,
+			    sip_parsed_header_t **);
+extern int		sip_parse_contentdis_header(_sip_header_t *,
+			    sip_parsed_header_t **);
+extern int		sip_parse_callinfo_header(_sip_header_t *,
+			    sip_parsed_header_t **);
+extern int		sip_parse_date_header(_sip_header_t *,
+			    sip_parsed_header_t **);
+extern int		sip_parse_warn_header(_sip_header_t *,
+			    sip_parsed_header_t **);
+extern int		sip_parse_cftr_header(_sip_header_t *,
+			    sip_parsed_header_t **);
+extern int		sip_parse_cseq_header(_sip_header_t *,
+			    sip_parsed_header_t **);
+extern int		sip_parse_cid_header(_sip_header_t *,
+			    sip_parsed_header_t **);
+extern int		sip_parse_via_header(_sip_header_t *,
+			    sip_parsed_header_t **);
+extern int		sip_parse_clen_header(_sip_header_t *,
+			    sip_parsed_header_t **);
+extern int		sip_parse_maxf_header(_sip_header_t *,
+			    sip_parsed_header_t **);
+extern int		sip_parse_ctype_header(_sip_header_t *,
+			    sip_parsed_header_t **);
+extern int		sip_parse_unknown_header(_sip_header_t *,
+			    sip_parsed_header_t **);
+extern int		sip_parse_ainfo_header(_sip_header_t *,
+			    sip_parsed_header_t **);
+extern int		sip_parse_preq_header(_sip_header_t *,
+			    sip_parsed_header_t **);
+extern int		sip_parse_author_header(_sip_header_t *,
+			    sip_parsed_header_t **);
+extern int		sip_parse_pauthor_header(_sip_header_t *,
+			    sip_parsed_header_t **);
+extern int		sip_parse_pauthen_header(_sip_header_t *,
+			    sip_parsed_header_t **);
+extern int		sip_parse_wauthen_header(_sip_header_t *,
+			    sip_parsed_header_t **);
+extern int		sip_parse_rseq(_sip_header_t *, sip_parsed_header_t **);
+extern int		sip_parse_rack(_sip_header_t *, sip_parsed_header_t **);
+extern int		sip_parse_passertedid(_sip_header_t *,
+			    sip_parsed_header_t **);
+extern int		sip_parse_ppreferredid(_sip_header_t *,
+			    sip_parsed_header_t **);
+extern int		sip_parse_privacy_header(_sip_header_t *,
+			    sip_parsed_header_t **);
+
+extern sip_param_t	*sip_get_param_from_list(sip_param_t *, char *);
+extern int		sip_copy_values(char *, _sip_header_t *);
+extern int		sip_add_content_length(_sip_msg_t *, int);
+extern int		sip_delete_start_line_locked(_sip_msg_t *);
+
+/* Useful access macros */
+#define	sip_resp_phrase_len	U.sip_response.sip_response_phrase.sip_str_len
+#define	sip_resp_phrase_ptr	U.sip_response.sip_response_phrase.sip_str_ptr
+
+#define	sip_resp_code		U.sip_response.sip_response_code
+#define	sip_resp_phrase		U.sip_response.sip_response_phrase
+
+#define	sip_req_method		U.sip_request.sip_request_method
+#define	sip_req_uri		U.sip_request.sip_request_uri
+#define	sip_req_uri_ptr		U.sip_request.sip_request_uri.sip_str_ptr
+#define	sip_req_uri_len		U.sip_request.sip_request_uri.sip_str_ptr
+#define	sip_req_parse_uri	U.sip_request.sip_parse_uri
+
+#define	sip_header_parse	sip_header_functions->header_parse_func
+#define	sip_header_name		sip_header_functions->header_name
+
+#define	sip_hdr_start		sip_hdr_general.sip_hdr_start
+#define	sip_hdr_end		sip_hdr_general.sip_hdr_end
+#define	sip_hdr_current		sip_hdr_general.sip_hdr_current
+#define	sip_hdr_parsed		sip_hdr_general.sip_hdr_parsed
+
+#ifdef	__cplusplus
+}
+#endif
+
+#endif	/* _SIP_MSG_H */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/lib/libsip/common/sip_parse_generic.c	Sat Oct 07 14:26:26 2006 -0700
@@ -0,0 +1,1294 @@
+/*
+ * 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"
+
+/*
+ * 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)
+{
+	assert(separator_1st != (char)NULL || separator_2nd != (char)NULL);
+	while (sip_header->sip_hdr_current < sip_header->sip_hdr_end) {
+		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) != 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) != 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) == 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) == 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) != 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);
+		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) !=
+		    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);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/lib/libsip/common/sip_parse_generic.h	Sat Oct 07 14:26:26 2006 -0700
@@ -0,0 +1,69 @@
+/*
+ * 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.
+ */
+
+#ifndef	_SIP_PARSE_GENERIC_H
+#define	_SIP_PARSE_GENERIC_H
+
+#pragma ident	"%Z%%M%	%I%	%E% SMI"
+
+#ifdef	__cplusplus
+extern "C" {
+#endif
+
+extern int		sip_atoi(_sip_header_t	*, int *);
+extern int		sip_find_token(_sip_header_t  *, char);
+extern int		sip_find_cr(_sip_header_t *);
+extern int		sip_find_separator(_sip_header_t *, char, char, char);
+extern int		sip_find_white_space(_sip_header_t *);
+extern int		sip_skip_white_space(_sip_header_t *);
+extern int		sip_reverse_skip_white_space(_sip_header_t *);
+extern int		sip_parse_goto_values(_sip_header_t  *);
+extern int		sip_goto_next_value(_sip_header_t *);
+extern int		sip_parse_params(_sip_header_t *, sip_param_t **);
+extern int		sip_prim_parsers(_sip_header_t *,
+			    sip_parsed_header_t **);
+extern boolean_t	sip_is_empty_hdr(_sip_header_t *);
+extern int		sip_parse_hdr_empty(_sip_header_t *,
+			    sip_parsed_header_t **);
+int			sip_get_protocol_version(_sip_header_t *,
+			    sip_proto_version_t *sip_proto_version);
+extern int		sip_parse_first_line(_sip_header_t *,
+			    sip_message_type_t **);
+extern int		sip_parse_hdr_parser1(_sip_header_t *,
+			    sip_parsed_header_t **, char);
+extern int		sip_parse_hdr_parser2(_sip_header_t *,
+			    sip_parsed_header_t **, int);
+extern int		sip_parse_hdr_parser3(_sip_header_t *,
+			    sip_parsed_header_t **, int, boolean_t);
+extern int		sip_parse_hdr_parser4(_sip_header_t *,
+			    sip_parsed_header_t **);
+extern int		sip_parse_hdr_parser5(_sip_header_t *,
+			    sip_parsed_header_t **, boolean_t);
+#ifdef	__cplusplus
+}
+#endif
+
+#endif	/* _SIP_PARSE_GENERIC_H */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/lib/libsip/common/sip_parse_hdrs.c	Sat Oct 07 14:26:26 2006 -0700
@@ -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);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/lib/libsip/common/sip_parse_uri.c	Sat Oct 07 14:26:26 2006 -0700
@@ -0,0 +1,1588 @@
+/*
+ * 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 <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+
+#include "sip_parse_uri.h"
+
+/*
+ * SIP-URI          =  "sip:" [ userinfo ] hostport uri-parameters [ headers ]
+ * SIPS-URI         =  "sips:" [ userinfo ] hostport uri-parameters [ headers ]
+ * userinfo         =  ( user / telephone-subscriber ) [ ":" password ] "@"
+ * user             =  1*( unreserved / escaped / user-unreserved )
+ * user-unreserved  =  "&" / "=" / "+" / "$" / "," / ";" / "?" / "/"
+ * password         =  *( unreserved / escaped / "&" / "=" / "+" / "$" / "," )
+ * hostport         =  host [ ":" port ]
+ * host             =  hostname / IPv4address / IPv6reference
+ * hostname         =  *( domainlabel "." ) toplabel [ "." ]
+ * domainlabel      =  alphanum / alphanum *( alphanum / "-" ) alphanum
+ * toplabel         =  ALPHA / ALPHA *( alphanum / "-" ) alphanum
+ * IPv4address    =  1*3DIGIT "." 1*3DIGIT "." 1*3DIGIT "." 1*3DIGIT
+ * IPv6reference  =  "[" IPv6address "]"
+ * IPv6address    =  hexpart [ ":" IPv4address ]
+ * hexpart        =  hexseq / hexseq "::" [ hexseq ] / "::" [ hexseq ]
+ * hexseq         =  hex4 *( ":" hex4)
+ * hex4           =  1*4HEXDIG
+ * port           =  1*DIGIT
+ *
+ * The BNF for telephone-subscriber can be found in RFC 2806 [9].  Note,
+ * however, that any characters allowed there that are not allowed in
+ * the user part of the SIP URI MUST be escaped.
+ *
+ * uri-parameters    =  *( ";" uri-parameter)
+ * uri-parameter     =  transport-param / user-param / method-param
+ *                      / ttl-param / maddr-param / lr-param / other-param
+ * transport-param   =  "transport="( "udp" / "tcp" / "sctp" / "tls"
+ *                     / other-transport)
+ * other-transport   =  token
+ * user-param        =  "user=" ( "phone" / "ip" / other-user)
+ * other-user        =  token
+ * method-param      =  "method=" Method
+ * ttl-param         =  "ttl=" ttl
+ * maddr-param       =  "maddr=" host
+ * lr-param          =  "lr"
+ * other-param       =  pname [ "=" pvalue ]
+ * pname             =  1*paramchar
+ * pvalue            =  1*paramchar
+ * paramchar         =  param-unreserved / unreserved / escaped
+ * param-unreserved  =  "[" / "]" / "/" / ":" / "&" / "+" / "$"
+ * headers         =  "?" header *( "&" header )
+ * header          =  hname "=" hvalue
+ * hname           =  1*( hnv-unreserved / unreserved / escaped )
+ * hvalue          =  *( hnv-unreserved / unreserved / escaped )
+ * hnv-unreserved  =  "[" / "]" / "/" / "?" / ":" / "+" / "$"
+ *
+ */
+
+#define	SIP_URI_MSG_BUF_SZ	100
+
+#define	SIP_URI_ISHEX(c)					\
+	(((int)(c) >= 0x30 && (int)(c) <= 0x39) || 	\
+	((int)(c) >= 0x41 && (int)(c) <= 0x46) || 	\
+	((int)(c) >= 0x61 && (int)(c) <= 0x66))
+
+#define	SIP_URI_ISURLESCAPE(scan, end)			\
+	((scan) + 2 < (end) && (scan)[0] == '%' && 	\
+	SIP_URI_ISHEX((scan)[1]) && SIP_URI_ISHEX((scan[2])))
+
+/*
+ * URL character classes
+ *  mark	- _ . ! ~ * ' ()
+ *  reserved	; / ? : @ & = + $ ,    also [] for IPv6
+ *  unreserved	alphanum mark
+ *  pchar	: @ & = + $ , unreserved
+ *  userinfo	; : & = + $ , unreserved escaped
+ *  relsegment	; @ & = + $ , unreserved escaped
+ *  reg_name	; : @ & = + $ , unreserved escaped
+ *  token	- _ . ! ~ * ' %  + `
+ *  param-unreserved  [ ] / : + $ &
+ *  hnv-unreserved    [ ] / : + $ ?
+ */
+#define	SIP_URI_ALPHA_BIT		0x0001
+#define	SIP_URI_DIGIT_BIT		0x0002
+#define	SIP_URI_ALNUM_BITS		0x0003
+#define	SIP_URI_SCHEME_BIT		0x0004	/* for - + . */
+#define	SIP_URI_TOKEN_BIT		0x0008	/* for - _ . ! ~ * ' % + ` */
+#define	SIP_URI_QUEST_BIT		0x0010	/* for ? */
+#define	SIP_URI_AT_BIT			0x0020	/* for @ */
+#define	SIP_URI_COLON_BIT		0x0040	/* for : */
+#define	SIP_URI_SEMI_BIT		0x0080	/* for ; */
+#define	SIP_URI_DASH_BIT		0x0100	/* for - */
+#define	SIP_URI_MARK_BIT		0x0200	/* for - _ . ! ~ * ' ( ) */
+#define	SIP_URI_AND_BIT			0x0400	/* for & */
+#define	SIP_URI_PHCOMM_BIT		0x0800	/* for [ ] / : + $ */
+#define	SIP_URI_OTHER_BIT		0x1000	/* for = + $ , */
+#define	SIP_URI_SLASH_BIT		0x2000	/* for / */
+#define	SIP_URI_VISUALSEP_BIT		0x4000	/* for -.() */
+#define	SIP_URI_DTMFURI_DIGIT_BIT	0x8000	/* for *ABCD */
+
+#define	a 			SIP_URI_ALPHA_BIT
+#define	d 			SIP_URI_DIGIT_BIT
+#define	s 			SIP_URI_SCHEME_BIT
+#define	t 			SIP_URI_TOKEN_BIT
+#define	q 			SIP_URI_QUEST_BIT
+#define	m 			SIP_URI_AT_BIT
+#define	c 			SIP_URI_COLON_BIT
+#define	i 			SIP_URI_SEMI_BIT
+#define	h 			SIP_URI_DASH_BIT
+#define	k 			SIP_URI_MARK_BIT
+#define	n 			SIP_URI_AND_BIT
+#define	o 			SIP_URI_PHCOMM_BIT
+#define	r 			SIP_URI_OTHER_BIT
+#define	l 			SIP_URI_SLASH_BIT
+#define	v 			SIP_URI_VISUALSEP_BIT
+#define	f 			SIP_URI_DTMFURI_DIGIT_BIT
+
+static const unsigned short sip_uri_table[256] = {
+	0,	0,	0,	0,	0,	0,	0,	0,
+	0,	0,	0,	0,	0,	0,	0,	0,
+	0,	0,	0,	0,	0,	0,	0,	0,
+	0,	0,	0,	0,	0,	0,	0,	0,
+	0,	t|k,	0,	0,	o|r,	t,	n,	t|k,
+	k|v,	k|v,	t|k|f, s|t|r|o,	r,  h|s|t|k|v, s|t|k|v,	o|l,
+	d,	d,	d,	d,	d,	d,	d,	d,
+	d,	d,	c|o,	i,	0,	r,	0,	q,
+	m,	a|f,	a|f,	a|f,	a|f,	a,	a,	a,
+	a,	a,	a,	a,	a,	a,	a,	a,
+	a,	a,	a,	a,	a,	a,	a,	a,
+	a,	a,	a,	o,	0,	o,	0,	t|k,
+	t,	a,	a,	a,	a,	a,	a,	a,
+	a,	a,	a,	a,	a,	a,	a,	a,
+	a,	a,	a,	a,	a,	a,	a,	a,
+	a,	a,	a,	0,	0,	0,	t|k,	0,
+	0,	0,	0,	0,	0,	0,	0,	0,
+	0,	0,	0,	0,	0,	0,	0,	0,
+	0,	0,	0,	0,	0,	0,	0,	0,
+	0,	0,	0,	0,	0,	0,	0,	0,
+	0,	0,	0,	0,	0,	0,	0,	0,
+	0,	0,	0,	0,	0,	0,	0,	0,
+	0,	0,	0,	0,	0,	0,	0,	0,
+	0,	0,	0,	0,	0,	0,	0,	0,
+	0,	0,	0,	0,	0,	0,	0,	0,
+	0,	0,	0,	0,	0,	0,	0,	0,
+	0,	0,	0,	0,	0,	0,	0,	0,
+	0,	0,	0,	0,	0,	0,	0,	0,
+	0,	0,	0,	0,	0,	0,	0,	0,
+	0,	0,	0,	0,	0,	0,	0,	0,
+	0,	0,	0,	0,	0,	0,	0,	0,
+	0,	0,	0,	0,	0,	0,	0,	0,
+};
+
+#undef	a
+#undef	d
+#undef	s
+#undef	t
+#undef	q
+#undef	m
+#undef	c
+#undef	i
+#undef	h
+#undef	k
+#undef	n
+#undef	o
+#undef	r
+#undef	l
+#undef	v
+#undef	f
+
+#define	SIP_URI_UT(c)			sip_uri_table[(unsigned char)(c)]
+#define	SIP_URI_ISALPHA(c)		(SIP_URI_UT(c) & SIP_URI_ALPHA_BIT)
+#define	SIP_URI_ISDIGIT(c)		(SIP_URI_UT(c) & SIP_URI_DIGIT_BIT)
+#define	SIP_URI_ISALNUM(c)		(SIP_URI_UT(c) & SIP_URI_ALNUM_BITS)
+#define	SIP_URI_ISSCHEME(c)		\
+		(SIP_URI_UT(c) & (SIP_URI_ALNUM_BITS|SIP_URI_SCHEME_BIT))
+#define	SIP_URI_ISTOKEN(c)		\
+		(SIP_URI_UT(c) & (SIP_URI_ALNUM_BITS|SIP_URI_TOKEN_BIT))
+#define	SIP_URI_ISSIPDELIM(c)		\
+		(SIP_URI_UT(c) & (SIP_URI_SEMI_BIT|SIP_URI_QUEST_BIT))
+#define	SIP_URI_ISSIPHDELIM(c)					\
+	(SIP_URI_UT(c) & (SIP_URI_COLON_BIT|SIP_URI_SEMI_BIT|SIP_URI_QUEST_BIT))
+#define	SIP_URI_ISHOST(c)		\
+		(SIP_URI_UT(c) & (SIP_URI_ALNUM_BITS|SIP_URI_DASH_BIT))
+#define	SIP_URI_ISUSER(c)						\
+	(SIP_URI_UT(c) & (SIP_URI_OTHER_BIT|SIP_URI_SEMI_BIT|		\
+	SIP_URI_QUEST_BIT|SIP_URI_SLASH_BIT|SIP_URI_AND_BIT))
+
+#define	SIP_URI_ISABSHDELIM(c)					\
+	(SIP_URI_UT(c) & \
+	(SIP_URI_SLASH_BIT|SIP_URI_COLON_BIT|SIP_URI_QUEST_BIT))
+#define	SIP_URI_ISABSDELIM(c)	\
+	(SIP_URI_UT(c) & (SIP_URI_SLASH_BIT|SIP_URI_QUEST_BIT))
+#define	SIP_URI_ISUNRESERVED(c)	\
+	(SIP_URI_UT(c) & (SIP_URI_ALNUM_BITS|SIP_URI_MARK_BIT))
+#define	SIP_URI_ISPARAM(c)						\
+	(SIP_URI_UT(c) & (SIP_URI_PHCOMM_BIT|SIP_URI_AND_BIT|\
+	SIP_URI_ALNUM_BITS|SIP_URI_MARK_BIT))
+#define	SIP_URI_ISHEADER(c)						\
+	(SIP_URI_UT(c) & (SIP_URI_PHCOMM_BIT|SIP_URI_QUEST_BIT|\
+	SIP_URI_ALNUM_BITS|SIP_URI_MARK_BIT))
+#define	SIP_URI_ISOTHER(c)		(SIP_URI_UT(c) & SIP_URI_OTHER_BIT)
+#define	SIP_URI_ISRESERVED(c)					\
+	(SIP_URI_UT(c) & (SIP_URI_SEMI_BIT|SIP_URI_SLASH_BIT|	\
+	SIP_URI_QUEST_BIT| SIP_URI_COLON_BIT|SIP_URI_AT_BIT|	\
+	SIP_URI_AND_BIT|SIP_URI_OTHER_BIT))
+#define	SIP_URI_ISPCHAR(c)	\
+	(SIP_URI_UT(c) & (SIP_URI_COLON_BIT|SIP_URI_AT_BIT|	\
+	SIP_URI_AND_BIT|SIP_URI_OTHER_BIT))
+#define	SIP_URI_ISREGNAME(c)					\
+	(SIP_URI_UT(c) & 	\
+	(SIP_URI_OTHER_BIT|SIP_URI_SEMI_BIT|SIP_URI_COLON_BIT|	\
+	SIP_URI_AT_BIT|SIP_URI_AND_BIT))
+#define	SIP_URI_ISPHONEDIGIT(c)	\
+	(SIP_URI_UT(c) & (SIP_URI_DIGIT_BIT|SIP_URI_VISUALSEP_BIT))
+#define	SIP_URI_ISDTMFDIGIT(c)	(SIP_URI_UT(c) & SIP_URI_DTMFURI_DIGIT_BIT)
+
+static int  sip_uri_url_casecmp(const char *, const char *, unsigned);
+static void sip_uri_parse_params(_sip_uri_t *, char *, char *);
+static void sip_uri_parse_headers(_sip_uri_t *, char *, char *);
+static void sip_uri_parse_abs_opaque(_sip_uri_t *, char *, char *);
+static void sip_uri_parse_abs_query(_sip_uri_t *, char *, char *);
+static void sip_uri_parse_abs_path(_sip_uri_t *, char *, char *);
+static void sip_uri_parse_abs_regname(_sip_uri_t *, char *, char *);
+static int  sip_uri_parse_scheme(_sip_uri_t *, char *, char *);
+static void sip_uri_parse_password(_sip_uri_t *, char *, char *);
+static void sip_uri_parse_user(_sip_uri_t *, char *, char *);
+static void sip_uri_parse_port(_sip_uri_t *, char *, char *);
+static void sip_uri_parse_netpath(_sip_uri_t *, char **, char *, boolean_t);
+static int  sip_uri_parse_ipv6(char *, char *);
+static int  sip_uri_parse_ipv4(char *, char *);
+static int  sip_uri_parse_hostname(char *, char *);
+static int sip_uri_parse_tel(char *, char *);
+static int sip_uri_parse_tel_areaspe(char *, char *);
+static int sip_uri_parse_tel_servicepro(char *, char *);
+static int sip_uri_parse_tel_futureext(char *, char *);
+static int sip_uri_isTokenchar(char **, char *);
+static int sip_uri_isEscapedPound(char **, char *);
+static int sip_uri_hexVal(char *, char *);
+static int SIP_URI_HEXVAL(int);
+
+/*
+ * get the hex value of a char
+ */
+static int
+SIP_URI_HEXVAL(int c)
+{
+	if (c >= 0x30 && c <= 0x39)
+		return (c - '0');
+	if (c >= 0x41 && c <= 0x46)
+		return (c - 'A' + 10);
+	if (c >= 0x61 && c <= 0x66)
+		return (c - 'a' + 10);
+	return (c);
+}
+
+/*
+ * basic ASCII case-insensitive comparison
+ */
+static int
+sip_uri_url_casecmp(const char *str1, const char *str2, unsigned len)
+{
+	unsigned	j;
+
+	for (j = 0; j < len && tolower(str1[j]) == tolower(str2[j]) &&
+	    str1[j] != '\0'; ++j) {
+		;
+	}
+	return (j == len ? 0 : tolower(str2[j]) - tolower(str1[j]));
+}
+
+/*
+ * telephone-subscriber  = global-phone-number / local-phone-number
+ * Please refer to RFC 2806
+ */
+static int
+sip_uri_parse_tel(char *scan, char *uend)
+{
+	char	*mark = (char *)0;
+	int	ret = 0;
+	int	isGlobal = 0;
+	int	quote = 0;
+
+	if (scan == uend)
+		return (0);
+	if (*scan == '+') {
+		++scan;
+		isGlobal = 1;
+	}
+	mark = scan;
+	if (isGlobal) {
+		while (scan < uend && SIP_URI_ISPHONEDIGIT(*scan))
+			++scan;
+	} else {
+		while (scan < uend &&
+		    (SIP_URI_ISPHONEDIGIT(*scan) ||
+		    SIP_URI_ISDTMFDIGIT(*scan) ||
+		    sip_uri_isEscapedPound(&scan, uend) ||
+		    *scan == 'p' || *scan == 'w')) {
+			++scan;
+		}
+	}
+	if (mark == scan || (scan < uend && *scan != ';'))
+		return (0);
+
+	/*
+	 * parse isdn-subaddress
+	 */
+	if (uend - scan > 6 && !sip_uri_url_casecmp(scan, ";isub=", 6)) {
+		scan += 6;
+		mark = scan;
+		while (scan < uend && SIP_URI_ISPHONEDIGIT(*scan))
+			++scan;
+		if (mark == scan || (scan < uend && *scan != ';'))
+			return (0);
+	}
+
+	/*
+	 * parse post-dial
+	 */
+	if (uend - scan > 7 && !sip_uri_url_casecmp(scan, ";postd=", 7)) {
+		scan += 7;
+		mark = scan;
+		while (scan < uend &&
+		    (SIP_URI_ISPHONEDIGIT(*scan) ||
+		    SIP_URI_ISDTMFDIGIT(*scan) ||
+		    sip_uri_isEscapedPound(&scan, uend) ||
+		    *scan == 'p' || *scan == 'w')) {
+			++scan;
+		}
+		if (mark == scan || (scan < uend && *scan != ';'))
+			return (0);
+	}
+
+	if (!isGlobal) {
+		/*
+		 * parse area-specifier
+		 */
+		if (uend - scan > 15 &&
+		    !sip_uri_url_casecmp(scan, ";phone-context=", 15)) {
+			scan += 15;
+			mark = scan;
+			while (scan < uend && *scan != ';')
+				++scan;
+			ret = sip_uri_parse_tel_areaspe(mark, scan);
+		}
+	} else {
+		ret = 1;
+	}
+
+	/*
+	 * parse area-specifier, service-provider, future-extension
+	 */
+	while (scan < uend && ret) {
+		if (uend - scan > 15 &&
+			!sip_uri_url_casecmp(scan, ";phone-context=", 15)) {
+			scan += 15;
+			mark = scan;
+			while (scan < uend && *scan != ';')
+				++scan;
+			ret = sip_uri_parse_tel_areaspe(mark, scan);
+		} else if (uend - scan > 5 &&
+		    !sip_uri_url_casecmp(scan, ";tsp=", 5)) {
+			scan += 5;
+			mark = scan;
+			while (scan < uend && *scan != ';')
+				++scan;
+			ret = sip_uri_parse_tel_servicepro(mark, scan);
+		} else {
+			++scan;
+			mark = scan;
+			while (scan < uend && (*scan != ';' || quote)) {
+				if (sip_uri_hexVal(scan, uend) == 0x22) {
+					quote = !quote;
+					scan += 3;
+				} else {
+					++scan;
+				}
+			}
+			ret = sip_uri_parse_tel_futureext(mark, scan);
+		}
+	}
+	return (ret && scan == uend);
+}
+
+/*
+ * area-specifier        = ";" phone-context-tag "=" phone-context-ident
+ * phone-context-tag     = "phone-context"
+ * phone-context-ident   = network-prefix / private-prefix
+ * network-prefix        = global-network-prefix / local-network-prefix
+ * global-network-prefix = "+" 1*phonedigit
+ * local-network-prefix  = 1*(phonedigit / dtmf-digit / pause-character)
+ * private-prefix        = (%x21-22 / %x24-27 / %x2C / %x2F / %x3A /
+ *                          %x3C-40 / %x45-4F / %x51-56 / %x58-60 /
+ *                          %x65-6F / %x71-76 / %x78-7E)
+ *                          *(%x21-3A / %x3C-7E)
+ * phonedigit            = DIGIT / visual-separator
+ * visual-separator      = "-" / "." / "(" / ")"
+ * pause-character       = one-second-pause / wait-for-dial-tone
+ * one-second-pause      = "p"
+ * wait-for-dial-tone    = "w"
+ * dtmf-digit            = "*" / "#" / "A" / "B" / "C" / "D"
+ */
+static int
+sip_uri_parse_tel_areaspe(char *scan, char *uend)
+{
+	int	uri_hexValue;
+
+	if (scan == uend)
+		return (0);
+
+	/*
+	 * parse global-network-prefix
+	 */
+	if (*scan == '+') {
+		++scan;
+		if (scan == uend)
+			return (0);
+		while (scan < uend && SIP_URI_ISPHONEDIGIT(*scan))
+			++scan;
+	/*
+	 * parse local-network-prefix
+	 */
+	} else if (SIP_URI_ISPHONEDIGIT(*scan) || SIP_URI_ISDTMFDIGIT(*scan) ||
+	    sip_uri_isEscapedPound(&scan, uend) ||
+	    *scan == 'p' || *scan == 'w') {
+		++scan;
+		while (scan < uend &&
+		    (SIP_URI_ISPHONEDIGIT(*scan) ||
+		    SIP_URI_ISDTMFDIGIT(*scan) ||
+		    sip_uri_isEscapedPound(&scan, uend) ||
+		    *scan == 'p' || *scan == 'w')) {
+			++scan;
+		}
+	} else {
+	/*
+	 * parse private-prefix
+	 *
+	 * any characters allowed in RFC 2806 that are not allowed in
+	 * the user part of the SIP URI MUST be escaped
+	 *
+	 * private-prefix	= (! $ & ', / = ? _
+	 *			EFGHIJKLMNOQRSTUVXYZ efghijklmnoqrstuvxyz
+	 *			{ } | ~ [ ] \ ^  ` " % : < > @)
+	 *			*(%x21-3A / %x3C-7E)
+	 *
+	 * following characters are allowed in RFC 2806 and
+	 * the user part of SIP URI
+	 *  ! $ & ', / = ? _ EFGHIJKLMNOQRSTUVXYZ efghijklmnoqrstuvxyz
+	 */
+		if (*scan == '!' || *scan == '$' || *scan == '&' ||
+		    *scan == '\'' || *scan == ',' || *scan == '/' ||
+		    *scan == '=' || *scan == '?' || *scan == '_' ||
+		    (*scan >= 'E' && *scan <= 'Z' &&
+		    *scan != 'P' && *scan != 'W') ||
+		    (*scan >= 'e' && *scan <= 'z' &&
+		    *scan != 'p' && *scan != 'w')) {
+			++scan;
+		} else {
+			uri_hexValue = sip_uri_hexVal(scan, uend);
+			if (uri_hexValue == 0x21 || uri_hexValue == 0x22 ||
+			    (uri_hexValue >= 0x24 && uri_hexValue <= 0x27) ||
+			    uri_hexValue == 0x2c || uri_hexValue == 0x2f ||
+			    uri_hexValue == 0x3a ||
+			    (uri_hexValue >= 0x3c && uri_hexValue <= 0x40) ||
+			    (uri_hexValue >= 0x45 && uri_hexValue <= 0x4f) ||
+			    (uri_hexValue >= 0x51 && uri_hexValue <= 0x56) ||
+			    (uri_hexValue >= 0x58 && uri_hexValue <= 0x60) ||
+			    (uri_hexValue >= 0x65 && uri_hexValue <= 0x6f) ||
+			    (uri_hexValue >= 0x71 && uri_hexValue <= 0x76) ||
+			    (uri_hexValue >= 0x78 && uri_hexValue <= 0x7e)) {
+				scan += 3;
+			} else {
+				return (0);
+			}
+		}
+		/*
+		 * parse *(%x21-3A / %x3C-7E)
+		 */
+		while (scan < uend) {
+			if (SIP_URI_ISUNRESERVED(*scan) ||
+			    (SIP_URI_ISUSER(*scan) && *scan != ';')) {
+				++scan;
+			} else {
+				uri_hexValue = sip_uri_hexVal(scan, uend);
+				if (uri_hexValue >= 0x21 &&
+				    uri_hexValue <= 0x7e &&
+				    uri_hexValue != 0x3b) {
+					scan += 3;
+				} else {
+					return (0);
+				}
+			}
+		}
+	}
+	if (scan < uend)
+		return (0);
+	return (1);
+}
+
+static int
+sip_uri_hexVal(char *scan, char *uend)
+{
+	int	ret = -1;
+
+	if (SIP_URI_ISURLESCAPE(scan, uend)) {
+		ret = (SIP_URI_ISDIGIT(scan[1]) ? (scan[1] - '0') :
+		    (tolower(scan[1]) - 'a' + 10)) * 16 +
+		    (SIP_URI_ISDIGIT(scan[2]) ? (scan[2] - '0') :
+		    (tolower(scan[2]) - 'a' + 10));
+	}
+	return (ret);
+}
+
+/*
+ * service-provider  = ";" provider-tag "=" provider-hostname
+ * provider-tag      = "tsp"
+ * provider-hostname = domain
+ */
+static int
+sip_uri_parse_tel_servicepro(char *scan, char *uend)
+{
+	char	*mark = (char *)0;
+
+	if (scan == uend)
+		return (0);
+
+	/*
+	 * parse domain=" "
+	 */
+	if (sip_uri_hexVal(scan, uend) == 0x20 && scan + 3 == uend)
+		return (1);
+	while (scan < uend) {
+		mark = scan;
+		while (scan < uend && (*scan == '-'|| SIP_URI_ISALNUM(*scan)))
+			++scan;
+		if ((scan < uend && *scan != '.') ||
+		    !SIP_URI_ISALPHA(*mark) || !SIP_URI_ISALNUM(*(scan - 1))) {
+			return (0);
+		}
+		if (scan < uend)
+			++scan;
+	}
+
+	if (scan < uend)
+		return (0);
+	return (1);
+}
+
+/*
+ * future-extension = ";" 1*(token-char) ["=" ((1*(token-char)
+ *                    ["?" 1*(token-char)]) / quoted-string )]
+ * token-char       = (%x21 / %x23-27 / %x2A-2B / %x2D-2E / %x30-39
+ *                     / %x41-5A / %x5E-7A / %x7C / %x7E)
+ */
+static int
+sip_uri_parse_tel_futureext(char *scan, char *uend)
+{
+	char	*mark;
+	int	uri_hexValue = 0;
+
+	if (scan == uend)
+		return (0);
+
+	/*
+	 * parse 1*(token-char)
+	 */
+	mark = scan;
+	while (scan < uend && sip_uri_isTokenchar(&scan, uend))
+		;
+	if (mark == scan ||
+	    (scan < uend && (*scan != '=' || scan + 1 == uend))) {
+		return (0);
+	}
+	if (scan == uend)
+		return (1);
+	++scan;
+
+	/*
+	 * parse 1*token-char ["?" 1*token-char]
+	 */
+	if (sip_uri_isTokenchar(&scan, uend)) {
+		while (sip_uri_isTokenchar(&scan, uend))
+			;
+		if (scan < uend) {
+			if (*scan != '?')
+				return (0);
+			++scan;
+			mark = scan;
+			while (sip_uri_isTokenchar(&scan, uend))
+				;
+			if (mark == scan)
+				return (0);
+		}
+	} else { /* parse quoted-string */
+		uri_hexValue = sip_uri_hexVal(scan, uend);
+		if (uri_hexValue != 0x22)
+			return (0);
+		scan += 3;
+		while (scan < uend && sip_uri_hexVal(scan, uend) != 0x22) {
+			/*
+			 * parse "\" CHAR
+			 */
+			if (sip_uri_hexVal(scan, uend) == 0x5c) {
+				scan += 3;
+				if (scan < uend) {
+					if (SIP_URI_ISUNRESERVED(*scan) ||
+					    SIP_URI_ISUSER(*scan)) {
+						++scan;
+					} else if (sip_uri_hexVal(scan, uend) >=
+					    0x00 &&
+					    sip_uri_hexVal(scan, uend) <=
+					    0x7f) {
+						scan += 3;
+					} else {
+						return (0);
+					}
+				} else {
+					return (0);
+				}
+			} else {
+				if (SIP_URI_ISUNRESERVED(*scan) ||
+				    SIP_URI_ISUSER(*scan)) {
+					++scan;
+				} else {
+					uri_hexValue =
+					    sip_uri_hexVal(scan, uend);
+					if ((uri_hexValue >= 0x20 &&
+						uri_hexValue <= 0x21) ||
+						(uri_hexValue >= 0x23 &&
+						uri_hexValue <= 0x7e) ||
+						(uri_hexValue >= 0x80 &&
+						uri_hexValue <= 0xff)) {
+						scan += 3;
+					} else {
+						return (0);
+					}
+				}
+			}
+		}
+		if (scan == uend ||
+		    (scan < uend && sip_uri_hexVal(scan, uend) != 0x22)) {
+			return (0);
+		}
+		scan += 3;
+	}
+
+	if (scan < uend)
+		return (0);
+	return (1);
+}
+
+/*
+ * Any characters allowed in RFC2806 tel URL that are not allowed in
+ * the user part of the SIP URI MUST be escaped.
+ * token-char = - _ . ! ~ * ' $ &  + DIGIT ALPHA #  % ^ ` |
+ */
+static int
+sip_uri_isTokenchar(char **pscan, char *uend)
+{
+	char	*scan = *pscan;
+	int	uri_hexValue = 0;
+
+	if (scan == uend)
+		return (0);
+
+	/*
+	 * for ALPAH DIGIT - _ . ! ~ * ' $ & +
+	 */
+	if ((SIP_URI_ISUNRESERVED(*scan) && *scan != '(' && *scan != ')') ||
+	    *scan == '$' || *scan == '&' || *scan == '+') {
+		++scan;
+		*pscan = scan;
+		return (1);
+	}
+
+	uri_hexValue = sip_uri_hexVal(scan, uend);
+	if (uri_hexValue == 0x21 || uri_hexValue == 0x7c ||
+	    uri_hexValue == 0x7e ||
+	    (uri_hexValue >= 0x23 && uri_hexValue <= 0x27) ||
+	    (uri_hexValue >= 0x2a && uri_hexValue <= 0x2b) ||
+	    (uri_hexValue >= 0x2d && uri_hexValue <= 0x2e) ||
+	    (uri_hexValue >= 0x30 && uri_hexValue <= 0x39) ||
+	    (uri_hexValue >= 0x41 && uri_hexValue <= 0x5a) ||
+	    (uri_hexValue >= 0x5e && uri_hexValue <= 0x7a)) {
+		scan += 3;
+		*pscan = scan;
+		return (1);
+	}
+	return (0);
+}
+
+/*
+ * '#' is not allowed in the telephone-subscriber part of SIP URI
+ * it must be escaped
+ */
+static int
+sip_uri_isEscapedPound(char **pscan, char *uend)
+{
+	char	*scan = *pscan;
+
+	if (scan == uend)
+		return (0);
+	if (*scan == '%' && scan + 2 < uend && scan[1] == '2' &&
+	    scan[2] == '3') {
+		scan += 2;
+		*pscan = scan;
+		return (1);
+	}
+	return (0);
+}
+
+/*
+ * scheme =  ALPHA *( ALPHA / DIGIT / "+" / "-" / "." )
+ */
+static int
+sip_uri_parse_scheme(_sip_uri_t *outurl, char *scan, char *uend)
+{
+	if (scan == uend) {
+		outurl->sip_uri_errflags |= SIP_URIERR_SCHEME;
+		return (0);
+	}
+	outurl->sip_uri_scheme.sip_str_ptr = scan;
+	outurl->sip_uri_scheme.sip_str_len = uend - scan;
+
+	if (scan < uend && SIP_URI_ISALPHA(*scan)) {
+		++scan;
+		while (scan < uend && SIP_URI_ISSCHEME(*scan))
+			++scan;
+	}
+	if (scan < uend)
+		outurl->sip_uri_errflags |= SIP_URIERR_SCHEME;
+	return (1);
+}
+
+/*
+ * The format of params is supposed to be;XXX;XXX;XXX
+ * uri-parameters	= *(";" uri-parameter)
+ * uri-parameter	= transport-param / user-param / method-param
+ * 			/ ttl-param / maddr-param / lr-param / other-param
+ * transport-param	=  "transport="
+ *			("udp" / "tcp" / "sctp" / "tls" / other-transport)
+ * other-transport		=  token
+ * user-param		=  "user=" ("phone" / "ip" / other-user)
+ * other-user		=  token
+ * method-param		=  "method=" Method
+ * ttl-param		=  "ttl=" ttl
+ * maddr-param		=  "maddr=" host
+ * lr-param		=  "lr"
+ * other-param		=  pname [ "=" pvalue ]
+ * pname		=  1*paramchar
+ * pvalue		=  1*paramchar
+ * paramchar		=  param-unreserved / unreserved / escaped
+ * param-unreserved	=  "[" / "]" / "/" / ":" / "&" / "+" / "$"
+ */
+static void
+sip_uri_parse_params(_sip_uri_t *outurl, char *scan, char *uend)
+{
+	char		*mark = (char *)0;
+	char		*equal = (char *)0;
+	int		i = 0;
+	int		ttl = 0;
+	int		paramleftlen = 0;
+	int		gothost = 0;
+	sip_param_t	*param = NULL;
+	sip_param_t	*new_param = NULL;
+
+	if (scan == uend || *scan != ';' || scan + 1 == uend) {
+		outurl->sip_uri_errflags |= SIP_URIERR_PARAM;
+		return;
+	}
+
+	while (scan < uend) {
+		mark = ++scan;
+		while (scan < uend && *scan != ';')
+			++scan;
+		if (scan == mark) {
+			outurl->sip_uri_errflags |= SIP_URIERR_PARAM;
+			return;
+		}
+
+		new_param = calloc(1, sizeof (sip_param_t));
+		if (new_param == NULL) {
+			outurl->sip_uri_errflags |= SIP_URIERR_MEMORY;
+			return;
+		}
+
+		if (param == NULL)
+			outurl->sip_uri_params = new_param;
+		else
+			param->param_next = new_param;
+
+		param = new_param;
+
+		param->param_name.sip_str_ptr = mark;
+		equal = memchr(mark, '=', scan - mark);
+		if (equal == (char *)0) {
+			param->param_name.sip_str_len = scan - mark;
+			param->param_value.sip_str_ptr = NULL;
+			param->param_value.sip_str_len = 0;
+			while (mark < scan && (SIP_URI_ISPARAM(*mark) ||
+			    SIP_URI_ISURLESCAPE(mark, scan))) {
+				++mark;
+			}
+		} else {
+			param->param_name.sip_str_len = equal - mark;
+			param->param_value.sip_str_ptr = equal + 1;
+			param->param_value.sip_str_len = scan - equal - 1;
+
+			if (mark == equal || equal + 1 == scan) {
+				outurl->sip_uri_errflags |= SIP_URIERR_PARAM;
+				return;
+			}
+			paramleftlen = equal - mark + 1;
+			if ((paramleftlen == 10 &&
+			    !sip_uri_url_casecmp(mark, "transport=", 10)) ||
+			    (paramleftlen == 5 &&
+			    !sip_uri_url_casecmp(mark, "user=", 5)) ||
+			    (paramleftlen == 7 &&
+			    !sip_uri_url_casecmp(mark, "method=", 7))) {
+				if (scan - equal == 1) {
+					outurl->sip_uri_errflags |=
+					    SIP_URIERR_PARAM;
+					return;
+				}
+				mark = equal + 1;
+				while (mark < scan && SIP_URI_ISTOKEN(*mark))
+					++mark;
+			} else if (paramleftlen == 4 &&
+			    !sip_uri_url_casecmp(mark, "ttl=", 4)) {
+				if (scan - equal == 1) {
+					outurl->sip_uri_errflags |=
+					    SIP_URIERR_PARAM;
+					return;
+				}
+				mark = equal;
+				for (i = 0; i < 3; ++i) {
+					++mark;
+					if (mark < scan &&
+					    SIP_URI_ISDIGIT(*mark)) {
+						ttl = ttl * 10 + (*mark - '0');
+					}
+					if (ttl > 255) {
+						outurl->sip_uri_errflags |=
+							SIP_URIERR_PARAM;
+						return;
+					}
+				}
+			} else if (paramleftlen == 6 &&
+			    !sip_uri_url_casecmp(mark, "maddr=", 6)) {
+				gothost = 0;
+				mark = equal + 1;
+				if (mark < scan && SIP_URI_ISDIGIT(*mark)) {
+					gothost = sip_uri_parse_ipv4(mark,
+					    scan);
+				}
+				/*
+				 * not valid syntax for a host or user name,
+				 * try IPv6 literal
+				 */
+				if (!gothost && mark < scan && *mark == '[') {
+					gothost = sip_uri_parse_ipv6(mark,
+					    scan);
+				}
+				/*
+				 * look for a valid host name:
+				 * *(domainlabel ".") toplabel ["."]
+				 */
+				if (!gothost && mark < scan) {
+					if (!(gothost =
+					    sip_uri_parse_hostname(mark,
+					    scan))) {
+						outurl->sip_uri_errflags |=
+							SIP_URIERR_PARAM;
+					}
+				}
+				if (gothost)
+					mark = scan;
+			} else if (paramleftlen == 3 &&
+			    !sip_uri_url_casecmp(mark, "lr=", 3)) {
+				outurl->sip_uri_errflags |= SIP_URIERR_PARAM;
+				return;
+			} else {
+				while (mark < scan && (SIP_URI_ISPARAM(*mark) ||
+				    SIP_URI_ISURLESCAPE(mark, scan) ||
+				    mark == equal)) {
+					++mark;
+				}
+			}
+		}
+		if (mark < scan) {
+			outurl->sip_uri_errflags |= SIP_URIERR_PARAM;
+			return;
+		}
+	}
+}
+
+/*
+ * The format of headers is supposed to be ?XXX&XXX&XXX
+ * headers         =  "?" header *("&" header
+ * header          =  hname "=" hvalue
+ * hname           =  1*(hnv-unreserved / unreserved / escaped
+ * hvalue          =  *(hnv-unreserved / unreserved / escaped
+ * hnv-unreserved  =  "[" / "]" / "/" / "?" / ":" / "+" / "$"
+ */
+static void
+sip_uri_parse_headers(_sip_uri_t *outurl, char *scan, char *uend)
+{
+	char	*mark = NULL;
+	char	*equal = NULL;
+
+	if (scan == uend || *scan != '?' || scan + 1 == uend) {
+		outurl->sip_uri_errflags |= SIP_URIERR_HEADER;
+		return;
+	}
+	outurl->sip_uri_headers.sip_str_ptr = scan + 1;
+	outurl->sip_uri_headers.sip_str_len = uend - (scan + 1);
+
+	while (scan < uend) {
+		mark = ++scan;
+		while (scan < uend && *scan != '&')
+			++scan;
+		if (scan == mark) {
+			outurl->sip_uri_errflags |= SIP_URIERR_HEADER;
+			return;
+		}
+		equal = memchr(mark, '=', scan - mark);
+		if (equal == mark || equal == (char *)0) {
+			outurl->sip_uri_errflags |= SIP_URIERR_HEADER;
+			return;
+		}
+		while (mark < scan &&
+		    (SIP_URI_ISHEADER(*mark) ||
+		    SIP_URI_ISURLESCAPE(mark, scan) || mark == equal)) {
+			++mark;
+		}
+		if (mark < scan) {
+			outurl->sip_uri_errflags |= SIP_URIERR_HEADER;
+			return;
+		}
+	}
+}
+
+/*
+ * opaque-part   =  uric-no-slash *uric
+ * uric          =  reserved / unreserved / escaped
+ * uric-no-slash =  unreserved / escaped / ";" / "?" / ":" / "@"
+ *                  / "&" / "=" / "+" / "$" / ","
+ */
+static void
+sip_uri_parse_abs_opaque(_sip_uri_t *outurl, char *scan, char *uend)
+{
+	if (scan == uend) {
+		outurl->sip_uri_errflags |= SIP_URIERR_OPAQUE;
+		return;
+	}
+	outurl->sip_uri_opaque.sip_str_ptr = scan;
+	outurl->sip_uri_opaque.sip_str_len = uend - scan;
+
+	if (SIP_URI_ISUNRESERVED(*scan) || SIP_URI_ISURLESCAPE(scan, uend) ||
+	    SIP_URI_ISOTHER(*scan) || *scan == ';' || *scan == '?' ||
+	    *scan == ':' || *scan == '@' || *scan == '&') {
+		++scan;
+	} else {
+		outurl->sip_uri_errflags |= SIP_URIERR_OPAQUE;
+		return;
+	}
+	while (scan < uend && (SIP_URI_ISRESERVED(*scan) ||
+	    SIP_URI_ISUNRESERVED(*scan) || SIP_URI_ISURLESCAPE(scan, uend))) {
+		++scan;
+	}
+	if (scan < uend)
+		outurl->sip_uri_errflags |= SIP_URIERR_OPAQUE;
+}
+
+/*
+ * format of query is supposed to be ?XXX
+ * query =  *uric
+ * uric  =  reserved / unreserved / escaped
+ */
+static void
+sip_uri_parse_abs_query(_sip_uri_t *outurl, char *scan, char *uend)
+{
+	if (uend == scan || *scan != '?' || scan + 1 == uend)
+		return;
+	++scan;
+	outurl->sip_uri_query.sip_str_ptr = scan;
+	outurl->sip_uri_query.sip_str_len = uend - scan;
+
+	while (scan < uend && (SIP_URI_ISRESERVED(*scan) ||
+	    SIP_URI_ISUNRESERVED(*scan) || SIP_URI_ISURLESCAPE(scan, uend))) {
+		++scan;
+	}
+	if (scan < uend)
+		outurl->sip_uri_errflags |= SIP_URIERR_QUERY;
+}
+
+/*
+ * the format of path is supposed to be /XXX;XXX/XXX;
+ * abs-path       =  "/" path-segments
+ * path-segments  =  segment *( "/" segment )
+ * segment        =  *pchar *( ";" param )
+ * param          =  *pchar
+ * pchar          =  unreserved / escaped /
+ *                   ":" / "@" / "&" / "=" / "+" / "$" / ","
+ */
+static void
+sip_uri_parse_abs_path(_sip_uri_t *outurl, char *scan, char *uend)
+{
+	if (scan == uend || *scan != '/')
+		return;
+	outurl->sip_uri_path.sip_str_ptr = scan;
+	outurl->sip_uri_path.sip_str_len = uend - scan;
+
+	++scan;
+	while (scan < uend && (SIP_URI_ISPCHAR(*scan) ||
+	    SIP_URI_ISUNRESERVED(*scan) || SIP_URI_ISURLESCAPE(scan, uend) ||
+	    *scan == '/' || *scan == ';')) {
+		++scan;
+	}
+	if (scan < uend)
+		outurl->sip_uri_errflags |= SIP_URIERR_PATH;
+}
+/*
+ * reg-name =  1*( unreserved / escaped / "$" / "," / ";"
+ *             / ":" / "@" / "&" / "=" / "+" )
+ */
+static void
+sip_uri_parse_abs_regname(_sip_uri_t *outurl, char *scan, char *uend)
+{
+	if (scan == uend)
+		return;
+	outurl->sip_uri_regname.sip_str_ptr = scan;
+	outurl->sip_uri_regname.sip_str_len = uend - scan;
+
+	while (scan < uend && (SIP_URI_ISUNRESERVED(*scan) ||
+	    SIP_URI_ISURLESCAPE(scan, uend) || SIP_URI_ISREGNAME(*scan))) {
+		++scan;
+	}
+	if (scan < uend)
+		outurl->sip_uri_errflags |= SIP_URIERR_REGNAME;
+}
+
+/*
+ * The format of the password is supposed to be :XXX
+ * password =  *( unreserved / escaped / "&" / "=" / "+" / "$" / "," )
+ */
+static void
+sip_uri_parse_password(_sip_uri_t *outurl, char *scan, char *uend)
+{
+	if (scan == uend || *scan != ':' || scan + 1 == uend)
+		return;
+	++scan;
+	outurl->sip_uri_password.sip_str_ptr = scan;
+	outurl->sip_uri_password.sip_str_len = uend - scan;
+
+	while (scan < uend && (SIP_URI_ISUNRESERVED(*scan) ||
+	    SIP_URI_ISURLESCAPE(scan, uend) || SIP_URI_ISOTHER(*scan) ||
+	    *scan == '&')) {
+		++scan;
+	}
+	if (scan < uend)
+		outurl->sip_uri_errflags |= SIP_URIERR_PASS;
+}
+
+/*
+ * user =  1*( unreserved / escaped / user-unreserved )
+ * user-unreserved  =  "&" / "=" / "+" / "$" / "," / ";" / "?" / "/"
+ */
+static void
+sip_uri_parse_user(_sip_uri_t *outurl, char *scan, char *uend)
+{
+	if (scan == uend) {
+		outurl->sip_uri_errflags |= SIP_URIERR_USER;
+		return;
+	}
+	outurl->sip_uri_user.sip_str_ptr = scan;
+	outurl->sip_uri_user.sip_str_len = uend - scan;
+
+	if (sip_uri_parse_tel(scan, uend)) {
+		outurl->sip_uri_isteluser = B_TRUE;
+	} else {
+		while (scan < uend && (SIP_URI_ISUNRESERVED(*scan) ||
+		    SIP_URI_ISURLESCAPE(scan, uend) || SIP_URI_ISUSER(*scan))) {
+			++scan;
+		}
+		if (scan < uend)
+			outurl->sip_uri_errflags |= SIP_URIERR_USER;
+	}
+}
+
+/*
+ * the format of port is supposed to be :XXX
+ * port =  1*DIGIT
+ */
+static void
+sip_uri_parse_port(_sip_uri_t *outurl, char *scan, char *uend)
+{
+	if (scan == uend || *scan != ':' || scan + 1 == uend) {
+		outurl->sip_uri_errflags |= SIP_URIERR_PORT;
+		return;
+	}
+	++scan;
+	/*
+	 * parse numeric port number
+	 */
+	if (SIP_URI_ISDIGIT(*scan)) {
+		outurl->sip_uri_port = *scan - '0';
+		while (++scan < uend && SIP_URI_ISDIGIT(*scan)) {
+		    outurl->sip_uri_port =
+			outurl->sip_uri_port * 10 + (*scan - '0');
+			if (outurl->sip_uri_port > 0xffff) {
+				outurl->sip_uri_errflags |= SIP_URIERR_PORT;
+				outurl->sip_uri_port = 0;
+				break;
+			}
+		}
+	}
+	if (scan < uend) {
+		outurl->sip_uri_errflags |= SIP_URIERR_PORT;
+		outurl->sip_uri_port = 0;
+	}
+}
+
+/*
+ * parse an IPv4 address
+ *    1*3DIGIT "." 1*3DIGIT "." 1*3DIGIT "." 1*3DIGIT
+ *  advances pscan to end of IPv4 address, or after last "." that was
+ *  a valid IPv4 or domain name.
+ * returns 1 if ipv4 found, 0 otherwise
+ */
+static int
+sip_uri_parse_ipv4(char *scan, char *uend)
+{
+	int	j = 0;
+	int	val = 0;
+
+	for (j = 0; j < 4; ++j) {
+		if (!SIP_URI_ISDIGIT(*scan))
+			break;
+		val = *scan - '0';
+		while (++scan < uend && SIP_URI_ISDIGIT(*scan)) {
+			val = val * 10 + (*scan - '0');
+			if (val > 255)
+				return (0);
+		}
+		if (j < 3) {
+			if (*scan != '.')
+				break;
+			++scan;
+		}
+	}
+
+	if (j == 4 && scan == uend)
+		return (1);
+
+	return (0);
+}
+
+/*
+ * parse an IPv6 address
+ *  IPv6address = hexpart [ ":" IPv4address ]
+ *  IPv4address = 1*3DIGIT "." 1*3DIGIT "." 1*3DIGIT "." 1*3DIGIT
+ *  hexpart = hexseq | hexseq "::" [ hexseq ] | "::" [ hexseq ]
+ *  hexseq  = hex4 *( ":" hex4)
+ *  hex4    = 1*4HEXDIG
+ *  if not found, leaves pscan unchanged, otherwise advances to end
+ *  returns 1 if valid,
+ *  0 if invalid
+ */
+static int
+sip_uri_parse_ipv6(char *scan, char *uend)
+{
+	char		*mark;
+	unsigned	j = 0;			/* index for addr */
+	unsigned	val = 0;		/* hex value */
+	int		zpad = 0;		/* index of :: delimiter */
+
+	if (*scan != '[')
+		return (0);
+	++scan;
+	j = 0;
+
+	/*
+	 * check for leading "::", set zpad to the position of the "::"
+	 */
+	if (scan + 1 < uend && scan[0] == ':' && scan[1] == ':') {
+		zpad = 0;
+		scan += 2;
+	} else {
+		zpad = -1;
+	}
+
+	/*
+	 * loop through up to 16 bytes of IPv6 address
+	 */
+	while (scan < uend && j < 15) {
+		if (!SIP_URI_ISHEX(*scan))
+			break;
+		mark = scan;
+		val = SIP_URI_HEXVAL(*scan);
+		while (++scan < uend && SIP_URI_ISHEX(*scan)) {
+			val = val * 16 + SIP_URI_HEXVAL(*scan);
+			if (val > 0xffff)
+				return (0);
+		}
+
+		/*
+		 * always require a delimiter or ]
+		 */
+		if (scan == uend)
+			return (0);
+
+		if (*scan == '.' && (j == 12 || (zpad != -1 && j < 12)) &&
+		    mark < uend && sip_uri_parse_ipv4(mark, uend - 1) &&
+		    *(uend - 1) == ']') {
+			mark = uend - 1;
+			j += 4;
+			scan = mark + 1;
+			break;
+		}
+
+		/*
+		 * set address
+		 */
+		j += 2;
+
+		/*
+		 * check for delimiter or ]
+		 */
+		if (*scan == ':') {
+			/*
+			 * found ":" delimiter, check for "::"
+			 */
+			if (++scan < uend && *scan == ':') {
+				if (zpad != -1)
+					return (0);
+				zpad = j;
+				if (++scan < uend && *scan == ']') {
+					++scan;
+					break;
+				}
+			}
+		} else if (*scan == ']' && (j == 16 || zpad != -1)) {
+			++scan;
+			break;
+		} else {
+			/*
+			 * not a valid delimiter
+			 */
+			return (0);
+		}
+	}
+	if (zpad == -1 && j < 16)
+		return (0);
+	if (zpad != -1) {
+		if (j > 15)
+			return (0);
+	}
+
+	if (scan == uend)
+		return (1);
+
+	return (0);
+}
+
+/*
+ * hostname         =  *( domainlabel "." ) toplabel [ "." ]
+ * domainlabel      =  alphanum / alphanum *( alphanum / "-" ) alphanum
+ * toplabel         =  ALPHA / ALPHA *( alphanum / "-" ) alphanum
+ */
+static int
+sip_uri_parse_hostname(char *scan, char *uend)
+{
+	int	sawalpha = 0;
+
+	if (scan < uend && SIP_URI_ISALNUM(*scan)) {
+		do {
+			sawalpha = SIP_URI_ISALPHA(*scan);
+			while (SIP_URI_ISHOST(*scan))
+				++scan;
+			if (*scan != '.')
+				break;
+			++scan;
+		} while (scan < uend && SIP_URI_ISALNUM(*scan));
+	}
+
+	if (sawalpha && scan == uend)
+		return (1);
+	return (0);
+}
+
+
+/*
+ * parse the network path portion of a full URL
+ */
+static void
+sip_uri_parse_netpath(_sip_uri_t *outurl, char **pscan, char *uend,
+    boolean_t issip)
+{
+	char	*mark = (char *)0;
+	char	*mark2 = (char *)0;
+	char	*scan = *pscan;
+	int	gothost = 0;
+
+	/*
+	 * look for the first high-level delimiter
+	 */
+	mark = scan;
+	while (scan < uend && *scan != '@')
+		++scan;
+	/*
+	 * handle userinfo section of URL
+	 */
+	if (scan < uend && *scan == '@') {
+		/*
+		 * parse user
+		 */
+		mark2 = mark;
+		while (mark < scan && *mark != ':')
+			++mark;
+		sip_uri_parse_user(outurl, mark2, mark);
+		/*
+		 * parse password
+		 */
+		if (*mark == ':')
+			sip_uri_parse_password(outurl, mark, scan);
+		mark = ++scan;
+	}
+
+	scan = mark;
+	if (scan < uend && *scan == '[') {	/* look for an IPv6 address */
+		while (scan < uend && *scan != ']')
+			++scan;
+		if (scan < uend) {
+			++scan;
+			if (sip_uri_parse_ipv6(mark, scan))
+				gothost = 1;
+		}
+	} else {
+		while (scan < uend && ((issip && !SIP_URI_ISSIPHDELIM(*scan)) ||
+		    (!issip && !SIP_URI_ISABSHDELIM(*scan)))) {
+			++scan;
+		}
+
+		/*
+		 * look for an IPv4 address
+		 */
+		if (mark < scan && SIP_URI_ISDIGIT(*mark) &&
+		    sip_uri_parse_ipv4(mark, scan)) {
+			gothost = 1;
+		}
+
+		/*
+		 * look for a valid host name
+		 */
+		if (!gothost && mark < scan &&
+		    sip_uri_parse_hostname(mark, scan)) {
+			gothost = 1;
+		}
+	}
+	/*
+	 * handle invalid host name
+	 */
+	if (!gothost)
+		outurl->sip_uri_errflags |= SIP_URIERR_HOST;
+	/*
+	 * save host name
+	 */
+	outurl->sip_uri_host.sip_str_ptr = mark;
+	outurl->sip_uri_host.sip_str_len = scan - mark;
+
+	mark = scan;
+	/*
+	 * parse the port number
+	 */
+	if (scan < uend && *scan == ':') {
+		while (scan < uend && ((issip && !SIP_URI_ISSIPDELIM(*scan)) ||
+		    (!issip && !SIP_URI_ISABSDELIM(*scan)))) {
+			++scan;
+		}
+		sip_uri_parse_port(outurl, mark, scan);
+	}
+
+	/*
+	 * set return pointer
+	 */
+	*pscan = scan;
+}
+
+/*
+ * parse a URL
+ * URL = SIP-URI / SIPS-URI / absoluteURI
+ */
+void
+sip_uri_parse_it(_sip_uri_t *outurl, sip_str_t *uri_str)
+{
+	char 		*mark;
+	char		*scan;
+	char		*uend;
+	char		*str = uri_str->sip_str_ptr;
+	unsigned	urlen = uri_str->sip_str_len;
+
+	/*
+	 * reset output parameters
+	 */
+	(void) memset(outurl, 0, sizeof (sip_uri_t));
+
+	/*
+	 * strip enclosing angle brackets
+	 */
+	if (urlen > 1 && str[0] == '<' && str[urlen-1] == '>') {
+		urlen -= 2;
+		++str;
+	}
+	uend = str + urlen;
+
+	/*
+	 * strip off space prefix and trailing spaces
+	 */
+	while (str < uend && isspace(*str)) {
+		++str;
+		--urlen;
+	}
+	while (str < uend && isspace(*(uend - 1))) {
+		--uend;
+		--urlen;
+	}
+
+	/*
+	 * strip off "URL:" prefix
+	 */
+	if (urlen > 4 && sip_uri_url_casecmp(str, "URL:", 4) == 0) {
+		str += 4;
+		urlen -= 4;
+	}
+
+	/*
+	 * parse the scheme name
+	 */
+	mark = scan = str;
+	while (scan < uend && *scan != ':')
+		++scan;
+	if (scan == uend || !sip_uri_parse_scheme(outurl, mark, scan)) {
+		outurl->sip_uri_errflags |= SIP_URIERR_SCHEME;
+		return;
+	}
+
+	if ((outurl->sip_uri_scheme.sip_str_len == SIP_SCHEME_LEN &&
+	    !memcmp(outurl->sip_uri_scheme.sip_str_ptr, SIP_SCHEME,
+	    SIP_SCHEME_LEN)) ||
+	    (outurl->sip_uri_scheme.sip_str_len == SIPS_SCHEME_LEN &&
+	    !memcmp(outurl->sip_uri_scheme.sip_str_ptr, SIPS_SCHEME,
+	    SIPS_SCHEME_LEN))) {
+		outurl->sip_uri_issip = B_TRUE;
+	} else {
+		outurl->sip_uri_issip = B_FALSE;
+	}
+	++scan; /* skip ':' */
+
+	if (outurl->sip_uri_issip) {
+		/*
+		 * parse SIP URL
+		 */
+		sip_uri_parse_netpath(outurl, &scan, uend, B_TRUE);
+
+		/*
+		 * parse parameters
+		 */
+		if (scan < uend && *scan == ';') {
+			mark = scan;
+			while (scan < uend && *scan != '?')
+				++scan;
+			sip_uri_parse_params(outurl, mark, scan);
+		}
+
+		/*
+		 * parse headers
+		 */
+		if (scan < uend && *scan == '?')
+			sip_uri_parse_headers(outurl, scan, uend);
+	} else if (scan < uend && scan[0] == '/') {	 /* parse absoluteURL */
+		++scan;
+		/*
+		 * parse authority
+		 * authority	= srvr / reg-name
+		 * srvr		= [ [ userinfo "@" ] hostport ]
+		 * reg-name	= 1*(unreserved / escaped / "$" / ","
+		 *			/ ";" / ":" / "@" / "&" / "=" / "+")
+		 */
+		if (scan < uend && *scan == '/') {
+			++scan;
+			mark = scan;
+			/*
+			 * take authority as srvr
+			 */
+			sip_uri_parse_netpath(outurl, &scan, uend, B_FALSE);
+
+			/*
+			 * if srvr failed, take it as reg-name
+			 * parse reg-name
+			 */
+			if (outurl->sip_uri_errflags & SIP_URIERR_USER ||
+			    outurl->sip_uri_errflags & SIP_URIERR_PASS ||
+			    outurl->sip_uri_errflags & SIP_URIERR_HOST ||
+			    outurl->sip_uri_errflags & SIP_URIERR_PORT) {
+				scan = mark;
+				while (scan < uend && *scan != '/' &&
+					*scan != '?') {
+					++scan;
+				}
+				sip_uri_parse_abs_regname(outurl, mark, scan);
+				if (!(outurl->sip_uri_errflags &
+				    SIP_URIERR_REGNAME)) {
+					/*
+					 * remove error info of user,
+					 * password, host, port
+					 */
+					outurl->sip_uri_user.sip_str_ptr = NULL;
+					outurl->sip_uri_user.sip_str_len = 0;
+					outurl->sip_uri_errflags &=
+					    ~SIP_URIERR_USER;
+					outurl->sip_uri_password.sip_str_ptr =
+					    NULL;
+					outurl->sip_uri_password.sip_str_len =
+					    0;
+					outurl->sip_uri_errflags &=
+					    ~SIP_URIERR_PASS;
+					outurl->sip_uri_host.sip_str_ptr = NULL;
+					outurl->sip_uri_host.sip_str_len = 0;
+					outurl->sip_uri_errflags &=
+					    ~SIP_URIERR_HOST;
+					outurl->sip_uri_port = 0;
+					outurl->sip_uri_errflags &=
+					    ~SIP_URIERR_PORT;
+				}
+			}
+		} else {
+			/*
+			 * there is no net-path
+			 */
+			--scan;
+		}
+		/*
+		 * parse abs-path
+		 */
+		if (scan < uend && *scan == '/') {
+			mark = scan;
+			while (scan < uend && *scan != '?')
+				++scan;
+			sip_uri_parse_abs_path(outurl, mark, scan);
+		}
+
+		/*
+		 * parse query
+		 */
+		if (scan < uend && *scan == '?')
+			sip_uri_parse_abs_query(outurl, scan, uend);
+	} else {
+		/*
+		 * parse opaque-part
+		 */
+		sip_uri_parse_abs_opaque(outurl, scan, uend);
+	}
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/lib/libsip/common/sip_parse_uri.h	Sat Oct 07 14:26:26 2006 -0700
@@ -0,0 +1,117 @@
+/*
+ * 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.
+ */
+
+#ifndef	_SIP_PARSE_URI_H
+#define	_SIP_PARSE_URI_H
+
+#pragma ident	"%Z%%M%	%I%	%E% SMI"
+
+#ifdef	__cplusplus
+extern "C" {
+#endif
+
+#include <sys/types.h>
+#include <sip.h>
+
+#define	SIP_URI_BUF_SIZE	128
+
+#define	SIP_SCHEME		"sip"
+#define	SIPS_SCHEME		"sips"
+
+#define	SIP_SCHEME_LEN		3
+#define	SIPS_SCHEME_LEN		4
+
+/*
+ * SIP-URI = "sip:" [ userinfo ] hostport
+ *           uri-parameters [ headers ]
+ * SIPS-URI =  "sips:" [ userinfo ] hostport
+ *           uri-parameters [ headers ]
+ * uri-parameters = *( ";" uri-parameter)
+ * uri-parameter = transport-param / user-param / method-param
+ *                 / ttl-param / maddr-param / lr-param / other-param
+ * transport-param   =  "transport="
+ *                      "udp" / "tcp" / "sctp" / "tls"/ other-transport)
+ * other-transport   =  token
+ * headers  =  "?" header *( "&" header )
+ */
+typedef struct sip_uri_sip_s {
+	sip_param_t 	*sip_params;
+	sip_str_t 	sip_headers;
+} sip_uri_sip_t;
+
+/*
+ * opaque	uri opaque part
+ * query	uri query
+ * path		uri path
+ * regname	uri reg-name
+ */
+typedef struct sip_uri_abs_s {
+	sip_str_t	sip_uri_opaque;
+	sip_str_t 	sip_uri_query;
+	sip_str_t 	sip_uri_path;
+	sip_str_t 	sip_uri_regname;
+} sip_uri_abs_t;
+
+/*
+ * structure for a parsed URI
+ *   sip_uri_scheme		URI scheme
+ *   sip_uri_user		user name
+ *   sip_uri_password		password for the user
+ *   sip_uri_host		host name
+ *   sip_uri_port		port number for the host (0 = none specified)
+ *   sip_uri_errflags		error flags
+ *   sip_uri_issip		is this a SIP  URI.
+ *   sip_uri_isteluser		user is a telephone-subscriber
+ */
+typedef struct sip_uri {
+	sip_str_t	sip_uri_scheme;
+	sip_str_t 	sip_uri_user;
+	sip_str_t	sip_uri_password;
+	sip_str_t	sip_uri_host;
+	uint_t		sip_uri_port;
+	uint_t		sip_uri_errflags;
+	boolean_t	sip_uri_issip;
+	boolean_t	sip_uri_isteluser;
+	union {
+		sip_uri_sip_t	sip_sipuri;	/* SIP URI */
+		sip_uri_abs_t	sip_absuri;	/* Absolute URI */
+	} specific;
+}_sip_uri_t;
+
+#define	sip_uri_params		specific.sip_sipuri.sip_params
+#define	sip_uri_headers		specific.sip_sipuri.sip_headers
+#define	sip_uri_opaque		specific.sip_absuri.sip_uri_opaque
+#define	sip_uri_query		specific.sip_absuri.sip_uri_query
+#define	sip_uri_path		specific.sip_absuri.sip_uri_path
+#define	sip_uri_regname		specific.sip_absuri.sip_uri_regname
+
+extern void	sip_uri_parse_it(_sip_uri_t *, sip_str_t *);
+
+#ifdef	__cplusplus
+}
+#endif
+
+#endif	/* _SIP_PARSE_URI_H */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/lib/libsip/common/sip_reass.c	Sat Oct 07 14:26:26 2006 -0700
@@ -0,0 +1,243 @@
+/*
+ * 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 <sys/socket.h>
+#include <netinet/in.h>
+#include "sip_miscdefs.h"
+
+/*
+ * Local version of case insensitive strstr().
+ */
+static char *
+sip_reass_strstr(const char *as1, const char *as2)
+{
+	const char	*s1;
+	const char	*s2;
+	const char	*tptr;
+	char	c;
+
+	s1 = as1;
+	s2 = as2;
+
+	if (s2 == NULL || *s2 == '\0')
+		return ((char *)s1);
+	c = *s2;
+
+	while (*s1)
+		if (tolower(*s1++) == c) {
+			tptr = s1;
+			while ((c = *++s2) == tolower(*s1++) && c)
+				;
+			if (c == 0)
+				return ((char *)tptr - 1);
+			s1 = tptr;
+			s2 = as2;
+			c = *s2;
+		}
+
+	return (NULL);
+}
+
+/*
+ * Get the value in the content-length field and add it to the header length
+ * and return the total length. returns -1 if the length cannot be determined
+ * or if the message does not contain the entire message.
+ */
+static int
+sip_get_msglen(char *p, size_t msglen)
+{
+	int	value = 0;
+	int 	hlen;
+	char	*c;
+	char	*e;
+	int	base = 10;
+	char	*edge;
+	int	digits = 0;
+
+	edge = p + msglen;
+	if ((c = sip_reass_strstr(p, "content-length")) == NULL)
+		return (-1);
+	hlen = c - p;
+	if ((hlen +  strlen("content-length")) >= msglen)
+		return (-1);
+	c += strlen("content-length");
+	e = c + 1;
+	while (*e == ' ' || *e == ':') {
+		e++;
+		if (e == edge)
+			return (-1);
+	}
+	while (*e  != '\r' && *e != ' ') {
+		if (e == edge)
+			return (-1);
+		if (*e >= '0' && *e <= '9')
+			digits = *e - '0';
+		else
+			return (-1);
+		value = (value * base) + digits;
+		e++;
+	}
+	while (*e != '\r') {
+		e++;
+		if (e == edge)
+			return (-1);
+	}
+	hlen = e - p + 4;	/* 4 for 2 CRLFs ?? */
+	value += hlen;
+
+	return (value);
+}
+
+/*
+ * We have determined that msg does not contain a *single* complete message.
+ * Add it to the reassembly list and check if we have a complete message.
+ * a NULL 'msg' means we are just checking if there are more complete
+ * messages in the list that can be passed up.
+ */
+char *
+sip_get_tcp_msg(sip_conn_object_t obj, char *msg, size_t *msglen)
+{
+	int			value;
+	sip_conn_obj_pvt_t	*pvt_data;
+	sip_reass_entry_t	*reass;
+	void			**obj_val;
+	char			*msgbuf = NULL;
+	int			splitlen;
+	char			*splitbuf;
+
+	if (msg != NULL) {
+		assert(*msglen > 0);
+		msgbuf = (char *)malloc(*msglen + 1);
+		if (msgbuf == NULL)
+			return (NULL);
+		(void) strncpy(msgbuf, msg, *msglen);
+		msgbuf[*msglen] = '\0';
+		msg = msgbuf;
+	}
+	obj_val = (void *)obj;
+	pvt_data = (sip_conn_obj_pvt_t *)*obj_val;
+	/*
+	 * connection object not initialized
+	 */
+	if (pvt_data == NULL) {
+		if (msg == NULL)
+			return (NULL);
+		value = sip_get_msglen(msg, *msglen);
+		if (value == *msglen) {
+			return (msg);
+		} else {
+			if (msgbuf != NULL)
+				free(msgbuf);
+			return (NULL);
+		}
+	}
+	(void) pthread_mutex_lock(&pvt_data->sip_conn_obj_reass_lock);
+	reass = pvt_data->sip_conn_obj_reass;
+	assert(reass != NULL);
+	if (reass->sip_reass_msg == NULL) {
+		assert(reass->sip_reass_msglen == 0);
+		if (msg == NULL) {
+			(void) pthread_mutex_unlock(
+			    &pvt_data->sip_conn_obj_reass_lock);
+			return (NULL);
+		}
+		value = sip_get_msglen(msg, *msglen);
+		if (value == *msglen) {
+			(void) pthread_mutex_unlock(
+			    &pvt_data->sip_conn_obj_reass_lock);
+			return (msg);
+		}
+		reass->sip_reass_msg = msg;
+		reass->sip_reass_msglen = *msglen;
+		if (value != -1 && value < reass->sip_reass_msglen)
+			goto tryone;
+		(void) pthread_mutex_unlock(&pvt_data->sip_conn_obj_reass_lock);
+		return (NULL);
+	} else if (msg != NULL) {
+		/*
+		 * Resize, not optimal
+		 */
+		int	newlen = reass->sip_reass_msglen + *msglen;
+		char	*newmsg;
+
+		assert(strlen(reass->sip_reass_msg) == reass->sip_reass_msglen);
+		newmsg = malloc(newlen + 1);
+		if (newmsg == NULL) {
+			(void) pthread_mutex_unlock(
+			    &pvt_data->sip_conn_obj_reass_lock);
+			if (msgbuf != NULL)
+				free(msgbuf);
+			return (NULL);
+		}
+		(void) strncpy(newmsg, reass->sip_reass_msg,
+		    reass->sip_reass_msglen);
+		newmsg[reass->sip_reass_msglen] = '\0';
+		(void) strncat(newmsg, msg, *msglen);
+		newmsg[newlen] = '\0';
+		assert(strlen(newmsg) == newlen);
+		reass->sip_reass_msglen = newlen;
+		free(msg);
+		free(reass->sip_reass_msg);
+		reass->sip_reass_msg = newmsg;
+	}
+	value = sip_get_msglen(reass->sip_reass_msg, reass->sip_reass_msglen);
+	if (value == -1 || value >  reass->sip_reass_msglen) {
+		(void) pthread_mutex_unlock(&pvt_data->sip_conn_obj_reass_lock);
+		return (NULL);
+	}
+tryone:
+	if (value == reass->sip_reass_msglen) {
+		msg = reass->sip_reass_msg;
+		*msglen = reass->sip_reass_msglen;
+		reass->sip_reass_msg = NULL;
+		reass->sip_reass_msglen = 0;
+		(void) pthread_mutex_unlock(&pvt_data->sip_conn_obj_reass_lock);
+		return (msg);
+	}
+	splitlen = reass->sip_reass_msglen - value;
+	msg = (char *)malloc(value + 1);
+	splitbuf = (char *)malloc(splitlen + 1);
+	if (msg == NULL || splitbuf == NULL) {
+		if (msg != NULL)
+			free(msg);
+		if (splitbuf != NULL)
+			free(splitbuf);
+		(void) pthread_mutex_unlock(&pvt_data->sip_conn_obj_reass_lock);
+		return (NULL);
+	}
+	(void) strncpy(msg, reass->sip_reass_msg, value);
+	msg[value] = '\0';
+	(void) strncpy(splitbuf, reass->sip_reass_msg + value, splitlen);
+	splitbuf[splitlen] = '\0';
+	free(reass->sip_reass_msg);
+	reass->sip_reass_msg = splitbuf;
+	reass->sip_reass_msglen = splitlen;
+	(void) pthread_mutex_unlock(&pvt_data->sip_conn_obj_reass_lock);
+	*msglen = value;
+	return (msg);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/lib/libsip/common/sip_timeout.c	Sat Oct 07 14:26:26 2006 -0700
@@ -0,0 +1,364 @@
+/*
+ * 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"
+
+/*
+ * Simple implementation of timeout functionality. The granuality is a sec
+ */
+#include <stdio.h>
+#include <pthread.h>
+#include <sys/errno.h>
+#include <stdlib.h>
+
+#include "sip_miscdefs.h"
+
+uint_t		sip_timeout(void *arg, void (*callback_func)(void *),
+		    struct timeval *timeout_time);
+boolean_t	sip_untimeout(uint_t);
+
+typedef struct timeout {
+	struct timeout *sip_timeout_next;
+	hrtime_t sip_timeout_val;
+	void (*sip_timeout_callback_func)(void *);
+	void *sip_timeout_callback_func_arg;
+	int   sip_timeout_id;
+} sip_timeout_t;
+
+static pthread_mutex_t timeout_mutex = PTHREAD_MUTEX_INITIALIZER;
+static pthread_cond_t  timeout_cond_var = PTHREAD_COND_INITIALIZER;
+static sip_timeout_t *timeout_list;
+static sip_timeout_t *timeout_current_start;
+static sip_timeout_t *timeout_current_end;
+
+/*
+ * LONG_SLEEP_TIME = (24 * 60 * 60 * NANOSEC)
+ */
+#define	LONG_SLEEP_TIME	(0x15180LL * 0x3B9ACA00LL)
+
+uint_t timer_id = 0;
+
+/*
+ * Invoke the callback function
+ */
+/* ARGSUSED */
+static void *
+sip_run_to_functions(void *arg)
+{
+	sip_timeout_t *timeout = NULL;
+
+	(void) pthread_mutex_lock(&timeout_mutex);
+	while (timeout_current_start != NULL) {
+		timeout = timeout_current_start;
+		if (timeout_current_end == timeout_current_start)
+			timeout_current_start = timeout_current_end = NULL;
+		else
+			timeout_current_start = timeout->sip_timeout_next;
+		(void) pthread_mutex_unlock(&timeout_mutex);
+		timeout->sip_timeout_callback_func(
+		    timeout->sip_timeout_callback_func_arg);
+		free(timeout);
+		(void) pthread_mutex_lock(&timeout_mutex);
+	}
+	(void) pthread_mutex_unlock(&timeout_mutex);
+	pthread_exit(NULL);
+	return ((void *)0);
+}
+
+/*
+ * In the very very unlikely case timer id wraps around and we have two timers
+ * with the same id. If that happens timer with the least amount of time left
+ * will be deleted. In case both timers have same time left than the one that
+ * was scheduled first will be deleted as it will be in the front of the list.
+ */
+boolean_t
+sip_untimeout(uint_t id)
+{
+	boolean_t	ret = B_FALSE;
+	sip_timeout_t	*current, *last;
+
+	last = NULL;
+	(void) pthread_mutex_lock(&timeout_mutex);
+
+	/*
+	 * Check if this is in the to-be run list
+	 */
+	if (timeout_current_start != NULL) {
+		current = timeout_current_start;
+		while (current != NULL) {
+			if (current->sip_timeout_id == id) {
+				if (current == timeout_current_start) {
+					timeout_current_start =
+					    current->sip_timeout_next;
+				} else {
+					last->sip_timeout_next =
+					    current->sip_timeout_next;
+				}
+				if (current == timeout_current_end)
+					timeout_current_end = last;
+				if (current->sip_timeout_callback_func_arg !=
+				    NULL) {
+					free(current->
+					    sip_timeout_callback_func_arg);
+					current->sip_timeout_callback_func_arg =
+					    NULL;
+				}
+				free(current);
+				ret = B_TRUE;
+				break;
+			}
+			last = current;
+			current = current->sip_timeout_next;
+		}
+	}
+
+	/*
+	 * Check if this is in the to-be scheduled list
+	 */
+	if (!ret && timeout_list != NULL) {
+		last = NULL;
+		current = timeout_list;
+		while (current != NULL) {
+			if (current->sip_timeout_id == id) {
+				if (current == timeout_list) {
+					timeout_list =
+					    current->sip_timeout_next;
+				} else {
+					last->sip_timeout_next =
+					    current->sip_timeout_next;
+				}
+				if (current->sip_timeout_callback_func_arg !=
+				    NULL) {
+					free(current->
+					    sip_timeout_callback_func_arg);
+					current->sip_timeout_callback_func_arg =
+					    NULL;
+				}
+				free(current);
+				ret = B_TRUE;
+				break;
+			}
+			last = current;
+			current = current->sip_timeout_next;
+		}
+	}
+	(void) pthread_mutex_unlock(&timeout_mutex);
+	return (ret);
+}
+
+/*
+ * Add a new timeout
+ */
+uint_t
+sip_timeout(void *arg, void (*callback_func)(void *),
+    struct timeval *timeout_time)
+{
+	sip_timeout_t	*new_timeout;
+	sip_timeout_t	*current, *last;
+	hrtime_t	future_time;
+	uint_t		tid;
+#ifdef	__linux__
+	struct timespec	tspec;
+	hrtime_t	now;
+#endif
+
+	new_timeout = malloc(sizeof (sip_timeout_t));
+	if (new_timeout == NULL)
+		return (0);
+
+#ifdef	__linux__
+	if (clock_gettime(CLOCK_REALTIME, &tspec) != 0)
+		return (0);
+	now = (hrtime_t)tspec.tv_sec * (hrtime_t)NANOSEC + tspec.tv_nsec;
+	future_time = (hrtime_t)timeout_time->tv_sec * (hrtime_t)NANOSEC +
+	    (hrtime_t)(timeout_time->tv_usec * MILLISEC) + now;
+#else
+	future_time = (hrtime_t)timeout_time->tv_sec * (hrtime_t)NANOSEC +
+	    (hrtime_t)(timeout_time->tv_usec * MILLISEC) + gethrtime();
+#endif
+	if (future_time <= 0L) {
+		free(new_timeout);
+		return (0);
+	}
+
+	new_timeout->sip_timeout_next = NULL;
+	new_timeout->sip_timeout_val = future_time;
+	new_timeout->sip_timeout_callback_func = callback_func;
+	new_timeout->sip_timeout_callback_func_arg = arg;
+	(void) pthread_mutex_lock(&timeout_mutex);
+	timer_id++;
+	if (timer_id == 0)
+		timer_id++;
+	tid = timer_id;
+	new_timeout->sip_timeout_id = tid;
+	last = current = timeout_list;
+	while (current != NULL) {
+		if (current->sip_timeout_val <= new_timeout->sip_timeout_val) {
+			last = current;
+			current = current->sip_timeout_next;
+		} else {
+			break;
+		}
+	}
+
+	if (current == timeout_list) {
+		new_timeout->sip_timeout_next  = timeout_list;
+		timeout_list = new_timeout;
+	} else {
+		new_timeout->sip_timeout_next = current,
+		last->sip_timeout_next = new_timeout;
+	}
+	(void) pthread_cond_signal(&timeout_cond_var);
+	(void) pthread_mutex_unlock(&timeout_mutex);
+	return (tid);
+}
+
+/*
+ * Schedule the next timeout
+ */
+static hrtime_t
+sip_schedule_to_functions()
+{
+	sip_timeout_t		*timeout = NULL;
+	sip_timeout_t		*last = NULL;
+	boolean_t		create_thread = B_FALSE;
+	hrtime_t		current_time;
+#ifdef	__linux__
+	struct timespec	tspec;
+#endif
+
+	/*
+	 * Thread is holding the mutex.
+	 */
+#ifdef	__linux__
+	if (clock_gettime(CLOCK_REALTIME, &tspec) != 0)
+		return ((hrtime_t)LONG_SLEEP_TIME + current_time);
+	current_time = (hrtime_t)tspec.tv_sec * (hrtime_t)NANOSEC +
+	    tspec.tv_nsec;
+#else
+	current_time = gethrtime();
+#endif
+	if (timeout_list == NULL)
+		return ((hrtime_t)LONG_SLEEP_TIME + current_time);
+	timeout = timeout_list;
+
+	/*
+	 * Get all the timeouts that have fired.
+	 */
+	while (timeout != NULL && timeout->sip_timeout_val <= current_time) {
+		last = timeout;
+		timeout = timeout->sip_timeout_next;
+	}
+
+	timeout = last;
+	if (timeout != NULL) {
+		if (timeout_current_end != NULL) {
+			timeout_current_end->sip_timeout_next = timeout_list;
+			timeout_current_end = timeout;
+		} else {
+			timeout_current_start = timeout_list;
+			timeout_current_end = timeout;
+			create_thread = B_TRUE;
+		}
+		timeout_list = timeout->sip_timeout_next;
+		timeout->sip_timeout_next = NULL;
+		if (create_thread) {
+			pthread_t	thr;
+
+			(void) pthread_create(&thr, NULL, sip_run_to_functions,
+			    NULL);
+			(void) pthread_detach(thr);
+		}
+	}
+	if (timeout_list != NULL)
+		return (timeout_list->sip_timeout_val);
+	else
+		return ((hrtime_t)LONG_SLEEP_TIME + current_time);
+}
+
+/*
+ * The timer routine
+ */
+/* ARGSUSED */
+static void *
+sip_timer_thr(void *arg)
+{
+	timestruc_t	to;
+	hrtime_t	current_time;
+	hrtime_t	next_timeout;
+	hrtime_t	delta;
+#ifdef	__linux__
+	struct timespec	tspec;
+#endif
+	to.tv_sec = time(NULL) + 5;
+	to.tv_nsec = 0;
+	(void) pthread_mutex_lock(&timeout_mutex);
+	for (;;) {
+		(void) pthread_cond_timedwait(&timeout_cond_var,
+		    &timeout_mutex, &to);
+		/*
+		 * We return from timedwait because we either timed out
+		 * or a new element was added and we need to reset the time
+		 */
+again:
+		next_timeout =  sip_schedule_to_functions();
+#ifdef	__linux__
+		if (clock_gettime(CLOCK_REALTIME, &tspec) != 0)
+			goto again; /* ??? */
+		current_time = (hrtime_t)tspec.tv_sec * (hrtime_t)NANOSEC +
+		    tspec.tv_nsec;
+#else
+		current_time = gethrtime();
+#endif
+		delta = next_timeout - current_time;
+		if (delta <= 0)
+			goto again;
+		to.tv_sec = time(NULL) + (delta / NANOSEC);
+		to.tv_nsec = delta % NANOSEC;
+	}
+	/* NOTREACHED */
+	return ((void *)0);
+}
+
+/*
+ * The init routine, starts the timer thread
+ */
+void
+sip_timeout_init()
+{
+	static boolean_t	timout_init = B_FALSE;
+	pthread_t		thread1;
+
+	(void) pthread_mutex_lock(&timeout_mutex);
+	if (timout_init == B_FALSE) {
+		timout_init = B_TRUE;
+		(void) pthread_mutex_unlock(&timeout_mutex);
+	} else {
+		(void) pthread_mutex_unlock(&timeout_mutex);
+		return;
+	}
+	(void) pthread_create(&thread1, NULL, sip_timer_thr, NULL);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/lib/libsip/common/sip_ui.c	Sat Oct 07 14:26:26 2006 -0700
@@ -0,0 +1,1342 @@
+/*
+ * 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_xaction.h"
+
+#define	SIP_BUF_SIZE	128
+
+/*
+ * Find the header named header, consecutive calls with old_header
+ * passed in will return next header of the same type.
+ * If no name is passed the first header is returned. consectutive calls
+ * with no name but an old header will return the next header.
+ */
+const struct sip_header *
+sip_get_header(sip_msg_t sip_msg, char *header_name, sip_header_t old_header,
+    int *error)
+{
+	_sip_msg_t		*_sip_msg;
+	const struct sip_header	*sip_hdr;
+
+	if (error != NULL)
+		*error = 0;
+	if (sip_msg == NULL) {
+		if (error != NULL)
+			*error = EINVAL;
+		return (NULL);
+	}
+	_sip_msg = (_sip_msg_t *)sip_msg;
+	(void) pthread_mutex_lock(&_sip_msg->sip_msg_mutex);
+	sip_hdr = (sip_header_t)sip_search_for_header((_sip_msg_t *)sip_msg,
+	    header_name, (_sip_header_t *)old_header);
+	(void) pthread_mutex_unlock(&_sip_msg->sip_msg_mutex);
+	if (sip_hdr == NULL && error != NULL)
+		*error = EINVAL;
+	return (sip_hdr);
+}
+
+/*
+ * Return the request line as a string. Caller releases the returned string.
+ */
+char *
+sip_reqline_to_str(sip_msg_t sip_msg, int *error)
+{
+	char	*reqstr;
+
+	if (error != NULL)
+		*error = 0;
+	if (sip_msg == NULL || !sip_msg_is_request(sip_msg, error)) {
+		if (error != NULL)
+			*error = EINVAL;
+		return (NULL);
+	}
+	reqstr = _sip_startline_to_str((_sip_msg_t *)sip_msg, error);
+	return (reqstr);
+}
+
+/*
+ * Return the response line as a string. Caller releases the returned string.
+ */
+char *
+sip_respline_to_str(sip_msg_t sip_msg, int *error)
+{
+	char	*respstr;
+
+	if (error != NULL)
+		*error = 0;
+	if (sip_msg == NULL || sip_msg_is_request(sip_msg, error)) {
+		if (error != NULL)
+			*error = EINVAL;
+		return (NULL);
+	}
+	respstr = _sip_startline_to_str((_sip_msg_t *)sip_msg, error);
+	return (respstr);
+}
+
+/*
+ * return the first value of the header
+ */
+const struct sip_value *
+sip_get_header_value(const struct sip_header *sip_header, int *error)
+{
+	_sip_header_t		*_sip_header;
+	sip_parsed_header_t	*sip_parsed_header;
+	int			ret = 0;
+	const struct sip_value	*value;
+
+	if (error != NULL)
+		*error = 0;
+	if (sip_header == NULL) {
+		if (error != NULL)
+			*error = EINVAL;
+		return (NULL);
+	}
+	_sip_header = (_sip_header_t *)sip_header;
+	if (_sip_header->sip_hdr_sipmsg != NULL) {
+		(void) pthread_mutex_lock(
+		    &_sip_header->sip_hdr_sipmsg->sip_msg_mutex);
+	}
+	if (_sip_header->sip_header_state == SIP_HEADER_DELETED) {
+		if (_sip_header->sip_hdr_sipmsg != NULL) {
+			(void) pthread_mutex_unlock(
+			    &_sip_header->sip_hdr_sipmsg->sip_msg_mutex);
+		}
+		if (error != NULL)
+			*error = EINVAL;
+		return (NULL);
+	}
+	ret = _sip_header->sip_header_functions->header_parse_func(
+	    _sip_header, &sip_parsed_header);
+	if (_sip_header->sip_hdr_sipmsg != NULL) {
+		(void) pthread_mutex_unlock
+		    (&_sip_header->sip_hdr_sipmsg->sip_msg_mutex);
+	}
+	if (error != NULL)
+		*error = ret;
+
+	if (ret != 0)
+		return (NULL);
+	value = (sip_header_value_t)sip_parsed_header->value;
+	while (value != NULL && value->value_state == SIP_VALUE_DELETED)
+		value = value->next;
+	if (value != NULL && value->value_state == SIP_VALUE_BAD &&
+	    error != NULL) {
+		*error = EPROTO;
+	}
+	return ((sip_header_value_t)value);
+}
+
+/*
+ * Return the next value of the header.
+ */
+const struct sip_value *
+sip_get_next_value(sip_header_value_t old_value, int *error)
+{
+	const struct sip_value *value;
+
+	if (error != NULL)
+		*error = 0;
+	if (old_value == NULL || old_value->next == NULL) {
+		if (error != NULL)
+			*error = EINVAL;
+		return (NULL);
+	}
+	/*
+	 * We never free the deleted values so no need to hold a lock.
+	 */
+	value = (sip_header_value_t)old_value->next;
+	while (value != NULL && value->value_state == SIP_VALUE_DELETED)
+		value = value->next;
+	if (value != NULL && value->value_state == SIP_VALUE_BAD &&
+	    error != NULL) {
+		*error = EPROTO;
+	}
+	return ((sip_header_value_t)value);
+}
+
+/*
+ * Given a SIP message, delete the header "header_name".
+ */
+int
+sip_delete_header_by_name(sip_msg_t msg, char *header_name)
+{
+	_sip_msg_t	*_msg = (_sip_msg_t *)msg;
+	sip_header_t	sip_hdr;
+	_sip_header_t	*_sip_hdr;
+
+	if (_msg == NULL || header_name == NULL)
+		return (EINVAL);
+	(void) pthread_mutex_lock(&_msg->sip_msg_mutex);
+	if (_msg->sip_msg_cannot_be_modified) {
+		(void) pthread_mutex_unlock(&_msg->sip_msg_mutex);
+		return (EPERM);
+	}
+	sip_hdr = (sip_header_t)sip_search_for_header(_msg, header_name, NULL);
+	if (sip_hdr == NULL) {
+		(void) pthread_mutex_unlock(&_msg->sip_msg_mutex);
+		return (EINVAL);
+	}
+	_sip_hdr = (_sip_header_t *)sip_hdr;
+	_sip_hdr->sip_header_state = SIP_HEADER_DELETED;
+	_sip_hdr->sip_hdr_sipmsg->sip_msg_len -= _sip_hdr->sip_hdr_end -
+	    _sip_hdr->sip_hdr_start;
+	assert(_sip_hdr->sip_hdr_sipmsg->sip_msg_len >= 0);
+	if (_msg->sip_msg_buf != NULL)
+		_msg->sip_msg_modified = B_TRUE;
+	(void) pthread_mutex_unlock(&_msg->sip_msg_mutex);
+
+	return (0);
+}
+
+/*
+ * Mark the header as deleted.
+ */
+int
+sip_delete_header(sip_header_t sip_header)
+{
+	_sip_header_t	*_sip_header;
+
+	if (sip_header == NULL)
+		return (EINVAL);
+	_sip_header = (_sip_header_t *)sip_header;
+	(void) pthread_mutex_lock(&_sip_header->sip_hdr_sipmsg->sip_msg_mutex);
+	if (_sip_header->sip_hdr_sipmsg->sip_msg_cannot_be_modified) {
+		(void) pthread_mutex_unlock
+		    (&_sip_header->sip_hdr_sipmsg->sip_msg_mutex);
+		return (EPERM);
+	}
+	if (_sip_header->sip_header_state == SIP_HEADER_DELETED) {
+		(void) pthread_mutex_unlock(
+		    &_sip_header->sip_hdr_sipmsg->sip_msg_mutex);
+		return (EINVAL);
+	}
+	_sip_header->sip_header_state = SIP_HEADER_DELETED;
+	_sip_header->sip_hdr_sipmsg->sip_msg_len -= _sip_header->sip_hdr_end -
+	    _sip_header->sip_hdr_start;
+	assert(_sip_header->sip_hdr_sipmsg->sip_msg_len >= 0);
+	if (_sip_header->sip_hdr_sipmsg->sip_msg_buf != NULL)
+		_sip_header->sip_hdr_sipmsg->sip_msg_modified = B_TRUE;
+	(void) pthread_mutex_unlock
+	    (&_sip_header->sip_hdr_sipmsg->sip_msg_mutex);
+	return (0);
+}
+
+/*
+ * Mark the value as deleted.
+ */
+int
+sip_delete_value(sip_header_t sip_header, sip_header_value_t sip_header_value)
+{
+	_sip_header_t	*_sip_header;
+	sip_value_t	*_sip_header_value;
+	int		vlen;
+	char		*c;
+
+	if (sip_header == NULL || sip_header_value == NULL)
+		return (EINVAL);
+	_sip_header = (_sip_header_t *)sip_header;
+	(void) pthread_mutex_lock(&_sip_header->sip_hdr_sipmsg->sip_msg_mutex);
+	if (_sip_header->sip_hdr_sipmsg->sip_msg_cannot_be_modified) {
+		(void) pthread_mutex_unlock(&_sip_header->
+		    sip_hdr_sipmsg->sip_msg_mutex);
+		return (EPERM);
+	}
+	if (_sip_header->sip_header_state == SIP_HEADER_DELETED) {
+		(void) pthread_mutex_unlock(
+		    &_sip_header->sip_hdr_sipmsg->sip_msg_mutex);
+		return (EINVAL);
+	}
+	_sip_header_value = (sip_value_t *)sip_header_value;
+	if (_sip_header_value->value_state == SIP_VALUE_DELETED) {
+		(void) pthread_mutex_unlock(
+		    &_sip_header->sip_hdr_sipmsg->sip_msg_mutex);
+		return (EINVAL);
+	}
+	_sip_header->sip_header_state = SIP_HEADER_DELETED_VAL;
+	_sip_header_value->value_state = SIP_VALUE_DELETED;
+	vlen = _sip_header_value->value_end - _sip_header_value->value_start;
+	if (_sip_header->sip_hdr_parsed->value == _sip_header_value) {
+		c = _sip_header_value->value_start;
+		while (*c-- != SIP_HCOLON)
+			vlen++;
+	} else {
+		c = _sip_header_value->value_start;
+		while (*c-- != SIP_COMMA)
+			vlen++;
+	}
+	if (_sip_header_value->next == NULL) {
+		sip_value_t	*value = _sip_header->sip_hdr_parsed->value;
+		boolean_t	crlf_present =  B_FALSE;
+		char		*s;
+
+		while (value != NULL && value != _sip_header_value) {
+			crlf_present = B_FALSE;
+
+			if (value->value_state == SIP_VALUE_DELETED) {
+				value = value->next;
+				continue;
+			}
+			s = value->value_end;
+			while (s != value->value_start) {
+				if (*s == '\r' && strncmp(s, SIP_CRLF,
+				    strlen(SIP_CRLF)) == 0) {
+					crlf_present = B_TRUE;
+					break;
+				}
+				s--;
+			}
+			value = value->next;
+		}
+		if (!crlf_present) {
+			c = _sip_header_value->value_end;
+			while (*c-- != '\r')
+				vlen--;
+			assert(vlen > 0);
+		}
+	}
+	_sip_header->sip_hdr_sipmsg->sip_msg_len -= vlen;
+	if (_sip_header->sip_hdr_sipmsg->sip_msg_buf != NULL)
+		_sip_header->sip_hdr_sipmsg->sip_msg_modified = B_TRUE;
+	(void) pthread_mutex_unlock
+	    (&_sip_header->sip_hdr_sipmsg->sip_msg_mutex);
+	return (0);
+}
+
+/*
+ * Given a param list, check if a param name exists.
+ */
+boolean_t
+sip_is_param_present(const sip_param_t *param_list, char *param_name,
+    int param_len)
+{
+	const sip_param_t	*param = param_list;
+
+	while (param != NULL) {
+		if (param->param_name.sip_str_len == param_len &&
+		    strncasecmp(param->param_name.sip_str_ptr, param_name,
+			param_len) == 0) {
+			return (B_TRUE);
+		}
+		param = param->param_next;
+	}
+	return (B_FALSE);
+}
+
+
+/*
+ * Given a value header return the value of the named param.
+ */
+const sip_str_t *
+sip_get_param_value(sip_header_value_t header_value, char *param_name,
+    int *error)
+{
+	sip_value_t	*_sip_header_value;
+	sip_param_t	*sip_param;
+
+	if (error != NULL)
+		*error = 0;
+	if (header_value == NULL || param_name == NULL) {
+		if (error != NULL)
+			*error = EINVAL;
+		return (NULL);
+	}
+	_sip_header_value = (sip_value_t *)header_value;
+	if (_sip_header_value->value_state == SIP_VALUE_DELETED) {
+		if (error != NULL)
+			*error = EINVAL;
+		return (NULL);
+	}
+	if (_sip_header_value->param_list == NULL) {
+		if (error != NULL)
+			*error = EINVAL;
+		return (NULL);
+	}
+	sip_param = sip_get_param_from_list(_sip_header_value->param_list,
+	    param_name);
+	if (sip_param != NULL)
+		return (&sip_param->param_value);
+	return (NULL);
+}
+
+/*
+ * Return the list of params in the header
+ */
+const sip_param_t *
+sip_get_params(sip_header_value_t header_value, int *error)
+{
+	sip_value_t	*sip_header_value;
+
+	if (error != NULL)
+		*error = 0;
+	if (header_value == NULL) {
+		if (error != NULL)
+			*error = EINVAL;
+		return (NULL);
+	}
+	sip_header_value = (sip_value_t *)header_value;
+	if (sip_header_value->value_state == SIP_VALUE_DELETED) {
+		if (error != NULL)
+			*error = EINVAL;
+		return (NULL);
+	}
+	return (sip_header_value->param_list);
+}
+
+/*
+ * Return true if this is a SIP request
+ */
+boolean_t
+sip_msg_is_request(sip_msg_t sip_msg, int *error)
+{
+	_sip_msg_t		*_sip_msg;
+	sip_message_type_t	*sip_msg_info;
+	boolean_t		ret;
+
+	if (error != NULL)
+		*error = 0;
+	if (sip_msg == NULL) {
+		if (error != NULL)
+			*error = EINVAL;
+		return (B_FALSE);
+	}
+	_sip_msg = (_sip_msg_t *)sip_msg;
+	(void) pthread_mutex_lock(&_sip_msg->sip_msg_mutex);
+	if (_sip_msg->sip_msg_req_res == NULL) {
+		(void) pthread_mutex_unlock(&_sip_msg->sip_msg_mutex);
+		if (error != NULL)
+			*error = EINVAL;
+		return (B_FALSE);
+	}
+	sip_msg_info = _sip_msg->sip_msg_req_res;
+	ret = sip_msg_info->is_request;
+	(void) pthread_mutex_unlock(&_sip_msg->sip_msg_mutex);
+	return (ret);
+}
+
+/*
+ * Return true if this is a SIP response
+ */
+boolean_t
+sip_msg_is_response(sip_msg_t sip_msg, int *error)
+{
+	boolean_t		is_resp;
+	_sip_msg_t		*_sip_msg;
+	sip_message_type_t	*sip_msg_info;
+
+	if (error != NULL)
+		*error = 0;
+	if (sip_msg == NULL) {
+		if (error != NULL)
+			*error = EINVAL;
+		return (B_FALSE);
+	}
+	_sip_msg = (_sip_msg_t *)sip_msg;
+	(void) pthread_mutex_lock(&_sip_msg->sip_msg_mutex);
+	if (_sip_msg->sip_msg_req_res == NULL) {
+		(void) pthread_mutex_unlock(&_sip_msg->sip_msg_mutex);
+		if (error != NULL)
+			*error = EINVAL;
+		return (B_FALSE);
+	}
+	sip_msg_info = _sip_msg->sip_msg_req_res;
+	is_resp = !sip_msg_info->is_request;
+	(void) pthread_mutex_unlock(&_sip_msg->sip_msg_mutex);
+	return (is_resp);
+}
+
+/*
+ * Return the method in the request line
+ */
+sip_method_t
+sip_get_request_method(sip_msg_t sip_msg, int *error)
+{
+	_sip_msg_t		*_sip_msg;
+	sip_message_type_t	*sip_msg_info;
+	sip_method_t 		ret = -1;
+
+	if (error != NULL)
+		*error = 0;
+	if (sip_msg == NULL) {
+		if (error != NULL)
+			*error = EINVAL;
+		return (ret);
+	}
+	_sip_msg = (_sip_msg_t *)sip_msg;
+	(void) pthread_mutex_lock(&_sip_msg->sip_msg_mutex);
+	sip_msg_info = _sip_msg->sip_msg_req_res;
+	if (_sip_msg->sip_msg_req_res == NULL) {
+		(void) pthread_mutex_unlock(&_sip_msg->sip_msg_mutex);
+		if (error != NULL)
+			*error = EINVAL;
+		return (ret);
+	}
+	if (sip_msg_info->is_request)
+		ret = sip_msg_info->sip_req_method;
+	else if (error != NULL)
+		*error = EINVAL;
+	(void) pthread_mutex_unlock(&_sip_msg->sip_msg_mutex);
+	return (ret);
+}
+
+/*
+ * Return the URI from the request line
+ */
+const sip_str_t *
+sip_get_request_uri_str(sip_msg_t sip_msg, int *error)
+{
+	_sip_msg_t		*_sip_msg;
+	sip_message_type_t	*sip_msg_info;
+	sip_str_t 		*ret = NULL;
+	struct sip_uri		*parsed_uri;
+
+	if (error != NULL)
+		*error = 0;
+	if (sip_msg == NULL) {
+		if (error != NULL)
+			*error = EINVAL;
+		return (NULL);
+	}
+	_sip_msg = (_sip_msg_t *)sip_msg;
+	(void) pthread_mutex_lock(&_sip_msg->sip_msg_mutex);
+	if (_sip_msg->sip_msg_req_res == NULL) {
+		(void) pthread_mutex_unlock(&_sip_msg->sip_msg_mutex);
+		if (error != NULL)
+			*error = EINVAL;
+		return (NULL);
+	}
+	sip_msg_info = _sip_msg->sip_msg_req_res;
+	if (sip_msg_info->is_request)
+		ret = &sip_msg_info->sip_req_uri;
+	(void) pthread_mutex_unlock(&_sip_msg->sip_msg_mutex);
+
+	/*
+	 * If the error is required, check the validity of the URI via
+	 * sip_uri_parse().
+	 */
+	if (error != NULL) {
+		parsed_uri = sip_parse_uri(ret, error);
+		if (parsed_uri != NULL)
+			sip_free_parsed_uri((sip_uri_t)parsed_uri);
+	}
+	return (ret);
+}
+
+/*
+ * Return the response code
+ */
+int
+sip_get_response_code(sip_msg_t sip_msg, int *error)
+{
+	_sip_msg_t		*_sip_msg;
+	sip_message_type_t	*sip_msg_info;
+	int 			ret = -1;
+
+	if (error != NULL)
+		*error = 0;
+	if (sip_msg == NULL) {
+		if (error != NULL)
+			*error = EINVAL;
+		return (ret);
+	}
+	_sip_msg = (_sip_msg_t *)sip_msg;
+	(void) pthread_mutex_lock(&_sip_msg->sip_msg_mutex);
+	if (_sip_msg->sip_msg_req_res == NULL) {
+		(void) pthread_mutex_unlock(&_sip_msg->sip_msg_mutex);
+		if (error != NULL)
+			*error = EINVAL;
+		return (ret);
+	}
+	sip_msg_info = _sip_msg->sip_msg_req_res;
+	if (!sip_msg_info->is_request)
+		ret = sip_msg_info->sip_resp_code;
+	else if (error != NULL)
+		*error = EINVAL;
+	(void) pthread_mutex_unlock(&_sip_msg->sip_msg_mutex);
+	return (ret);
+}
+
+/*
+ * Get the response phrase
+ */
+const sip_str_t *
+sip_get_response_phrase(sip_msg_t sip_msg, int *error)
+{
+	_sip_msg_t		*_sip_msg;
+	sip_message_type_t	*sip_msg_info;
+	sip_str_t 		*ret = NULL;
+
+	if (error != NULL)
+		*error = 0;
+	if (sip_msg == NULL) {
+		if (error != NULL)
+			*error = EINVAL;
+		return (ret);
+	}
+	_sip_msg = (_sip_msg_t *)sip_msg;
+	(void) pthread_mutex_lock(&_sip_msg->sip_msg_mutex);
+	if (_sip_msg->sip_msg_req_res == NULL) {
+		(void) pthread_mutex_unlock(&_sip_msg->sip_msg_mutex);
+		if (error != NULL)
+			*error = EINVAL;
+		return (ret);
+	}
+	sip_msg_info = _sip_msg->sip_msg_req_res;
+	(void) pthread_mutex_unlock(&_sip_msg->sip_msg_mutex);
+	if (!sip_msg_info->is_request) {
+		if (sip_msg_info->sip_resp_phrase_len == 0)
+			ret = NULL;
+		else
+			ret = &sip_msg_info->sip_resp_phrase;
+	} else if (error != NULL) {
+		*error = EINVAL;
+	}
+	return (ret);
+}
+
+/*
+ * Get the SIP version string
+ */
+const sip_str_t *
+sip_get_sip_version(sip_msg_t sip_msg, int *error)
+{
+	_sip_msg_t		*_sip_msg;
+	sip_message_type_t	*sip_msg_info;
+	sip_str_t		*ret = NULL;
+
+	if (error != NULL)
+		*error = 0;
+	if (sip_msg == NULL) {
+		if (error != NULL)
+			*error = EINVAL;
+		return (ret);
+	}
+	_sip_msg = (_sip_msg_t *)sip_msg;
+	(void) pthread_mutex_lock(&_sip_msg->sip_msg_mutex);
+	if (_sip_msg->sip_msg_req_res == NULL) {
+		(void) pthread_mutex_unlock(&_sip_msg->sip_msg_mutex);
+		if (error != NULL)
+			*error = EINVAL;
+		return (ret);
+	}
+	sip_msg_info = _sip_msg->sip_msg_req_res;
+	(void) pthread_mutex_unlock(&_sip_msg->sip_msg_mutex);
+	ret = &sip_msg_info->sip_proto_version.version;
+	return (ret);
+}
+
+/*
+ * Return the length of the SIP message
+ */
+int
+sip_get_msg_len(sip_msg_t sip_msg, int *error)
+{
+	_sip_msg_t	*_sip_msg;
+
+	if (error != NULL)
+		*error = 0;
+	if (sip_msg == NULL) {
+		if (error != NULL)
+			*error = EINVAL;
+		return (-1);
+	}
+	_sip_msg = (_sip_msg_t *)sip_msg;
+
+	return (_sip_msg->sip_msg_len);
+}
+
+/*
+ * Get content as a string. Caller frees the string
+ */
+char *
+sip_get_content(sip_msg_t sip_msg, int *error)
+{
+	_sip_msg_t	*_sip_msg;
+	sip_content_t	*sip_content;
+	char		*content;
+	int		len;
+	char		*p;
+
+	if (error != NULL)
+		*error = 0;
+
+	if (sip_msg == NULL) {
+		if (error != NULL)
+			*error = EINVAL;
+		return (NULL);
+	}
+	_sip_msg = (_sip_msg_t *)sip_msg;
+	(void) pthread_mutex_lock(&_sip_msg->sip_msg_mutex);
+	if (_sip_msg->sip_msg_content == NULL) {
+		(void) pthread_mutex_unlock(&_sip_msg->sip_msg_mutex);
+		if (error != NULL)
+			*error = EINVAL;
+		return (NULL);
+	}
+	content = malloc(_sip_msg->sip_msg_content_len + 1);
+	if (content == NULL) {
+		(void) pthread_mutex_unlock(&_sip_msg->sip_msg_mutex);
+		if (error != NULL)
+			*error = ENOMEM;
+		return (NULL);
+	}
+	p = content;
+	sip_content = _sip_msg->sip_msg_content;
+	while (sip_content != NULL) {
+		len =  sip_content->sip_content_end -
+		    sip_content->sip_content_start;
+		(void) strncpy(p, sip_content->sip_content_start, len);
+		p += len;
+		sip_content = sip_content->sip_content_next;
+	}
+	content[_sip_msg->sip_msg_content_len] = '\0';
+	(void) pthread_mutex_unlock(&_sip_msg->sip_msg_mutex);
+	return (content);
+}
+
+/*
+ * copy sip_header with param, if any, to sip_msg
+ */
+int
+sip_copy_header(sip_msg_t sip_msg, sip_header_t sip_header, char *param)
+{
+	_sip_msg_t	*_sip_msg;
+	_sip_header_t	*_sip_header;
+	int		ret;
+
+	if (sip_msg == NULL || sip_header == NULL)
+		return (EINVAL);
+	_sip_msg = (_sip_msg_t *)sip_msg;
+	_sip_header = (_sip_header_t *)sip_header;
+	(void) pthread_mutex_lock(&_sip_msg->sip_msg_mutex);
+	if (_sip_msg->sip_msg_cannot_be_modified) {
+		(void) pthread_mutex_unlock(&_sip_msg->sip_msg_mutex);
+		return (EPERM);
+	}
+	if (_sip_header->sip_header_state == SIP_HEADER_DELETED) {
+		(void) pthread_mutex_unlock(&_sip_msg->sip_msg_mutex);
+		return (EINVAL);
+	}
+
+	ret = _sip_copy_header(_sip_msg, _sip_header, param, B_TRUE);
+	if (_sip_msg->sip_msg_buf != NULL)
+		_sip_msg->sip_msg_modified = B_TRUE;
+	(void) pthread_mutex_unlock(&_sip_msg->sip_msg_mutex);
+	return (ret);
+}
+
+/*
+ * copy the header specified by header_name, with param, if any
+ */
+int
+sip_copy_header_by_name(sip_msg_t old_msg, sip_msg_t new_msg,
+    char *header_name, char *param)
+{
+	int		ret;
+	_sip_msg_t	*_old_msg = (_sip_msg_t *)old_msg;
+	_sip_msg_t	*_new_msg = (_sip_msg_t *)new_msg;
+
+	if (_old_msg == NULL || _new_msg == NULL || header_name == NULL ||
+	    _old_msg == _new_msg) {
+		return (EINVAL);
+	}
+	(void) pthread_mutex_lock(&_new_msg->sip_msg_mutex);
+	if (_new_msg->sip_msg_cannot_be_modified) {
+		(void) pthread_mutex_unlock(&_new_msg->sip_msg_mutex);
+		return (EPERM);
+	}
+
+	(void) pthread_mutex_lock(&_old_msg->sip_msg_mutex);
+	ret = _sip_find_and_copy_header(_old_msg, _new_msg, header_name, param,
+	    B_FALSE);
+	(void) pthread_mutex_unlock(&_old_msg->sip_msg_mutex);
+	if (_new_msg->sip_msg_buf != NULL)
+		_new_msg->sip_msg_modified = B_TRUE;
+	(void) pthread_mutex_unlock(&_new_msg->sip_msg_mutex);
+	return (ret);
+}
+
+/*
+ * add the given header to sip_message
+ */
+int
+sip_add_header(sip_msg_t sip_msg, char *header_string)
+{
+	int		header_size;
+	_sip_header_t	*new_header;
+	_sip_msg_t	*_sip_msg;
+
+	if (sip_msg == NULL || header_string == NULL)
+		return (EINVAL);
+	_sip_msg = (_sip_msg_t *)sip_msg;
+	(void) pthread_mutex_lock(&_sip_msg->sip_msg_mutex);
+	if (_sip_msg->sip_msg_cannot_be_modified) {
+		(void) pthread_mutex_unlock(&_sip_msg->sip_msg_mutex);
+		return (EPERM);
+	}
+	header_size = strlen(header_string) + strlen(SIP_CRLF);
+	new_header = sip_new_header(header_size);
+	if (new_header == NULL) {
+		(void) pthread_mutex_unlock(&_sip_msg->sip_msg_mutex);
+		return (ENOMEM);
+	}
+
+	(void) snprintf(new_header->sip_hdr_start, header_size + 1, "%s%s",
+	    header_string, SIP_CRLF);
+	_sip_add_header(_sip_msg, new_header, B_TRUE, B_FALSE, NULL);
+	if (_sip_msg->sip_msg_buf != NULL)
+		_sip_msg->sip_msg_modified = B_TRUE;
+	(void) pthread_mutex_unlock(&_sip_msg->sip_msg_mutex);
+	return (0);
+}
+
+/*
+ * add the given param to the sip_header. create a new header with the param
+ * and mark the old header as deleted.
+ */
+sip_header_t
+sip_add_param(sip_header_t sip_header, char *param, int *error)
+{
+	_sip_header_t	*_sip_header;
+	_sip_header_t	*new_header;
+	int		hdrlen;
+	_sip_msg_t	*_sip_msg;
+	int		param_len;
+	char		*tmp_ptr;
+
+	if (error != NULL)
+		*error = 0;
+
+	if (param == NULL || sip_header == NULL) {
+		if (error != NULL)
+			*error = EINVAL;
+		return (NULL);
+	}
+
+	_sip_header = (_sip_header_t *)sip_header;
+
+	(void) pthread_mutex_lock(&_sip_header->sip_hdr_sipmsg->sip_msg_mutex);
+	if (_sip_header->sip_hdr_sipmsg->sip_msg_cannot_be_modified) {
+		if (error != NULL)
+			*error = EPERM;
+		(void) pthread_mutex_unlock(
+		    &_sip_header->sip_hdr_sipmsg->sip_msg_mutex);
+		return (NULL);
+	}
+	if (_sip_header->sip_header_state == SIP_HEADER_DELETED) {
+		if (error != NULL)
+			*error = EINVAL;
+		(void) pthread_mutex_unlock(
+		    &_sip_header->sip_hdr_sipmsg->sip_msg_mutex);
+		return (NULL);
+	}
+
+	param_len = SIP_SPACE_LEN + sizeof (char) + SIP_SPACE_LEN +
+	    strlen(param);
+	hdrlen = _sip_header->sip_hdr_end - _sip_header->sip_hdr_start;
+	new_header = sip_new_header(hdrlen + param_len);
+	if (new_header == NULL) {
+		if (error != NULL)
+			*error = ENOMEM;
+		(void) pthread_mutex_unlock(
+		    &_sip_header->sip_hdr_sipmsg->sip_msg_mutex);
+		return (NULL);
+	}
+	(void) memcpy(new_header->sip_hdr_start, _sip_header->sip_hdr_start,
+	    hdrlen);
+	new_header->sip_hdr_end = new_header->sip_hdr_start + hdrlen;
+	hdrlen = param_len + 1;
+	/*
+	 * Find CRLF
+	 */
+	tmp_ptr = new_header->sip_hdr_end;
+	while (*tmp_ptr-- != '\n') {
+		hdrlen++;
+		if (tmp_ptr == new_header->sip_hdr_start) {
+			sip_free_header(new_header);
+			if (error != NULL)
+				*error = EINVAL;
+			(void) pthread_mutex_unlock(
+			    &_sip_header->sip_hdr_sipmsg->sip_msg_mutex);
+			return (NULL);
+		}
+	}
+	(void) snprintf(tmp_ptr, hdrlen + 1,
+	    " %c %s%s", SIP_SEMI, param, SIP_CRLF);
+	new_header->sip_hdr_end += param_len;
+	new_header->sip_header_functions = _sip_header->sip_header_functions;
+	_sip_msg = _sip_header->sip_hdr_sipmsg;
+	_sip_add_header(_sip_msg, new_header, B_TRUE, B_FALSE, NULL);
+	if (_sip_header->sip_hdr_sipmsg->sip_msg_buf != NULL)
+		_sip_header->sip_hdr_sipmsg->sip_msg_modified = B_TRUE;
+	(void) pthread_mutex_unlock(&new_header->sip_hdr_sipmsg->sip_msg_mutex);
+	(void) sip_delete_header(sip_header);
+	return ((sip_header_t)new_header);
+}
+
+/*
+ * Get Request URI
+ */
+const struct sip_uri *
+sip_get_request_uri(sip_msg_t sip_msg, int *error)
+{
+	_sip_msg_t		*_sip_msg;
+	sip_message_type_t	*sip_msg_info;
+	const struct sip_uri	*ret = NULL;
+
+	if (error != NULL)
+		*error = 0;
+
+	if (sip_msg == NULL) {
+		if (error != NULL)
+			*error = EINVAL;
+		return (NULL);
+	}
+	_sip_msg = (_sip_msg_t *)sip_msg;
+	(void) pthread_mutex_lock(&_sip_msg->sip_msg_mutex);
+	sip_msg_info = _sip_msg->sip_msg_req_res;
+	if (sip_msg_info != NULL && sip_msg_info->is_request) {
+		ret = sip_msg_info->sip_req_parse_uri;
+	} else {
+		if (error != NULL)
+			*error = EINVAL;
+	}
+	(void) pthread_mutex_unlock(&_sip_msg->sip_msg_mutex);
+
+	if (ret != NULL) {
+		if (ret->sip_uri_scheme.sip_str_len == 0 ||
+		    ret->sip_uri_scheme.sip_str_ptr == NULL) {
+			ret = NULL;
+			if (error != NULL)
+				*error = EINVAL;
+		} else if (ret->sip_uri_errflags != 0 && error != NULL) {
+			*error = EINVAL;
+		}
+	}
+	return ((sip_uri_t)ret);
+}
+
+/*
+ * returns a comma separated string of all the sent-by values registered by
+ * the UA.
+ */
+char *
+sip_sent_by_to_str(int *error)
+{
+	sent_by_list_t	*sb;
+	int		sb_len = 0;
+	int		slen;
+	char		*sb_str;
+	char		*p;
+	int		count = 0;
+	int		cnt = 0;
+
+	if (error != NULL)
+		*error = 0;
+
+	(void) pthread_mutex_lock(&sip_sent_by_lock);
+	if (sip_sent_by == NULL) {
+		(void) pthread_mutex_unlock(&sip_sent_by_lock);
+		return (NULL);
+	}
+	sb = sip_sent_by;
+	for (cnt = 0; cnt < sip_sent_by_count; cnt++) {
+		sb_len += strlen(sb->sb_val);
+		sb = sb->sb_next;
+	}
+	/*
+	 * for the commas
+	 */
+	sb_len += sip_sent_by_count - 1;
+	sb_str = malloc(sb_len + 1);
+	if (sb_str == NULL) {
+		if (error != NULL)
+			*error = ENOMEM;
+		(void) pthread_mutex_unlock(&sip_sent_by_lock);
+		return (NULL);
+	}
+	sb = sip_sent_by;
+	p = sb_str;
+	slen = sb_len + 1;
+	for (cnt = 0; cnt < sip_sent_by_count; cnt++) {
+		if (cnt == 0) {
+			count = snprintf(p, slen, "%s", sb->sb_val);
+		} else {
+			count = snprintf(p, slen, "%c%s", SIP_COMMA,
+			    sb->sb_val);
+		}
+		p += count;
+		slen -= count;
+		sb = sb->sb_next;
+	}
+	sb_str[sb_len] = '\0';
+	(void) pthread_mutex_unlock(&sip_sent_by_lock);
+	return (sb_str);
+}
+
+/*
+ * A comma separated list of sent-by values.
+ */
+int
+sip_register_sent_by(char *val)
+{
+	sent_by_list_t	*sb = NULL;
+	sent_by_list_t	*sb_tail = NULL;
+	char		*str;
+	int		count = 0;
+
+	if (val == NULL)
+		return (EINVAL);
+	str = strtok(val, ",");
+	while (str != NULL) {
+		int	slen;
+		char	*start = str;
+		char	*end = str + strlen(str) - 1;
+
+		while (isspace(*start))
+			start++;
+		while (isspace(*end))
+			end--;
+		if (end <= start)
+			goto err_ret;
+		slen = end - start + 1;
+		sb_tail = (sent_by_list_t *)malloc(sizeof (*sb_tail));
+		if (sb_tail == NULL)
+			goto err_ret;
+		sb_tail->sb_next = sb_tail->sb_prev = NULL;
+		if ((sb_tail->sb_val = (char *)malloc(slen + 1)) == NULL) {
+			free(sb_tail);
+			goto err_ret;
+		}
+		(void) strncpy(sb_tail->sb_val, start, slen);
+		sb_tail->sb_val[slen] = '\0';
+		if (sb == NULL) {
+			sb = sb_tail;
+		} else {
+			sb_tail->sb_next = sb;
+			sb->sb_prev = sb_tail;
+			sb = sb_tail;
+		}
+		count++;
+		str = strtok(NULL, ",");
+	}
+	sb_tail = sb;
+	while (sb_tail->sb_next != NULL)
+		sb_tail = sb_tail->sb_next;
+	(void) pthread_mutex_lock(&sip_sent_by_lock);
+	if (sip_sent_by != NULL) {
+		sb_tail->sb_next = sip_sent_by;
+		sip_sent_by->sb_prev = sb_tail;
+	}
+	sip_sent_by = sb;
+	sip_sent_by_count += count;
+	(void) pthread_mutex_unlock(&sip_sent_by_lock);
+	return (0);
+err_ret:
+	sb_tail = sb;
+	for (; count > 0; count--) {
+		sb = sb_tail->sb_next;
+		free(sb_tail->sb_val);
+		sb_tail->sb_next = NULL;
+		sb_tail->sb_prev = NULL;
+		free(sb_tail);
+		sb_tail = sb;
+	}
+	return (EINVAL);
+}
+
+/*
+ * Un-register sent-by values; 'val' contains a comma separated list
+ */
+void
+sip_unregister_sent_by(char *val)
+{
+	sent_by_list_t	*sb;
+	char		*str;
+	int		count = 0;
+
+	(void) pthread_mutex_lock(&sip_sent_by_lock);
+	str = strtok(val, ",");
+	while (str != NULL) {
+		sb = sip_sent_by;
+		for (count = 0; count < sip_sent_by_count; count++) {
+			if (strncmp(sb->sb_val, str, strlen(str)) == 0) {
+				if (sb == sip_sent_by) {
+					if (sb->sb_next != NULL)
+						sip_sent_by = sb->sb_next;
+					else
+						sip_sent_by = NULL;
+				} else if (sb->sb_next == NULL) {
+					sb->sb_prev->sb_next = NULL;
+				} else {
+					sb->sb_prev->sb_next = sb->sb_next;
+					sb->sb_next->sb_prev = sb->sb_prev;
+				}
+				sip_sent_by_count--;
+				sb->sb_next = NULL;
+				sb->sb_prev = NULL;
+				free(sb->sb_val);
+				free(sb);
+				break;
+			}
+			sb = sb->sb_next;
+		}
+		str = strtok(NULL, ",");
+	}
+	(void) pthread_mutex_unlock(&sip_sent_by_lock);
+}
+
+/*
+ * Un-register all the sent-by values
+ */
+void
+sip_unregister_all_sent_by()
+{
+	sent_by_list_t	*sb;
+	int		count;
+
+	(void) pthread_mutex_lock(&sip_sent_by_lock);
+	sb = sip_sent_by;
+	for (count = 0; count < sip_sent_by_count; count++) {
+		sip_sent_by = sb->sb_next;
+		free(sb->sb_val);
+		sb->sb_next = NULL;
+		sb->sb_prev = NULL;
+		free(sb);
+		sb = sip_sent_by;
+	}
+	sip_sent_by = NULL;
+	sip_sent_by_count = 0;
+	(void) pthread_mutex_unlock(&sip_sent_by_lock);
+}
+
+/*
+ * Given a response code, return the corresponding phrase
+ */
+char *
+sip_get_resp_desc(int resp_code)
+{
+	switch (resp_code) {
+	case SIP_TRYING:
+		return ("TRYING");
+	case SIP_RINGING:
+		return ("RINGING");
+	case SIP_CALL_IS_BEING_FORWARDED:
+		return ("CALL_IS_BEING_FORWARDED");
+	case SIP_QUEUED:
+		return ("QUEUED");
+	case SIP_SESSION_PROGRESS:
+		return ("SESSION_PROGRESS");
+	case SIP_OK:
+		return ("OK");
+	case SIP_ACCEPTED:
+		return ("ACCEPTED");
+	case SIP_MULTIPLE_CHOICES:
+		return ("MULTIPLE_CHOICES");
+	case SIP_MOVED_PERMANENTLY:
+		return ("MOVED_PERMANENTLY");
+	case SIP_MOVED_TEMPORARILY:
+		return ("MOVED_TEMPORARILY");
+	case SIP_USE_PROXY:
+		return ("USE_PROXY");
+	case SIP_ALTERNATIVE_SERVICE:
+		return ("ALTERNATIVE_SERVICE");
+	case SIP_BAD_REQUEST:
+		return ("BAD_REQUEST");
+	case SIP_UNAUTHORIZED:
+		return ("UNAUTHORIZED");
+	case SIP_PAYMENT_REQUIRED:
+		return ("PAYMENT_REQUIRED");
+	case SIP_FORBIDDEN:
+		return ("FORBIDDEN");
+	case SIP_NOT_FOUND:
+		return ("NOT_FOUND");
+	case SIP_METHOD_NOT_ALLOWED:
+		return ("METHOD_NOT_ALLOWED");
+	case SIP_NOT_ACCEPTABLE:
+		return ("NOT_ACCEPTABLE");
+	case SIP_PROXY_AUTH_REQUIRED:
+		return ("PROXY_AUTH_REQUIRED");
+	case SIP_REQUEST_TIMEOUT:
+		return ("REQUEST_TIMEOUT");
+	case SIP_GONE:
+		return ("GONE");
+	case SIP_REQUEST_ENTITY_2_LARGE:
+		return ("REQUEST_ENTITY_2_LARGE");
+	case SIP_REQUEST_URI_2_LONG:
+		return ("REQUEST_URI_2_LONG");
+	case SIP_UNSUPPORTED_MEDIA_TYPE:
+		return ("UNSUPPORTED_MEDIA_TYPE");
+	case SIP_UNSUPPORTED_URI_SCHEME:
+		return ("UNSUPPORTED_URI_SCHEME");
+	case SIP_BAD_EXTENSION:
+		return ("BAD_EXTENSION");
+	case SIP_EXTENSION_REQUIRED:
+		return ("EXTENSION_REQUIRED");
+	case SIP_INTERVAL_2_BRIEF:
+		return ("INTERVAL_2_BRIEF");
+	case SIP_TEMPORARILY_UNAVAIL:
+		return ("TEMPORARILY_UNAVAIL");
+	case SIP_CALL_NON_EXISTANT:
+		return ("CALL_NON_EXISTANT");
+	case SIP_LOOP_DETECTED:
+		return ("LOOP_DETECTED");
+	case SIP_TOO_MANY_HOOPS:
+		return ("TOO_MANY_HOOPS");
+	case SIP_ADDRESS_INCOMPLETE:
+		return ("ADDRESS_INCOMPLETE");
+	case SIP_AMBIGUOUS:
+		return ("AMBIGUOUS");
+	case SIP_BUSY_HERE:
+		return ("BUSY_HERE");
+	case SIP_REQUEST_TERMINATED:
+		return ("REQUEST_TERMINATED");
+	case SIP_NOT_ACCEPTABLE_HERE:
+		return ("NOT_ACCEPTABLE_HERE");
+	case SIP_BAD_EVENT:
+		return ("BAD_EVENT");
+	case SIP_REQUEST_PENDING:
+		return ("REQUEST_PENDING");
+	case SIP_UNDECIPHERABLE:
+		return ("UNDECIPHERABLE");
+	case SIP_SERVER_INTERNAL_ERROR:
+		return ("SERVER_INTERNAL_ERROR");
+	case SIP_NOT_IMPLEMENTED:
+		return ("NOT_IMPLEMENTED");
+	case SIP_BAD_GATEWAY:
+		return ("BAD_GATEWAY");
+	case SIP_SERVICE_UNAVAILABLE:
+		return ("SERVICE_UNAVAILABLE");
+	case SIP_SERVER_TIMEOUT:
+		return ("SERVER_TIMEOUT");
+	case SIP_VERSION_NOT_SUPPORTED:
+		return ("VERSION_NOT_SUPPORTED");
+	case SIP_MESSAGE_2_LARGE:
+		return ("MESSAGE_2_LARGE");
+	case SIP_BUSY_EVERYWHERE:
+		return ("BUSY_EVERYWHERE");
+	case SIP_DECLINE:
+		return ("DECLINE");
+	case SIP_DOES_NOT_EXIST_ANYWHERE:
+		return ("DOES_NOT_EXIST_ANYWHERE");
+	case SIP_NOT_ACCEPTABLE_ANYWHERE:
+		return ("NOT_ACCEPTABLE_ANYWHERE");
+	default:
+		return ("UNKNOWN");
+	}
+}
+
+/*
+ * The following three fns initialize and destroy the private library
+ * data in sip_conn_object_t. The assumption is that the 1st member
+ * of sip_conn_object_t is reserved for library use. The private data
+ * is used only for byte-stream protocols such as TCP to accumulate
+ * a complete SIP message, based on the CONTENT-LENGTH value, before
+ * processing it.
+ */
+int
+sip_init_conn_object(sip_conn_object_t obj)
+{
+	void			**obj_val;
+	sip_conn_obj_pvt_t	*pvt_data;
+
+	if (obj == NULL)
+		return (EINVAL);
+	pvt_data =  malloc(sizeof (sip_conn_obj_pvt_t));
+	if (pvt_data == NULL)
+		return (ENOMEM);
+	pvt_data->sip_conn_obj_cache = NULL;
+	pvt_data->sip_conn_obj_reass = malloc(sizeof (sip_reass_entry_t));
+	if (pvt_data->sip_conn_obj_reass == NULL) {
+		free(pvt_data);
+		return (ENOMEM);
+	}
+	bzero(pvt_data->sip_conn_obj_reass, sizeof (sip_reass_entry_t));
+	(void) pthread_mutex_init(&pvt_data->sip_conn_obj_reass_lock, NULL);
+	(void) pthread_mutex_init(&pvt_data->sip_conn_obj_cache_lock, NULL);
+	sip_refhold_conn(obj);
+	obj_val = (void *)obj;
+	*obj_val = (void *)pvt_data;
+
+	return (0);
+}
+
+/*
+ * Clear private date, if any
+ */
+void
+sip_clear_stale_data(sip_conn_object_t obj)
+{
+	void			**obj_val;
+	sip_conn_obj_pvt_t	*pvt_data;
+	sip_reass_entry_t	*reass;
+
+	if (obj == NULL)
+		return;
+	obj_val = (void *)obj;
+	pvt_data = (sip_conn_obj_pvt_t *)*obj_val;
+	(void) pthread_mutex_lock(&pvt_data->sip_conn_obj_reass_lock);
+	reass = pvt_data->sip_conn_obj_reass;
+	if (reass->sip_reass_msg != NULL) {
+		assert(reass->sip_reass_msglen > 0);
+		free(reass->sip_reass_msg);
+		reass->sip_reass_msglen = 0;
+	}
+	assert(reass->sip_reass_msglen == 0);
+	(void) pthread_mutex_unlock(&pvt_data->sip_conn_obj_reass_lock);
+}
+
+/*
+ * Walk through all the transactions, remove if this obj has been cached
+ * by any.
+ */
+void
+sip_conn_destroyed(sip_conn_object_t obj)
+{
+	void			**obj_val;
+	sip_conn_obj_pvt_t	*pvt_data;
+
+	if (obj == NULL)
+		return;
+	obj_val = (void *)obj;
+	pvt_data = (sip_conn_obj_pvt_t *)*obj_val;
+
+	sip_clear_stale_data(obj);
+	free(pvt_data->sip_conn_obj_reass);
+	pvt_data->sip_conn_obj_reass = NULL;
+	(void) pthread_mutex_destroy(&pvt_data->sip_conn_obj_reass_lock);
+
+	sip_del_conn_obj_cache(obj, NULL);
+	(void) pthread_mutex_destroy(&pvt_data->sip_conn_obj_cache_lock);
+
+	free(pvt_data);
+	*obj_val = NULL;
+	sip_refrele_conn(obj);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/lib/libsip/common/sip_uri_ui.c	Sat Oct 07 14:26:26 2006 -0700
@@ -0,0 +1,471 @@
+/*
+ * 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 <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <sys/errno.h>
+
+#include "sip_parse_uri.h"
+
+void
+sip_free_parsed_uri(sip_uri_t uri)
+{
+	_sip_uri_t	*_uri;
+
+	if (uri == NULL)
+		return;
+
+	_uri = (_sip_uri_t *)uri;
+	if (_uri->sip_uri_issip) {
+		sip_param_t	*param;
+		sip_param_t	*param_next;
+
+		param = _uri->sip_uri_params;
+		while (param != NULL) {
+			param_next = param->param_next;
+			free(param);
+			param = param_next;
+		}
+	}
+	free(_uri);
+}
+
+/*
+ * Parse the URI in uri_str
+ */
+struct sip_uri *
+sip_parse_uri(sip_str_t *uri_str, int *error)
+{
+	struct sip_uri	*parsed_uri;
+
+	if (error != NULL)
+		*error = 0;
+
+	if (uri_str == NULL) {
+		if (error != NULL)
+			*error = EINVAL;
+		return (NULL);
+	}
+	parsed_uri = calloc(1, sizeof (_sip_uri_t));
+	if (parsed_uri == NULL) {
+		if (error != NULL)
+			*error = ENOMEM;
+		return (NULL);
+	}
+
+	sip_uri_parse_it(parsed_uri, uri_str);
+	if (parsed_uri->sip_uri_errflags & SIP_URIERR_MEMORY) {
+		free(parsed_uri);
+		if (error != NULL)
+			*error = ENOMEM;
+		return (NULL);
+	}
+	if (parsed_uri->sip_uri_errflags != 0 && error != NULL)
+		*error = EPROTO;
+	return ((sip_uri_t)parsed_uri);
+}
+
+/*
+ * Get parsed URI
+ */
+const struct sip_uri *
+sip_get_uri_parsed(sip_header_value_t value, int *error)
+{
+	const struct sip_uri	*ret = NULL;
+
+	if (error != NULL)
+		*error = 0;
+	if (value == NULL || value->sip_value_parse_uri == NULL ||
+	    value->value_state == SIP_VALUE_DELETED) {
+		if (error != NULL)
+			*error = EINVAL;
+		return (NULL);
+	}
+	ret = value->sip_value_parse_uri;
+	if (ret->sip_uri_errflags != 0 && error != NULL)
+		*error = EINVAL;
+	return ((sip_uri_t)ret);
+}
+
+/*
+ * Return TRUE if this is a SIP URI
+ */
+boolean_t
+sip_is_sipuri(const struct sip_uri *uri)
+{
+	_sip_uri_t	*_uri;
+
+	if (uri == NULL)
+		return (B_FALSE);
+	_uri = (_sip_uri_t *)uri;
+	if ((_uri->sip_uri_errflags & SIP_URIERR_SCHEME) == 0 &&
+	    _uri->sip_uri_scheme.sip_str_len > 0 && _uri->sip_uri_issip) {
+		return (B_TRUE);
+	}
+	return (B_FALSE);
+}
+
+/*
+ * Some common checks
+ */
+static _sip_uri_t *
+sip_check_get_param(const struct sip_uri *uri, int *error)
+{
+	if (error != NULL)
+		*error = 0;
+
+	if (uri == NULL) {
+		if (error != NULL)
+			*error = EINVAL;
+		return (NULL);
+	}
+	return ((_sip_uri_t *)uri);
+}
+
+
+/*
+ * Return the URI scheme
+ */
+const sip_str_t *
+sip_get_uri_scheme(const struct sip_uri *uri, int *error)
+{
+	_sip_uri_t	*_uri;
+
+	_uri = sip_check_get_param(uri, error);
+	if (_uri == NULL)
+		return (NULL);
+
+	if (((_uri->sip_uri_errflags & SIP_URIERR_SCHEME) != 0 ||
+	    _uri->sip_uri_scheme.sip_str_len == 0) && error != NULL) {
+		*error = EINVAL;
+	}
+	if (_uri->sip_uri_scheme.sip_str_len > 0)
+		return (&_uri->sip_uri_scheme);
+	return (NULL);
+}
+
+/*
+ *  Return user name from URI
+ */
+const sip_str_t *
+sip_get_uri_user(const struct sip_uri *uri, int *error)
+{
+	_sip_uri_t	*_uri;
+
+	_uri = sip_check_get_param(uri, error);
+	if (_uri == NULL)
+		return (NULL);
+
+	if ((_uri->sip_uri_errflags & SIP_URIERR_USER) != 0 && error != NULL)
+		*error = EINVAL;
+	if (uri->sip_uri_user.sip_str_len > 0)
+		return (&uri->sip_uri_user);
+	return (NULL);
+}
+
+/*
+ *  Return password from URI
+ */
+const sip_str_t *
+sip_get_uri_password(const struct sip_uri *uri, int *error)
+{
+	_sip_uri_t	*_uri;
+
+	_uri = sip_check_get_param(uri, error);
+	if (_uri == NULL)
+		return (NULL);
+
+	if ((_uri->sip_uri_errflags & SIP_URIERR_PASS) != 0 && error != NULL)
+		*error = EINVAL;
+	if (_uri->sip_uri_password.sip_str_len > 0)
+		return (&_uri->sip_uri_password);
+	return (NULL);
+}
+
+/*
+ * Get host from the URI
+ */
+const sip_str_t *
+sip_get_uri_host(const struct sip_uri *uri, int *error)
+{
+	_sip_uri_t	*_uri;
+
+	_uri = sip_check_get_param(uri, error);
+	if (_uri == NULL)
+		return (NULL);
+
+	if ((_uri->sip_uri_errflags & SIP_URIERR_HOST) != 0 && error != NULL)
+		*error = EINVAL;
+	if (_uri->sip_uri_host.sip_str_len > 0)
+		return (&_uri->sip_uri_host);
+	return (NULL);
+}
+
+/*
+ * Get port from the URI
+ */
+int
+sip_get_uri_port(const struct sip_uri *uri, int *error)
+{
+	_sip_uri_t	*_uri;
+
+	_uri = sip_check_get_param(uri, error);
+	if (_uri == NULL)
+		return (NULL);
+
+	if ((_uri->sip_uri_errflags & SIP_URIERR_PORT) != 0) {
+		if (error != NULL)
+			*error = EINVAL;
+		return (0);
+	}
+	return (_uri->sip_uri_port);
+}
+
+const sip_param_t *
+sip_get_uri_params(const struct sip_uri *uri, int *error)
+{
+	_sip_uri_t		*_uri;
+
+	_uri = sip_check_get_param(uri, error);
+	if (_uri == NULL)
+		return (NULL);
+
+	if (!_uri->sip_uri_issip) {
+		if (error != NULL)
+			*error = EINVAL;
+		return (NULL);
+	}
+
+	if ((_uri->sip_uri_errflags & SIP_URIERR_PARAM) != 0 && error != NULL)
+		*error = EINVAL;
+	return (_uri->sip_uri_params);
+}
+
+/*
+ * Get headers from the URI
+ */
+const sip_str_t *
+sip_get_uri_headers(const struct sip_uri *uri, int *error)
+{
+	_sip_uri_t	*_uri;
+
+	_uri = sip_check_get_param(uri, error);
+	if (_uri == NULL)
+		return (NULL);
+
+	if (!_uri->sip_uri_issip) {
+		if (error != NULL)
+			*error = EINVAL;
+		return (NULL);
+	}
+	if ((_uri->sip_uri_errflags & SIP_URIERR_HEADER) != 0 && error != NULL)
+		*error = EINVAL;
+	if (_uri->sip_uri_headers.sip_str_len > 0)
+		return (&_uri->sip_uri_headers);
+	return (NULL);
+}
+
+/*
+ *  Return opaque value for an ABS URI
+ */
+const sip_str_t *
+sip_get_uri_opaque(const struct sip_uri *uri, int *error)
+{
+	_sip_uri_t	*_uri;
+
+	_uri = sip_check_get_param(uri, error);
+	if (_uri == NULL)
+		return (NULL);
+
+	if (_uri->sip_uri_issip) {
+		if (error != NULL)
+			*error = EINVAL;
+		return (NULL);
+	}
+	if ((_uri->sip_uri_errflags & SIP_URIERR_OPAQUE) != 0 && error != NULL)
+		*error = EINVAL;
+	if (_uri->sip_uri_opaque.sip_str_len > 0)
+		return (&_uri->sip_uri_opaque);
+	return (NULL);
+}
+
+/*
+ * Return query from an absolute URI
+ */
+const sip_str_t *
+sip_get_uri_query(const struct sip_uri *uri, int *error)
+{
+	_sip_uri_t	*_uri;
+
+	_uri = sip_check_get_param(uri, error);
+	if (_uri == NULL)
+		return (NULL);
+
+	if (_uri->sip_uri_issip) {
+		if (error != NULL)
+			*error = EINVAL;
+		return (NULL);
+	}
+	if ((_uri->sip_uri_errflags & SIP_URIERR_QUERY) != 0 && error != NULL)
+		*error = EINVAL;
+	if (_uri->sip_uri_query.sip_str_len > 0)
+		return (&_uri->sip_uri_query);
+	return (NULL);
+}
+
+/*
+ *  Get path from an assolute URI
+ */
+const sip_str_t *
+sip_get_uri_path(const struct sip_uri *uri, int *error)
+{
+	_sip_uri_t	*_uri;
+
+	_uri = sip_check_get_param(uri, error);
+	if (_uri == NULL)
+		return (NULL);
+
+	if (_uri->sip_uri_issip) {
+		if (error != NULL)
+			*error = EINVAL;
+		return (NULL);
+	}
+	if ((_uri->sip_uri_errflags & SIP_URIERR_PATH) != 0 && error != NULL)
+		*error = EINVAL;
+	if (_uri->sip_uri_path.sip_str_len > 0)
+		return (&_uri->sip_uri_path);
+	return (NULL);
+}
+
+/*
+ * Get the reg-name from absolute URI
+ */
+const sip_str_t	*
+sip_get_uri_regname(const struct sip_uri *uri, int *error)
+{
+	_sip_uri_t	*_uri;
+
+	_uri = sip_check_get_param(uri, error);
+	if (_uri == NULL)
+		return (NULL);
+
+	if (_uri->sip_uri_issip) {
+		if (error != NULL)
+			*error = EINVAL;
+		return (NULL);
+	}
+	if ((_uri->sip_uri_errflags & SIP_URIERR_REGNAME) != 0 && error != NULL)
+		*error = EINVAL;
+	if (_uri->sip_uri_regname.sip_str_len > 0)
+		return (&_uri->sip_uri_regname);
+	return (NULL);
+}
+
+/*
+ * Return TRUE if this is a teluser
+ */
+boolean_t
+sip_is_uri_teluser(const struct sip_uri *uri)
+{
+	_sip_uri_t	*_uri;
+
+	if (uri == NULL)
+		return (B_FALSE);
+
+	_uri = (_sip_uri_t *)uri;
+	return (_uri->sip_uri_isteluser);
+}
+
+int
+sip_get_uri_errflags(const struct sip_uri *uri, int *error)
+{
+	_sip_uri_t	*_uri;
+
+	_uri = sip_check_get_param(uri, error);
+	if (_uri == NULL)
+		return (0);
+	return (_uri->sip_uri_errflags);
+}
+
+/*
+ * the caller is responsible for freeing the returned string
+ */
+char *
+sip_uri_errflags_to_str(int errflags)
+{
+	char	*err_info = NULL;
+
+	if (errflags == 0)
+		return (NULL);
+
+	err_info = (char *)malloc(SIP_URI_BUF_SIZE);
+	if (err_info == NULL)
+		return (NULL);
+
+	if (errflags & SIP_URIERR_NOURI) {
+		(void) strncpy(err_info, "Error : No URI",
+		    strlen("Error : No URI"));
+		err_info[strlen("Error : No URI")] = '\0';
+		return (err_info);
+	}
+
+	(void) strncpy(err_info, "Error(s) in", strlen("Error(s) in"));
+	err_info[strlen("Error(s) in")] = '\0';
+	if (errflags & SIP_URIERR_SCHEME)
+		(void) strncat(err_info, " SCHEME,", strlen(" SCHEME,"));
+	if (errflags & SIP_URIERR_USER)
+		(void) strncat(err_info, " USER,", strlen(" USER,"));
+	if (errflags & SIP_URIERR_PASS)
+		(void) strncat(err_info, " PASSWORD,", strlen(" PASSWORD,"));
+	if (errflags & SIP_URIERR_HOST)
+		(void) strncat(err_info, " HOST,", strlen(" HOST,"));
+	if (errflags & SIP_URIERR_PORT)
+		(void) strncat(err_info, " PORT,", strlen(" PORT,"));
+	if (errflags & SIP_URIERR_PARAM) {
+		(void) strncat(err_info, " PARAMETERS,",
+		    strlen(" PARAMETERS,"));
+	}
+	if (errflags & SIP_URIERR_HEADER)
+		(void) strncat(err_info, " HEADERS,", strlen(" HEADERS,"));
+	if (errflags & SIP_URIERR_OPAQUE)
+		(void) strncat(err_info, " OPAQUE,", strlen(" OPAQUE,"));
+	if (errflags & SIP_URIERR_QUERY)
+		(void) strncat(err_info, " QUERY,", strlen(" QUERY,"));
+	if (errflags & SIP_URIERR_PATH)
+		(void) strncat(err_info, " PATH,", strlen(" PATH,"));
+	if (errflags & SIP_URIERR_REGNAME)
+		(void) strncat(err_info, " REG-NAME,", strlen(" REG-NAME,"));
+	if (strlen(err_info) == strlen("Error(s) in")) {
+		free(err_info);
+		err_info = NULL;
+	} else {
+		err_info[strlen(err_info) - 1] = '\0';
+		(void) strncat(err_info, " part(s)", strlen(" part(s)"));
+	}
+	return (err_info);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/lib/libsip/common/sip_xaction.c	Sat Oct 07 14:26:26 2006 -0700
@@ -0,0 +1,631 @@
+/*
+ * 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_xaction.h"
+#include "sip_hash.h"
+
+#define	RFC_3261_BRANCH "z9hG4bK"
+
+/*
+ * The transaction hash table
+ */
+sip_hash_t	sip_xaction_hash[SIP_HASH_SZ];
+
+int (*sip_xaction_ulp_trans_err)(sip_transaction_t, int, void *) = NULL;
+void (*sip_xaction_ulp_state_cb)(sip_transaction_t, sip_msg_t, int, int) = NULL;
+
+int sip_xaction_add(sip_xaction_t *, char *, _sip_msg_t *, sip_method_t);
+static boolean_t sip_is_conn_obj_cache(sip_conn_object_t, void *);
+
+/*
+ * Get the md5 hash of the required fields
+ */
+int
+sip_find_md5_digest(char *bid, _sip_msg_t *msg, uint16_t *hindex,
+    sip_method_t method)
+{
+	boolean_t	is_2543;
+
+	is_2543 = (bid == NULL ||
+	    strncmp(bid, RFC_3261_BRANCH, strlen(RFC_3261_BRANCH)) != 0);
+
+	if (is_2543 && msg == NULL)
+		return (EINVAL);
+	if (is_2543) {
+		_sip_header_t	*from = NULL;
+		_sip_header_t	*cid = NULL;
+		_sip_header_t	*via = NULL;
+		const sip_str_t	*to_uri = NULL;
+		int		cseq;
+		int		error = 0;
+
+		/*
+		 * Since the response might contain parameters not in the
+		 * request, just use the to URI.
+		 */
+		to_uri = sip_get_to_uri_str((sip_msg_t)msg, &error);
+		if (to_uri == NULL || error != 0)
+			return (EINVAL);
+		cseq = sip_get_callseq_num((sip_msg_t)msg, &error);
+		if (cseq < 0 || error != 0)
+			return (EINVAL);
+		(void) pthread_mutex_lock(&msg->sip_msg_mutex);
+		via = sip_search_for_header(msg, SIP_VIA, NULL);
+		from = sip_search_for_header(msg, SIP_FROM, NULL);
+		cid = sip_search_for_header(msg, SIP_CALL_ID, NULL);
+		(void) pthread_mutex_unlock(&msg->sip_msg_mutex);
+		if (via == NULL || from == NULL || cid == NULL)
+			return (EINVAL);
+		sip_md5_hash(via->sip_hdr_start,
+		    via->sip_hdr_end - via->sip_hdr_start,
+		    cid->sip_hdr_start,
+		    cid->sip_hdr_end - cid->sip_hdr_start,
+		    from->sip_hdr_start,
+		    from->sip_hdr_end - from->sip_hdr_start,
+		    (char *)&cseq, sizeof (int),
+		    (char *)&method, sizeof (sip_method_t),
+		    to_uri->sip_str_ptr, to_uri->sip_str_len,
+		    (uchar_t *)hindex);
+	} else {
+		sip_md5_hash(bid, strlen(bid), (char *)&method,
+		    sizeof (sip_method_t), NULL, 0, NULL, 0, NULL, 0, NULL, 0,
+		    (uchar_t *)hindex);
+	}
+	return (0);
+}
+
+/*
+ * Add object to the connection cache object. Not checking for duplicates!!
+ */
+int
+sip_add_conn_obj_cache(sip_conn_object_t obj, void *cobj)
+{
+	void			**obj_val;
+	sip_conn_obj_pvt_t	*pvt_data;
+	sip_conn_cache_t	*xaction_list;
+	sip_xaction_t		*sip_trans = (sip_xaction_t *)cobj;
+
+	/*
+	 * Is already cached
+	 */
+	if (sip_trans->sip_xaction_conn_obj != NULL) {
+		if (sip_is_conn_obj_cache(sip_trans->sip_xaction_conn_obj,
+		    (void *)sip_trans)) {
+			return (0);
+		}
+		/*
+		 * Transaction has cached a different conn_obj, release it
+		 */
+		sip_del_conn_obj_cache(sip_trans->sip_xaction_conn_obj,
+		    (void *)sip_trans);
+	}
+
+	xaction_list = malloc(sizeof (sip_conn_cache_t));
+	if (xaction_list == NULL)
+		return (ENOMEM);
+	xaction_list->obj = cobj;
+	xaction_list->next = xaction_list->prev = NULL;
+
+	obj_val = (void *)obj;
+	pvt_data = (sip_conn_obj_pvt_t *)*obj_val;
+	if (pvt_data == NULL) {
+		free(xaction_list);
+		return (EINVAL);
+	}
+	(void) pthread_mutex_lock(&pvt_data->sip_conn_obj_cache_lock);
+
+	if (pvt_data->sip_conn_obj_cache == NULL) {
+		pvt_data->sip_conn_obj_cache = xaction_list;
+	} else {
+		xaction_list->next =  pvt_data->sip_conn_obj_cache;
+		pvt_data->sip_conn_obj_cache->prev = xaction_list;
+		pvt_data->sip_conn_obj_cache = xaction_list;
+	}
+	sip_refhold_conn(obj);
+	sip_trans->sip_xaction_conn_obj = obj;
+	(void) pthread_mutex_unlock(&pvt_data->sip_conn_obj_cache_lock);
+	return (0);
+}
+
+/*
+ * Walk thru the list of transactions that have cached this obj and
+ * and return true if 'cobj' is one of them.
+ */
+static boolean_t
+sip_is_conn_obj_cache(sip_conn_object_t obj, void *cobj)
+{
+	void			**obj_val;
+	sip_conn_obj_pvt_t	*pvt_data;
+	sip_conn_cache_t	*xaction_list;
+	sip_xaction_t		*trans;
+	sip_xaction_t		*ctrans = (sip_xaction_t *)cobj;
+
+	obj_val = (void *)obj;
+	pvt_data = (sip_conn_obj_pvt_t *)*obj_val;
+	if (pvt_data == NULL)
+		return (B_FALSE);
+	(void) pthread_mutex_lock(&pvt_data->sip_conn_obj_cache_lock);
+	xaction_list = pvt_data->sip_conn_obj_cache;
+	while (xaction_list != NULL) {
+		trans = (sip_xaction_t *)xaction_list->obj;
+		if (ctrans != trans) {
+			xaction_list = xaction_list->next;
+			continue;
+		}
+		(void) pthread_mutex_unlock(&pvt_data->sip_conn_obj_cache_lock);
+		return (B_TRUE);
+	}
+	(void) pthread_mutex_unlock(&pvt_data->sip_conn_obj_cache_lock);
+	return (B_FALSE);
+}
+
+
+/*
+ * Walk thru the list of transactions that have cached this obj and
+ * refrele the objs.
+ */
+void
+sip_del_conn_obj_cache(sip_conn_object_t obj, void *cobj)
+{
+	void			**obj_val;
+	sip_conn_obj_pvt_t	*pvt_data;
+	sip_conn_cache_t	*xaction_list;
+	sip_conn_cache_t	*tmp_list;
+	sip_xaction_t		*trans;
+	sip_xaction_t		*ctrans = NULL;
+
+	if (cobj != NULL)
+		ctrans = (sip_xaction_t *)cobj;
+
+	obj_val = (void *)obj;
+	pvt_data = (sip_conn_obj_pvt_t *)*obj_val;
+	if (pvt_data == NULL) {	/* ASSERT FALSE if ctrans != NULL?? */
+		if (ctrans != NULL) {
+			sip_refrele_conn(obj);
+			ctrans->sip_xaction_conn_obj = NULL;
+		}
+		return;
+	}
+	(void) pthread_mutex_lock(&pvt_data->sip_conn_obj_cache_lock);
+	xaction_list = pvt_data->sip_conn_obj_cache;
+	while (xaction_list != NULL) {
+		tmp_list = xaction_list;
+		trans = (sip_xaction_t *)xaction_list->obj;
+		assert(trans != NULL);
+		if (ctrans != NULL && ctrans != trans) {
+			xaction_list = xaction_list->next;
+			continue;
+		}
+		if (ctrans == NULL)
+			(void) pthread_mutex_lock(&trans->sip_xaction_mutex);
+		assert(trans->sip_xaction_conn_obj == obj);
+		sip_refrele_conn(obj);
+		trans->sip_xaction_conn_obj = NULL;
+		if (ctrans == NULL)
+			(void) pthread_mutex_unlock(&trans->sip_xaction_mutex);
+		xaction_list = xaction_list->next;
+
+		/*
+		 * Take the obj out of the list
+		 */
+		if (tmp_list == pvt_data->sip_conn_obj_cache) {
+			if (xaction_list == NULL) {
+				pvt_data->sip_conn_obj_cache = NULL;
+			} else {
+				xaction_list->prev = NULL;
+				pvt_data->sip_conn_obj_cache = xaction_list;
+			}
+		} else if (xaction_list == NULL) {
+			assert(tmp_list->prev != NULL);
+			tmp_list->prev->next = NULL;
+		} else {
+			assert(tmp_list->prev != NULL);
+			tmp_list->prev->next = xaction_list;
+			xaction_list->prev = tmp_list->prev;
+		}
+		tmp_list->prev = NULL;
+		tmp_list->next = NULL;
+		tmp_list->obj = NULL;
+
+		free(tmp_list);
+	}
+	(void) pthread_mutex_unlock(&pvt_data->sip_conn_obj_cache_lock);
+}
+
+/*
+ * Check for a transaction match. Passed to sip_hash_find().
+ */
+boolean_t
+sip_xaction_match(void *obj, void *hindex)
+{
+	sip_xaction_t	*tmp = (sip_xaction_t *)obj;
+
+	tmp = (sip_xaction_t *)obj;
+
+	if (SIP_IS_XACTION_TERMINATED(tmp->sip_xaction_state))
+		return (B_FALSE);
+	if (bcmp(tmp->sip_xaction_hash_digest, hindex,
+	    sizeof (tmp->sip_xaction_hash_digest)) == 0) {
+		SIP_XACTION_REFCNT_INCR(tmp);
+		return (B_TRUE);
+	}
+	return (B_FALSE);
+}
+
+
+/*
+ * Find a transaction
+ */
+static sip_xaction_t *
+sip_xaction_find(char *branchid, _sip_msg_t *msg, int which)
+{
+	sip_xaction_t		*tmp;
+	uint16_t		hash_index[8];
+	int			hindex;
+	sip_method_t		method;
+	int			error;
+	sip_message_type_t	*sip_msg_info;
+
+	sip_msg_info = msg->sip_msg_req_res;
+	method = sip_get_callseq_method((sip_msg_t)msg, &error);
+	if (error != 0)
+		return (NULL);
+
+	/*
+	 * If we are getting a ACK/CANCEL we need to match with the
+	 * corresponding INVITE, if any.
+	 */
+	if (sip_msg_info->is_request && which == SIP_SERVER_TRANSACTION &&
+	    (method == ACK || method == CANCEL)) {
+		method = INVITE;
+	}
+	if (sip_find_md5_digest(branchid, msg, hash_index, method) != 0)
+		return (NULL);
+	hindex = SIP_DIGEST_TO_HASH(hash_index);
+	tmp = (sip_xaction_t *)sip_hash_find(sip_xaction_hash,
+	    (void *)hash_index, hindex, sip_xaction_match);
+	return (tmp);
+}
+
+/*
+ * create a transaction.
+ */
+static sip_xaction_t *
+sip_xaction_create(sip_conn_object_t obj, _sip_msg_t *msg, char *branchid,
+    int *error)
+{
+	sip_xaction_t		*trans;
+	sip_message_type_t	*sip_msg_info;
+	int			state = 0;
+	int			prev_state = 0;
+	sip_method_t		method;
+	int			ret;
+	int			timer1 = sip_timer_T1;
+	int			timer4 = sip_timer_T4;
+	int			timerd = sip_timer_TD;
+
+	if (error != NULL)
+		*error = 0;
+	/*
+	 * Make sure we are not creating a transaction for
+	 * an ACK request.
+	 */
+	trans = (sip_xaction_t *)malloc(sizeof (sip_xaction_t));
+	if (trans == NULL) {
+		if (error != NULL)
+			*error = ENOMEM;
+		return (NULL);
+	}
+	bzero(trans, sizeof (sip_xaction_t));
+	if (branchid == NULL) {
+		trans->sip_xaction_branch_id = (char *)sip_branchid(NULL);
+		if (trans->sip_xaction_branch_id == NULL) {
+			free(trans);
+			if (error != NULL)
+				*error = ENOMEM;
+			return (NULL);
+		}
+	} else {
+		trans->sip_xaction_branch_id = (char *)malloc(strlen(branchid)
+		    + 1);
+		if (trans->sip_xaction_branch_id == NULL) {
+			free(trans);
+			if (error != NULL)
+				*error = ENOMEM;
+			return (NULL);
+		}
+		(void) strncpy(trans->sip_xaction_branch_id, branchid,
+		    strlen(branchid));
+		trans->sip_xaction_branch_id[strlen(branchid)] = '\0';
+	}
+	(void) pthread_mutex_init(&trans->sip_xaction_mutex, NULL);
+	SIP_MSG_REFCNT_INCR(msg);
+	trans->sip_xaction_orig_msg = msg;
+	assert(msg->sip_msg_req_res != NULL);
+	sip_msg_info = msg->sip_msg_req_res;
+	if (sip_msg_info->is_request) {
+		method = sip_msg_info->sip_req_method;
+	} else {
+		method = sip_get_callseq_method((sip_msg_t)msg, &ret);
+		if (ret != 0) {
+			free(trans->sip_xaction_branch_id);
+			free(trans);
+			if (error != NULL)
+				*error = ret;
+			return (NULL);
+		}
+		if (method == INVITE)
+			state = SIP_SRV_INV_PROCEEDING;
+		else
+			state = SIP_SRV_TRYING;
+	}
+	trans->sip_xaction_method = method;
+	trans->sip_xaction_state = state;
+
+	/*
+	 * Get connection object specific timeouts, if present
+	 */
+	if (sip_conn_timer1 != NULL)
+		timer1 = sip_conn_timer1(obj);
+	if (sip_conn_timer4 != NULL)
+		timer4 = sip_conn_timer4(obj);
+	if (sip_conn_timerd != NULL)
+		timerd = sip_conn_timerd(obj);
+
+	SIP_INIT_TIMER(trans->sip_xaction_TA, 2 * timer1);
+	SIP_INIT_TIMER(trans->sip_xaction_TB, 64 * timer1)
+	SIP_INIT_TIMER(trans->sip_xaction_TD,  timerd);
+	SIP_INIT_TIMER(trans->sip_xaction_TE, timer1);
+	SIP_INIT_TIMER(trans->sip_xaction_TF, 64 * timer1);
+	SIP_INIT_TIMER(trans->sip_xaction_TG, 2 * timer1);
+	SIP_INIT_TIMER(trans->sip_xaction_TH, 64 * timer1);
+	SIP_INIT_TIMER(trans->sip_xaction_TI, timer4);
+	SIP_INIT_TIMER(trans->sip_xaction_TJ, 64 * timer1);
+	SIP_INIT_TIMER(trans->sip_xaction_TK, timer4);
+
+	if ((ret = sip_xaction_add(trans, branchid, msg, method)) != 0) {
+		(void) pthread_mutex_destroy(&trans->sip_xaction_mutex);
+		free(trans->sip_xaction_branch_id);
+		free(trans);
+		if (error != NULL)
+			*error = ret;
+		return (NULL);
+	}
+	if (sip_xaction_ulp_state_cb != NULL &&
+	    prev_state != trans->sip_xaction_state) {
+		sip_xaction_ulp_state_cb((sip_transaction_t)trans,
+		    (sip_msg_t)msg, prev_state, trans->sip_xaction_state);
+	}
+	return (trans);
+}
+
+/*
+ * Find a transaction, create if asked for
+ */
+sip_xaction_t *
+sip_xaction_get(sip_conn_object_t obj, sip_msg_t msg, boolean_t create,
+    int which, int *error)
+{
+	char			*branchid;
+	sip_xaction_t		*sip_trans;
+	_sip_msg_t		*_msg;
+	sip_message_type_t	*sip_msg_info;
+
+	if (error != NULL)
+		*error = 0;
+
+	_msg = (_sip_msg_t *)msg;
+	sip_msg_info = ((_sip_msg_t *)msg)->sip_msg_req_res;
+
+	branchid = sip_get_branchid(msg, NULL);
+	sip_trans = sip_xaction_find(branchid, _msg, which);
+	if (sip_trans == NULL && create) {
+		/*
+		 * If we are sending a request, must be conformant to RFC 3261.
+		 */
+		if (sip_msg_info->is_request &&
+		    (branchid == NULL || strncmp(branchid,
+		    RFC_3261_BRANCH, strlen(RFC_3261_BRANCH) != 0))) {
+			if (error != NULL)
+				*error = EINVAL;
+			if (branchid != NULL)
+				free(branchid);
+			return (NULL);
+		}
+		sip_trans = sip_xaction_create(obj, _msg, branchid, error);
+		if (sip_trans != NULL)
+			SIP_XACTION_REFCNT_INCR(sip_trans);
+	}
+	if (branchid != NULL)
+		free(branchid);
+	return (sip_trans);
+}
+
+
+/*
+ * Delete a transaction if the reference count is 0. Passed to
+ * sip_hash_delete().
+ */
+boolean_t
+sip_xaction_remove(void *obj, void *hindex, int *found)
+{
+	sip_xaction_t	*tmp = (sip_xaction_t *)obj;
+
+	*found = 0;
+	tmp = (sip_xaction_t *)obj;
+	(void) pthread_mutex_lock(&tmp->sip_xaction_mutex);
+	if (bcmp(tmp->sip_xaction_hash_digest, hindex,
+	    sizeof (tmp->sip_xaction_hash_digest)) == 0) {
+		*found = 1;
+		if (tmp->sip_xaction_ref_cnt != 0) {
+			(void) pthread_mutex_unlock(&tmp->sip_xaction_mutex);
+			return (B_FALSE);
+		}
+		(void) pthread_mutex_destroy(&tmp->sip_xaction_mutex);
+		SIP_CANCEL_TIMER(tmp->sip_xaction_TA);
+		SIP_CANCEL_TIMER(tmp->sip_xaction_TB);
+		SIP_CANCEL_TIMER(tmp->sip_xaction_TD);
+		SIP_CANCEL_TIMER(tmp->sip_xaction_TE);
+		SIP_CANCEL_TIMER(tmp->sip_xaction_TF);
+		SIP_CANCEL_TIMER(tmp->sip_xaction_TG);
+		SIP_CANCEL_TIMER(tmp->sip_xaction_TH);
+		SIP_CANCEL_TIMER(tmp->sip_xaction_TI);
+		SIP_CANCEL_TIMER(tmp->sip_xaction_TJ);
+		SIP_CANCEL_TIMER(tmp->sip_xaction_TK);
+		free(tmp->sip_xaction_branch_id);
+		if (tmp->sip_xaction_last_msg != NULL) {
+			SIP_MSG_REFCNT_DECR(tmp->sip_xaction_last_msg);
+			tmp->sip_xaction_last_msg = NULL;
+		}
+		if (tmp->sip_xaction_orig_msg != NULL) {
+			SIP_MSG_REFCNT_DECR(tmp->sip_xaction_orig_msg);
+			tmp->sip_xaction_orig_msg = NULL;
+		}
+		if (tmp->sip_xaction_conn_obj != NULL) {
+			sip_del_conn_obj_cache(tmp->sip_xaction_conn_obj,
+			    (void *)tmp);
+		}
+		free(tmp);
+		return (B_TRUE);
+	}
+	(void) pthread_mutex_unlock(&tmp->sip_xaction_mutex);
+	return (B_FALSE);
+}
+
+/*
+ * Delete a SIP transaction
+ */
+void
+sip_xaction_delete(sip_xaction_t *trans)
+{
+	int	hindex;
+
+	(void) pthread_mutex_lock(&trans->sip_xaction_mutex);
+	hindex = SIP_DIGEST_TO_HASH(trans->sip_xaction_hash_digest);
+	if (trans->sip_xaction_ref_cnt != 0) {
+		(void) pthread_mutex_unlock(&trans->sip_xaction_mutex);
+		return;
+	}
+	(void) pthread_mutex_unlock(&trans->sip_xaction_mutex);
+	sip_hash_delete(sip_xaction_hash, trans->sip_xaction_hash_digest,
+	    hindex, sip_xaction_remove);
+}
+
+/*
+ * Add a SIP transaction into the hash list.
+ */
+int
+sip_xaction_add(sip_xaction_t *trans, char *branchid, _sip_msg_t *msg,
+    sip_method_t method)
+{
+	uint16_t	hash_index[8];
+
+	if (sip_find_md5_digest(branchid, msg, hash_index, method) != 0)
+		return (EINVAL);
+
+	/*
+	 * trans is not in the list as yet, so no need to hold the lock
+	 */
+	bcopy(hash_index, trans->sip_xaction_hash_digest, sizeof (hash_index));
+
+	if (sip_hash_add(sip_xaction_hash, (void *)trans,
+	    SIP_DIGEST_TO_HASH(hash_index)) != 0) {
+		return (ENOMEM);
+	}
+	return (0);
+}
+
+
+/*
+ * Given a state, return the  string - This is mostly for debug purposes
+ */
+char *
+sip_get_xaction_state(int state)
+{
+	switch (state) {
+		case SIP_CLNT_CALLING:
+			return ("SIP_CLNT_CALLING");
+		case SIP_CLNT_INV_PROCEEDING:
+			return ("SIP_CLNT_INV_PROCEEDING");
+		case SIP_CLNT_INV_TERMINATED:
+			return ("SIP_CLNT_INV_TERMINATED");
+		case SIP_CLNT_INV_COMPLETED:
+			return ("SIP_CLNT_INV_COMPLETED");
+		case SIP_CLNT_TRYING:
+			return ("SIP_CLNT_TRYING");
+		case SIP_CLNT_NONINV_PROCEEDING:
+			return ("SIP_CLNT_NONINV_PROCEEDING");
+		case SIP_CLNT_NONINV_TERMINATED:
+			return ("SIP_CLNT_NONINV_TERMINATED");
+		case SIP_CLNT_NONINV_COMPLETED:
+			return ("SIP_CLNT_NONINV_COMPLETED");
+		case SIP_SRV_INV_PROCEEDING:
+			return ("SIP_SRV_INV_PROCEEDING");
+		case SIP_SRV_INV_COMPLETED:
+			return ("SIP_SRV_INV_COMPLETED");
+		case SIP_SRV_CONFIRMED:
+			return ("SIP_SRV_CONFIRMED");
+		case SIP_SRV_INV_TERMINATED:
+			return ("SIP_SRV_INV_TERMINATED");
+		case SIP_SRV_TRYING:
+			return ("SIP_SRV_TRYING");
+		case SIP_SRV_NONINV_PROCEEDING:
+			return ("SIP_SRV_NONINV_PROCEEDING");
+		case SIP_SRV_NONINV_COMPLETED:
+			return ("SIP_SRV_NONINV_COMPLETED");
+		case SIP_SRV_NONINV_TERMINATED:
+			return ("SIP_SRV_NONINV_TERMINATED");
+		default :
+			return ("unknown");
+	}
+}
+
+/*
+ * Initialize the hash table etc.
+ */
+void
+sip_xaction_init(int (*ulp_trans_err)(sip_transaction_t, int, void *),
+    void (*ulp_state_cb)(sip_transaction_t, sip_msg_t, int, int))
+{
+	int	cnt;
+
+	for (cnt = 0; cnt < SIP_HASH_SZ; cnt++) {
+		sip_xaction_hash[cnt].hash_count = 0;
+		sip_xaction_hash[cnt].hash_head = NULL;
+		sip_xaction_hash[cnt].hash_tail = NULL;
+		(void) pthread_mutex_init(
+		    &sip_xaction_hash[cnt].sip_hash_mutex, NULL);
+	}
+	if (ulp_trans_err != NULL)
+		sip_xaction_ulp_trans_err = ulp_trans_err;
+	if (ulp_state_cb != NULL)
+		sip_xaction_ulp_state_cb = ulp_state_cb;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/lib/libsip/common/sip_xaction.h	Sat Oct 07 14:26:26 2006 -0700
@@ -0,0 +1,127 @@
+/*
+ * 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.
+ */
+
+#ifndef	_SIP_XACTION_H
+#define	_SIP_XACTION_H
+
+#pragma ident	"%Z%%M%	%I%	%E% SMI"
+
+#ifdef	__cplusplus
+extern "C" {
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+/* Various transaction timers */
+typedef enum sip_timer_type_s {
+	SIP_XACTION_TIMER_A = 0,
+	SIP_XACTION_TIMER_B,
+	SIP_XACTION_TIMER_D,
+	SIP_XACTION_TIMER_E,
+	SIP_XACTION_TIMER_F,
+	SIP_XACTION_TIMER_G,
+	SIP_XACTION_TIMER_H,
+	SIP_XACTION_TIMER_I,
+	SIP_XACTION_TIMER_J,
+	SIP_XACTION_TIMER_K
+} sip_xaction_timer_type_t;
+
+
+/* Increment transaction reference count */
+#define	SIP_XACTION_REFCNT_INCR(trans)	\
+	(trans)->sip_xaction_ref_cnt++;
+
+/* Decrement transaction reference count */
+#define	SIP_XACTION_REFCNT_DECR(trans)	{				\
+	(void) pthread_mutex_lock(&((trans)->sip_xaction_mutex));	\
+	assert((trans)->sip_xaction_ref_cnt > 0);			\
+	(trans)->sip_xaction_ref_cnt--;					\
+	if ((trans)->sip_xaction_ref_cnt == 0 && 			\
+	    SIP_IS_XACTION_TERMINATED((trans)->sip_xaction_state)) {	\
+		(void) pthread_mutex_unlock(&((trans)->sip_xaction_mutex));\
+		sip_xaction_delete(trans);				\
+	} else {							\
+		(void) pthread_mutex_unlock(&((trans)->sip_xaction_mutex));\
+	}								\
+}
+
+/* True if transaction is in the terminated state */
+#define	SIP_IS_XACTION_TERMINATED(trans_state)				\
+	((trans_state) == SIP_CLNT_INV_TERMINATED ||			\
+	(trans_state) == SIP_CLNT_NONINV_TERMINATED	||		\
+	(trans_state) == SIP_SRV_INV_TERMINATED ||			\
+	(trans_state) == SIP_SRV_NONINV_TERMINATED)
+
+/* Transaction structure */
+typedef struct sip_xaction {
+	char			*sip_xaction_branch_id;	/* Transaction id */
+	uint16_t		sip_xaction_hash_digest[8];
+	_sip_msg_t		*sip_xaction_orig_msg;	/* orig request msg. */
+	_sip_msg_t		*sip_xaction_last_msg;	/* last msg sent */
+	sip_conn_object_t	sip_xaction_conn_obj;
+	int			sip_xaction_state;  /* Transaction State */
+	sip_method_t		sip_xaction_method;
+	uint32_t		sip_xaction_ref_cnt;
+	pthread_mutex_t		sip_xaction_mutex;
+	sip_timer_t		sip_xaction_TA;
+	sip_timer_t		sip_xaction_TB;
+	sip_timer_t		sip_xaction_TD;
+	sip_timer_t		sip_xaction_TE;
+	sip_timer_t		sip_xaction_TF;
+	sip_timer_t		sip_xaction_TG;
+	sip_timer_t		sip_xaction_TH;
+	sip_timer_t		sip_xaction_TI;
+	sip_timer_t		sip_xaction_TJ;
+	sip_timer_t		sip_xaction_TK;
+	void			*sip_xaction_ctxt;	/* currently unused */
+} sip_xaction_t;
+
+extern void		sip_xaction_init(int (*ulp_trans_err)(sip_transaction_t,
+			    int, void *), void (*ulp_state_cb)
+			    (sip_transaction_t, sip_msg_t, int, int));
+extern int		sip_xaction_output(sip_conn_object_t, sip_xaction_t *,
+			    _sip_msg_t *);
+extern int		sip_xaction_input(sip_conn_object_t, sip_xaction_t *,
+			    _sip_msg_t **);
+extern sip_xaction_t	*sip_xaction_get(sip_conn_object_t, sip_msg_t,
+			    boolean_t, int, int *);
+extern void		sip_xaction_delete(sip_xaction_t *);
+extern char		*sip_get_xaction_state(int);
+extern int 		(*sip_xaction_ulp_trans_err)(sip_transaction_t, int,
+			    void *);
+extern void 		(*sip_xaction_ulp_state_cb)(sip_transaction_t,
+			    sip_msg_t, int, int);
+extern void		sip_del_conn_obj_cache(sip_conn_object_t, void *);
+extern int		sip_add_conn_obj_cache(sip_conn_object_t, void *);
+extern void		sip_xaction_terminate(sip_xaction_t *, _sip_msg_t *,
+			    int);
+#ifdef	__cplusplus
+}
+#endif
+
+#endif	/* _SIP_XACTION_H */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/lib/libsip/common/sip_xaction_state_mc.c	Sat Oct 07 14:26:26 2006 -0700
@@ -0,0 +1,1514 @@
+/*
+ * 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"
+
+/*
+ * SIP Client/Server Invite/Non-Invite Transaction State machine.
+ */
+
+#include "sip_parse_uri.h"
+#include "sip_msg.h"
+#include "sip_miscdefs.h"
+#include "sip_xaction.h"
+
+
+/*
+ * Some Timer related info from RFC 3261, page 265.
+ *
+ * ----------------------------------------------------------------------
+ * Timer    Value            Section               Meaning
+ * ----------------------------------------------------------------------
+ * T1       500ms default    Section 17.1.1.1     RTT Estimate
+ * T2       4s               Section 17.1.2.2     The maximum retransmit
+ *                                                interval for non-INVITE
+ *                                                requests and INVITE
+ *                                                responses
+ * T4       5s               Section 17.1.2.2     Maximum duration a
+ *                                                message will
+ *                                                remain in the network
+ * ----------------------------------------------------------------------
+ * Timer A  initially T1     Section 17.1.1.2     INVITE request retransmit
+ *                                                interval, for UDP only
+ * Timer B  64*T1            Section 17.1.1.2     INVITE transaction
+ *                                                timeout timer
+ * Timer C  > 3min           Section 16.6         proxy INVITE transaction
+ *                            bullet 11            timeout
+ * Timer D  > 32s for UDP    Section 17.1.1.2     Wait time for response
+ *          0s for TCP/SCTP                       retransmits
+ * Timer E  initially T1     Section 17.1.2.2     non-INVITE request
+ *                                                retransmit interval,
+ *                                                UDP only
+ * Timer F  64*T1            Section 17.1.2.2     non-INVITE transaction
+ *                                                timeout timer
+ * Timer G  initially T1     Section 17.2.1       INVITE response
+ *                                                retransmit interval
+ * Timer H  64*T1            Section 17.2.1       Wait time for
+ *                                                ACK receipt
+ * Timer I  T4 for UDP       Section 17.2.1       Wait time for
+ *          0s for TCP/SCTP                       ACK retransmits
+ * Timer J  64*T1 for UDP    Section 17.2.2       Wait time for
+ *          0s for TCP/SCTP                       non-INVITE request
+ *                                                retransmits
+ * Timer K  T4 for UDP       Section 17.1.2.2     Wait time for
+ *          0s for TCP/SCTP                       response retransmits
+ * ----------------------------------------------------------------------
+ */
+
+#ifndef MIN
+#define	MIN(a, b)	(((a) < (b)) ? (a):(b))
+#endif
+
+/*
+ * Arg to the timer fire routine
+ */
+typedef	struct sip_xaction_timer_obj_s {
+	sip_xaction_timer_type_t	sip_xaction_timer_type;
+	sip_xaction_t			*sip_trans;
+	int				sip_xaction_timer_xport;
+} sip_xaction_time_obj_t;
+
+int		sip_xaction_output(sip_conn_object_t, sip_xaction_t *,
+		    _sip_msg_t *);
+int		sip_xaction_input(sip_conn_object_t, sip_xaction_t *,
+		    _sip_msg_t **);
+void		sip_xaction_terminate(sip_xaction_t *, _sip_msg_t *, int);
+
+static int 	sip_clnt_xaction_output(sip_conn_object_t, sip_xaction_t *,
+		    _sip_msg_t *);
+static int	sip_clnt_xaction_input(sip_conn_object_t, sip_xaction_t *,
+		    _sip_msg_t **);
+static int	sip_clnt_xaction_inv_res(sip_conn_object_t, sip_xaction_t *,
+		    _sip_msg_t **);
+static int	sip_clnt_xaction_noninv_res(sip_conn_object_t, sip_xaction_t *,
+		    _sip_msg_t **);
+static int 	sip_srv_xaction_output(sip_conn_object_t, sip_xaction_t *,
+		    _sip_msg_t *);
+static int	sip_srv_xaction_input(sip_conn_object_t, sip_xaction_t *,
+		    _sip_msg_t **);
+static int	sip_srv_xaction_inv_res(sip_conn_object_t, sip_xaction_t *,
+		    _sip_msg_t *);
+static int	sip_srv_xaction_noninv_res(sip_conn_object_t, sip_xaction_t *,
+		    _sip_msg_t *);
+static int	sip_create_send_nonOKack(sip_conn_object_t, sip_xaction_t *,
+		    _sip_msg_t *, boolean_t);
+void		sip_xaction_state_timer_fire(void *);
+
+static sip_xaction_time_obj_t	*sip_setup_timer(sip_conn_object_t,
+				    sip_xaction_t *, _sip_msg_t *,
+				    sip_timer_t, int);
+
+/*
+ * Return a timer object
+ */
+static sip_xaction_time_obj_t *
+sip_setup_timer(sip_conn_object_t conn_obj, sip_xaction_t *sip_trans,
+    _sip_msg_t *sip_msg, sip_timer_t timer, int type)
+{
+	sip_xaction_time_obj_t	*sip_timer_obj = NULL;
+
+	sip_timer_obj = (sip_xaction_time_obj_t *)
+	    malloc(sizeof (sip_xaction_time_obj_t));
+	if (sip_timer_obj == NULL)
+		return (NULL);
+	if (SIP_IS_TIMER_RUNNING(timer))
+		SIP_CANCEL_TIMER(timer);
+	sip_timer_obj->sip_xaction_timer_type = type;
+	sip_timer_obj->sip_xaction_timer_xport = sip_conn_transport(conn_obj);
+	sip_timer_obj->sip_trans = sip_trans;
+	/*
+	 * Save the message
+	 */
+	if (sip_msg != NULL) {
+		(void) sip_add_conn_obj_cache(conn_obj, (void *)sip_trans);
+		if (sip_trans->sip_xaction_last_msg != NULL) {
+			SIP_MSG_REFCNT_DECR(sip_trans->sip_xaction_last_msg);
+			sip_trans->sip_xaction_last_msg = NULL;
+		}
+		SIP_MSG_REFCNT_INCR(sip_msg);
+		sip_trans->sip_xaction_last_msg = sip_msg;
+	}
+	return (sip_timer_obj);
+}
+
+/*
+ * --------------------------- Output Routines ---------------------------
+ */
+
+/*
+ * Send a SIP message, request or response, out
+ */
+int
+sip_xaction_output(sip_conn_object_t conn_obj, sip_xaction_t *sip_trans,
+    _sip_msg_t *msg)
+{
+	sip_message_type_t	*sip_msg_info;
+	int			ret;
+
+	assert(conn_obj != NULL);
+	sip_msg_info = msg->sip_msg_req_res;
+
+	if (sip_msg_info->is_request)
+		return (sip_clnt_xaction_output(conn_obj, sip_trans, msg));
+
+	ret = sip_srv_xaction_output(conn_obj, sip_trans, msg);
+
+	return (ret);
+}
+
+/*
+ * Send a Request out
+ */
+static int
+sip_clnt_xaction_output(sip_conn_object_t conn_obj, sip_xaction_t *sip_trans,
+    _sip_msg_t *msg)
+{
+	sip_xaction_time_obj_t	*timer_obj_A = NULL;
+	sip_xaction_time_obj_t	*timer_obj_B = NULL;
+	sip_xaction_time_obj_t	*timer_obj_E = NULL;
+	sip_xaction_time_obj_t	*timer_obj_F = NULL;
+	sip_message_type_t	*sip_msg_info;
+	int			prev_state;
+	int			error = 0;
+	boolean_t		isreliable;
+
+	(void) pthread_mutex_lock(&sip_trans->sip_xaction_mutex);
+	prev_state = sip_trans->sip_xaction_state;
+	assert(msg->sip_msg_req_res != NULL);
+	sip_msg_info = msg->sip_msg_req_res;
+	isreliable = sip_is_conn_reliable(conn_obj);
+
+	if (sip_msg_info->sip_req_method == INVITE) {
+		/*
+		 * if transport is not reliable, start TIMER A.
+		 */
+		if (!isreliable) {
+			timer_obj_A = sip_setup_timer(conn_obj, sip_trans,
+			    msg, sip_trans->sip_xaction_TA,
+			    SIP_XACTION_TIMER_A);
+			if (timer_obj_A == NULL) {
+				error = ENOMEM;
+				goto error_ret;
+			}
+		}
+
+		timer_obj_B = sip_setup_timer(conn_obj, sip_trans, NULL,
+		    sip_trans->sip_xaction_TB, SIP_XACTION_TIMER_B);
+		if (timer_obj_B == NULL) {
+			error = ENOMEM;
+			goto error_ret;
+		}
+		if (timer_obj_A != NULL) {
+			SIP_SCHED_TIMER(sip_trans->sip_xaction_TA, timer_obj_A,
+			    sip_xaction_state_timer_fire);
+			if (!SIP_IS_TIMER_RUNNING(sip_trans->sip_xaction_TA)) {
+				error = ENOMEM;
+				goto error_ret;
+			}
+		}
+		SIP_SCHED_TIMER(sip_trans->sip_xaction_TB, timer_obj_B,
+		    sip_xaction_state_timer_fire);
+		if (!SIP_IS_TIMER_RUNNING(sip_trans->sip_xaction_TB)) {
+			if (timer_obj_A != NULL)
+				SIP_CANCEL_TIMER(sip_trans->sip_xaction_TA)
+			error = ENOMEM;
+			goto error_ret;
+		}
+		sip_trans->sip_xaction_state = SIP_CLNT_CALLING;
+	} else {
+		/*
+		 * if transport is not reliable, start rexmit Timer E.
+		 */
+		if (!isreliable) {
+			timer_obj_E = sip_setup_timer(conn_obj, sip_trans, msg,
+			    sip_trans->sip_xaction_TE, SIP_XACTION_TIMER_E);
+			if (timer_obj_E == NULL) {
+				error = ENOMEM;
+				goto error_ret;
+			}
+		}
+		/*
+		 * Start transaction Timer F
+		 */
+		timer_obj_F = sip_setup_timer(conn_obj, sip_trans, NULL,
+		    sip_trans->sip_xaction_TF, SIP_XACTION_TIMER_F);
+		if (timer_obj_F == NULL) {
+			error = ENOMEM;
+			goto error_ret;
+		}
+		if (timer_obj_E != NULL) {
+			SIP_SCHED_TIMER(sip_trans->sip_xaction_TE, timer_obj_E,
+			    sip_xaction_state_timer_fire);
+			if (!SIP_IS_TIMER_RUNNING(sip_trans->sip_xaction_TE)) {
+				error = ENOMEM;
+				goto error_ret;
+			}
+		}
+		SIP_SCHED_TIMER(sip_trans->sip_xaction_TF, timer_obj_F,
+		    sip_xaction_state_timer_fire);
+		if (!SIP_IS_TIMER_RUNNING(sip_trans->sip_xaction_TF)) {
+			if (timer_obj_E != NULL)
+				SIP_CANCEL_TIMER(sip_trans->sip_xaction_TE)
+			error = ENOMEM;
+			goto error_ret;
+		}
+		sip_trans->sip_xaction_state = SIP_CLNT_TRYING;
+	}
+	(void) pthread_mutex_unlock(&sip_trans->sip_xaction_mutex);
+	if (sip_xaction_ulp_state_cb != NULL) {
+		sip_xaction_ulp_state_cb((sip_transaction_t)sip_trans,
+		    (sip_msg_t)msg, prev_state, sip_trans->sip_xaction_state);
+	}
+	return (0);
+
+error_ret:
+	(void) pthread_mutex_unlock(&sip_trans->sip_xaction_mutex);
+	if (timer_obj_A != NULL)
+		free(timer_obj_A);
+	if (timer_obj_B != NULL)
+		free(timer_obj_B);
+	if (timer_obj_E != NULL)
+		free(timer_obj_E);
+	if (timer_obj_F != NULL)
+		free(timer_obj_F);
+	return (error);
+}
+
+/*
+ * Send a response out
+ */
+static int
+sip_srv_xaction_output(sip_conn_object_t conn_obj, sip_xaction_t *sip_trans,
+    _sip_msg_t *msg)
+{
+	int		ret;
+
+	if (sip_trans->sip_xaction_method == INVITE)
+		ret = sip_srv_xaction_inv_res(conn_obj, sip_trans, msg);
+	else
+		ret = sip_srv_xaction_noninv_res(conn_obj, sip_trans, msg);
+	return (ret);
+}
+
+/*
+ * Send a INVITE response out
+ */
+static int
+sip_srv_xaction_inv_res(sip_conn_object_t conn_obj, sip_xaction_t *sip_trans,
+    _sip_msg_t *msg)
+{
+	int			resp_code;
+	sip_xaction_time_obj_t	*timer_obj_G = NULL;
+	sip_xaction_time_obj_t	*timer_obj_H = NULL;
+	sip_message_type_t	*sip_msg_info = msg->sip_msg_req_res;
+	int			prev_state;
+	boolean_t		isreliable;
+
+	isreliable = sip_is_conn_reliable(conn_obj);
+
+	resp_code = sip_msg_info->sip_resp_code;
+	(void) pthread_mutex_lock(&sip_trans->sip_xaction_mutex);
+	prev_state = sip_trans->sip_xaction_state;
+	switch (sip_trans->sip_xaction_state) {
+		case SIP_SRV_INV_PROCEEDING:
+			if (SIP_PROVISIONAL_RESP(resp_code)) {
+				if (sip_trans->sip_xaction_last_msg != NULL) {
+					SIP_MSG_REFCNT_DECR(
+					    sip_trans->sip_xaction_last_msg);
+					sip_trans->sip_xaction_last_msg = NULL;
+				}
+				SIP_MSG_REFCNT_INCR(msg);
+				sip_trans->sip_xaction_last_msg = msg;
+				(void) sip_add_conn_obj_cache(conn_obj,
+				    (void *)sip_trans);
+			} else if (SIP_OK_RESP(resp_code)) {
+				sip_trans->sip_xaction_state =
+				    SIP_SRV_INV_TERMINATED;
+			} else  if (SIP_NONOK_FINAL_RESP(resp_code)) {
+				if (sip_trans->sip_xaction_last_msg != NULL) {
+					SIP_MSG_REFCNT_DECR(
+					    sip_trans->sip_xaction_last_msg);
+					sip_trans->sip_xaction_last_msg = NULL;
+				}
+				SIP_MSG_REFCNT_INCR(msg);
+				sip_trans->sip_xaction_last_msg = msg;
+				(void) sip_add_conn_obj_cache(conn_obj,
+				    (void *)sip_trans);
+				/*
+				 * For unreliable transport start timer G
+				 */
+				if (!isreliable) {
+					timer_obj_G = sip_setup_timer(
+					    conn_obj, sip_trans,
+					    NULL, sip_trans->sip_xaction_TG,
+					    SIP_XACTION_TIMER_G);
+					if (timer_obj_G == NULL) {
+						(void) pthread_mutex_unlock(
+						    &sip_trans->
+						    sip_xaction_mutex);
+						return (ENOMEM);
+					}
+				}
+				/*
+				 * Start Timer H
+				 */
+				timer_obj_H = sip_setup_timer(
+				    conn_obj, sip_trans,
+				    NULL, sip_trans->sip_xaction_TH,
+				    SIP_XACTION_TIMER_H);
+				if (timer_obj_H == NULL) {
+					if (timer_obj_G != NULL)
+						free(timer_obj_G);
+					(void) pthread_mutex_unlock(
+					    &sip_trans->sip_xaction_mutex);
+					return (ENOMEM);
+				}
+				if (timer_obj_G != NULL) {
+					SIP_SCHED_TIMER(
+					    sip_trans->sip_xaction_TG,
+					    timer_obj_G,
+					    sip_xaction_state_timer_fire);
+					if (!SIP_IS_TIMER_RUNNING(
+					    sip_trans->sip_xaction_TG)) {
+						(void) pthread_mutex_unlock(
+						    &sip_trans->
+						    sip_xaction_mutex);
+						free(timer_obj_G);
+						return (ENOMEM);
+					}
+				}
+				if (timer_obj_H != NULL) {
+					SIP_SCHED_TIMER(
+					    sip_trans->sip_xaction_TH,
+					    timer_obj_H,
+					    sip_xaction_state_timer_fire);
+					if (!SIP_IS_TIMER_RUNNING(
+					    sip_trans->sip_xaction_TH)) {
+						if (timer_obj_G != NULL) {
+							SIP_CANCEL_TIMER(
+							    sip_trans->
+							    sip_xaction_TG);
+							free(timer_obj_G);
+						}
+						(void) pthread_mutex_unlock(
+						    &sip_trans->
+						    sip_xaction_mutex);
+						free(timer_obj_H);
+						return (ENOMEM);
+					}
+				}
+				sip_trans->sip_xaction_state =
+				    SIP_SRV_INV_COMPLETED;
+			}
+			break;
+		default:
+			(void) pthread_mutex_unlock(
+			    &sip_trans->sip_xaction_mutex);
+			return (EPROTO);
+	}
+	(void) pthread_mutex_unlock(&sip_trans->sip_xaction_mutex);
+	if (prev_state != sip_trans->sip_xaction_state &&
+	    sip_xaction_ulp_state_cb != NULL) {
+		sip_xaction_ulp_state_cb((sip_transaction_t)sip_trans,
+		    (sip_msg_t)msg, prev_state, sip_trans->sip_xaction_state);
+	}
+	return (0);
+}
+
+/*
+ *  Send a NON-INVITE response out
+ */
+static int
+sip_srv_xaction_noninv_res(sip_conn_object_t conn_obj,
+    sip_xaction_t *sip_trans, _sip_msg_t *msg)
+{
+	int			resp_code;
+	sip_xaction_time_obj_t	*timer_obj_J = NULL;
+	sip_message_type_t	*sip_msg_info = msg->sip_msg_req_res;
+	int			prev_state;
+	boolean_t		isreliable;
+
+	resp_code = sip_msg_info->sip_resp_code;
+	isreliable = sip_is_conn_reliable(conn_obj);
+
+	(void) pthread_mutex_lock(&sip_trans->sip_xaction_mutex);
+	prev_state = sip_trans->sip_xaction_state;
+	switch (sip_trans->sip_xaction_state) {
+		case SIP_SRV_TRYING:
+			if (sip_trans->sip_xaction_last_msg != NULL) {
+				SIP_MSG_REFCNT_DECR(
+				    sip_trans->sip_xaction_last_msg);
+				sip_trans->sip_xaction_last_msg = NULL;
+			}
+			SIP_MSG_REFCNT_INCR(msg);
+			sip_trans->sip_xaction_last_msg = msg;
+			(void) sip_add_conn_obj_cache(conn_obj,
+			    (void *)sip_trans);
+			if (SIP_PROVISIONAL_RESP(resp_code)) {
+				sip_trans->sip_xaction_state =
+				    SIP_SRV_NONINV_PROCEEDING;
+			} else if (SIP_FINAL_RESP(resp_code)) {
+				/*
+				 * For unreliable transports, start Timer J
+				 */
+				if (!isreliable) {
+					timer_obj_J = sip_setup_timer(
+					    conn_obj, sip_trans,
+					    NULL, sip_trans->sip_xaction_TJ,
+					    SIP_XACTION_TIMER_J);
+					if (timer_obj_J == NULL) {
+						(void) pthread_mutex_unlock(&
+						    sip_trans->
+						    sip_xaction_mutex);
+						return (ENOMEM);
+					}
+					SIP_SCHED_TIMER(
+					    sip_trans->sip_xaction_TJ,
+					    timer_obj_J,
+					    sip_xaction_state_timer_fire);
+					if (!SIP_IS_TIMER_RUNNING(
+					    sip_trans->sip_xaction_TJ)) {
+						(void) pthread_mutex_unlock(&
+						    sip_trans->
+						    sip_xaction_mutex);
+						free(timer_obj_J);
+						return (ENOMEM);
+					}
+					sip_trans->sip_xaction_state =
+					    SIP_SRV_NONINV_COMPLETED;
+				} else {
+					sip_trans->sip_xaction_state =
+					    SIP_SRV_NONINV_TERMINATED;
+				}
+			}
+			break;
+		case SIP_SRV_NONINV_PROCEEDING:
+			if (sip_trans->sip_xaction_last_msg != NULL) {
+				SIP_MSG_REFCNT_DECR(
+				    sip_trans->sip_xaction_last_msg);
+				sip_trans->sip_xaction_last_msg = NULL;
+			}
+			SIP_MSG_REFCNT_INCR(msg);
+			sip_trans->sip_xaction_last_msg = msg;
+			(void) sip_add_conn_obj_cache(conn_obj,
+			    (void *)sip_trans);
+			if (SIP_PROVISIONAL_RESP(resp_code)) {
+				break;
+			} else if (SIP_FINAL_RESP(resp_code)) {
+				/*
+				 * For unreliable transports, start Timer J
+				 */
+				if (!isreliable) {
+					timer_obj_J = sip_setup_timer(
+					    conn_obj, sip_trans,
+					    NULL, sip_trans->sip_xaction_TJ,
+					    SIP_XACTION_TIMER_J);
+					if (timer_obj_J == NULL) {
+						(void) pthread_mutex_unlock(&
+						    sip_trans->
+						    sip_xaction_mutex);
+						return (ENOMEM);
+					}
+					SIP_SCHED_TIMER(
+					    sip_trans->sip_xaction_TJ,
+					    timer_obj_J,
+					    sip_xaction_state_timer_fire);
+					if (!SIP_IS_TIMER_RUNNING(
+					    sip_trans->sip_xaction_TJ)) {
+						(void) pthread_mutex_unlock(&
+						    sip_trans->
+						    sip_xaction_mutex);
+						free(timer_obj_J);
+						return (ENOMEM);
+					}
+					sip_trans->sip_xaction_state =
+					    SIP_SRV_NONINV_COMPLETED;
+				} else {
+					sip_trans->sip_xaction_state =
+					    SIP_SRV_NONINV_TERMINATED;
+				}
+			}
+			break;
+		default:
+			(void) pthread_mutex_unlock(
+			    &sip_trans->sip_xaction_mutex);
+			return (EPROTO);
+	}
+	(void) pthread_mutex_unlock(&sip_trans->sip_xaction_mutex);
+	if (prev_state != sip_trans->sip_xaction_state &&
+	    sip_xaction_ulp_state_cb != NULL) {
+		sip_xaction_ulp_state_cb((sip_transaction_t)sip_trans,
+		    (sip_msg_t)msg, prev_state, sip_trans->sip_xaction_state);
+	}
+	return (0);
+}
+
+
+/*
+ * -------------------------- Input Routines ---------------------------
+ */
+
+/*
+ * Process an incoming SIP message Request or Response
+ */
+int
+sip_xaction_input(sip_conn_object_t conn_obj, sip_xaction_t *sip_trans,
+    _sip_msg_t **sip_msg)
+{
+	sip_message_type_t	*sip_msg_info;
+	int			ret;
+
+	sip_msg_info = (*sip_msg)->sip_msg_req_res;
+	if (sip_msg_info->is_request)
+		ret = sip_srv_xaction_input(conn_obj, sip_trans, sip_msg);
+	else
+		ret = sip_clnt_xaction_input(conn_obj, sip_trans, sip_msg);
+	return (ret);
+}
+
+/*
+ * Process a Request from the transport
+ */
+static int
+sip_srv_xaction_input(sip_conn_object_t conn_obj, sip_xaction_t *sip_trans,
+    _sip_msg_t **sip_msg)
+{
+	sip_message_type_t	*sip_msg_info;
+	_sip_msg_t		*msg = *sip_msg;
+	int			prev_state;
+	boolean_t		isreliable;
+
+	sip_msg_info = msg->sip_msg_req_res;
+	isreliable = sip_is_conn_reliable(conn_obj);
+
+	/*
+	 * Cancel if the original transaction has not yet got a final
+	 * response and send a 487 response.
+	 */
+	if (sip_msg_info->sip_req_method == ACK) {
+		_sip_msg_t		*sip_last_resp;
+		const sip_str_t		*resp_to_tag;
+		const sip_str_t		*req_to_tag;
+		int			error;
+		sip_message_type_t	*last_msg_info;
+
+		(void) pthread_mutex_lock(&sip_trans->sip_xaction_mutex);
+
+		if (sip_trans->sip_xaction_last_msg != NULL)
+			sip_last_resp = sip_trans->sip_xaction_last_msg;
+		else
+			sip_last_resp = sip_trans->sip_xaction_orig_msg;
+		last_msg_info = sip_last_resp->sip_msg_req_res;
+		if (last_msg_info->is_request) {
+			(void) pthread_mutex_unlock(
+			    &sip_trans->sip_xaction_mutex);
+			return (0);
+		}
+		req_to_tag = sip_get_to_tag((sip_msg_t)msg, &error);
+		if (req_to_tag == NULL || error != 0) {
+			(void) pthread_mutex_unlock(
+			    &sip_trans->sip_xaction_mutex);
+			return (0);
+		}
+		resp_to_tag = sip_get_to_tag((sip_msg_t)sip_last_resp,
+		    &error);
+		if (req_to_tag == NULL || error != 0) {
+			(void) pthread_mutex_unlock(
+			    &sip_trans->sip_xaction_mutex);
+			return (0);
+		}
+		if (resp_to_tag->sip_str_len != req_to_tag->sip_str_len ||
+		    strncmp(resp_to_tag->sip_str_ptr, req_to_tag->sip_str_ptr,
+			req_to_tag->sip_str_len) != 0) {
+			(void) pthread_mutex_unlock(
+			    &sip_trans->sip_xaction_mutex);
+			return (0);
+		}
+		prev_state = sip_trans->sip_xaction_state;
+		if (sip_trans->sip_xaction_state == SIP_SRV_INV_COMPLETED) {
+			sip_xaction_time_obj_t	*timer_obj_I = NULL;
+
+			SIP_CANCEL_TIMER(sip_trans->sip_xaction_TG);
+			/*
+			 * Cancel Timer H and goto TERMINATED state for
+			 * reliable transports.
+			 */
+			if (isreliable) {
+				SIP_CANCEL_TIMER(
+				    sip_trans->sip_xaction_TH);
+				sip_trans->sip_xaction_state =
+				    SIP_SRV_INV_TERMINATED;
+				(void) pthread_mutex_unlock(
+				    &sip_trans->sip_xaction_mutex);
+				if (sip_xaction_ulp_state_cb != NULL) {
+					sip_xaction_ulp_state_cb(
+					    (sip_transaction_t)sip_trans,
+					    (sip_msg_t)msg, prev_state,
+					    sip_trans->sip_xaction_state);
+				}
+				return (0);
+			}
+			/*
+			 * For unreliable transports, start TIMER I and
+			 * transition to CONFIRMED state.
+			 */
+			timer_obj_I = sip_setup_timer(conn_obj, sip_trans,
+			    NULL,
+			    sip_trans->sip_xaction_TI, SIP_XACTION_TIMER_I);
+			if (timer_obj_I == NULL) {
+				(void) pthread_mutex_unlock(
+				    &sip_trans->sip_xaction_mutex);
+				return (ENOMEM);
+			}
+			SIP_SCHED_TIMER(sip_trans->sip_xaction_TI,
+			    timer_obj_I, sip_xaction_state_timer_fire);
+			if (!SIP_IS_TIMER_RUNNING(sip_trans->sip_xaction_TI)) {
+				(void) pthread_mutex_unlock(
+				    &sip_trans->sip_xaction_mutex);
+				free(timer_obj_I);
+				return (ENOMEM);
+			}
+			sip_trans->sip_xaction_state = SIP_SRV_CONFIRMED;
+		}
+		(void) pthread_mutex_unlock(&sip_trans->sip_xaction_mutex);
+		if (prev_state != sip_trans->sip_xaction_state &&
+		    sip_xaction_ulp_state_cb != NULL) {
+			sip_xaction_ulp_state_cb((sip_transaction_t)sip_trans,
+			    (sip_msg_t)msg, prev_state,
+			    sip_trans->sip_xaction_state);
+		}
+		return (0);
+	} else if (sip_msg_info->sip_req_method == CANCEL) {
+		if (sip_trans->sip_xaction_method == INVITE) {
+			(void) pthread_mutex_unlock(
+			    &sip_trans->sip_xaction_mutex);
+			return (0);
+		}
+	}
+	if (sip_msg_info->sip_req_method == INVITE) {
+		(void) pthread_mutex_lock(&sip_trans->sip_xaction_mutex);
+		assert(sip_trans->sip_xaction_method == INVITE);
+		/*
+		 * Retransmitted invite
+		 */
+		switch (sip_trans->sip_xaction_state) {
+			case SIP_SRV_INV_PROCEEDING:
+			case SIP_SRV_INV_COMPLETED:
+				if (sip_trans->sip_xaction_last_msg != NULL) {
+					_sip_msg_t	*new_msg;
+
+					new_msg =
+					    sip_trans->sip_xaction_last_msg;
+					(void) sip_stack_send(conn_obj,
+					    new_msg->sip_msg_buf,
+					    new_msg->sip_msg_len);
+				}
+				break;
+			default:
+				(void) pthread_mutex_unlock(
+				    &sip_trans->sip_xaction_mutex);
+				return (EPROTO);
+		}
+		(void) pthread_mutex_unlock(&sip_trans->sip_xaction_mutex);
+		sip_free_msg((sip_msg_t)msg);
+		*sip_msg = NULL;
+		return (0);
+	}
+	/*
+	 * Retransmitted request
+	 */
+	assert(sip_trans->sip_xaction_method != INVITE);
+	(void) pthread_mutex_lock(&sip_trans->sip_xaction_mutex);
+	switch (sip_trans->sip_xaction_state) {
+		case SIP_SRV_NONINV_PROCEEDING:
+		case SIP_SRV_NONINV_COMPLETED:
+			if (sip_trans->sip_xaction_last_msg != NULL) {
+				_sip_msg_t	*new_msg;
+
+				new_msg = sip_trans->sip_xaction_last_msg;
+				(void) sip_stack_send(conn_obj,
+				    new_msg->sip_msg_buf, new_msg->sip_msg_len);
+			}
+			break;
+		default:
+			(void) pthread_mutex_unlock(
+			    &sip_trans->sip_xaction_mutex);
+			return (EPROTO);
+	}
+	(void) pthread_mutex_unlock(&sip_trans->sip_xaction_mutex);
+	sip_free_msg((sip_msg_t)msg);
+	*sip_msg = NULL;
+	return (0);
+}
+
+/*
+ * Process a Response
+ */
+static int
+sip_clnt_xaction_input(sip_conn_object_t conn_obj, sip_xaction_t *sip_trans,
+    _sip_msg_t **msg)
+{
+	int		ret;
+
+	if (sip_trans->sip_xaction_method == INVITE)
+		ret = sip_clnt_xaction_inv_res(conn_obj, sip_trans, msg);
+	else
+		ret = sip_clnt_xaction_noninv_res(conn_obj, sip_trans, msg);
+
+	return (ret);
+}
+
+static int
+sip_create_send_nonOKack(sip_conn_object_t conn_obj, sip_xaction_t *sip_trans,
+    _sip_msg_t *msg, boolean_t copy)
+{
+	_sip_msg_t	*ack_msg;
+	int		ret = 0;
+
+	ack_msg = (_sip_msg_t *)sip_new_msg();
+	if (ack_msg == NULL)
+		return (ENOMEM);
+	if ((ret = sip_create_nonOKack(
+	    (sip_msg_t)sip_trans->sip_xaction_orig_msg, (sip_msg_t)msg,
+	    (sip_msg_t)ack_msg)) != 0) {
+		sip_free_msg((sip_msg_t)ack_msg);
+		return (ret);
+	}
+	if ((ret = sip_stack_send(conn_obj, ack_msg->sip_msg_buf,
+	    ack_msg->sip_msg_len)) != 0) {
+		sip_free_msg((sip_msg_t)ack_msg);
+		return (ret);
+	}
+	if (copy) {
+		SIP_MSG_REFCNT_INCR(ack_msg);
+		if (sip_trans->sip_xaction_last_msg != NULL) {
+			SIP_MSG_REFCNT_DECR(sip_trans->sip_xaction_last_msg);
+			sip_trans->sip_xaction_last_msg = NULL;
+		}
+		sip_trans->sip_xaction_last_msg = ack_msg;
+	}
+	sip_free_msg((sip_msg_t)ack_msg);
+	return (0);
+}
+
+/*
+ * Process a INVITE Response
+ */
+static int
+sip_clnt_xaction_inv_res(sip_conn_object_t conn_obj, sip_xaction_t *sip_trans,
+    _sip_msg_t **sip_msg)
+{
+	int			resp_code;
+	_sip_msg_t		*msg = *sip_msg;
+	sip_xaction_time_obj_t	*timer_obj_D = NULL;
+	sip_message_type_t	*sip_msg_info;
+	int			prev_state;
+	boolean_t		isreliable;
+
+	assert(msg->sip_msg_req_res != NULL);
+
+	sip_msg_info = msg->sip_msg_req_res;
+	resp_code = sip_msg_info->sip_resp_code;
+	isreliable = sip_is_conn_reliable(conn_obj);
+
+	(void) pthread_mutex_lock(&sip_trans->sip_xaction_mutex);
+	prev_state = sip_trans->sip_xaction_state;
+	switch (sip_trans->sip_xaction_state) {
+		case SIP_CLNT_CALLING:
+			if (SIP_PROVISIONAL_RESP(resp_code)) {
+				/*
+				 * sip_trans->sip_xaction_last_msg ?
+				 */
+				SIP_CANCEL_TIMER(
+				    sip_trans->sip_xaction_TA);
+				sip_trans->sip_xaction_state =
+				    SIP_CLNT_INV_PROCEEDING;
+			} else if (SIP_OK_RESP(resp_code)) {
+				/*
+				 * sip_trans->sip_xaction_last_msg ?
+				 */
+				SIP_CANCEL_TIMER(
+				    sip_trans->sip_xaction_TA);
+				SIP_CANCEL_TIMER(
+				    sip_trans->sip_xaction_TB);
+				sip_trans->sip_xaction_state =
+				    SIP_CLNT_INV_TERMINATED;
+			} else if (SIP_NONOK_FINAL_RESP(resp_code)) {
+				int	ret;
+
+				/*
+				 * sip_trans->sip_xaction_last_msg ?
+				 */
+				SIP_CANCEL_TIMER(
+				    sip_trans->sip_xaction_TA);
+				SIP_CANCEL_TIMER(
+				    sip_trans->sip_xaction_TB);
+				if ((ret = sip_create_send_nonOKack(conn_obj,
+				    sip_trans, msg, B_FALSE)) != 0) {
+					(void) pthread_mutex_unlock(
+					    &sip_trans->sip_xaction_mutex);
+					return (ret);
+				}
+				/*
+				 * start timer D for unreliable transports
+				 */
+				if (!isreliable) {
+					timer_obj_D = sip_setup_timer(
+					    conn_obj, sip_trans,
+					    NULL, sip_trans->sip_xaction_TD,
+					    SIP_XACTION_TIMER_D);
+					if (timer_obj_D == NULL) {
+						(void) pthread_mutex_unlock(
+						    &sip_trans->
+						    sip_xaction_mutex);
+						return (ENOMEM);
+					}
+					SIP_SCHED_TIMER(
+					    sip_trans->sip_xaction_TD,
+					    timer_obj_D,
+					    sip_xaction_state_timer_fire);
+					if (!SIP_IS_TIMER_RUNNING(
+					    sip_trans->sip_xaction_TD)) {
+						(void) pthread_mutex_unlock(
+						    &sip_trans->
+						    sip_xaction_mutex);
+						free(timer_obj_D);
+						return (ENOMEM);
+					}
+					sip_trans->sip_xaction_state =
+					    SIP_CLNT_INV_COMPLETED;
+				} else {
+					sip_trans->sip_xaction_state =
+					    SIP_CLNT_INV_TERMINATED;
+				}
+			} else {
+				/*
+				 * Invalid resp_code
+				 */
+				(void) pthread_mutex_unlock(
+				    &sip_trans->sip_xaction_mutex);
+				return (EPROTO);
+			}
+			break;
+		case SIP_CLNT_INV_PROCEEDING:
+			if (SIP_PROVISIONAL_RESP(resp_code)) {
+				break;
+			} else if (SIP_OK_RESP(resp_code)) {
+				SIP_CANCEL_TIMER(
+				    sip_trans->sip_xaction_TB);
+				sip_trans->sip_xaction_state =
+				    SIP_CLNT_INV_TERMINATED;
+			} else if (SIP_NONOK_FINAL_RESP(resp_code)) {
+				int	ret;
+
+				SIP_CANCEL_TIMER(
+				    sip_trans->sip_xaction_TB);
+				if ((ret = sip_create_send_nonOKack(conn_obj,
+				    sip_trans, msg, B_FALSE)) != 0) {
+					(void) pthread_mutex_unlock(
+					    &sip_trans->sip_xaction_mutex);
+					return (ret);
+				}
+				/*
+				 * start timer D for unreliable transports
+				 */
+				if (!isreliable) {
+					timer_obj_D = sip_setup_timer(
+					    conn_obj, sip_trans,
+					    NULL, sip_trans->sip_xaction_TD,
+					    SIP_XACTION_TIMER_D);
+					if (timer_obj_D == NULL) {
+						(void) pthread_mutex_unlock(
+						    &sip_trans->
+						    sip_xaction_mutex);
+						return (ENOMEM);
+					}
+					SIP_SCHED_TIMER(
+					    sip_trans->sip_xaction_TD,
+					    timer_obj_D,
+					    sip_xaction_state_timer_fire);
+					if (!SIP_IS_TIMER_RUNNING(
+					    sip_trans->sip_xaction_TD)) {
+						(void) pthread_mutex_unlock(
+						    &sip_trans->
+						    sip_xaction_mutex);
+						free(timer_obj_D);
+						return (ENOMEM);
+					}
+					sip_trans->sip_xaction_state =
+					    SIP_CLNT_INV_COMPLETED;
+				} else {
+					sip_trans->sip_xaction_state =
+					    SIP_CLNT_INV_TERMINATED;
+				}
+			} else {
+				(void) pthread_mutex_unlock(
+				    &sip_trans->sip_xaction_mutex);
+				return (EPROTO);
+			}
+			break;
+		case SIP_CLNT_INV_COMPLETED:
+			/*
+			 * Transport error takes it to
+			 * SIP_CLNT_INV_TERMINATED
+			 */
+			if (SIP_NONOK_FINAL_RESP(resp_code)) {
+				int	ret;
+
+				if ((ret = sip_create_send_nonOKack(conn_obj,
+				    sip_trans, msg, B_FALSE)) != 0) {
+					(void) pthread_mutex_unlock(
+					    &sip_trans->sip_xaction_mutex);
+					return (ret);
+				}
+			} else {
+				/*
+				 * Invalid resp_code
+				 */
+				(void) pthread_mutex_unlock(
+				    &sip_trans->sip_xaction_mutex);
+				return (EPROTO);
+			}
+			break;
+		default:
+			(void) pthread_mutex_unlock(
+			    &sip_trans->sip_xaction_mutex);
+			return (EPROTO);
+	}
+	(void) pthread_mutex_unlock(&sip_trans->sip_xaction_mutex);
+	if (prev_state != sip_trans->sip_xaction_state &&
+	    sip_xaction_ulp_state_cb != NULL) {
+		sip_xaction_ulp_state_cb((sip_transaction_t)sip_trans,
+		    (sip_msg_t)msg, prev_state, sip_trans->sip_xaction_state);
+	}
+	return (0);
+}
+
+/*
+ * Process a NON-INVITE Response
+ */
+static int
+sip_clnt_xaction_noninv_res(sip_conn_object_t conn_obj,
+    sip_xaction_t *sip_trans, _sip_msg_t **sip_msg)
+{
+	int			resp_code;
+	sip_xaction_time_obj_t	*timer_obj_K = NULL;
+	sip_message_type_t	*sip_msg_info;
+	int			prev_state;
+	_sip_msg_t		*msg = *sip_msg;
+	boolean_t		isreliable;
+
+	assert(msg->sip_msg_req_res != NULL);
+	assert(sip_trans != NULL);
+
+	sip_msg_info = msg->sip_msg_req_res;
+	isreliable = sip_is_conn_reliable(conn_obj);
+	resp_code = sip_msg_info->sip_resp_code;
+	(void) pthread_mutex_lock(&sip_trans->sip_xaction_mutex);
+	prev_state = sip_trans->sip_xaction_state;
+	switch (sip_trans->sip_xaction_state) {
+		case SIP_CLNT_TRYING:
+			if (SIP_PROVISIONAL_RESP(resp_code)) {
+				sip_trans->sip_xaction_state =
+				    SIP_CLNT_NONINV_PROCEEDING;
+			} else if (SIP_FINAL_RESP(resp_code)) {
+				SIP_CANCEL_TIMER(
+				    sip_trans->sip_xaction_TE);
+				SIP_CANCEL_TIMER(
+				    sip_trans->sip_xaction_TF);
+				/*
+				 * Start timer K for unreliable transports
+				 */
+				if (!isreliable) {
+					timer_obj_K = sip_setup_timer(
+					    conn_obj, sip_trans,
+					    NULL, sip_trans->sip_xaction_TK,
+					    SIP_XACTION_TIMER_K);
+					if (timer_obj_K == NULL) {
+						(void) pthread_mutex_unlock(&
+						    sip_trans->
+						    sip_xaction_mutex);
+						return (ENOMEM);
+					}
+					SIP_SCHED_TIMER(
+					    sip_trans->sip_xaction_TK,
+					    timer_obj_K,
+					    sip_xaction_state_timer_fire);
+					if (!SIP_IS_TIMER_RUNNING(
+					    sip_trans->sip_xaction_TK)) {
+						(void) pthread_mutex_unlock(
+						    &sip_trans->
+						    sip_xaction_mutex);
+						free(timer_obj_K);
+						return (ENOMEM);
+					}
+					sip_trans->sip_xaction_state =
+					    SIP_CLNT_NONINV_COMPLETED;
+				} else {
+					sip_trans->sip_xaction_state =
+					    SIP_CLNT_NONINV_TERMINATED;
+				}
+			}
+			break;
+		case SIP_CLNT_NONINV_PROCEEDING:
+			if (SIP_PROVISIONAL_RESP(resp_code)) {
+				break;
+			} else if (SIP_FINAL_RESP(resp_code)) {
+				SIP_CANCEL_TIMER(
+				    sip_trans->sip_xaction_TE);
+				SIP_CANCEL_TIMER(
+				    sip_trans->sip_xaction_TF);
+				/*
+				 * Start timer K for unreliable transports
+				 */
+				if (!isreliable) {
+					timer_obj_K = sip_setup_timer(
+					    conn_obj, sip_trans,
+					    NULL, sip_trans->sip_xaction_TK,
+					    SIP_XACTION_TIMER_K);
+					if (timer_obj_K == NULL) {
+						(void) pthread_mutex_unlock(&
+						    sip_trans->
+						    sip_xaction_mutex);
+						return (ENOMEM);
+					}
+					SIP_SCHED_TIMER(
+					    sip_trans->sip_xaction_TK,
+					    timer_obj_K,
+					    sip_xaction_state_timer_fire);
+					if (!SIP_IS_TIMER_RUNNING(
+					    sip_trans->sip_xaction_TK)) {
+						(void) pthread_mutex_unlock(
+						    &sip_trans->
+						    sip_xaction_mutex);
+						free(timer_obj_K);
+						return (ENOMEM);
+					}
+					sip_trans->sip_xaction_state =
+					    SIP_CLNT_NONINV_COMPLETED;
+				} else {
+					sip_trans->sip_xaction_state =
+					    SIP_CLNT_NONINV_TERMINATED;
+				}
+			}
+			break;
+		default:
+			(void) pthread_mutex_unlock(
+			    &sip_trans->sip_xaction_mutex);
+			return (EPROTO);
+	}
+	(void) pthread_mutex_unlock(&sip_trans->sip_xaction_mutex);
+	if (prev_state != sip_trans->sip_xaction_state &&
+	    sip_xaction_ulp_state_cb != NULL) {
+		sip_xaction_ulp_state_cb((sip_transaction_t)sip_trans,
+		    (sip_msg_t)msg, prev_state, sip_trans->sip_xaction_state);
+	}
+	return (0);
+}
+
+/*
+ * If there is a transport error, sending the message out, terminate the
+ * transaction.
+ */
+/* ARGSUSED */
+void
+sip_xaction_terminate(sip_xaction_t *sip_trans, _sip_msg_t *msg, int transport)
+{
+	sip_message_type_t	*sip_msg_info;
+	int			state;
+	int			prev_state;
+
+	sip_msg_info = msg->sip_msg_req_res;
+	(void) pthread_mutex_lock(&sip_trans->sip_xaction_mutex);
+	if (sip_msg_info->is_request) {
+		if (sip_trans->sip_xaction_method == INVITE)
+			state = SIP_CLNT_INV_TERMINATED;
+		else
+			state = SIP_CLNT_NONINV_TERMINATED;
+	} else {
+		if (sip_trans->sip_xaction_method == INVITE)
+			state = SIP_SRV_INV_TERMINATED;
+		else
+			state = SIP_SRV_NONINV_TERMINATED;
+	}
+	prev_state = sip_trans->sip_xaction_state;
+	sip_trans->sip_xaction_state = state;
+	(void) pthread_mutex_unlock(&sip_trans->sip_xaction_mutex);
+	if (sip_xaction_ulp_state_cb != NULL) {
+		sip_xaction_ulp_state_cb((sip_transaction_t)sip_trans,
+		    (sip_msg_t)msg, prev_state, sip_trans->sip_xaction_state);
+	}
+	sip_xaction_delete(sip_trans);
+}
+
+/*
+ * --------------------------- Timer Routine ---------------------------
+ */
+
+void
+sip_xaction_state_timer_fire(void *args)
+{
+	sip_xaction_time_obj_t	*time_obj = (sip_xaction_time_obj_t *)args;
+	sip_xaction_t		*sip_trans = time_obj->sip_trans;
+	_sip_msg_t		*new_msg;
+	boolean_t		destroy_trans = B_FALSE;
+	sip_conn_object_t	conn_obj;
+	int			prev_state;
+
+	assert(time_obj != NULL);
+
+	(void) pthread_mutex_lock(&sip_trans->sip_xaction_mutex);
+	prev_state = sip_trans->sip_xaction_state;
+	switch (time_obj->sip_xaction_timer_type) {
+		case SIP_XACTION_TIMER_A:
+			if (sip_trans->sip_xaction_state != SIP_CLNT_CALLING)
+				break;
+			/*
+			 * Assert candidate
+			 */
+			if (sip_trans->sip_xaction_last_msg == NULL)
+				break;
+			if (sip_trans->sip_xaction_conn_obj == NULL)
+				break;
+			new_msg = sip_trans->sip_xaction_last_msg;
+			conn_obj = sip_trans->sip_xaction_conn_obj;
+			if (sip_stack_send(conn_obj, new_msg->sip_msg_buf,
+			    new_msg->sip_msg_len) != 0) {
+				sip_del_conn_obj_cache(
+				    sip_trans->sip_xaction_conn_obj,
+				    (void *)sip_trans);
+				sip_trans->sip_xaction_state =
+				    SIP_CLNT_INV_TERMINATED;
+				(void) pthread_mutex_unlock(
+				    &sip_trans->sip_xaction_mutex);
+				if (sip_xaction_ulp_state_cb != NULL) {
+				    sip_xaction_ulp_state_cb(
+				    (sip_transaction_t)sip_trans, NULL,
+				    prev_state, sip_trans->sip_xaction_state);
+				}
+				if (sip_xaction_ulp_trans_err != NULL) {
+					sip_xaction_ulp_trans_err(sip_trans, 0,
+					    NULL);
+				}
+				sip_xaction_delete(sip_trans);
+				free(time_obj);
+				return;
+			}
+			SIP_SET_TIMEOUT(sip_trans->sip_xaction_TA,
+			    2 * SIP_GET_TIMEOUT(sip_trans->sip_xaction_TA));
+			/*
+			 * Reschedule the timer
+			 */
+			SIP_SCHED_TIMER(sip_trans->sip_xaction_TA,
+			    time_obj, sip_xaction_state_timer_fire);
+			if (!SIP_IS_TIMER_RUNNING(sip_trans->sip_xaction_TA)) {
+				sip_del_conn_obj_cache(
+				    sip_trans->sip_xaction_conn_obj,
+				    (void *)sip_trans);
+				sip_trans->sip_xaction_state =
+				    SIP_CLNT_INV_TERMINATED;
+				(void) pthread_mutex_unlock(
+				    &sip_trans->sip_xaction_mutex);
+				if (sip_xaction_ulp_state_cb != NULL) {
+				    sip_xaction_ulp_state_cb(
+				    (sip_transaction_t)sip_trans, NULL,
+				    prev_state, sip_trans->sip_xaction_state);
+				}
+				if (sip_xaction_ulp_trans_err != NULL) {
+					sip_xaction_ulp_trans_err(sip_trans, 0,
+					    NULL);
+				}
+				sip_xaction_delete(sip_trans);
+				free(time_obj);
+				return;
+			}
+			(void) pthread_mutex_unlock(
+			    &sip_trans->sip_xaction_mutex);
+			return;
+		case SIP_XACTION_TIMER_B:
+			SIP_CANCEL_TIMER(sip_trans->sip_xaction_TA);
+			if (sip_trans->sip_xaction_state == SIP_CLNT_CALLING) {
+				sip_trans->sip_xaction_state =
+				    SIP_CLNT_INV_TERMINATED;
+				(void) pthread_mutex_unlock(
+				    &sip_trans->sip_xaction_mutex);
+				if (sip_xaction_ulp_state_cb != NULL) {
+				    sip_xaction_ulp_state_cb(
+				    (sip_transaction_t)sip_trans, NULL,
+				    prev_state, sip_trans->sip_xaction_state);
+				}
+				if (sip_xaction_ulp_trans_err != NULL) {
+					sip_xaction_ulp_trans_err(sip_trans, 0,
+					    NULL);
+				}
+				sip_xaction_delete(sip_trans);
+				free(time_obj);
+				return;
+			}
+			break;
+		case SIP_XACTION_TIMER_D:
+			if (sip_trans->sip_xaction_state ==
+			    SIP_CLNT_INV_COMPLETED) {
+				SIP_CANCEL_TIMER(
+				    sip_trans->sip_xaction_TB);
+				sip_trans->sip_xaction_state =
+				    SIP_CLNT_INV_TERMINATED;
+				destroy_trans = B_TRUE;
+			}
+			break;
+		case SIP_XACTION_TIMER_E:
+			/*
+			 * Assert candidate
+			 */
+			if (sip_trans->sip_xaction_state != SIP_CLNT_TRYING &&
+			    sip_trans->sip_xaction_state !=
+			    SIP_CLNT_NONINV_PROCEEDING) {
+				break;
+			}
+			/*
+			 * Assert candidate
+			 */
+			if (sip_trans->sip_xaction_last_msg == NULL)
+				break;
+			if (sip_trans->sip_xaction_conn_obj == NULL)
+				break;
+			conn_obj = sip_trans->sip_xaction_conn_obj;
+			new_msg = sip_trans->sip_xaction_last_msg;
+			if (sip_stack_send(conn_obj, new_msg->sip_msg_buf,
+			    new_msg->sip_msg_len) != 0) {
+				sip_del_conn_obj_cache(
+				    sip_trans->sip_xaction_conn_obj,
+				    (void *)sip_trans);
+				sip_trans->sip_xaction_state =
+				    SIP_CLNT_NONINV_TERMINATED;
+				(void) pthread_mutex_unlock(
+				    &sip_trans->sip_xaction_mutex);
+				if (sip_xaction_ulp_state_cb != NULL) {
+				    sip_xaction_ulp_state_cb(
+				    (sip_transaction_t)sip_trans, NULL,
+				    prev_state, sip_trans->sip_xaction_state);
+				}
+				if (sip_xaction_ulp_trans_err != NULL) {
+					sip_xaction_ulp_trans_err(sip_trans, 0,
+					    NULL);
+				}
+				sip_xaction_delete(sip_trans);
+				free(time_obj);
+				return;
+			}
+			SIP_SET_TIMEOUT(sip_trans->sip_xaction_TE,
+			    MIN(SIP_TIMER_T2,
+			    2 * SIP_GET_TIMEOUT(sip_trans->sip_xaction_TE)));
+			/*
+			 * Reschedule the timer
+			 */
+			SIP_SCHED_TIMER(sip_trans->sip_xaction_TE,
+			    time_obj, sip_xaction_state_timer_fire);
+			if (!SIP_IS_TIMER_RUNNING(sip_trans->sip_xaction_TE)) {
+				sip_del_conn_obj_cache(
+				    sip_trans->sip_xaction_conn_obj,
+				    (void *)sip_trans);
+				sip_trans->sip_xaction_state =
+				    SIP_CLNT_NONINV_TERMINATED;
+				(void) pthread_mutex_unlock(
+				    &sip_trans->sip_xaction_mutex);
+				if (sip_xaction_ulp_state_cb != NULL) {
+				    sip_xaction_ulp_state_cb(
+				    (sip_transaction_t)sip_trans, NULL,
+				    prev_state, sip_trans->sip_xaction_state);
+				}
+				if (sip_xaction_ulp_trans_err != NULL) {
+					sip_xaction_ulp_trans_err(sip_trans, 0,
+					    NULL);
+				}
+				sip_xaction_delete(sip_trans);
+				free(time_obj);
+				return;
+			}
+			(void) pthread_mutex_unlock(
+			    &sip_trans->sip_xaction_mutex);
+			return;
+		case SIP_XACTION_TIMER_F:
+			SIP_CANCEL_TIMER(sip_trans->sip_xaction_TE);
+			if (sip_trans->sip_xaction_state == SIP_CLNT_TRYING ||
+			    sip_trans->sip_xaction_state ==
+			    SIP_CLNT_NONINV_PROCEEDING) {
+				sip_trans->sip_xaction_state =
+				    SIP_CLNT_NONINV_TERMINATED;
+				(void) pthread_mutex_unlock(
+				    &sip_trans->sip_xaction_mutex);
+				if (sip_xaction_ulp_state_cb != NULL) {
+				    sip_xaction_ulp_state_cb(
+				    (sip_transaction_t)sip_trans, NULL,
+				    prev_state, sip_trans->sip_xaction_state);
+				}
+				if (sip_xaction_ulp_trans_err != NULL) {
+					sip_xaction_ulp_trans_err(sip_trans, 0,
+					    NULL);
+				}
+				sip_xaction_delete(sip_trans);
+				free(time_obj);
+				return;
+			}
+			break;
+		case SIP_XACTION_TIMER_G:
+			/*
+			 * Assert candidate
+			 */
+			if (sip_trans->sip_xaction_last_msg == NULL)
+				break;
+			if (sip_trans->sip_xaction_conn_obj == NULL)
+				break;
+			if (sip_trans->sip_xaction_state !=
+			    SIP_SRV_INV_COMPLETED) {
+				break;
+			}
+			new_msg = sip_trans->sip_xaction_last_msg;
+			conn_obj = sip_trans->sip_xaction_conn_obj;
+			if (sip_stack_send(conn_obj, new_msg->sip_msg_buf,
+			    new_msg->sip_msg_len) != 0) {
+				sip_del_conn_obj_cache(
+				    sip_trans->sip_xaction_conn_obj,
+				    (void *)sip_trans);
+				sip_trans->sip_xaction_state =
+				    SIP_SRV_INV_TERMINATED;
+				(void) pthread_mutex_unlock(
+				    &sip_trans->sip_xaction_mutex);
+				if (sip_xaction_ulp_state_cb != NULL) {
+				    sip_xaction_ulp_state_cb(
+				    (sip_transaction_t)sip_trans, NULL,
+				    prev_state, sip_trans->sip_xaction_state);
+				}
+				if (sip_xaction_ulp_trans_err != NULL) {
+					sip_xaction_ulp_trans_err(sip_trans, 0,
+					    NULL);
+				}
+				sip_xaction_delete(sip_trans);
+				free(time_obj);
+				return;
+			}
+			SIP_SET_TIMEOUT(sip_trans->sip_xaction_TG,
+			    MIN(SIP_TIMER_T2,
+			    2 * SIP_GET_TIMEOUT(sip_trans->sip_xaction_TG)));
+			SIP_SCHED_TIMER(sip_trans->sip_xaction_TG,
+			    time_obj, sip_xaction_state_timer_fire);
+			if (!SIP_IS_TIMER_RUNNING(sip_trans->sip_xaction_TG)) {
+				sip_del_conn_obj_cache(
+				    sip_trans->sip_xaction_conn_obj,
+				    (void *)sip_trans);
+				sip_trans->sip_xaction_state =
+				    SIP_SRV_INV_TERMINATED;
+				(void) pthread_mutex_unlock(
+				    &sip_trans->sip_xaction_mutex);
+				if (sip_xaction_ulp_state_cb != NULL) {
+				    sip_xaction_ulp_state_cb(
+				    (sip_transaction_t)sip_trans, NULL,
+				    prev_state, sip_trans->sip_xaction_state);
+				}
+				if (sip_xaction_ulp_trans_err != NULL) {
+					sip_xaction_ulp_trans_err(sip_trans, 0,
+					    NULL);
+				}
+				sip_xaction_delete(sip_trans);
+				free(time_obj);
+				return;
+			}
+			(void) pthread_mutex_unlock(
+			    &sip_trans->sip_xaction_mutex);
+			return;
+		case SIP_XACTION_TIMER_H:
+			SIP_CANCEL_TIMER(sip_trans->sip_xaction_TG);
+			if (sip_trans->sip_xaction_state ==
+			    SIP_SRV_INV_COMPLETED) {
+				sip_trans->sip_xaction_state =
+				    SIP_SRV_INV_TERMINATED;
+				(void) pthread_mutex_unlock(
+				    &sip_trans->sip_xaction_mutex);
+				if (sip_xaction_ulp_state_cb != NULL) {
+				    sip_xaction_ulp_state_cb(
+				    (sip_transaction_t)sip_trans, NULL,
+				    prev_state, sip_trans->sip_xaction_state);
+				}
+				if (sip_xaction_ulp_trans_err != NULL) {
+					sip_xaction_ulp_trans_err(sip_trans, 0,
+					    NULL);
+				}
+				sip_xaction_delete(sip_trans);
+				free(time_obj);
+				return;
+			}
+			break;
+		case SIP_XACTION_TIMER_I:
+			if (sip_trans->sip_xaction_state ==
+			    SIP_SRV_CONFIRMED) {
+				SIP_CANCEL_TIMER(
+				    sip_trans->sip_xaction_TH);
+				sip_trans->sip_xaction_state =
+				    SIP_SRV_INV_TERMINATED;
+				destroy_trans = B_TRUE;
+			}
+			break;
+		case SIP_XACTION_TIMER_J:
+			if (sip_trans->sip_xaction_state ==
+			    SIP_SRV_NONINV_COMPLETED) {
+				sip_trans->sip_xaction_state =
+				    SIP_SRV_NONINV_TERMINATED;
+				destroy_trans = B_TRUE;
+
+			}
+			break;
+		case SIP_XACTION_TIMER_K:
+			if (sip_trans->sip_xaction_state ==
+			    SIP_CLNT_NONINV_COMPLETED) {
+				SIP_CANCEL_TIMER(
+				    sip_trans->sip_xaction_TF);
+				sip_trans->sip_xaction_state =
+				    SIP_CLNT_NONINV_TERMINATED;
+				destroy_trans = B_TRUE;
+			}
+			break;
+		default:
+			break;
+	}
+	if (destroy_trans) {
+		(void) pthread_mutex_unlock(&sip_trans->sip_xaction_mutex);
+		if (sip_xaction_ulp_state_cb != NULL &&
+		    prev_state != sip_trans->sip_xaction_state) {
+			sip_xaction_ulp_state_cb((sip_transaction_t)sip_trans,
+			    NULL, prev_state, sip_trans->sip_xaction_state);
+		}
+		sip_xaction_delete(sip_trans);
+		free(time_obj);
+		return;
+	}
+	(void) pthread_mutex_unlock(&sip_trans->sip_xaction_mutex);
+	if (sip_xaction_ulp_state_cb != NULL &&
+	    prev_state != sip_trans->sip_xaction_state) {
+		sip_xaction_ulp_state_cb((sip_transaction_t)sip_trans, NULL,
+		    prev_state, sip_trans->sip_xaction_state);
+	}
+	free(time_obj);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/lib/libsip/common/sip_xaction_ui.c	Sat Oct 07 14:26:26 2006 -0700
@@ -0,0 +1,204 @@
+/*
+ * 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_hash.h"
+#include "sip_parse_uri.h"
+#include "sip_msg.h"
+#include "sip_miscdefs.h"
+#include "sip_xaction.h"
+
+/*
+ * Hold transaction
+ */
+void
+sip_hold_trans(sip_transaction_t sip_trans)
+{
+	sip_xaction_t	*_trans;
+
+	if (sip_trans == NULL)
+		return;
+	_trans = (sip_xaction_t *)sip_trans;
+	(void) pthread_mutex_lock(&((_trans)->sip_xaction_mutex));
+	SIP_XACTION_REFCNT_INCR(_trans);
+	(void) pthread_mutex_unlock(&((_trans)->sip_xaction_mutex));
+}
+
+/*
+ * Release transaction
+ */
+void
+sip_release_trans(sip_transaction_t sip_trans)
+{
+	sip_xaction_t	*_trans;
+
+	if (sip_trans == NULL)
+		return;
+	_trans = (sip_xaction_t *)sip_trans;
+	SIP_XACTION_REFCNT_DECR(_trans);
+}
+
+/*
+ * Given a message get the client/server transaction. The caller is
+ * responsible for doing a sip_release_trans().
+ */
+const struct sip_xaction *
+sip_get_trans(sip_msg_t sip_msg, int which, int *error)
+{
+	if (error != NULL)
+		*error = 0;
+	if (sip_msg == NULL) {
+		if (error != NULL)
+			*error = EINVAL;
+		return (NULL);
+	}
+	return ((sip_transaction_t)sip_xaction_get(NULL, sip_msg, B_FALSE,
+	    which, NULL));
+}
+
+/*
+ * Get the last response sent for this transaction
+ */
+const struct sip_message *
+sip_get_trans_resp_msg(sip_transaction_t sip_trans, int *error)
+{
+	sip_xaction_t	*_trans;
+
+	if (error != NULL)
+		*error = 0;
+	if (sip_trans == NULL) {
+		if (error != NULL)
+			*error = EINVAL;
+		return (NULL);
+	}
+	_trans = (sip_xaction_t *)sip_trans;
+	if ((_trans->sip_xaction_last_msg != NULL) &&
+	    !sip_msg_is_request((sip_msg_t)_trans->sip_xaction_last_msg,
+	    error)) {
+		return (_trans->sip_xaction_last_msg);
+	} else if (!sip_msg_is_request((sip_msg_t)
+	    _trans->sip_xaction_orig_msg, error)) {
+		return (_trans->sip_xaction_orig_msg);
+	}
+	return (NULL);
+}
+
+/*
+ * Get the SIP message that created this transaction
+ */
+const struct sip_message *
+sip_get_trans_orig_msg(sip_transaction_t sip_trans, int *error)
+{
+	if (error != NULL)
+		*error = 0;
+	if (sip_trans == NULL) {
+		if (error != NULL)
+			*error = EINVAL;
+		return (NULL);
+	}
+	return (((sip_xaction_t *)sip_trans)->sip_xaction_orig_msg);
+}
+
+/*
+ * Get the connection object that was used to send the last message for this
+ * transaction.
+ */
+const struct sip_conn_object *
+sip_get_trans_conn_obj(sip_transaction_t sip_trans, int *error)
+{
+	if (error != NULL)
+		*error = 0;
+	if (sip_trans == NULL) {
+		if (error != NULL)
+			*error = EINVAL;
+		return (NULL);
+	}
+	return (((sip_xaction_t *)sip_trans)->sip_xaction_conn_obj);
+}
+
+/*
+ * Get the transaction method
+ */
+sip_method_t
+sip_get_trans_method(sip_transaction_t sip_trans, int *error)
+{
+	if (error != NULL)
+		*error = 0;
+
+	if (sip_trans == NULL) {
+		if (error != NULL)
+			*error = EINVAL;
+		return (-1);
+	}
+	return (((sip_xaction_t *)sip_trans)->sip_xaction_method);
+}
+
+/*
+ * Get the transaction id. Caller frees string
+ */
+char *
+sip_get_trans_branchid(sip_transaction_t trans, int *error)
+{
+	sip_xaction_t	*xaction = (sip_xaction_t *)trans;
+	char		*bid;
+
+	if (error != NULL)
+		*error = 0;
+	if (xaction == NULL || xaction->sip_xaction_branch_id == NULL) {
+		if (error != NULL)
+			*error = EINVAL;
+		return (NULL);
+	}
+	bid = malloc(strlen(xaction->sip_xaction_branch_id) + 1);
+	if (bid == NULL) {
+		if (error != NULL)
+			*error = ENOMEM;
+		return (NULL);
+	}
+	(void) strncpy(bid, xaction->sip_xaction_branch_id,
+	    strlen(xaction->sip_xaction_branch_id));
+	bid[strlen(xaction->sip_xaction_branch_id)] = '\0';
+	return (bid);
+}
+
+/*
+ * Get the transaction state
+ */
+int
+sip_get_trans_state(sip_transaction_t trans, int *error)
+{
+	sip_xaction_t	*xaction = (sip_xaction_t *)trans;
+
+	if (error != NULL)
+		*error = 0;
+	if (xaction == NULL) {
+		if (error != NULL)
+			*error = EINVAL;
+		return (NULL);
+	}
+	return (xaction->sip_xaction_state);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/lib/libsip/i386/Makefile	Sat Oct 07 14:26:26 2006 -0700
@@ -0,0 +1,29 @@
+#
+# 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.
+#
+# ident	"%Z%%M%	%I%	%E% SMI"
+#
+
+include ../Makefile.com
+
+install: all $(ROOTLIBS) $(ROOTLINKS) $(ROOTLINT)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/lib/libsip/sparc/Makefile	Sat Oct 07 14:26:26 2006 -0700
@@ -0,0 +1,29 @@
+#
+# 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.
+#
+# ident	"%Z%%M%	%I%	%E% SMI"
+#
+
+include ../Makefile.com
+
+install: all $(ROOTLIBS) $(ROOTLINKS) $(ROOTLINT)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/lib/libsip/sparcv9/Makefile	Sat Oct 07 14:26:26 2006 -0700
@@ -0,0 +1,30 @@
+#
+# 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.
+#
+# ident	"%Z%%M%	%I%	%E% SMI"
+#
+
+include ../Makefile.com
+include ../../Makefile.lib.64
+
+install: all $(ROOTLIBS64) $(ROOTLINKS64)
--- a/usr/src/pkgdefs/SUNWarc/prototype_com	Sat Oct 07 13:37:05 2006 -0700
+++ b/usr/src/pkgdefs/SUNWarc/prototype_com	Sat Oct 07 14:26:26 2006 -0700
@@ -173,6 +173,8 @@
 s none usr/lib/llib-lsecdb.ln=../../lib/llib-lsecdb.ln
 s none usr/lib/llib-lsendfile=../../lib/llib-lsendfile
 s none usr/lib/llib-lsendfile.ln=../../lib/llib-lsendfile.ln
+f none usr/lib/llib-lsip.ln 644 root bin
+f none usr/lib/llib-lsip 644 root bin
 f none usr/lib/llib-lsldap.ln 644 root bin
 f none usr/lib/llib-lsldap 644 root bin
 f none usr/lib/llib-lsmbios 644 root bin
--- a/usr/src/pkgdefs/SUNWarc/prototype_i386	Sat Oct 07 13:37:05 2006 -0700
+++ b/usr/src/pkgdefs/SUNWarc/prototype_i386	Sat Oct 07 14:26:26 2006 -0700
@@ -126,6 +126,7 @@
 s none usr/lib/amd64/llib-lsec.ln=../../../lib/amd64/llib-lsec.ln
 s none usr/lib/amd64/llib-lsecdb.ln=../../../lib/amd64/llib-lsecdb.ln
 s none usr/lib/amd64/llib-lsendfile.ln=../../../lib/amd64/llib-lsendfile.ln
+f none usr/lib/amd64/llib-lsip.ln 644 root bin
 f none usr/lib/amd64/llib-lsldap.ln 644 root bin
 f none usr/lib/amd64/llib-lsmbios.ln 644 root bin
 s none usr/lib/amd64/llib-lsocket.ln=../../../lib/amd64/llib-lsocket.ln
--- a/usr/src/pkgdefs/SUNWarc/prototype_sparc	Sat Oct 07 13:37:05 2006 -0700
+++ b/usr/src/pkgdefs/SUNWarc/prototype_sparc	Sat Oct 07 14:26:26 2006 -0700
@@ -121,6 +121,7 @@
 s none usr/lib/sparcv9/llib-lsec.ln=../../../lib/sparcv9/llib-lsec.ln
 s none usr/lib/sparcv9/llib-lsecdb.ln=../../../lib/sparcv9/llib-lsecdb.ln
 s none usr/lib/sparcv9/llib-lsendfile.ln=../../../lib/sparcv9/llib-lsendfile.ln
+f none usr/lib/sparcv9/llib-lsip.ln 644 root bin
 f none usr/lib/sparcv9/llib-lsldap.ln 644 root bin
 f none usr/lib/sparcv9/llib-lsmbios.ln 644 root bin
 s none usr/lib/sparcv9/llib-lsocket.ln=../../../lib/sparcv9/llib-lsocket.ln
--- a/usr/src/pkgdefs/SUNWcsl/prototype_com	Sat Oct 07 13:37:05 2006 -0700
+++ b/usr/src/pkgdefs/SUNWcsl/prototype_com	Sat Oct 07 14:26:26 2006 -0700
@@ -232,6 +232,8 @@
 s none usr/lib/libsecdb.so.1=../../lib/libsecdb.so.1
 s none usr/lib/libsendfile.so=../../lib/libsendfile.so.1
 s none usr/lib/libsendfile.so.1=../../lib/libsendfile.so.1
+s none usr/lib/libsip.so=./libsip.so.1
+f none usr/lib/libsip.so.1 755 root bin
 s none usr/lib/libsldap.so=libsldap.so.1
 f none usr/lib/libsldap.so.1 755 root bin
 s none usr/lib/libsmbios.so=libsmbios.so.1
--- a/usr/src/pkgdefs/SUNWcsl/prototype_i386	Sat Oct 07 13:37:05 2006 -0700
+++ b/usr/src/pkgdefs/SUNWcsl/prototype_i386	Sat Oct 07 14:26:26 2006 -0700
@@ -290,6 +290,8 @@
 s none usr/lib/amd64/libsecdb.so=../../../lib/amd64/libsecdb.so.1
 s none usr/lib/amd64/libsendfile.so.1=../../../lib/amd64/libsendfile.so.1
 s none usr/lib/amd64/libsendfile.so=../../../lib/amd64/libsendfile.so.1
+f none usr/lib/amd64/libsip.so.1 755 root bin
+s none usr/lib/amd64/libsip.so=./libsip.so.1
 s none usr/lib/amd64/libsldap.so=libsldap.so.1
 f none usr/lib/amd64/libsldap.so.1 755 root bin
 s none usr/lib/amd64/libsmbios.so=libsmbios.so.1
--- a/usr/src/pkgdefs/SUNWcsl/prototype_sparc	Sat Oct 07 13:37:05 2006 -0700
+++ b/usr/src/pkgdefs/SUNWcsl/prototype_sparc	Sat Oct 07 14:26:26 2006 -0700
@@ -278,6 +278,8 @@
 s none usr/lib/sparcv9/libsecdb.so=../../../lib/sparcv9/libsecdb.so.1
 s none usr/lib/sparcv9/libsendfile.so.1=../../../lib/sparcv9/libsendfile.so.1
 s none usr/lib/sparcv9/libsendfile.so=../../../lib/sparcv9/libsendfile.so.1
+f none usr/lib/sparcv9/libsip.so.1 755 root bin
+s none usr/lib/sparcv9/libsip.so=./libsip.so.1
 f none usr/lib/sparcv9/libsldap.so.1 755 root bin
 s none usr/lib/sparcv9/libsldap.so=libsldap.so.1
 f none usr/lib/sparcv9/libsmbios.so.1 755 root bin
--- a/usr/src/pkgdefs/SUNWhea/prototype_com	Sat Oct 07 13:37:05 2006 -0700
+++ b/usr/src/pkgdefs/SUNWhea/prototype_com	Sat Oct 07 14:26:26 2006 -0700
@@ -456,6 +456,7 @@
 f none usr/include/sched.h 644 root bin
 f none usr/include/schedctl.h 644 root bin
 f none usr/include/sac.h 644 root bin
+f none usr/include/sip.h 644 root bin
 d none usr/include/sasl 0755 root bin
 f none usr/include/sasl/prop.h 0644 root bin
 f none usr/include/sasl/sasl.h 0644 root bin