Created
December 17, 2024 22:34
-
-
Save sirkirby/0fa39ef4fdb0a53fae07365cef3b1284 to your computer and use it in GitHub Desktop.
Migrate pi-hole A and CNAME dns records to AdGuard DNS Rewrite rules
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import sys | |
import yaml | |
import re | |
from typing import List, Dict, Any | |
class NoAliasDumper(yaml.SafeDumper): | |
"""Custom YAML dumper that ignores aliases.""" | |
def ignore_aliases(self, data): | |
return True | |
def parse_custom_hosts(custom_list_content: str) -> List[Dict[str, str]]: | |
"""Parse Pi-hole custom.list format into list of host records.""" | |
records = [] | |
for line in custom_list_content.strip().split('\n'): | |
line = line.strip() | |
if not line or line.startswith('#'): | |
continue | |
# Split line into IP and hostname | |
parts = line.split() | |
if len(parts) != 2: | |
continue | |
ip_address, hostname = parts | |
# Validate IP address format | |
if not re.match(r'^(?:\d{1,3}\.){3}\d{1,3}$', ip_address): | |
print(f"Warning: Skipping invalid IP address: {ip_address}", file=sys.stderr) | |
continue | |
# Create rewrite record | |
records.append({ | |
'domain': hostname, | |
'answer': ip_address | |
}) | |
return records | |
def parse_cname_records(cname_content: str) -> List[Dict[str, str]]: | |
"""Parse Pi-hole CNAME records into list of CNAME mappings.""" | |
records = [] | |
for line in cname_content.strip().split('\n'): | |
line = line.strip() | |
if not line or line.startswith('#'): | |
continue | |
# Parse CNAME record format: cname=alias,target | |
if not line.startswith('cname='): | |
continue | |
# Remove 'cname=' prefix and split into alias and target | |
mapping = line[6:].split(',') | |
if len(mapping) != 2: | |
continue | |
alias, target = mapping | |
# Create rewrite record | |
records.append({ | |
'domain': alias, | |
'answer': target | |
}) | |
return records | |
def create_adguard_config(host_records: List[Dict[str, str]], | |
cname_records: List[Dict[str, str]]) -> Dict[str, Any]: | |
"""Create AdGuard Home configuration structure.""" | |
# Combine all records | |
all_records = host_records + cname_records | |
# Create config structure | |
config = { | |
'dns': { | |
'rewrites': all_records | |
} | |
} | |
return config | |
def main(): | |
if len(sys.argv) != 3: | |
print("Usage: python script.py <custom.list> <05-pihole-custom-cname.conf>") | |
sys.exit(1) | |
custom_list_file = sys.argv[1] | |
cname_file = sys.argv[2] | |
# Read input files | |
try: | |
with open(custom_list_file, 'r') as f: | |
custom_list_content = f.read() | |
except FileNotFoundError: | |
print(f"Error: Custom hosts file {custom_list_file} not found", file=sys.stderr) | |
sys.exit(1) | |
try: | |
with open(cname_file, 'r') as f: | |
cname_content = f.read() | |
except FileNotFoundError: | |
print(f"Error: CNAME file {cname_file} not found", file=sys.stderr) | |
sys.exit(1) | |
# Parse records | |
host_records = parse_custom_hosts(custom_list_content) | |
cname_records = parse_cname_records(cname_content) | |
# Create config | |
config = create_adguard_config(host_records, cname_records) | |
# Output YAML | |
print(yaml.dump(config, Dumper=NoAliasDumper, default_flow_style=False, | |
sort_keys=False, width=float("inf"))) | |
if __name__ == "__main__": | |
main() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment