1# SPDX-License-Identifier: GPL-2.0-only
2# This file is part of Scapy
3# See https://scapy.net/ for more information
4# Copyright (C) Philippe Biondi <phil@secdev.org>
5
6"""
7Functions common to different architectures
8"""
9
10import ctypes
11import re
12import socket
13
14from scapy.config import conf
15from scapy.data import MTU, ARPHRD_TO_DLT, DLT_RAW_ALT, DLT_RAW
16from scapy.error import Scapy_Exception, warning
17from scapy.interfaces import network_name, resolve_iface, NetworkInterface
18from scapy.libs.structures import bpf_program
19from scapy.pton_ntop import inet_pton
20from scapy.utils import decode_locale_str
21
22# Type imports
23import scapy
24from typing import (
25 List,
26 Optional,
27 Union,
28)
29
30# From if.h
31_iff_flags = [
32 "UP",
33 "BROADCAST",
34 "DEBUG",
35 "LOOPBACK",
36 "POINTTOPOINT",
37 "NOTRAILERS",
38 "RUNNING",
39 "NOARP",
40 "PROMISC",
41 "ALLMULTI",
42 "MASTER",
43 "SLAVE",
44 "MULTICAST",
45 "PORTSEL",
46 "AUTOMEDIA",
47 "DYNAMIC",
48 "LOWER_UP",
49 "DORMANT",
50 "ECHO"
51]
52
53
54def get_if_raw_addr(iff):
55 # type: (Union[NetworkInterface, str]) -> bytes
56 """Return the raw IPv4 address of interface"""
57 iff = resolve_iface(iff)
58 if not iff.ip:
59 return b"\x00" * 4
60 return inet_pton(socket.AF_INET, iff.ip)
61
62
63# BPF HANDLERS
64
65
66def compile_filter(filter_exp, # type: str
67 iface=None, # type: Optional[Union[str, 'scapy.interfaces.NetworkInterface']] # noqa: E501
68 linktype=None, # type: Optional[int]
69 promisc=False # type: bool
70 ):
71 # type: (...) -> bpf_program
72 """Asks libpcap to parse the filter, then build the matching
73 BPF bytecode.
74
75 :param iface: if provided, use the interface to compile
76 :param linktype: if provided, use the linktype to compile
77 """
78 try:
79 from scapy.libs.winpcapy import (
80 PCAP_ERRBUF_SIZE,
81 pcap_open_live,
82 pcap_compile,
83 pcap_compile_nopcap,
84 pcap_close
85 )
86 except OSError:
87 raise ImportError(
88 "libpcap is not available. Cannot compile filter !"
89 )
90 from ctypes import create_string_buffer
91 bpf = bpf_program()
92 bpf_filter = create_string_buffer(filter_exp.encode("utf8"))
93 if not linktype:
94 # Try to guess linktype to avoid root
95 if not iface:
96 if not conf.iface:
97 raise Scapy_Exception(
98 "Please provide an interface or linktype!"
99 )
100 iface = conf.iface
101 # Try to guess linktype to avoid requiring root
102 try:
103 arphd = resolve_iface(iface).type
104 linktype = ARPHRD_TO_DLT.get(arphd)
105 except Exception:
106 # Failed to use linktype: use the interface
107 pass
108 if linktype is not None:
109 # Some conversion aliases (e.g. linktype_to_dlt in libpcap)
110 if linktype == DLT_RAW_ALT:
111 linktype = DLT_RAW
112 ret = pcap_compile_nopcap(
113 MTU, linktype, ctypes.byref(bpf), bpf_filter, 1, -1
114 )
115 elif iface:
116 err = create_string_buffer(PCAP_ERRBUF_SIZE)
117 iface_b = create_string_buffer(network_name(iface).encode("utf8"))
118 pcap = pcap_open_live(
119 iface_b, MTU, promisc, 0, err
120 )
121 error = decode_locale_str(bytearray(err).strip(b"\x00"))
122 if error:
123 raise OSError(error)
124 ret = pcap_compile(
125 pcap, ctypes.byref(bpf), bpf_filter, 1, -1
126 )
127 pcap_close(pcap)
128 if ret == -1:
129 raise Scapy_Exception(
130 "Failed to compile filter expression %s (%s)" % (filter_exp, ret)
131 )
132 return bpf
133
134
135#######
136# DNS #
137#######
138
139def read_nameservers() -> List[str]:
140 """Return the nameservers configured by the OS
141 """
142 try:
143 with open('/etc/resolv.conf', 'r') as fd:
144 return re.findall(r"nameserver\s+([^\s]+)", fd.read())
145 except FileNotFoundError:
146 warning("Could not retrieve the OS's nameserver !")
147 return []