dnsdhcpsynology

How can I update a non-windows DNS server A records from DHCP on a synology NAS


Background I want to dynamically update my DNS A records from my DHCP server, both running on the same synology NAS. Unfortunately Synology has (still) not provided a working solution for this.

I have been using a shell script to accomplish this, but it seems to work imperfectly in that after a while (days, weeks) my DNS server does not resolve my local addresses anymore, most likely because the shell scripts puts the dns records in an incompatible order.

So..


Solution

  • Never mind, I found a different way than c# and mono to update my local dns dynamically.

    The script I tried in the first place was the one I found here, but after a couple of days my local DNS queries did not work anymore for some unknown reason.

    As it turns out the dhcp server on my Synology NAS (DS214Play) calls a bash script after leases have changed, in the location /usr/share/dhcpd/dhcpd-script.sh

    After I found out you can actually change your (local) dns records easily using the nsupdate command, I updated the existing script to the one below. The lines I added are marked with my initials HH.

    As for security on the dns update: I did not use a key, as my DNS server only accepts updates from the internal network and local host anyway.

    If you found my answer useful, please also upvote the question if it's still negative. (I answered it myself) NOTE1: I found out that DSM updates may revert the file to its original NOTE2: Some of my markings were lost, so I updated the code below

    #!/bin/sh
    # Copyright (c) 2000-2013 Synology Inc. All rights reserved.
    #HH20191123 Updated for dynamic DNS updates
    
    DHCPD_DIR="/etc/dhcpd/"
    LEASE_FILE="${DHCPD_DIR}/dhcpd.conf.leases"
    LOG_FILE="${DHCPD_DIR}/dhcpd-leases.log"
    TMP_FILE="${DHCPD_DIR}/tmp-dhcpd-leases.log"
    
    
    # HH20191123: Define the zone (local domain name) here, but without a trailing point
    ZONE_NAME="hhbhasenack.local"
    
    # HH20191123: Prepare a file for updating the DNS through the nsupdate command
    TMP_NSUPDATE="${DHCPD_DIR}/tmp-nsupdate.log"
    NSUPDATE_LOG_FILE="${DHCPD_DIR}/nsupdate.log"
    echo "server 127.0.0.1" > ${TMP_NSUPDATE}
    echo "zone ${ZONE_NAME}." >> ${TMP_NSUPDATE}
    
    
    
    del_leases() { # $2: mac
        local mac=$2
        grep -v "${mac}" ${LOG_FILE} > ${TMP_FILE}
        cp ${TMP_FILE} ${LOG_FILE}
    }
    
    renew_record() { # $1: expired $2 mac $3 ip $4 hostname $5 iface
        local record=$@
        local mac=$2
        local iface=$5
    
        grep -v "${mac}" ${LOG_FILE} > ${TMP_FILE}
        echo "${record}" >> ${TMP_FILE}
        cp ${TMP_FILE} ${LOG_FILE}
    
    #HH20191123: Prepare dns update command
        local ip="$3"
        local hostname="$4"
        echo "update delete ${hostname}.${ZONE_NAME} A"  >> ${TMP_NSUPDATE}
        echo "update add ${hostname}.${ZONE_NAME} 3600 A ${ip}" >> ${TMP_NSUPDATE}
    
    }
    
    add_new_record() {
        local record="$@"
        local mac=$2
    
        # when disable dhcp-server and any lease is expired, then next time the dhcp client
        # renew the lease the action will be add, so remove the old record has same MAC address
        grep -v "${mac}" ${LOG_FILE} > ${TMP_FILE}
        cp ${TMP_FILE} ${LOG_FILE}
    
        if [ -s ${LOG_FILE} ]; then
            sed -i "1 i${record}" ${LOG_FILE}
        else
            echo ${record} >> ${LOG_FILE}
        fi
    
    #HH20191123: Prepare dns update command
        local ip="$3"
        local hostname="$4"
        echo "update add ${hostname}.${ZONE_NAME} 3600 A ${ip}" >> ${TMP_NSUPDATE}
    
    }
    
    get_hostname_from_logfile() {
        local mac="$1"
        local filename="";
        local line="`grep \"${mac}\" ${LOG_FILE}`"
        local tokens=( $line )
    
        if [ 5 -eq ${#tokens[@]} ]; then
            filename=${tokens[3]}
        fi
    
        echo $filename
    }
    
    get_new_record() {
        local mac="$2"
        local ip="$3"
        local hostname="$4"
        local fileHostname=$(get_hostname_from_logfile $mac)
    
        if [ "x" = "x${hostname}" ] && [ "xold" = "x${ACTION}" ];then
            if [ "x" != "x${DNSMASQ_SUPPLIED_HOSTNAME}" ]; then
                hostname=${DNSMASQ_SUPPLIED_HOSTNAME}
            elif [ "x" != "x${fileHostname}" ]; then
                hostname=${fileHostname}
            fi
        fi
    
        NEW_RECORD="${DNSMASQ_LEASE_EXPIRES} ${mac} ${ip} ${hostname} ${DNSMASQ_INTERFACE}"
    }
    
    # record format: action mac ip hostname
    NEW_RECORD=$@
    ACTION=`echo ${NEW_RECORD} | awk '{print $1}'`
    
    if [ "${DNSMASQ_INTERFACE}" = "" ]; then
        exit 0
    fi
    get_new_record ${NEW_RECORD}
    
    case "${ACTION}" in
        old)
            renew_record ${NEW_RECORD}
            ;;
        add)
            add_new_record ${NEW_RECORD}
            ;;
        del)
            del_leases ${NEW_RECORD}
            ;;
        *)
            ;;
    esac
    
    
    #HH20191123: complete command file for nsupdate with a send command
    echo "send" >> ${TMP_NSUPDATE}
    
    #HH20191123: actually execute the nsupdate command
    nsupdate ${TMP_NSUPDATE} >>${NSUPDATE_LOG_FILE}
    
    
    
    exit 0