nats-io/nats-server · error

IPv4 address data too short: %d bytes

Error message

IPv4 address data too short: %d bytes

What it means

parseIPv4Addr requires at least proxyProtoAddrSizeIPv4 (12) bytes of address data for an AF_INET PROXY v2 header (4 src IP + 4 dst IP + 2 src port + 2 dst port). The declared addrLen from the header was smaller, so the payload cannot possibly contain a valid IPv4 endpoint pair and parsing is aborted immediately without reading from the connection.

Source

Thrown at server/client_proxyproto.go:385

		// UNSPEC family with PROXY command is valid but rare
		// Just skip the address data
		if addrLen > 0 {
			if _, err := io.CopyN(io.Discard, conn, int64(addrLen)); err != nil {
				return nil, fmt.Errorf("failed to discard UNSPEC address address data: %w", err)
			}
		}
		return nil, nil
	default:
		return nil, fmt.Errorf("%w: unsupported address family 0x%02x", errProxyProtoUnsupported, family)
	}
	return addr, err
}

// parseIPv4Addr parses IPv4 address data from PROXY protocol header
func parseIPv4Addr(conn net.Conn, addrLen uint16) (*proxyProtoAddr, error) {
	// IPv4: 4 (src IP) + 4 (dst IP) + 2 (src port) + 2 (dst port) = 12 bytes minimum
	if addrLen < proxyProtoAddrSizeIPv4 {
		return nil, fmt.Errorf("IPv4 address data too short: %d bytes", addrLen)
	}
	addrData := make([]byte, addrLen)
	if _, err := io.ReadFull(conn, addrData); err != nil {
		return nil, fmt.Errorf("failed to read IPv4 address data: %w", err)
	}
	return &proxyProtoAddr{
		srcIP:   net.IP(addrData[0:4]),
		dstIP:   net.IP(addrData[4:8]),
		srcPort: binary.BigEndian.Uint16(addrData[8:10]),
		dstPort: binary.BigEndian.Uint16(addrData[10:12]),
	}, nil
}

// parseIPv6Addr parses IPv6 address data from PROXY protocol header
func parseIPv6Addr(conn net.Conn, addrLen uint16) (*proxyProtoAddr, error) {
	// IPv6: 16 (src IP) + 16 (dst IP) + 2 (src port) + 2 (dst port) = 36 bytes minimum
	if addrLen < proxyProtoAddrSizeIPv6 {
		return nil, fmt.Errorf("IPv6 address data too short: %d bytes", addrLen)

View on GitHub (pinned to 3a66a489d2)

Solutions

  1. Fix the proxy to always declare 12 bytes for AF_INET (or 36 for AF_INET6) in the length field of the v2 header.
  2. Update/replace the middleware emitting PROXY v2 headers if it is a home-grown implementation.
  3. If you control the connecting client, switch it to PROXY v1 or plain TCP, since this server validates lengths strictly.

Example fix

// before: custom proxy writes only the two IPs
lenBuf := make([]byte, 2)
binary.BigEndian.PutUint16(lenBuf, 8)
// after: include ports (12 bytes for IPv4)
binary.BigEndian.PutUint16(lenBuf, 12)
Defensive patterns

Strategy: validation

Validate before calling

// pre-validate before sending a v2 header
canonical := map[byte]uint16{0x11: 12, 0x21: 36}
if want, ok := canonical[hdr[14]]; !ok || addrLen < want {
    return fmt.Errorf("addrLen %d too small for family 0x%02x", addrLen, hdr[14])
}

Type guard

func validIPv4AddrLen(n uint16) bool { return n >= 12 }

Prevention

When it happens

Trigger: A v2 PROXY header with command/family byte 0x11 but an address-length field less than 12 bytes, e.g. a truncated or hand-crafted header, or a sender that set the length to the IP-only size (8) and forgot the ports.

Common situations: Custom or buggy proxy implementations writing the wrong length field; packets truncated by MTU/fragmentation bugs; fuzz traffic; header corruption from TCP interception.

Related errors


AI-assisted analysis of nats-io/nats-server@3a66a489d2 (2026-09-02). Data as JSON: /api/errors/1505dee0c5774cf5. Report an issue: GitHub.