Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.8/site-packages/dns/inet.py: 19%
80 statements
« prev ^ index » next coverage.py v7.4.1, created at 2024-02-02 06:07 +0000
« prev ^ index » next coverage.py v7.4.1, created at 2024-02-02 06:07 +0000
1# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license
3# Copyright (C) 2003-2017 Nominum, Inc.
4#
5# Permission to use, copy, modify, and distribute this software and its
6# documentation for any purpose with or without fee is hereby granted,
7# provided that the above copyright notice and this permission notice
8# appear in all copies.
9#
10# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES
11# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR
13# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
16# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
18"""Generic Internet address helper functions."""
20import socket
21from typing import Any, Optional, Tuple
23import dns.ipv4
24import dns.ipv6
26# We assume that AF_INET and AF_INET6 are always defined. We keep
27# these here for the benefit of any old code (unlikely though that
28# is!).
29AF_INET = socket.AF_INET
30AF_INET6 = socket.AF_INET6
33def inet_pton(family: int, text: str) -> bytes:
34 """Convert the textual form of a network address into its binary form.
36 *family* is an ``int``, the address family.
38 *text* is a ``str``, the textual address.
40 Raises ``NotImplementedError`` if the address family specified is not
41 implemented.
43 Returns a ``bytes``.
44 """
46 if family == AF_INET:
47 return dns.ipv4.inet_aton(text)
48 elif family == AF_INET6:
49 return dns.ipv6.inet_aton(text, True)
50 else:
51 raise NotImplementedError
54def inet_ntop(family: int, address: bytes) -> str:
55 """Convert the binary form of a network address into its textual form.
57 *family* is an ``int``, the address family.
59 *address* is a ``bytes``, the network address in binary form.
61 Raises ``NotImplementedError`` if the address family specified is not
62 implemented.
64 Returns a ``str``.
65 """
67 if family == AF_INET:
68 return dns.ipv4.inet_ntoa(address)
69 elif family == AF_INET6:
70 return dns.ipv6.inet_ntoa(address)
71 else:
72 raise NotImplementedError
75def af_for_address(text: str) -> int:
76 """Determine the address family of a textual-form network address.
78 *text*, a ``str``, the textual address.
80 Raises ``ValueError`` if the address family cannot be determined
81 from the input.
83 Returns an ``int``.
84 """
86 try:
87 dns.ipv4.inet_aton(text)
88 return AF_INET
89 except Exception:
90 try:
91 dns.ipv6.inet_aton(text, True)
92 return AF_INET6
93 except Exception:
94 raise ValueError
97def is_multicast(text: str) -> bool:
98 """Is the textual-form network address a multicast address?
100 *text*, a ``str``, the textual address.
102 Raises ``ValueError`` if the address family cannot be determined
103 from the input.
105 Returns a ``bool``.
106 """
108 try:
109 first = dns.ipv4.inet_aton(text)[0]
110 return first >= 224 and first <= 239
111 except Exception:
112 try:
113 first = dns.ipv6.inet_aton(text, True)[0]
114 return first == 255
115 except Exception:
116 raise ValueError
119def is_address(text: str) -> bool:
120 """Is the specified string an IPv4 or IPv6 address?
122 *text*, a ``str``, the textual address.
124 Returns a ``bool``.
125 """
127 try:
128 dns.ipv4.inet_aton(text)
129 return True
130 except Exception:
131 try:
132 dns.ipv6.inet_aton(text, True)
133 return True
134 except Exception:
135 return False
138def low_level_address_tuple(
139 high_tuple: Tuple[str, int], af: Optional[int] = None
140) -> Any:
141 """Given a "high-level" address tuple, i.e.
142 an (address, port) return the appropriate "low-level" address tuple
143 suitable for use in socket calls.
145 If an *af* other than ``None`` is provided, it is assumed the
146 address in the high-level tuple is valid and has that af. If af
147 is ``None``, then af_for_address will be called.
148 """
149 address, port = high_tuple
150 if af is None:
151 af = af_for_address(address)
152 if af == AF_INET:
153 return (address, port)
154 elif af == AF_INET6:
155 i = address.find("%")
156 if i < 0:
157 # no scope, shortcut!
158 return (address, port, 0, 0)
159 # try to avoid getaddrinfo()
160 addrpart = address[:i]
161 scope = address[i + 1 :]
162 if scope.isdigit():
163 return (addrpart, port, 0, int(scope))
164 try:
165 return (addrpart, port, 0, socket.if_nametoindex(scope))
166 except AttributeError: # pragma: no cover (we can't really test this)
167 ai_flags = socket.AI_NUMERICHOST
168 ((*_, tup), *_) = socket.getaddrinfo(address, port, flags=ai_flags)
169 return tup
170 else:
171 raise NotImplementedError(f"unknown address family {af}")
174def any_for_af(af):
175 """Return the 'any' address for the specified address family."""
176 if af == socket.AF_INET:
177 return "0.0.0.0"
178 elif af == socket.AF_INET6:
179 return "::"
180 raise NotImplementedError(f"unknown address family {af}")
183def canonicalize(text: str) -> str:
184 """Verify that *address* is a valid text form IPv4 or IPv6 address and return its
185 canonical text form. IPv6 addresses with scopes are rejected.
187 *text*, a ``str``, the address in textual form.
189 Raises ``ValueError`` if the text is not valid.
190 """
191 try:
192 return dns.ipv6.canonicalize(text)
193 except Exception:
194 try:
195 return dns.ipv4.canonicalize(text)
196 except Exception:
197 raise ValueError