diff --git a/vertx-core/src/main/java/io/vertx/core/impl/HostnameResolver.java b/vertx-core/src/main/java/io/vertx/core/impl/HostnameResolver.java index 9074c010c00..5a9ad106c16 100644 --- a/vertx-core/src/main/java/io/vertx/core/impl/HostnameResolver.java +++ b/vertx-core/src/main/java/io/vertx/core/impl/HostnameResolver.java @@ -45,35 +45,26 @@ public class HostnameResolver implements AddressResolver { private static final Logger log = LoggerFactory.getLogger(HostnameResolver.class); - private static Pattern resolvOption(String regex) { - return Pattern.compile("^[ \\t\\f]*options[^\n]+" + regex + "(?=$|\\s)", Pattern.MULTILINE); - } - - private static final Pattern NDOTS_OPTIONS_PATTERN = resolvOption("ndots:[ \\t\\f]*(\\d)+"); - private static final Pattern ROTATE_OPTIONS_PATTERN = resolvOption("rotate"); + private static final Pattern WHITESPACE_PATTERN = Pattern.compile("\\s+"); + private static final String NDOTS_LABEL = "ndots:"; + private static final String ROTATE_LABEL = "rotate"; + private static final String OPTIONS_ROW_LABEL = "options"; public static final int DEFAULT_NDOTS_RESOLV_OPTION; public static final boolean DEFAULT_ROTATE_RESOLV_OPTION; + + private static final int DEFAULT_NDOTS = 1; + private static final boolean DEFAULT_ROTATE = false; + static { - int ndots = 1; - boolean rotate = false; if (isLinux()) { - File f = new File("/etc/resolv.conf"); - try { - if (f.exists() && f.isFile()) { - String conf = Files.readString(f.toPath()); - int ndotsOption = parseNdotsOptionFromResolvConf(conf); - if (ndotsOption != -1) { - ndots = ndotsOption; - } - rotate = parseRotateOptionFromResolvConf(conf); - } - } catch (Throwable t) { - log.debug("Failed to load options from /etc/resolv/.conf", t); - } + ResolverOptions options = parseLinux(new File("/etc/resolv.conf")); + DEFAULT_NDOTS_RESOLV_OPTION = options.getEffectiveNdots(); + DEFAULT_ROTATE_RESOLV_OPTION = options.isRotate(); + } else { + DEFAULT_NDOTS_RESOLV_OPTION = DEFAULT_NDOTS; + DEFAULT_ROTATE_RESOLV_OPTION = DEFAULT_ROTATE; } - DEFAULT_NDOTS_RESOLV_OPTION = ndots; - DEFAULT_ROTATE_RESOLV_OPTION = rotate; } private final Vertx vertx; @@ -129,18 +120,65 @@ public Future close() { return provider.close(); } - public static int parseNdotsOptionFromResolvConf(String s) { + // visible for testing + public static ResolverOptions parseLinux(File f) { + try { + if (f.exists() && f.isFile()) { + return parseLinux(new String(Files.readAllBytes(f.toPath()))); + } + } catch (Throwable t) { + log.debug("Failed to load options from /etc/resolv.conf", t); + } + return new ResolverOptions(DEFAULT_NDOTS, DEFAULT_ROTATE); + } + + // exists mainly to facilitate testing + public static ResolverOptions parseLinux(String input) { int ndots = -1; - Matcher matcher = NDOTS_OPTIONS_PATTERN.matcher(s); - while (matcher.find()) { - ndots = Integer.parseInt(matcher.group(1)); + boolean rotate = false; + try { + int optionsIndex = input.indexOf(OPTIONS_ROW_LABEL); + if (optionsIndex != -1) { + boolean isProperOptionsLabel = false; + if (optionsIndex == 0) { + isProperOptionsLabel = true; + } else if (Character.isWhitespace(input.charAt(optionsIndex - 1))) { + isProperOptionsLabel = true; + } + if (isProperOptionsLabel) { + String afterOptions = input.substring(optionsIndex + OPTIONS_ROW_LABEL.length()); + int rotateIndex = afterOptions.indexOf(ROTATE_LABEL); + if (rotateIndex != -1) { + if (!containsNewLine(afterOptions.substring(0, rotateIndex))) { + rotate = true; + } + } + int ndotsIndex = afterOptions.indexOf(NDOTS_LABEL); + if (ndotsIndex != -1) { + if (!containsNewLine(afterOptions.substring(0, ndotsIndex))) { + Matcher matcher = Pattern.compile("ndots:[ \\t\\f]*(\\d)+(?=$|\\s)").matcher(afterOptions.substring(ndotsIndex)); + while (matcher.find()) { + ndots = Integer.parseInt(matcher.group(1)); + } + } + } + } + } + } catch (NumberFormatException e) { + log.debug("Failed to load options from /etc/resolv.conf", e); } - return ndots; + return new ResolverOptions(ndots, rotate); } - public static boolean parseRotateOptionFromResolvConf(String s) { - Matcher matcher = ROTATE_OPTIONS_PATTERN.matcher(s); - return matcher.find(); + //TODO: this can easily be parallelized if necessary + private static boolean containsNewLine(String input) { + for (int i = 0; i < input.length(); i++) { + char c = input.charAt(i); + if (c == '\n') { + return true; + } + } + return false; } class Impl implements EndpointResolver { @@ -190,4 +228,26 @@ public void dispose(L data) { public void close() { } } + + public static class ResolverOptions { + private final int ndots; + private final boolean rotate; + + public ResolverOptions(int ndots, boolean rotate) { + this.ndots = ndots; + this.rotate = rotate; + } + + public int getNdots() { + return ndots; + } + + public int getEffectiveNdots() { + return ndots != -1 ? ndots : 1; + } + + public boolean isRotate() { + return rotate; + } + } } diff --git a/vertx-core/src/test/java/io/vertx/tests/dns/HostnameResolutionTest.java b/vertx-core/src/test/java/io/vertx/tests/dns/HostnameResolutionTest.java index dab3b97d3cd..6c2b764abc5 100644 --- a/vertx-core/src/test/java/io/vertx/tests/dns/HostnameResolutionTest.java +++ b/vertx-core/src/test/java/io/vertx/tests/dns/HostnameResolutionTest.java @@ -34,8 +34,9 @@ import io.vertx.test.core.TestUtils; import io.vertx.test.core.VertxTestBase; import io.vertx.test.fakedns.FakeDNSServer; +import java.io.BufferedReader; +import java.io.StringReader; import org.junit.Assume; -import org.junit.Ignore; import org.junit.Test; import java.io.File; @@ -769,40 +770,40 @@ public void testNetSearchDomain() throws Exception { @Test public void testParseResolvConf() { - assertEquals(-1, HostnameResolver.parseNdotsOptionFromResolvConf("options")); - assertEquals(4, HostnameResolver.parseNdotsOptionFromResolvConf("options ndots: 4")); - assertEquals(4, HostnameResolver.parseNdotsOptionFromResolvConf("\noptions ndots: 4")); - assertEquals(-1, HostnameResolver.parseNdotsOptionFromResolvConf("boptions ndots: 4")); - assertEquals(4, HostnameResolver.parseNdotsOptionFromResolvConf(" options ndots: 4")); - assertEquals(4, HostnameResolver.parseNdotsOptionFromResolvConf("\toptions ndots: 4")); - assertEquals(4, HostnameResolver.parseNdotsOptionFromResolvConf("\foptions ndots: 4")); - assertEquals(4, HostnameResolver.parseNdotsOptionFromResolvConf("\n options ndots: 4")); - - assertEquals(4, HostnameResolver.parseNdotsOptionFromResolvConf("options\tndots: 4")); - assertEquals(4, HostnameResolver.parseNdotsOptionFromResolvConf("options\fndots: 4")); - assertEquals(4, HostnameResolver.parseNdotsOptionFromResolvConf("options ndots: 4")); - assertEquals(-1, HostnameResolver.parseNdotsOptionFromResolvConf("options\nndots: 4")); - - assertEquals(4, HostnameResolver.parseNdotsOptionFromResolvConf("options ndots:4")); - assertEquals(4, HostnameResolver.parseNdotsOptionFromResolvConf("options ndots:\t4")); - assertEquals(4, HostnameResolver.parseNdotsOptionFromResolvConf("options ndots: 4")); - assertEquals(-1, HostnameResolver.parseNdotsOptionFromResolvConf("options ndots:\n4")); - - assertEquals(4, HostnameResolver.parseNdotsOptionFromResolvConf("options ndots:4 ")); - assertEquals(4, HostnameResolver.parseNdotsOptionFromResolvConf("options ndots:4\t")); - assertEquals(4, HostnameResolver.parseNdotsOptionFromResolvConf("options ndots:4\f")); - assertEquals(4, HostnameResolver.parseNdotsOptionFromResolvConf("options ndots:4\n")); - assertEquals(4, HostnameResolver.parseNdotsOptionFromResolvConf("options ndots:4\r")); - assertEquals(-1, HostnameResolver.parseNdotsOptionFromResolvConf("options ndots:4_")); - - assertEquals(2, HostnameResolver.parseNdotsOptionFromResolvConf("options ndots:4\noptions ndots:2")); - assertEquals(4, HostnameResolver.parseNdotsOptionFromResolvConf("options ndots:4 debug")); - assertEquals(4, HostnameResolver.parseNdotsOptionFromResolvConf("options debug ndots:4")); - - assertEquals(false, HostnameResolver.parseRotateOptionFromResolvConf("options")); - assertEquals(true, HostnameResolver.parseRotateOptionFromResolvConf("options rotate")); - assertEquals(true, HostnameResolver.parseRotateOptionFromResolvConf("options rotate\n")); - assertEquals(false, HostnameResolver.parseRotateOptionFromResolvConf("options\nrotate")); + assertEquals(-1, HostnameResolver.parseLinux("options").getNdots()); + assertEquals(4, HostnameResolver.parseLinux("options ndots: 4").getNdots()); + assertEquals(4, HostnameResolver.parseLinux("\noptions ndots: 4").getNdots()); + assertEquals(-1, HostnameResolver.parseLinux("boptions ndots: 4").getNdots()); + assertEquals(4, HostnameResolver.parseLinux(" options ndots: 4").getNdots()); + assertEquals(4, HostnameResolver.parseLinux("\toptions ndots: 4").getNdots()); + assertEquals(4, HostnameResolver.parseLinux("\foptions ndots: 4").getNdots()); + assertEquals(4, HostnameResolver.parseLinux("\n options ndots: 4").getNdots()); + + assertEquals(4, HostnameResolver.parseLinux("options\tndots: 4").getNdots()); + assertEquals(4, HostnameResolver.parseLinux("options\fndots: 4").getNdots()); + assertEquals(4, HostnameResolver.parseLinux("options ndots: 4").getNdots()); + assertEquals(-1, HostnameResolver.parseLinux("options\nndots: 4").getNdots()); + + assertEquals(4, HostnameResolver.parseLinux("options ndots:4").getNdots()); + assertEquals(4, HostnameResolver.parseLinux("options ndots:\t4").getNdots()); + assertEquals(4, HostnameResolver.parseLinux("options ndots: 4").getNdots()); + assertEquals(-1, HostnameResolver.parseLinux("options ndots:\n4").getNdots()); + + assertEquals(4, HostnameResolver.parseLinux("options ndots:4 ").getNdots()); + assertEquals(4, HostnameResolver.parseLinux("options ndots:4\t").getNdots()); + assertEquals(4, HostnameResolver.parseLinux("options ndots:4\f").getNdots()); + assertEquals(4, HostnameResolver.parseLinux("options ndots:4\n").getNdots()); + assertEquals(4, HostnameResolver.parseLinux("options ndots:4\r").getNdots()); + assertEquals(-1, HostnameResolver.parseLinux("options ndots:4_").getNdots()); + + assertEquals(2, HostnameResolver.parseLinux("options ndots:4\noptions ndots:2").getNdots()); + assertEquals(4, HostnameResolver.parseLinux("options ndots:4 debug").getNdots()); + assertEquals(4, HostnameResolver.parseLinux("options debug ndots:4").getNdots()); + + assertEquals(false, HostnameResolver.parseLinux("options").isRotate()); + assertEquals(true, HostnameResolver.parseLinux("options rotate").isRotate()); + assertEquals(true, HostnameResolver.parseLinux("options rotate\n").isRotate()); + assertEquals(false, HostnameResolver.parseLinux("options\nrotate").isRotate()); } @Test