/*
* 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 <stdlib.h>
#include <assert.h>
#include <ctype.h>
#include <pthread.h>
#include <strings.h>
#include <sip.h>
#include "sip_miscdefs.h"
/*
* Local version of case insensitive strstr().
*/
static char *
sip_reass_strstr(const char *as1, const char *as2)
{
const char *s1;
const char *s2;
const char *tptr;
char c;
s1 = as1;
s2 = as2;
if (s2 == NULL || *s2 == '\0')
return ((char *)s1);
c = *s2;
while (*s1)
if (tolower(*s1++) == c) {
tptr = s1;
while ((c = *++s2) == tolower(*s1++) && c)
;
if (c == 0)
return ((char *)tptr - 1);
s1 = tptr;
s2 = as2;
c = *s2;
}
return (NULL);
}
/*
* Get the value in the content-length field and add it to the header length
* and return the total length. returns -1 if the length cannot be determined
* or if the message does not contain the entire message.
*/
static int
sip_get_msglen(char *p, size_t msglen)
{
int value = 0;
int hlen;
char *c;
char *e;
int base = 10;
char *edge;
int digits = 0;
edge = p + msglen;
if ((c = sip_reass_strstr(p, "content-length")) == NULL)
return (-1);
hlen = c - p;
if ((hlen + strlen("content-length")) >= msglen)
return (-1);
c += strlen("content-length");
e = c + 1;
while (*e == ' ' || *e == ':') {
e++;
if (e == edge)
return (-1);
}
while (*e != '\r' && *e != ' ') {
if (e == edge)
return (-1);
if (*e >= '0' && *e <= '9')
digits = *e - '0';
else
return (-1);
value = (value * base) + digits;
e++;
}
while (*e != '\r') {
e++;
if (e == edge)
return (-1);
}
hlen = e - p + 4; /* 4 for 2 CRLFs ?? */
value += hlen;
return (value);
}
/*
* We have determined that msg does not contain a *single* complete message.
* Add it to the reassembly list and check if we have a complete message.
* a NULL 'msg' means we are just checking if there are more complete
* messages in the list that can be passed up.
*/
char *
sip_get_tcp_msg(sip_conn_object_t obj, char *msg, size_t *msglen)
{
int value;
sip_conn_obj_pvt_t *pvt_data;
sip_reass_entry_t *reass;
void **obj_val;
char *msgbuf = NULL;
int splitlen;
char *splitbuf;
if (msg != NULL) {
assert(*msglen > 0);
msgbuf = (char *)malloc(*msglen + 1);
if (msgbuf == NULL)
return (NULL);
(void) strncpy(msgbuf, msg, *msglen);
msgbuf[*msglen] = '\0';
msg = msgbuf;
}
obj_val = (void *)obj;
pvt_data = (sip_conn_obj_pvt_t *)*obj_val;
/*
* connection object not initialized
*/
if (pvt_data == NULL) {
if (msg == NULL)
return (NULL);
value = sip_get_msglen(msg, *msglen);
if (value == *msglen) {
return (msg);
} else {
if (msgbuf != NULL)
free(msgbuf);
return (NULL);
}
}
(void) pthread_mutex_lock(&pvt_data->sip_conn_obj_reass_lock);
reass = pvt_data->sip_conn_obj_reass;
assert(reass != NULL);
if (reass->sip_reass_msg == NULL) {
assert(reass->sip_reass_msglen == 0);
if (msg == NULL) {
(void) pthread_mutex_unlock(
&pvt_data->sip_conn_obj_reass_lock);
return (NULL);
}
value = sip_get_msglen(msg, *msglen);
if (value == *msglen) {
(void) pthread_mutex_unlock(
&pvt_data->sip_conn_obj_reass_lock);
return (msg);
}
reass->sip_reass_msg = msg;
reass->sip_reass_msglen = *msglen;
if (value != -1 && value < reass->sip_reass_msglen)
goto tryone;
(void) pthread_mutex_unlock(&pvt_data->sip_conn_obj_reass_lock);
return (NULL);
} else if (msg != NULL) {
/*
* Resize, not optimal
*/
int newlen = reass->sip_reass_msglen + *msglen;
char *newmsg;
assert(strlen(reass->sip_reass_msg) == reass->sip_reass_msglen);
newmsg = malloc(newlen + 1);
if (newmsg == NULL) {
(void) pthread_mutex_unlock(
&pvt_data->sip_conn_obj_reass_lock);
if (msgbuf != NULL)
free(msgbuf);
return (NULL);
}
(void) strncpy(newmsg, reass->sip_reass_msg,
reass->sip_reass_msglen);
newmsg[reass->sip_reass_msglen] = '\0';
(void) strncat(newmsg, msg, *msglen);
newmsg[newlen] = '\0';
assert(strlen(newmsg) == newlen);
reass->sip_reass_msglen = newlen;
free(msg);
free(reass->sip_reass_msg);
reass->sip_reass_msg = newmsg;
}
value = sip_get_msglen(reass->sip_reass_msg, reass->sip_reass_msglen);
if (value == -1 || value > reass->sip_reass_msglen) {
(void) pthread_mutex_unlock(&pvt_data->sip_conn_obj_reass_lock);
return (NULL);
}
tryone:
if (value == reass->sip_reass_msglen) {
msg = reass->sip_reass_msg;
*msglen = reass->sip_reass_msglen;
reass->sip_reass_msg = NULL;
reass->sip_reass_msglen = 0;
(void) pthread_mutex_unlock(&pvt_data->sip_conn_obj_reass_lock);
return (msg);
}
splitlen = reass->sip_reass_msglen - value;
msg = (char *)malloc(value + 1);
splitbuf = (char *)malloc(splitlen + 1);
if (msg == NULL || splitbuf == NULL) {
if (msg != NULL)
free(msg);
if (splitbuf != NULL)
free(splitbuf);
(void) pthread_mutex_unlock(&pvt_data->sip_conn_obj_reass_lock);
return (NULL);
}
(void) strncpy(msg, reass->sip_reass_msg, value);
msg[value] = '\0';
(void) strncpy(splitbuf, reass->sip_reass_msg + value, splitlen);
splitbuf[splitlen] = '\0';
free(reass->sip_reass_msg);
reass->sip_reass_msg = splitbuf;
reass->sip_reass_msglen = splitlen;
(void) pthread_mutex_unlock(&pvt_data->sip_conn_obj_reass_lock);
*msglen = value;
return (msg);
}