scapy.py
author Phil <phil@secdev.org>
Wed Apr 23 19:35:18 2008 +0200 (2 years ago)
changeset 806 87949e07281c
parent 805c919e540a57b
child 80719df3f1cd92b
permissions -rwxr-xr-x
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 
    23 from __future__ import generators
    24 import os
    25 
    26 VERSION = "1.2.0.2"
    27 
    28 DEFAULT_CONFIG_FILE = os.path.join(os.environ["HOME"], ".scapy_startup.py")
    29 
    30 try:
    31     os.stat(DEFAULT_CONFIG_FILE)
    32 except OSError:
    33     DEFAULT_CONFIG_FILE = None
    34 
    35 def 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 
    45 class Scapy_Exception(Exception):
    46     pass
    47 
    48 import logging,traceback,time
    49 
    50 class 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 
    78 log_scapy = logging.getLogger("scapy")
    79 console_handler = logging.StreamHandler()
    80 console_handler.setFormatter(logging.Formatter("%(levelname)s: %(message)s"))
    81 log_scapy.addHandler(console_handler)
    82 log_runtime = logging.getLogger("scapy.runtime")          # logs at runtime
    83 log_runtime.addFilter(ScapyFreqFilter())
    84 log_interactive = logging.getLogger("scapy.interactive")  # logs in interactive functions
    85 log_loading = logging.getLogger("scapy.loading")          # logs when loading scapy
    86 
    87 if __name__ == "__main__":
    88     log_scapy.setLevel(1)
    89 
    90 
    91 ##################
    92 ##### Module #####
    93 ##################
    94 
    95 import socket, sys, getopt, string, struct, random, code
    96 import cPickle, copy, types, gzip, base64, re, zlib, array
    97 from sets import Set
    98 from select import select
    99 from glob import glob
   100 from fcntl import ioctl
   101 import itertools
   102 import fcntl
   103 import warnings
   104 warnings.filterwarnings("ignore","tempnam",RuntimeWarning, __name__)
   105 
   106 
   107 try:
   108     import Gnuplot
   109     GNUPLOT=1
   110 except ImportError:
   111     log_loading.info("did not find python gnuplot wrapper . Won't be able to plot")
   112     GNUPLOT=0
   113 
   114 try:
   115     import pyx
   116     PYX=1
   117 except ImportError:
   118     log_loading.info("Can't import PyX. Won't be able to use psdump() or pdfdump()")
   119     PYX=0
   120 
   121 
   122 LINUX=sys.platform.startswith("linux")
   123 OPENBSD=sys.platform.startswith("openbsd")
   124 FREEBSD=sys.platform.startswith("freebsd")
   125 DARWIN=sys.platform.startswith("darwin")
   126 BIG_ENDIAN= struct.pack("H",1) == "\x00\x01"
   127 X86_64 = (os.uname()[4] == 'x86_64')
   128 SOLARIS=sys.platform.startswith("sunos")
   129 
   130 
   131 if LINUX:
   132     DNET=PCAP=0
   133 else:
   134     DNET=PCAP=1
   135     
   136 
   137 if 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 
   152 if 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 
   167 if 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 
   178 try:
   179     from Crypto.Cipher import ARC4
   180 except 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
   185 try:
   186     socket.inet_aton("255.255.255.255")
   187 except 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)
   193 else:
   194     inet_aton = socket.inet_aton
   195 
   196 inet_ntoa = socket.inet_ntoa
   197 try:
   198     inet_ntop = socket.inet_ntop
   199     inet_pton = socket.inet_pton
   200 except AttributeError:
   201     log_loading.info("inet_ntop/pton functions not found. Python IPv6 support not present")
   202 
   203 
   204 if SOLARIS:
   205     # GRE is missing on Solaris
   206     socket.IPPROTO_GRE = 47
   207 
   208 ###############################
   209 ## Direct Access dictionnary ##
   210 ###############################
   211 
   212 def fixname(x):
   213     if x and x[0] in "0123456789":
   214         x = "n_"+x
   215     return x.translate("________________________________________________0123456789_______ABCDEFGHIJKLMNOPQRSTUVWXYZ______abcdefghijklmnopqrstuvwxyz_____________________________________________________________________________________________________________________________________")
   216 
   217 
   218 class DADict_Exception(Scapy_Exception):
   219     pass
   220 
   221 class 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 
   290 ETHER_ANY = "\x00"*6
   291 ETHER_BROADCAST = "\xff"*6
   292 
   293 ETH_P_ALL = 3
   294 ETH_P_IP = 0x800
   295 ETH_P_ARP = 0x806
   296 
   297 # From net/if_arp.h
   298 ARPHDR_ETHER = 1
   299 ARPHDR_METRICOM = 23
   300 ARPHDR_PPP = 512
   301 ARPHDR_LOOPBACK = 772
   302 ARPHDR_TUN = 65534
   303 
   304 # From bits/ioctls.h
   305 SIOCGIFHWADDR  = 0x8927          # Get hardware address    
   306 SIOCGIFADDR    = 0x8915          # get PA address          
   307 SIOCGIFNETMASK = 0x891b          # get network PA mask     
   308 SIOCGIFNAME    = 0x8910          # get iface name          
   309 SIOCSIFLINK    = 0x8911          # set iface channel       
   310 SIOCGIFCONF    = 0x8912          # get iface list          
   311 SIOCGIFFLAGS   = 0x8913          # get flags               
   312 SIOCSIFFLAGS   = 0x8914          # set flags               
   313 SIOCGIFINDEX   = 0x8933          # name -> if_index mapping
   314 SIOCGIFCOUNT   = 0x8938          # get number of devices
   315 SIOCGSTAMP     = 0x8906          # get packet timestamp (as a timeval)
   316 
   317 
   318 # From if.h
   319 IFF_UP = 0x1               # Interface is up.
   320 IFF_BROADCAST = 0x2        # Broadcast address valid.
   321 IFF_DEBUG = 0x4            # Turn on debugging.
   322 IFF_LOOPBACK = 0x8         # Is a loopback net.
   323 IFF_POINTOPOINT = 0x10     # Interface is point-to-point link.
   324 IFF_NOTRAILERS = 0x20      # Avoid use of trailers.
   325 IFF_RUNNING = 0x40         # Resources allocated.
   326 IFF_NOARP = 0x80           # No address resolution protocol.
   327 IFF_PROMISC = 0x100        # Receive all packets.
   328 
   329 
   330 
   331 # From netpacket/packet.h
   332 PACKET_ADD_MEMBERSHIP  = 1
   333 PACKET_DROP_MEMBERSHIP = 2
   334 PACKET_RECV_OUTPUT     = 3
   335 PACKET_RX_RING         = 5
   336 PACKET_STATISTICS      = 6
   337 PACKET_MR_MULTICAST    = 0
   338 PACKET_MR_PROMISC      = 1
   339 PACKET_MR_ALLMULTI     = 2
   340 
   341 
   342 # From bits/socket.h
   343 SOL_PACKET = 263
   344 # From asm/socket.h
   345 SO_ATTACH_FILTER = 26
   346 SOL_SOCKET = 1
   347 
   348 # From net/route.h
   349 RTF_UP = 0x0001  # Route usable
   350 RTF_REJECT = 0x0200
   351 
   352 # From BSD net/bpf.h
   353 #BIOCIMMEDIATE=0x80044270
   354 BIOCIMMEDIATE=-2147204496
   355 
   356 MTU = 1600
   357 
   358  
   359 # file parsing to get some values :
   360 
   361 def 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 
   383 IP_PROTOS=load_protocols("/etc/protocols")
   384 
   385 def 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 
   409 ETHER_TYPES=load_ethertypes("/etc/ethertypes")
   410 
   411 def 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 
   439 TCP_SERVICES,UDP_SERVICES=load_services("/etc/services")
   440 
   441 class 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 
   460 def 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     
   482 MANUFDB = load_manuf("/usr/share/wireshark/wireshark/manuf")
   483 
   484 
   485 
   486 
   487 ###########
   488 ## Tools ##
   489 ###########
   490 
   491 def 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 
   501 def 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 
   511 def 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 
   521 def 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 
   538 def 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 
   548 def chexdump(x):
   549     x=str(x)
   550     print ", ".join(map(lambda x: "%#04x"%ord(x), x))
   551     
   552 def 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 
   561 def 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     
   670 crc32 = zlib.crc32
   671 
   672 if 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
   681 else:
   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 
   691 def warning(x):
   692     log_runtime.warning(x)
   693 
   694 def mac2str(mac):
   695     return "".join(map(lambda x: chr(int(x,16)), mac.split(":")))
   696 
   697 def str2mac(s):
   698     return ("%02x:"*6)[:-1] % tuple(map(ord, s)) 
   699 
   700 def strxor(x,y):
   701     return "".join(map(lambda x,y:chr(ord(x)^ord(y)),x,y))
   702 
   703 def 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]
   709 def ltoa(x):
   710     return inet_ntoa(struct.pack("!I", x))
   711 
   712 def itom(x):
   713     return (0xffffffff00000000L>>x)&0xffffffffL
   714 
   715 def 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     
   757 def tex_escape(x):
   758     s = ""
   759     for c in x:
   760         s += _TEX_TR.get(c,c)
   761     return s
   762 
   763 def 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 
   776 def incremental_label(label="tag%05i", start=0):
   777     while True:
   778         yield label % start
   779         start += 1
   780 
   781 #########################
   782 #### Enum management ####
   783 #########################
   784 
   785 class 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 
   800 class 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 
   828 def 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 
   850 def 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 
   858 def 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 
   866 def export_object(obj):
   867     print base64.encodestring(gzip.zlib.compress(cPickle.dumps(obj,2),9))
   868 
   869 def 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 
   875 def save_object(fname, obj):
   876     cPickle.dump(obj,gzip.open(fname,"wb"))
   877 
   878 def load_object(fname):
   879     return cPickle.load(gzip.open(fname,"rb"))
   880 
   881 
   882 ######################
   883 ## Extension system ##
   884 ######################
   885 
   886 
   887 def 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 
   926 class debug:
   927     recv=[]
   928     sent=[]
   929     match=[]
   930 
   931 
   932 ####################
   933 ## IP Tools class ##
   934 ####################
   935 
   936 class 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 
   952 class 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 
  1093 if 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
  1106 else:
  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 
  1117 if 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     
  1132 else:
  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 
  1188 if 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             
  1272 else:
  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     
  1331 def get_if_addr(iff):
  1332     return inet_ntoa(get_if_raw_addr(iff))
  1333     
  1334 def 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 
  1347 ARPTIMEOUT=120
  1348 
  1349 # XXX Fill arp_cache with /etc/ether and arp cache
  1350 arp_cache={}
  1351 
  1352 if 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()
  1368 else:
  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 
  1402 def 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 
  1441 class 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 
  1452 class RandField(VolatileValue):
  1453     pass
  1454 
  1455 
  1456 class 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 
  1464 class 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 
  1471 class 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 
  1478 class RandNumExpo(RandField):
  1479     def __init__(self, lambd):
  1480         self.lambd = lambd
  1481     def _fix(self):
  1482         return int(round(random.expovariate(self.lambd)))
  1483 
  1484 class RandByte(RandNum):
  1485     def __init__(self):
  1486         RandNum.__init__(self, 0, 2L**8-1)
  1487 
  1488 class RandShort(RandNum):
  1489     def __init__(self):
  1490         RandNum.__init__(self, 0, 2L**16-1)
  1491 
  1492 class RandInt(RandNum):
  1493     def __init__(self):
  1494         RandNum.__init__(self, 0, 2L**32-1)
  1495 
  1496 class RandSInt(RandNum):
  1497     def __init__(self):
  1498         RandNum.__init__(self, -2L**31, 2L**31-1)
  1499 
  1500 class RandLong(RandNum):
  1501     def __init__(self):
  1502         RandNum.__init__(self, 0, 2L**64-1)
  1503 
  1504 class RandSLong(RandNum):
  1505     def __init__(self):
  1506         RandNum.__init__(self, -2L**63, 2L**63-1)
  1507 
  1508 class 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     
  1516 class 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 
  1526 class RandBin(RandString):
  1527     def __init__(self, size):
  1528         RandString.__init__(self, size, "".join(map(chr,range(256))))
  1529 
  1530 
  1531 class 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 
  1540 class 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 
  1546 class 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 
  1564 class 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 
  1598 class 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 
  1617 class 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 
  1641 class 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             
  1650 class IntAutoTime(AutoTime):
  1651     def _fix(self):
  1652         return int(time.time()-self.diff)
  1653 
  1654 
  1655 class 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 
  1662 class 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 
  1670 class 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 
  1683 def 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 
  1692 def 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     
  1702 class 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 
  1710 class CorruptedBits(CorruptedBytes):
  1711     def _fix(self):
  1712         return corrupt_bits(self.s, self.p, self.n)
  1713 
  1714 ##############
  1715 #### ASN1 ####
  1716 ##############
  1717 
  1718 class ASN1_Error(Exception):
  1719     pass
  1720 
  1721 class ASN1_Encoding_Error(ASN1_Error):
  1722     pass
  1723 
  1724 class ASN1_Decoding_Error(ASN1_Error):
  1725     pass
  1726 
  1727 class ASN1_BadTag_Decoding_Error(ASN1_Decoding_Error):
  1728     pass
  1729 
  1730 
  1731 
  1732 class 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 
  1743 class ASN1_Codecs_metaclass(Enum_metaclass):
  1744     element_class = ASN1Codec
  1745 
  1746 class 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 
  1758 class 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 
  1782 class 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 
  1807 class ASN1_Class:
  1808     __metaclass__ = ASN1_Class_metaclass
  1809 
  1810 class 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 
  1847 class 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 
  1857 class 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 
  1877 class 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 
  1890 class 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 
  1897 class ASN1_BADTAG(ASN1_force):
  1898     pass
  1899 
  1900 class ASN1_INTEGER(ASN1_Object):
  1901     tag = ASN1_Class_UNIVERSAL.INTEGER
  1902 
  1903 class ASN1_STRING(ASN1_Object):
  1904     tag = ASN1_Class_UNIVERSAL.STRING
  1905 
  1906 class ASN1_BIT_STRING(ASN1_STRING):
  1907     tag = ASN1_Class_UNIVERSAL.BIT_STRING
  1908 
  1909 class ASN1_PRINTABLE_STRING(ASN1_STRING):
  1910     tag = ASN1_Class_UNIVERSAL.PRINTABLE_STRING
  1911 
  1912 class ASN1_T61_STRING(ASN1_STRING):
  1913     tag = ASN1_Class_UNIVERSAL.T61_STRING
  1914 
  1915 class ASN1_IA5_STRING(ASN1_STRING):
  1916     tag = ASN1_Class_UNIVERSAL.IA5_STRING
  1917 
  1918 class ASN1_NUMERIC_STRING(ASN1_STRING):
  1919     tag = ASN1_Class_UNIVERSAL.NUMERIC_STRING
  1920 
  1921 class ASN1_VIDEOTEX_STRING(ASN1_STRING):
  1922     tag = ASN1_Class_UNIVERSAL.VIDEOTEX_STRING
  1923 
  1924 class ASN1_UTC_TIME(ASN1_STRING):
  1925     tag = ASN1_Class_UNIVERSAL.UTC_TIME
  1926 
  1927 class ASN1_TIME_TICKS(ASN1_INTEGER):
  1928     tag = ASN1_Class_UNIVERSAL.TIME_TICKS
  1929 
  1930 class ASN1_BOOLEAN(ASN1_INTEGER):
  1931     tag = ASN1_Class_UNIVERSAL.BOOLEAN
  1932     
  1933 class ASN1_NULL(ASN1_INTEGER):
  1934     tag = ASN1_Class_UNIVERSAL.NULL
  1935 
  1936 class ASN1_COUNTER32(ASN1_INTEGER):
  1937     tag = ASN1_Class_UNIVERSAL.COUNTER32
  1938     
  1939 class 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     
  1947 class ASN1_SET(ASN1_SEQUENCE):
  1948     tag = ASN1_Class_UNIVERSAL.SET
  1949     
  1950 class 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 
  1969 class BER_Exception(Exception):
  1970     pass
  1971 
  1972 class 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 
  1986 class BER_BadTag_Decoding_Error(BER_Decoding_Error, ASN1_BadTag_Decoding_Error):
  1987     pass
  1988 
  1989 def 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
  2000 def 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         
  2013 def 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])
  2022 def 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 
  2036 class 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 
  2046 class 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 
  2124 ASN1_Codecs.BER.register_stem(BERcodec_Object)
  2125 
  2126 
  2127 class 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 
  2159 class BERcodec_BOOLEAN(BERcodec_INTEGER):
  2160     tag = ASN1_Class_UNIVERSAL.BOOLEAN
  2161 
  2162 class 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 
  2171 class 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 
  2181 class BERcodec_BIT_STRING(BERcodec_STRING):
  2182     tag = ASN1_Class_UNIVERSAL.BIT_STRING
  2183 
  2184 class BERcodec_PRINTABLE_STRING(BERcodec_STRING):
  2185     tag = ASN1_Class_UNIVERSAL.PRINTABLE_STRING
  2186 
  2187 class BERcodec_T61_STRING (BERcodec_STRING):
  2188     tag = ASN1_Class_UNIVERSAL.T61_STRING
  2189 
  2190 class BERcodec_IA5_STRING(BERcodec_STRING):
  2191     tag = ASN1_Class_UNIVERSAL.IA5_STRING
  2192 
  2193 class BERcodec_UTC_TIME(BERcodec_STRING):
  2194     tag = ASN1_Class_UNIVERSAL.UTC_TIME
  2195 
  2196 class BERcodec_TIME_TICKS(BERcodec_INTEGER):
  2197     tag = ASN1_Class_UNIVERSAL.TIME_TICKS
  2198 
  2199 class BERcodec_COUNTER32(BERcodec_INTEGER):
  2200     tag = ASN1_Class_UNIVERSAL.COUNTER32
  2201 
  2202 class 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 
  2230 class BERcodec_SET(BERcodec_SEQUENCE):
  2231     tag = ASN1_Class_UNIVERSAL.SET
  2232 
  2233 
  2234 class 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 
  2268 class 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 
  2314 def 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 
  2353 def 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 
  2391 class Gen(object):
  2392     def __iter__(self):
  2393         return iter([])
  2394     
  2395 class 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 
  2422 class 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 
  2464 class 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 
  2499 class 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
  2565 prn:     function to apply to each packet instead of lambda x:x.summary()
  2566 lfilter: 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
  2577 prn:     function to apply to each packet instead of lambda x:x.summary()
  2578 lfilter: 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 
  2910 class 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 
  2927 class 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 
  2936 class 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 
  2945 class 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 
  2994 class AS_resolver_riswhois(AS_resolver):
  2995     server = "riswhois.ripe.net"
  2996     options = "-k -M -1"
  2997 
  2998 
  2999 class AS_resolver_radb(AS_resolver):
  3000     server = "whois.ra.net"
  3001     options = "-k -M"
  3002     
  3003 
  3004 class 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 
  3029 class 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 
  3046 class 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)