#!/bin/bash
#
#Server-Manager: o_domain (Manages Hetzner-DNS entries) [Power: 755]
#
#Version: 1.0.0
#Date:    2023.01.11
#Unix:    Debian 11
#Author:  P.Klapp
#Company: Omoti UG (haftungsbeschränkt)
#Website: https://omoti.de
#
#Path:    /Commands/


#Version
    version=$(sed -n '5p' ${0})
    version=${version#*\ }

#Temporary file
    tmp="/srv/omoti/tmp/o_domain.$(date +'%H%M%S')$(pwgen -s -0 -N 1 2).tmp"

#Parameters
    #missing parameter
        if [[ -z ${1} ]]
        then
            echo "Missing parameter! Please use -h for help."
            exit 1
        fi

    while getopts ":a:c:d:hi:t:v:" option
    do
        case ${option} in
            a)  #add
                task='add'
                fqdn="${OPTARG}"
            ;;

            c)  #control 
                task='con'
                fqdn="${OPTARG}"
            ;;

            d)  #delete
                task='del'
                fqdn="${OPTARG}"
            ;;

            h)  #help
                echo ''
                echo 'Usage: o_domain OPTION...'
                echo "o_domain (v${version}) Manages Hetzner-DNS entries (only A & AAAA records)"
                echo ''
                echo 'Options:'
                echo '-a <fqdn>                 Adds a new DNS record'
                echo '-c <fqdn>                 Controls a DNS record'
                echo '-d <fqdn>                 Deletes a DNS record'
                echo '-h                        Display this help and exit'
                echo '-i <fqdn>                 Displays information about the dns record'
                echo '-t <token>                Hetzner API-Token'
                echo '-v <ip-address>           Value of record (IP-Address)'
                echo ''
                echo 'Further documentation can be found in the Omoti Wiki'
                echo 'https://gitlab.omoti.work/omoti/Server-Manager/-/wikis/home'
                echo ''
                exit 0
            ;;

            i)  #information
                task='inf'
                fqdn="${OPTARG}"
            ;;

            t)  #token 
                token="Auth-API-Token: ${OPTARG}"
            ;;

            v)  #value
                ipAddress="${OPTARG}"
            ;;

            \?) #invalid option
                echo "Invalid option! Please use -h for help."
                exit 1
            ;;

            :)  #missing argument
                echo "Missing argument! Please use -h for help."
                exit 1
            ;;
        esac
    done
    unset option

#Functions
    ErrorMessage () {
        echo -e "\033[31mAn error has occurred, please check the log!\033[0m"

        if [[ -f ${tmp} ]] && [[ $(cat ${tmp} | grep error) ]]
        then
            echo -e "\033[31mError:\033[33m $(jq .error.message ${tmp} | sed 's/\"//g')\033[0m"

        elif [[ -f ${tmp} ]] && [[ $(cat ${tmp} | grep message) ]]
        then
            echo -e "\033[31mError:\033[33m $(jq .message ${tmp} | sed 's/\"//g')\033[0m"
        fi
    }

    GetRecord () {
        if [[ $(VerifyIp ${ipAddress}) == 'IPv4' ]]
        then
            dig ${fqdn} A +short

        elif [[ $(VerifyIp ${ipAddress}) == 'IPv6' ]]
        then
            dig ${fqdn} AAAA +short

        else
            echo 'exit'
            o_log -w "Incorrect value (IP-Address: ${ipAddress})"
        fi
    }

    GetRecordID () {
        curl -s "${api}/records?zone_id=$(GetZoneID)" -H "${token}" > ${tmp}

        if ! [[ $(cat ${tmp} | grep message) ]]
        then
            cat ${tmp} | jq ".records[] | select(.name==\"${fqdn%%.*}\") | .id" | sed 's/"//g'

        else
            ErrorMessage
        fi
    }

    GetZoneID () {
        curl -s "${api}/zones" -H "${token}" > ${tmp}

        if ! [[ $(cat ${tmp} | grep message) ]]
        then
            cat ${tmp} | jq ".zones[] | select(.name==\"${fqdn#*.}\") | .id" | sed 's/"//g'

        else
            ErrorMessage
        fi
    }

    VerifyIp () {
        if [[ $(ipcalc-ng -c4 ${1} &> /dev/null; echo ${?}) == '0' ]]
        then
            echo 'IPv4'

        elif [[ $(ipcalc-ng -c6 ${1} &> /dev/null; echo ${?}) == '0' ]]
        then
            echo 'IPv6'
        fi
    }

#Dependencies
    #required software
        #dig
            if ! [[ $(which dig) ]]
            then
                o_log -e 'Client DNS program is not installed (dnsutils)'
                ErrorMessage
                exit 1
            fi

        #ipcalc-ng
            if ! [[ $(which ipcalc-ng) ]]
            then
                o_log -e 'Calculator for IP-Addresses is not installed (ipcalc-ng)'
                ErrorMessage
                exit 1
            fi

        #jq
            if ! [[ $(which jq) ]]
            then
                o_log -e 'JSON-Prozessor is not installed (jq)'
                ErrorMessage
                exit 1
            fi

#Variables
    api='https://dns.hetzner.com/api/v1'
    #control        -> Main
    #fqdn           -> Parameters
    hour='0'
    i='0'
    #ipAddress      -> Parameters
    minute='0'
    #option         -> Parameters
    second='0'
    #task           -> Parameters
    #tmp            -> Temporary file
    #token          -> Parameters
    #value          -> Main
    #version        -> Version

#Main
    #add
        if [[ ${task} == 'add' ]]
        then
            #required parameters
                #ipAddress
                    if [[ -z ${ipAddress} ]]
                    then
                        echo 'Parameter -v is required'
                        exit 1
                    fi

                #token
                    if [[ -z ${token} ]]
                    then
                        echo 'Parameter -t is required'
                        exit 1
                    fi

            #value
                if [[ $(VerifyIp ${ipAddress}) == 'IPv4' ]]
                then
                    value="{\"value\": \"${ipAddress}\",\"ttl\": 86400,\"type\": \"A\",\"name\": \"${fqdn%%.*}\",\"zone_id\": \"$(GetZoneID)\"}"

                elif [[ $(VerifyIp ${ipAddress}) == 'IPv6' ]]
                then
                    value="{\"value\": \"${ipAddress}\",\"ttl\": 86400,\"type\": \"AAAA\",\"name\": \"${fqdn%%.*}\",\"zone_id\": \"$(GetZoneID)\"}"

                else
                    o_log -w "Incorrect value (IP-Address: ${ipAddress})"
                    ErrorMessage
                    exit 1
                fi

            curl -s -X "POST" "${api}/records" -H 'Content-Type: application/json' -H "${token}" -d $"${value}" > ${tmp}

            if ! [[ $(cat ${tmp} | grep message) ]]
            then
                echo -e "\033[32mDNS record was created:\033[36m ${fqdn} -> ${ipAddress}\033[0m"
                echo -e "\033[32mRecord ID:\033[36m $(jq .record.id ${tmp} | sed 's/\"//g')\033[0m"
                o_log -i "DNS record was created: ${fqdn} -> ${ipAddress}"

            else
                o_log -w "DNS record could not be created (${fqdn}|${ipAddress})"
                ErrorMessage
                exit 1
            fi
        fi

    #control
        if [[ ${task} == 'con' ]]
        then
            #required parameters
                #ipAddress
                    if [[ -z ${ipAddress} ]]
                    then
                        echo 'Parameter -v is required'
                        exit 1
                    fi

            #loop
                control=$(GetRecord)
                while [[ ${control} != 'exit' ]] && [[ ${control} != ${ipAddress} ]]
                do
                    echo -ne "Domain ${fqdn} cannot be resolved to ${ipAddress}! Checking ... \033[36m$(printf '%02d' "${hour}"):$(printf '%02d' "${minute}"):$(printf '%02d' "${second}") \033[35m(Current:${control})\033[0m\r"
                    second=$((second + 10))

                    if [[ ${second} == '60' ]]
                    then
                        ((minute++))
                        second='0'
                    fi

                    if [[ ${minute} == '60' ]]
                    then
                        ((hour++))
                        minute='0'
                    fi

                    sleep 10s
                    control=$(GetRecord)
                done

            if [[ ${control} != 'exit' ]] && [[ $(GetRecord) == ${ipAddress} ]]
            then
                echo -e "\033[32mDomain ${fqdn} resolved to ${ipAddress}\033[0m"

            else
                ErrorMessage
                exit 1
            fi
        fi

    #delete
        if [[ ${task} == 'del' ]]
        then
            #required parameters
                #token
                    if [[ -z ${token} ]]
                    then
                        echo 'Parameter -t is required'
                        exit 1
                    fi

            #get record data
                IFS=$'\n' control=($(GetRecordID))

                if [[ -z ${control} ]] || [[ ${#control[@]} == '0' ]]
                then
                    echo -e "\033[33mNo matching record found\033[0m"
                    exit 1

                elif [[ ${#control[@]} == '1' ]]
                then
                    curl -s "${api}/records/${control}" -H "${token}" > ${tmp}
                    ipAddress=$(jq .record.value ${tmp} | sed 's/\"//g')

                else
                    while [[ ${i} != ${#control[@]} ]]
                    do
                        curl -s "${api}/records/${control[${i}]}" -H "${token}" > ${tmp}
                        ipAddress[${i}]=$(jq .record.value ${tmp} | sed 's/\"//g')
                        ((i++))
                    done

                    i='0'
                fi

            if [[ ${#control[@]} == '1' ]]
            then
                curl -s -X "DELETE" "${api}/records/${control}" -H "${token}" > ${tmp}

                if ! [[ $(cat ${tmp} | grep message) ]]
                then
                    echo -e "\033[32mDNS record was deleted:\033[36m ${fqdn} -> ${ipAddress}\033[0m"
                    o_log -i "DNS record was deleted: ${fqdn} -> ${ipAddress}"

                else
                    o_log -w "DNS record could not be deleted (${fqdn}|${ipAddress})"
                    ErrorMessage
                    exit 1
                fi

            else
                while [[ ${i} != ${#control[@]} ]]
                do
                    curl -s -X "DELETE" "${api}/records/${control[${i}]}" -H "${token}" > ${tmp}

                    if ! [[ $(cat ${tmp} | grep message) ]]
                    then
                        echo -e "\033[32mDNS record was deleted:\033[36m ${fqdn} -> ${ipAddress[${i}]}\033[0m"
                        o_log -i "DNS record was deleted: ${fqdn} -> ${ipAddress[${i}]}"

                    else
                        o_log -w "DNS record could not be deleted (${fqdn}|${ipAddress[${i}]})"
                        ErrorMessage
                        exit 1
                    fi

                    ((i++))
                done

                i='0'
            fi
        fi

    #information
        if [[ ${task} == 'inf' ]]
        then
            #required parameters
                #token
                    if [[ -z ${token} ]]
                    then
                        echo 'Parameter -t is required'
                        exit 1
                    fi

            IFS=$'\n' control=($(GetRecordID))

            if [[ -z ${control} ]] || [[ ${#control[@]} == '0' ]]
            then
                echo -e "\033[33mNo matching record found\033[0m"
                exit 1

            elif [[ ${#control[@]} == '1' ]]
            then
                curl -s "${api}/records/${control}" -H "${token}" | jq

            else
                echo 'Multiple entries found'
                while [[ ${i} != ${#control[@]} ]]
                do
                    echo ''
                    echo "Entry $((i + 1)):"
                    curl -s "${api}/records/${control[${i}]}" -H "${token}" | jq
                    ((i++))
                done
            fi
        fi