{"record":{"id":"2f519003b13f7256","repo":"XTLS/Xray-core","slug":"tries-to-resolve-itself","errorCode":null,"errorMessage":"tries to resolve itself!","messagePattern":"tries to resolve itself!","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"app/dns/nameserver_doh.go","lineNumber":140,"sourceCode":"}\n\nfunc (s *DoHNameServer) newReqID() uint16 {\n\treturn 0\n}\n\n// getCacheController implements CachedNameserver.\nfunc (s *DoHNameServer) getCacheController() *CacheController {\n\treturn s.cacheController\n}\n\n// sendQuery implements CachedNameserver.\nfunc (s *DoHNameServer) sendQuery(ctx context.Context, noResponseErrCh chan<- error, fqdn string, option dns_feature.IPOption) {\n\terrors.LogInfo(ctx, s.Name(), \" querying: \", fqdn)\n\n\tif s.Name()+\".\" == \"DOH//\"+fqdn {\n\t\terrors.LogError(ctx, s.Name(), \" tries to resolve itself! Use IP or set \\\"hosts\\\" instead\")\n\t\tif noResponseErrCh != nil {\n\t\t\terr := errors.New(\"tries to resolve itself!\", s.Name())\n\t\t\tif option.IPv4Enable {\n\t\t\t\tnoResponseErrCh <- err\n\t\t\t}\n\t\t\tif option.IPv6Enable {\n\t\t\t\tnoResponseErrCh <- err\n\t\t\t}\n\t\t}\n\t\treturn\n\t}\n\n\t// As we don't want our traffic pattern looks like DoH, we use Random-Length Padding instead of Block-Length Padding recommended in RFC 8467\n\t// Although DoH server like 1.1.1.1 will pad the response to Block-Length 468, at least it is better than no padding for response at all\n\treqs, err := buildReqMsgs(fqdn, option, s.newReqID, genEDNS0Options(s.clientIP, int(crypto.RandBetween(100, 300))))\n\tif err != nil {\n\t\terrors.LogErrorInner(ctx, err, \"failed to build dns query for \", fqdn)\n\t\tif noResponseErrCh != nil {\n\t\t\tif option.IPv4Enable {\n\t\t\t\tnoResponseErrCh <- err","sourceCodeStart":122,"sourceCodeEnd":158,"githubUrl":"https://github.com/XTLS/Xray-core/blob/7d214f8b094f75322fa3990f8aadad1c912f24f5/app/dns/nameserver_doh.go#L122-L158","documentation":"In selectPaddingVariant, after randomly choosing an entry from turn.sendVariants (or the implicit all-indices list), the referenced variant index must satisfy 0 <= index < len(turn.variants). An out-of-range entry means sendVariants contains an index pointing at a variant that does not exist. paddingTurnBounds performs the same check during validation, so hitting this at runtime implies the write path was entered without prior validation (e.g. direct writePaddingTurn calls) or schedules were mutated after validation.","triggerScenarios":"A paddingTurn with sendVariants like [3] while variants has 2 entries, written via writePaddingTurn directly; or schedule slices shared and mutated between validation and the write loop.","commonSituations":"Custom code composing turns programmatically with off-by-one indices; negative indices from unchecked config parsing; refactoring a variant list without updating the sendVariants index list.","solutions":["Ensure every sendVariants entry is within [0, len(variants)-1]","Prefer omitting sendVariants entirely (all variants are then selectable safely)","Run validatePaddingSchedule before writing so the bad index is reported deterministically with context"],"exampleFix":"// before\nvariants: []paddingVariant{v0, v1},\nsendVariants: []int{0, 2},\n// after\nvariants: []paddingVariant{v0, v1},\nsendVariants: []int{0, 1},","handlingStrategy":"validation","validationCode":"for _, idx := range turn.sendVariants {\n    if idx < 0 || idx >= len(turn.variants) {\n        return fmt.Errorf(\"send variant index %d out of range\", idx)\n    }\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Omit sendVariants unless subset selection is truly needed","Compute indices symbolically (named constants) instead of raw literals","Validate the schedule before writing so the bad index fails deterministically"],"tags":["padding","variants","validation","index","xmc"],"backgroundTag":null,"analyzedSha":"7d214f8b094f75322fa3990f8aadad1c912f24f5","analyzedAt":"2026-08-15T14:26:24.325Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}