summaryrefslogtreecommitdiff
path: root/usr/src/lib/libsip
diff options
context:
space:
mode:
authorvi117747 <none@none>2006-10-07 14:26:26 -0700
committervi117747 <none@none>2006-10-07 14:26:26 -0700
commit40cb5e5daa7b80bb70fcf8dadfb20f9281566331 (patch)
treedc95663e296c5dbf3cb8faa561e53416978eb4dc /usr/src/lib/libsip
parent56a424cca6b3f91f31bdab72a4626c48c779fe8b (diff)
downloadillumos-joyent-40cb5e5daa7b80bb70fcf8dadfb20f9281566331.tar.gz
PSARC 2006/402 SIP Library Integration
6461142 Integrate SIP in Solaris
Diffstat (limited to 'usr/src/lib/libsip')
-rw-r--r--usr/src/lib/libsip/Makefile53
-rw-r--r--usr/src/lib/libsip/Makefile.com51
-rw-r--r--usr/src/lib/libsip/amd64/Makefile30
-rw-r--r--usr/src/lib/libsip/common/llib-lsip31
-rw-r--r--usr/src/lib/libsip/common/mapfile-vers266
-rw-r--r--usr/src/lib/libsip/common/sip.h720
-rw-r--r--usr/src/lib/libsip/common/sip_add_hdrs.c1594
-rw-r--r--usr/src/lib/libsip/common/sip_dialog.c1669
-rw-r--r--usr/src/lib/libsip/common/sip_dialog.h102
-rw-r--r--usr/src/lib/libsip/common/sip_dialog_ui.c531
-rw-r--r--usr/src/lib/libsip/common/sip_gids.c302
-rw-r--r--usr/src/lib/libsip/common/sip_hash.c195
-rw-r--r--usr/src/lib/libsip/common/sip_hash.h75
-rw-r--r--usr/src/lib/libsip/common/sip_hdrs_ui.c1785
-rw-r--r--usr/src/lib/libsip/common/sip_headers.c1005
-rw-r--r--usr/src/lib/libsip/common/sip_itf.c642
-rw-r--r--usr/src/lib/libsip/common/sip_miscdefs.h164
-rw-r--r--usr/src/lib/libsip/common/sip_msg.c931
-rw-r--r--usr/src/lib/libsip/common/sip_msg.h518
-rw-r--r--usr/src/lib/libsip/common/sip_parse_generic.c1294
-rw-r--r--usr/src/lib/libsip/common/sip_parse_generic.h69
-rw-r--r--usr/src/lib/libsip/common/sip_parse_hdrs.c1678
-rw-r--r--usr/src/lib/libsip/common/sip_parse_uri.c1588
-rw-r--r--usr/src/lib/libsip/common/sip_parse_uri.h117
-rw-r--r--usr/src/lib/libsip/common/sip_reass.c243
-rw-r--r--usr/src/lib/libsip/common/sip_timeout.c364
-rw-r--r--usr/src/lib/libsip/common/sip_ui.c1342
-rw-r--r--usr/src/lib/libsip/common/sip_uri_ui.c471
-rw-r--r--usr/src/lib/libsip/common/sip_xaction.c631
-rw-r--r--usr/src/lib/libsip/common/sip_xaction.h127
-rw-r--r--usr/src/lib/libsip/common/sip_xaction_state_mc.c1514
-rw-r--r--usr/src/lib/libsip/common/sip_xaction_ui.c204
-rw-r--r--usr/src/lib/libsip/i386/Makefile29
-rw-r--r--usr/src/lib/libsip/sparc/Makefile29
-rw-r--r--usr/src/lib/libsip/sparcv9/Makefile30
35 files changed, 20394 insertions, 0 deletions
diff --git a/usr/src/lib/libsip/Makefile b/usr/src/lib/libsip/Makefile
new file mode 100644
index 0000000000..a61fcf8d7b
--- /dev/null
+++ b/usr/src/lib/libsip/Makefile
@@ -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
diff --git a/usr/src/lib/libsip/Makefile.com b/usr/src/lib/libsip/Makefile.com
new file mode 100644
index 0000000000..df2da7c646
--- /dev/null
+++ b/usr/src/lib/libsip/Makefile.com
@@ -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
diff --git a/usr/src/lib/libsip/amd64/Makefile b/usr/src/lib/libsip/amd64/Makefile
new file mode 100644
index 0000000000..41991fbf4c
--- /dev/null
+++ b/usr/src/lib/libsip/amd64/Makefile
@@ -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)
diff --git a/usr/src/lib/libsip/common/llib-lsip b/usr/src/lib/libsip/common/llib-lsip
new file mode 100644
index 0000000000..173c2178ea
--- /dev/null
+++ b/usr/src/lib/libsip/common/llib-lsip
@@ -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>
diff --git a/usr/src/lib/libsip/common/mapfile-vers b/usr/src/lib/libsip/common/mapfile-vers
new file mode 100644
index 0000000000..e32b223c1b
--- /dev/null
+++ b/usr/src/lib/libsip/common/mapfile-vers
@@ -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:
+ *;
+};
diff --git a/usr/src/lib/libsip/common/sip.h b/usr/src/lib/libsip/common/sip.h
new file mode 100644
index 0000000000..29f0731aff
--- /dev/null
+++ b/usr/src/lib/libsip/common/sip.h
@@ -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 */
diff --git a/usr/src/lib/libsip/common/sip_add_hdrs.c b/usr/src/lib/libsip/common/sip_add_hdrs.c
new file mode 100644
index 0000000000..3b696fd60a
--- /dev/null
+++ b/usr/src/lib/libsip/common/sip_add_hdrs.c
@@ -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));
+}
diff --git a/usr/src/lib/libsip/common/sip_dialog.c b/usr/src/lib/libsip/common/sip_dialog.c
new file mode 100644
index 0000000000..36d268e314
--- /dev/null
+++ b/usr/src/lib/libsip/common/sip_dialog.c
@@ -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;
+}
diff --git a/usr/src/lib/libsip/common/sip_dialog.h b/usr/src/lib/libsip/common/sip_dialog.h
new file mode 100644
index 0000000000..7596ee9bf3
--- /dev/null
+++ b/usr/src/lib/libsip/common/sip_dialog.h
@@ -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 */
diff --git a/usr/src/lib/libsip/common/sip_dialog_ui.c b/usr/src/lib/libsip/common/sip_dialog_ui.c
new file mode 100644
index 0000000000..698a0c8761
--- /dev/null
+++ b/usr/src/lib/libsip/common/sip_dialog_ui.c
@@ -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);
+}
diff --git a/usr/src/lib/libsip/common/sip_gids.c b/usr/src/lib/libsip/common/sip_gids.c
new file mode 100644
index 0000000000..f1ecfd726b
--- /dev/null
+++ b/usr/src/lib/libsip/common/sip_gids.c
@@ -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);
+}
diff --git a/usr/src/lib/libsip/common/sip_hash.c b/usr/src/lib/libsip/common/sip_hash.c
new file mode 100644
index 0000000000..ee920241f0
--- /dev/null
+++ b/usr/src/lib/libsip/common/sip_hash.c
@@ -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);
+}
diff --git a/usr/src/lib/libsip/common/sip_hash.h b/usr/src/lib/libsip/common/sip_hash.h
new file mode 100644
index 0000000000..99e2bedf38
--- /dev/null
+++ b/usr/src/lib/libsip/common/sip_hash.h
@@ -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 */
diff --git a/usr/src/lib/libsip/common/sip_hdrs_ui.c b/usr/src/lib/libsip/common/sip_hdrs_ui.c
new file mode 100644
index 0000000000..a10a787fe2
--- /dev/null
+++ b/usr/src/lib/libsip/common/sip_hdrs_ui.c
@@ -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);
+}
diff --git a/usr/src/lib/libsip/common/sip_headers.c b/usr/src/lib/libsip/common/sip_headers.c
new file mode 100644
index 0000000000..64c41de65d
--- /dev/null
+++ b/usr/src/lib/libsip/common/sip_headers.c
@@ -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);
+}
diff --git a/usr/src/lib/libsip/common/sip_itf.c b/usr/src/lib/libsip/common/sip_itf.c
new file mode 100644
index 0000000000..40f3c955e2
--- /dev/null
+++ b/usr/src/lib/libsip/common/sip_itf.c
@@ -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);
+}
diff --git a/usr/src/lib/libsip/common/sip_miscdefs.h b/usr/src/lib/libsip/common/sip_miscdefs.h
new file mode 100644
index 0000000000..bfde31527c
--- /dev/null
+++ b/usr/src/lib/libsip/common/sip_miscdefs.h
@@ -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 */
diff --git a/usr/src/lib/libsip/common/sip_msg.c b/usr/src/lib/libsip/common/sip_msg.c
new file mode 100644
index 0000000000..ed1169f66c
--- /dev/null
+++ b/usr/src/lib/libsip/common/sip_msg.c
@@ -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);
+}
diff --git a/usr/src/lib/libsip/common/sip_msg.h b/usr/src/lib/libsip/common/sip_msg.h
new file mode 100644
index 0000000000..3c9689257d
--- /dev/null
+++ b/usr/src/lib/libsip/common/sip_msg.h
@@ -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 */
diff --git a/usr/src/lib/libsip/common/sip_parse_generic.c b/usr/src/lib/libsip/common/sip_parse_generic.c
new file mode 100644
index 0000000000..782a69aa76
--- /dev/null
+++ b/usr/src/lib/libsip/common/sip_parse_generic.c
@@ -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);
+}
diff --git a/usr/src/lib/libsip/common/sip_parse_generic.h b/usr/src/lib/libsip/common/sip_parse_generic.h
new file mode 100644
index 0000000000..f7fb084f33
--- /dev/null
+++ b/usr/src/lib/libsip/common/sip_parse_generic.h
@@ -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 */
diff --git a/usr/src/lib/libsip/common/sip_parse_hdrs.c b/usr/src/lib/libsip/common/sip_parse_hdrs.c
new file mode 100644
index 0000000000..fc021678d8
--- /dev/null
+++ b/usr/src/lib/libsip/common/sip_parse_hdrs.c
@@ -0,0 +1,1678 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+
+/*
+ * Copyright 2006 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include "sip_parse_uri.h"
+#include "sip_msg.h"
+#include "sip_miscdefs.h"
+#include "sip_parse_generic.h"
+
+/*
+ * Accept = "Accept" HCOLON [ accept-range *(COMMA accept-range) ]
+ * accept-range = media-range *(SEMI accept-param)
+ * media-range = ("* / *" | (m-type SLASH "*") | (m-type SLASH m-subtype))
+ * *(SEMI m-param)
+ * accept-param = ("q" EQUAL qvalue) | generic-param
+ * qvalue = ("0" ["." 0*3DIGIT]) | ("1" ["." 0*3DIGIT])
+ * generic-param = token [ EQUAL gen-value]
+ * gen-value = token | host | quoted-str
+ */
+int
+sip_parse_acpt_header(_sip_header_t *sip_header, sip_parsed_header_t **header)
+{
+ if (sip_is_empty_hdr(sip_header))
+ return (sip_parse_hdr_empty(sip_header, header));
+ return (sip_parse_hdr_parser1(sip_header, header, SIP_SLASH));
+}
+
+/*
+ * Accept-Encoding = "Accept-Encoding" ":" 1#(codings [ ";" "q" "=" qval])
+ * codings = (content-coding | "*")
+ * content-coding = token
+ */
+int
+sip_parse_acpt_encode_header(_sip_header_t *sip_header,
+ sip_parsed_header_t **header)
+{
+ return (sip_parse_hdr_parser1(sip_header, header, (char)NULL));
+}
+
+/*
+ * Accept-Language = "Accept-Language" ":" [ lang * (COMMA lang) ]
+ * lang = lang-range *(SEMI accept-param)
+ * lang-range = ((1*8ALPHA * ("-" 1*8ALPHA)) | "*"
+ */
+int
+sip_parse_acpt_lang_header(_sip_header_t *sip_header,
+ sip_parsed_header_t **header)
+{
+ if (sip_is_empty_hdr(sip_header))
+ return (sip_parse_hdr_empty(sip_header, header));
+ return (sip_parse_hdr_parser1(sip_header, header, (char)NULL));
+}
+
+/*
+ * Alert-Info = "Alert-Info" ":" alert-param *(COMMA alert-param)
+ * alert-param = LAQUOT absoluteURI RAQUOT * (SEMI generic-param)
+ */
+int
+sip_parse_alert_header(_sip_header_t *sip_header, sip_parsed_header_t **header)
+{
+ return (sip_parse_hdr_parser3(sip_header, header, SIP_STR_VAL, B_TRUE));
+}
+
+/*
+ * Allow = "Allow" ":" method-name1[, method-name2..]
+ */
+int
+sip_parse_allow_header(_sip_header_t *hdr, sip_parsed_header_t **phdr)
+{
+ sip_parsed_header_t *parsed_header;
+ sip_hdr_value_t *value = NULL;
+ sip_hdr_value_t *last_value = NULL;
+ int len;
+ int i;
+ int ret;
+ boolean_t multi_value = B_FALSE;
+
+ if ((ret = sip_prim_parsers(hdr, phdr)) != 0)
+ return (ret);
+
+ if (*phdr != NULL)
+ return (0);
+
+ parsed_header = calloc(1, sizeof (sip_parsed_header_t));
+ if (parsed_header == NULL)
+ return (ENOMEM);
+ parsed_header->sip_parsed_header_version = SIP_PARSED_HEADER_VERSION_1;
+ parsed_header->sip_header = hdr;
+
+ while (hdr->sip_hdr_current < hdr->sip_hdr_end) {
+ value = calloc(1, sizeof (sip_hdr_value_t));
+ if (value == NULL) {
+ sip_free_phdr(parsed_header);
+ return (ENOMEM);
+ }
+ if (last_value != NULL)
+ last_value->sip_next_value = value;
+ else
+ parsed_header->value = (sip_value_t *)value;
+
+ value->sip_value_start = hdr->sip_hdr_current;
+ value->sip_value_header = parsed_header;
+
+ if (sip_find_separator(hdr, SIP_COMMA, (char)NULL,
+ (char)NULL) == 0) {
+ multi_value = B_TRUE;
+ }
+
+ len = hdr->sip_hdr_current - value->sip_value_start;
+ for (i = 1; i < MAX_SIP_METHODS; i++) {
+ if (strncmp(sip_methods[i].name, value->sip_value_start,
+ len) == 0) {
+ break;
+ }
+ }
+ if (i >= MAX_SIP_METHODS) {
+ value->int_val = 0;
+ value->sip_value_state = SIP_VALUE_BAD;
+ if (multi_value)
+ goto next_val;
+ else
+ goto end;
+ }
+ value->int_val = i;
+ if (!multi_value)
+ goto end;
+ next_val:
+ if (sip_find_token(hdr, SIP_COMMA) != 0)
+ break;
+ value->sip_value_end = hdr->sip_hdr_current - 1;
+ last_value = value;
+ (void) sip_skip_white_space(hdr);
+ }
+
+end:
+ *phdr = parsed_header;
+ return (0);
+}
+
+
+/*
+ * Call-Info = "Call-Info" HCOLON info * (COMMA info)
+ * info = LAQUOT absoluteURI RAQUOT * (SEMI info-param)
+ * info-param = ("purpose" EQUAL ("icon" | "info" | "card" | token)) |
+ * generic-param
+ */
+int
+sip_parse_callinfo_header(_sip_header_t *sip_header,
+ sip_parsed_header_t **header)
+{
+ return (sip_parse_hdr_parser3(sip_header, header, SIP_STR_VAL, B_TRUE));
+}
+
+/*
+ * Content-Disposition = "Content-Disposition" HCOLON disp-type *
+ * (SEMI disp-param)
+ * disp-type = "render" | "session" | "icon" | "alert" | disp-ext-token
+ * disp-param = handling-param | generic-param
+ * handling-param = "handling" EQUAL("optional" | "required" | other-handling)
+ * other-handling = token
+ * disp-ext-token = token
+ *
+ */
+int
+sip_parse_contentdis_header(_sip_header_t *sip_header,
+ sip_parsed_header_t **header)
+{
+ return (sip_parse_hdr_parser1(sip_header, header, (char)NULL));
+}
+
+/*
+ * Content-Encoding = ("Content-Encoding" | "e") HCOLON content-coding *
+ * (COMMA content-coding)
+ */
+int
+sip_parse_contentencode_header(_sip_header_t *sip_header,
+ sip_parsed_header_t **header)
+{
+ return (sip_parse_hdr_parser1(sip_header, header, (char)NULL));
+}
+
+/*
+ * Content-Language = ("Content-Language" | "l") HCOLON lang-tag *
+ * (COMMA lang-tag)
+ * lang-tag = primary-tag *("-" subtag)
+ * prmary-tag = 1*8ALPHA
+ * subtag = 1*8ALPHA
+ */
+int
+sip_parse_contentlang_header(_sip_header_t *sip_header,
+ sip_parsed_header_t **header)
+{
+ return (sip_parse_hdr_parser1(sip_header, header, (char)NULL));
+}
+
+/*
+ * Date = "Date" HCOLON SIPdate
+ * SIPdate = wkday "," SP date1 SP time SP "GMT"
+ * date1 = 2DIGIT SP mnth SP 4DIGIT; day month year
+ * time = 2DIGIT ":" 2DIGIT ":" 2DIGIT
+ * wkday = "Mon" | "Tue" | "Wed" | "Thu" | "Fri" | "Sat" | "Sun"
+ * month = "Jan" | "Feb" etc
+ */
+int
+sip_parse_date_header(_sip_header_t *sip_header, sip_parsed_header_t **header)
+{
+ sip_parsed_header_t *parsed_header;
+ int r;
+ sip_hdr_value_t *value = NULL;
+
+ if ((r = sip_prim_parsers(sip_header, header)) != 0)
+ return (r);
+
+ if (*header != NULL)
+ return (0);
+
+ parsed_header = calloc(1, sizeof (sip_parsed_header_t));
+ if (parsed_header == NULL)
+ return (ENOMEM);
+ parsed_header->sip_parsed_header_version = SIP_PARSED_HEADER_VERSION_1;
+ parsed_header->sip_header = sip_header;
+
+ value = calloc(1, sizeof (sip_hdr_value_t));
+ if (value == NULL) {
+ sip_free_phdr(parsed_header);
+ return (ENOMEM);
+ }
+ parsed_header->value = (sip_value_t *)value;
+
+ value->sip_value_start = sip_header->sip_hdr_current;
+ value->sip_value_header = parsed_header;
+ value->date_wd_ptr = sip_header->sip_hdr_current;
+ if (sip_find_token(sip_header, SIP_COMMA) == 0) {
+ value->date_wd_len = sip_header->sip_hdr_current -
+ value->date_wd_ptr - 1;
+ sip_header->sip_hdr_current++;
+ if (sip_skip_white_space(sip_header) != 0) {
+ value->sip_value_state = SIP_VALUE_BAD;
+ return (EPROTO);
+ }
+ } else {
+ value->sip_value_state = SIP_VALUE_BAD;
+ return (EPROTO);
+ }
+
+ if (sip_skip_white_space(sip_header) != 0) {
+ value->sip_value_state = SIP_VALUE_BAD;
+ return (EPROTO);
+ }
+ r = sip_atoi(sip_header, &value->date_d);
+ if (r != 0 || value->date_d < 0 || value->date_d > 31) {
+ value->sip_value_state = SIP_VALUE_BAD;
+ return (EPROTO);
+ }
+ if (sip_skip_white_space(sip_header) != 0) {
+ value->sip_value_state = SIP_VALUE_BAD;
+ return (EPROTO);
+ }
+ value->date_m_ptr = sip_header->sip_hdr_current;
+ if (sip_find_token(sip_header, SIP_SP) == 0) {
+ value->date_m_len = sip_header->sip_hdr_current -
+ value->date_m_ptr - 1;
+ } else {
+ value->sip_value_state = SIP_VALUE_BAD;
+ return (EPROTO);
+ }
+
+ r = sip_atoi(sip_header, &value->date_y);
+ if (r != 0 || value->date_y < 0) {
+ value->sip_value_state = SIP_VALUE_BAD;
+ return (EPROTO);
+ }
+ if (sip_skip_white_space(sip_header) != 0) {
+ value->sip_value_state = SIP_VALUE_BAD;
+ return (EPROTO);
+ }
+ value->date_t_ptr = sip_header->sip_hdr_current;
+ if (sip_find_token(sip_header, SIP_SP) == 0) {
+ value->date_t_len = sip_header->sip_hdr_current -
+ value->date_t_ptr - 1;
+ } else {
+ value->sip_value_state = SIP_VALUE_BAD;
+ return (EPROTO);
+ }
+
+ value->date_tz_ptr = sip_header->sip_hdr_current;
+ /*
+ * minus 2 to get rid of the CRLF
+ */
+ value->date_tz_len = sip_header->sip_hdr_end -
+ sip_header->sip_hdr_current - 2;
+
+ *header = parsed_header;
+
+ sip_header->sip_hdr_parsed = *header;
+ return (0);
+}
+
+/*
+ * Error-Info = "Error-Info" HCOLON error-uri *(COMMA error-uri)
+ * error-uri = LAQUOT absoluteURI RAQUOT *(SEMI generic-param)
+ */
+int
+sip_parse_errorinfo_header(_sip_header_t *sip_header,
+ sip_parsed_header_t **header)
+{
+ return (sip_parse_hdr_parser3(sip_header, header, SIP_STR_VAL, B_TRUE));
+}
+
+/*
+ * Expires = "Expires" HCOLON delta-seconds
+ */
+int
+sip_parse_expire_header(_sip_header_t *sip_header,
+ sip_parsed_header_t **header)
+{
+ return (sip_parse_hdr_parser2(sip_header, header, SIP_INT_VAL));
+}
+
+/*
+ * In-Reply-To = "In-Reply-To" HCOLON callid *(COMMA callid)
+ */
+int
+sip_parse_inreplyto_header(_sip_header_t *sip_header,
+ sip_parsed_header_t **header)
+{
+ return (sip_parse_hdr_parser1(sip_header, header, (char)NULL));
+}
+
+/*
+ * RSeq = "RSeq" HCOLON response-num
+ */
+int
+sip_parse_rseq(_sip_header_t *sip_header, sip_parsed_header_t **header)
+{
+ int r;
+ sip_hdr_value_t *rseq_value;
+
+ r = sip_parse_hdr_parser2(sip_header, header, SIP_INT_VAL);
+ /*
+ * Additionally, a value of 0 is bad_value
+ */
+ if (sip_header->sip_hdr_parsed != NULL &&
+ sip_header->sip_hdr_parsed->value != NULL) {
+ rseq_value = (sip_hdr_value_t *)
+ sip_header->sip_hdr_parsed->value;
+ if (rseq_value->int_val == 0)
+ rseq_value->sip_value_state = SIP_VALUE_BAD;
+ }
+ return (r);
+}
+
+/*
+ * Min-Expires = "Min-Expires" HCOLON delta-seconds
+ */
+int
+sip_parse_minexpire_header(_sip_header_t *sip_header,
+ sip_parsed_header_t **header)
+{
+ return (sip_parse_hdr_parser2(sip_header, header, SIP_INT_VAL));
+}
+
+/*
+ * MIME-Version = "MIME-Version" HCOLON 1*DIGIT "." 1*DIGIT
+ */
+int
+sip_parse_mimeversion_header(_sip_header_t *sip_header,
+ sip_parsed_header_t **header)
+{
+ return (sip_parse_hdr_parser4(sip_header, header));
+}
+
+/*
+ * Organization = "Organization" HCOLON [TEXT-UTF8-TRIM]
+ */
+int
+sip_parse_org_header(_sip_header_t *sip_header,
+ sip_parsed_header_t **header)
+{
+ if (sip_is_empty_hdr(sip_header))
+ return (sip_parse_hdr_empty(sip_header, header));
+ return (sip_parse_hdr_parser4(sip_header, header));
+}
+
+/*
+ * Priority = "Priority" HCOLON priority-val
+ * priority-val = "emergency" | "urgent" | "normal" | "non-urgent" | other
+ * other = token
+ */
+int
+sip_parse_priority_header(_sip_header_t *sip_header,
+ sip_parsed_header_t **header)
+{
+ return (sip_parse_hdr_parser4(sip_header, header));
+}
+
+/*
+ * Reply-To = "Reply-To" HCOLON rplyto-spec
+ * rplyto-spec = (name-addr | addr-spec) *(SEMI rplyto-param)
+ * rplyto-param = generic-param
+ * name-addr = [ display-name ] LAQUOT addr-spec RAQUOT
+ * addr-spec = SIP-URI | SIPS-URI | absolute URI
+ */
+int
+sip_parse_replyto_header(_sip_header_t *sip_header,
+ sip_parsed_header_t **header)
+{
+ return (sip_parse_hdr_parser3(sip_header, header, SIP_STRS_VAL,
+ B_TRUE));
+}
+
+/*
+ * PRIVACY = "Privacy" HCOLON priv-value *(COMMA priv-value)
+ * priv-value = "header" / "session" / "user" / "none" / "critical"
+ * / token / id
+ */
+int
+sip_parse_privacy_header(_sip_header_t *sip_header,
+ sip_parsed_header_t **header)
+{
+ return (sip_parse_hdr_parser1(sip_header, header, (char)NULL));
+}
+
+
+/*
+ * Require = "Require" HCOLON option-tag * (COMMA option-tag)
+ */
+int
+sip_parse_require_header(_sip_header_t *sip_header,
+ sip_parsed_header_t **header)
+{
+ return (sip_parse_hdr_parser1(sip_header, header, (char)NULL));
+}
+
+/*
+ * Retry-After = "Retry-After" HCOLON delta-seconds [ comment ] *
+ * (SEMI retry-param)
+ * retry-param = "duration" EQUAL delta-seconds
+ */
+int
+sip_parse_retryaft_header(_sip_header_t *sip_header,
+ sip_parsed_header_t **header)
+{
+ sip_parsed_header_t *parsed_header;
+ sip_hdr_value_t *value = NULL;
+ int ret;
+
+ if ((ret = sip_prim_parsers(sip_header, header)) != 0)
+ return (ret);
+
+ if (*header != NULL)
+ return (0);
+
+ parsed_header = calloc(1, sizeof (sip_parsed_header_t));
+ if (parsed_header == NULL)
+ return (ENOMEM);
+ parsed_header->sip_parsed_header_version = SIP_PARSED_HEADER_VERSION_1;
+ parsed_header->sip_header = sip_header;
+
+ value = calloc(1, sizeof (sip_hdr_value_t));
+ if (value == NULL) {
+ sip_free_phdr(parsed_header);
+ return (ENOMEM);
+ }
+
+ parsed_header->value = (sip_value_t *)value;
+ value->sip_value_start = sip_header->sip_hdr_current;
+ value->sip_value_header = parsed_header;
+
+ ret = sip_atoi(sip_header, &(value->intstr_int));
+ if (ret != 0)
+ value->sip_value_state = SIP_VALUE_BAD;
+ if (sip_find_token(sip_header, SIP_LPAR) == 0) {
+ value->intstr_str_ptr = sip_header->sip_hdr_current;
+ if (sip_find_token(sip_header, SIP_RPAR) == 0) {
+ value->intstr_str_len =
+ sip_header->sip_hdr_current -
+ value->intstr_str_ptr - 1;
+ if (sip_find_token(sip_header, SIP_SEMI) == 0) {
+ sip_header->sip_hdr_current--;
+ (void) sip_parse_params(sip_header,
+ &(value->sip_param_list));
+ }
+ } else {
+ value->sip_value_state = SIP_VALUE_BAD;
+ return (EPROTO);
+ }
+ } else {
+ value->intstr_str_ptr = NULL;
+ value->intstr_str_len = 0;
+
+ /*
+ * from value start, search if parameter list
+ */
+ sip_header->sip_hdr_current = value->sip_value_start;
+ if (sip_find_token(sip_header, SIP_SEMI) == 0) {
+ sip_header->sip_hdr_current--;
+ (void) sip_parse_params(sip_header,
+ &(value->sip_param_list));
+ }
+ }
+
+ *header = parsed_header;
+ sip_header->sip_hdr_parsed = *header;
+ return (0);
+}
+
+/*
+ * Server = "Server" HCOLON servel-val *(LWS server-val)
+ * servel-val = product|comment
+ * product = token [SLASH version]
+ * version = token
+ * Treated as one single string
+ */
+int
+sip_parse_server_header(_sip_header_t *sip_header, sip_parsed_header_t **header)
+{
+ return (sip_parse_hdr_parser4(sip_header, header));
+}
+
+/*
+ * Subject = ("Subject" | "s")HCOLON [TEXT-UTF8-TRIM]
+ */
+int
+sip_parse_subject_header(_sip_header_t *sip_header,
+ sip_parsed_header_t **header)
+{
+ if (sip_is_empty_hdr(sip_header))
+ return (sip_parse_hdr_empty(sip_header, header));
+ return (sip_parse_hdr_parser4(sip_header, header));
+}
+
+/*
+ * Supported = ("Supported" | "k") HCOLON [option-tag * (COMMA option-tag) ]
+ */
+int
+sip_parse_support_header(_sip_header_t *sip_header,
+ sip_parsed_header_t **header)
+{
+ if (sip_is_empty_hdr(sip_header))
+ return (sip_parse_hdr_empty(sip_header, header));
+ return (sip_parse_hdr_parser1(sip_header, header, (char)NULL));
+}
+
+/*
+ * Timestamp = "Timestamp" HCOLON 1*DIGIT ["." *(DIGIT)] [LWS delay]
+ */
+int
+sip_parse_timestamp_header(_sip_header_t *sip_header,
+ sip_parsed_header_t **header)
+{
+ sip_parsed_header_t *parsed_header;
+ sip_hdr_value_t *value = NULL;
+ int ret;
+
+ if ((ret = sip_prim_parsers(sip_header, header)) != 0)
+ return (ret);
+
+ if (*header != NULL)
+ return (0);
+
+ parsed_header = calloc(1, sizeof (sip_parsed_header_t));
+ if (parsed_header == NULL)
+ return (ENOMEM);
+ parsed_header->sip_parsed_header_version = SIP_PARSED_HEADER_VERSION_1;
+ parsed_header->sip_header = sip_header;
+
+ value = calloc(1, sizeof (sip_hdr_value_t));
+ if (value == NULL) {
+ sip_free_phdr(parsed_header);
+ return (ENOMEM);
+ }
+ parsed_header->value = (sip_value_t *)value;
+
+ value->sip_value_start = sip_header->sip_hdr_current;
+ value->sip_value_header = parsed_header;
+
+ if (sip_skip_white_space(sip_header) != 0) {
+ value->sip_value_state = SIP_VALUE_BAD;
+ return (EPROTO);
+ }
+ value->strs1_val_ptr = sip_header->sip_hdr_current;
+
+ if (sip_find_white_space(sip_header) == 0) {
+ /*
+ * timestamp and delay, timestamp in str1, delay in str2
+ */
+ value->strs1_val_len = sip_header->sip_hdr_current -
+ value->strs1_val_ptr;
+ (void) sip_skip_white_space(sip_header);
+
+ value->strs2_val_ptr = sip_header->sip_hdr_current;
+ if (sip_find_cr(sip_header) != 0) {
+ value->sip_value_state = SIP_VALUE_BAD;
+ return (EPROTO);
+ }
+ if (sip_header->sip_hdr_current < value->strs2_val_ptr) {
+ value->strs2_val_ptr = NULL;
+ value->strs2_val_len = 0;
+ } else {
+ value->strs2_val_len = sip_header->sip_hdr_current -
+ value->strs2_val_ptr;
+ }
+ } else {
+ /*
+ * no delay information
+ */
+ value->strs1_val_len = sip_header->sip_hdr_current
+ - value->strs1_val_ptr;
+ value->strs2_val_ptr = NULL;
+ value->strs2_val_len = 0;
+ }
+
+ *header = parsed_header;
+ sip_header->sip_hdr_parsed = *header;
+
+ return (0);
+}
+/*
+ * Unsupported = "Unsupported" HCOLON option-tag * (COMMA option-tag)
+ */
+int
+sip_parse_usupport_header(_sip_header_t *sip_header,
+ sip_parsed_header_t **header)
+{
+ return (sip_parse_hdr_parser1(sip_header, header, (char)NULL));
+}
+
+/*
+ * User-Agent = "User-Agent" HCOLON server-val * (LWS server-val)
+ * servel-val = product |comment
+ * product = token [SLASH version]
+ * version = token
+ */
+int
+sip_parse_useragt_header(_sip_header_t *sip_header,
+ sip_parsed_header_t **header)
+{
+ return (sip_parse_hdr_parser4(sip_header, header));
+}
+
+/*
+ * Warning = "Warning" HCOLON warning-value *(COMMA warning-value)
+ * warning-value = warn-code SP warn-agent SP warn-text
+ * warn-code = 3DIGIT
+ * warn-agent = hostport | pseudonym ;
+ * the name or pseudonym of the server adding;
+ * the Warning header, for use in debugging
+ * warn-text = quoted-string
+ * pseudonym = token
+ */
+int
+sip_parse_warn_header(_sip_header_t *sip_header, sip_parsed_header_t **header)
+{
+ sip_parsed_header_t *parsed_header;
+ int ret;
+ sip_hdr_value_t *value = NULL;
+ sip_hdr_value_t *last_value = NULL;
+
+ if ((ret = sip_prim_parsers(sip_header, header)) != 0)
+ return (ret);
+
+ if (*header != NULL)
+ return (0);
+
+ parsed_header = calloc(1, sizeof (sip_parsed_header_t));
+ if (parsed_header == NULL)
+ return (ENOMEM);
+ parsed_header->sip_parsed_header_version = SIP_PARSED_HEADER_VERSION_1;
+ parsed_header->sip_header = sip_header;
+
+ while (sip_header->sip_hdr_current < sip_header->sip_hdr_end) {
+ value = calloc(1, sizeof (sip_hdr_value_t));
+ if (value == NULL) {
+ sip_free_phdr(parsed_header);
+ return (ENOMEM);
+ }
+
+ if (last_value != NULL)
+ last_value->sip_next_value = value;
+ else
+ parsed_header->value = (sip_value_t *)value;
+
+ value->sip_value_start = sip_header->sip_hdr_current;
+ value->sip_value_header = parsed_header;
+
+ ret = sip_atoi(sip_header, &value->warn_code);
+ if (ret != 0 || value->warn_code < 100 ||
+ value->warn_code > 999) {
+ value->sip_value_state = SIP_VALUE_BAD;
+ goto get_next_val;
+ }
+ if (sip_skip_white_space(sip_header) != 0) {
+ value->sip_value_state = SIP_VALUE_BAD;
+ goto get_next_val;
+ }
+ value->warn_agt_ptr = sip_header->sip_hdr_current;
+
+ if (sip_find_token(sip_header, SIP_QUOTE) == 0) {
+ /*
+ * get warning agent
+ */
+ sip_header->sip_hdr_current--;
+ (void) sip_reverse_skip_white_space(sip_header);
+ value->warn_agt_len = sip_header->sip_hdr_current -
+ value->warn_agt_ptr - 1;
+ if (value->warn_agt_len <= 0) {
+ value->warn_agt_ptr = NULL;
+ value->sip_value_state = SIP_VALUE_BAD;
+ }
+
+ /*
+ * We will have a SIP_QUOTE here
+ */
+ (void) sip_find_token(sip_header, SIP_QUOTE);
+
+ value->warn_text_ptr = sip_header->sip_hdr_current;
+ if (sip_find_token(sip_header, SIP_QUOTE) == 0) {
+ value->warn_text_len =
+ sip_header->sip_hdr_current -
+ value->warn_text_ptr - 1;
+ } else {
+ value->sip_value_state = SIP_VALUE_BAD;
+ goto get_next_val;
+ }
+ } else
+ /*
+ * warning text must present
+ */
+ value->sip_value_state = SIP_VALUE_BAD;
+
+get_next_val:
+ if (sip_find_token(sip_header, SIP_COMMA) != 0)
+ break;
+ value->sip_value_end = sip_header->sip_hdr_current - 1;
+ last_value = value;
+ (void) sip_skip_white_space(sip_header);
+ }
+
+ *header = parsed_header;
+
+ sip_header->sip_hdr_parsed = *header;
+ return (0);
+}
+
+/*
+ * Parse RAck header
+ * "RAck" HCOLON response-num LWS CSeq-num LWS Method
+ * response-num = 1*DIGIT
+ * CSeq-num = 1*DIGIT
+ */
+int
+sip_parse_rack(_sip_header_t *sip_header, sip_parsed_header_t **header)
+{
+ sip_parsed_header_t *parsed_header;
+ sip_hdr_value_t *rack_value;
+ int len;
+ char *tmp_ptr;
+ int i;
+ int ret;
+
+ if ((ret = sip_prim_parsers(sip_header, header)) != 0)
+ return (ret);
+
+ if (*header != NULL)
+ return (0);
+
+ parsed_header = calloc(1, sizeof (sip_parsed_header_t));
+ if (parsed_header == NULL)
+ return (ENOMEM);
+ parsed_header->sip_parsed_header_version = SIP_PARSED_HEADER_VERSION_1;
+ parsed_header->sip_header = sip_header;
+
+ parsed_header->value = calloc(1, sizeof (sip_hdr_value_t));
+ if (parsed_header->value == NULL) {
+ free(parsed_header);
+ return (ENOMEM);
+ }
+ rack_value = (sip_hdr_value_t *)parsed_header->value;
+ rack_value->sip_value_version = SIP_VALUE_VERSION_1;
+ rack_value->sip_value_start = sip_header->sip_hdr_current;
+ rack_value->sip_value_header = parsed_header;
+ if (sip_atoi(sip_header, &rack_value->rack_resp) ||
+ rack_value->rack_resp == 0) {
+ rack_value->sip_value_state = SIP_VALUE_BAD;
+ rack_value->sip_value_end = sip_header->sip_hdr_end - 2;
+ goto rack_parse_done;
+ }
+ rack_value->sip_value_header = parsed_header;
+ /*
+ * Get cseq.
+ */
+ if (sip_skip_white_space(sip_header) != 0) {
+ rack_value->sip_value_state = SIP_VALUE_BAD;
+ rack_value->sip_value_end = sip_header->sip_hdr_end - 2;
+ goto rack_parse_done;
+ }
+ if (sip_atoi(sip_header, &rack_value->rack_cseq)) {
+ rack_value->sip_value_state = SIP_VALUE_BAD;
+ rack_value->sip_value_end = sip_header->sip_hdr_end - 2;
+ goto rack_parse_done;
+ }
+ /*
+ * Get method.
+ */
+ if (sip_skip_white_space(sip_header) != 0) {
+ rack_value->sip_value_state = SIP_VALUE_BAD;
+ rack_value->sip_value_end = sip_header->sip_hdr_end - 2;
+ goto rack_parse_done;
+ }
+
+ tmp_ptr = sip_header->sip_hdr_current;
+ if (sip_find_white_space(sip_header)) {
+ rack_value->sip_value_state = SIP_VALUE_BAD;
+ rack_value->sip_value_end = sip_header->sip_hdr_end - 2;
+ goto rack_parse_done;
+ }
+
+ len = sip_header->sip_hdr_current - tmp_ptr;
+
+ for (i = 1; i < MAX_SIP_METHODS; i++) {
+ if (strncmp(sip_methods[i].name, tmp_ptr, len) == 0)
+ break;
+ }
+
+ if (i >= MAX_SIP_METHODS) {
+ rack_value->sip_value_state = SIP_VALUE_BAD;
+ rack_value->sip_value_end = sip_header->sip_hdr_end - 2;
+ goto rack_parse_done;
+ }
+
+ rack_value->rack_method = i;
+ rack_value->sip_value_end = sip_header->sip_hdr_current;
+
+rack_parse_done:
+ sip_header->sip_hdr_parsed = parsed_header;
+
+ *header = parsed_header;
+ return (0);
+}
+
+/*
+ * Allow = "Allow" HCOLON [Method *(COMMA Method)]
+ */
+int
+sip_parse_allow_events_header(_sip_header_t *sip_header,
+ sip_parsed_header_t **header)
+{
+ return (sip_parse_hdr_parser1(sip_header, header, (char)NULL));
+}
+
+/*
+ * Event = ( "Event" / "o" ) HCOLON event-type
+ * *( SEMI event-param )
+ * event-type = event-package *( "." event-template )
+ * event-package = token-nodot
+ * event-template = token-nodot
+ * token-nodot = 1*( alphanum / "-" / "!" / "%" / "*"
+ * / "_" / "+" / "`" / "'" / "~" )
+ * event-param = generic-param / ( "id" EQUAL token )
+ */
+int
+sip_parse_event_header(_sip_header_t *sip_header, sip_parsed_header_t **header)
+{
+ return (sip_parse_hdr_parser1(sip_header, header, (char)NULL));
+}
+
+/*
+ * Subscription-State = "Subscription-State" HCOLON substate-value
+ * *( SEMI subexp-params )
+ * substate-value = "active" / "pending" / "terminated"
+ * / extension-substate
+ * extension-substate = token
+ * subexp-params = ("reason" EQUAL event-reason-value)
+ * / ("expires" EQUAL delta-seconds)*
+ * / ("retry-after" EQUAL delta-seconds)
+ * / generic-param
+ * event-reason-value = "deactivated"
+ * / "probation"
+ * / "rejected"
+ * / "timeout"
+ * / "giveup"
+ * / "noresource"
+ * / event-reason-extension
+ * event-reason-extension = token
+ */
+int
+sip_parse_substate_header(_sip_header_t *sip_header,
+ sip_parsed_header_t **header)
+{
+ return (sip_parse_hdr_parser1(sip_header, header, (char)NULL));
+}
+
+/*
+ * Authorization = "Authorization" HCOLON credentials
+ * credentials = ("Digest" LWS digest-response)
+ * / other-response
+ * digest-response = dig-resp *(COMMA dig-resp)
+ * dig-resp = username / realm / nonce / digest-uri
+ * / dresponse / algorithm / cnonce
+ * / opaque / message-qop
+ * / nonce-count / auth-param
+ * username = "username" EQUAL username-value
+ * username-value = quoted-string
+ * digest-uri = "uri" EQUAL LDQUOT digest-uri-value RDQUOT
+ * digest-uri-value = rquest-uri ; Equal to request-uri as specified
+ * by HTTP/1.1
+ * message-qop = "qop" EQUAL qop-value
+ * cnonce = "cnonce" EQUAL cnonce-value
+ * cnonce-value = nonce-value
+ * nonce-count = "nc" EQUAL nc-value
+ * nc-value = 8LHEX
+ * dresponse = "response" EQUAL request-digest
+ * request-digest = LDQUOT 32LHEX RDQUOT
+ * auth-param = auth-param-name EQUAL
+ * ( token / quoted-string )
+ * auth-param-name = token
+ * other-response = auth-scheme LWS auth-param
+ * *(COMMA auth-param)
+ * auth-scheme = token
+ */
+int
+sip_parse_author_header(_sip_header_t *sip_header,
+ sip_parsed_header_t **header)
+{
+ return (sip_parse_hdr_parser5(sip_header, header, B_TRUE));
+}
+
+/*
+ * Authentication-Info = "Authentication-Info" HCOLON ainfo
+ * *(COMMA ainfo)
+ * ainfo = nextnonce / message-qop
+ * / response-auth / cnonce
+ * / nonce-count
+ * nextnonce = "nextnonce" EQUAL nonce-value
+ * response-auth = "rspauth" EQUAL response-digest
+ * response-digest = LDQUOT *LHEX RDQUOT
+ *
+ */
+int
+sip_parse_ainfo_header(_sip_header_t *sip_header,
+ sip_parsed_header_t **header)
+{
+ return (sip_parse_hdr_parser1(sip_header, header, (char)NULL));
+}
+
+/*
+ * Proxy-Authenticate = "Proxy-Authenticate" HCOLON challenge
+ * challenge = ("Digest" LWS digest-cln *(COMMA digest-cln))
+ * / other-challenge
+ * other-challenge = auth-scheme LWS auth-param
+ * *(COMMA auth-param)
+ * digest-cln = realm / domain / nonce
+ * / opaque / stale / algorithm
+ * / qop-options / auth-param
+ * realm = "realm" EQUAL realm-value
+ * realm-value = quoted-string
+ * domain = "domain" EQUAL LDQUOT URI
+ * *( 1*SP URI ) RDQUOT
+ * URI = absoluteURI / abs-path
+ * nonce = "nonce" EQUAL nonce-value
+ * nonce-value = quoted-string
+ * opaque = "opaque" EQUAL quoted-string
+ * stale = "stale" EQUAL ( "true" / "false" )
+ * algorithm = "algorithm" EQUAL ( "MD5" / "MD5-sess"
+ * / token )
+ * qop-options = "qop" EQUAL LDQUOT qop-value
+ * *("," qop-value) RDQUOT
+ * qop-value = "auth" / "auth-int" / token
+ *
+ */
+int
+sip_parse_pauthen_header(_sip_header_t *sip_header,
+ sip_parsed_header_t **header)
+{
+ return (sip_parse_hdr_parser5(sip_header, header, B_TRUE));
+}
+
+/*
+ * Proxy-Authorization = "Proxy-Authorization" HCOLON credentials
+ */
+int
+sip_parse_pauthor_header(_sip_header_t *sip_header,
+ sip_parsed_header_t **header)
+{
+ return (sip_parse_hdr_parser5(sip_header, header, B_TRUE));
+}
+
+/*
+ * Proxy-Require = "Proxy-Require" HCOLON option-tag
+ * *(COMMA option-tag)
+ * option-tag = token
+ */
+int
+sip_parse_preq_header(_sip_header_t *sip_header,
+ sip_parsed_header_t **header)
+{
+ return (sip_parse_hdr_parser1(sip_header, header, (char)NULL));
+}
+
+/*
+ * WWW-Authenticate = "WWW-Authenticate" HCOLON challenge
+ * extension-header = header-name HCOLON header-value
+ * header-name = token
+ * header-value = *(TEXT-UTF8char / UTF8-CONT / LWS)
+ * message-body = *OCTET
+ *
+ */
+int
+sip_parse_wauthen_header(_sip_header_t *sip_header,
+ sip_parsed_header_t **header)
+{
+ return (sip_parse_hdr_parser5(sip_header, header, B_TRUE));
+}
+
+/*
+ * Call-ID = ( "Call-ID" / "i" ) HCOLON callid
+ */
+int
+sip_parse_cid_header(_sip_header_t *sip_header, sip_parsed_header_t **header)
+{
+ return (sip_parse_hdr_parser4(sip_header, header));
+}
+
+/*
+ * CSeq = "CSeq" HCOLON 1*DIGIT LWS Method
+ */
+int
+sip_parse_cseq_header(_sip_header_t *sip_header, sip_parsed_header_t **header)
+{
+ sip_parsed_header_t *parsed_header;
+ sip_hdr_value_t *cseq_value;
+ int len;
+ char *tmp_ptr;
+ int i;
+ int ret;
+
+ if ((ret = sip_prim_parsers(sip_header, header)) != 0)
+ return (ret);
+
+ if (*header != NULL)
+ return (0);
+
+ parsed_header = calloc(1, sizeof (sip_parsed_header_t));
+ if (parsed_header == NULL)
+ return (ENOMEM);
+ parsed_header->sip_parsed_header_version = SIP_PARSED_HEADER_VERSION_1;
+ parsed_header->sip_header = sip_header;
+
+ parsed_header->value = calloc(1, sizeof (sip_hdr_value_t));
+ if (parsed_header->value == NULL) {
+ free(parsed_header);
+ return (ENOMEM);
+ }
+ cseq_value = (sip_hdr_value_t *)parsed_header->value;
+ cseq_value->sip_value_version = SIP_VALUE_VERSION_1;
+ cseq_value->sip_value_start = sip_header->sip_hdr_current;
+ if (sip_atoi(sip_header, &cseq_value->cseq_num)) {
+ cseq_value->sip_value_state = SIP_VALUE_BAD;
+ cseq_value->sip_value_end = sip_header->sip_hdr_end - 2;
+ goto cseq_parse_done;
+ }
+ cseq_value->sip_value_header = parsed_header;
+ /*
+ * Get method.
+ */
+ if (sip_skip_white_space(sip_header) != 0) {
+ cseq_value->sip_value_state = SIP_VALUE_BAD;
+ cseq_value->sip_value_end = sip_header->sip_hdr_end - 2;
+ goto cseq_parse_done;
+ }
+
+ tmp_ptr = sip_header->sip_hdr_current;
+
+ if (sip_find_white_space(sip_header)) {
+ cseq_value->sip_value_state = SIP_VALUE_BAD;
+ cseq_value->sip_value_end = sip_header->sip_hdr_current;
+ goto cseq_parse_done;
+ }
+
+ len = sip_header->sip_hdr_current - tmp_ptr;
+
+ for (i = 1; i < MAX_SIP_METHODS; i++) {
+ if (strncmp(sip_methods[i].name, tmp_ptr, len) == 0)
+ break;
+ }
+
+ if (i >= MAX_SIP_METHODS) {
+ cseq_value->sip_value_state = SIP_VALUE_BAD;
+ cseq_value->sip_value_end = sip_header->sip_hdr_current;
+ goto cseq_parse_done;
+ }
+
+ cseq_value->cseq_method = i;
+ cseq_value->sip_value_end = sip_header->sip_hdr_current;
+cseq_parse_done:
+
+ sip_header->sip_hdr_parsed = parsed_header;
+
+ *header = parsed_header;
+ return (0);
+}
+
+
+/*
+ * Via = ( "Via" / "v" ) HCOLON via-parm *(COMMA via-parm)
+ * via-parm = sent-protocol LWS sent-by *( SEMI via-params )
+ * via-params = via-ttl / via-maddr
+ * / via-received / via-branch
+ * / via-extension
+ * via-ttl = "ttl" EQUAL ttl
+ * via-maddr = "maddr" EQUAL host
+ * via-received = "received" EQUAL (IPv4address / IPv6address)
+ * via-branch = "branch" EQUAL token
+ * via-extension = generic-param
+ * sent-protocol = protocol-name SLASH protocol-version
+ * SLASH transport
+ * protocol-name = "SIP" / token
+ * protocol-version = token
+ * transport = "UDP" / "TCP" / "TLS" / "SCTP"
+ * / other-transport
+ * sent-by = host [ COLON port ]
+ * ttl = 1*3DIGIT ; 0 to 255
+ *
+ * There can be multiple via headers we always append the header.
+ */
+int
+sip_parse_via_header(_sip_header_t *sip_header, sip_parsed_header_t **header)
+{
+ sip_parsed_header_t *parsed_header;
+ int ret;
+ sip_hdr_value_t *value = NULL;
+ sip_hdr_value_t *last_value = NULL;
+
+ if ((ret = sip_prim_parsers(sip_header, header)) != 0)
+ return (ret);
+
+ if (*header != NULL)
+ return (0);
+
+ parsed_header = calloc(1, sizeof (sip_parsed_header_t));
+ if (parsed_header == NULL)
+ return (ENOMEM);
+ parsed_header->sip_parsed_header_version = SIP_PARSED_HEADER_VERSION_1;
+ parsed_header->sip_header = sip_header;
+
+ while (sip_header->sip_hdr_current < sip_header->sip_hdr_end) {
+
+ value = calloc(1, sizeof (sip_hdr_value_t));
+ if (value == NULL) {
+ sip_free_phdr(parsed_header);
+ return (ENOMEM);
+ }
+ if (last_value != NULL)
+ last_value->sip_next_value = value;
+ else
+ parsed_header->value = (sip_value_t *)value;
+
+ value->sip_value_version = SIP_VALUE_VERSION_1;
+ value->sip_value_start = sip_header->sip_hdr_current;
+ value->sip_value_header = parsed_header;
+ value->via_protocol_name.sip_str_ptr =
+ sip_header->sip_hdr_current;
+
+ /*
+ * Check to see if there is a version number
+ */
+ if (sip_get_protocol_version(sip_header,
+ &value->via_protocol) != 0) {
+ if (sip_goto_next_value(sip_header) != 0) {
+ sip_free_phdr(parsed_header);
+ return (EPROTO);
+ }
+ value->sip_value_state = SIP_VALUE_BAD;
+ goto get_next_via_value;
+ }
+
+ if (sip_find_token(sip_header, SIP_SLASH) != 0) {
+ if (sip_goto_next_value(sip_header) != 0) {
+ sip_free_phdr(parsed_header);
+ return (EPROTO);
+ }
+ value->sip_value_state = SIP_VALUE_BAD;
+ goto get_next_via_value;
+ }
+
+ if (sip_skip_white_space(sip_header) != 0) {
+ if (sip_goto_next_value(sip_header) != 0) {
+ sip_free_phdr(parsed_header);
+ return (EPROTO);
+ }
+ value->sip_value_state = SIP_VALUE_BAD;
+ goto get_next_via_value;
+ }
+
+ value->via_protocol_transport.sip_str_ptr =
+ sip_header->sip_hdr_current;
+ if (sip_find_white_space(sip_header) != 0) {
+ if (sip_goto_next_value(sip_header) != 0) {
+ sip_free_phdr(parsed_header);
+ return (EPROTO);
+ }
+ value->sip_value_state = SIP_VALUE_BAD;
+ goto get_next_via_value;
+ }
+
+ value->via_protocol_transport.sip_str_len =
+ sip_header->sip_hdr_current -
+ value->via_protocol_transport.sip_str_ptr;
+
+ if (sip_skip_white_space(sip_header) != 0) {
+ if (sip_goto_next_value(sip_header) != 0) {
+ sip_free_phdr(parsed_header);
+ return (EPROTO);
+ }
+ value->sip_value_state = SIP_VALUE_BAD;
+ goto get_next_via_value;
+ }
+
+ value->via_sent_by_host.sip_str_ptr =
+ sip_header->sip_hdr_current;
+ if (*sip_header->sip_hdr_current == '[') {
+ if (sip_find_token(sip_header, ']')) {
+ if (sip_goto_next_value(sip_header) != 0) {
+ sip_free_phdr(parsed_header);
+ return (EPROTO);
+ }
+ value->sip_value_state = SIP_VALUE_BAD;
+ goto get_next_via_value;
+ }
+ } else if (sip_find_separator(sip_header, SIP_SEMI, SIP_COMMA,
+ SIP_HCOLON)) {
+ if (sip_goto_next_value(sip_header) != 0) {
+ sip_free_phdr(parsed_header);
+ return (EPROTO);
+ }
+ value->sip_value_state = SIP_VALUE_BAD;
+ goto get_next_via_value;
+ }
+ value->via_sent_by_host.sip_str_len =
+ sip_header->sip_hdr_current -
+ value->via_sent_by_host.sip_str_ptr;
+
+ if (sip_skip_white_space(sip_header) != 0) {
+ if (sip_goto_next_value(sip_header) != 0) {
+ sip_free_phdr(parsed_header);
+ return (EPROTO);
+ }
+ value->sip_value_state = SIP_VALUE_BAD;
+ goto get_next_via_value;
+ }
+
+ if (*sip_header->sip_hdr_current == SIP_HCOLON) {
+ sip_header->sip_hdr_current++;
+ /*
+ * We have a port number
+ */
+ if (sip_atoi(sip_header, &value->via_sent_by_port) !=
+ 0) {
+ if (sip_goto_next_value(sip_header) != 0) {
+ sip_free_phdr(parsed_header);
+ return (EPROTO);
+ }
+ value->sip_value_state = SIP_VALUE_BAD;
+ goto get_next_via_value;
+ }
+
+ }
+
+ /*
+ * Do some sanity checking.
+ * This should be replaced by a v4/v6 address check.
+ */
+ if (value->via_sent_by_host.sip_str_len == 0 ||
+ (!isalnum(*value->via_sent_by_host.sip_str_ptr) &&
+ *value->via_sent_by_host.sip_str_ptr != '[')) {
+ if (sip_goto_next_value(sip_header) != 0) {
+ sip_free_phdr(parsed_header);
+ return (EPROTO);
+ }
+ value->sip_value_state = SIP_VALUE_BAD;
+ goto get_next_via_value;
+ }
+
+ ret = sip_parse_params(sip_header, &value->sip_param_list);
+ if (ret == EPROTO) {
+ value->sip_value_state = SIP_VALUE_BAD;
+ } else if (ret != 0) {
+ sip_free_phdr(parsed_header);
+ return (ret);
+ }
+get_next_via_value:
+ value->sip_value_end = sip_header->sip_hdr_current;
+
+ if (sip_find_token(sip_header, SIP_COMMA) != 0)
+ break;
+ last_value = value;
+ (void) sip_skip_white_space(sip_header);
+ }
+
+ sip_header->sip_hdr_parsed = parsed_header;
+
+ *header = parsed_header;
+ return (0);
+}
+
+/*
+ * Max-Forwards = "Max-Forwards" HCOLON 1*DIGIT
+ */
+int
+sip_parse_maxf_header(_sip_header_t *sip_header, sip_parsed_header_t **header)
+{
+ return (sip_parse_hdr_parser2(sip_header, header, SIP_INT_VAL));
+}
+
+/*
+ * Content-Type = ( "Content-Type" / "c" ) HCOLON media-type
+ * media-type = m-type SLASH m-subtype *(SEMI m-parameter)
+ * m-type = discrete-type / composite-type
+ * discrete-type = "text" / "image" / "audio" / "video"
+ * / "application" / extension-token
+ * composite-type = "message" / "multipart" / extension-token
+ * extension-token = ietf-token / x-token
+ * ietf-token = token
+ * x-token = "x-" token
+ * m-subtype = extension-token / iana-token
+ * iana-token = token
+ * m-parameter = m-attribute EQUAL m-value
+ * m-attribute = token
+ * m-value = token / quoted-string
+ */
+int
+sip_parse_ctype_header(_sip_header_t *sip_header, sip_parsed_header_t **header)
+{
+ return (sip_parse_hdr_parser1(sip_header, header, SIP_SLASH));
+}
+
+/*
+ * Content-Length = ( "Content-Length" / "l" ) HCOLON 1*DIGIT
+ */
+int
+sip_parse_clen_header(_sip_header_t *sip_header, sip_parsed_header_t **header)
+{
+ return (sip_parse_hdr_parser2(sip_header, header, SIP_INT_VAL));
+}
+
+/*
+ * Generic parser for Contact, From, To, Route and Record-Route headers
+ *
+ * Contact = ("Contact" / "m" ) HCOLON
+ * ( STAR / (contact-param *(COMMA contact-param)))
+ * contact-param = (name-addr / addr-spec) *(SEMI contact-params)
+ * name-addr = [ display-name ] LAQUOT addr-spec RAQUOT
+ * addr-spec = SIP-URI / SIPS-URI / absoluteURI
+ * display-name = *(token LWS)/ quoted-string
+ * contact-params = c-p-q / c-p-expires
+ * / contact-extension
+ *
+ * From = ( "From" / "f" ) HCOLON from-spec
+ * from-spec = ( name-addr / addr-spec )
+ * *( SEMI from-param )
+ * from-param = tag-param / generic-param
+ * tag-param = "tag" EQUAL token
+ *
+ * To = ( "To" / "t" ) HCOLON ( name-addr
+ * / addr-spec ) *( SEMI to-param )
+ * to-param = tag-param / generic-param
+ *
+ * Route = "Route" HCOLON route-param *(COMMA route-param)
+ * route-param = name-addr *( SEMI rr-param )
+ *
+ * Record-Route = "Record-Route" HCOLON rec-route *(COMMA rec-route)
+ * rec-route = name-addr *( SEMI rr-param )
+ * rr-param = generic-param
+ *
+ * We could have multiple values for these headers. For the ones that have
+ * a display name we will have a LAQUOT/RAQUOT. If we encounter an error
+ * when parsing a value, we mark the value as bad and start paring the
+ * next value, if present. Before we start parsing the next value, we
+ * check for any parameters, if present.
+ */
+int
+sip_parse_cftr_header(_sip_header_t *sip_header, sip_parsed_header_t **header)
+{
+ sip_parsed_header_t *parsed_header;
+ char *tmp_ptr;
+ char *tmp_ptr_2;
+ int ret;
+ sip_hdr_value_t *value = NULL;
+ sip_hdr_value_t *last_value = NULL;
+
+ if ((ret = sip_prim_parsers(sip_header, header)) != 0)
+ return (ret);
+
+ if (*header != NULL)
+ return (0);
+
+ parsed_header = calloc(1, sizeof (sip_parsed_header_t));
+ if (parsed_header == NULL)
+ return (ENOMEM);
+ parsed_header->sip_parsed_header_version = SIP_PARSED_HEADER_VERSION_1;
+ parsed_header->sip_header = sip_header;
+ while (sip_header->sip_hdr_current < sip_header->sip_hdr_end) {
+ boolean_t quoted_name = B_FALSE;
+
+ value = calloc(1, sizeof (sip_hdr_value_t));
+ if (value == NULL) {
+ sip_free_cftr_header(parsed_header);
+ return (ENOMEM);
+ }
+ if (last_value != NULL)
+ last_value->sip_next_value = value;
+ else
+ parsed_header->value = (sip_value_t *)value;
+ if (*sip_header->sip_hdr_current == SIP_QUOTE) {
+ sip_header->sip_hdr_current++;
+ quoted_name = B_TRUE;
+ }
+ value->sip_value_version = SIP_VALUE_VERSION_1;
+ value->sip_value_start = sip_header->sip_hdr_current;
+ value->sip_value_header = parsed_header;
+ /*
+ * let's see if there is a display name
+ */
+ if (*sip_header->sip_hdr_current != SIP_LAQUOT) {
+
+ tmp_ptr = sip_header->sip_hdr_current;
+ /*
+ * According to 20.10 '<' may not have a leading
+ * space.
+ */
+ if (quoted_name &&
+ sip_find_token(sip_header, SIP_QUOTE) != 0) {
+ if (sip_goto_next_value(sip_header) != 0) {
+ sip_free_cftr_header(parsed_header);
+ return (EPROTO);
+ }
+ value->sip_value_state = SIP_VALUE_BAD;
+ goto get_next_cftr_value;
+ } else if (sip_find_separator(sip_header, SIP_SEMI,
+ SIP_LAQUOT, SIP_COMMA) != 0) {
+ /*
+ * only a uri.
+ */
+ value->cftr_uri.sip_str_ptr = tmp_ptr;
+ value->cftr_uri.sip_str_len =
+ sip_header->sip_hdr_current - tmp_ptr;
+ /*
+ * It's an error not to have a uri.
+ */
+ if (value->cftr_uri.sip_str_len == 0) {
+ if (sip_goto_next_value(sip_header) !=
+ 0) {
+ sip_free_cftr_header(
+ parsed_header);
+ return (EPROTO);
+ }
+ value->sip_value_state = SIP_VALUE_BAD;
+ goto get_next_cftr_value;
+ }
+ continue;
+ }
+
+ tmp_ptr_2 = sip_header->sip_hdr_current;
+ if (*sip_header->sip_hdr_current == SIP_SP) {
+ if (sip_skip_white_space(sip_header) != 0) {
+ /*
+ * only a uri.
+ */
+ value->cftr_uri.sip_str_ptr = tmp_ptr;
+ value->cftr_uri.sip_str_len =
+ tmp_ptr_2 - tmp_ptr;
+ /*
+ * It's an error not to have a uri.
+ */
+ if (value->cftr_uri.sip_str_len == 0) {
+ if (sip_goto_next_value(
+ sip_header) != 0) {
+ sip_free_cftr_header(
+ parsed_header);
+ return (EPROTO);
+ }
+ value->sip_value_state =
+ SIP_VALUE_BAD;
+ goto get_next_cftr_value;
+ }
+ continue;
+ }
+ }
+
+ if (*sip_header->sip_hdr_current != SIP_LAQUOT) {
+ /*
+ * No display name here.
+ */
+ value->cftr_uri.sip_str_ptr = tmp_ptr;
+ value->cftr_uri.sip_str_len = tmp_ptr_2 -
+ tmp_ptr;
+ /*
+ * It's an error not to have a uri.
+ */
+ if (value->cftr_uri.sip_str_len == 0) {
+ if (sip_goto_next_value(sip_header) !=
+ 0) {
+ sip_free_cftr_header(
+ parsed_header);
+ return (EPROTO);
+ }
+ value->sip_value_state = SIP_VALUE_BAD;
+ goto get_next_cftr_value;
+ }
+ goto get_params;
+ }
+
+ value->cftr_name = malloc(sizeof (sip_str_t));
+ if (value->cftr_name == NULL) {
+ sip_free_cftr_header(parsed_header);
+ return (ENOMEM);
+ }
+ value->cftr_name->sip_str_ptr = tmp_ptr;
+ value->cftr_name->sip_str_len = tmp_ptr_2 - tmp_ptr;
+ if (quoted_name)
+ value->cftr_name->sip_str_len--;
+ }
+
+ if (sip_find_token(sip_header, SIP_LAQUOT) != 0) {
+ if (sip_goto_next_value(sip_header) != 0) {
+ sip_free_cftr_header(parsed_header);
+ return (EPROTO);
+ }
+ value->sip_value_state = SIP_VALUE_BAD;
+ goto get_next_cftr_value;
+ }
+
+ if (*sip_header->sip_hdr_current == SIP_SP) {
+ if (sip_skip_white_space(sip_header) != 0) {
+ if (sip_goto_next_value(sip_header) != 0) {
+ sip_free_cftr_header(parsed_header);
+ return (EPROTO);
+ }
+ value->sip_value_state = SIP_VALUE_BAD;
+ goto get_next_cftr_value;
+ }
+ }
+
+ tmp_ptr = sip_header->sip_hdr_current;
+
+ if (sip_find_separator(sip_header, SIP_RAQUOT, (char)NULL,
+ (char)NULL)) {
+ if (sip_goto_next_value(sip_header) != 0) {
+ sip_free_cftr_header(parsed_header);
+ return (EPROTO);
+ }
+ value->sip_value_state = SIP_VALUE_BAD;
+ goto get_next_cftr_value;
+ }
+
+ value->cftr_uri.sip_str_ptr = tmp_ptr;
+ value->cftr_uri.sip_str_len =
+ sip_header->sip_hdr_current - tmp_ptr;
+
+ if (sip_find_token(sip_header, SIP_RAQUOT) != 0) {
+ if (sip_goto_next_value(sip_header) != 0) {
+ sip_free_cftr_header(parsed_header);
+ return (EINVAL);
+ }
+ value->sip_value_state = SIP_VALUE_BAD;
+ goto get_next_cftr_value;
+ }
+
+ if (value->cftr_uri.sip_str_len <= strlen("<>")) {
+ if (sip_goto_next_value(sip_header) != 0) {
+ sip_free_cftr_header(parsed_header);
+ return (EPROTO);
+ }
+ value->sip_value_state = SIP_VALUE_BAD;
+ goto get_next_cftr_value;
+ }
+
+get_params:
+ ret = sip_parse_params(sip_header, &value->sip_param_list);
+ if (ret == EPROTO) {
+ value->sip_value_state = SIP_VALUE_BAD;
+ } else if (ret != 0) {
+ sip_free_cftr_header(parsed_header);
+ return (ret);
+ }
+get_next_cftr_value:
+ value->sip_value_end = sip_header->sip_hdr_current;
+
+ /*
+ * Parse uri
+ */
+ if (value->cftr_uri.sip_str_len > 0) {
+ int error;
+
+ value->sip_value_parsed_uri = sip_parse_uri(
+ &value->cftr_uri, &error);
+ if (value->sip_value_parsed_uri == NULL) {
+ sip_free_cftr_header(parsed_header);
+ return (ENOMEM);
+ }
+ if (error != 0 ||
+ ((_sip_uri_t *)value->sip_value_parsed_uri)->
+ sip_uri_errflags != 0) {
+ value->sip_value_state = SIP_VALUE_BAD;
+ }
+ }
+
+ (void) sip_find_token(sip_header, SIP_COMMA);
+ last_value = value;
+ (void) sip_skip_white_space(sip_header);
+ }
+
+ sip_header->sip_hdr_parsed = parsed_header;
+
+ *header = parsed_header;
+ return (0);
+}
+
+/*
+ * PAssertedID = "P-Asserted-Identity" HCOLON PAssertedID-value
+ * *(COMMA PAssertedID-value)
+ * PAssertedID-value = name-addr / addr-spec
+ */
+int
+sip_parse_passertedid(_sip_header_t *sip_header, sip_parsed_header_t **header)
+{
+ return (sip_parse_hdr_parser3(sip_header, header, SIP_STRS_VAL,
+ B_TRUE));
+}
+
+/*
+ * PPreferredID = "P-Preferred-Identity" HCOLON PPreferredID-value
+ * *(COMMA PAssertedID-value)
+ * PPreferredID-value = name-addr / addr-spec
+ */
+int
+sip_parse_ppreferredid(_sip_header_t *sip_header, sip_parsed_header_t **header)
+{
+ return (sip_parse_hdr_parser3(sip_header, header, SIP_STRS_VAL,
+ B_TRUE));
+}
+
+
+/*
+ * We don't do anything for a header we don't understand
+ */
+/* ARGSUSED */
+int
+sip_parse_unknown_header(_sip_header_t *sip_header,
+ sip_parsed_header_t **header)
+{
+ return (EINVAL);
+}
diff --git a/usr/src/lib/libsip/common/sip_parse_uri.c b/usr/src/lib/libsip/common/sip_parse_uri.c
new file mode 100644
index 0000000000..8f44309860
--- /dev/null
+++ b/usr/src/lib/libsip/common/sip_parse_uri.c
@@ -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);
+ }
+}
diff --git a/usr/src/lib/libsip/common/sip_parse_uri.h b/usr/src/lib/libsip/common/sip_parse_uri.h
new file mode 100644
index 0000000000..f873420093
--- /dev/null
+++ b/usr/src/lib/libsip/common/sip_parse_uri.h
@@ -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 */
diff --git a/usr/src/lib/libsip/common/sip_reass.c b/usr/src/lib/libsip/common/sip_reass.c
new file mode 100644
index 0000000000..0957051307
--- /dev/null
+++ b/usr/src/lib/libsip/common/sip_reass.c
@@ -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);
+}
diff --git a/usr/src/lib/libsip/common/sip_timeout.c b/usr/src/lib/libsip/common/sip_timeout.c
new file mode 100644
index 0000000000..b60cb0b1de
--- /dev/null
+++ b/usr/src/lib/libsip/common/sip_timeout.c
@@ -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);
+}
diff --git a/usr/src/lib/libsip/common/sip_ui.c b/usr/src/lib/libsip/common/sip_ui.c
new file mode 100644
index 0000000000..20aadcfad7
--- /dev/null
+++ b/usr/src/lib/libsip/common/sip_ui.c
@@ -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);
+}
diff --git a/usr/src/lib/libsip/common/sip_uri_ui.c b/usr/src/lib/libsip/common/sip_uri_ui.c
new file mode 100644
index 0000000000..611091c7bd
--- /dev/null
+++ b/usr/src/lib/libsip/common/sip_uri_ui.c
@@ -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);
+}
diff --git a/usr/src/lib/libsip/common/sip_xaction.c b/usr/src/lib/libsip/common/sip_xaction.c
new file mode 100644
index 0000000000..2bc0db2c03
--- /dev/null
+++ b/usr/src/lib/libsip/common/sip_xaction.c
@@ -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;
+}
diff --git a/usr/src/lib/libsip/common/sip_xaction.h b/usr/src/lib/libsip/common/sip_xaction.h
new file mode 100644
index 0000000000..441599af49
--- /dev/null
+++ b/usr/src/lib/libsip/common/sip_xaction.h
@@ -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 */
diff --git a/usr/src/lib/libsip/common/sip_xaction_state_mc.c b/usr/src/lib/libsip/common/sip_xaction_state_mc.c
new file mode 100644
index 0000000000..c8a0087ddf
--- /dev/null
+++ b/usr/src/lib/libsip/common/sip_xaction_state_mc.c
@@ -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);
+}
diff --git a/usr/src/lib/libsip/common/sip_xaction_ui.c b/usr/src/lib/libsip/common/sip_xaction_ui.c
new file mode 100644
index 0000000000..865d13a63d
--- /dev/null
+++ b/usr/src/lib/libsip/common/sip_xaction_ui.c
@@ -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);
+}
diff --git a/usr/src/lib/libsip/i386/Makefile b/usr/src/lib/libsip/i386/Makefile
new file mode 100644
index 0000000000..8e913a86ca
--- /dev/null
+++ b/usr/src/lib/libsip/i386/Makefile
@@ -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)
diff --git a/usr/src/lib/libsip/sparc/Makefile b/usr/src/lib/libsip/sparc/Makefile
new file mode 100644
index 0000000000..8e913a86ca
--- /dev/null
+++ b/usr/src/lib/libsip/sparc/Makefile
@@ -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)
diff --git a/usr/src/lib/libsip/sparcv9/Makefile b/usr/src/lib/libsip/sparcv9/Makefile
new file mode 100644
index 0000000000..41991fbf4c
--- /dev/null
+++ b/usr/src/lib/libsip/sparcv9/Makefile
@@ -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)