forked from andrewchambers/ws-tcp-bridge
-
Notifications
You must be signed in to change notification settings - Fork 0
/
ws-tcp-bridge
executable file
·153 lines (127 loc) · 3.36 KB
/
ws-tcp-bridge
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
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
#!/usr/bin/env node
var ws_module = require('ws');
var net = require('net');
var argv = require('optimist')
.usage('Forward tcp connections to websocket servers, or websocket connections to tcp servers.')
.demand('lport')
.describe('lport','port to listen for connections on.')
.demand('rhost')
.describe('rhost','address to forward the connection to.')
.demand('method')
.describe('method','either tcp2ws or ws2tcp')
.default('protocol', 'binary, base64')
.describe('protocol', 'the websockets client subprotocol (used with tcp2ws)').argv;
var is_tcp2ws = (argv.method == 'tcp2ws');
function initSocketCallbacks(state,ws,s) {
function code_from_ws(data) {
if (state.binary) { return data; }
return tcp2ws ? new Buffer(data, 'base64') : data.toString('base64');
}
function code_from_s(data) {
if (state.binary) { return data; }
return tcp2ws ? data.toString('base64') : new Buffer(data, 'base64');
}
function flushSocketBuffer() {
if(state.sBuffer.length > 0) {
s.write(Buffer.concat(state.sBuffer));
}
state.sBuffer = null;
};
function flushWebsocketBuffer() {
if(state.wsBuffer.length > 0) {
ws.send(Buffer.concat(state.wsBuffer),{binary: state.binary, mask: is_tcp2ws});
}
state.wsBuffer = null;
};
s.on('close', function(had_error) {
ws.removeAllListeners('close');
ws.close();
});
ws.on('close', function() {
s.removeAllListeners('close');
s.end();
});
ws.on('error', function (e) {
console.log('websocket error');
console.log(e);
ws.removeAllListeners('close');
s.removeAllListeners('close');
ws.close();
s.end();
});
s.on('error', function (e) {
console.log('socket error');
console.log(e);
ws.removeAllListeners('close');
s.removeAllListeners('close');
ws.close();
s.end();
});
s.on('connect', function() {
state.sReady = true;
flushSocketBuffer();
});
ws.on('open', function () {
state.wsReady = true;
flushWebsocketBuffer();
});
s.on('data', function(data) {
data = code_from_s(data);
if(! state.wsReady) {
state.wsBuffer.push(data);
} else {
ws.send(data,{binary: state.binary, mask: is_tcp2ws});
}
});
ws.on('message', function(m,flags) {
state.binary = flags.binary;
m = code_from_ws(m);
if(!state.sReady) {
state.sBuffer.push(m);
} else {
s.write(m);
}
});
}
function tcp2ws() {
console.log('proxy mode tcp -> ws');
console.log('forwarding port ' + argv.lport + ' to ' + argv.rhost);
var server = net.createServer(function(s) {
var ws = new ws_module(argv.rhost, {
"protocol": 'binary, base64'
});
var state = {
sReady : true,
wsReady : false,
wsBuffer: [],
sBuffer : [],
binary: false
};
initSocketCallbacks(state,ws,s);
});
server.listen(argv.lport);
}
function ws2tcp() {
console.log('proxy mode ws -> tcp');
console.log('forwarding port ' + argv.lport + ' to ' + argv.rhost);
wss = new ws_module.Server({port: argv.lport});
wss.on('connection', function(ws) {
var addr_port = argv.rhost.split(':');
var s = net.connect(addr_port[1],addr_port[0]);
var state = {
sReady : false,
wsReady : true, // there is no callback so i assume its already connected
wsBuffer: [],
sBuffer : [],
binary: false
};
initSocketCallbacks(state,ws,s);
});
}
if(is_tcp2ws) {
tcp2ws();
} else if (argv.method == 'ws2tcp') {
ws2tcp();
} else {
console.error("Method must be either tcp2ws or ws2tcp!");
}