I'm in the process of a major refactor of malware.c and I discover that
we have an internally-developed scanner access method we we never got
around to contributing into the mainline. It's a generic socket
method; we use it for talking to some custom perl.
I've attached it as a patch. Does anyone feel strongly that it should
not go into the exim sourcebase?
(More on the refactor later. Currently down from 2000 lines to 1800).
--
Cheers,
Jeremy
diff --git a/src/src/malware.c b/src/src/malware.c
index 2b4d282..2803c6b 100644
--- a/src/src/malware.c
+++ b/src/src/malware.c
@@ -1812,6 +1812,203 @@ try_next_server:
/* ----------------------------------------------------------------------- */
+ /* generic "sock" scanner type ------------------------------------------- */
+ /* This code was derived by Martin Poole from the clamd code contributed by David Saez
+ and the cmdline code
+ */
+ else if (strcmpic(scanner_name,US"sock") == 0) {
+ uschar *sock_options;
+ uschar sock_options_buffer[1024];
+ uschar sock_options_default[] = "/tmp/malware.sock";
+ struct sockaddr_un server;
+ int sock,bread=0;
+ unsigned int port;
+ uschar file_name[1024];
+ uschar commandline[1024];
+ uschar av_buffer[1024];
+ uschar linebuffer[1024];
+ uschar hostname[256];
+ struct hostent *he;
+ struct in_addr in;
+ uschar *sockline_scanner;
+ uschar sockline_scanner_buffer[1024];
+ uschar sockline_scanner_default[] = "%s\n";
+ uschar *sockline_trigger;
+ uschar sockline_trigger_buffer[1024];
+ const pcre *sockline_trigger_re;
+ uschar *sockline_regex;
+ uschar sockline_regex_buffer[1024];
+ const pcre *sockline_regex_re;
+ int result;
+ int trigger = 0;
+ int ovector[30];
+
+ if ((sock_options = string_nextinlist(&av_scanner_work, &sep,
+ sock_options_buffer,
+ sizeof(sock_options_buffer))) == NULL) {
+ /* no options supplied, use default options */
+ sock_options = sock_options_default;
+ }
+
+ /* find scanner command line */
+ if ((sockline_scanner = string_nextinlist(&av_scanner_work, &sep,
+ sockline_scanner_buffer,
+ sizeof(sockline_scanner_buffer))) == NULL) {
+ sockline_scanner = sockline_scanner_default;
+ };
+
+ /* find scanner output trigger */
+ if ((sockline_trigger = string_nextinlist(&av_scanner_work, &sep,
+ sockline_trigger_buffer,
+ sizeof(sockline_trigger_buffer))) == NULL) {
+ /* no trigger regex supplied */
+ log_write(0, LOG_MAIN|LOG_PANIC,
+ "malware acl condition: missing trigger specification for sock scanner type.");
+ return DEFER;
+ };
+
+ /* precompile trigger regex */
+ sockline_trigger_re = pcre_compile(CS sockline_trigger, PCRE_COPT, (const char **)&rerror, &roffset, NULL);
+ if (sockline_trigger_re == NULL) {
+ log_write(0, LOG_MAIN|LOG_PANIC,
+ "malware acl condition: regular expression error in '%s': %s at offset %d", sockline_trigger, rerror, roffset);
+ return DEFER;
+ };
+
+ /* find scanner name regex */
+ if ((sockline_regex = string_nextinlist(&av_scanner_work, &sep,
+ sockline_regex_buffer,
+ sizeof(sockline_regex_buffer))) == NULL) {
+ /* no name regex supplied */
+ log_write(0, LOG_MAIN|LOG_PANIC,
+ "malware acl condition: missing virus name regex specification for sock scanner type.");
+ return DEFER;
+ };
+
+ /* precompile name regex */
+ sockline_regex_re = pcre_compile(CS sockline_regex, PCRE_COPT, (const char **)&rerror, &roffset, NULL);
+ if (sockline_regex_re == NULL) {
+ log_write(0, LOG_MAIN|LOG_PANIC,
+ "malware acl condition: regular expression error in '%s': %s at offset %d", sockline_regex, rerror, roffset);
+ return DEFER;
+ };
+ /* prepare scanner call */
+ (void)string_format(file_name,1024,"%s/scan/%s/%s.eml", spool_directory, message_id, message_id);
+ (void)string_format(commandline,1024, CS sockline_scanner,file_name);
+
+
+ /* socket does not start with '/' -> network socket */
+ if (*sock_options != '/') {
+
+ /* extract host and port part */
+ if( sscanf(CS sock_options, "%s %u", hostname, &port) != 2 ) {
+ log_write(0, LOG_MAIN|LOG_PANIC,
+ "malware acl condition: sock: invalid socket '%s'", sock_options);
+ return DEFER;
+ };
+
+ /* Lookup the host */
+ if((he = gethostbyname(CS hostname)) == 0) {
+ log_write(0, LOG_MAIN|LOG_PANIC,
+ "malware acl condition: sock: failed to lookup host '%s'", hostname);
+ return DEFER;
+ }
+
+ in = *(struct in_addr *) he->h_addr_list[0];
+
+ /* Open the Socket */
+ if ( (sock = ip_socket(SOCK_STREAM, AF_INET)) < 0) {
+ log_write(0, LOG_MAIN|LOG_PANIC,
+ "malware acl condition: sock: unable to acquire socket (%s)",
+ strerror(errno));
+ return DEFER;
+ }
+
+ if (ip_connect(sock, AF_INET, (uschar*)inet_ntoa(in), port, 5) < 0) {
+ (void)close(sock);
+ log_write(0, LOG_MAIN|LOG_PANIC,
+ "malware acl condition: sock: connection to %s, port %u failed (%s)",
+ inet_ntoa(in), port, strerror(errno));
+ return DEFER;
+ }
+
+ } else {
+ /* open the local socket */
+ if ((sock = socket(AF_UNIX, SOCK_STREAM, 0)) < 0) {
+ log_write(0, LOG_MAIN|LOG_PANIC,
+ "malware acl condition: sock: unable to acquire socket (%s)",
+ strerror(errno));
+ return DEFER;
+ }
+
+ server.sun_family = AF_UNIX;
+ Ustrcpy(server.sun_path, sock_options);
+
+ if (connect(sock, (struct sockaddr *) &server, sizeof(struct sockaddr_un)) < 0) {
+ (void)close(sock);
+ log_write(0, LOG_MAIN|LOG_PANIC,
+ "malware acl condition: sock: unable to connect to UNIX socket %s (%s)",
+ sock_options, strerror(errno) );
+ return DEFER;
+ }
+ }
+
+ /* Pass the command string to the socket */
+ if (send(sock, commandline, Ustrlen(commandline), 0) < 0) {
+ (void)close(sock);
+ log_write(0, LOG_MAIN|LOG_PANIC,"malware acl condition: sock: unable to write to socket (%s)",
+ strerror(errno));
+ return DEFER;
+ }
+
+ /*
+ We're done sending, close socket for writing.
+ */
+
+ /* shutdown(sock, SHUT_WR); */
+
+ /* Read the result */
+ memset(av_buffer, 0, sizeof(av_buffer));
+ bread = read(sock, av_buffer, sizeof(av_buffer));
+ (void)close(sock);
+
+ if (!(bread > 0)) {
+ log_write(0, LOG_MAIN|LOG_PANIC,
+ "malware acl condition: sock: unable to read from socket (%s)",
+ strerror(errno));
+ return DEFER;
+ }
+
+ if (bread == sizeof(av_buffer)) {
+ log_write(0, LOG_MAIN|LOG_PANIC,
+ "malware acl condition: sock: buffer too small");
+ return DEFER;
+ }
+ Ustrcpy( linebuffer, av_buffer );
+
+ /* try trigger match */
+ if (!trigger && regex_match_and_setup(sockline_trigger_re, linebuffer, 0, -1))
+ trigger = 1;
+
+ if (trigger) {
+ Ustrcpy( linebuffer, av_buffer );
+ /* setup default virus name */
+ Ustrcpy(malware_name_buffer,"unknown");
+ malware_name = malware_name_buffer;
+
+ result = pcre_exec(sockline_regex_re, NULL, CS linebuffer, Ustrlen(linebuffer), 0, 0, ovector, 30);
+ if (result >= 2) {
+ pcre_copy_substring(CS linebuffer, ovector, result, 1, CS malware_name_buffer, 255);
+ };
+ }
+ else {
+ /* no virus found */
+ malware_name = NULL;
+ };
+ }
+ /* ----------------------------------------------------------------------- */
+
+
/* "mksd" scanner type --------------------------------------------------- */
else if (strcmpic(scanner_name,US"mksd") == 0) {
uschar *mksd_options;