|
1 /*************************************************************************** |
|
2 f-protscanner.cpp - description |
|
3 ------------------- |
|
4 begin : Mit Jun 29 2005 |
|
5 copyright : (C) 2005 by Christian Hilgers |
|
6 email : christian@hilgers.ag |
|
7 ***************************************************************************/ |
|
8 |
|
9 /*************************************************************************** |
|
10 * * |
|
11 * This program is free software; you can redistribute it and/or modify * |
|
12 * it under the terms of the GNU General Public License as published by * |
|
13 * the Free Software Foundation; either version 2 of the License, or * |
|
14 * (at your option) any later version. * |
|
15 * * |
|
16 ***************************************************************************/ |
|
17 |
|
18 #include "f-protscanner.h" |
|
19 |
|
20 |
|
21 bool FProtScanner::InitDatabase() |
|
22 { |
|
23 return true; |
|
24 } |
|
25 int FProtScanner::ReloadDatabase() |
|
26 { |
|
27 return 0; |
|
28 } |
|
29 void FProtScanner::FreeDatabase() |
|
30 { |
|
31 } |
|
32 |
|
33 |
|
34 string FProtScanner::Scan( const char *FileName ) |
|
35 { |
|
36 if ( Connected == false && ConnectScanner() == false ) |
|
37 { |
|
38 ScannerAnswer = "2Could not connect to scanner"; |
|
39 return ScannerAnswer; |
|
40 } |
|
41 |
|
42 if ( Version == 0 ) |
|
43 { |
|
44 Version = TestVersion(); |
|
45 |
|
46 if ( Version == 0 ) |
|
47 { |
|
48 ScannerAnswer = "2Could not connect to scanner"; |
|
49 return ScannerAnswer; |
|
50 } |
|
51 |
|
52 if ( Connected == false && ConnectScanner() == false ) |
|
53 { |
|
54 ScannerAnswer = "2Could not connect to scanner"; |
|
55 return ScannerAnswer; |
|
56 } |
|
57 } |
|
58 |
|
59 if ( Version == 6 ) return ScanV6( FileName ); |
|
60 |
|
61 return ScanV4( FileName ); |
|
62 } |
|
63 |
|
64 |
|
65 string FProtScanner::ScanV6( const char *FileName ) |
|
66 { |
|
67 //Construct command for scanner |
|
68 ScannerCmd = "SCAN "; |
|
69 ScannerCmd += Opts; |
|
70 ScannerCmd += "FILE "; |
|
71 ScannerCmd += FileName; |
|
72 ScannerCmd += "\n"; |
|
73 |
|
74 if ( Scanner.Send( ScannerCmd ) == false ) |
|
75 { |
|
76 Scanner.Close(); |
|
77 Connected = false; |
|
78 |
|
79 LogFile::ErrorMessage("%s: Could not call scanner\n", ScannerNameShort.c_str()); |
|
80 ScannerAnswer = "2Could not call scanner"; |
|
81 return ScannerAnswer; |
|
82 } |
|
83 |
|
84 string Response; |
|
85 |
|
86 if ( Scanner.GetLine( Response, "\n", 600 ) == false ) |
|
87 { |
|
88 Scanner.Close(); |
|
89 Connected = false; |
|
90 |
|
91 LogFile::ErrorMessage("%s: Could not read scanner response\n", ScannerNameShort.c_str()); |
|
92 ScannerAnswer = "2Could not read scanner response"; |
|
93 return ScannerAnswer; |
|
94 } |
|
95 |
|
96 if ( MatchBegin( Response, "0 ", 2 ) ) |
|
97 { |
|
98 ScannerAnswer = "0Clean"; |
|
99 return ScannerAnswer; |
|
100 } |
|
101 |
|
102 int status = 0; |
|
103 if ( sscanf(Response.substr(0,4).c_str(), "%d ", &status) != 1 ) status = 0; |
|
104 |
|
105 if ( status & 1 || |
|
106 MatchBegin( Response, "1 ", 2 ) || |
|
107 Response.find("<unwanted") != string::npos ) |
|
108 { |
|
109 string VirusName = "unknown"; |
|
110 |
|
111 string::size_type PosEnd = Response.find( ">" ); |
|
112 |
|
113 if ( PosEnd != string::npos && PosEnd > 3 ) |
|
114 { |
|
115 VirusName = Response.substr( 3, PosEnd - 3 ); |
|
116 } |
|
117 |
|
118 SearchReplace( VirusName, "contains infected objects: ", "" ); |
|
119 SearchReplace( VirusName, "infected: ", "" ); |
|
120 |
|
121 //Reports "clean" for broken ZIPs with viruses? |
|
122 //Sadly it is the case when MAXSCANSIZE it reached too.. |
|
123 if ( VirusName == "clean" ) VirusName = "virus inside archive"; |
|
124 |
|
125 ScannerAnswer = "1" + VirusName; |
|
126 return ScannerAnswer; |
|
127 } |
|
128 |
|
129 //Code 2 for archive bombs etc |
|
130 //Different codes for encrypted files etc, but they are reported "clean" |
|
131 if ( MatchBegin( Response, "2 ", 2 ) || |
|
132 Response.find("<clean>") != string::npos ) |
|
133 { |
|
134 ScannerAnswer = "0Clean"; |
|
135 return ScannerAnswer; |
|
136 } |
|
137 |
|
138 LogFile::ErrorMessage("%s: Error response: %s\n", ScannerNameShort.c_str(), Response.c_str()); |
|
139 ScannerAnswer = "2Scanner error"; |
|
140 return ScannerAnswer; |
|
141 } |
|
142 |
|
143 string FProtScanner::ScanV4( const char *FileName ) |
|
144 { |
|
145 //Construct command for scanner |
|
146 ScannerCmd = "GET "; |
|
147 ScannerCmd += FileName; |
|
148 ScannerCmd += " HTTP/1.0\r\n\r\n"; |
|
149 |
|
150 if ( Scanner.Send( ScannerCmd ) == false ) |
|
151 { |
|
152 Scanner.Close(); |
|
153 Connected = false; |
|
154 |
|
155 LogFile::ErrorMessage("%s: Could not call scanner\n", ScannerNameShort.c_str()); |
|
156 ScannerAnswer = "2Could not call scanner"; |
|
157 return ScannerAnswer; |
|
158 } |
|
159 |
|
160 string Response; |
|
161 int ret; |
|
162 |
|
163 while ( (ret = Scanner.Recv( Response, true, 600 )) != 0 ) |
|
164 { |
|
165 if ( ret < 0 ) |
|
166 { |
|
167 Scanner.Close(); |
|
168 Connected = false; |
|
169 |
|
170 LogFile::ErrorMessage("%s: Could not read scanner response\n", ScannerNameShort.c_str()); |
|
171 ScannerAnswer = "2Could not read scanner response"; |
|
172 return ScannerAnswer; |
|
173 } |
|
174 } |
|
175 |
|
176 Scanner.Close(); |
|
177 Connected = false; |
|
178 |
|
179 string::size_type PositionEnd; |
|
180 |
|
181 if ( (PositionEnd = Response.rfind( "</summary>", string::npos )) == string::npos ) |
|
182 { |
|
183 LogFile::ErrorMessage("%s: Invalid response from scanner\n", ScannerNameShort.c_str()); |
|
184 ScannerAnswer = "2Invalid response from scanner"; |
|
185 return ScannerAnswer; |
|
186 } |
|
187 |
|
188 string::size_type Position; |
|
189 |
|
190 if ( (Position = Response.rfind( ">", PositionEnd )) == string::npos ) |
|
191 { |
|
192 LogFile::ErrorMessage("%s: Invalid response from scanner\n", ScannerNameShort.c_str()); |
|
193 ScannerAnswer = "2Invalid response from scanner"; |
|
194 return ScannerAnswer; |
|
195 } |
|
196 |
|
197 string SummaryCode = Response.substr( Position + 1, PositionEnd - (Position + 1) ); |
|
198 |
|
199 if ( SummaryCode == "clean" ) |
|
200 { |
|
201 ScannerAnswer = "0Clean"; |
|
202 return ScannerAnswer; |
|
203 } |
|
204 else if ( SummaryCode == "infected" ) |
|
205 { |
|
206 if ( (PositionEnd = Response.rfind( "</name>" )) != string::npos ) |
|
207 { |
|
208 if ( (Position = Response.rfind( ">", PositionEnd )) == string::npos ) |
|
209 { |
|
210 ScannerAnswer = "1Unknown"; |
|
211 return ScannerAnswer; |
|
212 } |
|
213 |
|
214 ScannerAnswer = "1" + Response.substr( Position+1, PositionEnd - (Position + 1) ); |
|
215 return ScannerAnswer; |
|
216 } |
|
217 else if ( (PositionEnd = Response.rfind( "</message>" )) != string::npos ) |
|
218 { |
|
219 if ( (Position = Response.rfind( ">", PositionEnd )) == string::npos ) |
|
220 { |
|
221 ScannerAnswer = "1Unknown"; |
|
222 return ScannerAnswer; |
|
223 } |
|
224 |
|
225 ScannerAnswer = "1" + Response.substr( Position+1, PositionEnd - (Position + 1) ); |
|
226 return ScannerAnswer; |
|
227 } |
|
228 else |
|
229 { |
|
230 ScannerAnswer = "1Unknown"; |
|
231 return ScannerAnswer; |
|
232 } |
|
233 } |
|
234 |
|
235 ScannerAnswer = "2Unknown response from scanner"; |
|
236 return ScannerAnswer; |
|
237 } |
|
238 |
|
239 |
|
240 int FProtScanner::TestVersion() |
|
241 { |
|
242 //Construct command for scanner |
|
243 ScannerCmd = "HELP\nQUIT\n"; |
|
244 |
|
245 if ( Scanner.Send( ScannerCmd ) == false ) |
|
246 { |
|
247 Scanner.Close(); |
|
248 Connected = false; |
|
249 |
|
250 LogFile::ErrorMessage("%s: Could not call scanner\n", ScannerNameShort.c_str()); |
|
251 return 0; |
|
252 } |
|
253 |
|
254 string Response; |
|
255 int ret = Scanner.Recv( Response, true, 30 ); |
|
256 |
|
257 Scanner.Close(); |
|
258 Connected = false; |
|
259 |
|
260 if ( ret < 0 ) |
|
261 { |
|
262 LogFile::ErrorMessage("%s: Could not read scanner response\n", ScannerNameShort.c_str()); |
|
263 return 0; |
|
264 } |
|
265 |
|
266 if ( MatchBegin( Response, "FPSCAND:6", 9 ) ) |
|
267 { |
|
268 return 6; |
|
269 } |
|
270 |
|
271 return 4; |
|
272 } |
|
273 |
|
274 |
|
275 bool FProtScanner::ConnectScanner() |
|
276 { |
|
277 if ( Version == 4 ) |
|
278 { |
|
279 if ( Scanner.SetDomainAndPort( ServerHost, ServerPort ) == false ) |
|
280 { |
|
281 LogFile::ErrorMessage("%s: Could not resolve scanner host", ScannerNameShort.c_str()); |
|
282 return false; |
|
283 } |
|
284 } |
|
285 |
|
286 if ( Scanner.ConnectToServer() == false ) |
|
287 { |
|
288 if ( Version == 4 ) |
|
289 { |
|
290 //Old F-Prot version might be updating, try next port.. |
|
291 if ( Scanner.SetDomainAndPort( ServerHost, ServerPort + 1 ) == false ) |
|
292 { |
|
293 LogFile::ErrorMessage("%s: Could not resolve scanner host", ScannerNameShort.c_str()); |
|
294 return false; |
|
295 } |
|
296 } |
|
297 else |
|
298 { |
|
299 sleep(1); |
|
300 } |
|
301 |
|
302 if ( Scanner.ConnectToServer() == false ) |
|
303 { |
|
304 Scanner.Close(); |
|
305 Connected = false; |
|
306 |
|
307 //Prevent log flooding, show error only once per minute |
|
308 if ( (LastError == 0) || (LastError + 60 < time(NULL)) ) |
|
309 { |
|
310 LogFile::ErrorMessage("%s: Could not connect to scanner! Scanner down?\n", ScannerNameShort.c_str()); |
|
311 LastError = time(NULL); |
|
312 } |
|
313 |
|
314 return false; |
|
315 } |
|
316 } |
|
317 |
|
318 Connected = true; |
|
319 return true; |
|
320 } |
|
321 |
|
322 |
|
323 //Close persistent connection from HAVP scanner initialization |
|
324 //Needed because it will fork later |
|
325 void FProtScanner::CloseSocket() |
|
326 { |
|
327 Scanner.Close(); |
|
328 Connected = false; |
|
329 } |
|
330 |
|
331 |
|
332 //Constructor |
|
333 FProtScanner::FProtScanner() |
|
334 { |
|
335 ScannerName = "F-Prot Socket Scanner"; |
|
336 ScannerNameShort = "F-Prot"; |
|
337 |
|
338 LastError = 0; |
|
339 Version = 0; |
|
340 Connected = false; |
|
341 |
|
342 ServerHost = Params::GetConfigString("FPROTSERVER"); |
|
343 ServerPort = Params::GetConfigInt("FPROTPORT"); |
|
344 Version = Params::GetConfigInt("FPROTVERSION"); |
|
345 |
|
346 Opts = ""; |
|
347 if ( Params::GetConfigString("FPROTOPTIONS") != "" ) |
|
348 { |
|
349 Opts = Params::GetConfigString("FPROTOPTIONS") + " "; |
|
350 } |
|
351 |
|
352 if ( Scanner.SetDomainAndPort( ServerHost, ServerPort ) == false ) |
|
353 { |
|
354 LogFile::ErrorMessage("%s: Could not resolve scanner host", ScannerNameShort.c_str()); |
|
355 } |
|
356 |
|
357 ScannerAnswer.reserve(100); |
|
358 } |
|
359 |
|
360 |
|
361 //Destructor |
|
362 FProtScanner::~FProtScanner() |
|
363 { |
|
364 } |
|
365 |