From 24784bfcd17edd6109fe9ffb22fbcaa72779871e Mon Sep 17 00:00:00 2001 From: alexlehm Date: Fri, 29 Apr 2016 21:06:30 +0200 Subject: [PATCH] Change hosts file resolver to be case-insensitive Motivation: Resolving hosts via the /etc/hosts file should be case-insensitive, e.g. localhost and LOCALHOST refer to the same host, this is the same that is applied to dns queries. Modifications: Store hosts Map with lowercase keys, lookup the keys as lowercase Add to unit test for the hosts file parser to use an UPPERCASE file entry Add unit test for DefaultHostsFileEntriesResolver to resolve both localhost and LOCALHOST Result: host resolution for local hosts file should match the rules applied to "getent hosts" or "ping" --- .../DefaultHostsFileEntriesResolver.java | 3 +- .../io/netty/resolver/HostsFileParser.java | 6 ++-- .../DefaultHostsFileEntriesResolverTest.java | 34 +++++++++++++++++++ .../netty/resolver/HostsFileParserTest.java | 6 +++- 4 files changed, 45 insertions(+), 4 deletions(-) create mode 100644 resolver/src/test/java/io/netty/resolver/DefaultHostsFileEntriesResolverTest.java diff --git a/resolver/src/main/java/io/netty/resolver/DefaultHostsFileEntriesResolver.java b/resolver/src/main/java/io/netty/resolver/DefaultHostsFileEntriesResolver.java index 3d19042e5c..dbf5fb9ca8 100644 --- a/resolver/src/main/java/io/netty/resolver/DefaultHostsFileEntriesResolver.java +++ b/resolver/src/main/java/io/netty/resolver/DefaultHostsFileEntriesResolver.java @@ -16,6 +16,7 @@ package io.netty.resolver; import java.net.InetAddress; +import java.util.Locale; import java.util.Map; /** @@ -27,6 +28,6 @@ public final class DefaultHostsFileEntriesResolver implements HostsFileEntriesRe @Override public InetAddress address(String inetHost) { - return entries.get(inetHost); + return entries.get(inetHost.toLowerCase(Locale.ENGLISH)); } } diff --git a/resolver/src/main/java/io/netty/resolver/HostsFileParser.java b/resolver/src/main/java/io/netty/resolver/HostsFileParser.java index 0e8b9163c7..a17a0f7068 100644 --- a/resolver/src/main/java/io/netty/resolver/HostsFileParser.java +++ b/resolver/src/main/java/io/netty/resolver/HostsFileParser.java @@ -29,6 +29,7 @@ import java.net.InetAddress; import java.util.ArrayList; import java.util.Collections; import java.util.List; +import java.util.Locale; import java.util.HashMap; import java.util.Map; import java.util.regex.Pattern; @@ -151,10 +152,11 @@ public final class HostsFileParser { // loop over hostname and aliases for (int i = 1; i < lineParts.size(); i ++) { String hostname = lineParts.get(i); - if (!entries.containsKey(hostname)) { + String hostnameLower = hostname.toLowerCase(Locale.ENGLISH); + if (!entries.containsKey(hostnameLower)) { // trying to map a host to multiple IPs is wrong // only the first entry is honored - entries.put(hostname, InetAddress.getByAddress(hostname, ipBytes)); + entries.put(hostnameLower, InetAddress.getByAddress(hostname, ipBytes)); } } } diff --git a/resolver/src/test/java/io/netty/resolver/DefaultHostsFileEntriesResolverTest.java b/resolver/src/test/java/io/netty/resolver/DefaultHostsFileEntriesResolverTest.java new file mode 100644 index 0000000000..7fac11b2d5 --- /dev/null +++ b/resolver/src/test/java/io/netty/resolver/DefaultHostsFileEntriesResolverTest.java @@ -0,0 +1,34 @@ +/* + * Copyright 2016 The Netty Project + * + * The Netty Project licenses this file to you under the Apache License, + * version 2.0 (the "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ +/** + * show issue https://github.com/netty/netty/issues/5182 + * HostsFileParser tries to resolve hostnames as case-sensitive + */ +package io.netty.resolver; + +import org.junit.Test; + +import static org.junit.Assert.assertNotNull; + +public class DefaultHostsFileEntriesResolverTest { + + @Test + public void testLocalhost() { + DefaultHostsFileEntriesResolver resolver = new DefaultHostsFileEntriesResolver(); + assertNotNull("localhost doesn't resolve", resolver.address("localhost")); + assertNotNull("LOCALHOST doesn't resolve", resolver.address("LOCALHOST")); + } +} diff --git a/resolver/src/test/java/io/netty/resolver/HostsFileParserTest.java b/resolver/src/test/java/io/netty/resolver/HostsFileParserTest.java index d326133895..bc059107b9 100644 --- a/resolver/src/test/java/io/netty/resolver/HostsFileParserTest.java +++ b/resolver/src/test/java/io/netty/resolver/HostsFileParserTest.java @@ -38,16 +38,20 @@ public class HostsFileParserTest { .append("192.168.0.2 host3 #comment").append("\n") // comment after hostname .append("192.168.0.3 host4 host5 host6").append("\n") // multiple aliases .append("192.168.0.4 host4").append("\n") // host mapped to a second address, must be ignored + .append("192.168.0.5 HOST7").append("\n") // uppercase host, should match lowercase host + .append("192.168.0.6 host7").append("\n") // should be ignored since we have the uppercase host already .toString(); Map entries = HostsFileParser.parse(new BufferedReader(new StringReader(hostsString))); - assertEquals("Expected 6 entries", 6, entries.size()); + assertEquals("Expected 7 entries", 7, entries.size()); assertEquals("127.0.0.1", entries.get("host1").getHostAddress()); assertEquals("192.168.0.1", entries.get("host2").getHostAddress()); assertEquals("192.168.0.2", entries.get("host3").getHostAddress()); assertEquals("192.168.0.3", entries.get("host4").getHostAddress()); assertEquals("192.168.0.3", entries.get("host5").getHostAddress()); assertEquals("192.168.0.3", entries.get("host6").getHostAddress()); + assertNotNull("uppercase host doesn't resolve", entries.get("host7")); + assertEquals("192.168.0.5", entries.get("host7").getHostAddress()); } }