summaryrefslogtreecommitdiff
path: root/usr/src/lib/libsip/common/sip_ui.c
diff options
context:
space:
mode:
Diffstat (limited to 'usr/src/lib/libsip/common/sip_ui.c')
-rw-r--r--usr/src/lib/libsip/common/sip_ui.c1342
1 files changed, 1342 insertions, 0 deletions
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);
+}