diff --git a/pom.xml b/pom.xml
index 57440bf..6045943 100644
--- a/pom.xml
+++ b/pom.xml
@@ -322,6 +322,13 @@
2.0.3
test
+
+ com.github.stefanbirkner
+ system-lambda
+ 1.2.1
+ test
+
+
diff --git a/src/main/java/net/juniper/netconf/Device.java b/src/main/java/net/juniper/netconf/Device.java
index 208b724..aa55f0e 100644
--- a/src/main/java/net/juniper/netconf/Device.java
+++ b/src/main/java/net/juniper/netconf/Device.java
@@ -11,7 +11,10 @@
import com.jcraft.jsch.ChannelSubsystem;
import com.jcraft.jsch.JSch;
import com.jcraft.jsch.JSchException;
+import com.jcraft.jsch.ProxyHTTP;
+import com.jcraft.jsch.ProxySOCKS5;
import com.jcraft.jsch.Session;
+import com.jcraft.jsch.SocketFactory;
import lombok.Builder;
import lombok.Getter;
import lombok.NonNull;
@@ -22,6 +25,7 @@
import org.w3c.dom.Document;
import org.xml.sax.SAXException;
+import javax.net.ssl.SSLSocketFactory;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
@@ -29,6 +33,8 @@
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
+import java.io.OutputStream;
+import java.net.Socket;
import java.nio.charset.Charset;
import java.util.Arrays;
import java.util.List;
@@ -228,6 +234,50 @@ private Session loginWithUserPass(int timeoutMilliSeconds) throws NetconfExcepti
session.setConfig("userauth", "password");
session.setConfig("StrictHostKeyChecking", isStrictHostKeyChecking() ? "yes" : "no");
session.setPassword(password);
+
+ final String socksProxyHost = System.getenv("SOCKS_PROXY_HOST");
+ final String socksProxyPort = System.getenv("SOCKS_PROXY_PORT");
+ final String socksProxyUser = System.getenv("SOCKS_PROXY_USER");
+ final String socksProxyPass = System.getenv("SOCKS_PROXY_PASS");
+ if (socksProxyHost != null && socksProxyPort != null && socksProxyUser != null && socksProxyPass != null) {
+ log.info("Using socks5 proxy");
+ final ProxySOCKS5 proxy = new ProxySOCKS5(socksProxyHost, Integer.parseInt(socksProxyPort));
+ proxy.setUserPasswd(socksProxyUser, socksProxyPass);
+ session.setProxy(proxy);
+ }
+ else {
+ final String httpProxyHost = System.getenv("HTTP_PROXY_HOST");
+ final String httpProxyPort = System.getenv("HTTP_PROXY_PORT");
+ final String httpProxyUser = System.getenv("HTTP_PROXY_USER");
+ final String httpProxyPass = System.getenv("HTTP_PROXY_PASS");
+ if(httpProxyHost != null && httpProxyPort != null && httpProxyUser != null && httpProxyPass != null) {
+ log.info("Using http proxy");
+ final ProxyHTTP proxy = new ProxyHTTP(httpProxyHost, Integer.parseInt(httpProxyPort));
+ proxy.setUserPasswd(httpProxyUser, httpProxyPass);
+ session.setProxy(proxy);
+ // ProxyHTTP class doesn't know anything about TLS. We are supplying a socket
+ // factory which takes care of the low level TLS tunnel between the client
+ // and the proxy server
+ session.setSocketFactory((new SocketFactory() {
+ final javax.net.SocketFactory fact = SSLSocketFactory.getDefault();
+ @Override
+ public Socket createSocket(String host, int port) throws IOException {
+ return fact.createSocket(host, port);
+ }
+
+ @Override
+ public InputStream getInputStream(Socket socket) throws IOException {
+ return socket.getInputStream();
+ }
+
+ @Override
+ public OutputStream getOutputStream(Socket socket) throws IOException {
+ return socket.getOutputStream();
+ }
+ }));
+ }
+ }
+
session.connect(timeoutMilliSeconds);
return session;
} catch (JSchException e) {
diff --git a/src/test/java/net/juniper/netconf/DeviceTest.java b/src/test/java/net/juniper/netconf/DeviceTest.java
index c72d2db..d06edf6 100644
--- a/src/test/java/net/juniper/netconf/DeviceTest.java
+++ b/src/test/java/net/juniper/netconf/DeviceTest.java
@@ -1,13 +1,17 @@
package net.juniper.netconf;
+import com.github.stefanbirkner.systemlambda.SystemLambda;
import com.jcraft.jsch.ChannelSubsystem;
import com.jcraft.jsch.HostKeyRepository;
import com.jcraft.jsch.JSch;
import com.jcraft.jsch.JSchException;
+import com.jcraft.jsch.ProxyHTTP;
+import com.jcraft.jsch.ProxySOCKS5;
import com.jcraft.jsch.Session;
import org.junit.Before;
import org.junit.Test;
import org.junit.experimental.categories.Category;
+import org.mockito.ArgumentCaptor;
import org.xmlunit.assertj.XmlAssert;
import java.io.ByteArrayInputStream;
@@ -136,6 +140,90 @@ public void GIVEN_sshAvailableNetconfNot_THEN_closeDevice() throws Exception {
verifyNoMoreInteractions(sshClient);
}
+ @Test
+ public void GIVEN_netConfWithHttpProxy_THEN_setHttpProxy() throws Exception {
+
+ String httpProxyHost = "testHttpProxyHost";
+ String httpProxyPort = "8080";
+ String httpProxyUser = "username";
+ String httpProxyPass = "password";
+ SystemLambda.withEnvironmentVariable("HTTP_PROXY_HOST", httpProxyHost)
+ .and("HTTP_PROXY_PORT", httpProxyPort)
+ .and("HTTP_PROXY_USER", httpProxyUser)
+ .and("HTTP_PROXY_PASS", httpProxyPass)
+ .execute(() -> {
+ JSch sshClient = mock(JSch.class);
+ Session session = mock(Session.class);
+ HostKeyRepository hostKeyRepository = mock(HostKeyRepository.class);
+
+ when(sshClient.getSession(eq(TEST_USERNAME), eq(TEST_HOSTNAME),
+ eq(DEFAULT_NETCONF_PORT))).thenReturn(session);
+ when(sshClient.getHostKeyRepository()).thenReturn(hostKeyRepository);
+
+ try (Device device = Device.builder()
+ .sshClient(sshClient)
+ .hostName(TEST_HOSTNAME)
+ .userName(TEST_USERNAME)
+ .password(TEST_PASSWORD)
+ .strictHostKeyChecking(false)
+ .build()) {
+ device.connect();
+ }
+ catch (NetconfException e) {
+ // Do nothing
+ }
+
+ ProxyHTTP httpProxy = new ProxyHTTP(httpProxyHost, Integer.parseInt(httpProxyPort));
+ httpProxy.setUserPasswd(httpProxyUser, httpProxyPass);
+ ArgumentCaptor actualProxyCaptor = ArgumentCaptor.forClass(ProxyHTTP.class);
+ verify(session).setProxy(actualProxyCaptor.capture());
+ ProxyHTTP actualHttpProxy = actualProxyCaptor.getValue();
+ assertThat(actualHttpProxy).usingRecursiveComparison().isEqualTo(httpProxy);
+ });
+ }
+
+ @Test
+ public void GIVEN_netConfWithSocksProxy_THEN_setHttpProxy() throws Exception {
+
+ String socksProxyHost = "testSocksProxyHost";
+ String socksProxyPort = "8080";
+ String socksProxyUser = "username";
+ String socksProxyPass = "password";
+ SystemLambda.withEnvironmentVariable("SOCKS_PROXY_HOST", socksProxyHost)
+ .and("SOCKS_PROXY_PORT", socksProxyPort)
+ .and("SOCKS_PROXY_USER", socksProxyUser)
+ .and("SOCKS_PROXY_PASS", socksProxyPass)
+ .execute(() -> {
+ JSch sshClient = mock(JSch.class);
+ Session session = mock(Session.class);
+ HostKeyRepository hostKeyRepository = mock(HostKeyRepository.class);
+
+ when(sshClient.getSession(eq(TEST_USERNAME), eq(TEST_HOSTNAME),
+ eq(DEFAULT_NETCONF_PORT))).thenReturn(session);
+ when(sshClient.getHostKeyRepository()).thenReturn(hostKeyRepository);
+
+ try (Device device = Device.builder()
+ .sshClient(sshClient)
+ .hostName(TEST_HOSTNAME)
+ .userName(TEST_USERNAME)
+ .password(TEST_PASSWORD)
+ .strictHostKeyChecking(false)
+ .build()) {
+ device.connect();
+ }
+ catch (NetconfException e) {
+ // Do nothing
+ }
+
+ ProxySOCKS5 socksProxy = new ProxySOCKS5(socksProxyHost, Integer.parseInt(socksProxyPort));
+ socksProxy.setUserPasswd(socksProxyUser, socksProxyPass);
+ ArgumentCaptor actualProxyCaptor = ArgumentCaptor.forClass(ProxySOCKS5.class);
+ verify(session).setProxy(actualProxyCaptor.capture());
+ ProxySOCKS5 actualSocksProxy = actualProxyCaptor.getValue();
+ assertThat(actualSocksProxy).usingRecursiveComparison().isEqualTo(socksProxy);
+ });
+ }
+
@Test
public void GIVEN_newDevice_WHEN_withNullUserName_THEN_throwsException() {
assertThatThrownBy(() -> Device.builder().hostName("foo").build())
@@ -222,4 +310,5 @@ private JSch givenConnectingSshClient() throws IOException, JSchException {
.thenReturn(sshSession);
return sshClient;
}
+
}