text: /*
   * 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 2007 Sun Microsystems, Inc.  All rights reserved.
   * Use is subject to license terms.
   */
  
  #pragma ident	"%Z%%M%	%I%	%E% SMI"
  
  #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()
  {
  	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);
  }
  