{"id":10615,"date":"2025-11-14T09:12:54","date_gmt":"2025-11-14T14:12:54","guid":{"rendered":"https:\/\/stevepedwards.today\/DebianAdmin\/?p=10615"},"modified":"2025-11-14T09:12:54","modified_gmt":"2025-11-14T14:12:54","slug":"python-script-to-find-local-lan-mdns-name-resolved-clients-to-rewrite-wsl2-hosts-file","status":"publish","type":"post","link":"https:\/\/stevepedwards.today\/DebianAdmin\/python-script-to-find-local-lan-mdns-name-resolved-clients-to-rewrite-wsl2-hosts-file\/","title":{"rendered":"Python Script to Find Local LAN mDNS Name Resolved Clients to Rewrite WSL2 Hosts File"},"content":{"rendered":"<div class=\"pvc_clear\"><\/div><p id=\"pvc_stats_10615\" class=\"pvc_stats all  \" data-element-id=\"10615\" style=\"\"><i class=\"pvc-stats-icon medium\" aria-hidden=\"true\"><svg aria-hidden=\"true\" focusable=\"false\" data-prefix=\"far\" data-icon=\"chart-bar\" role=\"img\" xmlns=\"http:\/\/www.w3.org\/2000\/svg\" viewBox=\"0 0 512 512\" class=\"svg-inline--fa fa-chart-bar fa-w-16 fa-2x\"><path fill=\"currentColor\" d=\"M396.8 352h22.4c6.4 0 12.8-6.4 12.8-12.8V108.8c0-6.4-6.4-12.8-12.8-12.8h-22.4c-6.4 0-12.8 6.4-12.8 12.8v230.4c0 6.4 6.4 12.8 12.8 12.8zm-192 0h22.4c6.4 0 12.8-6.4 12.8-12.8V140.8c0-6.4-6.4-12.8-12.8-12.8h-22.4c-6.4 0-12.8 6.4-12.8 12.8v198.4c0 6.4 6.4 12.8 12.8 12.8zm96 0h22.4c6.4 0 12.8-6.4 12.8-12.8V204.8c0-6.4-6.4-12.8-12.8-12.8h-22.4c-6.4 0-12.8 6.4-12.8 12.8v134.4c0 6.4 6.4 12.8 12.8 12.8zM496 400H48V80c0-8.84-7.16-16-16-16H16C7.16 64 0 71.16 0 80v336c0 17.67 14.33 32 32 32h464c8.84 0 16-7.16 16-16v-16c0-8.84-7.16-16-16-16zm-387.2-48h22.4c6.4 0 12.8-6.4 12.8-12.8v-70.4c0-6.4-6.4-12.8-12.8-12.8h-22.4c-6.4 0-12.8 6.4-12.8 12.8v70.4c0 6.4 6.4 12.8 12.8 12.8z\" class=\"\"><\/path><\/svg><\/i> <img loading=\"lazy\" decoding=\"async\" width=\"16\" height=\"16\" alt=\"Loading\" src=\"https:\/\/stevepedwards.today\/DebianAdmin\/wp-content\/plugins\/page-views-count\/ajax-loader-2x.gif\" border=0 \/><\/p><div class=\"pvc_clear\"><\/div>\r\n<pre class=\"wp-block-code\"><code>import os\r\nimport subprocess\r\nimport sys\r\nimport re\r\nimport socket\r\nimport time\r\n\r\n# --- Configuration ---\r\nHOSTS_FILE = \"\/etc\/hosts\"\r\n\r\n# Define the set of ALL known machines that need mDNS resolution in your network\r\n# The script will try to find and manage entries for all of these *except* the current machine.\r\n# Keep these lowercase as we will convert discovered hostnames to lowercase for comparison.\r\nALL_NETWORK_MACHINES = [\"hplaptop\", \"babyhp\"] # Add any other fixed machines here (e.g., \"myfileserver\")\r\n\r\n# Regex patterns for parsing avahi-browse -atr output\r\nHOSTNAME_PATTERN = re.compile(r'^\\s*hostname = \\[(.*?\\.local)\\]$')\r\nADDRESS_PATTERN = re.compile(r'^\\s*address = \\[(\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3})\\]$') # Only captures IPv4\r\n\r\n# --- Functions ---\r\n\r\ndef get_mdns_discovered_hosts():\r\n    \"\"\"\r\n    Uses avahi-browse -atr to discover .local hostnames and their IPs.\r\n    Returns a dictionary: { \"hostname.local\": \"IP_Address\" }\r\n    \"\"\"\r\n    discovered_hosts = {}\r\n    print(\"--- DEBUG: Starting mDNS discovery using avahi-browse -atr...\")\r\n    sys.stdout.flush()\r\n    try:\r\n        cmd = ['avahi-browse', '-atr']\r\n\r\n        result = subprocess.run(\r\n            cmd,\r\n            capture_output=True,\r\n            text=True,\r\n            check=True,\r\n            timeout=10\r\n        )\r\n\r\n        print(\"--- DEBUG: avahi-browse command stdout ---\")\r\n        print(result.stdout)\r\n        print(\"--- DEBUG: avahi-browse command stderr ---\")\r\n        print(result.stderr)\r\n        print(\"------------------------------------------\")\r\n        sys.stdout.flush()\r\n\r\n        current_hostname_full = None\r\n        for line in result.stdout.splitlines():\r\n            hostname_match = HOSTNAME_PATTERN.match(line)\r\n            if hostname_match:\r\n                current_hostname_full = hostname_match.group(1)\r\n                continue\r\n\r\n            address_match = ADDRESS_PATTERN.match(line)\r\n            if address_match:\r\n                ip_address = address_match.group(1)\r\n\r\n                if current_hostname_full:\r\n                    discovered_hosts[current_hostname_full] = ip_address\r\n                    print(f\"--- DEBUG: Found mDNS entry: {current_hostname_full} -&gt; {ip_address}\")\r\n                    sys.stdout.flush()\r\n                    current_hostname_full = None\r\n\r\n        print(\"--- DEBUG: mDNS discovery completed.\")\r\n        sys.stdout.flush()\r\n\r\n    except subprocess.CalledProcessError as e:\r\n        print(f\"--- ERROR: avahi-browse failed with exit code {e.returncode} ---\", file=sys.stderr)\r\n        print(f\"STDOUT: {e.stdout}\", file=sys.stderr)\r\n        print(f\"STDERR: {e.stderr}\", file=sys.stderr)\r\n        print(\"Please ensure avahi-browse is installed and avahi-daemon is running on advertising hosts.\", file=sys.stderr)\r\n        sys.stderr.flush()\r\n    except subprocess.TimeoutExpired:\r\n        print(\"--- ERROR: avahi-browse timed out after 10 seconds. It might not be running or discovery is very slow.\", file=sys.stderr)\r\n        sys.stderr.flush()\r\n    except FileNotFoundError:\r\n        print(\"--- ERROR: avahi-browse command not found. Is avahi-utils installed?\", file=sys.stderr)\r\n        sys.stderr.flush()\r\n    except Exception as e:\r\n        print(f\"--- ERROR: An unexpected error occurred while Browse mDNS: {e}\", file=sys.stderr)\r\n        sys.stderr.flush()\r\n\r\n    return discovered_hosts\r\n\r\ndef get_own_lan_ip():\r\n    \"\"\"\r\n    Retrieves the current machine's primary IPv4 address on the LAN.\r\n    \"\"\"\r\n    print(\"--- DEBUG: Getting own LAN IP...\")\r\n    sys.stdout.flush()\r\n    s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)\r\n    try:\r\n        s.connect(('8.8.8.8', 1))\r\n        IP = s.getsockname()[0]\r\n        print(f\"--- DEBUG: Own LAN IP found: {IP}\")\r\n        sys.stdout.flush()\r\n    except Exception as e:\r\n        IP = '127.0.0.1'\r\n        print(f\"--- DEBUG: Could not determine own LAN IP, falling back to {IP}. Error: {e}\")\r\n        sys.stdout.flush()\r\n    finally:\r\n        s.close()\r\n    return IP\r\n\r\ndef generate_new_hosts_content(current_machine_hostname, own_lan_ip, discovered_hosts_data):\r\n    \"\"\"\r\n    Generates the desired content for the new \/etc\/hosts file.\r\n    discovered_hosts_data is expected to be a dict of { \"short_hostname\": \"IP_Address\" }\r\n    \"\"\"\r\n    print(\"--- DEBUG: Generating new \/etc\/hosts content...\")\r\n    sys.stdout.flush()\r\n\r\n    hosts_content = [\r\n        \"127.0.0.1\\tlocalhost\",\r\n        \"127.0.1.1\\t\" + current_machine_hostname,\r\n        f\"{own_lan_ip}\\t{current_machine_hostname}\",\r\n    ]\r\n\r\n    added_hostnames = {current_machine_hostname} # Keep track of already added hostnames (e.g., current machine)\r\n\r\n    for short_name, ip_address in discovered_hosts_data.items():\r\n        # Ensure we don't add the current machine's own entry twice if discovered via mDNS\r\n        if short_name.lower() != current_machine_hostname.lower() and short_name not in added_hostnames: # Added .lower() for robustness\r\n            hosts_content.append(f\"{ip_address}\\t{short_name}\")\r\n            added_hostnames.add(short_name)\r\n            print(f\"--- DEBUG: Added\/Updated '{short_name}' to content with IP {ip_address}\")\r\n            sys.stdout.flush()\r\n\r\n    print(\"--- DEBUG: Finished generating content.\")\r\n    sys.stdout.flush()\r\n    return \"\\n\".join(hosts_content) + \"\\n\"\r\n\r\ndef recreate_hosts_file(content, hosts_file_path):\r\n    \"\"\"Deletes existing \/etc\/hosts and writes new content.\"\"\"\r\n    try:\r\n        print(f\"--- DEBUG: Deleting existing {hosts_file_path}...\")\r\n        sys.stdout.flush()\r\n        rm_result = subprocess.run(['sudo', 'rm', '-f', hosts_file_path], capture_output=True, text=True, check=True)\r\n        print(f\"--- DEBUG: rm stdout: {rm_result.stdout.strip()}\")\r\n        print(f\"--- DEBUG: rm stderr: {rm_result.stderr.strip()}\")\r\n        print(f\"Successfully deleted {hosts_file_path}.\")\r\n        sys.stdout.flush()\r\n\r\n        print(f\"--- DEBUG: Recreating {hosts_file_path} with new content...\")\r\n        sys.stdout.flush()\r\n\r\n        tee_process = subprocess.run(\r\n            ['sudo', 'tee', hosts_file_path],\r\n            input=content.encode('utf-8'),\r\n            capture_output=True,\r\n            check=True\r\n        )\r\n        print(f\"--- DEBUG: tee stdout: {tee_process.stdout.strip()}\")\r\n        print(f\"--- DEBUG: tee stderr: {tee_process.stderr.strip()}\")\r\n        print(f\"Successfully recreated {hosts_file_path}.\")\r\n        sys.stdout.flush()\r\n\r\n    except subprocess.CalledProcessError as e:\r\n        print(f\"--- ERROR: Recreating {hosts_file_path} failed with exit code {e.returncode} ---\", file=sys.stderr)\r\n        print(f\"STDOUT: {e.stdout}\", file=sys.stderr)\r\n        print(f\"STDERR: {e.stderr}\", file=sys.stderr)\r\n        sys.stderr.flush()\r\n        sys.exit(1)\r\n    except Exception as e:\r\n        print(f\"--- ERROR: An unexpected error occurred during hosts file recreation: {e}\", file=sys.stderr)\r\n        sys.stderr.flush()\r\n        sys.exit(1)\r\n\r\n# --- Main Execution ---\r\nif __name__ == \"__main__\":\r\n    print(\"--- SCRIPT EXECUTION START ---\")\r\n    sys.stdout.flush()\r\n\r\n    if os.geteuid() != 0:\r\n        print(\"This script needs to be run with sudo to modify \/etc\/hosts.\", file=sys.stderr)\r\n        sys.stderr.flush()\r\n        sys.exit(1)\r\n\r\n    print(\"Starting mDNS-based \/etc\/hosts recreation for IPv4 hosts...\")\r\n    sys.stdout.flush()\r\n\r\n    # Dynamically determine the current hostname of THIS machine (and convert to lowercase for comparison)\r\n    CURRENT_MACHINE_HOSTNAME = socket.gethostname().split('.')[0]\r\n    print(f\"--- DEBUG: Current machine's short hostname: {CURRENT_MACHINE_HOSTNAME}\")\r\n    sys.stdout.flush()\r\n\r\n    print(\"--- DEBUG: Checking for avahi-utils installation...\")\r\n    sys.stdout.flush()\r\n    try:\r\n        subprocess.run(['dpkg', '-s', 'avahi-utils'], capture_output=True, check=True, text=True)\r\n        print(\"--- DEBUG: avahi-utils found.\")\r\n        sys.stdout.flush()\r\n    except subprocess.CalledProcessError:\r\n        print(\"--- ERROR: avahi-utils not found. Please install it: sudo apt install avahi-utils\", file=sys.stderr)\r\n        sys.stderr.flush()\r\n        sys.exit(1)\r\n\r\n    # Get this machine's own LAN IP\r\n    own_ip = get_own_lan_ip()\r\n    print(f\"This machine ({CURRENT_MACHINE_HOSTNAME})'s LAN IP: {own_ip}\")\r\n    sys.stdout.flush()\r\n\r\n    # Get all discovered hosts from mDNS\r\n    discovered_mdns_hosts_full = get_mdns_discovered_hosts()\r\n\r\n    # Process discovered hosts into a map of { \"short_hostname\": \"ip_address\" }\r\n    filtered_and_shortened_discovered_hosts = {}\r\n    print(\"--- DEBUG: Processing mDNS discovered hosts for target machines...\")\r\n    sys.stdout.flush()\r\n    # Get current machine hostname in lowercase for robust comparison\r\n    current_machine_short_lower = CURRENT_MACHINE_HOSTNAME.lower()\r\n\r\n    for full_name_mdns, ip in discovered_mdns_hosts_full.items():\r\n        # Convert the discovered hostname to lowercase for robust comparison\r\n        short_name_lower = full_name_mdns.replace(\".local\", \"\").lower()\r\n\r\n        # Check if this discovered host is one of our target machines (case-insensitively)\r\n        # AND if it's not the current machine itself.\r\n        if short_name_lower in ALL_NETWORK_MACHINES and short_name_lower != current_machine_short_lower:\r\n            # We add the original full_name (e.g., BabyHP.local) to the filtered list's keys\r\n            # and the short_name (e.g., babyhp) as the key for the hosts content generation.\r\n            # This requires a slight change in how filtered_and_shortened_discovered_hosts is used.\r\n            # Let's simplify: store the original short_name (case as seen from Avahi) if desired,\r\n            # but use its lowercase form for comparison.\r\n\r\n            # The generate_new_hosts_content function expects short_hostname as key\r\n            # so let's use the original case as given by Avahi for the value in the hosts file.\r\n            # So, we need to save the original short_name from full_name_mdns, not the lowercase one.\r\n            original_short_name = full_name_mdns.replace(\".local\", \"\") # Keep original case for the hosts file entry\r\n\r\n            filtered_and_shortened_discovered_hosts[original_short_name] = ip\r\n            print(f\"  ADDING TARGET host to hosts file list: '{original_short_name}' at {ip}\")\r\n            sys.stdout.flush()\r\n        else:\r\n            print(f\"  Ignoring non-target\/self mDNS host: '{short_name_lower}' at {ip}\") # Use lowercase for debug output here for consistency\r\n            sys.stdout.flush()\r\n\r\n    # Generate the new content for \/etc\/hosts\r\n    new_hosts_content = generate_new_hosts_content(\r\n        CURRENT_MACHINE_HOSTNAME,\r\n        own_ip,\r\n        filtered_and_shortened_discovered_hosts\r\n    )\r\n\r\n    # Recreate the \/etc\/hosts file with the generated content\r\n    recreate_hosts_file(new_hosts_content, HOSTS_FILE)\r\n\r\n    print(\"--- SCRIPT EXECUTION FINISHED ---\")\r\n    sys.stdout.flush()<\/code><\/pre>\r\n","protected":false},"excerpt":{"rendered":"<div class=\"pvc_clear\"><\/div>\n<p id=\"pvc_stats_10615\" class=\"pvc_stats all  \" data-element-id=\"10615\" style=\"\"><i class=\"pvc-stats-icon medium\" aria-hidden=\"true\"><svg aria-hidden=\"true\" focusable=\"false\" data-prefix=\"far\" data-icon=\"chart-bar\" role=\"img\" xmlns=\"http:\/\/www.w3.org\/2000\/svg\" viewBox=\"0 0 512 512\" class=\"svg-inline--fa fa-chart-bar fa-w-16 fa-2x\"><path fill=\"currentColor\" d=\"M396.8 352h22.4c6.4 0 12.8-6.4 12.8-12.8V108.8c0-6.4-6.4-12.8-12.8-12.8h-22.4c-6.4 0-12.8 6.4-12.8 12.8v230.4c0 6.4 6.4 12.8 12.8 12.8zm-192 0h22.4c6.4 0 12.8-6.4 12.8-12.8V140.8c0-6.4-6.4-12.8-12.8-12.8h-22.4c-6.4 0-12.8 6.4-12.8 12.8v198.4c0 6.4 6.4 12.8 12.8 12.8zm96 0h22.4c6.4 0 12.8-6.4 12.8-12.8V204.8c0-6.4-6.4-12.8-12.8-12.8h-22.4c-6.4 0-12.8 6.4-12.8 12.8v134.4c0 6.4 6.4 12.8 12.8 12.8zM496 400H48V80c0-8.84-7.16-16-16-16H16C7.16 64 0 71.16 0 80v336c0 17.67 14.33 32 32 32h464c8.84 0 16-7.16 16-16v-16c0-8.84-7.16-16-16-16zm-387.2-48h22.4c6.4 0 12.8-6.4 12.8-12.8v-70.4c0-6.4-6.4-12.8-12.8-12.8h-22.4c-6.4 0-12.8 6.4-12.8 12.8v70.4c0 6.4 6.4 12.8 12.8 12.8z\" class=\"\"><\/path><\/svg><\/i> <img loading=\"lazy\" decoding=\"async\" width=\"16\" height=\"16\" alt=\"Loading\" src=\"https:\/\/stevepedwards.today\/DebianAdmin\/wp-content\/plugins\/page-views-count\/ajax-loader-2x.gif\" border=0 \/><\/p>\n<div class=\"pvc_clear\"><\/div>\n","protected":false},"author":1,"featured_media":0,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[4],"tags":[],"class_list":["post-10615","post","type-post","status-publish","format-standard","hentry","category-post"],"a3_pvc":{"activated":true,"total_views":2,"today_views":0},"_links":{"self":[{"href":"https:\/\/stevepedwards.today\/DebianAdmin\/wp-json\/wp\/v2\/posts\/10615","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/stevepedwards.today\/DebianAdmin\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/stevepedwards.today\/DebianAdmin\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/stevepedwards.today\/DebianAdmin\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/stevepedwards.today\/DebianAdmin\/wp-json\/wp\/v2\/comments?post=10615"}],"version-history":[{"count":1,"href":"https:\/\/stevepedwards.today\/DebianAdmin\/wp-json\/wp\/v2\/posts\/10615\/revisions"}],"predecessor-version":[{"id":10616,"href":"https:\/\/stevepedwards.today\/DebianAdmin\/wp-json\/wp\/v2\/posts\/10615\/revisions\/10616"}],"wp:attachment":[{"href":"https:\/\/stevepedwards.today\/DebianAdmin\/wp-json\/wp\/v2\/media?parent=10615"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/stevepedwards.today\/DebianAdmin\/wp-json\/wp\/v2\/categories?post=10615"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/stevepedwards.today\/DebianAdmin\/wp-json\/wp\/v2\/tags?post=10615"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}