#!/usr/bin/env sh
# Winbind DNS update tool
# Dynamic dns update for winbind backend
#
# Copyright (C) 2024 Evgenii Sozonov <arzdez@altlinux.org>
# Copyright (C) 2024 Andrey Limachko <liannnix@altlinux.org>
# Copyright (C) 2024 Olga Kamaeva <kamaevaoi@altlinux.org>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program.  If not, see <http://www.gnu.org/licenses/>.

### diable pipefail error
# shellcheck disable=SC3040
### disable local vars error
# shellcheck disable=SC3043
### disable "<<<" here-strings undefined
# shellcheck disable=SC3011
### disable trap ERR undefined
# shellcheck disable=SC3047
### disable -n flag for echo indefined error
# shellcheck disable=SC3037
### disable /bin/shell-getopt error
# shellcheck disable=SC1091

set -euo pipefail

# shellcheck source=/bin/shell-getopt
. shell-getopt

# Print info message
show_usage() {
cat << EOF
$PROG_NAME utility for update DNS records.
IPv4 is updated by default.

Usage: $PROG_NAME [options]

Options:
  -h, --help                    This message
  -v, --version                 Display version number
  -a, --all                     Enable update all DNS records type (IPv4,IPv6,PTR)
  -6, --update-ipv6             Enable update IPv6 DNS records
  -d, --daemon                  For send log to journalctl when programm run as systemd unit
  -t, --ttl <time>              Set TTL
  --allow-ipv4-ptr-update       Enable update IPv4 (A) PTR DNS records
  --allow-ipv6-ptr-update       Enable update IPv6 (AAAA) PTR DNS records

EOF
exit 0;
}

print_version() {
    echo "$VERSION"
    exit 0;
}

# Get params
PROG_NAME="winbind-dnsupdate"
VERSION=0.4
OPTIONS_LIST="help,
              version,
              daemon,
              update-ipv6,
              allow-ipv4-ptr-update,
              allow-ipv6-ptr-update,
              disable-dconf,
              all,
              only,
              ttl:"

OPTIONS_SHORT_LIST="h,
                    v
                    d,
                    6,
                    a,
                    t:"

TEMP=$(getopt -n "$PROG_NAME" -o "$OPTIONS_SHORT_LIST" -l "$OPTIONS_LIST" -- "$@")
eval set -- "$TEMP"

dconf_enable=1
## DNS update options
enable_update_ipv6=0 # Enable update ipv6
enable_update_ipv4=1 # Enable update ipv4
ipv4_ptr_update=0 # Enable update ipv4 ptr records
ipv6_ptr_update=0 # Enable update ipv6 ptr records
ttl=3600
## if enable log will send to journald
run_as_unit=0
# Temp krb cache vars
TMP_KRB_CCACHE_NAME=

while :; do
    case "$1" in
        -h|--help)
            show_usage
            ;;
        -v|--version)
            print_version
            ;;
        -d|--daemon)
            run_as_unit=1
            ;;
        -6|--update-ipv6)
            enable_update_ipv6=1
            ;;
        -a|--all)
            enable_update_ipv6=1
            ipv4_ptr_update=1
            ipv6_ptr_update=1
            ;;
        -t|--ttl) shift 
            ttl="$1"
            ;;
        --allow-ipv4-ptr-update)
            ipv4_ptr_update=1 
            ;;
        --allow-ipv6-ptr-update)
            ipv6_ptr_update=1 
            ;;
        --disable-dconf)
            dconf_enable=0
            ;;
        --) shift; break
            ;;
        *) fatal "Unrecognized option: $1"
            ;;
    esac
    shift
done

if [ $dconf_enable -eq 1 ]; then
    enable_update_ipv4="$(dconf read /Software/BaseALT/Policies/SambaWinbind/enable_update_ipv4)"
    enable_update_ipv6="$(dconf read /Software/BaseALT/Policies/SambaWinbind/enable_update_ipv6)"
    ipv4_ptr_update="$(dconf read /Software/BaseALT/Policies/SambaWinbind/ipv4_ptr_update)"
    ipv6_ptr_update="$(dconf read /Software/BaseALT/Policies/SambaWinbind/ipv6_ptr_update)"
    ttl="$(dconf read /Software/BaseALT/Policies/SambaWinbind/ttl)"
fi

log() {
    local log_message=
    local log_level=
    log_level="$1"
    log_message="$2"
    if [ "$log_level" = "ERROR" ]; then
        if [ $run_as_unit -eq 0 ]; then
            echo "[$log_level]: $log_message" 1>&2
        else  
            echo "[$log_level]: $log_message" | systemd-cat -t winbind-dnsupdate -p err
        fi
    elif [ "$log_level" = "WARNING" ]; then
        if [ $run_as_unit -eq 0 ]; then
            echo "[$log_level]: $log_message" 1>&2 
        else 
            echo "[$log_level]: $log_message" | systemd-cat -t winbind-dnsupdate -p warning
        fi
    elif [ "$log_level" = "INFO" ]; then
        if [ $run_as_unit -eq 0 ]; then
            echo "[$log_level]: $log_message" 1>&2 
        else
            echo "[$log_level]: $log_message" | systemd-cat -t winbind-dnsupdate -p info
        fi
    fi
}

log_warning() {
    local log_message=
    log_message="$1"
    log "WARNING" "$log_message"
}

log_error() {
    local log_message=
    log_message="$1"
    log "ERROR" "$log_message"
}

log_info() {
    local log_message=
    log_message="$1"
    log "INFO" "$log_message"
}

# To uppercase
upper() {
    # shellcheck disable=SC3037
    echo -n "$1" | tr '[:lower:]' '[:upper:]'
}

check_winbind_status() {
    log_info "Check winbind status."
    if ! systemctl status winbind.service > /dev/null 2>&1; then 
        log_error "Winbind service is not running." && return 1
    fi
    log_info "Winbind is running. Continue."
    return 0
}

# Check net is installed
check_net_is_install() {
    if ! net --help  >/dev/null 2>&1; then
        log_error "net is not installed." && return 1
    fi
    return 0
}

# Set tmp cache name for krb
set_cache_name() {
    TMP_KRB_CCACHE_NAME="$(mktemp)"
    export KRB5CCNAME="$TMP_KRB_CCACHE_NAME"
}

# Kinit with host cred
host_kinit() {
    local upper_host_name=
    local domain_realm="$2"
    upper_host_name="$( upper "$1" )"

    result="$(kinit -k "$upper_host_name"\$@"$domain_realm" 2>&1)" || \
        (log_error "kinit error. You should check the availability of the KDC and whether the host is a member of the domain" && \
        log_error "$result" && return 1)
}

check_dns_servers_reacheble() {
    local host_name="$1"
    local dc_name="$2"
    local ret=0
    log_info "Checking the availability of DNS server on $dc_name"
    out="$(dig -t a "$host_name" @"$dc_name" +short 2>&1)" || ret=1
    if [ $ret -eq 0 ] || grep -q "NXDOMAIN" <<< "$out"; then
        log_info "DNS server on $dc_name available"
        ret=0
    elif grep -q "no servers could be reached" <<< "$out"; then
        log_error "DNS server on $dc_name not responding"
        ret=1
    else
        log_error "DNS error"
        log_error "DNS server on $dc_name not responding"
        ret=1
    fi

    return $ret
}

check_exist_ptr_record() {
    local ip_addr="$1"
    local dns_server="$2"
    local ptr_record_exist=0
    local ret=0
    log_info "Checking the existence of a PTR record"
    out="$(host -t ptr "$ip_addr" "$dns_server" | grep -E -o "NXDOMAIN")"
    if [ "$out" = "NXDOMAIN" ]; then
        ptr_record_exist=1
        log_info "PTR record not exists."
    else
        log_info "PTR recotd exist."
    fi
    return $ptr_record_exist
}

check_exist_forward_lookup_record() {
    local host_name="$1" 
    local address_family="$2"
    local dns_server="$3"
    local a_record_exist=0
    local ret=0
    local record_type=

    if [ "$address_family" = "IPv4" ]; then
        record_type="A" 
    elif [ "$address_family" = "IPv6" ]; then
        record_type="AAAA"
    else
        log_warning "Unknown address family"
    fi

    log_info "Checking the existence of $record_type record"
    out="$(host -t "$record_type" "$host_name" "$dns_server" | grep -E -o "NXDOMAIN|has no A record|has no AAAA record")" || ret=1
    if [ "$out" = "NXDOMAIN" ] && [ $ret -eq 1 ]; then
        a_record_exist=1
        log_info "$address_family record not exists."
    elif [ "$out" = "has no $record_type record" ]; then
        a_record_exist=1
        log_info "$record_type record not exists."
    elif [ "$out" = "has no AAAA record" ]; then
        log_info "$record_type record not exists."
        a_record_exist=1
    else
        log_info "$address_family record exists."
    fi
    return $a_record_exist
}

get_domain_realm() {
    local domain_realm=
    domain_realm="$(upper "$(dnsdomainname)")"

    echo "$domain_realm"
}

get_fqdn_hostname() {
    local fqdn_name=
    fqdn_name="$(hostname -f)"

    echo "$fqdn_name"
}

get_short_hostname() {
    local upper_hostname=
    upper_hostname="$(upper "$(hostname -s)")"

    echo "$upper_hostname"
}

get_site_name() {
    local domain_realm="$1"
    local ret=0
    local site_name=

    site_name="$(wbinfo --dsgetdcname="$domain_realm" | tail -n1)" || ret=1
    if [ $ret -eq 1 ] || grep -q "Could not find dc for" <<< "$site_name"; then
        log_error "Can't get site name"
    else
        echo "$site_name"
    fi
    return $ret
}

get_dc_list() {
    local domain_realm="$1"
    local site_name="$2"
    local ret=0
    local dc_list=
    local out=
    out="$(host -t SRV _ldap._tcp."$site_name"._sites.dc._msdcs."$domain_realm")" || ret=1

    if [ $ret -eq 0 ] || grep -q "NXDOMAIN" <<< "$out"; then
        log_info "Success"
        dc_list="$(grep "has SRV record" <<< "$out" | awk '{print $NF}')"
        echo "$dc_list"
        return 0
    elif grep -q "no servers could be reached" <<< "$out"; then
        log_error "Unable to connect to any DNS server"
        log_error "Cant find any dc in site $site_name"
        return 1
    else
        log_error "Unable to connect to any DNS server"
        log_error "Cant find any dc in site $site_name"
        return 1
    fi
}

get_reacheble_dns_server() {
    local domain_realm="$1"
    local site_name="$2"
    local fqdn_name="$3"
    local ret=0
    local dns_reacheble=
    local dc_hostname=

    log_info "Trying to get a list of domain controllers"
    dc_list="$(get_dc_list "$domain_realm" "$site_name")"  || ret=1
    log_info "Trying to find an available DNS server"
    if [ $ret -eq 0 ]; then
        for dc_name in $dc_list; do
            dns_reacheble=0
            check_dns_servers_reacheble "$fqdn_name" "$dc_name" || dns_reacheble=1
            if [ $dns_reacheble -eq 0 ]; then
                dc_hostname="$(sed 's/.$//' <<< "$dc_name")"
                echo "$dc_hostname"
                break
            fi
        done

        if [ $dns_reacheble -eq 1 ]; then
            log_error "Unable to find an available DNS server for the $site_name"
            ret=1
        fi
    fi
    return $ret
}

validate_hostname() {
    local hostname="$1"
    local validate_regex=
    local ret=0
    validate_regex="^(([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9\-]*[a-zA-Z0-9])\.)*([A-Za-z0-9]|[A-Za-z0-9][A-Za-z0-9\-]*[A-Za-z0-9])$"
    grep -E "$validate_regex" >/dev/null 2>&1 <<< "$hostname" || ret=1
    return $ret
}

validate_ipv4() {
    local ipv4="$1"
    local validate_regex=
    local ret=0
    validate_regex="^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])$"
    grep -E "$validate_regex" >/dev/null 2>&1 <<< "$ipv4" || ret=1
    return $ret
}

validate_ipv6(){
    local ipv6="$1"
    local validate_regex=
    local ret=0
    validate_regex='^([0-9a-fA-F]{0,4}:){1,7}[0-9a-fA-F]{0,4}$'
    grep -q -E "$validate_regex" <<< "$ipv6" || ret=1
    return $ret
}

get_ip_from_hostname() {
    local hostname="$1"
    local address_family="$2"
    local dns_server="$3"
    local ret=1
    local record_type=
    local out=
    local ip=
    if [ "$address_family" = "IPv4" ]; then
        record_type="A" 
    elif [ "$address_family" = "IPv6" ]; then
        record_type="AAAA"
    else
        log_warning "Unknown address family"
    fi

    if validate_hostname "$hostname"; then
        if out="$(dig -t "$record_type" "$hostname" @"$dns_server" +short)"; then
            if ip="$(awk -F' ' '{print $NF}' 2>&1 <<< "$out")"; then
                if [ "$record_type" = "A" ]; then
                    if validate_ipv4 "$ip"; then
                        echo -n "$ip" && ret=0
                    else
                        log_warning "Invalid IPv4 address: $ip"
                    fi
                elif [ "$record_type" = "AAAA" ]; then
                    if validate_ipv6 "$ip"; then
                        echo -n "$ip" && ret=0
                    else
                        log_warning "Invalid IPv6 address: $ip"
                        log_warning "$out"
                    fi
                fi
            else
                log_warning "Can not parse IP address"
                log_warning "AWK errror: $ip"
            fi
        else
            log_warning "Can not resolve hostname: $hostname"
            log_warning "DNS error: $out"
        fi
    else
        log_warning "Invalid hostname: $hostname"
    fi
    return $ret
}

get_dc_hostname() {
    local dc=
    local out=
    local ret=1
    if out="$(wbinfo -P)"; then
        if dc="$(awk -F\" 'NR==1 {print $2}' 2>&1 <<< "$out")"; then
            if validate_hostname "$dc"; then
                echo -n "$dc" && ret=0
            else
                log_warning "Domain controller hostname invalid: $dc"
            fi
        else
            log_warning "Can not parse domain controller hostname"
            log_warning "AWK errror: $dc"
        fi
    else
        log_warning "Wbinfo error:"
        log_warning "$out"
    fi
    return $ret
}

get_connection_iface_to_ldap() {
    local address_family="$1"
    local dc_hostname="$2"
    local dc_ip=
    local connect_iface=
    local out=
    local ret=1

    # Get actual dc connection ip
    log_info "Trying to get $address_family address of a domain controller"
        if ! dc_ip="$(get_ip_from_hostname "$dc_hostname" "$address_family" "$dc_hostname" )"; then
            log_warning "Can not resolve domain controller address for $dc_hostname"
            return $ret
        else
            log_info "Successful. DC info:"
        fi

    # Find iface
    log_info "Domain controller name: $dc_hostname"
    log_info "Domain controller $address_family: $dc_ip"
    log_info "Trying parse connection interface name"
    
    if out="$(ip route get dport 389 "$dc_ip")"; then
        if connect_iface="$(awk -F'dev ' 'NR==1 {print $2}' <<< "$out" | awk '{print $1}' 2>&1)"; then
            log_info "Successful. Intraface name: $connect_iface"
            echo -n "$connect_iface" && ret=0
        else
            log_warning "Can not parse connection interface"
            log_warning "AWK errror: $connect_iface"
        fi
    else
        log_warning "Ip route error: $out"
    fi
    return $ret
}

get_old_addr() {
    local hostname="$1"
    local address_family="$2"
    local dc_hostname="$3"
    local ret=1
    local old_ip_addr=
        if old_ip_addr="$(get_ip_from_hostname "$hostname" "$address_family" "$dc_hostname")"; then
            echo -n "$old_ip_addr" && ret=0
        else
            log_error "Can't get old IP addres."
        fi
    return $ret
}

get_iface_ipv4_addr() {
    local iface="$1"
    local ipv4_addr=
    local iface_info=
    local ret=1
    if iface_info="$(ip -f inet addr show "$iface")"; then
        if ipv4_addr="$(sed -En -e 's/.*inet ([0-9.]+).*/\1/p' <<< "$iface_info")"; then
            if validate_ipv4 "$ipv4_addr"; then
                echo -n "$ipv4_addr" && ret=0
            else
                log_warning "Invalid IPv4 address: $ip"
            fi
        else
            log_warning "Can't get current IPv4 addres."
            log_warning "Parse IPv4 address error: $ipv4_addr "
        fi
    else
        log_warning "Error getting interface information: $iface_info"
    fi

    return $ret
}

get_iface_ipv6_addr(){
    local iface="$1"
    local ipv4_addr=
    local iface_info=
    local ret=1
    if iface_info="$(ip -f inet6 addr show "$iface")"; then
        if ipv6_addr="$(sed -e's/^.*inet6 \([^ ]*\)\/.*$/\1/;t;d' <<< "$iface_info")"; then
            if validate_ipv6 "$ipv6_addr"; then
                echo -n "$ipv6_addr" && ret=0
            else
                log_warning "Invalid IPv6 address: $ip"
            fi
        else
            log_warning "Can't get current IPv6 addres."
            log_warning "Parse IPv6 address error: $ipv4_addr "
        fi
    else
        log_warning "Error getting interface information: $iface_info"
    fi

    return $ret
}

generate_ipv4_ptr_ip() {
    local ptr_ipv4_addr=
    IFS=. read -r ip1 ip2 ip3 ip4 <<< "$1"
    ptr_ipv4_addr="$ip4"."$ip3"."$ip2"."$ip1".in-addr.arpa.

    echo -n "$ptr_ipv4_addr"
}

generate_ipv6_ptr_ip() {
    local ipv6_addr="$1"
    local ptr_ipv6_addr=

    ptr_ipv6_addr="$(awk -F: 'BEGIN {OFS=""; } {
                addCount = 9 - NF;
                for(i=1; i<=NF;i++) {
                    if(length($i) == 0) {
                        for(j=1;j<=addCount;j++) {
                            $i = ($i "0000")
                        ;}
                    } else { 
                        $i = substr(("0000" $i), length($i)+5-4)
                    ;}
                };
            print}' <<< "$ipv6_addr" | rev | sed -e "s/./&./g")"

    echo "$ptr_ipv6_addr"ip6.arpa
}

# Commands for first A records registration
first_forward_lookup_record_registration() {
    local ip_addr="$1"
    local fqdn_name="$2"
    local ttl="$3"
    local record_type="$4"
    cat << EOF | nsupdate -g 2>&1 || return 1
        update add $fqdn_name $ttl $record_type $ip_addr
        send
        quit
EOF
}

# Commands for first PTR records registration
first_ptr_registration() {
    local new_ptr_record="$1"
    local fqdn_name="$2"
    local ttl="$3"
    cat << EOF | nsupdate -g 2>&1 || return 1
        update add $new_ptr_record $ttl PTR $fqdn_name
        send
        quit
EOF
}

# Commands for update A records
nsupdate_forward_lookup_record_command() {
    local ip_addr="$1"
    local fqdn_name="$2"
    local ttl="$3"
    local record_type="$4"
    cat << EOF | nsupdate -g 2>&1|| return 1
        update delete $fqdn_name in $record_type
        update add $fqdn_name $ttl $record_type $ip_addr
        send
        quit
EOF
}

# Commands for update PTR records
nsupdate_ptr_record_command() {
    local new_ptr_record="$1"
    local fqdn_name="$2"
    local ttl="$3"
    local old_ptr_record="$4"
    cat << EOF | nsupdate -g 2>&1 || return 1
        update delete $old_ptr_record PTR
        update add $new_ptr_record $ttl PTR $fqdn_name
        send
        quit
EOF
}

# Function for update PTR records
call_update_ptr_record() {
    local ip_addr="$1"
    local fqdn_name="$2"
    local ttl="$3"
    local address_family="$4"
    local res=0
    local new_ptr_addr=
    local old_ptr_addr=

    if [ "$address_family" = "IPv4" ]; then
        new_ptr_addr="$(generate_ipv4_ptr_ip "$ip_addr")" 
    elif [ "$address_family" = "IPv6" ]; then
        new_ptr_addr="$(generate_ipv6_ptr_ip "$ip_addr")"
    else
        log_warning "Unknown address family"
    fi

    if [ $# -gt 4 ]; then
        local old_ptr_addr="$5"
    fi

    if [ -z "$old_ptr_addr" ]; then
        out="$(first_ptr_registration "$new_ptr_addr" "$fqdn_name" "$ttl")" || res=1
    else
        out="$(nsupdate_ptr_record_command "$new_ptr_addr" "$fqdn_name" "$ttl" "$old_ptr_addr")" || res=1
    fi

    if [ $res -eq 0 ]; then
        log_info "$address_family PTR record update successful."
    else
        log_error "Nsupdate error."
        log_error "$out"
        log_error "$address_family PTR record update failed."
        return 1
    fi

    return 0
}

# Function for update A records
call_update_forward_lookup_record() {
    local ipv4_addr="$1"
    local fqdn_name="$2"
    local ttl="$3"
    local address_family="$4"
    local first_registration="$5"
    local res=0
    local record_type=

    if [ "$address_family" = "IPv4" ]; then
        record_type="A" 
    elif [ "$address_family" = "IPv6" ]; then
        record_type="AAAA"
    else
        log_warning "Unknown address family"
    fi
    if [ "$first_registration" -eq 1 ]; then
        out="$(first_forward_lookup_record_registration "$ipv4_addr" "$fqdn_name" "$ttl" "$record_type")" || res=1
    else
        out="$(nsupdate_forward_lookup_record_command "$ipv4_addr" "$fqdn_name" "$ttl" "$record_type")" || res=1
    fi
    if [ $res -eq 0 ]; then
        log_info "A record update successful."
    else
        log_error "Nsupdate error:"
        log_error "$out"
        log_error "A record update failed."
        return 1
    fi

    return 0
}

update_ipv4(){
    local fqdn_name="$1"
    local ttl="$2"
    local ptr_update="$3"
    local dc_hostname="$4"
    local exist_a_record=0
    local exist_ptr_record=0
    local first_registration_flag=0
    local address_family="IPv4"
    local ipv4_addr=
    local old_ipv4_addr=
    local old_ptr_ipv4_addr=
    local connect_iface=

    # Get the interface through which the connection is established with ldаp
    connect_iface="$(get_connection_iface_to_ldap "$address_family" "$dc_hostname")" 
    ipv4_addr="$(get_iface_ipv4_addr "$connect_iface")"

    check_exist_forward_lookup_record "$fqdn_name" "$address_family" "$dc_hostname" || exist_a_record=1

    if [ "$ptr_update" -eq 1 ]; then
        check_exist_ptr_record "$ipv4_addr" "$dc_hostname" || exist_ptr_record=1
        # Message to recommend deleting the old PTR record if there is no old A record
        if [ $exist_ptr_record -eq 0 ] && [ $exist_a_record -eq 1 ]; then
            log_warning "The IPv4 PTR record exists, but the A record is missing and host IP was changed. To avoid duplicate entries,\
    you should delete the old entry manually."
        fi
    fi
    # If A record does not exist the start first registration, else call update command
    if [ $exist_a_record -eq 1 ]; then
        log_info "The A record does not exist. Start A record registration."
        # Set update flag as true and start first registration
        first_registration_flag=1
        call_update_forward_lookup_record "$ipv4_addr" "$fqdn_name" "$ttl" "$address_family" "$first_registration_flag"
        # Check if ptr update is enabled
        if [ "$ptr_update" -eq 1 ]; then
            log_info "Start IPv4 PTR record registration."
            call_update_ptr_record  "$ipv4_addr" "$fqdn_name" "$ttl" "$address_family"
        fi
    else
        # Obtaining an old IP address to check the need for an update
        old_ipv4_addr="$(get_old_addr "$fqdn_name" "$address_family" "$dc_hostname")"
        log_info "Checking whether the IPv4 records needs to be updated"
        log_info "Current IPv4 address: $ipv4_addr"
        log_info "IPv4 address in DNS server: $old_ipv4_addr"
        if [ "$ipv4_addr" = "$old_ipv4_addr" ]; then
            log_info "The IPv4 address of interface $connect_iface has not been changed."
            log_info "The update IPv4 was skipped"
            if [ "$ptr_update" -eq 1 ] && [ "$exist_ptr_record" -eq 1 ]; then
                log_info "The PTR record does not exist but IPv4 not changed and PTR record update enable"
                log_info "Start IPv4 PTR record registration."
                call_update_ptr_record  "$ipv4_addr" "$fqdn_name" "$ttl" "$address_family"
            fi
        else
            # Update addr if hi was changed
            log_info "The IPv4 address of interface $connect_iface has been changed."
            log_info "Start the A record update."
            call_update_forward_lookup_record "$ipv4_addr" "$fqdn_name" "$ttl" "$address_family" "$first_registration_flag"
            if [ "$ptr_update" -eq 1 ]; then
                log_info "Start IPv4 PTR record update."
                local old_ptr_ipv4_addr=
                old_ptr_ipv4_addr="$(generate_ipv4_ptr_ip "$old_ipv4_addr")"
                call_update_ptr_record "$ipv4_addr" "$fqdn_name" "$ttl" "$address_family" "$old_ptr_ipv4_addr"
            fi
        fi
    fi
    log_info "IPv4 update was successful"
}

update_ipv6(){
    local fqdn_name="$1"
    local ttl="$2"
    local ptr_update="$3"
    local dc_hostname="$4"
    local exist_aaaa_record=0
    local exist_ptr_record=0
    local first_registration_flag=0
    local address_family="IPv6"
    local ipv6_addr=
    local old_ipv6_addr=
    local old_ptr_ipv6_addr=
    local connect_iface=

    connect_iface="$(get_connection_iface_to_ldap "$address_family" "$dc_hostname")"
    ipv6_addr="$(get_iface_ipv6_addr "$connect_iface")"

    check_exist_forward_lookup_record "$fqdn_name"  "$address_family" "$dc_hostname" || exist_aaaa_record=1
    if [ "$ptr_update" -eq 1 ]; then
        check_exist_ptr_record "$ipv6_addr" "$dc_hostname" || exist_ptr_record=1

        if [ $exist_ptr_record -eq 0 ] && [ $exist_aaaa_record -eq 1 ]; then
            log_warning "The IPv6 PTR record exists, but the AAAA record is missing and host IP was changed. To avoid duplicate entries,\
    you should delete the old entry manually."
        fi
    fi

    if [ $exist_aaaa_record -eq 1 ]; then
        log_info "The AAAA record does not exist. Start AAAA record registration."
        # Set update flag as true and start first registration
        first_registration_flag=1
        call_update_forward_lookup_record "$ipv6_addr" "$fqdn_name" "$ttl" "$address_family" "$first_registration_flag"
        # Check if ptr update is enabled
        if [ "$ptr_update" -eq 1 ]; then
            log_info "Start IPv6 PTR record registration."
            call_update_ptr_record  "$ipv6_addr" "$fqdn_name" "$ttl" "$address_family"
        fi
    else
        old_ipv6_addr="$(get_old_addr "$fqdn_name" "$address_family" "$dc_hostname")"
        log_info "Checking whether the IPv6 records needs to be updated"
        log_info "Current IPv6 address: $ipv6_addr"
        log_info "IPv6 address in DNS server: $old_ipv6_addr"
        if [ "$ipv6_addr" = "$old_ipv6_addr" ]; then
            log_info "The IPv6 address of interface $connect_iface has not been changed."
            log_info "The update IPv6 was skipped"
            if [ "$ptr_update" -eq 1 ] && [ "$exist_ptr_record" -eq 1 ]; then
                log_info "The PTR record does not exist but IPv6 not changed and PTR record update enable"
                log_info "Start IPv6 PTR record registration"
                call_update_ptr_record  "$ipv6_addr" "$fqdn_name" "$ttl" "$address_family"
            fi
        else
            # Update addr if hi was changed
            log_info "The IPv6 address of interface $connect_iface has been changed."
            log_info "Start the AAAA record update."
            call_update_forward_lookup_record "$ipv6_addr" "$fqdn_name" "$ttl" "$address_family" "$first_registration_flag"
            if [ "$ptr_update" -eq 1 ]; then
                log_info "Start IPv6 PTR record update."
                local old_ptr_ipv6_addr=
                old_ptr_ipv6_addr="$(generate_ipv6_ptr_ip "$old_ipv6_addr")"
                call_update_ptr_record "$ipv6_addr" "$fqdn_name" "$ttl" "$address_family" "$old_ptr_ipv6_addr"
            fi
        fi
    fi
    log_info "IPv6 update was successful"
}

# message if update was failed
error_end_message() {
    log_info "The update completed with an error."
}

# message if update was successful
success_end_message(){ 
    log_info "The update was successful."
}

# Destory host credentials
destroy_credentials() {
    log_info "Destroy host credential."
    kdestroy -A > /dev/null 2>&1
    rm -f "$TMP_KRB_CCACHE_NAME"
    unset KRB5CCNAME
}

main() {
    trap "error_end_message && destroy_credentials" ERR

    # Vars
    local ttl="$1"
    local update_ipv4="$2"
    local update_ipv6="$3"
    local ipv4_ptr_update="$4"
    local ipv6_ptr_update="$5"
    local ret=0
    local fqdn_name=
    local upper_hostname=
    local domain_realm=
    local dc_hostname=
    local site_name=

    # Init vars
    domain_realm="$(get_domain_realm)"
    upper_hostname="$(get_short_hostname)"
    fqdn_name="$(get_fqdn_hostname)"
    log_info "Hostname: $fqdn_name"
    # Check
    check_winbind_status || ret=1
    check_net_is_install || ret=1
    # Get available dns server
    log_info "Trying to get the site name"
    site_name="$(get_site_name "$domain_realm")"
    log_info "Site: $site_name"
    dc_hostname="$(get_reacheble_dns_server "$domain_realm" "$site_name" "$fqdn_name" )" || ret=1
    if [ $ret -eq 0 ]; then
        # Host kinit
        set_cache_name
        log_info "Get host credentials"
        host_kinit "$upper_hostname" "$domain_realm"
        log_info "Retrieving host credentials successfully"

        if [ "$update_ipv4" -eq 1 ]; then
            log_info "Update IPv4"
            update_ipv4 "$fqdn_name" "$ttl" "$ipv4_ptr_update" "$dc_hostname"
        fi

        if [ "$update_ipv6" -eq 1 ]; then
            log_info "Update IPv6"
            update_ipv6 "$fqdn_name" "$ttl" "$ipv6_ptr_update" "$dc_hostname"
        fi

    else
        error_end_message
        destroy_credentials
        return 1
    fi

    success_end_message
    destroy_credentials
    return 0
}

main "$ttl" "$enable_update_ipv4" "$enable_update_ipv6" "$ipv4_ptr_update" "$ipv6_ptr_update"   2>&1
