16
16
import contextlib
17
17
import re
18
18
import base64
19
+ # _multiprocessing.sendfd() only exists in python2
20
+ if sys .version_info [0 ] < 3 :
21
+ import _multiprocessing
22
+ else :
23
+ import array
19
24
20
25
scan_response = re .compile (r"^(?P<path>.*): ((?P<virus>.+) )?(?P<status>(FOUND|OK|ERROR))$" )
21
26
EICAR = base64 .b64decode (
@@ -264,7 +269,7 @@ def _close_socket(self):
264
269
265
270
def _parse_response (self , msg ):
266
271
"""
267
- parses responses for SCAN, CONTSCAN, MULTISCAN and STREAM commands.
272
+ parses responses for SCAN, CONTSCAN, MULTISCAN, FILDES and STREAM commands.
268
273
"""
269
274
try :
270
275
return scan_response .match (msg ).group ("path" , "virus" , "status" )
@@ -287,6 +292,25 @@ class initialisation
287
292
self .unix_socket = path
288
293
self .timeout = timeout
289
294
295
+ # only works for python >= 3.3
296
+ def _send_fd_via_socket_sendmsg (self , fd ):
297
+ """
298
+ internal use only
299
+ """
300
+ return self .clamd_socket .sendmsg ([b'\0 ' ], [(socket .SOL_SOCKET , socket .SCM_RIGHTS , array .array ("i" , [fd ]))])
301
+
302
+ # _multiprocessing.sendfd() only exists in python2
303
+ def _send_fd_via_mp_sendfd (self , fd ):
304
+ """
305
+ internal use only
306
+ """
307
+ return _multiprocessing .sendfd (self .clamd_socket .fileno (), fd )
308
+
309
+ # make _send_fd refer to one of the two _send_fd_* methods above. Either
310
+ # _send_fd_via_socket_sendmsg or _send_fd_via_mp_sendfd, depending upon the version of python
311
+ """internal use only"""
312
+ _send_fd = _send_fd_via_socket_sendmsg if sys .version_info [0 ] >= 3 else _send_fd_via_mp_sendfd
313
+
290
314
def _init_socket (self ):
291
315
"""
292
316
internal use only
@@ -313,3 +337,19 @@ def _error_message(self, exception):
313
337
path = self .unix_socket ,
314
338
msg = exception .args [1 ]
315
339
)
340
+
341
+ def fdscan (self , path , fd ):
342
+ """
343
+ Scan a file referenced by a file descriptor.
344
+ path (string) : path of file to use for result dictionary (otherwise unused)
345
+ fd (int) : file descriptor number (fileno) of file to scan
346
+ """
347
+ try :
348
+ self ._init_socket ()
349
+ cmd = 'nFILDES\n ' .encode ('utf-8' )
350
+ self .clamd_socket .send (cmd )
351
+ self ._send_fd (fd )
352
+ _ , reason , status = self ._parse_response (self ._recv_response_multiline ())
353
+ return {path : (status , reason )}
354
+ finally :
355
+ self ._close_socket ()
0 commit comments