1 diff -urN exim-4.73_RC1/src/malware.c exim-4.73_RC1-new/src/malware.c
2 --- exim-4.73_RC1/src/malware.c 2010-12-23 14:19:35.000000000 +0000
3 +++ exim-4.73_RC1-new/src/malware.c 2011-01-05 09:58:34.000000000 +0000
6 #ifdef WITH_CONTENT_SCAN
8 +/* The maximum number of clamd servers that are supported in the configuration */
9 +#define MAX_CLAMD_SERVERS 32
10 +#define MAX_CLAMD_SERVERS_S "32"
11 +/* Maximum length of the hostname that can be specified in the clamd address list */
12 +#define MAX_CLAMD_ADDRESS_LENGTH 64
13 +#define MAX_CLAMD_ADDRESS_LENGTH_S "64"
15 +typedef struct clamd_address_container {
16 + uschar tcp_addr[MAX_CLAMD_ADDRESS_LENGTH];
17 + unsigned int tcp_port;
18 +} clamd_address_container;
20 /* declaration of private routines */
21 static int mksd_scan_packed(int sock, uschar *scan_filename);
22 static int malware_internal(uschar **listptr, uschar *eml_filename, BOOL faking);
24 * WITH_OLD_CLAMAV_STREAM is defined.
25 * See Exim bug 926 for details. */
26 else if (strcmpic(scanner_name,US"clamd") == 0) {
27 - uschar *clamd_options;
28 + uschar *clamd_options = NULL;
29 uschar clamd_options_buffer[1024];
30 uschar clamd_options_default[] = "/tmp/clamd";
31 uschar *p, *vname, *result_tag, *response_end;
32 @@ -1304,16 +1316,16 @@
34 uschar file_name[1024];
35 uschar av_buffer[1024];
36 - uschar hostname[256];
37 + uschar *hostname = "";
40 - uschar *clamd_options2;
41 - uschar clamd_options2_buffer[1024];
42 - uschar clamd_options2_default[] = "";
46 - BOOL use_scan_command, fits;
47 + BOOL use_scan_command = FALSE, fits;
48 + clamd_address_container * clamd_address_vector[MAX_CLAMD_SERVERS];
50 + int num_servers = 0;
51 #ifdef WITH_OLD_CLAMAV_STREAM
52 uschar av_buffer2[1024];
54 @@ -1327,16 +1339,60 @@
55 /* no options supplied, use default options */
56 clamd_options = clamd_options_default;
58 - if ((clamd_options2 = string_nextinlist(&av_scanner_work, &sep,
59 - clamd_options2_buffer,
60 - sizeof(clamd_options2_buffer))) == NULL) {
61 - clamd_options2 = clamd_options2_default;
64 - if ((*clamd_options == '/') || (strcmpic(clamd_options2,US"local") == 0))
65 + if (*clamd_options == '/')
66 + /* Local file; so we def want to use_scan_command and don't want to try
67 + * passing IP/port combinations */
68 use_scan_command = TRUE;
70 - use_scan_command = FALSE;
72 + uschar *address = clamd_options;
73 + uschar address_buffer[MAX_CLAMD_ADDRESS_LENGTH + 20];
75 + /* Go through the rest of the list of host/port and construct an array
76 + * of servers to try. The first one is the bit we just passed from
77 + * clamd_options so process that first and then scan the remainder of
78 + * the address buffer */
80 + clamd_address_container *this_clamd;
82 + /* The 'local' option means use the SCAN command over the network
83 + * socket (ie common file storage in use) */
84 + if (strcmpic(address,US"local") == 0) {
85 + use_scan_command = TRUE;
89 + /* XXX: If unsuccessful we should free this memory */
91 + (clamd_address_container *)store_get(sizeof(clamd_address_container));
93 + /* extract host and port part */
94 + if( sscanf(CS address, "%" MAX_CLAMD_ADDRESS_LENGTH_S "s %u", this_clamd->tcp_addr,
95 + &(this_clamd->tcp_port)) != 2 ) {
96 + log_write(0, LOG_MAIN|LOG_PANIC,
97 + "malware acl condition: clamd: invalid address '%s'", address);
101 + clamd_address_vector[num_servers] = this_clamd;
103 + if (num_servers >= MAX_CLAMD_SERVERS) {
104 + log_write(0, LOG_MAIN|LOG_PANIC,
105 + "More than " MAX_CLAMD_SERVERS_S " clamd servers specified; "
106 + "only using the first " MAX_CLAMD_SERVERS_S );
109 + } while ((address = string_nextinlist(&av_scanner_work, &sep,
111 + sizeof(address_buffer))) != NULL);
113 + /* check if we have at least one server */
114 + if (!num_servers) {
115 + log_write(0, LOG_MAIN|LOG_PANIC,
116 + "malware acl condition: clamd: no useable clamd server addresses in malware configuration option.");
121 /* See the discussion of response formats below to see why we really don't
122 like colons in filenames when passing filenames to ClamAV. */
123 @@ -1347,45 +1403,72 @@
127 - /* socket does not start with '/' -> network socket */
128 - if (*clamd_options != '/') {
129 + /* We have some network servers specified */
132 /* Confirmed in ClamAV source (0.95.3) that the TCPAddr option of clamd
133 * only supports AF_INET, but we should probably be looking to the
134 * future and rewriting this to be protocol-independent anyway. */
136 - /* extract host and port part */
137 - if( sscanf(CS clamd_options, "%s %u", hostname, &port) != 2 ) {
138 - log_write(0, LOG_MAIN|LOG_PANIC,
139 - "malware acl condition: clamd: invalid socket '%s'", clamd_options);
142 + while ( num_servers > 0 ) {
143 + /* Randomly pick a server to start with */
144 + current_server = random_number( num_servers );
146 + debug_printf("trying server name %s, port %u\n",
147 + clamd_address_vector[current_server]->tcp_addr,
148 + clamd_address_vector[current_server]->tcp_port);
150 + /* Lookup the host. This is to ensure that we connect to the same IP
151 + * on both connections (as one host could resolve to multiple ips) */
152 + if((he = gethostbyname(CS clamd_address_vector[current_server]->tcp_addr))
154 + log_write(0, LOG_MAIN|LOG_PANIC,
155 + "malware acl condition: clamd: failed to lookup host '%s'",
156 + clamd_address_vector[current_server]->tcp_addr
158 + goto try_next_server;
161 - /* Lookup the host */
162 - if((he = gethostbyname(CS hostname)) == 0) {
163 - log_write(0, LOG_MAIN|LOG_PANIC,
164 - "malware acl condition: clamd: failed to lookup host '%s'", hostname);
167 + in = *(struct in_addr *) he->h_addr_list[0];
169 - in = *(struct in_addr *) he->h_addr_list[0];
170 + /* Open the ClamAV Socket */
171 + if ( (sock = ip_socket(SOCK_STREAM, AF_INET)) < 0) {
172 + log_write(0, LOG_MAIN|LOG_PANIC,
173 + "malware acl condition: clamd: unable to acquire socket (%s)",
175 + goto try_next_server;
178 - /* Open the ClamAV Socket */
179 - if ( (sock = ip_socket(SOCK_STREAM, AF_INET)) < 0) {
180 - log_write(0, LOG_MAIN|LOG_PANIC,
181 - "malware acl condition: clamd: unable to acquire socket (%s)",
185 + if (ip_connect( sock,
187 + (uschar*)inet_ntoa(in),
188 + clamd_address_vector[current_server]->tcp_port,
190 + /* Connection successfully established with a server */
191 + hostname = clamd_address_vector[current_server]->tcp_addr;
194 + log_write(0, LOG_MAIN|LOG_PANIC,
195 + "malware acl condition: clamd: connection to %s, port %u failed (%s)",
196 + clamd_address_vector[current_server]->tcp_addr,
197 + clamd_address_vector[current_server]->tcp_port,
200 - if (ip_connect(sock, AF_INET, (uschar*)inet_ntoa(in), port, 5) < 0) {
202 - log_write(0, LOG_MAIN|LOG_PANIC,
203 - "malware acl condition: clamd: connection to %s, port %u failed (%s)",
204 - inet_ntoa(in), port, strerror(errno));
210 + /* Remove the server from the list. XXX We should free the memory */
213 + for( i = current_server; i < num_servers; i++ )
214 + clamd_address_vector[i] = clamd_address_vector[i+1];
217 + if ( num_servers == 0 ) {
218 + log_write(0, LOG_MAIN|LOG_PANIC, "malware acl condition: all clamd servers failed");
222 /* open the local socket */
223 if ((sock = socket(AF_UNIX, SOCK_STREAM, 0)) < 0) {