1## This file is part of Scapy 2## See http://www.secdev.org/projects/scapy for more informations 3## Copyright (C) Philippe Biondi <phil@secdev.org> 4## This program is published under a GPLv2 license 5 6""" 7Resolve Autonomous Systems (AS). 8""" 9 10 11from __future__ import absolute_import 12import socket, errno 13from scapy.config import conf 14from scapy.compat import * 15 16class AS_resolver: 17 server = None 18 options = "-k" 19 def __init__(self, server=None, port=43, options=None): 20 if server is not None: 21 self.server = server 22 self.port = port 23 if options is not None: 24 self.options = options 25 26 def _start(self): 27 self.s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 28 self.s.connect((self.server,self.port)) 29 if self.options: 30 self.s.send(self.options.encode("utf8")+b"\n") 31 self.s.recv(8192) 32 def _stop(self): 33 self.s.close() 34 35 def _parse_whois(self, txt): 36 asn,desc = None,b"" 37 for l in txt.splitlines(): 38 if not asn and l.startswith(b"origin:"): 39 asn = plain_str(l[7:].strip()) 40 if l.startswith(b"descr:"): 41 if desc: 42 desc += r"\n" 43 desc += l[6:].strip() 44 if asn is not None and desc: 45 break 46 return asn, plain_str(desc.strip()) 47 48 def _resolve_one(self, ip): 49 self.s.send(("%s\n" % ip).encode("utf8")) 50 x = b"" 51 while not (b"%" in x or b"source" in x): 52 x += self.s.recv(8192) 53 asn, desc = self._parse_whois(x) 54 return ip,asn,desc 55 def resolve(self, *ips): 56 self._start() 57 ret = [] 58 for ip in ips: 59 ip,asn,desc = self._resolve_one(ip) 60 if asn is not None: 61 ret.append((ip,asn,desc)) 62 self._stop() 63 return ret 64 65class AS_resolver_riswhois(AS_resolver): 66 server = "riswhois.ripe.net" 67 options = "-k -M -1" 68 69 70class AS_resolver_radb(AS_resolver): 71 server = "whois.ra.net" 72 options = "-k -M" 73 74 75class AS_resolver_cymru(AS_resolver): 76 server = "whois.cymru.com" 77 options = None 78 def resolve(self, *ips): 79 s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 80 s.connect((self.server,self.port)) 81 s.send(b"begin\r\n"+b"\r\n".join(ip.encode("utf8") for ip in ips)+b"\r\nend\r\n") 82 r = b"" 83 while True: 84 l = s.recv(8192) 85 if l == b"": 86 break 87 r += l 88 s.close() 89 90 return self.parse(r) 91 92 def parse(self, data): 93 """Parse bulk cymru data""" 94 95 ASNlist = [] 96 for l in data.splitlines()[1:]: 97 l = plain_str(l) 98 if "|" not in l: 99 continue 100 asn, ip, desc = [elt.strip() for elt in l.split('|')] 101 if asn == "NA": 102 continue 103 asn = "AS%s" % asn 104 ASNlist.append((ip, asn, desc)) 105 return ASNlist 106 107class AS_resolver_multi(AS_resolver): 108 resolvers_list = ( AS_resolver_riswhois(),AS_resolver_radb(),AS_resolver_cymru() ) 109 def __init__(self, *reslist): 110 if reslist: 111 self.resolvers_list = reslist 112 def resolve(self, *ips): 113 todo = ips 114 ret = [] 115 for ASres in self.resolvers_list: 116 try: 117 res = ASres.resolve(*todo) 118 except socket.error as e: 119 if e[0] in [errno.ECONNREFUSED, errno.ETIMEDOUT, errno.ECONNRESET]: 120 continue 121 resolved = [ ip for ip,asn,desc in res ] 122 todo = [ ip for ip in todo if ip not in resolved ] 123 ret += res 124 if len(todo) == 0: 125 break 126 if len(ips) != len(ret): 127 raise RuntimeError("Could not contact whois providers") 128 return ret 129 130 131conf.AS_resolver = AS_resolver_multi() 132