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 14, 2025
1 parent 56a122a commit d96a346
Show file tree
Hide file tree
Showing 2 changed files with 127 additions and 66 deletions.
122 changes: 91 additions & 31 deletions vertx-core/src/main/java/io/vertx/core/impl/HostnameResolver.java
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -129,18 +120,65 @@ public Future<Void> 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<L> implements EndpointResolver<SocketAddress, SocketAddress, L, L> {
Expand Down Expand Up @@ -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;
}
}
}
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,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
Expand Down

0 comments on commit d96a346

Please sign in to comment.