e3aaff8e |
/* |
c442ca9c |
* Copyright (C) 2013-2019 Cisco Systems, Inc. and/or its affiliates. All rights reserved.
* Copyright (C) 2009-2013 Sourcefire, Inc. |
f6870133 |
*
* 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> |
cfef445d |
#ifdef HAVE_SYS_LIMITS_H
#include <sys/limits.h>
#endif |
be4bf7f4 |
#ifdef HAVE_SYS_SELECT_H |
befa580a |
#include <sys/select.h> |
be4bf7f4 |
#endif |
4cd80898 |
#ifndef _WIN32
#include <sys/socket.h> |
e3aaff8e |
#include <sys/un.h>
#include <netinet/in.h>
#include <arpa/inet.h> |
f8f80da9 |
#include <netdb.h> |
63abd169 |
#include <utime.h> |
4cd80898 |
#endif |
63abd169 |
#include <errno.h> |
87066fbc |
#include <dirent.h> |
07e8d00f |
#include <fcntl.h> |
e3aaff8e |
|
7708ddfc |
#ifdef HAVE_SYS_UIO_H
#include <sys/uio.h>
#endif
|
60d8d2c3 |
#include "libclamav/clamav.h" |
a68d5e2f |
#include "shared/optparser.h"
#include "shared/output.h"
#include "shared/misc.h" |
ee6702ab |
#include "shared/actions.h" |
3507891f |
#include "shared/clamdcom.h"
|
a68d5e2f |
#include "libclamav/str.h" |
6630b2a9 |
#include "libclamav/others.h" |
a68d5e2f |
|
fc83da82 |
#include "client.h" |
f6870133 |
#include "proto.h" |
e3aaff8e |
|
9877f2d8 |
unsigned long int maxstream; |
4cd80898 |
#ifndef _WIN32 |
f0e1c2e6 |
struct sockaddr_un nixsock; |
4cd80898 |
#endif |
247fe3ae |
extern struct optstruct *clamdopts; |
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; |
1c1d726d |
char *ipaddr, port[10];
struct addrinfo hints, *info, *p;
int res; |
fe6c6a02 |
|
6df13d04 |
UNUSEDPARAM(opts);
|
be4bf7f4 |
#ifndef _WIN32 |
e9896728 |
if((opt = optget(clamdopts, "LocalSocket"))->enabled) { |
8c241c57 |
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';
return 0; |
7062124c |
} |
be4bf7f4 |
#endif |
247fe3ae |
if(!(opt = optget(clamdopts, "TCPSocket"))->enabled) |
8c241c57 |
return 0; |
247fe3ae |
|
1c1d726d |
snprintf(port, sizeof(port), "%lld", optget(clamdopts, "TCPSocket")->numarg); |
7a997ac9 |
|
8c241c57 |
opt = optget(clamdopts, "TCPAddr");
while (opt) { |
6432bbfb |
ipaddr = NULL;
if (opt->strarg) |
1c1d726d |
ipaddr = (!strcmp(opt->strarg, "any") ? NULL : opt->strarg);
memset(&hints, 0x00, sizeof(struct addrinfo));
hints.ai_family = AF_UNSPEC;
hints.ai_socktype = SOCK_STREAM;
hints.ai_flags = AI_PASSIVE;
if ((res = getaddrinfo(ipaddr, port, &hints, &info))) {
logg("!Can't lookup clamd hostname: %s\n", gai_strerror(res)); |
6432bbfb |
opt = opt->nextarg; |
1c1d726d |
continue; |
8c241c57 |
}
|
1c1d726d |
for (p = info; p != NULL; p = p->ai_next) {
if((s = socket(p->ai_family, p->ai_socktype, p->ai_protocol)) < 0) {
logg("isremote: socket() returning: %s.\n", strerror(errno));
continue;
}
|
f0e1c2e6 |
switch (p->ai_family) {
case AF_INET:
((struct sockaddr_in *)(p->ai_addr))->sin_port = htons(INADDR_ANY);
break;
case AF_INET6:
((struct sockaddr_in6 *)(p->ai_addr))->sin6_port = htons(INADDR_ANY);
break;
default:
break;
}
ret = bind(s, p->ai_addr, p->ai_addrlen); |
1c1d726d |
if (ret) { |
f0e1c2e6 |
if (errno == EADDRINUSE) {
/*
* If we can't bind, then either we're attempting to listen on an IP that isn't
* ours or that clamd is already listening on.
*/
closesocket(s);
freeaddrinfo(info);
return 0;
}
closesocket(s); |
1c1d726d |
freeaddrinfo(info);
return 1;
} |
f0e1c2e6 |
closesocket(s); |
8c241c57 |
}
|
1c1d726d |
freeaddrinfo(info); |
8c241c57 |
|
6432bbfb |
opt = opt->nextarg; |
ae8f72fc |
} |
1c1d726d |
|
f0e1c2e6 |
return 0; |
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 |
} |
58481352 |
if(!cli_is_abspath(basepath)) { |
befa580a |
if(!getcwd(ret, PATH_MAX)) { |
0ae41a2d |
logg("^Can't get absolute pathname of current working directory.\n"); |
befa580a |
free(ret); |
fe6c6a02 |
return NULL;
} |
6df3bfc3 |
#ifdef _WIN32
if(*basepath == '\\') {
namelen = 2;
basepath++;
} else
#endif |
befa580a |
namelen = strlen(ret); |
58481352 |
snprintf(&ret[namelen], PATH_MAX - namelen, PATHSEP"%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 */ |
dd5092ff |
static int client_scan(const char *file, int scantype, int *infected, int *err, int maxlevel, int session, int flags) { |
befa580a |
int ret;
char *fullpath = makeabs(file);
|
fadd3046 |
if(!fullpath)
return 0; |
befa580a |
if (!session) |
dd5092ff |
ret = serial_client_scan(fullpath, scantype, infected, err, maxlevel, flags); |
befa580a |
else |
dd5092ff |
ret = parallel_client_scan(fullpath, scantype, infected, err, 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((sockd = dconnect()) < 0) return 2; |
00f05166 |
recvlninit(&rcv, sockd); |
2a363377 |
|
a659e393 |
if(sendln(sockd, "zVERSION", 9)) { |
081f6473 |
closesocket(sockd); |
2a363377 |
return 2;
}
|
a659e393 |
while((len = recvln(&rcv, &buff, NULL))) {
if(len == -1) { |
7cd9337a |
logg("!Error occurred while receiving version information.\n"); |
a659e393 |
break;
} |
2a363377 |
printf("%s\n", buff);
}
|
081f6473 |
closesocket(sockd); |
2a363377 |
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((sockd = dconnect()) < 0) return 2; |
00f05166 |
recvlninit(&rcv, sockd); |
c1c9d9f9 |
|
a659e393 |
if(sendln(sockd, "zRELOAD", 8)) { |
081f6473 |
closesocket(sockd); |
c1c9d9f9 |
return 2;
}
|
a659e393 |
if(!(len = recvln(&rcv, &buff, NULL)) || len < 10 || memcmp(buff, "RELOADING", 9)) { |
3bf21904 |
logg("!Clamd did not reload the database\n"); |
081f6473 |
closesocket(sockd); |
c1c9d9f9 |
return 2;
} |
081f6473 |
closesocket(sockd); |
c1c9d9f9 |
return 0;
}
|
dd5092ff |
int client(const struct optstruct *opts, int *infected, int *err) |
fe6c6a02 |
{ |
ee6702ab |
int remote, scantype, session = 0, errors = 0, scandash = 0, maxrec, flags = 0; |
c2b6681b |
const char *fname; |
3bb3357a |
|
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; |
6ad45a29 |
}
else if(optget(opts, "multiscan")->enabled) scantype = MULTI;
else if(optget(opts, "allmatch")->enabled) scantype = ALLMATCH; |
07e8d00f |
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; |
e3aaff8e |
|
07e8d00f |
*infected = 0; |
afb48b28 |
|
07e8bf35 |
if(scandash) { |
a659e393 |
int sockd, ret; |
a2a004df |
STATBUF sb; |
eae1d361 |
if(FSTAT(0, &sb) < 0) {
logg("client.c: fstat failed for file name \"%s\", with %s\n.",
opts->filename[0], strerror(errno));
return 2;
} |
b120dc19 |
if((sb.st_mode & S_IFMT) != S_IFREG) scantype = STREAM; |
dd5092ff |
if((sockd = dconnect()) >= 0 && (ret = dsresult(sockd, scantype, NULL, &ret, NULL)) >= 0) |
2b68c490 |
*infected = ret; |
fe6c6a02 |
else |
5b1eee09 |
errors = 1; |
081f6473 |
if(sockd >= 0) closesocket(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");
|
dd5092ff |
while((fname = filelist(opts, NULL))) { |
c2b6681b |
if(!strcmp(fname, "-")) { |
07e8bf35 |
logg("!Scanning from standard input requires \"-\" to be the only file argument\n"); |
a659e393 |
continue; |
fe6c6a02 |
} |
dd5092ff |
errors += client_scan(fname, scantype, infected, err, maxrec, session, flags);
/* this may be too strict
if(errors >= 10) {
logg("!Too many errors\n");
break;
}
*/ |
e3aaff8e |
} |
07e8d00f |
} else { |
dd5092ff |
errors = client_scan("", scantype, infected, err, maxrec, session, flags); |
e3aaff8e |
} |
fe6c6a02 |
return *infected ? 1 : (errors ? 2 : 0); |
e3aaff8e |
} |