(hostname: str)
| 43 | |
| 44 | |
| 45 | def resolve_host_ips(hostname: str) -> tuple[ipaddress._BaseAddress, ...]: |
| 46 | try: |
| 47 | results = socket.getaddrinfo( |
| 48 | hostname, |
| 49 | None, |
| 50 | family=socket.AF_UNSPEC, |
| 51 | type=socket.SOCK_STREAM, |
| 52 | ) |
| 53 | except socket.gaierror as exc: |
| 54 | raise UnsafeUrlError(f"Unable to resolve hostname '{hostname}'") from exc |
| 55 | |
| 56 | ips: list[ipaddress._BaseAddress] = [] |
| 57 | seen: set[str] = set() |
| 58 | for _family, _type, _proto, _canonname, sockaddr in results: |
| 59 | address = sockaddr[0] |
| 60 | if "%" in address: |
| 61 | address = address.split("%", 1)[0] |
| 62 | ip = ipaddress.ip_address(address) |
| 63 | key = ip.compressed |
| 64 | if key in seen: |
| 65 | continue |
| 66 | seen.add(key) |
| 67 | ips.append(ip) |
| 68 | |
| 69 | if not ips: |
| 70 | raise UnsafeUrlError(f"Hostname '{hostname}' did not resolve to an IP address") |
| 71 | |
| 72 | return tuple(ips) |
| 73 | |
| 74 | |
| 75 | def validate_public_http_url(url: str) -> tuple[ipaddress._BaseAddress, ...]: |
no test coverage detected