Home > other >  Python socket.settimeout() not working when connecting to a domain that is down
Python socket.settimeout() not working when connecting to a domain that is down

Time:01-20

I need to check if a host is up or down quickly. For this, I added a socket timeout before making a connection, but the socket timeout doesn't seem to work. Why?

Here's my code:

import socket

def test_hostname(hostname, port):
    s = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
    s.settimeout(1.0) # timeout
    try:
        s.connect((hostname, port))
    except (socket.timeout, socket.gaierror):
        return False
    else:
        return True
    finally:
        s.close()

result = test_hostname("001.com",80) # 001.com is down

print(result)

I takes around 10-20 seconds instead of the specified timeout (1 second).

CodePudding user response:

This happens because the timeout for a Python socket does not limit the amount of time spent on DNS lookup. Since you are using a domain name that needs to be resolved to an IP, your s.connect((hostname, port)) will first need to make a DNS query for hostname, and only then it will be able to create a connection using one of the IPs returned by the DNS server (if any).

The domain 001.com does not seem to resolve to any IP address, so it can take several seconds for your system to return failure. When this happens, Python will bail out and the connection will fail, but the timeout will already be long expired.

If you know the IP address of the host you want to reach, use that instead of the hostname. If not, you will have to use a DNS resolution library to also time out DNS resolution, like for example dnspython:

import socket
import dns.resolver

def test_hostname(hostname, port):
    resolver = dns.resolver.Resolver()
    resolver.timeout = resolver.lifetime = 1

    try:
        answer = resolver.resolve(hostname, 'A')
    except dns.exception.Timeout:
        return False

    if not answer.rrset:
        return False

    addr = next(iter(answer.rrset)).to_text()

    s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    s.settimeout(1)

    try:
        s.connect((addr, port))
    except (socket.timeout, socket.gaierror):
        return False
    else:
        return True
    finally:
        s.close()
  •  Tags:  
  • Related