codekingpro/portable-devtools
114k
1# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license2 3# Copyright (C) 2003-2017 Nominum, Inc.4#5# Permission to use, copy, modify, and distribute this software and its6# documentation for any purpose with or without fee is hereby granted,7# provided that the above copyright notice and this permission notice8# appear in all copies.9#10# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES11# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF12# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR13# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES14# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN15# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT16# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.17 18"""Generic Internet address helper functions."""19 20import socket21from typing import Any, Optional, Tuple22 23import dns.ipv424import dns.ipv625 26# We assume that AF_INET and AF_INET6 are always defined. We keep27# these here for the benefit of any old code (unlikely though that28# is!).29AF_INET = socket.AF_INET30AF_INET6 = socket.AF_INET631 32 33def inet_pton(family: int, text: str) -> bytes:34 """Convert the textual form of a network address into its binary form.35 36 *family* is an ``int``, the address family.37 38 *text* is a ``str``, the textual address.39 40 Raises ``NotImplementedError`` if the address family specified is not41 implemented.42 43 Returns a ``bytes``.44 """45 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 NotImplementedError52 53 54def inet_ntop(family: int, address: bytes) -> str:55 """Convert the binary form of a network address into its textual form.56 57 *family* is an ``int``, the address family.58 59 *address* is a ``bytes``, the network address in binary form.60 61 Raises ``NotImplementedError`` if the address family specified is not62 implemented.63 64 Returns a ``str``.65 """66 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 NotImplementedError73 74 75def af_for_address(text: str) -> int:76 """Determine the address family of a textual-form network address.77 78 *text*, a ``str``, the textual address.79 80 Raises ``ValueError`` if the address family cannot be determined81 from the input.82 83 Returns an ``int``.84 """85 86 try:87 dns.ipv4.inet_aton(text)88 return AF_INET89 except Exception:90 try:91 dns.ipv6.inet_aton(text, True)92 return AF_INET693 except Exception:94 raise ValueError95 96 97def is_multicast(text: str) -> bool:98 """Is the textual-form network address a multicast address?99 100 *text*, a ``str``, the textual address.101 102 Raises ``ValueError`` if the address family cannot be determined103 from the input.104 105 Returns a ``bool``.106 """107 108 try:109 first = dns.ipv4.inet_aton(text)[0]110 return first >= 224 and first <= 239111 except Exception:112 try:113 first = dns.ipv6.inet_aton(text, True)[0]114 return first == 255115 except Exception:116 raise ValueError117 118 119def is_address(text: str) -> bool:120 """Is the specified string an IPv4 or IPv6 address?121 122 *text*, a ``str``, the textual address.123 124 Returns a ``bool``.125 """126 127 try:128 dns.ipv4.inet_aton(text)129 return True130 except Exception:131 try:132 dns.ipv6.inet_aton(text, True)133 return True134 except Exception:135 return False136 137 138def low_level_address_tuple(139 high_tuple: Tuple[str, int], af: Optional[int] = None140) -> Any:141 """Given a "high-level" address tuple, i.e.142 an (address, port) return the appropriate "low-level" address tuple143 suitable for use in socket calls.144 145 If an *af* other than ``None`` is provided, it is assumed the146 address in the high-level tuple is valid and has that af. If af147 is ``None``, then af_for_address will be called.148 """149 address, port = high_tuple150 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_NUMERICHOST168 ((*_, tup), *_) = socket.getaddrinfo(address, port, flags=ai_flags)169 return tup170 else:171 raise NotImplementedError(f"unknown address family {af}")172 173 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}")181 182 183def canonicalize(text: str) -> str:184 """Verify that *address* is a valid text form IPv4 or IPv6 address and return its185 canonical text form. IPv6 addresses with scopes are rejected.186 187 *text*, a ``str``, the address in textual form.188 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 ValueError198 