{"record":{"id":"38b3563b14c20242","repo":"fatedier/frp","slug":"ipv4-zone-is-forbidden","errorCode":null,"errorMessage":"IPv4 zone is forbidden","messagePattern":"IPv4 zone is forbidden","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"pkg/msg/udp_binary.go","lineNumber":212,"sourceCode":"\t\tipLen = net.IPv6len\n\tdefault:\n\t\treturn nil, offset, fmt.Errorf(\"unknown address family %d\", family)\n\t}\n\tif len(body)-offset < ipLen+3 {\n\t\treturn nil, offset, fmt.Errorf(\"truncated address\")\n\t}\n\tip := append(net.IP(nil), body[offset:offset+ipLen]...)\n\toffset += ipLen\n\tport := binary.BigEndian.Uint16(body[offset : offset+2])\n\toffset += 2\n\tzoneLen := int(body[offset])\n\toffset++\n\tif len(body)-offset < zoneLen {\n\t\treturn nil, offset, fmt.Errorf(\"truncated zone\")\n\t}\n\tzoneBytes := body[offset : offset+zoneLen]\n\tif family == 4 && zoneLen != 0 {\n\t\treturn nil, offset, fmt.Errorf(\"IPv4 zone is forbidden\")\n\t}\n\tif !utf8.Valid(zoneBytes) {\n\t\treturn nil, offset, fmt.Errorf(\"zone is not valid UTF-8\")\n\t}\n\toffset += zoneLen\n\treturn &net.UDPAddr{IP: ip, Port: int(port), Zone: string(zoneBytes)}, offset, nil\n}\n\ntype V2BinaryUDPPacketReadWriter struct {\n\tconn *wire.Conn\n}\n\nfunc NewV2BinaryUDPPacketReadWriter(rw io.ReadWriter) *V2BinaryUDPPacketReadWriter {\n\treturn &V2BinaryUDPPacketReadWriter{conn: wire.NewConn(rw)}\n}\n\nfunc (rw *V2BinaryUDPPacketReadWriter) ReadMsg() (Message, error) {\n\tframe, err := rw.conn.ReadFrame()","sourceCodeStart":194,"sourceCodeEnd":230,"githubUrl":"https://github.com/fatedier/frp/blob/6c8a8d0a97d03b44e9528d30b30c70cb9d61b405/pkg/msg/udp_binary.go#L194-L230","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"// before (hand-rolled encoder): always writes zone\nwriteZone(addr.Zone) // writes \"eth0\" even for IPv4\n\n// after\nif addr.IP.To4() != nil && addr.Zone != \"\" {\n\treturn errors.New(\"IPv4 address cannot carry a zone\")\n}\nwriteZone(addr.Zone) // empty for IPv4","handlingStrategy":"validation","validationCode":"// before sending a v2 binary UDP packet, normalize the address\nif ip4 := addr.IP.To4(); ip4 != nil {\n\taddr.Zone = \"\" // IPv4 must not carry a zone\n}","typeGuard":null,"tryCatchPattern":"if _, err := rw.ReadMsg(); err != nil {\n\tif strings.Contains(err.Error(), \"IPv4 zone is forbidden\") {\n\t\tconn.Close() // peer encoder is non-conformant\n\t}\n}","preventionTips":["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."],"tags":["protocol","binary","udp","ipv4","validation"],"backgroundTag":null,"analyzedSha":"6c8a8d0a97d03b44e9528d30b30c70cb9d61b405","analyzedAt":"2026-08-15T06:53:27.215Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}