mirror of
https://github.com/valyala/fasthttp.git
synced 2026-06-13 15:46:49 +03:00
196 lines
3.5 KiB
Go
196 lines
3.5 KiB
Go
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
|
|
}
|