-
Notifications
You must be signed in to change notification settings - Fork 26
/
TCPTunnel.java
executable file
·126 lines (112 loc) · 4.32 KB
/
TCPTunnel.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
package net.kanstren.tcptunnel.forwarder;
import net.kanstren.tcptunnel.Main;
import net.kanstren.tcptunnel.Params;
import net.kanstren.tcptunnel.Utils;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;
import java.text.SimpleDateFormat;
import java.util.Date;
/**
* Creates a TCP tunnel between two endpoints via two Forwarder instances.
* Data is forwarded in both directions using separate sockets.
* Any error on either socket causes the whole tunnel (both sockets) to be closed.
*/
public class TCPTunnel extends Thread {
private SimpleDateFormat sdf = new SimpleDateFormat("yyyy.MMM.dd HH:mm:ss");
/** Configuration parameters. */
private final Params params;
/** Local endpoint for the tunnel. */
private Socket localSocket;
/** Remote endpoint for the tunnel. */
private Socket serverSocket;
/** True if this tunnel is actively forwarding. False if stopped or not yet started. */
private boolean active = false;
/** Parent to notify when connection is broken. */
private final Main parent;
/**
* @param params Configuration parameters.
* @param localSocket Socket for the local port (endpoint 1 for tunnel).
* @param parent Parent to notify when connection is broken
*/
public TCPTunnel(Params params, Socket localSocket, Main parent) {
this.params = params;
this.localSocket = localSocket;
this.parent = parent;
}
/**
* Connects to the remote host and starts bidirectional forwarding (the tunnel).
*/
public void run() {
String dateStr = sdf.format(new Date());
try {
// Connect to the destination server
serverSocket = new Socket(params.getRemoteHost(), params.getRemotePort());
// Turn on keep-alive for both the sockets
serverSocket.setKeepAlive(true);
localSocket.setKeepAlive(true);
// Obtain client & server input & output streams
InputStream clientIn = localSocket.getInputStream();
OutputStream clientOut = localSocket.getOutputStream();
InputStream serverIn = serverSocket.getInputStream();
OutputStream serverOut = serverSocket.getOutputStream();
// Start forwarding data between server and client
active = true;
String clientAddr = toStr(localSocket);
String serverAddr = toStr(serverSocket);
String hummanClientAddr = Utils.mapAddrToHumanReadable(clientAddr);
String hummanServerAddr = Utils.mapAddrToHumanReadable(serverAddr);
clientAddr = clientAddr+" ("+hummanClientAddr+")";
serverAddr = serverAddr+" ("+hummanServerAddr+")";
TCPForwarder clientForward = new TCPForwarder(this, clientIn, serverOut, params, true, clientAddr);
clientForward.start();
TCPForwarder serverForward = new TCPForwarder(this, serverIn, clientOut, params, false, serverAddr);
serverForward.start();
if (params.isPrint()) {
System.out.println(dateStr+": TCP Forwarding " + clientAddr + " <--> " + serverAddr);
}
} catch (IOException ioe) {
if (params.isPrint()) {
String remoteAddr = params.getRemoteHost() + ":" + params.getRemotePort();
String humanRemoteAddr = Utils.mapAddrToHumanReadable(remoteAddr);
remoteAddr = remoteAddr + " ("+humanRemoteAddr+")";
System.err.println(dateStr + ": Failed to connect to remote host (" + remoteAddr + ")");
}
connectionBroken();
}
}
/**
* @param socket The socket to describe.
* @return A string representation of a socket (ip+port).
*/
private String toStr(Socket socket) {
String host = socket.getInetAddress().getHostAddress();
int port = socket.getPort();
return host + ":" + port;
}
/**
* Closes the tunnel (the forwarding sockets..).
*/
public void close() {
connectionBroken();
}
/**
* Called when an error is observed on one of the sockets making up the tunnel.
* Terminates the tunnel by closing both sockets.
*/
public synchronized void connectionBroken() {
try {
serverSocket.close();
} catch (Exception e) {}
try {
localSocket.close();
} catch (Exception e) {}
if (active) {
String dateStr = sdf.format(new Date());
if (params.isPrint()) System.out.println(dateStr+": TCP Forwarding " + toStr(localSocket) + " <--> " + toStr(serverSocket) + " stopped.");
active = false;
}
parent.closed(this);
}
}