Example #1
0
 def lint_ping(self):
     try:
         s = self.__init_socket__(oneshot=True)
     except Exception as e:
         print("Could not contact clamd: %s" % (str(e)))
         return False
     s.sendall(force_bString('PING'))
     result = s.recv(20000)
     print("Got Pong: %s" % force_uString(result))
     if result.strip() != b'PONG':
         print("Invalid PONG: %s" % force_uString(result))
     return True
Example #2
0
def readoptions(s):
    resp = receivemsg(s)
    opts = {}

    for l in resp:
        parts = optionsyntax.findall(l)
        for p in parts:
            p0 = force_uString(p[0])
            if p0 not in opts:
                opts[p0] = []

            opts[p0].append(force_uString(p[1]))

    return opts
Example #3
0
    def Dispatch(self, data):
        """Callback function for the milter socket server to handle a single
        milter command.  Parses the milter command data, invokes the milter
        handler, and formats a suitable response for the server to send
        on the socket.

        Args:
          data: A (binary) string (consisting of a command code character
                followed by binary data for that command code).

        Returns:
          A binary string to write on the socket and return to sendmail.  The
          string typically consists of a RESPONSE[] command character then
          some response-specific protocol data.

        Raises:
          PpyMilterCloseConnection: Indicating the (milter) connection should
                                    be closed.
        """
        cmd, data = data[0], data[1:]
        try:
            if cmd not in COMMANDS:
                logging.warn('Unknown command code: "%s" ("%s")',
                             force_uString(cmd), force_uString(data))
                return RESPONSE['CONTINUE']
            u_command = force_uString(COMMANDS[cmd])  # (unicode)
            parser_callback_name = '_Parse%s' % u_command
            handler_callback_name = 'On%s' % u_command

            if not hasattr(self, parser_callback_name):
                logging.error('No parser implemented for "%s"', u_command)
                return RESPONSE['CONTINUE']

            if not hasattr(self.__milter, handler_callback_name):
                logging.warn(
                    'Unimplemented command in milter %s: "%s" ("%s")' %
                    (self.__milter, u_command, data))
                return RESPONSE['CONTINUE']

            parser = getattr(self, parser_callback_name)
            callback = getattr(self.__milter, handler_callback_name)
            args = parser(cmd, data)
            return callback(*args)
        except PpyMilterTempFailure as e:
            logging.info('Temp Failure: %s', str(e))
            return RESPONSE['TEMPFAIL']
        except PpyMilterPermFailure as e:
            logging.info('Perm Failure: %s', str(e))
            return RESPONSE['REJECT']
Example #4
0
    def process(self, suspect, decision):
        recipient = force_uString(
            suspect.to_domain)  # work with unicode string
        if self.config.get(self.section, 'level') == 'email':
            recipient = suspect.to_address
        recipient = recipient.replace('.', '-')
        recipient = recipient.replace('@', '--')

        host = self.config.get(self.section, 'host')
        port = int(self.config.get(self.section, 'port'))

        buffer = ""
        if self.sock is None:
            addr_f = socket.getaddrinfo(host, 0)[0][0]
            self.sock = socket.socket(addr_f, socket.SOCK_DGRAM)

        if suspect.is_virus():
            buffer = "%s%s.fuglu.recipient.%s.virus:1|c\n" % (
                buffer, self.nodename, recipient)
        elif suspect.is_highspam():
            buffer = "%s%s.fuglu.recipient.%s.highspam:1|c\n" % (
                buffer, self.nodename, recipient)
        elif suspect.is_spam():
            buffer = "%s%s.fuglu.recipient.%s.spam:1|c\n" % (
                buffer, self.nodename, recipient)
        else:
            buffer = "%s%s.fuglu.recipient.%s.clean:1|c\n" % (
                buffer, self.nodename, recipient)

        self.sock.sendto(force_bString(buffer), (host, port))
Example #5
0
    def testSMIME(self):
        """test if S/MIME mails still pass the signature"""

        # give fuglu time to start listener
        time.sleep(1)

        # send test message
        smtpclient = smtplib.SMTP('127.0.0.1', SMIMETestCase.FUGLU_PORT)
        # smtpServer.set_debuglevel(1)
        smtpclient.helo('test.smime')
        inputfile = TESTDATADIR + '/smime/signedmessage.eml'
        (status, output) = self.verifyOpenSSL(inputfile)
        self.assertTrue(
            status == 0, "Testdata S/MIME verification failed: \n%s" % output)
        msgstring = open(inputfile, 'r').read()
        smtpclient.sendmail(
            '*****@*****.**', '*****@*****.**', force_uString(msgstring))

        smtpclient.quit()

        # verify the smtp server stored the file correctly
        tmpfile = self.smtp.tempfilename

        #self.failUnlessEqual(msgstring, tmpcontent, "SMTP Server did not store the tempfile correctly: %s"%tmpfile)
        (status, output) = self.verifyOpenSSL(tmpfile)
        self.assertTrue(
            status == 0, "S/MIME verification failed: \n%s\n tmpfile is:%s" % (output, tmpfile))
Example #6
0
    def lint_ping(self):
        """ping sa"""
        retries = self.config.getint(self.section, 'retries')
        for i in range(0, retries):
            try:
                self.logger.debug('Contacting spamd (Try %s of %s)' %
                                  (i + 1, retries))
                s = self.__init_socket()
                s.sendall(b'PING SPAMC/1.2')
                s.sendall(b"\r\n")
                s.shutdown(socket.SHUT_WR)
                socketfile = s.makefile("rb")
                line = force_uString(socketfile.readline())
                line = line.strip()
                answer = line.split()
                if len(answer) != 3:
                    print("Invalid SPAMD PONG: %s" % line)
                    return False

                if answer[2] != "PONG":
                    print("Invalid SPAMD Pong: %s" % line)
                    return False
                print("Got: %s" % line)
                return True
            except socket.timeout:
                print('SPAMD Socket timed out.')
            except socket.herror as h:
                print('SPAMD Herror encountered : %s' % str(h))
            except socket.gaierror as g:
                print('SPAMD gaierror encountered: %s' % str(g))
            except socket.error as e:
                print('SPAMD socket error: %s' % str(e))

            time.sleep(1)
        return False
Example #7
0
    def scan_stream(self, content, suspectid='(NA)'):
        """
        Scan a buffer

        content (string) : buffer to scan

        return either :
          - (dict) : {filename1: "virusname"}
          - None if no virus found
        """

        s = self.__init_socket__()
        content = force_bString(content)
        buflen = len(content)
        s.sendall(
            force_bString(
                'SCAN %s STREAM fu_stream SIZE %s' %
                (self.config.get(self.section, 'scanoptions'), buflen)))
        s.sendall(b'\n')
        self.logger.debug('%s Sending buffer (length=%s) to fpscand...' %
                          (suspectid, buflen))
        s.sendall(content)
        self.logger.debug(
            '%s Sent %s bytes to fpscand, waiting for scan result' %
            (suspectid, buflen))

        result = force_uString(s.recv(20000))
        if len(result) < 1:
            self.logger.error('Got no reply from fpscand')
        s.close()

        return self._parse_result(result)
Example #8
0
 def lint_version(self):
     try:
         s = self.__init_socket__(oneshot=True)
     except Exception:
         return False
     s.sendall(b'VERSION')
     result = s.recv(20000)
     print("Got Version: %s" % force_uString(result))
     return True
Example #9
0
    def safilter(self, messagecontent, user):
        """pass content to sa, return sa-processed mail"""
        retries = self.config.getint(self.section, 'retries')
        peruserconfig = self.config.getboolean(self.section, 'peruserconfig')
        spamsize = len(messagecontent)
        for i in range(0, retries):
            try:
                self.logger.debug('Contacting spamd (Try %s of %s)' %
                                  (i + 1, retries))
                s = self.__init_socket()
                s.sendall(force_bString('PROCESS SPAMC/1.2'))
                s.sendall(force_bString("\r\n"))
                s.sendall(force_bString("Content-length: %s" % spamsize))
                s.sendall(force_bString("\r\n"))
                if peruserconfig:
                    s.sendall(force_bString("User: %s" % user))
                    s.sendall(force_bString("\r\n"))
                s.sendall(force_bString("\r\n"))
                s.sendall(force_bString(messagecontent))
                self.logger.debug('Sent %s bytes to spamd' % spamsize)
                s.shutdown(socket.SHUT_WR)
                socketfile = s.makefile("rb")
                line1_info = socketfile.readline()
                line1_info = force_uString(
                    line1_info)  # convert to unicode string
                self.logger.debug(line1_info)
                line2_contentlength = socketfile.readline()
                line3_empty = socketfile.readline()
                content = socketfile.read()
                self.logger.debug('Got %s message bytes from back from spamd' %
                                  len(content))
                answer = line1_info.strip().split()
                if len(answer) != 3:
                    self.logger.error(
                        "Got invalid status line from spamd: %s" % line1_info)
                    continue

                version, number, status = answer
                if status != 'EX_OK':
                    self.logger.error("Got bad status from spamd: %s" % status)
                    continue

                return content
            except socket.timeout:
                self.logger.error('SPAMD Socket timed out.')
            except socket.herror as h:
                self.logger.error('SPAMD Herror encountered : %s' % str(h))
            except socket.gaierror as g:
                self.logger.error('SPAMD gaierror encountered: %s' % str(g))
            except socket.error as e:
                self.logger.error('SPAMD socket error: %s' % str(e))
            except Exception as e:
                self.logger.error(str(e))

            time.sleep(1)
        return None
Example #10
0
    def handlesession(self):
        line = force_uString(self.socket.recv(4096)).lower().strip()
        if line == '':
            self.socket.close()
            return

        self.logger.debug('Control Socket command: %s' % line)
        parts = line.split()
        answer = self.handle_command(parts[0], parts[1:])
        self.socket.sendall(force_bString(answer))
        self.socket.close()
Example #11
0
    def safilter_symbols(self, messagecontent, user):
        """Pass content to sa, return spamflag, score, rules"""
        ret = self._safilter_content(messagecontent, user, 'SYMBOLS')
        if ret is None:
            return None

        status, score, content = ret

        content = force_uString(content)
        rules = content.split(',')
        return status, score, rules
Example #12
0
    def _parse_result(self, result):
        dr = {}
        result = force_uString(result)
        for line in result.strip().split('\n'):
            m = self.pattern.match(force_bString(line))
            if m is None:
                self.logger.error('Could not parse line from f-prot: %s' %
                                  line)
                raise Exception('f-prot: Unparseable answer: %s' % result)
            status = force_uString(m.group(1))
            text = force_uString(m.group(2))
            details = force_uString(m.group(3))

            status = int(status)
            self.logger.debug("f-prot scan status: %s" % status)
            self.logger.debug("f-prot scan text: %s" % text)
            if status == 0:
                continue

            if status > 3:
                self.logger.warning("f-prot: got unusual status %s" % status)

            # http://www.f-prot.com/support/helpfiles/unix/appendix_c.html
            if status & 1 == 1 or status & 2 == 2:
                # we have a infection
                if text[0:10] == "infected: ":
                    text = text[10:]
                elif text[0:27] == "contains infected objects: ":
                    text = text[27:]
                else:
                    self.logger.warn("Unexpected reply from f-prot: %s" % text)
                    continue
                dr[details] = text

        if len(dr) == 0:
            return None
        else:
            return dr
Example #13
0
    def getincomingmail(self):
        """return true if mail got in, false on error Session will be kept open"""
        self.socket.send(force_bString("220 fuglu scanner ready \r\n"))

        while True:
            rawdata = b''
            data = ''
            completeLine = 0
            while not completeLine:

                lump = self.socket.recv(1024)

                if len(lump):
                    rawdata += lump

                    if (len(rawdata) >=
                            2) and rawdata[-2:] == force_bString('\r\n'):
                        completeLine = 1

                        if self.state != SMTPSession.ST_DATA:

                            # convert data to unicode if needed
                            data = force_uString(rawdata)
                            rsp, keep = self.doCommand(data)

                        else:
                            try:
                                #directly use raw bytes-string data
                                rsp = self.doData(rawdata)
                            except IOError:

                                self.endsession(
                                    421, "Could not write to temp file")
                                self._close_tempfile()
                                return False

                            if rsp is None:
                                continue
                            else:
                                # data finished.. keep connection open though
                                return True

                        self.socket.send(force_bString(rsp + "\r\n"))

                        if keep == 0:
                            self.closeconn()
                            return False
                else:
                    # EOF
                    return False
Example #14
0
    def re_inject(self, suspect):
        """Send message back to postfix"""
        if suspect.get_tag('noreinject'):
            # in esmtp sessions we don't want to provide info to the connecting
            # client
            return 250, 'OK'
        if suspect.get_tag('reinjectoriginal'):
            self.logger.info('Injecting original message source without modifications')
            msgcontent = suspect.get_original_source()
        else:
            msgcontent = buildmsgsource(suspect)

        code, answer = self.sess.forwardconn.data(force_bString(msgcontent))
        answer = force_uString(answer)
        return code, answer
Example #15
0
    def forwardCommand(self, command):
        """forward a esmtp command to outgoing postfix instance
        
        Args:
            command (str): command in unicode
        
        Returns (str): reply from outgoing server
        """
        
        command = command.strip()
        if self.forwardconn is None:
            targethost = self.config.get('main', 'outgoinghost')
            if targethost == '${injecthost}':
                targethost = self.socket.getpeername()[0]
            self.forwardconn = smtplib.SMTP(force_uString(targethost), self.config.getint('main', 'outgoingport'))
        self.logger.debug("""SEND: "%s" """ % command)

        # docmd seems to have a normal string as input, so
        # I guess unicode will work for python 3
        code, ans = self.forwardconn.docmd(command)
        ret = "%s %s" % (code, force_uString(ans))
        if ret.find('\n'):
            temprv = []
            parts = ret.split('\n')
            code = ret[:3]
            parts[0] = parts[0][3:]
            line = ''
            for line in parts:
                line = line.strip()
                temprv.append('%s-%s' % (code, line))
            # replace - with space on last line
            temprv[-1] = '%s %s' % (code, line)

            ret = '\r\n'.join(temprv)
        self.logger.debug("""RECEIVE: "%s" """ % ret)
        return ret.strip()
Example #16
0
    def scan_shell(self, content):
        clamscan = self.config.get(self.section, 'clamscan')
        timeout = self.config.getint(self.section, 'clamscantimeout')

        if not os.path.exists(clamscan):
            raise Exception('could not find clamscan executable in %s' %
                            clamscan)

        try:
            process = subprocess.Popen(
                [clamscan, u'-'],
                stdin=subprocess.PIPE,
                stdout=subprocess.PIPE)  # file data by pipe
            kill_proc = lambda p: p.kill()
            timer = threading.Timer(timeout, kill_proc, [process])
            timer.start()
            stdout = process.communicate(force_bString(content))[0]
            process.stdin.close()
            exitcode = process.wait()
            timer.cancel()
        except Exception:
            exitcode = -1
            stdout = ''

        if exitcode > 1:  # 0: no virus, 1: virus, >1: error, -1 subprocess error
            raise Exception('clamscan error')
        elif exitcode < 0:
            raise Exception('clamscan timeout after %ss' % timeout)

        dr = {}

        for line in stdout.splitlines():
            line = line.strip()
            if line.endswith(b'FOUND'):
                filename, virusname, found = line.rsplit(None, 2)
                filename = force_uString(filename.rstrip(b':'))
                dr[filename] = virusname

        if dr == {}:
            return None
        else:
            return dr
Example #17
0
    def getincomingmail(self):
        """return true if mail got in, false on error Session will be kept open"""
        self._send("220 dummy server ready \r\n")
        while 1:
            rawdata = b''
            data = ''
            completeLine = 0
            while not completeLine:
                lump = self.socket.recv(1024)
                if len(lump):
                    rawdata += lump
                    if (len(rawdata) >= 2) and rawdata[-2:] == '\r\n'.encode(
                            "utf-8", "strict"):
                        completeLine = 1
                        print("< %s" % data)
                        if self.state != SMTPSession.ST_DATA:
                            data = force_uString(rawdata)
                            rsp, keep = self.doCommand(data)
                        else:
                            try:
                                rsp = self.doData(rawdata)
                            except IOError:
                                self.endsession(
                                    421, "Could not write to temp file")
                                return False

                            if rsp == None:
                                continue
                            else:
                                # data finished.. keep connection open though
                                print('incoming message finished')
                                return True

                        self._send(rsp + "\r\n")
                        if keep == 0:
                            self.socket.close()
                            return False
                else:
                    # EOF
                    return False
        return False
Example #18
0
    def getincomingmail(self):
        """return true if mail got in, false on error Session will be kept open"""
        self.socket.send(force_bString("220 fuglu scanner ready \r\n"))

        while True:
            rawdata = b''
            completeLine = 0
            while not completeLine:
                lump = self.socket.recv(1024)
                if len(lump):
                    rawdata += lump
                    if (len(rawdata) >= 2) and rawdata[-2:] == b'\r\n':
                        completeLine = 1

                        if self.state != ESMTPPassthroughSession.ST_DATA:
                            # decode message (except data) from binary to unicode
                            data = force_uString(rawdata)
                            rsp, keep = self.doCommand(data)
                        else:
                            try:
                                rsp = self.doData(rawdata)
                            except IOError:
                                self.endsession(421, "Could not write to temp file")
                                self._close_tempfile()
                                return False

                            if rsp is None:
                                continue
                            else:
                                # data finished.. keep connection open though
                                self.logger.debug('incoming message finished')
                                return True
                        
                        self.socket.send(force_bString(rsp + "\r\n"))
                        if keep == 0:
                            self.closeconn()
                            self.finish_outgoing_connection()
                            return False
                else:
                    # EOF
                    return False
Example #19
0
    def send_template_file(self, recipient, templatefile, suspect, values):
        """Send a E-Mail Bounce Message

        Args:
            recipient    (str):  Message recipient ([email protected])
            templatefile (str): Template to use
            suspect      (fuglu.shared.Suspect) suspect that caused the bounce
            values            :Values to apply to the template. ensure all values are of type <str>

        If the suspect has the 'nobounce' tag set, the message will not be sent. The same happens
        if the global configuration 'disablebounces' is set.
        """

        if not os.path.exists(templatefile):
            self.logger.error(
                'Template file does not exist: %s' % templatefile)
            return

        with open(templatefile) as fp:
            filecontent = fp.read()

        self.send_template_string(recipient, force_uString(filecontent), suspect, values)
Example #20
0
    def _safilter_content(self, messagecontent, user, command):
        """pass content to sa, return body"""
        assert command in [
            'SYMBOLS',
            'REPORT',
        ]
        retries = self.config.getint(self.section, 'retries')
        peruserconfig = self.config.getboolean(self.section, 'peruserconfig')
        spamsize = len(messagecontent)
        for i in range(0, retries):
            try:
                self.logger.debug('Contacting spamd  (Try %s of %s)' %
                                  (i + 1, retries))
                s = self.__init_socket()
                s.sendall(force_bString('%s SPAMC/1.2' % command))
                s.sendall(force_bString("\r\n"))
                s.sendall(force_bString("Content-length: %s" % spamsize))
                s.sendall(force_bString("\r\n"))
                if peruserconfig:
                    s.sendall(force_bString("User: %s" % user))
                    s.sendall(force_bString("\r\n"))
                s.sendall(force_bString("\r\n"))
                s.sendall(force_bString(messagecontent))
                self.logger.debug('Sent %s bytes to spamd' % spamsize)
                s.shutdown(socket.SHUT_WR)
                socketfile = s.makefile("rb")
                line1_info = force_uString(socketfile.readline())
                self.logger.debug(line1_info)
                line2_spaminfo = force_uString(socketfile.readline())

                line3 = force_uString(socketfile.readline())
                content = socketfile.read()
                content = content.strip()

                self.logger.debug('Got %s message bytes from back from spamd' %
                                  len(content))
                answer = line1_info.strip().split()
                if len(answer) != 3:
                    self.logger.error(
                        "Got invalid status line from spamd: %s" % line1_info)
                    continue

                (version, number, status) = answer
                if status != 'EX_OK':
                    self.logger.error("Got bad status from spamd: %s" % status)
                    continue

                self.logger.debug('Spamd said: %s' % line2_spaminfo)
                spamword, spamstatusword, colon, score, slash, required = line2_spaminfo.split(
                )
                spstatus = False
                if spamstatusword == 'True':
                    spstatus = True

                return spstatus, float(score), content
            except socket.timeout:
                self.logger.error('SPAMD Socket timed out.')
            except socket.herror as h:
                self.logger.error('SPAMD Herror encountered : %s' % str(h))
            except socket.gaierror as g:
                self.logger.error('SPAMD gaierror encountered: %s' % str(g))
            except socket.error as e:
                self.logger.error('SPAMD socket error: %s' % str(e))

            time.sleep(1)
        return None
Example #21
0
    def scan_stream(self, content, suspectid="(NA)"):
        """
        Scan byte buffer

        return either :
          - (dict) : {filename1: "virusname"}
          - None if no virus found
          - raises Exception if something went wrong
        """
        pipelining = self.config.getboolean(self.section, 'pipelining')
        s = self.__init_socket__(oneshot=not pipelining)
        s.sendall(b'zINSTREAM\0')
        default_chunk_size = 2048
        remainingbytes = force_bString(content)

        numChunksToSend = math.ceil(len(remainingbytes) / default_chunk_size)
        iChunk = 0
        chunklength = 0
        self.logger.debug('%s: sending message in %u chunks of size %u bytes' %
                          (suspectid, numChunksToSend, default_chunk_size))

        while len(remainingbytes) > 0:
            iChunk = iChunk + 1
            chunklength = min(default_chunk_size, len(remainingbytes))
            #self.logger.debug('sending chunk %u/%u' % (iChunk,numChunksToSend))
            #self.logger.debug('sending %s byte chunk' % chunklength)
            chunkdata = remainingbytes[:chunklength]
            remainingbytes = remainingbytes[chunklength:]
            s.sendall(struct.pack(b'!L', chunklength))
            s.sendall(chunkdata)
        self.logger.debug(
            '%s: sent chunk %u/%u, last number of bytes sent was %u' %
            (suspectid, iChunk, numChunksToSend, chunklength))
        self.logger.debug(
            '%s: All chunks send, send 0 - size to tell ClamAV the whole message has been sent'
            % suspectid)
        s.sendall(struct.pack(b'!L', 0))
        dr = {}

        result = force_uString(self._read_until_delimiter(s,
                                                          suspectid)).strip()

        if result.startswith('INSTREAM size limit exceeded'):
            raise Exception(
                "%s: Clamd size limit exeeded. Make sure fuglu's clamd maxsize config is not larger than clamd's StreamMaxLength"
                % suspectid)
        if result.startswith('UNKNOWN'):
            raise Exception(
                "%s: Clamd doesn't understand INSTREAM command. very old version?"
                % suspectid)

        if pipelining:
            try:
                ans_id, filename, virusinfo = result.split(':', 2)
                filename = force_uString(
                    filename.strip())  # use unicode for filename
                virusinfo = force_uString(
                    virusinfo.strip())  # lets use unicode for the info
            except:
                raise Exception(
                    "%s: Protocol error, could not parse result: %s" %
                    (suspectid, result))

            threadLocal.expectedID += 1
            if threadLocal.expectedID != int(ans_id):
                raise Exception(
                    "Commands out of sync - expected ID %s - got %s" %
                    (threadLocal.expectedID, ans_id))

            if virusinfo[-5:] == 'ERROR':
                raise Exception(virusinfo)
            elif virusinfo != 'OK':
                dr[filename] = virusinfo.replace(" FOUND", '')

            if threadLocal.expectedID >= MAX_SCANS_PER_SOCKET:
                try:
                    s.sendall(b'zEND\0')
                    s.close()
                finally:
                    self.__invalidate_socket()
        else:
            filename, virusinfo = result.split(':', 1)
            filename = force_uString(
                filename.strip())  # use unicode for filename
            virusinfo = force_uString(
                virusinfo.strip())  # use unicode for virus info
            if virusinfo[-5:] == 'ERROR':
                raise Exception(virusinfo)
            elif virusinfo != 'OK':
                dr[filename] = virusinfo.replace(" FOUND", '')
            s.close()

        if dr == {}:
            return None
        else:
            return dr
Example #22
0
 def read(self, length):
     return force_uString(self.s.recv(length))
Example #23
0
    def scan_stream(self, content, suspectid='(NA)'):
        """
        Scan a buffer

        content (string) : buffer to scan

        return either :
          - (dict) : {filename1: "virusname"}
          - None if no virus found
        """

        s = self.__init_socket__()
        dr = {}

        # Read the welcome message

        if not exchangeGreetings(s):
            raise Exception("SSSP Greeting failed")

        # QUERY to discover the maxclassificationsize
        s.send(b'SSSP/1.0 QUERY\n')

        if not accepted(s):
            raise Exception("SSSP Query rejected")

        options = readoptions(s)

        # Set the options for classification
        enableoptions = [
            b"TnefAttachmentHandling",
            b"ActiveMimeHandling",
            b"Mime",
            b"ZipDecompression",
            b"DynamicDecompression",
        ]

        enablegroups = [
            b'GrpExecutable',
            b'GrpArchiveUnpack',
            b'GrpSelfExtract',
            b'GrpInternet',
            b'GrpSuper',
            b'GrpMisc',
        ]

        sendbuf = "OPTIONS\nreport:all\n"
        for opt in enableoptions:
            sendbuf += "savists: %s 1\n" % force_uString(opt)


        for grp in enablegroups:
            sendbuf += "savigrp: %s 1\n" % force_uString(grp)

        # all sent, add aditional newline
        sendbuf += "\n"

        s.send(force_bString(sendbuf))

        if not accepted(s):
            raise Exception("SSSP Options not accepted")

        resp = receivemsg(s)

        for l in resp:
            if donesyntax.match(l):
                parts = donesyntax.findall(l)
                if parts[0][0] != b'OK':
                    raise Exception("SSSP Options failed")
                break

        # Send the SCAN request

        s.send(force_bString('SCANDATA ' + str(len(content)) + '\n'))
        if not accepted(s):
            raise Exception("SSSP Scan rejected")

        s.sendall(force_bString(content))

        # and read the result
        events = receivemsg(s)

        for l in events:
            if virussyntax.match(l):
                parts = virussyntax.findall(l)
                virus = force_uString(parts[0][0])
                filename = force_uString(parts[0][1])
                dr[filename] = virus

        try:
            sayGoodbye(s)
            s.shutdown(socket.SHUT_RDWR)
        except socket.error as e:
            self.logger.warning('%s Error terminating connection: %s', (suspectid, str(e)))
        finally:
            s.close()

        if dr == {}:
            return None
        else:
            return dr