diff options
author | vi117747 <none@none> | 2006-10-07 14:26:26 -0700 |
---|---|---|
committer | vi117747 <none@none> | 2006-10-07 14:26:26 -0700 |
commit | 40cb5e5daa7b80bb70fcf8dadfb20f9281566331 (patch) | |
tree | dc95663e296c5dbf3cb8faa561e53416978eb4dc /usr/src/lib/libsip | |
parent | 56a424cca6b3f91f31bdab72a4626c48c779fe8b (diff) | |
download | illumos-joyent-40cb5e5daa7b80bb70fcf8dadfb20f9281566331.tar.gz |
PSARC 2006/402 SIP Library Integration
6461142 Integrate SIP in Solaris
Diffstat (limited to 'usr/src/lib/libsip')
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 (¶m->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) |