diff --git a/services/virus_scan/clamd_mod.c b/services/virus_scan/clamd_mod.c index dfca8b8..8fdffc2 100644 --- a/services/virus_scan/clamd_mod.c +++ b/services/virus_scan/clamd_mod.c @@ -27,6 +27,7 @@ int CLAMD_SESSION_REUSE = 100; int CLAMD_SESSION_TIMEOUT = 10; /*seconds*/ int CLAMD_MAX_CONNECTIONS = -1; char CLAMD_ADDR[CI_MAX_PATH]; +char *CLAMD_COMMAND = "zSCAN"; static struct ci_conf_entry clamd_conf_variables[] = { #ifdef HAVE_FD_PASSING @@ -38,6 +39,7 @@ static struct ci_conf_entry clamd_conf_variables[] = { {"SessionReuse", &CLAMD_SESSION_REUSE, ci_cfg_set_int, NULL}, {"SessionTimeout", &CLAMD_SESSION_TIMEOUT, ci_cfg_set_int, NULL}, {"MaxConnections", &CLAMD_MAX_CONNECTIONS, ci_cfg_set_int, NULL}, + {"ClamdCommand", &CLAMD_COMMAND, ci_cfg_set_str, NULL}, {NULL, NULL, NULL, NULL} }; @@ -411,34 +413,114 @@ static const char *clamd_response(struct clamd_conn *conn, char *buf, size_t siz return buf; } -static int send_filename(struct clamd_conn *conn, const char *filename) +static int send_filename_scan(struct clamd_conn *conn, const char *filename) { int len; char buf[CI_MAX_PATH]; - if (!conn || conn->sockd < 0) + len = snprintf(buf, sizeof(buf), "zSCAN %s", filename); + if (len >= sizeof(buf)) { + ci_debug_printf(1, "Too long filename: %s\n", filename); + return 0; + } + + ci_debug_printf(5, "send_filename_scan: Send '%s' to clamd (len=%d)\n", buf, len); + if (clamd_command(conn, buf, len + 1) <= 0) { + return 0; + } + return 1; +} + +static int send_filename_instream_filesize(const char *filename, unsigned int *len) +{ + FILE *fp; + + fp = fopen(filename, "r"); + if (!fp) { + ci_debug_printf(1, "Failed open file: '%s'\n", filename); return -1; + } + fseek(fp, 0L, SEEK_END); + *len = htonl(ftell(fp)); + fclose(fp); + return 1; +} - if (! filename) { - ci_debug_printf(1, "send_filename: Filename to be sent to clamd cannot be NULL!\n"); +/** + * Send: zINSTREAM\0\0\0\0\0 + */ +static int send_filename_instream(struct clamd_conn *conn, const char *filename) +{ + int fd; + int len; + unsigned int len_file; + char buf[CI_MAX_PATH]; + + if (send_filename_instream_filesize(filename, &len_file) < 0) { + return -1; + } + + len = snprintf(buf, sizeof(buf), "zINSTREAM"); + ci_debug_printf(5, "send_filename_instream: Send zINSTREAM to clamd\n"); + if (clamd_command(conn, buf, len + 1) <= 0) { + ci_debug_printf(1, "Failed send zINSTREAM to clamd\n"); return 0; } - ci_debug_printf(5, "send_filename: File '%s' should be scanned.\n", filename); - len = snprintf(buf, sizeof(buf), "zSCAN %s", filename); - if (len >= sizeof(buf)) { - ci_debug_printf(1, "Too long filename: %s\n", filename); + memcpy(buf, &len_file, sizeof(len_file)); + ci_debug_printf(5, "send_filename_instream: Send file size to clamd\n"); + if (clamd_command(conn, buf, sizeof(len_file)) <= 0) { return 0; } - ci_debug_printf(5, "send_filename: Send '%s' to clamd (len=%d)\n", buf, len); - if (clamd_command(conn, buf, len + 1) <= 0) { + fd = open(filename, O_RDONLY); + if (fd == -1) { + ci_debug_printf(1, "Failed open file: '%s'\n", filename); return 0; } + while ((len = read(fd, buf, CI_MAX_PATH - 1)) > 0) { + ci_debug_printf(5, "send_filename_instream: Send %d/%d byte to clamd\n", len, ntohl(len_file)); + if (clamd_command(conn, buf, len) <= 0) { + close(fd); + ci_debug_printf(1, "Failed send %d/%d byte to clamd\n", len, ntohl(len_file)); + return 0; + } + } + close(fd); + + ci_debug_printf(5, "send_filename_instream: INSTREAM close\n"); + memset(buf, 0, 4); + if (clamd_command(conn, buf, 4) <= 0) { + ci_debug_printf(1, "Failed INSTREAM close\n"); + return 0; + } return 1; } +/** + * return -1 internal error, 0 clamd error, >0 ok + */ +static int send_filename(struct clamd_conn *conn, const char *filename) +{ + if (!conn || conn->sockd < 0) + return -1; + + if (! filename) { + ci_debug_printf(1, "send_filename: Filename to be sent to clamd cannot be NULL!\n"); + return -1; + } + ci_debug_printf(5, "send_filename: File '%s' should be scanned.\n", filename); + + if (!strcmp(CLAMD_COMMAND, "zSCAN")) { + return send_filename_scan(conn, filename); + } else if (!strcmp(CLAMD_COMMAND, "zINSTREAM")) { + return send_filename_instream(conn, filename); + } + ci_debug_printf(1, "Command not recongnized: '%s'!\n", CLAMD_COMMAND); + return 0; +} + #ifdef HAVE_FD_PASSING static int send_fd(struct clamd_conn *conn, int fd) { @@ -645,9 +727,15 @@ static int clamd_scan_simple_file(ci_simple_file_t *body, av_virus_info_t *vinfo filename = body->filename; ci_debug_printf(5, "clamd_scan: Scan file '%s'\n", body->filename); ret = send_filename(&conn, filename); + if (ret < 0) { + ci_debug_printf(1, "clamd_scan: Error internal!\n"); + clamd_release_connection(&conn, 1); + sinfo->err = "Undefined"; + return 0; + } } const char *response = clamd_response(&conn, buffer, sizeof(buffer)); - if (ret < 0) { + if (!response) { ci_debug_printf(1, "clamd_scan: Error reading response from clamd server!\n"); clamd_release_connection(&conn, 1); sinfo->err = "Clamd response failed"; diff --git a/services/virus_scan/clamd_mod.conf b/services/virus_scan/clamd_mod.conf index 9df5c65..6f8efae 100644 --- a/services/virus_scan/clamd_mod.conf +++ b/services/virus_scan/clamd_mod.conf @@ -45,6 +45,14 @@ Module common clamd_mod.so # Default: # clamd_mod.ClamdPort None, ClamdSocket is used. +# TAG: clamd_mod.ClamdCommand +# Format: clamd_mod.ClamdCommand command +# Description: +# The command to be used to dialogue to Clamd if a ClamdPort is specified. +# Accept value: zINSTREAM, zSCAN +# Default: +# clamd_mod.ClamdCommand zSCAN + # TAG: clamd_mod.ReportVirusOnFailure # Format: clamd_mod.ReportVirusOnFailure on|off # Description: