You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
Splunk_Deploiement/apps/trackme/lib/solnlib/net_utils.py

156 lines
4.0 KiB

#
# Copyright 2025 Splunk Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
"""Net utilities."""
import re
import socket
__all__ = ["resolve_hostname", "validate_scheme_host_port"]
from typing import Optional, Union
def resolve_hostname(addr: str) -> Optional[str]:
"""Try to resolve an IP to a host name and returns None on common failures.
Arguments:
addr: IP address to resolve.
Returns:
Host name if success else None.
Raises:
ValueError: If `addr` is not a valid address.
"""
if is_valid_ip(addr):
try:
name, _, _ = socket.gethostbyaddr(addr)
return name
except socket.gaierror:
# [Errno 8] nodename nor servname provided, or not known
pass
except socket.herror:
# [Errno 1] Unknown host
pass
except socket.timeout:
# Timeout.
pass
return None
else:
raise ValueError("Invalid ip address.")
def is_valid_ip(addr: str) -> bool:
"""Validate an IPV4 address.
Arguments:
addr: IP address to validate.
Returns:
True if is valid else False.
"""
ip_rx = re.compile(
r"""
^(((
[0-1]\d{2} # matches 000-199
| 2[0-4]\d # matches 200-249
| 25[0-5] # matches 250-255
| \d{1,2} # matches 0-9, 00-99
)\.){3}) # 3 of the preceding stanzas
([0-1]\d{2}|2[0-4]\d|25[0-5]|\d{1,2})$ # final octet
""",
re.VERBOSE,
)
try:
return ip_rx.match(addr.strip())
except AttributeError:
# Value was not a string
return False
def is_valid_hostname(hostname: str) -> bool:
"""Validate a host name.
Arguments:
hostname: host name to validate.
Returns:
True if is valid else False.
"""
# Splunk IPv6 support.
# https://docs.splunk.com/Documentation/Splunk/9.0.0/Admin/ConfigureSplunkforIPv6#Change_the_prioritization_of_IPv4_and_IPv6_communications
if hostname == "[::1]":
return True
if len(hostname) > 255:
return False
if hostname[-1:] == ".":
hostname = hostname[:-1]
allowed = re.compile(r"(?!-)(::)?[A-Z\d-]{1,63}(?<!-)$", re.IGNORECASE)
return all(allowed.match(x) for x in hostname.split("."))
def is_valid_port(port: Union[str, int]) -> bool:
"""Validate a port.
Arguments:
port: port to validate.
Returns:
True if is valid else False.
"""
try:
return 0 < int(port) <= 65535
except ValueError:
return False
def is_valid_scheme(scheme: str) -> bool:
"""Validate a scheme.
Arguments:
scheme: scheme to validate.
Returns:
True if is valid else False.
"""
return scheme.lower() in ("http", "https")
def validate_scheme_host_port(scheme: str, host: str, port: Union[str, int]):
"""Validates scheme, host and port.
Arguments:
scheme: scheme to validate.
host: hostname to validate.
port: port to validate.
Raises:
ValueError: if scheme, host or port are invalid.
"""
if scheme is not None and not is_valid_scheme(scheme):
raise ValueError("Invalid scheme")
if host is not None and not is_valid_hostname(host):
raise ValueError("Invalid host")
if port is not None and not is_valid_port(port):
raise ValueError("Invalid port")