xref: /illumos-gate/usr/src/lib/libsip/common/sip_reass.c (revision 2bbdd445a21f9d61f4a0ca0faf05d5ceb2bd91f3)
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  */
41 static char *
42 sip_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  */
76 static int
77 sip_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  */
127 char *
128 sip_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 	}
218 tryone:
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