Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Python for loop slows and evenutally hangs

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()
like image 516
Tom Avatar asked Apr 20 '11 22:04

Tom


People also ask

Why does my loop slow down Python?

Memory - if you're storing all of the generated values somewhere, the loop might be slowing down because the of all the memory being used. Python has it's own memory manager, so using up lots of memory could eventually make the program slower.

Can a Python while loop iterate forever?

Infinite While Loop in PythonInfinite while loop refers to a while loop where the while condition never becomes false. When a condition never becomes false, the program enters the loop and keeps repeating that same block of code over and over again, and the loop never ends.

What can I use instead of a for loop in Python?

Map() Function in Python The map() function is a replacement to a for a loop. It applies a function for each element of an iterable.

Why is my for loop taking so long?

Growing variables in a loop takes very long. Each time you increase the length of the variable, a million times here, you force MATLAB to first create a variable with the initial length+1, then copy the contents, then delete the old variable. That's probably what is taking your code so long.


3 Answers

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
like image 146
samplebias Avatar answered Oct 11 '22 17:10

samplebias


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.

like image 34
eat_a_lemon Avatar answered Oct 11 '22 18:10

eat_a_lemon


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.

like image 23
avuko Avatar answered Oct 11 '22 16:10

avuko