whois/whois.py
changeset 12 c57439b500cb
parent 6 7dee244ba3ef
child 32 5f851e9c196a
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/whois/whois.py	Tue Jan 08 18:23:18 2013 +1100
@@ -0,0 +1,273 @@
+"""
+Whois client for python
+
+transliteration of:
+http://www.opensource.apple.com/source/adv_cmds/adv_cmds-138.1/whois/whois.c
+
+Copyright (c) 2010 Chris Wolf
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
+
+  Last edited by:  $Author$
+              on:  $DateTime$
+        Revision:  $Revision$
+              Id:  $Id$
+          Author:  Chris Wolf
+"""
+import sys
+import socket
+import optparse
+#import pdb
+
+def enforce_ascii(a):
+    if isinstance(a, str) or isinstance(a, unicode):
+        # return a.encode('ascii', 'replace')
+        r = ""
+        for i in a:
+            if ord(i) >= 128:
+                r += "?"
+            else:
+                r += i
+        return r
+    else:
+        return a
+
+class NICClient(object) :
+
+    ABUSEHOST           = "whois.abuse.net"
+    NICHOST             = "whois.crsnic.net"
+    INICHOST            = "whois.networksolutions.com"
+    DNICHOST            = "whois.nic.mil"
+    GNICHOST            = "whois.nic.gov"
+    ANICHOST            = "whois.arin.net"
+    LNICHOST            = "whois.lacnic.net"
+    RNICHOST            = "whois.ripe.net"
+    PNICHOST            = "whois.apnic.net"
+    MNICHOST            = "whois.ra.net"
+    QNICHOST_TAIL       = ".whois-servers.net"
+    SNICHOST            = "whois.6bone.net"
+    BNICHOST            = "whois.registro.br"
+    NORIDHOST           = "whois.norid.no"
+    IANAHOST            = "whois.iana.org"
+    DENICHOST           = "de.whois-servers.net"
+    DEFAULT_PORT        = "nicname"
+    WHOIS_SERVER_ID     = "Whois Server:"
+    WHOIS_ORG_SERVER_ID = "Registrant Street1:Whois Server:"
+
+
+    WHOIS_RECURSE       = 0x01
+    WHOIS_QUICK         = 0x02
+
+    ip_whois = [ LNICHOST, RNICHOST, PNICHOST, BNICHOST ]
+
+    def __init__(self) :
+        self.use_qnichost = False
+       
+    def findwhois_server(self, buf, hostname):
+        """Search the initial TLD lookup results for the regional-specifc
+        whois server for getting contact details.
+        """
+        #print 'finding whois server'
+        #print 'parameters:', buf, 'hostname', hostname
+        nhost = None
+        parts_index = 1
+        start = buf.find(NICClient.WHOIS_SERVER_ID)
+        #print 'start', start
+        if (start == -1):
+            start = buf.find(NICClient.WHOIS_ORG_SERVER_ID)
+            parts_index = 2
+       
+        if (start > -1):  
+            end = buf[start:].find('\n')
+            #print 'end:', end
+            whois_line = buf[start:end+start]
+            #print 'whois_line', whois_line
+            nhost = whois_line.split(NICClient.WHOIS_SERVER_ID+' ').pop()
+            nhost = nhost.split('http://').pop()
+            #if the whois address is domain.tld/something then
+            #s.connect((hostname, 43)) does not work
+            if nhost.count('/') > 0:
+                nhost = None
+            #print 'nhost:',nhost
+        elif (hostname == NICClient.ANICHOST):
+            for nichost in NICClient.ip_whois:
+                if (buf.find(nichost) != -1):
+                    nhost = nichost
+                    break
+        return nhost
+       
+    def whois(self, query, hostname, flags):
+        """Perform initial lookup with TLD whois server
+        then, if the quick flag is false, search that result
+        for the region-specifc whois server and do a lookup
+        there for contact details
+        """
+        #print 'Performing the whois'
+        #print 'parameters given:', query, hostname, flags
+        #pdb.set_trace()
+        s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
+        s.connect((hostname, 43))
+        """send takes bytes as an input
+        """
+        queryBytes = None
+        if (hostname == NICClient.DENICHOST):
+            #print 'the domain is in NIC DENIC'
+            queryBytes = ("-T dn,ace -C UTF-8 " + query + "\r\n").encode()
+            #print 'queryBytes:', queryBytes
+        else:
+            queryBytes = (query  + "\r\n").encode()        
+        s.send(queryBytes)
+        """recv returns bytes
+        """
+        #print s
+        response = b''
+        while True:
+            d = s.recv(4096)
+            response += d
+            if not d:
+                break
+        s.close()
+        #pdb.set_trace()
+        nhost = None
+        #print 'response', response
+        response = enforce_ascii(response)
+        if (flags & NICClient.WHOIS_RECURSE and nhost == None):
+            #print 'Inside first if'
+            nhost = self.findwhois_server(response.decode(), hostname)
+            #print 'nhost is:', nhost
+        if (nhost != None):
+            #print 'inside second if'
+            response += self.whois(query, nhost, 0)
+            #print 'response', response
+        #print 'returning whois response'
+        return response.decode()
+   
+    def choose_server(self, domain):
+        """Choose initial lookup NIC host"""
+        if (domain.endswith("-NORID")):
+            return NICClient.NORIDHOST
+        pos = domain.rfind('.')
+        if (pos == -1):
+            return None
+        tld = domain[pos+1:]
+        if (tld[0].isdigit()):
+            return NICClient.ANICHOST
+   
+        return tld + NICClient.QNICHOST_TAIL
+   
+    def whois_lookup(self, options, query_arg, flags):
+        """Main entry point: Perform initial lookup on TLD whois server,
+        or other server to get region-specific whois server, then if quick
+        flag is false, perform a second lookup on the region-specific
+        server for contact records"""
+        #print 'whois_lookup'
+        nichost = None
+        #pdb.set_trace()
+        # this would be the case when this function is called by other than main
+        if (options == None):                    
+            options = {}
+     
+        if ( (not 'whoishost' in options or options['whoishost'] == None)
+            and (not 'country' in options or options['country'] == None)):
+            self.use_qnichost = True
+            options['whoishost'] = NICClient.NICHOST
+            if ( not (flags & NICClient.WHOIS_QUICK)):
+                flags |= NICClient.WHOIS_RECURSE
+           
+        if ('country' in options and options['country'] != None):
+            result = self.whois(query_arg, options['country'] + NICClient.QNICHOST_TAIL, flags)
+        elif (self.use_qnichost):
+            nichost = self.choose_server(query_arg)
+            if (nichost != None):
+                result = self.whois(query_arg, nichost, flags)
+        else:
+            result = self.whois(query_arg, options['whoishost'], flags)
+        #print 'whois_lookup finished'
+        return result
+#---- END OF NICClient class def ---------------------
+   
+def parse_command_line(argv):
+    """Options handling mostly follows the UNIX whois(1) man page, except
+    long-form options can also be used.
+    """
+    flags = 0
+   
+    usage = "usage: %prog [options] name"
+           
+    parser = optparse.OptionParser(add_help_option=False, usage=usage)
+    parser.add_option("-a", "--arin", action="store_const",
+                      const=NICClient.ANICHOST, dest="whoishost",
+                      help="Lookup using host " + NICClient.ANICHOST)
+    parser.add_option("-A", "--apnic", action="store_const",
+                      const=NICClient.PNICHOST, dest="whoishost",
+                      help="Lookup using host " + NICClient.PNICHOST)
+    parser.add_option("-b", "--abuse", action="store_const",
+                      const=NICClient.ABUSEHOST, dest="whoishost",
+                      help="Lookup using host " + NICClient.ABUSEHOST)
+    parser.add_option("-c", "--country", action="store",
+                      type="string", dest="country",
+                      help="Lookup using country-specific NIC")
+    parser.add_option("-d", "--mil", action="store_const",
+                      const=NICClient.DNICHOST, dest="whoishost",
+                      help="Lookup using host " + NICClient.DNICHOST)
+    parser.add_option("-g", "--gov", action="store_const",
+                      const=NICClient.GNICHOST, dest="whoishost",
+                      help="Lookup using host " + NICClient.GNICHOST)
+    parser.add_option("-h", "--host", action="store",
+                      type="string", dest="whoishost",
+                       help="Lookup using specified whois host")
+    parser.add_option("-i", "--nws", action="store_const",
+                      const=NICClient.INICHOST, dest="whoishost",
+                      help="Lookup using host " + NICClient.INICHOST)
+    parser.add_option("-I", "--iana", action="store_const",
+                      const=NICClient.IANAHOST, dest="whoishost",
+                      help="Lookup using host " + NICClient.IANAHOST)
+    parser.add_option("-l", "--lcanic", action="store_const",
+                      const=NICClient.LNICHOST, dest="whoishost",
+                      help="Lookup using host " + NICClient.LNICHOST)
+    parser.add_option("-m", "--ra", action="store_const",
+                      const=NICClient.MNICHOST, dest="whoishost",
+                      help="Lookup using host " + NICClient.MNICHOST)
+    parser.add_option("-p", "--port", action="store",
+                      type="int", dest="port",
+                      help="Lookup using specified tcp port")
+    parser.add_option("-Q", "--quick", action="store_true",
+                     dest="b_quicklookup",
+                     help="Perform quick lookup")
+    parser.add_option("-r", "--ripe", action="store_const",
+                      const=NICClient.RNICHOST, dest="whoishost",
+                      help="Lookup using host " + NICClient.RNICHOST)
+    parser.add_option("-R", "--ru", action="store_const",
+                      const="ru", dest="country",
+                      help="Lookup Russian NIC")
+    parser.add_option("-6", "--6bone", action="store_const",
+                      const=NICClient.SNICHOST, dest="whoishost",
+                      help="Lookup using host " + NICClient.SNICHOST)
+    parser.add_option("-?", "--help", action="help")
+
+       
+    return parser.parse_args(argv)
+   
+if __name__ == "__main__":
+    flags = 0
+    nic_client = NICClient()
+    (options, args) = parse_command_line(sys.argv)
+    if (options.b_quicklookup is True):
+        flags = flags|NICClient.WHOIS_QUICK
+    print(nic_client.whois_lookup(options.__dict__, args[1], flags))