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

Pages: 1-4041-

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-25 10:29

you fail.

Name: Anonymous 2007-10-25 10:36

python has an smtp server module built in already

you just wasted weeks of your life

Name: Anonymous 2007-10-25 10:38

>>3
It's ok, it was a learning exercise

Name: Anonymous 2007-10-25 13:13

...........           ..........
...........           ..........

Name: Anonymous 2007-10-25 13:38

>>3
Weeks? I'd write that in C in a few of days.

Name: Anonymous 2007-10-25 13:41

echo 'text' | mail bill.gates@linux.com

You're all losers.

Name: Anonymous 2007-10-25 13:42

>>6
Dear ``FUCKING IDIOT''. Please follow OP's link and realize that he only wasted one day of his life writing this crap.

Name: Anonymous 2007-10-25 13:46

test

Name: Anonymous 2007-10-25 13:56

CRLF = "\r\n"
FAIL

Name: Anonymous 2007-10-25 15:02

snape fucks dumbledore

Name: Anonymous 2007-10-25 15:23

lol, I could write an SMTP server in about an hour with C.

Name: Anonymous 2007-10-25 19:34

>>12
DO IT, FAGGOT

Name: Anonymous 2007-10-25 19:35

>>10
how is that fail?

Name: Anonymous 2007-10-25 19:41

It is messy and needs improvement. When you have improved it, post again.

Name: Anonymous 2007-10-25 20:59

>>14
"Let's declare CRLF as a constant instead of using it in a literal, just in case it ever changes!"

Name: Anonymous 2007-10-25 21:48

>>16

It's not a constant, it's just capitalized

Name: Anonymous 2007-10-26 0:16

Name: Anonymous 2007-10-26 4:59

>>16
Using the variable CRLF, it's more clear of what it is than "\r\n".

By the way, Python doesn't support constants, so (as >>17 alludes to) it is customary to capitalize the variable name to indicate intent.

Name: Anonymous 2007-10-26 5:15

FAIL for:

now.strftime('%a, %d %b %Y %H:%M:%S +0000')
using +0000 is stupid, you need to get the proper timezone

self.remotehost = args.strip()
...
message = 'Return-path: <'+self.returnpath+'>' + CRLF + message
blindly trusting whatever is supplied in the HELO / EHLO parameter, and then putting it straight into the message

def resp_MESSAGE(self):
        self.sendline(..., '...')

this is retarded design, use a dictionary instead

message += line + CRLF
keeping the entire message in memory instead of spooling to disk - enjoy your resource exhaustion

...
everything else

Name: Anonymous 2007-10-26 5:37

>>19
How is writing "CRLF" more clear than writing a carriage return then a line feed? It's less straightforward and harder to type anyway.

Name: !Ocf8Jnb/l. 2007-10-26 6:05

>>21
I don't know (I'm not >>19 or anyone else prior poster in this thread), but I use constants instead of string literals each and every time I need to repeat a string, because this way I can't accidentally type "\r\m" or something once in such a way that the compiler or interpreter doesn't catch it immediately.

Name: Anonymous 2007-10-26 6:06

>>22
#TRIPCOD didn't produce desirable results.

Name: !GZO15gPeDo 2007-10-26 6:12

>>22
\m is not a valid.

Name: !Ocf8Jnb/l. 2007-10-26 6:16

>>24
OH RLY?

Name: Anonymous 2007-10-26 6:29

>>21

CRLF - shorter to type, more clear about what it is
"\r\n" - longer to type (more characters, have to press and unpress the Shift key twice, and use non-alpha keys), more cryptic

Name: Anonymous 2007-10-26 6:29

It amuses me that of all the things to criticize about the program, we're quibbling about CRLF

Name: Anonymous 2007-10-26 6:30

It amuses me that of all the things to criticize about the program, we're quibbling about CRLF

Name: Anonymous 2007-10-26 6:35

It amuses me that of all the things to criticize about the program, we're quibbling about CRLF

Name: Anonymous 2007-10-26 6:38

It amuses me dat uh all de doodads t'criticize about da damn honky code, we's quibblin' about CRLF

Name: Anonymous 2007-10-26 7:28

It amuses me that of all the things to criticize about the program, I'm criticizing the following:

- Automagically generate resp_* on the fly. You're just repeating the same shit over and over.
- Stop writing those horrible if-else behemoths. Restructure your code.
- cmd_rcpt and cmd_data are simply too long (read: broken). cmd_mail is a length-wise a borderline case, but it just generally sucks (see the previous).
- Start using descriptive docstrings when they would benefit the reader.
- sendline2 is a retarded method name. Replace it.
- args[:5]
- Don't do utterly retarded crap like
if len(atsplit) == 2 and len(atsplit[0]) > 0 and len(atsplit[1]) > 0:
    return True
return False

- if args[:5].lower() != 'from:'. lolwut. Replace these magic index fiddling tricks with real string handling.
- Unpack tuples when it improves readability (which is, practically always).
- Use format strings when your string patchwork starts to get out of hand. Instead of
'Received: from '+self.remotehost+' (['+self.addr+']) by '+HOSTNAME+'; '+now.strftime('%a, %d %b %Y %H:%M:%S +0000'),
use
'Received: from %(remote)s ([%s(addr)]) by %(host)s; %(now)s' % {
    'remote': self.remotehost,
    'addr':   self.addr,
    'host':   HOSTNAME,
    'now':    now.strftime('%a, %d %b %Y %H:%M:%S +0000')
    }

Name: Anonymous 2007-10-26 8:03

>>31
OP here. Thanks for the criticisms, it is very useful.

Name: Anonymous 2007-10-26 8:03

>>20
Thanks, I will fix those too.

Name: Anonymous 2007-10-26 8:09

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.

Name: Anonymous 2007-10-30 11:53

Also, ●眼見角落邊已經有女孩被瘋狂插著~另一邊也開始出現呻吟聲~●

Name: Anonymous 2007-10-30 12:59

>>35
Install spamd [1], it'll do you good.

[1] Oct 26 17:00:15 zaku spamd[31709]: (GREY) 123.252.121.107: <dvdr_mail2001@yahoo.com.tw>; -> <dvdr_mail2000@yahoo.com.cn>;
Oct 26 17:02:00 zaku spamd[31709]: 123.252.121.107: Subject:85.226.139.17
Oct 26 17:02:49 zaku spamd[31709]: 123.252.121.107: disconnected after
379 seconds.

Name: Anonymous 2007-10-30 13:24

>>37
Thanks for the link

Name: Anonymous 2007-10-30 14:47

>>38
No problem.

(In all honesty, I couldn't find it myself.  I run OpenBSD 4.2, so it's included with the base system, I dunno about any other system.)

Name: Anonymous 2007-10-30 15:00

>>39
Funny that, I didn't even realise you hadn't linked it. It was so natural to type the name into Google and follow the most likely looking results that I didn't even really think much of it.

Name: Anonymous 2007-10-30 16:19

How the fuck does this happen?

os.mkdir(dir) succeeds but the os.chdir(dir) immediately following it fails, even though the directory must exist as mkdir just completed without raising an exception!

ERROR while writing 20071030201458-dumzkcpmaxdchdt for ('yahoo.com.tw', 'yeh2200')
Exception in thread Thread-627:
Traceback (most recent call last):
  File "C:\Program Files\Python\lib\threading.py", line 460, in __bootstrap
    self.run()
  File "N:\vsss.py", line 77, in run
    getattr(self, 'cmd_'+command[0].lower())(command[2])
  File "N:\vsss.py", line 277, in cmd_data
    self.chdir_mkdir(self.escape_filename(recp[1]))
  File "N:\vsss.py", line 236, in chdir_mkdir
    os.chdir(dir)
WindowsError: [Error 2] The system cannot find the file specified: 'yeh2200'


This spam-hammer is bringing out loads of other bugs too, but this is the most bizarre. It happens every other minute or so.

Name: Anonymous 2007-10-30 16:25

>>41
One word.

Name: Anonymous 2007-10-30 16:39

>>41
Race condition. Your filesystem hasn't bothered making the directory by the time your program comes to change to it.

Name: Anonymous 2007-10-30 23:01

>>43
you're doin it wrong.
the real reason is the FORCED INDENTATION OF CODE

Name: Anonymous 2007-10-31 4:36

>>44
What does Python have to do with anything? You do realize in the end it's using system calls to create the directory on the file system, right? >>43 has the right idea. A crude way to fulfill >>41's intention is to spin loop after creating the directory, breaking once you can confirm it does exist.

Name: Anonymous 2007-10-31 6:23

Obviously Python has failed to provide the abstract API it aims for. If I have to micromanage OS-level filesystem problems like this, I might as well write in C.

Name: Anonymous 2007-10-31 6:33

>>46
[ ] File a bug report
[ ] GTFO

The Choice Is Yours

Name: Anonymous 2007-10-31 6:40

>>47
So it's a bug now?

Name: Anonymous 2007-10-31 6:44

>>48
Looks like a bug to me

Name: TuneAFish 2007-10-31 6:44

>>47
Asynchronous file creation is not a bug; you just suck. Spin-loop after the syscall

Name: Anonymous 2007-10-31 6:56

>>50
the program: please create me a directory with the name "faggots".
the system: ok, that's all done for you.
the program: thanks. now change to the directory named "faggots".
the system: sorry, i can't. it doesn't exist!
the program: but you just told me you made it.
the system: did i? i can't remember.
the program: yes, you did.
the system: no, i can't possibly have.
the program: yes you fucking well did. i'm filing a bug report!!
the system: oh, wait. here it is now. i just realised, i forgot to do it earlier. LOL!

Name: TuneAFish 2007-10-31 6:58

>>51
If it was a bug, don't you think it would have been fixed by now? I refuse to believe 4chan are able to do anything significant in the programming community. The behaviour you experience with that particular function is well-known and expected, not a bug.

This thread has ended peacefully

.

Name: Anonymous 2007-10-31 7:02

The behaviour you experience with that particular function is well-known and expected, not a bug.

Citation?

Name: TuneAFish 2007-10-31 7:05

>>53
Why certainly.
http://www.google.com
The fact that it exists, after all these years, is proof enough. Do you mind if I proceed with saging this thread?

Name: Anonymous 2007-10-31 7:18

>>54
No, it is not proof. I have already searched to see if this is a common behaviour and came up with nothing. Therefore, I can only conclude that this is a bug unless shown otherwise.

Name: TuneAFish 2007-10-31 7:24

In this case, I, TuneAFish, will submit my findings and see what they respond with. I'll be sure to keep you posted, Anon.

Name: Anonymous 2007-10-31 8:16

I think it's a threading bug. Using the following program with a parameter of 1, it works fine. But anything greater than that and the file not found error occurs in os.chdir until only one thread is left to run completely.

# mkdirtest.py
#
# usage: mkdirtest <number of threads>

import os, threading, sys

x = int(sys.argv[1])

class MkdirTest(threading.Thread):

    def __init__(self, t):
        threading.Thread.__init__(self)
        self.t = t
        print "new thread "+str(t)

    def run(self):
        for i in range(0,50):
            s = str(self.t)+"_"+str(i)
            print "mkdir "+s
            os.mkdir(s)
            os.chdir(s)
            os.chdir("..")
        print "end thread "+str(t)

for t in range(0,x):
    print t
    a = MkdirTest(t)
    a.start()

Name: Anonymous 2007-10-31 11:15

This isn't a bug in Python. Working directory, which os.chdir modifies, is process-global. One of your threads makes a directory, then gets suspended while another one makes a different directory and changes into it, then the first tries to change into its directory and fails.

Name: Anonymous 2007-10-31 11:44

>>58 wins the thread
>>1-57 FAIL

Name: Anonymous 2007-10-31 13:29

Indeed. Always use absolute pa

Name: Anonymous 2007-10-31 20:41

pa-pa pa-pa pa-pa pa-pa pa-pa-pa, pa-pa pa-pa pa-pa-pa-paaaah pa!

Name: Anonymous 2007-10-31 21:00

absolute pa = absolute python absolute

Name: Anonymous 2007-10-31 22:15

|pa|

Name: Anonymous 2007-11-01 11:50

Shithausen

Name: Anonymous 2007-11-01 14:21

Now I'm getting errors like this all the time:

new connection from 202.75.56.212:58287
Exception in thread Thread-43:
Traceback (most recent call last):
  File "C:\Program Files\Python\lib\threading.py", line 460, in __bootstrap
    self.run()
  File "N:\vsss.py", line 73, in run
    line = self.getline()
  File "N:\vsss.py", line 93, in getline
    recvbuf = self.sock.recv(4096)
error: (10055, 'No buffer space available')


Odd, very odd. I have no idea why this is happening. It is only on the recv, not the send.

Any ideas?

Name: Anonymous 2007-11-01 15:24

>>65
File "C:\Program Files\Python\lib\threading.py", line 460, in __bootstrap
There's your problem.

Name: Anonymous 2007-11-01 17:02

>>66
What's wrong with threads?

Name: Computer Scientist 2007-11-01 17:18

>>67
HERE'S WHAT'S WRONG WITH THREADS!! *shoves a cudder down your throat*

Name: Anonymous 2007-11-01 19:33

>>68
GTFO. Now.

Name: Anonymous 2007-11-01 20:04

>>69
NO U

Name: Anonymous 2008-09-18 8:20

しゃぶるマイおしっこ

Name: Anonymous 2008-09-18 8:26

>>71
FOR FUCK'S SAKE, STOP TALKING IN JAPANESE FAG AND SUCK MY DICK

Name: Anonymous 2008-09-18 16:25

>>72
That's funny, because he said "Suck my wee-wee" in Japanese.

Name: Anonymous 2008-09-18 17:41

>>71
Horrible GRAMMAR FAILURE

Name: Anonymous 2010-11-14 21:04

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