/*
* Copyright 2015 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.
*/
package io.netty.resolver;
import io.netty.util.
NetUtil;
import io.netty.util.internal.
PlatformDependent;
import io.netty.util.internal.
UnstableApi;
import io.netty.util.internal.logging.
InternalLogger;
import io.netty.util.internal.logging.
InternalLoggerFactory;
import java.io.
BufferedReader;
import java.io.
File;
import java.io.
FileReader;
import java.io.
IOException;
import java.io.
Reader;
import java.net.
Inet4Address;
import java.net.
Inet6Address;
import java.net.
InetAddress;
import java.util.
ArrayList;
import java.util.
List;
import java.util.
Locale;
import java.util.
HashMap;
import java.util.
Map;
import java.util.regex.
Pattern;
import static io.netty.util.internal.
ObjectUtil.*;
/**
* A parser for hosts files.
*/
@
UnstableApi
public final class
HostsFileParser {
private static final
String WINDOWS_DEFAULT_SYSTEM_ROOT = "C:\\Windows";
private static final
String WINDOWS_HOSTS_FILE_RELATIVE_PATH = "\\system32\\drivers\\etc\\hosts";
private static final
String X_PLATFORMS_HOSTS_FILE_PATH = "/etc/hosts";
private static final
Pattern WHITESPACES =
Pattern.
compile("[ \t]+");
private static final
InternalLogger logger =
InternalLoggerFactory.
getInstance(
HostsFileParser.class);
private static
File locateHostsFile() {
File hostsFile;
if (
PlatformDependent.
isWindows()) {
hostsFile = new
File(
System.
getenv("SystemRoot") +
WINDOWS_HOSTS_FILE_RELATIVE_PATH);
if (!
hostsFile.
exists()) {
hostsFile = new
File(
WINDOWS_DEFAULT_SYSTEM_ROOT +
WINDOWS_HOSTS_FILE_RELATIVE_PATH);
}
} else {
hostsFile = new
File(
X_PLATFORMS_HOSTS_FILE_PATH);
}
return
hostsFile;
}
/**
* Parse hosts file at standard OS location.
*
* @return a {@link HostsFileEntries}
*/
public static
HostsFileEntries parseSilently() {
File hostsFile =
locateHostsFile();
try {
return
parse(
hostsFile);
} catch (
IOException e) {
if (
logger.
isWarnEnabled()) {
logger.
warn("Failed to load and parse hosts file at " +
hostsFile.
getPath(),
e);
}
return
HostsFileEntries.
EMPTY;
}
}
/**
* Parse hosts file at standard OS location.
*
* @return a {@link HostsFileEntries}
* @throws IOException file could not be read
*/
public static
HostsFileEntries parse() throws
IOException {
return
parse(
locateHostsFile());
}
/**
* Parse a hosts file.
*
* @param file the file to be parsed
* @return a {@link HostsFileEntries}
* @throws IOException file could not be read
*/
public static
HostsFileEntries parse(
File file) throws
IOException {
checkNotNull(
file, "file");
if (
file.
exists() &&
file.
isFile()) {
return
parse(new
BufferedReader(new
FileReader(
file)));
} else {
return
HostsFileEntries.
EMPTY;
}
}
/**
* Parse a reader of hosts file format.
*
* @param reader the file to be parsed
* @return a {@link HostsFileEntries}
* @throws IOException file could not be read
*/
public static
HostsFileEntries parse(
Reader reader) throws
IOException {
checkNotNull(
reader, "reader");
BufferedReader buff = new
BufferedReader(
reader);
try {
Map<
String,
Inet4Address>
ipv4Entries = new
HashMap<
String,
Inet4Address>();
Map<
String,
Inet6Address>
ipv6Entries = new
HashMap<
String,
Inet6Address>();
String line;
while ((
line =
buff.
readLine()) != null) {
// remove comment
int
commentPosition =
line.
indexOf('#');
if (
commentPosition != -1) {
line =
line.
substring(0,
commentPosition);
}
// skip empty lines
line =
line.
trim();
if (
line.
isEmpty()) {
continue;
}
// split
List<
String>
lineParts = new
ArrayList<
String>();
for (
String s:
WHITESPACES.
split(
line)) {
if (!
s.
isEmpty()) {
lineParts.
add(
s);
}
}
// a valid line should be [IP, hostname, alias*]
if (
lineParts.
size() < 2) {
// skip invalid line
continue;
}
byte[]
ipBytes =
NetUtil.
createByteArrayFromIpAddressString(
lineParts.
get(0));
if (
ipBytes == null) {
// skip invalid IP
continue;
}
// loop over hostname and aliases
for (int
i = 1;
i <
lineParts.
size();
i ++) {
String hostname =
lineParts.
get(
i);
String hostnameLower =
hostname.
toLowerCase(
Locale.
ENGLISH);
InetAddress address =
InetAddress.
getByAddress(
hostname,
ipBytes);
if (
address instanceof
Inet4Address) {
Inet4Address previous =
ipv4Entries.
put(
hostnameLower, (
Inet4Address)
address);
if (
previous != null) {
// restore, we want to keep the first entry
ipv4Entries.
put(
hostnameLower,
previous);
}
} else {
Inet6Address previous =
ipv6Entries.
put(
hostnameLower, (
Inet6Address)
address);
if (
previous != null) {
// restore, we want to keep the first entry
ipv6Entries.
put(
hostnameLower,
previous);
}
}
}
}
return
ipv4Entries.
isEmpty() &&
ipv6Entries.
isEmpty() ?
HostsFileEntries.
EMPTY :
new
HostsFileEntries(
ipv4Entries,
ipv6Entries);
} finally {
try {
buff.
close();
} catch (
IOException e) {
logger.
warn("Failed to close a reader",
e);
}
}
}
/**
* Can't be instantiated.
*/
private
HostsFileParser() {
}
}