]>
Commit | Line | Data |
---|---|---|
db3fcec6 AM |
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 | |
f1cebd6b | 4 | @@ -12,6 +12,18 @@ |
db3fcec6 AM |
5 | #include "exim.h" |
6 | #ifdef WITH_CONTENT_SCAN | |
7 | ||
8 | +/* The maximum number of clamd servers that are supported in the configuration */ | |
9 | +#define MAX_CLAMD_SERVERS 32 | |
f1cebd6b | 10 | +#define MAX_CLAMD_SERVERS_S "32" |
db3fcec6 AM |
11 | +/* Maximum length of the hostname that can be specified in the clamd address list */ |
12 | +#define MAX_CLAMD_ADDRESS_LENGTH 64 | |
f1cebd6b | 13 | +#define MAX_CLAMD_ADDRESS_LENGTH_S "64" |
db3fcec6 AM |
14 | + |
15 | +typedef struct clamd_address_container { | |
16 | + uschar tcp_addr[MAX_CLAMD_ADDRESS_LENGTH]; | |
17 | + unsigned int tcp_port; | |
18 | +} clamd_address_container; | |
19 | + | |
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); | |
f1cebd6b | 23 | @@ -1295,7 +1307,7 @@ |
db3fcec6 AM |
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; | |
f1cebd6b | 32 | @@ -1304,16 +1316,16 @@ |
db3fcec6 AM |
33 | unsigned int port; |
34 | uschar file_name[1024]; | |
35 | uschar av_buffer[1024]; | |
36 | - uschar hostname[256]; | |
37 | + uschar *hostname = ""; | |
38 | struct hostent *he; | |
39 | struct in_addr in; | |
40 | - uschar *clamd_options2; | |
41 | - uschar clamd_options2_buffer[1024]; | |
42 | - uschar clamd_options2_default[] = ""; | |
43 | uschar *clamav_fbuf; | |
44 | int clam_fd, result; | |
45 | unsigned int fsize; | |
46 | - BOOL use_scan_command, fits; | |
47 | + BOOL use_scan_command = FALSE, fits; | |
48 | + clamd_address_container * clamd_address_vector[MAX_CLAMD_SERVERS]; | |
49 | + int current_server; | |
50 | + int num_servers = 0; | |
51 | #ifdef WITH_OLD_CLAMAV_STREAM | |
52 | uschar av_buffer2[1024]; | |
53 | int sockData; | |
f1cebd6b | 54 | @@ -1327,16 +1339,60 @@ |
db3fcec6 AM |
55 | /* no options supplied, use default options */ |
56 | clamd_options = clamd_options_default; | |
57 | } | |
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; | |
62 | - } | |
63 | ||
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; | |
69 | - else | |
70 | - use_scan_command = FALSE; | |
71 | + else { | |
72 | + uschar *address = clamd_options; | |
73 | + uschar address_buffer[MAX_CLAMD_ADDRESS_LENGTH + 20]; | |
74 | + | |
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 */ | |
79 | + do { | |
80 | + clamd_address_container *this_clamd; | |
81 | + | |
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; | |
86 | + continue; | |
87 | + } | |
88 | + | |
89 | + /* XXX: If unsuccessful we should free this memory */ | |
90 | + this_clamd = | |
91 | + (clamd_address_container *)store_get(sizeof(clamd_address_container)); | |
92 | + | |
93 | + /* extract host and port part */ | |
f1cebd6b | 94 | + if( sscanf(CS address, "%" MAX_CLAMD_ADDRESS_LENGTH_S "s %u", this_clamd->tcp_addr, |
db3fcec6 AM |
95 | + &(this_clamd->tcp_port)) != 2 ) { |
96 | + log_write(0, LOG_MAIN|LOG_PANIC, | |
97 | + "malware acl condition: clamd: invalid address '%s'", address); | |
98 | + continue; | |
99 | + } | |
100 | + | |
101 | + clamd_address_vector[num_servers] = this_clamd; | |
102 | + num_servers++; | |
103 | + if (num_servers >= MAX_CLAMD_SERVERS) { | |
104 | + log_write(0, LOG_MAIN|LOG_PANIC, | |
f1cebd6b AM |
105 | + "More than " MAX_CLAMD_SERVERS_S " clamd servers specified; " |
106 | + "only using the first " MAX_CLAMD_SERVERS_S ); | |
db3fcec6 AM |
107 | + break; |
108 | + } | |
109 | + } while ((address = string_nextinlist(&av_scanner_work, &sep, | |
110 | + address_buffer, | |
111 | + sizeof(address_buffer))) != NULL); | |
112 | + | |
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."); | |
117 | + return DEFER; | |
118 | + } | |
119 | + } | |
120 | ||
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. */ | |
f1cebd6b | 123 | @@ -1347,45 +1403,72 @@ |
db3fcec6 AM |
124 | return DEFER; |
125 | } | |
126 | ||
127 | - /* socket does not start with '/' -> network socket */ | |
128 | - if (*clamd_options != '/') { | |
129 | + /* We have some network servers specified */ | |
130 | + if (num_servers) { | |
131 | ||
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. */ | |
135 | ||
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); | |
140 | - return DEFER; | |
141 | - }; | |
142 | + while ( num_servers > 0 ) { | |
143 | + /* Randomly pick a server to start with */ | |
144 | + current_server = random_number( num_servers ); | |
145 | + | |
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); | |
149 | + | |
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)) | |
153 | + == 0) { | |
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 | |
157 | + ); | |
158 | + goto try_next_server; | |
159 | + } | |
160 | ||
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); | |
165 | - return DEFER; | |
166 | - } | |
167 | + in = *(struct in_addr *) he->h_addr_list[0]; | |
168 | ||
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)", | |
174 | + strerror(errno)); | |
175 | + goto try_next_server; | |
176 | + } | |
177 | ||
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)", | |
182 | - strerror(errno)); | |
183 | - return DEFER; | |
184 | - } | |
185 | + if (ip_connect( sock, | |
186 | + AF_INET, | |
187 | + (uschar*)inet_ntoa(in), | |
188 | + clamd_address_vector[current_server]->tcp_port, | |
189 | + 5 ) > -1) { | |
190 | + /* Connection successfully established with a server */ | |
191 | + hostname = clamd_address_vector[current_server]->tcp_addr; | |
192 | + break; | |
193 | + } else { | |
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, | |
198 | + strerror(errno)); | |
199 | ||
200 | - if (ip_connect(sock, AF_INET, (uschar*)inet_ntoa(in), port, 5) < 0) { | |
201 | - (void)close(sock); | |
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)); | |
205 | - return DEFER; | |
206 | + (void)close(sock); | |
207 | + } | |
208 | + | |
209 | +try_next_server: | |
210 | + /* Remove the server from the list. XXX We should free the memory */ | |
211 | + num_servers--; | |
212 | + int i; | |
213 | + for( i = current_server; i < num_servers; i++ ) | |
214 | + clamd_address_vector[i] = clamd_address_vector[i+1]; | |
215 | } | |
216 | ||
217 | + if ( num_servers == 0 ) { | |
218 | + log_write(0, LOG_MAIN|LOG_PANIC, "malware acl condition: all clamd servers failed"); | |
219 | + return DEFER; | |
220 | + } | |
221 | } else { | |
222 | /* open the local socket */ | |
223 | if ((sock = socket(AF_UNIX, SOCK_STREAM, 0)) < 0) { |