Skip to content

Instantly share code, notes, and snippets.

@shift
Last active April 6, 2024 09:20
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save shift/e9dca149259c96e11b4ac007ac0f4f0d to your computer and use it in GitHub Desktop.
Save shift/e9dca149259c96e11b4ac007ac0f4f0d to your computer and use it in GitHub Desktop.
Enables knotd+kresd to serve as a replacement for DNSmasq and still retain the dynamic DNS updates from DHCPv4 & DHCPv6 leases.
#!/bin/sh
# Configuration
lease_file='/tmp/hosts/odhcpd'
forward_zone='ber.section.me.'
reverse_zone_ipv4='1.168.192.in-addr.arpa.'
reverse_zone_ipv6='f.d.0.5.0.7.4.0.1.0.0.2.ip6.arpa.'
# Function for code reusability and readability
expand_ipv6() {
ip=$1
# Handle leading ::
echo $ip | grep -qs "^:" && ip="0${ip}"
# Expand ::
if echo $ip | grep -qs "::"; then
colons=$(echo $ip | sed 's/[^:]//g')
missing=$(echo ":::::::::" | sed "s/$colons//")
expanded=$(echo $missing | sed 's/:/:0/g')
ip=$(echo $ip | sed "s/::/$expanded/")
fi
# Split into blocks
blocks=$(echo $ip | grep -o "[0-9a-f]\\+")
set $blocks
printf "%04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x\\n" \
$(hex2dec $1) \
$(hex2dec $2) \
$(hex2dec $3) \
$(hex2dec $4) \
$(hex2dec $5) \
$(hex2dec $6) \
$(hex2dec $7) \
$(hex2dec $8)
}
# Function to convert hex to dec (portable version)
hex2dec() {
[ "$1" != "" ] && printf "%d" "$(( 0x$1 ))"
}
# Function to reverse a string
reverse_string() {
input=$1
reversed=""
while [ -n "$input" ]; do
# Extract the last character and append it to the reversed string
last_char=$(echo "$input" | sed 's/.*\(.\)$/\1/')
reversed="${reversed}${last_char}"
# Remove the last character from the input string
input=$(echo "$input" | sed 's/.\{1\}$//')
done
echo "$reversed"
}
# Function to convert an IPv6 address to reverse DNS format
ipv6_to_dns() {
ip=$(expand_ipv6 $1)
# Remove colons
ip=$(echo $ip | tr -d ':')
# Reverse the nibbles
rev_ip=$(reverse_string $ip)
# Insert dots between the nibbles
rev_ip=$(echo $rev_ip | sed 's/./&./g')
# Append .ip6.arpa
echo "${rev_ip}ip6.arpa."
}
update_records() {
hostname=$1
ip=$2
ip_type=$3
if [ "$ip_type" = "IPv4" ]; then
set -- $(echo "$ip" | tr '.' ' ')
reverse_ip="$4.$3.$2.$1"
last_octet="$4"
ptr_record="$last_octet.$reverse_zone_ipv4"
else # IPv6
ptr_record=$(ipv6_to_dns $ip)
fi
echo "Updating $hostname <-> $ip"
# Update DNS records (forward and reverse zones)
knotc zone-begin $forward_zone
knotc zone-unset $forward_zone $hostname $([ "$ip_type" = "IPv4" ] && echo A || echo AAAA)
knotc zone-set $forward_zone $hostname 300 $([ "$ip_type" = "IPv4" ] && echo A || echo AAAA) $ip
knotc zone-commit $forward_zone
knotc zone-begin $([ "$ip_type" = "IPv4" ] && echo $reverse_zone_ipv4 || echo $reverse_zone_ipv6)
knotc zone-unset $([ "$ip_type" = "IPv4" ] && echo $reverse_zone_ipv4 || echo $reverse_zone_ipv6) $ptr_record PTR
knotc zone-set $([ "$ip_type" = "IPv4" ] && echo $reverse_zone_ipv4 || echo $reverse_zone_ipv6) $ptr_record 300 PTR $hostname.$forward_zone
knotc zone-commit $([ "$ip_type" = "IPv4" ] && echo $reverse_zone_ipv4 || echo $reverse_zone_ipv6)
}
# Lease parsing
while read -r line; do
if [ "${line:0:1}" != "#" ]; then
ip=$(echo $line | cut -d ' ' -f1)
hostname=$(echo $line | cut -d ' ' -f2)
# IP Validation with improved IPv6 check
if echo "$ip" | grep -qE '^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$'; then
ip_type="IPv4"
elif echo "$ip" | grep -qE '(([0-9a-fA-F]{1,4}:){7}[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,7}:|([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}|([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}|([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}|([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}|[0-9a-fA-F]{1,4}:((:[0-9a-fA-F]{1,4}){1,6})|:((:[0-9a-fA-F]{1,4}){1,7}|:)|fe80:(:[0-9a-fA-F]{0,4}){0,4}%[0-9a-zA-Z]{1,}|::(ffff(:0{1,4}){0,1}:){0,1}((25[0-5]|(2[0-4]|1{0,2}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,2}[0-9]){0,1}[0-9])|([0-9a-fA-F]{1,4}:){1,4}:((25[0-5]|(2[0-4]|1{0,2}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,2}[0-9]){0,1}[0-9]))' ; then
ip_type="IPv6"
else
echo "Invalid IP address: $ip" >&2
continue
fi
update_records "$hostname" "$ip" "$ip_type"
fi
done < "$lease_file"
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment