Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/lavabit/magma.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
authorLadar Levison <ladar@lavabit.com>2022-04-13 18:18:11 +0300
committerLadar Levison <ladar@lavabit.com>2022-04-13 18:18:11 +0300
commite6b9946cb087ee79c3bfeeae77ea9873e8b95bbd (patch)
treeab942231b633c76c3a33e3d7ea9f355c58f90426 /lib
parent0084ae9dd5c568b2892f1434111f48c9c43eb769 (diff)
Fixes several bugs in the ClamAV 0.103.5 unit tests for clamd.
Diffstat (limited to 'lib')
-rw-r--r--lib/patches/clamav/check_clamd_fixes_01035.patch677
1 files changed, 677 insertions, 0 deletions
diff --git a/lib/patches/clamav/check_clamd_fixes_01035.patch b/lib/patches/clamav/check_clamd_fixes_01035.patch
new file mode 100644
index 00000000..73804416
--- /dev/null
+++ b/lib/patches/clamav/check_clamd_fixes_01035.patch
@@ -0,0 +1,677 @@
+diff --git a/unit_tests/check_clamd.c b/unit_tests/check_clamd.c
+index 11b9e1b..8572649 100644
+--- a/unit_tests/check_clamd.c
++++ b/unit_tests/check_clamd.c
+@@ -23,8 +23,10 @@
+ #if HAVE_CONFIG_H
+ #include "clamav-config.h"
+ #endif
++#ifndef _WIN32
+ #include <arpa/inet.h>
+ #include <netinet/in.h>
++#endif
+ #include <fcntl.h>
+ #include <stdio.h>
+ #include <stdlib.h>
+@@ -34,31 +36,52 @@
+ #ifdef HAVE_SYS_TYPES_H
+ #include <sys/types.h>
+ #endif
++#ifdef _WIN32
++#include <windows.h>
++#include <winsock2.h>
++#else
+ #include <sys/socket.h>
+-#include <sys/stat.h>
+ #include <sys/un.h>
+ #include <unistd.h>
+-
+ #include <sys/time.h>
+ #include <sys/resource.h>
++#endif
++#include <sys/stat.h>
++
+ #ifdef HAVE_SYS_SELECT_H
+ #include <sys/select.h>
+ #endif
+-#include <sys/time.h>
+-#include <sys/types.h>
+-#include <unistd.h>
++
+ #include <check.h>
+
+ // libclamav
+ #include "clamav.h"
++#include "platform.h"
+ #include "version.h"
++#include "str.h"
+
+-// shared
++// common
+ #include "fdpassing.h"
+
+-#include "checks_common.h"
++static int conn_tcp(int port)
++{
++ struct sockaddr_in server;
++ int rc;
++ int sd = socket(AF_INET, SOCK_STREAM, 0);
++ ck_assert_msg(sd != -1, "Unable to create socket: %s\n", strerror(errno));
++
++ memset(&server, 0, sizeof(server));
++ server.sin_family = AF_INET;
++ server.sin_port = htons(port);
++ server.sin_addr.s_addr = inet_addr("127.0.0.1");
++
++ rc = connect(sd, (struct sockaddr *)&server, (socklen_t)sizeof(server));
++ ck_assert_msg(rc != -1, "Unable to connect(): %s\n", strerror(errno));
++ return sd;
++}
+
+ static int sockd;
++#ifndef _WIN32
+ #define SOCKET "clamd-test.socket"
+ static void conn_setup_mayfail(int may)
+ {
+@@ -74,80 +97,78 @@ static void conn_setup_mayfail(int may)
+ ck_assert_msg(sockd != -1, "Unable to create socket: %s\n", strerror(errno));
+
+ rc = connect(sockd, (struct sockaddr *)&nixsock, (socklen_t)sizeof(nixsock));
+- ck_assert_msg(rc != -1, "Unable to connect(): %s\n", strerror(errno));
++ if (rc == -1 && (may && (errno == ECONNREFUSED))) {
++ close(sockd);
++ sockd = -1;
++ return;
++ }
++ ck_assert_msg(rc != -1, "Unable to connect(): %s: %s\n", strerror(errno), SOCKET);
+
+ signal(SIGPIPE, SIG_IGN);
+ }
+-
+-static void conn_setup(void)
++#else
++#define PORT 3319
++static void conn_setup_mayfail(int may)
+ {
+- conn_setup_mayfail(0);
++ sockd = conn_tcp(PORT);
++ if (sockd == -1 && (may && (errno == ECONNREFUSED)))
++ return;
++ ck_assert_msg(sockd != -1, "Unable to connect(): %s\n", strerror(errno));
+ }
++#endif
+
+-static int conn_tcp(int port)
++static void conn_setup(void)
+ {
+- struct sockaddr_in server;
+- int rc;
+- int sd = socket(AF_INET, SOCK_STREAM, 0);
+- ck_assert_msg(sd != -1, "Unable to create socket: %s\n", strerror(errno));
+-
+- memset(&server, 0, sizeof(server));
+- server.sin_family = AF_INET;
+- server.sin_port = htons(port);
+- server.sin_addr.s_addr = inet_addr("127.0.0.1");
+-
+- rc = connect(sd, (struct sockaddr *)&server, (socklen_t)sizeof(server));
+- ck_assert_msg(rc != -1, "Unable to connect(): %s\n", strerror(errno));
+- return sd;
++ conn_setup_mayfail(0);
+ }
+
+ static void conn_teardown(void)
+ {
+ if (sockd != -1)
++#ifndef _WIN32
+ close(sockd);
++#else
++ closesocket(sockd);
++#endif
+ }
+
+ #ifndef REPO_VERSION
+ #define REPO_VERSION VERSION
+ #endif
+
+-#define SCANFILE BUILDDIR "/../test/clam.exe"
+-#define FOUNDREPLY SCANFILE ": ClamAV-Test-File.UNOFFICIAL FOUND"
++
++#define SCANFILE BUILDDIR PATHSEP ".." PATHSEP "test" PATHSEP "clam.exe"
++#define FOUNDREPLY PATHSEP "clam.exe: ClamAV-Test-File.UNOFFICIAL FOUND"
+
+ /* some clean file */
+-#define CLEANFILE SRCDIR "/Makefile.am"
+-#define CLEANREPLY CLEANFILE ": OK"
++#define CLEANFILE SRCDIR PATHSEP "Makefile.am"
++#define CLEANREPLY PATHSEP "Makefile.am: OK"
+ #define UNKNOWN_REPLY "UNKNOWN COMMAND"
+
+-#define NONEXISTENT "/nonexistent\vfilename"
+-
++#define NONEXISTENT PATHSEP "nonexistentfilename"
+ #define NONEXISTENT_REPLY NONEXISTENT ": File path check failure: No such file or directory. ERROR"
+
+-#define ACCDENIED BUILDDIR "/accdenied"
+-#define ACCDENIED_REPLY ACCDENIED ": Access denied. ERROR"
+ static int isroot = 0;
++
+ static void commands_setup(void)
+ {
++#ifndef _WIN32
+ const char *nonempty = "NONEMPTYFILE";
+- int fd = open(NONEXISTENT, O_RDONLY);
++#endif
++
++ /*
++ * Verify that our NONEXISTENT filepath indeed does not exist.
++ */
++ int fd = open(NONEXISTENT, O_RDONLY | O_BINARY);
+ if (fd != -1) close(fd);
+ ck_assert_msg(fd == -1, "Nonexistent file exists!\n");
+
+- fd = open(ACCDENIED, O_CREAT | O_WRONLY, S_IWUSR);
+- ck_assert_msg(fd != -1,
+- "Failed to create file for access denied tests: %s\n", strerror(errno));
+-
+- ck_assert_msg(fchmod(fd, S_IWUSR) != -1,
+- "Failed to chmod: %s\n", strerror(errno));
+- /* must not be empty file */
+- ck_assert_msg((size_t)write(fd, nonempty, strlen(nonempty)) == strlen(nonempty),
+- "Failed to write into testfile: %s\n", strerror(errno));
+- close(fd);
+-
+- /* skip access denied tests when run as root, as root will ignore
+- * permissions */
+- if (!geteuid())
++#ifndef _WIN32
++ /* Prepare the "isroot" global so we can skip some tests when run as root */
++ if (!geteuid()) {
+ isroot = 1;
++ }
++#endif
+ }
+
+ static void commands_teardown(void)
+@@ -192,10 +213,6 @@ static struct basic_test {
+ {"SCAN " NONEXISTENT, NULL, NONEXISTENT_REPLY, 1, 0, IDS_OK},
+ {"CONTSCAN " NONEXISTENT, NULL, NONEXISTENT_REPLY, 1, 0, IDS_REJECT},
+ {"MULTISCAN " NONEXISTENT, NULL, NONEXISTENT_REPLY, 1, 0, IDS_REJECT},
+- /* commands for access denied files */
+- {"SCAN " ACCDENIED, NULL, ACCDENIED_REPLY, 1, 1, IDS_OK},
+- {"CONTSCAN " ACCDENIED, NULL, ACCDENIED_REPLY, 1, 1, IDS_REJECT},
+- {"MULTISCAN " ACCDENIED, NULL, ACCDENIED_REPLY, 1, 1, IDS_REJECT},
+ /* commands with invalid/missing arguments */
+ {"SCAN", NULL, UNKNOWN_REPLY, 1, 0, IDS_REJECT},
+ {"CONTSCAN", NULL, UNKNOWN_REPLY, 1, 0, IDS_REJECT},
+@@ -234,8 +251,9 @@ static void *recvfull(int sd, size_t *len)
+
+ static void test_command(const char *cmd, size_t len, const char *extra, const char *expect, size_t expect_len)
+ {
+- void *recvdata;
++ char *recvdata;
+ ssize_t rc;
++ void *expected_string_offset = NULL;
+
+ rc = send(sockd, cmd, len, 0);
+ ck_assert_msg((size_t)rc == len, "Unable to send(): %s\n", strerror(errno));
+@@ -244,14 +262,39 @@ static void test_command(const char *cmd, size_t len, const char *extra, const c
+ rc = send(sockd, extra, strlen(extra), 0);
+ ck_assert_msg((size_t)rc == strlen(extra), "Unable to send() extra for %s: %s\n", cmd, strerror(errno));
+ }
++#ifdef _WIN32
++ shutdown(sockd, SD_SEND);
++#else
+ shutdown(sockd, SHUT_WR);
+- recvdata = recvfull(sockd, &len);
+-
+- ck_assert_msg(len == expect_len, "Reply has wrong size: %lu, expected %lu, reply: %s, expected: %s\n",
+- len, expect_len, recvdata, expect);
+-
+- rc = memcmp(recvdata, expect, expect_len);
+- ck_assert_msg(!rc, "Wrong reply for command %s: |%s|, expected: |%s|\n", cmd, recvdata, expect);
++#endif
++ recvdata = (char *)recvfull(sockd, &len);
++
++ // The file path may be returned as an real path, a relative path, a path with a
++ // symbolic link ... and so on. So the old method of simply checking the
++ // response length no longer provides any value. Instead this code uses split
++ // logic. If the patch still matches the expected response length exactly,
++ // it will do a strict string comparison. This works because commands which
++ // don't include a filename have very predictable lengths.
++ if (len == strlen(expect)) {
++ rc = memcmp(expect, recvdata, len);
++ ck_assert_msg(!rc, "Wrong reply for command %s.\nReceived: \n%s\nExpected: \n%s\n", cmd, recvdata, expect);
++ }
++ // When a response is encountered where the length doesn't match precisely,
++ // this second check will simply ensure the expected string is somewhere inside
++ // the response buffer. Technically, the all of the current expected response
++ // strings should match the end of the response buffer, since they only use the
++ // filename (technically the basename), and the colon delimited command response.
++ // But that behavior isn't guaranteed, so the code doesn't currently currently
++ // enforce it. Two examples of strings matched this way are:
++ // (CLEAN FILE SCANS) = /Makefile.am: OK
++ // (INFECTED FILE SCANS) = /clam.exe: ClamAV-Test-File.UNOFFICIAL FOUND
++ // Note the forward slash (technically path separator), and colon. They
++ // collectively ensures only the filename is used for string matching.
++ else {
++ expected_string_offset = memmem(expect, strlen(expect), recvdata, len);
++ ck_assert_msg(expected_string_offset == NULL, "Wrong reply for command %s.\nReceived: \n%s\nExpected: \n%s\n", cmd, recvdata, expect);
++ }
++
+ free(recvdata);
+ }
+
+@@ -307,7 +350,7 @@ START_TEST(test_compat_commands)
+
+ if (!test->extra) {
+ /* FILDES won't support this, because it expects
+- * strlen("FILDES\n") characters, then 1 character and the FD. */
++ * strlen("FILDES\n") characters, then 1 character and the FD. */
+ /* one packet, \r\n delimited command, followed by "extra" if needed */
+ snprintf(nsend, sizeof(nsend), "%s\r\n", test->command);
+ conn_setup();
+@@ -331,7 +374,7 @@ START_TEST(test_stats)
+ rc = send(sockd, "nSTATS\n", len, 0);
+ ck_assert_msg((size_t)rc == len, "Unable to send(): %s\n", strerror(errno));
+
+- recvdata = recvfull(sockd, &len);
++ recvdata = (char *)recvfull(sockd, &len);
+
+ ck_assert_msg(len > strlen(STATS_REPLY), "Reply has wrong size: %lu, minimum %lu, reply: %s\n",
+ len, strlen(STATS_REPLY), recvdata);
+@@ -353,7 +396,7 @@ static size_t prepare_instream(char *buf, size_t off, size_t buflen)
+ uint32_t chunk;
+ ck_assert_msg(CLAMSTAT(SCANFILE, &stbuf) != -1, "stat failed for %s: %s", SCANFILE, strerror(errno));
+
+- fd = open(SCANFILE, O_RDONLY);
++ fd = open(SCANFILE, O_RDONLY | O_BINARY);
+ ck_assert_msg(fd != -1, "open failed: %s\n", strerror(errno));
+
+ chunk = htonl(stbuf.st_size);
+@@ -372,7 +415,7 @@ static size_t prepare_instream(char *buf, size_t off, size_t buflen)
+
+ START_TEST(test_instream)
+ {
+- void *recvdata;
++ char *recvdata;
+ size_t len, expect_len;
+ char buf[4096] = "nINSTREAM\n";
+ size_t off = strlen(buf);
+@@ -383,20 +426,21 @@ START_TEST(test_instream)
+ conn_setup();
+ ck_assert_msg((size_t)send(sockd, buf, off, 0) == off, "send() failed: %s\n", strerror(errno));
+
+- recvdata = recvfull(sockd, &len);
++ recvdata = (char *)recvfull(sockd, &len);
+
+ expect_len = strlen(EXPECT_INSTREAM);
+ ck_assert_msg(len == expect_len, "Reply has wrong size: %lu, expected %lu, reply: %s\n",
+ len, expect_len, recvdata);
+
+ rc = memcmp(recvdata, EXPECT_INSTREAM, expect_len);
+- ck_assert_msg(!rc, "Wrong reply for command INSTREAM: |%s|, expected: |%s|\n", recvdata, EXPECT_INSTREAM);
++ ck_assert_msg(!rc, "Wrong reply for command INSTREAM:\nReceived: \n%s\nExpected: \n%s\n", recvdata, EXPECT_INSTREAM);
+ free(recvdata);
+
+ conn_teardown();
+ }
+ END_TEST
+
++#ifndef _WIN32
+ static int sendmsg_fd(int sockd, const char *mesg, size_t msg_len, int fd, int singlemsg)
+ {
+ struct msghdr msg;
+@@ -454,7 +498,7 @@ static void tst_fildes(const char *cmd, size_t len, int fd,
+ if (closefd)
+ close(fd);
+
+- recvdata = recvfull(sockd, &len);
++ recvdata = (char *)recvfull(sockd, &len);
+ p = strchr(recvdata, ':');
+
+ ck_assert_msg(!!p, "Reply doesn't contain ':' : %s\n", recvdata);
+@@ -467,7 +511,7 @@ static void tst_fildes(const char *cmd, size_t len, int fd,
+ len, expect_len, p, expect);
+
+ rc = memcmp(p, expect, expect_len);
+- ck_assert_msg(!rc, "Wrong reply for command %s: |%s|, expected: |%s|\n", cmd, p, expect);
++ ck_assert_msg(!rc, "Wrong reply for command %s:\nReceived: \n%s\nExpected: \n%s\n", cmd, p, expect);
+ free(recvdata);
+ conn_teardown();
+ }
+@@ -528,8 +572,8 @@ START_TEST(test_fildes)
+
+ if (!closefd) {
+ /* closefd:
+- * 1 - close fd right after sending
+- * 0 - close fd after receiving reply */
++ * 1 - close fd right after sending
++ * 0 - close fd after receiving reply */
+ close(fd);
+ }
+ }
+@@ -538,6 +582,10 @@ END_TEST
+ START_TEST(test_fildes_many)
+ {
+ const char idsession[] = "zIDSESSION";
++ const char fildes[] = "zFILDES";
++ const char end[] = "zEND";
++ const char ping[] = "zPING";
++
+ int dummyfd, i, killed = 0;
+ conn_setup();
+ dummyfd = open(SCANFILE, O_RDONLY);
+@@ -545,19 +593,19 @@ START_TEST(test_fildes_many)
+
+ ck_assert_msg(send(sockd, idsession, sizeof(idsession), 0) == sizeof(idsession), "send IDSESSION failed\n");
+ for (i = 0; i < 1024; i++) {
+- if (sendmsg_fd(sockd, "zFILDES", sizeof("zFILDES"), dummyfd, 1) == -1) {
++ if (sendmsg_fd(sockd, fildes, sizeof(fildes), dummyfd, 1) == -1) {
+ killed = 1;
+ break;
+ }
+ }
+ close(dummyfd);
+- if (send(sockd, "zEND", sizeof("zEND"), 0) == -1) {
++ if (send(sockd, end, sizeof(end), 0) == -1) {
+ killed = 1;
+ }
+ conn_teardown();
+
+ conn_setup();
+- test_command("zPING", sizeof("zPING"), NULL, "PONG", 5);
++ test_command(ping, sizeof(ping), NULL, "PONG", 5);
+ conn_teardown();
+ }
+ END_TEST
+@@ -567,15 +615,16 @@ START_TEST(test_fildes_unwanted)
+ char *recvdata;
+ size_t len;
+ int dummyfd;
++ const char idsession[] = "zIDSESSION";
+ conn_setup();
+ dummyfd = open(SCANFILE, O_RDONLY);
+
+ /* send a 'zVERSION\0' including the ancillary data.
+ * The \0 is from the extra char needed when sending ancillary data */
+- ck_assert_msg(sendmsg_fd(sockd, "zIDSESSION", strlen("zIDSESSION"), dummyfd, 1) != -1,
++ ck_assert_msg(sendmsg_fd(sockd, idsession, sizeof(idsession), dummyfd, 1) != -1,
+ "sendmsg failed: %s\n", strerror(errno));
+
+- recvdata = recvfull(sockd, &len);
++ recvdata = (char *)recvfull(sockd, &len);
+
+ ck_assert_msg(!strcmp(recvdata, "1: PROTOCOL ERROR: ancillary data sent without FILDES. ERROR"),
+ "Wrong reply: %s\n", recvdata);
+@@ -585,6 +634,7 @@ START_TEST(test_fildes_unwanted)
+ conn_teardown();
+ }
+ END_TEST
++#endif
+
+ START_TEST(test_idsession_stress)
+ {
+@@ -592,20 +642,22 @@ START_TEST(test_idsession_stress)
+ size_t i;
+ char *data, *p;
+ size_t len;
++ const char idsession[] = "zIDSESSION";
++ const char version[] = "zVERSION";
+
+ conn_setup();
+
+- ck_assert_msg(send(sockd, "zIDSESSION", sizeof("zIDSESSION"), 0) == sizeof("zIDSESSION"),
++ ck_assert_msg(send(sockd, idsession, sizeof(idsession), 0) == sizeof(idsession),
+ "send() failed: %s\n", strerror(errno));
+ for (i = 0; i < 1024; i++) {
+ snprintf(buf, sizeof(buf), "%u", (unsigned)(i + 1));
+- ck_assert_msg(send(sockd, "zVERSION", sizeof("zVERSION"), 0) == sizeof("zVERSION"),
++ ck_assert_msg(send(sockd, version, sizeof(version), 0) == sizeof(version),
+ "send failed: %s\n", strerror(errno));
+ data = recvpartial(sockd, &len, 1);
+ p = strchr(data, ':');
+- ck_assert_msg(!!p, "wrong VERSION reply (%u): %s\n", i, data);
++ ck_assert_msg(!!p, "wrong VERSION reply (%zu): %s\n", i, data);
+ *p++ = '\0';
+- ck_assert_msg(*p == ' ', "wrong VERSION reply (%u): %s\n", i, p);
++ ck_assert_msg(*p == ' ', "wrong VERSION reply (%zu): %s\n", i, p);
+ *p++ = '\0';
+
+ ck_assert_msg(!strcmp(p, VERSION_REPLY), "wrong VERSION reply: %s\n", data);
+@@ -620,46 +672,62 @@ END_TEST
+
+ #define TIMEOUT_REPLY "TIMED OUT WAITING FOR COMMAND\n"
+
++#ifndef _WIN32
++/*
++ * Test that we can still interact with clamd when it has a lot of active connections.
++ *
++ * Porting this test to work on Windows is too tedious at present.
++ * I suspect it should be rewritten using threads. For now, skip on Windows.
++ */
+ START_TEST(test_connections)
+ {
+ int rc;
+ int i;
+ struct rlimit rlim;
+ int *sock;
+- int nf, maxfd = 0;
++ int num_fds, maxfd = 0;
+ ck_assert_msg(getrlimit(RLIMIT_NOFILE, &rlim) != -1,
+ "Failed to get RLIMIT_NOFILE: %s\n", strerror(errno));
+- nf = rlim.rlim_cur - 5;
+- sock = malloc(sizeof(int) * nf);
++
++ num_fds = MIN(rlim.rlim_cur - 5, 250);
++
++ sock = malloc(sizeof(int) * num_fds);
+
+ ck_assert_msg(!!sock, "malloc failed\n");
+
+- for (i = 0; i < nf; i++) {
++ for (i = 0; i < num_fds; i++) {
+ /* just open connections, and let them time out */
+ conn_setup_mayfail(1);
+ if (sockd == -1) {
+- nf = i;
++ /* close the previous one, to leave space for one more connection */
++ i--;
++ close(sock[i]);
++ sock[i] = -1;
++
++ num_fds = i;
+ break;
+ }
+ sock[i] = sockd;
+ if (sockd > maxfd)
+ maxfd = sockd;
+ }
++
+ rc = fork();
+ ck_assert_msg(rc != -1, "fork() failed: %s\n", strerror(errno));
+ if (rc == 0) {
++ /* Child */
+ char dummy;
+ int ret;
+ fd_set rfds;
+ FD_ZERO(&rfds);
+- for (i = 0; i < nf; i++) {
++ for (i = 0; i < num_fds; i++) {
+ FD_SET(sock[i], &rfds);
+ }
+ while (1) {
+ ret = select(maxfd + 1, &rfds, NULL, NULL, NULL);
+ if (ret < 0)
+ break;
+- for (i = 0; i < nf; i++) {
++ for (i = 0; i < num_fds; i++) {
+ if (FD_ISSET(sock[i], &rfds)) {
+ if (recv(sock[i], &dummy, 1, 0) == 0) {
+ close(sock[i]);
+@@ -671,7 +739,8 @@ START_TEST(test_connections)
+ free(sock);
+ exit(0);
+ } else {
+- for (i = 0; i < nf; i++) {
++ /* Parent */
++ for (i = 0; i < num_fds; i++) {
+ close(sock[i]);
+ }
+ free(sock);
+@@ -681,50 +750,12 @@ START_TEST(test_connections)
+ test_command("RELOAD", sizeof("RELOAD") - 1, NULL, "RELOADING\n", sizeof("RELOADING\n") - 1);
+ conn_teardown();
+ }
++ /* Ok we're done, kill the child process if it's still up, else it might hang the test framework */
++ kill(rc, SIGKILL);
+ }
+ }
+ END_TEST
+-
+-START_TEST(test_stream)
+-{
+- char buf[BUFSIZ];
+- char *recvdata;
+- size_t len;
+- unsigned port;
+- int streamsd, infd, nread;
+-
+- infd = open(SCANFILE, O_RDONLY);
+-
+- ck_assert_msg(infd != -1, "open failed: %s\n", strerror(errno));
+- conn_setup();
+- ck_assert_msg(
+- send(sockd, "zSTREAM", sizeof("zSTREAM"), 0) == sizeof("zSTREAM"),
+- "send failed: %s\n", strerror(errno));
+- recvdata = recvpartial(sockd, &len, 1);
+- ck_assert_msg(sscanf(recvdata, "PORT %u\n", &port) == 1,
+- "Wrong stream reply: %s\n", recvdata);
+-
+- free(recvdata);
+- streamsd = conn_tcp(port);
+-
+- do {
+- nread = read(infd, buf, sizeof(buf));
+- if (nread > 0)
+- ck_assert_msg(send(streamsd, buf, nread, 0) == nread,
+- "send failed: %s\n", strerror(errno));
+- } while (nread > 0 || (nread == -1 && errno == EINTR));
+- ck_assert_msg(nread != -1, "read failed: %s\n", strerror(errno));
+- close(infd);
+- close(streamsd);
+-
+- recvdata = recvfull(sockd, &len);
+- ck_assert_msg(!strcmp(recvdata, "stream: ClamAV-Test-File.UNOFFICIAL FOUND"),
+- "Wrong reply: %s\n", recvdata);
+- free(recvdata);
+-
+- conn_teardown();
+-}
+-END_TEST
++#endif
+
+ #define END_CMD "zEND"
+ #define INSTREAM_CMD "zINSTREAM"
+@@ -757,7 +788,7 @@ static void test_idsession_commands(int split, int instream)
+ if (instream && test->ids == IDS_END) {
+ uint32_t chunk;
+ /* IDS_END - in middle of other commands, perfect for inserting
+- * INSTREAM */
++ * INSTREAM */
+ ck_assert_msg(p + sizeof(INSTREAM_CMD) + 544 < buf + sizeof(buf), "Buffer too small");
+ memcpy(p, INSTREAM_CMD, sizeof(INSTREAM_CMD));
+ p += sizeof(INSTREAM_CMD);
+@@ -785,11 +816,11 @@ static void test_idsession_commands(int split, int instream)
+ if (split) {
+ /* test corner-cases: 1-byte sends */
+ for (i = 0; i < (size_t)(p - buf); i++)
+- ck_assert_msg((size_t)send(sockd, &buf[i], 1, 0) == 1, "send() failed: %u, %s\n", i, strerror(errno));
++ ck_assert_msg((size_t)send(sockd, &buf[i], 1, 0) == 1, "send() failed: %zu, %s\n", i, strerror(errno));
+ } else {
+ ck_assert_msg(send(sockd, buf, p - buf, 0) == p - buf, "send() failed: %s\n", strerror(errno));
+ }
+- recvdata = recvfull(sockd, &len);
++ recvdata = (char *)recvfull(sockd, &len);
+ p = recvdata;
+ for (i = 0; i < sizeof(basic_tests) / sizeof(basic_tests[0]); i++) {
+ const struct basic_test *test = &basic_tests[i];
+@@ -802,10 +833,10 @@ static void test_idsession_commands(int split, int instream)
+ *q = '\0';
+ ck_assert_msg(sscanf(p, "%u", &id) == 1, "Wrong ID in reply: %s\n", p);
+ ck_assert_msg(id > 0, "ID cannot be zero");
+- ck_assert_msg(id <= j, "ID too big: %u, max: %u\n", id, j);
++ ck_assert_msg(id <= j, "ID too big: %u, max: %zu\n", id, j);
+ q += 2;
+- ck_assert_msg(!strcmp(q, replies[id - 1]),
+- "Wrong ID reply for ID %u: %s, expected %s\n",
++ ck_assert_msg(NULL != strstr(q, replies[id - 1]),
++ "Wrong ID reply for ID %u:\nReceived: \n%s\nExpected: \n%s\n",
+ id,
+ q, replies[id - 1]);
+ p = q + strlen(q) + 1;
+@@ -843,12 +874,15 @@ static Suite *test_clamd_suite(void)
+
+ tcase_add_loop_test(tc_commands, test_basic_commands, 0, sizeof(basic_tests) / sizeof(basic_tests[0]));
+ tcase_add_loop_test(tc_commands, test_compat_commands, 0, sizeof(basic_tests) / sizeof(basic_tests[0]));
++#ifndef _WIN32 // Disabled on Windows because fd-passing not supported on Windows
+ tcase_add_loop_test(tc_commands, test_fildes, 0, 4 * sizeof(fildes_cmds) / sizeof(fildes_cmds[0]));
++#endif
+
+ tcase_add_test(tc_commands, test_stats);
+ tcase_add_test(tc_commands, test_instream);
+- tcase_add_test(tc_commands, test_stream);
+ tcase_add_test(tc_commands, test_idsession);
++
++#ifndef _WIN32 // Disabled because fd-passing not supported on Windows
+ tc_stress = tcase_create("clamd stress test");
+ suite_add_tcase(s, tc_stress);
+ tcase_set_timeout(tc_stress, 20);
+@@ -860,19 +894,30 @@ static Suite *test_clamd_suite(void)
+ * tcp/unix sockets, if I too quickly connect ~193 times, even if
+ * listen backlog is higher.
+ * Don't run this test on BSD for now */
+- tcase_add_test(tc_stress, test_connections);
++ tcase_add_test(tc_stress, test_connections); // Disabled on Windows because test uses fork() instead of threads, and needs to be rewritten.
++#endif
+ #endif
+ return s;
+ }
+
+ int main(void)
+ {
+- int nf;
++ int num_fds;
++
++#ifdef _WIN32
++ WSADATA wsaData;
++ if (WSAStartup(MAKEWORD(2, 2), &wsaData) != NO_ERROR) {
++ fprintf(stderr, "Error at WSAStartup(): %d\n", WSAGetLastError());
++ return EXIT_FAILURE;
++ }
++#endif
++
+ Suite *s = test_clamd_suite();
+ SRunner *sr = srunner_create(s);
+- srunner_set_log(sr, BUILDDIR "/test-clamd.log");
++ srunner_set_log(sr, BUILDDIR PATHSEP "test-clamd.log");
+ srunner_run_all(sr, CK_NORMAL);
+- nf = srunner_ntests_failed(sr);
++ num_fds = srunner_ntests_failed(sr);
+ srunner_free(sr);
+- return (nf == 0) ? EXIT_SUCCESS : EXIT_FAILURE;
++ return (num_fds == 0) ? EXIT_SUCCESS : EXIT_FAILURE;
+ }
++
+diff --git a/unit_tests/check_common.sh b/unit_tests/check_common.sh
+index 1c651b0..e447228 100644
+--- a/unit_tests/check_common.sh
++++ b/unit_tests/check_common.sh
+@@ -86,6 +86,7 @@ ScanPDF yes
+ CommandReadTimeout 1
+ MaxQueue 800
+ MaxConnectionQueueLength 1024
++ConcurrentDatabaseReload no
+ EOF
+ }
+