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