/* * 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 #else #include #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); }