-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathindex.js
148 lines (138 loc) · 4.71 KB
/
index.js
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
/**
* TCP Proxy
* @author Capasky([email protected])
*/
'use strict';
const tls = require('tls');
const net = require('net');
const EventEmitter = require('events');
/**
* A tiny TCP proxy
*/
class TCPProxy extends EventEmitter {
/**
* @param {Object} options options to init a TCPProxy
* @param {Number} options.port proxy linsten port
* @param {Number} options.target.port proxy target port
* @param {String} options.target.host proxy target host ip
* @param {Boolean} options.ssl whether to use ssl
*/
constructor(options) {
super();
this.port = options.port;
this.target = options.target;
this.ssl = options.ssl;
this.clientSockets = new Set();
this.serverSockets = new Set();
this.createServer();
}
/**
* Create a socket server to provide a proxy
*/
createServer() {
if (this._server) {
return;
}
this._server = this.ssl
? tls.createServer(this.ssl, this._socketHandler.bind(this))
: net.createServer(this._socketHandler.bind(this));
this.on('error', this._onError);
this._server.listen(this.port, () => {
console.log('TCP Server listen at %s', this.port);
});
}
/**
* Close socket server and destroy the proxy
*/
closeServer() {
if (this._server) {
for (let socket of this.clientSockets) {
!socket.destroyed && socket.destroy();
}
for (let socket of this.serverSockets) {
!socket.destroyed && socket.destroy();
}
this._server.close(() => {
console.log(`TCP Server(port=${this.port}) closed.`);
this._server = null;
});
}
}
/**
* @private
*/
_socketHandler(clientSocket) {
console.log(`Receive new socket connection from ${clientSocket.remoteAddress}:${clientSocket.remotePort}`);
let clientSocketConnected = true;
let serverSocketConnected = false;
clientSocket.setKeepAlive(true);
this.clientSockets.add(clientSocket);
const buffers = [];
const serverSocket = new net.createConnection({
port: this.target.port,
host: this.target.host
}, () => {
serverSocketConnected = true;
this.serverSockets.add(serverSocket)
try {
buffers.forEach(buffer => {
serverSocket.write(buffer);
});
} catch (error) {
serverSocket.destroy(error);
}
});
clientSocket.on('end', () => clientSocketConnected = false);
serverSocket.on('end', () => serverSocketConnected = false);
clientSocket.on("error", error => {
clientSocketConnected = false;
!serverSocket.destroyed && serverSocket.destroy(error);
console.error(`Client socket error`);
this.emit('error', error);
});
serverSocket.on("error", error => {
serverSocketConnected = false;
!clientSocket.destroyed && clientSocket.destroy(error);
console.error(`Could not connect to service at host ${this.target.host}:${this.target.port}`);
this.emit('error', error);
});
clientSocket.on("data", data => {
if (serverSocketConnected) {
try {
serverSocket.write(data);
} catch (error) {
this.emit('error', error);
}
} else {
buffers.push(data);
}
});
serverSocket.on("data", data => {
if (clientSocketConnected) {
try {
clientSocket.write(data);
} catch (error) {
this.emit('error', error);
}
}
});
clientSocket.on("close", error => {
console.log('Client socket closed.');
this.clientSockets.has(clientSocket) && this.clientSockets.delete(clientSocket);
this.emit('close', error);
});
serverSocket.on("close", error => {
console.log('Server socket closed.');
this.serverSockets.has(serverSocket) && this.serverSockets.delete(serverSocket);
this.emit('close', error);
});
}
/**
* @private
*/
_onError(err) {
console.error(err);
this._server.emit('error', err);
}
}
module.exports = TCPProxy;