package com.googlecode.mycontainer.util.tunnel;
import java.io.Closeable;
import java.io.IOException;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.SocketException;
import java.net.SocketTimeoutException;
import java.net.UnknownHostException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import javax.net.ServerSocketFactory;
import javax.net.SocketFactory;
import com.googlecode.mycontainer.util.Util;
import com.googlecode.mycontainer.util.log.Log;
public class Tunnel implements Closeable {
private static Log LOG = Log.get(Tunnel.class);
private String localHost;
private int localPort;
private String remoteHost;
private int remotePort;
private ServerSocket serverSocket;
private final List<TunnelConnection> connections = new ArrayList<TunnelConnection>();
public Tunnel() {
}
public Tunnel(String localHost, int localPort, String remoteHost, int remotePort) {
this.localHost = localHost;
this.localPort = localPort;
this.remoteHost = remoteHost;
this.remotePort = remotePort;
}
public String getLocalHost() {
return localHost;
}
public Tunnel setLocalHost(String localHost) {
this.localHost = localHost;
return this;
}
public int getLocalPort() {
return localPort;
}
public Tunnel setLocalPort(int localPort) {
this.localPort = localPort;
return this;
}
public String getRemoteHost() {
return remoteHost;
}
public Tunnel setRemoteHost(String remoteHost) {
this.remoteHost = remoteHost;
return this;
}
public int getRemotePort() {
return remotePort;
}
public Tunnel setRemotePort(int remotePort) {
this.remotePort = remotePort;
return this;
}
public void bind() {
try {
InetAddress address = InetAddress.getByName(localHost);
this.serverSocket = ServerSocketFactory.getDefault().createServerSocket(localPort, 50, address);
this.localPort = serverSocket.getLocalPort();
this.serverSocket.setSoTimeout(1);
LOG.info("Bound " + this);
} catch (UnknownHostException e) {
throw new RuntimeException(e);
} catch (SocketException e) {
throw new RuntimeException(e);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
@Override
public String toString() {
return "[Tunnel " + localHost + ":" + localPort + " " + remoteHost + ":" + remotePort + "]";
}
public void close() {
LOG.info("Closing " + this);
Util.close(serverSocket);
for (TunnelConnection conn : connections) {
Util.close(conn);
}
}
public boolean isClosed() {
return false;
}
public void accepts(TunnelHandler handler) {
boolean error = true;
Socket local = null;
Socket remote = null;
try {
local = serverAccept();
if (local == null) {
return;
}
TunnelConnection socketTunnel = new TunnelConnection();
socketTunnel.setLocal(local);
local.setSoTimeout(1);
remote = SocketFactory.getDefault().createSocket();
remote.connect(new InetSocketAddress(remoteHost, remotePort), 30000);
remote.setSoTimeout(1);
socketTunnel.setRemote(remote);
this.connections.add(socketTunnel);
handler.connected(socketTunnel);
error = false;
} catch (UnknownHostException e) {
LOG.error("error connecting", e);
} catch (IOException e) {
LOG.error("error connecting", e);
} finally {
if (error) {
Util.close(local);
Util.close(remote);
}
}
}
private Socket serverAccept() {
try {
return serverSocket.accept();
} catch (SocketTimeoutException e) {
return null;
} catch (IOException e) {
throw new RuntimeException(e);
}
}
public void read(TunnelHandler handler) {
Iterator<TunnelConnection> it = connections.iterator();
while (it.hasNext()) {
TunnelConnection conn = it.next();
if (conn.isStopped()) {
it.remove();
Util.close(conn);
handler.disconnected(conn);
continue;
}
conn.readData();
if (conn.hasBuffer()) {
handler.data(conn);
}
}
}
public List<TunnelConnection> getConnections() {
return Collections.unmodifiableList(connections);
}
public void closeFinisheds() {
Iterator<TunnelConnection> it = connections.iterator();
while (it.hasNext()) {
TunnelConnection conn = it.next();
TunnelData localData = conn.getLocalData();
TunnelData remoteData = conn.getRemoteData();
if (localData.isStopped() || remoteData.isStopped()) {
if (!localData.hasBuffer() && !remoteData.hasBuffer()) {
Util.close(conn);
}
}
}
}
public static Tunnel parse(String str) {
String[] array = str.split(":");
String localhost = "127.0.0.1";
int j = 0;
if (array.length >= 4) {
localhost = array[j++];
}
int localport = Integer.parseInt(array[j++]);
String remotehost = array[j++];
int remoteport = Integer.parseInt(array[j++]);
Tunnel tunnel = new Tunnel(localhost, localport, remotehost, remoteport);
return tunnel;
}
}