1 /*
2     MiddleMan filtering proxy server
3     Copyright (C) 2002  Jason McLaughlin
4 
5     This program is free software; you can redistribute it and/or modify
6     it under the terms of the GNU General Public License as published by
7     the Free Software Foundation; either version 2 of the License, or
8     (at your option) any later version.
9 
10     This program is distributed in the hope that it will be useful,
11     but WITHOUT ANY WARRANTY; without even the implied warranty of
12     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13     GNU General Public License for more details.
14 
15     You should have received a copy of the GNU General Public License
16     along with this program; if not, write to the Free Software
17     Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
18 */
19 
20 #include <stdio.h>
21 #include <stdlib.h>
22 #include <unistd.h>
23 #include <fcntl.h>
24 #include <string.h>
25 #include <ctype.h>
26 #include <stdarg.h>
27 #include "proto.h"
28 
29 extern GLOBAL *global;
30 
31 #ifdef HAVE_ZLIB
32 #include <zlib.h>
33 #endif				/* HAVE_ZLIB */
34 
35 /*
36 free memory used by FILEBUF-type structure
37 */
filebuf_free(FILEBUF * filebuf)38 void filebuf_free(FILEBUF * filebuf)
39 {
40 	if (!filebuf)
41 		return;
42 
43 	if (filebuf->data != NULL)
44 		xfree(filebuf->data);
45 	if (filebuf->type != NULL)
46 		xfree(filebuf->type);
47 
48 	xfree(filebuf);
49 }
50 
51 /*
52 create empty filebuf
53 */
filebuf_new()54 FILEBUF *filebuf_new()
55 {
56 	FILEBUF *filebuf;
57 
58 	filebuf = xmalloc(sizeof(FILEBUF));
59 
60 	filebuf->data = NULL;
61 	filebuf->type = NULL;
62 	filebuf->size = filebuf->realsize = 0;
63 	filebuf->code = -1;
64 
65 	return filebuf;
66 }
67 
filebuf_dup(FILEBUF * filebuf)68 FILEBUF *filebuf_dup(FILEBUF * filebuf)
69 {
70 	FILEBUF *ret;
71 
72 	ret = filebuf_new();
73 
74 	ret->data = (filebuf->size != 0) ? xmalloc(filebuf->size) : NULL;
75 	memcpy(ret->data, filebuf->data, filebuf->size);
76 	ret->size = ret->realsize = filebuf->size;
77 
78 	return ret;
79 }
80 
81 /*
82 reduce filebuf size to actual size of contents
83 */
filebuf_shorten(FILEBUF * filebuf)84 void filebuf_shorten(FILEBUF * filebuf)
85 {
86 	if (filebuf->realsize > filebuf->size) {
87 		filebuf->realsize = filebuf->size;
88 		filebuf->data = xrealloc(filebuf->data, filebuf->size);
89 	}
90 }
91 
92 /*
93 append data to a file buffer
94 */
filebuf_add(FILEBUF * filebuf,const char * data,int len)95 void filebuf_add(FILEBUF * filebuf, const char *data, int len)
96 {
97 	int x;
98 
99 	x = filebuf->size;
100 
101 	filebuf_resize(filebuf, filebuf->size + len);
102 
103 	memcpy(&filebuf->data[x], data, len);
104 }
105 
106 
107 /*
108 add formatted data to filebuf
109 */
filebuf_addf(FILEBUF * filebuf,const char * fmt,...)110 void filebuf_addf(FILEBUF * filebuf, const char *fmt, ...)
111 {
112 	int ret;
113 	char buf[8192];
114 	va_list valist;
115 
116 	va_start(valist, fmt);
117 	ret = vsnprintf(buf, sizeof(buf), fmt, valist);
118 	va_end(valist);
119 
120 	filebuf_add(filebuf, buf, (ret > sizeof(buf) || ret == -1) ? sizeof(buf) : ret);
121 }
122 
123 /*
124 resize a filebuf (without adding anything)
125 */
filebuf_resize(FILEBUF * filebuf,int newsize)126 void filebuf_resize(FILEBUF * filebuf, int newsize)
127 {
128 	if (filebuf->realsize >= newsize && filebuf->realsize <= newsize + FILEBUF_ALIGNMENT && filebuf->data != NULL)
129 		filebuf->size = newsize;
130 	else if (newsize != 0) {
131 		/* aligning the filebuf size makes it much more difficult to catch off-by-one errors */
132 #ifdef _DEBUG
133 		filebuf->realsize = newsize;
134 #else
135 		filebuf->realsize = ALIGN2(newsize, FILEBUF_ALIGNMENT);
136 #endif
137 		filebuf->data = xrealloc(filebuf->data, filebuf->realsize);
138 		filebuf->size = newsize;
139 	}
140 }
141 
142 /*
143 write a filebuf to a file
144 */
filebuf_save(FILEBUF * filebuf,char * filename)145 int filebuf_save(FILEBUF * filebuf, char *filename)
146 {
147 	int fd, ret;
148 
149 	fd = open(filename, O_CREAT | O_WRONLY);
150 	if (fd == -1)
151 		return -1;
152 
153 	ret = write(fd, filebuf->data, filebuf->size);
154 
155 	close(fd);
156 
157 	return ret;
158 }
159 
160 /*
161 read a file into a filebuf
162 */
filebuf_read(FILEBUF * filebuf,char * filename)163 int filebuf_read(FILEBUF * filebuf, char *filename)
164 {
165 	int x, ret = 0, fd;
166 	char buf[BLOCKSIZE];
167 
168 	fd = open(filename, O_RDONLY);
169 	if (fd == -1)
170 		return -1;
171 
172 	while ((x = read(fd, buf, BLOCKSIZE)) > 0) {
173 		filebuf_add(filebuf, buf, x);
174 
175 		ret += x;
176 	}
177 
178 	close(fd);
179 
180 	return ret;
181 }
182 
183 /*
184 replace area of filebuf at specified offset and length with something else
185 */
filebuf_replace(FILEBUF * filebuf,int so,int eo,char * replace)186 void filebuf_replace(FILEBUF * filebuf, int so, int eo, char *replace)
187 {
188 	int len1 = eo - so;
189 	int len2 = strlen(replace);
190 
191 	if (len2 > len1)
192 		filebuf_resize(filebuf, filebuf->size + (len2 - len1));
193 
194 	if (len1 != len2)
195 		memmove(&filebuf->data[eo + (len2 - len1)], &filebuf->data[eo], filebuf->size - eo);
196 
197 	if (len2 < len1)
198 		filebuf_resize(filebuf, filebuf->size + (len2 - len1));
199 
200 	memcpy(&filebuf->data[so], replace, len2);
201 }
202 
203 #ifdef HAVE_ZLIB
204 
filebuf_compress(FILEBUF * filebuf,int gzip)205 int filebuf_compress(FILEBUF * filebuf, int gzip)
206 {
207 	int x, i, done = 0;
208 	char gzhdr[10] = { 0x1f, 0x8b, Z_DEFLATED, 0, 0, 0, 0, 0, 0, 0x03 };
209 	char gzbuffer[8192], *ptr;
210 	unsigned long gz_crc32, len;
211 	z_stream gz_stream;
212 	FILEBUF *cfilebuf = NULL;
213 
214 	/* it seems zlib makes an allocation with a size of 0, so using the xmalloc/xfree
215 	   wrappers is out of the question since they assert for that */
216 	gz_stream.zalloc = NULL;
217 	gz_stream.zfree = NULL;
218 	gz_stream.opaque = NULL;
219 	gz_stream.next_in = gz_stream.next_out = 0;
220 	gz_stream.avail_in = gz_stream.avail_out = 0;
221 
222 	x = deflateInit2(&gz_stream, Z_BEST_COMPRESSION, Z_DEFLATED, -MAX_WBITS, 9, Z_DEFAULT_STRATEGY);
223 	if (x != Z_OK)
224 		return FALSE;
225 
226 	cfilebuf = filebuf_new();
227 	if (gzip == TRUE)
228 		filebuf_add(cfilebuf, gzhdr, sizeof(gzhdr));
229 
230 	gz_stream.next_out = gzbuffer;
231 	gz_stream.avail_out = sizeof(gzbuffer);
232 	gz_stream.next_in = filebuf->data;
233 	gz_stream.avail_in = filebuf->size;
234 
235 	while (gz_stream.avail_in != 0) {
236 		if (gz_stream.avail_out == 0) {
237 			filebuf_add(cfilebuf, gzbuffer, sizeof(gzbuffer));
238 
239 			gz_stream.next_out = gzbuffer;
240 			gz_stream.avail_out = sizeof(gzbuffer);
241 		}
242 
243 		x = deflate(&gz_stream, Z_NO_FLUSH);
244 		if (x == Z_STREAM_END)
245 			break;
246 		else if (x != Z_OK)
247 			goto error;
248 	}
249 
250 	while (1) {
251 		len = sizeof(gzbuffer) - gz_stream.avail_out;
252 		if (len > 0) {
253 			filebuf_add(cfilebuf, gzbuffer, len);
254 
255 			gz_stream.next_out = gzbuffer;
256 			gz_stream.avail_out = sizeof(gzbuffer);
257 		}
258 
259 		if (done)
260 			break;
261 
262 		x = deflate(&gz_stream, Z_FINISH);
263 		if (len == 0 && x == Z_BUF_ERROR)
264 			x = Z_OK;
265 
266 		done = (gz_stream.avail_out != 0 || x == Z_STREAM_END);
267 
268 		if (x != Z_OK && x != Z_STREAM_END)
269 			break;
270 	}
271 
272 	if (x != Z_STREAM_END)
273 		goto error;
274 
275 	if (gzip == TRUE) {
276 		len = cfilebuf->size;
277 		filebuf_resize(cfilebuf, cfilebuf->size + 8);
278 
279 		/* append the crc32 and size */
280 		gz_crc32 = crc32(0L, filebuf->data, filebuf->size);
281 
282 		for (ptr = cfilebuf->data + len, i = 0; i < 4; i++) {
283 			*ptr++ = (gz_crc32 & 0xFF);
284 			gz_crc32 >>= 8;
285 		}
286 
287 		x = gz_stream.total_in;
288 		for (i = 0; i < 4; i++) {
289 			*ptr++ = (x & 0xFF);
290 			x >>= 8;
291 		}
292 	}
293 
294 	xfree(filebuf->data);
295 	filebuf->data = cfilebuf->data;
296 	filebuf->size = cfilebuf->size;
297 	filebuf->realsize = cfilebuf->realsize;
298 
299 	cfilebuf->data = NULL;
300 	filebuf_free(cfilebuf);
301 
302 	deflateEnd(&gz_stream);
303 
304 	return TRUE;
305 
306       error:
307 
308 	zlib_error(x);
309 
310 	filebuf_free(cfilebuf);
311 	deflateEnd(&gz_stream);
312 
313 	return FALSE;
314 }
315 
filebuf_uncompress(FILEBUF * filebuf,int gzip)316 int filebuf_uncompress(FILEBUF * filebuf, int gzip)
317 {
318 	int x, done = 0;
319 	char gzbuffer[8192];
320 	unsigned long len;
321 	z_stream gz_stream;
322 	FILEBUF *cfilebuf = NULL;
323 
324 	/* sanity check, we assume it's at least this big further down */
325 	if (filebuf->size < 18 && gzip == TRUE)
326 		return FALSE;
327 
328 	gz_stream.zalloc = NULL;
329 	gz_stream.zfree = NULL;
330 	gz_stream.opaque = NULL;
331 	gz_stream.next_in = gz_stream.next_out = 0;
332 	gz_stream.avail_in = gz_stream.avail_out = 0;
333 
334 	x = inflateInit2(&gz_stream, -MAX_WBITS);
335 	if (x != Z_OK)
336 		return FALSE;
337 
338 	cfilebuf = filebuf_new();
339 
340 	gz_stream.next_out = gzbuffer;
341 	gz_stream.avail_out = sizeof(gzbuffer);
342 	if (gzip == TRUE) {
343 		/* skip gzip header */
344 		gz_stream.next_in = filebuf->data + 10;
345 		gz_stream.avail_in = filebuf->size - 18;
346 	} else {
347 		gz_stream.next_in = filebuf->data;
348 		gz_stream.avail_in = filebuf->size;
349 	}
350 
351 	while (gz_stream.avail_in != 0) {
352 		if (gz_stream.avail_out == 0) {
353 			filebuf_add(cfilebuf, gzbuffer, sizeof(gzbuffer));
354 
355 			gz_stream.next_out = gzbuffer;
356 			gz_stream.avail_out = sizeof(gzbuffer);
357 		}
358 
359 		x = inflate(&gz_stream, Z_NO_FLUSH);
360 		if (x == Z_STREAM_END)
361 			break;
362 		else if (x != Z_OK)
363 			goto error;
364 	}
365 
366 	while (1) {
367 		len = sizeof(gzbuffer) - gz_stream.avail_out;
368 		if (len > 0) {
369 			filebuf_add(cfilebuf, gzbuffer, len);
370 
371 			gz_stream.next_out = gzbuffer;
372 			gz_stream.avail_out = sizeof(gzbuffer);
373 		}
374 
375 		if (done)
376 			break;
377 
378 		x = inflate(&gz_stream, Z_SYNC_FLUSH);
379 		if (len == 0 && x == Z_BUF_ERROR)
380 			x = Z_OK;
381 
382 		done = (gz_stream.avail_out != 0 || x == Z_STREAM_END);
383 
384 		if (x != Z_OK && x != Z_STREAM_END)
385 			break;
386 	}
387 
388 	if (x != Z_STREAM_END)
389 		goto error;
390 
391 	/* skip validating crc32 and size... I'm lazy :P */
392 
393 	xfree(filebuf->data);
394 	filebuf->data = cfilebuf->data;
395 	filebuf->size = cfilebuf->size;
396 	filebuf->realsize = cfilebuf->realsize;
397 
398 	cfilebuf->data = NULL;
399 	filebuf_free(cfilebuf);
400 
401 	inflateEnd(&gz_stream);
402 
403 	return TRUE;
404 
405       error:
406 
407 	zlib_error(x);
408 
409 	filebuf_free(cfilebuf);
410 	inflateEnd(&gz_stream);
411 
412 	return FALSE;
413 }
414 
zlib_error(int x)415 void zlib_error(int x)
416 {
417 	switch (x) {
418 	case Z_DATA_ERROR:
419 		putlog(MMLOG_ERROR, "zlib data error");
420 		break;
421 	case Z_MEM_ERROR:
422 		putlog(MMLOG_ERROR, "zlib memory error");
423 		break;
424 	case Z_BUF_ERROR:
425 		putlog(MMLOG_ERROR, "zlib buffer error");
426 		break;
427 	case Z_STREAM_ERROR:
428 		putlog(MMLOG_ERROR, "zlib stream error");
429 		break;
430 	case Z_NEED_DICT:
431 		putlog(MMLOG_ERROR, "zlib dictionary error");
432 		break;
433 	default:
434 		putlog(MMLOG_ERROR, "zlib unknown error %d", x);
435 		break;
436 	}
437 }
438 
439 #else
440 
filebuf_compress(FILEBUF * filebuf,int gzip)441 int filebuf_compress(FILEBUF * filebuf, int gzip)
442 {
443 	return FALSE;
444 }
445 
filebuf_uncompress(FILEBUF * filebuf,int gzip)446 int filebuf_uncompress(FILEBUF * filebuf, int gzip)
447 {
448 	return FALSE;
449 }
450 
451 #endif				/* HAVE_ZLIB */
452 
filebuf_to_stack(FILEBUF * filebuf)453 STACK *filebuf_to_stack(FILEBUF * filebuf)
454 {
455 	char *lptr, *eolptr;
456 	STACK *ret = NULL, *start = NULL;
457 
458 	lptr = filebuf->data;
459 	while (lptr != NULL) {
460 		eolptr = memchr(lptr, '\n', filebuf->size - (lptr - filebuf->data));
461 
462 		PUSHN(ret, lptr, (eolptr != NULL) ? eolptr - lptr : filebuf->size - (lptr - filebuf->data));
463 		if (start == NULL)
464 			start = ret;
465 
466 		lptr = (eolptr != NULL && eolptr - filebuf->data != filebuf->size - 1) ? eolptr + 1 : NULL;
467 	}
468 
469 	return start;
470 }
471 
filebuf_decode(CONNECTION * connection,FILEBUF * filebuf)472 int filebuf_decode(CONNECTION * connection, FILEBUF * filebuf)
473 {
474 	int ret;
475 
476 	if (s_strcasestr(connection->rheader->content_encoding, "gzip") != NULL) {
477 		ret = filebuf_uncompress(filebuf, TRUE);
478 		if (ret == FALSE) {
479 			putlog(MMLOG_ERROR, "failed to decompress gzip encoded content");
480 
481 			/* failed to decompress, just send as-is */
482 			return FALSE;
483 		} else
484 			putlog(MMLOG_DEBUG, "decompressed gzip encoded file");
485 	} else if (s_strcasestr(connection->rheader->content_encoding, "deflate") != NULL) {
486 		ret = filebuf_uncompress(filebuf, FALSE);
487 		if (ret == FALSE) {
488 			putlog(MMLOG_ERROR, "failed to decompress deflate encoded content");
489 
490 			return FALSE;
491 		} else
492 			putlog(MMLOG_DEBUG, "decompressed deflate encoded file");
493 	} else {
494 		/* unhandled content encoding, send as-is */
495 		putlog(MMLOG_WARN, "unhandled content encoding: %s", connection->rheader->content_encoding);
496 
497 		return FALSE;
498 	}
499 
500 	return TRUE;
501 }
502 
filebuf_encode(CONNECTION * connection,FILEBUF * filebuf)503 int filebuf_encode(CONNECTION * connection, FILEBUF * filebuf)
504 {
505 	int ret;
506 
507 	if (connection->flags & CONNECTION_ENCODED)
508 		return FALSE;
509 
510 	if (connection->header->accept_encoding != NULL && atomic_read_rwlock(&global->general->lock, &global->general->compressout) == TRUE) {
511 		/* compress the content if the browser supports gzip encoding */
512 		if (s_strcasestr(connection->header->accept_encoding, "gzip") != NULL) {
513 			ret = filebuf_compress(filebuf, TRUE);
514 			if (ret == TRUE) {
515 				putlog(MMLOG_DEBUG, "gzip encoded file");
516 				connection->rheader->content_encoding = xstrdup("gzip");
517 
518 				return TRUE;
519 			} else {
520 				putlog(MMLOG_DEBUG, "failed to gzip encode file");
521 
522 				return FALSE;
523 			}
524 		} else if (s_strcasestr(connection->header->accept_encoding, "deflate") != NULL) {
525 			ret = filebuf_compress(filebuf, FALSE);
526 			if (ret == TRUE) {
527 				putlog(MMLOG_DEBUG, "deflate encoded file");
528 				connection->rheader->content_encoding = xstrdup("deflate");
529 
530 				return TRUE;
531 			} else {
532 				putlog(MMLOG_DEBUG, "failed to deflate encode file");
533 
534 				return FALSE;
535 			}
536 		}
537 	}
538 
539 	return FALSE;
540 }
541