test: support get_bind_addrs and feature_bind_extra on macOS #34256

pull l0rinc wants to merge 5 commits into bitcoin:master from l0rinc:l0rinc/test-macos-bind-netutil changing 4 files +65 −37
  1. l0rinc commented at 9:42 pm on January 11, 2026: contributor

    Problem

    Some functional tests are shown as skipped when running on macOS because test_framework/netutil.py only implemented the Linux-specific logic for checking which TCP sockets a node is listening on.

    Fix

    Add macOS implementations in test/functional/test_framework/netutil.py so tests can query:

    • which TCP sockets a node is listening on (get_bind_addrs(), via lsof)
    • a non-loopback interface address (all_interfaces(), via ifconfig)

    Then enable the previously Linux-only tests by switching to a shared platform guard.

    Commands

    Command used

    0lsof -nP -a -p <pid> -iTCP -sTCP:LISTEN -Ftn
    

    Flags

    • -n: no hostname resolution
    • -P: no service/port-name resolution
    • -a: AND all conditions
    • -p : filter by process ID
    • -iTCP: TCP sockets only
    • -sTCP:LISTEN: listening sockets only
    • -Ftn: machine-readable output (fields: type t, name n)

    Regex parser

    0t(IPv[46])\nn(\*|\[.+?]|[^:]+):(\d+)
    

    Captured groups

    • group 1: IPv4 / IPv6 (used to disambiguate *)
    • group 2: host (*, [::1], 127.0.0.1, …)
    • group 3: port

    Command used

    0ifconfig -au
    

    Regex parsing

    Interface blocks:

    0(?m)^(?P<iface>\S+):(?P<block>[^\n]*(?:\n[ \t]+[^\n]*)*)
    

    IPv4 extraction within each block:

    0inet (\S+)
    

    Notes

    The only remaining platform skips on macOS are the USDT/BPF tracing tests (interface_usdt_*.py).

  2. DrahtBot added the label Tests on Jan 11, 2026
  3. DrahtBot commented at 9:42 pm on January 11, 2026: contributor

    The following sections might be updated with supplementary metadata relevant to reviewers and maintainers.

    Code Coverage & Benchmarks

    For details see: https://corecheck.dev/bitcoin/bitcoin/pulls/34256.

    Reviews

    See the guideline for information on the review process.

    Type Reviewers
    Concept ACK Sjors
    Stale ACK bensig

    If your review is incorrectly listed, please copy-paste <!–meta-tag:bot-skip–> into the comment that the bot should ignore.

  4. in test/functional/test_framework/netutil.py:91 in 9b3a39cf60
    92-    bind_addrs = []
    93-    for conn in netstat('tcp') + netstat('tcp6'):
    94-        if conn[3] == STATE_LISTEN and conn[4] in inodes:
    95-            bind_addrs.append(conn[1])
    96-    return bind_addrs
    97+    if sys.platform.startswith("linux"):
    


    Sjors commented at 4:14 am on January 12, 2026:

    In 9b3a39cf60dbad453874d3474dc21ecb960ffcfc test: support get_bind_addrs on macOS nit: you can get rid of startswith, since our minimum Python version is well after 3.3 which removed “linux2” and “linux3”: https://docs.python.org/3.3/whatsnew/3.3.html#porting-python-code

    Replace sys.platform == ‘linux2’ with sys.platform.startswith(‘linux’), or directly sys.platform == ‘linux’ if you don’t need to support older Python versions.


    l0rinc commented at 7:47 am on January 12, 2026:
    Thanks, done, added you as coauthor
  5. in test/functional/test_framework/netutil.py:104 in 9b3a39cf60 outdated
    105+        import re
    106+        import subprocess
    107+        output = subprocess.check_output(["lsof", "-nP", "-a", "-p", str(pid), "-iTCP", "-sTCP:LISTEN", "-Ftn"], text=True)
    108+        return [
    109+            (addr_to_hex(("::" if sock_type == "IPv6" else "0.0.0.0") if host == "*" else host.strip("[]")), int(port))
    110+            for sock_type, host, port in re.findall(r"t(IPv[46])\nn(\*|\[.+?]|[^:]+):(\d+)", output)
    


    Sjors commented at 4:22 am on January 12, 2026:

    (delete wrong commit reference)

    Review note, this is what the command outputs (without -Ftv):

    0% lsof -nP -a -p 92306 -iTCP -sTCP:LISTEN
    1COMMAND    PID  USER   FD   TYPE             DEVICE SIZE/OFF NODE NAME
    2bitcoind 92306 sjors   10u  IPv6 0x41f09f8c114a7707      0t0  TCP [::1]:18443 (LISTEN)
    3bitcoind 92306 sjors   11u  IPv4 0x9b8ff0d88c43ff6f      0t0  TCP 127.0.0.1:18443 (LISTEN)
    4bitcoind 92306 sjors   19u  IPv4 0xd8de1cae67469884      0t0  TCP *:18444 (LISTEN)
    5bitcoind 92306 sjors   21u  IPv4 0x33676f2f9e2adc58      0t0  TCP 127.0.0.1:18445 (LISTEN)
    6bitcoind 92306 sjors   22u  IPv6 0x776a345d97d44893      0t0  TCP *:18444 (LISTEN)
    

    And with -Ftv:

     0p92306
     1f10
     2tIPv6
     3n[::1]:18443
     4f11
     5tIPv4
     6n127.0.0.1:18443
     7f19
     8tIPv4
     9n*:18444
    10f21
    11tIPv4
    12n127.0.0.1:18445
    13f22
    14tIPv6
    15n*:18444
    

    I didn’t study the regex in much detail, I guess it works…


    l0rinc commented at 7:47 am on January 12, 2026:
    Please see the collapsed sections in the PR description for more details. Let me know if you think more explanation is needed anywhere.
  6. Sjors commented at 4:24 am on January 12, 2026: member

    Concept ACK, still need to look at the third and fourth commit.

    Tested that the two functional tests now run on macOS. I also introduced some regressions to see if they’re caught.

    Nit: commit messages contain literal \n instead of newline.

  7. l0rinc force-pushed on Jan 12, 2026
  8. bensig commented at 9:54 pm on January 12, 2026: contributor
    ACK a843fde881820a70b72f0be0ff6e9602bb7ae1dc
  9. DrahtBot requested review from Sjors on Jan 12, 2026
  10. in test/functional/test_framework/netutil.py:111 in 2296a19c96
    105@@ -106,33 +106,44 @@ def get_bind_addrs(pid):
    106     else:
    107         raise NotImplementedError(f"get_bind_addrs is not supported on {sys.platform}")
    108 
    109-# from: https://code.activestate.com/recipes/439093/
    110 def all_interfaces():
    111     '''
    112     Return all interfaces that are up
    


    Sjors commented at 8:58 am on January 13, 2026:
    Let’s clarify that this only returns IPv4 interfaces (only obvious to people who recognise AF_INET in the Linux code).

  11. in test/functional/test_framework/netutil.py:143 in 2296a19c96 outdated
    161+        import subprocess
    162+        output = subprocess.check_output(["ifconfig", "-au"], text=True)
    163+        return [
    164+            (m["iface"].encode(), ip)
    165+            for m in re.finditer(r"(?m)^(?P<iface>\S+):(?P<block>[^\n]*(?:\n[ \t]+[^\n]*)*)", output)
    166+            for ip in re.findall(r"inet (\S+)", m["block"])
    


    Sjors commented at 3:16 pm on January 13, 2026:

    In 2296a19c96294eca9f376249a1745cd077fb1f6a test: support all_interfaces on macOS: I don’t mind keeping the regex approach, but something like this is easier to read:

    0# Get list of "up" interfaces
    1ifaces = subprocess.check_output(["ifconfig", "-lu"], text=True).split()
    2return [
    3    (iface.encode(), res.strip()) for iface in ifaces
    4    if (res := subprocess.run(["ipconfig", "getifaddr", iface], text=True).stdout) is not None
    5]
    

    Sjors commented at 8:05 pm on January 13, 2026:

    Actually the above short version doesn’t work, it just returns an empty result, which the test seems to be happy with.

    Back to the more verbose version:

     0results = []
     1for iface in ifaces:
     2    try:
     3        res = subprocess.check_output(
     4            ["ipconfig", "getifaddr", iface],
     5            text=True
     6        ).strip()
     7    except subprocess.CalledProcessError:
     8        continue
     9    if res:
    10        results.append((iface.encode(), res))
    11
    12return results
    

    However it’s still not identical to your version, because it doesn’t the return the loopback IP. That’s because ipconfig getifaddr lo0 returns nothing. Might be a matter of tweaking the getifaddr command, so the man page doesn’t suggest any obvious way.


    l0rinc commented at 12:24 pm on January 16, 2026:
  12. DrahtBot requested review from Sjors on Jan 13, 2026
  13. test: support `get_bind_addrs` on macOS
    The bind functional tests need to query which TCP sockets a node is listening on.
    
    Add a macOS implementation using `lsof` to query listening TCP sockets for a given PID and return results as `(addr_hex, port)` tuples, matching the Linux format.
    a7fdc42c58
  14. test: enable `feature_bind_extra` on macOS
    `feature_bind_extra` verifies `-bind/-whitebind` interactions and asserts that `bitcoind` listens on the expected addresses.
    
    With `get_bind_addrs()` now supported on macOS, allow the test to run there as well.
    Add a reusable `skip_if_platform_not_linux_or_mac()` helper and use it instead of a Linux-only skip.
    4fb1523f35
  15. test: support `all_interfaces` on macOS
    `rpc_bind` uses `all_interfaces()` to pick a non-loopback IPv4 address for `rpcallowip/rpcbind` coverage.
    
    Add a macOS implementation by parsing `ifconfig` output and returning `(ifname, ip)` tuples in the same shape.
    98c3629ec5
  16. test: enable `rpc_bind` on macOS
    `rpc_bind` validates `-rpcbind` and `-rpcallowip` behavior and asserts the node listens on the expected addresses.
    
    With `get_bind_addrs()` and `all_interfaces()` now supported on macOS, allow `rpc_bind` to run there as well by switching to `skip_if_platform_not_linux_or_mac()`.
    
    Co-authored-by: Sjors Provoost <sjors@sprovoost.nl>
    b1480b1f56
  17. test: use exact platform check for Linux in `get_bind_addrs` and `all_interfaces`
    See https://docs.python.org/3.3/whatsnew/3.3.html#porting-python-code
    > Replace sys.platform == 'linux2' with sys.platform.startswith('linux'), or directly sys.platform == 'linux' if you don't need to support older Python versions.
    
    Co-authored-by: Sjors Provoost <sjors@sprovoost.nl>
    67002b0b2c
  18. l0rinc force-pushed on Jan 16, 2026


l0rinc DrahtBot Sjors bensig


Sjors

Labels
Tests


github-metadata-mirror

This is a metadata mirror of the GitHub repository bitcoin/bitcoin. This site is not affiliated with GitHub. Content is generated from a GitHub metadata backup.
generated: 2026-02-02 06:13 UTC

This site is hosted by @0xB10C
More mirrored repositories can be found on mirror.b10c.me