changelog shortlog tags changeset files revisions annotate raw

scapy.py

changeset 806: 87949e07281c
parent:c919e540a57b
child:19df3f1cd92b
author: Phil <phil@secdev.org>
date: Wed Apr 23 19:35:18 2008 +0200 (21 months ago)
permissions: -rwxr-xr-x
description: Improved mib parsing a bit

It will never be perfect until I implement a real parser
instead of using regexp
1#! /usr/bin/env python
2
3#############################################################################
4## ##
5## scapy.py --- Interactive packet manipulation tool ##
6## see http://www.secdev.org/projects/scapy/ ##
7## for more informations ##
8## ##
9## Copyright (C) 2003 Philippe Biondi <phil@secdev.org> ##
10## ##
11## This program is free software; you can redistribute it and/or modify it ##
12## under the terms of the GNU General Public License version 2 as ##
13## published by the Free Software Foundation; version 2. ##
14## ##
15## This program is distributed in the hope that it will be useful, but ##
16## WITHOUT ANY WARRANTY; without even the implied warranty of ##
17## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ##
18## General Public License for more details. ##
19## ##
20#############################################################################
21
22
23from __future__ import generators
24import os
25
26VERSION = "1.2.0.2"
27
28DEFAULT_CONFIG_FILE = os.path.join(os.environ["HOME"], ".scapy_startup.py")
29
30try:
31 os.stat(DEFAULT_CONFIG_FILE)
32except OSError:
33 DEFAULT_CONFIG_FILE = None
34
35def usage():
36 print """Usage: scapy.py [-s sessionfile] [-c new_startup_file] [-C]
37 -C: do not read startup file"""
38 sys.exit(0)
39
40
41#############################
42##### Logging subsystem #####
43#############################
44
45class Scapy_Exception(Exception):
46 pass
47
48import logging,traceback,time
49
50class ScapyFreqFilter(logging.Filter):
51 def __init__(self):
52 logging.Filter.__init__(self)
53 self.warning_table = {}
54 def filter(self, record):
55 wt = conf.warning_threshold
56 if wt > 0:
57 stk = traceback.extract_stack()
58 caller=None
59 for f,l,n,c in stk:
60 if n == 'warning':
61 break
62 caller = l
63 tm,nb = self.warning_table.get(caller, (0,0))
64 ltm = time.time()
65 if ltm-tm > wt:
66 tm = ltm
67 nb = 0
68 else:
69 if nb < 2:
70 nb += 1
71 if nb == 2:
72 record.msg = "more "+record.msg
73 else:
74 return 0
75 self.warning_table[caller] = (tm,nb)
76 return 1
77
78log_scapy = logging.getLogger("scapy")
79console_handler = logging.StreamHandler()
80console_handler.setFormatter(logging.Formatter("%(levelname)s: %(message)s"))
81log_scapy.addHandler(console_handler)
82log_runtime = logging.getLogger("scapy.runtime") # logs at runtime
83log_runtime.addFilter(ScapyFreqFilter())
84log_interactive = logging.getLogger("scapy.interactive") # logs in interactive functions
85log_loading = logging.getLogger("scapy.loading") # logs when loading scapy
86
87if __name__ == "__main__":
88 log_scapy.setLevel(1)
89
90
91##################
92##### Module #####
93##################
94
95import socket, sys, getopt, string, struct, random, code
96import cPickle, copy, types, gzip, base64, re, zlib, array
97from sets import Set
98from select import select
99from glob import glob
100from fcntl import ioctl
101import itertools
102import fcntl
103import warnings
104warnings.filterwarnings("ignore","tempnam",RuntimeWarning, __name__)
105
106
107try:
108 import Gnuplot
109 GNUPLOT=1
110except ImportError:
111 log_loading.info("did not find python gnuplot wrapper . Won't be able to plot")
112 GNUPLOT=0
113
114try:
115 import pyx
116 PYX=1
117except ImportError:
118 log_loading.info("Can't import PyX. Won't be able to use psdump() or pdfdump()")
119 PYX=0
120
121
122LINUX=sys.platform.startswith("linux")
123OPENBSD=sys.platform.startswith("openbsd")
124FREEBSD=sys.platform.startswith("freebsd")
125DARWIN=sys.platform.startswith("darwin")
126BIG_ENDIAN= struct.pack("H",1) == "\x00\x01"
127X86_64 = (os.uname()[4] == 'x86_64')
128SOLARIS=sys.platform.startswith("sunos")
129
130
131if LINUX:
132 DNET=PCAP=0
133else:
134 DNET=PCAP=1
135
136
137if PCAP:
138 try:
139 import pcap
140 PCAP = 1
141 except ImportError:
142 if LINUX:
143 log_loading.warning("did not find pcap module. Fallback to linux primitives")
144 PCAP = 0
145 else:
146 if __name__ == "__main__":
147 log_loading.error("did not find pcap module")
148 raise SystemExit
149 else:
150 raise
151
152if DNET:
153 try:
154 import dnet
155 DNET = 1
156 except ImportError:
157 if LINUX:
158 log_loading.warning("did not find dnet module. Fallback to linux primitives")
159 DNET = 0
160 else:
161 if __name__ == "__main__":
162 log_loading.error("did not find dnet module")
163 raise SystemExit
164 else:
165 raise
166
167if not PCAP:
168 f = os.popen("tcpdump -V 2> /dev/null")
169 if f.close() >> 8 == 0x7f:
170 log_loading.warning("Failed to execute tcpdump. Check it is installed and in the PATH")
171 TCPDUMP=0
172 else:
173 TCPDUMP=1
174 del(f)
175
176
177
178try:
179 from Crypto.Cipher import ARC4
180except ImportError:
181 log_loading.info("Can't find Crypto python lib. Won't be able to decrypt WEP")
182
183
184# Workarround bug 643005 : https://sourceforge.net/tracker/?func=detail&atid=105470&aid=643005&group_id=5470
185try:
186 socket.inet_aton("255.255.255.255")
187except socket.error:
188 def inet_aton(x):
189 if x == "255.255.255.255":
190 return "\xff"*4
191 else:
192 return socket.inet_aton(x)
193else:
194 inet_aton = socket.inet_aton
195
196inet_ntoa = socket.inet_ntoa
197try:
198 inet_ntop = socket.inet_ntop
199 inet_pton = socket.inet_pton
200except AttributeError:
201 log_loading.info("inet_ntop/pton functions not found. Python IPv6 support not present")
202
203
204if SOLARIS:
205 # GRE is missing on Solaris
206 socket.IPPROTO_GRE = 47
207
208###############################
209## Direct Access dictionnary ##
210###############################
211
212def fixname(x):
213 if x and x[0] in "0123456789":
214 x = "n_"+x
215 return x.translate("________________________________________________0123456789_______ABCDEFGHIJKLMNOPQRSTUVWXYZ______abcdefghijklmnopqrstuvwxyz_____________________________________________________________________________________________________________________________________")
216
217
218class DADict_Exception(Scapy_Exception):
219 pass
220
221class DADict:
222 def __init__(self, _name="DADict", **kargs):
223 self._name=_name
224 self.__dict__.update(kargs)
225 def fixname(self,val):
226 return fixname(val)
227 def __contains__(self, val):
228 return val in self.__dict__
229 def __getitem__(self, attr):
230 return getattr(self, attr)
231 def __setitem__(self, attr, val):
232 return setattr(self, self.fixname(attr), val)
233 def __iter__(self):
234 return iter(map(lambda (x,y):y,filter(lambda (x,y):x and x[0]!="_", self.__dict__.items())))
235 def _show(self):
236 for k in self.__dict__.keys():
237 if k and k[0] != "_":
238 print "%10s = %r" % (k,getattr(self,k))
239 def __repr__(self):
240 return "<%s/ %s>" % (self._name," ".join(filter(lambda x:x and x[0]!="_",self.__dict__.keys())))
241
242 def _branch(self, br, uniq=0):
243 if uniq and br._name in self:
244 raise DADict_Exception("DADict: [%s] already branched in [%s]" % (br._name, self._name))
245 self[br._name] = br
246
247 def _my_find(self, *args, **kargs):
248 if args and self._name not in args:
249 return False
250 for k in kargs:
251 if k not in self or self[k] != kargs[k]:
252 return False
253 return True
254
255 def _find(self, *args, **kargs):
256 return self._recurs_find((), *args, **kargs)
257 def _recurs_find(self, path, *args, **kargs):
258 if self in path:
259 return None
260 if self._my_find(*args, **kargs):
261 return self
262 for o in self:
263 if isinstance(o, DADict):
264 p = o._recurs_find(path+(self,), *args, **kargs)
265 if p is not None:
266 return p
267 return None
268 def _find_all(self, *args, **kargs):
269 return self._recurs_find_all((), *args, **kargs)
270 def _recurs_find_all(self, path, *args, **kargs):
271 r = []
272 if self in path:
273 return r
274 if self._my_find(*args, **kargs):
275 r.append(self)
276 for o in self:
277 if isinstance(o, DADict):
278 p = o._recurs_find_all(path+(self,), *args, **kargs)
279 r += p
280 return r
281 def keys(self):
282 return filter(lambda x:x and x[0]!="_", self.__dict__.keys())
283
284
285
286############
287## Consts ##
288############
289
290ETHER_ANY = "\x00"*6
291ETHER_BROADCAST = "\xff"*6
292
293ETH_P_ALL = 3
294ETH_P_IP = 0x800
295ETH_P_ARP = 0x806
296
297# From net/if_arp.h
298ARPHDR_ETHER = 1
299ARPHDR_METRICOM = 23
300ARPHDR_PPP = 512
301ARPHDR_LOOPBACK = 772
302ARPHDR_TUN = 65534
303
304# From bits/ioctls.h
305SIOCGIFHWADDR = 0x8927 # Get hardware address
306SIOCGIFADDR = 0x8915 # get PA address
307SIOCGIFNETMASK = 0x891b # get network PA mask
308SIOCGIFNAME = 0x8910 # get iface name
309SIOCSIFLINK = 0x8911 # set iface channel
310SIOCGIFCONF = 0x8912 # get iface list
311SIOCGIFFLAGS = 0x8913 # get flags
312SIOCSIFFLAGS = 0x8914 # set flags
313SIOCGIFINDEX = 0x8933 # name -> if_index mapping
314SIOCGIFCOUNT = 0x8938 # get number of devices
315SIOCGSTAMP = 0x8906 # get packet timestamp (as a timeval)
316
317
318# From if.h
319IFF_UP = 0x1 # Interface is up.
320IFF_BROADCAST = 0x2 # Broadcast address valid.
321IFF_DEBUG = 0x4 # Turn on debugging.
322IFF_LOOPBACK = 0x8 # Is a loopback net.
323IFF_POINTOPOINT = 0x10 # Interface is point-to-point link.
324IFF_NOTRAILERS = 0x20 # Avoid use of trailers.
325IFF_RUNNING = 0x40 # Resources allocated.
326IFF_NOARP = 0x80 # No address resolution protocol.
327IFF_PROMISC = 0x100 # Receive all packets.
328
329
330
331# From netpacket/packet.h
332PACKET_ADD_MEMBERSHIP = 1
333PACKET_DROP_MEMBERSHIP = 2
334PACKET_RECV_OUTPUT = 3
335PACKET_RX_RING = 5
336PACKET_STATISTICS = 6
337PACKET_MR_MULTICAST = 0
338PACKET_MR_PROMISC = 1
339PACKET_MR_ALLMULTI = 2
340
341
342# From bits/socket.h
343SOL_PACKET = 263
344# From asm/socket.h
345SO_ATTACH_FILTER = 26
346SOL_SOCKET = 1
347
348# From net/route.h
349RTF_UP = 0x0001 # Route usable
350RTF_REJECT = 0x0200
351
352# From BSD net/bpf.h
353#BIOCIMMEDIATE=0x80044270
354BIOCIMMEDIATE=-2147204496
355
356MTU = 1600
357
358
359# file parsing to get some values :
360
361def load_protocols(filename):
362 spaces = re.compile("[ \t]+|\n")
363 dct = DADict(_name=filename)
364 try:
365 for l in open(filename):
366 try:
367 shrp = l.find("#")
368 if shrp >= 0:
369 l = l[:shrp]
370 l = l.strip()
371 if not l:
372 continue
373 lt = tuple(re.split(spaces, l))
374 if len(lt) < 2 or not lt[0]:
375 continue
376 dct[lt[0]] = int(lt[1])
377 except Exception,e:
378 log_loading.info("Couldn't parse file [%s]: line [%r] (%s)" % (filename,l,e))
379 except IOError:
380 log_loading.info("Can't open /etc/protocols file")
381 return dct
382
383IP_PROTOS=load_protocols("/etc/protocols")
384
385def load_ethertypes(filename):
386 spaces = re.compile("[ \t]+|\n")
387 dct = DADict(_name=filename)
388 try:
389 f=open(filename)
390 for l in f:
391 try:
392 shrp = l.find("#")
393 if shrp >= 0:
394 l = l[:shrp]
395 l = l.strip()
396 if not l:
397 continue
398 lt = tuple(re.split(spaces, l))
399 if len(lt) < 2 or not lt[0]:
400 continue
401 dct[lt[0]] = int(lt[1], 16)
402 except Exception,e:
403 log_loading.info("Couldn't parse file [%s]: line [%r] (%s)" % (filename,l,e))
404 f.close()
405 except IOError,msg:
406 pass
407 return dct
408
409ETHER_TYPES=load_ethertypes("/etc/ethertypes")
410
411def load_services(filename):
412 spaces = re.compile("[ \t]+|\n")
413 tdct=DADict(_name="%s-tcp"%filename)
414 udct=DADict(_name="%s-udp"%filename)
415 try:
416 f=open(filename)
417 for l in f:
418 try:
419 shrp = l.find("#")
420 if shrp >= 0:
421 l = l[:shrp]
422 l = l.strip()
423 if not l:
424 continue
425 lt = tuple(re.split(spaces, l))
426 if len(lt) < 2 or not lt[0]:
427 continue
428 if lt[1].endswith("/tcp"):
429 tdct[lt[0]] = int(lt[1].split('/')[0])
430 elif lt[1].endswith("/udp"):
431 udct[lt[0]] = int(lt[1].split('/')[0])
432 except Exception,e:
433 log_loading.warning("Couldn't file [%s]: line [%r] (%s)" % (filename,l,e))
434 f.close()
435 except IOError:
436 log_loading.info("Can't open /etc/services file")
437 return tdct,udct
438
439TCP_SERVICES,UDP_SERVICES=load_services("/etc/services")
440
441class ManufDA(DADict):
442 def fixname(self, val):
443 return val
444 def _get_manuf_couple(self, mac):
445 oui = ":".join(mac.split(":")[:3]).upper()
446 return self.__dict__.get(oui,(mac,mac))
447 def _get_manuf(self, mac):
448 return self._get_manuf_couple(mac)[1]
449 def _get_short_manuf(self, mac):
450 return self._get_manuf_couple(mac)[0]
451 def _resolve_MAC(self, mac):
452 oui = ":".join(mac.split(":")[:3]).upper()
453 if oui in self:
454 return ":".join([self[oui][0]]+ mac.split(":")[3:])
455 return mac
456
457
458
459
460def load_manuf(filename):
461 try:
462 manufdb=ManufDA(_name=filename)
463 for l in open(filename):
464 try:
465 l = l.strip()
466 if not l or l.startswith("#"):
467 continue
468 oui,shrt=l.split()[:2]
469 i = l.find("#")
470 if i < 0:
471 lng=shrt
472 else:
473 lng = l[i+2:]
474 manufdb[oui] = shrt,lng
475 except Exception,e:
476 log_loading.warning("Couldn't parse one line from [%s] [%r] (%s)" % (filename, l, e))
477 except IOError:
478 #log_loading.warning("Couldn't open [%s] file" % filename)
479 pass
480 return manufdb
481
482MANUFDB = load_manuf("/usr/share/wireshark/wireshark/manuf")
483
484
485
486
487###########
488## Tools ##
489###########
490
491def sane_color(x):
492 r=""
493 for i in x:
494 j = ord(i)
495 if (j < 32) or (j >= 127):
496 r=r+conf.color_theme.not_printable(".")
497 else:
498 r=r+i
499 return r
500
501def sane(x):
502 r=""
503 for i in x:
504 j = ord(i)
505 if (j < 32) or (j >= 127):
506 r=r+"."
507 else:
508 r=r+i
509 return r
510
511def lhex(x):
512 if type(x) in (int,long):
513 return hex(x)
514 elif type(x) is tuple:
515 return "(%s)" % ", ".join(map(lhex, x))
516 elif type(x) is list:
517 return "[%s]" % ", ".join(map(lhex, x))
518 else:
519 return x
520
521def hexdump(x):
522 x=str(x)
523 l = len(x)
524 i = 0
525 while i < l:
526 print "%04x " % i,
527 for j in range(16):
528 if i+j < l:
529 print "%02X" % ord(x[i+j]),
530 else:
531 print " ",
532 if j%16 == 7:
533 print "",
534 print " ",
535 print sane_color(x[i:i+16])
536 i += 16
537
538def linehexdump(x, onlyasc=0, onlyhex=0):
539 x = str(x)
540 l = len(x)
541 if not onlyasc:
542 for i in range(l):
543 print "%02X" % ord(x[i]),
544 print "",
545 if not onlyhex:
546 print sane_color(x)
547
548def chexdump(x):
549 x=str(x)
550 print ", ".join(map(lambda x: "%#04x"%ord(x), x))
551
552def hexstr(x, onlyasc=0, onlyhex=0):
553 s = []
554 if not onlyasc:
555 s.append(" ".join(map(lambda x:"%02x"%ord(x), x)))
556 if not onlyhex:
557 s.append(sane(x))
558 return " ".join(s)
559
560
561def hexdiff(x,y):
562 x=str(x)[::-1]
563 y=str(y)[::-1]
564 SUBST=1
565 INSERT=1
566 d={}
567 d[-1,-1] = 0,(-1,-1)
568 for j in range(len(y)):
569 d[-1,j] = d[-1,j-1][0]+INSERT, (-1,j-1)
570 for i in range(len(x)):
571 d[i,-1] = d[i-1,-1][0]+INSERT, (i-1,-1)
572
573 for j in range(len(y)):
574 for i in range(len(x)):
575 d[i,j] = min( ( d[i-1,j-1][0]+SUBST*(x[i] != y[j]), (i-1,j-1) ),
576 ( d[i-1,j][0]+INSERT, (i-1,j) ),
577 ( d[i,j-1][0]+INSERT, (i,j-1) ) )
578
579
580 backtrackx = []
581 backtracky = []
582 i=len(x)-1
583 j=len(y)-1
584 while not (i == j == -1):
585 i2,j2 = d[i,j][1]
586 backtrackx.append(x[i2+1:i+1])
587 backtracky.append(y[j2+1:j+1])
588 i,j = i2,j2
589
590
591
592 x = y = i = 0
593 colorize = { 0: lambda x:x,
594 -1: conf.color_theme.left,
595 1: conf.color_theme.right }
596
597 dox=1
598 doy=0
599 l = len(backtrackx)
600 while i < l:
601 separate=0
602 linex = backtrackx[i:i+16]
603 liney = backtracky[i:i+16]
604 xx = sum(len(k) for k in linex)
605 yy = sum(len(k) for k in liney)
606 if dox and not xx:
607 dox = 0
608 doy = 1
609 if dox and linex == liney:
610 doy=1
611
612 if dox:
613 xd = y
614 j = 0
615 while not linex[j]:
616 j += 1
617 xd -= 1
618 print colorize[doy-dox]("%04x" % xd),
619 x += xx
620 line=linex
621 else:
622 print " ",
623 if doy:
624 yd = y
625 j = 0
626 while not liney[j]:
627 j += 1
628 yd -= 1
629 print colorize[doy-dox]("%04x" % yd),
630 y += yy
631 line=liney
632 else:
633 print " ",
634
635 print " ",
636
637 cl = ""
638 for j in range(16):
639 if i+j < l:
640 if line[j]:
641 col = colorize[(linex[j]!=liney[j])*(doy-dox)]
642 print col("%02X" % ord(line[j])),
643 if linex[j]==liney[j]:
644 cl += sane_color(line[j])
645 else:
646 cl += col(sane(line[j]))
647 else:
648 print " ",
649 cl += " "
650 else:
651 print " ",
652 if j == 7:
653 print "",
654
655
656 print " ",cl
657
658 if doy or not yy:
659 doy=0
660 dox=1
661 i += 16
662 else:
663 if yy:
664 dox=0
665 doy=1
666 else:
667 i += 16
668
669
670crc32 = zlib.crc32
671
672if BIG_ENDIAN:
673 def checksum(pkt):
674 if len(pkt) % 2 == 1:
675 pkt += "\0"
676 s = sum(array.array("H", pkt))
677 s = (s >> 16) + (s & 0xffff)
678 s += s >> 16
679 s = ~s
680 return s & 0xffff
681else:
682 def checksum(pkt):
683 if len(pkt) % 2 == 1:
684 pkt += "\0"
685 s = sum(array.array("H", pkt))
686 s = (s >> 16) + (s & 0xffff)
687 s += s >> 16
688 s = ~s
689 return (((s>>8)&0xff)|s<<8) & 0xffff
690
691def warning(x):
692 log_runtime.warning(x)
693
694def mac2str(mac):
695 return "".join(map(lambda x: chr(int(x,16)), mac.split(":")))
696
697def str2mac(s):
698 return ("%02x:"*6)[:-1] % tuple(map(ord, s))
699
700def strxor(x,y):
701 return "".join(map(lambda x,y:chr(ord(x)^ord(y)),x,y))
702
703def atol(x):
704 try:
705 ip = inet_aton(x)
706 except socket.error:
707 ip = inet_aton(socket.gethostbyname(x))
708 return struct.unpack("!I", ip)[0]
709def ltoa(x):
710 return inet_ntoa(struct.pack("!I", x))
711
712def itom(x):
713 return (0xffffffff00000000L>>x)&0xffffffffL
714
715def do_graph(graph,prog=None,format="svg",target=None, type=None,string=None,options=None):
716 """do_graph(graph, prog=conf.prog.dot, format="svg",
717 target="| conf.prog.display", options=None, [string=1]):
718 string: if not None, simply return the graph string
719 graph: GraphViz graph description
720 format: output type (svg, ps, gif, jpg, etc.), passed to dot's "-T" option
721 target: filename or redirect. Defaults pipe to Imagemagick's display program
722 prog: which graphviz program to use
723 options: options to be passed to prog"""
724
725
726 if string:
727 return graph
728 if type is not None:
729 format=type
730 if prog is None:
731 prog = conf.prog.dot
732 if target is None:
733 target = "| %s" % conf.prog.display
734 if format is not None:
735 format = "-T %s" % format
736 w,r = os.popen2("%s %s %s %s" % (prog,options or "", format or "", target))
737 w.write(graph)
738 w.close()
739
740_TEX_TR = {
741 "{":"{\\tt\\char123}",
742 "}":"{\\tt\\char125}",
743 "\\":"{\\tt\\char92}",
744 "^":"\\^{}",
745 "$":"\\$",
746 "#":"\\#",
747 "~":"\\~",
748 "_":"\\_",
749 "&":"\\&",
750 "%":"\\%",
751 "|":"{\\tt\\char124}",
752 "~":"{\\tt\\char126}",
753 "<":"{\\tt\\char60}",
754 ">":"{\\tt\\char62}",
755 }
756
757def tex_escape(x):
758 s = ""
759 for c in x:
760 s += _TEX_TR.get(c,c)
761 return s
762
763def colgen(*lstcol,**kargs):
764 """Returns a generator that mixes provided quantities forever
765 trans: a function to convert the three arguments into a color. lambda x,y,z:(x,y,z) by default"""
766 if len(lstcol) < 2:
767 lstcol *= 2
768 trans = kargs.get("trans", lambda x,y,z: (x,y,z))
769 while 1:
770 for i in range(len(lstcol)):
771 for j in range(len(lstcol)):
772 for k in range(len(lstcol)):
773 if i != j or j != k or k != i:
774 yield trans(lstcol[(i+j)%len(lstcol)],lstcol[(j+k)%len(lstcol)],lstcol[(k+i)%len(lstcol)])
775
776def incremental_label(label="tag%05i", start=0):
777 while True:
778 yield label % start
779 start += 1
780
781#########################
782#### Enum management ####
783#########################
784
785class EnumElement:
786 _value=None
787 def __init__(self, key, value):
788 self._key = key
789 self._value = value
790 def __repr__(self):
791 return "<%s %s[%r]>" % (self.__dict__.get("_name", self.__class__.__name__), self._key, self._value)
792 def __getattr__(self, attr):
793 return getattr(self._value, attr)
794 def __str__(self):
795 return self._key
796 def __eq__(self, other):
797 return self._value == int(other)
798
799
800class Enum_metaclass(type):
801 element_class = EnumElement
802 def __new__(cls, name, bases, dct):
803 rdict={}
804 for k,v in dct.iteritems():
805 if type(v) is int:
806 v = cls.element_class(k,v)
807 dct[k] = v
808 rdict[v] = k
809 dct["__rdict__"] = rdict
810 return super(Enum_metaclass, cls).__new__(cls, name, bases, dct)
811 def __getitem__(self, attr):
812 return self.__rdict__[attr]
813 def __contains__(self, val):
814 return val in self.__rdict__
815 def get(self, attr, val=None):
816 return self._rdict__.get(attr, val)
817 def __repr__(self):
818 return "<%s>" % self.__dict__.get("name", self.__name__)
819
820
821
822
823##############################
824## Session saving/restoring ##
825##############################
826
827
828def save_session(fname, session=None, pickleProto=-1):
829 if session is None:
830 session = scapy_session
831
832 to_be_saved = session.copy()
833
834 if to_be_saved.has_key("__builtins__"):
835 del(to_be_saved["__builtins__"])
836
837 for k in to_be_saved.keys():
838 if type(to_be_saved[k]) in [types.TypeType, types.ClassType, types.ModuleType]:
839 log_interactive.error("[%s] (%s) can't be saved." % (k, type(to_be_saved[k])))
840 del(to_be_saved[k])
841
842 try:
843 os.rename(fname, fname+".bak")
844 except OSError:
845 pass
846 f=gzip.open(fname,"wb")
847 cPickle.dump(to_be_saved, f, pickleProto)
848 f.close()
849
850def load_session(fname):
851 try:
852 s = cPickle.load(gzip.open(fname,"rb"))
853 except IOError:
854 s = cPickle.load(open(fname,"rb"))
855 scapy_session.clear()
856 scapy_session.update(s)
857
858def update_session(fname):
859 try:
860 s = cPickle.load(gzip.open(fname,"rb"))
861 except IOError:
862 s = cPickle.load(open(fname,"rb"))
863 scapy_session.update(s)
864
865
866def export_object(obj):
867 print base64.encodestring(gzip.zlib.compress(cPickle.dumps(obj,2),9))
868
869def import_object(obj=None):
870 if obj is None:
871 obj = sys.stdin.read()
872 return cPickle.loads(gzip.zlib.decompress(base64.decodestring(obj.strip())))
873
874
875def save_object(fname, obj):
876 cPickle.dump(obj,gzip.open(fname,"wb"))
877
878def load_object(fname):
879 return cPickle.load(gzip.open(fname,"rb"))
880
881
882######################
883## Extension system ##
884######################
885
886
887def load_extension(filename):
888 import imp
889 paths = conf.extensions_paths
890 if type(paths) is not list:
891 paths = [paths]
892
893 name = os.path.realpath(os.path.expanduser(filename))
894 thepath = os.path.dirname(name)
895 thename = os.path.basename(name)
896 if thename.endswith(".py"):
897 thename = thename[:-3]
898
899 paths.insert(0, thepath)
900 cwd=syspath=None
901 try:
902 cwd = os.getcwd()
903 os.chdir(thepath)
904 syspath = sys.path[:]
905 sys.path += paths
906 try:
907 extf = imp.find_module(thename, paths)
908 except ImportError:
909 log_runtime.error("Module [%s] not found. Check conf.extensions_paths ?" % filename)
910 else:
911 ext = imp.load_module(thename, *extf)
912 import __builtin__
913 __builtin__.__dict__.update(ext.__dict__)
914 finally:
915 if syspath:
916 sys.path=syspath
917 if cwd:
918 os.chdir(cwd)
919
920
921
922#################
923## Debug class ##
924#################
925
926class debug:
927 recv=[]
928 sent=[]
929 match=[]
930
931
932####################
933## IP Tools class ##
934####################
935
936class IPTools:
937 """Add more powers to a class that have a "src" attribute."""
938 def whois(self):
939 os.system("whois %s" % self.src)
940 def ottl(self):
941 t = [32,64,128,255]+[self.ttl]
942 t.sort()
943 return t[t.index(self.ttl)+1]
944 def hops(self):
945 return self.ottl()-self.ttl-1
946
947
948##############################
949## Routing/Interfaces stuff ##
950##############################
951
952class Route:
953 def __init__(self):
954 self.resync()
955 self.s=socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
956 self.cache = {}
957
958 def invalidate_cache(self):
959 self.cache = {}
960
961 def resync(self):
962 self.invalidate_cache()
963 self.routes = read_routes()
964
965 def __repr__(self):
966 rt = "Network Netmask Gateway Iface Output IP\n"
967 for net,msk,gw,iface,addr in self.routes:
968 rt += "%-15s %-15s %-15s %-15s %-15s\n" % (ltoa(net),
969 ltoa(msk),
970 gw,
971 iface,
972 addr)
973 return rt
974
975 def make_route(self, host=None, net=None, gw=None, dev=None):
976 if host is not None:
977 thenet,msk = host,32
978 elif net is not None:
979 thenet,msk = net.split("/")
980 msk = int(msk)
981 else:
982 raise Scapy_Exception("make_route: Incorrect parameters. You should specify a host or a net")
983 if gw is None:
984 gw="0.0.0.0"
985 if dev is None:
986 if gw:
987 nhop = gw
988 else:
989 nhop = thenet
990 dev,ifaddr,x = self.route(nhop)
991 else:
992 ifaddr = get_if_addr(dev)
993 return (atol(thenet), itom(msk), gw, dev, ifaddr)
994
995 def add(self, *args, **kargs):
996 """Ex:
997 add(net="192.168.1.0/24",gw="1.2.3.4")
998 """
999 self.invalidate_cache()
1000 self.routes.append(self.make_route(*args,**kargs))
1001
1002
1003 def delt(self, *args, **kargs):
1004 """delt(host|net, gw|dev)"""
1005 self.invalidate_cache()
1006 route = self.make_route(*args,**kargs)
1007 try:
1008 i=self.routes.index(route)
1009 del(self.routes[i])
1010 except ValueError:
1011 warning("no matching route found")
1012
1013 def ifchange(self, iff, addr):
1014 self.invalidate_cache()
1015 the_addr,the_msk = (addr.split("/")+["32"])[:2]
1016 the_msk = itom(int(the_msk))
1017 the_rawaddr = atol(the_addr)
1018 the_net = the_rawaddr & the_msk
1019
1020
1021 for i in range(len(self.routes)):
1022 net,msk,gw,iface,addr = self.routes[i]
1023 if iface != iff:
1024 continue
1025 if gw == '0.0.0.0':
1026 self.routes[i] = (the_net,the_msk,gw,iface,the_addr)
1027 else:
1028 self.routes[i] = (net,msk,gw,iface,the_addr)
1029 for i in arp_cache.keys():
1030 del(arp_cache[i])
1031
1032
1033
1034 def ifdel(self, iff):
1035 self.invalidate_cache()
1036 new_routes=[]
1037 for rt in self.routes:
1038 if rt[3] != iff:
1039 new_routes.append(rt)
1040 self.routes=new_routes
1041
1042 def ifadd(self, iff, addr):
1043 self.invalidate_cache()
1044 the_addr,the_msk = (addr.split("/")+["32"])[:2]
1045 the_msk = itom(int(the_msk))
1046 the_rawaddr = atol(the_addr)
1047 the_net = the_rawaddr & the_msk
1048 self.routes.append((the_net,the_msk,'0.0.0.0',iff,the_addr))
1049
1050
1051 def route(self,dest,verbose=None):
1052 if dest in self.cache:
1053 return self.cache[dest]
1054 if verbose is None:
1055 verbose=conf.verb
1056 # Transform "192.168.*.1-5" to one IP of the set
1057 dst = dest.split("/")[0]
1058 dst = dst.replace("*","0")
1059 while 1:
1060 l = dst.find("-")
1061 if l < 0:
1062 break
1063 m = (dst[l:]+".").find(".")
1064 dst = dst[:l]+dst[l+m:]
1065
1066
1067 dst = atol(dst)
1068 pathes=[]
1069 for d,m,gw,i,a in self.routes:
1070 aa = atol(a)
1071 if aa == dst:
1072 pathes.append((0xffffffffL,("lo",a,"0.0.0.0")))
1073 if (dst & m) == (d & m):
1074 pathes.append((m,(i,a,gw)))
1075 if not pathes:
1076 if verbose:
1077 warning("No route found (no default route?)")
1078 return "lo","0.0.0.0","0.0.0.0" #XXX linux specific!
1079 # Choose the more specific route (greatest netmask).
1080 # XXX: we don't care about metrics
1081 pathes.sort()
1082 ret = pathes[-1][1]
1083 self.cache[dest] = ret
1084 return ret
1085
1086 def get_if_bcast(self, iff):
1087 for net, msk, gw, iface, addr in self.routes:
1088 if (iff == iface and net != 0L):
1089 bcast = atol(addr)|(~msk&0xffffffffL); # FIXME: check error in atol()
1090 return ltoa(bcast);
1091 warning("No broadcast address found for iface %s\n" % iff);
1092
1093if DNET:
1094 def get_if_raw_hwaddr(iff):
1095 if iff[:2] == "lo":
1096 return (772, '\x00'*6)
1097 try:
1098 l = dnet.intf().get(iff)
1099 l = l["link_addr"]
1100 except:
1101 raise Scapy_Exception("Error in attempting to get hw address for interface [%s]" % iff)
1102 return l.type,l.data
1103 def get_if_raw_addr(ifname):
1104 i = dnet.intf()
1105 return i.get(ifname)["addr"].data
1106else:
1107 def get_if_raw_hwaddr(iff):
1108 return struct.unpack("16xh6s8x",get_if(iff,SIOCGIFHWADDR))
1109
1110 def get_if_raw_addr(iff):
1111 try:
1112 return get_if(iff, SIOCGIFADDR)[20:24]
1113 except IOError:
1114 return "\0\0\0\0"
1115
1116
1117if PCAP:
1118 def get_if_list():
1119 # remove 'any' interface
1120 return map(lambda x:x[0],filter(lambda x:x[1] is None,pcap.findalldevs()))
1121 def get_working_if():
1122 try:
1123 return pcap.lookupdev()
1124 except Exception:
1125 return 'lo'
1126
1127 def attach_filter(s, filter):
1128 warning("attach_filter() should not be called in PCAP mode")
1129 def set_promisc(s,iff,val=1):
1130 warning("set_promisc() should not be called in DNET/PCAP mode")
1131
1132else:
1133 def get_if_list():
1134 f=open("/proc/net/dev","r")
1135 lst = []
1136 f.readline()
1137 f.readline()
1138 for l in f:
1139 lst.append(l.split(":")[0].strip())
1140 return lst
1141 def get_working_if():
1142 for i in get_if_list():
1143 if i == 'lo':
1144 continue
1145 ifflags = struct.unpack("16xH14x",get_if(i,SIOCGIFFLAGS))[0]
1146 if ifflags & IFF_UP:
1147 return i
1148 return "lo"
1149 def attach_filter(s, filter):
1150 # XXX We generate the filter on the interface conf.iface
1151 # because tcpdump open the "any" interface and ppp interfaces
1152 # in cooked mode. As we use them in raw mode, the filter will not
1153 # work... one solution could be to use "any" interface and translate
1154 # the filter from cooked mode to raw mode
1155 # mode
1156 if not TCPDUMP:
1157 return
1158 try:
1159 f = os.popen("%s -i %s -ddd -s 1600 '%s'" % (conf.prog.tcpdump,conf.iface,filter))
1160 except OSError,msg:
1161 log_interactive.warning("Failed to execute tcpdump: (%s)")
1162 return
1163 lines = f.readlines()
1164 if f.close():
1165 raise Scapy_Exception("Filter parse error")
1166 nb = int(lines[0])
1167 bpf = ""
1168 for l in lines[1:]:
1169 bpf += struct.pack("HBBI",*map(long,l.split()))
1170
1171 # XXX. Argl! We need to give the kernel a pointer on the BPF,
1172 # python object header seems to be 20 bytes. 36 bytes for x86 64bits arch.
1173 if X86_64:
1174 bpfh = struct.pack("HL", nb, id(bpf)+36)
1175 else:
1176 bpfh = struct.pack("HI", nb, id(bpf)+20)
1177 s.setsockopt(SOL_SOCKET, SO_ATTACH_FILTER, bpfh)
1178
1179 def set_promisc(s,iff,val=1):
1180 mreq = struct.pack("IHH8s", get_if_index(iff), PACKET_MR_PROMISC, 0, "")
1181 if val:
1182 cmd = PACKET_ADD_MEMBERSHIP
1183 else:
1184 cmd = PACKET_DROP_MEMBERSHIP
1185 s.setsockopt(SOL_PACKET, cmd, mreq)
1186
1187
1188if not LINUX:
1189
1190 def new_read_routes():
1191
1192 rtlst = []
1193 def addrt(rt,lst):
1194 dst,gw = rt
1195 lst.append(rt)
1196
1197 r = dnet.route()
1198 print r.loop(addrt, rtlst)
1199 return rtlst
1200
1201 def read_routes():
1202 if SOLARIS:
1203 f=os.popen("netstat -rvn") # -f inet
1204 elif FREEBSD:
1205 f=os.popen("netstat -rnW") # -W to handle long interface names
1206 else:
1207 f=os.popen("netstat -rn") # -f inet
1208 ok = 0
1209 mtu_present = False
1210 routes = []
1211 for l in f.readlines():
1212 if not l:
1213 break
1214 l = l.strip()
1215 if l.find("----") >= 0: # a separation line
1216 continue
1217 if l.find("Destination") >= 0:
1218 ok = 1
1219 if l.find("Mtu") >= 0:
1220 mtu_present = True
1221 continue
1222 if ok == 0:
1223 continue
1224 if not l:
1225 break
1226 if SOLARIS:
1227 dest,mask,gw,netif,mxfrg,rtt,ref,flg = l.split()[:8]
1228 else:
1229 if mtu_present:
1230 dest,gw,flg,ref,use,mtu,netif = l.split()[:7]
1231 else:
1232 dest,gw,flg,ref,use,netif = l.split()[:6]
1233 if flg.find("Lc") >= 0:
1234 continue
1235 if dest == "default":
1236 dest = 0L
1237 netmask = 0L
1238 else:
1239 if SOLARIS:
1240 netmask = atol(mask)
1241 elif "/" in dest:
1242 dest,netmask = dest.split("/")
1243 netmask = itom(int(netmask))
1244 else:
1245 netmask = itom((dest.count(".") + 1) * 8)
1246 dest += ".0"*(3-dest.count("."))
1247 dest = atol(dest)
1248 if not "G" in flg:
1249 gw = '0.0.0.0'
1250 ifaddr = get_if_addr(netif)
1251 routes.append((dest,netmask,gw,netif,ifaddr))
1252 f.close()
1253 return routes
1254
1255 def read_interfaces():
1256 i = dnet.intf()
1257 ifflist = {}
1258 def addif(iff,lst):
1259 if not iff.has_key("addr"):
1260 return
1261 if not iff.has_key("link_addr"):
1262 return
1263 rawip = iff["addr"].data
1264 ip = inet_ntoa(rawip)
1265 rawll = iff["link_addr"].data
1266 ll = str2mac(rawll)
1267 lst[iff["name"]] = (rawll,ll,rawip,ip)
1268 i.loop(addif, ifflist)
1269 return ifflist
1270
1271
1272else:
1273
1274 def read_routes():
1275 f=open("/proc/net/route","r")
1276 routes = []
1277 s=socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
1278 ifreq = ioctl(s, SIOCGIFADDR,struct.pack("16s16x","lo"))
1279 addrfamily = struct.unpack("h",ifreq[16:18])[0]
1280 if addrfamily == socket.AF_INET:
1281 ifreq2 = ioctl(s, SIOCGIFNETMASK,struct.pack("16s16x","lo"))
1282 msk = socket.ntohl(struct.unpack("I",ifreq2[20:24])[0])
1283 dst = socket.ntohl(struct.unpack("I",ifreq[20:24])[0]) & msk
1284 ifaddr = inet_ntoa(ifreq[20:24])
1285 routes.append((dst, msk, "0.0.0.0", "lo", ifaddr))
1286 else:
1287 warning("Interface lo: unkown address family (%i)"% addrfamily)
1288
1289 for l in f.readlines()[1:]:
1290 iff,dst,gw,flags,x,x,x,msk,x,x,x = l.split()
1291 flags = int(flags,16)
1292 if flags & RTF_UP == 0:
1293 continue
1294 if flags & RTF_REJECT:
1295 continue
1296 try:
1297 ifreq = ioctl(s, SIOCGIFADDR,struct.pack("16s16x",iff))
1298 except IOError: # interface is present in routing tables but does not have any assigned IP
1299 ifaddr="0.0.0.0"
1300 else:
1301 addrfamily = struct.unpack("h",ifreq[16:18])[0]
1302 if addrfamily == socket.AF_INET:
1303 ifaddr = inet_ntoa(ifreq[20:24])
1304 else:
1305 warning("Interface %s: unkown address family (%i)"%(iff, addrfamily))
1306 continue
1307 routes.append((socket.htonl(long(dst,16))&0xffffffffL,
1308 socket.htonl(long(msk,16))&0xffffffffL,
1309 inet_ntoa(struct.pack("I",long(gw,16))),
1310 iff, ifaddr))
1311
1312 f.close()
1313 return routes
1314
1315 def get_if(iff,cmd):
1316 s=socket.socket()
1317 ifreq = ioctl(s, cmd, struct.pack("16s16x",iff))
1318 s.close()
1319 return ifreq
1320
1321
1322 def get_if_index(iff):
1323 return int(struct.unpack("I",get_if(iff, SIOCGIFINDEX)[16:20])[0])
1324
1325 def get_last_packet_timestamp(sock):
1326 ts = ioctl(sock, SIOCGSTAMP, "12345678")
1327 s,us = struct.unpack("II",ts)
1328 return s+us/1000000.0
1329
1330
1331def get_if_addr(iff):
1332 return inet_ntoa(get_if_raw_addr(iff))
1333
1334def get_if_hwaddr(iff):
1335 addrfamily, mac = get_if_raw_hwaddr(iff)
1336 if addrfamily in [ARPHDR_ETHER,ARPHDR_LOOPBACK]:
1337 return str2mac(mac)
1338 else:
1339 raise Scapy_Exception("Unsupported address family (%i) for interface [%s]" % (addrfamily,iff))
1340
1341
1342
1343#####################
1344## ARP cache stuff ##
1345#####################
1346
1347ARPTIMEOUT=120
1348
1349# XXX Fill arp_cache with /etc/ether and arp cache
1350arp_cache={}
1351
1352if 0 and DNET: ## XXX Can't use this because it does not resolve IPs not in cache
1353 dnet_arp_object = dnet.arp()
1354 def getmacbyip(ip, chainCC=0):
1355 tmp = map(ord, inet_aton(ip))
1356 if (tmp[0] & 0xf0) == 0xe0: # mcast @
1357 return "01:00:5e:%.2x:%.2x:%.2x" % (tmp[1]&0x7f,tmp[2],tmp[3])
1358 iff,a,gw = conf.route.route(ip)
1359 if iff == "lo":
1360 return "ff:ff:ff:ff:ff:ff"
1361 if gw != "0.0.0.0":
1362 ip = gw
1363 res = dnet_arp_object.get(dnet.addr(ip))
1364 if res is None:
1365 return None
1366 else:
1367 return res.ntoa()
1368else:
1369 def getmacbyip(ip, chainCC=0):
1370 tmp = map(ord, inet_aton(ip))
1371 if (tmp[0] & 0xf0) == 0xe0: # mcast @
1372 return "01:00:5e:%.2x:%.2x:%.2x" % (tmp[1]&0x7f,tmp[2],tmp[3])
1373 iff,a,gw = conf.route.route(ip)
1374 if ( (iff == "lo") or (ip == conf.route.get_if_bcast(iff)) ):
1375 return "ff:ff:ff:ff:ff:ff"
1376 if gw != "0.0.0.0":
1377 ip = gw
1378
1379 if arp_cache.has_key(ip):
1380 mac, timeout = arp_cache[ip]
1381 if not timeout or (time.time()-timeout < ARPTIMEOUT):
1382 return mac
1383
1384 res = srp1(Ether(dst=ETHER_BROADCAST)/ARP(op="who-has", pdst=ip),
1385 type=ETH_P_ARP,
1386 iface = iff,
1387 timeout=2,
1388 verbose=0,
1389 chainCC=chainCC,
1390 nofilter=1)
1391 if res is not None:
1392 mac = res.payload.hwsrc
1393 arp_cache[ip] = (mac,time.time())
1394 return mac
1395 return None
1396
1397
1398####################
1399## Random numbers ##
1400####################
1401
1402def randseq(inf, sup, seed=None, forever=1, renewkeys=0):
1403 """iterate through a sequence in random order.
1404 When all the values have been drawn, if forever=1, the drawing is done again.
1405 If renewkeys=0, the draw will be in the same order, guaranteeing that the same
1406 number will be drawn in not less than the number of integers of the sequence"""
1407 rnd = random.Random(seed)
1408 sbox_size = 256
1409
1410 top = sup-inf+1
1411
1412 n=0
1413 while (1<<n) < top:
1414 n += 1
1415
1416 fs = min(3,(n+1)/2)
1417 fsmask = 2**fs-1
1418 rounds = max(n,3)
1419 turns = 0
1420
1421 while 1:
1422 if turns == 0 or renewkeys:
1423 sbox = [rnd.randint(0,fsmask) for k in xrange(sbox_size)]
1424 turns += 1
1425 i = 0
1426 while i < 2**n:
1427 ct = i
1428 i += 1
1429 for k in range(rounds): # Unbalanced Feistel Network
1430 lsb = ct & fsmask
1431 ct >>= fs
1432 lsb ^= sbox[ct%sbox_size]
1433 ct |= lsb << (n-fs)
1434
1435 if ct < top:
1436 yield inf+ct
1437 if not forever:
1438 break
1439
1440
1441class VolatileValue:
1442 def __repr__(self):
1443 return "<%s>" % self.__class__.__name__
1444 def __getattr__(self, attr):
1445 if attr == "__setstate__":
1446 raise AttributeError(attr)
1447 return getattr(self._fix(),attr)
1448 def _fix(self):
1449 return None
1450
1451
1452class RandField(VolatileValue):
1453 pass
1454
1455
1456class RandNum(RandField):
1457 min = 0
1458 max = 0
1459 def __init__(self, min, max):
1460 self.seq = randseq(min,max)
1461 def _fix(self):
1462 return self.seq.next()
1463
1464class RandNumGamma(RandField):
1465 def __init__(self, alpha, beta):
1466 self.alpha = alpha
1467 self.beta = beta
1468 def _fix(self):
1469 return int(round(random.gammavariate(self.alpha, self.beta)))
1470
1471class RandNumGauss(RandField):
1472 def __init__(self, mu, sigma):
1473 self.mu = mu
1474 self.sigma = sigma
1475 def _fix(self):
1476 return int(round(random.gauss(self.mu, self.sigma)))
1477
1478class RandNumExpo(RandField):
1479 def __init__(self, lambd):
1480 self.lambd = lambd
1481 def _fix(self):
1482 return int(round(random.expovariate(self.lambd)))
1483
1484class RandByte(RandNum):
1485 def __init__(self):
1486 RandNum.__init__(self, 0, 2L**8-1)
1487
1488class RandShort(RandNum):
1489 def __init__(self):
1490 RandNum.__init__(self, 0, 2L**16-1)
1491
1492class RandInt(RandNum):
1493 def __init__(self):
1494 RandNum.__init__(self, 0, 2L**32-1)
1495
1496class RandSInt(RandNum):
1497 def __init__(self):
1498 RandNum.__init__(self, -2L**31, 2L**31-1)
1499
1500class RandLong(RandNum):
1501 def __init__(self):
1502 RandNum.__init__(self, 0, 2L**64-1)
1503
1504class RandSLong(RandNum):
1505 def __init__(self):
1506 RandNum.__init__(self, -2L**63, 2L**63-1)
1507
1508class RandChoice(RandField):
1509 def __init__(self, *args):
1510 if not args:
1511 raise TypeError("RandChoice needs at least one choice")
1512 self._choice = args
1513 def _fix(self):
1514 return random.choice(self._choice)
1515
1516class RandString(RandField):
1517 def __init__(self, size, chars="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"):
1518 self.chars = chars
1519 self.size = size
1520 def _fix(self):
1521 s = ""
1522 for i in range(self.size):
1523 s += random.choice(self.chars)
1524 return s
1525
1526class RandBin(RandString):
1527 def __init__(self, size):
1528 RandString.__init__(self, size, "".join(map(chr,range(256))))
1529
1530
1531class RandTermString(RandString):
1532 def __init__(self, size, term):
1533 RandString.__init__(self, size, "".join(map(chr,range(1,256))))
1534 self.term = term
1535 def _fix(self):
1536 return RandString._fix(self)+self.term
1537
1538
1539
1540class RandIP(RandString):
1541 def __init__(self, iptemplate="0.0.0.0/0"):
1542 self.ip = Net(iptemplate)
1543 def _fix(self):
1544 return self.ip.choice()
1545
1546class RandMAC(RandString):
1547 def __init__(self, template="*"):
1548 template += ":*:*:*:*:*"
1549 template = template.split(":")
1550 self.mac = ()
1551 for i in range(6):
1552 if template[i] == "*":
1553 v = RandByte()
1554 elif "-" in template[i]:
1555 x,y = template[i].split("-")
1556 v = RandNum(int(x,16), int(y,16))
1557 else:
1558 v = int(template[i],16)
1559 self.mac += (v,)
1560 def _fix(self):
1561 return "%02x:%02x:%02x:%02x:%02x:%02x" % self.mac
1562
1563
1564class RandOID(RandString):
1565 def __init__(self, fmt=None, depth=RandNumExpo(0.1), idnum=RandNumExpo(0.01)):
1566 self.ori_fmt = fmt
1567 if fmt is not None:
1568 fmt = fmt.split(".")
1569 for i in range(len(fmt)):
1570 if "-" in fmt[i]:
1571 fmt[i] = tuple(map(int, fmt[i].split("-")))
1572 self.fmt = fmt
1573 self.depth = depth
1574 self.idnum = idnum
1575 def __repr__(self):
1576 if self.ori_fmt is None:
1577 return "<%s>" % self.__class__.__name__
1578 else:
1579 return "<%s [%s]>" % (self.__class__.__name__, self.ori_fmt)
1580 def _fix(self):
1581 if self.fmt is None:
1582 return ".".join(map(str, [self.idnum for i in xrange(1+self.depth)]))
1583 else:
1584 oid = []
1585 for i in self.fmt:
1586 if i == "*":
1587 oid.append(str(self.idnum))
1588 elif i == "**":
1589 oid += map(str, [self.idnum for i in xrange(1+self.depth)])
1590 elif type(i) is tuple:
1591 oid.append(str(random.randrange(*i)))
1592 else:
1593 oid.append(i)
1594 return ".".join(oid)
1595
1596
1597
1598class RandASN1Object(RandField):
1599 def __init__(self, objlist=None):
1600 if objlist is None:
1601 objlist = map(lambda x:x._asn1_obj,
1602 filter(lambda x:hasattr(x,"_asn1_obj"), ASN1_Class_UNIVERSAL.__rdict__.values()))
1603 self.objlist = objlist
1604 self.chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"
1605 def _fix(self, n=0):
1606 o = random.choice(self.objlist)
1607 if issubclass(o, ASN1_INTEGER):
1608 return o(int(random.gauss(0,1000)))
1609 elif issubclass(o, ASN1_STRING):
1610 z = int(random.expovariate(0.05)+1)
1611 return o("".join([random.choice(self.chars) for i in range(z)]))
1612 elif issubclass(o, ASN1_SEQUENCE) and (n < 10):
1613 z = int(random.expovariate(0.08)+1)
1614 return o(map(lambda x:x._fix(n+1), [self.__class__(objlist=self.objlist)]*z))
1615 return ASN1_INTEGER(int(random.gauss(0,1000)))
1616
1617class RandDHCPOptions(RandField):
1618 def __init__(self, size=None, rndstr=None):
1619 if size is None:
1620 size = RandNumExpo(0.05)
1621 self.size = size
1622 if rndstr is None:
1623 rndstr = RandBin(RandNum(0,255))
1624 self.rndstr=rndstr
1625 self._opts = DHCPOptions.values()
1626 self._opts.remove("pad")
1627 self._opts.remove("end")
1628 def _fix(self):
1629 op = []
1630 for k in range(self.size):
1631 o = random.choice(self._opts)
1632 if type(o) is str:
1633 op.append((o,self.rndstr*1))
1634 else:
1635 op.append((o.name, o.randval()._fix()))
1636 return op
1637
1638
1639# Automatic timestamp
1640
1641class AutoTime(VolatileValue):
1642 def __init__(self, base=None):
1643 if base == None:
1644 self.diff = 0
1645 else:
1646 self.diff = time.time()-base
1647 def _fix(self):
1648 return time.time()-self.diff
1649
1650class IntAutoTime(AutoTime):
1651 def _fix(self):
1652 return int(time.time()-self.diff)
1653
1654
1655class ZuluTime(AutoTime):
1656 def __init__(self, diff=None):
1657 self.diff=diff
1658 def _fix(self):
1659 return time.strftime("%y%m%d%H%M%SZ",time.gmtime(time.time()+self.diff))
1660
1661
1662class DelayedEval(VolatileValue):
1663 """ Exemple of usage: DelayedEval("time.time()") """
1664 def __init__(self, expr):
1665 self.expr = expr
1666 def _fix(self):
1667 return eval(self.expr)
1668
1669
1670class IncrementalValue(VolatileValue):
1671 def __init__(self, start=0, step=1, restart=-1):
1672 self.start = self.val = start
1673 self.step = step
1674 self.restart = restart
1675 def _fix(self):
1676 v = self.val
1677 if self.val == self.restart :
1678 self.val = self.start
1679 else:
1680 self.val += self.step
1681 return v
1682
1683def corrupt_bytes(s, p=0.01, n=None):
1684 s = array.array("B",str(s))
1685 l = len(s)
1686 if n is None:
1687 n = max(1,int(l*p))
1688 for i in random.sample(xrange(l), n):
1689 s[i] = random.randint(0,255)
1690 return s.tostring()
1691
1692def corrupt_bits(s, p=0.01, n=None):
1693 s = array.array("B",str(s))
1694 l = len(s)*8
1695 if n is None:
1696 n = max(1,int(l*p))
1697 for i in random.sample(xrange(l), n):
1698 s[i/8] ^= 1 << (i%8)
1699 return s.tostring()
1700
1701
1702class CorruptedBytes(VolatileValue):
1703 def __init__(self, s, p=0.01, n=None):
1704 self.s = s
1705 self.p = p
1706 self.n = n
1707 def _fix(self):
1708 return corrupt_bytes(self.s, self.p, self.n)
1709
1710class CorruptedBits(CorruptedBytes):
1711 def _fix(self):
1712 return corrupt_bits(self.s, self.p, self.n)
1713
1714##############
1715#### ASN1 ####
1716##############
1717
1718class ASN1_Error(Exception):
1719 pass
1720
1721class ASN1_Encoding_Error(ASN1_Error):
1722 pass
1723
1724class ASN1_Decoding_Error(ASN1_Error):
1725 pass
1726
1727class ASN1_BadTag_Decoding_Error(ASN1_Decoding_Error):
1728 pass
1729
1730
1731
1732class ASN1Codec(EnumElement):
1733 def register_stem(cls, stem):
1734 cls._stem = stem
1735 def dec(cls, s, context=None):
1736 return cls._stem.dec(s, context=context)
1737 def safedec(cls, s, context=None):
1738 return cls._stem.safedec(s, context=context)
1739 def get_stem(cls):
1740 return cls.stem
1741
1742
1743class ASN1_Codecs_metaclass(Enum_metaclass):
1744 element_class = ASN1Codec
1745
1746class ASN1_Codecs:
1747 __metaclass__ = ASN1_Codecs_metaclass
1748 BER = 1
1749 DER = 2
1750 PER = 3
1751 CER = 4
1752 LWER = 5
1753 BACnet = 6
1754 OER = 7
1755 SER = 8
1756 XER = 9
1757
1758class ASN1Tag(EnumElement):
1759 def __init__(self, key, value, context=None, codec=None):
1760 EnumElement.__init__(self, key, value)
1761 self._context = context
1762 if codec == None:
1763 codec = {}
1764 self._codec = codec
1765 def clone(self): # /!\ not a real deep copy. self.codec is shared
1766 return self.__class__(self._key, self._value, self._context, self._codec)
1767 def register_asn1_object(self, asn1obj):
1768 self._asn1_obj = asn1obj
1769 def asn1_object(self, val):
1770 if hasattr(self,"_asn1_obj"):
1771 return self._asn1_obj(val)
1772 raise ASN1_Error("%r does not have any assigned ASN1 object" % self)
1773 def register(self, codecnum, codec):
1774 self._codec[codecnum] = codec
1775 def get_codec(self, codec):
1776 try:
1777 c = self._codec[codec]
1778 except KeyError,msg:
1779 raise ASN1_Error("Codec %r not found for tag %r" % (codec, self))
1780 return c
1781
1782class ASN1_Class_metaclass(Enum_metaclass):
1783 element_class = ASN1Tag
1784 def __new__(cls, name, bases, dct): # XXX factorise a bit with Enum_metaclass.__new__()
1785 for b in bases:
1786 for k,v in b.__dict__.iteritems():
1787 if k not in dct and isinstance(v,ASN1Tag):
1788 dct[k] = v.clone()
1789
1790 rdict = {}
1791 for k,v in dct.iteritems():
1792 if type(v) is int:
1793 v = ASN1Tag(k,v)
1794 dct[k] = v
1795 rdict[v] = v
1796 elif isinstance(v, ASN1Tag):
1797 rdict[v] = v
1798 dct["__rdict__"] = rdict
1799
1800 cls = type.__new__(cls, name, bases, dct)
1801 for v in cls.__dict__.values():
1802 if isinstance(v, ASN1Tag):
1803 v.context = cls # overwrite ASN1Tag contexts, even cloned ones
1804 return cls
1805
1806
1807class ASN1_Class:
1808 __metaclass__ = ASN1_Class_metaclass
1809
1810class ASN1_Class_UNIVERSAL(ASN1_Class):
1811 name = "UNIVERSAL"
1812 ERROR = -3
1813 RAW = -2
1814 NONE = -1
1815 ANY = 0
1816 BOOLEAN = 1
1817 INTEGER = 2
1818 BIT_STRING = 3
1819 STRING = 4
1820 NULL = 5
1821 OID = 6
1822 OBJECT_DESCRIPTOR = 7
1823 EXTERNAL = 8
1824 REAL = 9
1825 ENUMERATED = 10
1826 EMBEDDED_PDF = 11
1827 UTF8_STRING = 12
1828 RELATIVE_OID = 13
1829 SEQUENCE = 0x30#XXX 16 ??
1830 SET = 0x31 #XXX 17 ??
1831 NUMERIC_STRING = 18
1832 PRINTABLE_STRING = 19
1833 T61_STRING = 20
1834 VIDEOTEX_STRING = 21
1835 IA5_STRING = 22
1836 UTC_TIME = 23
1837 GENERALIZED_TIME = 24
1838 GRAPHIC_STRING = 25
1839 ISO646_STRING = 26
1840 GENERAL_STRING = 27
1841 UNIVERSAL_STRING = 28
1842 CHAR_STRING = 29
1843 BMP_STRING = 30
1844 COUNTER32 = 0x41
1845 TIME_TICKS = 0x43
1846
1847class ASN1_Object_metaclass(type):
1848 def __new__(cls, name, bases, dct):
1849 c = super(ASN1_Object_metaclass, cls).__new__(cls, name, bases, dct)
1850 try:
1851 c.tag.register_asn1_object(c)
1852 except:
1853 warning("Error registering %r for %r" % (c.tag, c.codec))
1854 return c
1855
1856
1857class ASN1_Object:
1858 __metaclass__ = ASN1_Object_metaclass
1859 tag = ASN1_Class_UNIVERSAL.ANY
1860 def __init__(self, val):
1861 self.val = val
1862 def enc(self, codec):
1863 return self.tag.get_codec(codec).enc(self.val)
1864 def __repr__(self):
1865 return "<%s[%r]>" % (self.__dict__.get("name", self.__class__.__name__), self.val)
1866 def __str__(self):
1867 return self.enc(conf.ASN1_default_codec)
1868 def strshow(self, lvl=0):
1869 return (" "*lvl)+repr(self)+"\n"
1870 def show(self, lvl=0):
1871 print self.strshow(lvl)
1872 def __eq__(self, other):
1873 return self.val == other
1874 def __cmp__(self, other):
1875 return cmp(self.val, other)
1876
1877class ASN1_DECODING_ERROR(ASN1_Object):
1878 tag = ASN1_Class_UNIVERSAL.ERROR
1879 def __init__(self, val, exc=None):
1880 ASN1_Object.__init__(self, val)
1881 self.exc = exc
1882 def __repr__(self):
1883 return "<%s[%r]{{%s}}>" % (self.__dict__.get("name", self.__class__.__name__),
1884 self.val, self.exc.args[0])
1885 def enc(self, codec):
1886 if isinstance(self.val, ASN1_Object):
1887 return self.val.enc(codec)
1888 return self.val
1889
1890class ASN1_force(ASN1_Object):
1891 tag = ASN1_Class_UNIVERSAL.RAW
1892 def enc(self, codec):
1893 if isinstance(self.val, ASN1_Object):
1894 return self.val.enc(codec)
1895 return self.val
1896
1897class ASN1_BADTAG(ASN1_force):
1898 pass
1899
1900class ASN1_INTEGER(ASN1_Object):
1901 tag = ASN1_Class_UNIVERSAL.INTEGER
1902
1903class ASN1_STRING(ASN1_Object):
1904 tag = ASN1_Class_UNIVERSAL.STRING
1905
1906class ASN1_BIT_STRING(ASN1_STRING):
1907 tag = ASN1_Class_UNIVERSAL.BIT_STRING
1908
1909class ASN1_PRINTABLE_STRING(ASN1_STRING):
1910 tag = ASN1_Class_UNIVERSAL.PRINTABLE_STRING
1911
1912class ASN1_T61_STRING(ASN1_STRING):
1913 tag = ASN1_Class_UNIVERSAL.T61_STRING
1914
1915class ASN1_IA5_STRING(ASN1_STRING):
1916 tag = ASN1_Class_UNIVERSAL.IA5_STRING
1917
1918class ASN1_NUMERIC_STRING(ASN1_STRING):
1919 tag = ASN1_Class_UNIVERSAL.NUMERIC_STRING
1920
1921class ASN1_VIDEOTEX_STRING(ASN1_STRING):
1922 tag = ASN1_Class_UNIVERSAL.VIDEOTEX_STRING
1923
1924class ASN1_UTC_TIME(ASN1_STRING):
1925 tag = ASN1_Class_UNIVERSAL.UTC_TIME
1926
1927class ASN1_TIME_TICKS(ASN1_INTEGER):
1928 tag = ASN1_Class_UNIVERSAL.TIME_TICKS
1929
1930class ASN1_BOOLEAN(ASN1_INTEGER):
1931 tag = ASN1_Class_UNIVERSAL.BOOLEAN
1932
1933class ASN1_NULL(ASN1_INTEGER):
1934 tag = ASN1_Class_UNIVERSAL.NULL
1935
1936class ASN1_COUNTER32(ASN1_INTEGER):
1937 tag = ASN1_Class_UNIVERSAL.COUNTER32
1938
1939class ASN1_SEQUENCE(ASN1_Object):
1940 tag = ASN1_Class_UNIVERSAL.SEQUENCE
1941 def strshow(self, lvl=0):
1942 s = (" "*lvl)+("# %s:" % self.__class__.__name__)+"\n"
1943 for o in self.val:
1944 s += o.strshow(lvl=lvl+1)
1945 return s
1946
1947class ASN1_SET(ASN1_SEQUENCE):
1948 tag = ASN1_Class_UNIVERSAL.SET
1949
1950class ASN1_OID(ASN1_Object):
1951 tag = ASN1_Class_UNIVERSAL.OID
1952 def __init__(self, val):
1953 val = conf.mib._oid(val)
1954 ASN1_Object.__init__(self, val)
1955 def __repr__(self):
1956 return "<%s[%r]>" % (self.__dict__.get("name", self.__class__.__name__), conf.mib._oidname(self.val))
1957
1958
1959
1960##################
1961## BER encoding ##
1962##################
1963
1964
1965
1966#####[ BER tools ]#####
1967
1968
1969class BER_Exception(Exception):
1970 pass
1971
1972class BER_Decoding_Error(ASN1_Decoding_Error):
1973 def __init__(self, msg, decoded=None, remaining=None):
1974 Exception.__init__(self, msg)
1975 self.remaining = remaining
1976 self.decoded = decoded
1977 def __str__(self):
1978 s = Exception.__str__(self)
1979 if isinstance(self.decoded, BERcodec_Object):
1980 s+="\n### Already decoded ###\n%s" % self.decoded.strshow()
1981 else:
1982 s+="\n### Already decoded ###\n%r" % self.decoded
1983 s+="\n### Remaining ###\n%r" % self.remaining
1984 return s
1985
1986class BER_BadTag_Decoding_Error(BER_Decoding_Error, ASN1_BadTag_Decoding_Error):
1987 pass
1988
1989def BER_len_enc(l, size=0):
1990 if l <= 127 and size==0:
1991 return chr(l)
1992 s = ""
1993 while l or size>0:
1994 s = chr(l&0xff)+s
1995 l >>= 8L
1996 size -= 1
1997 if len(s) > 127:
1998 raise BER_Exception("BER_len_enc: Length too long (%i) to be encoded [%r]" % (len(s),s))
1999 return chr(len(s)|0x80)+s
2000def BER_len_dec(s):
2001 l = ord(s[0])
2002 if not l & 0x80:
2003 return l,s[1:]
2004 l &= 0x7f
2005 if len(s) <= l:
2006 raise BER_Decoding_Error("BER_len_dec: Got %i bytes while expecting %i" % (len(s)-1, l),remaining=s)
2007 ll = 0L
2008 for c in s[1:l+1]:
2009 ll <<= 8L
2010 ll |= ord(c)
2011 return ll,s[l+1:]
2012
2013def BER_num_enc(l, size=1):
2014 x=[]
2015 while l or size>0:
2016 x.insert(0, l & 0x7f)
2017 if len(x) > 1:
2018 x[0] |= 0x80
2019 l >>= 7
2020 size -= 1
2021 return "".join([chr(k) for k in x])
2022def BER_num_dec(s):
2023 x = 0
2024 for i in range(len(s)):
2025 c = ord(s[i])
2026 x <<= 7
2027 x |= c&0x7f
2028 if not c&0x80:
2029 break
2030 if c&0x80:
2031 raise BER_Decoding_Error("BER_num_dec: unfinished number description", remaining=s)
2032 return x, s[i+1:]
2033
2034#####[ BER classes ]#####
2035
2036class BERcodec_metaclass(type):
2037 def __new__(cls, name, bases, dct):
2038 c = super(BERcodec_metaclass, cls).__new__(cls, name, bases, dct)
2039 try:
2040 c.tag.register(c.codec, c)
2041 except:
2042 warning("Error registering %r for %r" % (c.tag, c.codec))
2043 return c
2044
2045
2046class BERcodec_Object:
2047 __metaclass__ = BERcodec_metaclass
2048 codec = ASN1_Codecs.BER
2049 tag = ASN1_Class_UNIVERSAL.ANY
2050
2051 @classmethod
2052 def asn1_object(cls, val):
2053 return cls.tag.asn1_object(val)
2054
2055 @classmethod
2056 def check_string(cls, s):
2057 if not s:
2058 raise BER_Decoding_Error("%s: Got empty object while expecting tag %r" %
2059 (cls.__name__,cls.tag), remaining=s)
2060 @classmethod
2061 def check_type(cls, s):
2062 cls.check_string(s)
2063 if cls.tag != ord(s[0]):
2064 raise BER_BadTag_Decoding_Error("%s: Got tag [%i/%#x] while expecting %r" %
2065 (cls.__name__, ord(s[0]), ord(s[0]),cls.tag), remaining=s)
2066 return s[1:]
2067 @classmethod
2068 def check_type_get_len(cls, s):
2069 s2 = cls.check_type(s)
2070 if not s2:
2071 raise BER_Decoding_Error("%s: No bytes while expecting a length" %
2072 cls.__name__, remaining=s)
2073 return BER_len_dec(s2)
2074 @classmethod
2075 def check_type_check_len(cls, s):
2076 l,s3 = cls.check_type_get_len(s)
2077 if len(s3) < l:
2078 raise BER_Decoding_Error("%s: Got %i bytes while expecting %i" %
2079 (cls.__name__, len(s3), l), remaining=s)
2080 return l,s3[:l],s3[l:]
2081
2082 @classmethod
2083 def do_dec(cls, s, context=None, safe=False):
2084 if context is None:
2085 context = cls.tag.context
2086 cls.check_string(s)
2087 p = ord(s[0])
2088 if p not in context:
2089 t = s
2090 if len(t) > 18:
2091 t = t[:15]+"..."
2092 raise BER_Decoding_Error("Unknown prefix [%02x] for [%r]" % (p,t), remaining=s)
2093 codec = context[p].get_codec(ASN1_Codecs.BER)
2094 return codec.dec(s,context,safe)
2095
2096 @classmethod
2097 def dec(cls, s, context=None, safe=False):
2098 if not safe:
2099 return cls.do_dec(s, context, safe)
2100 try:
2101 return cls.do_dec(s, context, safe)
2102 except BER_BadTag_Decoding_Error,e:
2103 o,remain = BERcodec_Object.dec(e.remaining, context, safe)
2104 return ASN1_BADTAG(o),remain
2105 except BER_Decoding_Error, e:
2106 return ASN1_DECODING_ERROR(s, exc=e),""
2107 except ASN1_Error, e:
2108 return ASN1_DECODING_ERROR(s, exc=e),""
2109
2110 @classmethod
2111 def safedec(cls, s, context=None):
2112 return cls.dec(s, context, safe=True)
2113
2114
2115 @classmethod
2116 def enc(cls, s):
2117 if type(s) is str:
2118 return BERcodec_STRING.enc(s)
2119 else:
2120 return BERcodec_INTEGER.enc(int(s))
2121
2122
2123
2124ASN1_Codecs.BER.register_stem(BERcodec_Object)
2125
2126
2127class BERcodec_INTEGER(BERcodec_Object):
2128 tag = ASN1_Class_UNIVERSAL.INTEGER
2129 @classmethod
2130 def enc(cls, i):
2131 s = []
2132 while 1:
2133 s.append(i&0xff)
2134 if -127 <= i < 0:
2135 break
2136 if 128 <= i <= 255:
2137 s.append(0)
2138 i >>= 8
2139 if not i:
2140 break
2141 s = map(chr, s)
2142 s.append(BER_len_enc(len(s)))
2143 s.append(chr(cls.tag))
2144 s.reverse()
2145 return "".join(s)
2146 @classmethod
2147 def do_dec(cls, s, context=None, safe=False):
2148 l,s,t = cls.check_type_check_len(s)
2149 x = 0L
2150 if s:
2151 if ord(s[0])&0x80: # negative int
2152 x = -1L
2153 for c in s:
2154 x <<= 8
2155 x |= ord(c)
2156 return cls.asn1_object(x),t
2157
2158
2159class BERcodec_BOOLEAN(BERcodec_INTEGER):
2160 tag = ASN1_Class_UNIVERSAL.BOOLEAN
2161
2162class BERcodec_NULL(BERcodec_INTEGER):
2163 tag = ASN1_Class_UNIVERSAL.NULL
2164 @classmethod
2165 def enc(cls, i):
2166 if i == 0:
2167 return chr(cls.tag)+"\0"
2168 else:
2169 return super(cls,cls).enc(i)
2170
2171class BERcodec_STRING(BERcodec_Object):
2172 tag = ASN1_Class_UNIVERSAL.STRING
2173 @classmethod
2174 def enc(cls,s):
2175 return chr(cls.tag)+BER_len_enc(len(s))+s
2176 @classmethod
2177 def do_dec(cls, s, context=None, safe=False):
2178 l,s,t = cls.check_type_check_len(s)
2179 return cls.tag.asn1_object(s),t
2180
2181class BERcodec_BIT_STRING(BERcodec_STRING):
2182 tag = ASN1_Class_UNIVERSAL.BIT_STRING
2183
2184class BERcodec_PRINTABLE_STRING(BERcodec_STRING):
2185 tag = ASN1_Class_UNIVERSAL.PRINTABLE_STRING
2186
2187class BERcodec_T61_STRING (BERcodec_STRING):
2188 tag = ASN1_Class_UNIVERSAL.T61_STRING
2189
2190class BERcodec_IA5_STRING(BERcodec_STRING):
2191 tag = ASN1_Class_UNIVERSAL.IA5_STRING
2192
2193class BERcodec_UTC_TIME(BERcodec_STRING):
2194 tag = ASN1_Class_UNIVERSAL.UTC_TIME
2195
2196class BERcodec_TIME_TICKS(BERcodec_INTEGER):
2197 tag = ASN1_Class_UNIVERSAL.TIME_TICKS
2198
2199class BERcodec_COUNTER32(BERcodec_INTEGER):
2200 tag = ASN1_Class_UNIVERSAL.COUNTER32
2201
2202class BERcodec_SEQUENCE(BERcodec_Object):
2203 tag = ASN1_Class_UNIVERSAL.SEQUENCE
2204 @classmethod
2205 def enc(cls, l):
2206 if type(l) is not str:
2207 l = "".join(map(lambda x: x.enc(cls.codec), l))
2208 return chr(cls.tag)+BER_len_enc(len(l))+l
2209 @classmethod
2210 def do_dec(cls, s, context=None, safe=False):
2211 if context is None:
2212 context = cls.tag.context
2213 l,st = cls.check_type_get_len(s) # we may have len(s) < l
2214 s,t = st[:l],st[l:]
2215 obj = []
2216 while s:
2217 try:
2218 o,s = BERcodec_Object.dec(s, context, safe)
2219 except BER_Decoding_Error, err:
2220 err.remaining += t
2221 if err.decoded is not None:
2222 obj.append(err.decoded)
2223 err.decoded = obj
2224 raise
2225 obj.append(o)
2226 if len(st) < l:
2227 raise BER_Decoding_Error("Not enough bytes to decode sequence", decoded=obj)
2228 return cls.asn1_object(obj),t
2229
2230class BERcodec_SET(BERcodec_SEQUENCE):
2231 tag = ASN1_Class_UNIVERSAL.SET
2232
2233
2234class BERcodec_OID(BERcodec_Object):
2235 tag = ASN1_Class_UNIVERSAL.OID
2236
2237 @classmethod
2238 def enc(cls, oid):
2239 lst = [int(x) for x in oid.strip(".").split(".")]
2240 if len(lst) >= 2:
2241 lst[1] += 40*lst[0]
2242 del(lst[0])
2243 s = "".join([BER_num_enc(k) for k in lst])
2244 return chr(cls.tag)+BER_len_enc(len(s))+s
2245 @classmethod
2246 def do_dec(cls, s, context=None, safe=False):
2247 l,s,t = cls.check_type_check_len(s)
2248 lst = []
2249 while s:
2250 l,s = BER_num_dec(s)
2251 lst.append(l)
2252 if (len(lst) > 0):
2253 lst.insert(0,lst[0]/40)
2254 lst[1] %= 40
2255 return cls.asn1_object(".".join([str(k) for k in lst])), t
2256
2257
2258#################
2259## MIB parsing ##
2260#################
2261
2262_mib_re_integer = re.compile("^[0-9]+$")
2263_mib_re_both = re.compile("^([a-zA-Z_][a-zA-Z0-9_-]*)\(([0-9]+)\)$")
2264_mib_re_oiddecl = re.compile("$\s*([a-zA-Z0-9_-]+)\s+OBJECT([^:\{\}]|\{[^:]+\})+::=\s*\{([^\}]+)\}",re.M)
2265_mib_re_strings = re.compile('"[^"]*"')
2266_mib_re_comments = re.compile('--.*(\r|\n)')
2267
2268class MIBDict(DADict):
2269 def _findroot(self, x):
2270 if x.startswith("."):
2271 x = x[1:]
2272 if not x.endswith("."):
2273 x += "."
2274 max=0
2275 root="."
2276 for k in self.keys():
2277 if x.startswith(self[k]+"."):
2278 if max < len(self[k]):
2279 max = len(self[k])
2280 root = k
2281 return root, x[max:-1]
2282 def _oidname(self, x):
2283 root,remainder = self._findroot(x)
2284 return root+remainder
2285 def _oid(self, x):
2286 xl = x.strip(".").split(".")
2287 p = len(xl)-1
2288 while p >= 0 and _mib_re_integer.match(xl[p]):
2289 p -= 1
2290 if p != 0 or xl[p] not in self:
2291 return x
2292 xl[p] = self[xl[p]]
2293 return ".".join(xl[p:])
2294 def _make_graph(self, other_keys=[], **kargs):
2295 nodes = [(k,self[k]) for k in self.keys()]
2296 oids = [self[k] for k in self.keys()]
2297 for k in other_keys:
2298 if k not in oids:
2299 nodes.append(self.oidname(k),k)
2300 s = 'digraph "mib" {\n\trankdir=LR;\n\n'
2301 for k,o in nodes:
2302 s += '\t"%s" [ label="%s" ];\n' % (o,k)
2303 s += "\n"
2304 for k,o in nodes:
2305 parent,remainder = self._findroot(o[:-1])
2306 remainder = remainder[1:]+o[-1]
2307 if parent != ".":
2308 parent = self[parent]
2309 s += '\t"%s" -> "%s" [label="%s"];\n' % (parent, o,remainder)
2310 s += "}\n"
2311 do_graph(s, **kargs)
2312
2313
2314def mib_register(ident, value, the_mib, unresolved):
2315 if ident in the_mib or ident in unresolved:
2316 return ident in the_mib
2317 resval = []
2318 not_resolved = 0
2319 for v in value:
2320 if _mib_re_integer.match(v):
2321 resval.append(v)
2322 else:
2323 v = fixname(v)
2324 if v not in the_mib:
2325 not_resolved = 1
2326 if v in the_mib:
2327 v = the_mib[v]
2328 elif v in unresolved:
2329 v = unresolved[v]
2330 if type(v) is list:
2331 resval += v
2332 else:
2333 resval.append(v)
2334 if not_resolved:
2335 unresolved[ident] = resval
2336 return False
2337 else:
2338 the_mib[ident] = resval
2339 keys = unresolved.keys()
2340 i = 0
2341 while i < len(keys):
2342 k = keys[i]
2343 if mib_register(k,unresolved[k], the_mib, {}):
2344 del(unresolved[k])
2345 del(keys[i])
2346 i = 0
2347 else:
2348 i += 1
2349
2350 return True
2351
2352
2353def load_mib(filenames):
2354 the_mib = {'iso': ['1']}
2355 unresolved = {}
2356 for k in conf.mib.keys():
2357 mib_register(k, conf.mib[k].split("."), the_mib, unresolved)
2358
2359 if type(filenames) is str:
2360 filenames = [filenames]
2361 for fnames in filenames:
2362 for fname in glob(fnames):
2363 f = open(fname)
2364 text = f.read()
2365 cleantext = " ".join(_mib_re_strings.split(" ".join(_mib_re_comments.split(text))))
2366 for m in _mib_re_oiddecl.finditer(cleantext):
2367 gr = m.groups()
2368 ident,oid = gr[0],gr[-1]
2369 ident=fixname(ident)
2370 oid = oid.split()
2371 for i in range(len(oid)):
2372 m = _mib_re_both.match(oid[i])
2373 if m:
2374 oid[i] = m.groups()[1]
2375 mib_register(ident, oid, the_mib, unresolved)
2376
2377 newmib = MIBDict(_name="MIB")
2378 for k,o in the_mib.iteritems():
2379 newmib[k]=".".join(o)
2380 for k,o in unresolved.iteritems():
2381 newmib[k]=".".join(o)
2382
2383 conf.mib=newmib
2384
2385
2386
2387################
2388## Generators ##
2389################
2390
2391class Gen(object):
2392 def __iter__(self):
2393 return iter([])
2394
2395class SetGen(Gen):
2396 def __init__(self, set, _iterpacket=1):
2397 self._iterpacket=_iterpacket
2398 if type(set) is list:
2399 self.set = set
2400 elif isinstance(set, PacketList):
2401 self.set = list(set)
2402 else:
2403 self.set = [set]
2404 def transf(self, element):
2405 return element
2406 def __iter__(self):
2407 for i in self.set:
2408 if (type(i) is tuple) and (len(i) == 2) and type(i[0]) is int and type(i[1]) is int:
2409 if (i[0] <= i[1]):
2410 j=i[0]
2411 while j <= i[1]:
2412 yield j
2413 j += 1
2414 elif isinstance(i, Gen) and (self._iterpacket or not isinstance(i,Packet)):
2415 for j in i:
2416 yield j
2417 else:
2418 yield i
2419 def __repr__(self):
2420 return "<SetGen %s>" % self.set.__repr__()
2421
2422class Net(Gen):
2423 """Generate a list of IPs from a network address or a name"""
2424 name = "ip"
2425 ipaddress = re.compile(r"^(\*|[0-2]?[0-9]?[0-9](-[0-2]?[0-9]?[0-9])?)\.(\*|[0-2]?[0-9]?[0-9](-[0-2]?[0-9]?[0-9])?)\.(\*|[0-2]?[0-9]?[0-9](-[0-2]?[0-9]?[0-9])?)\.(\*|[0-2]?[0-9]?[0-9](-[0-2]?[0-9]?[0-9])?)(/[0-3]?[0-9])?$")
2426 def __init__(self, net):
2427 self.repr=net
2428
2429 tmp=net.split('/')+["32"]
2430 if not self.ipaddress.match(net):
2431 tmp[0]=socket.gethostbyname(tmp[0])
2432 netmask = int(tmp[1])
2433
2434 def parse_digit(a,netmask):
2435 netmask = min(8,max(netmask,0))
2436 if a == "*":
2437 a = (0,256)
2438 elif a.find("-") >= 0:
2439 x,y = map(int,a.split("-"))
2440 if x > y:
2441 y = x
2442 a = (x & (0xffL<<netmask) , max(y, (x | (0xffL>>(8-netmask))))+1)
2443 else:
2444 a = (int(a) & (0xffL<<netmask),(int(a) | (0xffL>>(8-netmask)))+1)
2445 return a
2446
2447 self.parsed = map(lambda x,y: parse_digit(x,y), tmp[0].split("."), map(lambda x,nm=netmask: x-nm, (8,16,24,32)))
2448
2449 def __iter__(self):
2450 for d in xrange(*self.parsed[3]):
2451 for c in xrange(*self.parsed[2]):
2452 for b in xrange(*self.parsed[1]):
2453 for a in xrange(*self.parsed[0]):
2454 yield "%i.%i.%i.%i" % (a,b,c,d)
2455 def choice(self):
2456 ip = []
2457 for v in self.parsed:
2458 ip.append(str(random.randint(v[0],v[1]-1)))
2459 return ".".join(ip)
2460
2461 def __repr__(self):
2462 return "Net(%r)" % self.repr
2463
2464class OID(Gen):
2465 name = "OID"
2466 def __init__(self, oid):
2467 self.oid = oid
2468 self.cmpt = []
2469 fmt = []
2470 for i in oid.split("."):
2471 if "-" in i:
2472 fmt.append("%i")
2473 self.cmpt.append(tuple(map(int, i.split("-"))))
2474 else:
2475 fmt.append(i)
2476 self.fmt = ".".join(fmt)
2477 def __repr__(self):
2478 return "OID(%r)" % self.oid
2479 def __iter__(self):
2480 ii = [k[0] for k in self.cmpt]
2481 while 1:
2482 yield self.fmt % tuple(ii)
2483 i = 0
2484 while 1:
2485 if i >= len(ii):
2486 raise StopIteration
2487 if ii[i] < self.cmpt[i][1]:
2488 ii[i]+=1
2489 break
2490 else:
2491 ii[i] = self.cmpt[i][0]
2492 i += 1
2493
2494
2495#############
2496## Results ##
2497#############
2498
2499class PacketList:
2500 res = []
2501 def __init__(self, res=None, name="PacketList", stats=None):
2502 """create a packet list from a list of packets
2503 res: the list of packets
2504 stats: a list of classes that will appear in the stats (defaults to [TCP,UDP,ICMP])"""
2505 if stats is None:
2506 stats = [ TCP,UDP,ICMP ]
2507 self.stats = stats
2508 if res is None:
2509 res = []
2510 if isinstance(res, PacketList):
2511 res = res.res
2512 self.res = res
2513 self.listname = name
2514 def _elt2pkt(self, elt):
2515 return elt
2516 def _elt2sum(self, elt):
2517 return elt.summary()
2518 def _elt2show(self, elt):
2519 return self._elt2sum(elt)
2520 def __repr__(self):
2521# stats=dict.fromkeys(self.stats,0) ## needs python >= 2.3 :(
2522 stats = dict(map(lambda x: (x,0), self.stats))
2523 other = 0
2524 for r in self.res:
2525 f = 0
2526 for p in stats:
2527 if self._elt2pkt(r).haslayer(p):
2528 stats[p] += 1
2529 f = 1
2530 break
2531 if not f:
2532 other += 1
2533 s = ""
2534 ct = conf.color_theme
2535 for p in self.stats:
2536 s += " %s%s%s" % (ct.packetlist_proto(p.name),
2537 ct.punct(":"),
2538 ct.packetlist_value(stats[p]))
2539 s += " %s%s%s" % (ct.packetlist_proto("Other"),
2540 ct.punct(":"),
2541 ct.packetlist_value(other))
2542 return "%s%s%s%s%s" % (ct.punct("<"),
2543 ct.packetlist_name(self.listname),
2544 ct.punct(":"),
2545 s,
2546 ct.punct(">"))
2547 def __getattr__(self, attr):
2548 return getattr(self.res, attr)
2549 def __getitem__(self, item):
2550 if isinstance(item,type) and issubclass(item,Packet):
2551 return self.__class__(filter(lambda x: item in self._elt2pkt(x),self.res),
2552 name="%s from %s"%(item.__name__,self.listname))
2553 if type(item) is slice:
2554 return self.__class__(self.res.__getitem__(item),
2555 name = "mod %s" % self.listname)
2556 return self.res.__getitem__(item)
2557 def __getslice__(self, *args, **kargs):
2558 return self.__class__(self.res.__getslice__(*args, **kargs),
2559 name="mod %s"%self.listname)
2560 def __add__(self, other):
2561 return self.__class__(self.res+other.res,
2562 name="%s+%s"%(self.listname,other.listname))
2563 def summary(self, prn=None, lfilter=None):
2564 """prints a summary of each packet
2565prn: function to apply to each packet instead of lambda x:x.summary()
2566lfilter: truth function to apply to each packet to decide whether it will be displayed"""
2567 for r in self.res:
2568 if lfilter is not None:
2569 if not lfilter(r):
2570 continue
2571 if prn is None:
2572 print self._elt2sum(r)
2573 else:
2574 print prn(r)
2575 def nsummary(self,prn=None, lfilter=None):
2576 """prints a summary of each packet with the packet's number
2577prn: function to apply to each packet instead of lambda x:x.summary()
2578lfilter: truth function to apply to each packet to decide whether it will be displayed"""
2579 for i in range(len(self.res)):
2580 if lfilter is not None:
2581 if not lfilter(self.res[i]):
2582 continue
2583 print conf.color_theme.id(i,"%04i"),
2584 if prn is None:
2585 print self._elt2sum(self.res[i])
2586 else:
2587 print prn(self.res[i])
2588 def display(self): # Deprecated. Use show()
2589 """deprecated. is show()"""
2590 self.show()
2591 def show(self, *args, **kargs):
2592 """Best way to display the packet list. Defaults to nsummary() method"""
2593 return self.nsummary(*args, **kargs)
2594
2595 def filter(self, func):
2596 """Returns a packet list filtered by a truth function"""
2597 return self.__class__(filter(func,self.res),
2598 name="filtered %s"%self.listname)
2599 def make_table(self, *args, **kargs):
2600 """Prints a table using a function that returs for each packet its head column value, head row value and displayed value
2601 ex: p.make_table(lambda x:(x[IP].dst, x[TCP].dport, x[TCP].sprintf("%flags%")) """
2602 return make_table(self.res, *args, **kargs)
2603 def make_lined_table(self, *args, **kargs):
2604 """Same as make_table, but print a table with lines"""
2605 return make_lined_table(self.res, *args, **kargs)
2606 def make_tex_table(self, *args, **kargs):
2607 """Same as make_table, but print a table with LaTeX syntax"""
2608 return make_tex_table(self.res, *args, **kargs)
2609
2610 def plot(self, f, lfilter=None,**kargs):
2611 """Applies a function to each packet to get a value that will be plotted with GnuPlot. A gnuplot object is returned
2612 lfilter: a truth function that decides whether a packet must be ploted"""
2613 g=Gnuplot.Gnuplot()
2614 l = self.res
2615 if lfilter is not None:
2616 l = filter(lfilter, l)
2617 l = map(f,l)
2618 g.plot(Gnuplot.Data(l, **kargs))
2619 return g
2620
2621 def diffplot(self, f, delay=1, lfilter=None, **kargs):
2622 """diffplot(f, delay=1, lfilter=None)
2623 Applies a function to couples (l[i],l[i+delay])"""
2624 g = Gnuplot.Gnuplot()
2625 l = self.res
2626 if lfilter is not None:
2627 l = filter(lfilter, l)
2628 l = map(f,l[:-delay],l[delay:])
2629 g.plot(Gnuplot.Data(l, **kargs))
2630 return g
2631
2632 def multiplot(self, f, lfilter=None, **kargs):
2633 """Uses a function that returns a label and a value for this label, then plots all the values label by label"""
2634 g=Gnuplot.Gnuplot()
2635 l = self.res
2636 if lfilter is not None:
2637 l = filter(lfilter, l)
2638
2639 d={}
2640 for e in l:
2641 k,v = f(e)
2642 if k in d:
2643 d[k].append(v)
2644 else:
2645 d[k] = [v]
2646 data=[]
2647 for k in d:
2648 data.append(Gnuplot.Data(d[k], title=k, **kargs))
2649
2650 g.plot(*data)
2651 return g
2652
2653
2654 def rawhexdump(self):
2655 """Prints an hexadecimal dump of each packet in the list"""
2656 for p in self:
2657 hexdump(self._elt2pkt(p))
2658
2659 def hexraw(self, lfilter=None):
2660 """Same as nsummary(), except that if a packet has a Raw layer, it will be hexdumped
2661 lfilter: a truth function that decides whether a packet must be displayed"""
2662 for i in range(len(self.res)):
2663 p = self._elt2pkt(self.res[i])
2664 if lfilter is not None and not lfilter(p):
2665 continue
2666 print "%s %s %s" % (conf.color_theme.id(i,"%04i"),
2667 p.sprintf("%.time%"),
2668 self._elt2sum(self.res[i]))
2669 if p.haslayer(Raw):
2670 hexdump(p.getlayer(Raw).load)
2671
2672 def hexdump(self, lfilter=None):
2673 """Same as nsummary(), except that packets are also hexdumped
2674 lfilter: a truth function that decides whether a packet must be displayed"""
2675 for i in range(len(self.res)):
2676 p = self._elt2pkt(self.res[i])
2677 if lfilter is not None and not lfilter(p):
2678 continue
2679 print "%s %s %s" % (conf.color_theme.id(i,"%04i"),
2680 p.sprintf("%.time%"),
2681 self._elt2sum(self.res[i]))
2682 hexdump(p)
2683
2684 def padding(self, lfilter=None):
2685 """Same as hexraw(), for Padding layer"""
2686 for i in range(len(self.res)):
2687 p = self._elt2pkt(self.res[i])
2688 if p.haslayer(Padding):
2689 if lfilter is None or lfilter(p):
2690 print "%s %s %s" % (conf.color_theme.id(i,"%04i"),
2691 p.sprintf("%.time%"),
2692 self._elt2sum(self.res[i]))
2693 hexdump(p.getlayer(Padding).load)
2694
2695 def nzpadding(self, lfilter=None):
2696 """Same as padding() but only non null padding"""
2697 for i in range(len(self.res)):
2698 p = self._elt2pkt(self.res[i])
2699 if p.haslayer(Padding):
2700 pad = p.getlayer(Padding).load
2701 if pad == pad[0]*len(pad):
2702 continue
2703 if lfilter is None or lfilter(p):
2704 print "%s %s %s" % (conf.color_theme.id(i,"%04i"),
2705 p.sprintf("%.time%"),
2706 self._elt2sum(self.res[i]))
2707 hexdump(p.getlayer(Padding).load)
2708
2709
2710 def conversations(self, getsrcdst=None,**kargs):
2711 """Graphes a conversations between sources and destinations and display it
2712 (using graphviz and imagemagick)
2713 getsrcdst: a function that takes an element of the list and return the source and dest
2714 by defaults, return source and destination IP
2715 type: output type (svg, ps, gif, jpg, etc.), passed to dot's "-T" option
2716 target: filename or redirect. Defaults pipe to Imagemagick's display program
2717 prog: which graphviz program to use"""
2718 if getsrcdst is None:
2719 getsrcdst = lambda x:(x[IP].src, x[IP].dst)
2720 conv = {}
2721 for p in self.res:
2722 p = self._elt2pkt(p)
2723 try:
2724 c = getsrcdst(p)
2725 except:
2726 #XXX warning()
2727 continue
2728 conv[c] = conv.get(c,0)+1
2729 gr = 'digraph "conv" {\n'
2730 for s,d in conv:
2731 gr += '\t "%s" -> "%s"\n' % (s,d)
2732 gr += "}\n"
2733 return do_graph(gr, **kargs)
2734
2735 def afterglow(self, src=None, event=None, dst=None, **kargs):
2736 """Experimental clone attempt of http://sourceforge.net/projects/afterglow
2737 each datum is reduced as src -> event -> dst and the data are graphed.
2738 by default we have IP.src -> IP.dport -> IP.dst"""
2739 if src is None:
2740 src = lambda x: x[IP].src
2741 if event is None:
2742 event = lambda x: x[IP].dport
2743 if dst is None:
2744 dst = lambda x: x[IP].dst
2745 sl = {}
2746 el = {}
2747 dl = {}
2748 for i in self.res:
2749 try:
2750 s,e,d = src(i),event(i),dst(i)
2751 if s in sl:
2752 n,l = sl[s]
2753 n += 1
2754 if e not in l:
2755 l.append(e)
2756 sl[s] = (n,l)
2757 else:
2758 sl[s] = (1,[e])
2759 if e in el:
2760 n,l = el[e]
2761 n+=1
2762 if d not in l:
2763 l.append(d)
2764 el[e] = (n,l)
2765 else:
2766 el[e] = (1,[d])
2767 dl[d] = dl.get(d,0)+1
2768 except:
2769 continue
2770
2771 import math
2772 def normalize(n):
2773 return 2+math.log(n)/4.0
2774
2775 def minmax(x):
2776 m,M = min(x),max(x)
2777 if m == M:
2778 m = 0
2779 if M == 0:
2780 M = 1
2781 return m,M
2782
2783 mins,maxs = minmax(map(lambda (x,y): x, sl.values()))
2784 mine,maxe = minmax(map(lambda (x,y): x, el.values()))
2785 mind,maxd = minmax(dl.values())
2786
2787 gr = 'digraph "afterglow" {\n\tedge [len=2.5];\n'
2788
2789 gr += "# src nodes\n"
2790 for s in sl:
2791 n,l = sl[s]; n = 1+float(n-mins)/(maxs-mins)
2792 gr += '"src.%s" [label = "%s", shape=box, fillcolor="#FF0000", style=filled, fixedsize=1, height=%.2f,width=%.2f];\n' % (`s`,`s`,n,n)
2793 gr += "# event nodes\n"
2794 for e in el:
2795 n,l = el[e]; n = n = 1+float(n-mine)/(maxe-mine)
2796 gr += '"evt.%s" [label = "%s", shape=circle, fillcolor="#00FFFF", style=filled, fixedsize=1, height=%.2f, width=%.2f];\n' % (`e`,`e`,n,n)
2797 for d in dl:
2798 n = dl[d]; n = n = 1+float(n-mind)/(maxd-mind)
2799 gr += '"dst.%s" [label = "%s", shape=triangle, fillcolor="#0000ff", style=filled, fixedsize=1, height=%.2f, width=%.2f];\n' % (`d`,`d`,n,n)
2800
2801 gr += "###\n"
2802 for s in sl:
2803 n,l = sl[s]
2804 for e in l:
2805 gr += ' "src.%s" -> "evt.%s";\n' % (`s`,`e`)
2806 for e in el:
2807 n,l = el[e]
2808 for d in l:
2809 gr += ' "evt.%s" -> "dst.%s";\n' % (`e`,`d`)
2810
2811 gr += "}"
2812 open("/tmp/aze","w").write(gr)
2813 return do_graph(gr, **kargs)
2814
2815
2816
2817 def timeskew_graph(self, ip, **kargs):
2818 """Tries to graph the timeskew between the timestamps and real time for a given ip"""
2819 res = map(lambda x: self._elt2pkt(x), self.res)
2820 b = filter(lambda x:x.haslayer(IP) and x.getlayer(IP).src == ip and x.haslayer(TCP), res)
2821 c = []
2822 for p in b:
2823 opts = p.getlayer(TCP).options
2824 for o in opts:
2825 if o[0] == "Timestamp":
2826 c.append((p.time,o[1][0]))
2827 if not c:
2828 warning("No timestamps found in packet list")
2829 return
2830 d = map(lambda (x,y): (x%2000,((x-c[0][0])-((y-c[0][1])/1000.0))),c)
2831 g = Gnuplot.Gnuplot()
2832 g.plot(Gnuplot.Data(d,**kargs))
2833 return g
2834
2835 def _dump_document(self, **kargs):
2836 d = pyx.document.document()
2837 l = len(self.res)
2838 for i in range(len(self.res)):
2839 elt = self.res[i]
2840 c = self._elt2pkt(elt).canvas_dump(**kargs)
2841 cbb = c.bbox()
2842 c.text(cbb.left(),cbb.top()+1,r"\font\cmssfont=cmss12\cmssfont{Frame %i/%i}" % (i,l),[pyx.text.size.LARGE])
2843 if conf.verb >= 2:
2844 os.write(1,".")
2845 d.append(pyx.document.page(c, paperformat=pyx.document.paperformat.A4,
2846 margin=1*pyx.unit.t_cm,
2847 fittosize=1))
2848 return d
2849
2850
2851
2852 def psdump(self, filename = None, **kargs):
2853 """Creates a multipage poscript file with a psdump of every packet
2854 filename: name of the file to write to. If empty, a temporary file is used and
2855 conf.prog.psreader is called"""
2856 d = self._dump_document(**kargs)
2857 if filename is None:
2858 filename = "/tmp/scapy.psd.%i" % os.getpid()
2859 d.writePSfile(filename)
2860 os.system("%s %s.ps &" % (conf.prog.psreader,filename))
2861 else:
2862 d.writePSfile(filename)
2863 print
2864
2865 def pdfdump(self, filename = None, **kargs):
2866 """Creates a PDF file with a psdump of every packet
2867 filename: name of the file to write to. If empty, a temporary file is used and
2868 conf.prog.pdfreader is called"""
2869 d = self._dump_document(**kargs)
2870 if filename is None:
2871 filename = "/tmp/scapy.psd.%i" % os.getpid()
2872 d.writePDFfile(filename)
2873 os.system("%s %s.pdf &" % (conf.prog.pdfreader,filename))
2874 else:
2875 d.writePDFfile(filename)
2876 print
2877
2878 def sr(self,multi=0):
2879 """sr([multi=1]) -> (SndRcvList, PacketList)
2880 Matches packets in the list and return ( (matched couples), (unmatched packets) )"""
2881 remain = self.res[:]
2882 sr = []
2883 i = 0
2884 while i < len(remain):
2885 s = remain[i]
2886 j = i
2887 while j < len(remain)-1:
2888 j += 1
2889 r = remain[j]
2890 if r.answers(s):
2891 sr.append((s,r))
2892 if multi:
2893 remain[i]._answered=1
2894 remain[j]._answered=2
2895 continue
2896 del(remain[j])
2897 del(remain[i])
2898 i -= 1
2899 break
2900 i += 1
2901 if multi:
2902 remain = filter(lambda x:not hasattr(x,"_answered"), remain)
2903 return SndRcvList(sr),PacketList(remain)
2904
2905
2906
2907
2908
2909
2910class Dot11PacketList(PacketList):
2911 def __init__(self, res=None, name="Dot11List", stats=None):
2912 if stats is None:
2913 stats = [Dot11WEP, Dot11Beacon, UDP, ICMP, TCP]
2914
2915 PacketList.__init__(self, res, name, stats)
2916 def toEthernet(self):
2917 data = map(lambda x:x.getlayer(Dot11), filter(lambda x : x.haslayer(Dot11) and x.type == 2, self.res))
2918 r2 = []
2919 for p in data:
2920 q = p.copy()
2921 q.unwep()
2922 r2.append(Ether()/q.payload.payload.payload) #Dot11/LLC/SNAP/IP
2923 return PacketList(r2,name="Ether from %s"%self.listname)
2924
2925
2926
2927class SndRcvList(PacketList):
2928 def __init__(self, res=None, name="Results", stats=None):
2929 PacketList.__init__(self, res, name, stats)
2930 def _elt2pkt(self, elt):
2931 return elt[1]
2932 def _elt2sum(self, elt):
2933 return "%s ==> %s" % (elt[0].summary(),elt[1].summary())
2934
2935
2936class ARPingResult(SndRcvList):
2937 def __init__(self, res=None, name="ARPing", stats=None):
2938 PacketList.__init__(self, res, name, stats)
2939
2940 def show(self):
2941 for s,r in self.res:
2942 print r.sprintf("%Ether.src% %ARP.psrc%")
2943
2944
2945class AS_resolver:
2946 server = None
2947 options = "-k"
2948 def __init__(self, server=None, port=43, options=None):
2949 if server is not None:
2950 self.server = server
2951 self.port = port
2952 if options is not None:
2953 self.options = options
2954
2955 def _start(self):
2956 self.s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
2957 self.s.connect((self.server,self.port))
2958 if self.options:
2959 self.s.send(self.options+"\n")
2960 self.s.recv(8192)
2961 def _stop(self):
2962 self.s.close()
2963
2964 def _parse_whois(self, txt):
2965 asn,desc = None,""
2966 for l in txt.splitlines():
2967 if not asn and l.startswith("origin:"):
2968 asn = l[7:].strip()
2969 if l.startswith("descr:"):
2970 if desc:
2971 desc += r"\n"
2972 desc += l[6:].strip()
2973 if asn is not None and desc:
2974 break
2975 return asn,desc.strip()
2976
2977 def _resolve_one(self, ip):
2978 self.s.send("%s\n" % ip)
2979 x = ""
2980 while not ("%" in x or "source" in x):
2981 x += self.s.recv(8192)
2982 asn, desc = self._parse_whois(x)
2983 return ip,asn,desc
2984 def resolve(self, *ips):
2985 self._start()
2986 ret = []
2987 for ip in ips:
2988 ip,asn,desc = self._resolve_one(ip)
2989 if asn is not None:
2990 ret.append((ip,asn,desc))
2991 self._stop()
2992 return ret
2993
2994class AS_resolver_riswhois(AS_resolver):
2995 server = "riswhois.ripe.net"
2996 options = "-k -M -1"
2997
2998
2999class AS_resolver_radb(AS_resolver):
3000 server = "whois.ra.net"
3001 options = "-k -M"
3002
3003
3004class AS_resolver_cymru(AS_resolver):
3005 server = "whois.cymru.com"
3006 options = None
3007 def resolve(self, *ips):
3008 ASNlist = []
3009 s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
3010 s.connect((self.server,self.port))
3011 s.send("begin\r\n"+"\r\n".join(ips)+"\r\nend\r\n")
3012 r = ""
3013 while 1:
3014 l = s.recv(8192)
3015 if l == "":
3016 break
3017 r += l
3018 s.close()
3019 for l in r.splitlines()[1:]:
3020 if "|" not in l:
3021 continue
3022 asn,ip,desc = map(str.strip, l.split("|"))
3023 if asn == "NA":
3024 continue
3025 asn = int(asn)
3026 ASNlist.append((ip,asn,desc))
3027 return ASNlist
3028
3029class AS_resolver_multi(AS_resolver):
3030 resolvers_list = ( AS_resolver_cymru(),AS_resolver_riswhois(),AS_resolver_radb() )
3031 def __init__(self, *reslist):
3032 if reslist:
3033 self.resolvers_list = reslist
3034 def resolve(self, *ips):
3035 todo = ips
3036 ret = []
3037 for ASres in self.resolvers_list:
3038 res = ASres.resolve(*todo)
3039 resolved = [ ip for ip,asn,desc in res ]
3040 todo = [ ip for ip in todo if ip not in resolved ]
3041 ret += res
3042 return ret
3043
3044
3045
3046class TracerouteResult(SndRcvList):
3047 def __init__(self, res=None, name="Traceroute", stats=None):
3048 PacketList.__init__(self, res, name, stats)
3049 self.graphdef = None
3050 self.graphASres = 0
3051 self.padding = 0
3052 self.hloc = None
3053 self.nloc = None
3054
3055 def show(self):
3056 return self.make_table(lambda (s,r): (s.sprintf("%IP.dst%:{TCP:tcp%ir,TCP.dport%}{UDP:udp%ir,UDP.dport%}{ICMP:ICMP}"),
3057 s.ttl,
3058 r.sprintf("%-15s,IP.src% {TCP:%TCP.flags%}{ICMP:%ir,ICMP.type%}")))
3059
3060
3061 def get_trace(self):
3062 trace = {}
3063 for s,r in self.res:
3064 if IP not in s:
3065 continue
3066 d = s[IP].dst
3067 if d not in trace:
3068 trace[d] = {}
3069 trace[d][s[IP].ttl] = r[IP].src, ICMP not in r
3070 for k in trace.values():
3071 m = filter(lambda x:k[x][1], k.keys())
3072 if not m:
3073 continue
3074 m = min(m)
3075 for l in k.keys():
3076 if l > m:
3077 del(k[l])
3078 return trace
3079
3080 def trace3D(self):
3081 """Give a 3D representation of the traceroute.
3082 right button: rotate the scene
3083 middle button: zoom
3084 left button: move the scene
3085 left button on a ball: toggle IP displaying
3086 ctrl-left button on a ball: scan ports 21,22,23,25,80 and 443 and display the result"""
3087 trace = self.get_trace()
3088 import visual
3089
3090 class IPsphere(visual.sphere):
3091 def __init__(self, ip, **kargs):
3092 visual.sphere.__init__(self, **kargs)
3093 self.ip=ip
3094 self.label=None
3095 self.setlabel(self.ip)
3096 def setlabel(self, txt,visible=None):
3097 if self.label is not None:
3098 if visible is None:
3099 visible = self.label.visible
3100 self.label.visible = 0
3101 elif visible is None:
3102 visible=0
3103 self.label=visual.label(text=txt, pos=self.pos, space=self.radius, xoffset=10, yoffset=20, visible=visible)
3104 def action(self):
3105 self.label.visible ^= 1
3106
3107 visual.scene = visual.display()
3108 visual.scene.exit_on_close(0)
3109 start = visual.box()
3110 rings={}
3111 tr3d = {}
3112 for i in trace:
3113 tr = trace[i]
3114 tr3d[i] = []
3115 ttl = tr.keys()
3116 for t in range(1,max(ttl)+1):
3117 if t not in rings:
3118 rings[t] = []
3119 if t in tr:
3120 if tr[t] not in rings[t]:
3121 rings[t].append(tr[t])
3122 tr3d[i].append(rings[t].index(tr[t]))
3123 else:
3124 rings[t].append(("unk",-1))
3125 tr3d[i].append(len(rings[t])-1)
3126 for t in rings:
3127 r = rings[t]
3128 l = len(r)
3129 for i in range(l):
3130 if r[i][1] == -1:
3131 col = (0.75,0.75,0.75)
3132 elif r[i][1]:
3133 col = visual.color.green
3134 else:
3135 col = visual.color.blue
3136
3137 s = IPsphere(pos=((l-1)*visual.cos(2*i*visual.pi/l),(l-1)*visual.sin(2*i*visual.pi/l),2*t),
3138 ip = r[i][0],
3139 color = col)
3140 for trlst in tr3d.values():
3141 if t <= len(trlst):
3142 if trlst[t-1] == i:
3143 trlst[t-1] = s
3144 forecol = colgen(0.625, 0.4375, 0.25, 0.125)
3145 for trlst in tr3d.values():
3146 col = forecol.next()
3147 start = (0,0,0)
3148 for ip in trlst:
3149 visual.cylinder(pos=start,axis=ip.pos-start,color=col,radius=0.2)
3150 start = ip.pos
3151
3152 movcenter=None
3153 while 1:
3154 if visual.scene.kb.keys:
3155 k = visual.scene.kb.getkey()
3156 if k == "esc":
3157 break
3158 if visual.scene.mouse.events:
3159 ev = visual.scene.mouse.getevent()
3160 if ev.press == "left":
3161 o = ev.pick
3162 if o:
3163 if ev.ctrl:
3164 if o.ip == "unk":
3165 continue
3166 savcolor = o.color
3167 o.color = (1,0,0)
3168 a,b=sr(IP(dst=o.ip)/TCP(dport=[21,22,23,25,80,443]),timeout=2)
3169 o.color = savcolor
3170 if len(a) == 0:
3171 txt = "%s:\nno results" % o.ip
3172 else:
3173 txt = "%s:\n" % o.ip
3174 for s,r in a:
3175 txt += r.sprintf("{TCP:%IP.src%:%TCP.sport% %TCP.flags%}{TCPerror:%IPerror.dst%:%TCPerror.dport% %IP.src% %ir,ICMP.type%}\n")
3176 o.setlabel(txt, visible=1)
3177 else:
3178 if hasattr(o, "action"):
3179 o.action()
3180 elif ev.drag == "left":
3181 movcenter = ev.pos
3182 elif ev.drop == "left":
3183 movcenter = None
3184 if movcenter:
3185 visual.scene.center -= visual.scene.mouse.pos-movcenter
3186 movcenter = visual.scene.mouse.pos
3187
3188
3189 def world_trace(self):
3190 ips = {}
3191 rt = {}
3192 ports_done = {}
3193 for s,r in self.res:
3194 ips[r.src] = None
3195 if s.haslayer(TCP) or s.haslayer(UDP):
3196 trace_id = (s.src,s.dst,s.proto,s.dport)
3197 elif s.haslayer(ICMP):
3198 trace_id = (s.src,s.dst,s.proto,s.type)
3199 else:
3200 trace_id = (s.src,s.dst,s.proto,0)
3201 trace = rt.get(trace_id,{})
3202 if not r.haslayer(ICMP) or r.type != 11:
3203 if ports_done.has_key(trace_id):
3204 continue
3205 ports_done[trace_id] = None
3206 trace[s.ttl] = r.src
3207 rt[trace_id] = trace
3208
3209 trt = {}
3210 for trace_id in rt:
3211 trace = rt[trace_id]
3212 loctrace = []
3213 for i in range(max(trace.keys())):
3214 ip = trace.get(i,None)
3215 if ip is None:
3216 continue
3217 loc = locate_ip(ip)
3218 if loc is None:
3219 continue
3220# loctrace.append((ip,loc)) # no labels yet
3221 loctrace.append(loc)
3222 if loctrace:
3223 trt[trace_id] = loctrace
3224
3225 tr = map(lambda x: Gnuplot.Data(x,with="lines"), trt.values())
3226 g = Gnuplot.Gnuplot()
3227 world = Gnuplot.File(conf.gnuplot_world,with="lines")
3228 g.plot(world,*tr)
3229 return g
3230
3231 def make_graph(self,ASres=None,padding=0):
3232 if ASres is None:
3233 ASres = conf.AS_resolver
3234 self.graphASres = ASres
3235 self.graphpadding = padding
3236 ips = {}
3237 rt = {}
3238 ports = {}
3239 ports_done = {}
3240 for s,r in self.res:
3241 r = r[IP] or r[IPv6] or r
3242 s = s[IP] or s[IPv6] or s
3243 ips[r.src] = None
3244 if TCP in s:
3245 trace_id = (s.src,s.dst,6,s.dport)
3246 elif UDP in s:
3247 trace_id = (s.src,s.dst,17,s.dport)
3248 elif ICMP in s:
3249 trace_id = (s.src,s.dst,1,s.type)
3250 else:
3251 trace_id = (s.src,s.dst,s.proto,0)
3252 trace = rt.get(trace_id,{})
3253 ttl = IPv6 in s and s.hlim or s.ttl
3254 if not (ICMP in r and r[ICMP].type == 11) and not (IPv6 in r and ICMPv6TimeExceeded in r):
3255 if trace_id in ports_done:
3256 continue
3257 ports_done[trace_id] = None
3258 p = ports.get(r.src,[])
3259 if TCP in r:
3260 p.append(r.sprintf("<T%ir,TCP.sport%> %TCP.sport% %TCP.flags%"))
3261 trace[ttl] = r.sprintf('"%r,src%":T%ir,TCP.sport%')
3262 elif UDP in r:
3263 p.append(r.sprintf("<U%ir,UDP.sport%> %UDP.sport%"))
3264 trace[ttl] = r.sprintf('"%r,src%":U%ir,UDP.sport%')
3265 elif ICMP in r:
3266 p.append(r.sprintf("<I%ir,ICMP.type%> ICMP %ICMP.type%"))
3267 trace[ttl] = r.sprintf('"%r,src%":I%ir,ICMP.type%')
3268 else:
3269 p.append(r.sprintf("{IP:<P%ir,proto%> IP %proto%}{IPv6:<P%ir,nh%> IPv6 %nh%}"))
3270 trace[ttl] = r.sprintf('"%r,src%":{IP:P%ir,proto%}{IPv6:P%ir,nh%}')
3271 ports[r.src] = p
3272 else:
3273 trace[ttl] = r.sprintf('"%r,src%"')
3274 rt[trace_id] = trace
3275
3276 # Fill holes with unk%i nodes
3277 unknown_label = incremental_label("unk%i")
3278 blackholes = []
3279 bhip = {}
3280 for rtk in rt:
3281 trace = rt[rtk]
3282 k = trace.keys()
3283 for n in range(min(k), max(k)):
3284 if not trace.has_key(n):
3285 trace[n] = unknown_label.next()
3286 if not ports_done.has_key(rtk):
3287 if rtk[2] == 1: #ICMP
3288 bh = "%s %i/icmp" % (rtk[1],rtk[3])
3289 elif rtk[2] == 6: #TCP
3290 bh = "%s %i/tcp" % (rtk[1],rtk[3])
3291 elif rtk[2] == 17: #UDP
3292 bh = '%s %i/udp' % (rtk[1],rtk[3])
3293 else:
3294 bh = '%s %i/proto' % (rtk[1],rtk[2])
3295 ips[bh] = None
3296 bhip[rtk[1]] = bh
3297 bh = '"%s"' % bh
3298 trace[max(k)+1] = bh
3299 blackholes.append(bh)
3300
3301 # Find AS numbers
3302 ASN_query_list = dict.fromkeys(map(lambda x:x.rsplit(" ",1)[0],ips)).keys()
3303 if ASres is None:
3304 ASNlist = []
3305 else:
3306 ASNlist = ASres.resolve(*ASN_query_list)
3307
3308 ASNs = {}
3309 ASDs = {}
3310 for ip,asn,desc, in ASNlist:
3311 if asn is None:
3312 continue
3313 iplist = ASNs.get(asn,[])
3314 if ip in bhip:
3315 if ip in ports:
3316 iplist.append(ip)
3317 iplist.append(bhip[ip])
3318 else:
3319 iplist.append(ip)
3320 ASNs[asn] = iplist
3321 ASDs[asn] = desc
3322
3323
3324 backcolorlist=colgen("60","86","ba","ff")
3325 forecolorlist=colgen("a0","70","40","20")
3326
3327 s = "digraph trace {\n"
3328
3329 s += "\n\tnode [shape=ellipse,color=black,style=solid];\n\n"
3330
3331 s += "\n#ASN clustering\n"
3332 for asn in ASNs:
3333 s += '\tsubgraph cluster_%s {\n' % asn
3334 col = backcolorlist.next()
3335 s += '\t\tcolor="#%s%s%s";' % col
3336 s += '\t\tnode [fillcolor="#%s%s%s",style=filled];' % col
3337 s += '\t\tfontsize = 10;'
3338 s += '\t\tlabel = "%s\\n[%s]"\n' % (asn,ASDs[asn])
3339 for ip in ASNs[asn]:
3340
3341 s += '\t\t"%s";\n'%ip
3342 s += "\t}\n"
3343
3344
3345
3346
3347 s += "#endpoints\n"
3348 for p in ports:
3349 s += '\t"%s" [shape=record,color=black,fillcolor=green,style=filled,label="%s|%s"];\n' % (p,p,"|".join(ports[p]))
3350
3351 s += "\n#Blackholes\n"
3352 for bh in blackholes:
3353 s += '\t%s [shape=octagon,color=black,fillcolor=red,style=filled];\n' % bh
3354
3355 if padding:
3356 s += "\n#Padding\n"
3357 pad={}
3358 for snd,rcv in self.res:
3359 if rcv.src not in ports and rcv.haslayer(Padding):
3360 p = rcv.getlayer(Padding).load
3361 if p != "\x00"*len(p):
3362 pad[rcv.src]=None
3363 for rcv in pad:
3364 s += '\t"%s" [shape=triangle,color=black,fillcolor=red,style=filled];\n' % rcv
3365
3366
3367
3368 s += "\n\tnode [shape=ellipse,color=black,style=solid];\n\n"
3369
3370
3371 for rtk in rt:
3372 s += "#---[%s\n" % `rtk`
3373 s += '\t\tedge [color="#%s%s%s"];\n' % forecolorlist.next()
3374 trace = rt[rtk]
3375 k = trace.keys()
3376 for n in range(min(k), max(k)):
3377 s += '\t%s ->\n' % trace[n]
3378 s += '\t%s;\n' % trace[max(k)]
3379
3380 s += "}\n";
3381 self.graphdef = s
3382
3383 def graph(self, ASres=None, padding=0, **kargs):
3384 """x.graph(ASres=conf.AS_resolver, other args):
3385 ASres=None : no AS resolver => no clustering
3386 ASres=AS_resolver() : default whois AS resolver (riswhois.ripe.net)
3387 ASres=AS_resolver_cymru(): use whois.cymru.com whois database
3388 ASres=AS_resolver(server="whois.ra.net")
3389 type: output type (svg, ps, gif, jpg, etc.), passed to dot's "-T" option
3390 target: filename or redirect. Defaults pipe to Imagemagick's display program
3391 prog: which graphviz program to use"""
3392 if ASres is None:
3393 ASres = conf.AS_resolver
3394 if (self.graphdef is None or
3395 self.graphASres != ASres or
3396 self.graphpadding != padding):
3397 self.make_graph(ASres,padding)
3398
3399 return do_graph(self.graphdef, **kargs)
3400
3401
3402
3403
3404############
3405## Fields ##
3406############
3407
3408class Field:
3409 """For more informations on how this work, please refer to
3410 http://www.secdev.org/projects/scapy/files/scapydoc.pdf
3411 chapter ``Adding a New Field''"""
3412 islist=0
3413 holds_packets=0
3414 def __init__(self, name, default, fmt="H"):
3415 self.name = name
3416 if fmt[0] in "@=<>!":
3417 self.fmt = fmt
3418 else:
3419 self.fmt = "!"+fmt
3420 self.default = self.any2i(None,default)
3421 self.sz = struct.calcsize(self.fmt)
3422 self.owners = []
3423
3424 def register_owner(self, cls):
3425 self.owners.append(cls)
3426
3427 def i2len(self, pkt, x):
3428 """Convert internal value to a length usable by a FieldLenField"""
3429 return self.sz
3430 def i2count(self, pkt, x):
3431 """Convert internal value to a number of elements usable by a FieldLenField.
3432 Always 1 except for list fields"""
3433 return 1
3434 def h2i(self, pkt, x):
3435 """Convert human value to internal value"""
3436 return x
3437 def i2h(self, pkt, x):
3438 """Convert internal value to human value"""
3439 return x
3440 def m2i(self, pkt, x):
3441 """Convert machine value to internal value"""
3442 return x
3443 def i2m(self, pkt, x):
3444 """Convert internal value to machine value"""
3445 if x is None:
3446 x = 0
3447 return x
3448 def any2i(self, pkt, x):
3449 """Try to understand the most input values possible and make an internal value from them"""
3450 return self.h2i(pkt, x)
3451 def i2repr(self, pkt, x):
3452 """Convert internal value to a nice representation"""
3453 if x is None:
3454 x = 0
3455 return repr(self.i2h(pkt,x))
3456 def addfield(self, pkt, s, val):
3457 """Add an internal value to a string"""
3458 return s+struct.pack(self.fmt, self.i2m(pkt,val))
3459 def getfield(self, pkt, s):
3460 """Extract an internal value from a string"""
3461 return s[self.sz:], self.m2i(pkt, struct.unpack(self.fmt, s[:self.sz])[0])
3462 def do_copy(self, x):
3463 if hasattr(x, "copy"):
3464 return x.copy()
3465 if type(x) is list:
3466 x = x[:]
3467 for i in xrange(len(x)):
3468 if isinstance(x[i], Packet):
3469 x[i] = x[i].copy()
3470 return x
3471 def __repr__(self):
3472 return "<Field (%s).%s>" % (",".join(x.__name__ for x in self.owners),self.name)
3473 def copy(self):
3474 return copy.deepcopy(self)
3475 def randval(self):
3476 """Return a volatile object whose value is both random and suitable for this field"""
3477 fmtt = self.fmt[-1]
3478 if fmtt in "BHIQ":
3479 return {"B":RandByte,"H":RandShort,"I":RandInt, "Q":RandLong}[fmtt]()
3480 elif fmtt == "s":
3481 if self.fmt[0] in "0123456789":
3482 l = int(self.fmt[:-1])
3483 else:
3484 l = int(self.fmt[1:-1])
3485 return RandBin(l)
3486 else:
3487 warning("no random class for [%s] (fmt=%s)." % (self.name, self.fmt))
3488
3489
3490
3491
3492class Emph:
3493 fld = ""
3494 def __init__(self, fld):
3495 self.fld = fld
3496 def __getattr__(self, attr):
3497 return getattr(self.fld,attr)
3498 def __hash__(self):
3499 return hash(self.fld)
3500 def __eq__(self, other):
3501 return self.fld == other
3502
3503
3504class ActionField:
3505 _fld = None
3506 def __init__(self, fld, action_method, **kargs):
3507 self._fld = fld
3508 self._action_method = action_method
3509 self._privdata = kargs
3510 def any2i(self, pkt, val):
3511 getattr(pkt, self._action_method)(val, self._fld, **self._privdata)
3512 return getattr(self._fld, "any2i")(pkt, val)
3513 def __getattr__(self, attr):
3514 return getattr(self._fld,attr)
3515
3516
3517class ConditionalField:
3518 fld = None
3519 def __init__(self, fld, cond):
3520 self.fld = fld
3521 self.cond = cond
3522 def _evalcond(self,pkt):
3523 return self.cond(pkt)
3524
3525 def getfield(self, pkt, s):
3526 if self._evalcond(pkt):
3527 return self.fld.getfield(pkt,s)
3528 else:
3529 return s,None
3530
3531 def addfield(self, pkt, s, val):
3532 if self._evalcond(pkt):
3533 return self.fld.addfield(pkt,s,val)
3534 else:
3535 return s
3536 def __getattr__(self, attr):
3537 return getattr(self.fld,attr)
3538
3539
3540class PadField:
3541 """Add bytes after the proxified field so that it ends at the specified
3542 alignment from its begining"""
3543 _fld = None
3544 def __init__(self, fld, align, padwith=None):
3545 self._fld = fld
3546 self._align = align
3547 self._padwith = padwith or ""
3548
3549 def addfield(self, pkt, s, val):
3550 sval = self._fld.addfield(pkt, "", val)
3551 return s+sval+struct.pack("%is" % (-len(sval)%self._align), self._padwith)
3552
3553 def __getattr__(self, attr):
3554 return getattr(self._fld,attr)
3555
3556
3557class MACField(Field):
3558 def __init__(self, name, default):
3559 Field.__init__(self, name, default, "6s")
3560 def i2m(self, pkt, x):
3561 if x is None:
3562 return "\0\0\0\0\0\0"
3563 return mac2str(x)
3564 def m2i(self, pkt, x):
3565 return str2mac(x)
3566 def any2i(self, pkt, x):
3567 if type(x) is str and len(x) is 6:
3568 x = self.m2i(pkt, x)
3569 return x
3570 def i2repr(self, pkt, x):
3571 x = self.i2h(pkt, x)
3572 if self in conf.resolve:
3573 x = conf.manufdb._resolve_MAC(x)
3574 return x
3575 def randval(self):
3576 return RandMAC()
3577
3578class DestMACField(MACField):
3579 def __init__(self, name):
3580 MACField.__init__(self, name, None)
3581 def i2h(self, pkt, x):
3582 if x is None:
3583 dstip = None
3584 if isinstance(pkt.payload, IPv6):
3585 dstip = pkt.payload.dst
3586 elif isinstance(pkt.payload, IP):
3587 dstip = pkt.payload.dst
3588 elif isinstance(pkt.payload, ARP):
3589 dstip = pkt.payload.pdst
3590 if isinstance(dstip, Gen):
3591 dstip = dstip.__iter__().next()
3592 if dstip is not None:
3593 if isinstance(pkt.payload, IPv6):
3594 x = getmacbyip6(dstip, chainCC=1)
3595 else:
3596 x = getmacbyip(dstip, chainCC=1)
3597 if x is None:
3598 x = "ff:ff:ff:ff:ff:ff"
3599 warning("Mac address to reach %s not found\n"%dstip)
3600 return MACField.i2h(self, pkt, x)
3601 def i2m(self, pkt, x):
3602 return MACField.i2m(self, pkt, self.i2h(pkt, x))
3603
3604class SourceMACField(MACField):
3605 def __init__(self, name):
3606 MACField.__init__(self, name, None)
3607 def i2h(self, pkt, x):
3608 if x is None:
3609 dstip = None
3610 if isinstance(pkt.payload, IPv6):
3611 dstip = pkt.payload.dst
3612 elif isinstance(pkt.payload, IP):
3613 dstip = pkt.payload.dst
3614 elif isinstance(pkt.payload, ARP):
3615 dstip = pkt.payload.pdst
3616 if isinstance(dstip, Gen):
3617 dstip = dstip.__iter__().next()
3618 if dstip is not None:
3619 if isinstance(pkt.payload, IPv6):
3620 iff,a,nh = conf.route6.route(dstip)
3621 else:
3622 iff,a,gw = conf.route.route(dstip)
3623 try:
3624 x = get_if_hwaddr(iff)
3625 except:
3626 pass
3627 if x is None:
3628 x = "00:00:00:00:00:00"
3629 return MACField.i2h(self, pkt, x)
3630 def i2m(self, pkt, x):
3631 return MACField.i2m(self, pkt, self.i2h(pkt, x))
3632
3633class ARPSourceMACField(MACField):
3634 def __init__(self, name):
3635 MACField.__init__(self, name, None)
3636 def i2h(self, pkt, x):
3637 if x is None:
3638 dstip = pkt.pdst
3639 if isinstance(dstip, Gen):
3640 dstip = dstip.__iter__().next()
3641 if dstip is not None:
3642 iff,a,gw = conf.route.route(dstip)
3643 try:
3644 x = get_if_hwaddr(iff)
3645 except:
3646 pass
3647 if x is None:
3648 x = "00:00:00:00:00:00"
3649 return MACField.i2h(self, pkt, x)
3650 def i2m(self, pkt, x):
3651 return MACField.i2m(self, pkt, self.i2h(pkt, x))
3652
3653class Dot11AddrMACField(MACField):
3654 def is_applicable(self, pkt):
3655 return 1
3656 def addfield(self, pkt, s, val):
3657 if self.is_applicable(pkt):
3658 return MACField.addfield(self, pkt, s, val)
3659 else:
3660 return s
3661 def getfield(self, pkt, s):
3662 if self.is_applicable(pkt):
3663 return MACField.getfield(self, pkt, s)
3664 else:
3665 return s,None
3666
3667class Dot11Addr2MACField(Dot11AddrMACField):
3668 def is_applicable(self, pkt):
3669 if pkt.type == 1:
3670 return pkt.subtype in [ 0xb, 0xa, 0xe, 0xf] # RTS, PS-Poll, CF-End, CF-End+CF-Ack
3671 return 1
3672
3673class Dot11Addr3MACField(Dot11AddrMACField):
3674 def is_applicable(self, pkt):
3675 if pkt.type in [0,2]:
3676 return 1
3677 return 0
3678
3679class Dot11Addr4MACField(Dot11AddrMACField):
3680 def is_applicable(self, pkt):
3681 if pkt.type == 2:
3682 if pkt.FCfield & 0x3 == 0x3: # To-DS and From-DS are set
3683 return 1
3684 return 0
3685
3686class IPField(Field):
3687 def __init__(self, name, default):
3688 Field.__init__(self, name, default, "4s")
3689 def h2i(self, pkt, x):
3690 if type(x) is str:
3691 try:
3692 inet_aton(x)
3693 except socket.error:
3694 x = Net(x)
3695 elif type(x) is list:
3696 x = [self.h2i(pkt, n) for n in x]
3697 return x
3698 def resolve(self, x):
3699 if self in conf.resolve:
3700 try:
3701 ret = socket.gethostbyaddr(x)[0]
3702 except:
3703 pass
3704 else:
3705 if ret:
3706 return ret
3707 return x
3708 def i2m(self, pkt, x):
3709 return inet_aton(x)
3710 def m2i(self, pkt, x):
3711 return inet_ntoa(x)
3712 def any2i(self, pkt, x):
3713 return self.h2i(pkt,x)
3714 def i2repr(self, pkt, x):
3715 return self.resolve(self.i2h(pkt, x))
3716 def randval(self):
3717 return RandIP()
3718
3719class SourceIPField(IPField):
3720 def __init__(self, name, dstname):
3721 IPField.__init__(self, name, None)
3722 self.dstname = dstname
3723 def i2m(self, pkt, x):
3724 if x is None:
3725 iff,x,gw = conf.route.route(getattr(pkt,self.dstname))
3726 return IPField.i2m(self, pkt, x)
3727 def i2h(self, pkt, x):
3728 if x is None:
3729 dst=getattr(pkt,self.dstname)
3730 if isinstance(dst,Gen):
3731 r = map(conf.route.route, dst)
3732 r.sort()
3733 if r[0] == r[-1]:
3734 x=r[0][1]
3735 else:
3736 warning("More than one possible route for %s"%repr(dst))
3737 return None
3738 else:
3739 iff,x,gw = conf.route.route(dst)
3740 return IPField.i2h(self, pkt, x)
3741
3742
3743
3744
3745class ByteField(Field):
3746 def __init__(self, name, default):
3747 Field.__init__(self, name, default, "B")
3748
3749class XByteField(ByteField):
3750 def i2repr(self, pkt, x):
3751 if x is None:
3752 x = 0
3753 return lhex(self.i2h(pkt, x))
3754
3755class X3BytesField(XByteField):
3756 def __init__(self, name, default):
3757 Field.__init__(self, name, default, "!I")
3758 def addfield(self, pkt, s, val):
3759 return s+struct.pack(self.fmt, self.i2m(pkt,val))[1:4]
3760 def getfield(self, pkt, s):
3761 return s[3:], self.m2i(pkt, struct.unpack(self.fmt, "\x00"+s[:3])[0])
3762
3763
3764class ShortField(Field):
3765 def __init__(self, name, default):
3766 Field.__init__(self, name, default, "H")
3767
3768class LEShortField(Field):
3769 def __init__(self, name, default):
3770 Field.__init__(self, name, default, "<H")
3771
3772class XShortField(ShortField):
3773 def i2repr(self, pkt, x):
3774 if x is None:
3775 x = 0
3776 return lhex(self.i2h(pkt, x))
3777
3778
3779class IntField(Field):
3780 def __init__(self, name, default):
3781 Field.__init__(self, name, default, "I")
3782
3783class SignedIntField(Field):
3784 def __init__(self, name, default):
3785 Field.__init__(self, name, default, "i")
3786 def randval(self):
3787 return RandSInt()
3788
3789class LEIntField(Field):
3790 def __init__(self, name, default):
3791 Field.__init__(self, name, default, "<I")
3792
3793class LESignedIntField(Field):
3794 def __init__(self, name, default):
3795 Field.__init__(self, name, default, "<i")
3796 def randval(self):
3797 return RandSInt()
3798
3799class XIntField(IntField):
3800 def i2repr(self, pkt, x):
3801 if x is None:
3802 x = 0
3803 return lhex(self.i2h(pkt, x))
3804
3805
3806class LongField(Field):
3807 def __init__(self, name, default):
3808 Field.__init__(self, name, default, "Q")
3809
3810class XLongField(LongField):
3811 def i2repr(self, pkt, x):
3812 if x is None:
3813 x = 0
3814 return lhex(self.i2h(pkt, x))
3815
3816class IEEEFloatField(Field):
3817 def __init__(self, name, default):
3818 Field.__init__(self, name, default, "f")
3819
3820class IEEEDoubleField(Field):
3821 def __init__(self, name, default):
3822 Field.__init__(self, name, default, "d")
3823
3824
3825def FIELD_LENGTH_MANAGEMENT_DEPRECATION(x):
3826 try:
3827 for tb in traceback.extract_stack()+[("??",-1,None,"")]:
3828 f,l,_,line = tb
3829 if line.startswith("fields_desc"):
3830 break
3831 except:
3832 f,l="??",-1
3833 log_loading.warning("Deprecated use of %s (%s l. %i). See http://trac.secdev.org/scapy/wiki/LengthFields" % (x,f,l))
3834
3835class StrField(Field):
3836 def __init__(self, name, default, fmt="H", remain=0, shift=0):
3837 Field.__init__(self,name,default,fmt)
3838 self.remain = remain
3839 self.shift = shift
3840 if shift != 0:
3841 FIELD_LENGTH_MANAGEMENT_DEPRECATION(self.__class__.__name__)
3842 def i2len(self, pkt, i):
3843 return len(i)+self.shift
3844 def i2m(self, pkt, x):
3845 if x is None:
3846 x = ""
3847 elif type(x) is not str:
3848 x=str(x)
3849 return x
3850 def addfield(self, pkt, s, val):
3851 return s+self.i2m(pkt, val)
3852 def getfield(self, pkt, s):
3853 if self.remain == 0:
3854 return "",self.m2i(pkt, s)
3855 else:
3856 return s[-self.remain:],self.m2i(pkt, s[:-self.remain])
3857 def randval(self):
3858 return RandBin(RandNum(0,1200))
3859
3860class PacketField(StrField):
3861 holds_packets=1
3862 def __init__(self, name, default, cls, remain=0, shift=0):
3863 StrField.__init__(self, name, default, remain=remain, shift=shift)
3864 self.cls = cls
3865 def i2m(self, pkt, i):
3866 return str(i)
3867 def m2i(self, pkt, m):
3868 return self.cls(m)
3869 def getfield(self, pkt, s):
3870 i = self.m2i(pkt, s)
3871 remain = ""
3872 if i.haslayer(Padding):
3873 r = i.getlayer(Padding)
3874 del(r.underlayer.payload)
3875 remain = r.load
3876 return remain,i
3877
3878class PacketLenField(PacketField):
3879 holds_packets=1
3880 def __init__(self, name, default, cls, fld=None, length_from=None, shift=0):
3881 PacketField.__init__(self, name, default, cls, shift=shift)
3882 self.length_from = length_from
3883 if fld is not None or shift != 0:
3884 FIELD_LENGTH_MANAGEMENT_DEPRECATION(self.__class__.__name__)
3885 self.count_from = lambda pkt,fld=fld,shift=shift: getattr(pkt,fld)-shift
3886 def getfield(self, pkt, s):
3887 l = self.length_from(pkt)
3888 i = self.m2i(pkt, s[:l])
3889 return s[l:],i
3890
3891
3892class PacketListField(PacketField):
3893 islist = 1
3894 holds_packets=1
3895 def __init__(self, name, default, cls, fld=None, count_from=None, length_from=None, shift=0):
3896 if default is None:
3897 default = [] # Create a new list for each instance
3898 PacketField.__init__(self, name, default, cls, shift=shift)
3899 self.count_from = count_from
3900 self.length_from = length_from
3901
3902 if fld is not None or shift != 0:
3903 FIELD_LENGTH_MANAGEMENT_DEPRECATION(self.__class__.__name__)
3904 if fld is not None:
3905 self.count_from = lambda pkt,fld=fld,shift=shift: getattr(pkt,fld)-shift
3906
3907 def any2i(self, pkt, x):
3908 if type(x) is not list:
3909 return [x]
3910 else:
3911 return x
3912 def i2count(self, pkt, val):
3913 if type(val) is list:
3914 return len(val)
3915 return 1
3916 def i2len(self, pkt, val):
3917 return sum( len(p) for p in val )
3918 def do_copy(self, x):
3919 return map(lambda p:p.copy(), x)
3920 def getfield(self, pkt, s):
3921 c = l = None
3922 if self.length_from is not None:
3923 l = self.length_from(pkt)
3924 elif self.count_from is not None:
3925 c = self.count_from(pkt)
3926
3927 lst = []
3928 ret = ""
3929 remain = s
3930 if l is not None: