View Javadoc
1   /*
2    * Copyright 2024 The Netty Project
3    *
4    * The Netty Project licenses this file to you under the Apache License,
5    * version 2.0 (the "License"); you may not use this file except in compliance
6    * with the License. You may obtain a copy of the License at:
7    *
8    *   https://www.apache.org/licenses/LICENSE-2.0
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12   * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13   * License for the specific language governing permissions and limitations
14   * under the License.
15   */
16  package io.netty.resolver.dns;
17  
18  import io.netty.util.internal.BoundedInputStream;
19  
20  import java.io.BufferedReader;
21  import java.io.FileInputStream;
22  import java.io.IOException;
23  import java.io.InputStreamReader;
24  import java.net.InetSocketAddress;
25  import java.util.ArrayList;
26  import java.util.Collections;
27  import java.util.List;
28  
29  /**
30   * Looks up the {@code nameserver}s from the {@code /etc/resolv.conf} file, intended for Linux and macOS.
31   */
32  final class ResolvConf {
33      private final List<InetSocketAddress> nameservers;
34  
35      /**
36       * Reads from the given reader and extracts the {@code nameserver}s using the syntax of the
37       * {@code /etc/resolv.conf} file, see {@code man resolv.conf}.
38       *
39       * @param reader contents of {@code resolv.conf} are read from this {@link BufferedReader},
40       *              up to the caller to close it
41       */
42      static ResolvConf fromReader(BufferedReader reader) throws IOException {
43          return new ResolvConf(reader);
44      }
45  
46      /**
47       * Reads the given file and extracts the {@code nameserver}s using the syntax of the
48       * {@code /etc/resolv.conf} file, see {@code man resolv.conf}.
49       */
50      static ResolvConf fromFile(String file) throws IOException {
51          // Use 1 MB to be a bit conservative
52          try (BufferedReader reader = new BufferedReader(new InputStreamReader(
53                  new BoundedInputStream(new FileInputStream(file), 1024 * 1024)))) {
54              return fromReader(reader);
55          }
56      }
57  
58      /**
59       * Returns the {@code nameserver}s from the {@code /etc/resolv.conf} file. The file is only read once
60       * during the lifetime of this class.
61       */
62      static ResolvConf system() {
63          ResolvConf resolvConv = ResolvConfLazy.machineResolvConf;
64          if (resolvConv != null) {
65              return resolvConv;
66          }
67          throw new IllegalStateException("/etc/resolv.conf could not be read");
68      }
69  
70      private ResolvConf(BufferedReader reader) throws IOException {
71          List<InetSocketAddress> nameservers = new ArrayList<>();
72          String ln;
73          while ((ln = reader.readLine()) != null) {
74              ln = ln.trim();
75              if (ln.isEmpty()) {
76                  continue;
77              }
78  
79              if (ln.startsWith("nameserver")) {
80                  ln = ln.substring("nameserver".length());
81                  int cIndex = ln.indexOf('#');
82                  if (cIndex != -1) {
83                      ln = ln.substring(0, cIndex);
84                  }
85                  ln = ln.trim();
86                  if (ln.isEmpty()) {
87                      continue;
88                  }
89                  nameservers.add(new InetSocketAddress(ln, 53));
90              }
91          }
92          this.nameservers = Collections.unmodifiableList(nameservers);
93      }
94  
95      List<InetSocketAddress> getNameservers() {
96          return nameservers;
97      }
98  
99      private static final class ResolvConfLazy {
100         static final ResolvConf machineResolvConf;
101 
102         static {
103             ResolvConf resolvConf;
104             try {
105                 resolvConf = ResolvConf.fromFile("/etc/resolv.conf");
106             } catch (IOException | SecurityException e) {
107                 resolvConf = null;
108             }
109             machineResolvConf = resolvConf;
110         }
111     }
112 }