Skip to content

Commit 2554281

Browse files
committed
clean up exceptions
1 parent e50440c commit 2554281

2 files changed

Lines changed: 90 additions & 55 deletions

File tree

clamd.py

Lines changed: 85 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@
4141

4242

4343
import socket
44+
import sys
4445
import struct
4546
import contextlib
4647
import re
@@ -51,18 +52,68 @@
5152
b'QVJELUFOVElWSVJVUy1URVNU\nLUZJTEUhJEgrSCo=\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+
)

tests/test_api.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,3 +80,8 @@ def test_instream(self):
8080
class TestUnixSocketTimeout(TestUnixSocket):
8181
def __init__(self):
8282
self.kwargs = {"timeout": 10}
83+
84+
85+
# class TestNetworkSocket(TestUnixSocket):
86+
# def setup(self):
87+
# self.cd = clamd.ClamdNetworkSocket()

0 commit comments

Comments
 (0)