clamd/server-th.c
e3aaff8e
 /*
e1cbc270
  *  Copyright (C) 2013-2019 Cisco Systems, Inc. and/or its affiliates. All rights reserved.
  *  Copyright (C) 2007-2013 Sourcefire, Inc.
086eab5c
  *
  *  Authors: Tomasz Kojm, Trog, Török Edvin
e3aaff8e
  *
  *  This program is free software; you can redistribute it and/or modify
  *  it under the terms of the GNU General Public License as published by
  *  the Free Software Foundation; either version 2 of the License, or
  *  (at your option) any later version.
  *
  *  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
  */
 
98ac8d19
 #if HAVE_CONFIG_H
 #include "clamav-config.h"
 #endif
 
e3aaff8e
 #include <pthread.h>
c238ac42
 #include <errno.h>
e3aaff8e
 #include <signal.h>
c238ac42
 #include <stdio.h>
a9ebff44
 #include <string.h>
c238ac42
 #include <time.h>
a9ebff44
 #include <sys/types.h>
288057e9
 #ifndef _WIN32
a9ebff44
 #include <sys/socket.h>
9a03413e
 #include <sys/time.h>
 #include <sys/resource.h>
e0bb54d7
 #include <arpa/inet.h>
67118e92
 #endif
288057e9
 #ifdef HAVE_UNISTD_H
520cf7eb
 #include <unistd.h>
67118e92
 #endif
bd8603aa
 
0378a9ab
 #include <fcntl.h>
41feff65
 #ifdef C_SOLARIS
 #include <stdio_ext.h>
 #endif
bd8603aa
 #include "libclamav/clamav.h"
 
 #include "shared/output.h"
064b4a0c
 #include "shared/optparser.h"
b68375fd
 #include "shared/misc.h"
e3aaff8e
 
f4265a5a
 #include "shared/idmef_logging.h"
 
e3aaff8e
 #include "server.h"
c238ac42
 #include "thrmgr.h"
81131381
 #include "session.h"
c238ac42
 #include "others.h"
afb48b28
 #include "shared.h"
9e751804
 #include "libclamav/others.h"
3eba9d83
 #include "libclamav/readdb.h"
e3aaff8e
 
c238ac42
 #define BUFFSIZE 1024
e3aaff8e
 
288057e9
 int progexit                 = 0;
 pthread_mutex_t exit_mutex   = PTHREAD_MUTEX_INITIALIZER;
 int reload                   = 0;
 time_t reloaded_time         = 0;
c6266a2c
 pthread_mutex_t reload_mutex = PTHREAD_MUTEX_INITIALIZER;
288057e9
 int sighup                   = 0;
1361015c
 extern pthread_mutex_t logg_mutex;
949c6fe5
 static struct cl_stat dbstat;
81131381
 
288057e9
 void *event_wake_recv   = NULL;
3d7d59c1
 void *event_wake_accept = NULL;
5eca45b5
 
fc83da82
 static void scanner_thread(void *arg)
e3aaff8e
 {
288057e9
     client_conn_t *conn = (client_conn_t *)arg;
 #ifndef _WIN32
     sigset_t sigset;
67118e92
 #endif
288057e9
     int ret;
     int virus = 0, errors = 0;
c238ac42
 
288057e9
 #ifndef _WIN32
e3aaff8e
     /* ignore all signals */
     sigfillset(&sigset);
fdea27e5
     /* The behavior of a process is undefined after it ignores a
efac2f09
      * SIGFPE, SIGILL, SIGSEGV, or SIGBUS signal */
     sigdelset(&sigset, SIGFPE);
     sigdelset(&sigset, SIGILL);
     sigdelset(&sigset, SIGSEGV);
 #ifdef SIGBUS
     sigdelset(&sigset, SIGBUS);
 #endif
949c6fe5
     sigdelset(&sigset, SIGTSTP);
     sigdelset(&sigset, SIGCONT);
e3aaff8e
     pthread_sigmask(SIG_SETMASK, &sigset, NULL);
67118e92
 #endif
e3aaff8e
 
949c6fe5
     ret = command(conn, &virus);
     if (ret == -1) {
288057e9
         pthread_mutex_lock(&exit_mutex);
         progexit = 1;
         pthread_mutex_unlock(&exit_mutex);
         errors = 1;
949c6fe5
     } else
288057e9
         errors = ret;
45905a4a
 
deb30312
     thrmgr_setactiveengine(NULL);
949c6fe5
 
     if (conn->filename)
288057e9
         free(conn->filename);
fb6fe4f5
     logg("$Finished scanthread\n");
288057e9
     if (thrmgr_group_finished(conn->group, virus ? EXIT_OTHER : errors ? EXIT_ERROR : EXIT_OK)) {
         logg("$Scanthread: connection shut down (FD %d)\n", conn->sd);
         /* close connection if we were last in group */
         shutdown(conn->sd, 2);
         closesocket(conn->sd);
949c6fe5
     }
370892d0
     cl_engine_free(conn->engine);
5a66732f
     free(conn);
c238ac42
     return;
e3aaff8e
 }
 
b82eea8d
 static int syncpipe_wake_recv_w = -1;
 
c238ac42
 void sighandler_th(int sig)
e3aaff8e
 {
b82eea8d
     int action = 0;
288057e9
     switch (sig) {
         case SIGINT:
         case SIGTERM:
             progexit = 1;
             action   = 1;
             break;
 
 #ifdef SIGHUP
         case SIGHUP:
             sighup = 1;
             action = 1;
             break;
67118e92
 #endif
d056f4d6
 
288057e9
 #ifdef SIGUSR2
         case SIGUSR2:
             reload = 1;
             action = 1;
             break;
67118e92
 #endif
ae203685
 
288057e9
         default:
             break; /* Take no action on other signals - e.g. SIGPIPE */
c238ac42
     }
b82eea8d
     /* a signal doesn't always wake poll(), for example on FreeBSD */
     if (action && syncpipe_wake_recv_w != -1)
288057e9
         if (write(syncpipe_wake_recv_w, "", 1) != 1)
             logg("$Failed to write to syncpipe\n");
c238ac42
 }
e3aaff8e
 
064b4a0c
 static struct cl_engine *reload_db(struct cl_engine *engine, unsigned int dboptions, const struct optstruct *opts, int do_check, int *ret)
c238ac42
 {
288057e9
     const char *dbdir;
     int retval;
     unsigned int sigs            = 0;
     struct cl_settings *settings = NULL;
9e431a95
 
2dab0a15
     *ret = 0;
288057e9
     if (do_check) {
         if (!dbstat.entries) {
             logg("No stats for Database check - forcing reload\n");
             return engine;
         }
e3aaff8e
 
288057e9
         if (cl_statchkdir(&dbstat) == 1) {
             logg("SelfCheck: Database modification detected. Forcing reload.\n");
             return engine;
         } else {
             logg("SelfCheck: Database status OK.\n");
             return NULL;
         }
c238ac42
     }
e3aaff8e
 
395fb661
     /* release old structure */
288057e9
     if (engine) {
         /* copy current settings */
         settings = cl_engine_settings_copy(engine);
         if (!settings)
             logg("^Can't make a copy of the current engine settings\n");
395fb661
 
288057e9
         thrmgr_setactiveengine(NULL);
         cl_engine_free(engine);
395fb661
     }
 
064b4a0c
     dbdir = optget(opts, "DatabaseDirectory")->strarg;
c238ac42
     logg("Reading databases from %s\n", dbdir);
e3aaff8e
 
288057e9
     if (dbstat.entries)
         cl_statfree(&dbstat);
46c2e927
 
949c6fe5
     memset(&dbstat, 0, sizeof(struct cl_stat));
288057e9
     if ((retval = cl_statinidir(dbdir, &dbstat))) {
         logg("!cl_statinidir() failed: %s\n", cl_strerror(retval));
         *ret = 1;
         if (settings)
             cl_engine_settings_free(settings);
         return NULL;
     }
 
     if (!(engine = cl_engine_new())) {
         logg("!Can't initialize antivirus engine\n");
         *ret = 1;
         if (settings)
             cl_engine_settings_free(settings);
         return NULL;
     }
 
     if (settings) {
         retval = cl_engine_settings_apply(engine, settings);
         if (retval != CL_SUCCESS) {
             logg("^Can't apply previous engine settings: %s\n", cl_strerror(retval));
             logg("^Using default engine settings\n");
         }
         cl_engine_settings_free(settings);
c238ac42
     }
46c2e927
 
288057e9
     if ((retval = cl_load(dbdir, engine, &sigs, dboptions))) {
         logg("!reload db failed: %s\n", cl_strerror(retval));
         cl_engine_free(engine);
         *ret = 1;
         return NULL;
c238ac42
     }
e3aaff8e
 
288057e9
     if ((retval = cl_engine_compile(engine)) != 0) {
         logg("!Database initialization error: can't compile engine: %s\n", cl_strerror(retval));
         cl_engine_free(engine);
         *ret = 1;
         return NULL;
e3aaff8e
     }
1095156a
     logg("Database correctly reloaded (%u signatures)\n", sigs);
e3aaff8e
 
deb30312
     thrmgr_setactiveengine(engine);
a57e3d41
     return engine;
e3aaff8e
 }
 
4e24a361
 /*
  * zCOMMANDS are delimited by \0
  * nCOMMANDS are delimited by \n
  * Old-style non-prefixed commands are one packet, optionally delimited by \n,
  * with trailing \r|\n ignored
  */
537292a7
 static const char *get_cmd(struct fd_buf *buf, size_t off, size_t *len, char *term, int *oldstyle)
949c6fe5
 {
e4a0f2c9
     char *pos;
949c6fe5
     if (!buf->off || off >= buf->off) {
288057e9
         *len = 0;
         return NULL;
949c6fe5
     }
 
     *term = '\n';
456e31a3
     switch (buf->buffer[off]) {
288057e9
         /* commands terminated by delimiters */
         case 'z':
             *term = '\0';
         case 'n':
             pos = memchr(buf->buffer + off, *term, buf->off - off);
             if (!pos) {
                 /* we don't have another full command yet */
                 *len = 0;
                 return NULL;
             }
             *pos = '\0';
             if (*term) {
                 *len = cli_chomp(buf->buffer + off);
             } else {
                 *len = pos - buf->buffer - off;
             }
             *oldstyle = 0;
             return buf->buffer + off + 1;
         default:
             /* one packet = one command */
             if (off)
                 return NULL;
             pos = memchr(buf->buffer, '\n', buf->off);
             if (pos) {
                 *len = pos - buf->buffer;
                 *pos = '\0';
             } else {
                 *len                  = buf->off;
                 buf->buffer[buf->off] = '\0';
             }
             cli_chomp(buf->buffer);
             *oldstyle = 1;
             return buf->buffer;
949c6fe5
     }
 }
 
288057e9
 int statinidir_th(const char *dirname)
5327ca42
 {
288057e9
     if (!dbstat.entries) {
         memset(&dbstat, 0, sizeof(dbstat));
     }
5327ca42
 
288057e9
     return cl_statinidir(dirname, &dbstat);
5327ca42
 }
 
949c6fe5
 struct acceptdata {
     struct fd_data fds;
     struct fd_data recv_fds;
5f6edb22
     pthread_cond_t cond_nfds;
     int max_queue;
     int commandtimeout;
949c6fe5
     int syncpipe_wake_recv[2];
     int syncpipe_wake_accept[2];
 };
 
288057e9
 #define ACCEPTDATA_INIT(mutex1, mutex2)                                               \
     {                                                                                 \
         FDS_INIT(mutex1), FDS_INIT(mutex2), PTHREAD_COND_INITIALIZER, 0, 0, {-1, -1}, \
         {                                                                             \
             -1, -1                                                                    \
         }                                                                             \
     }
80681b50
 
949c6fe5
 static void *acceptloop_th(void *arg)
 {
     char buff[BUFFSIZE + 1];
     size_t i;
288057e9
     struct acceptdata *data  = (struct acceptdata *)arg;
     struct fd_data *fds      = &data->fds;
949c6fe5
     struct fd_data *recv_fds = &data->recv_fds;
288057e9
     int max_queue            = data->max_queue;
     int commandtimeout       = data->commandtimeout;
949c6fe5
 
7660b7cb
     pthread_mutex_lock(fds->buf_mutex);
949c6fe5
     for (;;) {
288057e9
         /* Block waiting for data to become available for reading */
         int new_sd = fds_poll_recv(fds, -1, 0, event_wake_accept);
80f4bd69
 #ifdef _WIN32
288057e9
         ResetEvent(event_wake_accept);
80f4bd69
 #endif
288057e9
         /* TODO: what about sockets that get rm-ed? */
         if (!fds->nfds) {
             /* no more sockets to poll, all gave an error */
             logg("!Main socket gone: fatal\n");
             break;
         }
 
         if (new_sd == -1 && errno != EINTR) {
             logg("!Failed to poll sockets, fatal\n");
             pthread_mutex_lock(&exit_mutex);
             progexit = 1;
             pthread_mutex_unlock(&exit_mutex);
             break;
         }
 
         /* accept() loop */
         for (i = 0; i < fds->nfds && new_sd >= 0; i++) {
             struct fd_buf *buf = &fds->buf[i];
             if (!buf->got_newdata)
                 continue;
80f4bd69
 #ifndef _WIN32
288057e9
             if (buf->fd == data->syncpipe_wake_accept[0]) {
                 /* dummy sync pipe, just to wake us */
                 if (read(buf->fd, buff, sizeof(buff)) < 0) {
                     logg("^Syncpipe read failed\n");
                 }
                 continue;
             }
1c4d9a22
 #endif
288057e9
             if (buf->got_newdata == -1) {
                 logg("$Acceptloop closed FD: %d\n", buf->fd);
                 shutdown(buf->fd, 2);
                 closesocket(buf->fd);
                 buf->fd = -1;
                 continue;
             }
 
             /* don't accept unlimited number of connections, or
6c03dc5d
 	         * we'll run out of file descriptors */
288057e9
             pthread_mutex_lock(recv_fds->buf_mutex);
             while (recv_fds->nfds > (unsigned)max_queue) {
                 pthread_mutex_lock(&exit_mutex);
                 if (progexit) {
                     pthread_mutex_unlock(&exit_mutex);
                     break;
                 }
                 pthread_mutex_unlock(&exit_mutex);
                 pthread_cond_wait(&data->cond_nfds, recv_fds->buf_mutex);
             }
             pthread_mutex_unlock(recv_fds->buf_mutex);
 
             pthread_mutex_lock(&exit_mutex);
             if (progexit) {
                 pthread_mutex_unlock(&exit_mutex);
                 break;
             }
             pthread_mutex_unlock(&exit_mutex);
 
             /* listen only socket */
             new_sd = accept(fds->buf[i].fd, NULL, NULL);
 
             if (new_sd >= 0) {
                 int ret, flags;
0378a9ab
 #ifdef F_GETFL
288057e9
                 flags = fcntl(new_sd, F_GETFL, 0);
                 if (flags != -1) {
                     if (fcntl(new_sd, F_SETFL, flags | O_NONBLOCK) == -1) {
                         logg("^Can't set socket to nonblocking mode, errno %d\n",
                              errno);
                     }
                 } else {
                     logg("^Can't get socket flags, errno %d\n", errno);
                 }
4d26118b
 #else
288057e9
                 logg("^Nonblocking sockets not available!\n");
0378a9ab
 #endif
288057e9
                 logg("$Got new connection, FD %d\n", new_sd);
                 pthread_mutex_lock(recv_fds->buf_mutex);
                 ret = fds_add(recv_fds, new_sd, 0, commandtimeout);
                 pthread_mutex_unlock(recv_fds->buf_mutex);
 
                 if (ret == -1) {
                     logg("!fds_add failed\n");
                     closesocket(new_sd);
                     continue;
                 }
 
                 /* notify recvloop */
1c4d9a22
 #ifdef _WIN32
288057e9
                 SetEvent(event_wake_recv);
1c4d9a22
 #else
288057e9
                 if (write(data->syncpipe_wake_recv[1], "", 1) == -1) {
                     logg("!write syncpipe failed\n");
                     continue;
                 }
1c4d9a22
 #endif
288057e9
             } else if (errno != EINTR) {
                 /* very bad - need to exit or restart */
fdea27e5
                 logg("!accept() failed: %s\n", cli_strerror(errno, buff, BUFFSIZE));
288057e9
                 /* give the poll loop a chance to close disconnected FDs */
                 break;
             }
         }
949c6fe5
 
288057e9
         /* handle progexit */
         pthread_mutex_lock(&exit_mutex);
         if (progexit) {
             pthread_mutex_unlock(&exit_mutex);
             break;
         }
         pthread_mutex_unlock(&exit_mutex);
949c6fe5
     }
7660b7cb
     pthread_mutex_unlock(fds->buf_mutex);
949c6fe5
 
288057e9
     if (sd_listen_fds(0) == 0) {
b68375fd
         /* only close the sockets, when not using systemd socket activation */
288057e9
         for (i = 0; i < fds->nfds; i++) {
b68375fd
             if (fds->buf[i].fd == -1)
                 continue;
             logg("$Shutdown: closed fd %d\n", fds->buf[i].fd);
             shutdown(fds->buf[i].fd, 2);
             closesocket(fds->buf[i].fd);
         }
949c6fe5
     }
b68375fd
 
949c6fe5
     fds_free(fds);
6b04dfd0
     pthread_mutex_destroy(fds->buf_mutex);
949c6fe5
     pthread_mutex_lock(&exit_mutex);
     progexit = 1;
     pthread_mutex_unlock(&exit_mutex);
1c4d9a22
 #ifdef _WIN32
5eca45b5
     SetEvent(event_wake_recv);
1c4d9a22
 #else
949c6fe5
     if (write(data->syncpipe_wake_recv[1], "", 1) < 0) {
288057e9
         logg("$Syncpipe write failed\n");
949c6fe5
     }
1c4d9a22
 #endif
949c6fe5
     return NULL;
 }
 
288057e9
 static const char *parse_dispatch_cmd(client_conn_t *conn, struct fd_buf *buf, size_t *ppos, int *error, const struct optstruct *opts, int readtimeout)
5d34634e
 {
e4a0f2c9
     const char *cmd = NULL;
5d34634e
     int rc;
     size_t cmdlen;
     char term;
     int oldstyle;
     size_t pos = *ppos;
     /* Parse & dispatch commands */
     while ((conn->mode == MODE_COMMAND) &&
288057e9
            (cmd = get_cmd(buf, pos, &cmdlen, &term, &oldstyle)) != NULL) {
         const char *argument;
         enum commands cmdtype;
         if (conn->group && oldstyle) {
             logg("$Received oldstyle command inside IDSESSION: %s\n", cmd);
             conn_reply_error(conn, "Only nCMDS\\n and zCMDS\\0 are accepted inside IDSESSION.");
             *error = 1;
             break;
         }
         cmdtype = parse_command(cmd, &argument, oldstyle);
         logg("$got command %s (%u, %u), argument: %s\n",
              cmd, (unsigned)cmdlen, (unsigned)cmdtype, argument ? argument : "");
         if (cmdtype == COMMAND_FILDES) {
             if (buf->buffer + buf->off <= cmd + strlen("FILDES\n")) {
                 /* we need the extra byte from recvmsg */
                 conn->mode = MODE_WAITANCILL;
                 buf->mode  = MODE_WAITANCILL;
                 /* put term back */
                 buf->buffer[pos + cmdlen] = term;
                 cmdlen                    = 0;
                 logg("$RECVTH: mode -> MODE_WAITANCILL\n");
                 break;
             }
             /* eat extra \0 for controlmsg */
             cmdlen++;
             logg("$RECVTH: FILDES command complete\n");
         }
         conn->term = term;
         buf->term  = term;
 
         if ((rc = execute_or_dispatch_command(conn, cmdtype, argument)) < 0) {
             logg("!Command dispatch failed\n");
             if (rc == -1 && optget(opts, "ExitOnOOM")->enabled) {
                 pthread_mutex_lock(&exit_mutex);
                 progexit = 1;
                 pthread_mutex_unlock(&exit_mutex);
             }
             *error = 1;
         }
         if (thrmgr_group_need_terminate(conn->group)) {
             logg("$Receive thread: have to terminate group\n");
             *error = CL_ETIMEOUT;
             break;
         }
         if (*error || !conn->group || rc) {
             if (rc && thrmgr_group_finished(conn->group, EXIT_OK)) {
                 logg("$Receive thread: closing conn (FD %d), group finished\n", conn->sd);
                 /* if there are no more active jobs */
                 shutdown(conn->sd, 2);
                 closesocket(conn->sd);
                 buf->fd     = -1;
                 conn->group = NULL;
             } else if (conn->mode != MODE_STREAM) {
                 logg("$mode -> MODE_WAITREPLY\n");
                 /* no more commands are accepted */
                 conn->mode = MODE_WAITREPLY;
                 /* Stop monitoring this FD, it will be closed either
6c03dc5d
                  * by us, or by the scanner thread.
                  * Never close a file descriptor that is being
                  * monitored by poll()/select() from another thread,
                  * because this can lead to subtle bugs such as:
                  * Other thread closes file descriptor -> POLLHUP is
                  * set, but the poller thread doesn't wake up yet.
                  * Another client opens a connection and sends some
                  * data. If the socket reuses the previous file descriptor,
                  * then POLLIN is set on the file descriptor too.
                  * When poll() wakes up it sees POLLIN | POLLHUP
                  * and thinks that the client has sent some data,
                  * and closed the connection, so clamd closes the
                  * connection in turn resulting in a bug.
                  *
                  * If we wouldn't have poll()-ed the file descriptor
                  * we closed in another thread, but rather made sure
                  * that we don't put a FD that we're about to close
                  * into poll()'s list of watched fds; then POLLHUP
                  * would be set, but the file descriptor would stay
                  * open, until we wake up from poll() and close it.
                  * Thus a new connection won't be able to reuse the
                  * same FD, and there is no bug.
                  */
288057e9
                 buf->fd = -1;
             }
         }
         /* we received a command, set readtimeout */
         time(&buf->timeout_at);
         buf->timeout_at += readtimeout;
         pos += cmdlen + 1;
         if (conn->mode == MODE_STREAM) {
             /* TODO: this doesn't belong here */
             buf->dumpname = conn->filename;
             buf->dumpfd   = conn->scanfd;
             logg("$Receive thread: INSTREAM: %s fd %u\n", buf->dumpname, buf->dumpfd);
         }
         if (conn->mode != MODE_COMMAND) {
             logg("$Breaking command loop, mode is no longer MODE_COMMAND\n");
             break;
         }
         conn->id++;
5d34634e
     }
288057e9
     *ppos      = pos;
     buf->mode  = conn->mode;
     buf->id    = conn->id;
5d34634e
     buf->group = conn->group;
     buf->quota = conn->quota;
     if (conn->scanfd != -1 && conn->scanfd != buf->dumpfd) {
288057e9
         logg("$Unclaimed file descriptor received, closing: %d\n", conn->scanfd);
         close(conn->scanfd);
         /* protocol error */
         conn_reply_error(conn, "PROTOCOL ERROR: ancillary data sent without FILDES.");
         *error = 1;
         return NULL;
5d34634e
     }
     if (!*error) {
288057e9
         /* move partial command to beginning of buffer */
         if (pos < buf->off) {
             memmove(buf->buffer, &buf->buffer[pos], buf->off - pos);
             buf->off -= pos;
         } else
             buf->off = 0;
         if (buf->off)
             logg("$Moved partial command: %lu\n", (unsigned long)buf->off);
         else
             logg("$Consumed entire command\n");
         /* adjust pos to account for the buffer shuffle */
         pos = 0;
5d34634e
     }
     *ppos = pos;
     return cmd;
 }
 
b44fbcf5
 /* static const unsigned char* parse_dispatch_cmd(client_conn_t *conn, struct fd_buf *buf, size_t *ppos, int *error, const struct optstruct *opts, int readtimeout) */
b6de553d
 static int handle_stream(client_conn_t *conn, struct fd_buf *buf, const struct optstruct *opts, int *error, size_t *ppos, int readtimeout)
5d34634e
 {
     int rc;
     size_t pos = *ppos;
     size_t cmdlen;
288057e9
 
5d34634e
     logg("$mode == MODE_STREAM\n");
65188cce
     /* we received some data, set readtimeout */
b6de553d
     time(&buf->timeout_at);
     buf->timeout_at += readtimeout;
65188cce
     while (pos <= buf->off) {
288057e9
         if (!buf->chunksize) {
             /* read chunksize */
             if (buf->off - pos >= 4) {
                 uint32_t cs;
                 memmove(&cs, buf->buffer + pos, 4);
                 pos += 4;
                 buf->chunksize = ntohl(cs);
                 logg("$Got chunksize: %u\n", buf->chunksize);
                 if (!buf->chunksize) {
                     /* chunksize 0 marks end of stream */
                     conn->scanfd = buf->dumpfd;
                     conn->term   = buf->term;
                     buf->dumpfd  = -1;
                     buf->mode    = buf->group ? MODE_COMMAND : MODE_WAITREPLY;
                     if (buf->mode == MODE_WAITREPLY)
                         buf->fd = -1;
                     logg("$Chunks complete\n");
                     buf->dumpname = NULL;
                     if ((rc = execute_or_dispatch_command(conn, COMMAND_INSTREAMSCAN, NULL)) < 0) {
                         logg("!Command dispatch failed\n");
                         if (rc == -1 && optget(opts, "ExitOnOOM")->enabled) {
                             pthread_mutex_lock(&exit_mutex);
                             progexit = 1;
                             pthread_mutex_unlock(&exit_mutex);
                         }
                         *error = 1;
                     } else {
                         memmove(buf->buffer, &buf->buffer[pos], buf->off - pos);
                         buf->off -= pos;
                         *ppos = 0;
                         buf->id++;
                         return 0;
65188cce
                     }
288057e9
                 }
                 if (buf->chunksize > buf->quota) {
                     logg("^INSTREAM: Size limit reached, (requested: %lu, max: %lu)\n",
                          (unsigned long)buf->chunksize, (unsigned long)buf->quota);
                     conn_reply_error(conn, "INSTREAM size limit exceeded.");
65188cce
                     *error = 1;
288057e9
                     *ppos  = pos;
                     return -1;
65188cce
                 } else {
288057e9
                     buf->quota -= buf->chunksize;
65188cce
                 }
288057e9
                 logg("$Quota Remaining: %lu\n", buf->quota);
             } else {
                 /* need more data, so return and wait for some */
                 memmove(buf->buffer, &buf->buffer[pos], buf->off - pos);
                 buf->off -= pos;
                 *ppos = 0;
65188cce
                 return -1;
             }
288057e9
         }
         if (pos + buf->chunksize < buf->off)
             cmdlen = buf->chunksize;
         else
             cmdlen = buf->off - pos;
         buf->chunksize -= cmdlen;
6c03dc5d
         if (cli_writen(buf->dumpfd, buf->buffer + pos, cmdlen) == (size_t)-1) {
288057e9
             conn_reply_error(conn, "Error writing to temporary file");
             logg("!INSTREAM: Can't write to temporary file.\n");
             *error = 1;
         }
         logg("$Processed %llu bytes of chunkdata, pos %llu\n", (long long unsigned)cmdlen, (long long unsigned)pos);
         pos += cmdlen;
         if (pos == buf->off) {
             buf->off = 0;
             pos      = 0;
             /* need more data, so return and wait for some */
             *ppos = pos;
65188cce
             return -1;
288057e9
         }
5d34634e
     }
     *ppos = pos;
     return 0;
 }
 
949c6fe5
 int recvloop_th(int *socketds, unsigned nsockets, struct cl_engine *engine, unsigned int dboptions, const struct optstruct *opts)
e3aaff8e
 {
288057e9
     int max_threads, max_queue, readtimeout, ret = 0;
     struct cl_scan_options options;
     char timestr[32];
 #ifndef _WIN32
     struct sigaction sigact;
     sigset_t sigset;
     struct rlimit rlim;
67118e92
 #endif
288057e9
     mode_t old_umask;
     const struct optstruct *opt;
     char buff[BUFFSIZE + 1];
     pid_t mainpid;
     int idletimeout;
     unsigned long long val;
     size_t i, j, rr_last = 0;
     pthread_t accept_th;
     pthread_mutex_t fds_mutex     = PTHREAD_MUTEX_INITIALIZER;
     pthread_mutex_t recvfds_mutex = PTHREAD_MUTEX_INITIALIZER;
     struct acceptdata acceptdata  = ACCEPTDATA_INIT(&fds_mutex, &recvfds_mutex);
     struct fd_data *fds           = &acceptdata.recv_fds;
     time_t start_time, current_time;
     unsigned int selfchk;
     threadpool_t *thr_pool;
6b8aa2d0
 
288057e9
 #ifndef _WIN32
     memset(&sigact, 0, sizeof(struct sigaction));
67118e92
 #endif
e3aaff8e
 
288057e9
     /* Initalize scan options struct */
     memset(&options, 0, sizeof(struct cl_scan_options));
048a88e6
 
370892d0
     /* set up limits */
b056e8e4
     if ((opt = optget(opts, "MaxScanTime"))->active) {
         if ((ret = cl_engine_set_num(engine, CL_ENGINE_MAX_SCANTIME, opt->numarg))) {
             logg("!cl_engine_set_num(CL_ENGINE_MAX_SCANTIME) failed: %s\n", cl_strerror(ret));
             cl_engine_free(engine);
             return 1;
         }
     }
     val = cl_engine_get_num(engine, CL_ENGINE_MAX_SCANTIME, NULL);
     if (val)
         logg("Limits: Global time limit set to %llu milliseconds.\n", val);
     else
         logg("^Limits: Global time limit protection disabled.\n");
 
288057e9
     if ((opt = optget(opts, "MaxScanSize"))->active) {
         if ((ret = cl_engine_set_num(engine, CL_ENGINE_MAX_SCANSIZE, opt->numarg))) {
             logg("!cl_engine_set_num(CL_ENGINE_MAX_SCANSIZE) failed: %s\n", cl_strerror(ret));
             cl_engine_free(engine);
             return 1;
         }
e3aaff8e
     }
2accc66f
     val = cl_engine_get_num(engine, CL_ENGINE_MAX_SCANSIZE, NULL);
288057e9
     if (val)
         logg("Limits: Global size limit set to %llu bytes.\n", val);
370892d0
     else
288057e9
         logg("^Limits: Global size limit protection disabled.\n");
281c7642
 
288057e9
     if ((opt = optget(opts, "MaxFileSize"))->active) {
         if ((ret = cl_engine_set_num(engine, CL_ENGINE_MAX_FILESIZE, opt->numarg))) {
             logg("!cl_engine_set_num(CL_ENGINE_MAX_FILESIZE) failed: %s\n", cl_strerror(ret));
             cl_engine_free(engine);
             return 1;
         }
23f5dfbd
     }
2accc66f
     val = cl_engine_get_num(engine, CL_ENGINE_MAX_FILESIZE, NULL);
288057e9
     if (val)
         logg("Limits: File size limit set to %llu bytes.\n", val);
370892d0
     else
288057e9
         logg("^Limits: File size limit protection disabled.\n");
e3aaff8e
 
e0bb54d7
 #ifndef _WIN32
288057e9
     if (getrlimit(RLIMIT_FSIZE, &rlim) == 0) {
         if (rlim.rlim_cur < (rlim_t)cl_engine_get_num(engine, CL_ENGINE_MAX_FILESIZE, NULL))
             logg("^System limit for file size is lower than engine->maxfilesize\n");
         if (rlim.rlim_cur < (rlim_t)cl_engine_get_num(engine, CL_ENGINE_MAX_SCANSIZE, NULL))
             logg("^System limit for file size is lower than engine->maxscansize\n");
9a03413e
     } else {
288057e9
         logg("^Cannot obtain resource limits for file size\n");
9a03413e
     }
 #endif
 
288057e9
     if ((opt = optget(opts, "MaxRecursion"))->active) {
         if ((ret = cl_engine_set_num(engine, CL_ENGINE_MAX_RECURSION, opt->numarg))) {
             logg("!cl_engine_set_num(CL_ENGINE_MAX_RECURSION) failed: %s\n", cl_strerror(ret));
             cl_engine_free(engine);
             return 1;
         }
23f5dfbd
     }
2accc66f
     val = cl_engine_get_num(engine, CL_ENGINE_MAX_RECURSION, NULL);
288057e9
     if (val)
         logg("Limits: Recursion level limit set to %u.\n", (unsigned int)val);
370892d0
     else
288057e9
         logg("^Limits: Recursion level limit protection disabled.\n");
370892d0
 
288057e9
     if ((opt = optget(opts, "MaxFiles"))->active) {
         if ((ret = cl_engine_set_num(engine, CL_ENGINE_MAX_FILES, opt->numarg))) {
             logg("!cl_engine_set_num(CL_ENGINE_MAX_FILES) failed: %s\n", cl_strerror(ret));
             cl_engine_free(engine);
             return 1;
         }
23f5dfbd
     }
2accc66f
     val = cl_engine_get_num(engine, CL_ENGINE_MAX_FILES, NULL);
288057e9
     if (val)
         logg("Limits: Files limit set to %u.\n", (unsigned int)val);
370892d0
     else
288057e9
         logg("^Limits: Files limit protection disabled.\n");
e3aaff8e
 
e0bb54d7
 #ifndef _WIN32
9390411b
     if (getrlimit(RLIMIT_CORE, &rlim) == 0) {
288057e9
         logg("*Limits: Core-dump limit is %lu.\n", (unsigned long)rlim.rlim_cur);
9390411b
     }
 #endif
23f5dfbd
 
b2726a53
     /* Engine max sizes */
 
288057e9
     if ((opt = optget(opts, "MaxEmbeddedPE"))->active) {
         if ((ret = cl_engine_set_num(engine, CL_ENGINE_MAX_EMBEDDEDPE, opt->numarg))) {
b2726a53
             logg("!cli_engine_set_num(CL_ENGINE_MAX_EMBEDDEDPE) failed: %s\n", cl_strerror(ret));
             cl_engine_free(engine);
             return 1;
         }
     }
     val = cl_engine_get_num(engine, CL_ENGINE_MAX_EMBEDDEDPE, NULL);
     logg("Limits: MaxEmbeddedPE limit set to %llu bytes.\n", val);
 
288057e9
     if ((opt = optget(opts, "MaxHTMLNormalize"))->active) {
         if ((ret = cl_engine_set_num(engine, CL_ENGINE_MAX_HTMLNORMALIZE, opt->numarg))) {
b2726a53
             logg("!cli_engine_set_num(CL_ENGINE_MAX_HTMLNORMALIZE) failed: %s\n", cl_strerror(ret));
             cl_engine_free(engine);
             return 1;
         }
     }
     val = cl_engine_get_num(engine, CL_ENGINE_MAX_HTMLNORMALIZE, NULL);
     logg("Limits: MaxHTMLNormalize limit set to %llu bytes.\n", val);
 
288057e9
     if ((opt = optget(opts, "MaxHTMLNoTags"))->active) {
         if ((ret = cl_engine_set_num(engine, CL_ENGINE_MAX_HTMLNOTAGS, opt->numarg))) {
b2726a53
             logg("!cli_engine_set_num(CL_ENGINE_MAX_HTMLNOTAGS) failed: %s\n", cl_strerror(ret));
             cl_engine_free(engine);
             return 1;
         }
     }
     val = cl_engine_get_num(engine, CL_ENGINE_MAX_HTMLNOTAGS, NULL);
     logg("Limits: MaxHTMLNoTags limit set to %llu bytes.\n", val);
 
288057e9
     if ((opt = optget(opts, "MaxScriptNormalize"))->active) {
         if ((ret = cl_engine_set_num(engine, CL_ENGINE_MAX_SCRIPTNORMALIZE, opt->numarg))) {
b2726a53
             logg("!cli_engine_set_num(CL_ENGINE_MAX_SCRIPTNORMALIZE) failed: %s\n", cl_strerror(ret));
             cl_engine_free(engine);
             return 1;
         }
     }
     val = cl_engine_get_num(engine, CL_ENGINE_MAX_SCRIPTNORMALIZE, NULL);
     logg("Limits: MaxScriptNormalize limit set to %llu bytes.\n", val);
 
288057e9
     if ((opt = optget(opts, "MaxZipTypeRcg"))->active) {
         if ((ret = cl_engine_set_num(engine, CL_ENGINE_MAX_ZIPTYPERCG, opt->numarg))) {
b2726a53
             logg("!cli_engine_set_num(CL_ENGINE_MAX_ZIPTYPERCG) failed: %s\n", cl_strerror(ret));
             cl_engine_free(engine);
             return 1;
         }
     }
     val = cl_engine_get_num(engine, CL_ENGINE_MAX_ZIPTYPERCG, NULL);
     logg("Limits: MaxZipTypeRcg limit set to %llu bytes.\n", val);
 
288057e9
     if ((opt = optget(opts, "MaxPartitions"))->active) {
         if ((ret = cl_engine_set_num(engine, CL_ENGINE_MAX_PARTITIONS, opt->numarg))) {
97fbb02b
             logg("!cli_engine_set_num(MaxPartitions) failed: %s\n", cl_strerror(ret));
             cl_engine_free(engine);
             return 1;
         }
     }
     val = cl_engine_get_num(engine, CL_ENGINE_MAX_PARTITIONS, NULL);
     logg("Limits: MaxPartitions limit set to %llu.\n", val);
 
288057e9
     if ((opt = optget(opts, "MaxIconsPE"))->active) {
         if ((ret = cl_engine_set_num(engine, CL_ENGINE_MAX_ICONSPE, opt->numarg))) {
067bce5f
             logg("!cli_engine_set_num(MaxIconsPE) failed: %s\n", cl_strerror(ret));
             cl_engine_free(engine);
             return 1;
         }
     }
     val = cl_engine_get_num(engine, CL_ENGINE_MAX_ICONSPE, NULL);
     logg("Limits: MaxIconsPE limit set to %llu.\n", val);
 
288057e9
     if ((opt = optget(opts, "MaxRecHWP3"))->active) {
         if ((ret = cl_engine_set_num(engine, CL_ENGINE_MAX_RECHWP3, opt->numarg))) {
731c8e62
             logg("!cli_engine_set_num(MaxRecHWP3) failed: %s\n", cl_strerror(ret));
             cl_engine_free(engine);
             return 1;
         }
     }
     val = cl_engine_get_num(engine, CL_ENGINE_MAX_RECHWP3, NULL);
     logg("Limits: MaxRecHWP3 limit set to %llu.\n", val);
 
e492c653
     /* options are handled in main (clamd.c) */
9bc7c138
     val = cl_engine_get_num(engine, CL_ENGINE_PCRE_MATCH_LIMIT, NULL);
     logg("Limits: PCREMatchLimit limit set to %llu.\n", val);
 
     val = cl_engine_get_num(engine, CL_ENGINE_PCRE_RECMATCH_LIMIT, NULL);
     logg("Limits: PCRERecMatchLimit limit set to %llu.\n", val);
 
288057e9
     if ((opt = optget(opts, "PCREMaxFileSize"))->active) {
         if ((ret = cl_engine_set_num(engine, CL_ENGINE_PCRE_MAX_FILESIZE, opt->numarg))) {
37415732
             logg("!cli_engine_set_num(PCREMaxFileSize) failed: %s\n", cl_strerror(ret));
             cl_engine_free(engine);
             return 1;
         }
     }
     val = cl_engine_get_num(engine, CL_ENGINE_PCRE_MAX_FILESIZE, NULL);
     logg("Limits: PCREMaxFileSize limit set to %llu.\n", val);
 
c6cea234
     if (optget(opts, "ScanArchive")->enabled) {
         logg("Archive support enabled.\n");
         options.parse |= CL_SCAN_PARSE_ARCHIVE;
     } else {
         logg("Archive support disabled.\n");
     }
a6945b5d
 
c6cea234
     /* TODO: Remove deprecated option in a future feature release. */
     if (optget(opts, "ArchiveBlockEncrypted")->enabled) {
         if (options.parse & CL_SCAN_PARSE_ARCHIVE) {
             logg(
288057e9
                 "^Using deprecated option \"ArchiveBlockEncrypted\" to alert on "
                 "encrypted archives _and_ documents. Please update your "
                 "configuration to use replacement options \"AlertEncrypted\", or "
                 "\"AlertEncryptedArchive\" and/or \"AlertEncryptedDoc\".\n");
c6cea234
             options.heuristic |= CL_SCAN_HEURISTIC_ENCRYPTED_ARCHIVE;
             options.heuristic |= CL_SCAN_HEURISTIC_ENCRYPTED_DOC;
         } else {
             logg(
288057e9
                 "^Using deprecated option \"ArchiveBlockEncrypted\" to alert on "
                 "encrypted documents. Please update your configuration to use "
                 "replacement options \"AlertEncrypted\", or "
                 "\"AlertEncryptedArchive\" and/or \"AlertEncryptedDoc\".\n");
c6cea234
             options.heuristic |= CL_SCAN_HEURISTIC_ENCRYPTED_DOC;
         }
     }
0f34221a
 
c6cea234
     if (optget(opts, "AlertEncrypted")->enabled) {
         if (options.parse & CL_SCAN_PARSE_ARCHIVE) {
             logg("Alerting of encrypted archives _and_ documents enabled.\n");
             options.heuristic |= CL_SCAN_HEURISTIC_ENCRYPTED_ARCHIVE;
             options.heuristic |= CL_SCAN_HEURISTIC_ENCRYPTED_DOC;
         } else {
             logg("Alerting of encrypted documents enabled.\n");
             options.heuristic |= CL_SCAN_HEURISTIC_ENCRYPTED_DOC;
         }
     }
 
     if (optget(opts, "AlertEncryptedArchive")->enabled) {
         if (options.parse & CL_SCAN_PARSE_ARCHIVE) {
             logg("Alerting of encrypted archives _and_ documents enabled.\n");
             options.heuristic |= CL_SCAN_HEURISTIC_ENCRYPTED_ARCHIVE;
         } else {
             logg("^Encrypted archive alerting requested, but archive support "
                  "is disabled!\n");
         }
     }
 
     if (optget(opts, "AlertEncryptedDoc")->enabled) {
         logg("Alerting of encrypted documents enabled.\n");
         options.heuristic |= CL_SCAN_HEURISTIC_ENCRYPTED_DOC;
e3aaff8e
     }
 
c6cea234
     /* TODO: Remove deprecated option in a future feature release. */
e7dfe57d
     if (optget(opts, "BlockMax")->enabled) {
c6cea234
         logg("^Using deprecated option \"BlockMax\" to enable heuristic alerts "
              "when scans exceed set maximums. Please update your configuration "
              "to use replacement option \"AlertExceedsMax\".\n");
         options.heuristic |= CL_SCAN_HEURISTIC_EXCEEDS_MAX;
     } else if (optget(opts, "AlertExceedsMax")->enabled) {
         logg("Heuristic alerting enabled for scans that exceed set maximums.\n");
048a88e6
         options.heuristic |= CL_SCAN_HEURISTIC_EXCEEDS_MAX;
e7dfe57d
     } else {
c6cea234
         logg("AlertExceedsMax heuristic detection disabled.\n");
e7dfe57d
     }
 
c6cea234
     /* TODO: Remove deprecated option in a future feature release. */
26dc74fa
     if (!optget(opts, "AlgorithmicDetection")->enabled) {
         logg("^Using deprecated option \"AlgorithmicDetection\" to disable "
c6cea234
              "heuristic alerts. Please update your configuration to use "
              "replacement option \"HeuristicAlerts\".\n");
26dc74fa
     } else if (!optget(opts, "HeuristicAlerts")->enabled) {
c6cea234
         logg("Heuristic alerts disabled.\n");
288057e9
     } else {
         logg("Heuristic alerts enabled.\n");
         options.general |= CL_SCAN_GENERAL_HEURISTICS;
1b661cef
     }
 
288057e9
     if (optget(opts, "ScanPE")->enabled) {
         logg("Portable Executable support enabled.\n");
         options.parse |= CL_SCAN_PARSE_PE;
3f97a1e7
     } else {
288057e9
         logg("Portable Executable support disabled.\n");
3f97a1e7
     }
20c3d44d
 
288057e9
     if (optget(opts, "ScanELF")->enabled) {
         logg("ELF support enabled.\n");
         options.parse |= CL_SCAN_PARSE_ELF;
3f97a1e7
     } else {
288057e9
         logg("ELF support disabled.\n");
3f97a1e7
     }
 
288057e9
     /* TODO: Remove deprecated option in a future feature release */
c6cea234
     if (optget(opts, "ScanPE")->enabled || optget(opts, "ScanELF")->enabled) {
288057e9
         if ((optget(opts, "DetectBrokenExecutables")->enabled) ||
             (optget(opts, "AlertBrokenExecutables")->enabled)) {
c6cea234
             logg("Alerting on broken executables enabled.\n");
             options.heuristic |= CL_SCAN_HEURISTIC_BROKEN;
         }
a9082ea2
     }
 
288057e9
     if (optget(opts, "ScanMail")->enabled) {
         logg("Mail files support enabled.\n");
         options.parse |= CL_SCAN_PARSE_MAIL;
a36e6e5c
 
288057e9
         if (optget(opts, "ScanPartialMessages")->enabled) {
             logg("Mail: RFC1341 handling enabled.\n");
             options.mail |= CL_SCAN_MAIL_PARTIAL_MESSAGE;
         }
4270f93b
 
e3aaff8e
     } else {
288057e9
         logg("Mail files support disabled.\n");
e3aaff8e
     }
 
c6cea234
     if (optget(opts, "ScanOLE2")->enabled) {
         logg("OLE2 support enabled.\n");
         options.parse |= CL_SCAN_PARSE_OLE2;
288057e9
 
         /* TODO: Remove deprecated option in a future feature release */
c6cea234
         if ((optget(opts, "OLE2BlockMacros")->enabled) ||
288057e9
             (optget(opts, "AlertOLE2Macros")->enabled)) {
c6cea234
             logg("OLE2: Alerting on all VBA macros.\n");
             options.heuristic |= CL_SCAN_HEURISTIC_MACROS;
         }
e8c9ccdb
     } else {
c6cea234
         logg("OLE2 support disabled.\n");
e8c9ccdb
     }
 
288057e9
     if (optget(opts, "ScanPDF")->enabled) {
         logg("PDF support enabled.\n");
         options.parse |= CL_SCAN_PARSE_PDF;
c5107e70
     } else {
288057e9
         logg("PDF support disabled.\n");
1e41fdba
     }
 
288057e9
     if (optget(opts, "ScanSWF")->enabled) {
         logg("SWF support enabled.\n");
         options.parse |= CL_SCAN_PARSE_SWF;
1e41fdba
     } else {
288057e9
         logg("SWF support disabled.\n");
c5107e70
     }
 
288057e9
     if (optget(opts, "ScanHTML")->enabled) {
         logg("HTML support enabled.\n");
         options.parse |= CL_SCAN_PARSE_HTML;
888f5794
     } else {
288057e9
         logg("HTML support disabled.\n");
888f5794
     }
 
2e7f86f8
     #ifdef PRELUDE
     if (optget(opts, "PreludeEnable")->enabled){
         if ((opt = optget(opts, "PreludeAnalyzerName"))->enabled){
             prelude_initialize_client(opt->strarg);
         } else {
             prelude_initialize_client("ClamAV");
         }
     }
     #endif
 
288057e9
     if (optget(opts, "ScanXMLDOCS")->enabled) {
         logg("XMLDOCS support enabled.\n");
         options.parse |= CL_SCAN_PARSE_XMLDOCS;
ea9ffd29
     } else {
288057e9
         logg("XMLDOCS support disabled.\n");
ea9ffd29
     }
 
288057e9
     if (optget(opts, "ScanHWP3")->enabled) {
         logg("HWP3 support enabled.\n");
         options.parse |= CL_SCAN_PARSE_HWP3;
ea9ffd29
     } else {
288057e9
         logg("HWP3 support disabled.\n");
ea9ffd29
     }
 
c6cea234
     if (optget(opts, "PhishingScanURLs")->enabled) {
288057e9
         /* TODO: Remove deprecated option in a future feature release */
c6cea234
         if ((optget(opts, "PhishingAlwaysBlockCloak")->enabled) ||
             (optget(opts, "AlertPhishingCloak")->enabled)) {
             options.heuristic |= CL_SCAN_HEURISTIC_PHISHING_CLOAK;
             logg("Phishing: Always checking for cloaked urls\n");
         }
288057e9
         /* TODO: Remove deprecated option in a future feature release */
c6cea234
         if ((optget(opts, "PhishingAlwaysBlockSSLMismatch")->enabled) ||
             (optget(opts, "AlertPhishingSSLMismatch")->enabled)) {
             options.heuristic |= CL_SCAN_HEURISTIC_PHISHING_SSL_MISMATCH;
             logg("Phishing: Always checking for ssl mismatches\n");
         }
19b3e182
     }
 
288057e9
     /* TODO: Remove deprecated option in a future feature release */
     if ((optget(opts, "PartitionIntersection")->enabled) ||
         (optget(opts, "AlertPartitionIntersection")->enabled)) {
048a88e6
         options.heuristic |= CL_SCAN_HEURISTIC_PARTITION_INTXN;
c6cea234
         logg("Raw DMG: Alert on partitions intersections\n");
97fbb02b
     }
 
288057e9
     if (optget(opts, "HeuristicScanPrecedence")->enabled) {
         options.general |= CL_SCAN_GENERAL_HEURISTIC_PRECEDENCE;
         logg("Heuristic: precedence enabled\n");
7f0d1148
     }
 
288057e9
     if (optget(opts, "StructuredDataDetection")->enabled) {
048a88e6
         options.heuristic |= CL_SCAN_HEURISTIC_STRUCTURED;
26fbf6bd
 
288057e9
         if ((opt = optget(opts, "StructuredMinCreditCardCount"))->enabled) {
             if ((ret = cl_engine_set_num(engine, CL_ENGINE_MIN_CC_COUNT, opt->numarg))) {
                 logg("!cl_engine_set_num(CL_ENGINE_MIN_CC_COUNT) failed: %s\n", cl_strerror(ret));
                 cl_engine_free(engine);
                 return 1;
             }
         }
         val = cl_engine_get_num(engine, CL_ENGINE_MIN_CC_COUNT, NULL);
         logg("Structured: Minimum Credit Card Number Count set to %u\n", (unsigned int)val);
 
         if ((opt = optget(opts, "StructuredMinSSNCount"))->enabled) {
             if ((ret = cl_engine_set_num(engine, CL_ENGINE_MIN_SSN_COUNT, opt->numarg))) {
                 logg("!cl_engine_set_num(CL_ENGINE_MIN_SSN_COUNT) failed: %s\n", cl_strerror(ret));
                 cl_engine_free(engine);
                 return 1;
             }
         }
         val = cl_engine_get_num(engine, CL_ENGINE_MIN_SSN_COUNT, NULL);
         logg("Structured: Minimum Social Security Number Count set to %u\n", (unsigned int)val);
 
         if (optget(opts, "StructuredSSNFormatNormal")->enabled)
048a88e6
             options.heuristic |= CL_SCAN_HEURISTIC_STRUCTURED_SSN_NORMAL;
26fbf6bd
 
288057e9
         if (optget(opts, "StructuredSSNFormatStripped")->enabled)
             options.heuristic |= CL_SCAN_HEURISTIC_STRUCTURED_SSN_STRIPPED;
26fbf6bd
     }
 
3d7547cf
 #ifdef HAVE__INTERNAL__SHA_COLLECT
288057e9
     if (optget(opts, "DevCollectHashes")->enabled)
         options.dev |= CL_SCAN_DEV_COLLECT_SHA;
3d7547cf
 #endif
 
064b4a0c
     selfchk = optget(opts, "SelfCheck")->numarg;
288057e9
     if (!selfchk) {
         logg("Self checking disabled.\n");
c238ac42
     } else {
288057e9
         logg("Self checking every %u seconds.\n", selfchk);
c238ac42
     }
e3aaff8e
 
370892d0
     /* save the PID */
     mainpid = getpid();
288057e9
     if ((opt = optget(opts, "PidFile"))->enabled) {
         FILE *fd;
         old_umask = umask(0002);
         if ((fd = fopen(opt->strarg, "w")) == NULL) {
             logg("!Can't save PID in file %s\n", opt->strarg);
         } else {
             if (fprintf(fd, "%u\n", (unsigned int)mainpid) < 0) {
                 logg("!Can't save PID in file %s\n", opt->strarg);
             }
             fclose(fd);
         }
         umask(old_umask);
     }
 
     logg("*Listening daemon: PID: %u\n", (unsigned int)mainpid);
     max_threads               = optget(opts, "MaxThreads")->numarg;
     max_queue                 = optget(opts, "MaxQueue")->numarg;
5f6edb22
     acceptdata.commandtimeout = optget(opts, "CommandReadTimeout")->numarg;
288057e9
     readtimeout               = optget(opts, "ReadTimeout")->numarg;
370892d0
 
e0bb54d7
 #if !defined(_WIN32) && defined(RLIMIT_NOFILE)
9390411b
     if (getrlimit(RLIMIT_NOFILE, &rlim) == 0) {
288057e9
         /* don't warn if default value is too high, silently fix it */
         unsigned maxrec;
         int max_max_queue;
         unsigned warn             = optget(opts, "MaxQueue")->active;
         const unsigned clamdfiles = 6;
72b7aef1
 #ifdef C_SOLARIS
288057e9
         int solaris_has_extended_stdio = 0;
72b7aef1
 #endif
288057e9
         /* Condition to not run out of file descriptors:
6c03dc5d
 	     * MaxThreads * MaxRecursion + (MaxQueue - MaxThreads) + CLAMDFILES < RLIMIT_NOFILE
 	     * CLAMDFILES is 6: 3 standard FD + logfile + 2 FD for reloading the DB
 	     * */
41feff65
 #ifdef C_SOLARIS
72b7aef1
 
288057e9
         /*
6c03dc5d
 	     **  If compiling 64bit, then set the solaris_has_extended_stdio
 	     **  flag
 	     */
72b7aef1
 
 #if defined(_LP64)
288057e9
         solaris_has_extended_stdio++;
72b7aef1
 #endif
 
d5db07a9
 #ifdef HAVE_ENABLE_EXTENDED_FILE_STDIO
288057e9
         if (enable_extended_FILE_stdio(-1, -1) == -1) {
             logg("^Unable to set extended FILE stdio, clamd will be limited to max 256 open files\n");
             rlim.rlim_cur = rlim.rlim_cur > 255 ? 255 : rlim.rlim_cur;
         } else {
             solaris_has_extended_stdio++;
         }
72b7aef1
 
41feff65
 #elif !defined(_LP64)
288057e9
         if (solaris_has_extended_stdio && rlim.rlim_cur > 255) {
             rlim.rlim_cur = 255;
             logg("^Solaris only supports 256 open files for 32-bit processes, you need at least Solaris 10u4, or compile as 64-bit to support more!\n");
         }
41feff65
 #endif
72b7aef1
 
288057e9
         /*
6c03dc5d
 	     **  If compiling in 64bit or the file stdio has been extended,
 	     **  then increase the soft limit for the number of open files
 	     **  as the default is usually 256
 	     */
72b7aef1
 
288057e9
         if (solaris_has_extended_stdio) {
             rlim_t saved_soft_limit = rlim.rlim_cur;
72b7aef1
 
288057e9
             rlim.rlim_cur = rlim.rlim_max;
             if (setrlimit(RLIMIT_NOFILE, &rlim) < 0) {
                 logg("!setrlimit() for RLIMIT_NOFILE to %lu failed: %s\n",
                      (unsigned long)rlim.rlim_cur, strerror(errno));
                 rlim.rlim_cur = saved_soft_limit;
             }
         } /*  If 64bit or has extended stdio  */
72b7aef1
 
41feff65
 #endif
288057e9
         opt           = optget(opts, "MaxRecursion");
         maxrec        = opt->numarg;
         max_max_queue = rlim.rlim_cur - maxrec * max_threads - clamdfiles + max_threads;
         if (max_queue < max_threads) {
             max_queue = max_threads;
             if (warn)
                 logg("^MaxQueue value too low, increasing to: %d\n", max_queue);
         }
         if (max_max_queue < max_threads) {
             logg("^MaxThreads * MaxRecursion is too high: %d, open file descriptor limit is: %lu\n",
                  maxrec * max_threads, (unsigned long)rlim.rlim_cur);
             max_max_queue = max_threads;
         }
         if (max_queue > max_max_queue) {
             max_queue = max_max_queue;
             if (warn)
                 logg("^MaxQueue value too high, lowering to: %d\n", max_queue);
         } else if (max_queue < 2 * max_threads && max_queue < max_max_queue) {
             max_queue = 2 * max_threads;
             if (max_queue > max_max_queue)
                 max_queue = max_max_queue;
             /* always warn here */
             logg("^MaxQueue is lower than twice MaxThreads, increasing to: %d\n", max_queue);
         }
9390411b
     }
 #endif
b6de553d
     logg("*MaxQueue set to: %d\n", max_queue);
9390411b
     acceptdata.max_queue = max_queue;
 
288057e9
 #ifndef _WIN32
e3aaff8e
     /* set up signal handling */
     sigfillset(&sigset);
     sigdelset(&sigset, SIGINT);
     sigdelset(&sigset, SIGTERM);
b4912e71
     sigdelset(&sigset, SIGSEGV);
f456f28e
     sigdelset(&sigset, SIGHUP);
d056f4d6
     sigdelset(&sigset, SIGPIPE);
ae203685
     sigdelset(&sigset, SIGUSR2);
fdea27e5
     /* The behavior of a process is undefined after it ignores a
efac2f09
      * SIGFPE, SIGILL, SIGSEGV, or SIGBUS signal */
     sigdelset(&sigset, SIGFPE);
     sigdelset(&sigset, SIGILL);
     sigdelset(&sigset, SIGSEGV);
288057e9
 #ifdef SIGBUS
efac2f09
     sigdelset(&sigset, SIGBUS);
 #endif
949c6fe5
     sigdelset(&sigset, SIGTSTP);
     sigdelset(&sigset, SIGCONT);
e3aaff8e
     sigprocmask(SIG_SETMASK, &sigset, NULL);
efac2f09
 
b4912e71
     /* SIGINT, SIGTERM, SIGSEGV */
8ac7b1ce
     sigact.sa_handler = sighandler_th;
e3aaff8e
     sigemptyset(&sigact.sa_mask);
     sigaddset(&sigact.sa_mask, SIGINT);
     sigaddset(&sigact.sa_mask, SIGTERM);
f456f28e
     sigaddset(&sigact.sa_mask, SIGHUP);
d056f4d6
     sigaddset(&sigact.sa_mask, SIGPIPE);
ae203685
     sigaddset(&sigact.sa_mask, SIGUSR2);
e3aaff8e
     sigaction(SIGINT, &sigact, NULL);
     sigaction(SIGTERM, &sigact, NULL);
d056f4d6
     sigaction(SIGHUP, &sigact, NULL);
     sigaction(SIGPIPE, &sigact, NULL);
ae203685
     sigaction(SIGUSR2, &sigact, NULL);
67118e92
 #endif
c238ac42
 
064b4a0c
     idletimeout = optget(opts, "IdleTimeout")->numarg;
02b4b0c7
 
288057e9
     for (i = 0; i < nsockets; i++)
         if (fds_add(&acceptdata.fds, socketds[i], 1, 0) == -1) {
             logg("!fds_add failed\n");
             cl_engine_free(engine);
             return 1;
         }
1c4d9a22
 #ifdef _WIN32
288057e9
     event_wake_accept = CreateEvent(NULL, TRUE, FALSE, NULL);
     event_wake_recv   = CreateEvent(NULL, TRUE, FALSE, NULL);
1c4d9a22
 #else
949c6fe5
     if (pipe(acceptdata.syncpipe_wake_recv) == -1 ||
288057e9
         (pipe(acceptdata.syncpipe_wake_accept) == -1)) {
949c6fe5
 
288057e9
         logg("!pipe failed\n");
         exit(-1);
949c6fe5
     }
b82eea8d
     syncpipe_wake_recv_w = acceptdata.syncpipe_wake_recv[1];
 
5f6edb22
     if (fds_add(fds, acceptdata.syncpipe_wake_recv[0], 1, 0) == -1 ||
288057e9
         fds_add(&acceptdata.fds, acceptdata.syncpipe_wake_accept[0], 1, 0)) {
         logg("!failed to add pipe fd\n");
         exit(-1);
949c6fe5
     }
1c4d9a22
 #endif
949c6fe5
 
     if ((thr_pool = thrmgr_new(max_threads, idletimeout, max_queue, scanner_thread)) == NULL) {
288057e9
         logg("!thrmgr_new failed\n");
         exit(-1);
c238ac42
     }
e3aaff8e
 
949c6fe5
     if (pthread_create(&accept_th, NULL, acceptloop_th, &acceptdata)) {
288057e9
         logg("!pthread_create failed\n");
         exit(-1);
949c6fe5
     }
e3aaff8e
 
949c6fe5
     time(&start_time);
288057e9
     for (;;) {
         int new_sd;
 
         /* Block waiting for connection on any of the sockets */
         pthread_mutex_lock(fds->buf_mutex);
         fds_cleanup(fds);
         /* signal that we can accept more connections */
         if (fds->nfds <= (unsigned)max_queue)
             pthread_cond_signal(&acceptdata.cond_nfds);
         new_sd = fds_poll_recv(fds, selfchk ? (int)selfchk : -1, 1, event_wake_recv);
80f4bd69
 #ifdef _WIN32
288057e9
         ResetEvent(event_wake_recv);
80f4bd69
 #else
288057e9
         if (!fds->nfds) {
             /* at least the dummy/sync pipe should have remained */
             logg("!All recv() descriptors gone: fatal\n");
             pthread_mutex_lock(&exit_mutex);
             progexit = 1;
             pthread_mutex_unlock(&exit_mutex);
             pthread_mutex_unlock(fds->buf_mutex);
             break;
         }
5eca45b5
 #endif
288057e9
         if (new_sd == -1 && errno != EINTR) {
             logg("!Failed to poll sockets, fatal\n");
             pthread_mutex_lock(&exit_mutex);
             progexit = 1;
             pthread_mutex_unlock(&exit_mutex);
         }
 
         if (fds->nfds) i = (rr_last + 1) % fds->nfds;
         for (j = 0; j < fds->nfds && new_sd >= 0; j++, i = (i + 1) % fds->nfds) {
             size_t pos         = 0;
             int error          = 0;
             struct fd_buf *buf = &fds->buf[i];
             if (!buf->got_newdata)
                 continue;
949c6fe5
 
88ce192e
 #ifndef _WIN32
288057e9
             if (buf->fd == acceptdata.syncpipe_wake_recv[0]) {
                 /* dummy sync pipe, just to wake us */
                 if (read(buf->fd, buff, sizeof(buff)) < 0) {
                     logg("^Syncpipe read failed\n");
                 }
                 continue;
             }
1c4d9a22
 #endif
288057e9
             if (buf->got_newdata == -1) {
                 if (buf->mode == MODE_WAITREPLY) {
                     logg("$mode WAIT_REPLY -> closed\n");
                     buf->fd = -1;
                     thrmgr_group_terminate(buf->group);
                     thrmgr_group_finished(buf->group, EXIT_ERROR);
b68375fd
                     continue;
288057e9
                 } else {
                     logg("$client read error or EOF on read\n");
                     error = 1;
                 }
             }
 
             if (buf->fd != -1 && buf->got_newdata == -2) {
                 logg("$Client read timed out\n");
                 mdprintf(buf->fd, "COMMAND READ TIMED OUT\n");
                 error = 1;
             }
 
             rr_last = i;
             if (buf->mode == MODE_WAITANCILL) {
                 buf->mode = MODE_COMMAND;
                 logg("$mode -> MODE_COMMAND\n");
             }
             while (!error && buf->fd != -1 && buf->buffer && pos < buf->off &&
                    buf->mode != MODE_WAITANCILL) {
                 client_conn_t conn;
                 const char *cmd = NULL;
                 int rc;
                 /* New data available to read on socket. */
 
                 memset(&conn, 0, sizeof(conn));
                 conn.scanfd   = buf->recvfd;
                 buf->recvfd   = -1;
                 conn.sd       = buf->fd;
                 conn.options  = &options;
                 conn.opts     = opts;
                 conn.thrpool  = thr_pool;
                 conn.engine   = engine;
                 conn.group    = buf->group;
                 conn.id       = buf->id;
                 conn.quota    = buf->quota;
                 conn.filename = buf->dumpname;
                 conn.mode     = buf->mode;
                 conn.term     = buf->term;
 
                 /* Parse & dispatch command */
                 cmd = parse_dispatch_cmd(&conn, buf, &pos, &error, opts, readtimeout);
 
                 if (conn.mode == MODE_COMMAND && !cmd)
                     break;
                 if (!error) {
                     if (buf->mode == MODE_WAITREPLY && buf->off) {
                         /* Client is not supposed to send anything more */
                         logg("^Client sent garbage after last command: %lu bytes\n", (unsigned long)buf->off);
                         buf->buffer[buf->off] = '\0';
                         logg("$Garbage: %s\n", buf->buffer);
                         error = 1;
                     } else if (buf->mode == MODE_STREAM) {
                         rc = handle_stream(&conn, buf, opts, &error, &pos, readtimeout);
                         if (rc == -1)
                             break;
                         else
                             continue;
                     }
                 }
                 if (error && error != CL_ETIMEOUT) {
                     conn_reply_error(&conn, "Error processing command.");
                 }
             }
             if (error) {
                 if (buf->dumpfd != -1) {
                     close(buf->dumpfd);
                     if (buf->dumpname) {
                         cli_unlink(buf->dumpname);
                         free(buf->dumpname);
                     }
                     buf->dumpfd = -1;
                 }
                 thrmgr_group_terminate(buf->group);
                 if (thrmgr_group_finished(buf->group, EXIT_ERROR)) {
                     if (buf->fd < 0) {
                         logg("$Skipping shutdown of bad socket after error (FD %d)\n", buf->fd);
                     } else {
                         logg("$Shutting down socket after error (FD %d)\n", buf->fd);
                         shutdown(buf->fd, 2);
                         closesocket(buf->fd);
                     }
                 } else
                     logg("$Socket not shut down due to active tasks\n");
                 buf->fd = -1;
             }
         }
         pthread_mutex_unlock(fds->buf_mutex);
 
         /* handle progexit */
         pthread_mutex_lock(&exit_mutex);
         if (progexit) {
             pthread_mutex_unlock(&exit_mutex);
             pthread_mutex_lock(fds->buf_mutex);
             if (sd_listen_fds(0) == 0) {
                 /* only close the sockets, when not using systemd socket activation */
                 for (i = 0; i < fds->nfds; i++) {
                     if (fds->buf[i].fd == -1)
                         continue;
                     thrmgr_group_terminate(fds->buf[i].group);
                     if (thrmgr_group_finished(fds->buf[i].group, EXIT_ERROR)) {
                         logg("$Shutdown closed fd %d\n", fds->buf[i].fd);
                         shutdown(fds->buf[i].fd, 2);
                         closesocket(fds->buf[i].fd);
                         fds->buf[i].fd = -1;
                     }
                 }
             }
             pthread_mutex_unlock(fds->buf_mutex);
             break;
         }
         pthread_mutex_unlock(&exit_mutex);
 
         /* SIGHUP */
         if (sighup) {
             logg("SIGHUP caught: re-opening log file.\n");
             logg_close();
             sighup = 0;
             if (!logg_file && (opt = optget(opts, "LogFile"))->enabled)
                 logg_file = opt->strarg;
         }
 
         /* SelfCheck */
         if (selfchk) {
             time(&current_time);
             if ((current_time - start_time) >= (time_t)selfchk) {
                 if (reload_db(engine, dboptions, opts, TRUE, &ret)) {
                     pthread_mutex_lock(&reload_mutex);
                     reload = 1;
                     pthread_mutex_unlock(&reload_mutex);
b68375fd
                 }
288057e9
                 time(&start_time);
             }
         }
 
         /* DB reload */
         pthread_mutex_lock(&reload_mutex);
         if (reload) {
             pthread_mutex_unlock(&reload_mutex);
 
             engine = reload_db(engine, dboptions, opts, FALSE, &ret);
             if (ret) {
                 logg("Terminating because of a fatal error.\n");
                 if (new_sd >= 0)
                     closesocket(new_sd);
                 break;
b68375fd
             }
288057e9
 
             pthread_mutex_lock(&reload_mutex);
             reload = 0;
             time(&reloaded_time);
             pthread_mutex_unlock(&reload_mutex);
             time(&start_time);
         } else {
             pthread_mutex_unlock(&reload_mutex);
         }
c238ac42
     }
e3aaff8e
 
949c6fe5
     pthread_mutex_lock(&exit_mutex);
     progexit = 1;
     pthread_mutex_unlock(&exit_mutex);
1c4d9a22
 #ifdef _WIN32
5eca45b5
     SetEvent(event_wake_accept);
1c4d9a22
 #else
949c6fe5
     if (write(acceptdata.syncpipe_wake_accept[1], "", 1) < 0) {
288057e9
         logg("^Write to syncpipe failed\n");
949c6fe5
     }
1c4d9a22
 #endif
40107990
     /* Destroy the thread manager.
      * This waits for all current tasks to end
      */
949c6fe5
     logg("*Waiting for all threads to finish\n");
40107990
     thrmgr_destroy(thr_pool);
288057e9
     if (engine) {
         thrmgr_setactiveengine(NULL);
         cl_engine_free(engine);
deb30312
     }
2dab0a15
 
949c6fe5
     pthread_join(accept_th, NULL);
     fds_free(fds);
6b04dfd0
     pthread_mutex_destroy(fds->buf_mutex);
     pthread_cond_destroy(&acceptdata.cond_nfds);
1c4d9a22
 #ifdef _WIN32
5eca45b5
     CloseHandle(event_wake_accept);
     CloseHandle(event_wake_recv);
1c4d9a22
 #else
949c6fe5
     close(acceptdata.syncpipe_wake_accept[1]);
     close(acceptdata.syncpipe_wake_recv[1]);
1c4d9a22
 #endif
288057e9
     if (dbstat.entries)
         cl_statfree(&dbstat);
     if (sd_listen_fds(0) == 0) {
b68375fd
         /* only close the sockets, when not using systemd socket activation */
         logg("*Shutting down the main socket%s.\n", (nsockets > 1) ? "s" : "");
         for (i = 0; i < nsockets; i++)
             shutdown(socketds[i], 2);
     }
a3e7b8a1
 
288057e9
     if ((opt = optget(opts, "PidFile"))->enabled) {
         if (unlink(opt->strarg) == -1)
             logg("!Can't unlink the pid file %s\n", opt->strarg);
         else
             logg("Pid file removed.\n");
a3e7b8a1
     }
 
520cf7eb
     time(&current_time);
9e751804
     logg("--- Stopped at %s", cli_ctime(&current_time, timestr, sizeof(timestr)));
520cf7eb
 
c95ab85e
     return ret;
288057e9
 }