Skip to content

Commit c2d3b1b

Browse files
committed
Handle invalid/uninitialized file times
#400
1 parent c1478fa commit c2d3b1b

2 files changed

Lines changed: 52 additions & 7 deletions

File tree

src/main/java/org/xbill/DNS/hosts/HostsFileParser.java

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -46,8 +46,8 @@ public final class HostsFileParser {
4646
@SuppressWarnings("java:S3077")
4747
private volatile Map<String, InetAddress> hostsCache;
4848

49-
private Instant lastFileModificationCheckTime = Instant.MIN;
50-
private Instant lastFileReadTime = Instant.MIN;
49+
private Instant lastFileModificationCheckTime = null;
50+
private Instant lastFileReadTime = null;
5151
private boolean isEntireFileParsed;
5252
private boolean hostsFileWarningLogged = false;
5353
private long hostsFileSizeBytes;
@@ -254,15 +254,19 @@ private void validateCache() throws IOException {
254254
return;
255255
}
256256

257-
if (lastFileModificationCheckTime.plus(fileChangeCheckInterval).isBefore(clock.instant())) {
257+
if (hostsCache == null
258+
|| lastFileModificationCheckTime == null
259+
|| lastFileModificationCheckTime.plus(fileChangeCheckInterval).isBefore(clock.instant())) {
258260
log.debug("Checked for changes more than 5minutes ago, checking");
259261
// A filewatcher / inotify etc. would be nicer, but doesn't work. c.f. the write up at
260262
// https://blog.arkey.fr/2019/09/13/watchservice-and-bind-mount/
261263

262264
synchronized (this) {
263-
if (!lastFileModificationCheckTime
264-
.plus(fileChangeCheckInterval)
265-
.isBefore(clock.instant())) {
265+
if (hostsCache != null
266+
&& lastFileModificationCheckTime != null
267+
&& !lastFileModificationCheckTime
268+
.plus(fileChangeCheckInterval)
269+
.isBefore(clock.instant())) {
266270
log.debug("Never mind, check fulfilled in another thread");
267271
return;
268272
}
@@ -276,7 +280,7 @@ private void validateCache() throws IOException {
276280
private void readHostsFile() throws IOException {
277281
if (Files.exists(path)) {
278282
Instant fileTime = Files.getLastModifiedTime(path).toInstant();
279-
if (!lastFileReadTime.equals(fileTime)) {
283+
if (lastFileReadTime == null || !lastFileReadTime.equals(fileTime)) {
280284
createOrClearCache();
281285

282286
hostsFileSizeBytes = Files.size(path);

src/test/java/org/xbill/DNS/hosts/HostsFileParserTest.java

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,17 @@
11
// SPDX-License-Identifier: BSD-3-Clause
22
package org.xbill.DNS.hosts;
33

4+
import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
45
import static org.junit.jupiter.api.Assertions.assertEquals;
56
import static org.junit.jupiter.api.Assertions.assertNotEquals;
67
import static org.junit.jupiter.api.Assertions.assertThrows;
78
import static org.junit.jupiter.api.Assertions.assertTrue;
9+
import static org.mockito.ArgumentMatchers.any;
10+
import static org.mockito.ArgumentMatchers.eq;
11+
import static org.mockito.Mockito.doAnswer;
12+
import static org.mockito.Mockito.doReturn;
813
import static org.mockito.Mockito.mock;
14+
import static org.mockito.Mockito.spy;
915
import static org.mockito.Mockito.when;
1016

1117
import java.io.BufferedWriter;
@@ -14,16 +20,21 @@
1420
import java.net.URISyntaxException;
1521
import java.net.UnknownHostException;
1622
import java.nio.charset.StandardCharsets;
23+
import java.nio.file.FileSystem;
1724
import java.nio.file.Files;
1825
import java.nio.file.Path;
1926
import java.nio.file.Paths;
2027
import java.nio.file.StandardCopyOption;
2128
import java.nio.file.StandardOpenOption;
29+
import java.nio.file.attribute.BasicFileAttributes;
2230
import java.nio.file.attribute.FileTime;
31+
import java.nio.file.spi.FileSystemProvider;
2332
import java.time.Clock;
2433
import java.time.Duration;
2534
import java.time.Instant;
35+
import java.time.ZoneId;
2636
import java.util.Optional;
37+
import org.apache.commons.io.file.spi.FileSystemProviders;
2738
import org.junit.jupiter.api.BeforeAll;
2839
import org.junit.jupiter.api.Test;
2940
import org.junit.jupiter.api.io.TempDir;
@@ -52,6 +63,36 @@ void testArguments() {
5263
assertThrows(IllegalArgumentException.class, () -> new HostsFileParser(tempDir));
5364
}
5465

66+
@Test
67+
void handleNoValidClock() {
68+
HostsFileParser p = new HostsFileParser(hostsFileWindows);
69+
p.setClock(Clock.fixed(Instant.MIN, ZoneId.systemDefault()));
70+
assertDoesNotThrow(() -> p.getAddressForHost(Name.root, Type.A));
71+
}
72+
73+
@Test
74+
void handleNoModificationTime() throws IOException {
75+
FileSystemProvider spiedFsp = spy(FileSystemProviders.getFileSystemProvider(hostsFileWindows));
76+
doAnswer(
77+
a -> {
78+
BasicFileAttributes attributes = spy((BasicFileAttributes) a.callRealMethod());
79+
when(attributes.lastModifiedTime()).thenReturn(FileTime.from(Instant.MIN));
80+
return attributes;
81+
})
82+
.when(spiedFsp)
83+
.readAttributes(any(), eq(BasicFileAttributes.class));
84+
Path spiedPath = spy(spiedFsp.getPath(hostsFileWindows.toUri()));
85+
when(spiedPath.getFileSystem())
86+
.thenAnswer(
87+
a -> {
88+
FileSystem spiedFs = spy((FileSystem) a.callRealMethod());
89+
doReturn(spiedFsp).when(spiedFs).provider();
90+
return spiedFs;
91+
});
92+
HostsFileParser p = new HostsFileParser(spiedPath);
93+
assertDoesNotThrow(() -> p.getAddressForHost(Name.root, Type.A));
94+
}
95+
5596
@Test
5697
void testLookupType() {
5798
HostsFileParser hostsFileParser = new HostsFileParser(hostsFileWindows);

0 commit comments

Comments
 (0)