View Javadoc
1   /*
2    * Copyright 2020 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.util;
17  
18  import io.netty.util.internal.PlatformDependent;
19  import io.netty.util.internal.SocketUtils;
20  import io.netty.util.internal.logging.InternalLogger;
21  import io.netty.util.internal.logging.InternalLoggerFactory;
22  
23  import java.net.Inet4Address;
24  import java.net.Inet6Address;
25  import java.net.InetAddress;
26  import java.net.NetworkInterface;
27  import java.net.SocketException;
28  import java.util.ArrayList;
29  import java.util.Collection;
30  import java.util.Collections;
31  import java.util.Enumeration;
32  import java.util.List;
33  
34  final class NetUtilInitializations {
35      /**
36       * The logger being used by this class
37       */
38      private static final InternalLogger logger = InternalLoggerFactory.getInstance(NetUtilInitializations.class);
39  
40      private NetUtilInitializations() {
41      }
42  
43      static Inet4Address createLocalhost4() {
44          byte[] LOCALHOST4_BYTES = {127, 0, 0, 1};
45  
46          Inet4Address localhost4 = null;
47          try {
48              localhost4 = (Inet4Address) InetAddress.getByAddress("localhost", LOCALHOST4_BYTES);
49          } catch (Exception e) {
50              // We should not get here as long as the length of the address is correct.
51              PlatformDependent.throwException(e);
52          }
53  
54          return localhost4;
55      }
56  
57      static Inet6Address createLocalhost6() {
58          byte[] LOCALHOST6_BYTES = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1};
59  
60          Inet6Address localhost6 = null;
61          try {
62              localhost6 = (Inet6Address) InetAddress.getByAddress("localhost", LOCALHOST6_BYTES);
63          } catch (Exception e) {
64              // We should not get here as long as the length of the address is correct.
65              PlatformDependent.throwException(e);
66          }
67  
68          return localhost6;
69      }
70  
71      static Collection<NetworkInterface> networkInterfaces() {
72          List<NetworkInterface> networkInterfaces = new ArrayList<NetworkInterface>();
73          try {
74              Enumeration<NetworkInterface> interfaces = NetworkInterface.getNetworkInterfaces();
75              if (interfaces != null) {
76                  while (interfaces.hasMoreElements()) {
77                      networkInterfaces.add(interfaces.nextElement());
78                  }
79              }
80          } catch (SocketException e) {
81              logger.warn("Failed to retrieve the list of available network interfaces", e);
82          } catch (NullPointerException e) {
83              if (!PlatformDependent.isAndroid()) {
84                  throw e;
85              }
86              // Might happen on earlier version of Android.
87              // See https://developer.android.com/reference/java/net/NetworkInterface#getNetworkInterfaces()
88          }
89          return Collections.unmodifiableList(networkInterfaces);
90      }
91  
92      static NetworkIfaceAndInetAddress determineLoopback(
93              Collection<NetworkInterface> networkInterfaces, Inet4Address localhost4, Inet6Address localhost6) {
94          // Retrieve the list of available network interfaces.
95          List<NetworkInterface> ifaces = new ArrayList<NetworkInterface>();
96          for (NetworkInterface iface: networkInterfaces) {
97              // Use the interface with proper INET addresses only.
98              if (SocketUtils.addressesFromNetworkInterface(iface).hasMoreElements()) {
99                  ifaces.add(iface);
100             }
101         }
102 
103         // Find the first loopback interface available from its INET address (127.0.0.1 or ::1)
104         // Note that we do not use NetworkInterface.isLoopback() in the first place because it takes long time
105         // on a certain environment. (e.g. Windows with -Djava.net.preferIPv4Stack=true)
106         NetworkInterface loopbackIface = null;
107         InetAddress loopbackAddr = null;
108         loop: for (NetworkInterface iface: ifaces) {
109             for (Enumeration<InetAddress> i = SocketUtils.addressesFromNetworkInterface(iface); i.hasMoreElements();) {
110                 InetAddress addr = i.nextElement();
111                 if (addr.isLoopbackAddress()) {
112                     // Found
113                     loopbackIface = iface;
114                     loopbackAddr = addr;
115                     break loop;
116                 }
117             }
118         }
119 
120         // If failed to find the loopback interface from its INET address, fall back to isLoopback().
121         if (loopbackIface == null) {
122             try {
123                 for (NetworkInterface iface: ifaces) {
124                     if (iface.isLoopback()) {
125                         Enumeration<InetAddress> i = SocketUtils.addressesFromNetworkInterface(iface);
126                         if (i.hasMoreElements()) {
127                             // Found the one with INET address.
128                             loopbackIface = iface;
129                             loopbackAddr = i.nextElement();
130                             break;
131                         }
132                     }
133                 }
134 
135                 if (loopbackIface == null) {
136                     logger.warn("Failed to find the loopback interface");
137                 }
138             } catch (SocketException e) {
139                 logger.warn("Failed to find the loopback interface", e);
140             }
141         }
142 
143         if (loopbackIface != null) {
144             // Found the loopback interface with an INET address.
145             logger.debug(
146                     "Loopback interface: {} ({}, {})",
147                     loopbackIface.getName(), loopbackIface.getDisplayName(), loopbackAddr.getHostAddress());
148         } else {
149             // Could not find the loopback interface, but we can't leave LOCALHOST as null.
150             // Use LOCALHOST6 or LOCALHOST4, preferably the IPv6 one.
151             if (loopbackAddr == null) {
152                 try {
153                     if (NetworkInterface.getByInetAddress(localhost6) != null) {
154                         logger.debug("Using hard-coded IPv6 localhost address: {}", localhost6);
155                         loopbackAddr = localhost6;
156                     }
157                 } catch (Exception e) {
158                     // Ignore
159                 } finally {
160                     if (loopbackAddr == null) {
161                         logger.debug("Using hard-coded IPv4 localhost address: {}", localhost4);
162                         loopbackAddr = localhost4;
163                     }
164                 }
165             }
166         }
167 
168         return new NetworkIfaceAndInetAddress(loopbackIface, loopbackAddr);
169     }
170 
171     static final class NetworkIfaceAndInetAddress {
172         private final NetworkInterface iface;
173         private final InetAddress address;
174 
175         NetworkIfaceAndInetAddress(NetworkInterface iface, InetAddress address) {
176             this.iface = iface;
177             this.address = address;
178         }
179 
180         public NetworkInterface iface() {
181             return iface;
182         }
183 
184         public InetAddress address() {
185             return address;
186         }
187     }
188 }