1## This file is part of Scapy
2## See http://www.secdev.org/projects/scapy for more informations
3## Copyright (C) Philippe Biondi <phil@secdev.org>
4## This program is published under a GPLv2 license
5
6"""
7Global variables and functions for handling external data sets.
8"""
9
10
11import os
12import re
13import sys
14import time
15
16
17from scapy.dadict import DADict
18from scapy.consts import DARWIN, FREEBSD, NETBSD, OPENBSD
19from scapy.error import log_loading
20from scapy.compat import *
21
22
23############
24## Consts ##
25############
26
27ETHER_ANY = b"\x00"*6
28ETHER_BROADCAST = b"\xff"*6
29
30ETH_P_ALL = 3
31ETH_P_IP = 0x800
32ETH_P_ARP = 0x806
33ETH_P_IPV6 = 0x86dd
34ETH_P_MACSEC = 0x88e5
35
36# From net/if_arp.h
37ARPHDR_ETHER = 1
38ARPHDR_METRICOM = 23
39ARPHDR_PPP = 512
40ARPHDR_LOOPBACK = 772
41ARPHDR_TUN = 65534
42
43# From pcap/dlt.h
44DLT_NULL = 0
45DLT_EN10MB = 1
46DLT_EN3MB = 2
47DLT_AX25 = 3
48DLT_PRONET = 4
49DLT_CHAOS = 5
50DLT_IEEE802 = 6
51DLT_ARCNET = 7
52DLT_SLIP = 8
53DLT_PPP = 9
54DLT_FDDI = 10
55if OPENBSD:
56    DLT_RAW = 14
57else:
58    DLT_RAW = 12
59DLT_RAW_ALT = 101  # At least in Argus
60if FREEBSD or NETBSD:
61    DLT_SLIP_BSDOS = 13
62    DLT_PPP_BSDOS = 14
63else:
64    DLT_SLIP_BSDOS = 15
65    DLT_PPP_BSDOS = 16
66if FREEBSD:
67    DLT_PFSYNC = 121
68else:
69    DLT_PFSYNC = 18
70    DLT_HHDLC = 121
71DLT_ATM_CLIP = 19
72DLT_PPP_SERIAL = 50
73DLT_PPP_ETHER = 51
74DLT_SYMANTEC_FIREWALL = 99
75DLT_C_HDLC = 104
76DLT_IEEE802_11 = 105
77if OPENBSD:
78    DLT_LOOP = 12
79    DLT_ENC = 13
80else:
81    DLT_LOOP = 108
82    DLT_ENC = 109
83DLT_LINUX_SLL = 113
84DLT_PFLOG = 117
85DLT_PRISM_HEADER = 119
86DLT_AIRONET_HEADER = 120
87DLT_IEEE802_11_RADIO = 127
88DLT_LINUX_IRDA  = 144
89DLT_BLUETOOTH_HCI_H4 = 187
90DLT_PPI   = 192
91DLT_CAN_SOCKETCAN = 227
92DLT_IPV4 = 228
93DLT_IPV6 = 229
94
95# From net/ipv6.h on Linux (+ Additions)
96IPV6_ADDR_UNICAST     = 0x01
97IPV6_ADDR_MULTICAST   = 0x02
98IPV6_ADDR_CAST_MASK   = 0x0F
99IPV6_ADDR_LOOPBACK    = 0x10
100IPV6_ADDR_GLOBAL      = 0x00
101IPV6_ADDR_LINKLOCAL   = 0x20
102IPV6_ADDR_SITELOCAL   = 0x40     # deprecated since Sept. 2004 by RFC 3879
103IPV6_ADDR_SCOPE_MASK  = 0xF0
104#IPV6_ADDR_COMPATv4   = 0x80     # deprecated; i.e. ::/96
105#IPV6_ADDR_MAPPED     = 0x1000   # i.e.; ::ffff:0.0.0.0/96
106IPV6_ADDR_6TO4        = 0x0100   # Added to have more specific info (should be 0x0101 ?)
107IPV6_ADDR_UNSPECIFIED = 0x10000
108
109
110# On windows, epoch is 01/02/1970 at 00:00
111EPOCH = time.mktime((1970, 1, 2, 0, 0, 0, 3, 1, 0))-86400
112
113MTU = 0xffff # a.k.a give me all you have
114
115WINDOWS=sys.platform.startswith("win")
116
117
118# file parsing to get some values :
119
120def load_protocols(filename):
121    spaces = re.compile(b"[ \t]+|\n")
122    dct = DADict(_name=filename)
123    try:
124        for l in open(filename, "rb"):
125            try:
126                shrp = l.find(b"#")
127                if  shrp >= 0:
128                    l = l[:shrp]
129                l = l.strip()
130                if not l:
131                    continue
132                lt = tuple(re.split(spaces, l))
133                if len(lt) < 2 or not lt[0]:
134                    continue
135                dct[lt[0]] = int(lt[1])
136            except Exception as e:
137                log_loading.info("Couldn't parse file [%s]: line [%r] (%s)", filename, l, e)
138    except IOError:
139        log_loading.info("Can't open %s file", filename)
140    return dct
141
142def load_ethertypes(filename):
143    spaces = re.compile(b"[ \t]+|\n")
144    dct = DADict(_name=filename)
145    try:
146        f=open(filename, "rb")
147        for l in f:
148            try:
149                shrp = l.find(b"#")
150                if  shrp >= 0:
151                    l = l[:shrp]
152                l = l.strip()
153                if not l:
154                    continue
155                lt = tuple(re.split(spaces, l))
156                if len(lt) < 2 or not lt[0]:
157                    continue
158                dct[lt[0]] = int(lt[1], 16)
159            except Exception as e:
160                log_loading.info("Couldn't parse file [%s]: line [%r] (%s)", filename, l, e)
161        f.close()
162    except IOError as msg:
163        pass
164    return dct
165
166def load_services(filename):
167    spaces = re.compile(b"[ \t]+|\n")
168    tdct=DADict(_name="%s-tcp"%filename)
169    udct=DADict(_name="%s-udp"%filename)
170    try:
171        f=open(filename, "rb")
172        for l in f:
173            try:
174                shrp = l.find(b"#")
175                if  shrp >= 0:
176                    l = l[:shrp]
177                l = l.strip()
178                if not l:
179                    continue
180                lt = tuple(re.split(spaces, l))
181                if len(lt) < 2 or not lt[0]:
182                    continue
183                if lt[1].endswith(b"/tcp"):
184                    tdct[lt[0]] = int(lt[1].split(b'/')[0])
185                elif lt[1].endswith(b"/udp"):
186                    udct[lt[0]] = int(lt[1].split(b'/')[0])
187            except Exception as e:
188                log_loading.warning("Couldn't parse file [%s]: line [%r] (%s)", filename, l, e)
189        f.close()
190    except IOError:
191        log_loading.info("Can't open /etc/services file")
192    return tdct,udct
193
194
195class ManufDA(DADict):
196    def fixname(self, val):
197        return plain_str(val)
198    def _get_manuf_couple(self, mac):
199        oui = ":".join(mac.split(":")[:3]).upper()
200        return self.__dict__.get(oui,(mac,mac))
201    def _get_manuf(self, mac):
202        return self._get_manuf_couple(mac)[1]
203    def _get_short_manuf(self, mac):
204        return self._get_manuf_couple(mac)[0]
205    def _resolve_MAC(self, mac):
206        oui = ":".join(mac.split(":")[:3]).upper()
207        if oui in self:
208            return ":".join([self[oui][0]]+ mac.split(":")[3:])
209        return mac
210    def __repr__(self):
211        return "\n".join("<%s %s, %s>" % (i[0], i[1][0], i[1][1]) for i in self.__dict__.items())
212
213
214
215def load_manuf(filename):
216    manufdb=ManufDA(_name=filename)
217    with open(filename, "rb") as fdesc:
218        for l in fdesc:
219            try:
220                l = l.strip()
221                if not l or l.startswith(b"#"):
222                    continue
223                oui,shrt=l.split()[:2]
224                i = l.find(b"#")
225                if i < 0:
226                    lng=shrt
227                else:
228                    lng = l[i+2:]
229                manufdb[oui] = plain_str(shrt), plain_str(lng)
230            except Exception:
231                log_loading.warning("Couldn't parse one line from [%s] [%r]",
232                                    filename, l, exc_info=True)
233    return manufdb
234
235
236if WINDOWS:
237    ETHER_TYPES=load_ethertypes("ethertypes")
238    IP_PROTOS=load_protocols(os.environ["SystemRoot"]+"\system32\drivers\etc\protocol")
239    TCP_SERVICES,UDP_SERVICES=load_services(os.environ["SystemRoot"] + "\system32\drivers\etc\services")
240    # Default value, will be updated by arch.windows
241    try:
242        MANUFDB = load_manuf(os.environ["ProgramFiles"] + "\\wireshark\\manuf")
243    except IOError:
244        MANUFDB = None
245else:
246    IP_PROTOS=load_protocols("/etc/protocols")
247    ETHER_TYPES=load_ethertypes("/etc/ethertypes")
248    TCP_SERVICES,UDP_SERVICES=load_services("/etc/services")
249    MANUFDB = None
250    for prefix in ['/usr', '/usr/local', '/opt', '/opt/wireshark']:
251        try:
252            MANUFDB = load_manuf(os.path.join(prefix, "share", "wireshark",
253                                              "manuf"))
254            if MANUFDB:
255                break
256        except IOError:
257            pass
258    if not MANUFDB:
259        log_loading.warning("Cannot read wireshark manuf database")
260
261
262#####################
263## knowledge bases ##
264#####################
265
266class KnowledgeBase:
267    def __init__(self, filename):
268        self.filename = filename
269        self.base = None
270
271    def lazy_init(self):
272        self.base = ""
273
274    def reload(self, filename = None):
275        if filename is not None:
276            self.filename = filename
277        oldbase = self.base
278        self.base = None
279        self.lazy_init()
280        if self.base is None:
281            self.base = oldbase
282
283    def get_base(self):
284        if self.base is None:
285            self.lazy_init()
286        return self.base
287
288
289