Skip to content

Instantly share code, notes, and snippets.

@sirkirby
Created December 17, 2024 22:34
Show Gist options
  • Save sirkirby/0fa39ef4fdb0a53fae07365cef3b1284 to your computer and use it in GitHub Desktop.
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
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