clamd/scanner.c
e3aaff8e
 /*
c442ca9c
  *  Copyright (C) 2013-2019 Cisco Systems, Inc. and/or its affiliates. All rights reserved.
  *  Copyright (C) 2007-2013 Sourcefire, Inc.
086eab5c
  *
  *  Authors: Tomasz Kojm, Török Edvin
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
  */
 
98ac8d19
 #if HAVE_CONFIG_H
 #include "clamav-config.h"
 #endif
 
e3aaff8e
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
67118e92
 #ifdef	HAVE_UNISTD_H
e3aaff8e
 #include <unistd.h>
67118e92
 #endif
a7282c2f
 #include <errno.h>
e3aaff8e
 #include <sys/stat.h>
 #include <sys/types.h>
e0bb54d7
 #include <dirent.h>
 #ifndef	_WIN32
31e6c6fb
 #include <sys/time.h>
e3aaff8e
 #include <sys/wait.h>
15edd45f
 #include <sys/param.h>
1a4994e3
 #include <signal.h>
e3aaff8e
 #include <sys/socket.h>
 #include <netinet/in.h>
a0283d44
 #include <arpa/inet.h>
 #include <netdb.h>
67118e92
 #endif
d9855a95
 #include <pthread.h>
31e6c6fb
 
2bb229f6
 #if defined(HAVE_READDIR_R_3) || defined(HAVE_READDIR_R_2)
 #include <limits.h>
88794204
 #include <stddef.h>
2bb229f6
 #endif
 
bd8603aa
 #include "libclamav/clamav.h"
 #include "libclamav/others.h"
edbba730
 #include "libclamav/scanners.h"
bd8603aa
 
d18d7221
 #include "shared/idmef_logging.h"
064b4a0c
 #include "shared/optparser.h"
bd8603aa
 #include "shared/output.h"
53725d8c
 #include "shared/misc.h"
bd8603aa
 
e3aaff8e
 #include "others.h"
 #include "scanner.h"
afb48b28
 #include "shared.h"
a617b5ea
 #include "thrmgr.h"
949c6fe5
 #include "server.h"
e3aaff8e
 
c695dab4
 #ifdef C_LINUX
 dev_t procdev; /* /proc device */
 #endif
 
949c6fe5
 extern int progexit;
 extern time_t reloaded_time;
 extern pthread_mutex_t reload_mutex;
a617b5ea
 
769f37a6
 void msg_callback(enum cl_msg severity, const char *fullmsg, const char *msg, void *ctx)
 {
     struct cb_context *c = ctx;
     const char *filename = (c && c->filename) ? c->filename : "";
 
6df13d04
     UNUSEDPARAM(fullmsg);
 
769f37a6
     switch (severity) {
 	case CL_MSG_ERROR:
 	    logg("^[LibClamAV] %s: %s", filename, msg);
 	    break;
 	case CL_MSG_WARN:
 	    logg("~[LibClamAV] %s: %s", filename, msg);
 	    break;
 	case CL_MSG_INFO_VERBOSE:
 	    logg("*[LibClamAV] %s: %s", filename, msg);
 	    break;
 	default:
 	    logg("$[LibClamAV] %s: %s", filename, msg);
 	    break;
     }
 }
 
 void hash_callback(int fd, unsigned long long size, const unsigned char *md5, const char *virname, void *ctx)
 {
     struct cb_context *c = ctx;
6df13d04
     UNUSEDPARAM(fd);
     UNUSEDPARAM(virname);
 
769f37a6
     if (!c)
 	return;
     c->virsize = size;
6df13d04
     strncpy(c->virhash, (const char *)md5, 32);
769f37a6
     c->virhash[32] = '\0';
 }
 
1f1bf36b
 void clamd_virus_found_cb(int fd, const char *virname, void *ctx)
 {
     struct cb_context *c = ctx;
     struct scan_cb_data *d = c->scandata;
     const char *fname;
     
     if (d == NULL)
         return;
d7979d4f
     if (!(d->options->general & CL_SCAN_GENERAL_ALLMATCHES) && !(d->options->general & CL_SCAN_GENERAL_HEURISTIC_PRECEDENCE))
1f1bf36b
         return;
     if (virname == NULL)
         return;
 
     fname = (c && c->filename) ? c->filename : "(filename not set)";
 
     if (virname) {
cbf5017a
         d->infected++;
1f1bf36b
         conn_reply_virus(d->conn, fname, virname);
         if(c->virsize > 0 && optget(d->opts, "ExtendedDetectionInfo")->enabled)
             logg("~%s: %s(%s:%llu) FOUND\n", fname, virname, c->virhash, c->virsize);
         logg("~%s: %s FOUND\n", fname, virname);
     }
 
     return;
 }
 
949c6fe5
 #define BUFFSIZE 1024
a2a004df
 int scan_callback(STATBUF *sb, char *filename, const char *msg, enum cli_ftw_reason reason, struct cli_ftw_cbdata *data)
e3aaff8e
 {
949c6fe5
     struct scan_cb_data *scandata = data->data;
fab9317e
     const char *virname = NULL;
949c6fe5
     int ret;
     int type = scandata->type;
769f37a6
     struct cb_context context;
949c6fe5
 
7ff6ec03
     /* detect disconnected socket, 
      * this should NOT detect half-shutdown sockets (SHUT_WR) */
     if (send(scandata->conn->sd, &ret, 0, 0) == -1 && errno != EINTR) {
36e4bc14
 	logg("$Client disconnected while command was active!\n");
 	thrmgr_group_terminate(scandata->conn->group);
727e0560
 	if (reason == visit_file)
 	    free(filename);
36e4bc14
 	return CL_BREAK;
     }
 
949c6fe5
     if (thrmgr_group_need_terminate(scandata->conn->group)) {
 	logg("^Client disconnected while scanjob was active\n");
 	if (reason == visit_file)
 	    free(filename);
 	return CL_BREAK;
     }
     scandata->total++;
     switch (reason) {
 	case error_mem:
 	    if (msg)
 		logg("!Memory allocation failed during cli_ftw() on %s\n",
 		     msg);
 	    else
 		logg("!Memory allocation failed during cli_ftw()\n");
 	    scandata->errors++;
 	    return CL_EMEM;
 	case error_stat:
7e98915f
 	    conn_reply_errno(scandata->conn, msg, "lstat() failed:");
949c6fe5
 	    logg("^lstat() failed on: %s\n", msg);
 	    scandata->errors++;
 	    return CL_SUCCESS;
 	case warning_skipped_dir:
 	    logg("^Directory recursion limit reached, skipping %s\n",
 		     msg);
 	    return CL_SUCCESS;
 	case warning_skipped_link:
fb6fe4f5
 	    logg("$Skipping symlink: %s\n", msg);
949c6fe5
 	    return CL_SUCCESS;
 	case warning_skipped_special:
 	    if (msg == scandata->toplevel_path)
 		conn_reply(scandata->conn, msg, "Not supported file type", "ERROR");
 	    logg("*Not supported file type: %s\n", msg);
 	    return CL_SUCCESS;
 	case visit_directory_toplev:
 	    return CL_SUCCESS;
 	case visit_file:
 	    break;
     }
e3aaff8e
 
949c6fe5
     /* check whether the file is excluded */
 #ifdef C_LINUX
     if(procdev && sb && (sb->st_dev == procdev)) {
 	free(filename);
 	return CL_SUCCESS;
     }
2bb229f6
 #endif
f45d19ac
 
949c6fe5
     if(sb && sb->st_size == 0) { /* empty file */
 	if (msg == scandata->toplevel_path)
 	    conn_reply_single(scandata->conn, filename, "Empty file");
 	free(filename);
 	return CL_SUCCESS;
e3aaff8e
     }
 
949c6fe5
     if (type == TYPE_MULTISCAN) {
 	client_conn_t *client_conn = (client_conn_t *) calloc(1, sizeof(struct client_conn_tag));
 	if(client_conn) {
 	    client_conn->scanfd = -1;
 	    client_conn->sd = scandata->odesc;
 	    client_conn->filename = filename;
 	    client_conn->cmdtype = COMMAND_MULTISCANFILE;
 	    client_conn->term = scandata->conn->term;
 	    client_conn->options = scandata->options;
 	    client_conn->opts = scandata->opts;
 	    client_conn->group = scandata->group;
 	    if(cl_engine_addref(scandata->engine)) {
 		logg("!cl_engine_addref() failed\n");
 		free(filename);
cbb9db19
 		free(client_conn);
949c6fe5
 		return CL_EMEM;
 	    } else {
 		client_conn->engine = scandata->engine;
 		pthread_mutex_lock(&reload_mutex);
 		client_conn->engine_timestamp = reloaded_time;
 		pthread_mutex_unlock(&reload_mutex);
1514794c
 		if(!thrmgr_group_dispatch(scandata->thr_pool, scandata->group, client_conn, 1)) {
949c6fe5
 		    logg("!thread dispatch failed\n");
cbb9db19
 		    cl_engine_free(scandata->engine);
949c6fe5
 		    free(filename);
cbb9db19
 		    free(client_conn);
949c6fe5
 		    return CL_EMEM;
e3aaff8e
 		}
 	    }
949c6fe5
 	} else {
 	    logg("!Can't allocate memory for client_conn\n");
 	    scandata->errors++;
 	    free(filename);
 	    return CL_EMEM;
e3aaff8e
 	}
949c6fe5
 	return CL_SUCCESS;
e3aaff8e
     }
 
949c6fe5
     if (access(filename, R_OK)) {
727e0560
 	if (conn_reply(scandata->conn, filename, "Access denied.", "ERROR") == -1) {
 	    free(filename);
0378a9ab
 	    return CL_ETIMEOUT;
727e0560
 	}
949c6fe5
 	logg("*Access denied: %s\n", filename);
 	scandata->errors++;
 	free(filename);
 	return CL_SUCCESS;
a617b5ea
     }
 
6174db3a
     thrmgr_setactivetask(filename, NULL);
769f37a6
     context.filename = filename;
     context.virsize = 0;
1f1bf36b
     context.scandata = scandata;
     ret = cl_scanfile_callback(filename, &virname, &scandata->scanned, scandata->engine, scandata->options, &context);
949c6fe5
     thrmgr_setactivetask(NULL, NULL);
e3aaff8e
 
0378a9ab
     if (thrmgr_group_need_terminate(scandata->conn->group)) {
727e0560
 	free(filename);
0378a9ab
 	logg("*Client disconnected while scanjob was active\n");
 	return ret == CL_ETIMEOUT ? ret : CL_BREAK;
     }
 
fab9317e
     if ((ret == CL_VIRUS) && (virname == NULL)) {
aec1e3be
         logg("*%s: reported CL_VIRUS but no virname returned!\n", filename);
fab9317e
         ret = CL_EMEM;
     }
 
949c6fe5
     if (ret == CL_VIRUS) {
97930c24
 
d7979d4f
          if (scandata->options->general & CL_SCAN_GENERAL_ALLMATCHES || (scandata->infected && scandata->options->general & CL_SCAN_GENERAL_HEURISTIC_PRECEDENCE)) {
97930c24
             if(optget(scandata->opts, "PreludeEnable")->enabled){
                 prelude_logging(filename, virname, context.virhash, context.virsize);
             }
             virusaction(filename, virname, scandata->opts);
         } else {
cbf5017a
            scandata->infected++;
97930c24
             if (conn_reply_virus(scandata->conn, filename, virname) == -1) {
                 free(filename);
                 return CL_ETIMEOUT;
             }
             if(optget(scandata->opts, "PreludeEnable")->enabled){
                 prelude_logging(filename, virname, context.virhash, context.virsize);
             }
 
             if(context.virsize && optget(scandata->opts, "ExtendedDetectionInfo")->enabled)
                 logg("~%s: %s(%s:%llu) FOUND\n", filename, virname, context.virhash, context.virsize);
             else
                 logg("~%s: %s FOUND\n", filename, virname);
             virusaction(filename, virname, scandata->opts);
         }
949c6fe5
     } else if (ret != CL_CLEAN) {
 	scandata->errors++;
727e0560
 	if (conn_reply(scandata->conn, filename, cl_strerror(ret), "ERROR") == -1) {
 	    free(filename);
0378a9ab
 	    return CL_ETIMEOUT;
727e0560
 	}
949c6fe5
 	logg("~%s: %s ERROR\n", filename, cl_strerror(ret));
     } else if (logok) {
 	logg("~%s: OK\n", filename);
e3aaff8e
     }
 
949c6fe5
     free(filename);
1f1bf36b
 
949c6fe5
     if(ret == CL_EMEM) /* stop scanning */
 	return ret;
e0591f05
 
949c6fe5
     if (type == TYPE_SCAN) {
 	/* virus -> break */
 	return ret;
e3aaff8e
     }
 
949c6fe5
     /* keep scanning always */
     return CL_SUCCESS;
e3aaff8e
 }
 
51bbedb1
 int scan_pathchk(const char *path, struct cli_ftw_cbdata *data)
 {
 	struct scan_cb_data *scandata = data->data;
 	const struct optstruct *opt;
a2a004df
 	STATBUF statbuf;
51bbedb1
 
     if((opt = optget(scandata->opts, "ExcludePath"))->enabled) {
 	while(opt) {
 	    if(match_regex(path, opt->strarg) == 1) {
 		if(scandata->type != TYPE_MULTISCAN)
 		    conn_reply_single(scandata->conn, path, "Excluded");
2086dc5c
 		return 1;
51bbedb1
 	    }
 	    opt = (const struct optstruct *) opt->nextarg;
 	}
     }
2086dc5c
 
     if(!optget(scandata->opts, "CrossFilesystems")->enabled) {
d9b6b8c7
 	if(CLAMSTAT(path, &statbuf) == 0) {
2086dc5c
 	    if(statbuf.st_dev != scandata->dev) {
 		if(scandata->type != TYPE_MULTISCAN)
 		    conn_reply_single(scandata->conn, path, "Excluded (another filesystem)");
 		return 1;
 	    }
 	}
     }
 
51bbedb1
     return 0;
 }
 
d7979d4f
 int scanfd(
 	const client_conn_t *conn,
 	unsigned long int *scanned,
 	const struct cl_engine *engine,
 	struct cl_scan_options *options,
 	const struct optstruct *opts,
 	int odesc,
 	int stream)
725a2969
 {
0f4639ec
     int ret, fd = conn->scanfd;
e551468a
 	const char *virname = NULL;
a2a004df
 	STATBUF statbuf;
769f37a6
 	struct cb_context context;
8f66206b
 	char fdstr[32];
 	const char*reply_fdstr;
725a2969
 
6df13d04
     UNUSEDPARAM(odesc);
 
0f4639ec
 	if (stream) {
 	    struct sockaddr_in sa;
 	    socklen_t salen = sizeof(sa);
 	    if(getpeername(conn->sd, (struct sockaddr *)&sa, &salen) || salen > sizeof(sa) || sa.sin_family != AF_INET)
 		strncpy(fdstr, "instream(local)", sizeof(fdstr));
 	    else
 		snprintf(fdstr, sizeof(fdstr), "instream(%s@%u)", inet_ntoa(sa.sin_addr), ntohs(sa.sin_port));
 	    reply_fdstr = "stream";
 	} else {
949c6fe5
 	    snprintf(fdstr, sizeof(fdstr), "fd[%d]", fd);
0f4639ec
 	    reply_fdstr = fdstr;
 	}
a2a004df
 	if(FSTAT(fd, &statbuf) == -1 || !S_ISREG(statbuf.st_mode)) {
949c6fe5
 		logg("%s: Not a regular file. ERROR\n", fdstr);
0f4639ec
 		if (conn_reply(conn, reply_fdstr, "Not a regular file", "ERROR") == -1)
0378a9ab
 		    return CL_ETIMEOUT;
725a2969
 		return -1;
949c6fe5
 	}
725a2969
 
aa22174b
 	thrmgr_setactivetask(fdstr, NULL);
769f37a6
 	context.filename = fdstr;
 	context.virsize = 0;
d39cb658
 	context.scandata = NULL;
80fd9074
 	ret = cl_scandesc_callback(fd, conn->filename, &virname, scanned, engine, options, &context);
aa22174b
 	thrmgr_setactivetask(NULL, NULL);
725a2969
 
0378a9ab
 	if (thrmgr_group_need_terminate(conn->group)) {
 	    logg("*Client disconnected while scanjob was active\n");
 	    return ret == CL_ETIMEOUT ? ret : CL_BREAK;
 	}
 
725a2969
 	if(ret == CL_VIRUS) {
0f4639ec
 		if (conn_reply_virus(conn, reply_fdstr, virname) == -1)
0378a9ab
 		    ret = CL_ETIMEOUT;
49f8de22
 		if(context.virsize && optget(opts, "ExtendedDetectionInfo")->enabled)
769f37a6
 		    logg("%s: %s(%s:%llu) FOUND\n", fdstr, virname, context.virhash, context.virsize);
edbba730
 		else
 		    logg("%s: %s FOUND\n", fdstr, virname);
0f4639ec
 		virusaction(reply_fdstr, virname, opts);
725a2969
 	} else if(ret != CL_CLEAN) {
0f4639ec
 		if (conn_reply(conn, reply_fdstr, cl_strerror(ret), "ERROR") == -1)
0378a9ab
 		    ret = CL_ETIMEOUT;
725a2969
 		logg("%s: %s ERROR\n", fdstr, cl_strerror(ret));
 	} else {
0f4639ec
 		if (conn_reply_single(conn, reply_fdstr, "OK") == CL_ETIMEOUT)
0378a9ab
 		    ret = CL_ETIMEOUT;
725a2969
 		if(logok)
 			logg("%s: OK\n", fdstr);
 	}
 	return ret;
 }
 
d7979d4f
 int scanstream(
 	int odesc,
 	unsigned long int *scanned,
 	const struct cl_engine *engine,
 	struct cl_scan_options *options,
 	const struct optstruct *opts,
 	char term)
e3aaff8e
 {
1095156a
 	int ret, sockfd, acceptd;
5f6edb22
 	int tmpd, bread, retval, firsttimeout, timeout, btread;
769f37a6
 	unsigned int port = 0, portscan, min_port, max_port;
949c6fe5
 	unsigned long int quota = 0, maxsize = 0;
 	short bound = 0;
e551468a
 	const char *virname = NULL;
a0283d44
 	char buff[FILEBUFF];
769f37a6
 	char peer_addr[32];
 	struct cb_context context;
e3aaff8e
 	struct sockaddr_in server;
47b68859
 	struct sockaddr_in peer;
 	socklen_t addrlen;
0e621e7d
 	char *tmpname;
e3aaff8e
 
32c970f8
 
064b4a0c
     min_port = optget(opts, "StreamMinPort")->numarg;
     max_port = optget(opts, "StreamMaxPort")->numarg;
949c6fe5
 
     /* search for a free port to bind to */
     port = cli_rndnum(max_port - min_port);
     bound = 0;
     for (portscan = 0; portscan < 1000; portscan++) {
 	port = (port - 1) % (max_port - min_port + 1);
e3aaff8e
 
 	memset((char *) &server, 0, sizeof(server));
 	server.sin_family = AF_INET;
949c6fe5
 	server.sin_port = htons(min_port + port);
 	server.sin_addr.s_addr = htonl(INADDR_ANY);
e3aaff8e
 
 	if((sockfd = socket(AF_INET, SOCK_STREAM, 0)) == -1)
 	    continue;
 
138b5aba
 	if(bind(sockfd, (struct sockaddr *) &server, (socklen_t)sizeof(struct sockaddr_in)) == -1)
67118e92
 	    closesocket(sockfd);
949c6fe5
 	else {
55216b6e
 	    bound = 1;
949c6fe5
 	    break;
 	}
e3aaff8e
     }
949c6fe5
     port += min_port;
32c970f8
 
064b4a0c
     timeout = optget(opts, "ReadTimeout")->numarg;
5f6edb22
     firsttimeout = optget(opts, "CommandReadTimeout")->numarg;
e3aaff8e
 
82c0a0b0
     if(!bound) {
e3aaff8e
 	logg("!ScanStream: Can't find any free port.\n");
949c6fe5
 	mdprintf(odesc, "Can't find any free port. ERROR%c", term);
e3aaff8e
 	return -1;
     } else {
138b5aba
 	if (listen(sockfd, 1) == -1) {
         logg("!ScanStream: listen() error on socket. Error returned is %s.\n", strerror(errno));
         closesocket(sockfd);
         return -1;
     }
949c6fe5
 	if(mdprintf(odesc, "PORT %u%c", port, term) <= 0) {
c477e3ba
 	    logg("!ScanStream: error transmitting port.\n");
a9d3aa14
 	    closesocket(sockfd);
c477e3ba
 	    return -1;
 	}
e3aaff8e
     }
 
5f6edb22
     retval = poll_fd(sockfd, firsttimeout, 0);
949c6fe5
     if (!retval || retval == -1) {
 	const char *reason = !retval ? "timeout" : "poll";
 	mdprintf(odesc, "Accept %s. ERROR%c", reason, term);
 	logg("!ScanStream %u: accept %s.\n", port, reason);
 	closesocket(sockfd);
 	return -1;
c238ac42
     }
 
47b68859
     addrlen = sizeof(peer);
138b5aba
     if((acceptd = accept(sockfd, (struct sockaddr *) &peer, (socklen_t *)&addrlen)) == -1) {
a9d3aa14
 	closesocket(sockfd);
949c6fe5
 	mdprintf(odesc, "accept() ERROR%c", term);
1095156a
 	logg("!ScanStream %u: accept() failed.\n", port);
e3aaff8e
 	return -1;
     }
 
3b074c78
     *peer_addr = '\0';
49e5f658
     inet_ntop(peer.sin_family, &peer.sin_addr, peer_addr, sizeof(peer_addr));
47b68859
     logg("*Accepted connection from %s on port %u, fd %d\n", peer_addr, port, acceptd);
8139fd99
 
064b4a0c
     if(cli_gentempfd(optget(opts, "TemporaryDirectory")->strarg, &tmpname, &tmpd)) {
32c970f8
 	shutdown(sockfd, 2);
a9d3aa14
 	closesocket(sockfd);
 	closesocket(acceptd);
949c6fe5
 	mdprintf(odesc, "cli_gentempfd() failed. ERROR%c", term);
47b68859
 	logg("!ScanStream(%s@%u): Can't create temporary file.\n", peer_addr, port);
32c970f8
 	return -1;
     }
 
949c6fe5
     quota = maxsize = optget(opts, "StreamMaxLength")->numarg;
32c970f8
 
f22f13d9
     while((retval = poll_fd(acceptd, timeout, 0)) == 1) {
949c6fe5
 	/* only read up to max */
 	btread = (maxsize && (quota < sizeof(buff))) ? quota : sizeof(buff);
 	if (!btread) {
 		logg("^ScanStream(%s@%u): Size limit reached (max: %lu)\n", peer_addr, port, maxsize);
 		break; /* Scan what we have */
 	}
67118e92
 	bread = recv(acceptd, buff, btread, 0);
32c970f8
 	if(bread <= 0)
 	    break;
949c6fe5
 
 	quota -= bread;
32c970f8
 
 	if(writen(tmpd, buff, bread) != bread) {
e3aaff8e
 	    shutdown(sockfd, 2);
67118e92
 	    closesocket(sockfd);
 	    closesocket(acceptd);
949c6fe5
 	    mdprintf(odesc, "Temporary file -> write ERROR%c", term);
47b68859
 	    logg("!ScanStream(%s@%u): Can't write to temporary file.\n", peer_addr, port);
0e621e7d
 	    close(tmpd);
064b4a0c
 	    if(!optget(opts, "LeaveTemporaryFiles")->enabled)
0e621e7d
 		unlink(tmpname);
 	    free(tmpname);
e3aaff8e
 	    return -1;
 	}
32c970f8
     }
e3aaff8e
 
32c970f8
     switch(retval) {
 	case 0: /* timeout */
949c6fe5
 	    mdprintf(odesc, "read timeout ERROR%c", term);
47b68859
 	    logg("!ScanStream(%s@%u): read timeout.\n", peer_addr, port);
0e621e7d
 	    break;
32c970f8
 	case -1:
949c6fe5
 	    mdprintf(odesc, "read poll ERROR%c", term);
47b68859
 	    logg("!ScanStream(%s@%u): read poll failed.\n", peer_addr, port);
0e621e7d
 	    break;
32c970f8
     }
e3aaff8e
 
6d5c43a1
     if(retval == 1) {
d39cb658
 		lseek(tmpd, 0, SEEK_SET);
 		thrmgr_setactivetask(peer_addr, NULL);
 		context.filename = peer_addr;
 		context.virsize = 0;
 		context.scandata = NULL;
80fd9074
 		ret = cl_scandesc_callback(tmpd, tmpname, &virname, scanned, engine, options, &context);
d39cb658
 		thrmgr_setactivetask(NULL, NULL);
6d5c43a1
     } else {
     	ret = -1;
     }
0e621e7d
     close(tmpd);
064b4a0c
     if(!optget(opts, "LeaveTemporaryFiles")->enabled)
0e621e7d
 	unlink(tmpname);
     free(tmpname);
e3aaff8e
 
67118e92
     closesocket(acceptd);
     closesocket(sockfd);
e3aaff8e
 
     if(ret == CL_VIRUS) {
49f8de22
 	if(context.virsize && optget(opts, "ExtendedDetectionInfo")->enabled) {
769f37a6
 	    mdprintf(odesc, "stream: %s(%s:%llu) FOUND%c", virname, context.virhash, context.virsize, term);
 	    logg("stream(%s@%u): %s(%s:%llu) FOUND\n", peer_addr, port, virname, context.virhash, context.virsize);
edbba730
 	} else {
 	    mdprintf(odesc, "stream: %s FOUND%c", virname, term);
 	    logg("stream(%s@%u): %s FOUND\n", peer_addr, port, virname);
 	}
064b4a0c
 	virusaction("stream", virname, opts);
e3aaff8e
     } else if(ret != CL_CLEAN) {
6d5c43a1
     	if(retval == 1) {
949c6fe5
 	    mdprintf(odesc, "stream: %s ERROR%c", cl_strerror(ret), term);
47b68859
 	    logg("stream(%s@%u): %s ERROR\n", peer_addr, port, cl_strerror(ret));
6d5c43a1
 	}
ee039e40
     } else {
949c6fe5
 	mdprintf(odesc, "stream: OK%c", term);
7708ddfc
         if(logok)
47b68859
 	    logg("stream(%s@%u): OK\n", peer_addr, port); 
ee039e40
     }
e3aaff8e
 
     return ret;
 }