{"sha":"eeed84177eb584ff10bbfa7e0fe0ae233ee68a9d","node_id":"C_kwDOABA-c9oAKGVlZWQ4NDE3N2ViNTg0ZmYxMGJiZmE3ZTBmZTBhZTIzM2VlNjhhOWQ","commit":{"author":{"name":"Norman Maurer","email":"norman_maurer@apple.com","date":"2026-06-01T15:03:00Z"},"committer":{"name":"GitHub","email":"noreply@github.com","date":"2026-06-01T15:03:00Z"},"message":"Merge commit from fork\n\nDNS: Only cache CNAME if part of the queried domain\nMotivation:\n\nWe should only cache the CNAME if it is part of the queried domain to ensure the name server is really authoritive for it and not provide us incorrect data.\n\nModifications:\n\n- Only cache if CNAME is part of the queried domain\n- Add unit test\n\nResult:\n\nNo more DNS Cache Poisoning (Bailiwick Bypass) possible","tree":{"sha":"887d2482a13e08384060ac4886c66122fc2dfd23","url":"https://api.github.com/repos/netty/netty/git/trees/887d2482a13e08384060ac4886c66122fc2dfd23"},"url":"https://api.github.com/repos/netty/netty/git/commits/eeed84177eb584ff10bbfa7e0fe0ae233ee68a9d","comment_count":0,"verification":{"verified":true,"reason":"valid","signature":"-----BEGIN PGP SIGNATURE-----\n\nwsFcBAABCAAQBQJqHZ8kCRC1aQ7uu5UhlAAAUt4QAI7mj7Em4YpWKXAQWSpe3luj\nh7JbdUWPM4hNf+kyH8fMUW5kpevHvhLZnHplB3lpZ8jMzBEO2t7sZ1VHm0HxSTXl\n1wpSZyMK+dJaxSCmW62BkZJURR/bZP5UKuAQ8i0/f9+7rUfHg/t8dpVN/x8eJ69C\nYLhuqUWdKMX5Csp1rH8RZr5niEKXTQTX9KXtpXxxo4huj/TP8uxtA7I6X9S6qGwG\nT8gM+rxt5M1aKT3qwpuAl69mTqNjohgszTy7v4RGALFMF2VT2tShBR1UWaT8VFQR\nirjrAryStIGNoaB4NLjRdyhBUv4oR0Y5phrkjPcIIOeBQd3RpOMrXI+ZVkmAIktF\nQ0Pb4Ub3i7gr6IX0QKNJ+8lgTfC+3ge5Zfvz9giEH7fJVGGMaeFGdqgFJHXhZto0\nn0VixO8VZSAnBEI94rteTZixiG1awr6If7PdypAO5+RJnVTDOPOTbzhuIC0w4U9F\ngrxMHqK0vsCvSJwBHWqWc6LysJEzW8q+H/l/g5IZWbODv9/vJmJLfGY1k+zYauK+\nEcJqFUslTmpczefsJ2R66dtrjqWKz0aH6o1z5WmBoKlqdQDqk6qMC50+ehNC3FMp\n08IGeJK2wZPAt9TWF7yOBCL7F6FotWrjrKLLoiWgNCKYA0jlbgXJhATv7x7IsNKF\nw/lK/sXfZP4e02i6rGcp\n=Bgt+\n-----END PGP SIGNATURE-----\n","payload":"tree 887d2482a13e08384060ac4886c66122fc2dfd23\nparent 89aa20ad1c5356b44e1294a59ec05e1ab1fe5924\nauthor Norman Maurer <norman_maurer@apple.com> 1780326180 +0200\ncommitter GitHub <noreply@github.com> 1780326180 +0200\n\nMerge commit from fork\n\nDNS: Only cache CNAME if part of the queried domain\nMotivation:\n\nWe should only cache the CNAME if it is part of the queried domain to ensure the name server is really authoritive for it and not provide us incorrect data.\n\nModifications:\n\n- Only cache if CNAME is part of the queried domain\n- Add unit test\n\nResult:\n\nNo more DNS Cache Poisoning (Bailiwick Bypass) possible","verified_at":"2026-06-01T15:04:57Z"}},"url":"https://api.github.com/repos/netty/netty/commits/eeed84177eb584ff10bbfa7e0fe0ae233ee68a9d","html_url":"https://github.com/netty/netty/commit/eeed84177eb584ff10bbfa7e0fe0ae233ee68a9d","comments_url":"https://api.github.com/repos/netty/netty/commits/eeed84177eb584ff10bbfa7e0fe0ae233ee68a9d/comments","author":{"login":"normanmaurer","id":439362,"node_id":"MDQ6VXNlcjQzOTM2Mg==","avatar_url":"https://avatars.githubusercontent.com/u/439362?v=4","gravatar_id":"","url":"https://api.github.com/users/normanmaurer","html_url":"https://github.com/normanmaurer","followers_url":"https://api.github.com/users/normanmaurer/followers","following_url":"https://api.github.com/users/normanmaurer/following{/other_user}","gists_url":"https://api.github.com/users/normanmaurer/gists{/gist_id}","starred_url":"https://api.github.com/users/normanmaurer/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/normanmaurer/subscriptions","organizations_url":"https://api.github.com/users/normanmaurer/orgs","repos_url":"https://api.github.com/users/normanmaurer/repos","events_url":"https://api.github.com/users/normanmaurer/events{/privacy}","received_events_url":"https://api.github.com/users/normanmaurer/received_events","type":"User","user_view_type":"public","site_admin":false},"committer":{"login":"web-flow","id":19864447,"node_id":"MDQ6VXNlcjE5ODY0NDQ3","avatar_url":"https://avatars.githubusercontent.com/u/19864447?v=4","gravatar_id":"","url":"https://api.github.com/users/web-flow","html_url":"https://github.com/web-flow","followers_url":"https://api.github.com/users/web-flow/followers","following_url":"https://api.github.com/users/web-flow/following{/other_user}","gists_url":"https://api.github.com/users/web-flow/gists{/gist_id}","starred_url":"https://api.github.com/users/web-flow/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/web-flow/subscriptions","organizations_url":"https://api.github.com/users/web-flow/orgs","repos_url":"https://api.github.com/users/web-flow/repos","events_url":"https://api.github.com/users/web-flow/events{/privacy}","received_events_url":"https://api.github.com/users/web-flow/received_events","type":"User","user_view_type":"public","site_admin":false},"parents":[{"sha":"89aa20ad1c5356b44e1294a59ec05e1ab1fe5924","url":"https://api.github.com/repos/netty/netty/commits/89aa20ad1c5356b44e1294a59ec05e1ab1fe5924","html_url":"https://github.com/netty/netty/commit/89aa20ad1c5356b44e1294a59ec05e1ab1fe5924"}],"stats":{"total":86,"additions":81,"deletions":5},"files":[{"sha":"fdc0102d8d45011d9bd23898f7826c828e5bd671","filename":"resolver-dns/src/main/java/io/netty/resolver/dns/DnsResolveContext.java","status":"modified","additions":13,"deletions":5,"changes":18,"blob_url":"https://github.com/netty/netty/blob/eeed84177eb584ff10bbfa7e0fe0ae233ee68a9d/resolver-dns%2Fsrc%2Fmain%2Fjava%2Fio%2Fnetty%2Fresolver%2Fdns%2FDnsResolveContext.java","raw_url":"https://github.com/netty/netty/raw/eeed84177eb584ff10bbfa7e0fe0ae233ee68a9d/resolver-dns%2Fsrc%2Fmain%2Fjava%2Fio%2Fnetty%2Fresolver%2Fdns%2FDnsResolveContext.java","contents_url":"https://api.github.com/repos/netty/netty/contents/resolver-dns%2Fsrc%2Fmain%2Fjava%2Fio%2Fnetty%2Fresolver%2Fdns%2FDnsResolveContext.java?ref=eeed84177eb584ff10bbfa7e0fe0ae233ee68a9d","patch":"@@ -622,8 +622,9 @@ private void onResponse(final DnsServerAddressStream nameServerAddrStream, final\n                 final DnsRecordType type = question.type();\n \n                 if (type == DnsRecordType.CNAME) {\n-                    onResponseCNAME(question, buildAliasMap(envelope.content(), cnameCache(), parent.executor()),\n-                                    queryLifecycleObserver, promise);\n+                    onResponseCNAME(question,\n+                            buildAliasMap(question.name(), envelope.content(), cnameCache(), parent.executor()),\n+                            queryLifecycleObserver, promise);\n                     return;\n                 }\n \n@@ -813,7 +814,7 @@ private void onExpectedResponse(\n \n         // We often get a bunch of CNAMES as well when we asked for A/AAAA.\n         final DnsResponse response = envelope.content();\n-        final Map<String, String> cnames = buildAliasMap(response, cnameCache(), parent.executor());\n+        final Map<String, String> cnames = buildAliasMap(question.name(), response, cnameCache(), parent.executor());\n         final int answerCount = response.count(DnsSection.ANSWER);\n \n         boolean found = false;\n@@ -972,7 +973,8 @@ private void onResponseCNAME(\n         }\n     }\n \n-    private static Map<String, String> buildAliasMap(DnsResponse response, DnsCnameCache cache, EventLoop loop) {\n+    private static Map<String, String> buildAliasMap(\n+            String queryName, DnsResponse response, DnsCnameCache cache, EventLoop loop) {\n         final int answerCount = response.count(DnsSection.ANSWER);\n         Map<String, String> cnames = null;\n         for (int i = 0; i < answerCount; i ++) {\n@@ -1003,7 +1005,13 @@ private static Map<String, String> buildAliasMap(DnsResponse response, DnsCnameC\n             String nameWithDot = hostnameWithDot(name);\n             String mappingWithDot = hostnameWithDot(mapping);\n             if (!nameWithDot.equalsIgnoreCase(mappingWithDot)) {\n-                cache.cache(nameWithDot, mappingWithDot, r.timeToLive(), loop);\n+                String queryNameWithDot = hostnameWithDot(queryName.toLowerCase(Locale.US));\n+                // Only cache the CNAME if the owner is in the bailiwick of the original query name.\n+                boolean inBailiwick = nameWithDot.equals(queryNameWithDot) ||\n+                        nameWithDot.endsWith(\".\" + queryNameWithDot);\n+                if (inBailiwick) {\n+                    cache.cache(nameWithDot, mappingWithDot, r.timeToLive(), loop);\n+                }\n                 cnames.put(name, mapping);\n             }\n         }"},{"sha":"95f73cbcc0a4a51253106ccd6881acef6b5df604","filename":"resolver-dns/src/test/java/io/netty/resolver/dns/DnsNameResolverTest.java","status":"modified","additions":68,"deletions":0,"changes":68,"blob_url":"https://github.com/netty/netty/blob/eeed84177eb584ff10bbfa7e0fe0ae233ee68a9d/resolver-dns%2Fsrc%2Ftest%2Fjava%2Fio%2Fnetty%2Fresolver%2Fdns%2FDnsNameResolverTest.java","raw_url":"https://github.com/netty/netty/raw/eeed84177eb584ff10bbfa7e0fe0ae233ee68a9d/resolver-dns%2Fsrc%2Ftest%2Fjava%2Fio%2Fnetty%2Fresolver%2Fdns%2FDnsNameResolverTest.java","contents_url":"https://api.github.com/repos/netty/netty/contents/resolver-dns%2Fsrc%2Ftest%2Fjava%2Fio%2Fnetty%2Fresolver%2Fdns%2FDnsNameResolverTest.java?ref=eeed84177eb584ff10bbfa7e0fe0ae233ee68a9d","patch":"@@ -3040,6 +3040,74 @@ public boolean clear(String hostname) {\n         }\n     }\n \n+    @ParameterizedTest\n+    @EnumSource(DnsNameResolverChannelStrategy.class)\n+    public void testCnameCacheBailiwick(DnsNameResolverChannelStrategy strategy) throws Exception {\n+        final Map<String, String> cache = new ConcurrentHashMap<>();\n+\n+        TestDnsServer dnsServer = new TestDnsServer(question -> {\n+            if (\"x.netty.io\".equals(question.getDomainName())) {\n+                Set<ResourceRecord> records = new HashSet<>();\n+                // Valid CNAME (in bailiwick of query)\n+                records.add(new TestDnsServer.TestResourceRecord(\n+                        \"x.netty.io\", RecordType.CNAME,\n+                        Collections.singletonMap(DnsAttribute.DOMAIN_NAME.toLowerCase(), \"cname.netty.io\")));\n+                // Invalid CNAME (out of bailiwick of query)\n+                records.add(new TestDnsServer.TestResourceRecord(\n+                        \"cname.netty.io\", RecordType.CNAME,\n+                        Collections.singletonMap(DnsAttribute.DOMAIN_NAME.toLowerCase(), \"evil.com\")));\n+                // Provide an A record to satisfy the resolution\n+                records.add(new TestDnsServer.TestResourceRecord(\n+                        \"evil.com\", RecordType.A,\n+                        Collections.singletonMap(DnsAttribute.IP_ADDRESS.toLowerCase(), \"10.0.0.99\")));\n+                return records;\n+            }\n+            return Collections.emptySet();\n+        });\n+        dnsServer.start();\n+        DnsNameResolver resolver = null;\n+        try {\n+            DnsNameResolverBuilder builder = newResolver(strategy)\n+                    .recursionDesired(true)\n+                    .resolvedAddressTypes(ResolvedAddressTypes.IPV4_ONLY)\n+                    .maxQueriesPerResolve(16)\n+                    .nameServerProvider(new SingletonDnsServerAddressStreamProvider(dnsServer.localAddress()))\n+                    .resolveCache(NoopDnsCache.INSTANCE)\n+                    .cnameCache(new DnsCnameCache() {\n+                        @Override\n+                        public String get(String hostname) {\n+                            return cache.get(hostname);\n+                        }\n+\n+                        @Override\n+                        public void cache(String hostname, String cname, long originalTtl, EventLoop loop) {\n+                            cache.put(hostname, cname);\n+                        }\n+\n+                        @Override\n+                        public void clear() {\n+                        }\n+\n+                        @Override\n+                        public boolean clear(String hostname) {\n+                            return false;\n+                        }\n+                    });\n+            resolver = builder.build();\n+            resolver.resolveAll(\"x.netty.io\").syncUninterruptibly();\n+\n+            // The CNAME for x.netty.io should be cached because it was the queried name\n+            assertEquals(\"cname.netty.io.\", cache.get(\"x.netty.io.\"));\n+            // The CNAME for cname.netty.io should NOT be cached because it is out of bailiwick for x.netty.io\n+            assertNull(cache.get(\"cname.netty.io.\"));\n+        } finally {\n+            dnsServer.stop();\n+            if (resolver != null) {\n+                resolver.close();\n+            }\n+        }\n+    }\n+\n     @Test\n     public void testInstanceWithNullPreferredAddressType() {\n         new DnsNameResolver("}]}