summaryrefslogtreecommitdiff
path: root/usr/src/lib/libsip/common/sip_msg.c
diff options
context:
space:
mode:
Diffstat (limited to 'usr/src/lib/libsip/common/sip_msg.c')
-rw-r--r--usr/src/lib/libsip/common/sip_msg.c931
1 files changed, 931 insertions, 0 deletions
diff --git a/usr/src/lib/libsip/common/sip_msg.c b/usr/src/lib/libsip/common/sip_msg.c
new file mode 100644
index 0000000000..ed1169f66c
--- /dev/null
+++ b/usr/src/lib/libsip/common/sip_msg.c
@@ -0,0 +1,931 @@
+/*
+ * 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_parse_generic.h"
+
+/*
+ * Response consists of SIP version, response code, response phrase and CRLF.
+ */
+#define SIP_RESPONSE "%s %d %s%s"
+
+void sip_free_content(_sip_msg_t *);
+
+/*
+ * Allocate a new sip msg struct.
+ */
+sip_msg_t
+sip_new_msg()
+{
+ _sip_msg_t *sip_msg;
+
+ sip_msg = calloc(1, sizeof (_sip_msg_t));
+ if (sip_msg != NULL) {
+ sip_msg->sip_msg_ref_cnt = 1;
+ (void) pthread_mutex_init(&sip_msg->sip_msg_mutex, NULL);
+ }
+ return ((sip_msg_t)sip_msg);
+}
+
+/*
+ * Free all resources. The lock is taken by SIP_MSG_REFCNT_DECR. The
+ * thread that decrements the last refcount should take care that
+ * the message is not accessible to other threads before doing so.
+ * Else, if the message is still accessible to others, it is
+ * possible that the other thread could be waiting to take the
+ * lock when we proceed to destroy it.
+ */
+void
+sip_destroy_msg(_sip_msg_t *_sip_msg)
+{
+#ifdef __solaris__
+ assert(mutex_held(&_sip_msg->sip_msg_mutex));
+#endif
+ (void) sip_delete_start_line_locked(_sip_msg);
+ assert(_sip_msg->sip_msg_ref_cnt == 0);
+ sip_delete_all_headers((sip_msg_t)_sip_msg);
+ sip_free_content(_sip_msg);
+ if (_sip_msg->sip_msg_buf != NULL)
+ free(_sip_msg->sip_msg_buf);
+
+ if (_sip_msg->sip_msg_old_buf != NULL)
+ free(_sip_msg->sip_msg_old_buf);
+
+ while (_sip_msg->sip_msg_req_res != NULL) {
+ sip_message_type_t *sip_msg_type_ptr;
+
+ sip_msg_type_ptr = _sip_msg->sip_msg_req_res->sip_next;
+ if (_sip_msg->sip_msg_req_res->is_request) {
+ sip_request_t *reqline;
+
+ reqline = &_sip_msg->sip_msg_req_res->U.sip_request;
+ if (reqline->sip_parse_uri != NULL) {
+ sip_free_parsed_uri(reqline->sip_parse_uri);
+ reqline->sip_parse_uri = NULL;
+ }
+ }
+ free(_sip_msg->sip_msg_req_res);
+ _sip_msg->sip_msg_req_res = sip_msg_type_ptr;
+ }
+ (void) pthread_mutex_destroy(&_sip_msg->sip_msg_mutex);
+ free(_sip_msg);
+}
+
+/*
+ * Free a sip msg struct.
+ */
+void
+sip_free_msg(sip_msg_t sip_msg)
+{
+ if (sip_msg == NULL)
+ return;
+
+ SIP_MSG_REFCNT_DECR((_sip_msg_t *)sip_msg);
+}
+
+/*
+ * Hold a sip msg struct.
+ */
+void
+sip_hold_msg(sip_msg_t sip_msg)
+{
+
+ if (sip_msg == NULL)
+ return;
+
+ SIP_MSG_REFCNT_INCR((_sip_msg_t *)sip_msg);
+}
+
+/*
+ * Clone a message
+ */
+sip_msg_t
+sip_clone_msg(sip_msg_t sip_msg)
+{
+ _sip_msg_t *new_msg;
+ _sip_msg_t *_sip_msg;
+ sip_content_t *sip_content;
+ sip_content_t *msg_content;
+ sip_content_t *new_content = NULL;
+ int len;
+
+ if (sip_msg == NULL)
+ return (NULL);
+ new_msg = (_sip_msg_t *)sip_new_msg();
+ if (new_msg == NULL)
+ return (NULL);
+ _sip_msg = (_sip_msg_t *)sip_msg;
+ /*
+ * Get start line
+ */
+ if (sip_copy_start_line(_sip_msg, new_msg) != 0) {
+ sip_free_msg((sip_msg_t)new_msg);
+ return (NULL);
+ }
+ if (sip_copy_all_headers(_sip_msg, new_msg) != 0) {
+ sip_free_msg((sip_msg_t)new_msg);
+ return (NULL);
+ }
+ (void) pthread_mutex_lock(&_sip_msg->sip_msg_mutex);
+ sip_content = _sip_msg->sip_msg_content;
+ while (sip_content != NULL) {
+ msg_content = calloc(1, sizeof (sip_content_t));
+ if (msg_content == NULL) {
+ sip_free_msg((sip_msg_t)new_msg);
+ (void) pthread_mutex_unlock(&_sip_msg->sip_msg_mutex);
+ return (NULL);
+ }
+ len = sip_content->sip_content_end -
+ sip_content->sip_content_start;
+ msg_content->sip_content_start = malloc(len + 1);
+ if (msg_content->sip_content_start == NULL) {
+ sip_free_msg((sip_msg_t)new_msg);
+ (void) pthread_mutex_unlock(&_sip_msg->sip_msg_mutex);
+ return (NULL);
+ }
+ (void) strncpy(msg_content->sip_content_start,
+ sip_content->sip_content_start, len);
+ msg_content->sip_content_start[len] = '\0';
+ msg_content->sip_content_current =
+ msg_content->sip_content_start;
+ msg_content->sip_content_end = msg_content->sip_content_start +
+ len;
+ msg_content->sip_content_allocated = B_TRUE;
+ new_msg->sip_msg_content_len += len;
+ new_msg->sip_msg_len += len;
+ if (new_msg->sip_msg_content == NULL)
+ new_msg->sip_msg_content = msg_content;
+ else
+ new_content->sip_content_next = msg_content;
+ new_content = msg_content;
+ sip_content = sip_content->sip_content_next;
+ }
+ (void) pthread_mutex_unlock(&_sip_msg->sip_msg_mutex);
+ /*
+ * Since this is a new message, no threads should be referring
+ * to this, so it is not necessary to take the lock, however,
+ * since sip_msg_to_msgbuf() expects the lock to be held, we'll
+ * take it here.
+ */
+ (void) pthread_mutex_lock(&new_msg->sip_msg_mutex);
+ new_msg->sip_msg_buf = sip_msg_to_msgbuf((sip_msg_t)new_msg, NULL);
+ if (new_msg->sip_msg_buf == NULL) {
+ (void) pthread_mutex_unlock(&new_msg->sip_msg_mutex);
+ sip_free_msg((sip_msg_t)new_msg);
+ return (NULL);
+ }
+ new_msg->sip_msg_cannot_be_modified = B_TRUE;
+ (void) pthread_mutex_unlock(&new_msg->sip_msg_mutex);
+
+ return ((sip_msg_t)new_msg);
+}
+
+/*
+ * Return the SIP message as a string. Caller frees the string
+ */
+char *
+sip_msg_to_str(sip_msg_t sip_msg, int *error)
+{
+ _sip_msg_t *msg;
+ char *msgstr;
+
+ if (sip_msg == NULL) {
+ if (error != NULL)
+ *error = EINVAL;
+ return (NULL);
+ }
+ msg = (_sip_msg_t *)sip_msg;
+ (void) pthread_mutex_lock(&msg->sip_msg_mutex);
+ msgstr = sip_msg_to_msgbuf(msg, error);
+ (void) pthread_mutex_unlock(&msg->sip_msg_mutex);
+ return (msgstr);
+}
+
+/*
+ * Given a message generate a string that includes all the headers and the
+ * content.
+ */
+char *
+sip_msg_to_msgbuf(_sip_msg_t *msg, int *error)
+{
+ _sip_header_t *header;
+ int len = 0;
+ char *p;
+ char *e;
+ sip_content_t *sip_content;
+#ifdef _DEBUG
+ int tlen = 0;
+ int clen = 0;
+#endif
+
+ if (error != NULL)
+ *error = 0;
+
+ if (msg == NULL) {
+ if (error != NULL)
+ *error = EINVAL;
+ return (NULL);
+ }
+#ifdef __solaris__
+ assert(mutex_held(&msg->sip_msg_mutex));
+#endif
+
+ p = (char *)malloc(msg->sip_msg_len + 1);
+ if (p == NULL) {
+ if (error != 0)
+ *error = ENOMEM;
+ return (NULL);
+ }
+ e = p;
+
+ /*
+ * Get the start line
+ */
+ if (msg->sip_msg_start_line != NULL) {
+ len = msg->sip_msg_start_line->sip_hdr_end -
+ msg->sip_msg_start_line->sip_hdr_start;
+ (void) strncpy(e, msg->sip_msg_start_line->sip_hdr_start, len);
+ e += len;
+#ifdef _DEBUG
+ tlen += len;
+#endif
+ }
+ header = sip_search_for_header(msg, NULL, NULL);
+ while (header != NULL) {
+ if (header->sip_header_state != SIP_HEADER_DELETED) {
+ if (header->sip_header_state ==
+ SIP_HEADER_DELETED_VAL) {
+ len = sip_copy_values(e, header);
+ } else {
+ len = header->sip_hdr_end -
+ header->sip_hdr_start;
+ (void) strncpy(e, header->sip_hdr_start, len);
+ }
+#ifdef _DEBUG
+ tlen += len;
+ assert(tlen <= msg->sip_msg_len);
+#endif
+ }
+ header = sip_search_for_header(msg, NULL, header);
+ e += len;
+ }
+ sip_content = msg->sip_msg_content;
+ while (sip_content != NULL) {
+ len = sip_content->sip_content_end -
+ sip_content->sip_content_start;
+#ifdef _DEBUG
+ clen += len;
+ assert(clen <= msg->sip_msg_content_len);
+ tlen += len;
+ assert(tlen <= msg->sip_msg_len);
+#endif
+ (void) strncpy(e, sip_content->sip_content_start, len);
+ e += len;
+ sip_content = sip_content->sip_content_next;
+ }
+ p[msg->sip_msg_len] = '\0';
+ return (p);
+}
+
+/*
+ * This is called just before sending the message to the transport. It
+ * creates the sip_msg_buf from the SIP headers.
+ */
+int
+sip_adjust_msgbuf(_sip_msg_t *msg)
+{
+ _sip_header_t *header;
+ int ret;
+#ifdef _DEBUG
+ int tlen = 0;
+ int clen = 0;
+#endif
+
+ if (msg == NULL)
+ return (EINVAL);
+
+ (void) pthread_mutex_lock(&msg->sip_msg_mutex);
+ if ((msg->sip_msg_buf != NULL) && (!msg->sip_msg_modified)) {
+ /*
+ * We could just be forwarding the message we
+ * received.
+ */
+ (void) pthread_mutex_unlock(&msg->sip_msg_mutex);
+ return (0);
+ }
+
+ /*
+ * We are sending a new message or a message that we received
+ * but have modified it. We keep the old
+ * msgbuf till the message is freed as some
+ * headers still point to it.
+ */
+
+ assert(msg->sip_msg_old_buf == NULL);
+ msg->sip_msg_old_buf = msg->sip_msg_buf;
+ /*
+ * We add the content-length header here, if it has not
+ * already been added.
+ */
+ header = sip_search_for_header(msg, SIP_CONTENT_LENGTH, NULL);
+ if (header != NULL) {
+ /*
+ * Mark the previous header as deleted.
+ */
+ header->sip_header_state = SIP_HEADER_DELETED;
+ header->sip_hdr_sipmsg->sip_msg_len -= header->sip_hdr_end -
+ header->sip_hdr_start;
+ }
+ (void) pthread_mutex_unlock(&msg->sip_msg_mutex);
+ ret = sip_add_content_length(msg, msg->sip_msg_content_len);
+ if (ret != 0) {
+ (void) pthread_mutex_unlock(&msg->sip_msg_mutex);
+ return (ret);
+ }
+ (void) pthread_mutex_lock(&msg->sip_msg_mutex);
+ msg->sip_msg_modified = B_FALSE;
+
+ msg->sip_msg_buf = sip_msg_to_msgbuf((sip_msg_t)msg, &ret);
+ if (msg->sip_msg_buf == NULL) {
+ (void) pthread_mutex_unlock(&msg->sip_msg_mutex);
+ return (ret);
+ }
+ /*
+ * Once the message has been sent it can not be modified
+ * any furthur as we keep a pointer to it for retransmission
+ */
+ msg->sip_msg_cannot_be_modified = B_TRUE;
+
+ (void) pthread_mutex_unlock(&msg->sip_msg_mutex);
+ return (0);
+}
+
+/*
+ * Copy header values into ptr
+ */
+int
+sip_copy_values(char *ptr, _sip_header_t *header)
+{
+ sip_header_value_t value;
+ int tlen = 0;
+ int len = 0;
+ boolean_t first = B_TRUE;
+ char *p = ptr;
+ char *s;
+ boolean_t crlf_present = B_FALSE;
+
+ if (sip_parse_goto_values(header) != 0)
+ return (0);
+
+ len = header->sip_hdr_current - header->sip_hdr_start;
+ (void) strncpy(p, header->sip_hdr_start, len);
+ tlen += len;
+ p += len;
+ value = header->sip_hdr_parsed->value;
+ while (value != NULL) {
+ if (value->value_state != SIP_VALUE_DELETED) {
+ crlf_present = B_FALSE;
+ len = value->value_end - value->value_start;
+ if (first) {
+ (void) strncpy(p, value->value_start, len);
+ first = B_FALSE;
+ } else {
+ s = value->value_start;
+ while (*s != SIP_COMMA)
+ s--;
+ len += value->value_start - s;
+ (void) strncpy(p, s, len);
+ }
+ tlen += len;
+ p += len;
+ 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--;
+ }
+ } else {
+ if (value->next == NULL && !first && !crlf_present) {
+ s = value->value_end;
+ while (*s != '\r')
+ s--;
+ len = value->value_end - s;
+ (void) strncpy(p, s, len);
+ tlen += len;
+ p += len;
+ }
+ }
+ value = value->next;
+ }
+ return (tlen);
+}
+
+
+/*
+ * Add content (message body) to sip_msg
+ */
+int
+sip_add_content(sip_msg_t sip_msg, char *content)
+{
+ size_t len;
+ sip_content_t **loc;
+ sip_content_t *msg_content;
+ _sip_msg_t *_sip_msg;
+
+ if (sip_msg == NULL || content == NULL || strlen(content) == 0)
+ return (EINVAL);
+ len = strlen(content);
+ _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 (ENOTSUP);
+ }
+
+ msg_content = calloc(1, sizeof (sip_content_t));
+ if (msg_content == NULL) {
+ (void) pthread_mutex_unlock(&_sip_msg->sip_msg_mutex);
+ return (ENOMEM);
+ }
+ msg_content->sip_content_start = malloc(strlen(content) + 1);
+ if (msg_content->sip_content_start == NULL) {
+ (void) pthread_mutex_unlock(&_sip_msg->sip_msg_mutex);
+ free(msg_content);
+ return (ENOMEM);
+ }
+ (void) strncpy(msg_content->sip_content_start, content,
+ strlen(content));
+ msg_content->sip_content_start[strlen(content)] = '\0';
+ msg_content->sip_content_current = msg_content->sip_content_start;
+ msg_content->sip_content_end = msg_content->sip_content_start +
+ strlen(msg_content->sip_content_start);
+ msg_content->sip_content_allocated = B_TRUE;
+
+ loc = &_sip_msg->sip_msg_content;
+ while (*loc != NULL)
+ loc = &((*loc)->sip_content_next);
+ *loc = msg_content;
+
+ _sip_msg->sip_msg_content_len += len;
+ _sip_msg->sip_msg_len += len;
+ 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);
+}
+
+/*
+ * Free the message content
+ */
+void
+sip_free_content(_sip_msg_t *sip_msg)
+{
+ sip_content_t *content;
+
+ if (sip_msg == NULL)
+ return;
+ content = sip_msg->sip_msg_content;
+ while (content != NULL) {
+ sip_content_t *content_tmp;
+
+ content_tmp = content;
+ content = content->sip_content_next;
+ if (content_tmp->sip_content_allocated)
+ free(content_tmp->sip_content_start);
+ free(content_tmp);
+ }
+ sip_msg->sip_msg_content = NULL;
+}
+
+
+/*
+ * Add a response line to sip_response
+ */
+int
+sip_add_response_line(sip_msg_t sip_response, int response, char *response_code)
+{
+ _sip_header_t *new_header;
+ int header_size;
+ _sip_msg_t *_sip_response;
+ int ret;
+
+ if (sip_response == NULL || response < 0 || response_code == NULL)
+ return (EINVAL);
+ _sip_response = (_sip_msg_t *)sip_response;
+ (void) pthread_mutex_lock(&_sip_response->sip_msg_mutex);
+ if (_sip_response->sip_msg_cannot_be_modified) {
+ (void) pthread_mutex_unlock(&_sip_response->sip_msg_mutex);
+ return (ENOTSUP);
+ }
+ header_size = strlen(SIP_VERSION) + SIP_SPACE_LEN +
+ SIP_SIZE_OF_STATUS_CODE + SIP_SPACE_LEN + strlen(response_code) +
+ strlen(SIP_CRLF);
+
+ new_header = sip_new_header(header_size);
+ if (new_header == NULL) {
+ (void) pthread_mutex_unlock(&_sip_response->sip_msg_mutex);
+ return (ENOMEM);
+ }
+ new_header->sip_hdr_sipmsg = _sip_response;
+
+ (void) snprintf(new_header->sip_hdr_start, header_size + 1,
+ SIP_RESPONSE, SIP_VERSION, response, response_code, SIP_CRLF);
+
+ new_header->sip_hdr_next = _sip_response->sip_msg_start_line;
+ _sip_response->sip_msg_start_line = new_header;
+ _sip_response->sip_msg_len += header_size;
+ ret = sip_parse_first_line(_sip_response->sip_msg_start_line,
+ &_sip_response->sip_msg_req_res);
+ if (_sip_response->sip_msg_buf != NULL)
+ _sip_response->sip_msg_modified = B_TRUE;
+ (void) pthread_mutex_unlock(&_sip_response->sip_msg_mutex);
+ return (ret);
+}
+
+/*
+ * create a response based on the sip_request.
+ * Copies Call-ID, CSeq, From, To and Via headers from the request.
+ */
+sip_msg_t
+sip_create_response(sip_msg_t sip_request, int response, char *response_code,
+ char *totag, char *mycontact)
+{
+ _sip_msg_t *new_msg;
+ _sip_msg_t *_sip_request;
+ boolean_t ttag_present;
+
+ if (sip_request == NULL || response_code == NULL)
+ return (NULL);
+
+ ttag_present = sip_get_to_tag(sip_request, NULL) != NULL;
+
+ new_msg = (_sip_msg_t *)sip_new_msg();
+ if (new_msg == NULL)
+ return (NULL);
+ _sip_request = (_sip_msg_t *)sip_request;
+
+ (void) pthread_mutex_lock(&_sip_request->sip_msg_mutex);
+
+ /*
+ * Add response line.
+ */
+ if (sip_add_response_line(new_msg, response, response_code) != 0)
+ goto error;
+
+ /*
+ * Copy Via headers
+ */
+ if (_sip_find_and_copy_all_header(_sip_request, new_msg, SIP_VIA) != 0)
+ goto error;
+
+ /*
+ * Copy From header.
+ */
+ if (_sip_find_and_copy_header(_sip_request, new_msg, SIP_FROM,
+ NULL, B_FALSE)) {
+ goto error;
+ }
+ /*
+ * Copy To header. If To tag is present, copy it, if not then
+ * add one if the repsonse is not provisional.
+ */
+ if (ttag_present || (totag == NULL && response == SIP_TRYING)) {
+ if (_sip_find_and_copy_header(_sip_request, new_msg, SIP_TO,
+ NULL, B_FALSE)) {
+ goto error;
+ }
+ } else {
+ char *xtra_param;
+ boolean_t tag_alloc = B_FALSE;
+ int taglen;
+
+ if (totag == NULL) {
+ totag = sip_guid();
+ if (totag == NULL)
+ goto error;
+ tag_alloc = B_TRUE;
+ }
+ taglen = strlen(SIP_TAG) + strlen(totag) + 1;
+ xtra_param = (char *)malloc(taglen);
+ if (xtra_param == NULL) {
+ if (tag_alloc)
+ free(totag);
+ goto error;
+ }
+ (void) snprintf(xtra_param, taglen, "%s%s", SIP_TAG, totag);
+ if (tag_alloc)
+ free(totag);
+ if (_sip_find_and_copy_header(_sip_request, new_msg,
+ SIP_TO, xtra_param, B_FALSE)) {
+ free(xtra_param);
+ goto error;
+ }
+ free(xtra_param);
+ }
+
+ /*
+ * Copy Call-ID header.
+ */
+ if (_sip_find_and_copy_header(_sip_request, new_msg, SIP_CALL_ID, NULL,
+ B_FALSE)) {
+ goto error;
+ }
+ /*
+ * Copy CSEQ header
+ */
+ if (_sip_find_and_copy_header(_sip_request, new_msg, SIP_CSEQ, NULL,
+ B_FALSE)) {
+ goto error;
+ }
+ /*
+ * Copy RECORD-ROUTE header, if present.
+ */
+ if (sip_search_for_header(_sip_request, SIP_RECORD_ROUTE, NULL) !=
+ NULL) {
+ if (_sip_find_and_copy_all_header(_sip_request, new_msg,
+ SIP_RECORD_ROUTE) != 0) {
+ goto error;
+ }
+ }
+ if (mycontact != NULL) {
+ if (sip_add_contact(new_msg, NULL, mycontact, B_FALSE,
+ NULL) != 0) {
+ goto error;
+ }
+ }
+ (void) pthread_mutex_unlock(&_sip_request->sip_msg_mutex);
+ return ((sip_msg_t)new_msg);
+error:
+ sip_free_msg((sip_msg_t)new_msg);
+ (void) pthread_mutex_unlock(&_sip_request->sip_msg_mutex);
+ return (NULL);
+}
+
+/*
+ * NON OK ACK : MUST contain values for the Call-ID, From, and Request-URI
+ * that are equal to the values of those header fields in the orig request
+ * passed to the transport. The To header field in the ACK MUST equal the To
+ * header field in the response being acknowledged. The ACK MUST contain the
+ * top Via header field of the original request. The CSeq header field in
+ * the ACK MUST contain the same value for the sequence number as was
+ * present in the original request, but the method parameter MUST be equal
+ * to "ACK".
+ */
+int
+sip_create_nonOKack(sip_msg_t request, sip_msg_t response, sip_msg_t ack_msg)
+{
+ int seqno;
+ char *uri;
+ _sip_msg_t *_request;
+ _sip_msg_t *_response;
+ _sip_msg_t *_ack_msg;
+ int ret;
+
+ if (request == NULL || response == NULL || ack_msg == NULL ||
+ request == ack_msg) {
+ return (EINVAL);
+ }
+ _request = (_sip_msg_t *)request;
+ _response = (_sip_msg_t *)response;
+ _ack_msg = (_sip_msg_t *)ack_msg;
+
+ (void) pthread_mutex_lock(&_request->sip_msg_mutex);
+ if (_request->sip_msg_req_res == NULL) {
+ if ((ret = sip_parse_first_line(_request->sip_msg_start_line,
+ &_request->sip_msg_req_res)) != 0) {
+ (void) pthread_mutex_unlock(&_request->sip_msg_mutex);
+ return (ret);
+ }
+ }
+ if (_request->sip_msg_req_res->U.sip_request.sip_request_uri.
+ sip_str_ptr == NULL) {
+ (void) pthread_mutex_unlock(&_request->sip_msg_mutex);
+ return (EINVAL);
+ }
+ uri = (char *)malloc(_request->sip_msg_req_res->U.sip_request.
+ sip_request_uri.sip_str_len + 1);
+ if (uri == NULL) {
+ (void) pthread_mutex_unlock(&_request->sip_msg_mutex);
+ return (EINVAL);
+ }
+ (void) strncpy(uri,
+ _request->sip_msg_req_res->U.sip_request.sip_request_uri.
+ sip_str_ptr, _request->sip_msg_req_res->U.sip_request.
+ sip_request_uri.sip_str_len);
+ uri[_request->sip_msg_req_res->U.sip_request.
+ sip_request_uri.sip_str_len] = '\0';
+ if ((ret = sip_add_request_line(_ack_msg, ACK, uri)) != 0) {
+ (void) pthread_mutex_unlock(&_request->sip_msg_mutex);
+ return (ret);
+ }
+ free(uri);
+ if ((ret = _sip_find_and_copy_header(_request, _ack_msg, SIP_VIA,
+ NULL, B_TRUE)) != 0) {
+ (void) pthread_mutex_unlock(&_request->sip_msg_mutex);
+ return (ret);
+ }
+ (void) _sip_find_and_copy_header(_request, _ack_msg,
+ SIP_MAX_FORWARDS, NULL, B_TRUE);
+
+ (void) pthread_mutex_lock(&_response->sip_msg_mutex);
+ if ((ret = _sip_find_and_copy_header(_response, _ack_msg, SIP_TO,
+ NULL, B_TRUE)) != 0) {
+ (void) pthread_mutex_unlock(&_response->sip_msg_mutex);
+ return (ret);
+ }
+ (void) pthread_mutex_unlock(&_response->sip_msg_mutex);
+ if ((ret = _sip_find_and_copy_header(_request, _ack_msg, SIP_FROM,
+ NULL, B_TRUE)) != 0) {
+ (void) pthread_mutex_unlock(&_request->sip_msg_mutex);
+ return (ret);
+ }
+ if ((ret = _sip_find_and_copy_header(_request, _ack_msg, SIP_CALL_ID,
+ NULL, B_TRUE)) != 0) {
+ (void) pthread_mutex_unlock(&_request->sip_msg_mutex);
+ return (ret);
+ }
+ (void) pthread_mutex_unlock(&_request->sip_msg_mutex);
+ seqno = sip_get_callseq_num(_request, &ret);
+ if (ret != 0)
+ return (ret);
+ if ((ret = sip_add_cseq(_ack_msg, ACK, seqno)) != 0)
+ return (ret);
+ if ((ret = sip_adjust_msgbuf(_ack_msg)) != 0)
+ return (ret);
+ return (0);
+}
+
+/*
+ * This is a 2XX ACK, for others ACK is constructed differently,
+ * esp. the branch id is retained.
+ */
+int
+sip_create_OKack(sip_msg_t response, sip_msg_t ack_msg, char *transport,
+ char *sent_by, int sent_by_port, char *via_params)
+{
+ int seqno;
+ char *uri;
+ sip_parsed_header_t *parsed_header;
+ sip_hdr_value_t *contact_value;
+ _sip_header_t *header;
+ _sip_msg_t *_response;
+ _sip_msg_t *_ack_msg;
+ int ret;
+
+ if (response == NULL || response == NULL || transport == NULL)
+ return (EINVAL);
+ _response = (_sip_msg_t *)response;
+ _ack_msg = (_sip_msg_t *)ack_msg;
+
+ /*
+ * Get URI from the response, Contact field
+ */
+ (void) pthread_mutex_lock(&_response->sip_msg_mutex);
+ if ((header = sip_search_for_header(_response, SIP_CONTACT,
+ NULL)) == NULL) {
+ (void) pthread_mutex_unlock(&_response->sip_msg_mutex);
+ return (EINVAL);
+ }
+ if ((ret = sip_parse_cftr_header(header, (void *)&parsed_header)) !=
+ 0) {
+ (void) pthread_mutex_unlock(&_response->sip_msg_mutex);
+ return (ret);
+ }
+ contact_value = (sip_hdr_value_t *)parsed_header->value;
+ if (contact_value->cftr_uri.sip_str_ptr == NULL) {
+ (void) pthread_mutex_unlock(&_response->sip_msg_mutex);
+ return (EINVAL);
+ }
+ uri = (char *)malloc(contact_value->cftr_uri.sip_str_len + 1);
+ if (uri == NULL) {
+ (void) pthread_mutex_unlock(&_response->sip_msg_mutex);
+ return (ENOMEM);
+ }
+ (void) strncpy(uri, contact_value->cftr_uri.sip_str_ptr,
+ contact_value->cftr_uri.sip_str_len);
+ uri[contact_value->cftr_uri.sip_str_len] = '\0';
+ if ((ret = sip_add_request_line(_ack_msg, ACK, uri)) != 0) {
+ (void) pthread_mutex_unlock(&_response->sip_msg_mutex);
+ return (ret);
+ }
+ free(uri);
+ if ((ret = sip_add_via(_ack_msg, transport, sent_by, sent_by_port,
+ via_params)) != 0) {
+ (void) pthread_mutex_unlock(&_response->sip_msg_mutex);
+ return (ret);
+ }
+
+ if ((ret = _sip_find_and_copy_header(_response, _ack_msg, SIP_TO,
+ NULL, B_TRUE)) != 0) {
+ (void) pthread_mutex_unlock(&_response->sip_msg_mutex);
+ return (ret);
+ }
+ if ((ret = _sip_find_and_copy_header(_response, _ack_msg, SIP_FROM,
+ NULL, B_TRUE)) != 0) {
+ (void) pthread_mutex_unlock(&_response->sip_msg_mutex);
+ return (ret);
+ }
+ if ((ret = _sip_find_and_copy_header(_response, _ack_msg, SIP_CALL_ID,
+ NULL, B_TRUE)) != 0) {
+ (void) pthread_mutex_unlock(&_response->sip_msg_mutex);
+ return (ret);
+ }
+ /*
+ * Copy Max-Forward if present
+ */
+ if (sip_search_for_header(_response, SIP_MAX_FORWARDS, NULL) != NULL) {
+ if ((ret = _sip_find_and_copy_header(_response, _ack_msg,
+ SIP_MAX_FORWARDS, NULL, B_TRUE)) != 0) {
+ (void) pthread_mutex_unlock(&_response->sip_msg_mutex);
+ return (ret);
+ }
+ }
+ (void) pthread_mutex_unlock(&_response->sip_msg_mutex);
+ seqno = sip_get_callseq_num(_response, &ret);
+ if (ret != 0)
+ return (ret);
+ if ((ret = sip_add_cseq(_ack_msg, ACK, seqno)) != 0)
+ return (ret);
+
+ return (0);
+}
+
+/*
+ * Request-Line = Method SP Request-URI SP SIP-Version CRLF
+ */
+int
+sip_add_request_line(sip_msg_t sip_request, sip_method_t method,
+ char *request_uri)
+{
+ _sip_header_t *new_header;
+ int header_size;
+ _sip_msg_t *_sip_request;
+
+ if (method < INVITE || method >= MAX_SIP_METHODS ||
+ request_uri == NULL || sip_request == NULL) {
+ return (EINVAL);
+ }
+
+ _sip_request = (_sip_msg_t *)sip_request;
+ (void) pthread_mutex_lock(&_sip_request->sip_msg_mutex);
+ if (_sip_request->sip_msg_cannot_be_modified) {
+ (void) pthread_mutex_unlock(&_sip_request->sip_msg_mutex);
+ return (ENOTSUP);
+ }
+
+ header_size = strlen(sip_methods[method].name) + SIP_SPACE_LEN +
+ strlen(request_uri) + SIP_SPACE_LEN + strlen(SIP_VERSION) +
+ strlen(SIP_CRLF);
+
+ new_header = sip_new_header(header_size);
+ if (new_header == NULL) {
+ (void) pthread_mutex_unlock(&_sip_request->sip_msg_mutex);
+ return (ENOMEM);
+ }
+ new_header->sip_hdr_sipmsg = _sip_request;
+
+ (void) snprintf(new_header->sip_hdr_start, header_size + 1,
+ "%s %s %s%s", sip_methods[method].name, request_uri,
+ SIP_VERSION, SIP_CRLF);
+
+ new_header->sip_hdr_next = _sip_request->sip_msg_start_line;
+ _sip_request->sip_msg_start_line = new_header;
+ _sip_request->sip_msg_len += header_size;
+ (void) sip_parse_first_line(_sip_request->sip_msg_start_line,
+ &_sip_request->sip_msg_req_res);
+ if (_sip_request->sip_msg_buf != NULL)
+ _sip_request->sip_msg_modified = B_TRUE;
+ (void) pthread_mutex_unlock(&_sip_request->sip_msg_mutex);
+ return (0);
+}