xref: /illumos-gate/usr/src/lib/libsip/common/sip_reass.c (revision 7a6d80f1660abd4755c68cbd094d4a914681d26e)
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 #include <stdlib.h>
28 #include <assert.h>
29 #include <ctype.h>
30 #include <pthread.h>
31 #include <strings.h>
32 #include <sip.h>
33 
34 #include "sip_miscdefs.h"
35 
36 /*
37  * Local version of case insensitive strstr().
38  */
39 static char *
40 sip_reass_strstr(const char *as1, const char *as2)
41 {
42 	const char	*s1;
43 	const char	*s2;
44 	const char	*tptr;
45 	char	c;
46 
47 	s1 = as1;
48 	s2 = as2;
49 
50 	if (s2 == NULL || *s2 == '\0')
51 		return ((char *)s1);
52 	c = *s2;
53 
54 	while (*s1)
55 		if (tolower(*s1++) == c) {
56 			tptr = s1;
57 			while ((c = *++s2) == tolower(*s1++) && c)
58 				;
59 			if (c == 0)
60 				return ((char *)tptr - 1);
61 			s1 = tptr;
62 			s2 = as2;
63 			c = *s2;
64 		}
65 
66 	return (NULL);
67 }
68 
69 /*
70  * Get the value in the content-length field and add it to the header length
71  * and return the total length. returns -1 if the length cannot be determined
72  * or if the message does not contain the entire message.
73  */
74 static int
75 sip_get_msglen(char *p, size_t msglen)
76 {
77 	int	value = 0;
78 	int 	hlen;
79 	char	*c;
80 	char	*e;
81 	int	base = 10;
82 	char	*edge;
83 	int	digits = 0;
84 
85 	edge = p + msglen;
86 	if ((c = sip_reass_strstr(p, "content-length")) == NULL)
87 		return (-1);
88 	hlen = c - p;
89 	if ((hlen +  strlen("content-length")) >= msglen)
90 		return (-1);
91 	c += strlen("content-length");
92 	e = c + 1;
93 	while (*e == ' ' || *e == ':') {
94 		e++;
95 		if (e == edge)
96 			return (-1);
97 	}
98 	while (*e  != '\r' && *e != ' ') {
99 		if (e == edge)
100 			return (-1);
101 		if (*e >= '0' && *e <= '9')
102 			digits = *e - '0';
103 		else
104 			return (-1);
105 		value = (value * base) + digits;
106 		e++;
107 	}
108 	while (*e != '\r') {
109 		e++;
110 		if (e == edge)
111 			return (-1);
112 	}
113 	hlen = e - p + 4;	/* 4 for 2 CRLFs ?? */
114 	value += hlen;
115 
116 	return (value);
117 }
118 
119 /*
120  * We have determined that msg does not contain a *single* complete message.
121  * Add it to the reassembly list and check if we have a complete message.
122  * a NULL 'msg' means we are just checking if there are more complete
123  * messages in the list that can be passed up.
124  */
125 char *
126 sip_get_tcp_msg(sip_conn_object_t obj, char *msg, size_t *msglen)
127 {
128 	int			value;
129 	sip_conn_obj_pvt_t	*pvt_data;
130 	sip_reass_entry_t	*reass;
131 	void			**obj_val;
132 	char			*msgbuf = NULL;
133 	int			splitlen;
134 	char			*splitbuf;
135 
136 	if (msg != NULL) {
137 		assert(*msglen > 0);
138 		msgbuf = (char *)malloc(*msglen + 1);
139 		if (msgbuf == NULL)
140 			return (NULL);
141 		(void) strncpy(msgbuf, msg, *msglen);
142 		msgbuf[*msglen] = '\0';
143 		msg = msgbuf;
144 	}
145 	obj_val = (void *)obj;
146 	pvt_data = (sip_conn_obj_pvt_t *)*obj_val;
147 	/*
148 	 * connection object not initialized
149 	 */
150 	if (pvt_data == NULL) {
151 		if (msg == NULL)
152 			return (NULL);
153 		value = sip_get_msglen(msg, *msglen);
154 		if (value == *msglen) {
155 			return (msg);
156 		} else {
157 			if (msgbuf != NULL)
158 				free(msgbuf);
159 			return (NULL);
160 		}
161 	}
162 	(void) pthread_mutex_lock(&pvt_data->sip_conn_obj_reass_lock);
163 	reass = pvt_data->sip_conn_obj_reass;
164 	assert(reass != NULL);
165 	if (reass->sip_reass_msg == NULL) {
166 		assert(reass->sip_reass_msglen == 0);
167 		if (msg == NULL) {
168 			(void) pthread_mutex_unlock(
169 			    &pvt_data->sip_conn_obj_reass_lock);
170 			return (NULL);
171 		}
172 		value = sip_get_msglen(msg, *msglen);
173 		if (value == *msglen) {
174 			(void) pthread_mutex_unlock(
175 			    &pvt_data->sip_conn_obj_reass_lock);
176 			return (msg);
177 		}
178 		reass->sip_reass_msg = msg;
179 		reass->sip_reass_msglen = *msglen;
180 		if (value != -1 && value < reass->sip_reass_msglen)
181 			goto tryone;
182 		(void) pthread_mutex_unlock(&pvt_data->sip_conn_obj_reass_lock);
183 		return (NULL);
184 	} else if (msg != NULL) {
185 		/*
186 		 * Resize, not optimal
187 		 */
188 		int	newlen = reass->sip_reass_msglen + *msglen;
189 		char	*newmsg;
190 
191 		assert(strlen(reass->sip_reass_msg) == reass->sip_reass_msglen);
192 		newmsg = malloc(newlen + 1);
193 		if (newmsg == NULL) {
194 			(void) pthread_mutex_unlock(
195 			    &pvt_data->sip_conn_obj_reass_lock);
196 			if (msgbuf != NULL)
197 				free(msgbuf);
198 			return (NULL);
199 		}
200 		(void) strncpy(newmsg, reass->sip_reass_msg,
201 		    reass->sip_reass_msglen);
202 		newmsg[reass->sip_reass_msglen] = '\0';
203 		(void) strncat(newmsg, msg, *msglen);
204 		newmsg[newlen] = '\0';
205 		assert(strlen(newmsg) == newlen);
206 		reass->sip_reass_msglen = newlen;
207 		free(msg);
208 		free(reass->sip_reass_msg);
209 		reass->sip_reass_msg = newmsg;
210 	}
211 	value = sip_get_msglen(reass->sip_reass_msg, reass->sip_reass_msglen);
212 	if (value == -1 || value >  reass->sip_reass_msglen) {
213 		(void) pthread_mutex_unlock(&pvt_data->sip_conn_obj_reass_lock);
214 		return (NULL);
215 	}
216 tryone:
217 	if (value == reass->sip_reass_msglen) {
218 		msg = reass->sip_reass_msg;
219 		*msglen = reass->sip_reass_msglen;
220 		reass->sip_reass_msg = NULL;
221 		reass->sip_reass_msglen = 0;
222 		(void) pthread_mutex_unlock(&pvt_data->sip_conn_obj_reass_lock);
223 		return (msg);
224 	}
225 	splitlen = reass->sip_reass_msglen - value;
226 	msg = (char *)malloc(value + 1);
227 	splitbuf = (char *)malloc(splitlen + 1);
228 	if (msg == NULL || splitbuf == NULL) {
229 		if (msg != NULL)
230 			free(msg);
231 		if (splitbuf != NULL)
232 			free(splitbuf);
233 		(void) pthread_mutex_unlock(&pvt_data->sip_conn_obj_reass_lock);
234 		return (NULL);
235 	}
236 	(void) strncpy(msg, reass->sip_reass_msg, value);
237 	msg[value] = '\0';
238 	(void) strncpy(splitbuf, reass->sip_reass_msg + value, splitlen);
239 	splitbuf[splitlen] = '\0';
240 	free(reass->sip_reass_msg);
241 	reass->sip_reass_msg = splitbuf;
242 	reass->sip_reass_msglen = splitlen;
243 	(void) pthread_mutex_unlock(&pvt_data->sip_conn_obj_reass_lock);
244 	*msglen = value;
245 	return (msg);
246 }
247