{"record":{"id":"375ca322555e2484","repo":"XTLS/Xray-core","slug":"empty-domain-name","errorCode":null,"errorMessage":"empty domain name","messagePattern":"empty domain name","errorType":"exception","errorClass":"errors.Error","httpStatus":null,"severity":"info","filePath":"proxy/dns/dns.go","lineNumber":396,"sourceCode":"\t}\n\tmsgBytes, err := builder.Finish()\n\tif err != nil {\n\t\terrors.LogInfoInner(context.Background(), err, \"pack message\")\n\t\tb.Release()\n\t\ttimer.SetTimeout(0)\n\t}\n\tb.Resize(0, int32(len(msgBytes)))\n\n\tif err := writer.WriteMessage(b); err != nil {\n\t\terrors.LogInfoInner(context.Background(), err, \"write IP answer\")\n\t\ttimer.SetTimeout(0)\n\t}\n}\n\nfunc (h *Handler) rejectNonIPQuery(id uint16, qType dnsmessage.Type, domain string, writer dns_proto.MessageWriter, rCode dnsmessage.RCode) error {\n\tdomainT := strings.TrimSuffix(domain, \".\")\n\tif domainT == \"\" {\n\t\treturn errors.New(\"empty domain name\")\n\t}\n\tb := buf.New()\n\trawBytes := b.Extend(buf.Size)\n\tbuilder := dnsmessage.NewBuilder(rawBytes[:0], dnsmessage.Header{\n\t\tID:                 id,\n\t\tRCode:              rCode,\n\t\tRecursionAvailable: true,\n\t\tRecursionDesired:   true,\n\t\tResponse:           true,\n\t\tAuthoritative:      true,\n\t})\n\tbuilder.EnableCompression()\n\tcommon.Must(builder.StartQuestions())\n\terr := builder.Question(dnsmessage.Question{\n\t\tName:  dnsmessage.MustNewName(domain),\n\t\tClass: dnsmessage.ClassINET,\n\t\tType:  qType,\n\t})","sourceCodeStart":378,"sourceCodeEnd":414,"githubUrl":"https://github.com/XTLS/Xray-core/blob/7d214f8b094f75322fa3990f8aadad1c912f24f5/proxy/dns/dns.go#L378-L414","documentation":"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.","triggerScenarios":"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).","commonSituations":"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.","solutions":["Identify the client issuing root-label queries (packet capture, query logging) — often a probe or scanner","If the traffic is unwanted, block it upstream of Xray or with a pre-rule (e.g. drop before dns outbound)","No config change inside Xray fixes it: the dns outbound simply cannot answer root queries; treat as expected behavior","Update/patch clients that query the root through the proxy"],"exampleFix":"// example: guard in your own handler before delegating to dns outbound\n// before\n_ = dnsOut.Process(ctx, link, dialer)\n\n// after\nif q.Name == \".\" || strings.TrimSuffix(q.Name, \".\") == \"\" {\n    return sendRefused(id, q.Type, writer)\n}\n_ = dnsOut.Process(ctx, link, dialer)","handlingStrategy":"validation","validationCode":"qname := strings.TrimSuffix(string(q.Name), \".\")\nif qname == \"\" { sendRefusedAndReturn() } // guard before routing to dns outbound","typeGuard":null,"tryCatchPattern":"if err := handler.RejectNonIP(id, qType, domain, w, rcode); err != nil {\n    if err.Error() == \"empty domain name\" { dropPacket(); return }\n    return err\n}","preventionTips":["Filter root-label (\".\") DNS queries upstream or with pre-rules","Treat this error as expected for scanners/probes; alert only on spikes","Keep DNS query logging on to attribute root queries to clients"],"tags":["go","xray","dns","protocol","edge-case"],"backgroundTag":null,"analyzedSha":"7d214f8b094f75322fa3990f8aadad1c912f24f5","analyzedAt":"2026-08-15T14:26:24.325Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}