diff options
Diffstat (limited to 'usr/src/lib/libsip/common/sip_ui.c')
-rw-r--r-- | usr/src/lib/libsip/common/sip_ui.c | 1342 |
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); +} |