| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195 |
- package fasthttp
- import (
- "bytes"
- "errors"
- )
- var (
- errInvalidIPv6Host = errors.New("invalid IPv6 host")
- errInvalidIPv6Zone = errors.New("invalid IPv6 zone")
- errInvalidIPv6Address = errors.New("invalid IPv6 address")
- )
- func validateIPv6Literal(host []byte) error {
- if len(host) == 0 || host[0] != '[' {
- return nil
- }
- end := bytes.IndexByte(host, ']')
- if end < 0 || end == 1 {
- return errInvalidIPv6Host
- }
- addr := host[1:end]
- // Optional zone.
- if zi := bytes.IndexByte(addr, '%'); zi >= 0 {
- if zi == len(addr)-1 {
- return errInvalidIPv6Zone
- }
- addr = addr[:zi]
- }
- // Must have a colon to be IPv6.
- if bytes.IndexByte(addr, ':') < 0 {
- return errInvalidIPv6Address
- }
- // IPv4-embedded?
- if bytes.IndexByte(addr, '.') >= 0 {
- lastColon := bytes.LastIndexByte(addr, ':')
- if lastColon < 0 || lastColon == len(addr)-1 {
- return errInvalidIPv6Address
- }
- ipv4 := addr[lastColon+1:]
- if !validIPv4(ipv4) {
- return errInvalidIPv6Address
- }
- head := addr[:lastColon]
- seenDoubleAtSplit := lastColon > 0 && addr[lastColon-1] == ':'
- if seenDoubleAtSplit {
- head = addr[:lastColon-1]
- }
- hextets, seenDoubleHead, ok := parseIPv6Hextets(head, false)
- if !ok {
- return errInvalidIPv6Address
- }
- if seenDoubleHead && seenDoubleAtSplit {
- return errInvalidIPv6Address
- }
- hextets += 2 // IPv4 tail = 2 hextets
- seenDouble := seenDoubleHead || seenDoubleAtSplit
- // '::' must compress at least one hextet.
- if (!seenDouble && hextets != 8) || (seenDouble && hextets >= 8) {
- return errInvalidIPv6Address
- }
- return nil
- }
- // Pure IPv6
- hextets, seenDouble, ok := parseIPv6Hextets(addr, false)
- if !ok {
- return errInvalidIPv6Address
- }
- if (!seenDouble && hextets != 8) || (seenDouble && hextets >= 8) {
- return errInvalidIPv6Address
- }
- return nil
- }
- func parseIPv6Hextets(s []byte, allowTrailingColon bool) (groups int, seenDouble, ok bool) {
- n := len(s)
- if n == 0 {
- return 0, false, true
- }
- i := 0
- justSawDouble := false
- for i < n {
- if s[i] == ':' {
- if i+1 < n && s[i+1] == ':' {
- if seenDouble || justSawDouble {
- return 0, false, false
- }
- seenDouble = true
- justSawDouble = true
- i += 2
- if i == n {
- break
- }
- continue
- }
- if i == 0 {
- return 0, false, false
- }
- if justSawDouble {
- return 0, false, false
- }
- if i == n-1 {
- if allowTrailingColon {
- break
- }
- return 0, false, false
- }
- if !ishex(s[i+1]) {
- return 0, false, false
- }
- i++
- continue
- }
- justSawDouble = false
- cnt := 0
- for cnt < 4 && i < n && ishex(s[i]) {
- i++
- cnt++
- }
- if cnt == 0 {
- return 0, false, false
- }
- groups++
- if i < n && s[i] != ':' {
- return 0, false, false
- }
- }
- return groups, seenDouble, true
- }
- // validIPv4 validates a dotted-quad (exactly 4 parts, 0..255) with no leading zeros
- // unless the octet is exactly "0".
- func validIPv4(s []byte) bool {
- parts := 0
- i := 0
- n := len(s)
- for parts < 4 {
- if i >= n {
- return false
- }
- start := i
- val := 0
- digits := 0
- for i < n {
- c := s[i]
- if c < '0' || c > '9' {
- break
- }
- val = val*10 + int(c-'0')
- if val > 255 {
- return false
- }
- i++
- digits++
- if digits > 3 {
- return false
- }
- }
- if digits == 0 {
- return false
- }
- // Disallow leading zeros like "00", "01", "001".
- // Allowed: exactly "0" or any number that doesn't start with '0'.
- if digits > 1 && s[start] == '0' {
- return false
- }
- parts++
- if parts == 4 {
- return i == n // must consume all input
- }
- if i >= n || s[i] != '.' {
- return false
- }
- i++ // skip dot
- }
- return false
- }
|