Skip to content

Commit

Permalink
add Socks support
Browse files Browse the repository at this point in the history
  • Loading branch information
MCMicS committed Oct 3, 2023
1 parent 25589fd commit 4e96a03
Show file tree
Hide file tree
Showing 4 changed files with 215 additions and 7 deletions.
1 change: 1 addition & 0 deletions gradle.properties
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ platformVersionToVerify=232.8660.142

# Plugin Dependencies -> https://plugins.jetbrains.com/docs/intellij/plugin-dependencies.html
# Example: platformPlugins = com.intellij.java, com.jetbrains.php:203.4449.22, com.intellij.kubernetes
#platformPlugins=com.intellij.java,com.jetbrains.restClient
platformPlugins=com.intellij.java

# Java language level used to compile sources and to generate the files for - Java 11 is required since 2020.3
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,7 @@ class DefaultSecurityClient implements SecurityClient {
.setConnectionRequestTimeout(connectionTimout)
.setSocketTimeout(connectionTimout);
this.credentialsProvider = new BasicCredentialsProvider();
final var dnsResolver = new JenkinsDnsResolver();

final RequestConfig defaultRequestConfig = requestConfig
.setMaxRedirects(10)
Expand All @@ -94,14 +95,19 @@ class DefaultSecurityClient implements SecurityClient {
.setSSLContext(sslContext)
.setDefaultSocketConfig(socketConfig)
.setDefaultRequestConfig(defaultRequestConfig)
.setDnsResolver(dnsResolver)
.setDefaultCredentialsProvider(credentialsProvider)
.setRedirectStrategy(new JenkinsRedirectStrategy());
this.httpClient = httpClientBuilder.build();
if (useProxySettings) {
this.configCreator = url -> {
final var configForUrl = RequestConfig.copy(defaultRequestConfig);
IdeHttpClientHelpers.ApacheHttpClient4.setProxyForUrlIfEnabled(configForUrl, url);
IdeHttpClientHelpers.ApacheHttpClient4.setProxyCredentialsForUrlIfEnabled(credentialsProvider, url);
final var useSocks = JenkinsConnectionSocketFactory.INSTANCE.prepareContext(url, sslContext,
getHttpClientContext(), dnsResolver);
if (!useSocks) {
IdeHttpClientHelpers.ApacheHttpClient4.setProxyForUrlIfEnabled(configForUrl, url);
IdeHttpClientHelpers.ApacheHttpClient4.setProxyCredentialsForUrlIfEnabled(credentialsProvider, url);
}
return configForUrl.build();
};
} else {
Expand Down Expand Up @@ -180,7 +186,7 @@ protected final void addAuthenticationPreemptive(HttpHost host, UsernamePassword

protected final HttpHost getLastRedirectionHost(HttpHost host) {
final var httpHead = new HttpHead(host.toURI());
LOG.trace(String.format("Sending HEAD request to: %s", host.toURI()));
LOG.trace(String.format(" %s", host.toURI()));
try {
final var context = getHttpClientContext();
final var response = executeHttp(httpHead);
Expand Down Expand Up @@ -249,14 +255,14 @@ private void runMethod(String url, @NotNull Collection<RequestData> data, Respon
}

protected final HttpResponse executeHttp(HttpPost post) throws IOException {
final var postConfig = post.getConfig();
if (postConfig == null) {
post.setConfig(configCreator.apply(post.getURI().toASCIIString()));
}
return executeHttp((HttpRequestBase) post);
}

protected final HttpResponse executeHttp(HttpRequestBase httpRequest) throws IOException {
final var httpRequestConfig = httpRequest.getConfig();
if (httpRequestConfig == null) {
httpRequest.setConfig(configCreator.apply(httpRequest.getURI().toASCIIString()));
}
return getHttpClient().execute(httpRequest, this.httpClientContext);
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,168 @@
package org.codinjutsu.tools.jenkins.security;

import com.intellij.openapi.vfs.VfsUtil;
import com.intellij.util.net.HttpConfigurable;
import com.intellij.util.net.IdeaWideProxySelector;
import org.apache.http.HttpHost;
import org.apache.http.client.protocol.HttpClientContext;
import org.apache.http.config.Registry;
import org.apache.http.config.RegistryBuilder;
import org.apache.http.conn.socket.ConnectionSocketFactory;
import org.apache.http.conn.socket.PlainConnectionSocketFactory;
import org.apache.http.conn.ssl.DefaultHostnameVerifier;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.conn.util.PublicSuffixMatcherLoader;
import org.apache.http.protocol.HttpContext;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.SSLContext;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.Proxy;
import java.net.Socket;
import java.net.URI;
import java.util.Collections;
import java.util.List;

public class JenkinsConnectionSocketFactory {
@NotNull
public static final JenkinsConnectionSocketFactory INSTANCE = new JenkinsConnectionSocketFactory();

public @Nullable Registry<ConnectionSocketFactory> getRegistry(@NotNull String url, @NotNull SSLContext sslContext) {
final Proxy proxy = getProxy(url);
if(proxy.type() == Proxy.Type.SOCKS) {
return createSocksRegistry(toUri(url), proxy, sslContext);
}
return createDefaultRegistry(sslContext);
}

@NotNull
private static Registry<ConnectionSocketFactory> createDefaultRegistry(@NotNull SSLContext sslContext) {
String[] supportedProtocols = null;
String[] supportedCipherSuites = null;
HostnameVerifier proxyAuthStrategyCopy = new DefaultHostnameVerifier(PublicSuffixMatcherLoader.getDefault());
final SSLConnectionSocketFactory sslConnectionSocketFactory = new SSLConnectionSocketFactory(sslContext,
supportedProtocols, supportedCipherSuites, proxyAuthStrategyCopy);

return RegistryBuilder.<ConnectionSocketFactory>create()
.register("http", PlainConnectionSocketFactory.getSocketFactory())
.register("https", sslConnectionSocketFactory)
.build();
}

public boolean prepareContext(@NotNull String url,
@NotNull SSLContext sslContext,
@NotNull HttpClientContext httpClientContext,
@NotNull JenkinsDnsResolver dnsResolver) {
httpClientContext.removeAttribute("http.socket-factory-registry");
dnsResolver.clearFakes();
final URI uri = toUri(url);
final Proxy proxy = getProxy(uri);
if(proxy.type() == Proxy.Type.SOCKS) {
// see org.apache.http.impl.conn.DefaultHttpClientConnectionOperator.SOCKET_FACTORY_REGISTRY
httpClientContext.setAttribute("http.socket-factory-registry", getRegistry(url, sslContext));
if (uri != null) {
dnsResolver.addFakeHost(uri.getHost());
}
return true;
}
return false;
}

private Registry<ConnectionSocketFactory> createSocksRegistry(@Nullable URI uri,
@NotNull Proxy proxy,
@NotNull SSLContext sslContext) {
return RegistryBuilder.<ConnectionSocketFactory>create()
.register("http", new SocksPlainConnectionSocketFactory(proxy))
.register("https", new SocksSSLConnectionSocketFactory(uri != null ? uri.getHost() : null, sslContext, proxy))
.build();
}

@NotNull
private static HttpConfigurable getHttpConfigurable() {
return HttpConfigurable.getInstance();
}

private static @NotNull Proxy getProxy(String url) {
return getProxy(toUri(url));
}

private static @NotNull Proxy getProxy(@Nullable URI uri) {
final List<Proxy> proxies = uri == null ? Collections.emptyList() :
new IdeaWideProxySelector(getHttpConfigurable()).select(uri);
return proxies.isEmpty() ? Proxy.NO_PROXY : proxies.get(0);
}

@Nullable
private static URI toUri(String url) {
return url != null ? VfsUtil.toUri(url) : null;
}


private static final class SocksPlainConnectionSocketFactory extends PlainConnectionSocketFactory {
@NotNull
private final Proxy proxy;

public SocksPlainConnectionSocketFactory(@NotNull Proxy proxy) {
super();
this.proxy = proxy;
}

@NotNull
public Proxy getProxy() {
return this.proxy;
}

@NotNull
public Socket createSocket(@Nullable HttpContext context) {
return new Socket(this.proxy);
}

@NotNull
public Socket connectSocket(int connectTimeout,
@Nullable Socket socket,
@NotNull HttpHost host,
@NotNull InetSocketAddress remoteAddress,
@Nullable InetSocketAddress localAddress,
@Nullable HttpContext context) throws IOException {
InetSocketAddress unresolvedRemote = InetSocketAddress.createUnresolved(host.getHostName(), remoteAddress.getPort());
return super.connectSocket(connectTimeout, socket, host, unresolvedRemote, localAddress, context);
}
}

private static final class SocksSSLConnectionSocketFactory extends SSLConnectionSocketFactory {
@Nullable
private final String host;
@NotNull
private final Proxy proxy;

public SocksSSLConnectionSocketFactory(@Nullable String host, @NotNull SSLContext sslContext, @NotNull Proxy proxy) {
super(sslContext);
this.host = host;
this.proxy = proxy;
}

@NotNull
public Proxy getProxy() {
return this.proxy;
}

@NotNull
public Socket createSocket(@NotNull HttpContext context) {
return new Socket(this.proxy);
}

@NotNull
public Socket connectSocket(int connectTimeout,
@Nullable Socket socket,
@NotNull HttpHost host,
@NotNull InetSocketAddress remoteAddress,
@Nullable InetSocketAddress localAddress,
@Nullable HttpContext context) throws IOException {
final var unresolvedRemote = InetSocketAddress.createUnresolved(host.getHostName(), remoteAddress.getPort());
return super.connectSocket(connectTimeout, socket, host, unresolvedRemote, localAddress, context);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
package org.codinjutsu.tools.jenkins.security;

import org.apache.http.conn.DnsResolver;
import org.apache.http.impl.conn.SystemDefaultDnsResolver;
import org.jetbrains.annotations.NotNull;

import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.HashSet;
import java.util.Set;

public class JenkinsDnsResolver implements DnsResolver {
private static final DnsResolver DNS_RESOLVER = SystemDefaultDnsResolver.INSTANCE;
private final Set<String> hostWithFakeDns = new HashSet<>();

public void clearFakes() {
hostWithFakeDns.clear();
}

public void addFakeHost(String host) {
hostWithFakeDns.add(host);
}

@Override
public InetAddress[] resolve(@NotNull String host) throws UnknownHostException {
return hostWithFakeDns.contains(host) ? returnFakeDns(): DNS_RESOLVER.resolve(host);
}

private InetAddress[] returnFakeDns() throws UnknownHostException {
final var fakeIp = new byte[]{1, 1, 1, 1};
return new InetAddress[]{InetAddress.getByAddress(fakeIp)};
}
}

0 comments on commit 4e96a03

Please sign in to comment.