fatedier/frp · error
IPv4 zone is forbidden
Error message
IPv4 zone is forbidden
What it means
The v2 binary UDP address decoder refuses a non-zero zone length when the address family is IPv4. IPv6 addresses may carry a zone (scope) string such as eth0, but an IPv4 address with a zone is meaningless, so the codec treats it as a malformed packet. This is a strict validation that catches encoder bugs and hostile/malformed input.
Source
Thrown at pkg/msg/udp_binary.go:212
ipLen = net.IPv6len
default:
return nil, offset, fmt.Errorf("unknown address family %d", family)
}
if len(body)-offset < ipLen+3 {
return nil, offset, fmt.Errorf("truncated address")
}
ip := append(net.IP(nil), body[offset:offset+ipLen]...)
offset += ipLen
port := binary.BigEndian.Uint16(body[offset : offset+2])
offset += 2
zoneLen := int(body[offset])
offset++
if len(body)-offset < zoneLen {
return nil, offset, fmt.Errorf("truncated zone")
}
zoneBytes := body[offset : offset+zoneLen]
if family == 4 && zoneLen != 0 {
return nil, offset, fmt.Errorf("IPv4 zone is forbidden")
}
if !utf8.Valid(zoneBytes) {
return nil, offset, fmt.Errorf("zone is not valid UTF-8")
}
offset += zoneLen
return &net.UDPAddr{IP: ip, Port: int(port), Zone: string(zoneBytes)}, offset, nil
}
type V2BinaryUDPPacketReadWriter struct {
conn *wire.Conn
}
func NewV2BinaryUDPPacketReadWriter(rw io.ReadWriter) *V2BinaryUDPPacketReadWriter {
return &V2BinaryUDPPacketReadWriter{conn: wire.NewConn(rw)}
}
func (rw *V2BinaryUDPPacketReadWriter) ReadMsg() (Message, error) {
frame, err := rw.conn.ReadFrame()View on GitHub (pinned to 6c8a8d0a97)
Solutions
- When encoding IPv4 addresses, always write zone length 0 and omit the zone string.
- Prefer the library's own EncodeUDPPacketBinary rather than hand-rolling the encoding.
- If fuzzing, register this input as an expected rejection rather than a crash.
- Close the connection on receipt — the peer's encoder is non-conformant.
Example fix
// before (hand-rolled encoder): always writes zone
writeZone(addr.Zone) // writes "eth0" even for IPv4
// after
if addr.IP.To4() != nil && addr.Zone != "" {
return errors.New("IPv4 address cannot carry a zone")
}
writeZone(addr.Zone) // empty for IPv4 Defensive patterns
Strategy: validation
Validate before calling
// before sending a v2 binary UDP packet, normalize the address
if ip4 := addr.IP.To4(); ip4 != nil {
addr.Zone = "" // IPv4 must not carry a zone
} Try / catch
if _, err := rw.ReadMsg(); err != nil {
if strings.Contains(err.Error(), "IPv4 zone is forbidden") {
conn.Close() // peer encoder is non-conformant
}
} Prevention
- Always encode with the library's EncodeUDPPacketBinary.
- Zero the zone for IPv4 addresses in any custom serialization.
- Fuzzers: add this input to the rejection corpus.
When it happens
Trigger: DecodeUDPPacketBinary receives a frame with family=4 and a zone length > 0. Typically from a custom or buggy encoder that always writes a zone, or from fuzzed/garbage data hitting the decoder.
Common situations: Custom client code that builds binary UDP packets and reuses the IPv6 layout for IPv4; a fuzzing corpus exercising the decoder; a protocol-level test fixture authored with a wrong zone length.
Understand the failure class
- Authentication and authorization failures — expired tokens, bad credentials, and missing scopes.
Related errors
- zone is not valid UTF-8
- truncated zone
- message frame payload too short
- invalid protocol
- received JSON UDP packet after binary codec negotiation
AI-assisted analysis of fatedier/frp@6c8a8d0a97 (2026-08-15).
Data as JSON: /api/errors/38b3563b14c20242.
Report an issue: GitHub.