001/*
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements. See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache license, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License. You may obtain a copy of the License at
008 *
009 *      http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the license for the specific language governing permissions and
015 * limitations under the license.
016 */
017package org.apache.logging.log4j.core.util;
018
019import java.io.File;
020import java.net.Inet4Address;
021import java.net.Inet6Address;
022import java.net.InetAddress;
023import java.net.MalformedURLException;
024import java.net.NetworkInterface;
025import java.net.SocketException;
026import java.net.URI;
027import java.net.URISyntaxException;
028import java.net.URL;
029import java.net.UnknownHostException;
030import java.util.ArrayList;
031import java.util.Arrays;
032import java.util.Enumeration;
033import java.util.List;
034
035import org.apache.logging.log4j.Logger;
036import org.apache.logging.log4j.status.StatusLogger;
037import org.apache.logging.log4j.util.Strings;
038
039/**
040 * Networking-related convenience methods.
041 */
042public final class NetUtils {
043
044    private static final Logger LOGGER = StatusLogger.getLogger();
045    private static final String UNKNOWN_LOCALHOST = "UNKNOWN_LOCALHOST";
046
047    private NetUtils() {
048        // empty
049    }
050
051    /**
052     * This method gets the network name of the machine we are running on. Returns "UNKNOWN_LOCALHOST" in the unlikely
053     * case where the host name cannot be found.
054     *
055     * @return String the name of the local host
056     */
057    public static String getLocalHostname() {
058        try {
059            final InetAddress addr = InetAddress.getLocalHost();
060            return addr == null ? UNKNOWN_LOCALHOST : addr.getHostName();
061        } catch (final UnknownHostException uhe) {
062            try {
063                final Enumeration<NetworkInterface> interfaces = NetworkInterface.getNetworkInterfaces();
064                if (interfaces != null) {
065                    while (interfaces.hasMoreElements()) {
066                        final NetworkInterface nic = interfaces.nextElement();
067                        final Enumeration<InetAddress> addresses = nic.getInetAddresses();
068                        while (addresses.hasMoreElements()) {
069                            final InetAddress address = addresses.nextElement();
070                            if (!address.isLoopbackAddress()) {
071                                final String hostname = address.getHostName();
072                                if (hostname != null) {
073                                    return hostname;
074                                }
075                            }
076                        }
077                    }
078                }
079            } catch (final SocketException se) {
080                // ignore and log below.
081            }
082            LOGGER.error("Could not determine local host name", uhe);
083            return UNKNOWN_LOCALHOST;
084        }
085    }
086
087    /**
088     * Returns all the local host names and ip addresses.
089     * @return The local host names and ip addresses.
090     */
091    public static List<String> getLocalIps() {
092        List<String> localIps = new ArrayList<>();
093        localIps.add("localhost");
094        localIps.add("127.0.0.1");
095        try {
096            final InetAddress addr = Inet4Address.getLocalHost();
097            setHostName(addr, localIps);
098        } catch (final UnknownHostException ex) {
099            // Ignore this.
100        }
101        try {
102            final Enumeration<NetworkInterface> interfaces = NetworkInterface.getNetworkInterfaces();
103            if (interfaces != null) {
104                while (interfaces.hasMoreElements()) {
105                    final NetworkInterface nic = interfaces.nextElement();
106                    final Enumeration<InetAddress> addresses = nic.getInetAddresses();
107                    while (addresses.hasMoreElements()) {
108                        final InetAddress address = addresses.nextElement();
109                        setHostName(address, localIps);
110                    }
111                }
112            }
113        } catch (final SocketException se) {
114            // ignore.
115        }
116        return localIps;
117    }
118
119    private static void setHostName(InetAddress address, List<String> localIps) {
120        String[] parts = address.toString().split("\\s*/\\s*");
121        if (parts.length > 0) {
122            for (String part : parts) {
123                if (Strings.isNotBlank(part) && !localIps.contains(part)) {
124                    localIps.add(part);
125                }
126            }
127        }
128    }
129
130    /**
131     *  Returns the local network interface's MAC address if possible. The local network interface is defined here as
132     *  the {@link java.net.NetworkInterface} that is both up and not a loopback interface.
133     *
134     * @return the MAC address of the local network interface or {@code null} if no MAC address could be determined.
135     */
136    public static byte[] getMacAddress() {
137        byte[] mac = null;
138        try {
139            final InetAddress localHost = InetAddress.getLocalHost();
140            try {
141                final NetworkInterface localInterface = NetworkInterface.getByInetAddress(localHost);
142                if (isUpAndNotLoopback(localInterface)) {
143                    mac = localInterface.getHardwareAddress();
144                }
145                if (mac == null) {
146                    final Enumeration<NetworkInterface> networkInterfaces = NetworkInterface.getNetworkInterfaces();
147                    if (networkInterfaces != null) {
148                        while (networkInterfaces.hasMoreElements() && mac == null) {
149                            final NetworkInterface nic = networkInterfaces.nextElement();
150                            if (isUpAndNotLoopback(nic)) {
151                                mac = nic.getHardwareAddress();
152                            }
153                        }
154                    }
155                }
156            } catch (final SocketException e) {
157                LOGGER.catching(e);
158            }
159            if (ArrayUtils.isEmpty(mac) && localHost != null) {
160                // Emulate a MAC address with an IP v4 or v6
161                final byte[] address = localHost.getAddress();
162                // Take only 6 bytes if the address is an IPv6 otherwise will pad with two zero bytes
163                mac = Arrays.copyOf(address, 6);
164            }
165        } catch (final UnknownHostException ignored) {
166            // ignored
167        }
168        return mac;
169    }
170
171    /**
172     * Returns the mac address, if it is available, as a string with each byte separated by a ":" character.
173     * @return the mac address String or null.
174     */
175    public static String getMacAddressString() {
176        final byte[] macAddr = getMacAddress();
177        if (!ArrayUtils.isEmpty(macAddr)) {
178            StringBuilder sb = new StringBuilder(String.format("%02x", macAddr[0]));
179            for (int i = 1; i < macAddr.length; ++i) {
180                sb.append(":").append(String.format("%02x", macAddr[i]));
181            }
182            return sb.toString();
183
184        }
185        return null;
186    }
187
188    private static boolean isUpAndNotLoopback(final NetworkInterface ni) throws SocketException {
189        return ni != null && !ni.isLoopback() && ni.isUp();
190    }
191
192    /**
193     * Converts a URI string or file path to a URI object.
194     *
195     * @param path the URI string or path
196     * @return the URI object
197     */
198    public static URI toURI(final String path) {
199        try {
200            // Resolves absolute URI
201            return new URI(path);
202        } catch (final URISyntaxException e) {
203            // A file path or a Apache Commons VFS URL might contain blanks.
204            // A file path may start with a driver letter
205            try {
206                final URL url = new URL(path);
207                return new URI(url.getProtocol(), url.getHost(), url.getPath(), null);
208            } catch (MalformedURLException | URISyntaxException nestedEx) {
209                return new File(path).toURI();
210            }
211        }
212    }
213
214}