modules/rwhois.py
changeset 0 93b25987d3e5
child 18 3a35dd9adc73
equal deleted inserted replaced
-1:000000000000 0:93b25987d3e5
       
     1 #! /usr/bin/python --
       
     2 
       
     3 """
       
     4 usage: %(progname)s [domain...]
       
     5 
       
     6 Version: %(version)s
       
     7 
       
     8 Contacts the apropriate whois database for each domain and displays
       
     9 the result.
       
    10 
       
    11 class WhoisRecord:
       
    12 	self.domain			 -- Domain Name
       
    13 	self.whoisserver		-- Whoisserver associated with domain
       
    14 	self.page			   -- raw whois record data
       
    15 
       
    16 	public methods:
       
    17 		
       
    18 		def WhoisRecord(domain=None)
       
    19 			Whois object constructor
       
    20 			
       
    21 		def whois(domainname=None, server=None, cache=0)
       
    22 			Fetches whoisrecord and places result in self.page
       
    23 			Raises NoSuchDomain if the domain doesn't exist.
       
    24 		
       
    25 		
       
    26 class DomainRecord(WhoisRecord):
       
    27 	self.domainid		   -- domainid for this domain
       
    28 	self.created			-- date in which the domain was created
       
    29 	self.lastupdated		-- date in which the domain was last updated.
       
    30 	self.expires			-- date in which the domain expires
       
    31 	self.databaseupdated	-- date in which the database was last updated.
       
    32 	self.servers			-- list of (hostname, ip) pairs of the nameservers. 
       
    33 	self.registrant		 -- ContactRecord of domain owner. 
       
    34 	self.contacts		   -- dictionary of contacts (ContactRecord objects)
       
    35 
       
    36 	public methods:
       
    37 		
       
    38 	def DomainRecord(domain=None)
       
    39 		Constructor for DomainRecord
       
    40 		
       
    41 	def Parse()
       
    42 		Creates a parsed version of the information contained in 
       
    43 		the whois record for domain from self.page
       
    44 		raises NoParser if a parser does not exist for a registry.
       
    45 
       
    46 
       
    47 	
       
    48 class Contact:
       
    49 	self.type			   -- Type of contact
       
    50 	self.organization	   -- Organization associated with contact.
       
    51 	self.person			 -- Person associated with contact.
       
    52 	self.handle			 -- NIC Handle
       
    53 	self.address			-- Street address of contact
       
    54 	self.email			  -- Email address of contact
       
    55 	self.phone			  -- Phone Number
       
    56 	self.fax				-- Fax Number
       
    57 	self.lastupdated		-- Last update of contact record
       
    58 """
       
    59 
       
    60 _version = "1.1"
       
    61 
       
    62 import os, sys, string, time, getopt, socket, select, re, errno, copy, signal
       
    63 
       
    64 
       
    65 timeout=5
       
    66 
       
    67 class WhoisRecord:	  
       
    68 	
       
    69 	defaultserver='whois.networksolutions.com'
       
    70 	whoismap={ 'com' : 'whois.internic.net' , \
       
    71 			   'org' : 'whois.internic.net' , \
       
    72 			   'net' : 'whois.internic.net' , \
       
    73 			   'edu' : 'whois.networksolutions.com' , \
       
    74 			   'de'  : 'whois.denic.de' , \
       
    75 			   'gov' : 'whois.nic.gov' , \
       
    76 			   # See http://www.nic.gov/cgi-bin/whois 
       
    77 			   'mil' : 'whois.nic.mil' , \
       
    78 			   # See http://www.nic.mil/cgi-bin/whois
       
    79 			   'ca'  : 'whois.cdnnet.ca' , \
       
    80 			   'uk'  : 'whois.nic.uk' , \
       
    81 			   'au'  : 'whois.aunic.net' , \
       
    82 			   'hu'  : 'whois.nic.hu' , \
       
    83 			   
       
    84 			   # All the following are unverified/checked. 
       
    85 			   'be'  : 'whois.ripe.net',
       
    86 			   'it'  : 'whois.ripe.net' , \
       
    87 			   # also whois.nic.it
       
    88 			   'at'  : 'whois.ripe.net' , \
       
    89 			   # also www.nic.at, whois.aco.net
       
    90 			   'dk'  : 'whois.ripe.net' , \
       
    91 			   'fo'  : 'whois.ripe.net' , \
       
    92 			   'lt'  : 'whois.ripe.net' , \
       
    93 			   'no'  : 'whois.ripe.net' , \
       
    94 			   'sj'  : 'whois.ripe.net' , \
       
    95 			   'sk'  : 'whois.ripe.net' , \
       
    96 			   'tr'  : 'whois.ripe.net' , \
       
    97 			   # also whois.metu.edu.tr
       
    98 			   'il'  : 'whois.ripe.net' , \
       
    99 			   'bv'  : 'whois.ripe.net' , \
       
   100 			   'se'  : 'whois.nic-se.se' , \
       
   101 			   'br'  : 'whois.nic.br' , \
       
   102 			   # a.k.a. whois.fapesp.br?
       
   103 			   'fr'  : 'whois.nic.fr' , \
       
   104 			   'sg'  : 'whois.nic.net.sg' , \
       
   105 			   'hm'  : 'whois.registry.hm' , \
       
   106 			   # see also whois.nic.hm
       
   107 			   'nz'  : 'domainz.waikato.ac.nz' , \
       
   108 			   'nl'  : 'whois.domain-registry.nl' , \
       
   109 			   # RIPE also handles other countries
       
   110 			   # See  http://www.ripe.net/info/ncc/rir-areas.html
       
   111 			   'ru'  : 'whois.ripn.net' , \
       
   112 			   'ch'  : 'whois.nic.ch' , \
       
   113 			   # see http://www.nic.ch/whois_readme.html
       
   114 			   'jp'  : 'whois.nic.ad.jp' , \
       
   115 			   # (use DOM foo.jp/e for english; need to lookup !handles separately)
       
   116 			   'to'  : 'whois.tonic.to' , \
       
   117 			   'nu'  : 'whois.nic.nu' , \
       
   118 			   'fm'  : 'www.dot.fm' , \
       
   119 			   # http request http://www.dot.fm/search.html
       
   120 			   'am'  : 'whois.nic.am' , \
       
   121 			   'nu'  : 'www.nunames.nu' , \
       
   122 			   # http request
       
   123 			   # e.g. http://www.nunames.nu/cgi-bin/drill.cfm?domainname=nunames.nu
       
   124 			   #'cx'  : 'whois.nic.cx' , \		# no response from this server
       
   125 			   'af'  : 'whois.nic.af' , \
       
   126 			   'as'  : 'whois.nic.as' , \
       
   127 			   'li'  : 'whois.nic.li' , \
       
   128 			   'lk'  : 'whois.nic.lk' , \
       
   129 			   'mx'  : 'whois.nic.mx' , \
       
   130 			   'pw'  : 'whois.nic.pw' , \
       
   131 			   'sh'  : 'whois.nic.sh' , \
       
   132 			   # consistently resets connection
       
   133 			   'tj'  : 'whois.nic.tj' , \
       
   134 			   'tm'  : 'whois.nic.tm' , \
       
   135 			   'pt'  : 'whois.dns.pt' , \
       
   136 			   'kr'  : 'whois.nic.or.kr' , \
       
   137 			   # see also whois.krnic.net
       
   138 			   'kz'  : 'whois.nic.or.kr' , \
       
   139 			   # see also whois.krnic.net
       
   140 			   'al'  : 'whois.ripe.net' , \
       
   141 			   'az'  : 'whois.ripe.net' , \
       
   142 			   'ba'  : 'whois.ripe.net' , \
       
   143 			   'bg'  : 'whois.ripe.net' , \
       
   144 			   'by'  : 'whois.ripe.net' , \
       
   145 			   'cy'  : 'whois.ripe.net' , \
       
   146 			   'cz'  : 'whois.ripe.net' , \
       
   147 			   'dz'  : 'whois.ripe.net' , \
       
   148 			   'ee'  : 'whois.ripe.net' , \
       
   149 			   'eg'  : 'whois.ripe.net' , \
       
   150 			   'es'  : 'whois.ripe.net' , \
       
   151 			   'fi'  : 'whois.ripe.net' , \
       
   152 			   'gr'  : 'whois.ripe.net' , \
       
   153 			   'hr'  : 'whois.ripe.net' , \
       
   154 			   'lu'  : 'whois.ripe.net' , \
       
   155 			   'lv'  : 'whois.ripe.net' , \
       
   156 			   'ma'  : 'whois.ripe.net' , \
       
   157 			   'md'  : 'whois.ripe.net' , \
       
   158 			   'mk'  : 'whois.ripe.net' , \
       
   159 			   'mt'  : 'whois.ripe.net' , \
       
   160 			   'pl'  : 'whois.ripe.net' , \
       
   161 			   'ro'  : 'whois.ripe.net' , \
       
   162 			   'si'  : 'whois.ripe.net' , \
       
   163 			   'sm'  : 'whois.ripe.net' , \
       
   164 			   'su'  : 'whois.ripe.net' , \
       
   165 			   'tn'  : 'whois.ripe.net' , \
       
   166 			   'ua'  : 'whois.ripe.net' , \
       
   167 			   'va'  : 'whois.ripe.net' , \
       
   168 			   'yu'  : 'whois.ripe.net' , \
       
   169 			   # unchecked
       
   170 			   'ac'  : 'whois.nic.ac' , \
       
   171 			   'cc'  : 'whois.nic.cc' , \
       
   172 			   #'cn'  : 'whois.cnnic.cn' , \		# connection refused
       
   173 			   'gs'  : 'whois.adamsnames.tc' , \
       
   174 			   'hk'  : 'whois.apnic.net' , \
       
   175 			   #'ie'  : 'whois.ucd.ie' , \		# connection refused
       
   176 			   #'is'  : 'whois.isnet.is' , \# connection refused
       
   177 			   #'mm'  : 'whois.nic.mm' , \		# connection refused
       
   178 			   'ms'  : 'whois.adamsnames.tc' , \
       
   179 			   'my'  : 'whois.mynic.net' , \
       
   180 			   #'pe'  : 'whois.rcp.net.pe' , \		# connection refused
       
   181 			   'st'  : 'whois.nic.st' , \
       
   182 			   'tc'  : 'whois.adamsnames.tc' , \
       
   183 			   'tf'  : 'whois.adamsnames.tc' , \
       
   184 			   'th'  : 'whois.thnic.net' , \
       
   185 			   'tw'  : 'whois.twnic.net' , \
       
   186 			   'us'  : 'whois.isi.edu' , \
       
   187 			   'vg'  : 'whois.adamsnames.tc' , \
       
   188 			   #'za'  : 'whois.co.za'		# connection refused
       
   189 			   }
       
   190 
       
   191 
       
   192 
       
   193 	def __init__(self,domain=None):
       
   194 		self.domain=domain
       
   195 		self.whoisserver=None		
       
   196 		self.page=None
       
   197 		return
       
   198 	
       
   199 	def whois(self,domain=None, server=None, cache=0):
       
   200 		if domain is not None:
       
   201 			self.domain=domain
       
   202 			pass
       
   203 		
       
   204 		if server is not None:
       
   205 			self.whoisserver=server
       
   206 			pass
       
   207 		
       
   208 		if self.domain is None:
       
   209 			print "No Domain"
       
   210 			raise "No Domain"
       
   211 
       
   212 		if self.whoisserver is None:
       
   213 			self.chooseserver()
       
   214 
       
   215 		if self.whoisserver is None:
       
   216 			print "No Server"
       
   217 			raise "No Server"
       
   218 		
       
   219 		if cache:
       
   220 			fn = "%s.dom" % domainname
       
   221 			if os.path.exists(fn):
       
   222 				return open(fn).read()
       
   223 			pass
       
   224 		
       
   225 		self.page=self._whois()		
       
   226 		
       
   227 		if cache:
       
   228 			open(fn, "w").write(page)
       
   229 			pass
       
   230 		
       
   231 		return
       
   232 	
       
   233 	def chooseserver(self):
       
   234 		try:
       
   235 			(secondlevel,toplevel)=string.split(self.domain,'.')
       
   236 			self.whoisserver=WhoisRecord.whoismap.get(toplevel)
       
   237 			if self.whoisserver==None:
       
   238 				self.whoisserver=WhoisRecord.defaultserver
       
   239 				return
       
   240 			pass
       
   241 		except:
       
   242 			self.whoisserver=WhoisRecord.defaultserver
       
   243 			return
       
   244 		
       
   245 		if(toplevel=='com' or toplevel=='org' or toplevel=='net'):
       
   246 			tmp=self._whois()
       
   247 			m=re.search("Whois Server:(.+)",tmp)
       
   248 			if m:
       
   249 				self.whoisserver=string.strip(m.group(1))
       
   250 				return
       
   251 			self.whoisserver='whois.networksolutions.com'
       
   252 			tmp=self._whois()
       
   253 			m=re.search("Whois Server:(.+)",tmp)
       
   254 			if m:
       
   255 				self.whoisserver=string.strip(m.group(1))
       
   256 				return
       
   257 			pass
       
   258 		return
       
   259 	
       
   260 
       
   261 	def _whois(self):
       
   262 		def alrmhandler(signum,frame):
       
   263 			raise "TimedOut", "on connect"
       
   264 		
       
   265 		s = None
       
   266 		
       
   267 		## try until we timeout
       
   268 		s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
       
   269 		s.setblocking(1)
       
   270 		#signal.signal(signal.SIGALRM,alrmhandler)
       
   271 		#signal.alarm(timeout)
       
   272 		while 1:		
       
   273 			try:
       
   274 				s.connect((self.whoisserver, 43))
       
   275 			except socket.error, (ecode, reason):
       
   276 				if ecode==errno.EINPROGRESS: 
       
   277 					continue
       
   278 				elif ecode==errno.EALREADY:
       
   279 					continue
       
   280 				else:
       
   281 					raise socket.error, (ecode, reason)
       
   282 				pass
       
   283 			
       
   284 			break
       
   285 		
       
   286 		#signal.alarm(0)
       
   287 		
       
   288 		ret = select.select ([s], [s], [], 30)
       
   289 		
       
   290 		if len(ret[1])== 0 and len(ret[0]) == 0:
       
   291 			s.close()
       
   292 			raise TimedOut, "on data"
       
   293 		
       
   294 		s.setblocking(1)
       
   295 		
       
   296 		s.send("%s\n" % self.domain)
       
   297 		page = ""
       
   298 		while 1:
       
   299 			data = s.recv(8196)
       
   300 			if not data: break
       
   301 			page = page + data
       
   302 			pass
       
   303 		
       
   304 		s.close()
       
   305 		
       
   306 		
       
   307 		if string.find(page, "No match for") != -1:
       
   308 			raise 'NoSuchDomain', self.domain
       
   309 
       
   310 		if string.find(page, "No entries found") != -1:
       
   311 			raise 'NoSuchDomain', self.domain
       
   312 
       
   313 		if string.find(page, "no domain specified") != -1:
       
   314 			raise 'NoSuchDomain', self.domain
       
   315 
       
   316 		if string.find(page, "NO MATCH:") != -1:
       
   317 			raise 'NoSuchDomain', self.domain
       
   318 		
       
   319 		return page
       
   320 
       
   321 ##
       
   322 ## ----------------------------------------------------------------------
       
   323 ##
       
   324 class ContactRecord:
       
   325 	def __init__(self):
       
   326 		self.type=None
       
   327 		self.organization=None
       
   328 		self.person=None
       
   329 		self.handle=None
       
   330 		self.address=None
       
   331 		self.email=None
       
   332 		self.phone=None
       
   333 		self.fax=None
       
   334 		self.lastupdated=None
       
   335 		return
       
   336 	def __str__(self):
       
   337 		return "Type: %s\nOrganization: %s\nPerson: %s\nHandle: %s\nAddress: %s\nEmail: %s\nPhone: %s\nFax: %s\nLastupdate: %s\n" % (self.type,self.organization,self.person,self.handle,self.address,self.email,self.phone,self.fax,self.lastupdated)
       
   338 	
       
   339 
       
   340 class DomainRecord(WhoisRecord):
       
   341 	
       
   342 	parsemap={ 'whois.networksolutions.com' : 'ParseWhois_NetworkSolutions' , \
       
   343 			   'whois.register.com'		 : 'ParseWhois_RegisterCOM' }
       
   344 			   
       
   345 	def __init__(self,domain=None):
       
   346 		WhoisRecord.__init__(self,domain)
       
   347 		self.domainid = None
       
   348 		self.created = None
       
   349 		self.lastupdated = None
       
   350 		self.expires = None
       
   351 		self.databaseupdated = None
       
   352 		self.servers = None
       
   353 		self.registrant = ContactRecord()
       
   354 		self.registrant.type='registrant'
       
   355 		self.contacts = {}
       
   356 		return
       
   357 	def __str__(self):
       
   358 		con=''
       
   359 		for (k,v) in self.contacts.items():
       
   360 			con=con + str(v) +'\n'
       
   361 		return "%s (%s):\nWhoisServer: %s\nCreated : %s\nLastupdated : %s\nDatabaseupdated : %s\nExpires : %s\nServers : %s\nRegistrant >>\n\n%s\nContacts >>\n\n%s\n" % (self.domain, self.domainid,self.whoisserver,self.created, self.lastupdated, self.databaseupdated, self.expires,self.servers, self.registrant, con)
       
   362 
       
   363 	def Parse(self):
       
   364 		self._ParseWhois()
       
   365 		return
       
   366 	
       
   367 	def _ParseWhois(self):
       
   368 		parser=DomainRecord.parsemap.get(self.whoisserver)
       
   369 		if parser==None:
       
   370 			raise 'NoParser'
       
   371 		parser='self.'+parser+'()'
       
   372 		eval(parser)
       
   373 		return
       
   374 	
       
   375 	##
       
   376 	## ----------------------------------------------------------------------
       
   377 	##
       
   378 	def _ParseContacts_RegisterCOM(self,page):
       
   379 		
       
   380 		parts = re.split("((?:(?:Administrative|Billing|Technical|Zone) Contact,?[ ]*)+:)\n", page)
       
   381 
       
   382 		contacttypes = None
       
   383 		for part in parts:
       
   384 			if string.find(part, "Contact:") != -1:
       
   385 				if part[-1] == ":": part = part[:-1]
       
   386 				contacttypes = string.split(part, ",")
       
   387 				continue
       
   388 			part = string.strip(part)
       
   389 			if not part: continue
       
   390 			
       
   391 			contact=ContactRecord()
       
   392 			
       
   393 			m = re.search("Email: (.+@.+)", part)
       
   394 			if m:
       
   395 				contact.email=string.lower(string.strip(m.group(1)))
       
   396 				
       
   397 			m = re.search("\s+Phone: (.+)", part)
       
   398 			if m:
       
   399 				contact.phone=m.group(1)
       
   400 				end=m.start(0)
       
   401 				
       
   402 			start=0
       
   403 		
       
   404 			lines = string.split(part[start:end], "\n")
       
   405 			lines = map(string.strip,lines)
       
   406 			
       
   407 			contact.organization = lines.pop(0)
       
   408 			contact.person = lines.pop(0)
       
   409 		
       
   410 			contact.address=string.join(lines,'\n')
       
   411 		
       
   412 			for contacttype in contacttypes:
       
   413 				contacttype = string.lower(string.strip(contacttype))
       
   414 				contacttype = string.replace(contacttype, " contact", "")
       
   415 				contact.type=contacttype
       
   416 				self.contacts[contacttype] = copy.copy(contact)
       
   417 				pass
       
   418 			pass
       
   419 		
       
   420 		return
       
   421 
       
   422 
       
   423 	def ParseWhois_RegisterCOM(self):
       
   424 		m = re.search("Record last updated on.*: (.+)", self.page)
       
   425 		if m: self.lastupdated = m.group(1)
       
   426 	
       
   427 		m = re.search("Created on.*: (.+)", self.page)
       
   428 		if m: self.created = m.group(1)
       
   429 	
       
   430 		m = re.search("Expires on.*: (.+)", self.page)
       
   431 		if m: self.expires = m.group(1)
       
   432 	
       
   433 		m = re.search("Phone: (.+)", self.page)
       
   434 		if m: self.registrant.phone=m.group(1)
       
   435 	
       
   436 		m = re.search("Email: (.+@.+)",self.page)
       
   437 		if m: self.registrant.email=m.group(1)
       
   438 	
       
   439 		m = re.search("Organization:(.+?)Phone:",self.page,re.S)
       
   440 		if m: 
       
   441 			start=m.start(1)
       
   442 			end=m.end(1)
       
   443 			registrant = string.strip(self.page[start:end])
       
   444 			registrant = string.split(registrant, "\n")
       
   445 			registrant = map(string.strip,registrant)
       
   446 			
       
   447 			self.registrant.organization = registrant[0]
       
   448 			self.registrant.person =registrant[1]
       
   449 			self.registrant.address = string.join(registrant[2:], "\n")
       
   450 			pass
       
   451 		
       
   452 		m = re.search("Domain servers in listed order:\n\n(.+?)\n\n", self.page, re.S)
       
   453 		if m:
       
   454 			start = m.start(1)
       
   455 			end = m.end(1)
       
   456 			servers = string.strip(self.page[start:end])
       
   457 			lines = string.split(servers, "\n")
       
   458 			
       
   459 			
       
   460 			self.servers = []
       
   461 			for line in lines:
       
   462 				m=re.search("(\w|\.)+?\s*(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})",string.strip(line))
       
   463 				if m:
       
   464 					self.servers.append(m.group(1), m.group(2))
       
   465 					pass
       
   466 				pass
       
   467 			pass
       
   468 
       
   469 		m = re.search("((?:(?:Administrative|Billing|Technical|Zone) Contact,?[ ]*)+:)\n", self.page)
       
   470 		if m:
       
   471 			i = m.start()
       
   472 			m = re.search("Domain servers in listed order", self.page)
       
   473 			j = m.start()
       
   474 			contacts = string.strip(self.page[i:j])
       
   475 			pass
       
   476 		self._ParseContacts_RegisterCOM(contacts)
       
   477 		return
       
   478 
       
   479 	
       
   480 	def _ParseContacts_NetworkSolutions(self,page):
       
   481 		
       
   482 		parts = re.split("((?:(?:Administrative|Billing|Technical|Zone) Contact,?[ ]*)+:)\n", page)
       
   483 	
       
   484 		contacttypes = None
       
   485 		for part in parts:
       
   486 			if string.find(part, "Contact:") != -1:
       
   487 				if part[-1] == ":": part = part[:-1]
       
   488 				contacttypes = string.split(part, ",")
       
   489 				continue
       
   490 			part = string.strip(part)
       
   491 			if not part: continue
       
   492 	  
       
   493 			record=ContactRecord()
       
   494 		
       
   495 			lines = string.split(part, "\n")
       
   496 			m = re.search("(.+) \((.+)\) (.+@.+)", lines.pop(0))
       
   497 			if m:
       
   498 				record.person = string.strip(m.group(1))
       
   499 				record.handle = string.strip(m.group(2))
       
   500 				record.email = string.lower(string.strip(m.group(3)))
       
   501 				pass
       
   502 		
       
   503 			record.organization=string.strip(lines.pop(0))
       
   504 		
       
   505 			flag = 0
       
   506 			addresslines = []
       
   507 			phonelines = []
       
   508 			phonelines.append(string.strip(lines.pop()))
       
   509 			for line in lines:
       
   510 				line = string.strip(line)
       
   511 				#m=re.search("^(\d|-|\+|\s)+$",line)
       
   512 				#if m: flag = 1
       
   513 				if flag == 0:
       
   514 					addresslines.append(line)
       
   515 				else:
       
   516 					phonelines.append(line)
       
   517 					pass
       
   518 				pass
       
   519 			record.phone = string.join(phonelines, "\n")
       
   520 			record.address = string.join(addresslines, "\n")
       
   521 			
       
   522 			for contacttype in contacttypes:
       
   523 				contacttype = string.lower(string.strip(contacttype))
       
   524 				contacttype = string.replace(contacttype, " contact", "")
       
   525 				record.type=contacttype
       
   526 				self.contacts.update({contacttype:copy.copy(record)})
       
   527 				pass
       
   528 			pass
       
   529 		return
       
   530 		
       
   531 	def ParseWhois_NetworkSolutions(self):
       
   532 		 
       
   533 		m = re.search("Record last updated on (.+)\.", self.page)
       
   534 		if m: self.lastupdated = m.group(1)
       
   535 		
       
   536 		m = re.search("Record created on (.+)\.", self.page)
       
   537 		if m: self.created = m.group(1)
       
   538 		
       
   539 		m = re.search("Database last updated on (.+)\.", self.page)
       
   540 		if m: self.databaseupdated = m.group(1)
       
   541 		
       
   542 		m = re.search("Record expires on (.+)\.",self.page)
       
   543 		if m: self.expires=m.group(1)
       
   544 		
       
   545 		m = re.search("Registrant:(.+?)\n\n", self.page, re.S)
       
   546 		if m: 
       
   547 			start= m.start(1)
       
   548 			end = m.end(1)
       
   549 			reg = string.strip(self.page[start:end])
       
   550 			
       
   551 			reg = string.split(reg, "\n")
       
   552 			reg = map(string.strip,reg)
       
   553 			self.registrant.organization = reg[0]
       
   554 			self.registrant.address = string.join(reg[1:],'\n')
       
   555 			
       
   556 			m = re.search("(.+) \((.+)\)", self.registrant.organization)
       
   557 			if m: 
       
   558 				self.domainid   = m.group(2)
       
   559 				pass
       
   560 			pass
       
   561 		
       
   562 		m = re.search("Domain servers in listed order:\n\n", self.page)
       
   563 		if m:
       
   564 			i = m.end()
       
   565 			m = re.search("\n\n", self.page[i:])
       
   566 			j = m.start()
       
   567 			servers = string.strip(self.page[i:i+j])
       
   568 			lines = string.split(servers, "\n")
       
   569 			self.servers = []
       
   570 			for line in lines:
       
   571 				m=re.search("(\w|\.)+?\s*(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})",string.strip(line))
       
   572 				if m:
       
   573 					self.servers.append(m.group(1), m.group(2))
       
   574 					pass
       
   575 				pass
       
   576 			pass
       
   577 		
       
   578 		m = re.search("((?:(?:Administrative|Billing|Technical|Zone) Contact,?[ ]*)+:)\n", self.page)
       
   579 		if m:
       
   580 			i = m.start()
       
   581 			m = re.search("Record last updated on", self.page)
       
   582 			j = m.start()
       
   583 			contacts = string.strip(self.page[i:j])
       
   584 			pass
       
   585 		self._ParseContacts_NetworkSolutions(contacts)
       
   586 		
       
   587 		return
       
   588 
       
   589 		
       
   590 ##
       
   591 ## ----------------------------------------------------------------------
       
   592 ##
       
   593 		   
       
   594 
       
   595 
       
   596 
       
   597 ##
       
   598 ## ----------------------------------------------------------------------
       
   599 ##
       
   600 
       
   601 def usage(progname):
       
   602 	version = _version
       
   603 	print __doc__ % vars()
       
   604 
       
   605 def main(argv, stdout, environ):
       
   606 	progname = argv[0]
       
   607 	list, args = getopt.getopt(argv[1:], "", ["help", "version"])
       
   608 	
       
   609 	for (field, val) in list:
       
   610 		if field == "--help":
       
   611 			usage(progname)
       
   612 			return
       
   613 		elif field == "--version":
       
   614 			print progname, _version
       
   615 			return
       
   616 	
       
   617 	rec=WhoisRecord();
       
   618 	
       
   619 	for domain in args:
       
   620 		whoisserver=None
       
   621 		if string.find(domain,'@')!=-1:
       
   622 			(domain,whoisserver)=string.split(domain,'@')
       
   623 		try:
       
   624 			rec.whois(domain,whoisserver)
       
   625 			print rec.page
       
   626 		except 'NoSuchDomain', reason:
       
   627 			print "ERROR: no such domain %s" % domain
       
   628 		except socket.error, (ecode,reason):
       
   629 			print reason
       
   630 		except "TimedOut", reason:
       
   631 			print "Timed out", reason
       
   632 			
       
   633 if __name__ == "__main__":
       
   634 	main(sys.argv, sys.stdout, os.environ)