Skip to content

Commit

Permalink
Use simple parser instead of regex for parsing resolv.conf
Browse files Browse the repository at this point in the history
Closes: #5429
  • Loading branch information
geoand committed Jan 10, 2025
1 parent 56a122a commit 534e14e
Show file tree
Hide file tree
Showing 2 changed files with 120 additions and 68 deletions.
113 changes: 80 additions & 33 deletions vertx-core/src/main/java/io/vertx/core/impl/HostnameResolver.java
Original file line number Diff line number Diff line change
Expand Up @@ -26,10 +26,13 @@
import io.vertx.core.spi.dns.AddressResolverProvider;
import io.vertx.core.spi.endpoint.EndpointResolver;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.nio.file.Files;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
Expand All @@ -45,35 +48,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;
Expand Down Expand Up @@ -129,18 +123,49 @@ public Future<Void> close() {
return provider.close();
}

public static int parseNdotsOptionFromResolvConf(String s) {
int ndots = -1;
Matcher matcher = NDOTS_OPTIONS_PATTERN.matcher(s);
while (matcher.find()) {
ndots = Integer.parseInt(matcher.group(1));
// visible for testing
public static ResolverOptions parseLinux(File f) {
try {
if (f.exists() && f.isFile()) {
try (BufferedReader br = new BufferedReader(new FileReader(f))) {
return parseLinux(br);
}
}
} catch (Throwable t) {
log.debug("Failed to load options from /etc/resolv.conf", t);
}
return ndots;
return new ResolverOptions(DEFAULT_NDOTS, DEFAULT_ROTATE);
}

public static boolean parseRotateOptionFromResolvConf(String s) {
Matcher matcher = ROTATE_OPTIONS_PATTERN.matcher(s);
return matcher.find();
// exists mainly to facilitate testing
public static ResolverOptions parseLinux(BufferedReader br) {
String line;
int ndots = -1;
boolean rotate = false;
try {
while ((line = br.readLine()) != null) {
if (line.trim().startsWith(OPTIONS_ROW_LABEL)) {
String[] parts = WHITESPACE_PATTERN.split(line.substring(OPTIONS_ROW_LABEL.length()));
for (int i = 0; i < parts.length; i++) {
String part = parts[i];
if (part.startsWith(NDOTS_LABEL)) {
String value;
if (i < parts.length - 1 && part.equals(NDOTS_LABEL)) {
value = parts[i + 1];
} else {
value = part.substring(NDOTS_LABEL.length());
}
ndots = Integer.parseInt(value);
} else if (part.contains(ROTATE_LABEL)) {
rotate = true;
}
}
}
}
} catch (IOException | NumberFormatException e) {
log.debug("Failed to load options from /etc/resolv.conf", e);
}
return new ResolverOptions(ndots, rotate);
}

class Impl<L> implements EndpointResolver<SocketAddress, SocketAddress, L, L> {
Expand Down Expand Up @@ -190,4 +215,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;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -769,40 +770,44 @@ 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(toBufferedReader("options")).getNdots());
assertEquals(4, HostnameResolver.parseLinux(toBufferedReader("options ndots: 4")).getNdots());
assertEquals(4, HostnameResolver.parseLinux(toBufferedReader("\noptions ndots: 4")).getNdots());
assertEquals(-1, HostnameResolver.parseLinux(toBufferedReader("boptions ndots: 4")).getNdots());
assertEquals(4, HostnameResolver.parseLinux(toBufferedReader(" options ndots: 4")).getNdots());
assertEquals(4, HostnameResolver.parseLinux(toBufferedReader("\toptions ndots: 4")).getNdots());
assertEquals(4, HostnameResolver.parseLinux(toBufferedReader("\foptions ndots: 4")).getNdots());
assertEquals(4, HostnameResolver.parseLinux(toBufferedReader("\n options ndots: 4")).getNdots());

assertEquals(4, HostnameResolver.parseLinux(toBufferedReader("options\tndots: 4")).getNdots());
assertEquals(4, HostnameResolver.parseLinux(toBufferedReader("options\fndots: 4")).getNdots());
assertEquals(4, HostnameResolver.parseLinux(toBufferedReader("options ndots: 4")).getNdots());
assertEquals(-1, HostnameResolver.parseLinux(toBufferedReader("options\nndots: 4")).getNdots());

assertEquals(4, HostnameResolver.parseLinux(toBufferedReader("options ndots:4")).getNdots());
assertEquals(4, HostnameResolver.parseLinux(toBufferedReader("options ndots:\t4")).getNdots());
assertEquals(4, HostnameResolver.parseLinux(toBufferedReader("options ndots: 4")).getNdots());
assertEquals(-1, HostnameResolver.parseLinux(toBufferedReader("options ndots:\n4")).getNdots());

assertEquals(4, HostnameResolver.parseLinux(toBufferedReader("options ndots:4 ")).getNdots());
assertEquals(4, HostnameResolver.parseLinux(toBufferedReader("options ndots:4\t")).getNdots());
assertEquals(4, HostnameResolver.parseLinux(toBufferedReader("options ndots:4\f")).getNdots());
assertEquals(4, HostnameResolver.parseLinux(toBufferedReader("options ndots:4\n")).getNdots());
assertEquals(4, HostnameResolver.parseLinux(toBufferedReader("options ndots:4\r")).getNdots());
assertEquals(-1, HostnameResolver.parseLinux(toBufferedReader("options ndots:4_")).getNdots());

assertEquals(2, HostnameResolver.parseLinux(toBufferedReader("options ndots:4\noptions ndots:2")).getNdots());
assertEquals(4, HostnameResolver.parseLinux(toBufferedReader("options ndots:4 debug")).getNdots());
assertEquals(4, HostnameResolver.parseLinux(toBufferedReader("options debug ndots:4")).getNdots());

assertEquals(false, HostnameResolver.parseLinux(toBufferedReader("options")).isRotate());
assertEquals(true, HostnameResolver.parseLinux(toBufferedReader("options rotate")).isRotate());
assertEquals(true, HostnameResolver.parseLinux(toBufferedReader("options rotate\n")).isRotate());
assertEquals(false, HostnameResolver.parseLinux(toBufferedReader("options\nrotate")).isRotate());
}

private static BufferedReader toBufferedReader(String s) {
return new BufferedReader(new StringReader(s));
}

@Test
Expand Down

0 comments on commit 534e14e

Please sign in to comment.