1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 package io.netty.resolver;
17
18 import io.netty.util.NetUtil;
19 import io.netty.util.internal.PlatformDependent;
20 import io.netty.util.internal.logging.InternalLogger;
21 import io.netty.util.internal.logging.InternalLoggerFactory;
22
23 import java.io.BufferedReader;
24 import java.io.File;
25 import java.io.FileInputStream;
26 import java.io.IOException;
27 import java.io.InputStreamReader;
28 import java.io.Reader;
29 import java.net.Inet4Address;
30 import java.net.InetAddress;
31 import java.nio.charset.Charset;
32 import java.util.ArrayList;
33 import java.util.Collections;
34 import java.util.HashMap;
35 import java.util.List;
36 import java.util.Locale;
37 import java.util.Map;
38 import java.util.regex.Pattern;
39
40 import static io.netty.util.internal.ObjectUtil.checkNotNull;
41
42
43
44
45 public final class HostsFileEntriesProvider {
46
47 public interface Parser {
48
49
50
51
52
53
54
55 HostsFileEntriesProvider parse() throws IOException;
56
57
58
59
60
61
62
63
64
65 HostsFileEntriesProvider parse(Charset... charsets) throws IOException;
66
67
68
69
70
71
72
73
74
75
76
77
78 HostsFileEntriesProvider parse(File file, Charset... charsets) throws IOException;
79
80
81
82
83
84
85
86 HostsFileEntriesProvider parse(Reader reader) throws IOException;
87
88
89
90
91
92
93 HostsFileEntriesProvider parseSilently();
94
95
96
97
98
99
100
101
102 HostsFileEntriesProvider parseSilently(Charset... charsets);
103
104
105
106
107
108
109
110
111
112
113
114 HostsFileEntriesProvider parseSilently(File file, Charset... charsets);
115 }
116
117
118
119
120
121
122 public static Parser parser() {
123 return ParserImpl.INSTANCE;
124 }
125
126 static final HostsFileEntriesProvider EMPTY =
127 new HostsFileEntriesProvider(
128 Collections.<String, List<InetAddress>>emptyMap(),
129 Collections.<String, List<InetAddress>>emptyMap());
130
131 private final Map<String, List<InetAddress>> ipv4Entries;
132 private final Map<String, List<InetAddress>> ipv6Entries;
133
134 HostsFileEntriesProvider(Map<String, List<InetAddress>> ipv4Entries, Map<String, List<InetAddress>> ipv6Entries) {
135 this.ipv4Entries = Collections.unmodifiableMap(new HashMap<String, List<InetAddress>>(ipv4Entries));
136 this.ipv6Entries = Collections.unmodifiableMap(new HashMap<String, List<InetAddress>>(ipv6Entries));
137 }
138
139
140
141
142
143
144 public Map<String, List<InetAddress>> ipv4Entries() {
145 return ipv4Entries;
146 }
147
148
149
150
151
152
153 public Map<String, List<InetAddress>> ipv6Entries() {
154 return ipv6Entries;
155 }
156
157 private static final class ParserImpl implements Parser {
158
159 private static final String WINDOWS_DEFAULT_SYSTEM_ROOT = "C:\\Windows";
160 private static final String WINDOWS_HOSTS_FILE_RELATIVE_PATH = "\\system32\\drivers\\etc\\hosts";
161 private static final String X_PLATFORMS_HOSTS_FILE_PATH = "/etc/hosts";
162
163 private static final Pattern WHITESPACES = Pattern.compile("[ \t]+");
164
165 private static final InternalLogger logger = InternalLoggerFactory.getInstance(Parser.class);
166
167 static final ParserImpl INSTANCE = new ParserImpl();
168
169 private ParserImpl() {
170
171 }
172
173 @Override
174 public HostsFileEntriesProvider parse() throws IOException {
175 return parse(locateHostsFile(), Charset.defaultCharset());
176 }
177
178 @Override
179 public HostsFileEntriesProvider parse(Charset... charsets) throws IOException {
180 return parse(locateHostsFile(), charsets);
181 }
182
183 @Override
184 public HostsFileEntriesProvider parse(File file, Charset... charsets) throws IOException {
185 checkNotNull(file, "file");
186 checkNotNull(charsets, "charsets");
187 if (charsets.length == 0) {
188 charsets = new Charset[]{Charset.defaultCharset()};
189 }
190 if (file.exists() && file.isFile()) {
191 for (Charset charset : charsets) {
192 BufferedReader reader = new BufferedReader(
193 new InputStreamReader(new FileInputStream(file), charset));
194 try {
195 HostsFileEntriesProvider entries = parse(reader);
196 if (entries != HostsFileEntriesProvider.EMPTY) {
197 return entries;
198 }
199 } finally {
200 reader.close();
201 }
202 }
203 }
204 return HostsFileEntriesProvider.EMPTY;
205 }
206
207 @Override
208 public HostsFileEntriesProvider parse(Reader reader) throws IOException {
209 checkNotNull(reader, "reader");
210 BufferedReader buff = new BufferedReader(reader);
211 try {
212 Map<String, List<InetAddress>> ipv4Entries = new HashMap<String, List<InetAddress>>();
213 Map<String, List<InetAddress>> ipv6Entries = new HashMap<String, List<InetAddress>>();
214 String line;
215 while ((line = buff.readLine()) != null) {
216
217 int commentPosition = line.indexOf('#');
218 if (commentPosition != -1) {
219 line = line.substring(0, commentPosition);
220 }
221
222 line = line.trim();
223 if (line.isEmpty()) {
224 continue;
225 }
226
227
228 List<String> lineParts = new ArrayList<String>();
229 for (String s : WHITESPACES.split(line)) {
230 if (!s.isEmpty()) {
231 lineParts.add(s);
232 }
233 }
234
235
236 if (lineParts.size() < 2) {
237
238 continue;
239 }
240
241 byte[] ipBytes = NetUtil.createByteArrayFromIpAddressString(lineParts.get(0));
242
243 if (ipBytes == null) {
244
245 continue;
246 }
247
248
249 for (int i = 1; i < lineParts.size(); i++) {
250 String hostname = lineParts.get(i);
251 String hostnameLower = hostname.toLowerCase(Locale.ENGLISH);
252 InetAddress address = InetAddress.getByAddress(hostname, ipBytes);
253 List<InetAddress> addresses;
254 if (address instanceof Inet4Address) {
255 addresses = ipv4Entries.get(hostnameLower);
256 if (addresses == null) {
257 addresses = new ArrayList<InetAddress>();
258 ipv4Entries.put(hostnameLower, addresses);
259 }
260 } else {
261 addresses = ipv6Entries.get(hostnameLower);
262 if (addresses == null) {
263 addresses = new ArrayList<InetAddress>();
264 ipv6Entries.put(hostnameLower, addresses);
265 }
266 }
267 addresses.add(address);
268 }
269 }
270 return ipv4Entries.isEmpty() && ipv6Entries.isEmpty() ?
271 HostsFileEntriesProvider.EMPTY :
272 new HostsFileEntriesProvider(ipv4Entries, ipv6Entries);
273 } finally {
274 try {
275 buff.close();
276 } catch (IOException e) {
277 logger.warn("Failed to close a reader", e);
278 }
279 }
280 }
281
282 @Override
283 public HostsFileEntriesProvider parseSilently() {
284 return parseSilently(locateHostsFile(), Charset.defaultCharset());
285 }
286
287 @Override
288 public HostsFileEntriesProvider parseSilently(Charset... charsets) {
289 return parseSilently(locateHostsFile(), charsets);
290 }
291
292 @Override
293 public HostsFileEntriesProvider parseSilently(File file, Charset... charsets) {
294 try {
295 return parse(file, charsets);
296 } catch (IOException e) {
297 if (logger.isWarnEnabled()) {
298 logger.warn("Failed to load and parse hosts file at " + file.getPath(), e);
299 }
300 return HostsFileEntriesProvider.EMPTY;
301 }
302 }
303
304 private static File locateHostsFile() {
305 File hostsFile;
306 if (PlatformDependent.isWindows()) {
307 hostsFile = new File(System.getenv("SystemRoot") + WINDOWS_HOSTS_FILE_RELATIVE_PATH);
308 if (!hostsFile.exists()) {
309 hostsFile = new File(WINDOWS_DEFAULT_SYSTEM_ROOT + WINDOWS_HOSTS_FILE_RELATIVE_PATH);
310 }
311 } else {
312 hostsFile = new File(X_PLATFORMS_HOSTS_FILE_PATH);
313 }
314 return hostsFile;
315 }
316 }
317 }