| /******************************************************************************* |
| * Copyright (c) 2021 Red Hat Inc. and others |
| * |
| * This program and the accompanying materials |
| * are made available under the terms of the Eclipse Public License 2.0 |
| * which accompanies this distribution, and is available at |
| * https://www.eclipse.org/legal/epl-2.0/ |
| * |
| * SPDX-License-Identifier: EPL-2.0 |
| * |
| * Contributors: |
| * Red Hat Inc. - initial API and implementation |
| *******************************************************************************/ |
| package org.eclipse.core.net; |
| |
| import java.io.IOException; |
| import java.net.URI; |
| import java.util.ArrayList; |
| import java.util.Locale; |
| import java.util.Objects; |
| import java.util.Properties; |
| |
| import org.eclipse.core.internal.net.AbstractProxyProvider; |
| import org.eclipse.core.internal.net.Activator; |
| import org.eclipse.core.internal.net.Policy; |
| import org.eclipse.core.internal.net.ProxyData; |
| import org.eclipse.core.internal.net.StringUtil; |
| import org.eclipse.core.net.proxy.IProxyData; |
| |
| import com.sun.jna.Library; |
| import com.sun.jna.Native; |
| import com.sun.jna.Pointer; |
| import com.sun.jna.ptr.PointerByReference; |
| |
| /** |
| * JNA version of org.eclipse.core.internal.net.UnixProxyProvider |
| * |
| * @author jjohnstn |
| * |
| */ |
| public class ProxyProvider extends AbstractProxyProvider { |
| |
| private static LibGio fLibGio; |
| |
| private static Pointer proxySettings = Pointer.NULL; |
| private static Pointer httpProxySettings = Pointer.NULL; |
| private static Pointer httpsProxySettings = Pointer.NULL; |
| private static Pointer socksProxySettings = Pointer.NULL; |
| private static Pointer ftpProxySettings = Pointer.NULL; |
| |
| private static final String ENABLE_GNOME = Activator.ID + ".enableGnome"; //$NON-NLS-1$ |
| |
| private static final String LIBRARY_NAME = "gio-2.0"; //$NON-NLS-1$ |
| |
| private static boolean isGnomeLibLoaded = false; |
| |
| static { |
| // Load the GSettings JNI library if org.eclipse.core.net.enableGnome is specified |
| String value = System.getProperty(ENABLE_GNOME); |
| if ("".equals(value) || "true".equals(value)) { //$NON-NLS-1$ //$NON-NLS-2$ |
| initializeSettings(); |
| } |
| } |
| |
| public ProxyProvider() { |
| // no initialization required |
| } |
| |
| @Override |
| public IProxyData[] select(URI uri) { |
| String[] nonProxyHosts = getNonProxiedHosts(); |
| if (nonProxyHosts != null) { |
| String host = uri.getHost(); |
| for (String nonProxyHost : nonProxyHosts) { |
| if (StringUtil.hostMatchesFilter(host, nonProxyHost)) { |
| return new IProxyData[0]; |
| } |
| } |
| } |
| IProxyData[] proxies = new IProxyData[0]; |
| if (uri.getScheme() != null) { |
| ProxyData pd = getSystemProxyInfo(uri.getScheme()); |
| proxies = pd != null ? new IProxyData[] { pd } : new IProxyData[0]; |
| } else { |
| proxies = getProxyData(); |
| } |
| if (Policy.DEBUG) { |
| Policy.debug("LinuxProxyProvider#select result for [" + uri + "]"); //$NON-NLS-1$ //$NON-NLS-2$ |
| for (IProxyData proxy : proxies) { |
| System.out.println(" " + proxy); //$NON-NLS-1$ |
| } |
| } |
| return proxies; |
| } |
| |
| @Override |
| public IProxyData[] getProxyData() { |
| String[] commonTypes = new String[] { IProxyData.HTTP_PROXY_TYPE, |
| IProxyData.SOCKS_PROXY_TYPE, IProxyData.HTTPS_PROXY_TYPE }; |
| return getProxyForTypes(commonTypes); |
| } |
| |
| private IProxyData[] getProxyForTypes(String[] types) { |
| ArrayList<IProxyData> allData = new ArrayList<>(); |
| for (String type : types) { |
| ProxyData pd = getSystemProxyInfo(type); |
| if (pd != null && pd.getHost() != null) { |
| allData.add(pd); |
| } |
| } |
| return allData.toArray(new IProxyData[0]); |
| } |
| |
| @Override |
| public String[] getNonProxiedHosts() { |
| String[] npHosts; |
| |
| if (Policy.DEBUG_SYSTEM_PROVIDERS) |
| Policy.debug("Getting no_proxy"); //$NON-NLS-1$ |
| |
| // First try the environment variable which is a URL |
| String npEnv = getEnv("no_proxy"); //$NON-NLS-1$ |
| if (npEnv != null) { |
| npHosts = StringUtil.split(npEnv, new String[] { "," }); //$NON-NLS-1$ |
| for (int i = 0; i < npHosts.length; i++) |
| npHosts[i] = npHosts[i].trim(); |
| if (Policy.DEBUG_SYSTEM_PROVIDERS) { |
| Policy.debug("Got Env no_proxy: " + npEnv); //$NON-NLS-1$ |
| debugPrint(npHosts); |
| } |
| return npHosts; |
| } |
| |
| if (isGnomeLibLoaded) { |
| try { |
| npHosts = getGSettingsNonProxyHosts(); |
| if (npHosts != null && npHosts.length > 0) { |
| if (Policy.DEBUG_SYSTEM_PROVIDERS) { |
| Policy.debug("Got Gnome no_proxy"); //$NON-NLS-1$ |
| debugPrint(npHosts); |
| } |
| return npHosts; |
| } |
| } catch (UnsatisfiedLinkError e) { |
| // The library should be loaded, so this is a real exception |
| Activator.logError( |
| "Problem during accessing Gnome library", e); //$NON-NLS-1$ |
| } |
| } |
| |
| return new String[0]; |
| } |
| |
| // Returns null if something wrong or there is no proxy for the protocol |
| protected ProxyData getSystemProxyInfo(String protocol) { |
| ProxyData pd = null; |
| String envName = null; |
| |
| if (Policy.DEBUG_SYSTEM_PROVIDERS) |
| Policy.debug("Getting proxies for: " + protocol); //$NON-NLS-1$ |
| |
| try { |
| // protocol schemes are ISO 8859 (ASCII) |
| protocol = protocol.toLowerCase(Locale.ENGLISH); |
| |
| // First try the environment variable which is a URL |
| envName = protocol + "_proxy"; //$NON-NLS-1$ |
| String proxyEnv = getEnv(envName); |
| if (Policy.DEBUG_SYSTEM_PROVIDERS) |
| Policy.debug("Got proxyEnv: " + proxyEnv); //$NON-NLS-1$ |
| |
| if (proxyEnv != null) { |
| int colonInd = proxyEnv.indexOf(":"); //$NON-NLS-1$ |
| if (colonInd !=-1 && proxyEnv.length() > colonInd + 2 && !"//".equals(proxyEnv.substring(colonInd + 1, colonInd + 3))) { //$NON-NLS-1$ |
| proxyEnv = "http://" + proxyEnv; //$NON-NLS-1$ |
| } |
| URI uri = new URI(proxyEnv); |
| pd = new ProxyData(protocol); |
| pd.setHost(Objects.requireNonNull(uri.getHost(), "no host in " + proxyEnv)); //$NON-NLS-1$ |
| int port = uri.getPort(); |
| if (port == -1) { |
| throw new IllegalStateException("no port in " + proxyEnv); //$NON-NLS-1$ |
| } |
| pd.setPort(port); |
| String userInfo = uri.getUserInfo(); |
| if (userInfo != null) { |
| String user = null; |
| String password = null; |
| int pwInd = userInfo.indexOf(':'); |
| if (pwInd >= 0) { |
| user = userInfo.substring(0, pwInd); |
| password = userInfo.substring(pwInd + 1); |
| } else { |
| user = userInfo; |
| } |
| pd.setUserid(user); |
| pd.setPassword(password); |
| } |
| pd.setSource("LINUX_ENV"); //$NON-NLS-1$ |
| if (Policy.DEBUG_SYSTEM_PROVIDERS) |
| Policy.debug("Got Env proxy: " + pd); //$NON-NLS-1$ |
| return pd; |
| } |
| } catch (Exception e) { |
| Activator.logError( |
| "Problem during accessing system variable: " + envName, e); //$NON-NLS-1$ |
| } |
| |
| if (isGnomeLibLoaded) { |
| try { |
| // Then ask Gnome |
| pd = getGSettingsProxyInfo(protocol); |
| if (pd != null) { |
| if (Policy.DEBUG_SYSTEM_PROVIDERS) |
| Policy.debug("Got Gnome proxy: " + pd); //$NON-NLS-1$ |
| pd.setSource("LINUX_GNOME"); //$NON-NLS-1$ |
| return pd; |
| } |
| } catch (UnsatisfiedLinkError e) { |
| // The library should be loaded, so this is a real exception |
| Activator.logError( |
| "Problem during accessing Gnome library", e); //$NON-NLS-1$ |
| } |
| } |
| |
| return null; |
| } |
| |
| private static String getEnv(String env) { |
| try { |
| return System.getenv(env); |
| } catch (Throwable t) { |
| // Fall-back to running 'env' directly. Warning this is very slow... |
| // up to 200ms |
| String cmd[] = { "/bin/sh", //$NON-NLS-1$ |
| "-c", //$NON-NLS-1$ |
| "env | grep -i proxy" }; //$NON-NLS-1$ |
| Properties props = new Properties(); |
| Process proc = null; |
| try { |
| proc = Runtime.getRuntime().exec(cmd); |
| props.load(proc.getInputStream()); |
| } catch (IOException e) { |
| Activator.logError( |
| "Problem during accessing system variable: " + env, e); //$NON-NLS-1$ |
| } catch (IllegalArgumentException e) { |
| Activator.logError( |
| "Problem during accessing system variable: " + env, e); //$NON-NLS-1$ |
| } finally { |
| if (proc != null) { |
| proc.destroy(); |
| } |
| } |
| return props.getProperty(env); |
| } |
| } |
| |
| private void debugPrint(String[] strs) { |
| for (int i = 0; i < strs.length; i++) |
| System.out.println(i + ": " + strs[i]); //$NON-NLS-1$ |
| } |
| |
| private interface LibGio extends Library { |
| Pointer g_settings_new(String schema); |
| boolean g_settings_get_boolean(Pointer settings, String key); |
| Pointer g_settings_get_string(Pointer settings, String key); |
| int g_settings_get_int(Pointer settings, String key); |
| PointerByReference g_settings_get_strv(Pointer Settings, String key); |
| void g_strfreev(PointerByReference p); |
| void g_free(Pointer p); |
| } |
| |
| private static void initializeSettings() { |
| try { |
| fLibGio = Native.load(LIBRARY_NAME, LibGio.class); |
| proxySettings = fLibGio.g_settings_new ("org.gnome.system.proxy"); //$NON-NLS-1$ |
| httpProxySettings = fLibGio.g_settings_new ("org.gnome.system.proxy.http"); //$NON-NLS-1$ |
| httpsProxySettings = fLibGio.g_settings_new ("org.gnome.system.proxy.https"); //$NON-NLS-1$ |
| socksProxySettings = fLibGio.g_settings_new ("org.gnome.system.proxy.socks"); //$NON-NLS-1$ |
| ftpProxySettings = fLibGio.g_settings_new ("org.gnome.system.proxy.ftp"); //$NON-NLS-1$ |
| isGnomeLibLoaded= true; |
| if (Policy.DEBUG_SYSTEM_PROVIDERS) |
| Policy.debug("Loaded " + //$NON-NLS-1$ |
| System.mapLibraryName(LIBRARY_NAME) + " library"); //$NON-NLS-1$ |
| } catch (UnsatisfiedLinkError e) { |
| isGnomeLibLoaded= false; |
| if (Policy.DEBUG_SYSTEM_PROVIDERS) |
| Policy.debug("Could not load library: " //$NON-NLS-1$ |
| + System.mapLibraryName(LIBRARY_NAME)); |
| } |
| } |
| |
| protected static ProxyData getGSettingsProxyInfo(String protocol) { |
| |
| if (protocol == null) { |
| return null; |
| } |
| |
| if (proxySettings == Pointer.NULL) { |
| initializeSettings(); |
| } |
| |
| ProxyData proxyData = new ProxyData(protocol); |
| boolean useSame = fLibGio.g_settings_get_boolean(proxySettings, "use-same-proxy"); //$NON-NLS-1$ |
| |
| if (protocol.equalsIgnoreCase("http") || useSame) { //$NON-NLS-1$ |
| boolean useProxy = fLibGio.g_settings_get_boolean(httpProxySettings, "enabled"); //$NON-NLS-1$ |
| if (!useProxy) { |
| return null; |
| } |
| Pointer host = fLibGio.g_settings_get_string(httpProxySettings, "host"); //$NON-NLS-1$ |
| proxyData.setHost(host.getString(0)); |
| fLibGio.g_free(host); |
| |
| int port = fLibGio.g_settings_get_int(httpProxySettings, "port"); //$NON-NLS-1$ |
| proxyData.setPort(port); |
| |
| boolean reqAuth = fLibGio.g_settings_get_boolean(httpProxySettings, "use-authentication"); //$NON-NLS-1$ |
| if (reqAuth) { |
| Pointer user = fLibGio.g_settings_get_string(httpProxySettings, "authentication-user"); //$NON-NLS-1$ |
| proxyData.setUserid(user.getString(0)); |
| fLibGio.g_free(user); |
| |
| Pointer password = fLibGio.g_settings_get_string(httpProxySettings, "authentication-password"); //$NON-NLS-1$ |
| proxyData.setPassword(password.getString(0)); |
| fLibGio.g_free(password); |
| } |
| return proxyData; |
| } |
| |
| // Everything else applies only if the system proxy mode is manual |
| Pointer mode = fLibGio.g_settings_get_string(proxySettings, "mode"); //$NON-NLS-1$ |
| if (!mode.getString(0).equalsIgnoreCase("manual")) { //$NON-NLS-1$ |
| fLibGio.g_free(mode); |
| return null; |
| } |
| fLibGio.g_free(mode); |
| |
| Pointer host; |
| int port; |
| |
| if (protocol.equalsIgnoreCase("https")) { //$NON-NLS-1$ |
| host = fLibGio.g_settings_get_string(httpsProxySettings, "host"); //$NON-NLS-1$ |
| port = fLibGio.g_settings_get_int(httpsProxySettings, "port"); //$NON-NLS-1$ |
| } else if (protocol.equalsIgnoreCase("socks")) { //$NON-NLS-1$ |
| host = fLibGio.g_settings_get_string(socksProxySettings, "host"); //$NON-NLS-1$ |
| port = fLibGio.g_settings_get_int(socksProxySettings, "port"); //$NON-NLS-1$ |
| } else if (protocol.equalsIgnoreCase("ftp")) { //$NON-NLS-1$ |
| host = fLibGio.g_settings_get_string(ftpProxySettings, "host"); //$NON-NLS-1$ |
| port = fLibGio.g_settings_get_int(ftpProxySettings, "port"); //$NON-NLS-1$ |
| } else { |
| return null; |
| } |
| |
| proxyData.setHost(host.getString(0)); |
| fLibGio.g_free(host); |
| proxyData.setPort(port); |
| |
| return proxyData; |
| } |
| |
| protected static String[] getGSettingsNonProxyHosts() { |
| if (proxySettings == Pointer.NULL) { |
| initializeSettings(); |
| } |
| |
| PointerByReference npHostsArray = fLibGio.g_settings_get_strv(proxySettings, "ignore-hosts"); //$NON-NLS-1$ |
| String[] npHosts = npHostsArray.getPointer().getStringArray(0); |
| |
| fLibGio.g_strfreev(npHostsArray); |
| |
| return npHosts; |
| } |
| |
| } |
| |