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 try (BufferedReader reader = new BufferedReader(
193 new InputStreamReader(new FileInputStream(file), charset))) {
194 HostsFileEntriesProvider entries = parse(reader);
195 if (entries != HostsFileEntriesProvider.EMPTY) {
196 return entries;
197 }
198 }
199 }
200 }
201 return HostsFileEntriesProvider.EMPTY;
202 }
203
204 @Override
205 public HostsFileEntriesProvider parse(Reader reader) throws IOException {
206 checkNotNull(reader, "reader");
207 try (BufferedReader buff = new BufferedReader(reader)) {
208 Map<String, List<InetAddress>> ipv4Entries = new HashMap<>();
209 Map<String, List<InetAddress>> ipv6Entries = new HashMap<>();
210 String line;
211 while ((line = buff.readLine()) != null) {
212
213 int commentPosition = line.indexOf('#');
214 if (commentPosition != -1) {
215 line = line.substring(0, commentPosition);
216 }
217
218 line = line.trim();
219 if (line.isEmpty()) {
220 continue;
221 }
222
223
224 List<String> lineParts = new ArrayList<>();
225 for (String s : WHITESPACES.split(line)) {
226 if (!s.isEmpty()) {
227 lineParts.add(s);
228 }
229 }
230
231
232 if (lineParts.size() < 2) {
233
234 continue;
235 }
236
237 byte[] ipBytes = NetUtil.createByteArrayFromIpAddressString(lineParts.get(0));
238
239 if (ipBytes == null) {
240
241 continue;
242 }
243
244
245 for (int i = 1; i < lineParts.size(); i++) {
246 String hostname = lineParts.get(i);
247 String hostnameLower = hostname.toLowerCase(Locale.ENGLISH);
248 InetAddress address = InetAddress.getByAddress(hostname, ipBytes);
249 List<InetAddress> addresses;
250 if (address instanceof Inet4Address) {
251 addresses = ipv4Entries.get(hostnameLower);
252 if (addresses == null) {
253 addresses = new ArrayList<>();
254 ipv4Entries.put(hostnameLower, addresses);
255 }
256 } else {
257 addresses = ipv6Entries.get(hostnameLower);
258 if (addresses == null) {
259 addresses = new ArrayList<>();
260 ipv6Entries.put(hostnameLower, addresses);
261 }
262 }
263 addresses.add(address);
264 }
265 }
266 return ipv4Entries.isEmpty() && ipv6Entries.isEmpty() ?
267 HostsFileEntriesProvider.EMPTY :
268 new HostsFileEntriesProvider(ipv4Entries, ipv6Entries);
269 }
270 }
271
272 @Override
273 public HostsFileEntriesProvider parseSilently() {
274 return parseSilently(locateHostsFile(), Charset.defaultCharset());
275 }
276
277 @Override
278 public HostsFileEntriesProvider parseSilently(Charset... charsets) {
279 return parseSilently(locateHostsFile(), charsets);
280 }
281
282 @Override
283 public HostsFileEntriesProvider parseSilently(File file, Charset... charsets) {
284 try {
285 return parse(file, charsets);
286 } catch (IOException e) {
287 if (logger.isWarnEnabled()) {
288 logger.warn("Failed to load and parse hosts file at " + file.getPath(), e);
289 }
290 return HostsFileEntriesProvider.EMPTY;
291 }
292 }
293
294 private static File locateHostsFile() {
295 File hostsFile;
296 if (PlatformDependent.isWindows()) {
297 hostsFile = new File(System.getenv("SystemRoot") + WINDOWS_HOSTS_FILE_RELATIVE_PATH);
298 if (!hostsFile.exists()) {
299 hostsFile = new File(WINDOWS_DEFAULT_SYSTEM_ROOT + WINDOWS_HOSTS_FILE_RELATIVE_PATH);
300 }
301 } else {
302 hostsFile = new File(X_PLATFORMS_HOSTS_FILE_PATH);
303 }
304 return hostsFile;
305 }
306 }
307 }