diff --git a/profile.d/net.sh b/profile.d/net.sh index 3c23ffa..f36dbc8 100644 --- a/profile.d/net.sh +++ b/profile.d/net.sh @@ -206,20 +206,21 @@ isipv4() for oct in "${ip_arr[@]}"; do # Reject leading plus/minus or empty entries if [[ -z $oct || $oct =~ [^0-9] ]]; then - [[ -t 1 ]] && disp "The given parameter is NOT a valid IPv4." + [[ -t 1 ]] && disp E "The given parameter is NOT a valid IPv4." return 1 fi - if (( oct > 255 )); then - [[ -t 1 ]] && disp "The given parameter is NOT a valid IPv4." + # 10# forces base-10 so leading-zero octets (08, 09) are not read as octal + if (( 10#$oct > 255 )); then + [[ -t 1 ]] && disp E "The given parameter is NOT a valid IPv4." return 1 fi done - [[ -t 1 ]] && disp "The given IPv4 is valid." + [[ -t 1 ]] && disp I "The given IPv4 is valid." return 0 fi - [[ -t 1 ]] && disp "The given parameter is NOT a valid IPv4." + [[ -t 1 ]] && disp E "The given parameter is NOT a valid IPv4." return 1 } export -f isipv4 @@ -229,19 +230,94 @@ export -f isipv4 # ------------------------------------------------------------------------------ # Determine if parameter is a valid IPv6 address # Usage: isipv6 +# Accepts full and "::"-compressed forms, an embedded IPv4 suffix +# (e.g. ::ffff:192.0.2.1) and an optional RFC 4007 zone id (e.g. fe80::1%eth0). isipv6() { local ip="$1" - local regex='^([0-9a-fA-F]{0,4}:){1,7}[0-9a-fA-F]{0,4}$' - if [[ $ip =~ $regex ]]; then - if [[ -t 1 ]]; then - disp "The given IPv6 is valid." + local valid=0 + + # A single-pass validation; any failing check breaks out with valid=0. + while true; do + [[ -z $ip ]] && break + + # Strip an optional zone identifier (fe80::1%eth0); reject empty/multiple. + if [[ $ip == *%* ]]; then + [[ $ip == *%*%* ]] && break + [[ -z ${ip#*%} ]] && break + ip="${ip%%[%]*}" fi + + # Reject ':::' and more than one '::' compression. + [[ $ip == *:::* ]] && break + local compressed=0 + if [[ $ip == *::* ]]; then + [[ ${ip#*::} == *::* ]] && break + compressed=1 + fi + + # A lone leading/trailing ':' that is not part of a '::' is invalid. + [[ $ip == :* && $ip != ::* ]] && break + [[ $ip == *: && $ip != *:: ]] && break + + # An embedded IPv4 is allowed only as the trailing 32 bits. Validate it, + # then replace it with two zero hextets so the rest is a clean hextet list. + if [[ $ip == *.* ]]; then + local v4="${ip##*:}" + [[ $v4 == *.*.*.* ]] || break + local -a oct=() + IFS='.' read -ra oct <<< "$v4" + [[ ${#oct[@]} -eq 4 ]] || break + local o v4ok=1 + for o in "${oct[@]}"; do + if [[ ! $o =~ ^[0-9]{1,3}$ ]] || (( 10#$o > 255 )); then + v4ok=0 + break + fi + done + (( v4ok )) || break + ip="${ip%"$v4"}0:0" + fi + + # Count the hextets on each side of an optional '::'. + local left right seg part total=0 ok=1 + if (( compressed )); then + left="${ip%%::*}" + right="${ip#*::}" + else + left="$ip" + right="" + fi + + for seg in "$left" "$right"; do + [[ -z $seg ]] && continue + local -a parts=() + IFS=':' read -ra parts <<< "$seg" + for part in "${parts[@]}"; do + [[ $part =~ ^[0-9a-fA-F]{1,4}$ ]] || { ok=0; break; } + (( total++ )) + done + (( ok )) || break + done + (( ok )) || break + + # Without '::' there must be exactly 8 hextets; with it, fewer than 8 + # (the '::' expands to at least one zero hextet). + if (( compressed )); then + (( total <= 7 )) || break + else + (( total == 8 )) || break + fi + + valid=1 + break + done + + if (( valid )); then + [[ -t 1 ]] && disp I "The given IPv6 is valid." return 0 fi - if [[ -t 1 ]]; then - disp "The given parameter is not a valid IPv6." - fi + [[ -t 1 ]] && disp E "The given parameter is not a valid IPv6." return 1 } export -f isipv6 @@ -256,12 +332,13 @@ urlencode() local LANG=C local str="$*" local length="${#str}" + local i for (( i = 0; i < length; i++ )); do local c="${str:i:1}" case "$c" in [a-zA-Z0-9.~_-]) printf '%s' "$c" ;; ' ') printf '+' ;; - *) printf '%%%02X' "'$c" #| cut -d' ' -f2 ;; + *) printf '%%%02X' "'$c" ;; esac done }