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