コード例 #1
0
ファイル: port_range_10.c プロジェクト: ausanyal/testCode
uint16 itr_get_port_from_path_num(uint16 path_id, uint16 magic_num)
{
    uint16 src_port, running_part;

    if (path_id == 65535) {
        switch (magic_num) {
            case INT_DPS_MAGIC_NUM:
                mdprintf("case INT_DPS_MAGIC_NUM. returning %x", INT_DPE_MAGIC_NUM);
                return INT_DPE_MAGIC_NUM;
            case INT_SPS_MAGIC_NUM:
                mdprintf("case INT_SPS_MAGIC_NUM. returning %x", INT_SPE_MAGIC_NUM);
                return INT_SPE_MAGIC_NUM;
            case EXT_DPS_MAGIC_NUM:
                mdprintf("case EXT_DPS_MAGIC_NUM. returning %x", EXT_DPE_MAGIC_NUM);
                return EXT_DPE_MAGIC_NUM;
            case EXT_SPS_MAGIC_NUM:
                mdprintf("case EXT_SPS_MAGIC_NUM. returning %x", EXT_SPE_MAGIC_NUM);
                return EXT_SPE_MAGIC_NUM;
                break;
            default:
                return 0;
        }
    }
    running_part = itr_get_srcport(path_id);
    // src_port = ITR_UDP_SRC_PORT_MAGGIC_NUMBER;
    src_port = magic_num;
    src_port = src_port << 12;
    src_port = src_port & 0xf000;
    src_port = src_port | running_part;

    return src_port;
}
コード例 #2
0
ファイル: session.c プロジェクト: Yuyue/clamav-devel
int conn_reply_virus(const client_conn_t *conn, const char *file,
	       const char *virname)
{
    if (conn->id) {
	return mdprintf(conn->sd, "%u: %s: %s FOUND%c", conn->id, file, virname,
	    conn->term);
    }
    return mdprintf(conn->sd, "%s: %s FOUND%c", file, virname, conn->term);
}
コード例 #3
0
ファイル: session.c プロジェクト: Yuyue/clamav-devel
int conn_reply_single(const client_conn_t *conn, const char *path, const char *status)
{
    if (conn->id) {
	if (path)
	    return mdprintf(conn->sd, "%u: %s: %s%c", conn->id, path, status, conn->term);
	return mdprintf(conn->sd, "%u: %s%c", conn->id, status, conn->term);
    }
    if (path)
	return mdprintf(conn->sd, "%s: %s%c", path, status, conn->term);
    return mdprintf(conn->sd, "%s%c", status, conn->term);
}
コード例 #4
0
ファイル: session.c プロジェクト: Yuyue/clamav-devel
static int print_ver(int desc, char term, const struct cl_engine *engine)
{
    uint32_t ver;

    ver = cl_engine_get_num(engine, CL_ENGINE_DB_VERSION, NULL);
    if(ver) {
	char timestr[32];
	const char *tstr;
	time_t t;
	t = cl_engine_get_num(engine, CL_ENGINE_DB_TIME, NULL);
	tstr = cli_ctime(&t, timestr, sizeof(timestr));
	/* cut trailing \n */
	timestr[strlen(tstr)-1] = '\0';
	return mdprintf(desc, "ClamAV %s/%u/%s%c", get_version(), (unsigned int) ver, tstr, term);
    }
    return mdprintf(desc, "ClamAV %s%c", get_version(), term);
}
コード例 #5
0
ファイル: session.c プロジェクト: Yuyue/clamav-devel
static void print_commands(int desc, char term, const struct cl_engine *engine)
{
    unsigned i, n;
    const char *engine_ver = cl_retver();
    const char *clamd_ver = get_version();
    if (strcmp(engine_ver, clamd_ver)) {
	mdprintf(desc, "ENGINE VERSION MISMATCH: %s != %s. ERROR%c",
		 engine_ver, clamd_ver, term);
	return;
    }
    print_ver(desc, '|', engine);
    mdprintf(desc, " COMMANDS:");
    n = sizeof(commands)/sizeof(commands[0]);
    for (i=0;i<n;i++) {
	mdprintf(desc, " %s", commands[i].cmd);
    }
    mdprintf(desc, "%c", term);
}
コード例 #6
0
int command(int desc, const struct cl_node *root, const struct cl_limits *limits, int options, const struct cfgstruct *copt, int timeout)
{
	char buff[1025];
	int bread, opt, retval;
	struct cfgstruct *cpt;


    retval = poll_fd(desc, timeout);
    switch (retval) {
    case 0: /* timeout */
	return -2;
    case -1:
	mdprintf(desc, "ERROR\n");
	logg("!Command: poll_fd failed.\n");
	return -1;
    }

    while((bread = readsock(desc, buff, 1024)) == -1 && errno == EINTR);

    if(bread == 0) {
	/* Connection closed */
	return -1;
    }

    if(bread < 0) {
	logg("!Command parser: read() failed.\n");
	/* at least try to display this error message */
	/* mdprintf(desc, "ERROR: Command parser: read() failed.\n"); */
	return -1;
    }

    buff[bread] = 0;
    cli_chomp(buff);

    if(!strncmp(buff, CMD1, strlen(CMD1))) { /* SCAN */
	if(scan(buff + strlen(CMD1) + 1, NULL, root, limits, options, copt, desc, 0) == -2)
	    if(cfgopt(copt, "ExitOnOOM"))
		return COMMAND_SHUTDOWN;

    } else if(!strncmp(buff, CMD2, strlen(CMD2))) { /* RAWSCAN */
	opt = options & ~CL_SCAN_ARCHIVE;
	if(scan(buff + strlen(CMD2) + 1, NULL, root, NULL, opt, copt, desc, 0) == -2)
	    if(cfgopt(copt, "ExitOnOOM"))
		return COMMAND_SHUTDOWN;

    } else if(!strncmp(buff, CMD3, strlen(CMD3))) { /* QUIT */
	return COMMAND_SHUTDOWN;

    } else if(!strncmp(buff, CMD4, strlen(CMD4))) { /* RELOAD */
	mdprintf(desc, "RELOADING\n");
	return COMMAND_RELOAD;

    } else if(!strncmp(buff, CMD5, strlen(CMD5))) { /* PING */
	mdprintf(desc, "PONG\n");

    } else if(!strncmp(buff, CMD6, strlen(CMD6))) { /* CONTSCAN */
	if(scan(buff + strlen(CMD6) + 1, NULL, root, limits, options, copt, desc, 1) == -2)
	    if(cfgopt(copt, "ExitOnOOM"))
		return COMMAND_SHUTDOWN;

    } else if(!strncmp(buff, CMD7, strlen(CMD7))) { /* VERSION */
	    const char *dbdir;
	    char *path;
	    struct cl_cvd *daily;

	if((cpt = cfgopt(copt, "DatabaseDirectory")) || (cpt = cfgopt(copt, "DataDirectory")))
	    dbdir = cpt->strarg;
	else
	    dbdir = cl_retdbdir();

	if(!(path = mmalloc(strlen(dbdir) + 11))) {
	    mdprintf(desc, "Memory allocation error - SHUTDOWN forced\n");
	    return COMMAND_SHUTDOWN;
	}

	sprintf(path, "%s/daily.cvd", dbdir);

	if((daily = cl_cvdhead(path))) {
		time_t t = (time_t) daily->stime;

	    pthread_mutex_lock(&ctime_mutex);
	    mdprintf(desc, "ClamAV "VERSION"/%d/%s", daily->version, ctime(&t));
	    pthread_mutex_unlock(&ctime_mutex);
	    cl_cvdfree(daily);
	} else {
	    mdprintf(desc, "ClamAV "VERSION"\n");
	}

	free(path);

    } else if(!strncmp(buff, CMD8, strlen(CMD8))) { /* STREAM */
	if(scanstream(desc, NULL, root, limits, options, copt) == CL_EMEM)
	    if(cfgopt(copt, "ExitOnOOM"))
		return COMMAND_SHUTDOWN;

    } else if(!strncmp(buff, CMD9, strlen(CMD9))) { /* SESSION */
	return COMMAND_SESSION;

    } else if(!strncmp(buff, CMD10, strlen(CMD10))) { /* END */
	return COMMAND_END;

    } else if(!strncmp(buff, CMD11, strlen(CMD11))) { /* SHUTDOWN */
	return COMMAND_SHUTDOWN;

    } else if(!strncmp(buff, CMD12, strlen(CMD12))) { /* FD */
	    int fd = atoi(buff + strlen(CMD12) + 1);

	scanfd(fd, NULL, root, limits, options, copt, desc, 0);
	close(fd); /* FIXME: should we close it here? */

    } else {
	mdprintf(desc, "UNKNOWN COMMAND\n");
    }

    return 0; /* no error and no 'special' command executed */
}
コード例 #7
0
ファイル: session.c プロジェクト: Yuyue/clamav-devel
/* returns:
 *  <0 for error
 *     -1 out of memory
 *     -2 other
 *   0 for async dispatched
 *   1 for command completed (connection can be closed)
 */
int execute_or_dispatch_command(client_conn_t *conn, enum commands cmd, const char *argument)
{
    int desc = conn->sd;
    char term = conn->term;
    const struct cl_engine *engine = conn->engine;
    /* execute commands that can be executed quickly on the recvloop thread,
     * these must:
     *  - not involve any operation that can block for a long time, such as disk
     *  I/O
     *  - send of atomic message is allowed.
     * Dispatch other commands */
    if (conn->group) {
	switch (cmd) {
	    case COMMAND_FILDES:
	    case COMMAND_SCAN:
	    case COMMAND_END:
	    case COMMAND_INSTREAM:
	    case COMMAND_INSTREAMSCAN:
	    case COMMAND_VERSION:
	    case COMMAND_PING:
	    case COMMAND_STATS:
	    case COMMAND_COMMANDS:
		/* These commands are accepted inside IDSESSION */
		break;
	    default:
		/* these commands are not recognized inside an IDSESSION */
		conn_reply_error(conn, "Command invalid inside IDSESSION.");
		logg("$SESSION: command is not valid inside IDSESSION: %d\n", cmd);
		conn->group = NULL;
		return 1;
	}
    }

    switch (cmd) {
	case COMMAND_SHUTDOWN:
	    pthread_mutex_lock(&exit_mutex);
	    progexit = 1;
	    pthread_mutex_unlock(&exit_mutex);
	    return 1;
	case COMMAND_RELOAD:
	    pthread_mutex_lock(&reload_mutex);
	    reload = 1;
	    pthread_mutex_unlock(&reload_mutex);
	    mdprintf(desc, "RELOADING%c", term);
	    /* we set reload flag, and we'll reload before closing the
	     * connection */
	    return 1;
	case COMMAND_PING:
	    if (conn->group)
		mdprintf(desc, "%u: PONG%c", conn->id, term);
	    else
		mdprintf(desc, "PONG%c", term);
	    return conn->group ? 0 : 1;
	case COMMAND_VERSION:
	    {
		if (conn->group)
		    mdprintf(desc, "%u: ", conn->id);
		print_ver(desc, conn->term, engine);
		return conn->group ? 0 : 1;
	    }
	case COMMAND_COMMANDS:
	    {
		if (conn->group)
		    mdprintf(desc, "%u: ", conn->id);
		print_commands(desc, conn->term, engine);
		return conn->group ? 0 : 1;
	    }
	case COMMAND_DETSTATSCLEAR:
	    {
		detstats_clear();
		return 1;
	    }
	case COMMAND_DETSTATS:
	    {
		detstats_print(desc, conn->term);
		return 1;
	    }
	case COMMAND_INSTREAM:
	    {
		int rc = cli_gentempfd(optget(conn->opts, "TemporaryDirectory")->strarg, &conn->filename, &conn->scanfd);
		if (rc != CL_SUCCESS)
		    return rc;
		conn->quota = optget(conn->opts, "StreamMaxLength")->numarg;
		conn->mode = MODE_STREAM;
		return 0;
	    }
	case COMMAND_STREAM:
	case COMMAND_MULTISCAN:
	case COMMAND_CONTSCAN:
	case COMMAND_STATS:
	case COMMAND_FILDES:
	case COMMAND_SCAN:
	case COMMAND_INSTREAMSCAN:
	    return dispatch_command(conn, cmd, argument);
	case COMMAND_IDSESSION:
	    conn->group = thrmgr_group_new();
	    if (!conn->group)
		return CL_EMEM;
	    return 0;
	case COMMAND_END:
	    if (!conn->group) {
		/* end without idsession? */
		conn_reply_single(conn, NULL, "UNKNOWN COMMAND");
		return 1;
	    }
	    /* need to close connection  if we were last in group */
	    return 1;
	/*case COMMAND_UNKNOWN:*/
	default:
	    conn_reply_single(conn, NULL, "UNKNOWN COMMAND");
	    return 1;
    }
}
コード例 #8
0
ファイル: session.c プロジェクト: Yuyue/clamav-devel
/* returns
 *  -1 on fatal error (shutdown)
 *  0 on ok
 *  >0 errors encountered
 */
int command(client_conn_t *conn, int *virus)
{
    int desc = conn->sd;
    struct cl_engine *engine = conn->engine;
    unsigned int options = conn->options;
    const struct optstruct *opts = conn->opts;
    int type = -1; /* TODO: make this enum */
    int maxdirrec;
    int ret = 0;
    int flags = CLI_FTW_STD;

    struct scan_cb_data scandata;
    struct cli_ftw_cbdata data;
    unsigned ok, error, total;
    STATBUF sb;
    jobgroup_t *group = NULL;

    if (thrmgr_group_need_terminate(conn->group)) {
	logg("$Client disconnected while command was active\n");
	if (conn->scanfd != -1)
	    close(conn->scanfd);
	return 1;
    }
    thrmgr_setactiveengine(engine);

    data.data = &scandata;
    memset(&scandata, 0, sizeof(scandata));
    scandata.id = conn->id;
    scandata.group = conn->group;
    scandata.odesc = desc;
    scandata.conn = conn;
    scandata.options = options;
    scandata.engine = engine;
    scandata.opts = opts;
    scandata.thr_pool = conn->thrpool;
    scandata.toplevel_path = conn->filename;

    switch (conn->cmdtype) {
	case COMMAND_SCAN:
	    thrmgr_setactivetask(NULL, "SCAN");
	    type = TYPE_SCAN;
	    break;
	case COMMAND_CONTSCAN:
	    thrmgr_setactivetask(NULL, "CONTSCAN");
	    type = TYPE_CONTSCAN;
	    break;
	case COMMAND_MULTISCAN: {
	    int multiscan, max, alive;

	    /* use MULTISCAN only for directories (bb #1869) */
	    if (STAT(conn->filename, &sb) == 0 &&
		!S_ISDIR(sb.st_mode)) {
		thrmgr_setactivetask(NULL, "CONTSCAN");
		type = TYPE_CONTSCAN;
		break;
	    }

	    pthread_mutex_lock(&conn->thrpool->pool_mutex);
	    multiscan = conn->thrpool->thr_multiscan;
	    max = conn->thrpool->thr_max;
	    if (multiscan+1 < max)
		conn->thrpool->thr_multiscan = multiscan+1;
	    else {
		alive = conn->thrpool->thr_alive;
		ret = -1;
	    }
	    pthread_mutex_unlock(&conn->thrpool->pool_mutex);
	    if (ret) {
		/* multiscan has 1 control thread, so there needs to be at least
		   1 threads that is a non-multiscan controlthread to scan and
		   make progress. */
		logg("^Not enough threads for multiscan. Max: %d, Alive: %d, Multiscan: %d+1\n",
		     max, alive, multiscan);
		conn_reply(conn, conn->filename, "Not enough threads for multiscan. Increase MaxThreads.", "ERROR");
		return 1;
	    }
	    flags &= ~CLI_FTW_NEED_STAT;
	    thrmgr_setactivetask(NULL, "MULTISCAN");
	    type = TYPE_MULTISCAN;
	    scandata.group = group = thrmgr_group_new();
	    if (!group) {
	      if(optget(opts, "ExitOnOOM")->enabled)
		return -1;
	      else
		return 1;
	    }
	    break;
	    }
	case COMMAND_MULTISCANFILE:
	    thrmgr_setactivetask(NULL, "MULTISCANFILE");
	    scandata.group = NULL;
	    scandata.type = TYPE_SCAN;
	    scandata.thr_pool = NULL;
	    /* TODO: check ret value */
	    ret = scan_callback(NULL, conn->filename, conn->filename, visit_file, &data);	    /* callback freed it */
	    conn->filename = NULL;
	    *virus = scandata.infected;
	    if (ret == CL_BREAK) {
		thrmgr_group_terminate(conn->group);
		return 1;
	    }
	    return scandata.errors > 0 ? scandata.errors : 0;
	case COMMAND_FILDES:
	    thrmgr_setactivetask(NULL, "FILDES");
#ifdef HAVE_FD_PASSING
	    if (conn->scanfd == -1) {
		conn_reply_error(conn, "FILDES: didn't receive file descriptor.");
		return 1;
	    }
	    else {
		ret = scanfd(conn, NULL, engine, options, opts, desc, 0);
		if (ret == CL_VIRUS) {
		    *virus = 1;
		    ret = 0;
		} else if (ret == CL_EMEM) {
		    if(optget(opts, "ExitOnOOM")->enabled)
			ret = -1;
		    else
		        ret = 1;
		} else if (ret == CL_ETIMEOUT) {
			thrmgr_group_terminate(conn->group);
			ret = 1;
		} else
		    ret = 0;
		logg("$Closed fd %d\n", conn->scanfd);
		close(conn->scanfd);
	    }
	    return ret;
#else
	    conn_reply_error(conn, "FILDES support not compiled in.");
	    close(conn->scanfd);
	    return 0;
#endif
	case COMMAND_STATS:
	    thrmgr_setactivetask(NULL, "STATS");
	    if (conn->group)
		mdprintf(desc, "%u: ", conn->id);
	    thrmgr_printstats(desc, conn->term);
	    return 0;
	case COMMAND_STREAM:
	    thrmgr_setactivetask(NULL, "STREAM");
	    ret = scanstream(desc, NULL, engine, options, opts, conn->term);
	    if (ret == CL_VIRUS)
		*virus = 1;
	    if (ret == CL_EMEM) {
		if(optget(opts, "ExitOnOOM")->enabled)
		    return -1;
		else
		    return 1;
	    }
	    return 0;
	case COMMAND_INSTREAMSCAN:
	    thrmgr_setactivetask(NULL, "INSTREAM");
	    ret = scanfd(conn, NULL, engine, options, opts, desc, 1);
	    if (ret == CL_VIRUS) {
		*virus = 1;
		ret = 0;
	    } else if (ret == CL_EMEM) {
		if(optget(opts, "ExitOnOOM")->enabled)
		    ret = -1;
		else
 		    ret = 1;
	    } else if (ret == CL_ETIMEOUT) {
		thrmgr_group_terminate(conn->group);
		ret = 1;
	    } else
		ret = 0;
	    if (ftruncate(conn->scanfd, 0) == -1) {
		/* not serious, we're going to close it and unlink it anyway */
		logg("*ftruncate failed: %d\n", errno);
	    }
	    close(conn->scanfd);
	    conn->scanfd = -1;
	    cli_unlink(conn->filename);
	    return ret;
	default:
	    logg("!Invalid command distpached: %d\n", conn->cmdtype);
	    return 1;
    }

    scandata.type = type;
    maxdirrec = optget(opts, "MaxDirectoryRecursion")->numarg;
    if (optget(opts, "FollowDirectorySymlinks")->enabled)
	flags |= CLI_FTW_FOLLOW_DIR_SYMLINK;
    if (optget(opts, "FollowFileSymlinks")->enabled)
	flags |= CLI_FTW_FOLLOW_FILE_SYMLINK;

    if(!optget(opts, "CrossFilesystems")->enabled)
	if(STAT(conn->filename, &sb) == 0)
	    scandata.dev = sb.st_dev;

    ret = cli_ftw(conn->filename, flags,  maxdirrec ? maxdirrec : INT_MAX, scan_callback, &data, scan_pathchk);
    if (ret == CL_EMEM) {
	if(optget(opts, "ExitOnOOM")->enabled)
	    return -1;
	else
	    return 1;
    }
    if (scandata.group && type == TYPE_MULTISCAN) {
	thrmgr_group_waitforall(group, &ok, &error, &total);
	pthread_mutex_lock(&conn->thrpool->pool_mutex);
	conn->thrpool->thr_multiscan--;
	pthread_mutex_unlock(&conn->thrpool->pool_mutex);
    } else {
	error = scandata.errors;
	total = scandata.total;
	ok = total - error - scandata.infected;
    }

    if (ok + error == total && (error != total)) {
	if (conn_reply_single(conn, conn->filename, "OK") == -1)
	    ret = CL_ETIMEOUT;
    }
    *virus = total - (ok + error);

    if (ret == CL_ETIMEOUT)
	thrmgr_group_terminate(conn->group);
    return error;
}
コード例 #9
0
ファイル: scanner.c プロジェクト: anh2hn/clamav-devel
int scanstream(int odesc, unsigned long int *scanned, const struct cl_engine *engine, unsigned int options, const struct optstruct *opts, char term)
{
	int ret, sockfd, acceptd;
	int tmpd, bread, retval, firsttimeout, timeout, btread;
	unsigned int port = 0, portscan, min_port, max_port;
	unsigned long int quota = 0, maxsize = 0;
	short bound = 0;
	const char *virname;
	char buff[FILEBUFF];
	char peer_addr[32];
	struct cb_context context;
	struct sockaddr_in server;
	struct sockaddr_in peer;
	socklen_t addrlen;
	char *tmpname;


    min_port = optget(opts, "StreamMinPort")->numarg;
    max_port = optget(opts, "StreamMaxPort")->numarg;

    /* 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);

	memset((char *) &server, 0, sizeof(server));
	server.sin_family = AF_INET;
	server.sin_port = htons(min_port + port);
	server.sin_addr.s_addr = htonl(INADDR_ANY);

	if((sockfd = socket(AF_INET, SOCK_STREAM, 0)) == -1)
	    continue;

	if(bind(sockfd, (struct sockaddr *) &server, (socklen_t)sizeof(struct sockaddr_in)) == -1)
	    closesocket(sockfd);
	else {
	    bound = 1;
	    break;
	}
    }
    port += min_port;

    timeout = optget(opts, "ReadTimeout")->numarg;
    firsttimeout = optget(opts, "CommandReadTimeout")->numarg;

    if(!bound) {
	logg("!ScanStream: Can't find any free port.\n");
	mdprintf(odesc, "Can't find any free port. ERROR%c", term);
	return -1;
    } else {
	if (listen(sockfd, 1) == -1) {
        logg("!ScanStream: listen() error on socket. Error returned is %s.\n", strerror(errno));
        closesocket(sockfd);
        return -1;
    }
	if(mdprintf(odesc, "PORT %u%c", port, term) <= 0) {
	    logg("!ScanStream: error transmitting port.\n");
	    closesocket(sockfd);
	    return -1;
	}
    }

    retval = poll_fd(sockfd, firsttimeout, 0);
    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;
    }

    addrlen = sizeof(peer);
    if((acceptd = accept(sockfd, (struct sockaddr *) &peer, (socklen_t *)&addrlen)) == -1) {
	closesocket(sockfd);
	mdprintf(odesc, "accept() ERROR%c", term);
	logg("!ScanStream %u: accept() failed.\n", port);
	return -1;
    }

    *peer_addr = '\0';
    inet_ntop(peer.sin_family, &peer.sin_addr, peer_addr, sizeof(peer_addr));
    logg("*Accepted connection from %s on port %u, fd %d\n", peer_addr, port, acceptd);

    if(cli_gentempfd(optget(opts, "TemporaryDirectory")->strarg, &tmpname, &tmpd)) {
	shutdown(sockfd, 2);
	closesocket(sockfd);
	closesocket(acceptd);
	mdprintf(odesc, "cli_gentempfd() failed. ERROR%c", term);
	logg("!ScanStream(%s@%u): Can't create temporary file.\n", peer_addr, port);
	return -1;
    }

    quota = maxsize = optget(opts, "StreamMaxLength")->numarg;

    while((retval = poll_fd(acceptd, timeout, 0)) == 1) {
	/* 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 */
	}
	bread = recv(acceptd, buff, btread, 0);
	if(bread <= 0)
	    break;

	quota -= bread;

	if(writen(tmpd, buff, bread) != bread) {
	    shutdown(sockfd, 2);
	    closesocket(sockfd);
	    closesocket(acceptd);
	    mdprintf(odesc, "Temporary file -> write ERROR%c", term);
	    logg("!ScanStream(%s@%u): Can't write to temporary file.\n", peer_addr, port);
	    close(tmpd);
	    if(!optget(opts, "LeaveTemporaryFiles")->enabled)
		unlink(tmpname);
	    free(tmpname);
	    return -1;
	}
    }

    switch(retval) {
	case 0: /* timeout */
	    mdprintf(odesc, "read timeout ERROR%c", term);
	    logg("!ScanStream(%s@%u): read timeout.\n", peer_addr, port);
	    break;
	case -1:
	    mdprintf(odesc, "read poll ERROR%c", term);
	    logg("!ScanStream(%s@%u): read poll failed.\n", peer_addr, port);
	    break;
    }

    if(retval == 1) {
	lseek(tmpd, 0, SEEK_SET);
	thrmgr_setactivetask(peer_addr, NULL);
	context.filename = peer_addr;
	context.virsize = 0;
        context.scandata = NULL;
	ret = cl_scandesc_callback(tmpd, &virname, scanned, engine, options, &context);
	thrmgr_setactivetask(NULL, NULL);
    } else {
    	ret = -1;
    }
    close(tmpd);
    if(!optget(opts, "LeaveTemporaryFiles")->enabled)
	unlink(tmpname);
    free(tmpname);

    closesocket(acceptd);
    closesocket(sockfd);

    if(ret == CL_VIRUS) {
	if(context.virsize && optget(opts, "ExtendedDetectionInfo")->enabled) {
	    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);
	} else {
	    mdprintf(odesc, "stream: %s FOUND%c", virname, term);
	    logg("stream(%s@%u): %s FOUND\n", peer_addr, port, virname);
	}
	virusaction("stream", virname, opts);
    } else if(ret != CL_CLEAN) {
    	if(retval == 1) {
	    mdprintf(odesc, "stream: %s ERROR%c", cl_strerror(ret), term);
	    logg("stream(%s@%u): %s ERROR\n", peer_addr, port, cl_strerror(ret));
	}
    } else {
	mdprintf(odesc, "stream: OK%c", term);
        if(logok)
	    logg("stream(%s@%u): OK\n", peer_addr, port); 
    }

    return ret;
}
コード例 #10
0
ファイル: server-th.c プロジェクト: jmatthed/clamav-devel
int recvloop_th(int *socketds, unsigned nsockets, struct cl_engine *engine, unsigned int dboptions, const struct optstruct *opts)
{
	int max_threads, max_queue, readtimeout, ret = 0;
	unsigned int options = 0;
	char timestr[32];
#ifndef	_WIN32
	struct sigaction sigact;
	sigset_t sigset;
	struct rlimit rlim;
#endif
	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;

#if defined(FANOTIFY) || defined(CLAMAUTH)
	pthread_t fan_pid;
	pthread_attr_t fan_attr;
	struct thrarg *tharg = NULL; /* shut up gcc */
#endif

#ifndef	_WIN32
	memset(&sigact, 0, sizeof(struct sigaction));
#endif

    /* set up limits */
    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;
	}
    }
    val = cl_engine_get_num(engine, CL_ENGINE_MAX_SCANSIZE, NULL);
    if(val)
    	logg("Limits: Global size limit set to %llu bytes.\n", val);
    else
    	logg("^Limits: Global size limit protection disabled.\n");

    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;
	}
    }
    val = cl_engine_get_num(engine, CL_ENGINE_MAX_FILESIZE, NULL);
    if(val)
    	logg("Limits: File size limit set to %llu bytes.\n", val);
    else
    	logg("^Limits: File size limit protection disabled.\n");

#ifndef _WIN32
    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");
    } else {
	logg("^Cannot obtain resource limits for file size\n");
    }
#endif

    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;
	}
    }
    val = cl_engine_get_num(engine, CL_ENGINE_MAX_RECURSION, NULL);
    if(val)
    	logg("Limits: Recursion level limit set to %u.\n", (unsigned int) val);
    else
    	logg("^Limits: Recursion level limit protection disabled.\n");

    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;
	}
    }
    val = cl_engine_get_num(engine, CL_ENGINE_MAX_FILES, NULL);
    if(val)
    	logg("Limits: Files limit set to %u.\n", (unsigned int) val);
    else
    	logg("^Limits: Files limit protection disabled.\n");

#ifndef _WIN32
    if (getrlimit(RLIMIT_CORE, &rlim) == 0) {
	logg("*Limits: Core-dump limit is %lu.\n", (unsigned long)rlim.rlim_cur);
    }
#endif

    /* Engine max sizes */

    if((opt = optget(opts, "MaxEmbeddedPE"))->active) {
        if((ret = cl_engine_set_num(engine, CL_ENGINE_MAX_EMBEDDEDPE, opt->numarg))) {
            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);

    if((opt = optget(opts, "MaxHTMLNormalize"))->active) {
        if((ret = cl_engine_set_num(engine, CL_ENGINE_MAX_HTMLNORMALIZE, opt->numarg))) {
            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);

    if((opt = optget(opts, "MaxHTMLNoTags"))->active) {
        if((ret = cl_engine_set_num(engine, CL_ENGINE_MAX_HTMLNOTAGS, opt->numarg))) {
            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);

    if((opt = optget(opts, "MaxScriptNormalize"))->active) {
        if((ret = cl_engine_set_num(engine, CL_ENGINE_MAX_SCRIPTNORMALIZE, opt->numarg))) {
            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);

    if((opt = optget(opts, "MaxZipTypeRcg"))->active) {
        if((ret = cl_engine_set_num(engine, CL_ENGINE_MAX_ZIPTYPERCG, opt->numarg))) {
            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);

    if((opt = optget(opts, "MaxPartitions"))->active) {
        if((ret = cl_engine_set_num(engine, CL_ENGINE_MAX_PARTITIONS, opt->numarg))) {
            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);

    if((opt = optget(opts, "MaxIconsPE"))->active) {
        if((ret = cl_engine_set_num(engine, CL_ENGINE_MAX_ICONSPE, opt->numarg))) {
            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);

    if((opt = optget(opts, "PCREMatchLimit"))->active) {
        if((ret = cl_engine_set_num(engine, CL_ENGINE_PCRE_MATCH_LIMIT, opt->numarg))) {
            logg("!cli_engine_set_num(PCREMatchLimit) failed: %s\n", cl_strerror(ret));
            cl_engine_free(engine);
            return 1;
        }
    }
    val = cl_engine_get_num(engine, CL_ENGINE_PCRE_MATCH_LIMIT, NULL);
    logg("Limits: PCREMatchLimit limit set to %llu.\n", val);

    if((opt = optget(opts, "PCRERecMatchLimit"))->active) {
        if((ret = cl_engine_set_num(engine, CL_ENGINE_PCRE_RECMATCH_LIMIT, opt->numarg))) {
            logg("!cli_engine_set_num(PCRERecMatchLimit) failed: %s\n", cl_strerror(ret));
            cl_engine_free(engine);
            return 1;
        }
    }
    val = cl_engine_get_num(engine, CL_ENGINE_PCRE_RECMATCH_LIMIT, NULL);
    logg("Limits: PCRERecMatchLimit limit set to %llu.\n", val);

    if((opt = optget(opts, "PCREMaxFileSize"))->active) {
        if((ret = cl_engine_set_num(engine, CL_ENGINE_PCRE_MAX_FILESIZE, opt->numarg))) {
            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);

    if(optget(opts, "ScanArchive")->enabled) {
	logg("Archive support enabled.\n");
	options |= CL_SCAN_ARCHIVE;

	if(optget(opts, "ArchiveBlockEncrypted")->enabled) {
	    logg("Archive: Blocking encrypted archives.\n");
	    options |= CL_SCAN_BLOCKENCRYPTED;
	}

    } else {
	logg("Archive support disabled.\n");
    }

    if(optget(opts, "AlgorithmicDetection")->enabled) {
	logg("Algorithmic detection enabled.\n");
	options |= CL_SCAN_ALGORITHMIC;
    } else {
	logg("Algorithmic detection disabled.\n");
    }

    if(optget(opts, "ScanPE")->enabled) {
	logg("Portable Executable support enabled.\n");
	options |= CL_SCAN_PE;
    } else {
	logg("Portable Executable support disabled.\n");
    }

    if(optget(opts, "ScanELF")->enabled) {
	logg("ELF support enabled.\n");
	options |= CL_SCAN_ELF;
    } else {
	logg("ELF support disabled.\n");
    }

    if(optget(opts, "ScanPE")->enabled || optget(opts, "ScanELF")->enabled) {
	if(optget(opts, "DetectBrokenExecutables")->enabled) {
	    logg("Detection of broken executables enabled.\n");
	    options |= CL_SCAN_BLOCKBROKEN;
	}
    }

    if(optget(opts, "ScanMail")->enabled) {
	logg("Mail files support enabled.\n");
	options |= CL_SCAN_MAIL;

	if(optget(opts, "ScanPartialMessages")->enabled) {
	    logg("Mail: RFC1341 handling enabled.\n");
	    options |= CL_SCAN_PARTIAL_MESSAGE;
	}

    } else {
	logg("Mail files support disabled.\n");
    }

    if(optget(opts, "ScanOLE2")->enabled) {
	logg("OLE2 support enabled.\n");
	options |= CL_SCAN_OLE2;
	if(optget(opts, "OLE2BlockMacros")->enabled) {
	    logg("OLE2: Blocking all VBA macros.\n");
	    options |= CL_SCAN_BLOCKMACROS;
	}
    } else {
	logg("OLE2 support disabled.\n");
    }

    if(optget(opts, "ScanPDF")->enabled) {
	logg("PDF support enabled.\n");
	options |= CL_SCAN_PDF;
    } else {
	logg("PDF support disabled.\n");
    }

    if(optget(opts, "ScanSWF")->enabled) {
	logg("SWF support enabled.\n");
	options |= CL_SCAN_SWF;
    } else {
	logg("SWF support disabled.\n");
    }

    if(optget(opts, "ScanHTML")->enabled) {
	logg("HTML support enabled.\n");
	options |= CL_SCAN_HTML;
    } else {
	logg("HTML support disabled.\n");
    }

    if(optget(opts,"PhishingScanURLs")->enabled) {

	if(optget(opts,"PhishingAlwaysBlockCloak")->enabled) {
	    options |= CL_SCAN_PHISHING_BLOCKCLOAK; 
	    logg("Phishing: Always checking for cloaked urls\n");
	}

	if(optget(opts,"PhishingAlwaysBlockSSLMismatch")->enabled) {
	    options |= CL_SCAN_PHISHING_BLOCKSSL;
	    logg("Phishing: Always checking for ssl mismatches\n");
	}
    }

    if(optget(opts,"PartitionIntersection")->enabled) {
        options |= CL_SCAN_PARTITION_INTXN;
        logg("Raw DMG: Always checking for partitons intersections\n");
    }

    if(optget(opts,"HeuristicScanPrecedence")->enabled) {
	    options |= CL_SCAN_HEURISTIC_PRECEDENCE;
	    logg("Heuristic: precedence enabled\n");
    }

    if(optget(opts, "StructuredDataDetection")->enabled) {
        options |= CL_SCAN_STRUCTURED;

	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)
            options |= CL_SCAN_STRUCTURED_SSN_NORMAL;

        if(optget(opts, "StructuredSSNFormatStripped")->enabled)
	    options |= CL_SCAN_STRUCTURED_SSN_STRIPPED;
    }

#ifdef HAVE__INTERNAL__SHA_COLLECT
    if(optget(opts, "DevCollectHashes")->enabled)
	options |= CL_SCAN_INTERNAL_COLLECT_SHA;
#endif

    selfchk = optget(opts, "SelfCheck")->numarg;
    if(!selfchk) {
	logg("Self checking disabled.\n");
    } else {
	logg("Self checking every %u seconds.\n", selfchk);
    }

    /* save the PID */
    mainpid = getpid();
    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;
    acceptdata.commandtimeout = optget(opts, "CommandReadTimeout")->numarg;
    readtimeout = optget(opts, "ReadTimeout")->numarg;

#if !defined(_WIN32) && defined(RLIMIT_NOFILE)
    if (getrlimit(RLIMIT_NOFILE, &rlim) == 0) {
	/* 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;
	/* Condition to not run out of file descriptors:
	 * MaxThreads * MaxRecursion + (MaxQueue - MaxThreads) + CLAMDFILES < RLIMIT_NOFILE 
	 * CLAMDFILES is 6: 3 standard FD + logfile + 2 FD for reloading the DB
	 * */
#ifdef C_SOLARIS
#ifdef HAVE_ENABLE_EXTENDED_FILE_STDIO
	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;
	}
#elif !defined(_LP64)
	if (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");
	}
#endif
#endif
	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);
	}
    }
#endif
    logg("*MaxQueue set to: %d\n", max_queue);
    acceptdata.max_queue = max_queue;

    if(optget(opts, "ScanOnAccess")->enabled)

#if defined(FANOTIFY) || defined(CLAMAUTH)
    {
        do {
	    if(pthread_attr_init(&fan_attr)) break;
	    pthread_attr_setdetachstate(&fan_attr, PTHREAD_CREATE_JOINABLE);
	    if(!(tharg = (struct thrarg *) malloc(sizeof(struct thrarg)))) break;
	    tharg->opts = opts;
	    tharg->engine = engine;
	    tharg->options = options;
	    if(!pthread_create(&fan_pid, &fan_attr, onas_fan_th, tharg)) break;
	    free(tharg);
	    tharg=NULL;
	} while(0);
	if (!tharg) logg("!Unable to start on-access scan\n");
    }
#else
	logg("!On-access scan is not available\n");
#endif


#ifndef	_WIN32
    /* set up signal handling */
    sigfillset(&sigset);
    sigdelset(&sigset, SIGINT);
    sigdelset(&sigset, SIGTERM);
    sigdelset(&sigset, SIGSEGV);
    sigdelset(&sigset, SIGHUP);
    sigdelset(&sigset, SIGPIPE);
    sigdelset(&sigset, SIGUSR2);
    /* The behavior of a process is undefined after it ignores a 
     * SIGFPE, SIGILL, SIGSEGV, or SIGBUS signal */
    sigdelset(&sigset, SIGFPE);
    sigdelset(&sigset, SIGILL);
    sigdelset(&sigset, SIGSEGV);
#ifdef SIGBUS    
    sigdelset(&sigset, SIGBUS);
#endif
    sigdelset(&sigset, SIGTSTP);
    sigdelset(&sigset, SIGCONT);
    sigprocmask(SIG_SETMASK, &sigset, NULL);

    /* SIGINT, SIGTERM, SIGSEGV */
    sigact.sa_handler = sighandler_th;
    sigemptyset(&sigact.sa_mask);
    sigaddset(&sigact.sa_mask, SIGINT);
    sigaddset(&sigact.sa_mask, SIGTERM);
    sigaddset(&sigact.sa_mask, SIGHUP);
    sigaddset(&sigact.sa_mask, SIGPIPE);
    sigaddset(&sigact.sa_mask, SIGUSR2);
    sigaction(SIGINT, &sigact, NULL);
    sigaction(SIGTERM, &sigact, NULL);
    sigaction(SIGHUP, &sigact, NULL);
    sigaction(SIGPIPE, &sigact, NULL);
    sigaction(SIGUSR2, &sigact, NULL);
#endif

    idletimeout = optget(opts, "IdleTimeout")->numarg;

    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;
	}
#ifdef _WIN32
	event_wake_accept = CreateEvent(NULL, TRUE, FALSE, NULL);
	event_wake_recv = CreateEvent(NULL, TRUE, FALSE, NULL);
#else
    if (pipe(acceptdata.syncpipe_wake_recv) == -1 ||
	(pipe(acceptdata.syncpipe_wake_accept) == -1)) {

	logg("!pipe failed\n");
	exit(-1);
    }
    syncpipe_wake_recv_w = acceptdata.syncpipe_wake_recv[1];

    if (fds_add(fds, acceptdata.syncpipe_wake_recv[0], 1, 0) == -1 ||
	fds_add(&acceptdata.fds, acceptdata.syncpipe_wake_accept[0], 1, 0)) {
	logg("!failed to add pipe fd\n");
	exit(-1);
    }
#endif

    if ((thr_pool = thrmgr_new(max_threads, idletimeout, max_queue, scanner_thread)) == NULL) {
	logg("!thrmgr_new failed\n");
	exit(-1);
    }

    if (pthread_create(&accept_th, NULL, acceptloop_th, &acceptdata)) {
	logg("!pthread_create failed\n");
	exit(-1);
    }

    time(&start_time);
    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);
#ifdef _WIN32
	ResetEvent(event_wake_recv);
#else
	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;
	}
#endif
	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;

#ifndef _WIN32
	    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;
	    }
#endif
	    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);
		    continue;
		} 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);
	    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);
		}
		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;
	    }

	    pthread_mutex_lock(&reload_mutex);
	    reload = 0;
	    time(&reloaded_time);
	    pthread_mutex_unlock(&reload_mutex);

#if defined(FANOTIFY) || defined(CLAMAUTH)
	    if(optget(opts, "ScanOnAccess")->enabled && tharg) {
		tharg->engine = engine;
	    }
#endif
	    time(&start_time);
	} else {
	    pthread_mutex_unlock(&reload_mutex);
	}
    }

    pthread_mutex_lock(&exit_mutex);
    progexit = 1;
    pthread_mutex_unlock(&exit_mutex);
#ifdef _WIN32
    SetEvent(event_wake_accept);
#else
    if (write(acceptdata.syncpipe_wake_accept[1], "", 1) < 0) {
	logg("^Write to syncpipe failed\n");
    }
#endif
    /* Destroy the thread manager.
     * This waits for all current tasks to end
     */
    logg("*Waiting for all threads to finish\n");
    thrmgr_destroy(thr_pool);
#if defined(FANOTIFY) || defined(CLAMAUTH)
    if(optget(opts, "ScanOnAccess")->enabled && tharg) {
	logg("Stopping on-access scan\n");
	pthread_mutex_lock(&logg_mutex);
	pthread_kill(fan_pid, SIGUSR1);
	pthread_mutex_unlock(&logg_mutex);
	pthread_join(fan_pid, NULL);
    free(tharg);
    }
#endif
    if(engine) {
	thrmgr_setactiveengine(NULL);
	cl_engine_free(engine);
    }

    pthread_join(accept_th, NULL);
    fds_free(fds);
    pthread_mutex_destroy(fds->buf_mutex);
    pthread_cond_destroy(&acceptdata.cond_nfds);
#ifdef _WIN32
    CloseHandle(event_wake_accept);
    CloseHandle(event_wake_recv);
#else
    close(acceptdata.syncpipe_wake_accept[1]);
    close(acceptdata.syncpipe_wake_recv[1]);
#endif
    if(dbstat.entries)
	cl_statfree(&dbstat);
    logg("*Shutting down the main socket%s.\n", (nsockets > 1) ? "s" : "");
    for (i = 0; i < nsockets; i++)
	shutdown(socketds[i], 2);

    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");
    }

    time(&current_time);
    logg("--- Stopped at %s", cli_ctime(&current_time, timestr, sizeof(timestr)));

    return ret;
} 
コード例 #11
0
ファイル: port_range_10.c プロジェクト: ausanyal/testCode
int main()
{
    cfg_t   cfg_int = {0}, cfg_ext = {0}, cfg_new;
    int     ch, rv = 0;
    size_t  len, read;
    FILE    *fp = NULL;
    char    *line = NULL;
    
    mprintf("Read from file (y/n): ");
    //ch = getc(stdin);
    ch = 'y';
    do {
        if (ch  == 'y') {
            if (fp == NULL) {
                fp = fopen("range_input.txt", "r");
            }

            if (line != NULL) {
                free(line);
            }

            line = NULL;
            len = 0;

            read = getline(&line, &len, fp); 
            if (strstr(line, "#") == NULL) {

                rv = sscanf(line, "%hu %hu %hu %hu %hu", 
                            &cfg_int.sps, &cfg_int.spe, &cfg_int.dps, 
                            &cfg_int.dpe, &cfg_int.np); 
                if (rv == -1) {
                    mprintf("EOF \n");
                    break;
                }
            }
            else {
                mdprintf("...... skipping line: %s \n", line);
                continue;
            }

            memcpy(&cfg_ext, &cfg_int, sizeof(cfg_t));
            mprintf("********************* Internal *********************\n");
            itr_ext_validate_get_sport_dport_range_best_blocks(&cfg_int, 
                                                    &cfg_new, ITR_INT_TRACE);
            mprintf("********************* External *********************\n");
            itr_ext_validate_get_sport_dport_range_best_blocks(&cfg_ext, 
                                                    &cfg_new, ITR_EXT_TRACE);

        }
        else {
            mprintf("Enter sps / spe / dps / dpe / np : \n");
            scanf("%hu", &cfg_int.sps);
            scanf("%hu", &cfg_int.spe);
            scanf("%hu", &cfg_int.dps);
            scanf("%hu", &cfg_int.dpe);
            scanf("%hu", &cfg_int.np);
            memcpy(&cfg_ext, &cfg_int, sizeof(cfg_t));
            mprintf("********************* Internal *********************\n");
            itr_ext_validate_get_sport_dport_range_best_blocks(&cfg_int, 
                                                    &cfg_new, ITR_INT_TRACE);
            mprintf("********************* External *********************\n");
            itr_ext_validate_get_sport_dport_range_best_blocks(&cfg_ext, 
                                                    &cfg_new, ITR_EXT_TRACE);
            break;
        }

    } while(1);

    if (fp != NULL) {
        fclose(fp);
    }

    return 1;
}
コード例 #12
0
ファイル: port_range_10.c プロジェクト: ausanyal/testCode
void
itr_ext_validate_get_sport_dport_range (cfg_t *cfg, cfg_t *cfg_new,
                                        trace_type_t trace_type)
{
    bool sport_wildcard = FALSE, dport_wildcard = FALSE;
    bool sport_range = FALSE, dport_range = FALSE;
    unsigned int  num_sports = 0, num_dports = 0;

    memset(cfg_new, 0, sizeof(cfg_t));
    mdprintf("===============================================================\n");
    mdprintf("I/P (type %d): sps(%d) spe(%d) dps(%d) dpe(%d) np(%d) \n", 
            trace_type, SPS, SPE, DPS, DPE, NP);
    mdprintf("---------------------------------------------------------------\n");

    if ((SPS == 0) && (SPE == 65535)) { // Wild card
        sport_wildcard = TRUE;
    } else if (SPS != SPE) {
        sport_range = TRUE;
        num_sports = SPE - SPS +  1;
    } else {
        num_sports = SPE - SPS +  1;
    }

    if ((DPS == 0) && (DPE == 65535)) { // Wild card
        dport_wildcard = TRUE;
    } else if (DPS != DPE) {
        dport_range = TRUE;
        num_dports = DPE - DPS + 1;
    } else {
        num_dports = DPE - DPS + 1;
    }

    mdprintf("sp_wc: %d sp_rg: %d dp_wc: %d dp_rg: %d num_sp: %d num_dp: %d\n",
            sport_wildcard, sport_range, dport_wildcard, dport_range, 
            num_sports, num_dports);

    if ((sport_wildcard == TRUE) && (dport_wildcard != TRUE)) {
        // only sport varies
        SPS = itr_get_sport_from_path_num(0, trace_type);
        //SPE = itr_get_sport_from_path_num(NP/num_dports + 1, trace_type);
        SPE = itr_get_sport_from_path_num(65535, trace_type);
        mprintf("Only sport varies SPS %d SPE %d DPS %d DPE %d",
                 SPS, SPE, DPS, DPE);
        itr_check_if_ranges_intersect_get_new_range(cfg, cfg_new);
        num_sports = SPE - SPS + 1;
    }
    else if ((sport_wildcard != TRUE) && (dport_wildcard == TRUE)) {
        // only sport varies
        DPS = itr_get_dport_from_path_num(0, trace_type);
        //DPE = itr_get_dport_from_path_num(NP/num_sports + 1, trace_type);
        DPE = itr_get_dport_from_path_num(65535, trace_type);
        mprintf("Only dport varies SPS %d SPE %d DPS %d DPE %d", 
                 SPS, SPE, DPS, DPE);
        itr_check_if_ranges_intersect_get_new_range(cfg, cfg_new);
        num_dports = DPE - DPS + 1;
    }
    else if ((sport_wildcard == TRUE) && (dport_wildcard == TRUE)) {
        // both sport/dport vary
        SPS = itr_get_sport_from_path_num(0, trace_type);
        SPE = itr_get_sport_from_path_num(65535, trace_type);
        DPS = itr_get_dport_from_path_num(0, trace_type);
        DPE = itr_get_dport_from_path_num(65535, trace_type);
        mprintf("Both sport/dport vary SPS %d SPE %d DPS %d DPE %d\n", 
                 SPS, SPE, DPS, DPE);
        itr_check_if_ranges_intersect_get_new_range(cfg, cfg_new);
        num_sports = SPE - SPS + 1;
        num_dports = DPE - DPS + 1;
    }
    else {
        // both sport and dport fixed range provided 
        itr_check_if_ranges_intersect_get_new_range(cfg, cfg_new);
    }

    if ((num_sports * num_dports) < NP ) {
        cfg_new->npp = NPP = (num_sports * num_dports);
    }
    else {
        cfg_new->npp = NPP = NP;
    }

    if (NPP > MAX_EXT_PATH) {
        cfg_new->npp = NPP = MAX_EXT_PATH;
    }

    mdprintf("O/P: sps(%d) spe(%d) dps(%d) dpe(%d) np(%d) npp(%d)\n", 
            cfg_new->sps, cfg_new->spe, cfg_new->dps, cfg_new->dpe, cfg_new->np, cfg_new->npp);
    mdprintf("==============================================================\n");
    fflush(stdout);

    return;
}
コード例 #13
0
ファイル: port_range_10.c プロジェクト: ausanyal/testCode
void
itr_check_if_ranges_intersect_get_new_range(cfg_t *cfg, cfg_t *cfg_new)
{
#define NSPS (cfg_new->sps)
#define NSPE (cfg_new->spe)
#define NDPS (cfg_new->dps)
#define NDPE (cfg_new->dpe)
#define NNP  (cfg_new->np)

    int num_sp, num_dp, delta;
    int leeway_1 = 0, leeway_2 = 0, leeway_needed;
    double delta_sqrt;

    num_sp      = SPE - SPS + 1;
    num_dp      = DPE - DPS + 1;
    delta       = (num_sp * num_dp) - NP;
    mprintf("delta: %d num_sp %d num_dp %d NP: %d \n", delta, num_sp, num_dp, NP);
    memcpy(cfg_new, cfg, sizeof(cfg_t));
    if (delta > 0) { 
        delta_sqrt  = (int)sqrt((double)delta); 
        leeway_1 = delta_sqrt/2;
        leeway_2 = delta_sqrt - leeway_1;
        //leeway_1 = leeway_2 = delta_sqrt;
    }
    else {
        mdprintf("No change possible \n");
        return;
    }

    mprintf("\t\t\t ---------------- cfg----------------  \n");
    itr_print_cfg(cfg);
    mdprintf("\t\t\t-------------------------------------\n");

    // Note - we cannot shrink SPS/DPS or push SPE/DPE
    // (1) SPS ------------------ SPE         
    //Ref line >>>>>   DPS ----------------------------- DPE
    if ((SPS <= DPS) && (SPE >= DPS) && (SPE <= DPE)) {
        mdprintf("\t\t\t Case 1\n");
        // Try to move DPS beyond SPE:
        leeway_needed = (SPE - DPS + 1);
        mprintf("\t\t\t Case 1 leeway_1 %d leeway_2 %d leeway_needed %d \n",
                leeway_1, leeway_2, leeway_needed);
        if ((leeway_1 + leeway_2) > leeway_needed) {
            leeway_1 = leeway_needed/2;
            leeway_2 = leeway_needed - leeway_1;
        }
        // shrink SPE by leeway_1 and push DPS by leeway_2
        NSPS = SPS;
        if ( (SPE - SPS + 1) > leeway_1) {
            NSPE = SPE - leeway_1;
        }
        if ( (DPE - DPS + 1) > leeway_2) {
            NDPS = DPS + leeway_2;
        }
        NDPE = DPE;
    }
    // (2)                                   SPS ---------------------- SPE
    //Ref line >>>>>   DPS ----------------------------- DPE
    else if ((SPS >= DPS) && (SPS <= DPE) && (SPE >= DPE)) {
        mdprintf("\t\t\t Case 2\n");
        // Try to move SPS beyond DPE:
        leeway_needed = (DPE - SPS + 1);
        if ((leeway_1 + leeway_2) > leeway_needed) {
            leeway_1 = leeway_needed/2;
            leeway_2 = leeway_needed - leeway_1;
        }
        // push SPS by leeway_1 and shrink DPE by leeway_2
        if ( (SPE - SPS + 1) > leeway_1) {
            NSPS = SPS + leeway_1;
        }
        NSPE = SPE;
        NDPS = DPS;
        if ( (DPE - DPS + 1) > leeway_2) {
            NDPE = DPE - leeway_2;
        }
    }
    // (3)                      SPS ------------- SPE
    //Ref line >>>>>   DPS ----------------------------- DPE
    else if ((SPS >= DPS) && (SPE <= DPE)) {
        mdprintf("\t\t\t Case 3\n");
        // Make the call based on (SPE - DPS) < (DPE - SPS)
        if ((SPE - DPS) < (DPE - SPS)) {
            leeway_needed = (SPE - DPS + 1);
            if ((leeway_1 + leeway_2) > leeway_needed) {
                leeway_1 = leeway_needed/2;
                leeway_2 = leeway_needed - leeway_1;
            }
            // shrink SPE by leeway_1 and push DPS by leeway_2
            NSPS = SPS;
            if ( (SPE - SPS + 1) > leeway_1) {
                NSPE = SPE - leeway_1;
            }
            if ( (DPE - DPS + 1) > leeway_2) {
                NDPS = DPS + leeway_2;
            }
            NDPE = DPE;

        }
        else {
            leeway_needed = (DPE - SPS + 1);
            if ((leeway_1 + leeway_2) > leeway_needed) {
                leeway_1 = leeway_needed/2;
                leeway_2 = leeway_needed - leeway_1;
            }
            // push SPS by leeway_1 and shrink DPE by leeway_2
            if ( (SPE - SPS + 1) > leeway_1) {
                NSPS = SPS + leeway_1;
            }
            NSPE = SPE;
            NDPS = DPS;
            if ( (DPE - DPS + 1) > leeway_2) {
                NDPE = DPE - leeway_2;
            }
        }
    }
    // (4)  SPS --------------------------------------------------- SPE
    //Ref line >>>>>   DPS ----------------------------- DPE
    else if ((SPS <= DPS) && (SPE >= DPE)) {
        mdprintf("\t\t\t Case 4\n");
        // Make the call based on (DPE - SPS) vs (SPE - DPS)
        if ((DPE - SPS) < (SPE - DPS)) {
            mdprintf("\t\t\t\t Case 4.1\n");
            leeway_needed = (DPE - SPS + 1);
            if ((leeway_1 + leeway_2) > leeway_needed) {
                leeway_1 = leeway_needed/2;
                leeway_2 = leeway_needed - leeway_1;
            }
            // push SPS by leeway_1 and shrink DPE by leeway_2
            if ( (SPE - SPS + 1) > leeway_1) {
                NSPS = SPS + leeway_1;
            }
            NSPE = SPE;
            NDPS = DPS;
            if ( (DPE - DPS + 1) > leeway_2) {
                NDPE = DPE - leeway_2;
            }
        }
        else {
            mdprintf("\t\t\t\t Case 4.2\n");
            leeway_needed = (SPE - DPS + 1);
            if ((leeway_1 + leeway_2) > leeway_needed) {
                leeway_1 = leeway_needed/2;
                leeway_2 = leeway_needed - leeway_1;
            }
            // shrink SPE by leeway_1 and push DPS by leeway_2
            NSPS = SPS;
            if ( (SPE - SPS + 1) > leeway_1) {
                NSPE = SPE - leeway_1;
            }
            if ( (DPE - DPS + 1) > leeway_2) {
                NDPS = DPS + leeway_2;
            }
            NDPE = DPE;

        }
    }
    else {
        // Error
        mdprintf("No intersection detected. Not changing anything \n");
        memcpy(cfg_new, cfg, sizeof(cfg_t));
    }

    mprintf("\t\t\t -------------- cfg_new --------------  \n");
    itr_print_cfg(cfg_new);
    mdprintf("\t\t\t-------------------------------------\n");
}
コード例 #14
0
ファイル: session.c プロジェクト: OPSF/uClinux
/* returns
 *  -1 on fatal error (shutdown)
 *  0 on ok
 *  >0 errors encountered
 */
int command(client_conn_t *conn, int *virus)
{
    int desc = conn->sd;
    struct cl_engine *engine = conn->engine;
    unsigned int options = conn->options;
    const struct optstruct *opts = conn->opts;
    int type = -1; /* TODO: make this enum */
    int maxdirrec;
    int ret = 0;
    int flags = CLI_FTW_STD;

    struct scan_cb_data scandata;
    struct cli_ftw_cbdata data;
    unsigned ok, error, total;
    jobgroup_t *group = NULL;

    if (thrmgr_group_need_terminate(conn->group)) {
	logg("$Client disconnected while command was active\n");
	if (conn->scanfd != -1)
	    close(conn->scanfd);
	return 1;
    }
    thrmgr_setactiveengine(engine);

    data.data = &scandata;
    memset(&scandata, 0, sizeof(scandata));
    scandata.id = conn->id;
    scandata.group = conn->group;
    scandata.odesc = desc;
    scandata.conn = conn;
    scandata.options = options;
    scandata.engine = engine;
    scandata.opts = opts;
    scandata.thr_pool = conn->thrpool;
    scandata.toplevel_path = conn->filename;

    switch (conn->cmdtype) {
	case COMMAND_SCAN:
	    thrmgr_setactivetask(NULL, "SCAN");
	    type = TYPE_SCAN;
	    break;
	case COMMAND_CONTSCAN:
	    thrmgr_setactivetask(NULL, "CONTSCAN");
	    type = TYPE_CONTSCAN;
	    break;
	case COMMAND_MULTISCAN:
	    flags &= ~CLI_FTW_NEED_STAT;
	    thrmgr_setactivetask(NULL, "MULTISCAN");
	    type = TYPE_MULTISCAN;
	    scandata.group = group = thrmgr_group_new();
	    if (!group)
		return CL_EMEM;
	    break;
	case COMMAND_MULTISCANFILE:
	    thrmgr_setactivetask(NULL, "MULTISCANFILE");
	    scandata.group = NULL;
	    scandata.type = TYPE_SCAN;
	    scandata.thr_pool = NULL;
	    /* TODO: check ret value */
	    ret = scan_callback(NULL, conn->filename, conn->filename, visit_file, &data);	    /* callback freed it */
	    conn->filename = NULL;
	    *virus = scandata.infected;
	    if (ret == CL_BREAK) {
		thrmgr_group_terminate(conn->group);
		return CL_BREAK;
	    }
	    return scandata.errors > 0 ? scandata.errors : 0;
	case COMMAND_FILDES:
	    thrmgr_setactivetask(NULL, "FILDES");
#ifdef HAVE_FD_PASSING
	    if (conn->scanfd == -1)
		conn_reply_error(conn, "FILDES: didn't receive file descriptor.");
	    else {
		ret = scanfd(conn->scanfd, conn, NULL, engine, options, opts, desc, 0);
		if (ret == CL_VIRUS) {
		    *virus = 1;
		} else if (ret == CL_EMEM) {
		    if(optget(opts, "ExitOnOOM")->enabled)
			ret = -1;
		} else if (ret == CL_ETIMEOUT) {
			thrmgr_group_terminate(conn->group);
			ret = 1;
		} else
		    ret = 0;
	    }
	    logg("$Closed fd %d\n", conn->scanfd);
	    close(conn->scanfd);
	    return ret;
#else
	    conn_reply_error(conn, "FILDES support not compiled in.");
	    close(conn->scanfd);
	    return 0;
#endif
	case COMMAND_STATS:
	    thrmgr_setactivetask(NULL, "STATS");
	    if (conn->group)
		mdprintf(desc, "%u: ", conn->id);
	    thrmgr_printstats(desc);
	    return 0;
	case COMMAND_STREAM:
	    thrmgr_setactivetask(NULL, "STREAM");
	    ret = scanstream(desc, NULL, engine, options, opts, conn->term);
	    if (ret == CL_VIRUS)
		*virus = 1;
	    if (ret == CL_EMEM) {
		if(optget(opts, "ExitOnOOM")->enabled)
		    return -1;
	    }
	    return 0;
	case COMMAND_INSTREAMSCAN:
	    thrmgr_setactivetask(NULL, "INSTREAM");
	    ret = scanfd(conn->scanfd, conn, NULL, engine, options, opts, desc, 1);
	    if (ret == CL_VIRUS) {
		*virus = 1;
	    } else if (ret == CL_EMEM) {
		if(optget(opts, "ExitOnOOM")->enabled)
		    ret = -1;
	    } else if (ret == CL_ETIMEOUT) {
		thrmgr_group_terminate(conn->group);
		ret = 1;
	    } else
		ret = 0;
	    if (ftruncate(conn->scanfd, 0) == -1) {
		/* not serious, we're going to close it and unlink it anyway */
		logg("*ftruncate failed: %d\n", errno);
	    }
	    close(conn->scanfd);
	    conn->scanfd = -1;
	    cli_unlink(conn->filename);
	    return ret;
    }

    scandata.type = type;
    maxdirrec = optget(opts, "MaxDirectoryRecursion")->numarg;
    if (optget(opts, "FollowDirectorySymlinks")->enabled)
	flags |= CLI_FTW_FOLLOW_DIR_SYMLINK;
    if (optget(opts, "FollowFileSymlinks")->enabled)
	flags |= CLI_FTW_FOLLOW_FILE_SYMLINK;
    ret = cli_ftw(conn->filename, flags,  maxdirrec ? maxdirrec : INT_MAX, scan_callback, &data);
    if (ret == CL_EMEM)
	if(optget(opts, "ExitOnOOM")->enabled)
	    return -1;
    if (scandata.group && conn->cmdtype == COMMAND_MULTISCAN) {
	thrmgr_group_waitforall(group, &ok, &error, &total);
    } else {
	error = scandata.errors;
	total = scandata.total;
	ok = total - error - scandata.infected;
    }

    if (ok + error == total && (error != total)) {
	if (conn_reply_single(conn, conn->filename, "OK") == -1)
	    ret = CL_ETIMEOUT;
    }
    *virus = total - (ok + error);

    if (ret == CL_ETIMEOUT)
	thrmgr_group_terminate(conn->group);
    return error;
}