Return Styles: Pseud0ch, Terminal, Valhalla, NES, Geocities, Blue Moon. Entire thread

forced indentation of smtp

Name: Anonymous 2007-10-25 10:06

Hey guys, I am the OP from http://dis.4chan.org/read/prog/1193225538/. It's quite shit, but here is what I ended up writing:

# vsss
# (very simple smtp server)
# revision 0.1

import socket, threading, urllib, os, random, datetime

HOSTNAME = ''
VALID_RECIPIENTS = [
    ('dis.4chan.org', ['prog', 'VIP']),
    ('example.com',   ['lol'])
    ]
MAIL_PATH = 'Z:\MAIL'
DEBUG = False

########################################

CRLF = "\r\n"

def gethostname():

    try:
        # this returns ip address of the requestor
        # i do it this way to help NAT'd servers find their external address
        ipuo = urllib.urlopen('http://zxcsh.com/ip/')
        ip = ipuo.read()
    except:
        try:
            # if that doesn't work, just get hostname in usual way
            hn = socket.gethostname()
        except:
            # and if that fails too, just make one up
            hn = 'vsss'
    else:
        try:
            # hostname using reverse dns
            hn = socket.gethostbyaddr(ip)[0]
        except:
            # or just the ip address if lookup fails
            hn = '[' + ip + ']'
    return hn


class SMTPServer(threading.Thread):

    def __init__(self, sock, addr, port):

        threading.Thread.__init__(self)
        self.sock = sock
        self.addr = addr
        self.port = port
        self.buffer = ''
        self.running = True
       
        self.remotehost = None
        self.returnpath = None
        self.recipients = []

        self.debug('new connection')

    def debug(self, message):
        if DEBUG:
            print self.addr+':'+str(self.port)+' '+message

    def run(self):

        # send greeting
        self.sendline(220, HOSTNAME+' ESMTP')

        # loop until quit
        while self.running:
            line = self.getline()
            if line == None:
                break;
            command = line.partition(' ')
            try:
                getattr(self, 'cmd_'+command[0].lower())(command[2])
            except AttributeError:
                self.resp_badcmd()

        # this checks if it was a clean exit or not
        if not self.running:
            self.sendline(221, '2.0.0 '+HOSTNAME+' Service closing transmission channel')

        self.sock.close()
        self.debug('connection closed')

    def getline(self):
        # keep filling buffer until it contains a crlf
        while CRLF not in self.buffer:
            recvbuf = self.sock.recv(4096)
            if recvbuf == '':
                return None
            self.buffer += recvbuf
        # then return everything up to the crlf, and keep the rest in the buffer
        crlfpart = self.buffer.partition(CRLF)
        self.buffer = crlfpart[2]
        self.debug('<<< '+crlfpart[0])
        return crlfpart[0]

    def sendline(self, status, message):
        if type(message) == str:
            message = [message]
        for line in message[:-1]:
            self.sendline2(str(status)+'-'+line)
        self.sendline2(str(status)+' '+message[-1])

    def sendline2(self, output):
        self.sock.send(output+CRLF)
        self.debug('>>> '+output)

    def resp_badcmd(self):
        self.sendline(502, '5.5.1 Syntax error, command unrecognized')

    def resp_ok(self):
        self.sendline(250, '2.0.0 OK')

    def resp_noremotehost(self):
        self.sendline(503, '5.5.1 Commands out of sequence, require HELO or EHLO')

    def resp_noreturnpath(self):
        self.sendline(503, '5.5.1 Commands out of sequence, require MAIL FROM')

    def resp_norecipients(self):
        self.sendline(503, '5.5.1 Commands out of sequence, require RCPT TO')

    def resp_badreturnpath(self):
        self.sendline(501, '5.5.4 Return path is invalid')

    def resp_badaddress(self):
        self.sendline(501, '5.5.4 Recipient address is invalid')

    def resp_baddomain(self):
        self.sendline(550, '5.7.1 Unable to relay')

    def resp_badrecipient(self):
        self.sendline(550, '5.7.1 Recipient address does not exist')

    def resp_dataerror(self):
        self.sendline(451, '4.3.0 Unknown error while accepting mail, try later')
       
    def cmd_helo(self, args):
        self.sendline(250, HOSTNAME+' says hello')
        self.remotehost = args.strip()

    def cmd_ehlo(self, args):
        self.sendline(250, [HOSTNAME+' says hello','8BITMIME','ENHANCEDSTATUSCODES'])
        self.remotehost = args.strip()

    def check_returnpath(self, returnpath):
        # (TODO: read the relevant RFCs and do this properly)
        if returnpath == '':
            return True
        atsplit = returnpath.split('@')
        if len(atsplit) == 2 and len(atsplit[0]) > 0 and len(atsplit[1]) > 0:
            return True
        return False

    def cmd_mail(self, args):
        if args[:5].lower() != 'from:':
            self.resp_badcmd()
        else:
            if self.remotehost == None:
                self.resp_noremotehost()
            else:
                args = args[5:].strip()
                if args == '':
                    self.resp_badreturnpath()
                    return
                # ignore any other args
                rp = args.partition(' ')[0]
                if rp[0] == '<':
                    if rp[-1] == '>':
                        rp = rp[1:-1]
                    else:
                        self.resp_badreturnpath()
                        return
                if self.check_returnpath(rp):
                    self.returnpath = rp
                    self.resp_ok()
                else:
                    self.resp_badreturnpath()

    def cmd_rcpt(self, args):
        if args[:3].lower() != 'to:':
            self.resp_badcmd()
        else:
            if self.remotehost == None:
                self.resp_noremotehost()
            elif self.returnpath == None:
                self.resp_noreturnpath()
            else:
                args = args[3:].strip()
                if args == '':
                    self.resp_badaddress()
                    return
                ra = args.partition(' ')[0]
                if ra[0] == '<':
                    if ra[-1] == '>':
                        ra = ra[1:-1]
                    else:
                        self.resp_badaddress()
                        return
                atsplit = ra.split('@')
                if not (len(atsplit) == 2 and len(atsplit[0])>0 and len(atsplit[1])>0):
                    self.resp_badaddress()
                    return
                matchvr = None
                for vr in VALID_RECIPIENTS:
                    if atsplit[1].lower() == vr[0].lower():
                        matchvr = vr
                if matchvr == None:
                    self.resp_baddomain()
                    return
                for recp in matchvr[1]:
                    if atsplit[0].lower() == recp:
                        self.recipients.append((matchvr[0],recp))
                        self.resp_ok()
                        return
                self.resp_badrecipient()

    def chdir_mkdir(self, dir):
        try:
            os.chdir(dir)
        except:
            os.mkdir(dir)
            os.chdir(dir)

    def random_string(self):
        s = ''
        for i in range(1,16):
            s += chr(random.randint(96,122))
        return s
   
    def cmd_data(self, args):
        if self.remotehost == None:
            self.resp_noremotehost()
        if self.returnpath == None:
            self.resp_noreturnpath()
        elif self.recipients == []:
            self.resp_norecipients()
        else:
            self.sendline(354, 'Start mail input; end with <CRLF>.<CRLF>')
            message = ''
            line = self.getline()
            while line != '.':
                if line[:1] == '.':
                    line = line[1:]
                message += line + CRLF
                line = self.getline()
            now = datetime.datetime.now()
            message = 'Received: from '+self.remotehost+' (['+self.addr+']) by '+HOSTNAME+'; '+now.strftime('%a, %d %b %Y %H:%M:%S +0000') + CRLF + message
            message = 'Return-path: <'+self.returnpath+'>' + CRLF + message
            filename = now.strftime('%Y%m%d%H%M%S-')+self.random_string()
            try:
                for recp in self.recipients:
                    os.chdir(MAIL_PATH)
                    self.chdir_mkdir(recp[0])
                    self.chdir_mkdir(recp[1])
                    mailfile = open(filename+'.eml', 'wb')
                    mailfile.write(message)
                    mailfile.close()
                    print "wrote mail "+filename+" for "+str(recp)
            except:
                                print "ERROR!"
                self.resp_dataerror()
            else:
                self.resp_ok()
            os.chdir(MAIL_PATH)
           
    def cmd_rset(self, args):
        self.returnpath = None
        self.recipients = []
        self.resp_ok()

    def cmd_noop(self, args):
        self.resp_ok()

    def cmd_quit(self, args):
        self.running = False


########################################

print 'vsss - very simple smtp server'
print

# generate hostname, if required
if HOSTNAME == '':
    HOSTNAME = gethostname()
print 'hostname is '+HOSTNAME

# listen on port 25
srvsock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
srvsock.bind(('', 25))
srvsock.listen(5)

# create thread for each connection
while True:
    (clisock, addrport) = srvsock.accept()
    srvthrd = SMTPServer(clisock, addrport[0], addrport[1])
    srvthrd.start()        

# this is not reached (yet)
srvsock.close()

Name: Anonymous 2007-11-01 19:33

>>68
GTFO. Now.

Newer Posts
Don't change these.
Name: Email:
Entire Thread Thread List