Socket freezes while connecting (socket.connect())

Question:

I am attempting to create a subnet scanner in python. The first part of the code takes input from a user and finds the subnet based on the input. The second part takes the subnet and goes through all 255 hosts.

The problem is, the code freezes when it tries to scan a host that is down.

main.py

import os
import socket


def spread():
    ip_list = []
    lst = []
    new_ip = []
    ip = input("Enter any ip on your network: ")

    for ch in range(0, len(ip)):
        new_ip += ip[ch]
        if ip[ch] == ".":
            print(ip[ch])
            lst.append(ch)

    t = True
    while t == True:
        try:
            new_ip.pop(lst[2] + 1)
        except:
            t = False
            print(new_ip)
            break
        else:
            pass

    target = ""
    for char in new_ip:
        target += char
    print(target)

    #print(f"{ip} == {new_ip}")
    for i in range(1,255):
        print("socket initialized")
        from portscanner import scanner as scan
        for port in range(1,1025):
            try:

                scan(target + str(i))
                #HERE IS WHERE THE CODE FREEZES 
            except:
                continue
            else:
                pass

portscanner.py

def scanner(ip):
    for port in range(2, 1025):
        s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        socket.setdefaulttimeout(0.002)

        # returns an error indicator
        try:
            result = s.connect_ex((ip, port))
            #More specifically, here is where the code freezes
        except:
            continue
        else:
            pass
        if result == 0:
            print("Port {} is open".format(port))
        else:
            print("Port {} is CLOSED on ".format(port) + ip)
            continue
            #print(port)
        s.close()

My theory is that the code freezes because the host I am trying to connect to is down. How can I work around this and/or check to see if a host is up?

Asked By: Pork Lord

||

Answers:

First of all, no one would answer you in 2 ms (0.002 seconds). The correct way would be to use asyncio / multithreading / nonblocking select, and spread over the ports concurrently while waiting a little longer.

Second of all, as scanner doesn’t receive a port number, for every port between 1 and 1024 you scan every port between 2 and 1024. See the issue here?

Third of all, you set the default timeout for all sockets in every port scan. Why?

Fourth of all, you can use ipaddress.ip_address to find a network or at least use str.rindex to find the last byte and strip it off instead of the for and while loops.

Last of all, except: continue means you just ignore all exceptions, some of them might be relevant. I’d suggest printing such errors so you will be able to further debug the problem, or at least change the result to -1 so it’ll show it’s closed. It also prevents you from using ctrl+c to stop the program.

I’ve refactored your code a little, and it works:

from ipaddress import ip_network
import socket

def main():
    socket.setdefaulttimeout(0.4)
    network = ip_network(input("Enter an IPv4 network: "))
    for ip in network.hosts():
        scan_ip(ip)

def scan_ip(ip):
    for port in range(1, 1025):
        with socket.socket() as s:
            try:
                result = s.connect_ex((str(ip), port))
            except Exception as e:
                print(f"Error on {ip=!s}, {port=}: {e}")
            else:
                if result == 0:
                    print(f"Port {port} is open on {ip}")
                else:
                    print(f"Port {port} is CLOSED on {ip}")

Usage:

>>> main()
Enter an IPv4 network: 1.2.3.0/24
...

I leave the multithreading/asyncio for you to implement.

Answered By: Bharel

This fix was extremely simple. All I had to do was set the timeout of the socket. What this does is it waits a certain amount of seconds for a response. If that response is not received, the socket closes.

ip = 192.168.1.1
port = 80

import socket
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

# Waits for 5 seconds
s.settimeout(5)
# Waits for 10 seconds
s.settimeout(10)

try:
    s.connect(ip)
    #If it freezes here for more than 10 seconds,
except Exception as e:
    print(e)
else:
    print(f"{ip}:{port} is open!")
s.close()

Answered By: Pork Lord