{"record":{"id":"1716b1c38f8f191d","repo":"nats-io/nats-server","slug":"w-tcp4-with-ipv6-address","errorCode":null,"errorMessage":"%w: TCP4 with IPv6 address","messagePattern":"%w: TCP4 with IPv6 address","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"server/client_proxyproto.go","lineNumber":216,"sourceCode":"\tsrcPort, err := strconv.ParseUint(parts[3], 10, 16)\n\tif err != nil {\n\t\treturn nil, nil, fmt.Errorf(\"invalid source port: %w\", err)\n\t}\n\n\tdstPort, err := strconv.ParseUint(parts[4], 10, 16)\n\tif err != nil {\n\t\treturn nil, nil, fmt.Errorf(\"invalid dest port: %w\", err)\n\t}\n\n\t// Validate protocol matches IP version. The textual form determines the\n\t// family: TCP4 requires dotted-quad addresses, TCP6 requires IPv6\n\t// addresses. IPv4-mapped IPv6 addresses (e.g. \"::ffff:192.0.2.1\") are\n\t// valid for TCP6 since dual-stack proxies can emit those for IPv4\n\t// clients on IPv6 sockets, matching the v2 parser behavior.\n\tsrcIsV6 := strings.Contains(parts[1], \":\")\n\tdstIsV6 := strings.Contains(parts[2], \":\")\n\tif protocol == proxyProtoV1TCP4 && (srcIsV6 || dstIsV6) {\n\t\treturn nil, nil, fmt.Errorf(\"%w: TCP4 with IPv6 address\", errProxyProtoInvalid)\n\t}\n\tif protocol == proxyProtoV1TCP6 && (!srcIsV6 || !dstIsV6) {\n\t\treturn nil, nil, fmt.Errorf(\"%w: TCP6 with IPv4 address\", errProxyProtoInvalid)\n\t}\n\tif protocol != proxyProtoV1TCP4 && protocol != proxyProtoV1TCP6 {\n\t\treturn nil, nil, fmt.Errorf(\"%w: invalid protocol %s\", errProxyProtoInvalid, protocol)\n\t}\n\n\treturn &proxyProtoAddr{\n\t\tsrcIP:   srcIP,\n\t\tsrcPort: uint16(srcPort),\n\t\tdstIP:   dstIP,\n\t\tdstPort: uint16(dstPort),\n\t}, remaining, nil\n}\n\n// readProxyProtoHeader reads and parses PROXY protocol (v1 or v2) from the connection.\n// Automatically detects version and routes to appropriate parser.","sourceCodeStart":198,"sourceCodeEnd":234,"githubUrl":"https://github.com/nats-io/nats-server/blob/3a66a489d262bf89b71a71c955c94920394532f3/server/client_proxyproto.go#L198-L234","documentation":"The header declared protocol TCP4 but at least one of the address fields contains a colon, indicating an IPv6 literal. The PROXY protocol spec requires TCP4 to carry IPv4 dotted-quad addresses for both source and destination, so the server rejects the header with errProxyProtoInvalid.","triggerScenarios":"Header like 'PROXY TCP4 2001:db8::1 198.51.100.7 35646 4222\\r\\n' (or with an IPv6 destination); any colon in either IP field while parts[0] == 'TCP4'.","commonSituations":"Dual-stack proxy hardcoding TCP4 while its sockets are IPv6; proxy behind an IPv6-only upstream; template not switching protocol keyword based on address family; NAT64/mapped-address setups emitting v6 literals under TCP4.","solutions":["Make the proxy emit TCP6 when addresses are IPv6 literals","If the address is IPv4-mapped IPv6, use TCP6 (the server accepts '::ffff:x.x.x.x' for TCP6) or rewrite to a plain IPv4 literal with TCP4","Fix the proxy's family-detection logic to derive the protocol keyword from the actual socket family","Update hardcoded 'TCP4' templates used in dual-stack environments"],"exampleFix":"// before\n\"PROXY TCP4 2001:db8::1 2001:db8::2 35646 4222\\r\\n\"\n// after\n\"PROXY TCP6 2001:db8::1 2001:db8::2 35646 4222\\r\\n\"","handlingStrategy":"validation","validationCode":"// Before sending, derive the keyword from the address family:\nproto := \"TCP4\"\nif strings.Contains(srcIPStr, \":\") || strings.Contains(dstIPStr, \":\") {\n    proto = \"TCP6\"\n}\nheader := fmt.Sprintf(\"PROXY %s %s %s %d %d\\r\\n\", proto, srcIPStr, dstIPStr, sport, dport)","typeGuard":"func protoMatchesFamily(proto, src, dst string) bool {\n    srcV6, dstV6 := strings.Contains(src, \":\"), strings.Contains(dst, \":\")\n    if proto == \"TCP4\" { return !srcV6 && !dstV6 }\n    if proto == \"TCP6\" { return srcV6 && dstV6 }\n    return false\n}","tryCatchPattern":"_, _, err := readProxyProtoHeader(conn)\nif err != nil {\n    if errors.Is(err, errProxyProtoInvalid) && strings.Contains(err.Error(), \"TCP4 with IPv6\") {\n        log.Printf(\"peer sent TCP4 with IPv6 address: %v\", err)\n        return\n    }\n    return err\n}","preventionTips":["Never hardcode TCP4 in dual-stack proxy templates","Derive the protocol keyword from the socket's address family","Use TCP6 with IPv4-mapped literals ('::ffff:x.x.x.x') for IPv4 clients on IPv6 sockets"],"tags":["proxy-protocol","network","ipv6","nats"],"backgroundTag":"proxy-protocol-invalid-header","analyzedSha":"3a66a489d262bf89b71a71c955c94920394532f3","analyzedAt":"2026-09-02T04:41:54.247Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T15:18:49.778Z"}