| 0. 1. 2. 3. 4. 5. 6. 7. 8. 9. 10. 11. 12. 13. 14. 15. 16. 17. 18. 19. 20. 21. 22. 23. 24. 25. 26. 27. 28. 29. 30. 31. 32. 33. 34. 35. 36. 37. 38. 39. 40. 41. 42. 43. 44. 45. 46. 47. 48. 49. 50. 51. 52. 53. 54. 55. 56. 57. 58. 59. 60. 61. 62. 63. 64. 65. 66. 67. 68. 69. 70. 71. 72. 73. 74. 75. 76. 77. 78. 79. 80. 81. 82. 83. 84. 85. 86. 87. 88. 89. 90. 91. 92. 93. 94. 95. 96. 97. 98. 99. 100. 101. 102. 103. 104. 105. 106. 107. 108. 109. 110. 111. 112. 113. 114. 115. 116. 117. 118. 119. 120. 121. 122. 123. 124. 125. 126. 127. 128. 129. 130. 131. 132. 133. 134. 135. 136. 137. 138. 139. 140. 141. 142. 143. 144. 145. 146. 147. 148. 149. 150. 151. 152. 153. 154. 155. 156. 157. 158. 159. 160. 161. 162. 163. 164. 165. 166. 167. 168. 169. 170. 171. 172. 173. 174. 175. 176. 177. 178. 179. 180. 181. 182. 183. 184. 185. 186. 187. 188. 189. 190. 191. 192. 193. 194. 195. 196. 197. 198. 199. 200. 201. 202. 203. 204. 205. 206. 207. 208. 209. 210. 211. 212. 213. 214. 215. 216. 217. 218. 219. 220. 221. 222. 223. 224. 225. 226. 227. 228. 229. 230. 231. 232. 233. 234. 235. 236. 237. 238. 239. 240. 241. 242. 243. 244. 245. 246. 247. 248. 249. 250. 251. 252. 253. 254. 255. 256. 257. 258. 259. 260. 261. 262. 263. 264. 265. 266. 267. 268. 269. 270. 271. 272. 273. 274. 275. 276. 277. 278. 279. 280. 281. 282. 283. 284. 285. 286. 287. 288. 289. 290. 291. 292. 293. 294. 295. 296. 297. 298. 299. 300. 301. 302. 303. 304. 305. 306. 307. 308. 309. 310. 311. 312. 313. 314. 315. 316. 317. 318. 319. 320. 321. 322. 323. 324. 325. 326. 327. 328. 329. 330. 331. 332. 333. 334. 335. 336. 337. 338. 339. 340. 341. 342. 343. 344. 345. 346. 347. 348. 349. 350. 351. 352. 353. 354. 355. 356. 357. 358. 359. 360. 361. 362. 363. 364. 365. 366. 367. 368. 369. 370. 371. 372. 373. 374. 375. 376. 377. 378. 379. 380. 381. 382. 383. 384. 385. 386. 387. 388. 389. 390. 391. 392. 393. 394. 395. 396. 397. 398. 399. 400. 401. 402. 403. 404. 405. 406. 407. 408. 409. 410. 411. 412. 413. 414. 415. 416. 417. 418. 419. 420. 421. 422. 423. 424. 425. 426. 427. 428. 429. 430. 431. 432. 433. 434. 435. 436. 437. 438. 439. 440. 441. 442. 443. 444. 445. 446. 447. 448. 449. 450. 451. 452. 453. 454. 455. 456. 457. 458. 459. 460. 461. 462. 463. 464. 465. 466. 467. 468. 469. 470. 471. 472. 473. 474. 475. 476. 477. 478. 479. 480. 481. 482. 483. 484. 485. 486. 487. 488. 489. 490. 491. 492. 493. 494. 495. 496. 497. 498. 499. 500. 501. 502. 503. 504. 505. 506. 507. 508. 509. 510. 511. 512. 513. 514. 515. 516. 517. 518. 519. 520. 521. 522. 523. 524. 525. 526. 527. 528. 529. 530. 531. 532. 533. 534. 535. 536. 537. 538. 539. 540. 541. 542. 543. 544. 545. 546. 547. 548. 549. 550. 551. 552. 553. 554. 555. 556. 557. 558. 559. 560. 561. 562. 563. 564. 565. 566. 567. 568. 569. 570. 571. 572. 573. 574. 575. 576. 577. 578. 579. 580. 581. 582. 583. 584. 585. 586. 587. 588. 589. 590. 591. 592. 593. 594. 595. 596. 597. 598. 599. 600. 601. 602. 603. 604. 605. 606. 607. 608. 609. 610. 611. 612. 613. 614. 615. 616. 617. 618. 619. 620. 621. 622. 623. 624. 625. 626. 627. 628. 629. 630. 631. 632. 633. 634. 635. 636. 637. 638. 639. 640. 641. 642. 643. 644. 645. 646. 647. 648. 649. 650. 651. 652. 653. 654. 655. 656. 657. 658. 659. 660. 661. 662. 663. 664. 665. 666. 667. 668. 669. 670. 671. 672. 673. 674. 675. 676. 677. 678. 679. 680. 681. 682. 683. 684. 685. 686. 687. 688. 689. 690. 691. 692. 693. 694. 695. 696. 697. 698. 699. 700. 701. 702. 703. 704. 705. 706. 707. 708. 709. 710. 711. 712. 713. 714. 715. 716. 717. 718. 719. 720. 721. 722. 723. 724. 725. 726. 727. 728. 729. 730. 731. 732. 733. 734. 735. 736. 737. 738. 739. 740. 741. 742. 743. 744. 745. 746. 747. 748. 749. 750. 751. 752. 753. 754. 755. 756. 757. 758. 759. 760. 761. 762. 763. 764. 765. 766. 767. 768. 769. 770. 771. 772. 773. 774. 775. 776. 777. 778. 779. 780. 781. 782. 783. 784. 785. 786. 787. 788. 789. 790. 791. 792. 793. 794. 795. 796. 797. 798. 799. 800. 801. 802. 803. 804. 805. 806. 807. 808. 809. 810. 811. 812. 813. 814. 815. 816. 817. 818. 819. 820. 821. 822. 823. 824. 825. 826. 827. 828. 829. 830. 831. 832. 833. 834. 835. 836. 837. 838. 839. 840. 841. 842. 843. 844. 845. 846. 847. 848. 849. 850. 851. 852. 853. 854. 855. 856. 857. 858. 859. 860. 861. 862. 863. 864. 865. 866. 867. 868. 869. 870. 871. 872. 873. 874. 875. 876. 877. 878. 879. 880. 881. 882. 883. 884. 885. 886. 887. 888. 889. 890. 891. 892. 893. 894. 895. 896. 897. 898. 899. 900. 901. 902. 903. 904. 905. 906. 907. 908. 909. 910. 911. 912. 913. 914. 915. 916. 917. 918. 919. 920. 921. 922. 923. 924. 925. 926. 927. 928. 929. 930. 931. 932. 933. | #! /usr/bin/python -- """ usage: %(progname)s [domain...] Version: %(version)s Contacts the apropriate whois database for each domain and displays the result. class WhoisRecord: self.domain -- Domain Name self.whoisserver -- Whoisserver associated with domain self.page -- raw whois record data public methods: def WhoisRecord(domain=None) Whois object constructor def whois(domainname=None, server=None, cache=0) Fetches whoisrecord and places result in self.page Raises NoSuchDomain if the domain doesn't exist. class DomainRecord(WhoisRecord): self.domainid -- domainid for this domain self.created -- date in which the domain was created self.lastupdated -- date in which the domain was last updated. self.expires -- date in which the domain expires self.databaseupdated -- date in which the database was last updated. self.servers -- list of (hostname, ip) pairs of the nameservers. self.registrant -- ContactRecord of domain owner. self.contacts -- dictionary of contacts (ContactRecord objects) public methods: def DomainRecord(domain=None) Constructor for DomainRecord def Parse() Creates a parsed version of the information contained in the whois record for domain from self.page raises NoParser if a parser does not exist for a registry. class Contact: self.type -- Type of contact self.organization -- Organization associated with contact. self.person -- Person associated with contact. self.handle -- NIC Handle self.address -- Street address of contact self.address2 -- self.address3 -- self.city -- city of addr self.state -- address state self.zip -- zipcode self.country -- country self.email -- Email address of contact self.phone -- Phone Number self.fax -- Fax Number self.lastupdated -- Last update of contact record """ _version = "1.5" import os, sys, string, time, getopt, socket, select, re, errno, copy, signal timeout=5 try: signal.SIGALRM HAS_ALARM = 1 except: HAS_ALARM = 0 class WhoisRecord: defaultserver='whois.verisign-grs.net'#'whois.networksolutions.com' whoismap={ 'com' : 'whois.internic.net' , \ 'org' : 'whois.pir.org' , \ 'net' : 'whois.internic.net' , \ 'edu' : 'whois.networksolutions.com' , \ 'biz' : 'whois.biz' , \ 'info': 'whois.afilias.info' , \ 'us' : 'whois.nic.us', \ 'de' : 'whois.denic.de' , \ 'gov' : 'whois.nic.gov' , \ 'name': 'whois.nic.name' , \ #??? 'pro': 'whois.nic.name' , \ 'museum': 'whois.museum' , \ 'int': 'whois.iana.org' , \ 'aero': 'whois.information.aero' , \ 'coop': 'whois.nic.coop' , \ # See http://www.nic.gov/cgi-bin/whois 'mil' : 'whois.nic.mil' , \ # See http://www.nic.mil/cgi-bin/whois 'ca' : 'whois.cdnnet.ca' , \ 'uk' : 'whois.nic.uk' , \ 'au' : 'whois.aunic.net' , \ 'hu' : 'whois.nic.hu' , \ # All the following are unverified/checked. 'be' : 'whois.ripe.net', 'it' : 'whois.ripe.net' , \ # also whois.nic.it 'at' : 'whois.ripe.net' , \ # also www.nic.at, whois.aco.net 'dk' : 'whois.ripe.net' , \ 'fo' : 'whois.ripe.net' , \ 'lt' : 'whois.ripe.net' , \ 'no' : 'whois.ripe.net' , \ 'sj' : 'whois.ripe.net' , \ 'sk' : 'whois.ripe.net' , \ 'tr' : 'whois.ripe.net' , \ # also whois.metu.edu.tr 'il' : 'whois.ripe.net' , \ 'bv' : 'whois.ripe.net' , \ 'se' : 'whois.nic-se.se' , \ 'br' : 'whois.nic.br' , \ # a.k.a. whois.fapesp.br? 'fr' : 'whois.nic.fr' , \ 'sg' : 'whois.nic.net.sg' , \ 'hm' : 'whois.registry.hm' , \ # see also whois.nic.hm 'nz' : 'domainz.waikato.ac.nz' , \ 'nl' : 'whois.domain-registry.nl' , \ # RIPE also handles other countries # See http://www.ripe.net/info/ncc/rir-areas.html 'ru' : 'whois.ripn.net' , \ 'ch' : 'whois.nic.ch' , \ # see http://www.nic.ch/whois_readme.html 'jp' : 'whois.nic.ad.jp' , \ # (use DOM foo.jp/e for english; need to lookup !handles separately) 'to' : 'whois.tonic.to' , \ 'nu' : 'whois.nic.nu' , \ 'fm' : 'www.dot.fm' , \ # http request http://www.dot.fm/search.html 'am' : 'whois.nic.am' , \ 'nu' : 'www.nunames.nu' , \ # http request # e.g. http://www.nunames.nu/cgi-bin/drill.cfm?domainname=nunames.nu #'cx' : 'whois.nic.cx' , \ # no response from this server 'af' : 'whois.nic.af' , \ 'as' : 'whois.nic.as' , \ 'li' : 'whois.nic.li' , \ 'lk' : 'whois.nic.lk' , \ 'mx' : 'whois.nic.mx' , \ 'pw' : 'whois.nic.pw' , \ 'sh' : 'whois.nic.sh' , \ # consistently resets connection 'tj' : 'whois.nic.tj' , \ 'tm' : 'whois.nic.tm' , \ 'pt' : 'whois.dns.pt' , \ 'kr' : 'whois.nic.or.kr' , \ # see also whois.krnic.net 'kz' : 'whois.nic.or.kr' , \ # see also whois.krnic.net 'al' : 'whois.ripe.net' , \ 'az' : 'whois.ripe.net' , \ 'ba' : 'whois.ripe.net' , \ 'bg' : 'whois.ripe.net' , \ 'by' : 'whois.ripe.net' , \ 'cy' : 'whois.ripe.net' , \ 'cz' : 'whois.ripe.net' , \ 'dz' : 'whois.ripe.net' , \ 'ee' : 'whois.ripe.net' , \ 'eg' : 'whois.ripe.net' , \ 'es' : 'whois.ripe.net' , \ 'fi' : 'whois.ripe.net' , \ 'gr' : 'whois.ripe.net' , \ 'hr' : 'whois.ripe.net' , \ 'lu' : 'whois.ripe.net' , \ 'lv' : 'whois.ripe.net' , \ 'ma' : 'whois.ripe.net' , \ 'md' : 'whois.ripe.net' , \ 'mk' : 'whois.ripe.net' , \ 'mt' : 'whois.ripe.net' , \ 'pl' : 'whois.ripe.net' , \ 'ro' : 'whois.ripe.net' , \ 'si' : 'whois.ripe.net' , \ 'sm' : 'whois.ripe.net' , \ 'su' : 'whois.ripe.net' , \ 'tn' : 'whois.ripe.net' , \ 'ua' : 'whois.ripe.net' , \ 'va' : 'whois.ripe.net' , \ 'yu' : 'whois.ripe.net' , \ # unchecked 'ac' : 'whois.nic.ac' , \ 'cc' : 'whois.nic.cc' , \ #'cn' : 'whois.cnnic.cn' , \ # connection refused 'gs' : 'whois.adamsnames.tc' , \ 'hk' : 'whois.apnic.net' , \ #'ie' : 'whois.ucd.ie' , \ # connection refused #'is' : 'whois.isnet.is' , \# connection refused #'mm' : 'whois.nic.mm' , \ # connection refused 'ms' : 'whois.adamsnames.tc' , \ 'my' : 'whois.mynic.net' , \ #'pe' : 'whois.rcp.net.pe' , \ # connection refused 'st' : 'whois.nic.st' , \ 'tc' : 'whois.adamsnames.tc' , \ 'tf' : 'whois.adamsnames.tc' , \ 'th' : 'whois.thnic.net' , \ 'tw' : 'whois.twnic.net' , \ # 'us' : 'whois.isi.edu' , \ 'vg' : 'whois.adamsnames.tc' , \ #'za' : 'whois.co.za' # connection refused } def __init__(self,domain=None): self.domain=domain self.whoisserver=None self.page=None def whois(self,domain=None, server=None, cache=0): if domain is not None: self.domain=domain if server is not None: self.whoisserver=server if self.domain is None: print "No Domain" raise "No Domain" if self.whoisserver is None: self.chooseserver() if self.whoisserver is None: print "No Server" raise "No Server" if cache: fn = "%s.dom" % domainname if os.path.exists(fn): return open(fn).read() self.page=self._whois() if cache: open(fn, "w").write(page) def chooseserver(self): try: toplevel = string.split(self.domain, ".")[-1] self.whoisserver=WhoisRecord.whoismap.get(toplevel) #print toplevel, "---", self.whoisserver if self.whoisserver==None: self.whoisserver=WhoisRecord.defaultserver return except: self.whoisserver=WhoisRecord.defaultserver return if toplevel in ('com', 'org', 'net'): tmp=self._whois() m = re.search("Whois Server:(.+)", tmp) if m: self.whoisserver=string.strip(m.group(1)) #print "server 2:", self.whoisserver return self.whoisserver='whois.networksolutions.com' tmp=self._whois() m=re.search("Whois Server:(.+)",tmp) if m: self.whoisserver=string.strip(m.group(1)) #print "server 1:", self.whoisserver return #print "server 3:", self.whoisserver def _whois(self): def alrmhandler(signum,frame): raise "TimedOut", "on connect" s = None ## try until we timeout s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) if HAS_ALARM: s.setblocking(0) signal.signal(signal.SIGALRM,alrmhandler) signal.alarm(timeout) while 1: try: s.connect((self.whoisserver, 43)) except socket.error, (ecode, reason): if ecode==errno.EINPROGRESS: continue elif ecode==errno.EALREADY: continue else: raise socket.error, (ecode, reason) pass break if HAS_ALARM: signal.alarm(0) ret = select.select ([s], [s], [], 30) if len(ret[1])== 0 and len(ret[0]) == 0: s.close() raise TimedOut, "on data" s.setblocking(1) s.send("%s\n" % self.domain) page = "" while 1: data = s.recv(8196) if not data: break page = page + data pass s.close() if string.find(page, "No match for") != -1: raise 'NoSuchDomain', self.domain if string.find(page, "No entries found") != -1: raise 'NoSuchDomain', self.domain if string.find(page, "no domain specified") != -1: raise 'NoSuchDomain', self.domain if string.find(page, "NO MATCH:") != -1: raise 'NoSuchDomain', self.domain return page ## ## ---------------------------------------------------------------------- ## class ContactRecord: def __init__(self): self.type=None self.organization=None self.person=None self.handle=None self.address=None self.address2=None self.address3=None self.city=None self.country=None self.state=None self.zip=None self.email=None self.phone=None self.fax=None self.lastupdated=None return def __str__(self): return "Type: %s\nOrganization: %s\nPerson: %s\nHandle: %s\nAddress: %s\nAddress2: %s\nAddress3: %s\nCity: %s\nState: %s\nZip: %s\nCountry: %s\nEmail: %s\nPhone: %s\nFax: %s\nLastupdate: %s\n" % (self.type,self.organization,self.person,self.handle,self.address, self.address2, self.address3, self.city, self.state, self.zip, self.country, self.email,self.phone,self.fax,self.lastupdated) class DomainRecord(WhoisRecord): parsemap={ 'whois.networksolutions.com' : 'ParseWhois_NetworkSolutions' , 'whois.opensrs.net' : 'ParseWhois_NetworkSolutions' , #needs to be customized! 'whois.register.com' : 'ParseWhois_RegisterCOM' , 'whois.alldomains.com' : 'ParseWhois_AllDomainsCOM', 'whois.iana.org' : 'ParseWhois_INT', 'whois.nic.coop' : 'ParseWhois_COOP', 'whois.biz' : 'ParseWhois_BIZ' , 'whois.nic.us' : 'ParseWhois_BIZ', 'whois.information.aero' : 'ParseWhois_INFO', 'whois.nic.name' : 'ParseWhois_INFO', 'whois.pir.org' : 'ParseWhois_INFO', 'whois.museum' : 'ParseWhois_INFO', 'whois.afilias.info' : 'ParseWhois_INFO' } def __init__(self,domain=None): WhoisRecord.__init__(self,domain) self.domainid = None self.created = None self.lastupdated = None self.expires = None self.databaseupdated = None self.servers = None self.registrant = ContactRecord() self.registrant.type='registrant' self.contacts = {} return def __str__(self): con='' for (k,v) in self.contacts.items(): con=con + str(v) +'\n' 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) def Parse(self): self._ParseWhois() return def _ParseWhois(self): m = re.search("Registrar Whois: (.*)", self.page) if m: self.whoisserver = m.group(1) parser=DomainRecord.parsemap.get(self.whoisserver) if parser==None: raise 'NoParser for', self.whoisserver #print "---> PARSER:", self.whoisserver, "-", parser parser='self.'+parser+'()' eval(parser) return ## ## ---------------------------------------------------------------------- ## def _ParseContacts_RegisterCOM(self,page): parts = re.split("((?:(?:Administrative|Billing|Technical|Zone) Contact,?[ ]*)+:)\n", page) contacttypes = None for part in parts: if string.find(part, "Contact:") != -1: if part[-1] == ":": part = part[:-1] contacttypes = string.split(part, ",") continue part = string.strip(part) if not part: continue contact=ContactRecord() m = re.search("Email: (.+@.+)", part) if m: contact.email=string.lower(string.strip(m.group(1))) m = re.search("\s+Fax\.\.: (.+)", part) if m: contact.fax=string.lower(string.strip(m.group(1))) m = re.search("\s+Phone: (.+)", part) if m: contact.phone=m.group(1) end=m.start(0) start=0 lines = string.split(part[start:end], "\n") lines = map(string.strip,lines) contact.organization = lines.pop(0) contact.person = lines.pop(0) contact.address=string.join(lines,'\n') for contacttype in contacttypes: contacttype = string.lower(string.strip(contacttype)) contacttype = string.replace(contacttype, " contact", "") contact.type=contacttype self.contacts[contacttype] = copy.copy(contact) def ParseWhois_NetworkSolutions(self): m = re.search("Record last updated on (.+)\.", self.page) if m: self.lastupdated = m.group(1) m = re.search("Record created on (.+)\.", self.page) if m: self.created = m.group(1) m = re.search("Database last updated on (.+)\.", self.page) if m: self.databaseupdated = m.group(1) m = re.search("Record expires on (.+)\.",self.page) if m: self.expires=m.group(1) m = re.search("Registrant:(.+?)\n\n", self.page, re.S) if m: start= m.start(1) end = m.end(1) reg = string.strip(self.page[start:end]) reg = string.split(reg, "\n") reg = map(string.strip,reg) self.registrant.organization = reg[0] self.registrant.address = string.join(reg[1:],'\n') m = re.search("(.+) \((.+)\)", self.registrant.organization) if m: self.domainid = m.group(2) m = re.search("Domain servers in listed order:\n\n", self.page) if m: i = m.end() m = re.search("\n\n", self.page[i:]) j = m.start() servers = string.strip(self.page[i:i+j]) lines = string.split(servers, "\n") self.servers = [] for line in lines: m=re.search("([\w|\.]+?)\s*(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})",string.strip(line)) if m: self.servers.append((m.group(1), m.group(2))) m = re.search("((?:(?:Administrative|Billing|Technical|Zone) Contact,?[ ]*)+:)\n", self.page) if m: i = m.start() m = re.search("Record expires on", self.page) j = m.start() contacts = string.strip(self.page[i:j]) self._ParseContacts_NetworkSolutions(contacts) def ParseWhois_RegisterCOM(self): m = re.search("Record last updated on.*: (.+)", self.page) if m: self.lastupdated = m.group(1) m = re.search("Created on.*: (.+)", self.page) if m: self.created = m.group(1) m = re.search("Expires on.*: (.+)", self.page) if m: self.expires = m.group(1) m = re.search("Phone: (.+)", self.page) if m: self.registrant.phone=m.group(1) m = re.search("Email: (.+@.+)",self.page) if m: self.registrant.email=m.group(1) m = re.search("Fax\.\.: (.+@.+)",self.page) if m: self.registrant.fax=m.group(1) m = re.search("Organization:(.+?)Phone:",self.page,re.S) if m: start=m.start(1) end=m.end(1) registrant = string.strip(self.page[start:end]) registrant = string.split(registrant, "\n") registrant = map(string.strip,registrant) self.registrant.organization = registrant[0] self.registrant.person =registrant[1] self.registrant.address = string.join(registrant[2:], "\n") m = re.search("Domain servers in listed order:\n\n(.+?)\n\n", self.page, re.S) if m: start = m.start(1) end = m.end(1) servers = string.strip(self.page[start:end]) lines = string.split(servers, "\n") self.servers = [] for line in lines: m=re.search("([\w|\.]+?)\s*(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})",string.strip(line)) if m: self.servers.append((m.group(1), m.group(2))) m = re.search("((?:(?:Administrative|Billing|Technical|Zone) Contact,?[ ]*)+:)\n", self.page) if m: i = m.start() m = re.search("Domain servers in listed order", self.page) j = m.start() contacts = string.strip(self.page[i:j]) self._ParseContacts_RegisterCOM(contacts) def _ParseContacts_NetworkSolutions(self,page): parts = re.split("((?:(?:Administrative|Billing|Technical|Zone) Contact,?[ ]*)+:)\n", page) contacttypes = None for part in parts: if string.find(part, "Contact:") != -1: if part[-1] == ":": part = part[:-1] contacttypes = string.split(part, ",") continue part = string.strip(part) if not part: continue record=ContactRecord() lines = string.split(part, "\n") m = re.search("(.+) \((.+)\) (.+@.+)", lines.pop(0)) if m: record.person = string.strip(m.group(1)) record.handle = string.strip(m.group(2)) record.email = string.lower(string.strip(m.group(3))) pass record.organization=string.strip(lines.pop(0)) flag = 0 addresslines = [] phonelines = [] phonelines.append(string.strip(lines.pop())) for line in lines: line = string.strip(line) #m=re.search("^(\d|-|\+|\s)+$",line) #if m: flag = 1 if flag == 0: addresslines.append(line) else: phonelines.append(line) pass pass record.phone = string.join(phonelines, "\n") record.address = string.join(addresslines, "\n") for contacttype in contacttypes: contacttype = string.lower(string.strip(contacttype)) contacttype = string.replace(contacttype, " contact", "") record.type=contacttype self.contacts.update({contacttype:copy.copy(record)}) return def ParseWhois_AllDomainsCOM(self): m = re.search("Record last updated on\.*: (.+)\.", self.page) if m: self.lastupdated = m.group(1) m = re.search("Created on\.*: (.+)\.", self.page) if m: self.created = m.group(1) m = re.search("Expires on\.*: (.+)\.",self.page) if m: self.expires=m.group(1) rawstr = r'Registrant:\n(?P<org>.*?)\s*\((?P<id>DOM-.*)\)' m = re.search(rawstr, self.page) if m: self.registrant.organization = m.group('org') idx = string.find(self.page, "\n\n", m.end(2)) self.registrant.address = string.strip(self.page[m.end(2)+1:idx]) m = re.search("(.+) \((.+)\)", self.registrant.organization) if m: self.domainid = m.group('id') m = re.search("Domain servers in listed order:\n\n", self.page) if m: i = m.end() m = re.search("\n\n", self.page[i:]) j = m.start() servers = string.strip(self.page[i:i+j]) lines = string.split(servers, "\n") self.servers = [] for line in lines: m=re.search("([\w|\.]+?)\s*(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})",string.strip(line)) if m: self.servers.append((m.group(1), m.group(2))) m = re.search("((?:(?:Administrative|Billing|Technical|Zone) Contact,?[ ]*)+:)\n", self.page) if m: i = m.start() m = re.search("Created on", self.page) try: j = m.start() except: j = len(self.page) contacts = string.strip(self.page[i:j]) self._ParseContacts_AllDomainsCOM(contacts) def _ParseContacts_AllDomainsCOM(self,page): parts = re.split("((?:(?:Administrative|Billing|Technical|Zone) Contact,?[ ]*)+:)\n", page) contacttypes = None for part in parts: if string.find(part, "Contact:") != -1: if part[-1] == ":": part = part[:-1] contacttypes = string.split(part, ",") continue part = string.strip(part) if not part: continue record=ContactRecord() rawstr = r'(?P<person>.*?)\s*\((?P<handle>NIC-.*)\)\s*(?P<org>.*?)\n' m = re.search(rawstr, part, re.DOTALL) if m: record.handle = m.group('handle') record.organization = m.group('org') record.person = m.group('person') part = part[m.end('org'):] rawstr = r'.*\s(?P<email>.*?@.*?)\s(?P<phone>.*?)\sFAX-\s(?P<fax>.*)' m = re.search(rawstr, part, re.IGNORECASE | re.DOTALL) if m: record.email = m.group('email') record.phone = string.strip(m.group('phone')) record.fax = m.group('fax') part = part[:m.start('email')] lines = string.split(part, "\n") addresslines = map(string.strip, lines) record.address = string.strip(string.join(addresslines, "\n")) for contacttype in contacttypes: contacttype = string.lower(string.strip(contacttype)) contacttype = string.replace(contacttype, " contact", "") record.type=contacttype self.contacts.update({contacttype:copy.copy(record)}) def ParseWhois_BIZ(self): biz_contacts = ( {"page_field": "Administrative", "rec_field": "administrative"}, {"page_field": "Billing", "rec_field": "billing"}, {"page_field": "Technical", "rec_field": "technical"}, {"page_field": "Registrant", "rec_field": "registrant"} ) # map of each page field name to the corresponding field of a ContactRecord() # struct is a tuple of dicts where each dict has "page_field" and "rec_field" key/vals biz_contact_fields = ( {"page_field": "ID", "rec_field": "handle"}, {"page_field": "Organization", "rec_field": "organization"}, {"page_field": "Name", "rec_field": "person"}, {"page_field": "Address1", "rec_field": "address"}, {"page_field": "City", "rec_field": "city"}, {"page_field": "State/Province", "rec_field": "state"}, {"page_field": "Postal Code", "rec_field": "zip"}, {"page_field": "Country", "rec_field": "country"}, {"page_field": "Phone Number", "rec_field": "phone"}, {"page_field": "Facsimile", "rec_field": "fax"}, {"page_field": "Email", "rec_field": "email"} ) self._ParseContacts_Generic(biz_contacts, biz_contact_fields) biz_fields = ( {'page_field': "Domain ID", "rec_field": "domainid"}, {'page_field': "Domain Registration Date", "rec_field": "created"}, {'page_field': "Domain Expiration Date", "rec_field": "expires"}, {'page_field': "Name Server", "rec_field": "servers"} ) self._ParseWhois_Generic(biz_fields) def ParseWhois_INFO(self): info_contacts = ( {"page_field": "Admin", "rec_field": "administrative"}, {"page_field": "Billing", "rec_field": "billing"}, {"page_field": "Tech", "rec_field": "technical"}, {"page_field": "Registrant", "rec_field": "registrant"} ) # map of each page field name to the corresponding field of a ContactRecord() # struct is a tuple of dicts where each dict has "page_field" and "rec_field" key/vals info_contact_fields = ( {"page_field": "ID", "rec_field": "handle"}, {"page_field": "Organization", "rec_field": "organization"}, {"page_field": "Name", "rec_field": "person"}, {"page_field": "Address", "rec_field": "address"}, # for NAME {"page_field": "Adress", "rec_field": "address"}, # for MUSEUM (mispelled!) {"page_field": "Street1", "rec_field": "address"}, # for INFO {"page_field": "Street2", "rec_field": "address2"}, {"page_field": "Street3", "rec_field": "address3"}, {"page_field": "City", "rec_field": "city"}, {"page_field": "State/Province", "rec_field": "state"}, {"page_field": "Postal Code", "rec_field": "zip"}, {"page_field": "Country", "rec_field": "country"}, {"page_field": "Phone", "rec_field": "phone"}, {"page_field": "Phone Number", "rec_field": "phone"}, # for AERO, MUSEUM {"page_field": "Facsimile", "rec_field": "fax"}, {"page_field": "Fax Number", "rec_field": "fax"}, # for AERO, MUSEUM {"page_field": "Email", "rec_field": "email"} ) self._ParseContacts_Generic(info_contacts, info_contact_fields) info_fields = ( {'page_field': "Domain ID", "rec_field": "domainid"}, {'page_field': "Created On", "rec_field": "created"}, {'page_field': "Creation Date", "rec_field": "created"}, {'page_field': "Expires On", "rec_field": "expires"}, #NAME {'page_field': "Expiration Date", "rec_field": "expires"}, #INFO. #AERO {'page_field': "Last Updated", "rec_field": "lastupdated"}, {'page_field': "Updated On", "rec_field": "lastupdated"}, #MUSEUM {'page_field': "Name Server", "rec_field": "servers"} ) self._ParseWhois_Generic(info_fields) def ParseWhois_COOP(self): coop_contacts = ( {"page_field": "domain:Admin-", "rec_field": "administrative"}, {"page_field": "domain:Billing-", "rec_field": "billing"}, {"page_field": "domain:Tech-", "rec_field": "technical"}, {"page_field": "domain:Registrant-", "rec_field": "registrant"} ) # map of each page field name to the corresponding field of a ContactRecord() # struct is a tuple of dicts where each dict has "page_field" and "rec_field" key/vals coop_contact_fields = ( {"page_field": "ID", "rec_field": "handle"}, {"page_field": "Organization", "rec_field": "organization"}, {"page_field": "Name", "rec_field": "person"}, {"page_field": "Address1", "rec_field": "address"}, {"page_field": "Address2", "rec_field": "address2"}, {"page_field": "City", "rec_field": "city"}, {"page_field": "State/Province", "rec_field": "state"}, {"page_field": "Postcode", "rec_field": "zip"}, {"page_field": "Country", "rec_field": "country"}, {"page_field": "Phone-Number", "rec_field": "phone"}, {"page_field": "Facsimile-Number", "rec_field": "fax"}, {"page_field": "Email", "rec_field": "email"} ) self._ParseContacts_Generic(coop_contacts, coop_contact_fields) coop_fields = ( {'page_field': "domain:ID", "rec_field": "domainid"}, {'page_field': "domain:Registration", "rec_field": "created"}, {'page_field': "domain:Expiration", "rec_field": "expires"}, {'page_field': "domain:Name-Server", "rec_field": "servers"} ) self._ParseWhois_Generic(coop_fields) def _ParseWhois_Generic(self, fields): for field in fields: regex = "%s: *(.+)" % field['page_field'] #print regex if field['rec_field'] == "servers": self.servers = [] servers = re.findall(regex, self.page) for server in servers: try: server = string.strip(server) ip = socket.gethostbyname(server) except: ip = "?" self.servers.append((server, ip)) else: m = re.search(regex, self.page) #if m: print m.group(1) if m: setattr(self, field['rec_field'], string.strip(m.group(1))) def _ParseContacts_Generic(self, contacts, fields): for contact in contacts: if contact['rec_field'] == 'registrant': self._ParseGenericContact(self.registrant, contact['page_field'], fields) else: self.contacts[contact['rec_field']] = ContactRecord() self.contacts[contact['rec_field']].type = contact['rec_field'] self._ParseGenericContact(self.contacts[contact['rec_field']], contact['page_field'], fields) def _ParseGenericContact(self, contact_rec, contact, fields): for field in fields: m = re.search("%s *.*%s: *(.+)" % (contact, field['page_field']), self.page) if not m: continue setattr(contact_rec, field['rec_field'], string.strip(m.group(1))) def ParseWhois_INT(self): int_contacts = ( {"page_field": "Registrant", "rec_field": "registrant"}, {"page_field": "Administrative Contact", "rec_field": "administrative"}, {"page_field": "Technical Contact", "rec_field": "technical"}) page = string.replace(self.page, "\r\n", "\n") for contact in int_contacts: page_field = contact['page_field'] s = "%s:(.*)\n\W" % page_field m = re.search(s, page, re.DOTALL) #if m: print m.group(1) print "-------------------" ## ## ---------------------------------------------------------------------- ## ## ## ---------------------------------------------------------------------- ## def usage(progname): version = _version print __doc__ % vars() def main(argv, stdout, environ): progname = argv[0] list, args = getopt.getopt(argv[1:], "", ["help", "version"]) for (field, val) in list: if field == "--help": usage(progname) return elif field == "--version": print progname, _version return #rec=WhoisRecord() #rec=DomainRecord() for domain in args: whoisserver=None if string.find(domain,'@')!=-1: (domain, whoisserver)=string.split(domain,'@') try: d = DomainRecord(domain) d.whois(domain, whoisserver) #print "---> PAGE:", d.page d.Parse() print str(d) #print rec.page except 'NoSuchDomain', reason: print "ERROR: no such domain %s" % domain # except socket.error, (ecode,reason): print reason except "TimedOut", reason: print "Timed out", reason if __name__ == "__main__": main(sys.argv, sys.stdout, os.environ) |