namespy 0.1.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- namespy/__init__.py +176 -0
- namespy/__main__.py +1727 -0
- namespy/aggressive.py +488 -0
- namespy/anchors.py +524 -0
- namespy/apps/__init__.py +60 -0
- namespy/apps/forwarder.py +275 -0
- namespy/apps/hosts.py +226 -0
- namespy/apps/lookup.py +319 -0
- namespy/apps/routing.py +203 -0
- namespy/apps/stub.py +257 -0
- namespy/bundle.py +352 -0
- namespy/cache/__init__.py +863 -0
- namespy/cache/sqlite.py +436 -0
- namespy/crypto.py +555 -0
- namespy/dnssec.py +1320 -0
- namespy/doh.py +431 -0
- namespy/edns.py +850 -0
- namespy/engine.py +554 -0
- namespy/enums.py +492 -0
- namespy/filtering.py +179 -0
- namespy/idna.py +361 -0
- namespy/maintenance.py +434 -0
- namespy/minimis.py +148 -0
- namespy/mqtype.py +320 -0
- namespy/named.root +92 -0
- namespy/names.py +587 -0
- namespy/nsec.py +1237 -0
- namespy/opportunistic.py +594 -0
- namespy/packet.py +934 -0
- namespy/py.typed +3 -0
- namespy/querylog.py +89 -0
- namespy/rbl.py +216 -0
- namespy/rdata.py +1919 -0
- namespy/resolver.py +5588 -0
- namespy/server.py +2377 -0
- namespy/special.py +149 -0
- namespy/stub.py +622 -0
- namespy/svcb.py +1891 -0
- namespy/trace.py +279 -0
- namespy/validate.py +1110 -0
- namespy/verify.py +519 -0
- namespy/wire.py +138 -0
- namespy/zone.py +780 -0
- namespy-0.1.0.dist-info/METADATA +491 -0
- namespy-0.1.0.dist-info/RECORD +48 -0
- namespy-0.1.0.dist-info/WHEEL +4 -0
- namespy-0.1.0.dist-info/entry_points.txt +2 -0
- namespy-0.1.0.dist-info/licenses/LICENSE +202 -0
namespy/__init__.py
ADDED
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
"""Namespy: an async DNS resolver, message codec, and inspector.
|
|
2
|
+
|
|
3
|
+
Two things live here that every other module reaches for: the logging posture,
|
|
4
|
+
and the socket options used to keep UDP datagrams from being fragmented.
|
|
5
|
+
|
|
6
|
+
**Logging.** A library must not configure logging for its host application, and
|
|
7
|
+
must not be silent either; the usual convention is to attach a
|
|
8
|
+
:class:`logging.NullHandler` to the top-level logger and let the application
|
|
9
|
+
decide. Previously each protocol object built its own ``logging.Logger(...)``
|
|
10
|
+
and attached a shared module-level :data:`console` handler to it, so importing
|
|
11
|
+
Namespy and constructing a resolver caused output no matter what the application
|
|
12
|
+
had configured, and every extra object re-attached the same handler until each
|
|
13
|
+
line printed twice. ``console`` is still exported for anyone who was using it,
|
|
14
|
+
but it is no longer attached to anything.
|
|
15
|
+
|
|
16
|
+
**Path MTU discovery.** ``IP_MTU_DISCOVER``/``IP_PMTUDISC_DO`` are Linux
|
|
17
|
+
``IP``-level socket options (numbers 10 and 2). Setting them hard from
|
|
18
|
+
``connection_made`` raised :exc:`OSError` on any other platform -- on Windows
|
|
19
|
+
the option does not exist -- which means a resolver or server that worked on the
|
|
20
|
+
developer's box crashed at the first callback elsewhere. The values now come
|
|
21
|
+
from :mod:`socket` where available, are ``None`` where not, and callers gate on
|
|
22
|
+
:data:`PMTUD_SUPPORTED`.
|
|
23
|
+
|
|
24
|
+
This matters for DNS specifically because a response that exceeds the path MTU
|
|
25
|
+
gets fragmented, and fragment reassembly failure looks identical to packet loss
|
|
26
|
+
-- which is how an EDNS(0)-sized answer (RFC 6891 section 6.2.5) turns into a
|
|
27
|
+
timeout. Asking the kernel not to fragment makes the failure visible instead.
|
|
28
|
+
"""
|
|
29
|
+
|
|
30
|
+
import logging
|
|
31
|
+
import socket
|
|
32
|
+
|
|
33
|
+
__author__ = "b1tninja"
|
|
34
|
+
__version__ = "0.1.0"
|
|
35
|
+
|
|
36
|
+
#: ``IP_MTU`` socket option: query the path MTU of a connected socket.
|
|
37
|
+
IP_MTU = getattr(socket, "IP_MTU", 14)
|
|
38
|
+
|
|
39
|
+
#: ``IP_MTU_DISCOVER`` socket option -- Linux only (``None`` elsewhere).
|
|
40
|
+
IP_MTU_DISCOVER = getattr(socket, "IP_MTU_DISCOVER", None)
|
|
41
|
+
|
|
42
|
+
#: ``IP_PMTUDISC_DO``: do PMTU discovery and refuse to fragment.
|
|
43
|
+
IP_PMTUDISC_DO = getattr(socket, "IP_PMTUDISC_DO", None)
|
|
44
|
+
|
|
45
|
+
#: Whether this platform understands the PMTUD option pair at all.
|
|
46
|
+
PMTUD_SUPPORTED = IP_MTU_DISCOVER is not None and IP_PMTUDISC_DO is not None
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
def enable_path_mtu_discovery(transport, log=None) -> bool:
|
|
50
|
+
"""Ask the kernel not to fragment this socket's datagrams.
|
|
51
|
+
|
|
52
|
+
Returns whether the option was actually set. Both the resolver and the
|
|
53
|
+
server call this from ``connection_made``, and neither may treat failure as
|
|
54
|
+
fatal: the option is Linux-specific, the socket may be IPv6 (where the
|
|
55
|
+
equivalent is ``IPV6_MTU_DISCOVER``, a different number), and a restrictive
|
|
56
|
+
container may refuse the call altogether. An earlier version called
|
|
57
|
+
:meth:`~socket.socket.setsockopt` unguarded, which meant a datagram
|
|
58
|
+
transport could not even open on Windows.
|
|
59
|
+
|
|
60
|
+
:param transport: the asyncio transport whose socket should be adjusted.
|
|
61
|
+
:param log: optional logger for a debug-level note about what happened.
|
|
62
|
+
"""
|
|
63
|
+
sock = transport.get_extra_info("socket")
|
|
64
|
+
if sock is None:
|
|
65
|
+
return False
|
|
66
|
+
if not PMTUD_SUPPORTED:
|
|
67
|
+
if log is not None:
|
|
68
|
+
log.debug("pmtud_unavailable(option=IP_MTU_DISCOVER platform_supported=False)")
|
|
69
|
+
return False
|
|
70
|
+
try:
|
|
71
|
+
sock.setsockopt(socket.IPPROTO_IP, IP_MTU_DISCOVER, IP_PMTUDISC_DO)
|
|
72
|
+
except OSError as exc:
|
|
73
|
+
if log is not None:
|
|
74
|
+
log.debug("pmtud_failed(err=%s)", exc)
|
|
75
|
+
return False
|
|
76
|
+
return True
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
#: A ready-made stderr handler, for scripts that want Namespy's messages without
|
|
80
|
+
#: configuring :mod:`logging`. Attaching handlers is the application's job.
|
|
81
|
+
console = logging.StreamHandler()
|
|
82
|
+
console.setLevel(logging.DEBUG)
|
|
83
|
+
console.setFormatter(logging.Formatter("%(name)-12s: %(levelname)-8s %(message)s"))
|
|
84
|
+
|
|
85
|
+
# Library logging posture: silence unless the application opts in.
|
|
86
|
+
logging.getLogger(__name__).addHandler(logging.NullHandler())
|
|
87
|
+
|
|
88
|
+
# Re-exports, deliberately at the bottom.
|
|
89
|
+
#
|
|
90
|
+
# resolver.py and server.py both say ``from . import enable_path_mtu_discovery``,
|
|
91
|
+
# so the package body has to finish defining that function before either
|
|
92
|
+
# submodule runs -- and importing a submodule here executes it immediately. Moved
|
|
93
|
+
# these lines above the definition and every import in the package raises
|
|
94
|
+
# ImportError on a half-built module object, which is a confusing failure to
|
|
95
|
+
# diagnose because it names neither of the two files that caused it.
|
|
96
|
+
#
|
|
97
|
+
# They exist because ``from namespy.resolver import RecursiveResolver`` is not a
|
|
98
|
+
# reasonable thing to make a caller type, and because the previous ``__all__``
|
|
99
|
+
# advertised socket option constants as the package's public surface while the
|
|
100
|
+
# classes people actually want were only reachable through module paths.
|
|
101
|
+
from .cache import ( # noqa: E402
|
|
102
|
+
DnsCache,
|
|
103
|
+
FailureCache,
|
|
104
|
+
FailureEntry,
|
|
105
|
+
MemoryDnsCache,
|
|
106
|
+
NegativeEntry,
|
|
107
|
+
PositiveEntry,
|
|
108
|
+
SqliteDnsCache,
|
|
109
|
+
)
|
|
110
|
+
from .edns import EdnsOption, ExtendedDNSError # noqa: E402
|
|
111
|
+
from .enums import ( # noqa: E402
|
|
112
|
+
DnsOpCode,
|
|
113
|
+
DnsQClass,
|
|
114
|
+
DnsQR,
|
|
115
|
+
DnsQType,
|
|
116
|
+
DnsRClass,
|
|
117
|
+
DnsResponseCode,
|
|
118
|
+
DnsRType,
|
|
119
|
+
)
|
|
120
|
+
from .names import DomainName # noqa: E402
|
|
121
|
+
from .packet import ( # noqa: E402
|
|
122
|
+
DnsPacket,
|
|
123
|
+
DnsPacketParseError,
|
|
124
|
+
DnsQuestion,
|
|
125
|
+
DnsRecord,
|
|
126
|
+
Query,
|
|
127
|
+
Response,
|
|
128
|
+
)
|
|
129
|
+
from .resolver import GluePolicy, NxdomainPolicy, RecursiveResolver, Resolver # noqa: E402
|
|
130
|
+
from .trace import Trace # noqa: E402
|
|
131
|
+
from .wire import DnsNameParseError # noqa: E402
|
|
132
|
+
|
|
133
|
+
__all__ = [
|
|
134
|
+
# Socket-level helpers, kept exported because server.py and resolver.py
|
|
135
|
+
# share them and a caller building their own transport needs the guard.
|
|
136
|
+
"IP_MTU",
|
|
137
|
+
"IP_MTU_DISCOVER",
|
|
138
|
+
"IP_PMTUDISC_DO",
|
|
139
|
+
"PMTUD_SUPPORTED",
|
|
140
|
+
# The API surface: names a caller should not have to know a module path for.
|
|
141
|
+
"DnsCache",
|
|
142
|
+
"DnsNameParseError",
|
|
143
|
+
"DnsOpCode",
|
|
144
|
+
"DnsPacket",
|
|
145
|
+
"DnsPacketParseError",
|
|
146
|
+
"DnsQClass",
|
|
147
|
+
"DnsQR",
|
|
148
|
+
"DnsQType",
|
|
149
|
+
"DnsQuestion",
|
|
150
|
+
"DnsRClass",
|
|
151
|
+
"DnsRType",
|
|
152
|
+
"DnsRecord",
|
|
153
|
+
"DnsResponseCode",
|
|
154
|
+
"DomainName",
|
|
155
|
+
"EdnsOption",
|
|
156
|
+
"ExtendedDNSError",
|
|
157
|
+
"FailureCache",
|
|
158
|
+
"FailureEntry",
|
|
159
|
+
"GluePolicy",
|
|
160
|
+
"MemoryDnsCache",
|
|
161
|
+
"NegativeEntry",
|
|
162
|
+
"NxdomainPolicy",
|
|
163
|
+
"PositiveEntry",
|
|
164
|
+
"Query",
|
|
165
|
+
"RecursiveResolver",
|
|
166
|
+
"Resolver",
|
|
167
|
+
"Response",
|
|
168
|
+
"SqliteDnsCache",
|
|
169
|
+
"Trace",
|
|
170
|
+
# Trailing because ruff's RUF022 sorts dunders last within this group; the
|
|
171
|
+
# grouping in this list is by case convention, not by meaning.
|
|
172
|
+
"__author__",
|
|
173
|
+
"__version__",
|
|
174
|
+
"console",
|
|
175
|
+
"enable_path_mtu_discovery",
|
|
176
|
+
]
|