diff options
Diffstat (limited to 'usr/src/lib/libsip/common/sip_itf.c')
-rw-r--r-- | usr/src/lib/libsip/common/sip_itf.c | 642 |
1 files changed, 642 insertions, 0 deletions
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); +} |