1/*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
7 *
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
12 *
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 *
19 * CDDL HEADER END
20 */
21
22/*
23 * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
24 * Use is subject to license terms.
25 */
26
27#pragma ident	"%Z%%M%	%I%	%E% SMI"
28
29#include <stdlib.h>
30#include <assert.h>
31#include <ctype.h>
32#include <pthread.h>
33#include <strings.h>
34#include <sip.h>
35
36#include "sip_miscdefs.h"
37
38/*
39 * Local version of case insensitive strstr().
40 */
41static char *
42sip_reass_strstr(const char *as1, const char *as2)
43{
44	const char	*s1;
45	const char	*s2;
46	const char	*tptr;
47	char	c;
48
49	s1 = as1;
50	s2 = as2;
51
52	if (s2 == NULL || *s2 == '\0')
53		return ((char *)s1);
54	c = *s2;
55
56	while (*s1)
57		if (tolower(*s1++) == c) {
58			tptr = s1;
59			while ((c = *++s2) == tolower(*s1++) && c)
60				;
61			if (c == 0)
62				return ((char *)tptr - 1);
63			s1 = tptr;
64			s2 = as2;
65			c = *s2;
66		}
67
68	return (NULL);
69}
70
71/*
72 * Get the value in the content-length field and add it to the header length
73 * and return the total length. returns -1 if the length cannot be determined
74 * or if the message does not contain the entire message.
75 */
76static int
77sip_get_msglen(char *p, size_t msglen)
78{
79	int	value = 0;
80	int 	hlen;
81	char	*c;
82	char	*e;
83	int	base = 10;
84	char	*edge;
85	int	digits = 0;
86
87	edge = p + msglen;
88	if ((c = sip_reass_strstr(p, "content-length")) == NULL)
89		return (-1);
90	hlen = c - p;
91	if ((hlen +  strlen("content-length")) >= msglen)
92		return (-1);
93	c += strlen("content-length");
94	e = c + 1;
95	while (*e == ' ' || *e == ':') {
96		e++;
97		if (e == edge)
98			return (-1);
99	}
100	while (*e  != '\r' && *e != ' ') {
101		if (e == edge)
102			return (-1);
103		if (*e >= '0' && *e <= '9')
104			digits = *e - '0';
105		else
106			return (-1);
107		value = (value * base) + digits;
108		e++;
109	}
110	while (*e != '\r') {
111		e++;
112		if (e == edge)
113			return (-1);
114	}
115	hlen = e - p + 4;	/* 4 for 2 CRLFs ?? */
116	value += hlen;
117
118	return (value);
119}
120
121/*
122 * We have determined that msg does not contain a *single* complete message.
123 * Add it to the reassembly list and check if we have a complete message.
124 * a NULL 'msg' means we are just checking if there are more complete
125 * messages in the list that can be passed up.
126 */
127char *
128sip_get_tcp_msg(sip_conn_object_t obj, char *msg, size_t *msglen)
129{
130	int			value;
131	sip_conn_obj_pvt_t	*pvt_data;
132	sip_reass_entry_t	*reass;
133	void			**obj_val;
134	char			*msgbuf = NULL;
135	int			splitlen;
136	char			*splitbuf;
137
138	if (msg != NULL) {
139		assert(*msglen > 0);
140		msgbuf = (char *)malloc(*msglen + 1);
141		if (msgbuf == NULL)
142			return (NULL);
143		(void) strncpy(msgbuf, msg, *msglen);
144		msgbuf[*msglen] = '\0';
145		msg = msgbuf;
146	}
147	obj_val = (void *)obj;
148	pvt_data = (sip_conn_obj_pvt_t *)*obj_val;
149	/*
150	 * connection object not initialized
151	 */
152	if (pvt_data == NULL) {
153		if (msg == NULL)
154			return (NULL);
155		value = sip_get_msglen(msg, *msglen);
156		if (value == *msglen) {
157			return (msg);
158		} else {
159			if (msgbuf != NULL)
160				free(msgbuf);
161			return (NULL);
162		}
163	}
164	(void) pthread_mutex_lock(&pvt_data->sip_conn_obj_reass_lock);
165	reass = pvt_data->sip_conn_obj_reass;
166	assert(reass != NULL);
167	if (reass->sip_reass_msg == NULL) {
168		assert(reass->sip_reass_msglen == 0);
169		if (msg == NULL) {
170			(void) pthread_mutex_unlock(
171			    &pvt_data->sip_conn_obj_reass_lock);
172			return (NULL);
173		}
174		value = sip_get_msglen(msg, *msglen);
175		if (value == *msglen) {
176			(void) pthread_mutex_unlock(
177			    &pvt_data->sip_conn_obj_reass_lock);
178			return (msg);
179		}
180		reass->sip_reass_msg = msg;
181		reass->sip_reass_msglen = *msglen;
182		if (value != -1 && value < reass->sip_reass_msglen)
183			goto tryone;
184		(void) pthread_mutex_unlock(&pvt_data->sip_conn_obj_reass_lock);
185		return (NULL);
186	} else if (msg != NULL) {
187		/*
188		 * Resize, not optimal
189		 */
190		int	newlen = reass->sip_reass_msglen + *msglen;
191		char	*newmsg;
192
193		assert(strlen(reass->sip_reass_msg) == reass->sip_reass_msglen);
194		newmsg = malloc(newlen + 1);
195		if (newmsg == NULL) {
196			(void) pthread_mutex_unlock(
197			    &pvt_data->sip_conn_obj_reass_lock);
198			if (msgbuf != NULL)
199				free(msgbuf);
200			return (NULL);
201		}
202		(void) strncpy(newmsg, reass->sip_reass_msg,
203		    reass->sip_reass_msglen);
204		newmsg[reass->sip_reass_msglen] = '\0';
205		(void) strncat(newmsg, msg, *msglen);
206		newmsg[newlen] = '\0';
207		assert(strlen(newmsg) == newlen);
208		reass->sip_reass_msglen = newlen;
209		free(msg);
210		free(reass->sip_reass_msg);
211		reass->sip_reass_msg = newmsg;
212	}
213	value = sip_get_msglen(reass->sip_reass_msg, reass->sip_reass_msglen);
214	if (value == -1 || value >  reass->sip_reass_msglen) {
215		(void) pthread_mutex_unlock(&pvt_data->sip_conn_obj_reass_lock);
216		return (NULL);
217	}
218tryone:
219	if (value == reass->sip_reass_msglen) {
220		msg = reass->sip_reass_msg;
221		*msglen = reass->sip_reass_msglen;
222		reass->sip_reass_msg = NULL;
223		reass->sip_reass_msglen = 0;
224		(void) pthread_mutex_unlock(&pvt_data->sip_conn_obj_reass_lock);
225		return (msg);
226	}
227	splitlen = reass->sip_reass_msglen - value;
228	msg = (char *)malloc(value + 1);
229	splitbuf = (char *)malloc(splitlen + 1);
230	if (msg == NULL || splitbuf == NULL) {
231		if (msg != NULL)
232			free(msg);
233		if (splitbuf != NULL)
234			free(splitbuf);
235		(void) pthread_mutex_unlock(&pvt_data->sip_conn_obj_reass_lock);
236		return (NULL);
237	}
238	(void) strncpy(msg, reass->sip_reass_msg, value);
239	msg[value] = '\0';
240	(void) strncpy(splitbuf, reass->sip_reass_msg + value, splitlen);
241	splitbuf[splitlen] = '\0';
242	free(reass->sip_reass_msg);
243	reass->sip_reass_msg = splitbuf;
244	reass->sip_reass_msglen = splitlen;
245	(void) pthread_mutex_unlock(&pvt_data->sip_conn_obj_reass_lock);
246	*msglen = value;
247	return (msg);
248}
249