{"record":{"id":"789a9aa6589bf745","repo":"XTLS/Xray-core","slug":"invalid-fakedns-setting","errorCode":null,"errorMessage":"invalid fakeDNS setting","messagePattern":"invalid fakeDNS setting","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"app/dns/fakedns/fake.go","lineNumber":48,"sourceCode":"}\n\nfunc (fkdns *Holder) GetFakeIPForDomain3(domain string, ipv4, ipv6 bool) []net.Address {\n\tisIPv6 := fkdns.ipRange.IP.To4() == nil\n\tif (isIPv6 && ipv6) || (!isIPv6 && ipv4) {\n\t\treturn fkdns.GetFakeIPForDomain(domain)\n\t}\n\treturn []net.Address{}\n}\n\nfunc (*Holder) Type() interface{} {\n\treturn (*dns.FakeDNSEngine)(nil)\n}\n\nfunc (fkdns *Holder) Start() error {\n\tif fkdns.config != nil && fkdns.config.IpPool != \"\" && fkdns.config.LruSize != 0 {\n\t\treturn fkdns.initializeFromConfig()\n\t}\n\treturn errors.New(\"invalid fakeDNS setting\")\n}\n\nfunc (fkdns *Holder) Close() error {\n\t// nothing to do for now, just wait GC\n\treturn nil\n}\n\nfunc NewFakeDNSHolder() (*Holder, error) {\n\tvar fkdns *Holder\n\tvar err error\n\n\tif fkdns, err = NewFakeDNSHolderConfigOnly(nil); err != nil {\n\t\treturn nil, errors.New(\"Unable to create Fake Dns Engine\").Base(err).AtError()\n\t}\n\terr = fkdns.initialize(dns.FakeIPv4Pool, 65535)\n\tif err != nil {\n\t\treturn nil, err\n\t}","sourceCodeStart":30,"sourceCodeEnd":66,"githubUrl":"https://github.com/XTLS/Xray-core/blob/7d214f8b094f75322fa3990f8aadad1c912f24f5/app/dns/fakedns/fake.go#L30-L66","documentation":"The first chunk of a padding record must carry the varint length header (1-5 bytes), so writePaddingTurnWithBuffer requires chunks[0] >= header.Len(). With a generated (range-based) turn, defaultPaddingChunks sizes the first chunk at min(recordLength, writeChunkLength) which is always >= recordLength's own varint size, so this fires only with variant-based turns whose first post-prefix chunk is smaller than the encoded header.","triggerScenarios":"A variant whose first chunk after prefix trimming is 1 byte while recordLength >= 128 (2-byte varint), or generally first chunk < varint length of the remaining record. Detected at write time, after validation has passed (validation checks chunk bounds but not header fit).","commonSituations":"Traffic-shaping variants crafted with a small leading chunk to mimic a specific protocol's first segment; prefix trimming that consumes large leading chunks and exposes a tiny next chunk.","solutions":["Enlarge the variant's first post-prefix chunk to at least 5 bytes (safe upper bound for any varint)","Or reorder chunks so a chunk >= the encoded record length's varint size comes first","Recompute after any prefix-length change, since trimming shifts which chunk is first"],"exampleFix":"// before: record ~300 bytes needs a 2-byte header, first chunk is 1\npaddingVariant{chunks: []int{100, 1, 200}}\n// after\npaddingVariant{chunks: []int{100, 8, 193}}","handlingStrategy":"validation","validationCode":"func firstChunkFitsHeader(v paddingVariant, prefix int) bool {\n    chunks, _, err := trimPaddingPrefix(v, prefix)\n    if err != nil || len(chunks) == 0 {\n        return false\n    }\n    total := 0\n    for _, c := range chunks {\n        total += c\n    }\n    return chunks[0] >= len(Varint(total - 0).bytesIfAvailable()) // fallback: chunks[0] >= 5 is always safe\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Make the first post-prefix chunk at least 5 bytes (max varint size) in every variant","Recheck after prefix changes since trimming changes which chunk is first"],"tags":["padding","variants","validation","xmc"],"backgroundTag":null,"analyzedSha":"7d214f8b094f75322fa3990f8aadad1c912f24f5","analyzedAt":"2026-08-15T14:26:24.325Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}