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
6 import os,re,sys,socket,time
8 from scapy.config import conf,ConfClass
9 from scapy.error import Scapy_Exception,log_loading,log_runtime
10 from scapy.utils import atol, inet_aton, inet_ntoa, PcapReader
11 from scapy.base_classes import Gen, Net, SetGen
12 import scapy.plist as plist
13 from scapy.sendrecv import debug, srp1
14 from scapy.layers.l2 import Ether, ARP
15 from scapy.data import MTU, ETHER_BROADCAST, ETH_P_ARP
19 from scapy.arch import pcapdnet
20 from scapy.arch.pcapdnet import *
26 def _where(filename, dirs=[], env="PATH"):
27 """Find file in current dir or system path"""
28 if not isinstance(dirs, list):
32 paths = [os.curdir] + os.environ[env].split(os.path.pathsep) + dirs
34 for match in glob(os.path.join(path, filename)):
36 return os.path.normpath(match)
37 raise IOError("File not found: %s" % filename)
39 def win_find_exe(filename, installsubdir=None, env="ProgramFiles"):
40 """Find executable in current dir, system path or given ProgramFiles subdir"""
41 for fn in [filename, filename+".exe"]:
43 if installsubdir is None:
46 path = _where(fn, dirs=[os.path.join(os.environ[env], installsubdir)])
54 class WinProgPath(ConfClass):
55 _default = "<System default>"
56 # We try some magic to find the appropriate executables
57 pdfreader = win_find_exe("AcroRd32")
58 psreader = win_find_exe("gsview32.exe", "Ghostgum/gsview")
59 dot = win_find_exe("dot", "ATT/Graphviz/bin")
60 tcpdump = win_find_exe("windump")
61 tcpreplay = win_find_exe("tcpreplay")
63 hexedit = win_find_exe("hexer")
64 wireshark = win_find_exe("wireshark", "wireshark")
66 conf.prog = WinProgPath()
74 class PcapNameNotFoundError(Scapy_Exception):
77 class NetworkInterface(object):
78 """A network interface of your local host"""
80 def __init__(self, dnetdict=None):
87 self.dnetdict = dnetdict
88 if dnetdict is not None:
91 def update(self, dnetdict):
92 """Update info about network interface according to given dnet dictionary"""
93 self.name = dnetdict["name"]
94 # Other attributes are optional
96 self.ip = socket.inet_ntoa(dnetdict["addr"].ip)
97 except (KeyError, AttributeError, NameError):
100 self.mac = dnetdict["link_addr"]
103 self._update_pcapdata()
105 def _update_pcapdata(self):
106 """Supplement more info from pypcap and the Windows registry"""
108 # XXX: We try eth0 - eth29 by bruteforce and match by IP address,
109 # because only the IP is available in both pypcap and dnet.
110 # This may not work with unorthodox network configurations and is
111 # slow because we have to walk through the Windows registry.
114 win_name = pcapdnet.pcap.ex_name(guess)
115 if win_name.endswith("}"):
117 uuid = win_name[win_name.index("{"):win_name.index("}")+1]
118 keyname = r"SYSTEM\CurrentControlSet\Services\Tcpip\Parameters\Interfaces\%s" % uuid
120 key = _winreg.OpenKey(_winreg.HKEY_LOCAL_MACHINE, keyname)
122 log_loading.debug("Couldn't open 'HKEY_LOCAL_MACHINE\\%s' (for guessed pcap iface name '%s')." % (keyname, guess))
125 fixed_ip = _winreg.QueryValueEx(key, "IPAddress")[0][0].encode("utf-8")
126 except (WindowsError, UnicodeDecodeError, IndexError):
129 dhcp_ip = _winreg.QueryValueEx(key, "DhcpIPAddress")[0].encode("utf-8")
130 except (WindowsError, UnicodeDecodeError, IndexError):
132 # "0.0.0.0" or None means the value is not set (at least not correctly).
133 # If both fixed_ip and dhcp_ip are set, fixed_ip takes precedence
134 if fixed_ip is not None and fixed_ip != "0.0.0.0":
136 elif dhcp_ip is not None and dhcp_ip != "0.0.0.0":
144 self.pcap_name = guess
145 self.win_name = win_name
149 raise PcapNameNotFoundError
152 return "<%s: %s %s %s pcap_name=%s win_name=%s>" % (self.__class__.__name__,
153 self.name, self.ip, self.mac, self.pcap_name, self.win_name)
155 from UserDict import IterableUserDict
157 class NetworkInterfaceDict(IterableUserDict):
158 """Store information about network interfaces and convert between names"""
160 def load_from_dnet(self):
161 """Populate interface table via dnet"""
162 for i in pcapdnet.dnet.intf():
164 # XXX: Only Ethernet for the moment: localhost is not supported by dnet and pcap
165 # We only take interfaces that have an IP address, because the IP
166 # is used for the mapping between dnet and pcap interface names
167 # and this significantly improves Scapy's startup performance
168 if i["name"].startswith("eth") and "addr" in i:
169 self.data[i["name"]] = NetworkInterface(i)
170 except (KeyError, PcapNameNotFoundError):
172 if len(self.data) == 0:
173 log_loading.warning("No match between your pcap and dnet network interfaces found. "
174 "You probably won't be able to send packets. "
175 "Deactivating unneeded interfaces and restarting Scapy might help.")
177 def pcap_name(self, devname):
178 """Return pypcap device name for given libdnet/Scapy device name
180 This mapping is necessary because pypcap numbers the devices differently."""
183 pcap_name = self.data[devname].pcap_name
185 raise ValueError("Unknown network interface %r" % devname)
189 def devname(self, pcap_name):
190 """Return libdnet/Scapy device name for given pypcap device name
192 This mapping is necessary because pypcap numbers the devices differently."""
194 for devname, iface in self.items():
195 if iface.pcap_name == pcap_name:
197 raise ValueError("Unknown pypcap network interface %r" % pcap_name)
199 def show(self, resolve_mac=True):
200 """Print list of available network interfaces in human readable form"""
201 print "%s %s %s" % ("IFACE".ljust(5), "IP".ljust(15), "MAC")
202 for iface_name in sorted(self.data.keys()):
203 dev = self.data[iface_name]
206 mac = conf.manufdb._resolve_MAC(mac)
207 print "%s %s %s" % (str(dev.name).ljust(5), str(dev.ip).ljust(15), mac)
209 ifaces = NetworkInterfaceDict()
210 ifaces.load_from_dnet()
212 def pcap_name(devname):
213 """Return pypcap device name for given libdnet/Scapy device name"""
215 pcap_name = ifaces.pcap_name(devname)
217 # pcap.pcap() will choose a sensible default for sniffing if iface=None
221 def devname(pcap_name):
222 """Return libdnet/Scapy device name for given pypcap device name"""
223 return ifaces.devname(pcap_name)
225 def show_interfaces(resolve_mac=True):
226 """Print list of available network interfaces"""
227 return ifaces.show(resolve_mac)
229 _orig_open_pcap = pcapdnet.open_pcap
230 pcapdnet.open_pcap = lambda iface,*args,**kargs: _orig_open_pcap(pcap_name(iface),*args,**kargs)
235 ip = '(\d+\.\d+\.\d+\.\d+)'
236 # On Vista and Windows 7 the gateway can be IP or 'On-link'.
237 # But the exact 'On-link' string depends on the locale, so we allow any text.
239 metric_pattern = "(\d+)"
240 delim = "\s+" # The columns are separated by whitespace
241 netstat_line = delim.join([ip, ip, gw_pattern, ip, metric_pattern])
242 pattern = re.compile(netstat_line)
243 f=os.popen("netstat -rn")
244 for l in f.readlines():
245 match = re.search(pattern,l)
247 dest = match.group(1)
248 mask = match.group(2)
250 netif = match.group(4)
251 metric = match.group(5)
253 intf = pcapdnet.dnet.intf().get_dst(pcapdnet.dnet.addr(type=2, addrtxt=dest))
255 log_loading.warning("Building Scapy's routing table: Couldn't get outgoing interface for destination %s" % dest)
257 if not intf.has_key("addr"):
259 addr = str(intf["addr"])
260 addr = addr.split("/")[0]
264 # If the gateway is no IP we assume it's on-link
265 gw_ipmatch = re.search('\d+\.\d+\.\d+\.\d+', gw)
267 gw = gw_ipmatch.group(0)
270 routes.append((dest,mask,gw, str(intf["name"]), addr))
277 def getmacbyip(ip, chainCC=0):
278 """Return MAC address corresponding to a given IP address"""
279 if isinstance(ip,Net):
281 tmp = map(ord, inet_aton(ip))
282 if (tmp[0] & 0xf0) == 0xe0: # mcast @
283 return "01:00:5e:%.2x:%.2x:%.2x" % (tmp[1]&0x7f,tmp[2],tmp[3])
284 iff,a,gw = conf.route.route(ip)
285 if ( (iff == LOOPBACK_NAME) or (ip == conf.route.get_if_bcast(iff)) ):
286 return "ff:ff:ff:ff:ff:ff"
287 # Windows uses local IP instead of 0.0.0.0 to represent locally reachable addresses
288 ifip = str(pcapdnet.dnet.intf().get(iff)['addr'])
289 if gw != ifip.split('/')[0]:
292 mac = conf.netcache.arp_cache.get(ip)
296 res = srp1(Ether(dst=ETHER_BROADCAST)/ARP(op="who-has", pdst=ip),
304 mac = res.payload.hwsrc
305 conf.netcache.arp_cache[ip] = mac
309 import scapy.layers.l2
310 scapy.layers.l2.getmacbyip = getmacbyip
314 console = readline.GetOutputFile()
315 except (ImportError, AttributeError):
316 log_loading.info("Could not get readline console. Will not interpret ANSI color codes.")
318 conf.readfunc = readline.rl.readline
319 orig_stdout = sys.stdout
326 def sndrcv(pks, pkt, timeout = 2, inter = 0, verbose=None, chainCC=0, retry=0, multi=0):
327 if not isinstance(pkt, Gen):
332 debug.recv = plist.PacketList([],"Unanswered")
333 debug.sent = plist.PacketList([],"Sent")
334 debug.match = plist.SndRcvList([])
337 # do it here to fix random fields, so that parent and child have the same
338 all_stimuli = tobesent = [p for p in pkt]
339 notans = len(tobesent)
363 if WINDOWS or pid == 0:
368 print "Begin emission:"
374 print "Finished to send %i packets." % i
377 except KeyboardInterrupt:
380 log_runtime.exception("--- Error sending packets")
381 log_runtime.info("--- Error sending packets")
384 sent_times = [p.sent_time for p in all_stimuli if p.sent_time]
387 if WINDOWS or pid > 0:
388 # Timeout starts after last packet is sent (as in Unix version)
390 stoptime = time.time()+timeout
394 inmask = [pks.ins.fd]
399 remaintime = stoptime-time.time()
409 for i in range(len(hlst)):
410 if r.answers(hlst[i]):
411 ans.append((hlst[i],r))
419 if not hasattr(hlst[i], '_answered'):
421 hlst[i]._answered = 1;
423 if notans == 0 and not multi:
431 except KeyboardInterrupt:
436 for p,t in zip(all_stimuli, sent_times):
441 remain = reduce(list.__add__, hsent.values(), [])
443 remain = filter(lambda p: not hasattr(p, '_answered'), remain);
445 if autostop and len(remain) > 0 and len(remain) != len(tobesent):
449 if len(tobesent) == 0:
454 debug.sent=plist.PacketList(remain[:],"Sent")
455 debug.match=plist.SndRcvList(ans[:])
457 #clean the ans list to delete the field _answered
460 if hasattr(s, '_answered'):
464 print "\nReceived %i packets, got %i answers, remaining %i packets" % (nbrecv+len(ans), len(ans), notans)
465 return plist.SndRcvList(ans),plist.PacketList(remain,"Unanswered")
468 import scapy.sendrecv
469 scapy.sendrecv.sndrcv = sndrcv
471 def sniff(count=0, store=1, offline=None, prn = None, lfilter=None, L2socket=None, timeout=None, *arg, **karg):
473 sniff([count=0,] [prn=None,] [store=1,] [offline=None,] [lfilter=None,] + L2ListenSocket args) -> list of packets
475 count: number of packets to capture. 0 means infinity
476 store: wether to store sniffed packets or discard them
477 prn: function to apply to each packet. If something is returned,
479 ex: prn = lambda x: x.summary()
480 lfilter: python function applied to each packet to determine
481 if further action may be done
482 ex: lfilter = lambda x: x.haslayer(Padding)
483 offline: pcap file to read packets from, instead of sniffing them
484 timeout: stop sniffing after a given time (default: None)
485 L2socket: use the provided L2socket
491 L2socket = conf.L2listen
492 s = L2socket(type=ETH_P_ALL, *arg, **karg)
494 s = PcapReader(offline)
497 if timeout is not None:
498 stoptime = time.time()+timeout
502 if timeout is not None:
503 remain = stoptime-time.time()
509 except PcapTimeoutElapsed:
513 if lfilter and not lfilter(p):
522 if count > 0 and c >= count:
524 except KeyboardInterrupt:
527 return plist.PacketList(lst,"Sniffed")
529 import scapy.sendrecv
530 scapy.sendrecv.sniff = sniff
533 return sorted(ifaces.keys())
535 def get_working_if():
537 return devname(pcap.lookupdev())