3proxy/tests/cases/sysdns.py
Vladimir Dubrovin 235f899668
Some checks failed
C/C++ CI Linux / ${{ matrix.target }} (ubuntu-24.04-arm) (push) Has been cancelled
C/C++ CI Linux / ${{ matrix.target }} (ubuntu-latest) (push) Has been cancelled
C/C++ CI MacOS / ${{ matrix.target }} (macos-15) (push) Has been cancelled
C/C++ CI Windows / ${{ matrix.target }} (windows-2022) (push) Has been cancelled
C/C++ CI cmake / ${{ matrix.target }} (macos-15) (push) Has been cancelled
C/C++ CI cmake / ${{ matrix.target }} (ubuntu-24.04-arm) (push) Has been cancelled
C/C++ CI cmake / ${{ matrix.target }} (ubuntu-latest) (push) Has been cancelled
C/C++ CI cmake / ${{ matrix.target }} (windows-2022) (push) Has been cancelled
C/C++ CI cmake / ubuntu-latest (wolfSSL) (push) Has been cancelled
help: the features of the build listed, and the tests skip what they need
3proxy --help (and the usage message of a bad invocation) now lists
what the build was made with: TLS and its backend, RADIUS, PCRE, IPv6,
UNIX sockets, the built-in HTTP server, transparency, ODBC and the
optional proxy services. The test runner reads the list before the
first case and the cases state the features they need with
t.skip_unless(), so a build without a feature skips its cases instead
of failing them; a binary too old to report anything skips the cases
with needs and runs the rest.
2026-10-30 14:17:54 +03:00

121 lines
4.0 KiB
Python

"""The system's name servers and hosts file as the default resolver: with
no nserver in a configuration, names in the hosts file are answered from
it; with nserver lines it is not. Each service resolves names the way the
last of defaultresolve, sysresolve and fakeresolve before it says."""
import os
import re
import sys
import time
def hosts_path():
if sys.platform == "win32":
return os.path.join(os.environ.get("SystemRoot", r"C:\Windows"),
"System32", "drivers", "etc", "hosts")
return "/etc/hosts"
def hosts_localhost():
"""Whether the hosts file maps localhost to 127.0.0.1 itself."""
try:
with open(hosts_path(), errors="replace") as f:
for line in f:
words = line.split("#", 1)[0].split()
if len(words) > 1 and words[0] == "127.0.0.1" and "localhost" in [w.lower() for w in words[1:]]:
return True
except OSError:
pass
return False
def system_nameserver():
"""Whether there is a name server for 3proxy to find: resolv.conf has
one, or on Windows the registry is taken to."""
if sys.platform == "win32":
return True
try:
with open("/etc/resolv.conf") as f:
return any(re.match(r"nameserver\s+[0-9a-fA-F.:]+\s*$", l.split("#")[0]) for l in f)
except OSError:
return False
def wait(t, port, name):
deadline = time.time() + 5
while time.time() < deadline:
if t.dns_query(port, name):
return
def run(t):
if not t.skip_unless("httpsrv"):
return
if not hosts_localhost() or not system_nameserver():
t.skip("no localhost in the hosts file, or no system name server")
return
port = t.free_port()
t.start("sysdns", f"""
log
flush
auth iponly
allow *
dnspr -p{port}
""")
wait(t, port, "localhost")
t.eq(["127.0.0.1"], t.dns_query(port, "localhost"),
"without nserver, a name of the hosts file is answered from it")
t.eq(["127.0.0.1"], t.dns_query(port, "LocalHost"),
"whatever its case")
# with a name server configured, one that answers nothing, the hosts
# file is not used
other = t.free_port()
t.start("sysdns_nserver", f"""
log
flush
nserver 127.0.0.1:{t.free_port()}
nscache 1024
auth iponly
allow *
dnspr -p{other}
""")
wait(t, other, "localhost")
t.ne(["127.0.0.1"], t.dns_query(other, "localhost") or [],
"with nserver, the hosts file is not used")
# each service resolves the way the command before it says: fake (to
# 127.0.0.2, where the server is not), the system routines (no
# nsrecord) or the default (the system's name servers, and nsrecord)
fake, system, default, srv = t.free_port(), t.free_port(), t.free_port(), t.free_port()
t.start("sysdns_modes", f"""
log
nscache 1024
nsrecord only3proxy.test 127.0.0.1
flush
auth iponly
allow *
fakeresolve
proxy -p{fake}
sysresolve
proxy -p{system}
defaultresolve
proxy -p{default}
flush
auth iponly
allow *
http echo * /echo**
httpsrv -i127.0.0.1 -p{srv}
""", ports=[fake, system, default, srv])
t.eq(200, t.http(f"http://localhost:{srv}/echo", proxy=f"127.0.0.1:{default}").status,
"defaultresolve: a name of the hosts file resolves")
t.eq(200, t.http(f"http://only3proxy.test:{srv}/echo", proxy=f"127.0.0.1:{default}").status,
"and an nsrecord name")
t.eq(200, t.http(f"http://localhost:{srv}/echo", proxy=f"127.0.0.1:{system}").status,
"sysresolve: names resolve through the system routines")
t.ne(200, t.http(f"http://only3proxy.test:{srv}/echo", proxy=f"127.0.0.1:{system}").status,
"which know no nsrecord name")
t.ne(200, t.http(f"http://localhost:{srv}/echo", proxy=f"127.0.0.1:{fake}").status,
"fakeresolve: every name is 127.0.0.2, in a service before the others")