package org.simplejavamail.mailer.internal.socks.socks5client;
import org.simplejavamail.mailer.internal.socks.common.SocksException;
import org.simplejavamail.internal.util.MiscUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.*;
import java.nio.channels.SocketChannel;
import java.util.ArrayList;
import java.util.List;
public class SocksSocket extends Socket {
private static final Logger LOGGER = LoggerFactory.getLogger(SocksSocket.class);
private final Socks5 proxy;
private String remoteServerHost;
private int remoteServerPort;
private Socket proxySocket;
private SocksSocket(final Socks5 proxy, final String remoteServerHost, final int remoteServerPort)
throws IOException {
this.proxy = MiscUtil.checkNotNull(proxy, "Argument [proxy] may not be null").copy();
this.proxy.setProxySocket(proxySocket);
this.remoteServerHost = MiscUtil.checkNotNull(remoteServerHost, "Argument [remoteServerHost] may not be null");
this.remoteServerPort = remoteServerPort;
this.proxy.buildConnection();
proxySocket = this.proxy.getProxySocket();
initProxyChain();
this.proxy.requestConnect(remoteServerHost, remoteServerPort);
}
private SocksSocket(final Socks5 proxy, final InetAddress address, final int port)
throws IOException {
this(proxy, new InetSocketAddress(address, port));
}
public SocksSocket(final Socks5 proxy, final InetSocketAddress socketAddress)
throws IOException {
MiscUtil.checkNotNull(proxy, "Argument [proxy] may not be null");
MiscUtil.checkNotNull(socketAddress, "Argument [socketAddress] may not be null");
this.proxy = proxy.copy();
this.remoteServerHost = socketAddress.getHostString();
this.remoteServerPort = socketAddress.getPort();
this.proxy.buildConnection();
proxySocket = this.proxy.getProxySocket();
initProxyChain();
this.proxy.requestConnect(socketAddress.getAddress(), socketAddress.getPort());
}
@SuppressWarnings("WeakerAccess")
public SocksSocket(final Socks5 proxy)
throws IOException {
this(proxy, proxy.createProxySocket());
}
public SocksSocket(final Socks5 proxy, final Socket proxySocket, final InetSocketAddress socketAddress)
throws IOException {
this(proxy, proxySocket);
connect(socketAddress);
}
@SuppressWarnings("WeakerAccess")
public SocksSocket(final Socks5 proxy, final Socket proxySocket) {
MiscUtil.checkNotNull(proxy, "Argument [proxy] may not be null");
MiscUtil.checkNotNull(proxySocket, "Argument [proxySocket] may not be null");
if (proxySocket.isConnected()) {
throw new IllegalArgumentException("Proxy socket should be unconnected");
}
this.proxySocket = proxySocket;
this.proxy = proxy.copy();
this.proxy.setProxySocket(proxySocket);
}
private void initProxyChain()
throws IOException {
final List<Socks5> proxyChain = new ArrayList<>();
Socks5 temp = proxy;
while (temp.getChainProxy() != null) {
temp.getChainProxy().setProxySocket(proxySocket);
proxyChain.add(temp.getChainProxy());
temp = temp.getChainProxy();
}
LOGGER.debug("Proxy chain has:{} proxy", proxyChain.size());
if (proxyChain.size() > 0) {
Socks5 pre = proxy;
for (final Socks5 chain : proxyChain) {
pre.requestConnect(chain.getInetAddress(), chain.getPort());
proxy.getChainProxy().buildConnection();
pre = chain;
}
}
}
@Override
public void connect(final SocketAddress endpoint)
throws IOException {
connect(endpoint, 0);
}
@Override
public void connect(final SocketAddress endpoint, final int timeout)
throws IOException {
if (!(endpoint instanceof InetSocketAddress)) {
throw new IllegalArgumentException("Unsupported address type");
}
remoteServerHost = ((InetSocketAddress) endpoint).getHostName();
remoteServerPort = ((InetSocketAddress) endpoint).getPort();
proxy.getProxySocket().setSoTimeout(timeout);
proxy.buildConnection();
initProxyChain();
proxy.requestConnect(endpoint);
}
@Override
public InputStream getInputStream()
throws IOException {
return proxy.getProxySocket().getInputStream();
}
@Override
public OutputStream getOutputStream()
throws IOException {
return proxy.getProxySocket().getOutputStream();
}
@Override
public void bind(final SocketAddress bindpoint)
throws IOException {
proxy.getProxySocket().bind(bindpoint);
}
@Override
public InetAddress getInetAddress() {
try {
return InetAddress.getByName(remoteServerHost);
} catch (final UnknownHostException e) {
throw new SocksException(e.getMessage(), e);
}
}
@Override
public InetAddress getLocalAddress() {
return proxy.getProxySocket().getLocalAddress();
}
@SuppressWarnings("SuspiciousGetterSetter")
@Override
public int getPort() {
return remoteServerPort;
}
@Override
public int getLocalPort() {
return proxy.getProxySocket().getLocalPort();
}
@Override
public SocketAddress getRemoteSocketAddress() {
return proxy.getProxySocket().getRemoteSocketAddress();
}
@Override
public SocketAddress getLocalSocketAddress() {
return proxy.getProxySocket().getLocalSocketAddress();
}
@Override
public SocketChannel getChannel() {
return proxy.getProxySocket().getChannel();
}
@Override
public boolean getTcpNoDelay()
throws SocketException {
return proxy.getProxySocket().getTcpNoDelay();
}
@Override
public void setTcpNoDelay(final boolean on)
throws SocketException {
proxy.getProxySocket().setTcpNoDelay(on);
}
@Override
public void setSoLinger(final boolean on, final int linger)
throws SocketException {
proxy.getProxySocket().setSoLinger(on, linger);
}
@Override
public int getSoLinger()
throws SocketException {
return proxy.getProxySocket().getSoLinger();
}
@Override
public void sendUrgentData(final int data)
throws IOException {
proxy.getProxySocket().sendUrgentData(data);
}
@Override
public boolean getOOBInline()
throws SocketException {
return proxy.getProxySocket().getOOBInline();
}
@Override
public void setOOBInline(final boolean on)
throws SocketException {
proxy.getProxySocket().setOOBInline(on);
}
@Override
public synchronized int getSoTimeout()
throws SocketException {
return proxy.getProxySocket().getSoTimeout();
}
@Override
public synchronized void setSoTimeout(final int timeout)
throws SocketException {
proxy.getProxySocket().setSoTimeout(timeout);
}
@Override
public synchronized int getSendBufferSize()
throws SocketException {
return proxy.getProxySocket().getSendBufferSize();
}
@Override
public synchronized void setSendBufferSize(final int size)
throws SocketException {
proxy.getProxySocket().setSendBufferSize(size);
}
@Override
public synchronized int getReceiveBufferSize()
throws SocketException {
return proxy.getProxySocket().getReceiveBufferSize();
}
@Override
public synchronized void setReceiveBufferSize(final int size)
throws SocketException {
proxy.getProxySocket().setReceiveBufferSize(size);
}
@Override
public boolean getKeepAlive()
throws SocketException {
return proxy.getProxySocket().getKeepAlive();
}
@Override
public void setKeepAlive(final boolean on)
throws SocketException {
proxy.getProxySocket().setKeepAlive(on);
}
@Override
public int getTrafficClass()
throws SocketException {
return proxy.getProxySocket().getTrafficClass();
}
@Override
public void setTrafficClass(final int tc)
throws SocketException {
proxy.getProxySocket().setTrafficClass(tc);
}
@Override
public boolean getReuseAddress()
throws SocketException {
return proxy.getProxySocket().getReuseAddress();
}
@Override
public void setReuseAddress(final boolean on)
throws SocketException {
proxy.getProxySocket().setReuseAddress(on);
}
@Override
public synchronized void close()
throws IOException {
if (proxy.getProxySocket() != null) {
proxy.getProxySocket().close();
proxy.setProxySocket(null);
}
}
@Override
public void shutdownInput()
throws IOException {
proxy.getProxySocket().shutdownInput();
}
@Override
public void shutdownOutput()
throws IOException {
proxy.getProxySocket().shutdownOutput();
}
@Override
public boolean isConnected() {
return proxy.getProxySocket().isConnected();
}
@Override
public boolean isBound() {
return proxy.getProxySocket().isBound();
}
@Override
public boolean isClosed() {
return proxy.getProxySocket().isClosed();
}
@Override
public boolean isInputShutdown() {
return proxy.getProxySocket().isInputShutdown();
}
@Override
public boolean isOutputShutdown() {
return proxy.getProxySocket().isOutputShutdown();
}
@Override
public void setPerformancePreferences(final int connectionTime, final int latency, final int bandwidth) {
proxy.getProxySocket().setPerformancePreferences(connectionTime, latency, bandwidth);
}
}