mirror of https://github.com/Cisco-Talos/clamav
commit 0ddbd1a7117e207b8aa87079568a056691d5eb32
Author: acab <acab@1337ness.(none)>
Date: Thu Nov 27 17:18:04 2008 +0100
to be committed
commit 51f8010a275717b8a56a3ab22d49755899819518
Author: acab <acab@1337ness.(none)>
Date: Thu Nov 27 15:18:40 2008 +0100
mangle body
commit cd38463f4739f4a7778b6bf294cfb2166024656e
Author: acab <acab@1337ness.(none)>
Date: Thu Nov 27 14:43:39 2008 +0100
parse clamd result
commit 7b8a39495e59ac745342455f8e9aab09951d1041
Author: acab <acab@1337ness.(none)>
Date: Thu Nov 27 03:19:45 2008 +0100
handle calloc shifts
commit a58b732af99f9e3f18d2327e977432f1117028ee
Author: acab <acab@1337ness.(none)>
Date: Thu Nov 27 02:07:19 2008 +0100
spam added, local is b0rked
commit 22604b4b037cfdbc92d6b29986652e2f004bd1f3
Author: acab <acab@albe.digitalfuture.it>
Date: Wed Nov 26 19:44:59 2008 +0100
free/close stuff
commit 33a02aed984981d3e80ca4930a482f702624f08f
Author: aCaB <acab@digitalfuture.it>
Date: Wed Nov 26 03:11:29 2008 +0100
skeleton ready
commit 4630d9902ee74b6137abf6526c6a9ad3e41fc597
Author: aCaB <acab@digitalfuture.it>
Date: Tue Nov 25 22:59:41 2008 +0100
need to rebase
commit 083f5f98aecfce2763870f20ae97643d5683613a
Author: aCaB <acab@digitalfuture.it>
Date: Tue Nov 25 22:45:11 2008 +0100
in body()
commit 08f040f67de1264810953415c0a47c95ec9acff0
Author: aCaB <acab@digitalfuture.it>
Date: Tue Nov 25 19:51:15 2008 +0100
clamfi
commit bc08fe8f72580b8be81791a7c03ec38952781af7
Author: aCaB <acab@digitalfuture.it>
Date: Tue Nov 25 16:47:17 2008 +0100
adding milter
commit 34dcebd9294a059f2c45ec9d1817bdb75f423cb3
Author: aCaB <acab@digitalfuture.it>
Date: Tue Nov 25 00:35:11 2008 +0100
netcode works, fix to cfgparser
commit 3cc0997d907e817954328c60e43cdcca0667d6f3
Author: aCaB <acab@digitalfuture.it>
Date: Mon Nov 24 23:46:05 2008 +0100
socket probe
commit 0c3bbd6d03f8df931cb114b07150cd0b7dcd0aff
Author: aCaB <acab@digitalfuture.it>
Date: Mon Nov 24 19:02:06 2008 +0100
nonblock started
commit a1193ff0de0d0a3d9212d833110236b1329c1f2e
Author: aCaB <acab@digitalfuture.it>
Date: Mon Nov 24 11:17:41 2008 +0100
legacy options
commit 90519c59ff8a4f44fceaf84e8c40116254c73045
Author: aCaB <acab@digitalfuture.it>
Date: Sun Nov 23 19:41:26 2008 +0100
sockets pool
commit 897b0c5f82503530ba3fbb2fcc3a9c007488e90a
Author: aCaB <acab@digitalfuture.it>
Date: Sun Nov 23 01:02:59 2008 +0100
Logging done
commit 4cf3a218756271c74782a1649f728e1c6e977ae5
Author: aCaB <acab@digitalfuture.it>
Date: Sat Nov 22 23:31:34 2008 +0100
Config done
commit 93b271579f680286c697476c00d8eec7effb2fc4
Author: aCaB <acab@digitalfuture.it>
Date: Sat Nov 22 22:50:42 2008 +0100
Sanitise config file options
commit bf5e81b8561b258daaef134894761fa80d44f5b1
Author: aCaB <acab@digitalfuture.it>
Date: Sat Nov 22 20:25:06 2008 +0100
cfgparser hacks to allow for milter own conf
commit 8924e93b2567a1f378c2177b4011b67631b49cc1
Author: aCaB <acab@digitalfuture.it>
Date: Sat Nov 22 19:03:45 2008 +0100
command line parser
commit d2180e370f75948b6ce12f89979bda9e92ccd5f8
Author: aCaB <acab@digitalfuture.it>
Date: Sat Nov 22 16:36:37 2008 +0100
restart from scratch
commit 249d6cdbddbea622c949753aafbf48e526e0ef4d
Author: aCaB <acab@digitalfuture.it>
Date: Sun Nov 16 19:39:12 2008 +0100
more diversion
commit b948990103867b59c749da88d6384128c1e5e6d3
Author: aCaB <acab@digitalfuture.it>
Date: Sun Nov 16 17:31:42 2008 +0100
milter separation (milter)
commit 6dc259a5d4f8f6e4428953055510382160ef8df4
Author: aCaB <acab@digitalfuture.it>
Date: Sun Nov 16 17:23:14 2008 +0100
milter separation
git-svn-id: file:///var/lib/svn/clamav-devel/branches/milter-v2.0@4486 77e5149b-7576-45b1-b177-96237e5ba77b
remotes/push_mirror/0.95
parent
88651f7c42
commit
c6385428e2
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,215 @@ |
||||
/*
|
||||
* Copyright (C)2008 Sourcefire, Inc. |
||||
* |
||||
* Author: aCaB <acab@clamav.net> |
||||
* |
||||
* This program is free software; you can redistribute it and/or modify |
||||
* it under the terms of the GNU General Public License version 2 as |
||||
* published by the Free Software Foundation. |
||||
* |
||||
* This program is distributed in the hope that it will be useful, |
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||||
* GNU General Public License for more details. |
||||
* |
||||
* You should have received a copy of the GNU General Public License |
||||
* along with this program; if not, write to the Free Software |
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, |
||||
* MA 02110-1301, USA. |
||||
*/ |
||||
|
||||
#if HAVE_CONFIG_H |
||||
#include "clamav-config.h" |
||||
#endif |
||||
|
||||
#include <stdlib.h> |
||||
#include <string.h> |
||||
#include <sys/types.h> |
||||
#include <unistd.h> |
||||
|
||||
#include <libmilter/mfapi.h> |
||||
|
||||
#include "shared/output.h" |
||||
|
||||
#include "connpool.h" |
||||
#include "netcode.h" |
||||
|
||||
uint64_t maxfilesize; |
||||
|
||||
#define CLAMFIBUFSZ 1424 |
||||
|
||||
struct CLAMFI { |
||||
char buffer[CLAMFIBUFSZ]; |
||||
int local; |
||||
int main; |
||||
int alt; |
||||
unsigned int altsz; |
||||
unsigned int bufsz; |
||||
}; |
||||
|
||||
|
||||
#define FREECF freecf(ctx, cf) |
||||
|
||||
static void freecf(SMFICTX *ctx, struct CLAMFI *cf) { |
||||
close(cf->main); |
||||
close(cf->alt); |
||||
smfi_setpriv(ctx, NULL); |
||||
free(cf); |
||||
} |
||||
|
||||
|
||||
static sfsistat sendchunk(struct CLAMFI *cf, unsigned char *bodyp, size_t len, SMFICTX *ctx) { |
||||
if(cf->altsz > maxfilesize) |
||||
return SMFIS_CONTINUE; /* FIXME: SMFIS_SKIP needs negotiation (only for _body() */ |
||||
|
||||
if(cf->altsz + len > maxfilesize) |
||||
len = maxfilesize - cf->altsz; |
||||
|
||||
if(cf->local) { |
||||
while(len) { |
||||
int n = write(cf->alt, bodyp, len); |
||||
|
||||
if (n==-1) { |
||||
logg("!clamfi_body: Failed to write temporary file\n"); |
||||
FREECF; |
||||
return SMFIS_TEMPFAIL; |
||||
} |
||||
len -= n; |
||||
bodyp += n; |
||||
} |
||||
} else { |
||||
int sendfailed = 0; |
||||
|
||||
if(len < CLAMFIBUFSZ - cf->bufsz) { |
||||
memcpy(&cf->buffer[cf->bufsz], bodyp, len); |
||||
cf->bufsz += len; |
||||
} else if(len < CLAMFIBUFSZ) { |
||||
memcpy(&cf->buffer[cf->bufsz], bodyp, CLAMFIBUFSZ - cf->bufsz); |
||||
sendfailed = nc_send(cf->alt, cf->buffer, CLAMFIBUFSZ); |
||||
len -= (CLAMFIBUFSZ - cf->bufsz); |
||||
memcpy(cf->buffer, &bodyp[CLAMFIBUFSZ - cf->bufsz], len); |
||||
cf->bufsz = len; |
||||
} else { |
||||
sendfailed = nc_send(cf->alt, cf->buffer, cf->bufsz); |
||||
sendfailed += nc_send(cf->alt, bodyp, len); |
||||
cf->bufsz = 0; |
||||
} |
||||
if(sendfailed) { |
||||
logg("!clamfi_body: Streaming failed\n"); |
||||
FREECF; |
||||
return SMFIS_TEMPFAIL; |
||||
} |
||||
} |
||||
cf->altsz += len; |
||||
return SMFIS_CONTINUE; |
||||
} |
||||
|
||||
|
||||
sfsistat clamfi_body(SMFICTX *ctx, unsigned char *bodyp, size_t len) { |
||||
struct CLAMFI *cf; |
||||
|
||||
if(!(cf = (struct CLAMFI *)smfi_getpriv(ctx))) { |
||||
sfsistat ret; |
||||
cf = (struct CLAMFI *)malloc(sizeof(*cf)); |
||||
if(!cf) { |
||||
logg("!clamfi_body: Failed to allocate CLAMFI struct\n"); |
||||
return SMFIS_TEMPFAIL; |
||||
} |
||||
cf->altsz = 0; |
||||
cf->bufsz = 0; |
||||
if(nc_connect_rand(&cf->main, &cf->alt, &cf->local)) { |
||||
logg("!clamfi_body: Failed to initiate streaming/fdpassing\n"); |
||||
free(cf); |
||||
return SMFIS_TEMPFAIL; |
||||
} |
||||
smfi_setpriv(ctx, (void *)cf); |
||||
if((ret = sendchunk(cf, (unsigned char *)"From clamav-milter\n", 19, ctx)) != SMFIS_CONTINUE) |
||||
return ret; |
||||
} |
||||
return sendchunk(cf, bodyp, len, ctx); |
||||
} |
||||
|
||||
|
||||
sfsistat clamfi_eom(SMFICTX *ctx) { |
||||
struct CLAMFI *cf; |
||||
char *reply; |
||||
int len, ret; |
||||
|
||||
if(!(cf = (struct CLAMFI *)smfi_getpriv(ctx))) |
||||
return SMFIS_CONTINUE; /* whatever */ |
||||
|
||||
if(cf->local) { |
||||
struct iovec iov[1]; |
||||
struct msghdr msg; |
||||
struct cmsghdr *cmsg; |
||||
unsigned char fdbuf[CMSG_SPACE(sizeof(int))]; |
||||
char dummy[]=""; |
||||
|
||||
if(nc_send(cf->main, "nFILDES\n", 8)) { |
||||
logg("!clamfi_eom: FD scan request failed\n"); |
||||
FREECF; |
||||
return SMFIS_TEMPFAIL; |
||||
} |
||||
|
||||
lseek(cf->alt, 0, SEEK_SET); |
||||
iov[0].iov_base = dummy; |
||||
iov[0].iov_len = 1; |
||||
memset(&msg, 0, sizeof(msg)); |
||||
msg.msg_control = fdbuf; |
||||
msg.msg_iov = iov; |
||||
msg.msg_iovlen = 1; |
||||
msg.msg_controllen = CMSG_LEN(sizeof(int)); |
||||
cmsg = CMSG_FIRSTHDR(&msg); |
||||
cmsg->cmsg_len = CMSG_LEN(sizeof(int)); |
||||
cmsg->cmsg_level = SOL_SOCKET; |
||||
cmsg->cmsg_type = SCM_RIGHTS; |
||||
*(int *)CMSG_DATA(cmsg) = cf->alt; |
||||
if(sendmsg(cf->main, &msg, 0) == -1) { |
||||
/* FIXME: nonblock code needed (?) */ |
||||
logg("!clamfi_eom: FD send failed\n"); |
||||
FREECF; |
||||
return SMFIS_TEMPFAIL; |
||||
} |
||||
} else { |
||||
if(cf->bufsz && nc_send(cf->alt, cf->buffer, cf->bufsz)) { |
||||
logg("!clamfi_eom: Flushing failed\n"); |
||||
FREECF; |
||||
return SMFIS_TEMPFAIL; |
||||
} |
||||
close(cf->alt); |
||||
} |
||||
|
||||
reply = nc_recv(cf->main); |
||||
|
||||
if(cf->local) close(cf->alt); |
||||
close(cf->main); |
||||
close(cf->alt); |
||||
smfi_setpriv(ctx, NULL); |
||||
free(cf); |
||||
|
||||
if(!reply) { |
||||
logg("!clamfi_eom: no reply to scan request\n"); |
||||
return SMFIS_TEMPFAIL; |
||||
} |
||||
len = strlen(reply); |
||||
if(len>5 && !strcmp(reply + len - 5, ": OK\n")) |
||||
ret = SMFIS_ACCEPT; |
||||
else if (len>7 && !strcmp(reply + len - 7, " FOUND\n")) |
||||
ret = SMFIS_REJECT; |
||||
else { |
||||
logg("!clamfi_eom: unknown reply from clamd\n"); |
||||
ret = SMFIS_TEMPFAIL; |
||||
} |
||||
|
||||
free(reply); |
||||
return ret; |
||||
} |
||||
|
||||
/*
|
||||
* Local Variables: |
||||
* mode: c |
||||
* c-basic-offset: 4 |
||||
* tab-width: 8 |
||||
* End:
|
||||
* vim: set cindent smartindent autoindent softtabstop=4 shiftwidth=4 tabstop=8:
|
||||
*/ |
||||
@ -0,0 +1,11 @@ |
||||
#ifndef _CLAMFI_H |
||||
#define _CLAMFI_H |
||||
|
||||
#include <libmilter/mfapi.h> |
||||
|
||||
uint64_t maxfilesize; |
||||
|
||||
sfsistat clamfi_body(SMFICTX *ctx, unsigned char *bodyp, size_t len); |
||||
sfsistat clamfi_eom(SMFICTX *ctx); |
||||
|
||||
#endif |
||||
@ -0,0 +1,324 @@ |
||||
/*
|
||||
* Copyright (C)2008 Sourcefire, Inc. |
||||
* |
||||
* Author: aCaB <acab@clamav.net> |
||||
* |
||||
* This program is free software; you can redistribute it and/or modify |
||||
* it under the terms of the GNU General Public License version 2 as |
||||
* published by the Free Software Foundation. |
||||
* |
||||
* This program is distributed in the hope that it will be useful, |
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||||
* GNU General Public License for more details. |
||||
* |
||||
* You should have received a copy of the GNU General Public License |
||||
* along with this program; if not, write to the Free Software |
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, |
||||
* MA 02110-1301, USA. |
||||
*/ |
||||
|
||||
#if HAVE_CONFIG_H |
||||
#include "clamav-config.h" |
||||
#endif |
||||
|
||||
#include <stdlib.h> |
||||
#include <string.h> |
||||
#include <strings.h> |
||||
#include <sys/socket.h> |
||||
#include <netinet/in.h> |
||||
#include <arpa/inet.h> |
||||
#include <sys/types.h> |
||||
#include <sys/socket.h> |
||||
#include <sys/un.h> |
||||
#include <netdb.h> |
||||
#include <unistd.h> |
||||
#include <time.h> |
||||
|
||||
#include "shared/cfgparser.h" |
||||
#include "shared/output.h" |
||||
|
||||
#include "connpool.h" |
||||
#include "netcode.h" |
||||
|
||||
#ifdef HAVE_GETADDRINFO |
||||
#define SETGAI(k, v) {(k)->gai = (void *)(v);} while(0) |
||||
#define FREESRV(k) { if((k).gai) freeaddrinfo((k).gai); else if((k).server) free((k).server); } while(0) |
||||
#else |
||||
#include <netdb.h> |
||||
#define SETGAI |
||||
#define FREESRV(k) { if ((k).server) free((k).server); } while(0) |
||||
#endif |
||||
|
||||
struct CPOOL *cp = NULL; |
||||
|
||||
static int cpool_addunix(char *path) { |
||||
struct sockaddr_un *srv; |
||||
struct CP_ENTRY *cpe = &cp->pool[cp->entries-1]; |
||||
|
||||
if(strlen(path)<2 || *path!='/') { |
||||
logg("!Unix clamd socket must be an absolute path\n"); |
||||
return 1; |
||||
} |
||||
if(!(srv = (struct sockaddr_un *)malloc(sizeof(*srv)))) { |
||||
logg("!Out of memory allocating unix socket space\n"); |
||||
return 1; |
||||
} |
||||
|
||||
srv->sun_family = AF_UNIX; |
||||
strncpy(srv->sun_path, path, sizeof(srv->sun_path)); |
||||
srv->sun_path[sizeof(srv->sun_path)-1]='\0'; |
||||
cpe->type = 0; |
||||
cpe->dead = 1; |
||||
cpe->local = 1; |
||||
cpe->last_poll = 0; |
||||
cpe->server = (struct sockaddr *)srv; |
||||
cpe->socklen = sizeof(*srv); |
||||
SETGAI(cpe, NULL); |
||||
if(!cp->local_cpe) cp->local_cpe = cpe; |
||||
logg("*Local socket unix:%s added to the pool (slot %d)\n", srv->sun_path, cp->entries); |
||||
return 0; |
||||
} |
||||
|
||||
|
||||
static int islocal(struct sockaddr *sa, socklen_t addrlen) { |
||||
int s = socket(sa->sa_family, SOCK_STREAM, 0); |
||||
int ret; |
||||
if (!s) return 0; |
||||
ret = (bind(s, sa, addrlen) == 0); |
||||
close(s); |
||||
return ret; |
||||
} |
||||
|
||||
|
||||
#ifdef HAVE_GETADDRINFO |
||||
static int cpool_addtcp(char *addr, char *port) { |
||||
struct addrinfo hints, *res, *res2;; |
||||
struct CP_ENTRY *cpe = (struct CP_ENTRY *)&cp->pool[cp->entries-1]; |
||||
|
||||
memset(&hints, 0, sizeof(hints)); |
||||
#ifdef SUPPORT_IPv6 |
||||
hints.ai_family = AF_UNSPEC; |
||||
#else |
||||
hints.ai_family = AF_INET; |
||||
#endif |
||||
hints.ai_socktype = SOCK_STREAM; |
||||
|
||||
if(getaddrinfo(addr, port ? port : "3310", &hints, &res)) { |
||||
logg("^Can't resolve hostname %s\n", addr ? addr : ""); |
||||
return 1; |
||||
} |
||||
cpe->type = 1; |
||||
cpe->dead = 1; |
||||
|
||||
memset(&hints, 0, sizeof(hints)); |
||||
hints.ai_flags = AI_PASSIVE; |
||||
hints.ai_socktype = SOCK_STREAM; |
||||
#ifdef SUPPORT_IPv6 |
||||
hints.ai_family = AF_UNSPEC; |
||||
#else |
||||
hints.ai_family = AF_INET; |
||||
#endif |
||||
if(!getaddrinfo(addr, NULL, &hints, &res2)) { |
||||
cpe->local = islocal(res2->ai_addr, res2->ai_addrlen); |
||||
freeaddrinfo(res2); |
||||
} else cpe->local = 0; |
||||
cpe->last_poll = 0; |
||||
cpe->server = res->ai_addr; |
||||
cpe->socklen = res->ai_addrlen; |
||||
SETGAI(cpe, res); |
||||
logg("*%s socket tcp:%s:%s added to the pool (slot %d)\n", cpe->local ? "Local" : "Remote", addr ? addr : "localhost", port ? port : "3310", cp->entries); |
||||
return 0; |
||||
} |
||||
#else |
||||
static int cpool_addtcp(char *addr, char *port) { |
||||
struct sockaddr_in *srv; |
||||
struct CP_ENTRY *cpe = (struct CP_ENTRY *)&cp->pool[cp->entries-1]; |
||||
int nport = 3310; |
||||
|
||||
if(port) { |
||||
nport = atoi(port); |
||||
if (nport<=0 || nport>65535) { |
||||
logg("!Bad port for clamd socket (%d)\n", nport); |
||||
return 1; |
||||
} |
||||
} |
||||
if(!(srv = malloc(sizeof(*srv)))) { |
||||
logg("!Out of memory allocating unix socket space\n"); |
||||
return 1; |
||||
} |
||||
|
||||
srv->sin_family = AF_INET; |
||||
|
||||
if (addr) { |
||||
struct hostent *h; |
||||
if(!(h=gethostbyname(addr))) { |
||||
logg("^Can't resolve tcp socket hostname %s\n", addr); |
||||
free(srv); |
||||
return 1; |
||||
} |
||||
memcpy(&srv->sin_addr.s_addr, h->h_addr_list[0], 4); |
||||
} else { |
||||
srv->sin_addr.s_addr = htonl(INADDR_LOOPBACK); |
||||
} |
||||
cpe->type = 1; |
||||
cpe->dead = 1; |
||||
srv->sin_port = htons(INADDR_ANY); |
||||
cpe->local = islocal(srv, sizeof(srv)); |
||||
srv->sin_port = htons(nport); |
||||
cpe->last_poll = 0; |
||||
cpe->server = (struct sockaddr *)srv; |
||||
cpe->socklen = sizeof(*srv); |
||||
logg("*%s socket tcp:%s:%u added to the pool (slot %d)\n", cpe->local ? "Local" : "Remote", addr ? addr : "localhost", nport, cp->entries); |
||||
return 0; |
||||
}
|
||||
#endif |
||||
|
||||
|
||||
int addslot(void) { |
||||
struct CP_ENTRY *cpe; |
||||
|
||||
if(!(cpe = realloc(cp->pool, (cp->entries + 1) * sizeof(struct CP_ENTRY)))) { |
||||
logg("!Out of memory while initializing the connection pool\n"); |
||||
cpool_free(); |
||||
return 1; |
||||
} |
||||
if(cp->local_cpe) |
||||
cp->local_cpe = (struct CP_ENTRY *)((char *)cp->local_cpe + ((char *)cpe - (char *)cp->pool)); |
||||
memset(&cpe[cp->entries], 0, sizeof(*cpe)); |
||||
cp->pool = cpe; |
||||
cp->entries++; |
||||
return 0; |
||||
} |
||||
|
||||
|
||||
void cpool_probe(void) { |
||||
unsigned int i, dead=0; |
||||
struct CP_ENTRY *cpe = cp->pool; |
||||
time_t lastpoll = time(NULL) - 5*60; |
||||
|
||||
for(i=1; i<=cp->entries; i++) { |
||||
if(cpe->dead && lastpoll > cpe->last_poll) { |
||||
nc_ping_entry(cpe); |
||||
logg("*Probe for slot %u returned: %s\n", i, cpe->dead ? "failed" : "success"); |
||||
} |
||||
dead += cpe->dead; |
||||
cpe++; |
||||
} |
||||
cp->alive = cp->entries - dead; |
||||
if(!cp->alive) |
||||
logg("^No clamd server appears to be available, trying again in 5 minutes.\n"); |
||||
} |
||||
|
||||
|
||||
void cpool_init(struct cfgstruct *copt) { |
||||
const struct cfgstruct *cpt; |
||||
int failed = 0; |
||||
|
||||
if(!(cp=calloc(sizeof(*cp), 1))) { |
||||
logg("!Out of memory while initializing the connection pool"); |
||||
return; |
||||
} |
||||
|
||||
cp->local_cpe = NULL; |
||||
|
||||
if((cpt = cfgopt(copt, "ClamdSocket"))->enabled) { |
||||
while(cpt) { |
||||
char *socktype = cpt->strarg; |
||||
|
||||
if(addslot()) return; |
||||
if(!strncasecmp(socktype, "unix:", 5)) { |
||||
failed = cpool_addunix(socktype+5); |
||||
} else if(!strncasecmp(socktype, "tcp:", 4)) { |
||||
char *port = strrchr(socktype+4, ':'); |
||||
if(port) { |
||||
*port='\0'; |
||||
port++; |
||||
} |
||||
failed = cpool_addtcp(socktype+4, port); |
||||
} else { |
||||
logg("!Failed to parse ClamdSocket directive '%s'\n", socktype); |
||||
failed = 1; |
||||
} |
||||
if(failed) break; |
||||
cpt = (struct cfgstruct *) cpt->nextarg; |
||||
} |
||||
if(failed) { |
||||
cpool_free(); |
||||
return; |
||||
} |
||||
} |
||||
|
||||
#ifdef MILTER_LEGACY |
||||
if((cpt = cfgopt(copt, "LocalSocket"))->enabled) { |
||||
if(addslot()) return; |
||||
if(cpool_addunix(cpt->strarg)) { |
||||
cpool_free(); |
||||
return; |
||||
} |
||||
} |
||||
|
||||
if((cpt = cfgopt(copt, "TCPSocket"))->enabled) { |
||||
char *addr = NULL; |
||||
char port[5]; |
||||
|
||||
if(addslot()) return; |
||||
snprintf(port, 5, "%d", cpt->numarg); |
||||
port[5] = 0; |
||||
if((cpt = cfgopt(copt, "TCPAddr"))->enabled) |
||||
addr = cpt->strarg; |
||||
if(cpool_addtcp(addr, port)) { |
||||
cpool_free(); |
||||
return; |
||||
} |
||||
} |
||||
#endif |
||||
|
||||
if(!cp->entries) { |
||||
logg("!No ClamdSocket specified\n"); |
||||
cpool_free(); |
||||
return; |
||||
} |
||||
cpool_probe(); |
||||
srand(time(NULL)); /* FIXME: naive ? */ |
||||
} |
||||
|
||||
|
||||
void cpool_free(void) { |
||||
unsigned int i; |
||||
for(i=0; i<cp->entries; i++) |
||||
FREESRV(cp->pool[i]); |
||||
free(cp->pool); |
||||
free(cp); |
||||
} |
||||
|
||||
|
||||
struct CP_ENTRY *cpool_get_rand(void) { |
||||
unsigned int start, i; |
||||
struct CP_ENTRY *cpe; |
||||
|
||||
if(!cp->alive) { |
||||
logg("!No sockets are alive. Probe forced...\n"); |
||||
/* FIXME: yeah, actually do force smthng here */ |
||||
return NULL; |
||||
} |
||||
start = rand() % cp->entries; |
||||
for(i=0; i<cp->entries; i++) { |
||||
cpe = &cp->pool[(i+start) % cp->entries]; |
||||
if(cpe->dead) continue; |
||||
if(cpe->local && cp->local_cpe && !cp->local_cpe->dead) |
||||
return cp->local_cpe; |
||||
return cpe; |
||||
} |
||||
return NULL; |
||||
} |
||||
|
||||
|
||||
/*
|
||||
* Local Variables: |
||||
* mode: c |
||||
* c-basic-offset: 4 |
||||
* tab-width: 8 |
||||
* End:
|
||||
* vim: set cindent smartindent autoindent softtabstop=4 shiftwidth=4 tabstop=8:
|
||||
*/ |
||||
@ -0,0 +1,48 @@ |
||||
#ifndef _CONNPOOL_H |
||||
#define _CONNPOOL_H |
||||
|
||||
#if HAVE_CONFIG_H |
||||
#include "clamav-config.h" |
||||
#endif |
||||
|
||||
#include <sys/socket.h> |
||||
#include <netinet/in.h> |
||||
#include <netinet/ip.h> |
||||
|
||||
#include "shared/cfgparser.h" |
||||
|
||||
struct CP_ENTRY { |
||||
uint8_t type; |
||||
uint8_t dead; |
||||
uint8_t local; |
||||
time_t last_poll; |
||||
struct sockaddr *server; |
||||
socklen_t socklen; |
||||
#ifdef HAVE_GETADDRINFO |
||||
void *gai; |
||||
#endif |
||||
}; |
||||
|
||||
struct CPOOL { |
||||
unsigned int entries; |
||||
unsigned int alive; |
||||
struct CP_ENTRY *local_cpe; |
||||
struct CP_ENTRY *pool; |
||||
}; |
||||
|
||||
void cpool_init(struct cfgstruct *copt); |
||||
void cpool_free(void); |
||||
struct CP_ENTRY *cpool_get_rand(void); |
||||
|
||||
extern struct CPOOL *cp; |
||||
|
||||
#endif |
||||
|
||||
/*
|
||||
* Local Variables: |
||||
* mode: c |
||||
* c-basic-offset: 4 |
||||
* tab-width: 8 |
||||
* End:
|
||||
* vim: set cindent smartindent autoindent softtabstop=4 shiftwidth=4 tabstop=8:
|
||||
*/ |
||||
@ -0,0 +1,274 @@ |
||||
/*
|
||||
* Copyright (C)2008 Sourcefire, Inc. |
||||
* |
||||
* Author: aCaB <acab@clamav.net> |
||||
* |
||||
* This program is free software; you can redistribute it and/or modify |
||||
* it under the terms of the GNU General Public License version 2 as |
||||
* published by the Free Software Foundation. |
||||
* |
||||
* This program is distributed in the hope that it will be useful, |
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||||
* GNU General Public License for more details. |
||||
* |
||||
* You should have received a copy of the GNU General Public License |
||||
* along with this program; if not, write to the Free Software |
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, |
||||
* MA 02110-1301, USA. |
||||
*/ |
||||
|
||||
#if HAVE_CONFIG_H |
||||
#include "clamav-config.h" |
||||
#endif |
||||
|
||||
#include <stdio.h> |
||||
#include <string.h> |
||||
#include <stdlib.h> |
||||
#include <sys/types.h> |
||||
#include <sys/socket.h> |
||||
#include <unistd.h> |
||||
#include <fcntl.h> |
||||
#include <sys/time.h> |
||||
#include <sys/select.h> |
||||
#include <time.h> |
||||
#include <errno.h> |
||||
|
||||
#include "shared/output.h" |
||||
#include "netcode.h" |
||||
|
||||
|
||||
/* FIXME: for connect and send */ |
||||
#define TIMEOUT 60 |
||||
/* for recv */ |
||||
long readtimeout; |
||||
|
||||
|
||||
int nc_socket(struct CP_ENTRY *cpe) { |
||||
int flags, s = socket(cpe->server->sa_family, SOCK_STREAM, 0); |
||||
|
||||
if (s == -1) return -1; |
||||
flags = fcntl(s, F_GETFL, 0); |
||||
if (flags == -1) { |
||||
close(s); |
||||
return -1; |
||||
} |
||||
flags |= O_NONBLOCK; |
||||
if (fcntl(s, F_SETFL, flags) == -1) { |
||||
close(s); |
||||
return -1; |
||||
} |
||||
return s; |
||||
} |
||||
|
||||
|
||||
int nc_connect(int s, struct CP_ENTRY *cpe) { |
||||
time_t timeout = time(NULL) + TIMEOUT; |
||||
int res = connect(s, cpe->server, cpe->socklen); |
||||
struct timeval tv; |
||||
|
||||
if (!res) return 0; |
||||
if (errno != EINPROGRESS) { |
||||
close(s); |
||||
return -1; |
||||
} |
||||
|
||||
tv.tv_sec = TIMEOUT; |
||||
tv.tv_usec = 0; |
||||
while(1) { |
||||
fd_set fds; |
||||
int s_err; |
||||
socklen_t s_len = sizeof(s_err); |
||||
|
||||
FD_ZERO(&fds); |
||||
FD_SET(s, &fds); |
||||
res = select(s+1, NULL, &fds, NULL, &tv); |
||||
if(res < 1) { |
||||
time_t now; |
||||
if (res == -1 && errno == EINTR && ((now = time(NULL)) < timeout)) { |
||||
tv.tv_sec = timeout - now; |
||||
tv.tv_usec = 0; |
||||
continue; |
||||
} |
||||
close(s); |
||||
return -1; |
||||
} |
||||
if (getsockopt(s, SOL_SOCKET, SO_ERROR, &s_err, &s_len) || s_err) { |
||||
logg("!getsockopt failed: %s\n", strerror(s_err)); |
||||
close(s); |
||||
return -1; |
||||
} |
||||
return 0; |
||||
} |
||||
} |
||||
|
||||
|
||||
int nc_send(int s, const void *buf, size_t len) { |
||||
while(len) { |
||||
int res = send(s, buf, len, 0); |
||||
time_t timeout = time(NULL) + TIMEOUT; |
||||
struct timeval tv; |
||||
|
||||
if(res!=-1) { |
||||
len-=res; |
||||
buf+=res; |
||||
timeout = time(NULL) + TIMEOUT; |
||||
continue; |
||||
} |
||||
if(errno != EAGAIN && errno != EWOULDBLOCK) { |
||||
close(s); |
||||
return 1; |
||||
} |
||||
|
||||
tv.tv_sec = TIMEOUT; |
||||
tv.tv_usec = 0; |
||||
while(1) { |
||||
fd_set fds; |
||||
int s_err; |
||||
socklen_t s_len = sizeof(s_err); |
||||
|
||||
FD_ZERO(&fds); |
||||
FD_SET(s, &fds); |
||||
res = select(s+1, NULL, &fds, NULL, &tv); |
||||
if(res < 1) { |
||||
time_t now; |
||||
if (res == -1 && errno == EINTR && ((now = time(NULL)) < timeout)) { |
||||
tv.tv_sec = timeout - now; |
||||
tv.tv_usec = 0; |
||||
continue; |
||||
} |
||||
close(s); |
||||
return 1; |
||||
} |
||||
if (getsockopt(s, SOL_SOCKET, SO_ERROR, &s_err, &s_len)) { |
||||
close(s); |
||||
return 1; |
||||
} |
||||
len-=s_len; |
||||
buf+=s_len; |
||||
break; |
||||
} |
||||
} |
||||
return 0; |
||||
} |
||||
|
||||
|
||||
char *nc_recv(int s) { |
||||
char buf[BUFSIZ], *ret=NULL; |
||||
time_t timeout = time(NULL) + readtimeout; |
||||
struct timeval tv; |
||||
fd_set fds; |
||||
int res; |
||||
|
||||
tv.tv_sec = readtimeout; |
||||
tv.tv_usec = 0; |
||||
|
||||
FD_ZERO(&fds); |
||||
FD_SET(s, &fds); |
||||
while(1) { |
||||
res = select(s+1, &fds, NULL, NULL, &tv); |
||||
if(res<1) { |
||||
time_t now; |
||||
if (res == -1 && errno == EINTR && ((now = time(NULL)) < timeout)) { |
||||
tv.tv_sec = timeout - now; |
||||
tv.tv_usec = 0; |
||||
continue; |
||||
} |
||||
close(s); |
||||
return NULL; |
||||
} |
||||
break; |
||||
} |
||||
/* FIXME: check for EOL@EObuf ? */ |
||||
res = recv(s, buf, sizeof(buf), 0); |
||||
if (res==-1 || !(ret = (char *)malloc(res+1))) { |
||||
close(s); |
||||
return NULL; |
||||
} |
||||
memcpy(ret, buf, res); |
||||
ret[res]='\0'; |
||||
return ret; |
||||
} |
||||
|
||||
|
||||
int nc_connect_entry(struct CP_ENTRY *cpe) { |
||||
int s = nc_socket(cpe); |
||||
if(s==-1) return -1; |
||||
return nc_connect(s, cpe) ? -1 : s; |
||||
} |
||||
|
||||
|
||||
void nc_ping_entry(struct CP_ENTRY *cpe) { |
||||
int s = nc_connect_entry(cpe); |
||||
char *reply; |
||||
if (s!=-1 && !nc_send(s, "nPING\n", 6) && (reply = nc_recv(s))) { |
||||
cpe->dead = strcmp(reply, "PONG\n")!=0; |
||||
free(reply); |
||||
close(s); |
||||
} else cpe->dead = 1; |
||||
} |
||||
|
||||
|
||||
int nc_connect_rand(int *main, int *alt, int *local) { |
||||
struct CP_ENTRY *cpe = cpool_get_rand(); |
||||
|
||||
if(!cpe) return 1; |
||||
*local = cpe->local; |
||||
if ((*main = nc_connect_entry(cpe)) == -1) return 1; /* FIXME : this should be delayed till eom if local */ |
||||
if(*local) { |
||||
char tmpn[] = "/tmp/clamav-milter-XXXXXX";
|
||||
if((*alt = mkstemp(tmpn))==-1) { /* FIXME */ |
||||
logg("!Failed to create temporary file\n"); |
||||
close(*main); |
||||
return 1; |
||||
} |
||||
unlink(tmpn); |
||||
} else { |
||||
char *reply=NULL, *port; |
||||
int nport; |
||||
struct CP_ENTRY new_cpe; |
||||
union { |
||||
struct sockaddr_in sa4; |
||||
struct sockaddr_in6 sa6; |
||||
} sa; |
||||
|
||||
if(nc_send(*main, "nSTREAM\n", 8) || !(reply = nc_recv(*main)) || !(port = strstr(reply, "PORT"))) { |
||||
logg("!Failed to communicate with clamd\n"); |
||||
if(reply) free(reply); |
||||
close(*main); |
||||
return 1; |
||||
} |
||||
port+=5; |
||||
sscanf(port, "%d", &nport); |
||||
free(reply); |
||||
if(cpe->server->sa_family == AF_INET && cpe->socklen == sizeof(struct sockaddr_in)) { |
||||
memcpy(&sa, cpe->server, sizeof(struct sockaddr_in)); |
||||
sa.sa4.sin_port = htons(nport); |
||||
new_cpe.socklen = sizeof(struct sockaddr_in); |
||||
} else if(cpe->server->sa_family == AF_INET6 && cpe->socklen == sizeof(struct sockaddr_in6)) { |
||||
memcpy(&sa, cpe->server, sizeof(struct sockaddr_in6)); |
||||
sa.sa6.sin6_port = htons(nport); |
||||
new_cpe.socklen = sizeof(struct sockaddr_in6); |
||||
} else { |
||||
logg("!WTF WHY AM I DOING HERE???"); |
||||
close(*main); |
||||
return 1; |
||||
} |
||||
new_cpe.server = (struct sockaddr *)&sa; |
||||
if ((*alt = nc_connect_entry(&new_cpe)) == -1) { |
||||
logg("!Failed to communicate with clamd for streaming\n"); |
||||
close(*main); |
||||
return 1; |
||||
} |
||||
} |
||||
return 0; |
||||
} |
||||
|
||||
/*
|
||||
* Local Variables: |
||||
* mode: c |
||||
* c-basic-offset: 4 |
||||
* tab-width: 8 |
||||
* End:
|
||||
* vim: set cindent smartindent autoindent softtabstop=4 shiftwidth=4 tabstop=8:
|
||||
*/ |
||||
@ -0,0 +1,13 @@ |
||||
#ifndef _NETCODE_H |
||||
#define _NETCODE_H |
||||
|
||||
#include "connpool.h" |
||||
|
||||
void nc_ping_entry(struct CP_ENTRY *cpe); |
||||
int nc_connect_rand(int *main, int *alt, int *local); |
||||
int nc_send(int s, const void *buf, size_t len); |
||||
char *nc_recv(int s); |
||||
|
||||
extern long readtimeout; |
||||
|
||||
#endif |
||||
@ -0,0 +1,166 @@ |
||||
## |
||||
## Example config file for the clamav-milter |
||||
## /* FIXME : NOT DONE YET */ Please read the clamav-milter.conf(5) manual before editing this file. |
||||
## |
||||
|
||||
# Comment or remove the line below. |
||||
Example |
||||
|
||||
|
||||
## |
||||
## Main options |
||||
## |
||||
|
||||
# Define the interface through which we communicate with sendmail |
||||
# This option is mandatory! Possible formats are: |
||||
# [[unix|local]:]/path/to/file - to specify a unix domain socket |
||||
# inet:port@[hostname|ip-address] - to specify an ipv4 socket |
||||
# inet6:port@[hostname|ip-address] - to specify an ipv6 socket |
||||
#Default: no default |
||||
##MilterSocket /tmp/clamav-milter.socket |
||||
##MilterSocket inet:7357 |
||||
|
||||
# Remove stale socket after unclean shutdown. |
||||
# Default: yes |
||||
#FixStaleSocket yes |
||||
|
||||
# Maximum number of threads running at the same time. |
||||
# Default: 10 |
||||
##MaxThreads 20 |
||||
|
||||
# Run as another user (clamav-milter must be started by root for this option to work) |
||||
# Default: don't drop privileges |
||||
##User clamav |
||||
|
||||
# Initialize supplementary group access (clamd must be started by root). |
||||
# Default: no |
||||
##AllowSupplementaryGroups no |
||||
|
||||
# Waiting for data from clamd will timeout after this time (seconds). |
||||
# Value of 0 disables the timeout. |
||||
# Default: 120 |
||||
##ReadTimeout 300 |
||||
|
||||
# Don't fork into background. |
||||
# Default: no |
||||
##Foreground yes |
||||
|
||||
|
||||
## |
||||
## Clamd options |
||||
## |
||||
|
||||
# Define the clamd socket to connect to for scanning. |
||||
# If not set (the default), clamav-milter uses internal mode. |
||||
# This option is mandatory! Syntax: |
||||
# ClamdSocket unix:path |
||||
# ClamdSocket tcp:host:port |
||||
# The first syntax specifies a local unix socket (needs an bsolute path) e.g.: |
||||
# ClamdSocket unix:/var/run/clamd/clamd.socket |
||||
# The second syntax specifies a tcp local or remote tcp socket: the |
||||
# host can be a hostname or an ip address; the ":port" field is only required |
||||
# for IPv6 addresses, otherwise it defaults to 3310 |
||||
# ClamdSocket tcp:192.168.0.1 |
||||
# |
||||
# This option can be repeated several times with different sockets or even |
||||
# with the same socket: clamd servers will be selected in a round-robin fashion. |
||||
# |
||||
# Default: no default |
||||
##ClamdSocket tcp:scanner.mydomain:7357 |
||||
|
||||
# WARNING: The following options are deprecated and may go away soon. |
||||
# Please use ClamdSocket instead! |
||||
# Default: disabled |
||||
#LocalSocket |
||||
#TCPSocket |
||||
#TCPAddr |
||||
|
||||
|
||||
## |
||||
## Logging options |
||||
## |
||||
|
||||
# Uncomment this option to enable logging. |
||||
# LogFile must be writable for the user running daemon. |
||||
# A full path is required. |
||||
# Default: disabled |
||||
##LogFile /tmp/clamav-milter.log |
||||
|
||||
# By default the log file is locked for writing - the lock protects against |
||||
# running clamav-milter multiple times. |
||||
# This option disables log file locking. |
||||
# Default: no |
||||
##LogFileUnlock yes |
||||
|
||||
# Maximum size of the log file. |
||||
# Value of 0 disables the limit. |
||||
# You may use 'M' or 'm' for megabytes (1M = 1m = 1048576 bytes) |
||||
# and 'K' or 'k' for kilobytes (1K = 1k = 1024 bytes). To specify the size |
||||
# in bytes just don't use modifiers. |
||||
# Default: 1M |
||||
##LogFileMaxSize 2M |
||||
|
||||
# Log time with each message. |
||||
# Default: no |
||||
##LogTime yes |
||||
|
||||
# Also log clean files. Useful in debugging but drastically increases the |
||||
# log size. |
||||
# Default: no |
||||
##LogClean yes |
||||
|
||||
# Use system logger (can work together with LogFile). |
||||
# Default: no |
||||
##LogSyslog yes |
||||
|
||||
# Specify the type of syslog messages - please refer to 'man syslog' |
||||
# for facility names. |
||||
# Default: LOG_LOCAL6 |
||||
##LogFacility LOG_MAIL |
||||
|
||||
# Enable verbose logging. |
||||
# Default: no |
||||
##LogVerbose yes |
||||
|
||||
|
||||
## |
||||
## Limits |
||||
## |
||||
|
||||
# Files larger than this value won't be scanned. |
||||
# Default: 25M |
||||
##MaxFileSize 150M |
||||
|
||||
# WARNING: The following two options are deprecated and may go away soon. |
||||
# Please use MaxFile size instead! |
||||
# For compatibility reasons the minimum value among MaxFileSize, |
||||
# MaxScanSize and StreamMaxLength will be used. |
||||
#MaxScanSize |
||||
#StreamMaxLength |
||||
|
||||
|
||||
## |
||||
## Deprecated options |
||||
## |
||||
|
||||
# The following deprecated options are only kept for compatibility |
||||
# reaosns and may go away soon. These do not affect clamav-milter |
||||
# in any way, except for a small warning emitted on startup. |
||||
|
||||
#ArchiveBlockEncrypted |
||||
#DatabaseDirectory |
||||
#Debug |
||||
#DetectBrokenExecutables |
||||
#LeaveTemporaryFiles |
||||
#MailFollowURLs |
||||
#MaxRecursion |
||||
#MaxFiles |
||||
#PhishingSignatures |
||||
#PidFile |
||||
#ScanArchive |
||||
#ScanHTML |
||||
#ScanMail |
||||
#ScanOLE2 |
||||
#ScanPE |
||||
#TemporaryDirectory |
||||
|
||||
Loading…
Reference in new issue