1616import contextlib
1717import re
1818import base64
19+ # _multiprocessing.sendfd() only exists in python2
20+ if sys .version_info [0 ] < 3 :
21+ import _multiprocessing
22+ else :
23+ import array
1924
2025scan_response = re .compile (r"^(?P<path>.*): ((?P<virus>.+) )?(?P<status>(FOUND|OK|ERROR))$" )
2126EICAR = base64 .b64decode (
@@ -264,7 +269,7 @@ def _close_socket(self):
264269
265270 def _parse_response (self , msg ):
266271 """
267- parses responses for SCAN, CONTSCAN, MULTISCAN and STREAM commands.
272+ parses responses for SCAN, CONTSCAN, MULTISCAN, FILDES and STREAM commands.
268273 """
269274 try :
270275 return scan_response .match (msg ).group ("path" , "virus" , "status" )
@@ -287,6 +292,25 @@ class initialisation
287292 self .unix_socket = path
288293 self .timeout = timeout
289294
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+
290314 def _init_socket (self ):
291315 """
292316 internal use only
@@ -313,3 +337,19 @@ def _error_message(self, exception):
313337 path = self .unix_socket ,
314338 msg = exception .args [1 ]
315339 )
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