+ /* prepare variables */
+ drweb_cmd = htonl(DRWEBD_SCAN_CMD);
+ drweb_flags = htonl(DRWEBD_RETURN_VIRUSES | DRWEBD_IS_MAIL);
+
+ if (*scanner_options != '/')
+ {
+ /* calc file size */
+ if ((drweb_fd = exim_open2(CCS eml_filename, O_RDONLY)) == -1)
+ return m_panic_defer_3(scanent, NULL,
+ string_sprintf("can't open spool file %s: %s",
+ eml_filename, strerror(errno)),
+ malware_daemon_ctx.sock);
+
+ if ((fsize = lseek(drweb_fd, 0, SEEK_END)) == -1)
+ {
+ int err;
+badseek: err = errno;
+ (void)close(drweb_fd);
+ return m_panic_defer_3(scanent, NULL,
+ string_sprintf("can't seek spool file %s: %s",
+ eml_filename, strerror(err)),
+ malware_daemon_ctx.sock);
+ }
+ fsize_uint = (unsigned int) fsize;
+ if ((off_t)fsize_uint != fsize)
+ {
+ (void)close(drweb_fd);
+ return m_panic_defer_3(scanent, NULL,
+ string_sprintf("seeking spool file %s, size overflow",
+ eml_filename),
+ malware_daemon_ctx.sock);
+ }
+ drweb_slen = htonl(fsize);
+ if (lseek(drweb_fd, 0, SEEK_SET) < 0)
+ goto badseek;
+
+ DEBUG(D_acl) debug_printf_indent("Malware scan: issuing %s remote scan [%s]\n",
+ scanner_name, scanner_options);
+
+ /* send scan request */
+ if ((send(malware_daemon_ctx.sock, &drweb_cmd, sizeof(drweb_cmd), 0) < 0) ||
+ (send(malware_daemon_ctx.sock, &drweb_flags, sizeof(drweb_flags), 0) < 0) ||
+ (send(malware_daemon_ctx.sock, &drweb_fin, sizeof(drweb_fin), 0) < 0) ||
+ (send(malware_daemon_ctx.sock, &drweb_slen, sizeof(drweb_slen), 0) < 0))
+ {
+ (void)close(drweb_fd);
+ return m_panic_defer_3(scanent, CUS callout_address, string_sprintf(
+ "unable to send commands to socket (%s)", scanner_options),
+ malware_daemon_ctx.sock);
+ }
+
+ if (!(drweb_fbuf = store_malloc(fsize_uint)))
+ {
+ (void)close(drweb_fd);
+ return m_panic_defer_3(scanent, NULL,
+ string_sprintf("unable to allocate memory %u for file (%s)",
+ fsize_uint, eml_filename),
+ malware_daemon_ctx.sock);
+ }
+
+ if ((result = read (drweb_fd, drweb_fbuf, fsize)) == -1)
+ {
+ int err = errno;
+ (void)close(drweb_fd);
+ store_free(drweb_fbuf);
+ return m_panic_defer_3(scanent, NULL,
+ string_sprintf("can't read spool file %s: %s",
+ eml_filename, strerror(err)),
+ malware_daemon_ctx.sock);
+ }
+ (void)close(drweb_fd);
+
+ /* send file body to socket */
+ if (send(malware_daemon_ctx.sock, drweb_fbuf, fsize, 0) < 0)
+ {
+ store_free(drweb_fbuf);
+ return m_panic_defer_3(scanent, CUS callout_address, string_sprintf(
+ "unable to send file body to socket (%s)", scanner_options),
+ malware_daemon_ctx.sock);
+ }
+ store_free(drweb_fbuf);
+ }
+ else
+ {
+ drweb_slen = htonl(Ustrlen(eml_filename));
+
+ DEBUG(D_acl) debug_printf_indent("Malware scan: issuing %s local scan [%s]\n",
+ scanner_name, scanner_options);
+
+ /* send scan request */
+ if ((send(malware_daemon_ctx.sock, &drweb_cmd, sizeof(drweb_cmd), 0) < 0) ||
+ (send(malware_daemon_ctx.sock, &drweb_flags, sizeof(drweb_flags), 0) < 0) ||
+ (send(malware_daemon_ctx.sock, &drweb_slen, sizeof(drweb_slen), 0) < 0) ||
+ (send(malware_daemon_ctx.sock, eml_filename, Ustrlen(eml_filename), 0) < 0) ||
+ (send(malware_daemon_ctx.sock, &drweb_fin, sizeof(drweb_fin), 0) < 0))
+ return m_panic_defer_3(scanent, CUS callout_address, string_sprintf(
+ "unable to send commands to socket (%s)", scanner_options),
+ malware_daemon_ctx.sock);
+ }
+
+ /* wait for result */
+ if (!recv_len(malware_daemon_ctx.sock, &drweb_rc, sizeof(drweb_rc), tmo))
+ return m_panic_defer_3(scanent, CUS callout_address,
+ US"unable to read return code", malware_daemon_ctx.sock);
+ drweb_rc = ntohl(drweb_rc);
+
+ if (!recv_len(malware_daemon_ctx.sock, &drweb_vnum, sizeof(drweb_vnum), tmo))
+ return m_panic_defer_3(scanent, CUS callout_address,
+ US"unable to read the number of viruses", malware_daemon_ctx.sock);
+ drweb_vnum = ntohl(drweb_vnum);
+
+ /* "virus(es) found" if virus number is > 0 */
+ if (drweb_vnum)
+ {
+ gstring * g = NULL;
+
+ /* setup default virus name */
+ malware_name = US"unknown";
+
+ /* set up match regex */
+ if (!drweb_re)
+ drweb_re = m_pcre_compile(drweb_re_str, FALSE, &errstr);
+
+ /* read and concatenate virus names into one string */
+ for (int i = 0; i < drweb_vnum; i++)
+ {
+ pcre2_match_data * md = pcre2_match_data_create(2, pcre_gen_ctx);
+
+ /* read the size of report */
+ if (!recv_len(malware_daemon_ctx.sock, &drweb_slen, sizeof(drweb_slen), tmo))
+ return m_panic_defer_3(scanent, CUS callout_address,
+ US"cannot read report size", malware_daemon_ctx.sock);
+ drweb_slen = ntohl(drweb_slen);
+
+ /* assume tainted, since it is external input */
+ tmpbuf = store_get(drweb_slen, GET_TAINTED);
+
+ /* read report body */
+ if (!recv_len(malware_daemon_ctx.sock, tmpbuf, drweb_slen, tmo))
+ return m_panic_defer_3(scanent, CUS callout_address,
+ US"cannot read report string", malware_daemon_ctx.sock);
+ tmpbuf[drweb_slen] = '\0';
+
+ /* try matcher on the line, grab substring */
+ result = pcre2_match(drweb_re, (PCRE2_SPTR)tmpbuf, PCRE2_ZERO_TERMINATED,
+ 0, 0, md, pcre_gen_mtc_ctx);
+ if (result >= 2)
+ {
+ PCRE2_SIZE * ovec = pcre2_get_ovector_pointer(md);
+
+ if (i==0) /* the first name we just copy to malware_name */
+ g = string_catn(NULL, US ovec[2], ovec[3] - ovec[2]);
+
+ else /* concatenate each new virus name to previous */
+ {
+ g = string_catn(g, US"/", 1);
+ g = string_catn(g, US ovec[2], ovec[3] - ovec[2]);
+ }
+ }
+ /* pcre2_match_data_free(md); gen ctx needs no free */
+ }
+ malware_name = string_from_gstring(g);
+ }
+ else
+ {
+ const char *drweb_s = NULL;
+
+ if (drweb_rc & DERR_READ_ERR) drweb_s = "read error";
+ if (drweb_rc & DERR_NOMEMORY) drweb_s = "no memory";
+ if (drweb_rc & DERR_TIMEOUT) drweb_s = "timeout";
+ if (drweb_rc & DERR_BAD_CALL) drweb_s = "wrong command";
+ /* retcodes DERR_SYMLINK, DERR_NO_REGFILE, DERR_SKIPPED.
+ * DERR_TOO_BIG, DERR_TOO_COMPRESSED, DERR_SPAM,
+ * DERR_CRC_ERROR, DERR_READSOCKET, DERR_WRITE_ERR
+ * and others are ignored */
+ if (drweb_s)
+ return m_panic_defer_3(scanent, CUS callout_address,
+ string_sprintf("drweb daemon retcode 0x%x (%s)", drweb_rc, drweb_s),
+ malware_daemon_ctx.sock);
+
+ /* no virus found */
+ malware_name = NULL;
+ }
+ break;
+ } /* drweb */
+#endif
+
+#ifndef DISABLE_MAL_AVE
+ case M_AVES: /* "aveserver" scanner type -------------------------------- */
+ {
+ uschar buf[32768];
+ int result;