• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1# SPDX-License-Identifier: GPL-2.0-only
2# This file is part of Scapy
3# See https://scapy.net/ for more information
4
5"""
6Python 2 and 3 link classes.
7"""
8
9import base64
10import binascii
11import struct
12import sys
13
14from typing import (
15    Any,
16    AnyStr,
17    Callable,
18    Optional,
19    TypeVar,
20    TYPE_CHECKING,
21    Union,
22)
23
24# Very important: will issue typing errors otherwise
25__all__ = [
26    # typing
27    'DecoratorCallable',
28    'Literal',
29    'Protocol',
30    'Self',
31    'UserDict',
32    # compat
33    'base64_bytes',
34    'bytes_base64',
35    'bytes_encode',
36    'bytes_hex',
37    'chb',
38    'hex_bytes',
39    'orb',
40    'plain_str',
41    'raw',
42]
43
44# Typing compatibility
45
46# Note:
47# supporting typing on multiple python versions is a nightmare.
48# we provide a FakeType class to be able to use types added on
49# later Python versions (since we run mypy on 3.12), on older
50# ones.
51
52
53# Import or create fake types
54
55def _FakeType(name, cls=object):
56    # type: (str, Optional[type]) -> Any
57    class _FT(object):
58        def __init__(self, name):
59            # type: (str) -> None
60            self.name = name
61
62        # make the objects subscriptable indefinitely
63        def __getitem__(self, item):  # type: ignore
64            return cls
65
66        def __call__(self, *args, **kargs):
67            # type: (*Any, **Any) -> Any
68            if isinstance(args[0], str):
69                self.name = args[0]
70            return self
71
72        def __repr__(self):
73            # type: () -> str
74            return "<Fake typing.%s>" % self.name
75    return _FT(name)
76
77
78# Python 3.8 Only
79if sys.version_info >= (3, 8):
80    from typing import Literal
81    from typing import Protocol
82else:
83    Literal = _FakeType("Literal")
84
85    class Protocol:
86        pass
87
88
89# Python 3.9 Only
90if sys.version_info >= (3, 9):
91    from collections import UserDict
92else:
93    from collections import UserDict as _UserDict
94    UserDict = _FakeType("_UserDict", _UserDict)
95
96
97# Python 3.11 Only
98if sys.version_info >= (3, 11):
99    from typing import Self
100else:
101    Self = _FakeType("Self")
102
103###########
104# Python3 #
105###########
106
107# https://mypy.readthedocs.io/en/stable/generics.html#declaring-decorators
108DecoratorCallable = TypeVar("DecoratorCallable", bound=Callable[..., Any])
109
110
111# This is ugly, but we don't want to move raw() out of compat.py
112# and it makes it much clearer
113if TYPE_CHECKING:
114    from scapy.packet import Packet
115
116
117def raw(x):
118    # type: (Packet) -> bytes
119    """
120    Builds a packet and returns its bytes representation.
121    This function is and will always be cross-version compatible
122    """
123    return bytes(x)
124
125
126def bytes_encode(x):
127    # type: (Any) -> bytes
128    """Ensure that the given object is bytes. If the parameter is a
129        packet, raw() should be preferred.
130
131    """
132    if isinstance(x, str):
133        return x.encode()
134    return bytes(x)
135
136
137def plain_str(x):
138    # type: (Any) -> str
139    """Convert basic byte objects to str"""
140    if isinstance(x, bytes):
141        return x.decode(errors="backslashreplace")
142    return str(x)
143
144
145def chb(x):
146    # type: (int) -> bytes
147    """Same than chr() but encode as bytes."""
148    return struct.pack("!B", x)
149
150
151def orb(x):
152    # type: (Union[int, str, bytes]) -> int
153    """Return ord(x) when not already an int."""
154    if isinstance(x, int):
155        return x
156    return ord(x)
157
158
159def bytes_hex(x):
160    # type: (AnyStr) -> bytes
161    """Hexify a str or a bytes object"""
162    return binascii.b2a_hex(bytes_encode(x))
163
164
165def hex_bytes(x):
166    # type: (AnyStr) -> bytes
167    """De-hexify a str or a byte object"""
168    return binascii.a2b_hex(bytes_encode(x))
169
170
171def int_bytes(x, size):
172    # type: (int, int) -> bytes
173    """Convert an int to an arbitrary sized bytes string"""
174    return x.to_bytes(size, byteorder='big')
175
176
177def bytes_int(x):
178    # type: (bytes) -> int
179    """Convert an arbitrary sized bytes string to an int"""
180    return int.from_bytes(x, "big")
181
182
183def base64_bytes(x):
184    # type: (AnyStr) -> bytes
185    """Turn base64 into bytes"""
186    return base64.decodebytes(bytes_encode(x))
187
188
189def bytes_base64(x):
190    # type: (AnyStr) -> bytes
191    """Turn bytes into base64"""
192    return base64.encodebytes(bytes_encode(x)).replace(b'\n', b'')
193