e3aaff8e |
/* |
f6870133 |
* Copyright (C) 2009 Sourcefire, Inc.
*
* Authors: Tomasz Kojm, aCaB |
e3aaff8e |
*
* This program is free software; you can redistribute it and/or modify |
bb34cb31 |
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation. |
e3aaff8e |
*
* 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 |
48b7b4a7 |
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
* MA 02110-1301, USA. |
e3aaff8e |
*/
|
6d6e8271 |
#if HAVE_CONFIG_H
#include "clamav-config.h"
#endif
|
e3aaff8e |
#include <stdio.h> |
87066fbc |
#include <stdlib.h> |
ab1ed22f |
#ifdef HAVE_UNISTD_H |
63abd169 |
#include <unistd.h> |
ab1ed22f |
#endif |
63abd169 |
#include <string.h> |
e3aaff8e |
#include <sys/types.h> |
afb48b28 |
#include <sys/stat.h> |
e3aaff8e |
#include <sys/socket.h> |
cfef445d |
#ifdef HAVE_SYS_LIMITS_H
#include <sys/limits.h>
#endif |
befa580a |
#include <sys/select.h> |
e3aaff8e |
#include <sys/un.h>
#include <netinet/in.h>
#include <arpa/inet.h> |
f8f80da9 |
#include <netdb.h> |
63abd169 |
#include <utime.h>
#include <errno.h> |
87066fbc |
#include <dirent.h> |
07e8d00f |
#include <fcntl.h> |
e3aaff8e |
|
7708ddfc |
#ifdef HAVE_SYS_UIO_H
#include <sys/uio.h>
#endif
|
a68d5e2f |
#include "shared/optparser.h"
#include "shared/output.h"
#include "shared/misc.h" |
ee6702ab |
#include "shared/actions.h" |
a68d5e2f |
#include "libclamav/str.h" |
6630b2a9 |
#include "libclamav/others.h" |
a68d5e2f |
|
fc83da82 |
#include "client.h" |
f6870133 |
#include "proto.h" |
e3aaff8e |
|
07e8bf35 |
#ifndef INADDR_LOOPBACK
#define INADDR_LOOPBACK 0x7f000001 |
e871e527 |
#endif |
fe6c6a02 |
|
f6870133 |
struct sockaddr *mainsa = NULL;
int mainsasz; |
9877f2d8 |
unsigned long int maxstream; |
a659e393 |
static struct sockaddr_un nixsock;
static struct sockaddr_in tcpsock; |
7708ddfc |
|
fadd3046 |
/* Inits the communication layer
* Returns 0 if clamd is local, non zero if clamd is remote */ |
a659e393 |
static int isremote(const struct optstruct *opts) {
int s, ret;
const struct optstruct *opt;
struct optstruct *clamdopts;
const char *clamd_conf = optget(opts, "config-file")->strarg; |
befa580a |
static struct sockaddr_in testsock; |
fe6c6a02 |
|
a659e393 |
if((clamdopts = optparse(clamd_conf, 0, NULL, 1, OPT_CLAMD, 0, NULL)) == NULL) {
logg("!Can't parse clamd configuration file %s\n", clamd_conf);
return 0; |
040a5084 |
} |
e9896728 |
if((opt = optget(clamdopts, "LocalSocket"))->enabled) { |
a659e393 |
memset((void *)&nixsock, 0, sizeof(nixsock));
nixsock.sun_family = AF_UNIX;
strncpy(nixsock.sun_path, opt->strarg, sizeof(nixsock.sun_path));
nixsock.sun_path[sizeof(nixsock.sun_path)-1]='\0';
mainsa = (struct sockaddr *)&nixsock;
mainsasz = sizeof(nixsock);
optfree(clamdopts);
return 0; |
7062124c |
} |
a659e393 |
if(!(opt = optget(clamdopts, "TCPSocket"))->enabled) {
optfree(clamdopts);
return 0; |
fe6c6a02 |
} |
a659e393 |
mainsa = (struct sockaddr *)&tcpsock;
mainsasz = sizeof(tcpsock); |
7a997ac9 |
if (cfg_tcpsock(clamdopts, &tcpsock, INADDR_LOOPBACK) == -1) {
logg("!Can't lookup clamd hostname: %s.\n", strerror(errno)); |
a659e393 |
optfree(clamdopts);
mainsa = NULL;
return 0; |
fe6c6a02 |
} |
7a997ac9 |
optfree(clamdopts); |
befa580a |
memcpy((void *)&testsock, (void *)&tcpsock, sizeof(testsock));
testsock.sin_port = htons(INADDR_ANY);
if(!(s = socket(testsock.sin_family, SOCK_STREAM, 0))) return 0;
ret = (bind(s, (struct sockaddr *)&testsock, sizeof(testsock)) != 0); |
a659e393 |
close(s);
return ret; |
fe6c6a02 |
}
|
cfef445d |
|
fadd3046 |
/* Turns a relative path into an absolute one
* Returns a pointer to the path (which must be
* freed by the caller) or NULL on error */ |
befa580a |
static char *makeabs(const char *basepath) { |
b1442ed1 |
int namelen; |
befa580a |
char *ret; |
fe6c6a02 |
|
befa580a |
if(!(ret = malloc(PATH_MAX + 1))) {
logg("^Can't make room for fullpath.\n"); |
fe6c6a02 |
return NULL; |
befa580a |
}
if(*basepath != '/') { /* FIXME: to be unified */
if(!getcwd(ret, PATH_MAX)) { |
0ae41a2d |
logg("^Can't get absolute pathname of current working directory.\n"); |
befa580a |
free(ret); |
fe6c6a02 |
return NULL;
} |
befa580a |
namelen = strlen(ret);
snprintf(&ret[namelen], PATH_MAX - namelen, "/%s", basepath); |
e3aaff8e |
} else { |
befa580a |
strncpy(ret, basepath, PATH_MAX); |
e3aaff8e |
} |
befa580a |
ret[PATH_MAX] = '\0';
return ret;
} |
e3aaff8e |
|
fadd3046 |
/* Recursively scans a path with the given scantype
* Returns non zero for serious errors, zero otherwise */ |
5b1eee09 |
static int client_scan(const char *file, int scantype, int *infected, int maxlevel, int session, int flags) { |
befa580a |
int ret;
char *fullpath = makeabs(file);
|
fadd3046 |
if(!fullpath)
return 0; |
befa580a |
if (!session) |
5b1eee09 |
ret = serial_client_scan(fullpath, scantype, infected, maxlevel, flags); |
befa580a |
else |
5b1eee09 |
ret = parallel_client_scan(fullpath, scantype, infected, maxlevel, flags); |
befa580a |
free(fullpath); |
fadd3046 |
return ret; |
fe6c6a02 |
} |
e3aaff8e |
|
a68d5e2f |
int get_clamd_version(const struct optstruct *opts) |
2a363377 |
{ |
a659e393 |
char *buff;
int len, sockd;
struct RCVLN rcv; |
2a363377 |
|
a659e393 |
isremote(opts);
if(!mainsa) return 2;
if((sockd = dconnect()) < 0) return 2; |
00f05166 |
recvlninit(&rcv, sockd); |
2a363377 |
|
a659e393 |
if(sendln(sockd, "zVERSION", 9)) { |
c1c9d9f9 |
close(sockd); |
2a363377 |
return 2;
}
|
a659e393 |
while((len = recvln(&rcv, &buff, NULL))) {
if(len == -1) {
logg("!Error occoured while receiving version information.\n");
break;
} |
2a363377 |
printf("%s\n", buff);
}
close(sockd);
return 0;
}
|
a68d5e2f |
int reload_clamd_database(const struct optstruct *opts) |
c1c9d9f9 |
{ |
a659e393 |
char *buff;
int len, sockd;
struct RCVLN rcv; |
c1c9d9f9 |
|
a659e393 |
isremote(opts);
if(!mainsa) return 2;
if((sockd = dconnect()) < 0) return 2; |
00f05166 |
recvlninit(&rcv, sockd); |
c1c9d9f9 |
|
a659e393 |
if(sendln(sockd, "zRELOAD", 8)) { |
c1c9d9f9 |
close(sockd);
return 2;
}
|
a659e393 |
if(!(len = recvln(&rcv, &buff, NULL)) || len < 10 || memcmp(buff, "RELOADING", 9)) { |
3bf21904 |
logg("!Clamd did not reload the database\n"); |
c1c9d9f9 |
close(sockd);
return 2;
}
close(sockd);
return 0;
}
|
a68d5e2f |
int client(const struct optstruct *opts, int *infected) |
fe6c6a02 |
{ |
07e8d00f |
const char *clamd_conf = optget(opts, "config-file")->strarg;
struct optstruct *clamdopts; |
ee6702ab |
int remote, scantype, session = 0, errors = 0, scandash = 0, maxrec, flags = 0; |
c2b6681b |
const char *fname; |
3bb3357a |
|
07e8d00f |
if((clamdopts = optparse(clamd_conf, 0, NULL, 1, OPT_CLAMD, 0, NULL)) == NULL) {
logg("!Can't parse clamd configuration file %s\n", clamd_conf);
return 2;
} |
e3aaff8e |
|
c2b6681b |
scandash = (opts->filename && opts->filename[0] && !strcmp(opts->filename[0], "-") && !optget(opts, "file-list")->enabled && !opts->filename[1]); |
408be01f |
remote = isremote(opts) | optget(opts, "stream")->enabled; |
07e8d00f |
#ifdef HAVE_FD_PASSING |
07e8bf35 |
if(!remote && optget(clamdopts, "LocalSocket")->enabled && (optget(opts, "fdpass")->enabled || scandash)) { |
07e8d00f |
scantype = FILDES;
session = optget(opts, "multiscan")->enabled; |
07e8bf35 |
} else |
07e8d00f |
#endif |
07e8bf35 |
if(remote || scandash) {
scantype = STREAM;
session = optget(opts, "multiscan")->enabled; |
07e8d00f |
} else if(optget(opts, "multiscan")->enabled) scantype = MULTI;
else scantype = CONT; |
e3aaff8e |
|
b1442ed1 |
maxrec = optget(clamdopts, "MaxDirectoryRecursion")->numarg; |
83c52f7e |
maxstream = optget(clamdopts, "StreamMaxLength")->numarg; |
ee6702ab |
if (optget(clamdopts, "FollowDirectorySymlinks")->enabled)
flags |= CLI_FTW_FOLLOW_DIR_SYMLINK;
if (optget(clamdopts, "FollowFileSymlinks")->enabled)
flags |= CLI_FTW_FOLLOW_FILE_SYMLINK; |
a1598d7c |
flags |= CLI_FTW_TRIM_SLASHES; |
07e8d00f |
optfree(clamdopts); |
e3aaff8e |
|
07e8d00f |
if(!mainsa) {
logg("!Clamd is not configured properly.\n");
return 2;
} |
e3aaff8e |
|
07e8d00f |
*infected = 0; |
afb48b28 |
|
07e8bf35 |
if(scandash) { |
a659e393 |
int sockd, ret; |
b120dc19 |
struct stat sb;
fstat(0, &sb);
if((sb.st_mode & S_IFMT) != S_IFREG) scantype = STREAM; |
22446430 |
if((sockd = dconnect()) >= 0 && (ret = dsresult(sockd, scantype, NULL, &ret)) >= 0) |
2b68c490 |
*infected = ret; |
fe6c6a02 |
else |
5b1eee09 |
errors = 1; |
1d9da95b |
if(sockd >= 0) close(sockd); |
c2b6681b |
} else if(opts->filename || optget(opts, "file-list")->enabled) {
if(opts->filename && optget(opts, "file-list")->enabled)
logg("^Only scanning files from --file-list (files passed at cmdline are ignored)\n");
while(!errors && (fname = filelist(opts, NULL))) {
if(!strcmp(fname, "-")) { |
07e8bf35 |
logg("!Scanning from standard input requires \"-\" to be the only file argument\n"); |
a659e393 |
continue; |
fe6c6a02 |
} |
c2b6681b |
errors = client_scan(fname, scantype, infected, maxrec, session, flags); |
e3aaff8e |
} |
07e8d00f |
} else { |
5b1eee09 |
errors = client_scan("", scantype, infected, maxrec, session, flags); |
e3aaff8e |
} |
fe6c6a02 |
return *infected ? 1 : (errors ? 2 : 0); |
e3aaff8e |
} |