ddns-scripts: uclient-fetch supports -4/-6
[feed/packages.git] / net / ddns-scripts / files / dynamic_dns_functions.sh
index 0023f88f05198c6a083e5b21641f7b1efd4eea1c..438ba21d3f437606f8f9d51c661e97e834cba07a 100755 (executable)
@@ -1,20 +1,12 @@
 #!/bin/sh
 # /usr/lib/ddns/dynamic_dns_functions.sh
 #
-# Original written by Eric Paul Bishop, January 2008
 #.Distributed under the terms of the GNU General Public License (GPL) version 2.0
+# Original written by Eric Paul Bishop, January 2008
 # (Loosely) based on the script on the one posted by exobyte in the forums here:
 # http://forum.openwrt.org/viewtopic.php?id=14040
-#
-# extended and partial rewritten in August 2014 by
-#.Christian Schoenebeck <christian dot schoenebeck at gmail dot com>
-# to support:
-# - IPv6 DDNS services
-# - setting DNS Server to retrieve current IP including TCP transport
-# - Proxy Server to send out updates or retrieving WEB based IP detection
-# - force_interval=0 to run once (useful for cron jobs etc.)
-# - the usage of BIND's host instead of BusyBox's nslookup if installed (DNS via TCP)
-# - extended Verbose Mode and log file support for better error detection
+# extended and partial rewritten
+#.2014-2016 Christian Schoenebeck <christian dot schoenebeck at gmail dot com>
 #
 # function timeout
 # copied from http://www.ict.griffith.edu.au/anthony/software/timeout.sh
@@ -29,6 +21,7 @@
 . /lib/functions/network.sh
 
 # GLOBAL VARIABLES #
+VERSION="2.6.1-1"
 SECTION_ID=""          # hold config's section name
 VERBOSE_MODE=1         # default mode is log to console, but easily changed with parameter
 
@@ -53,6 +46,9 @@ LOCAL_IP=""           # holds the local IP read from the box
 
 URL_USER=""            # url encoded $username from config file
 URL_PASS=""            # url encoded $password from config file
+URL_PENC=""            # url encoded $param_enc from config file
+
+SRV_ANSWER=""          # Answer given by service on success
 
 ERR_LAST=0             # used to save $? return code of program and function calls
 ERR_UPDATE=0           # error counter on different local and registered ip
@@ -299,40 +295,38 @@ urlencode() {
 # file /usr/lib/ddns/services_ipv6 for IPv6
 # $1   Name of Variable to store url to
 # $2   Name of Variable to store script to
+# $3   Name of Variable to store service answer to
 get_service_data() {
-       local __LINE __FILE __NAME __URL __SERVICES __DATA
-       local __SCRIPT=""
-       local __OLD_IFS=$IFS
-       local __NEWLINE_IFS='
-' # __NEWLINE_IFS
-       [ $# -ne 2 ] && write_log 12 "Error calling 'get_service_data()' - wrong number of parameters"
+       [ $# -ne 3 ] && write_log 12 "Error calling 'get_service_data()' - wrong number of parameters"
 
        __FILE="/usr/lib/ddns/services"                                 # IPv4
        [ $use_ipv6 -ne 0 ] && __FILE="/usr/lib/ddns/services_ipv6"     # IPv6
 
-       # remove any lines not containing data, and then make sure fields are enclosed in double quotes
-       __SERVICES=$(cat $__FILE | grep "^[\t ]*[^#]" | \
-               awk ' gsub("\x27", "\"") { if ($1~/^[^\"]*$/) $1="\""$1"\"" }; { if ( $NF~/^[^\"]*$/) $NF="\""$NF"\""  }; { print $0 }')
-
-       IFS=$__NEWLINE_IFS
-       for __LINE in $__SERVICES; do
-               # grep out proper parts of data and use echo to remove quotes
-               __NAME=$(echo $__LINE | grep -o "^[\t ]*\"[^\"]*\"" | xargs -r -n1 echo)
-               __DATA=$(echo $__LINE | grep -o "\"[^\"]*\"[\t ]*$" | xargs -r -n1 echo)
-
-               if [ "$__NAME" = "$service_name" ]; then
-                       break                   # found so leave for loop
+       # workaround with variables; pipe create subshell with no give back of variable content
+       mkfifo pipe_$$
+       # only grep without # or whitespace at linestart | remove "
+#      grep -v -E "(^#|^[[:space:]]*$)" $__FILE | sed -e s/\"//g > pipe_$$ &
+       sed '/^#/d/^[ \t]*$/ds/\"//g' $__FILE  > pipe_$$ &
+
+       while read __SERVICE __DATA __ANSWER; do
+               if [ "$__SERVICE" = "$service_name" ]; then
+                       # check if URL or SCRIPT is given
+                       __URL=$(echo "$__DATA" | grep "^http")
+                       [ -z "$__URL" ] && __SCRIPT="/usr/lib/ddns/$__DATA"
+
+                       eval "$1=\"$__URL\""
+                       eval "$2=\"$__SCRIPT\""
+                       eval "$3=\"$__ANSWER\""
+                       rm pipe_$$
+                       return 0
                fi
-       done
-       IFS=$__OLD_IFS
-
-       # check if URL or SCRIPT is given
-       __URL=$(echo "$__DATA" | grep "^http")
-       [ -z "$__URL" ] && __SCRIPT="/usr/lib/ddns/$__DATA"
+       done < pipe_$$
+       rm pipe_$$
 
-       eval "$1=\"$__URL\""
-       eval "$2=\"$__SCRIPT\""
-       return 0
+       eval "$1=\"\""  # no service match clear variables
+       eval "$2=\"\""
+       eval "$3=\"\""
+       return 1
 }
 
 # Calculate seconds from interval and unit
@@ -465,12 +459,12 @@ verify_host_port() {
        __IPV6=$(echo $__HOST | grep -m 1 -o "$IPV6_REGEX")
        # if FQDN given get IP address
        [ -z "$__IPV4" -a -z "$__IPV6" ] && {
-               if [ -x /usr/bin/host ]; then   # use BIND host if installed
+               if [ -n "$(which host)" ]; then # use BIND host if installed
                        __PROG="BIND host"
-                       __RUNPROG="/usr/bin/host -t ANY $__HOST >$DATFILE 2>$ERRFILE"
+                       __RUNPROG="$(which host) -t ANY $__HOST >$DATFILE 2>$ERRFILE"
                else    # use BusyBox nslookup
                        __PROG="BusyBox nslookup"
-                       __RUNPROG="/usr/bin/nslookup $__HOST >$DATFILE 2>$ERRFILE"
+                       __RUNPROG="$(which nslookup) $__HOST >$DATFILE 2>$ERRFILE"
                fi
                write_log 7 "#> $__RUNPROG"
                eval $__RUNPROG
@@ -486,8 +480,8 @@ verify_host_port() {
                        __IPV4=$(cat $DATFILE | awk -F "address " '/has address/ {print $2; exit}' )
                        __IPV6=$(cat $DATFILE | awk -F "address " '/has IPv6/ {print $2; exit}' )
                else    # use BusyBox nslookup
-                       __IPV4=$(cat $DATFILE | sed -ne "3,\$ { s/^Address[0-9 ]\{0,\}: \($IPV4_REGEX\).*$/\\1/p }")
-                       __IPV6=$(cat $DATFILE | sed -ne "3,\$ { s/^Address[0-9 ]\{0,\}: \($IPV6_REGEX\).*$/\\1/p }")
+                       __IPV4=$(cat $DATFILE | sed -ne "/^Name:/,\$ { s/^Address[0-9 ]\{0,\}: \($IPV4_REGEX\).*$/\\1/p }")
+                       __IPV6=$(cat $DATFILE | sed -ne "/^Name:/,\$ { s/^Address[0-9 ]\{0,\}: \($IPV6_REGEX\).*$/\\1/p }")
                fi
        }
 
@@ -641,9 +635,8 @@ do_transfer() {
        [ $# -ne 1 ] && write_log 12 "Error in 'do_transfer()' - wrong number of parameters"
 
        # lets prefer GNU Wget because it does all for us - IPv4/IPv6/HTTPS/PROXY/force IP version
-       grep -i "\+ssl" /usr/bin/wget >/dev/null 2>&1   # check for Wget with SSL support
-       if [ $? -eq 0 -a $USE_CURL -eq 0 ]; then        # except global option use_curl is set to "1"
-               __PROG="/usr/bin/wget -nv -t 1 -O $DATFILE -o $ERRFILE" # non_verbose no_retry outfile errfile
+       if [ -n "$(which wget-ssl)" -a $USE_CURL -eq 0 ]; then                  # except global option use_curl is set to "1"
+               __PROG="$(which wget-ssl) -nv -t 1 -O $DATFILE -o $ERRFILE"     # non_verbose no_retry outfile errfile
                # force network/ip to use for communication
                if [ -n "$bind_network" ]; then
                        local __BINDIP
@@ -666,7 +659,7 @@ do_transfer() {
                                __PROG="$__PROG --ca-certificate=${cacert}"
                        elif [ -d "$cacert" ]; then
                                __PROG="$__PROG --ca-directory=${cacert}"
-                       else    # exit here because it makes no sense to start loop
+                       elif [ -n "$cacert" ]; then             # it's not a file and not a directory but given
                                write_log 14 "No valid certificate(s) found at '$cacert' for HTTPS communication"
                        fi
                fi
@@ -677,9 +670,13 @@ do_transfer() {
                __PROG="GNU Wget"               # reuse for error logging
 
        # 2nd choice is cURL IPv4/IPv6/HTTPS
-       # libcurl might be compiled without Proxy Support (default in trunk)
-       elif [ -x /usr/bin/curl ]; then
-               __PROG="/usr/bin/curl -RsS -o $DATFILE --stderr $ERRFILE"
+       # libcurl might be compiled without Proxy or HTTPS Support
+       elif [ -n "$(which curl)" ]; then
+               __PROG="$(which curl) -RsS -o $DATFILE --stderr $ERRFILE"
+               # check HTTPS support
+               /usr/bin/curl -V | grep "Protocols:" | grep -F "https" >/dev/null 2>&1
+               [ $? -eq 1 -a $use_https -eq 1 ] && \
+                       write_log 13 "cURL: libcurl compiled without https support"
                # force network/interface-device to use for communication
                if [ -n "$bind_network" ]; then
                        local __DEVICE
@@ -700,7 +697,7 @@ do_transfer() {
                                __PROG="$__PROG --cacert $cacert"
                        elif [ -d "$cacert" ]; then
                                __PROG="$__PROG --capath $cacert"
-                       else    # exit here because it makes no sense to start loop
+                       elif [ -n "$cacert" ]; then             # it's not a file and not a directory but given
                                write_log 14 "No valid certificate(s) found at '$cacert' for HTTPS communication"
                        fi
                fi
@@ -718,9 +715,37 @@ do_transfer() {
                __RUNPROG="$__PROG '$__URL'"    # build final command
                __PROG="cURL"                   # reuse for error logging
 
-       # busybox Wget (did not support neither IPv6 nor HTTPS)
-       elif [ -x /usr/bin/wget ]; then
-               __PROG="/usr/bin/wget -q -O $DATFILE"
+       # uclient-fetch possibly with ssl support if /lib/libustream-ssl.so installed
+       elif [ -n "$(which uclient-fetch)" ]; then
+               __PROG="$(which uclient-fetch) -q -O $DATFILE"
+               # force network/ip not supported
+               [ -n "$__BINDIP" ] && \
+                       write_log 14 "uclient-fetch: FORCE binding to specific address not supported"
+               # force ip version to use
+               if [ $force_ipversion -eq 1 ]; then
+                       [ $use_ipv6 -eq 0 ] && __PROG="$__PROG -4" || __PROG="$__PROG -6"       # force IPv4/IPv6
+               fi
+               # https possibly not supported
+               [ $use_https -eq 1 -a ! -f /lib/libustream-ssl.so ] && \
+                       write_log 14 "uclient-fetch: no HTTPS support! Additional install one of ustream-ssl packages"
+               # proxy support
+               [ -z "$proxy" ] && __PROG="$__PROG -Y off" || __PROG="$__PROG -Y on"
+               # https & certificates
+               if [ $use_https -eq 1 ]; then
+                       if [ "$cacert" = "IGNORE" ]; then
+                               __PROG="$__PROG --no-check-certificate"
+                       elif [ -f "$cacert" ]; then
+                               __PROG="$__PROG --ca-certificate=$cacert"
+                       elif [ -n "$cacert" ]; then             # it's not a file; nothing else supported
+                               write_log 14 "No valid certificate file '$cacert' for HTTPS communication"
+                       fi
+               fi
+               __RUNPROG="$__PROG '$__URL' 2>$ERRFILE"         # build final command
+               __PROG="uclient-fetch"                          # reuse for error logging
+
+       # Busybox Wget or any other wget in search $PATH (did not support neither IPv6 nor HTTPS)
+       elif [ -n "$(which wget)" ]; then
+               __PROG="$(which wget) -q -O $DATFILE"
                # force network/ip not supported
                [ -n "$__BINDIP" ] && \
                        write_log 14 "BusyBox Wget: FORCE binding to specific address not supported"
@@ -737,7 +762,7 @@ do_transfer() {
                __PROG="Busybox Wget"                           # reuse for error logging
 
        else
-               write_log 13 "Neither 'Wget' nor 'cURL' installed or executable"
+               write_log 13 "Neither 'Wget' nor 'cURL' nor 'uclient-fetch' installed or executable"
        fi
 
        while : ; do
@@ -793,19 +818,19 @@ send_update() {
                local __URL __ERR
 
                # do replaces in URL
-               __URL=$(echo $update_url | sed -e "s#\[USERNAME\]#$URL_USER#g" -e "s#\[PASSWORD\]#$URL_PASS#g" \
-                                              -e "s#\[DOMAIN\]#$domain#g" -e "s#\[IP\]#$__IP#g")
+               __URL=$(echo $update_url | sed -e "s#\[USERNAME\]#$URL_USER#g"  -e "s#\[PASSWORD\]#$URL_PASS#g" \
+                                              -e "s#\[PARAMENC\]#$URL_PENC#g"  -e "s#\[PARAMOPT\]#$param_opt#g" \
+                                              -e "s#\[DOMAIN\]#$domain#g"      -e "s#\[IP\]#$__IP#g")
                [ $use_https -ne 0 ] && __URL=$(echo $__URL | sed -e 's#^http:#https:#')
 
                do_transfer "$__URL" || return 1
 
                write_log 7 "DDNS Provider answered:\n$(cat $DATFILE)"
 
-               return 0
-               # TODO analyze providers answer
-               # "good" or "nochg"             = dyndns.com compatible API
-               # grep -i -E "good|nochg" $DATFILE >/dev/null 2>&1
-               # return $?     # "0" if found
+               [ -z "$SRV_ANSWER" ] && return 0        # not set then ignore
+
+               grep -i -E "$SRV_ANSWER" $DATFILE >/dev/null 2>&1
+               return $?       # "0" if found
        fi
 }
 
@@ -849,7 +874,7 @@ get_local_ip () {
                                                )
                                        else
                                                __DATA=$(awk '
-                                                       /inet6/ && /: [0-9a-eA-E]/ && !/\/128/ {        # Filter IPv6 exclude fxxx and /128 prefix
+                                                       /inet6/ && /: [0-9a-eA-E]/ {    # Filter IPv6 exclude fxxx
                                                        #   inet6 addr: 2001:db8::xxxx:xxxx/32 Scope:Global
                                                        FS="/";         # separator "/"
                                                        $0=$0;          # reread to activate separator
@@ -925,7 +950,8 @@ get_registered_ip() {
        # $2    (optional) if set, do not retry on error
        local __CNT=0   # error counter
        local __ERR=255
-       local __REGEX  __PROG  __RUNPROG  __DATA
+       local __REGEX  __PROG  __RUNPROG  __DATA  __IP
+       local __MUSL=$(nslookup localhost 2>&1 | grep -qF "(null)"; echo $?) # 0 == busybox compiled with musl "(null)" found
        # return codes
        # 1     no IP detected
 
@@ -935,21 +961,47 @@ get_registered_ip() {
        # set correct regular expression
        [ $use_ipv6 -eq 0 ] && __REGEX="$IPV4_REGEX" || __REGEX="$IPV6_REGEX"
 
-       if [ -x /usr/bin/host ]; then
-               __PROG="/usr/bin/host"
+       if [ -n "$(which host)" ]; then
+               __PROG="$(which host)"
                [ $use_ipv6 -eq 0 ] && __PROG="$__PROG -t A"  || __PROG="$__PROG -t AAAA"
                if [ $force_ipversion -eq 1 ]; then                     # force IP version
                        [ $use_ipv6 -eq 0 ] && __PROG="$__PROG -4"  || __PROG="$__PROG -6"
                fi
                [ $force_dnstcp -eq 1 ] && __PROG="$__PROG -T"  # force TCP
 
-               __RUNPROG="$__PROG $domain $dns_server >$DATFILE 2>$ERRFILE"
+               __RUNPROG="$__PROG $lookup_host $dns_server >$DATFILE 2>$ERRFILE"
                __PROG="BIND host"
-       elif [ -x /usr/bin/nslookup ]; then     # last use BusyBox nslookup
+       elif [ -n "$(which hostip)" ]; then     # hostip package installed
+               __PROG="$(which hostip)"
+               [ $force_dnstcp -ne 0 ] && \
+                       write_log 14 "hostip - no support for 'DNS over TCP'"
+
+               # is IP given as dns_server ?
+               __IP=$(echo $dns_server | grep -m 1 -o "$IPV4_REGEX")
+               [ -z "$__IP" ] && __IP=$(echo $dns_server | grep -m 1 -o "$IPV6_REGEX")
+
+               # we got NO ip for dns_server, so build command
+               [ -z "$__IP" -a -n "$dns_server" ] && {
+                       __IP="\`/usr/bin/hostip"
+                       [ $use_ipv6 -eq 1 -a $force_ipversion -eq 1 ] && __IP="$__IP -6"
+                       __IP="$__IP $dns_server | grep -m 1 -o"
+                       [ $use_ipv6 -eq 1 -a $force_ipversion -eq 1 ] \
+                               && __IP="$__IP '$IPV6_REGEX'" \
+                               || __IP="$__IP '$IPV4_REGEX'"
+                       __IP="$__IP \`"
+               }
+
+               [ $use_ipv6 -eq 1 ] && __PROG="$__PROG -6"
+               [ -n "$dns_server" ] && __PROG="$__PROG -r $__IP"
+               __RUNPROG="$__PROG $lookup_host >$DATFILE 2>$ERRFILE"
+               __PROG="hostip"
+       elif [ -n "$(which nslookup)" ]; then   # last use BusyBox nslookup
                [ $force_ipversion -ne 0 -o $force_dnstcp -ne 0 ] && \
                        write_log 14 "Busybox nslookup - no support to 'force IP Version' or 'DNS over TCP'"
+               [ $__MUSL -eq 0 -a -n "$dns_server" ] && \
+                       write_log 14 "Busybox compiled with musl - nslookup - no support to set/use DNS Server"
 
-               __RUNPROG="/usr/bin/nslookup $domain $dns_server >$DATFILE 2>$ERRFILE"
+               __RUNPROG="$(which nslookup) $lookup_host $dns_server >$DATFILE 2>$ERRFILE"
                __PROG="BusyBox nslookup"
        else    # there must be an error
                write_log 12 "Error in 'get_registered_ip()' - no supported Name Server lookup software accessible"
@@ -965,8 +1017,10 @@ get_registered_ip() {
                else
                        if [ "$__PROG" = "BIND host" ]; then
                                __DATA=$(cat $DATFILE | awk -F "address " '/has/ {print $2; exit}' )
+                       elif [ "$__PROG" = "hostip" ]; then
+                               __DATA=$(cat $DATFILE | grep -m 1 -o "$__REGEX")
                        else
-                               __DATA=$(cat $DATFILE | sed -ne "3,\$ { s/^Address[0-9 ]\{0,\}: \($__REGEX\).*$/\\1/p }" )
+                               __DATA=$(cat $DATFILE | sed -ne "/^Name:/,\$ { s/^Address[0-9 ]\{0,\}: \($__REGEX\).*$/\\1/p }" )
                        fi
                        [ -n "$__DATA" ] && {
                                write_log 7 "Registered IP '$__DATA' detected"
@@ -981,16 +1035,16 @@ get_registered_ip() {
                [ -n "$2" ] && return $__ERR            # $2 is given -> no retry
                [ $VERBOSE_MODE -gt 1 ] && {
                        # VERBOSE_MODE > 1 then NO retry
-                       write_log 4 "Get registered/public IP for '$domain' failed - Verbose Mode: $VERBOSE_MODE - NO retry on error"
+                       write_log 4 "Get registered/public IP for '$lookup_host' failed - Verbose Mode: $VERBOSE_MODE - NO retry on error"
                        return $__ERR
                }
 
                __CNT=$(( $__CNT + 1 )) # increment error counter
                # if error count > retry_count leave here
                [ $retry_count -gt 0 -a $__CNT -gt $retry_count ] && \
-                       write_log 14 "Get registered/public IP for '$domain' failed after $retry_count retries"
+                       write_log 14 "Get registered/public IP for '$lookup_host' failed after $retry_count retries"
 
-               write_log 4 "Get registered/public IP for '$domain' failed - retry $__CNT/$retry_count in $RETRY_SECONDS seconds"
+               write_log 4 "Get registered/public IP for '$lookup_host' failed - retry $__CNT/$retry_count in $RETRY_SECONDS seconds"
                sleep $RETRY_SECONDS &
                PID_SLEEP=$!
                wait $PID_SLEEP # enable trap-handler
@@ -1105,7 +1159,7 @@ split_FQDN() {
 
        # the leftover parameters are the HOST/SUBDOMAIN
        while [ -n "$1" ]; do
-               _HOST="$1 $HOST"                # remember we need to invert
+               _HOST="$1 $_HOST"               # remember we need to invert
                shift
        done
        _HOST=$(echo $_HOST | tr " " ".")       # insert DOT
@@ -1122,3 +1176,68 @@ split_FQDN() {
        eval "$4=''"            # clear HOST/SUBDOMAIN
        return 1
 }
+
+expand_ipv6() {
+       # Original written for bash by
+       # Author:  Florian Streibelt <florian@f-streibelt.de>
+       # Date:    08.04.2012
+       # License: Public Domain, but please be fair and
+       #          attribute the original author(s) and provide
+       #          a link to the original source for corrections:
+       #.         https://github.com/mutax/IPv6-Address-checks
+
+       # $1    IPv6 t0 expand
+       # $2    name of variable to store expanded IPv6
+       [ $# -ne 2 ] && write_log 12 "Error calling 'expand_ipv6()' - wrong number of parameters"
+
+       INPUT="$(echo "$1" | tr 'A-F' 'a-f')"
+       [ "$INPUT" = "::" ] && INPUT="::0"      # special case ::
+
+       O=""
+
+       while [ "$O" != "$INPUT" ]; do
+               O="$INPUT"
+
+               # fill all words with zeroes
+               INPUT=$( echo "$INPUT" | sed    -e 's|:\([0-9a-f]\{3\}\):|:0\1:|g' \
+                                               -e 's|:\([0-9a-f]\{3\}\)$|:0\1|g' \
+                                               -e 's|^\([0-9a-f]\{3\}\):|0\1:|g' \
+                                               -e 's|:\([0-9a-f]\{2\}\):|:00\1:|g' \
+                                               -e 's|:\([0-9a-f]\{2\}\)$|:00\1|g' \
+                                               -e 's|^\([0-9a-f]\{2\}\):|00\1:|g' \
+                                               -e 's|:\([0-9a-f]\):|:000\1:|g' \
+                                               -e 's|:\([0-9a-f]\)$|:000\1|g' \
+                                               -e 's|^\([0-9a-f]\):|000\1:|g' )
+
+       done
+
+       # now expand the ::
+       ZEROES=""
+
+       echo "$INPUT" | grep -qs "::"
+       if [ "$?" -eq 0 ]; then
+               GRPS="$( echo "$INPUT" | sed  's|[0-9a-f]||g' | wc -m )"
+               GRPS=$(( GRPS-1 ))              # remove carriage return
+               MISSING=$(( 8-GRPS ))
+               while [ $MISSING -gt 0 ]; do
+                       ZEROES="$ZEROES:0000"
+                       MISSING=$(( MISSING-1 ))
+               done
+
+               # be careful where to place the :
+               INPUT=$( echo "$INPUT" | sed    -e 's|\(.\)::\(.\)|\1'$ZEROES':\2|g' \
+                                               -e 's|\(.\)::$|\1'$ZEROES':0000|g' \
+                                               -e 's|^::\(.\)|'$ZEROES':0000:\1|g;s|^:||g' )
+       fi
+
+       # an expanded address has 39 chars + CR
+       if [ $(echo $INPUT | wc -m) != 40 ]; then
+               write_log 4 "Error in 'expand_ipv6()' - invalid IPv6 found: '$1' expanded: '$INPUT'"
+               eval "$2='invalid'"
+               return 1
+       fi
+
+       # echo the fully expanded version of the address
+       eval "$2=$INPUT"
+       return 0
+}