/* * Copyright (C) 2012-2016 Facebook, Inc. * * Licensed 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 com.facebook.nifty.ssl; import com.google.common.base.Throwables; import org.apache.tomcat.jni.Library; import org.apache.tomcat.jni.SSL; import org.jboss.netty.handler.ssl.OpenSsl; import org.jboss.netty.util.internal.NativeLibraryLoader; import java.lang.reflect.Field; import java.lang.reflect.Modifier; import java.util.LinkedHashSet; import java.util.Locale; import java.util.Set; /** * Adds support for the Netty-tcnative uber jar. The current version of netty nifty runs with does not support the * uber jar. This should be removed when we move to new version of netty. * <p></p> * Most of this code is taken from * https://github.com/netty/netty/blob/379ad2c02ed0c0ae9f94e4081e3f910ece6380b7/handler/src/main/java/io/netty/handler/ssl/OpenSsl.java */ public class NettyTcNativeLoader { private static final String LINUX = "linux"; private static final String UNKNOWN = "unknown"; private static final Throwable UNAVAILABILITY_CAUSE; static { Throwable cause = null; try { loadTcNative(); } catch (Throwable t) { cause = t; } UNAVAILABILITY_CAUSE = cause; } public static void ensureAvailable() { if (UNAVAILABILITY_CAUSE != null) { throw (Error) new UnsatisfiedLinkError( "failed to load the required native library").initCause(UNAVAILABILITY_CAUSE); } } private static void loadTcNative() { String os = normalizeOs(System.getProperty("os.name", "")); String arch = normalizeArch(System.getProperty("os.arch", "")); Set<String> libNames = new LinkedHashSet<String>(3); // First, try loading the platform-specific library. Platform-specific // libraries will be available if using a tcnative uber jar. libNames.add("netty-tcnative-" + os + '-' + arch); if (LINUX.equalsIgnoreCase(os)) { // Fedora SSL lib so naming (libssl.so.10 vs libssl.so.1.0.0).. libNames.add("netty-tcnative-" + os + '-' + arch + "-fedora"); } // finally the default library. libNames.add("netty-tcnative"); boolean loaded = false; Throwable lastException = null; for (String libName : libNames) { try { NativeLibraryLoader.load(libName, SSL.class.getClassLoader()); loaded = true; break; } catch (Throwable t) { lastException = t; continue; } } if (!loaded) { throw Throwables.propagate(lastException); } try { overrideExceptionValue(); Library.initialize("provided"); SSL.initialize(null); } catch (Throwable t) { throw Throwables.propagate(t); } } // Netty hardcodes the dependency to the Openssl class in such a way that it makes it difficult // to load tcnative outside netty. // This overrides netty's Openssl availablity to always report success since we are taking care of loading // the library. private static void overrideExceptionValue() { Field exceptionField = null; Field modifiersField = null; int originalModifiers = 0; boolean setModifiers = false; try { exceptionField = OpenSsl.class.getDeclaredField("UNAVAILABILITY_CAUSE"); exceptionField.setAccessible(true); modifiersField = exceptionField.getClass().getDeclaredField("modifiers"); modifiersField.setAccessible(true); originalModifiers = modifiersField.getInt(exceptionField); modifiersField.setInt(exceptionField, originalModifiers & ~Modifier.FINAL); setModifiers = true; // Override the default value. This will trigger the static block to run which tries to load the libs, // however we know that will fail. exceptionField.set(null, null); } catch (Throwable t) { throw Throwables.propagate(t); } finally { // Restore the fields to their original values if (exceptionField != null && modifiersField != null & setModifiers) { try { modifiersField.setInt(exceptionField, originalModifiers); } catch (IllegalAccessException e) { // Allow the original exception to propagate instead. return; } } } } private static String normalizeOs(String value) { value = normalize(value); if (value.startsWith("aix")) { return "aix"; } if (value.startsWith("hpux")) { return "hpux"; } if (value.startsWith("os400")) { // Avoid the names such as os4000 if (value.length() <= 5 || !Character.isDigit(value.charAt(5))) { return "os400"; } } if (value.startsWith(LINUX)) { return LINUX; } if (value.startsWith("macosx") || value.startsWith("osx")) { return "osx"; } if (value.startsWith("freebsd")) { return "freebsd"; } if (value.startsWith("openbsd")) { return "openbsd"; } if (value.startsWith("netbsd")) { return "netbsd"; } if (value.startsWith("solaris") || value.startsWith("sunos")) { return "sunos"; } if (value.startsWith("windows")) { return "windows"; } return UNKNOWN; } private static String normalizeArch(String value) { value = normalize(value); if (value.matches("^(x8664|amd64|ia32e|em64t|x64)$")) { return "x86_64"; } if (value.matches("^(x8632|x86|i[3-6]86|ia32|x32)$")) { return "x86_32"; } if (value.matches("^(ia64|itanium64)$")) { return "itanium_64"; } if (value.matches("^(sparc|sparc32)$")) { return "sparc_32"; } if (value.matches("^(sparcv9|sparc64)$")) { return "sparc_64"; } if (value.matches("^(arm|arm32)$")) { return "arm_32"; } if ("aarch64".equals(value)) { return "aarch_64"; } if (value.matches("^(ppc|ppc32)$")) { return "ppc_32"; } if ("ppc64".equals(value)) { return "ppc_64"; } if ("ppc64le".equals(value)) { return "ppcle_64"; } if ("s390".equals(value)) { return "s390_32"; } if ("s390x".equals(value)) { return "s390_64"; } return UNKNOWN; } private static String normalize(String value) { return value.toLowerCase(Locale.US).replaceAll("[^a-z0-9]+", ""); } }