timeoutsocket module help

Steve Holden sholden at holdenweb.com
Wed Sep 18 12:32:59 EDT 2002


"Sheila King" <usenet at thinkspot.net> wrote ...
> OK, I'm doing DNS lookups in a script I've written, to determine whether
> certain IP addresses are listed in DNS BL lists (for blocking spammers).
>
> Have been using the code I've written for a long time now (months) without
> significant problems. However, someone has made the point to me, that my
> code should technically have a default timeout on the socket that tries to
> do the DNS lookup. And they are right, so I'm trying to modify my code.
>
> So, I'm using Tim O'Malley's timeoutsocket module, or trying to. But I am
> not having any success with this. In the following C&P from an interactive
> session, it took 6 or 7 seconds to return the socket error, which is the
> result I would expect in the case that the host name is not in the DNS
list
> (and in this case it is not, so this is a correct result). The problem is,
> that I set the default time out (I thought???) to 0.02 seconds, but the
> lookup went waaaayyyy past that and basically seemed to ignore the
> instruction.
>
> >>> import sys
> >>> import socket
> >>>
sys.path.append(r'j:\web\thinkspotwebsite\xthinkspot\emailfilter_test')
> >>> import timeoutsocket
> >>> timeoutsocket.setDefaultSocketTimeout(0.02)
> >>> socket.gethostbyname('226.200.3.4.bl.spamcop.net')
> Traceback (most recent call last):
>   File "<pyshell#5>", line 1, in ?
>     socket.gethostbyname('226.200.3.4.bl.spamcop.net')
> gaierror: (7, 'getaddrinfo failed')
> >>>
>
> Now, I will note, that the comments in the timeoutsocket module indicate
> that it works for TCP connections, and I'm not sure that TCP is being used
> in the gethostbyname function, as it is my understanding that DNS should
> usually be done by UDP instead. So, maybe in this case, using O'Malley's
> timeoutsocket module is totally inappropriate and won't work.
>
> In any case, I'm hoping to get some clues here.
>
You are correct in noting that the timeoutsocket module won't help if you
use UDP, which by default is used for DNS. I found it was actually quite
easy to effect my own timeouts and do my own DNS lookups -- in my case,
looking for MX records, but there's no reason why you shouldn't extend it to
others. The dnslib module is an immense help here.

Here's some sample code that uses it, and uses select.select() to apply a
fifteen-second timeout to the DNS transaction. I'm afraid the code is
missing a lot of context:

def MXlist(qname, server):
    """Return a list of MX exchangers for a given domain.

    This code is based on the dnslib library, and  is complicated
    by the necessity to handle UDP errors where no response is
    generated by (or at least received from) the remote host."""
    global mxdict       # Cacheing dictionary
    if mxdict.has_key(qname):
        lf.log("[[[ Domain %s MX hosts were cached" % (qname, ))
        return mxdict[qname]
    protocol = 'udp'    # This is bogus: not used anywhere!
    port = 53
    opcode = dnsopcode.QUERY
    rd = 1              # server should recurse
    qtype = dnstype.MX
    m = dnslib.Mpacker()
    m.addHeader(0, 0, opcode, 0, 0, rd, 0, 0, 0, 1, 0, 0, 0)
    m.addQuestion(qname, qtype, dnsclass.IN)
    request = m.getbuf()
    s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
    s.setblocking(0) # We need a non-blocking socket
    s.connect((server, port))
    r, w, e = select.select([s], [s], [s], 15)
    if [r, w, e] == [[], [], []]:
        lf.debug("!!! Connect timed out in MXlist")
        return []
    s.send(request)
    r, w, e = select.select([s], [s], [s], 15)
    if [r, w, e] == [[], [], []]:
        lf.debug("!!! Send timed out in MXlist")
        return []
    r,w,e = select.select([s], [], [], 15.0)
    if r:
        reply = s.recv(2048)
    else:
        s.close()
        raise Timeout("Recv timed out in MXlist")
    u = dnslib.Munpacker(reply)
    (id, qr, opcode, aa, tc, rd, ra, z, rcode,
        qdcount, ancount, nscount, arcount) = u.getHeader()
    for i in range(qdcount):
        qname, qtype, qclass = u.getQuestion()
    MX = []
    for i in range(ancount):
        name, type, klass, ttl, rdlength = u.getRRheader()
        MX.append([u.get16bit(), u.getname()])
    MX.sort()
    servers = []
    for mx in MX:
        servers.append(string.lower(mx[1]))
    s.close()
    mxdict[qname] = servers
    lf.log("[[[ %d candidate mail exchangers for %s: %s" %
                (len(servers), qname, string.join(servers, ", ")))
    return servers

Hope this is enough to get you going.

regards
-----------------------------------------------------------------------
Steve Holden                                  http://www.holdenweb.com/
Python Web Programming                 http://pydish.holdenweb.com/pwp/
Previous .sig file retired to                    www.homeforoldsigs.com
-----------------------------------------------------------------------






More information about the Python-list mailing list