364
301
static pthread_mutex_t tables_mutex = PTHREAD_MUTEX_INITIALIZER;
379
#include <sys/mman.h>
380
#else /* HAVE_SYS_MMAN_H */
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
396
#define MAX_ALLOCATION 134217728 /* see libclamav/others.c */
397
#define NW_MAX_FILE_SIZE MAX_ALLOCATION
402
encoding_type decoder; /* only BASE64 and QUOTEDPRINTABLE for now */
403
struct scanlist *next;
407
const char *offset; /* sorted */
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);
419
* This could be the future. Instead of parsing and decoding it just decodes.
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.
425
* Currently only supports base64 and quoted-printable
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...
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
435
* FIXME: quoted printable doesn't know when to stop, so size related virus
438
* TODO: Fall through to cli_parse_mbox() too often
440
* TODO: Add support for systems without mmap()
442
* TODO: partial_dir fall through
444
* FIXME: Some EICAR gets through
447
cli_mbox(const char *dir, int desc, cli_ctx *ctx)
449
char *start, *ptr, *line;
450
const char *last, *p, *q;
457
struct scanlist *scanlist, *scanelem;
460
cli_dbgmsg("cli_mbox called with NULL dir\n");
463
if(fstat(desc, &statb) < 0)
466
size = statb.st_size;
471
#ifdef NW_MAX_FILE_SIZE
472
if(size > NW_MAX_FILE_SIZE)
473
return cli_parse_mbox(dir, desc, ctx);
476
/*cli_warnmsg("NEW_WORLD is new code - use at your own risk.\n");*/
478
cli_warnmsg("PARTIAL_DIR doesn't work in the NEW_WORLD yet\n");
481
start = mmap(NULL, size, PROT_READ, MAP_PRIVATE, desc, 0);
482
if(start == MAP_FAILED)
485
cli_dbgmsg("mmap'ed mbox\n");
487
ptr = cli_malloc(size);
489
memcpy(ptr, start, size);
496
/* last points to the last *valid* address in the array */
497
last = &start[size - 1];
499
create_map(start, last);
501
scanelem = scanlist = NULL;
504
* FIXME: mismatch of const char * and char * here and in later calls
507
while((p = find_in_map(q, "base64")) != NULL) {
508
cli_dbgmsg("Found base64\n");
510
scanelem->next = cli_malloc(sizeof(struct scanlist));
511
scanelem = scanelem->next;
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);
523
scanelem->size = (size_t)(last - scanelem->start) + 1;
526
cli_dbgmsg("base64: last %u q %u\n", (unsigned int)last, (unsigned int)q);
527
assert(scanelem->size <= size);
531
while((p = find_in_map(q, "quoted-printable")) != NULL) {
536
case '=': /* wrong but allow it */
540
cli_dbgmsg("Ignore quoted-printable false positive\n");
541
continue; /* false positive */
544
cli_dbgmsg("Found quoted-printable\n");
547
* The problem with quoted printable is recognising when to stop
551
scanelem->next = cli_malloc(sizeof(struct scanlist));
552
scanelem = scanelem->next;
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);
564
cli_dbgmsg("qp: scanelem->size = %u\n", scanelem->size);
566
scanelem->size = (size_t)(last - scanelem->start) + 1;
569
assert(scanelem->size <= size);
577
return cli_parse_mbox(dir, desc, ctx);
581
if(scanlist == NULL) {
582
const struct tableinit *tableinit;
583
bool anyHeadersFound = FALSE;
584
bool hasuuencode = FALSE;
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;
594
if((!anyHeadersFound) &&
595
((p = find_in_map(start, "\nbegin ")) != NULL) &&
596
(isuuencodebegin(++p)))
600
cli_dbgmsg("Nothing encoded, looking for a text part to save\n");
601
ret = save_text(ctx, dir, start, size);
608
if(ret != CL_EFORMAT)
615
type = cli_filetype(start, size, ctx->engine);
617
if((type == CL_TYPE_TEXT_ASCII) &&
618
(strncmp(start, "Microsoft Mail Internet Headers", 31) == 0))
626
if(anyHeadersFound || hasuuencode) {
627
/* TODO: reduce the number of falls through here */
629
/* TODO: fast track visa */
630
cli_dbgmsg("New world - fall back to old uudecoder\n");
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);
639
#if 0 /* I don't believe this is needed any more */
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
646
* if((type == CL_TYPE_MAIL) && (!(no-phishing))
648
if(type == CL_TYPE_MAIL)
649
return cli_parse_mbox(dir, desc, ctx);
651
cli_dbgmsg("cli_mbox: I believe it's plain text (type == %d) which must be clean\n",
657
const char *max = NULL;
659
for(scanelem = scanlist; scanelem; scanelem = scanelem->next) {
660
const char *end = &scanelem->start[scanelem->size];
667
printf("could free %d bytes\n", (int)(last - max));
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;
676
cli_dbgmsg("b64size = %lu\n", b64size);
677
while((*b64start != '\n') && (*b64start != '\r')) {
682
* Look for the end of the headers
684
while(b64start < last) {
685
if(*b64start == ';') {
688
} else if((memcmp(b64start, "\n\n", 2) == 0) ||
689
(memcmp(b64start, "\r\r", 2) == 0)) {
693
} else if(memcmp(b64start, "\r\n\r\n", 4) == 0) {
697
} else if(memcmp(b64start, "\n \n", 3) == 0) {
699
* Some viruses are broken and have
700
* one space character at the end of
706
} else if(memcmp(b64start, "\r\n \r\n", 5) == 0) {
708
* Some viruses are broken and have
709
* one space character at the end of
721
while((!isalnum(*b64start)) && (*b64start != '/')) {
732
cli_dbgmsg("cli_mbox: decoding %ld base64 bytes\n", b64size);
733
if((fb = fileblobCreate()) == NULL) {
743
tmpfilename = cli_gentemp(dir);
744
if(tmpfilename == NULL) {
754
fileblobSetFilename(fb, dir, tmpfilename);
770
messageSetEncoding(m, "base64");
772
messageSetCTX(m, ctx);
773
fileblobSetCTX(fb, ctx);
777
int length = 0, datalen;
778
char *newline, *equal;
779
unsigned char *bigbuf, *data;
780
unsigned char smallbuf[1024];
783
/*printf("%ld: ", b64size); fflush(stdout);*/
785
for(cptr = b64start; b64size && (*cptr != '\n') && (*cptr != '\r'); cptr++) {
790
/*printf("%d: ", length); fflush(stdout);*/
792
newline = cli_realloc(line, length + 1);
797
memcpy(line, b64start, length);
800
equal = strchr(line, '=');
808
if(messageAddStr(m, line) < 0)
811
if(length >= (int)sizeof(smallbuf)) {
812
datalen = length + 2;
813
data = bigbuf = cli_malloc(datalen);
819
datalen = sizeof(data) - 1;
821
uptr = decodeLine(m, BASE64, line, data, datalen);
828
/*cli_dbgmsg("base64: write %u bytes\n", (size_t)(uptr - data));*/
829
datalen = fileblobAddData(fb, data, (size_t)(uptr - data));
835
if(fileblobContainsVirus(fb))
838
if((b64size > 0) && (*cptr == '\r')) {
842
if((b64size > 0) && (*cptr == '\n')) {
848
} while(b64size > 0L);
851
unsigned char data[4];
853
uptr = base64Flush(m, data);
855
/*cli_dbgmsg("base64: flush %u bytes\n", (size_t)(uptr - data));*/
856
(void)fileblobAddData(fb, data, (size_t)(uptr - data));
867
} else if(scanelem->decoder == QUOTEDPRINTABLE) {
868
const char *quotedstart = scanelem->start;
869
size_t quotedsize = scanelem->size;
871
cli_dbgmsg("quotedsize = %lu\n", quotedsize);
872
while(*quotedstart != '\n') {
877
* Look for the end of the headers
879
while(quotedstart < last) {
880
if(*quotedstart == ';') {
883
} else if((*quotedstart == '\n') || (*quotedstart == '\r')) {
886
if((*quotedstart == '\n') || (*quotedstart == '\r')) {
896
while(!isalnum(*quotedstart)) {
901
if(quotedsize > 0L) {
902
cli_dbgmsg("cli_mbox: decoding %ld quoted-printable bytes\n", quotedsize);
914
messageSetEncoding(m, "quoted-printable");
915
messageSetCTX(m, ctx);
924
/*printf("%ld: ", quotedsize); fflush(stdout);*/
926
for(cptr = quotedstart; quotedsize && (*cptr != '\n') && (*cptr != '\r'); cptr++) {
931
/*printf("%d: ", length); fflush(stdout);*/
933
newline = cli_realloc(line, length + 1);
938
memcpy(line, quotedstart, length);
943
if(messageAddStr(m, line) < 0)
946
if((quotedsize > 0) && (*cptr == '\r')) {
947
quotedstart = ++cptr;
950
if((quotedsize > 0) && (*cptr == '\n')) {
951
quotedstart = ++cptr;
954
} while(quotedsize > 0L);
957
fb = messageToFileblob(m, dir, 1);
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
975
* if((type == CL_TYPE_MAIL) && (!(no-phishing))
977
ret = save_text(ctx, dir, start, size);
982
struct scanlist *n = scanelem->next;
994
* FIXME: Need to run cl_scandir() here and return that value
996
cli_dbgmsg("cli_mbox: ret = %d\n", ret);
997
if(ret != CL_EFORMAT)
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);
1007
* Save a text part - it could contain phish or jscript
1010
save_text(cli_ctx *ctx, const char *dir, const char *start, size_t len)
1014
if((p = find_in_map(start, "\n\n")) || (p = find_in_map(start, "\r\n\r\n"))) {
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");
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");
1032
len = (size_t)(q - p);
1035
cli_dbgmsg("save_text: Too small\n");
1039
*ctx->scanned += len / CL_COUNT_PRECISION;
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
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);
1051
fb = fileblobCreate();
1055
tmpfilename = cli_gentemp(dir);
1057
if(tmpfilename == NULL) {
1058
fileblobDestroy(fb);
1061
cli_dbgmsg("save plain bit to %s, %u bytes\n",
1064
fileblobSetFilename(fb, dir, tmpfilename);
1067
(void)fileblobAddData(fb, (const unsigned char *)p, len);
1068
fileblobDestroy(fb);
1071
cli_dbgmsg("No text part found to save\n");
1076
create_map(const char *begin, const char *end)
1078
const struct wordlist {
1083
{ "quoted-printable", 16 },
1092
cli_dbgmsg("create_map called without free_map\n");
1095
while(begin < end) {
1096
const struct wordlist *word;
1098
for(word = wordlist; word->word; word++) {
1099
if((end - begin) < word->len)
1101
if(strncasecmp(begin, word->word, word->len) == 0) {
1102
add_to_map(begin, word->word);
1110
/* To sort map, assume 'offset' is presented in sorted order */
1112
add_to_map(const char *offset, const char *word)
1115
tail->next = cli_malloc(sizeof(struct map)); /* FIXME: verify */
1118
map = tail = cli_malloc(sizeof(struct map)); /* FIXME: verify */
1120
tail->offset = offset;
1126
find_in_map(const char *offset, const char *word)
1128
const struct map *item;
1130
for(item = map; item; item = item->next)
1131
if(item->offset >= offset)
1132
if(strcasecmp(word, item->word) == 0)
1133
return item->offset;
1142
struct map *next = map->next;
1150
#else /*!NEW_WORLD*/
1152
cli_mbox(const char *dir, int desc, cli_ctx *ctx)
1155
cli_dbgmsg("cli_mbox called with NULL dir\n");
1158
return cli_parse_mbox(dir, desc, ctx);
305
cli_mbox(const char *dir, int desc, cli_ctx *ctx)
308
cli_dbgmsg("cli_mbox called with NULL dir\n");
311
return cli_parse_mbox(dir, desc, ctx);
1163
315
* TODO: when signal handling is added, need to remove temp files when a
4082
3139
cli_dbgmsg("PH:Phishing found\n");
4085
if(is_html && (mctx->ctx->options&CL_SCAN_MAILURL) && (*rc != VIRUS))
4086
do_checkURLs(mctx, &hrefs);
4088
3143
hrefs_done(b,&hrefs);
4091
#if defined(FOLLOWURLS) && (FOLLOWURLS > 0)
4093
do_checkURLs(mbox_ctx *mctx, tag_arguments_t *hrefs)
4098
#ifdef CL_THREAD_SAFE
4099
pthread_t tid[FOLLOWURLS];
4100
struct arg args[FOLLOWURLS];
4111
* Sort .exes higher up so that there's more chance they'll be
4112
* downloaded and scanned
4114
for(i = FOLLOWURLS; (i < hrefs->count) && (n < FOLLOWURLS); i++) {
4115
char *url = (char *)hrefs->value[i];
4118
if(strncasecmp("http://", url, 7) != 0)
4121
ptr = strrchr(url, '.');
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;
4135
for(i = 0; i < hrefs->count; i++) {
4136
const char *url = (const char *)hrefs->value[i];
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
4143
if(strncasecmp("http://", url, 7) == 0) {
4144
#ifndef CL_THREAD_SAFE
4147
char name[NAME_MAX + 1];
4149
if(tableFind(t, url) == 1) {
4150
cli_dbgmsg("URL %s already downloaded\n", url);
4154
* What about foreign character spoofing?
4156
if(strchr(url, '%') && strchr(url, '@'))
4157
cli_dbgmsg("Possible URL spoofing attempt noticed, but not blocked (%s)\n", url);
4159
if(n == FOLLOWURLS) {
4160
cli_dbgmsg("URL %s will not be scanned (FOLLOWURLS limit %d was reached)\n",
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 */
4171
#ifdef CL_THREAD_SAFE
4173
args[n].url = cli_strdup(url);
4174
args[n].filename = cli_strdup(name);
4176
if(pthread_create(&tid[n], NULL, getURL, &args[n])) {
4177
cli_warnmsg("thread creation failed\n");
4178
free(args[n].filename);
4183
arg.url = cli_strdup(url);
4185
arg.filename = name;
4195
#ifdef CL_THREAD_SAFE
4196
assert(n <= FOLLOWURLS);
4197
cli_dbgmsg("checkURLs: waiting for %d thread(s) to finish\n", n);
4199
pthread_join(tid[n], NULL);
4200
free(args[n].filename);
4206
#else /*!FOLLOWURLS*/
4209
do_checkURLs(mbox_ctx *mctx, tag_arguments_t *hrefs)
4215
#if defined(FOLLOWURLS) && (FOLLOWURLS > 0)
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
4226
* Simple implementation of a subset of RFC1945 (HTTP/1.0)
4227
* TODO: HTTP/1.1 (RFC2068)
4230
#ifdef CL_THREAD_SAFE
4233
getURL(struct arg *arg)
4237
#ifdef CL_THREAD_SAFE
4238
struct arg *arg = (struct arg *)a;
4240
const char *url = arg->url;
4241
const char *dir = arg->dir;
4242
const char *filename = arg->filename;
4244
struct sockaddr_in server;
4245
#ifdef HAVE_IN_ADDR_T
4251
static in_port_t default_port;
4253
int doingsite, firstpacket;
4257
char buf[BUFSIZ + 1], site[BUFSIZ], fout[NAME_MAX + 1];
4259
if(strlen(url) > (sizeof(site) - 1)) {
4260
cli_dbgmsg("Ignoring long URL \"%s\"\n", url);
4264
snprintf(fout, sizeof(fout) - 1, "%s/%s", dir, filename);
4266
fp = fopen(fout, "wb");
4269
cli_errmsg("Can't open '%s' for writing\n", fout);
4272
cli_dbgmsg("Saving %s to %s\n", url, fout);
4276
const struct protoent *proto = getprotobyname("tcp");
4279
cli_warnmsg("Unknown prototol tcp, check /etc/protocols\n");
4283
tcp = proto->p_proto;
4289
if(default_port == 0) {
4290
const struct servent *servent = getservbyname("http", "tcp");
4293
default_port = (in_port_t)ntohs(servent->s_port);
4296
#if !defined(C_WINDOWS) && !defined(C_BEOS)
4300
port = default_port;
4305
proxy = getenv("http_proxy"); /* FIXME: handle no_proxy */
4307
via_proxy = (proxy && *proxy);
4310
if(strncasecmp(proxy, "http://", 7) != 0) {
4311
cli_warnmsg("Unsupported proxy protocol (proxy = %s)\n",
4317
cli_dbgmsg("Getting %s via %s\n", url, proxy);
4321
if(doingsite && (*proxy == ':')) {
4323
while(isdigit(*++proxy)) {
4325
port += *proxy - '0';
4329
if(doingsite && (*proxy == '/')) {
4336
cli_dbgmsg("Getting %s\n", url);
4338
if(strncasecmp(url, "http://", 7) != 0) {
4339
cli_dbgmsg("Unsupported protocol\n");
4346
if(doingsite && (*url == ':')) {
4348
while(isdigit(*++url)) {
4354
if(doingsite && (*url == '/')) {
4363
memset((char *)&server, '\0', sizeof(struct sockaddr_in));
4364
server.sin_family = AF_INET;
4365
server.sin_port = (in_port_t)htons(port);
4367
ip = inet_addr(site);
4369
if(ip == INADDR_NONE) {
4371
if(ip == (in_addr_t)-1) {
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);
4383
memcpy((char *)&ip, h.h_addr, sizeof(ip));
4385
if((sd = socket(AF_INET, SOCK_STREAM, tcp)) < 0) {
4389
server.sin_addr.s_addr = ip;
4390
if(nonblock_connect(sd, &server, url) < 0) {
4397
* TODO: consider HTTP/1.1
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());
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());
4408
/*cli_dbgmsg("%s", buf);*/
4410
if(send(sd, buf, (int)strlen(buf), 0) < 0) {
4417
shutdown(sd, SHUT_WR);
4432
tv.tv_sec = 30; /* FIXME: make this customisable */
4435
if(select((int)sd + 1, &set, NULL, NULL, &tv) < 0) {
4442
if(!FD_ISSET(sd, &set)) {
4447
n = recv(sd, buf, sizeof(buf) - 1, 0);
4458
* FIXME: Handle header in more than one packet
4465
statusptr = cli_strtok(buf, 1, " ");
4468
int status = atoi(statusptr);
4470
cli_dbgmsg("HTTP status %d\n", status);
4474
if((status == 301) || (status == 302)) {
4477
location = strstr(buf, "\nLocation: ");
4482
if (cli_unlink(fout)) return NULL;
4485
while(*end && (*end != '\n'))
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);
4493
if(strcmp(location, arg->url) == 0) {
4494
cli_dbgmsg("URL %s redirects to itself\n",
4502
if(strlen(arg->url) < strlen(location)) {
4504
arg->url = cli_strdup(location);
4506
strcpy(arg->url, location);
4508
cli_dbgmsg("Redirecting to %s\n", location);
4514
* Don't write the HTTP header
4516
if((ptr = strstr(buf, "\r\n\r\n")) != NULL) {
4518
n -= (int)(ptr - buf);
4519
} else if((ptr = strstr(buf, "\n\n")) != NULL) {
4521
n -= (int)(ptr - buf);
4529
if(n && (fwrite(ptr, n, 1, fp) != 1)) {
4530
cli_warnmsg("Error writing %d bytes to %s\n",
4542
* Have a copy here because r_gethostbyname is in shared not libclamav :-(
4545
my_r_gethostbyname(const char *hostname, struct hostent *hp, char *buf, size_t len)
4547
struct hostent *hp2;
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;
4555
if((hostname == NULL) || (hp == NULL))
4557
memset(hp, 0, sizeof(struct hostent));
4558
#if defined(HAVE_GETHOSTBYNAME_R_6)
4561
if(gethostbyname_r(hostname, hp, buf, len, &hp2, &ret) < 0)
4563
#elif defined(HAVE_GETHOSTBYNAME_R_5)
4564
/* e.g. BSD, Solaris, Cygwin */
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
4570
if(gethostbyname_r(hostname, hp, buf, len, &ret) == NULL)
4572
#elif defined(HAVE_GETHOSTBYNAME_R_3)
4573
/* e.g. HP/UX, AIX */
4574
if(gethostbyname_r(hostname, &hp, (struct hostent_data *)buf) < 0)
4577
/* Single thread the code e.g. VS2005 */
4578
#ifdef CL_THREAD_SAFE
4579
pthread_mutex_lock(&hostent_mutex);
4581
if((hp2 = gethostbyname(hostname)) == NULL) {
4582
#ifdef CL_THREAD_SAFE
4583
pthread_mutex_unlock(&hostent_mutex);
4587
memcpy(hp, hp2, sizeof(struct hostent));
4588
#ifdef CL_THREAD_SAFE
4589
pthread_mutex_unlock(&hostent_mutex);
4597
* FIXME: There are lots of copies of this code :-(
4600
nonblock_connect(SOCKET sock, const struct sockaddr_in *sin, const char *hostname)
4602
int select_failures; /* Max. of unexpected select() failures */
4604
struct timeval timeout; /* When we should time out */
4605
int numfd; /* Highest fdset fd plus 1 */
4609
gettimeofday(&timeout, 0); /* store when we started to connect */
4611
if(hostname == NULL)
4612
hostname = "remote"; /* It's only used in debug messages */
4615
flags = fcntl(sock, F_GETFL, 0);
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)));
4624
if(connect(sock, (const struct sockaddr *)sin, sizeof(struct sockaddr_in)) != 0)
4628
cli_dbgmsg("%s: connect: %s\n", hostname,
4629
cli_strerror(errno, err, sizeof(err)));
4630
break; /* wait for connection */
4632
return 0; /* connected */
4634
cli_dbgmsg("%s: connect: %s\n",
4635
hostname, cli_strerror(errno, err, sizeof(err)));
4638
if(fcntl(sock, F_SETFL, flags))
4639
cli_dbgmsg("f_setfl: %s\n", cli_strerror(errno, err, sizeof(err)));
4641
return -1; /* failed */
4646
if(fcntl(sock, F_SETFL, flags))
4647
cli_dbgmsg("f_setfl: %s\n", cli_strerror(errno, err, sizeof(err)));
4649
return connect_error(sock, hostname);
4652
numfd = (int)sock + 1;
4653
select_failures = NONBLOCK_SELECT_MAX_FAILURES;
4655
timeout.tv_sec += CONNECT_TIMEOUT;
4660
struct timeval now, waittime;
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);
4669
cli_dbgmsg("%s: connect timeout (%d secs)\n",
4670
hostname, CONNECT_TIMEOUT);
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) {
4679
waittime.tv_usec += 1000000;
4682
/* Init fds with 'sock' as the only fd */
4686
n = select(numfd, 0, &fds, 0, &waittime);
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 */
4695
cli_dbgmsg("%s: select = %d\n", hostname, n);
4700
if(fcntl(sock, F_SETFL, flags))
4701
cli_dbgmsg("f_setfl: %s\n", cli_strerror(errno, err, sizeof(err)));
4703
return connect_error(sock, hostname);
4707
if(attempts++ == NONBLOCK_MAX_ATTEMPTS) {
4708
cli_dbgmsg("timeout connecting to %s\n", hostname);
4715
if(fcntl(sock, F_SETFL, flags))
4716
cli_dbgmsg("f_setfl: %s\n", cli_strerror(errno, err, sizeof(err)));
4718
return -1; /* failed */
4722
connect_error(SOCKET sock, const char *hostname)
4727
socklen_t optlen = sizeof(optval);
4729
getsockopt(sock, SOL_SOCKET, SO_ERROR, &optval, &optlen);
4732
cli_dbgmsg("%s: %s\n", hostname, cli_strerror(optval, err, sizeof(err)));
4742
3146
#ifdef HAVE_BACKTRACE
4744
3148
sigsegv(int sig)