~louis/ubuntu/trusty/clamav/lp799623_fix_logrotate

« back to all changes in this revision

Viewing changes to libclamav/mbox.c

  • Committer: Bazaar Package Importer
  • Author(s): Scott Kitterman
  • Date: 2010-03-12 11:30:04 UTC
  • mfrom: (0.41.1 upstream)
  • Revision ID: james.westby@ubuntu.com-20100312113004-b0fop4bkycszdd0z
Tags: 0.96~rc1+dfsg-0ubuntu1
* New upstream RC - FFE (LP: #537636):
  - Add OfficialDatabaseOnly option to clamav-base.postinst.in
  - Add LocalSocketGroup option to clamav-base.postinst.in
  - Add LocalSocketMode option to clamav-base.postinst.in
  - Add CrossFilesystems option to clamav-base.postinst.in
  - Add ClamukoScannerCount option to clamav-base.postinst.in
  - Add BytecodeSecurity opiton to clamav-base.postinst.in
  - Add DetectionStatsHostID option to clamav-freshclam.postinst.in
  - Add Bytecode option to clamav-freshclam.postinst.in
  - Add MilterSocketGroup option to clamav-milter.postinst.in
  - Add MilterSocketMode option to clamav-milter.postinst.in
  - Add ReportHostname option to clamav-milter.postinst.in
  - Bump libclamav SO version to 6.1.0 in libclamav6.install
  - Drop clamdmon from clamav.examples (no longer shipped by upstream)
  - Drop libclamav.a from libclamav-dev.install (not built by upstream)
  - Update SO version for lintian override for libclamav6
  - Add new Bytecode Testing Tool, usr/bin/clambc, to clamav.install
  - Add build-depends on python and python-setuptools for new test suite
  - Update debian/copyright for the embedded copy of llvm (using the system
    llvm is not currently feasible)

Show diffs side-by-side

added added

removed removed

Lines of Context:
20
20
 
21
21
static  char    const   rcsid[] = "$Id: mbox.c,v 1.381 2007/02/15 12:26:44 njh Exp $";
22
22
 
23
 
#ifdef  _MSC_VER
24
 
#include <winsock.h>    /* only needed in CL_EXPERIMENTAL */
25
 
#endif
26
 
 
27
23
#if HAVE_CONFIG_H
28
24
#include "clamav-config.h"
29
25
#endif
54
50
#include <sys/param.h>
55
51
#endif
56
52
#include "clamav.h"
57
 
#ifndef C_WINDOWS
58
53
#include <dirent.h>
59
 
#endif
60
54
#include <limits.h>
61
55
#include <signal.h>
62
56
 
78
72
#include "mbox.h"
79
73
#include "dconf.h"
80
74
#include "md5.h"
 
75
#include "fmap.h"
81
76
 
82
77
#define DCONF_PHISHING mctx->ctx->dconf->phishing
83
78
 
137
132
 
138
133
#define SAVE_TO_DISC    /* multipart/message are saved in a temporary file */
139
134
 
140
 
#define FOLLOWURLS      5       /*
141
 
                                 * Maximum number of URLs scanned in a message
142
 
                                 * part. Helps to prevent Dialer.gen-45 and
143
 
                                 * Trojan.WinREG.Zapchast which are often
144
 
                                 * dispatched by emails which point to it. If
145
 
                                 * not defined, don't check any URLs
146
 
                                 * It is also used to indicate the number of
147
 
                                 * 301/302 redirects we wish to follow
148
 
                                 */
149
 
 
150
135
#include "htmlnorm.h"
151
136
 
152
137
#include "phishcheck.h"
153
138
 
154
 
#ifndef C_WINDOWS
 
139
#ifndef _WIN32
 
140
#include <sys/time.h>
155
141
#include <netdb.h>
156
142
#include <sys/socket.h>
157
143
#include <netinet/in.h>
161
147
#endif
162
148
#endif
163
149
 
164
 
#ifndef C_WINDOWS
165
 
#define closesocket(s)  close(s)
166
 
#define SOCKET  int
167
 
#endif
168
 
 
169
150
#include <fcntl.h>
170
 
#ifndef C_WINDOWS
171
 
#include <sys/time.h>
172
 
#endif
173
 
 
174
 
#ifndef HAVE_IN_PORT_T
175
 
typedef unsigned        short   in_port_t;
176
 
#endif
177
 
 
178
 
#ifndef HAVE_IN_ADDR_T
179
 
typedef unsigned        int     in_addr_t;
180
 
#endif
181
 
 
182
 
#if     (!defined(EALREADY)) && (defined(WSAEALREADY))
183
 
#define EALREADY        WSAEALREADY
184
 
#endif
185
 
#if     (!defined(EINPROGRESS)) && (defined(WSAEINPROGRESS))
186
 
#define EINPROGRESS     WSAEINPROGRESS
187
 
#endif
188
 
#if     (!defined(EISCONN)) && (defined(WSAEISCONN))
189
 
#define EISCONN WSAEISCONN
190
 
#endif
191
151
 
192
152
/*
193
153
 * Use CL_SCAN_PARTIAL_MESSAGE to handle messages covered by section 7.3.2 of RFC1341.
227
187
#endif
228
188
 
229
189
static  int     cli_parse_mbox(const char *dir, int desc, cli_ctx *ctx);
230
 
static  message *parseEmailFile(FILE *fin, const table_t *rfc821Table, const char *firstLine, const char *dir);
 
190
static  message *parseEmailFile(fmap_t *map, size_t *at, const table_t *rfc821Table, const char *firstLine, const char *dir);
231
191
static  message *parseEmailHeaders(message *m, const table_t *rfc821Table);
232
192
static  int     parseEmailHeader(message *m, const char *line, const table_t *rfc821Table);
233
193
static  mbox_status     parseEmailBody(message *messageIn, text *textIn, mbox_ctx *mctx, unsigned int recursion_level);
242
202
static  char    *rfc822comments(const char *in, char *out);
243
203
static  int     rfc1341(message *m, const char *dir);
244
204
static  bool    usefulHeader(int commandNumber, const char *cmd);
245
 
static  char    *getline_from_mbox(char *buffer, size_t len, FILE *fin);
 
205
static  char    *getline_from_mbox(char *buffer, size_t len, fmap_t *map, size_t *at);
246
206
static  bool    isBounceStart(mbox_ctx *mctx, const char *line);
247
207
static  bool    exportBinhexMessage(mbox_ctx *mctx, message *m);
248
208
static  int     exportBounceMessage(mbox_ctx *ctx, text *start);
254
214
static  blob    *getHrefs(message *m, tag_arguments_t *hrefs);
255
215
static  void    hrefs_done(blob *b, tag_arguments_t *hrefs);
256
216
static  void    checkURLs(message *m, mbox_ctx *mctx, mbox_status *rc, int is_html);
257
 
static  void    do_checkURLs(mbox_ctx *mctx, tag_arguments_t *hrefs);
258
 
 
259
 
#if     defined(FOLLOWURLS) && (FOLLOWURLS > 0)
260
 
struct arg {
261
 
        char *url;
262
 
        const char *dir;
263
 
        char *filename;
264
 
        int     depth;
265
 
};
266
 
#define CONNECT_TIMEOUT 5       /* Allow 5 seconds to connect */
267
 
#ifdef  CL_THREAD_SAFE
268
 
static  void    *getURL(void *a);
269
 
#else
270
 
static  void    *getURL(struct arg *arg);
271
 
#endif
272
 
static  int     nonblock_connect(SOCKET sock, const struct sockaddr_in *sin, const char *hostname);
273
 
static  int     connect_error(SOCKET sock, const char *hostname);
274
 
static  int     my_r_gethostbyname(const char *hostname, struct hostent *hp, char *buf, size_t len);
275
 
 
276
 
#define NONBLOCK_SELECT_MAX_FAILURES    3
277
 
#define NONBLOCK_MAX_ATTEMPTS   10
278
 
 
279
 
#endif
280
217
 
281
218
/* Maximum line length according to RFC2821 */
282
219
#define RFC2821LENGTH   1000
364
301
static  pthread_mutex_t tables_mutex = PTHREAD_MUTEX_INITIALIZER;
365
302
#endif
366
303
 
367
 
#ifndef O_BINARY
368
 
#define O_BINARY        0
369
 
#endif
370
 
 
371
 
#ifdef  NEW_WORLD
372
 
 
373
 
#include "matcher.h"
374
 
 
375
 
#undef  PARTIAL_DIR
376
 
 
377
 
#if HAVE_MMAP
378
 
#if HAVE_SYS_MMAN_H
379
 
#include <sys/mman.h>
380
 
#else /* HAVE_SYS_MMAN_H */
381
 
#undef HAVE_MMAP
382
 
#endif
383
 
#else   /*HAVE_MMAP*/
384
 
#undef  NEW_WORLD
385
 
#endif
386
 
#endif
387
 
 
388
 
#ifdef  NEW_WORLD
389
 
/*
390
 
 * Files larger than this are scanned with the old method, should be
391
 
 *      StreamMaxLength, I guess
392
 
 * If NW_MAX_FILE_SIZE is not defined, all files go through the
393
 
 *      new method. This definition is for machines very tight on RAM, or
394
 
 *      with large StreamMaxLength values
395
 
 */
396
 
#define MAX_ALLOCATION  134217728       /* see libclamav/others.c */
397
 
#define NW_MAX_FILE_SIZE        MAX_ALLOCATION
398
 
 
399
 
struct scanlist {
400
 
        const   char    *start;
401
 
        size_t  size;
402
 
        encoding_type   decoder;        /* only BASE64 and QUOTEDPRINTABLE for now */
403
 
        struct  scanlist *next;
404
 
};
405
 
 
406
 
static struct map {
407
 
        const   char    *offset;        /* sorted */
408
 
        const   char    *word;
409
 
        struct  map     *next;
410
 
} *map, *tail;
411
 
 
412
 
static  int     save_text(cli_ctx *ctx, const char *dir, const char *start, size_t len);
413
 
static  void    create_map(const char *begin, const char *end);
414
 
static  void    add_to_map(const char *offset, const char *word);
415
 
static  const   char    *find_in_map(const char *offset, const char *word);
416
 
static  void    free_map(void);
417
 
 
418
 
/*
419
 
 * This could be the future. Instead of parsing and decoding it just decodes.
420
 
 *
421
 
 * USE IT AT YOUR PERIL, a large number of viruses are not detected with this
422
 
 * method, possibly because the decoded files must be exact and not have
423
 
 * extra data at the start or end, which this code will produce.
424
 
 *
425
 
 * Currently only supports base64 and quoted-printable
426
 
 *
427
 
 * You may also see a lot of warnings. For the moment it falls back to old
428
 
 *      world mode if it doesn't know what to do - that'll be removed.
429
 
 * The code is untidy...
430
 
 *
431
 
 * FIXME: Some mailbox scans are slower with this method. I suspect that it's
432
 
 * because the scan can proceed to the end of the file rather than the end
433
 
 * of the attachment which can mean than later emails are scanned many times
434
 
 *
435
 
 * FIXME: quoted printable doesn't know when to stop, so size related virus
436
 
 *      matching breaks
437
 
 *
438
 
 * TODO: Fall through to cli_parse_mbox() too often
439
 
 *
440
 
 * TODO: Add support for systems without mmap()
441
 
 *
442
 
 * TODO: partial_dir fall through
443
 
 *
444
 
 * FIXME: Some EICAR gets through
445
 
 */
446
 
int
447
 
cli_mbox(const char *dir, int desc, cli_ctx *ctx)
448
 
{
449
 
        char *start, *ptr, *line;
450
 
        const char *last, *p, *q;
451
 
        size_t size;
452
 
        struct stat statb;
453
 
        message *m;
454
 
        fileblob *fb;
455
 
        int ret = CL_CLEAN;
456
 
        int wasAlloced;
457
 
        struct scanlist *scanlist, *scanelem;
458
 
 
459
 
        if(dir == NULL) {
460
 
                cli_dbgmsg("cli_mbox called with NULL dir\n");
461
 
                return CL_ENULLARG;
462
 
        }
463
 
        if(fstat(desc, &statb) < 0)
464
 
                return CL_EOPEN;
465
 
 
466
 
        size = statb.st_size;
467
 
 
468
 
        if(size == 0)
469
 
                return CL_CLEAN;
470
 
 
471
 
#ifdef  NW_MAX_FILE_SIZE
472
 
        if(size > NW_MAX_FILE_SIZE)
473
 
                return cli_parse_mbox(dir, desc, ctx);
474
 
#endif
475
 
 
476
 
        /*cli_warnmsg("NEW_WORLD is new code - use at your own risk.\n");*/
477
 
#ifdef  PARTIAL_DIR
478
 
        cli_warnmsg("PARTIAL_DIR doesn't work in the NEW_WORLD yet\n");
479
 
#endif
480
 
 
481
 
        start = mmap(NULL, size, PROT_READ, MAP_PRIVATE, desc, 0);
482
 
        if(start == MAP_FAILED)
483
 
                return CL_EMAP;
484
 
 
485
 
        cli_dbgmsg("mmap'ed mbox\n");
486
 
 
487
 
        ptr = cli_malloc(size);
488
 
        if(ptr) {
489
 
                memcpy(ptr, start, size);
490
 
                munmap(start, size);
491
 
                start = ptr;
492
 
                wasAlloced = 1;
493
 
        } else
494
 
                wasAlloced = 0;
495
 
 
496
 
        /* last points to the last *valid* address in the array */
497
 
        last = &start[size - 1];
498
 
 
499
 
        create_map(start, last);
500
 
 
501
 
        scanelem = scanlist = NULL;
502
 
        q = start;
503
 
        /*
504
 
         * FIXME: mismatch of const char * and char * here and in later calls
505
 
         *      to find_in_map()
506
 
         */
507
 
        while((p = find_in_map(q, "base64")) != NULL) {
508
 
                cli_dbgmsg("Found base64\n");
509
 
                if(scanelem) {
510
 
                        scanelem->next = cli_malloc(sizeof(struct scanlist));
511
 
                        scanelem = scanelem->next;
512
 
                } else
513
 
                        scanlist = scanelem = cli_malloc(sizeof(struct scanlist));
514
 
                scanelem->next = NULL;
515
 
                scanelem->decoder = BASE64;
516
 
                q = scanelem->start = &p[6];
517
 
                if(((p = find_in_map(q, "\nFrom ")) != NULL) ||
518
 
                   ((p = find_in_map(q, "base64")) != NULL) ||
519
 
                   ((p = find_in_map(q, "quoted-printable")) != NULL)) {
520
 
                        scanelem->size = (size_t)(p - q);
521
 
                        q = p;
522
 
                } else {
523
 
                        scanelem->size = (size_t)(last - scanelem->start) + 1;
524
 
                        break;
525
 
                }
526
 
                cli_dbgmsg("base64: last %u q %u\n", (unsigned int)last, (unsigned int)q);
527
 
                assert(scanelem->size <= size);
528
 
        }
529
 
 
530
 
        q = start;
531
 
        while((p = find_in_map(q, "quoted-printable")) != NULL) {
532
 
                if(p != q)
533
 
                        switch(p[-1]) {
534
 
                                case ' ':
535
 
                                case ':':
536
 
                                case '=':       /* wrong but allow it */
537
 
                                        break;
538
 
                                default:
539
 
                                        q = &p[16];
540
 
                                        cli_dbgmsg("Ignore quoted-printable false positive\n");
541
 
                                        continue;       /* false positive */
542
 
                        }
543
 
 
544
 
                cli_dbgmsg("Found quoted-printable\n");
545
 
#ifdef  notdef
546
 
                /*
547
 
                 * The problem with quoted printable is recognising when to stop
548
 
                 * parsing
549
 
                 */
550
 
                if(scanelem) {
551
 
                        scanelem->next = cli_malloc(sizeof(struct scanlist));
552
 
                        scanelem = scanelem->next;
553
 
                } else
554
 
                        scanlist = scanelem = cli_malloc(sizeof(struct scanlist));
555
 
                scanelem->next = NULL;
556
 
                scanelem->decoder = QUOTEDPRINTABLE;
557
 
                q = scanelem->start = &p[16];
558
 
                cli_dbgmsg("qp: last %u q %u\n", (unsigned int)last, (unsigned int)q);
559
 
                if(((p = find_in_map(q, "\nFrom ")) != NULL) ||
560
 
                   ((p = find_in_map(q, "quoted-printable")) != NULL) ||
561
 
                   ((p = find_in_map(q, "base64")) != NULL)) {
562
 
                        scanelem->size = (size_t)(p - q);
563
 
                        q = p;
564
 
                        cli_dbgmsg("qp: scanelem->size = %u\n", scanelem->size);
565
 
                } else {
566
 
                        scanelem->size = (size_t)(last - scanelem->start) + 1;
567
 
                        break;
568
 
                }
569
 
                assert(scanelem->size <= size);
570
 
#else
571
 
                if(wasAlloced)
572
 
                        free(start);
573
 
                else
574
 
                        munmap(start, size);
575
 
 
576
 
                free_map();
577
 
                return cli_parse_mbox(dir, desc, ctx);
578
 
#endif
579
 
        }
580
 
 
581
 
        if(scanlist == NULL) {
582
 
                const struct tableinit *tableinit;
583
 
                bool anyHeadersFound = FALSE;
584
 
                bool hasuuencode = FALSE;
585
 
                cli_file_t type;
586
 
 
587
 
                /* FIXME: message: There could of course be no decoder needed... */
588
 
                for(tableinit = rfc821headers; tableinit->key; tableinit++)
589
 
                        if(find_in_map(start, tableinit->key)) {
590
 
                                anyHeadersFound = TRUE;
591
 
                                break;
592
 
                        }
593
 
 
594
 
                if((!anyHeadersFound) &&
595
 
                   ((p = find_in_map(start, "\nbegin ")) != NULL) &&
596
 
                   (isuuencodebegin(++p)))
597
 
                        /* uuencoded part */
598
 
                        hasuuencode = TRUE;
599
 
                else {
600
 
                        cli_dbgmsg("Nothing encoded, looking for a text part to save\n");
601
 
                        ret = save_text(ctx, dir, start, size);
602
 
                        if(wasAlloced)
603
 
                                free(start);
604
 
                        else
605
 
                                munmap(start, size);
606
 
 
607
 
                        free_map();
608
 
                        if(ret != CL_EFORMAT)
609
 
                                return ret;
610
 
                        ret = CL_CLEAN;
611
 
                }
612
 
 
613
 
                free_map();
614
 
 
615
 
                type = cli_filetype(start, size, ctx->engine);
616
 
 
617
 
                if((type == CL_TYPE_TEXT_ASCII) &&
618
 
                   (strncmp(start, "Microsoft Mail Internet Headers", 31) == 0))
619
 
                        type = CL_TYPE_MAIL;
620
 
 
621
 
                if(wasAlloced)
622
 
                        free(start);
623
 
                else
624
 
                        munmap(start, size);
625
 
 
626
 
                if(anyHeadersFound || hasuuencode) {
627
 
                        /* TODO: reduce the number of falls through here */
628
 
                        if(hasuuencode)
629
 
                                /* TODO: fast track visa */
630
 
                                cli_dbgmsg("New world - fall back to old uudecoder\n");
631
 
                        else
632
 
                                cli_dbgmsg("cli_mbox: unknown encoder, type %d\n", type);
633
 
                        if(type == CL_TYPE_MAIL)
634
 
                                return cli_parse_mbox(dir, desc, ctx);
635
 
                        cli_dbgmsg("Unknown filetype %d, return CLEAN\n", type);
636
 
                        return CL_CLEAN;
637
 
                }
638
 
 
639
 
#if     0       /* I don't believe this is needed any more */
640
 
                /*
641
 
                 * The message could be a plain text phish
642
 
                 * FIXME: Can't get to the option whether we are looking for
643
 
                 *      phishes or not, so assume we are, this slows things a
644
 
                 *      lot
645
 
                 * Should be
646
 
                 *      if((type == CL_TYPE_MAIL) && (!(no-phishing))
647
 
                 */
648
 
                if(type == CL_TYPE_MAIL)
649
 
                        return cli_parse_mbox(dir, desc, ctx);
650
 
#endif
651
 
                cli_dbgmsg("cli_mbox: I believe it's plain text (type == %d) which must be clean\n",
652
 
                        type);
653
 
                return CL_CLEAN;
654
 
        }
655
 
#if     0
656
 
        if(wasAlloced) {
657
 
                const char *max = NULL;
658
 
 
659
 
                for(scanelem = scanlist; scanelem; scanelem = scanelem->next) {
660
 
                        const char *end = &scanelem->start[scanelem->size];
661
 
 
662
 
                        if(end > max)
663
 
                                max = end;
664
 
                }
665
 
 
666
 
                if(max < last)
667
 
                        printf("could free %d bytes\n", (int)(last - max));
668
 
        }
669
 
#endif
670
 
 
671
 
        for(scanelem = scanlist; scanelem; scanelem = scanelem->next) {
672
 
                if(scanelem->decoder == BASE64) {
673
 
                        const char *b64start = scanelem->start;
674
 
                        size_t b64size = scanelem->size;
675
 
 
676
 
                        cli_dbgmsg("b64size = %lu\n", b64size);
677
 
                        while((*b64start != '\n') && (*b64start != '\r')) {
678
 
                                b64start++;
679
 
                                b64size--;
680
 
                        }
681
 
                        /*
682
 
                         * Look for the end of the headers
683
 
                         */
684
 
                        while(b64start < last) {
685
 
                                if(*b64start == ';') {
686
 
                                        b64start++;
687
 
                                        b64size--;
688
 
                                } else if((memcmp(b64start, "\n\n", 2) == 0) ||
689
 
                                          (memcmp(b64start, "\r\r", 2) == 0)) {
690
 
                                        b64start += 2;
691
 
                                        b64size -= 2;
692
 
                                        break;
693
 
                                } else if(memcmp(b64start, "\r\n\r\n", 4) == 0) {
694
 
                                        b64start += 4;
695
 
                                        b64size -= 4;
696
 
                                        break;
697
 
                                } else if(memcmp(b64start, "\n \n", 3) == 0) {
698
 
                                        /*
699
 
                                         * Some viruses are broken and have
700
 
                                         * one space character at the end of
701
 
                                         * the headers
702
 
                                         */
703
 
                                        b64start += 3;
704
 
                                        b64size -= 3;
705
 
                                        break;
706
 
                                } else if(memcmp(b64start, "\r\n \r\n", 5) == 0) {
707
 
                                        /*
708
 
                                         * Some viruses are broken and have
709
 
                                         * one space character at the end of
710
 
                                         * the headers
711
 
                                         */
712
 
                                        b64start += 5;
713
 
                                        b64size -= 5;
714
 
                                        break;
715
 
                                }
716
 
                                b64start++;
717
 
                                b64size--;
718
 
                        }
719
 
 
720
 
                        if(b64size > 0L)
721
 
                                while((!isalnum(*b64start)) && (*b64start != '/')) {
722
 
                                        if(b64size-- == 0L)
723
 
                                                break;
724
 
                                        b64start++;
725
 
                                }
726
 
 
727
 
                        if(b64size > 0L) {
728
 
                                int lastline;
729
 
                                char *tmpfilename;
730
 
                                unsigned char *uptr;
731
 
 
732
 
                                cli_dbgmsg("cli_mbox: decoding %ld base64 bytes\n", b64size);
733
 
                                if((fb = fileblobCreate()) == NULL) {
734
 
                                        free_map();
735
 
                                        if(wasAlloced)
736
 
                                                free(start);
737
 
                                        else
738
 
                                                munmap(start, size);
739
 
 
740
 
                                        return CL_EMEM;
741
 
                                }
742
 
 
743
 
                                tmpfilename = cli_gentemp(dir);
744
 
                                if(tmpfilename == NULL) {
745
 
                                        free_map();
746
 
                                        if(wasAlloced)
747
 
                                                free(start);
748
 
                                        else
749
 
                                                munmap(start, size);
750
 
                                        fileblobDestroy(fb);
751
 
 
752
 
                                        return CL_EMEM;
753
 
                                }
754
 
                                fileblobSetFilename(fb, dir, tmpfilename);
755
 
                                free(tmpfilename);
756
 
 
757
 
                                line = NULL;
758
 
 
759
 
                                m = messageCreate();
760
 
                                if(m == NULL) {
761
 
                                        free_map();
762
 
                                        if(wasAlloced)
763
 
                                                free(start);
764
 
                                        else
765
 
                                                munmap(start, size);
766
 
                                        fileblobDestroy(fb);
767
 
 
768
 
                                        return CL_EMEM;
769
 
                                }
770
 
                                messageSetEncoding(m, "base64");
771
 
 
772
 
                                messageSetCTX(m, ctx);
773
 
                                fileblobSetCTX(fb, ctx);
774
 
 
775
 
                                lastline = 0;
776
 
                                do {
777
 
                                        int length = 0, datalen;
778
 
                                        char *newline, *equal;
779
 
                                        unsigned char *bigbuf, *data;
780
 
                                        unsigned char smallbuf[1024];
781
 
                                        const char *cptr;
782
 
 
783
 
                                        /*printf("%ld: ", b64size); fflush(stdout);*/
784
 
 
785
 
                                        for(cptr = b64start; b64size && (*cptr != '\n') && (*cptr != '\r'); cptr++) {
786
 
                                                length++;
787
 
                                                --b64size;
788
 
                                        }
789
 
 
790
 
                                        /*printf("%d: ", length); fflush(stdout);*/
791
 
 
792
 
                                        newline = cli_realloc(line, length + 1);
793
 
                                        if(newline == NULL)
794
 
                                                break;
795
 
                                        line = newline;
796
 
 
797
 
                                        memcpy(line, b64start, length);
798
 
                                        line[length] = '\0';
799
 
 
800
 
                                        equal = strchr(line, '=');
801
 
                                        if(equal) {
802
 
                                                lastline++;
803
 
                                                *equal = '\0';
804
 
                                        }
805
 
                                        /*puts(line);*/
806
 
 
807
 
#if     0
808
 
                                        if(messageAddStr(m, line) < 0)
809
 
                                                break;
810
 
#endif
811
 
                                        if(length >= (int)sizeof(smallbuf)) {
812
 
                                                datalen = length + 2;
813
 
                                                data = bigbuf = cli_malloc(datalen);
814
 
                                                if(data == NULL)
815
 
                                                        break;
816
 
                                        } else {
817
 
                                                bigbuf = NULL;
818
 
                                                data = smallbuf;
819
 
                                                datalen = sizeof(data) - 1;
820
 
                                        }
821
 
                                        uptr = decodeLine(m, BASE64, line, data, datalen);
822
 
 
823
 
                                        if(uptr == NULL) {
824
 
                                                if(bigbuf)
825
 
                                                        free(bigbuf);
826
 
                                                break;
827
 
                                        }
828
 
                                        /*cli_dbgmsg("base64: write %u bytes\n", (size_t)(uptr - data));*/
829
 
                                        datalen = fileblobAddData(fb, data, (size_t)(uptr - data));
830
 
                                        if(bigbuf)
831
 
                                                free(bigbuf);
832
 
 
833
 
                                        if(datalen < 0)
834
 
                                                break;
835
 
                                        if(fileblobContainsVirus(fb))
836
 
                                                break;
837
 
 
838
 
                                        if((b64size > 0) && (*cptr == '\r')) {
839
 
                                                b64start = ++cptr;
840
 
                                                --b64size;
841
 
                                        }
842
 
                                        if((b64size > 0) && (*cptr == '\n')) {
843
 
                                                b64start = ++cptr;
844
 
                                                --b64size;
845
 
                                        }
846
 
                                        if(lastline)
847
 
                                                break;
848
 
                                } while(b64size > 0L);
849
 
 
850
 
                                if(m->base64chars) {
851
 
                                        unsigned char data[4];
852
 
 
853
 
                                        uptr = base64Flush(m, data);
854
 
                                        if(uptr) {
855
 
                                                /*cli_dbgmsg("base64: flush %u bytes\n", (size_t)(uptr - data));*/
856
 
                                                (void)fileblobAddData(fb, data, (size_t)(uptr - data));
857
 
                                        }
858
 
                                }
859
 
                                if(fb)
860
 
                                        fileblobDestroy(fb);
861
 
                                else
862
 
                                        ret = -1;
863
 
 
864
 
                                messageDestroy(m);
865
 
                                free(line);
866
 
                        }
867
 
                } else if(scanelem->decoder == QUOTEDPRINTABLE) {
868
 
                        const char *quotedstart = scanelem->start;
869
 
                        size_t quotedsize = scanelem->size;
870
 
 
871
 
                        cli_dbgmsg("quotedsize = %lu\n", quotedsize);
872
 
                        while(*quotedstart != '\n') {
873
 
                                quotedstart++;
874
 
                                quotedsize--;
875
 
                        }
876
 
                        /*
877
 
                         * Look for the end of the headers
878
 
                         */
879
 
                        while(quotedstart < last) {
880
 
                                if(*quotedstart == ';') {
881
 
                                        quotedstart++;
882
 
                                        quotedsize--;
883
 
                                } else if((*quotedstart == '\n') || (*quotedstart == '\r')) {
884
 
                                        quotedstart++;
885
 
                                        quotedsize--;
886
 
                                        if((*quotedstart == '\n') || (*quotedstart == '\r')) {
887
 
                                                quotedstart++;
888
 
                                                quotedsize--;
889
 
                                                break;
890
 
                                        }
891
 
                                }
892
 
                                quotedstart++;
893
 
                                quotedsize--;
894
 
                        }
895
 
 
896
 
                        while(!isalnum(*quotedstart)) {
897
 
                                quotedstart++;
898
 
                                quotedsize--;
899
 
                        }
900
 
 
901
 
                        if(quotedsize > 0L) {
902
 
                                cli_dbgmsg("cli_mbox: decoding %ld quoted-printable bytes\n", quotedsize);
903
 
 
904
 
                                m = messageCreate();
905
 
                                if(m == NULL) {
906
 
                                        free_map();
907
 
                                        if(wasAlloced)
908
 
                                                free(start);
909
 
                                        else
910
 
                                                munmap(start, size);
911
 
 
912
 
                                        return CL_EMEM;
913
 
                                }
914
 
                                messageSetEncoding(m, "quoted-printable");
915
 
                                messageSetCTX(m, ctx);
916
 
 
917
 
                                line = NULL;
918
 
 
919
 
                                do {
920
 
                                        int length = 0;
921
 
                                        char *newline;
922
 
                                        const char *cptr;
923
 
 
924
 
                                        /*printf("%ld: ", quotedsize); fflush(stdout);*/
925
 
 
926
 
                                        for(cptr = quotedstart; quotedsize && (*cptr != '\n') && (*cptr != '\r'); cptr++) {
927
 
                                                length++;
928
 
                                                --quotedsize;
929
 
                                        }
930
 
 
931
 
                                        /*printf("%d: ", length); fflush(stdout);*/
932
 
 
933
 
                                        newline = cli_realloc(line, length + 1);
934
 
                                        if(newline == NULL)
935
 
                                                break;
936
 
                                        line = newline;
937
 
 
938
 
                                        memcpy(line, quotedstart, length);
939
 
                                        line[length] = '\0';
940
 
 
941
 
                                        /*puts(line);*/
942
 
 
943
 
                                        if(messageAddStr(m, line) < 0)
944
 
                                                break;
945
 
 
946
 
                                        if((quotedsize > 0) && (*cptr == '\r')) {
947
 
                                                quotedstart = ++cptr;
948
 
                                                --quotedsize;
949
 
                                        }
950
 
                                        if((quotedsize > 0) && (*cptr == '\n')) {
951
 
                                                quotedstart = ++cptr;
952
 
                                                --quotedsize;
953
 
                                        }
954
 
                                } while(quotedsize > 0L);
955
 
 
956
 
                                free(line);
957
 
                                fb = messageToFileblob(m, dir, 1);
958
 
                                messageDestroy(m);
959
 
 
960
 
                                if(fb)
961
 
                                        fileblobDestroy(fb);
962
 
                                else
963
 
                                        ret = -1;
964
 
                        }
965
 
                }
966
 
        }
967
 
        scanelem = scanlist;
968
 
 
969
 
        /*
970
 
         * There could be a phish in the plain text part, so save that
971
 
         * FIXME: Can't get to the option whether we are looking for
972
 
         *      phishes or not, so assume we are, this slows things a
973
 
         *      lot
974
 
         * Should be
975
 
         *      if((type == CL_TYPE_MAIL) && (!(no-phishing))
976
 
         */
977
 
        ret = save_text(ctx, dir, start, size);
978
 
 
979
 
        free_map();
980
 
 
981
 
        while(scanelem) {
982
 
                struct scanlist *n = scanelem->next;
983
 
 
984
 
                free(scanelem);
985
 
                scanelem = n;
986
 
        }
987
 
 
988
 
        if(wasAlloced)
989
 
                free(start);
990
 
        else
991
 
                munmap(start, size);
992
 
 
993
 
        /*
994
 
         * FIXME: Need to run cl_scandir() here and return that value
995
 
         */
996
 
        cli_dbgmsg("cli_mbox: ret = %d\n", ret);
997
 
        if(ret != CL_EFORMAT)
998
 
                return ret;
999
 
 
1000
 
        cli_dbgmsg("New world - don't know what to do - fall back to old world\n");
1001
 
        /* Fall back for now */
1002
 
        lseek(desc, 0L, SEEK_SET);
1003
 
        return cli_parse_mbox(dir, desc, ctx);
1004
 
}
1005
 
 
1006
 
/*
1007
 
 * Save a text part - it could contain phish or jscript
1008
 
 */
1009
 
static int
1010
 
save_text(cli_ctx *ctx, const char *dir, const char *start, size_t len)
1011
 
{
1012
 
        const char *p;
1013
 
 
1014
 
        if((p = find_in_map(start, "\n\n")) || (p = find_in_map(start, "\r\n\r\n"))) {
1015
 
                const char *q;
1016
 
                fileblob *fb;
1017
 
                char *tmpfilename;
1018
 
 
1019
 
                if(((q = find_in_map(start, "base64")) == NULL) &&
1020
 
                   ((q = find_in_map(start, "quoted_printable")) == NULL)) {
1021
 
                        cli_dbgmsg("It's all plain text!\n");
1022
 
                        if(*p == '\r')
1023
 
                                p += 4;
1024
 
                        else
1025
 
                                p += 2;
1026
 
                        len -= (p - start);
1027
 
                } else if(((q = find_in_map(p, "\nFrom ")) == NULL) &&
1028
 
                   ((q = find_in_map(p, "base64")) == NULL) &&
1029
 
                   ((q = find_in_map(p, "quoted-printable")) == NULL))
1030
 
                        cli_dbgmsg("Can't find end of plain text - assume it's all\n");
1031
 
                else
1032
 
                        len = (size_t)(q - p);
1033
 
 
1034
 
                if(len < 5) {
1035
 
                        cli_dbgmsg("save_text: Too small\n");
1036
 
                        return CL_EFORMAT;
1037
 
                }
1038
 
                if(ctx->scanned)
1039
 
                        *ctx->scanned += len / CL_COUNT_PRECISION;
1040
 
 
1041
 
                /*
1042
 
                 * This doesn't work, cli_scanbuff isn't designed to be used
1043
 
                 *      in this way. It gets the "filetype" wrong and then
1044
 
                 *      doesn't scan correctly
1045
 
                 */
1046
 
                if(cli_scanbuff((char *)p, len, 0, ctx, CL_TYPE_BINARY_DATA, NULL) == CL_VIRUS) {
1047
 
                        cli_dbgmsg("save_text: found %s\n", *ctx->virname);
1048
 
                        return CL_VIRUS;
1049
 
                }
1050
 
 
1051
 
                fb = fileblobCreate();
1052
 
                if(fb == NULL)
1053
 
                        return CL_EMEM;
1054
 
 
1055
 
                tmpfilename = cli_gentemp(dir);
1056
 
 
1057
 
                if(tmpfilename == NULL) {
1058
 
                        fileblobDestroy(fb);
1059
 
                        return CL_ETMPFILE;
1060
 
                }
1061
 
                cli_dbgmsg("save plain bit to %s, %u bytes\n",
1062
 
                        tmpfilename, len);
1063
 
 
1064
 
                fileblobSetFilename(fb, dir, tmpfilename);
1065
 
                free(tmpfilename);
1066
 
 
1067
 
                (void)fileblobAddData(fb, (const unsigned char *)p, len);
1068
 
                fileblobDestroy(fb);
1069
 
                return CL_SUCCESS;
1070
 
        }
1071
 
        cli_dbgmsg("No text part found to save\n");
1072
 
        return CL_EFORMAT;
1073
 
}
1074
 
 
1075
 
static void
1076
 
create_map(const char *begin, const char *end)
1077
 
{
1078
 
        const struct wordlist {
1079
 
                const char *word;
1080
 
                int len;
1081
 
        } wordlist[] = {
1082
 
                {       "base64",               6       },
1083
 
                {       "quoted-printable",     16      },
1084
 
                {       "\nbegin ",             7       },
1085
 
                {       "\nFrom ",              6       },
1086
 
                {       "\n\n",                 2       },
1087
 
                {       "\r\n\r\n",             4       },
1088
 
                {       NULL,                   0       }
1089
 
        };
1090
 
 
1091
 
        if(map) {
1092
 
                cli_dbgmsg("create_map called without free_map\n");
1093
 
                free_map();
1094
 
        }
1095
 
        while(begin < end) {
1096
 
                const struct wordlist *word;
1097
 
 
1098
 
                for(word = wordlist; word->word; word++) {
1099
 
                        if((end - begin) < word->len)
1100
 
                                continue;
1101
 
                        if(strncasecmp(begin, word->word, word->len) == 0) {
1102
 
                                add_to_map(begin, word->word);
1103
 
                                break;
1104
 
                        }
1105
 
                }
1106
 
                begin++;
1107
 
        }
1108
 
}
1109
 
 
1110
 
/* To sort map, assume 'offset' is presented in sorted order */
1111
 
static void
1112
 
add_to_map(const char *offset, const char *word)
1113
 
{
1114
 
        if(map) {
1115
 
                tail->next = cli_malloc(sizeof(struct map));    /* FIXME: verify */
1116
 
                tail = tail->next;
1117
 
        } else
1118
 
                map = tail = cli_malloc(sizeof(struct map));    /* FIXME: verify */
1119
 
 
1120
 
        tail->offset = offset;
1121
 
        tail->word = word;
1122
 
        tail->next = NULL;
1123
 
}
1124
 
 
1125
 
static const char *
1126
 
find_in_map(const char *offset, const char *word)
1127
 
{
1128
 
        const struct map *item;
1129
 
 
1130
 
        for(item = map; item; item = item->next)
1131
 
                if(item->offset >= offset)
1132
 
                        if(strcasecmp(word, item->word) == 0)
1133
 
                                return item->offset;
1134
 
 
1135
 
        return NULL;
1136
 
}
1137
 
 
1138
 
static void
1139
 
free_map(void)
1140
 
{
1141
 
        while(map) {
1142
 
                struct map *next = map->next;
1143
 
 
1144
 
                free(map);
1145
 
                map = next;
1146
 
        }
1147
 
        map = NULL;
1148
 
}
1149
 
 
1150
 
#else   /*!NEW_WORLD*/
1151
 
int
1152
 
cli_mbox(const char *dir, int desc, cli_ctx *ctx)
1153
 
{
1154
 
        if(dir == NULL) {
1155
 
                cli_dbgmsg("cli_mbox called with NULL dir\n");
1156
 
                return CL_ENULLARG;
1157
 
        }
1158
 
        return cli_parse_mbox(dir, desc, ctx);
1159
 
}
1160
 
#endif
 
304
int
 
305
cli_mbox(const char *dir, int desc, cli_ctx *ctx)
 
306
{
 
307
        if(dir == NULL) {
 
308
                cli_dbgmsg("cli_mbox called with NULL dir\n");
 
309
                return CL_ENULLARG;
 
310
        }
 
311
        return cli_parse_mbox(dir, desc, ctx);
 
312
}
1161
313
 
1162
314
/*
1163
315
 * TODO: when signal handling is added, need to remove temp files when a
1177
329
static int
1178
330
cli_parse_mbox(const char *dir, int desc, cli_ctx *ctx)
1179
331
{
1180
 
        int retcode, i;
 
332
        int retcode;
1181
333
        message *body;
1182
 
        FILE *fd;
1183
334
        char buffer[RFC2821LENGTH + 1];
1184
335
        mbox_ctx mctx;
1185
 
#ifdef HAVE_BACKTRACE
1186
 
        void (*segv)(int);
1187
 
#endif
1188
336
        static table_t *rfc821, *subtype;
1189
 
#ifdef  SAVE_TMP
1190
 
        char tmpfilename[16];
1191
 
        int tmpfd;
1192
 
#endif
 
337
        size_t at = 0;
 
338
        fmap_t *map = *ctx->fmap;
1193
339
 
1194
 
#ifdef  NEW_WORLD
1195
 
        cli_dbgmsg("fall back to old world\n");
1196
 
#else
1197
340
        cli_dbgmsg("in mbox()\n");
1198
 
#endif
1199
 
 
1200
 
        i = dup(desc);
1201
 
        if((fd = fdopen(i, "rb")) == NULL) {
1202
 
                cli_errmsg("Can't open descriptor %d\n", desc);
1203
 
                close(i);
1204
 
                return CL_EOPEN;
1205
 
        }
1206
 
        rewind(fd);     /* bug 240 */
1207
 
#ifdef  SAVE_TMP
1208
 
        /*
1209
 
         * Copy the incoming mail for debugging, so that if it falls over
1210
 
         * we have a copy of the offending email. This is debugging code
1211
 
         * that you shouldn't of course install in a live environment. I am
1212
 
         * not interested in hearing about security issues with this section
1213
 
         * of the parser.
1214
 
         */
1215
 
        strcpy(tmpfilename, "/tmp/mboxXXXXXX");
1216
 
        tmpfd = mkstemp(tmpfilename);
1217
 
        if(tmpfd < 0) {
1218
 
                perror(tmpfilename);
1219
 
                cli_errmsg("Can't make debugging file\n");
1220
 
        } else {
1221
 
                FILE *tmpfp = fdopen(tmpfd, "w");
1222
 
 
1223
 
                if(tmpfp) {
1224
 
                        while(fgets(buffer, sizeof(buffer) - 1, fd) != NULL)
1225
 
                                fputs(buffer, tmpfp);
1226
 
                        fclose(tmpfp);
1227
 
                        rewind(fd);
1228
 
                } else
1229
 
                        cli_errmsg("Can't fdopen debugging file\n");
1230
 
        }
1231
 
#endif
1232
 
        if(fgets(buffer, sizeof(buffer) - 1, fd) == NULL) {
 
341
 
 
342
        if(!fmap_gets(map, buffer, &at, sizeof(buffer) - 1)) {
1233
343
                /* empty message */
1234
 
                fclose(fd);
1235
 
#ifdef  SAVE_TMP
1236
 
                if (cli_unlink(tmpfilename)) return CL_EUNLINK;
1237
 
#endif
1238
344
                return CL_CLEAN;
1239
345
        }
1240
346
#ifdef  CL_THREAD_SAFE
1249
355
#ifdef  CL_THREAD_SAFE
1250
356
                        pthread_mutex_unlock(&tables_mutex);
1251
357
#endif
1252
 
                        fclose(fd);
1253
 
#ifdef  SAVE_TMP
1254
 
                        if (cli_unlink(tmpfilename)) return CL_EUNLINK;
1255
 
#endif
1256
358
                        return CL_EMEM;
1257
359
                }
1258
360
        }
1260
362
        pthread_mutex_unlock(&tables_mutex);
1261
363
#endif
1262
364
 
1263
 
#ifdef HAVE_BACKTRACE
1264
 
        segv = signal(SIGSEGV, sigsegv);
1265
 
#endif
1266
 
 
1267
365
        retcode = CL_SUCCESS;
1268
366
        body = NULL;
1269
367
 
1306
404
                int messagenumber;
1307
405
                message *m = messageCreate();
1308
406
 
1309
 
                if(m == NULL) {
1310
 
                        fclose(fd);
1311
 
#ifdef HAVE_BACKTRACE
1312
 
                        signal(SIGSEGV, segv);
1313
 
#endif
1314
 
#ifdef  SAVE_TMP
1315
 
                        if (cli_unlink(tmpfilename)) return CL_EUNLINK;
1316
 
#endif
 
407
                if(m == NULL)
1317
408
                        return CL_EMEM;
1318
 
                }
1319
409
 
1320
410
                lastLineWasEmpty = FALSE;
1321
411
                messagenumber = 1;
1371
461
                                 * Fast track visa to uudecode.
1372
462
                                 * TODO: binhex, yenc
1373
463
                                 */
1374
 
                                if(uudecodeFile(m, buffer, dir, fd) < 0)
 
464
                          if(uudecodeFile(m, buffer, dir, map, &at) < 0)
1375
465
                                        if(messageAddStr(m, buffer) < 0)
1376
466
                                                break;
1377
467
                        } else
1378
468
                                /* at this point, the \n has been removed */
1379
469
                                if(messageAddStr(m, buffer) < 0)
1380
470
                                        break;
1381
 
                } while(fgets(buffer, sizeof(buffer) - 1, fd) != NULL);
1382
 
 
1383
 
                fclose(fd);
 
471
                } while(fmap_gets(map, buffer, &at, sizeof(buffer) - 1));
1384
472
 
1385
473
                if(retcode == CL_SUCCESS) {
1386
474
                        cli_dbgmsg("Extract attachments from email %d\n", messagenumber);
1397
485
                         * CommuniGate Pro format: ignore headers until
1398
486
                         * blank line
1399
487
                         */
1400
 
                        while((fgets(buffer, sizeof(buffer) - 1, fd) != NULL) &&
 
488
                        while(fmap_gets(map, buffer, &at, sizeof(buffer) - 1) &&
1401
489
                                (strchr("\r\n", buffer[0]) == NULL))
1402
490
                                        ;
1403
 
                LOCKFILE(fd);
1404
491
                /* getline_from_mbox could be using unlocked_stdio(3),
1405
492
                 * so lock file here */
1406
493
                /*
1407
494
                 * Ignore any blank lines at the top of the message
1408
495
                 */
1409
496
                while(strchr("\r\n", buffer[0]) &&
1410
 
                     (getline_from_mbox(buffer, sizeof(buffer) - 1, fd) != NULL))
 
497
                      (getline_from_mbox(buffer, sizeof(buffer) - 1, map, &at) != NULL))
1411
498
                        ;
1412
499
 
1413
500
                buffer[sizeof(buffer) - 1] = '\0';
1414
501
 
1415
 
                body = parseEmailFile(fd, rfc821, buffer, dir);
1416
 
                UNLOCKFILE(fd);
1417
 
                fclose(fd);
 
502
                body = parseEmailFile(map, &at, rfc821, buffer, dir);
1418
503
        }
1419
504
 
1420
505
        if(body) {
1460
545
        }
1461
546
 
1462
547
        if((retcode == CL_CLEAN) && ctx->found_possibly_unwanted && (*ctx->virname == NULL)) {
1463
 
                *ctx->virname = "Phishing.Heuristics.Email";
 
548
                *ctx->virname = "Heuristics.Phishing.Email";
1464
549
                ctx->found_possibly_unwanted = 0;
1465
550
                retcode = CL_VIRUS;
1466
551
        }
1467
552
 
1468
553
        cli_dbgmsg("cli_mbox returning %d\n", retcode);
1469
554
 
1470
 
#ifdef HAVE_BACKTRACE
1471
 
        signal(SIGSEGV, segv);
1472
 
#endif
1473
 
 
1474
 
#ifdef  SAVE_TMP
1475
 
        if (cli_unlink(tmpfilename)) return CL_EUNLINK;
1476
 
#endif
1477
555
        return retcode;
1478
556
}
1479
557
 
1484
562
 * handled ungracefully...
1485
563
 */
1486
564
static message *
1487
 
parseEmailFile(FILE *fin, const table_t *rfc821, const char *firstLine, const char *dir)
 
565
parseEmailFile(fmap_t *map, size_t *at, const table_t *rfc821, const char *firstLine, const char *dir)
1488
566
{
1489
567
        bool inHeader = TRUE;
1490
568
        bool bodyIsEmpty = TRUE;
1578
656
                                bodyIsEmpty = TRUE;
1579
657
                        } else {
1580
658
                                char *ptr;
1581
 
                                int lookahead;
 
659
                                char *lookahead;
1582
660
 
1583
661
                                if(fullline == NULL) {
1584
662
                                        char cmd[RFC2821LENGTH + 1], out[RFC2821LENGTH + 1];
1631
709
 
1632
710
                                assert(fullline != NULL);
1633
711
 
1634
 
                                lookahead = GETC(fin);
1635
 
                                if(lookahead != EOF) {
1636
 
                                        ungetc(lookahead, fin);
1637
 
 
 
712
                                if((lookahead = fmap_need_off_once(map, *at, 1))) {
1638
713
                                        /*
1639
714
                                         * Section B.2 of RFC822 says TAB or
1640
715
                                         * SPACE means a continuation of the
1642
717
                                         *
1643
718
                                         * Add all the arguments on the line
1644
719
                                         */
1645
 
                                        if(isblank(lookahead))
 
720
                                        if(isblank(*lookahead))
1646
721
                                                continue;
1647
722
                                }
1648
723
 
1675
750
                         * TODO: binhex, yenc
1676
751
                         */
1677
752
                        bodyIsEmpty = FALSE;
1678
 
                        if(uudecodeFile(ret, line, dir, fin) < 0)
 
753
                        if(uudecodeFile(ret, line, dir, map, at) < 0)
1679
754
                                if(messageAddStr(ret, line) < 0)
1680
755
                                        break;
1681
756
                } else {
1709
784
                        if(messageAddStr(ret, line) < 0)
1710
785
                                break;
1711
786
                }
1712
 
        } while(getline_from_mbox(buffer, sizeof(buffer) - 1, fin) != NULL);
 
787
        } while(getline_from_mbox(buffer, sizeof(buffer) - 1, map, at) != NULL);
1713
788
 
1714
789
        if(boundary)
1715
790
                free(boundary);
2071
1146
                         */
2072
1147
                case TEXT:
2073
1148
                        /* text/plain has been preprocessed as no encoding */
2074
 
                        if(((mctx->ctx->options&CL_SCAN_MAILURL) && (subtype == HTML)) || doPhishingScan) {
 
1149
                        if(doPhishingScan) {
2075
1150
                                /*
2076
1151
                                 * It would be better to save and scan the
2077
1152
                                 * file and only checkURLs if it's found to be
3719
2794
        if(id == NULL)
3720
2795
                return -1;
3721
2796
 
3722
 
/* do we need this for C_WINDOWS?
3723
 
#ifdef  C_CYGWIN
3724
 
        if((tmpdir = getenv("TEMP")) == (char *)NULL)
3725
 
                if((tmpdir = getenv("TMP")) == (char *)NULL)
3726
 
                        if((tmpdir = getenv("TMPDIR")) == (char *)NULL)
3727
 
                                tmpdir = "C:\\";
3728
 
#else
3729
 
*/
3730
 
        if((tmpdir = getenv("TMPDIR")) == (char *)NULL)
3731
 
                if((tmpdir = getenv("TMP")) == (char *)NULL)
3732
 
                        if((tmpdir = getenv("TEMP")) == (char *)NULL)
3733
 
#ifdef  P_tmpdir
3734
 
                                tmpdir = P_tmpdir;
3735
 
#else
3736
 
                                tmpdir = "/tmp";
3737
 
#endif
 
2797
        tmpdir = cli_gettmpdir();
3738
2798
 
3739
 
        snprintf(pdir, sizeof(pdir) - 1, "%s/clamav-partial", tmpdir);
 
2799
        snprintf(pdir, sizeof(pdir) - 1, "%s"PATHSEP"clamav-partial", tmpdir);
3740
2800
 
3741
2801
        if((mkdir(pdir, S_IRWXU) < 0) && (errno != EEXIST)) {
3742
2802
                cli_errmsg("Can't create the directory '%s'\n", pdir);
3820
2880
 
3821
2881
                        sanitiseName(id);
3822
2882
 
3823
 
                        snprintf(outname, sizeof(outname) - 1, "%s/%s", dir, id);
 
2883
                        snprintf(outname, sizeof(outname) - 1, "%s"PATHSEP"%s", dir, id);
3824
2884
 
3825
2885
                        cli_dbgmsg("outname: %s\n", outname);
3826
2886
 
3859
2919
                                        int nblanks;
3860
2920
                                        struct stat statb;
3861
2921
                                        const char *dentry_idpart;
3862
 
#ifndef C_WINDOWS
 
2922
 
3863
2923
                                        if(dent->d_ino == 0)
3864
2924
                                                continue;
3865
 
#endif
3866
2925
 
3867
2926
                                        if(!strcmp(".",dent->d_name) ||
3868
2927
                                                        !strcmp("..", dent->d_name))
3869
2928
                                                continue;
3870
2929
                                        snprintf(fullname, sizeof(fullname) - 1,
3871
 
                                                "%s/%s", pdir, dent->d_name);
 
2930
                                                "%s"PATHSEP"%s", pdir, dent->d_name);
3872
2931
                                        dentry_idpart = strchr(dent->d_name, '_');
3873
2932
 
3874
2933
                                        if(!dentry_idpart ||
4055
3114
 
4056
3115
        hrefs.scanContents = mctx->ctx->engine->dboptions&CL_DB_PHISHING_URLS && (DCONF_PHISHING & PHISHING_CONF_ENGINE);
4057
3116
 
4058
 
#if    (!defined(FOLLOWURLS)) || (FOLLOWURLS <= 0)
4059
3117
        if(!hrefs.scanContents)
4060
3118
                /*
4061
3119
                 * Don't waste time extracting hrefs (parsing html), nobody
4062
3120
                 * will need it
4063
3121
                 */
4064
3122
                return;
4065
 
#endif
4066
3123
 
4067
3124
        hrefs.count = 0;
4068
3125
        hrefs.tag = hrefs.value = NULL;
4082
3139
                                cli_dbgmsg("PH:Phishing found\n");
4083
3140
                        }
4084
3141
                }
4085
 
                if(is_html && (mctx->ctx->options&CL_SCAN_MAILURL) && (*rc != VIRUS))
4086
 
                        do_checkURLs(mctx, &hrefs);
4087
3142
        }
4088
3143
        hrefs_done(b,&hrefs);
4089
3144
}
4090
3145
 
4091
 
#if     defined(FOLLOWURLS) && (FOLLOWURLS > 0)
4092
 
static void
4093
 
do_checkURLs(mbox_ctx *mctx, tag_arguments_t *hrefs)
4094
 
{
4095
 
        table_t *t;
4096
 
        int i, n;
4097
 
        const char *dir;
4098
 
#ifdef  CL_THREAD_SAFE
4099
 
        pthread_t tid[FOLLOWURLS];
4100
 
        struct arg args[FOLLOWURLS];
4101
 
#endif
4102
 
 
4103
 
        t = tableCreate();
4104
 
        if(t == NULL)
4105
 
                return;
4106
 
 
4107
 
        n = 0;
4108
 
        dir = mctx->dir;
4109
 
 
4110
 
        /*
4111
 
         * Sort .exes higher up so that there's more chance they'll be
4112
 
         * downloaded and scanned
4113
 
         */
4114
 
        for(i = FOLLOWURLS; (i < hrefs->count) && (n < FOLLOWURLS); i++) {
4115
 
                char *url = (char *)hrefs->value[i];
4116
 
                char *ptr;
4117
 
 
4118
 
                if(strncasecmp("http://", url, 7) != 0)
4119
 
                        continue;
4120
 
 
4121
 
                ptr = strrchr(url, '.');
4122
 
                if(ptr == NULL)
4123
 
                        continue;
4124
 
                if(strcasecmp(ptr, ".exe") == 0) {
4125
 
                        /* FIXME: Could be swapping with another .exe */
4126
 
                        cli_dbgmsg("swap %s %s\n", hrefs->value[n], hrefs->value[i]);
4127
 
                        ptr = (char *)hrefs->value[n];
4128
 
                        hrefs->value[n++] = (unsigned char *)url;
4129
 
                        hrefs->value[i] = (unsigned char *)ptr;
4130
 
                }
4131
 
        }
4132
 
 
4133
 
        n = 0;
4134
 
 
4135
 
        for(i = 0; i < hrefs->count; i++) {
4136
 
                const char *url = (const char *)hrefs->value[i];
4137
 
 
4138
 
                /*
4139
 
                 * TODO: If it's an image source, it'd be nice to note beacons
4140
 
                 *      where width="0" height="0", which needs support from
4141
 
                 *      the HTML normalise code
4142
 
                 */
4143
 
                if(strncasecmp("http://", url, 7) == 0) {
4144
 
#ifndef CL_THREAD_SAFE
4145
 
                        struct arg arg;
4146
 
#endif
4147
 
                        char name[NAME_MAX + 1];
4148
 
 
4149
 
                        if(tableFind(t, url) == 1) {
4150
 
                                cli_dbgmsg("URL %s already downloaded\n", url);
4151
 
                                continue;
4152
 
                        }
4153
 
                        /*
4154
 
                         * What about foreign character spoofing?
4155
 
                         */
4156
 
                        if(strchr(url, '%') && strchr(url, '@'))
4157
 
                                cli_dbgmsg("Possible URL spoofing attempt noticed, but not blocked (%s)\n", url);
4158
 
 
4159
 
                        if(n == FOLLOWURLS) {
4160
 
                                cli_dbgmsg("URL %s will not be scanned (FOLLOWURLS limit %d was reached)\n",
4161
 
                                        url, FOLLOWURLS);
4162
 
                                break;
4163
 
                        }
4164
 
 
4165
 
                        (void)tableInsert(t, url, 1);
4166
 
                        cli_dbgmsg("Downloading URL %s to be scanned\n", url);
4167
 
                        strncpy(name, url, sizeof(name) - 1);
4168
 
                        name[sizeof(name) - 1] = '\0';
4169
 
                        sanitiseName(name);     /* bug #538 */
4170
 
 
4171
 
#ifdef  CL_THREAD_SAFE
4172
 
                        args[n].dir = dir;
4173
 
                        args[n].url = cli_strdup(url);
4174
 
                        args[n].filename = cli_strdup(name);
4175
 
                        args[n].depth = 0;
4176
 
                        if(pthread_create(&tid[n], NULL, getURL, &args[n])) {
4177
 
                                cli_warnmsg("thread creation failed\n");
4178
 
                                free(args[n].filename);
4179
 
                                free(args[n].url);
4180
 
                                break;
4181
 
                        }
4182
 
#else
4183
 
                        arg.url = cli_strdup(url);
4184
 
                        arg.dir = dir;
4185
 
                        arg.filename = name;
4186
 
                        arg.depth = 0;
4187
 
                        getURL(&arg);
4188
 
                        free(arg.url);
4189
 
#endif
4190
 
                        ++n;
4191
 
                }
4192
 
        }
4193
 
        tableDestroy(t);
4194
 
 
4195
 
#ifdef  CL_THREAD_SAFE
4196
 
        assert(n <= FOLLOWURLS);
4197
 
        cli_dbgmsg("checkURLs: waiting for %d thread(s) to finish\n", n);
4198
 
        while(--n >= 0) {
4199
 
                pthread_join(tid[n], NULL);
4200
 
                free(args[n].filename);
4201
 
                free(args[n].url);
4202
 
        }
4203
 
#endif
4204
 
}
4205
 
 
4206
 
#else   /*!FOLLOWURLS*/
4207
 
 
4208
 
static void
4209
 
do_checkURLs(mbox_ctx *mctx, tag_arguments_t *hrefs)
4210
 
{
4211
 
}
4212
 
 
4213
 
#endif
4214
 
 
4215
 
#if     defined(FOLLOWURLS) && (FOLLOWURLS > 0)
4216
 
/*
4217
 
 * FIXME: Often WMF exploits work by sending people an email directing them
4218
 
 *      to a page which displays a picture containing the exploit. This is not
4219
 
 *      currently found, since only the HTML on the referred page is downloaded.
4220
 
 *      It would be useful to scan the HTML for references to pictures and
4221
 
 *      download them for scanning. But that will hit performance so there is
4222
 
 *      an issue here.
4223
 
 */
4224
 
 
4225
 
/*
4226
 
 * Simple implementation of a subset of RFC1945 (HTTP/1.0)
4227
 
 * TODO: HTTP/1.1 (RFC2068)
4228
 
 */
4229
 
static void *
4230
 
#ifdef  CL_THREAD_SAFE
4231
 
getURL(void *a)
4232
 
#else
4233
 
getURL(struct arg *arg)
4234
 
#endif
4235
 
{
4236
 
        FILE *fp;
4237
 
#ifdef  CL_THREAD_SAFE
4238
 
        struct arg *arg = (struct arg *)a;
4239
 
#endif
4240
 
        const char *url = arg->url;
4241
 
        const char *dir = arg->dir;
4242
 
        const char *filename = arg->filename;
4243
 
        SOCKET sd;
4244
 
        struct sockaddr_in server;
4245
 
#ifdef  HAVE_IN_ADDR_T
4246
 
        in_addr_t ip;
4247
 
#else
4248
 
        unsigned int ip;
4249
 
#endif
4250
 
        in_port_t port;
4251
 
        static in_port_t default_port;
4252
 
        static int tcp;
4253
 
        int doingsite, firstpacket;
4254
 
        char *ptr;
4255
 
        int via_proxy;
4256
 
        const char *proxy;
4257
 
        char buf[BUFSIZ + 1], site[BUFSIZ], fout[NAME_MAX + 1];
4258
 
 
4259
 
        if(strlen(url) > (sizeof(site) - 1)) {
4260
 
                cli_dbgmsg("Ignoring long URL \"%s\"\n", url);
4261
 
                return NULL;
4262
 
        }
4263
 
 
4264
 
        snprintf(fout, sizeof(fout) - 1, "%s/%s", dir, filename);
4265
 
 
4266
 
        fp = fopen(fout, "wb");
4267
 
 
4268
 
        if(fp == NULL) {
4269
 
                cli_errmsg("Can't open '%s' for writing\n", fout);
4270
 
                return NULL;
4271
 
        }
4272
 
        cli_dbgmsg("Saving %s to %s\n", url, fout);
4273
 
 
4274
 
#ifndef C_BEOS
4275
 
        if(tcp == 0) {
4276
 
                const struct protoent *proto = getprotobyname("tcp");
4277
 
 
4278
 
                if(proto == NULL) {
4279
 
                        cli_warnmsg("Unknown prototol tcp, check /etc/protocols\n");
4280
 
                        fclose(fp);
4281
 
                        return NULL;
4282
 
                }
4283
 
                tcp = proto->p_proto;
4284
 
#ifndef C_WINDOWS
4285
 
                endprotoent();
4286
 
#endif
4287
 
        }
4288
 
#endif
4289
 
        if(default_port == 0) {
4290
 
                const struct servent *servent = getservbyname("http", "tcp");
4291
 
 
4292
 
                if(servent)
4293
 
                        default_port = (in_port_t)ntohs(servent->s_port);
4294
 
                else
4295
 
                        default_port = 80;
4296
 
#if     !defined(C_WINDOWS) && !defined(C_BEOS)
4297
 
                endservent();
4298
 
#endif
4299
 
        }
4300
 
        port = default_port;
4301
 
 
4302
 
        doingsite = 1;
4303
 
        ptr = site;
4304
 
 
4305
 
        proxy = getenv("http_proxy");   /* FIXME: handle no_proxy */
4306
 
 
4307
 
        via_proxy = (proxy && *proxy);
4308
 
 
4309
 
        if(via_proxy) {
4310
 
                if(strncasecmp(proxy, "http://", 7) != 0) {
4311
 
                        cli_warnmsg("Unsupported proxy protocol (proxy = %s)\n",
4312
 
                                proxy);
4313
 
                        fclose(fp);
4314
 
                        return NULL;
4315
 
                }
4316
 
 
4317
 
                cli_dbgmsg("Getting %s via %s\n", url, proxy);
4318
 
 
4319
 
                proxy += 7;
4320
 
                while(*proxy) {
4321
 
                        if(doingsite && (*proxy == ':')) {
4322
 
                                port = 0;
4323
 
                                while(isdigit(*++proxy)) {
4324
 
                                        port *= 10;
4325
 
                                        port += *proxy - '0';
4326
 
                                }
4327
 
                                continue;
4328
 
                        }
4329
 
                        if(doingsite && (*proxy == '/')) {
4330
 
                                proxy++;
4331
 
                                break;
4332
 
                        }
4333
 
                        *ptr++ = *proxy++;
4334
 
                }
4335
 
        } else {
4336
 
                cli_dbgmsg("Getting %s\n", url);
4337
 
 
4338
 
                if(strncasecmp(url, "http://", 7) != 0) {
4339
 
                        cli_dbgmsg("Unsupported protocol\n");
4340
 
                        fclose(fp);
4341
 
                        return NULL;
4342
 
                }
4343
 
 
4344
 
                url += 7;
4345
 
                while(*url) {
4346
 
                        if(doingsite && (*url == ':')) {
4347
 
                                port = 0;
4348
 
                                while(isdigit(*++url)) {
4349
 
                                        port *= 10;
4350
 
                                        port += *url - '0';
4351
 
                                }
4352
 
                                continue;
4353
 
                        }
4354
 
                        if(doingsite && (*url == '/')) {
4355
 
                                url++;
4356
 
                                break;
4357
 
                        }
4358
 
                        *ptr++ = *url++;
4359
 
                }
4360
 
        }
4361
 
        *ptr = '\0';
4362
 
 
4363
 
        memset((char *)&server, '\0', sizeof(struct sockaddr_in));
4364
 
        server.sin_family = AF_INET;
4365
 
        server.sin_port = (in_port_t)htons(port);
4366
 
 
4367
 
        ip = inet_addr(site);
4368
 
#ifdef  INADDR_NONE
4369
 
        if(ip == INADDR_NONE) {
4370
 
#else
4371
 
        if(ip == (in_addr_t)-1) {
4372
 
#endif
4373
 
                struct hostent h;
4374
 
 
4375
 
                if((my_r_gethostbyname(site, &h, buf, sizeof(buf)) != 0) ||
4376
 
                   (h.h_addr_list == NULL) ||
4377
 
                   (h.h_addr == NULL)) {
4378
 
                        cli_dbgmsg("Unknown host %s\n", site);
4379
 
                        fclose(fp);
4380
 
                        return NULL;
4381
 
                }
4382
 
 
4383
 
                memcpy((char *)&ip, h.h_addr, sizeof(ip));
4384
 
        }
4385
 
        if((sd = socket(AF_INET, SOCK_STREAM, tcp)) < 0) {
4386
 
                fclose(fp);
4387
 
                return NULL;
4388
 
        }
4389
 
        server.sin_addr.s_addr = ip;
4390
 
        if(nonblock_connect(sd, &server, url) < 0) {
4391
 
                closesocket(sd);
4392
 
                fclose(fp);
4393
 
                return NULL;
4394
 
        }
4395
 
 
4396
 
        /*
4397
 
         * TODO: consider HTTP/1.1
4398
 
         */
4399
 
        if(via_proxy)
4400
 
                snprintf(buf, sizeof(buf) - 1,
4401
 
                        "GET %s HTTP/1.0\r\nHost: %s\r\nUser-Agent: ClamAV %s\r\n\r\n",
4402
 
                                url, site, cl_retver());
4403
 
        else
4404
 
                snprintf(buf, sizeof(buf) - 1,
4405
 
                        "GET /%s HTTP/1.0\r\nHost: %s\r\nUser-Agent: ClamAV %s\r\n\r\n",
4406
 
                                url, site, cl_retver());
4407
 
 
4408
 
        /*cli_dbgmsg("%s", buf);*/
4409
 
 
4410
 
        if(send(sd, buf, (int)strlen(buf), 0) < 0) {
4411
 
                closesocket(sd);
4412
 
                fclose(fp);
4413
 
                return NULL;
4414
 
        }
4415
 
 
4416
 
#ifdef  SHUT_WR
4417
 
        shutdown(sd, SHUT_WR);
4418
 
#else
4419
 
        shutdown(sd, 1);
4420
 
#endif
4421
 
 
4422
 
        firstpacket = 1;
4423
 
 
4424
 
        for(;;) {
4425
 
                fd_set set;
4426
 
                struct timeval tv;
4427
 
                int n;
4428
 
 
4429
 
                FD_ZERO(&set);
4430
 
                FD_SET(sd, &set);
4431
 
 
4432
 
                tv.tv_sec = 30; /* FIXME: make this customisable */
4433
 
                tv.tv_usec = 0;
4434
 
 
4435
 
                if(select((int)sd + 1, &set, NULL, NULL, &tv) < 0) {
4436
 
                        if(errno == EINTR)
4437
 
                                continue;
4438
 
                        closesocket(sd);
4439
 
                        fclose(fp);
4440
 
                        return NULL;
4441
 
                }
4442
 
                if(!FD_ISSET(sd, &set)) {
4443
 
                        fclose(fp);
4444
 
                        closesocket(sd);
4445
 
                        return NULL;
4446
 
                }
4447
 
                n = recv(sd, buf, sizeof(buf) - 1, 0);
4448
 
 
4449
 
                if(n < 0) {
4450
 
                        fclose(fp);
4451
 
                        closesocket(sd);
4452
 
                        return NULL;
4453
 
                }
4454
 
                if(n == 0)
4455
 
                        break;
4456
 
 
4457
 
                /*
4458
 
                 * FIXME: Handle header in more than one packet
4459
 
                 */
4460
 
                if(firstpacket) {
4461
 
                        char *statusptr;
4462
 
 
4463
 
                        buf[n] = '\0';
4464
 
 
4465
 
                        statusptr = cli_strtok(buf, 1, " ");
4466
 
 
4467
 
                        if(statusptr) {
4468
 
                                int status = atoi(statusptr);
4469
 
 
4470
 
                                cli_dbgmsg("HTTP status %d\n", status);
4471
 
 
4472
 
                                free(statusptr);
4473
 
 
4474
 
                                if((status == 301) || (status == 302)) {
4475
 
                                        char *location;
4476
 
 
4477
 
                                        location = strstr(buf, "\nLocation: ");
4478
 
 
4479
 
                                        if(location) {
4480
 
                                                char *end;
4481
 
 
4482
 
                                                if (cli_unlink(fout)) return NULL;
4483
 
                                                location += 11;
4484
 
                                                end = location;
4485
 
                                                while(*end && (*end != '\n'))
4486
 
                                                        end++;
4487
 
                                                *end = '\0';
4488
 
                                                if(arg->depth >= FOLLOWURLS) {
4489
 
                                                        cli_dbgmsg("URL %s will not be followed to %s (FOLLOWURLS limit %d was reached)\n",
4490
 
                                                                arg->url, location, FOLLOWURLS);
4491
 
                                                        break;
4492
 
                                                }
4493
 
                                                if(strcmp(location, arg->url) == 0) {
4494
 
                                                        cli_dbgmsg("URL %s redirects to itself\n",
4495
 
                                                                location);
4496
 
                                                        break;
4497
 
                                                }
4498
 
 
4499
 
                                                fclose(fp);
4500
 
                                                closesocket(sd);
4501
 
 
4502
 
                                                if(strlen(arg->url) < strlen(location)) {
4503
 
                                                        free(arg->url);
4504
 
                                                        arg->url = cli_strdup(location);
4505
 
                                                } else
4506
 
                                                        strcpy(arg->url, location);
4507
 
                                                arg->depth++;
4508
 
                                                cli_dbgmsg("Redirecting to %s\n", location);
4509
 
                                                return getURL(arg);
4510
 
                                        }
4511
 
                                }
4512
 
                        }
4513
 
                        /*
4514
 
                         * Don't write the HTTP header
4515
 
                         */
4516
 
                        if((ptr = strstr(buf, "\r\n\r\n")) != NULL) {
4517
 
                                ptr += 4;
4518
 
                                n -= (int)(ptr - buf);
4519
 
                        } else if((ptr = strstr(buf, "\n\n")) != NULL) {
4520
 
                                ptr += 2;
4521
 
                                n -= (int)(ptr - buf);
4522
 
                        } else
4523
 
                                ptr = buf;
4524
 
 
4525
 
                        firstpacket = 0;
4526
 
                } else
4527
 
                        ptr = buf;
4528
 
 
4529
 
                if(n && (fwrite(ptr, n, 1, fp) != 1)) {
4530
 
                        cli_warnmsg("Error writing %d bytes to %s\n",
4531
 
                                n, fout);
4532
 
                        break;
4533
 
                }
4534
 
        }
4535
 
 
4536
 
        fclose(fp);
4537
 
        closesocket(sd);
4538
 
        return NULL;
4539
 
}
4540
 
 
4541
 
/*
4542
 
 * Have a copy here because r_gethostbyname is in shared not libclamav :-(
4543
 
 */
4544
 
static int
4545
 
my_r_gethostbyname(const char *hostname, struct hostent *hp, char *buf, size_t len)
4546
 
{
4547
 
        struct hostent *hp2;
4548
 
        int ret = -1;
4549
 
#if !defined(HAVE_GETHOSTBYNAME_R_6) && !defined(HAVE_GETHOSTBYNAME_R_5) && !defined(HAVE_GETHOSTBYNAME_R_3)
4550
 
#ifdef  CL_THREAD_SAFE
4551
 
        static pthread_mutex_t hostent_mutex = PTHREAD_MUTEX_INITIALIZER;
4552
 
#endif
4553
 
#endif
4554
 
 
4555
 
        if((hostname == NULL) || (hp == NULL))
4556
 
                return -1;
4557
 
        memset(hp, 0, sizeof(struct hostent));
4558
 
#if     defined(HAVE_GETHOSTBYNAME_R_6)
4559
 
        /* e.g. Linux */
4560
 
 
4561
 
        if(gethostbyname_r(hostname, hp, buf, len, &hp2, &ret) < 0)
4562
 
                return ret;
4563
 
#elif   defined(HAVE_GETHOSTBYNAME_R_5)
4564
 
        /* e.g. BSD, Solaris, Cygwin */
4565
 
        /*
4566
 
         * Configure doesn't work on BeOS. We need -lnet to link, but configure
4567
 
         * doesn't add it, so you need to do something like
4568
 
         *      LIBS=-lnet ./configure --enable-cache --disable-clamav
4569
 
         */
4570
 
        if(gethostbyname_r(hostname, hp, buf, len, &ret) == NULL)
4571
 
                return ret;
4572
 
#elif   defined(HAVE_GETHOSTBYNAME_R_3)
4573
 
        /* e.g. HP/UX, AIX */
4574
 
        if(gethostbyname_r(hostname, &hp, (struct hostent_data *)buf) < 0)
4575
 
                return h_errno;
4576
 
#else
4577
 
        /* Single thread the code e.g. VS2005 */
4578
 
#ifdef  CL_THREAD_SAFE
4579
 
        pthread_mutex_lock(&hostent_mutex);
4580
 
#endif
4581
 
        if((hp2 = gethostbyname(hostname)) == NULL) {
4582
 
#ifdef  CL_THREAD_SAFE
4583
 
                pthread_mutex_unlock(&hostent_mutex);
4584
 
#endif
4585
 
                return h_errno;
4586
 
        }
4587
 
        memcpy(hp, hp2, sizeof(struct hostent));
4588
 
#ifdef  CL_THREAD_SAFE
4589
 
        pthread_mutex_unlock(&hostent_mutex);
4590
 
#endif
4591
 
 
4592
 
#endif
4593
 
        return 0;
4594
 
}
4595
 
 
4596
 
/*
4597
 
 * FIXME: There are lots of copies of this code :-(
4598
 
 */
4599
 
static int
4600
 
nonblock_connect(SOCKET sock, const struct sockaddr_in *sin, const char *hostname)
4601
 
{
4602
 
        int select_failures;    /* Max. of unexpected select() failures */
4603
 
        int attempts;
4604
 
        struct timeval timeout; /* When we should time out */
4605
 
        int numfd;              /* Highest fdset fd plus 1 */
4606
 
        long flags;
4607
 
        char err[128];
4608
 
 
4609
 
        gettimeofday(&timeout, 0);      /* store when we started to connect */
4610
 
 
4611
 
        if(hostname == NULL)
4612
 
                hostname = "remote";    /* It's only used in debug messages */
4613
 
 
4614
 
#ifdef  F_GETFL
4615
 
        flags = fcntl(sock, F_GETFL, 0);
4616
 
 
4617
 
        if(flags == -1L)
4618
 
                cli_dbgmsg("getfl: %s\n", cli_strerror(errno, err, sizeof(err)));
4619
 
        else if(fcntl(sock, F_SETFL, (long)(flags | O_NONBLOCK)) < 0)
4620
 
                cli_dbgmsg("setfl: %s\n", cli_strerror(errno, err, sizeof(err)));
4621
 
#else
4622
 
        flags = -1L;
4623
 
#endif
4624
 
        if(connect(sock, (const struct sockaddr *)sin, sizeof(struct sockaddr_in)) != 0)
4625
 
                switch(errno) {
4626
 
                        case EALREADY:
4627
 
                        case EINPROGRESS:
4628
 
                                cli_dbgmsg("%s: connect: %s\n", hostname,
4629
 
                                        cli_strerror(errno, err, sizeof(err)));
4630
 
                                break; /* wait for connection */
4631
 
                        case EISCONN:
4632
 
                                return 0; /* connected */
4633
 
                        default:
4634
 
                                cli_dbgmsg("%s: connect: %s\n",
4635
 
                                        hostname, cli_strerror(errno, err, sizeof(err)));
4636
 
#ifdef  F_SETFL
4637
 
                                if(flags != -1L)
4638
 
                                        if(fcntl(sock, F_SETFL, flags))
4639
 
                                                cli_dbgmsg("f_setfl: %s\n", cli_strerror(errno, err, sizeof(err)));
4640
 
#endif
4641
 
                                return -1; /* failed */
4642
 
                }
4643
 
        else {
4644
 
#ifdef  F_SETFL
4645
 
                if(flags != -1L)
4646
 
                        if(fcntl(sock, F_SETFL, flags))
4647
 
                                cli_dbgmsg("f_setfl: %s\n", cli_strerror(errno, err, sizeof(err)));
4648
 
#endif
4649
 
                return connect_error(sock, hostname);
4650
 
        }
4651
 
 
4652
 
        numfd = (int)sock + 1;
4653
 
        select_failures = NONBLOCK_SELECT_MAX_FAILURES;
4654
 
        attempts = 1;
4655
 
        timeout.tv_sec += CONNECT_TIMEOUT;
4656
 
 
4657
 
        for (;;) {
4658
 
                int n, t;
4659
 
                fd_set fds;
4660
 
                struct timeval now, waittime;
4661
 
 
4662
 
                /* Force timeout if we ran out of time */
4663
 
                gettimeofday(&now, 0);
4664
 
                t = (now.tv_sec == timeout.tv_sec) ?
4665
 
                        (now.tv_usec > timeout.tv_usec) :
4666
 
                        (now.tv_sec > timeout.tv_sec);
4667
 
 
4668
 
                if(t) {
4669
 
                        cli_dbgmsg("%s: connect timeout (%d secs)\n",
4670
 
                                hostname, CONNECT_TIMEOUT);
4671
 
                        break;
4672
 
                }
4673
 
 
4674
 
                /* Calculate how long to wait */
4675
 
                waittime.tv_sec = timeout.tv_sec - now.tv_sec;
4676
 
                waittime.tv_usec = timeout.tv_usec - now.tv_usec;
4677
 
                if(waittime.tv_usec < 0) {
4678
 
                        waittime.tv_sec--;
4679
 
                        waittime.tv_usec += 1000000;
4680
 
                }
4681
 
 
4682
 
                /* Init fds with 'sock' as the only fd */
4683
 
                FD_ZERO(&fds);
4684
 
                FD_SET(sock, &fds);
4685
 
 
4686
 
                n = select(numfd, 0, &fds, 0, &waittime);
4687
 
                if(n < 0) {
4688
 
                        cli_dbgmsg("%s: select attempt %d %s\n",
4689
 
                                hostname, select_failures, cli_strerror(errno, err, sizeof(err)));
4690
 
                        if(--select_failures >= 0)
4691
 
                                continue; /* not timed-out, try again */
4692
 
                        break; /* failed */
4693
 
                }
4694
 
 
4695
 
                cli_dbgmsg("%s: select = %d\n", hostname, n);
4696
 
 
4697
 
                if(n) {
4698
 
#ifdef  F_SETFL
4699
 
                        if(flags != -1L)
4700
 
                                if(fcntl(sock, F_SETFL, flags))
4701
 
                                        cli_dbgmsg("f_setfl: %s\n", cli_strerror(errno, err, sizeof(err)));
4702
 
#endif
4703
 
                        return connect_error(sock, hostname);
4704
 
                }
4705
 
 
4706
 
                /* timeout */
4707
 
                if(attempts++ == NONBLOCK_MAX_ATTEMPTS) {
4708
 
                        cli_dbgmsg("timeout connecting to %s\n", hostname);
4709
 
                        break;
4710
 
                }
4711
 
        }
4712
 
 
4713
 
#ifdef  F_SETFL
4714
 
        if(flags != -1L)
4715
 
                if(fcntl(sock, F_SETFL, flags))
4716
 
                        cli_dbgmsg("f_setfl: %s\n", cli_strerror(errno, err, sizeof(err)));
4717
 
#endif
4718
 
        return -1; /* failed */
4719
 
}
4720
 
 
4721
 
static int
4722
 
connect_error(SOCKET sock, const char *hostname)
4723
 
{
4724
 
        char err[128];
4725
 
#ifdef  SO_ERROR
4726
 
        int optval;
4727
 
        socklen_t optlen = sizeof(optval);
4728
 
 
4729
 
        getsockopt(sock, SOL_SOCKET, SO_ERROR, &optval, &optlen);
4730
 
 
4731
 
        if(optval) {
4732
 
                cli_dbgmsg("%s: %s\n", hostname, cli_strerror(optval, err, sizeof(err)));
4733
 
                return -1;
4734
 
        }
4735
 
#endif
4736
 
 
4737
 
        return 0;
4738
 
}
4739
 
 
4740
 
#endif
4741
 
 
4742
3146
#ifdef HAVE_BACKTRACE
4743
3147
static void
4744
3148
sigsegv(int sig)
4806
3210
 * Like fgets but cope with end of line by "\n", "\r\n", "\n\r", "\r"
4807
3211
 */
4808
3212
static char *
4809
 
getline_from_mbox(char *buffer, size_t len, FILE *fin)
 
3213
getline_from_mbox(char *buffer, size_t buffer_len, fmap_t *map, size_t *at)
4810
3214
{
4811
 
        char *ret;
 
3215
    char *src, *cursrc, *curbuf;
 
3216
    size_t i;
 
3217
    size_t input_len = MIN(map->len - *at, buffer_len + 1);
 
3218
    src = cursrc = fmap_need_off_once(map, *at, input_len);
4812
3219
 
4813
3220
/*      we check for eof from the result of GETC()
4814
3221
 *      if(feof(fin)) 
4815
3222
                return NULL;*/
4816
 
 
4817
 
        if((len == 0) || (buffer == NULL)) {
4818
 
                cli_errmsg("Invalid call to getline_from_mbox(). Refer to http://www.clamav.net/bugs\n");
4819
 
                return NULL;
4820
 
        }
4821
 
 
4822
 
        ret = buffer;
4823
 
 
4824
 
        do {
4825
 
                int c = GETC(fin);
4826
 
 
4827
 
                switch(c) {
4828
 
                        default:
4829
 
                                *buffer = (char)c;
4830
 
                                buffer += (c != 0);
4831
 
                                continue;
4832
 
                        case '\n':
4833
 
                                *buffer++ = '\n';
4834
 
                                c = GETC(fin);
4835
 
                                if((c != '\r') && !feof(fin))
4836
 
                                        ungetc(c, fin);
4837
 
                                break;
4838
 
                        case EOF:
4839
 
                                if(ret == buffer || ferror(fin)) {
4840
 
                                        /* EOF on first char, or error */
4841
 
                                        return NULL;
4842
 
                                }
4843
 
                                break;
4844
 
                        case '\r':
4845
 
                                *buffer++ = '\n';
4846
 
                                c = GETC(fin);
4847
 
                                if((c != '\n') && !feof(fin))
4848
 
                                        ungetc(c, fin);
4849
 
                                break;
4850
 
                }
4851
 
                break;
4852
 
        } while(--len > 1);
4853
 
 
4854
 
        if(len == 0) {
4855
 
                /* the email probably breaks RFC821 */
4856
 
                cli_dbgmsg("getline_from_mbox: buffer overflow stopped, line lost\n");
4857
 
                return NULL;
4858
 
        }
4859
 
        *buffer = '\0';
4860
 
 
4861
 
        if(len == 1)
4862
 
                /* overflows will have appeared on separate lines */
4863
 
                cli_dbgmsg("getline_from_mbox: buffer overflow stopped, line recovered\n");
4864
 
 
4865
 
        return ret;
 
3223
    if(!src) {
 
3224
        cli_dbgmsg("getline_from_mbox: fmap need failed\n");
 
3225
        return NULL;
 
3226
    }
 
3227
    if((buffer_len == 0) || (buffer == NULL)) {
 
3228
        cli_errmsg("Invalid call to getline_from_mbox(). Refer to http://www.clamav.net/bugs\n");
 
3229
        return NULL;
 
3230
    }
 
3231
 
 
3232
    curbuf = buffer;
 
3233
        
 
3234
    for(i=0; i<buffer_len-1; i++) {
 
3235
        char c;
 
3236
 
 
3237
        if(!input_len--) {
 
3238
            if(curbuf == buffer) {
 
3239
                /* EOF on first char */
 
3240
                return NULL;
 
3241
            }
 
3242
            break;
 
3243
        }
 
3244
 
 
3245
        switch((c = *cursrc++)) {
 
3246
        case '\0':
 
3247
            continue;
 
3248
        case '\n':
 
3249
            *curbuf++ = '\n';
 
3250
            if(input_len && *cursrc == '\r') {
 
3251
                i++;
 
3252
                cursrc++;
 
3253
            }
 
3254
            break;
 
3255
        case '\r':
 
3256
            *curbuf++ = '\r';
 
3257
            if(input_len && *cursrc == '\n') {
 
3258
                i++;
 
3259
                cursrc++;
 
3260
            }
 
3261
            break;
 
3262
        default:
 
3263
            *curbuf++ = c;
 
3264
            continue;
 
3265
        }
 
3266
        break;
 
3267
    }
 
3268
    *at += cursrc - src;
 
3269
    *curbuf = '\0';
 
3270
    
 
3271
    return buffer;
4866
3272
}
4867
3273
 
4868
3274
/*
5096
3502
                                                cli_dbgmsg("Treating inline as attachment\n");
5097
3503
                                } else {
5098
3504
                                        const int is_html = (tableFind(mctx->subtypeTable, cptr) == HTML);
5099
 
                                        if((mctx->ctx->options&CL_SCAN_MAILURL) && is_html)
5100
 
                                                checkURLs(aMessage, mctx, rc, 1);
5101
 
                                        else if(doPhishingScan)
 
3505
                                        if(doPhishingScan)
5102
3506
                                                checkURLs(aMessage, mctx, rc, is_html);
5103
3507
                                        messageAddArgument(aMessage,
5104
3508
                                                "filename=mixedtextportion");