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