12

I'm making a simple multi-threaded port scanner. It scans all ports on host and returns open ports. The trouble is interrupting the scan. It take a lot of time for a scan to complete and sometimes I wish to kill program with C-c while in the middle of scan. Trouble is the scan won't stop. Main thread is locked on queue.join() and oblivious to KeyboardInterrupt, until all data from queue is processed thus deblocking main thread and exiting program gracefully. All my threads are daemonized so when main thread dies they should die with him.

I tried using signal lib, no success. Overriding threading.Thread class and adding method for graceful termination didn't work... Main thread just won't receive KeyboardInterrupt while executing queue.join()

import threading, sys, Queue, socket

queue = Queue.Queue()

def scan(host):
    while True:
        port = queue.get()

        if port > 999 and port % 1000 == 0:
            print port
        try:
            #sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 
            #sock.settimeout(2) #you need timeout or else it will try to connect forever! 
            #sock.connect((host, port))
            #----OR----
            sock = socket.create_connection((host, port), timeout = 2)

            sock.send('aaa')
            data = sock.recv(100)
            print "Port {} open, message: {}".format(port, data)
            sock.shutdown()
            sock.close()
            queue.task_done()
        except:
            queue.task_done()


def main(host):
    #populate queue
    for i in range(1, 65536):
        queue.put(i)
    #spawn worker threads
    for port in range(100):
        t = threading.Thread(target = scan, args = (host,))
        t.daemon = True
        t.start()

if __name__ == '__main__':
    host = ""

    #does input exist?
    try:
        host = sys.argv[1]
    except:
        print "No argument was recivied!"
        exit(1)

    #is input sane?
    try:
        host = socket.gethostbyname(host)
    except:
        print "Adress does not exist"
        exit(2)

    #execute main program and wait for scan to complete
    main(host)
    print "Post main() call!"
    try:
        queue.join()
    except KeyboardInterrupt:
        print "C-C"
        exit(3)

EDIT:

I have found a solution by using time module.

#execute main program and wait for scan to complete
main(host)

#a little trick. queue.join() makes main thread immune to keyboardinterrupt. So use queue.empty() with time.sleep()
#queue.empty() is "unreliable" so it may return True a bit earlier then intented.
#when queue is true, queue.join() is executed, to confirm that all data was processed.
#not a true solution, you can't interrupt main thread near the end of scan (when queue.empty() returns True)
try:
    while True:
        if queue.empty() == False:
            time.sleep(1)
        else:
            break
except KeyboardInterrupt:
    print "Alas poor port scanner..."
    exit(1)
queue.join()
RedSparrow
  • 307
  • 2
  • 3
  • 9
  • 2
    Does anyone know if there's a way to do this without a while True? This wastes a ton of CPU and I am using Thread from threading to avoid a while True. `thread.join` is so perfect if only I could freaking kill it (without a CPU time-wasting infinite loop). Any ideas? The test I will be running doesn't require that I kill it because the thread continues indefinitely (it would theoretically continue forever), however, for testing purposes and future reference (since it is a pain to do `pkill python` in another term. window every time), I would genuinely like to know. Thanks(and don't say ctrl+z) – dylnmc Oct 01 '14 at 13:53
  • 1
    If you're running this on Windows, this is "normal". The Python interpreter catches the CTRL-C and sets an internal flag, then waits for control to return to the Python interpret so that it can raise `KeyboardInterrupt`. It relies on blocking system calls to return `EINTR` in order to go back and check that internal flag. However, blocking system operations on Windows don't return with an `EINTR` error code when such a thing happens, so the `KeyboardInterrupt` exception is delayed until your blocking operation completes. – André Caron Jan 22 '15 at 16:33

2 Answers2

4

You made your threads daemons already, but you need to keep your main thread alive while daemon threads are there, there's how to do that: Cannot kill Python script with Ctrl-C

Community
  • 1
  • 1
solusipse
  • 589
  • 6
  • 21
  • 4
    My main thread is alive. queue.join() blocks thread until all data in queue is processed, then queue.join() deblocks main thread and program terminates normally. The problem is that when main thread reaches queue.join() it makes thread "immune" to KeyboardInterrupt. If I can't kill main thread, I can't kill daemon threads. I will clarify my question... – RedSparrow Aug 01 '13 at 11:43
  • 3
    Care to share the solution? – Rishi Jan 06 '14 at 10:22
0

When you create the threads add them to a list of running threads and when dealing with ctrl-C send a kill signal to each thread on the list. That way you are actively cleaning up rather than relying on it being done for you.

Steve Barnes
  • 27,618
  • 6
  • 63
  • 73