|
|
|
@ -114,132 +114,250 @@ static int recvfd_and_scan(int desc, const struct cl_engine *engine, unsigned in |
|
|
|
|
} |
|
|
|
|
#endif |
|
|
|
|
|
|
|
|
|
int command(client_conn_t *conn, int timeout) |
|
|
|
|
enum commands parse_command(const char *cmd, const char **argument) |
|
|
|
|
{ |
|
|
|
|
*argument = NULL; |
|
|
|
|
if (!strncmp(cmd, CMD1, strlen(CMD1))) { /* SCAN */ |
|
|
|
|
*argument = cmd + strlen(CMD1) + 1; |
|
|
|
|
return COMMAND_SCAN; |
|
|
|
|
} else if (!strncmp(cmd, CMD3, strlen(CMD3))) { /* QUIT */ |
|
|
|
|
return COMMAND_SHUTDOWN; |
|
|
|
|
} else if (!strncmp(cmd, CMD4, strlen(CMD4))) { /* RELOAD */ |
|
|
|
|
return COMMAND_RELOAD; |
|
|
|
|
} else if (!strncmp(cmd, CMD5, strlen(CMD5))) { /* PING */ |
|
|
|
|
return COMMAND_PING; |
|
|
|
|
} else if (!strncmp(cmd, CMD6, strlen(CMD6))) { /* CONTSCAN */ |
|
|
|
|
*argument = cmd + strlen(CMD6) + 1; |
|
|
|
|
return COMMAND_CONTSCAN; |
|
|
|
|
} else if (!strncmp(cmd, CMD7, strlen(CMD7))) { /* VERSION */ |
|
|
|
|
return COMMAND_VERSION; |
|
|
|
|
} else if (!strncmp(cmd, CMD8, strlen(CMD8))) { /* STREAM */ |
|
|
|
|
return COMMAND_STREAM; |
|
|
|
|
#if 0 |
|
|
|
|
} else if (!strncmp(cmd, CMD9, strlen(CMD9))) { /* SESSION */ |
|
|
|
|
return COMMAND_SESSION; |
|
|
|
|
#endif |
|
|
|
|
} else if (!strncmp(cmd, CMD10, strlen(CMD10))) { /* END */ |
|
|
|
|
return COMMAND_END; |
|
|
|
|
} else if (!strncmp(cmd, CMD11, strlen(CMD11))) { /* SHUTDOWN */ |
|
|
|
|
return COMMAND_SHUTDOWN; |
|
|
|
|
} else if (!strncmp(cmd, CMD13, strlen(CMD13))) { /* MULTISCAN */ |
|
|
|
|
*argument = cmd + strlen(CMD13) + 1; |
|
|
|
|
return COMMAND_MULTISCAN; |
|
|
|
|
} else if (!strncmp(cmd, CMD14, strlen(CMD14))) { /* FILDES */ |
|
|
|
|
return COMMAND_FILDES; |
|
|
|
|
} else if (!strncmp(cmd, CMD15, strlen(CMD15))) { /* STATS */ |
|
|
|
|
return COMMAND_STATS; |
|
|
|
|
} else if (!strncmp(cmd, CMD16, strlen(CMD16))) { /* IDSESSION */ |
|
|
|
|
return COMMAND_IDSESSION; |
|
|
|
|
} |
|
|
|
|
return COMMAND_UNKNOWN; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
int command(client_conn_t *conn) |
|
|
|
|
{ |
|
|
|
|
int desc = conn->sd; |
|
|
|
|
char *buff = conn->cmd; |
|
|
|
|
size_t cmdlen = conn->cmdlen; |
|
|
|
|
struct cl_engine *engine = conn->engine; |
|
|
|
|
unsigned int options = conn->options; |
|
|
|
|
const struct optstruct *opts = conn->opts; |
|
|
|
|
const char term = conn->term; |
|
|
|
|
int type; /* TODO: make this enum */ |
|
|
|
|
|
|
|
|
|
if (conn->term) |
|
|
|
|
cli_chomp(buff); |
|
|
|
|
thrmgr_setactiveengine(engine); |
|
|
|
|
|
|
|
|
|
if(!strncmp(buff, CMD1, strlen(CMD1))) { /* SCAN */ |
|
|
|
|
thrmgr_setactivetask(NULL, CMD1); |
|
|
|
|
if(scan(buff + strlen(CMD1) + 1, term, NULL, engine, options, opts, desc, TYPE_SCAN) == -2) |
|
|
|
|
if(optget(opts, "ExitOnOOM")->enabled) |
|
|
|
|
return COMMAND_SHUTDOWN; |
|
|
|
|
|
|
|
|
|
} else if(!strncmp(buff, CMD3, strlen(CMD3))) { /* QUIT */ |
|
|
|
|
thrmgr_setactivetask(NULL, CMD3); |
|
|
|
|
return COMMAND_SHUTDOWN; |
|
|
|
|
|
|
|
|
|
} else if(!strncmp(buff, CMD4, strlen(CMD4))) { /* RELOAD */ |
|
|
|
|
thrmgr_setactivetask(NULL, CMD4); |
|
|
|
|
/* we'll reload, hide the engine, if we are the last
|
|
|
|
|
* holding a ref to the engine it'll be freed, |
|
|
|
|
* we don't want STATS command to access it */ |
|
|
|
|
thrmgr_setactiveengine(NULL); |
|
|
|
|
mdprintf(desc, "RELOADING%c", conn->term); |
|
|
|
|
return COMMAND_RELOAD; |
|
|
|
|
|
|
|
|
|
} else if(!strncmp(buff, CMD5, strlen(CMD5))) { /* PING */ |
|
|
|
|
thrmgr_setactivetask(NULL, CMD5); |
|
|
|
|
mdprintf(desc, "PONG%c", conn->term); |
|
|
|
|
|
|
|
|
|
} else if(!strncmp(buff, CMD6, strlen(CMD6))) { /* CONTSCAN */ |
|
|
|
|
thrmgr_setactivetask(NULL, CMD6); |
|
|
|
|
if(scan(buff + strlen(CMD6) + 1, term, NULL, engine, options, opts, desc, TYPE_CONTSCAN) == -2) |
|
|
|
|
if(optget(opts, "ExitOnOOM")->enabled) |
|
|
|
|
return COMMAND_SHUTDOWN; |
|
|
|
|
|
|
|
|
|
} else if(!strncmp(buff, CMD7, strlen(CMD7))) { /* VERSION */ |
|
|
|
|
uint32_t ver; |
|
|
|
|
|
|
|
|
|
thrmgr_setactivetask(NULL, CMD7); |
|
|
|
|
cl_engine_get(engine, CL_ENGINE_DB_VERSION, &ver); |
|
|
|
|
if(ver) { |
|
|
|
|
char timestr[32]; |
|
|
|
|
const char *tstr; |
|
|
|
|
time_t t; |
|
|
|
|
cl_engine_get(engine, CL_ENGINE_DB_TIME, &t); |
|
|
|
|
tstr = cli_ctime(&t, timestr, sizeof(timestr)); |
|
|
|
|
/* cut trailing \n */ |
|
|
|
|
timestr[strlen(tstr)-1] = '\0'; |
|
|
|
|
mdprintf(desc, "ClamAV %s/%u/%s%c", get_version(), (unsigned int) ver, tstr, conn->term); |
|
|
|
|
} else { |
|
|
|
|
mdprintf(desc, "ClamAV %s%c", get_version(), conn->term); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
} else if(!strncmp(buff, CMD8, strlen(CMD8))) { /* STREAM */ |
|
|
|
|
thrmgr_setactivetask(NULL, CMD8); |
|
|
|
|
if(scanstream(desc, NULL, engine, options, opts, conn->term) == CL_EMEM) |
|
|
|
|
if(optget(opts, "ExitOnOOM")->enabled) |
|
|
|
|
return COMMAND_SHUTDOWN; |
|
|
|
|
|
|
|
|
|
} else if(!strncmp(buff, CMD9, strlen(CMD9))) { /* SESSION */ |
|
|
|
|
thrmgr_setactivetask(NULL, CMD9); |
|
|
|
|
return COMMAND_SESSION; |
|
|
|
|
|
|
|
|
|
} else if(!strncmp(buff, CMD10, strlen(CMD10))) { /* END */ |
|
|
|
|
thrmgr_setactivetask(NULL, CMD10); |
|
|
|
|
return COMMAND_END; |
|
|
|
|
|
|
|
|
|
} else if(!strncmp(buff, CMD11, strlen(CMD11))) { /* SHUTDOWN */ |
|
|
|
|
thrmgr_setactivetask(NULL, CMD11); |
|
|
|
|
return COMMAND_SHUTDOWN; |
|
|
|
|
struct scan_cb_data scandata; |
|
|
|
|
struct cli_ftw_cbdata data; |
|
|
|
|
unsigned ok, error, total; |
|
|
|
|
jobgroup_t group = JOBGROUP_INITIALIZER; |
|
|
|
|
|
|
|
|
|
} else if(!strncmp(buff, CMD13, strlen(CMD13))) { /* MULTISCAN */ |
|
|
|
|
struct scan_cb_data scandata; |
|
|
|
|
struct cli_ftw_cbdata data; |
|
|
|
|
unsigned ok, error, total; |
|
|
|
|
const char *path = buff + strlen(CMD13) + 1; |
|
|
|
|
jobgroup_t group = JOBGROUP_INITIALIZER; |
|
|
|
|
data.data = &scandata; |
|
|
|
|
|
|
|
|
|
memset(&scandata, 0, sizeof(scandata)); |
|
|
|
|
scandata.type = TYPE_MULTISCAN; |
|
|
|
|
scandata.odesc = desc; |
|
|
|
|
scandata.term = term; |
|
|
|
|
scandata.options = options; |
|
|
|
|
scandata.engine = engine; |
|
|
|
|
scandata.opts = opts; |
|
|
|
|
scandata.thr_pool = conn->thrpool; |
|
|
|
|
scandata.group = &group; |
|
|
|
|
thrmgr_setactivetask(buff+strlen(CMD13)+1, CMD13); |
|
|
|
|
int maxdirrec = optget(opts, "MaxDirectoryRecursion")->numarg; |
|
|
|
|
|
|
|
|
|
if (cli_ftw(path, CLI_FTW_STD, maxdirrec ? maxdirrec : INT_MAX, scan_callback, &data) == CL_EMEM)
|
|
|
|
|
if(optget(opts, "ExitOnOOM")->enabled) |
|
|
|
|
return COMMAND_SHUTDOWN; |
|
|
|
|
thrmgr_group_waitforall(&group, &ok, &error, &total); |
|
|
|
|
if (ok + error == total) { |
|
|
|
|
mdprintf(desc, "%s: OK%c", path, conn->term); |
|
|
|
|
} |
|
|
|
|
thrmgr_setactiveengine(engine); |
|
|
|
|
|
|
|
|
|
} else if(!strncmp(buff, CMD14, strlen(CMD14))) { /* FILDES */ |
|
|
|
|
thrmgr_setactivetask(NULL, CMD14); |
|
|
|
|
data.data = &scandata; |
|
|
|
|
memset(&scandata, 0, sizeof(scandata)); |
|
|
|
|
scandata.group = conn->group; |
|
|
|
|
scandata.type = type; |
|
|
|
|
scandata.odesc = desc; |
|
|
|
|
scandata.term = term; |
|
|
|
|
scandata.options = options; |
|
|
|
|
scandata.engine = engine; |
|
|
|
|
scandata.opts = opts; |
|
|
|
|
scandata.thr_pool = conn->thrpool; |
|
|
|
|
|
|
|
|
|
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: |
|
|
|
|
thrmgr_setactivetask(NULL, "MULTISCAN"); |
|
|
|
|
type = TYPE_MULTISCAN; |
|
|
|
|
scandata.group = &group; |
|
|
|
|
break; |
|
|
|
|
case COMMAND_MULTISCANFILE: |
|
|
|
|
thrmgr_setactivetask(NULL, "MULTISCANFILE"); |
|
|
|
|
scandata.group = NULL; |
|
|
|
|
/* TODO: check ret value */ |
|
|
|
|
scan_callback(NULL, conn->filename, conn->filename, visit_file, &data); |
|
|
|
|
break; |
|
|
|
|
case COMMAND_FILDES: |
|
|
|
|
thrmgr_setactivetask(NULL, "FILDES"); |
|
|
|
|
#ifdef HAVE_FD_PASSING |
|
|
|
|
if (conn->scanfd == -1) |
|
|
|
|
mdprintf(desc, "FILDES: didn't receive file descriptor %c", conn->term); |
|
|
|
|
else if (scanfd(conn->scanfd, conn->term, NULL, engine, options, opts, desc) == -2) |
|
|
|
|
if(optget(opts, "ExitOnOOM")->enabled) |
|
|
|
|
return COMMAND_SHUTDOWN; |
|
|
|
|
if (conn->scanfd == -1) |
|
|
|
|
mdprintf(desc, "FILDES: didn't receive file descriptor %c", conn->term); |
|
|
|
|
else if (scanfd(conn->scanfd, conn->term, NULL, engine, options, opts, desc) == -2) |
|
|
|
|
if(optget(opts, "ExitOnOOM")->enabled) |
|
|
|
|
return COMMAND_SHUTDOWN; |
|
|
|
|
return 0; |
|
|
|
|
#else |
|
|
|
|
mdprintf(desc, "FILDES support not compiled inERROR%c",conn->term); |
|
|
|
|
return -1; |
|
|
|
|
mdprintf(desc, "FILDES support not compiled inERROR%c",conn->term); |
|
|
|
|
return -1; |
|
|
|
|
#endif |
|
|
|
|
} else if(!strncmp(buff, CMD15, strlen(CMD15))) { /* STATS */ |
|
|
|
|
thrmgr_setactivetask(NULL, CMD15); |
|
|
|
|
case COMMAND_STATS: |
|
|
|
|
thrmgr_setactivetask(NULL, "STATS"); |
|
|
|
|
thrmgr_printstats(desc); |
|
|
|
|
} else { |
|
|
|
|
mdprintf(desc, "UNKNOWN COMMAND%c", conn->term); |
|
|
|
|
return 0; |
|
|
|
|
case COMMAND_STREAM: |
|
|
|
|
thrmgr_setactivetask(NULL, "STREAM"); |
|
|
|
|
if(scanstream(desc, NULL, engine, options, opts, conn->term) == CL_EMEM) |
|
|
|
|
if(optget(opts, "ExitOnOOM")->enabled) |
|
|
|
|
return COMMAND_SHUTDOWN; |
|
|
|
|
return 0; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
int maxdirrec = optget(opts, "MaxDirectoryRecursion")->numarg; |
|
|
|
|
// TODO: flags symlink from opt
|
|
|
|
|
if (cli_ftw(conn->filename, CLI_FTW_STD, maxdirrec ? maxdirrec : INT_MAX, scan_callback, &data) == CL_EMEM)
|
|
|
|
|
if(optget(opts, "ExitOnOOM")->enabled) |
|
|
|
|
return COMMAND_SHUTDOWN; |
|
|
|
|
if (scandata.group) |
|
|
|
|
thrmgr_group_waitforall(&group, &ok, &error, &total); |
|
|
|
|
if (ok + error == total) { |
|
|
|
|
mdprintf(desc, "%s: OK%c", conn->filename, conn->term); |
|
|
|
|
} |
|
|
|
|
return 0; /* no error and no 'special' command executed */ |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
static int dispatch_command(const client_conn_t *conn, enum commands command, const char *argument) |
|
|
|
|
{ |
|
|
|
|
client_conn_t *dup_conn = (client_conn_t *) malloc(sizeof(struct client_conn_tag)); |
|
|
|
|
if(!dup_conn) { |
|
|
|
|
logg("!Can't allocate memory for client_conn\n"); |
|
|
|
|
return -1; |
|
|
|
|
} |
|
|
|
|
memcpy(dup_conn, conn, sizeof(*conn)); |
|
|
|
|
dup_conn->cmdtype = command; |
|
|
|
|
if(cl_engine_addref(dup_conn->engine)) { |
|
|
|
|
logg("!cl_engine_addref() failed\n"); |
|
|
|
|
return -1; |
|
|
|
|
} |
|
|
|
|
dup_conn->scanfd = -1; |
|
|
|
|
switch (command) { |
|
|
|
|
case COMMAND_FILDES: |
|
|
|
|
dup_conn->scanfd = conn->scanfd; |
|
|
|
|
break; |
|
|
|
|
case COMMAND_SCAN: |
|
|
|
|
case COMMAND_CONTSCAN: |
|
|
|
|
case COMMAND_MULTISCAN: |
|
|
|
|
dup_conn->filename = strdup(argument); |
|
|
|
|
if (!dup_conn->filename) { |
|
|
|
|
logg("!Failed to allocate memory for filename\n"); |
|
|
|
|
return -1; |
|
|
|
|
} |
|
|
|
|
break; |
|
|
|
|
case COMMAND_STREAM: |
|
|
|
|
case COMMAND_STATS: |
|
|
|
|
/* just dispatch the command */ |
|
|
|
|
break; |
|
|
|
|
} |
|
|
|
|
if(!thrmgr_dispatch(dup_conn->thrpool, dup_conn)) { |
|
|
|
|
logg("!thread dispatch failed\n"); |
|
|
|
|
return -2; |
|
|
|
|
} |
|
|
|
|
return 0; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/* 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 command, 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 */ |
|
|
|
|
switch (command) { |
|
|
|
|
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: |
|
|
|
|
mdprintf(desc, "PONG%c", term); |
|
|
|
|
return 1; |
|
|
|
|
case COMMAND_VERSION: |
|
|
|
|
{ |
|
|
|
|
uint32_t ver; |
|
|
|
|
cl_engine_get(engine, CL_ENGINE_DB_VERSION, &ver); |
|
|
|
|
if(ver) { |
|
|
|
|
char timestr[32]; |
|
|
|
|
const char *tstr; |
|
|
|
|
time_t t; |
|
|
|
|
cl_engine_get(engine, CL_ENGINE_DB_TIME, &t); |
|
|
|
|
tstr = cli_ctime(&t, timestr, sizeof(timestr)); |
|
|
|
|
/* cut trailing \n */ |
|
|
|
|
timestr[strlen(tstr)-1] = '\0'; |
|
|
|
|
mdprintf(desc, "ClamAV %s/%u/%s%c", get_version(), (unsigned int) ver, tstr, term); |
|
|
|
|
} else { |
|
|
|
|
mdprintf(desc, "ClamAV %s%c", get_version(), conn->term); |
|
|
|
|
} |
|
|
|
|
return 1; |
|
|
|
|
} |
|
|
|
|
case COMMAND_FILDES: |
|
|
|
|
case COMMAND_SCAN: |
|
|
|
|
case COMMAND_CONTSCAN: |
|
|
|
|
case COMMAND_STREAM: |
|
|
|
|
case COMMAND_MULTISCAN: |
|
|
|
|
case COMMAND_STATS: |
|
|
|
|
return dispatch_command(conn, command, argument); |
|
|
|
|
case COMMAND_IDSESSION: |
|
|
|
|
if (conn->group) { |
|
|
|
|
/* we are already inside an idsession/multiscan */ |
|
|
|
|
mdprintf(desc, "UNKNOWN COMMAND%c", term); |
|
|
|
|
return 1; |
|
|
|
|
} |
|
|
|
|
conn->group = calloc(1, sizeof(*conn->group)); |
|
|
|
|
if (!conn->group) |
|
|
|
|
return CL_EMEM; |
|
|
|
|
return 0; |
|
|
|
|
case COMMAND_END: |
|
|
|
|
if (!conn->group) { |
|
|
|
|
/* end without idsession? */ |
|
|
|
|
mdprintf(desc, "UNKNOWN COMMAND%c", term); |
|
|
|
|
return 1; |
|
|
|
|
} |
|
|
|
|
return 1; |
|
|
|
|
/*case COMMAND_UNKNOWN:*/ |
|
|
|
|
default: |
|
|
|
|
mdprintf(desc, "UNKNOWN COMMAND%c", term); |
|
|
|
|
return 1; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|