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-10-30 11:52

After modifying the code in >>1 slightly to accept messages for any recipient, I ran the server, sat back and waited. After about two days I received the following:

Return-path: <michael78694@MyMainServer.com>;
Received: from www.MyMainServer.com ([122.120.0.130]) by [my server name]; Tue, 30 Oct 2007 14:39:03 +0000
From: "opr" <michael78694@MyMainServer.com>;
To: "opr mailer" <candy59839@yahoo.com.tw>;
Subject: 2007-10-31 10:35:51 BC_[my IP address]
Date: Mon, 23 Jan 2006 11:22:33 +0800

MIME-Version: 1.0
Content-Type: text/plain;
    charset="big5"
Content-Transfer-Encoding: 8bit
X-Priority: 3
X-MSMail-Priority: Normal
X-Mailer: Microsoft Outlook Express 6.00.2800.1437
X-MimeOLE: Produced By Microsoft MimeOLE V6.00.2800.1441

Greeting

I then manually relayed the message to candy59839@yahoo.com.tw, and approximately an hour later the smtp server started getting absolutely hammered by spams.

Of course, I'm not passing any of them on to the recipients. But I am now watching for any tracer emails like the first one, to see how long it takes them to detect that the spams aren't being  relayed.

After only 15 minutes of this I have received almost 3000 spams, all of them to addresses in the yahoo.com.tw domain. They're all in Chinese.

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