16 # User the existing logger instance
17 __LOG = logging.getLogger("IkarusScanner")
19 __MINOPTS = { "Main" : ["LocalScanserverURL", "RemoteScanserverURL", "MaxFileSize", "RetryTimeout"]}
20 __CONFIG_NOT_READABLE = "Configfile is not readable"
21 __CONFIG_WRONG = "Something is wrong with the config"
22 __CONFIG_MISSING = "Section: \"%s\" Option: \"%s\" in configfile is missing"
23 __LOCAL_SCANSERVER_URL = ""
24 __REMOTE_SCANSERVER_URL = ""
25 __STATUS_CODE_OK = 200
26 __STATUS_CODE_INFECTED = 210
27 __STATUS_CODE_NOT_FOUND = 404
28 __MAX_SCAN_FILE_SIZE = 50 * 0x100000
29 __SCANSERVER_RETRY_TIMEOUT = 60
31 # Global http pool manager used to connect to the scan server
32 __remoteScanserverReachable = True
33 __scanserverTimestamp = 0
34 __httpPool = urllib3.PoolManager(num_pools = 1, timeout = 3)
36 def __init__ (self, scanner_config_path):
37 config = self.loadConfig (scanner_config_path)
39 self.__scanserverTimestamp = time.time()
41 __LOCAL_SCANSERVER_URL = config.get("Main", "LocalScanserverURL")
42 __REMOTE_SCANSERVER_URL = config.get("Main", "RemoteScanserverURL")
43 __SCANSERVER_RETRY_TIMEOUT = int(config.get("Main", "RetryTimeout"))
45 # Convert file size from MB to byte
46 __MAX_SCAN_FILE_SIZE = int(config.get("Main", "MaxFileSize")) * 0x100000
49 def checkMinimumOptions (self, config):
50 for section, options in self.__MINOPTS.iteritems ():
51 for option in options:
52 if (config.has_option(section, option) == False):
53 self.__LOG.error (self.__CONFIG_MISSING % (section, option))
56 def loadConfig (self, scanner_config_path):
58 configfile = scanner_config_path
59 config = ConfigParser.SafeConfigParser ()
61 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)):
62 self.__LOG.error(self.__CONFIG_NOT_READABLE);
63 raise SystemError(self.__CONFIG_NOT_READABLE)
66 config.read (scanner_config_path)
68 self.__LOG.error("Error: %s" % (e));
69 raise SystemError("Error: %s" % (e))
71 self.checkMinimumOptions (config)
75 def contactScanserver(self, url, fields):
76 return httpPool.request_encode_body('POST', url, fields = fields, retries = 0)
78 def scanFile (self, path, fileobject):
79 return self.scanFileIkarus (path, fileobject)
81 def scanFileIkarus (self, path, fileobject):
82 retval = { "infected" : False, "virusname" : "Unknown" }
83 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"] = True
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 = 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.info("Next check for remote server in %s seconds." % (self.__SCANSERVER_RETRY_TIMEOUT))
106 self.__remoteScanserverReachable = False
107 self.__scanserverTimestamp = time.time()
110 response = contactScanserver(self.__LOCAL_SCANSERVER_URL, fields)
112 self.__LOG.error ("Connection to local scan server could not be established.")
113 self.__LOG.error ("Exception: %s" %(sys.exc_info()[0]))
117 response = contactScanserver(self.__LOCAL_SCANSERVER_URL, fields)
119 self.__LOG.error ("Connection to local scan server could not be established.")
120 self.__LOG.error ("Exception: %s" %(sys.exc_info()[0]))
124 if response.status == self.__STATUS_CODE_OK:
125 retval["infected"] = False
126 elif response.status == self.__STATUS_CODE_INFECTED:
127 # Parse xml for info if desired
128 #contentXML = r.content
129 #root = ET.fromstring(contentXML)
130 #status = root[1][2].text
131 retval["infected"] = True
133 self.__LOG.error ("Connection error to scan server.")
135 if (retval["infected"] == True):
136 self.__LOG.error ("Virus found, denying access.")
138 self.__LOG.debug ("No virus found.")