Added virus name to the return value of the scan function.
Fixed errors.
13 import xml.etree.ElementTree as ET
17 # User the existing logger instance
18 __LOG = logging.getLogger("IkarusScanner")
20 __MINOPTS = { "Main" : ["LocalScanserverURL", "RemoteScanserverURL", "MaxFileSize", "RetryTimeout"]}
21 __CONFIG_NOT_READABLE = "Configfile is not readable"
22 __CONFIG_WRONG = "Something is wrong with the config"
23 __CONFIG_MISSING = "Section: \"%s\" Option: \"%s\" in configfile is missing"
24 __LOCAL_SCANSERVER_URL = ""
25 __REMOTE_SCANSERVER_URL = ""
26 __STATUS_CODE_OK = 200
27 __STATUS_CODE_INFECTED = 210
28 __STATUS_CODE_NOT_FOUND = 404
29 __MAX_SCAN_FILE_SIZE = 50 * 0x100000
30 __SCANSERVER_RETRY_TIMEOUT = 60
32 # Global http pool manager used to connect to the scan server
33 __remoteScanserverReachable = True
34 __scanserverTimestamp = 0
35 __httpPool = urllib3.PoolManager(num_pools = 1, timeout = 3)
37 def __init__ (self, scanner_config_path):
38 config = self.loadConfig (scanner_config_path)
40 self.__scanserverTimestamp = time.time()
42 self.__LOCAL_SCANSERVER_URL = config.get("Main", "LocalScanserverURL")
43 self.__REMOTE_SCANSERVER_URL = config.get("Main", "RemoteScanserverURL")
44 self.__SCANSERVER_RETRY_TIMEOUT = int(config.get("Main", "RetryTimeout"))
46 # Convert file size from MB to byte
47 self.__MAX_SCAN_FILE_SIZE = int(config.get("Main", "MaxFileSize")) * 0x100000
50 def checkMinimumOptions (self, config):
51 for section, options in self.__MINOPTS.iteritems ():
52 for option in options:
53 if (config.has_option(section, option) == False):
54 self.__LOG.error (self.__CONFIG_MISSING % (section, option))
57 def loadConfig (self, scanner_config_path):
59 configfile = scanner_config_path
60 config = ConfigParser.SafeConfigParser ()
62 if ((os.path.exists (scanner_config_path) == False) or (os.path.isfile (scanner_config_path) == False) or (os.access (scanner_config_path, os.R_OK) == False)):
63 self.__LOG.error(self.__CONFIG_NOT_READABLE);
64 raise SystemError(self.__CONFIG_NOT_READABLE)
67 config.read (scanner_config_path)
69 self.__LOG.error("Error: %s" % (e));
70 raise SystemError("Error: %s" % (e))
72 self.checkMinimumOptions (config)
76 def contactScanserver(self, url, fields):
77 self.__LOG.debug("Contacting server %s" % url)
78 return self.__httpPool.request_encode_body('POST', url, fields = fields, retries = 0)
80 def scanFile (self, path, fileobject):
81 return self.scanFileIkarus (path, fileobject)
83 def scanFileIkarus (self, path, fileobject):
84 retval = { "infected" : False, "virusname" : "Unknown" }
85 self.__LOG.debug ("Scan File: %s" % (path))
87 if (os.fstat(fileobject.fileno()).st_size > self.__MAX_SCAN_FILE_SIZE):
88 self.__LOG.info("File max size exceeded. The file is not scanned.")
89 retval["infected"] = False
90 retval["virusname"] = "File is to big to be scanned."
93 fields = { 'up_file' : fileobject.read() }
95 if (self.__remoteScanserverReachable == False) and ((self.__scanserverTimestamp + self.__SCANSERVER_RETRY_TIMEOUT) < time.time()):
96 self.__remoteScanserverReachable = True
98 if self.__remoteScanserverReachable:
100 response = self.contactScanserver(self.__REMOTE_SCANSERVER_URL, fields)
101 # We should catch socket.error here, but this does not work. Needs checking.
103 self.__LOG.info("Remote scan server unreachable, using local scan server.")
104 self.__LOG.debug("Exception: %s: %s" % (sys.exc_info()[0], sys.exc_info()[1]))
105 self.__LOG.info("Next check for remote server in %s seconds." % (self.__SCANSERVER_RETRY_TIMEOUT))
107 self.__remoteScanserverReachable = False
108 self.__scanserverTimestamp = time.time()
111 response = self.contactScanserver(self.__LOCAL_SCANSERVER_URL, fields)
113 self.__LOG.error ("Connection to local scan server could not be established.")
114 self.__LOG.debug ("Exception: %s" % (sys.exc_info()[0]))
118 response = self.contactScanserver(self.__LOCAL_SCANSERVER_URL, fields)
120 self.__LOG.error ("Connection to local scan server could not be established.")
121 self.__LOG.error ("Exception: %s" %(sys.exc_info()[0]))
125 if response.status == self.__STATUS_CODE_OK:
126 retval["infected"] = False
127 elif response.status == self.__STATUS_CODE_INFECTED:
129 root = ET.fromstring(response.data)
131 # this should be done in a more generic way
132 retval["virusname"] = root[1][3][0].text
133 retval["infected"] = True
135 self.__LOG.error ("Connection error to scan server.")
137 if (retval["infected"] == True):
138 self.__LOG.error ("Virus found, denying access.")
140 self.__LOG.debug ("No virus found.")