XTLS/Xray-core · error
failed to read domain for socks 4a
Error message
failed to read domain for socks 4a
What it means
Thrown in handshake4 (proxy/socks/protocol.go:78) for a SOCKS4a request. When the 4-byte address starts with 0x00 the client is expected to send a null-terminated domain name after the userid; ReadUntilNull failing (EOF/reset before the terminating 0x00) produces this error.
Source
Thrown at proxy/socks/protocol.go:78
{
buffer := buf.StackNew()
if _, err := buffer.ReadFullFrom(reader, 6); err != nil {
buffer.Release()
return nil, errors.New("insufficient header").Base(err)
}
port = net.PortFromBytes(buffer.BytesRange(0, 2))
address = net.IPAddress(buffer.BytesRange(2, 6))
buffer.Release()
}
if _, err := ReadUntilNull(reader); /* user id */ err != nil {
return nil, err
}
if address.IP()[0] == 0x00 {
domain, err := ReadUntilNull(reader)
if err != nil {
return nil, errors.New("failed to read domain for socks 4a").Base(err)
}
address = net.ParseAddress(domain)
}
switch cmd {
case cmdTCPConnect:
request := &protocol.RequestHeader{
Command: protocol.RequestCommandTCP,
Address: address,
Port: port,
Version: socks4Version,
}
if err := writeSocks4Response(writer, socks4RequestGranted, net.AnyIP, net.Port(0)); err != nil {
return nil, err
}
return request, nil
default:
writeSocks4Response(writer, socks4RequestRejected, net.AnyIP, net.Port(0))View on GitHub (pinned to 7d214f8b09)
Solutions
- Fix the client to send: USERID + 0x00 + DOMAIN + 0x00 after the 6-byte header, with DSTIP = 0x00 0x00 0x00 <non-zero>.
- Prefer SOCKS4a's modern replacement: use SOCKS5 hostname support (cmdTorResolve/connect with address type domain) instead.
- Check the base error for EOF/reset to distinguish a client bug from a network break.
Example fix
// before: 4a marker set but no domain sent
conn.Write([]byte{0x04, 0x01, 0x00, 0x50, 0x00, 0x00, 0x00, 0xFF, 0x00}) // then close
// after: append null-terminated domain
conn.Write([]byte{0x04, 0x01, 0x00, 0x50, 0x00, 0x00, 0x00, 0xFF, 0x00})
conn.Write([]byte("example.com\x00")) Defensive patterns
Strategy: validation
Validate before calling
// Client-side: build a well-formed SOCKS4a request with null-terminated fields
func socks4aRequest(w io.Writer, domain string, port uint16) error {
if len(domain) > 253 || strings.IndexByte(domain, 0) >= 0 {
return fmt.Errorf("invalid domain %q", domain)
}
req := []byte{0x04, 0x01, byte(port >> 8), byte(port), 0, 0, 0, 0xFF, 0x00}
req = append(req, domain...)
req = append(req, 0x00)
_, err := w.Write(req)
return err
} Try / catch
if err != nil && strings.Contains(err.Error(), "failed to read domain for socks 4a") {
return errors.New("client sent a SOCKS4a request without a null-terminated domain")
} Prevention
- Always terminate userid and domain strings with 0x00.
- Prefer SOCKS5 (ATYP 0x03) for hostname targets instead of SOCKS4a.
When it happens
Trigger: Client sets DSTIP bytes 0-2 to non-zero and byte 3 to 0x00 (the 4a convention) but never sends the domain string, sends it without the trailing null byte, or the connection breaks before the domain arrives.
Common situations: Buggy SOCKS4a client implementations that forget the null terminator; clients that craft the 4a marker bytes incorrectly; connection drops during slow handshakes.
Related errors
- socks 4 is not allowed when auth is required.
- insufficient header
- unsupported command: %v
- failed to read auth methods
- failed to read request
AI-assisted analysis of XTLS/Xray-core@7d214f8b09 (2026-08-15).
Data as JSON: /api/errors/a961b897d0bdcfb9.
Report an issue: GitHub.