diff --git a/build.gradle b/build.gradle index 4505fa3db..5cf20406e 100644 --- a/build.gradle +++ b/build.gradle @@ -9,14 +9,14 @@ allprojects { apply plugin: "com.github.spotbugs" group 'com.velocitypowered' - version '3.1.1-SNAPSHOT' + version '3.1.1' ext { // dependency versions adventureVersion = '4.9.3' junitVersion = '5.7.0' slf4jVersion = '1.7.30' - log4jVersion = '2.14.1' + log4jVersion = '2.15.0' nettyVersion = '4.1.69.Final' guavaVersion = '25.1-jre' checkerFrameworkVersion = '3.6.1' diff --git a/proxy/build.gradle b/proxy/build.gradle index 38d577d34..fd38a2004 100644 --- a/proxy/build.gradle +++ b/proxy/build.gradle @@ -37,8 +37,6 @@ shadowJar { tasks.withType(Checkstyle) { exclude('**/com/velocitypowered/proxy/protocol/packet/*.java') - // TODO: Remove during l4j2 2.15 update - exclude('**/org/apache/logging/log4j/core/**/*.java') } dependencies { diff --git a/proxy/src/main/java/org/apache/logging/log4j/core/appender/mom/JmsAppender.java b/proxy/src/main/java/org/apache/logging/log4j/core/appender/mom/JmsAppender.java deleted file mode 100644 index d3f0d49b8..000000000 --- a/proxy/src/main/java/org/apache/logging/log4j/core/appender/mom/JmsAppender.java +++ /dev/null @@ -1,310 +0,0 @@ -/* - * Copyright (C) 2018 Velocity Contributors - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache license, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the license for the specific language governing permissions and - * limitations under the license. - */ -package org.apache.logging.log4j.core.appender.mom; - -import java.io.Serializable; -import java.util.Properties; -import java.util.concurrent.TimeUnit; - -import org.apache.logging.log4j.core.Appender; -import org.apache.logging.log4j.core.Filter; -import org.apache.logging.log4j.core.Layout; -import org.apache.logging.log4j.core.LogEvent; -import org.apache.logging.log4j.core.appender.AbstractAppender; -import org.apache.logging.log4j.core.appender.AbstractManager; -import org.apache.logging.log4j.core.appender.mom.JmsManager.JmsManagerConfiguration; -import org.apache.logging.log4j.core.config.Node; -import org.apache.logging.log4j.core.config.Property; -import org.apache.logging.log4j.core.config.plugins.Plugin; -import org.apache.logging.log4j.core.config.plugins.PluginAliases; -import org.apache.logging.log4j.core.config.plugins.PluginBuilderAttribute; -import org.apache.logging.log4j.core.config.plugins.PluginBuilderFactory; -import org.apache.logging.log4j.core.config.plugins.validation.constraints.Required; -import org.apache.logging.log4j.core.net.JndiManager; - -/** - * Generic JMS Appender plugin for both queues and topics. This Appender replaces the previous split ones. However, - * configurations set up for the 2.0 version of the JMS appenders will still work. - */ -@Plugin(name = "JMS", category = Node.CATEGORY, elementType = Appender.ELEMENT_TYPE, printObject = true) -@PluginAliases({ "JMSQueue", "JMSTopic" }) -public class JmsAppender extends AbstractAppender { - - public static class Builder> extends AbstractAppender.Builder - implements org.apache.logging.log4j.core.util.Builder { - - public static final int DEFAULT_RECONNECT_INTERVAL_MILLIS = 5000; - - @PluginBuilderAttribute - private String factoryName; - - @PluginBuilderAttribute - private String providerUrl; - - @PluginBuilderAttribute - private String urlPkgPrefixes; - - @PluginBuilderAttribute - private String securityPrincipalName; - - @PluginBuilderAttribute(sensitive = true) - private String securityCredentials; - - @PluginBuilderAttribute - @Required(message = "A javax.jms.ConnectionFactory JNDI name must be specified") - private String factoryBindingName; - - @PluginBuilderAttribute - @PluginAliases({ "queueBindingName", "topicBindingName" }) - @Required(message = "A javax.jms.Destination JNDI name must be specified") - private String destinationBindingName; - - @PluginBuilderAttribute - private String userName; - - @PluginBuilderAttribute(sensitive = true) - private char[] password; - - @PluginBuilderAttribute - private long reconnectIntervalMillis = DEFAULT_RECONNECT_INTERVAL_MILLIS; - - @PluginBuilderAttribute - private boolean immediateFail; - - @PluginBuilderAttribute - private String allowedLdapClasses; - - @PluginBuilderAttribute - private String allowedLdapHosts; - - @PluginBuilderAttribute - private String allowedJndiProtocols; - - // Programmatic access only for now. - private JmsManager jmsManager; - - private Builder() { - } - - @SuppressWarnings("resource") // actualJmsManager and jndiManager are managed by the JmsAppender - @Override - public JmsAppender build() { - JmsManager actualJmsManager = jmsManager; - JmsManagerConfiguration configuration = null; - if (actualJmsManager == null) { - Properties additionalProperties = null; - if (allowedLdapClasses != null || allowedLdapHosts != null) { - additionalProperties = new Properties(); - if (allowedLdapHosts != null) { - additionalProperties.put(JndiManager.ALLOWED_HOSTS, allowedLdapHosts); - } - if (allowedLdapClasses != null) { - additionalProperties.put(JndiManager.ALLOWED_CLASSES, allowedLdapClasses); - } - if (allowedJndiProtocols != null) { - additionalProperties.put(JndiManager.ALLOWED_PROTOCOLS, allowedJndiProtocols); - } - } - final Properties jndiProperties = JndiManager.createProperties(factoryName, providerUrl, urlPkgPrefixes, - securityPrincipalName, securityCredentials, additionalProperties); - configuration = new JmsManagerConfiguration(jndiProperties, factoryBindingName, destinationBindingName, - userName, password, false, reconnectIntervalMillis); - actualJmsManager = AbstractManager.getManager(getName(), JmsManager.FACTORY, configuration); - } - if (actualJmsManager == null) { - // JmsManagerFactory has already logged an ERROR. - return null; - } - final Layout layout = getLayout(); - if (layout == null) { - LOGGER.error("No layout provided for JmsAppender"); - return null; - } - //try { - return new JmsAppender(getName(), getFilter(), layout, isIgnoreExceptions(), getPropertyArray(), - actualJmsManager); - /*} catch (final JMSException e) { - // Never happens since the ctor no longer actually throws a JMSException. - throw new IllegalStateException(e); - }*/ - } - - public Builder setDestinationBindingName(final String destinationBindingName) { - this.destinationBindingName = destinationBindingName; - return this; - } - - public Builder setFactoryBindingName(final String factoryBindingName) { - this.factoryBindingName = factoryBindingName; - return this; - } - - public Builder setFactoryName(final String factoryName) { - this.factoryName = factoryName; - return this; - } - - public Builder setImmediateFail(final boolean immediateFail) { - this.immediateFail = immediateFail; - return this; - } - - public Builder setJmsManager(final JmsManager jmsManager) { - this.jmsManager = jmsManager; - return this; - } - - public Builder setPassword(final char[] password) { - this.password = password; - return this; - } - - /** - * @deprecated Use setPassword(char[]) - */ - @Deprecated - public Builder setPassword(final String password) { - this.password = password == null ? null : password.toCharArray(); - return this; - } - - public Builder setProviderUrl(final String providerUrl) { - this.providerUrl = providerUrl; - return this; - } - - public Builder setReconnectIntervalMillis(final long reconnectIntervalMillis) { - this.reconnectIntervalMillis = reconnectIntervalMillis; - return this; - } - - public Builder setSecurityCredentials(final String securityCredentials) { - this.securityCredentials = securityCredentials; - return this; - } - - public Builder setSecurityPrincipalName(final String securityPrincipalName) { - this.securityPrincipalName = securityPrincipalName; - return this; - } - - public Builder setUrlPkgPrefixes(final String urlPkgPrefixes) { - this.urlPkgPrefixes = urlPkgPrefixes; - return this; - } - - /** - * @deprecated Use {@link #setUserName(String)}. - */ - @Deprecated - public Builder setUsername(final String username) { - this.userName = username; - return this; - } - - public Builder setUserName(final String userName) { - this.userName = userName; - return this; - } - - public Builder setAllowedLdapClasses(final String allowedLdapClasses) { - this.allowedLdapClasses = allowedLdapClasses; - return this; - } - - public Builder setAllowedLdapHosts(final String allowedLdapHosts) { - this.allowedLdapHosts = allowedLdapHosts; - return this; - } - - public Builder setAllowedJndiProtocols(final String allowedJndiProtocols) { - this.allowedJndiProtocols = allowedJndiProtocols; - return this; - } - - /** - * Does not include the password. - */ - @Override - public String toString() { - return "Builder [name=" + getName() + ", factoryName=" + factoryName + ", providerUrl=" + providerUrl - + ", urlPkgPrefixes=" + urlPkgPrefixes + ", securityPrincipalName=" + securityPrincipalName - + ", securityCredentials=" + securityCredentials + ", factoryBindingName=" + factoryBindingName - + ", destinationBindingName=" + destinationBindingName + ", username=" + userName + ", layout=" - + getLayout() + ", filter=" + getFilter() + ", ignoreExceptions=" + isIgnoreExceptions() - + ", jmsManager=" + jmsManager + ", allowedLdapClasses=" + allowedLdapClasses - + ", allowedLdapHosts=" + allowedLdapHosts + ", allowedJndiProtocols=" + allowedJndiProtocols + "]"; - } - - } - - @PluginBuilderFactory - public static Builder newBuilder() { - return new Builder(); - } - - private volatile JmsManager manager; - - protected JmsAppender(final String name, final Filter filter, final Layout layout, - final boolean ignoreExceptions, final Property[] properties, final JmsManager manager) /*throws JMSException*/ { - super(name, filter, layout, ignoreExceptions, properties); - this.manager = manager; - } - - @Deprecated - protected JmsAppender(final String name, final Filter filter, final Layout layout, - final boolean ignoreExceptions, final JmsManager manager) /*throws JMSException*/ { - super(name, filter, layout, ignoreExceptions, Property.EMPTY_ARRAY); - this.manager = manager; - } - - @Override - public void append(final LogEvent event) { - this.manager.send(event, toSerializable(event)); - } - - public JmsManager getManager() { - return manager; - } - - @Override - public boolean stop(final long timeout, final TimeUnit timeUnit) { - setStopping(); - boolean stopped = super.stop(timeout, timeUnit, false); - stopped &= this.manager.stop(timeout, timeUnit); - setStopped(); - return stopped; - } - -} diff --git a/proxy/src/main/java/org/apache/logging/log4j/core/net/JndiManager.java b/proxy/src/main/java/org/apache/logging/log4j/core/net/JndiManager.java deleted file mode 100644 index 5c4b45067..000000000 --- a/proxy/src/main/java/org/apache/logging/log4j/core/net/JndiManager.java +++ /dev/null @@ -1,317 +0,0 @@ -/* - * Copyright (C) 2018 Velocity Contributors - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache license, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the license for the specific language governing permissions and - * limitations under the license. - */ -package org.apache.logging.log4j.core.net; - -import java.net.URI; -import java.net.URISyntaxException; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.HashMap; -import java.util.List; -import java.util.Locale; -import java.util.Map; -import java.util.Properties; -import java.util.concurrent.TimeUnit; - -import javax.naming.Context; -import javax.naming.NamingEnumeration; -import javax.naming.NamingException; -import javax.naming.directory.Attribute; -import javax.naming.directory.Attributes; -import javax.naming.directory.DirContext; -import javax.naming.directory.InitialDirContext; - -import org.apache.logging.log4j.core.appender.AbstractManager; -import org.apache.logging.log4j.core.appender.ManagerFactory; -import org.apache.logging.log4j.core.util.JndiCloser; -import org.apache.logging.log4j.core.util.NetUtils; -import org.apache.logging.log4j.util.PropertiesUtil; - -/** - * Manages a JNDI {@link javax.naming.directory.DirContext}. - * - * @since 2.1 - */ -public class JndiManager extends AbstractManager { - - public static final String ALLOWED_HOSTS = "allowedLdapHosts"; - public static final String ALLOWED_CLASSES = "allowedLdapClasses"; - public static final String ALLOWED_PROTOCOLS = "allowedJndiProtocols"; - - private static final JndiManagerFactory FACTORY = new JndiManagerFactory(); - private static final String PREFIX = "log4j2."; - private static final String LDAP = "ldap"; - private static final String LDAPS = "ldaps"; - private static final String JAVA = "java"; - private static final List permanentAllowedHosts = NetUtils.getLocalIps(); - private static final List permanentAllowedClasses = Arrays.asList(Boolean.class.getName(), - Byte.class.getName(), Character.class.getName(), Double.class.getName(), Float.class.getName(), - Integer.class.getName(), Long.class.getName(), Short.class.getName(), String.class.getName()); - private static final List permanentAllowedProtocols = Arrays.asList(JAVA, LDAP, LDAPS); - private static final String SERIALIZED_DATA = "javaSerializedData"; - private static final String CLASS_NAME = "javaClassName"; - private static final String REFERENCE_ADDRESS = "javaReferenceAddress"; - private static final String OBJECT_FACTORY = "javaFactory"; - private final List allowedHosts; - private final List allowedClasses; - private final List allowedProtocols; - - private final DirContext context; - - private JndiManager(final String name, final DirContext context, final List allowedHosts, - final List allowedClasses, final List allowedProtocols) { - super(null, name); - this.context = context; - this.allowedHosts = allowedHosts; - this.allowedClasses = allowedClasses; - this.allowedProtocols = allowedProtocols; - } - - /** - * Gets the default JndiManager using the default {@link javax.naming.InitialContext}. - * - * @return the default JndiManager - */ - public static JndiManager getDefaultManager() { - return getManager(JndiManager.class.getName(), FACTORY, null); - } - - /** - * Gets a named JndiManager using the default {@link javax.naming.InitialContext}. - * - * @param name the name of the JndiManager instance to create or use if available - * @return a default JndiManager - */ - public static JndiManager getDefaultManager(final String name) { - return getManager(name, FACTORY, null); - } - - /** - * Gets a JndiManager with the provided configuration information. - * - * @param initialContextFactoryName Fully qualified class name of an implementation of - * {@link javax.naming.spi.InitialContextFactory}. - * @param providerURL The provider URL to use for the JNDI connection (specific to the above factory). - * @param urlPkgPrefixes A colon-separated list of package prefixes for the class name of the factory - * class that will create a URL context factory - * @param securityPrincipal The name of the identity of the Principal. - * @param securityCredentials The security credentials of the Principal. - * @param additionalProperties Any additional JNDI environment properties to set or {@code null} for none. - * @return the JndiManager for the provided parameters. - */ - public static JndiManager getJndiManager(final String initialContextFactoryName, - final String providerURL, - final String urlPkgPrefixes, - final String securityPrincipal, - final String securityCredentials, - final Properties additionalProperties) { - final Properties properties = createProperties(initialContextFactoryName, providerURL, urlPkgPrefixes, - securityPrincipal, securityCredentials, additionalProperties); - return getManager(createManagerName(), FACTORY, properties); - } - - /** - * Gets a JndiManager with the provided configuration information. - * - * @param properties JNDI properties, usually created by calling {@link #createProperties(String, String, String, String, String, Properties)}. - * @return the JndiManager for the provided parameters. - * @see #createProperties(String, String, String, String, String, Properties) - * @since 2.9 - */ - public static JndiManager getJndiManager(final Properties properties) { - return getManager(createManagerName(), FACTORY, properties); - } - - private static String createManagerName() { - return JndiManager.class.getName() + '@' + JndiManager.class.hashCode(); - } - - /** - * Creates JNDI Properties with the provided configuration information. - * - * @param initialContextFactoryName - * Fully qualified class name of an implementation of {@link javax.naming.spi.InitialContextFactory}. - * @param providerURL - * The provider URL to use for the JNDI connection (specific to the above factory). - * @param urlPkgPrefixes - * A colon-separated list of package prefixes for the class name of the factory class that will create a - * URL context factory - * @param securityPrincipal - * The name of the identity of the Principal. - * @param securityCredentials - * The security credentials of the Principal. - * @param additionalProperties - * Any additional JNDI environment properties to set or {@code null} for none. - * @return the Properties for the provided parameters. - * @since 2.9 - */ - public static Properties createProperties(final String initialContextFactoryName, final String providerURL, - final String urlPkgPrefixes, final String securityPrincipal, final String securityCredentials, - final Properties additionalProperties) { - if (initialContextFactoryName == null) { - return null; - } - final Properties properties = new Properties(); - properties.setProperty(Context.INITIAL_CONTEXT_FACTORY, initialContextFactoryName); - if (providerURL != null) { - properties.setProperty(Context.PROVIDER_URL, providerURL); - } else { - LOGGER.warn("The JNDI InitialContextFactory class name [{}] was provided, but there was no associated " - + "provider URL. This is likely to cause problems.", initialContextFactoryName); - } - if (urlPkgPrefixes != null) { - properties.setProperty(Context.URL_PKG_PREFIXES, urlPkgPrefixes); - } - if (securityPrincipal != null) { - properties.setProperty(Context.SECURITY_PRINCIPAL, securityPrincipal); - if (securityCredentials != null) { - properties.setProperty(Context.SECURITY_CREDENTIALS, securityCredentials); - } else { - LOGGER.warn("A security principal [{}] was provided, but with no corresponding security credentials.", - securityPrincipal); - } - } - if (additionalProperties != null) { - properties.putAll(additionalProperties); - } - return properties; - } - - @Override - protected boolean releaseSub(final long timeout, final TimeUnit timeUnit) { - return JndiCloser.closeSilently(this.context); - } - - /** - * Looks up a named object through this JNDI context. - * - * @param name name of the object to look up. - * @param the type of the object. - * @return the named object if it could be located. - * @throws NamingException if a naming exception is encountered - */ - @SuppressWarnings("unchecked") - public synchronized T lookup(final String name) throws NamingException { - try { - URI uri = new URI(name); - if (uri.getScheme() != null) { - if (!allowedProtocols.contains(uri.getScheme().toLowerCase(Locale.ROOT))) { - LOGGER.warn("Log4j JNDI does not allow protocol {}", uri.getScheme()); - return null; - } - if (LDAP.equalsIgnoreCase(uri.getScheme()) || LDAPS.equalsIgnoreCase(uri.getScheme())) { - if (!allowedHosts.contains(uri.getHost())) { - LOGGER.warn("Attempt to access ldap server not in allowed list"); - return null; - } - Attributes attributes = this.context.getAttributes(name); - if (attributes != null) { - // In testing the "key" for attributes seems to be lowercase while the attribute id is - // camelcase, but that may just be true for the test LDAP used here. This copies the Attributes - // to a Map ignoring the "key" and using the Attribute's id as the key in the Map so it matches - // the Java schema. - Map attributeMap = new HashMap<>(); - NamingEnumeration enumeration = attributes.getAll(); - while (enumeration.hasMore()) { - Attribute attribute = enumeration.next(); - attributeMap.put(attribute.getID(), attribute); - } - Attribute classNameAttr = attributeMap.get(CLASS_NAME); - if (attributeMap.get(SERIALIZED_DATA) != null) { - if (classNameAttr != null) { - String className = classNameAttr.get().toString(); - if (!allowedClasses.contains(className)) { - LOGGER.warn("Deserialization of {} is not allowed", className); - return null; - } - } else { - LOGGER.warn("No class name provided for {}", name); - return null; - } - } else if (attributeMap.get(REFERENCE_ADDRESS) != null - || attributeMap.get(OBJECT_FACTORY) != null) { - LOGGER.warn("Referenceable class is not allowed for {}", name); - return null; - } - } - } - } - } catch (URISyntaxException ex) { - // This is OK. - } - return (T) this.context.lookup(name); - } - - private static class JndiManagerFactory implements ManagerFactory { - - @Override - public JndiManager createManager(final String name, final Properties data) { - String hosts = data != null ? data.getProperty(ALLOWED_HOSTS) : null; - String classes = data != null ? data.getProperty(ALLOWED_CLASSES) : null; - String protocols = data != null ? data.getProperty(ALLOWED_PROTOCOLS) : null; - List allowedHosts = new ArrayList<>(); - List allowedClasses = new ArrayList<>(); - List allowedProtocols = new ArrayList<>(); - addAll(hosts, allowedHosts, permanentAllowedHosts, ALLOWED_HOSTS, data); - addAll(classes, allowedClasses, permanentAllowedClasses, ALLOWED_CLASSES, data); - addAll(protocols, allowedProtocols, permanentAllowedProtocols, ALLOWED_PROTOCOLS, data); - try { - return new JndiManager(name, new InitialDirContext(data), allowedHosts, allowedClasses, - allowedProtocols); - } catch (final NamingException e) { - LOGGER.error("Error creating JNDI InitialContext.", e); - return null; - } - } - - private void addAll(String toSplit, List list, List permanentList, String propertyName, - Properties data) { - if (toSplit != null) { - list.addAll(Arrays.asList(toSplit.split("\\s*,\\s*"))); - data.remove(propertyName); - } - toSplit = PropertiesUtil.getProperties().getStringProperty(PREFIX + propertyName); - if (toSplit != null) { - list.addAll(Arrays.asList(toSplit.split("\\s*,\\s*"))); - } - list.addAll(permanentList); - } - } - - @Override - public String toString() { - return "JndiManager [context=" + context + ", count=" + count + "]"; - } - -} diff --git a/proxy/src/main/java/org/apache/logging/log4j/core/util/NetUtils.java b/proxy/src/main/java/org/apache/logging/log4j/core/util/NetUtils.java deleted file mode 100644 index dd4d1ef33..000000000 --- a/proxy/src/main/java/org/apache/logging/log4j/core/util/NetUtils.java +++ /dev/null @@ -1,232 +0,0 @@ -/* - * Copyright (C) 2018 Velocity Contributors - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache license, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the license for the specific language governing permissions and - * limitations under the license. - */ - -package org.apache.logging.log4j.core.util; - -import java.io.File; -import java.net.Inet4Address; -import java.net.Inet6Address; -import java.net.InetAddress; -import java.net.MalformedURLException; -import java.net.NetworkInterface; -import java.net.SocketException; -import java.net.URI; -import java.net.URISyntaxException; -import java.net.URL; -import java.net.UnknownHostException; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Enumeration; -import java.util.List; - -import org.apache.logging.log4j.Logger; -import org.apache.logging.log4j.status.StatusLogger; -import org.apache.logging.log4j.util.Strings; - -/** - * Networking-related convenience methods. - */ -public final class NetUtils { - - private static final Logger LOGGER = StatusLogger.getLogger(); - private static final String UNKNOWN_LOCALHOST = "UNKNOWN_LOCALHOST"; - - private NetUtils() { - // empty - } - - /** - * This method gets the network name of the machine we are running on. Returns "UNKNOWN_LOCALHOST" in the unlikely - * case where the host name cannot be found. - * - * @return String the name of the local host - */ - public static String getLocalHostname() { - try { - final InetAddress addr = InetAddress.getLocalHost(); - return addr == null ? UNKNOWN_LOCALHOST : addr.getHostName(); - } catch (final UnknownHostException uhe) { - try { - final Enumeration interfaces = NetworkInterface.getNetworkInterfaces(); - if (interfaces != null) { - while (interfaces.hasMoreElements()) { - final NetworkInterface nic = interfaces.nextElement(); - final Enumeration addresses = nic.getInetAddresses(); - while (addresses.hasMoreElements()) { - final InetAddress address = addresses.nextElement(); - if (!address.isLoopbackAddress()) { - final String hostname = address.getHostName(); - if (hostname != null) { - return hostname; - } - } - } - } - } - } catch (final SocketException se) { - // ignore and log below. - } - LOGGER.error("Could not determine local host name", uhe); - return UNKNOWN_LOCALHOST; - } - } - - /** - * Returns all the local host names and ip addresses. - * @return The local host names and ip addresses. - */ - public static List getLocalIps() { - List localIps = new ArrayList<>(); - localIps.add("localhost"); - localIps.add("127.0.0.1"); - try { - final InetAddress addr = Inet4Address.getLocalHost(); - setHostName(addr, localIps); - } catch (final UnknownHostException ex) { - // Ignore this. - } - try { - final Enumeration interfaces = NetworkInterface.getNetworkInterfaces(); - if (interfaces != null) { - while (interfaces.hasMoreElements()) { - final NetworkInterface nic = interfaces.nextElement(); - final Enumeration addresses = nic.getInetAddresses(); - while (addresses.hasMoreElements()) { - final InetAddress address = addresses.nextElement(); - setHostName(address, localIps); - } - } - } - } catch (final SocketException se) { - // ignore. - } - return localIps; - } - - private static void setHostName(InetAddress address, List localIps) { - String[] parts = address.toString().split("\\s*/\\s*"); - if (parts.length > 0) { - for (String part : parts) { - if (Strings.isNotBlank(part) && !localIps.contains(part)) { - localIps.add(part); - } - } - } - } - - /** - * Returns the local network interface's MAC address if possible. The local network interface is defined here as - * the {@link java.net.NetworkInterface} that is both up and not a loopback interface. - * - * @return the MAC address of the local network interface or {@code null} if no MAC address could be determined. - */ - public static byte[] getMacAddress() { - byte[] mac = null; - try { - final InetAddress localHost = InetAddress.getLocalHost(); - try { - final NetworkInterface localInterface = NetworkInterface.getByInetAddress(localHost); - if (isUpAndNotLoopback(localInterface)) { - mac = localInterface.getHardwareAddress(); - } - if (mac == null) { - final Enumeration networkInterfaces = NetworkInterface.getNetworkInterfaces(); - if (networkInterfaces != null) { - while (networkInterfaces.hasMoreElements() && mac == null) { - final NetworkInterface nic = networkInterfaces.nextElement(); - if (isUpAndNotLoopback(nic)) { - mac = nic.getHardwareAddress(); - } - } - } - } - } catch (final SocketException e) { - LOGGER.catching(e); - } - if (ArrayUtils.isEmpty(mac) && localHost != null) { - // Emulate a MAC address with an IP v4 or v6 - final byte[] address = localHost.getAddress(); - // Take only 6 bytes if the address is an IPv6 otherwise will pad with two zero bytes - mac = Arrays.copyOf(address, 6); - } - } catch (final UnknownHostException ignored) { - // ignored - } - return mac; - } - - /** - * Returns the mac address, if it is available, as a string with each byte separated by a ":" character. - * @return the mac address String or null. - */ - public static String getMacAddressString() { - final byte[] macAddr = getMacAddress(); - if (!ArrayUtils.isEmpty(macAddr)) { - StringBuilder sb = new StringBuilder(String.format("%02x", macAddr[0])); - for (int i = 1; i < macAddr.length; ++i) { - sb.append(":").append(String.format("%02x", macAddr[i])); - } - return sb.toString(); - - } - return null; - } - - private static boolean isUpAndNotLoopback(final NetworkInterface ni) throws SocketException { - return ni != null && !ni.isLoopback() && ni.isUp(); - } - - /** - * Converts a URI string or file path to a URI object. - * - * @param path the URI string or path - * @return the URI object - */ - public static URI toURI(final String path) { - try { - // Resolves absolute URI - return new URI(path); - } catch (final URISyntaxException e) { - // A file path or a Apache Commons VFS URL might contain blanks. - // A file path may start with a driver letter - try { - final URL url = new URL(path); - return new URI(url.getProtocol(), url.getHost(), url.getPath(), null); - } catch (MalformedURLException | URISyntaxException nestedEx) { - return new File(path).toURI(); - } - } - } - -}