4141
4242
4343import socket
44+ import sys
4445import struct
4546import contextlib
4647import re
5152 b'QVJELUFOVElWSVJVUy1URVNU\n LUZJTEUhJEgrSCo=\n ' )
5253
5354
54- class BufferTooLongError (ValueError ):
55+ class ClamdError (Exception ):
56+ pass
57+
58+
59+ class ResponseError (ClamdError ):
60+ pass
61+
62+
63+ class BufferTooLongError (ClamdError ):
5564 """Class for errors with clamd using INSTREAM with a buffer lenght > StreamMaxLength in /etc/clamav/clamd.conf"""
5665
5766
58- class ConnectionError (socket . error ):
67+ class ConnectionError (ClamdError ):
5968 """Class for errors communication with clamd"""
6069
6170
62- class _ClamdGeneric (object ):
71+ class ClamdNetworkSocket (object ):
6372 """
64- Abstract class for clamd
73+ Class for using clamd with a network socket
6574 """
75+ def __init__ (self , host = '127.0.0.1' , port = 3310 , timeout = None ):
76+ """
77+ class initialisation
78+
79+ host (string) : hostname or ip address
80+ port (int) : TCP port
81+ timeout (float or None) : socket timeout
82+ """
83+
84+ self .host = host
85+ self .port = port
86+ self .timeout = timeout
87+
88+ def _init_socket (self ):
89+ """
90+ internal use only
91+ """
92+ try :
93+ self .clamd_socket = socket .socket (socket .AF_INET , socket .SOCK_STREAM )
94+ self .clamd_socket .connect ((self .host , self .port ))
95+ self .clamd_socket .settimeout (self .timeout )
96+
97+ except socket .error :
98+ e = sys .exc_info ()[1 ]
99+ raise ConnectionError (self ._error_message (e ))
100+
101+ def _error_message (self , exception ):
102+ # args for socket.error can either be (errno, "message")
103+ # or just "message"
104+ if len (exception .args ) == 1 :
105+ return "Error connecting to {host}:{port}. {msg}." .format (
106+ host = self .host ,
107+ port = self .port ,
108+ msg = exception .args [0 ]
109+ )
110+ else :
111+ return "Error {erno} connecting {host}:{port}. {msg}." .format (
112+ erno = exception .args [0 ],
113+ host = self .host ,
114+ port = self .port ,
115+ msg = exception .args [1 ]
116+ )
66117
67118 def ping (self ):
68119 return self ._basic_command ("PING" )
@@ -86,9 +137,8 @@ def shutdown(self):
86137 self ._init_socket ()
87138 self ._send_command ('SHUTDOWN' )
88139 # result = self._recv_response()
140+ finally :
89141 self ._close_socket ()
90- except socket .error :
91- raise ConnectionError ('Could probably not shutdown clamd' )
92142
93143 def scan (self , file ):
94144 return self ._file_system_scan ('SCAN' , file )
@@ -107,8 +157,6 @@ def _basic_command(self, command):
107157 try :
108158 self ._send_command (command )
109159 return self ._recv_response ()
110- except socket .error :
111- raise ConnectionError ('Could not complete command {command}' .format (command = command ))
112160 finally :
113161 self ._close_socket ()
114162
@@ -139,8 +187,6 @@ def _file_system_scan(self, command, file):
139187
140188 return dr
141189
142- except socket .error :
143- raise ConnectionError ('Unable to scan {file}' .format (file = file ))
144190 finally :
145191 self ._close_socket ()
146192
@@ -180,9 +226,6 @@ def instream(self, buff):
180226
181227 filename , reason , status = self ._parse_response (result )
182228 return {filename : (status , reason )}
183-
184- except socket .error :
185- raise ConnectionError ('Unable to scan stream' )
186229 finally :
187230 self ._close_socket ()
188231
@@ -199,8 +242,6 @@ def stats(self):
199242 try :
200243 self ._send_command ('STATS' )
201244 return self ._recv_response_multiline ()
202- except socket .error :
203- raise ConnectionError ('Could not get version information from server' )
204245 finally :
205246 self ._close_socket ()
206247
@@ -215,21 +256,28 @@ def _send_command(self, cmd, *args):
215256
216257 cmd = 'n{cmd}{args}\n ' .format (cmd = cmd , args = concat_args ).encode ('utf-8' )
217258 self .clamd_socket .send (cmd )
218- return
219259
220260 def _recv_response (self ):
221261 """
222262 receive line from clamd
223263 """
224- with contextlib .closing (self .clamd_socket .makefile ('rb' )) as f :
225- return f .readline ().decode ('utf-8' ).strip ()
264+ try :
265+ with contextlib .closing (self .clamd_socket .makefile ('rb' )) as f :
266+ return f .readline ().decode ('utf-8' ).strip ()
267+ except (socket .error , socket .timeout ):
268+ e = sys .exc_info ()[1 ]
269+ raise ConnectionError ("Error while reading from socket: {0}" .format (e .args ))
226270
227271 def _recv_response_multiline (self ):
228272 """
229273 receive multiple line response from clamd and strip all whitespace characters
230274 """
231- with contextlib .closing (self .clamd_socket .makefile ('rb' )) as f :
232- return f .read ().decode ('utf-8' )
275+ try :
276+ with contextlib .closing (self .clamd_socket .makefile ('rb' )) as f :
277+ return f .read ().decode ('utf-8' )
278+ except (socket .error , socket .timeout ):
279+ e = sys .exc_info ()[1 ]
280+ raise ConnectionError ("Error while reading from socket: {0}" .format (e .args ))
233281
234282 def _close_socket (self ):
235283 """
@@ -245,7 +293,7 @@ def _parse_response(self, msg):
245293 return scan_response .match (msg ).group ("path" , "virus" , "status" )
246294
247295
248- class ClamdUnixSocket (_ClamdGeneric ):
296+ class ClamdUnixSocket (ClamdNetworkSocket ):
249297 """
250298 Class for using clamd with an unix socket
251299 """
@@ -269,38 +317,20 @@ def _init_socket(self):
269317 self .clamd_socket .connect (self .unix_socket )
270318 self .clamd_socket .settimeout (self .timeout )
271319 except socket .error :
272- raise ConnectionError ('Could not reach clamd using unix socket {0}' .format (self .unix_socket ))
273-
274-
275- class ClamdNetworkSocket (_ClamdGeneric ):
276- """
277- Class for using clamd with a network socket
278- """
279- def __init__ (self , host = '127.0.0.1' , port = 3310 , timeout = None ):
280- """
281- class initialisation
282-
283- host (string) : hostname or ip address
284- port (int) : TCP port
285- timeout (float or None) : socket timeout
286- """
287-
288- self .host = host
289- self .port = port
290- self .timeout = timeout
291-
292- def _init_socket (self ):
293- """
294- internal use only
295- """
296- try :
297- self .clamd_socket = socket .socket (socket .AF_INET , socket .SOCK_STREAM )
298- self .clamd_socket .connect ((self .host , self .port ))
299- self .clamd_socket .settimeout (self .timeout )
300-
301- except socket .error :
302- raise ConnectionError ('Could not reach clamd using network ({host}, {port})' .format (
303- host = self .host ,
304- port = self .port
305- )
320+ e = sys .exc_info ()[1 ]
321+ raise ConnectionError (self ._error_message (e ))
322+
323+ def _error_message (self , exception ):
324+ # args for socket.error can either be (errno, "message")
325+ # or just "message"
326+ if len (exception .args ) == 1 :
327+ return "Error connecting to {path}. {msg}." .format (
328+ path = self .unix_socket ,
329+ msg = exception .args [0 ]
306330 )
331+ else :
332+ return "Error {erno} connecting {host}:{port}. {msg}." .format (
333+ erno = exception .args [0 ],
334+ path = self .unix_socket ,
335+ msg = exception .args [1 ]
336+ )
0 commit comments