/* * 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) { 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() { } }