/*
* 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 (c) 2006, 2011, Oracle and/or its affiliates. All rights reserved.
*/
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <ctype.h>
#include <errno.h>
#include <strings.h>
#include <pthread.h>
#include <sip.h>
#include "sip_msg.h"
#include "sip_miscdefs.h"
#include "sip_parse_uri.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(void)
{
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);
}