#!/bin/sh
#
# A basic IPv4 dhcp server and client test
# It also checks if dhclient handles correctly resolv.conf in a network
# namespace

set -e
set -x

trap 'cleanup' EXIT

cleanup() {
    netstat -putan
    ip netns exec serverdhcp netstat -putan
    echo "================================"
    dmesg || true
    journalctl || true
    cat /var/log/syslog* || true
    sleep 10
    echo "================================"
    journalctl -u isc-dhcp-server
    sleep 10
    echo "================================"
    ip netns exec serverdhcp rndc -s 10.42.42.1 status || true
    ip netns exec serverdhcp ping -c 5 10.42.42.1 || true
    echo "================================"
    cat "/var/log/named/named-ddns.log" || true
    cat /etc/bind/rndc.conf || true
    cat /etc/bind/rndc.key || true
    echo "================================"
    kill -9 $(cat /run/dhcpd.pid) || true
    kill -9 $(cat /run/named.pid) || true
}


# disable appamor for dhclient
aa-complain /etc/apparmor.d/* || true
systemctl stop apparmor || true
systemctl stop bind9 || true

nsserver=serverdhcp
nsclient=clientdhcp
ip_addr_server="10.42.42.1/24"
ifaceserver=veth-server
ifaceclient=veth-client

ip netns add $nsserver
ip netns add $nsclient

ip link add \
       ptp-$ifaceserver \
       type veth \
       peer name ptp-$ifaceclient

ip link set ptp-$ifaceserver netns $nsserver
ip link set ptp-$ifaceclient netns $nsclient

rndc-confgen -a -b 512 -k dhcpupdate -c /etc/bind/rndc.key
chown root:bind /etc/bind/rndc.key
chmod 0640 /etc/bind/rndc.key
ls -l /etc/bind/rndc.key

mkdir -p /var/log/named
chown bind:bind /var/log/named/
chmod 770 /var/log/named/
touch /var/log/named/named-ddns.log
chown bind:bind /var/log/named/named-ddns.log
chmod 660 /var/log/named/named-ddns.log

cat << EOF > /etc/bind/rndc.conf
include "/etc/bind/rndc.key";

options {
    default-key "dhcpupdate";
    default-server 10.42.42.1;
    default-port 953;
};
EOF
chown bind:bind /etc/bind/rndc.conf
chmod 0640 /etc/bind/rndc.conf

cat /etc/dhcp/dhcpd.conf
cat << EOF > /etc/dhcp/dhcpd.conf
option domain-name "example.org";
option domain-name-servers 10.42.42.1;
ddns-update-style interim;
ddns-domainname "example.org";
ddns-rev-domainname "in-addr.arpa.";
include "/etc/bind/rndc.key";

default-lease-time 600;
max-lease-time 7200;

log-facility local7;

subnet 10.42.42.0 netmask 255.255.255.0 {
    range 10.42.42.10 10.42.42.19;
    option subnet-mask 255.255.255.0;
    option domain-name "example.org";
    option domain-name-servers 10.42.42.1;

    # Allow clients to send their hostname
    allow client-updates;
}

zone example.org. {
    primary 10.42.42.1;
    key dhcpupdate;
}

zone 42.42.10.in-addr.arpa. {
    primary 10.42.42.1;
    key dhcpupdate;
}
EOF

ip netns exec $nsserver ip addr \
    add $ip_addr_server dev ptp-$ifaceserver
ip netns exec $nsserver ip link set \
    dev ptp-$ifaceserver up
ip netns exec $nsclient ip link set \
    dev ptp-$ifaceclient up
ip netns exec $nsserver ip link set dev lo up
ip netns exec $nsclient ip link set dev lo up

if ! /usr/sbin/dhcpd -t -q -cf /etc/dhcp/dhcpd.conf > /dev/null 2>&1; then
    echo "dhcpd self-test failed. Please fix it."
    echo "The error was: "
    /usr/sbin/dhcpd -t -cf /etc/dhcp/dhcpd.conf
    exit 1
fi

cat <<EOF >/etc/bind/named.conf.options
options {
	directory "/var/cache/bind";
	listen-on port 53 { any; };
	allow-query { any; };
	recursion yes;
	minimal-responses no;
};
EOF

tee /etc/bind/named.conf.local <<'EOF'
include "/etc/bind/rndc.key";

controls {
	inet 10.42.42.1 port 953 allow { 10.42.42.0/24; } keys { "dhcpupdate"; };
};

logging {
    channel update_debug {
        file "/var/log/named/named-ddns.log" versions 3 size 5m;
        severity debug 3;
        print-time yes;
    };

    category update { update_debug; };
    category update-security { update_debug; };
};

zone "example.org" {
    type master;
    file "/etc/bind/db.example.org";
    allow-update { key "dhcpupdate"; };
};

zone "42.42.10.in-addr.arpa" {
    type master;
    file "/etc/bind/db.42.42.10.in-addr.arpa";
    allow-update { key "dhcpupdate"; };
};
EOF

# ugly for dynamic update
chown bind:bind /etc/bind

tee "/etc/bind/db.example.org"<<'EOF'
$TTL 86400
@       IN  SOA ns1.example.org. admin.example.org. (
            2025010101 ; Serial
            3600       ; Refresh
            1800       ; Retry
            604800     ; Expire
            86400 )    ; Minimum

        IN  NS      ns1.example.org.

; Static host: DNS server itself
ns1     IN  A       10.42.42.1
EOF

tee "/etc/bind/db.42.42.10.in-addr.arpa"<<'EOF'
$TTL 86400
@       IN  SOA ns1.example.org. admin.example.org. (
            2025010101 ; Serial
            3600       ; Refresh
            1800       ; Retry
            604800     ; Expire
            86400 )    ; Minimum

        IN  NS      ns1.example.org.

; Static PTRs (optional)
1       IN  PTR     ns1.example.org.
EOF
chown bind:bind /etc/bind/db.example.org
chown bind:bind /etc/bind/db.42.42.10.in-addr.arpa

hostnamectl set-hostname mytesthost

ip netns exec $nsserver /sbin/start-stop-daemon --start -v --exec /usr/sbin/named \
		--pidfile /run/named.pid -- -c /etc/bind/named.conf -d 4 -u bind
ip netns exec $nsserver /usr/sbin/dhcpd -cf /etc/dhcp/dhcpd.conf -pf /run/dhcpd.pid


ip netns exec $nsclient dhclient -v ptp-veth-client
ip netns exec $nsclient ip link
ip netns exec $nsclient ip route
ip netns exec $nsclient ip addr
# for debugging display
ip netns exec $nsclient dig @10.42.42.1 mytesthost.example.org
ip netns exec $nsclient dig @10.42.42.1 mytesthost.example.org +noall +answer
# now fail if not found
ip netns exec $nsclient dig @10.42.42.1 mytesthost.example.org +noall +answer | grep 'mytesthost.example.org'

trap - EXIT

kill -9 $(cat /run/dhcpd.pid) || true
kill -9 $(cat /run/named.pid) || true
