Esempio n. 1
0
    def smtp_RCPT(self, arg):
        event.ReceivedSMTPRcpt.notify()
        print('===> RCPT', arg, file = debug.stream())
        # protocol validation (rfc8321)
        if not self.transaction:
            self.sendReply('503 Error: need MAIL command', event.ReceivedSMTPRcpt)
            return
        address = self.__getaddr('TO:', arg) if arg else None
        if not address:
            self.sendReply('501 Syntax: RCPT TO: <address>', event.ReceivedSMTPRcpt)
            return
        # target smtp validation
        try:
            (code, reply) = self.server.rcpt(address)
            if 250 != code:
                # TODO: maybe examine reply?
                self.sendReply((code, reply), event.ReceivedSMTPRcpt)
                return
        except Exception:
            self.sendReply('451 Error in processing', event.ReceivedSMTPRcpt)
            return
        # end validation

        result = sfsp.plugin.filter.Filter.validateRecipient(address)
        if 250 != result.mainresult.smtp_error:
            self.sendReply((result.mainresult.smtp_error, result.mainresult.message), event.ReceivedSMTPRcpt)
            return


        # end filtering

        self.transaction.addRecipient(address)
        print('recips:', self.transaction.recipients, file = debug.stream())
        self.sendOK(event.ReceivedSMTPRcpt)
Esempio n. 2
0
    def smtp_MAIL(self, arg):
        event.ReceivedSMTPMail.notify()
        print('===> MAIL', arg, file=debug.stream())
        # protocol validation (rfc8321)
        if not self.seen_greeting:
            self.sendReply('503 Error: MAIL before (EH|HE)LO',
                           event.SendSMTPMailResponse)
            return
        if self.transaction:
            self.sendReply('503 Error: nested MAIL command',
                           event.SendSMTPMailResponse)
            return
        address = self.__getaddr('FROM:', arg) if arg else None
        if not address:
            self.sendReply('501 Syntax: MAIL FROM:<address>',
                           event.SendSMTPMailResponse)
            return
        # target smtp validation
        try:
            (code, reply) = self.server.connect_if_needed(
                self.sfsp.options.remoteaddress, self.sfsp.options.remoteport)
            if 220 != code and 250 != code:
                # TODO: maybe only forward 421?
                self.sendReply((code, reply), event.SendSMTPMailResponse)
                return
        except Exception:
            self.sendReply(
                '451 Error in processing (connection to remote smtp server failed)',
                event.SendSMTPMailResponse)
            return
        try:
            self.server.ehlo_or_helo_if_needed()
        except smtplib.SMTPHeloError as excpt:
            self.sendReply('451 Error in processing (%s)' % excpt.resp,
                           event.SendSMTPMailResponse)
            return
        try:
            (code, reply) = self.server.mail(address)
            # TODO: process reply code
            if 250 != code:
                self.sendReply((code, reply), event.SendSMTPMailResponse)
                return
        except Exception:
            self.sendReply(
                '451 Error in processing (connection to remote smtp server failed)',
                event.SendSMTPMailResponse)
            return
        # end validation

        self.transaction = SMTPTransaction(address)

        print('sender:', self.transaction.mailfrom, file=debug.stream())
        self.sendOK(event.SendSMTPMailResponse)
Esempio n. 3
0
 def connect_if_needed(self, host, port):
     print("connect_if_needed()", file=debug.stream())
     if not self.connected:
         print("  not connected, try connect", file=debug.stream())
         try:
             reply = self.connect(host, port)
             print("  reply (%s, %s)" % reply, file=debug.stream())
             if 220 == reply[0]:
                 print("  connected = True", file=debug.stream())
                 self.connected = True
             return reply
         except Exception:
             raise
     else:
         return (250, 'Already connected')
Esempio n. 4
0
    def initSMTP(self):
        event.StartSession.notify()
        self.fqdn = socket.getfqdn()
        """ TODO: kann peer wirklich unterschiedlich sein zu address?
        try:
            self.peer = self.sock.getpeername()
        except socket.error as err:
            # a race condition  may occur if the other end is closing
            # before we can get the peername
            self.close()
            if err.args[0] != errno.ENOTCONN:
                raise
            return
        print('Peer:', repr(self.peer), file = debug.stream())
        """
        print('Address:', repr(self.client.address), file=debug.stream())

        result = sfsp.plugin.filter.Filter.validateClientAddress(self.client)
        if result.failNow() or (not self.sfsp.options.defer_errors
                                and result.failDefer()):
            self.sendReply(result)
        elif result.failChoose():
            # TODO: choose something?
            pass

        self.sendReply((220, '%s %s' % (self.fqdn, self.sfsp.version)),
                       event.SendSMTPBanner)
        self.smtp_state = self.SMTP_COMMAND
Esempio n. 5
0
    def data_terminated(self, line):
        if self.smtp_state != self.SMTP_DATA or (self.data_state != self.DATA_HEADER and self.data_state != self.DATA_BODY):
            self.sendReply((451, 'Internal confusion'))
            self.num_bytes = 0
            return
        if self.num_bytes > self.data_size_limit:
            self.sendReply((552, 'Error: Too much mail data'))
            self.num_bytes = 0
            return

        if '.' == line:
            # end of transaction
            self.process_message()
            self.data_state = self.DATA_NONE
        else:
            # Remove extraneous carriage returns and de-transparency according
            # to RFC 821, Section 4.5.2.
            if line and '.' == line[0]:
                line = line[1:]
            if self.DATA_HEADER == self.data_state:
                if '' == line:
                    print("end of headers", file = debug.stream())
                    self.data_state = self.DATA_BODY
                else:
                    self.transaction.appendHeaderLine(line)
            else:
                self.transaction.appendBodyLine(line)
Esempio n. 6
0
    def data_terminated(self, line):
        if self.smtp_state != self.SMTP_DATA or (
                self.data_state != self.DATA_HEADER
                and self.data_state != self.DATA_BODY):
            self.sendReply((451, 'Internal confusion'))
            self.num_bytes = 0
            return
        if self.num_bytes > self.data_size_limit:
            self.sendReply((552, 'Error: Too much mail data'))
            self.num_bytes = 0
            return

        if '.' == line:
            # end of transaction
            self.process_message()
            self.data_state = self.DATA_NONE
        else:
            # Remove extraneous carriage returns and de-transparency according
            # to RFC 821, Section 4.5.2.
            if line and '.' == line[0]:
                line = line[1:]
            if self.DATA_HEADER == self.data_state:
                if '' == line:
                    print("end of headers", file=debug.stream())
                    self.data_state = self.DATA_BODY
                else:
                    self.transaction.appendHeaderLine(line)
            else:
                self.transaction.appendBodyLine(line)
Esempio n. 7
0
    def initSMTP(self):
        event.StartSession.notify()
        self.fqdn = socket.getfqdn()
        """ TODO: kann peer wirklich unterschiedlich sein zu address?
        try:
            self.peer = self.sock.getpeername()
        except socket.error as err:
            # a race condition  may occur if the other end is closing
            # before we can get the peername
            self.close()
            if err.args[0] != errno.ENOTCONN:
                raise
            return
        print('Peer:', repr(self.peer), file = debug.stream())
        """
        print('Address:', repr(self.client.address), file = debug.stream())

        result = sfsp.plugin.filter.Filter.validateClientAddress(self.client)
        if result.failNow() or (not self.sfsp.options.defer_errors and result.failDefer()):
            self.sendReply(result)
        elif result.failChoose():
            # TODO: choose something?
            pass

        self.sendReply((220, '%s %s' % (self.fqdn, self.sfsp.version)), event.SendSMTPBanner)
        self.smtp_state = self.SMTP_COMMAND
Esempio n. 8
0
 def found_terminator(self):
     line = EMPTYSTRING.join(self.received_lines)
     print('Data:', repr(line), file = debug.stream())
     self.received_lines = []
     if self.smtp_state == self.SMTP_COMMAND:
         self.command_terminated(line)
     else:
         self.data_terminated(line)
Esempio n. 9
0
 def found_terminator(self):
     line = EMPTYSTRING.join(self.received_lines)
     print('Data:', repr(line), file=debug.stream())
     self.received_lines = []
     if self.smtp_state == self.SMTP_COMMAND:
         self.command_terminated(line)
     else:
         self.data_terminated(line)
Esempio n. 10
0
    def smtp_MAIL(self, arg):
        event.ReceivedSMTPMail.notify()
        print('===> MAIL', arg, file = debug.stream())
        # protocol validation (rfc8321)
        if not self.seen_greeting:
            self.sendReply('503 Error: MAIL before (EH|HE)LO', event.SendSMTPMailResponse)
            return
        if self.transaction:
            self.sendReply('503 Error: nested MAIL command', event.SendSMTPMailResponse)
            return
        address = self.__getaddr('FROM:', arg) if arg else None
        if not address:
            self.sendReply('501 Syntax: MAIL FROM:<address>', event.SendSMTPMailResponse)
            return
        # target smtp validation
        try:
            (code, reply) = self.server.connect_if_needed(self.sfsp.options.remoteaddress, self.sfsp.options.remoteport)
            if 220 != code and 250 != code:
                # TODO: maybe only forward 421?
                self.sendReply((code, reply), event.SendSMTPMailResponse)
                return
        except Exception:
            self.sendReply('451 Error in processing (connection to remote smtp server failed)', event.SendSMTPMailResponse)
            return
        try:
            self.server.ehlo_or_helo_if_needed()
        except smtplib.SMTPHeloError as excpt:
            self.sendReply('451 Error in processing (%s)' % excpt.resp, event.SendSMTPMailResponse)
            return
        try:
            (code, reply) = self.server.mail(address)
            # TODO: process reply code
            if 250 != code:
                self.sendReply((code, reply), event.SendSMTPMailResponse)
                return
        except Exception:
            self.sendReply('451 Error in processing (connection to remote smtp server failed)', event.SendSMTPMailResponse)
            return
        # end validation

        self.transaction = SMTPTransaction(address)

        print('sender:', self.transaction.mailfrom, file = debug.stream())
        self.sendOK(event.SendSMTPMailResponse)
Esempio n. 11
0
 def __getaddr(self, keyword, arg):
     address = None
     keylen = len(keyword)
     print(arg[:keylen].upper(), keyword, file=debug.stream())
     if arg[:keylen].upper() == keyword:
         address = arg[keylen:].strip()
         if not address:
             pass
         # TODO: Advanced address parsing?
         elif address[0] == '<' and address[-1] == '>' and address != '<>':
             # Addresses can be in the form <*****@*****.**> but watch out
             # for null address, e.g. <>
             address = address[1:-1]
     return address
Esempio n. 12
0
 def __getaddr(self, keyword, arg):
     address = None
     keylen = len(keyword)
     print(arg[:keylen].upper(), keyword, file = debug.stream())
     if arg[:keylen].upper() == keyword:
         address = arg[keylen:].strip()
         if not address:
             pass
         # TODO: Advanced address parsing?
         elif address[0] == '<' and address[-1] == '>' and address != '<>':
             # Addresses can be in the form <*****@*****.**> but watch out
             # for null address, e.g. <>
             address = address[1:-1]
     return address
Esempio n. 13
0
 def validateRecipient(self, evt, session, address):
     triplet = (session.client.address[0], session.transaction.mailfrom,
                address)
     if triplet in Greylisting.whitetriplets:
         print("found triplet",
               triplet,
               "in whitelist, accepting",
               file=debug.stream())
         return self.passed()
     elif not triplet in Greylisting.greytriplets:
         print("unknwon triplet",
               triplet,
               ", greylisting",
               file=debug.stream())
         Greylisting.greytriplets[triplet] = time.time()
         return self.greylisted()
     elif Greylisting.greytriplets[triplet] > time.time() - 15:
         print("triplet",
               triplet,
               "still greylisted, try again later",
               file=debug.stream())
         # TODO: restart waiting?
         # Greylisting.greytriplets[triplet] = time.time()
         return self.greylisted()
     elif Greylisting.greytriplets[triplet] < time.time(
     ) - 60 * 60 * 24 * 3:  # older than 3 days
         print("triplet",
               triplet,
               "greylisted but not seen for very long, try again later",
               file=debug.stream())
         Greylisting.greytriplets[triplet] = time.time()
         return self.greylisted()
     else:
         print("triplet", triplet, "known, moving to whitelist, accepting")
         Greylisting.greytriplets.pop(triplet)
         Greylisting.whitetriplets[triplet] = time.time()
         return self.passed()
Esempio n. 14
0
    def smtp_RCPT(self, arg):
        event.ReceivedSMTPRcpt.notify()
        print('===> RCPT', arg, file=debug.stream())
        # protocol validation (rfc8321)
        if not self.transaction:
            self.sendReply('503 Error: need MAIL command',
                           event.ReceivedSMTPRcpt)
            return
        address = self.__getaddr('TO:', arg) if arg else None
        if not address:
            self.sendReply('501 Syntax: RCPT TO: <address>',
                           event.ReceivedSMTPRcpt)
            return
        # target smtp validation
        try:
            (code, reply) = self.server.rcpt(address)
            if 250 != code:
                # TODO: maybe examine reply?
                self.sendReply((code, reply), event.ReceivedSMTPRcpt)
                return
        except Exception:
            self.sendReply('451 Error in processing', event.ReceivedSMTPRcpt)
            return
        # end validation

        result = sfsp.plugin.filter.Filter.validateRecipient(address)
        if 250 != result.mainresult.smtp_error:
            self.sendReply(
                (result.mainresult.smtp_error, result.mainresult.message),
                event.ReceivedSMTPRcpt)
            return

        # end filtering

        self.transaction.addRecipient(address)
        print('recips:', self.transaction.recipients, file=debug.stream())
        self.sendOK(event.ReceivedSMTPRcpt)
Esempio n. 15
0
 def __init__(self, options, version):
     self.options = options
     self.version = version
     asyncore.dispatcher.__init__(self)
     try:
         self.create_socket(socket.AF_INET, socket.SOCK_STREAM)
         # try to re-use a server port if possible
         self.set_reuse_addr()
         self.bind((self.options.localaddress, self.options.localport))
         self.listen(5)
     except:
         self.close()
         raise
     else:
         print('%s started at %s\n\tLocal addr: %s\n\tRemote addr:%s' %
               (self.__class__.__name__, time.ctime(time.time()),
                (self.options.localaddress, self.options.localport),
                (self.options.remoteaddress, self.options.remoteport)),
               file=debug.stream())
Esempio n. 16
0
 def reset_if_needed(self):
     print("reset_if_needed()", file=debug.stream())
     if self.connected:
         print("  connected, try reset", file=debug.stream())
         self.rset()
Esempio n. 17
0
 def handle_accepted(self, socket, addr):
     print('Incoming connection from %s' % repr(addr), file = debug.stream())
     channel = SMTPSession(self, socket, addr)
     channel.initSMTP()
Esempio n. 18
0
 def SendSMPTBanner(self, event, session):
     delay = int(Delay.delay - (time.time() - self.start_time))
     if 0 < delay:
         print("Delaying for %d seconds" % delay, file=debug.stream())
         time.sleep(delay)
Esempio n. 19
0
 def SendSMPTBanner(self, event, session):
     delay = int(Delay.delay - (time.time() - self.start_time))
     if 0 < delay:
         print("Delaying for %d seconds" % delay, file = debug.stream())
         time.sleep(delay)
Esempio n. 20
0
 def __init__(self, options, version):
     self.options = options
     self.version = version
     asyncore.dispatcher.__init__(self)
     try:
         self.create_socket(socket.AF_INET, socket.SOCK_STREAM)
         # try to re-use a server port if possible
         self.set_reuse_addr()
         self.bind((self.options.localaddress, self.options.localport))
         self.listen(5)
     except:
         self.close()
         raise
     else:
         print('%s started at %s\n\tLocal addr: %s\n\tRemote addr:%s' % (
             self.__class__.__name__, time.ctime(time.time()),
             (self.options.localaddress, self.options.localport), (self.options.remoteaddress, self.options.remoteport)), file = debug.stream())
Esempio n. 21
0
 def registerPlugin(cls, pluginClass, pluginScope):
     print('loading plugin', pluginClass.__name__, 'from', pluginClass.__module__, file = debug.stream())
     if event.Scope.GLOBAL == pluginScope:
         plugin = pluginClass()
     else:
         plugin = pluginClass
     for key, func in pluginClass.__dict__.items():
         if hasattr(func, 'eventListener'):
             for (evt, priority) in getattr(func, 'eventListener'):
                 evt.register(plugin, key, priority, pluginScope)
     cls.loadedPlugins.add(pluginClass)
Esempio n. 22
0
 def handle_accepted(self, socket, addr):
     print('Incoming connection from %s' % repr(addr), file=debug.stream())
     channel = SMTPSession(self, socket, addr)
     channel.initSMTP()