POSIX script, IPv4 IPv6 . IP-, , . , , , grep , , , .
IPv6, ::, . , . , , , .
#!/bin/sh
set -e
is_numeric() {
case "$1" in
"" | *[![:digit:]]* | 0[[:digit:]]* ) return 1;;
esac
}
is_hex() {
case "$1" in
"" | *[![:xdigit:]]* ) return 1;;
esac
}
is_ip4() {
[ -n "$1" ] || return
IP4_ADDR="$1"
case "$IP4_ADDR" in
*"/"* )
IP4_GROUP="${IP4_ADDR##*"/"}"
is_numeric "$IP4_GROUP" && [ "$IP4_GROUP" -le 32 ] || return
IP4_ADDR="${IP4_ADDR%"/$IP4_GROUP"}";;
esac
IP4_IFS="$IFS"; IFS="."
IP4_COUNT=0
for IP4_GROUP in $IP4_ADDR ;do
! is_numeric "$IP4_GROUP" || [ "$IP4_GROUP" -gt 255 ] && IFS="$IP4_IFS" && return 1
IP4_COUNT=$(( IP4_COUNT + 1 ))
[ "$IP4_COUNT" -le 4 ] || break
done
IFS="$IP4_IFS"
[ "$IP4_COUNT" -eq 4 ]
}
is_ip6() {
[ -n "$1" ] || return
IP6_ADDR="$1"
case "$IP6_ADDR" in
*"/"* )
IP6_GROUP="${IP6_ADDR##*"/"}"
is_numeric "$IP6_GROUP" && [ "$IP6_GROUP" -le 128 ] || return
IP6_ADDR="${IP6_ADDR%"/$IP6_GROUP"}";;
esac
case "$IP6_ADDR" in
*"::"*"::"* | *":::"* | *[^:]":" | *"."*":"* ) return 1;;
*"::"* )
IP6_EXPANDED=0
IP6_COUNT=1;;
* )
IP6_EXPANDED=""
IP6_COUNT=0;;
esac
IP6_IFS="$IFS"; IFS=":"
for IP6_GROUP in $IP6_ADDR ;do
[ -z "$IP6_GROUP" ] && IP6_COUNT=$(( IP6_COUNT + 1 )) && continue
case "$IP6_GROUP" in
*"."* )
! is_ip4 "$IP6_GROUP/1" && IFS="$IP6_IFS" && return 1
IP6_COUNT=$(( IP6_COUNT + 2 ))
break;;
esac
[ "${#IP6_GROUP}" -gt 4 ] || ! is_hex "$IP6_GROUP" && IFS="$IP6_IFS" && return 1
IP6_COUNT=$(( IP6_COUNT + 1 ))
[ "$IP6_COUNT" -le 8 ] || break
done
IFS="$IP6_IFS"
[ "$IP6_EXPANDED" = "0" ] && [ "$IP6_COUNT" -le 8 ] && return
[ "$IP6_COUNT" -eq 8 ]
}
.
TEST_PASSES=0
TEST_FAILURES=0
for TEST_IP in 0.0.0.0 255.255.255.255 1.2.3.4/1 1.2.3.4/32 12.12.12.12 123.123.123.123 101.201.201.109 ;do
! is_ip4 "$TEST_IP" && printf "IP4 test failed, test case '%s' returned invalid\n" "$TEST_IP" && TEST_FAILURES=$(( TEST_FAILURES + 1 )) || TEST_PASSES=$(( TEST_PASSES + 1 ))
done
for TEST_IP in ::1 ::1/128 ::1/0 ::1234 ::bad ::12 1:2:3:4:5:6:7:8 1234:5678:90ab:cdef:1234:5678:90ab:cdef \
1234:5678:90ab:cdef:1234:5678:90ab:cdef/127 1234:5678:90ab::5678:90ab:cdef/64 f:1234:c:ba:240::1 \
1:2:3:4:5:6:1.2.3.4 ::1.2.3.4 ::1.2.3.4/0 ::ffff:1.2.3.4 ;do
! is_ip6 "$TEST_IP" && printf "IP6 test failed, test case '%s' returned invalid\n" "$TEST_IP" && TEST_FAILURES=$(( TEST_FAILURES + 1 )) || TEST_PASSES=$(( TEST_PASSES + 1 ))
done
for TEST_IP in junk . / 0 -1.0.0.0 1.2.c.0 a.0.0.0 " 1.2.3.4" "1.2.3.4 " " " 01.0.0.0 09.0.0.0 0.0.0.01 \
0.0.0.09 0.09.0.0.0 0.01.0.0 0.0.01.0 0.0.0.a 0.0.0 .0.0.0.0 256.0.0.0 0.0.0.256 "" 0 1 12 \
123 1.2.3.4/s 1.2.3.4/33 1.2.3.4/1/1 ;do
is_ip4 "$TEST_IP" && printf "IP4 test failed, test case '%s' returned valid\n" "$TEST_IP" && TEST_FAILURES=$(( TEST_FAILURES + 1 )) || TEST_PASSES=$(( TEST_PASSES + 1 ))
done
for TEST_IP in junk "" : / :1 ::1/ ::1/1/1 :::1 ::1/129 ::12345 ::bog ::1234:345.234.0.0 ::sdf.d ::1g2 \
1:2:3:44444:5:6:7:8 1:2:3:4:5:6:7 1:2:3:4:5:6:7:8/1c1 1234:5678:90ab:cdef:1234:5678:90ab:cdef:1234/64 \
1234:5678:90ab:cdef:1234:5678::cdef/64 ::1.2.3.4:1 1.2.3.4:: ::1.2.3.4j ::1.2.3.4/ ::1.2.3.4:junk ::1.2.3.4.junk ;do
is_ip6 "$TEST_IP" && printf "IP6 test failed, test case '%s' returned valid\n" "$TEST_IP" && TEST_FAILURES=$(( TEST_FAILURES + 1 )) || TEST_PASSES=$(( TEST_PASSES + 1 ))
done
printf "test complete, %s passes and %s failures\n" "$TEST_PASSES" "$TEST_FAILURES"