|
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) |