~ubuntu-branches/ubuntu/intrepid/dansguardian/intrepid-security

« back to all changes in this revision

Viewing changes to src/DataBuffer.cpp

  • Committer: Bazaar Package Importer
  • Author(s): Alexander Wirt
  • Date: 2008-04-06 14:47:06 UTC
  • mfrom: (1.1.5 upstream)
  • Revision ID: james.westby@ubuntu.com-20080406144706-2r26l1rougdmb1sd
Tags: 2.9.9.3-2
This time build with gcc 4.3 (Closes: #454889) 

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
//Please refer to http://dansguardian.org/?page=copyright
 
2
//for the license for this code.
 
3
//Written by Daniel Barron (daniel@//jadeb/.com).
 
4
//For support go to http://groups.yahoo.com/group/dansguardian
 
5
 
 
6
//  This program is free software; you can redistribute it and/or modify
 
7
//  it under the terms of the GNU General Public License as published by
 
8
//  the Free Software Foundation; either version 2 of the License, or
 
9
//  (at your option) any later version.
 
10
//
 
11
//  This program is distributed in the hope that it will be useful,
 
12
//  but WITHOUT ANY WARRANTY; without even the implied warranty of
 
13
//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
14
//  GNU General Public License for more details.
 
15
//
 
16
//  You should have received a copy of the GNU General Public License
 
17
//  along with this program; if not, write to the Free Software
 
18
//  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 
19
 
 
20
 
 
21
// INCLUDES
 
22
 
 
23
#include "platform.h"
 
24
 
 
25
#include "HTTPHeader.hpp"
 
26
#include "OptionContainer.hpp"
 
27
 
 
28
#include <syslog.h>
 
29
#include <algorithm>
 
30
#include <cstdlib>
 
31
#include <unistd.h>
 
32
#include <zlib.h>
 
33
#include <cerrno>
 
34
#include <fstream>
 
35
#include <sys/time.h>
 
36
#include <queue>
 
37
 
 
38
#ifdef __GCCVER3
 
39
#include <istream>
 
40
#else
 
41
#include <istream.h>
 
42
#endif
 
43
 
 
44
// DEFINES
 
45
 
 
46
#define __DGHEADER_SENDALL 0
 
47
#define __DGHEADER_SENDFIRSTLINE 1
 
48
#define __DGHEADER_SENDREST 2
 
49
 
 
50
 
 
51
// GLOBALS
 
52
 
 
53
extern OptionContainer o;
 
54
 
 
55
 
 
56
// IMPLEMENTATION
 
57
 
 
58
DataBuffer::DataBuffer():data(new char[1]), buffer_length(0), compresseddata(NULL), compressed_buffer_length(0),
 
59
        tempfilesize(0), dontsendbody(false), tempfilefd(-1), timeout(20), bytesalreadysent(0), preservetemp(false)
 
60
{
 
61
        data[0] = '\0';
 
62
}
 
63
 
 
64
DataBuffer::DataBuffer(const void* indata, off_t length):data(new char[length]), buffer_length(length), compresseddata(NULL), compressed_buffer_length(0),
 
65
        tempfilesize(0), dontsendbody(false), tempfilefd(-1), timeout(20), bytesalreadysent(0), preservetemp(false)
 
66
{
 
67
        memcpy(data, indata, length);
 
68
}
 
69
 
 
70
void DataBuffer::reset()
 
71
{
 
72
        delete[]data;
 
73
        data = new char[1];
 
74
        data[0] = '\0';
 
75
        delete[]compresseddata;
 
76
        compresseddata = NULL;
 
77
        buffer_length = 0;
 
78
        compressed_buffer_length = 0;
 
79
        if (tempfilefd > -1) {
 
80
                close(tempfilefd);
 
81
                if (!preservetemp) {
 
82
                        unlink(tempfilepath.toCharArray());
 
83
                }
 
84
                tempfilefd = -1;
 
85
                tempfilesize = 0;
 
86
        }
 
87
        bytesalreadysent = 0;
 
88
        dontsendbody = false;
 
89
        preservetemp = false;
 
90
        decompress = "";
 
91
}
 
92
 
 
93
// delete the memory block when the class is destroyed
 
94
DataBuffer::~DataBuffer()
 
95
{
 
96
        delete[]data;
 
97
        if (compresseddata != NULL) {
 
98
                delete[]compresseddata;
 
99
                compresseddata = NULL;
 
100
        }
 
101
        if (tempfilefd > -1) {
 
102
                close(tempfilefd);
 
103
                if (!preservetemp) {
 
104
                        unlink(tempfilepath.toCharArray());
 
105
                }
 
106
                tempfilefd = -1;
 
107
                tempfilesize = 0;
 
108
        }
 
109
}
 
110
 
 
111
// swap back to a compressed version of the data, if one exits
 
112
// also delete uncompressed version
 
113
// if body was decompressed but not modified, this can save bandwidth
 
114
void DataBuffer::swapbacktocompressed()
 
115
{
 
116
        if (compresseddata != NULL && compressed_buffer_length > 0) {
 
117
                delete[]data;
 
118
                buffer_length = compressed_buffer_length;
 
119
                data = compresseddata;
 
120
                compresseddata = NULL;
 
121
                compressed_buffer_length = 0;
 
122
        }       
 
123
}
 
124
 
 
125
// a much more efficient reader that does not assume the contents of
 
126
// the buffer gets filled thus reducing memcpy()ing and new()ing
 
127
int DataBuffer::bufferReadFromSocket(Socket * sock, char *buffer, int size, int sockettimeout)
 
128
{
 
129
        int pos = 0;
 
130
        int rc;
 
131
        while (pos < size) {
 
132
                rc = sock->readFromSocket(&buffer[pos], size - pos, 0, sockettimeout);
 
133
                if (rc < 1) {
 
134
                        // none recieved or an error
 
135
                        if (pos > 0) {
 
136
                                return pos;  // some was recieved previous into buffer
 
137
                        }
 
138
                        return rc;  // just return with the return code
 
139
                }
 
140
                pos += rc;
 
141
        }
 
142
        return size;  // full buffer
 
143
}
 
144
 
 
145
// a much more efficient reader that does not assume the contents of
 
146
// the buffer gets filled thus reducing memcpy()ing and new()ing.
 
147
// in addition to the actual socket timeout, used for each individual read, this version
 
148
// incorporates a "global" timeout within which all reads must complete.
 
149
int DataBuffer::bufferReadFromSocket(Socket * sock, char *buffer, int size, int sockettimeout, int timeout)
 
150
{
 
151
 
 
152
        int pos = 0;
 
153
        int rc;
 
154
        struct timeval starttime;
 
155
        struct timeval nowadays;
 
156
        gettimeofday(&starttime, NULL);
 
157
        while (pos < size) {
 
158
                rc = sock->readFromSocket(&buffer[pos], size - pos, 0, sockettimeout, false);
 
159
                if (rc < 1) {
 
160
                        // none recieved or an error
 
161
                        if (pos > 0) {
 
162
                                return pos;  // some was recieved previous into buffer
 
163
                        }
 
164
                        return rc;  // just return with the return code
 
165
                }
 
166
                pos += rc;
 
167
                gettimeofday(&nowadays, NULL);
 
168
                if (nowadays.tv_sec - starttime.tv_sec > timeout) {
 
169
#ifdef DGDEBUG
 
170
                        std::cout << "buffered socket read more than timeout" << std::endl;
 
171
#endif
 
172
                        return pos;  // just return how much got so far then
 
173
                }
 
174
        }
 
175
        return size;  // full buffer
 
176
}
 
177
 
 
178
// make a temp file and return its FD. only currently used in DM plugins.
 
179
int DataBuffer::getTempFileFD()
 
180
{
 
181
        if (tempfilefd > -1) {
 
182
                return tempfilefd;
 
183
        }
 
184
        tempfilepath = o.download_dir.c_str();
 
185
        tempfilepath += "/tfXXXXXX";
 
186
        char *tempfilepatharray = new char[tempfilepath.length() + 1];
 
187
        strcpy(tempfilepatharray, tempfilepath.toCharArray());
 
188
        if ((tempfilefd = mkstemp(tempfilepatharray)) < 0) {
 
189
#ifdef DGDEBUG
 
190
                std::cerr << "error creating temp " << tempfilepath << ": " << strerror(errno) << std::endl;
 
191
#endif
 
192
                syslog(LOG_ERR, "Could not create temp file to store download for scanning: %s", strerror(errno));
 
193
                tempfilefd = -1;
 
194
                tempfilepath = "";
 
195
        } else {
 
196
                tempfilepath = tempfilepatharray;
 
197
        }
 
198
        delete[]tempfilepatharray;
 
199
        return tempfilefd;
 
200
}
 
201
 
 
202
// check the client's user agent, see if we have a DM plugin compatible with it, and use it to download the body of the given request
 
203
bool DataBuffer::in(Socket * sock, Socket * peersock, HTTPHeader * requestheader, HTTPHeader * docheader, bool runav, int *headersent)
 
204
{
 
205
        //Socket *sock = where to read from
 
206
        //Socket *peersock = browser to send stuff to for keeping it alive
 
207
        //HTTPHeader *requestheader = header client used to request
 
208
        //HTTPHeader *docheader = header used for sending first line of reply
 
209
        //bool runav = to determine if limit is av or not
 
210
        //int *headersent = to use to send the first line of header if needed
 
211
        //                                or to mark that the header has already been sent
 
212
 
 
213
        // so we know if we only partially downloaded from
 
214
        // squid so later, if allowed, we can send the rest
 
215
        bool toobig = false;
 
216
 
 
217
        // match request to download manager so browsers potentially can have a prettier version
 
218
        // and software updates, stream clients, etc. can have a compatible version.
 
219
        int rc = 0;
 
220
# ifdef DGDEBUG
 
221
        int j = 0;
 
222
#endif
 
223
        for (std::deque<Plugin *>::iterator i = o.dmplugins_begin; i != o.dmplugins_end; i++) {
 
224
                if ((i + 1) == o.dmplugins_end) {
 
225
#ifdef DGDEBUG
 
226
                        std::cerr << "Got to final download manager so defaulting to always match." << std::endl;
 
227
#endif
 
228
                        dm_plugin = (DMPlugin*)(*i);
 
229
                        rc = dm_plugin->in(this, sock, peersock, requestheader, docheader, runav, headersent, &toobig);
 
230
                        break;
 
231
                } else {
 
232
                        if (((DMPlugin*)(*i))->willHandle(requestheader, docheader)) {
 
233
#ifdef DGDEBUG
 
234
                                std::cerr << "Matching download manager number: " << j << std::endl;
 
235
#endif
 
236
                                dm_plugin = (DMPlugin*)(*i);
 
237
                                rc = dm_plugin->in(this, sock, peersock, requestheader, docheader, runav, headersent, &toobig);
 
238
                                break;
 
239
                        }
 
240
                }
 
241
#ifdef DGDEBUG
 
242
                j++;
 
243
#endif
 
244
        }
 
245
        // we should check rc and log on error/warn
 
246
        // note for later - Tue 16th November 2004
 
247
        return toobig;
 
248
}
 
249
 
 
250
// send the request body to the client after having been handled by a DM plugin
 
251
void DataBuffer::out(Socket * sock) throw(exception)
 
252
{
 
253
        if (dontsendbody) {
 
254
#ifdef DGDEBUG
 
255
                std::cout << "dontsendbody true; not sending" << std::endl;
 
256
#endif
 
257
                return;
 
258
        }
 
259
        (*sock).readyForOutput(timeout);  // exceptions on timeout or error
 
260
 
 
261
        if (tempfilefd > -1) {
 
262
                // must have been too big for ram so stream from disk in blocks
 
263
#ifdef DGDEBUG
 
264
                std::cout << "Sending " << tempfilesize - bytesalreadysent << " bytes from temp file (" << bytesalreadysent << " already sent)" << std::endl;
 
265
#endif
 
266
                off_t sent = bytesalreadysent;
 
267
                int rc;
 
268
                lseek(tempfilefd, bytesalreadysent, SEEK_SET);
 
269
                while (sent < tempfilesize) {
 
270
                        rc = readEINTR(tempfilefd, data, buffer_length);
 
271
#ifdef DGDEBUG
 
272
                        std::cout << "reading temp file rc:" << rc << std::endl;
 
273
#endif
 
274
                        if (rc < 0) {
 
275
#ifdef DGDEBUG
 
276
                                std::cout << "error reading temp file so throwing exception" << std::endl;
 
277
#endif
 
278
                                throw exception();
 
279
                        }
 
280
                        if (rc == 0) {
 
281
#ifdef DGDEBUG
 
282
                                std::cout << "got zero bytes reading temp file" << std::endl;
 
283
#endif
 
284
                                break;  // should never happen
 
285
                        }
 
286
                        // as it's cached to disk the buffer must be reasonably big
 
287
                        if (!(*sock).writeToSocket(data, rc, 0, timeout)) {
 
288
                                throw exception();
 
289
                        }
 
290
                        sent += rc;
 
291
#ifdef DGDEBUG
 
292
                        std::cout << "total sent from temp:" << sent << std::endl;
 
293
#endif
 
294
                }
 
295
                close(tempfilefd);
 
296
                tempfilefd = -1;
 
297
                tempfilesize = 0;
 
298
                unlink(tempfilepath.toCharArray());
 
299
        } else {
 
300
#ifdef DGDEBUG
 
301
                std::cout << "Sending " << buffer_length - bytesalreadysent << " bytes from RAM (" << buffer_length << " in buffer; " << bytesalreadysent << " already sent)" << std::endl;
 
302
#endif
 
303
                // it's in RAM, so just send it, no streaming from disk
 
304
                if (buffer_length != 0) {
 
305
                        if (!(*sock).writeToSocket(data + bytesalreadysent, buffer_length - bytesalreadysent, 0, timeout))
 
306
                                throw exception();
 
307
                } else {
 
308
                        if (!sock->writeToSocket("\r\n\r\n", 4, 0, timeout))
 
309
                                throw exception();
 
310
                }
 
311
        }
 
312
}
 
313
 
 
314
// zlib decompression
 
315
void DataBuffer::zlibinflate(bool header)
 
316
{
 
317
        if (buffer_length < 12) {
 
318
                return;  // it can't possibly be zlib'd
 
319
        }
 
320
#ifdef DGDEBUG
 
321
        std::cout << "compressed size:" << buffer_length << std::endl;
 
322
#endif
 
323
 
 
324
#if ZLIB_VERNUM < 0x1210
 
325
#warning ************************************
 
326
#warning For gzip support you need zlib 1.2.1
 
327
#warning or later to be installed.
 
328
#warning You can ignore this warning but
 
329
#warning internet bandwidth may be wasted.
 
330
#warning ************************************
 
331
        if (header) {
 
332
                return;
 
333
        }
 
334
#endif
 
335
 
 
336
        int newsize = buffer_length * 5;  // good estimate of deflated HTML
 
337
 
 
338
        char *block = new char[newsize];
 
339
        char *temp = NULL;
 
340
        int err;
 
341
        off_t bytesgot = 0;
 
342
 
 
343
        z_stream d_stream;
 
344
        d_stream.zalloc = (alloc_func) 0;
 
345
        d_stream.zfree = (free_func) 0;
 
346
        d_stream.opaque = (voidpf) 0;
 
347
        d_stream.next_in = (Bytef *) data;
 
348
        d_stream.avail_in = buffer_length;
 
349
        d_stream.next_out = (Bytef *) block;
 
350
        d_stream.avail_out = newsize;
 
351
 
 
352
        // inflate either raw zlib, or possibly gzip with a header
 
353
        if (header) {
 
354
                err = inflateInit2(&d_stream, 15 + 32);
 
355
        } else {
 
356
                err = inflateInit2(&d_stream, -15);
 
357
        }
 
358
 
 
359
        if (err != Z_OK) {      // was a problem so just return
 
360
                delete[]block;  // don't forget to free claimed memory
 
361
#ifdef DGDEBUG
 
362
                std::cerr << "bad init inflate: " << err << std::endl;
 
363
#endif
 
364
                return;
 
365
        }
 
366
        while (true) {
 
367
#ifdef DGDEBUG
 
368
                std::cerr << "inflate loop" << std::endl;
 
369
#endif
 
370
                err = inflate(&d_stream, Z_SYNC_FLUSH);
 
371
                bytesgot = d_stream.total_out;
 
372
                if (err == Z_STREAM_END) {
 
373
                        err = inflateEnd(&d_stream);
 
374
                        if (err != Z_OK) {
 
375
                                delete[] block;
 
376
#ifdef DGDEBUG
 
377
                                std::cerr << "bad inflateEnd: " << d_stream.msg << std::endl;
 
378
#endif
 
379
                                return;
 
380
                        }
 
381
                        break;
 
382
                }
 
383
                if (err != Z_OK) {      // was a problem so just return
 
384
                        delete[]block;  // don't forget to free claimed memory
 
385
#ifdef DGDEBUG
 
386
                        std::cerr << "bad inflate: " << d_stream.msg << std::endl;
 
387
#endif
 
388
                        err = inflateEnd(&d_stream);
 
389
                        if (err != Z_OK) {
 
390
#ifdef DGDEBUG
 
391
                                std::cerr << "bad inflateEnd: " << d_stream.msg << std::endl;
 
392
#endif
 
393
                        }
 
394
                        return;
 
395
                }
 
396
                if (bytesgot > o.max_content_filter_size) {
 
397
                        delete[]block;  // don't forget to free claimed memory
 
398
#ifdef DGDEBUG
 
399
                        std::cerr << "inflated file larger than maxcontentfiltersize, not inflating further" << std::endl;
 
400
#endif
 
401
                        err = inflateEnd(&d_stream);
 
402
                        if (err != Z_OK) {
 
403
#ifdef DGDEBUG
 
404
                                std::cerr << "bad inflateEnd: " << d_stream.msg << std::endl;
 
405
#endif
 
406
                        }
 
407
                        return;
 
408
                }
 
409
 
 
410
                // inflation is going ok, but we don't have enough room in the output buffer
 
411
                newsize = bytesgot * 2;
 
412
                temp = new char[newsize];
 
413
                memcpy(temp, block, bytesgot);
 
414
                delete[]block;
 
415
                block = temp;
 
416
                temp = NULL;
 
417
 
 
418
                d_stream.next_out = (Bytef *) (block + bytesgot);
 
419
                d_stream.avail_out = newsize - bytesgot;
 
420
        }
 
421
        compresseddata = data;
 
422
        compressed_buffer_length = buffer_length;
 
423
        buffer_length = bytesgot;
 
424
#ifdef DGDEBUG
 
425
        std::cout << "decompressed size: " << buffer_length << std::endl;
 
426
#endif
 
427
        data = new char[bytesgot+1];
 
428
        data[bytesgot] = '\0';
 
429
        memcpy(data, block, bytesgot);
 
430
        delete[] block;
 
431
}
 
432
 
 
433
// Does a regexp search and replace.
 
434
        typedef struct newreplacement
 
435
        {
 
436
                int match;
 
437
                String replacement;
 
438
        };
 
439
bool DataBuffer::contentRegExp(int filtergroup)
 
440
{
 
441
 
 
442
#ifdef DGDEBUG
 
443
        std::cout << "Starting content reg exp replace" << std::endl;
 
444
#endif
 
445
        bool contentmodified = false;
 
446
        unsigned int i;
 
447
        unsigned int j, k, m;
 
448
        unsigned int s = (*o.fg[filtergroup]).content_regexp_list_comp.size();
 
449
        unsigned int matches;
 
450
        unsigned int submatch, submatches;
 
451
        RegExp *re;
 
452
        String *replacement;
 
453
        unsigned int replen;
 
454
        int sizediff;
 
455
        char *newblock;
 
456
        char *dstpos;
 
457
        unsigned int srcoff;
 
458
        unsigned int nextoffset;
 
459
        unsigned int matchlen;
 
460
 
 
461
        std::queue<newreplacement*> matchqueue;
 
462
 
 
463
        for (i = 0; i < s; i++) {
 
464
                re = &((*o.fg[filtergroup]).content_regexp_list_comp[i]);
 
465
                if (re->match(data)) {
 
466
                        replacement = &((*o.fg[filtergroup]).content_regexp_list_rep[i]);
 
467
                        //replen = replacement->length();
 
468
                        matches = re->numberOfMatches();
 
469
 
 
470
                        sizediff = 0;
 
471
                        m = 0;
 
472
                        for (j = 0; j < matches; j++) {
 
473
                                srcoff = re->offset(j);
 
474
                                matchlen = re->length(j);
 
475
 
 
476
                                // Count matches for ()'s
 
477
                                for (submatches = 0; j+submatches+1 < matches; submatches++)
 
478
                                        if (re->offset(j+submatches+1) + re->length(j+submatches+1) > srcoff + matchlen)
 
479
                                                break;
 
480
 
 
481
                                // \1 and $1 replacement
 
482
                                
 
483
                                // store match no. and default (empty) replacement string
 
484
                                newreplacement* newrep = new newreplacement;
 
485
                                newrep->match = j;
 
486
                                newrep->replacement = "";
 
487
                                // iterate over regex's replacement string
 
488
                                for (k = 0; k < replacement->length(); k++) {
 
489
                                        // look for \1..\9 and $1..$9
 
490
                                        if (((*replacement)[k] == '\\' || (*replacement)[k] == '$') && (*replacement)[k+1] >= '1' && (*replacement)[k+1] <= '9') {
 
491
                                                // determine match number
 
492
                                                submatch = (*replacement)[++k] - '0';
 
493
                                                // add submatch contents to replacement string
 
494
                                                if (submatch <= submatches) {
 
495
                                                        newrep->replacement += re->result(j + submatch).c_str();
 
496
                                                }
 
497
                                        } else {
 
498
                                                // unescape \\ and \$, and add other non-backreference characters
 
499
                                                if ((*replacement)[k] == '\\' && ((*replacement)[k+1] == '\\' || (*replacement)[k+1] == '$'))
 
500
                                                        k++;
 
501
                                                newrep->replacement += replacement->subString(k, 1);
 
502
                                        }
 
503
                                }
 
504
                                matchqueue.push(newrep);
 
505
 
 
506
                                // update size difference between original and modified content
 
507
                                sizediff -= re->length(j);
 
508
                                sizediff += newrep->replacement.length();
 
509
                                // skip submatches to next top level match
 
510
                                j += submatches;
 
511
                                m++;
 
512
                        }
 
513
 
 
514
                        // now we know eventual size of content-replaced block, allocate memory for it
 
515
                        newblock = new char[buffer_length + sizediff + 1];
 
516
                        newblock[buffer_length + sizediff] = '\0';
 
517
                        srcoff = 0;
 
518
                        dstpos = newblock;
 
519
                        matches = m;
 
520
 
 
521
#ifdef DGDEBUG
 
522
                        std::cout << "content matches:" << matches << std::endl;
 
523
#endif
 
524
                        // replace top-level matches using filled-out replacement strings
 
525
                        newreplacement* newrep;
 
526
                        for (j = 0; j < matches; j++) {
 
527
                                newrep = matchqueue.front();
 
528
                                nextoffset = re->offset(newrep->match);
 
529
                                if (nextoffset > srcoff) {
 
530
                                        memcpy(dstpos, data + srcoff, nextoffset - srcoff);
 
531
                                        dstpos += nextoffset - srcoff;
 
532
                                        srcoff = nextoffset;
 
533
                                }
 
534
                                replen = newrep->replacement.length();
 
535
                                memcpy(dstpos, newrep->replacement.toCharArray(), replen);
 
536
                                dstpos += replen;
 
537
                                srcoff += re->length(newrep->match);
 
538
                                delete newrep;
 
539
                                matchqueue.pop();
 
540
                        }
 
541
                        if (srcoff < buffer_length) {
 
542
                                memcpy(dstpos, data + srcoff, buffer_length - srcoff);
 
543
                        }
 
544
                        delete[]data;
 
545
                        data = newblock;
 
546
                        buffer_length = buffer_length + sizediff;
 
547
                        contentmodified = true;
 
548
                }
 
549
        }
 
550
        return contentmodified;
 
551
}