X-Git-Url: https://tinc-vpn.org/git/browse?p=tinc;a=blobdiff_plain;f=test%2Fintegration%2Fbind_port.py;fp=test%2Fintegration%2Fbind_port.py;h=11fbc4a3b64e8589922ce557091e56ef268a5c66;hp=0000000000000000000000000000000000000000;hb=c8402791b82947c49ba1d04f855dab04191607ca;hpb=66eb66ec8f872db3dc12e1d01101772918d69a4a diff --git a/test/integration/bind_port.py b/test/integration/bind_port.py new file mode 100755 index 00000000..11fbc4a3 --- /dev/null +++ b/test/integration/bind_port.py @@ -0,0 +1,99 @@ +#!/usr/bin/env python3 + +"""Test binding to ports on localhost.""" + +import socket +import sys +import typing as T + +from testlib import check, util +from testlib.const import EXIT_SKIP +from testlib.log import log +from testlib.proc import Script +from testlib.test import Test + +# Call to close opened port +Closer = T.Callable[[], None] + + +def bind_random_port() -> T.Tuple[T.Optional[int], Closer]: + """Bind to random port and return it, keeping the bind.""" + try: + sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + sock.bind(("127.0.0.1", 0)) + sock.listen() + _, port = sock.getsockname() + return port, sock.close + except OSError: + return None, sys.exit + + +def test_bind_port(ctx: Test, ok_ports: T.List[int], bad_ports: T.List[int]) -> None: + """Test binding to ports on localhost.""" + + foo = ctx.node(init="set LogLevel 1") + foo.add_script(Script.TINC_UP) + foo.add_script(Script.TINC_DOWN) + log_path = foo.sub("log") + + if ok_ports: + log.info("check that tincd successfully binds to %s", ok_ports) + + for port in ok_ports: + foo.cmd("add", "BindToAddress", f"127.0.0.1 {port}") + + proc = foo.tincd("-D") + foo[Script.TINC_UP].wait() + foo.cmd("stop") + foo[Script.TINC_DOWN].wait() + check.success(proc.wait()) + + foo_log = util.read_text(log_path) + + for port in ok_ports: + check.is_in(f"Listening on 127.0.0.1 port {port}", foo_log) + + if bad_ports: + log.info("check that tincd fails to bind to %s", bad_ports) + + for port in bad_ports: + foo.cmd("add", "BindToAddress", f"127.0.0.1 {port}") + + util.remove_file(log_path) + proc = foo.tincd("-D") + + # Flush logs to the log file + if ok_ports: + foo[Script.TINC_UP].wait() + foo.cmd("stop") + foo[Script.TINC_DOWN].wait() + check.success(proc.wait()) + else: + check.failure(proc.wait()) + + foo_log = util.read_text(log_path) + + for port in bad_ports: + check.is_in(f"Can't bind to 127.0.0.1 port {port}", foo_log) + + if not ok_ports: + check.is_in("Unable to create any listening socket", foo_log) + + +port0, close0 = bind_random_port() +port1, close1 = bind_random_port() + +if not port0 or not port1: + log.info("could not bind ports, skipping test") + sys.exit(EXIT_SKIP) + +with Test("test binding with both ports unavailable") as context: + test_bind_port(context, [], [port0, port1]) + +with Test("test binding to one free and one unavailable port") as context: + close0() + test_bind_port(context, [port0], [port1]) + +with Test("test binding to two free ports") as context: + close1() + test_bind_port(context, [port0, port1], [])