Python for loop slows and evenutally hangs

回眸只為那壹抹淺笑 提交于 2019-12-23 10:56:37

问题


I'm totally new to Python (as of half an hour ago) and trying to write a simple script to enumerate users on an SMTP server.

The users file is a simple list (one per line) of usernames.

The script runs fine but with each iteration of the loop it slows until, around loop 14, it seems to hang completely. No error - I have to ^c.

Can anyone shed some light on the problem please?

TIA, Tom

#!/usr/bin/python

import socket
import sys

if len(sys.argv) != 2:
        print "Usage: vrfy.py <username file>"
        sys.exit(0)

#open user file
file=open(sys.argv[1], 'r')
users=[x.strip() for x in file.readlines()]
file.close

#Just for debugging
print users

# Create a Socket
s=socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# Connect to the Server
connect=s.connect(('192.168.13.222',25))

for x in users:
        # VRFY a user
        s.send('VRFY ' + x + '\r\n')
        result=s.recv(1024)
        print result

# Close the socket
s.close()

回答1:


Most likely your SMTP server is tarpitting your client connection. This is a defense against runaway clients, or clients which submit large volumes of "junk" commands. From the manpage for Postfix smtpd:

   smtpd_junk_command_limit (normal: 100, stress: 1)
          The number of junk commands (NOOP, VRFY, ETRN or  RSET)  that  a
          remote  SMTP  client  can  send  before  the Postfix SMTP server
          starts to increment the error counter with each junk command.

The smtpd daemon will insert a 1-second delay before replying after a certain amount of junk is seen. If you have root access to the smtp server in question, try an strace to see if nanosleep syscalls are being issued by the server.

Here is a trace from running your script against my local server. After 100 VRFY commands it starts sleeping between commands. Your server may have a lower limit of ~15 junk commands:

nanosleep({1, 0}, 0x7fffda9a67a0)       = 0
poll([{fd=9, events=POLLOUT}], 1, 300000) = 1 ([{fd=9, revents=POLLOUT}])
write(9, "252 2.0.0 pat\r\n", 15)       = 15
poll([{fd=9, events=POLLIN}], 1, 300000) = 1 ([{fd=9, revents=POLLIN}])
read(9, "VRFY pat\r\n", 4096)           = 10



回答2:


s.recv blocks so if you have no more data on the socket then it will block forever.
You have to keep track of how much data you are receiving. You need to know this ahead of time so the client and the server can agree on the size.




回答3:


Solving the exact same problem I also ran into the issue. I'm almost sure @samplebias is right. I found I could work around the "tarpitting" by abusing the poor system even more, tearing down and rebuilding every connection:

#[ ...Snip... ]
import smtplib
#[ ...Snip... ]
for USER in open(opts.USERS,'r'):
    smtpserver = smtplib.SMTP(HOST,PORT)
    smtpserver.ehlo()
    verifyuser = smtpserver.verify(USER)
    print("%s %s:  %s") % (HOST.rstrip(), USER.rstrip(), verifyuser)
    smtpserver.quit()

I'm curious whether this particular type of hammering would work in a live environment, but too certain it would make some people very unhappy.

PS, python: batteries included.




回答4:


In a glance, your code has no bugs. However, you shall notice that TCP isn't a "message" oriented protocol. So, you can't use socket.send in a loop assuming that one message will be actually sent through the medium at every call. Thus, if some calls starts to get buffered in the output buffer, and you just call socket.recv after it, your program will stuck in a deadlock.

What you should do is a threaded or asynchronous code. Maybe Twisted Framework may help you.



来源:https://stackoverflow.com/questions/5737336/python-for-loop-slows-and-evenutally-hangs

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!