XTLS/Xray-core · info · errors.Error

empty domain name

Error message

empty domain name

What it means

rejectNonIPQuery builds a DNS response for non-A/AAAA queries, but first requires a non-empty QNAME: after trimming the trailing root dot, the domain must not be empty. A query for the root ("." or "") fails this check, so the handler cannot construct a valid reply message and returns this error.

Source

Thrown at proxy/dns/dns.go:396

	}
	msgBytes, err := builder.Finish()
	if err != nil {
		errors.LogInfoInner(context.Background(), err, "pack message")
		b.Release()
		timer.SetTimeout(0)
	}
	b.Resize(0, int32(len(msgBytes)))

	if err := writer.WriteMessage(b); err != nil {
		errors.LogInfoInner(context.Background(), err, "write IP answer")
		timer.SetTimeout(0)
	}
}

func (h *Handler) rejectNonIPQuery(id uint16, qType dnsmessage.Type, domain string, writer dns_proto.MessageWriter, rCode dnsmessage.RCode) error {
	domainT := strings.TrimSuffix(domain, ".")
	if domainT == "" {
		return errors.New("empty domain name")
	}
	b := buf.New()
	rawBytes := b.Extend(buf.Size)
	builder := dnsmessage.NewBuilder(rawBytes[:0], dnsmessage.Header{
		ID:                 id,
		RCode:              rCode,
		RecursionAvailable: true,
		RecursionDesired:   true,
		Response:           true,
		Authoritative:      true,
	})
	builder.EnableCompression()
	common.Must(builder.StartQuestions())
	err := builder.Question(dnsmessage.Question{
		Name:  dnsmessage.MustNewName(domain),
		Class: dnsmessage.ClassINET,
		Type:  qType,
	})

View on GitHub (pinned to 7d214f8b09)

Solutions

  1. Identify the client issuing root-label queries (packet capture, query logging) — often a probe or scanner
  2. If the traffic is unwanted, block it upstream of Xray or with a pre-rule (e.g. drop before dns outbound)
  3. No config change inside Xray fixes it: the dns outbound simply cannot answer root queries; treat as expected behavior
  4. Update/patch clients that query the root through the proxy

Example fix

// example: guard in your own handler before delegating to dns outbound
// before
_ = dnsOut.Process(ctx, link, dialer)

// after
if q.Name == "." || strings.TrimSuffix(q.Name, ".") == "" {
    return sendRefused(id, q.Type, writer)
}
_ = dnsOut.Process(ctx, link, dialer)
Defensive patterns

Strategy: validation

Validate before calling

qname := strings.TrimSuffix(string(q.Name), ".")
if qname == "" { sendRefusedAndReturn() } // guard before routing to dns outbound

Try / catch

if err := handler.RejectNonIP(id, qType, domain, w, rcode); err != nil {
    if err.Error() == "empty domain name" { dropPacket(); return }
    return err
}

Prevention

When it happens

Trigger: A DNS query with QNAME "." (root NS/SOA queries) or an empty QNAME reaching the dns outbound and being classified as a non-IP query (not TypeA/TypeAAAA).

Common situations: Recursive resolvers or monitoring probes issuing root NS / . IN SOA queries; some DNS-SD/LLMNR traffic; usually an edge case hit by scanners rather than normal clients.

Related errors


AI-assisted analysis of XTLS/Xray-core@7d214f8b09 (2026-08-15). Data as JSON: /api/errors/375ca322555e2484. Report an issue: GitHub.