Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.11/site-packages/dns/inet.py: 20%
Shortcuts on this page
r m x toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
Shortcuts on this page
r m x toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
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
23import dns._text_util
24import dns.ipv4
25import dns.ipv6
27# We assume that AF_INET and AF_INET6 are always defined. We keep
28# these here for the benefit of any old code (unlikely though that
29# is!).
30AF_INET = socket.AF_INET
31AF_INET6 = socket.AF_INET6
34def inet_pton(family: int, text: str) -> bytes:
35 """Convert the textual form of a network address into its binary form.
37 :param family: The address family.
38 :type family: int
39 :param text: The textual address.
40 :type text: str
41 :raises NotImplementedError: If the address family is not implemented.
42 :rtype: bytes
43 """
45 if family == AF_INET:
46 return dns.ipv4.inet_aton(text)
47 elif family == AF_INET6:
48 return dns.ipv6.inet_aton(text, True)
49 else:
50 raise NotImplementedError
53def inet_ntop(family: int, address: bytes) -> str:
54 """Convert the binary form of a network address into its textual form.
56 :param family: The address family.
57 :type family: int
58 :param address: The network address in binary form.
59 :type address: bytes
60 :raises NotImplementedError: If the address family is not implemented.
61 :rtype: str
62 """
64 if family == AF_INET:
65 return dns.ipv4.inet_ntoa(address)
66 elif family == AF_INET6:
67 return dns.ipv6.inet_ntoa(address)
68 else:
69 raise NotImplementedError
72def af_for_address(text: str) -> int:
73 """Determine the address family of a textual-form network address.
75 :param text: The textual address.
76 :type text: str
77 :raises ValueError: If the address family cannot be determined.
78 :rtype: int
79 """
81 try:
82 dns.ipv4.inet_aton(text)
83 return AF_INET
84 except Exception:
85 try:
86 dns.ipv6.inet_aton(text, True)
87 return AF_INET6
88 except Exception:
89 raise ValueError
92def is_multicast(text: str) -> bool:
93 """Is the textual-form network address a multicast address?
95 :param text: The textual address.
96 :type text: str
97 :raises ValueError: If the address family cannot be determined.
98 :rtype: bool
99 """
101 try:
102 first = dns.ipv4.inet_aton(text)[0]
103 return first >= 224 and first <= 239
104 except Exception:
105 try:
106 first = dns.ipv6.inet_aton(text, True)[0]
107 return first == 255
108 except Exception:
109 raise ValueError
112def is_address(text: str) -> bool:
113 """Is the specified string an IPv4 or IPv6 address?
115 :param text: The textual address.
116 :type text: str
117 :rtype: bool
118 """
120 try:
121 dns.ipv4.inet_aton(text)
122 return True
123 except Exception:
124 try:
125 dns.ipv6.inet_aton(text, True)
126 return True
127 except Exception:
128 return False
131def low_level_address_tuple(high_tuple: tuple[str, int], af: int | None = None) -> Any:
132 """Given a "high-level" address tuple, i.e.
133 an (address, port) return the appropriate "low-level" address tuple
134 suitable for use in socket calls.
136 If an *af* other than ``None`` is provided, it is assumed the
137 address in the high-level tuple is valid and has that af. If af
138 is ``None``, then af_for_address will be called.
139 """
140 address, port = high_tuple
141 if af is None:
142 af = af_for_address(address)
143 if af == AF_INET:
144 return (address, port)
145 elif af == AF_INET6:
146 i = address.find("%")
147 if i < 0:
148 # no scope, shortcut!
149 return (address, port, 0, 0)
150 # try to avoid getaddrinfo()
151 addrpart = address[:i]
152 scope = address[i + 1 :]
153 if dns._text_util.is_ascii_digits(scope):
154 return (addrpart, port, 0, int(scope))
155 try:
156 return (addrpart, port, 0, socket.if_nametoindex(scope))
157 except AttributeError: # pragma: no cover (we can't really test this)
158 ai_flags = socket.AI_NUMERICHOST
159 (*_, tup), *_ = socket.getaddrinfo(address, port, flags=ai_flags)
160 return tup
161 else:
162 raise NotImplementedError(f"unknown address family {af}")
165def any_for_af(af):
166 """Return the 'any' address for the specified address family."""
167 if af == socket.AF_INET:
168 return "0.0.0.0"
169 elif af == socket.AF_INET6:
170 return "::"
171 raise NotImplementedError(f"unknown address family {af}")
174def canonicalize(text: str) -> str:
175 """Verify that *address* is a valid text form IPv4 or IPv6 address and return its
176 canonical text form. IPv6 addresses with scopes are rejected.
178 :param text: The address in textual form.
179 :type text: str
180 :raises ValueError: If the text is not a valid address.
181 """
182 try:
183 return dns.ipv6.canonicalize(text)
184 except Exception:
185 try:
186 return dns.ipv4.canonicalize(text)
187 except Exception:
188 raise ValueError