-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
0 parents
commit 9a0be46
Showing
9 changed files
with
361 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
*.map | ||
.idea/* | ||
compile/* | ||
node_modules/ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
Boring | ||
==== | ||
|
||
Bore a tunnel through WebSocket, Socks5 or HTTP Connection. | ||
|
||
# Usage | ||
|
||
Install through `npm install tboring -g`, check the help through `boring -h`, | ||
|
||
``` | ||
Help: | ||
To run server: boring -s localip:localport [remoteip:remoteport] | ||
To run client: boring -t [localip:]localport[:remoteip:remoteport] ws[s]://wshost:wsport | ||
Options: | ||
-s, --server run as server, listen on localip:localport | ||
-t, --tunnel run as tunnel client, specify localip:localport | ||
-c, --cert path to the https server's cert, optional | ||
-k, --key path to the https server's key, optional | ||
``` | ||
|
||
# TODO | ||
|
||
- [ ] Support Socks5 | ||
- [ ] Support HTTP Connection | ||
- [ ] Support secured WebSocket client connection |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,64 @@ | ||
#!/usr/bin/env node | ||
const { Server, Client } = require("../lib/boring"); | ||
const Help = `Help: | ||
To run server: boring -s localip:localport [remoteip:remoteport] | ||
To run client: boring -t [localip:]localport[:remoteip:remoteport] ws[s]://wshost:wsport | ||
`; | ||
|
||
(() => { | ||
const optimist = require("optimist"); | ||
const argv = optimist | ||
.usage(Help) | ||
.string("s") | ||
.string("t") | ||
.string("c") | ||
.string("k") | ||
.alias("s", "server") | ||
.alias("t", "tunnel") | ||
.alias("c", "cert") | ||
.alias("k", "key") | ||
.describe("s", "run as server, listen on localip:localport") | ||
.describe("t", "run as tunnel client, specify localip:localport") | ||
.describe("c", "path to the https server's cert, optional") | ||
.describe("k", "path to the https server's key, optional").argv; | ||
if (argv.s) { | ||
const server = new Server(); | ||
const remoteAddr = argv._[0]; | ||
let [cert, key] = [null, null]; | ||
if (argv.c && argv.k) { | ||
cert = argv.c; | ||
key = argv.k; | ||
} | ||
server.start(argv.s, remoteAddr, cert, key, (err) => | ||
err ? console.log(err) : console.log(`Server is listening on ${argv.s}`) | ||
); | ||
} else if (argv.t) { | ||
const client = new Client(); | ||
let localHost = "127.0.0.1", | ||
localPort, | ||
remoteAddr; | ||
const toks = argv.t.split(":"); | ||
if (toks.length === 4) { | ||
[localHost, localPort] = toks; | ||
remoteAddr = `${toks[2]}:${toks[3]}`; | ||
} else if (toks.length === 3) { | ||
remoteAddr = `${toks[1]}:${toks[2]}`; | ||
localPort = toks[0]; | ||
} else if (toks.length === 2) { | ||
[localHost, localPort] = toks; | ||
} else if (toks.length === 1) { | ||
localPort = toks[0]; | ||
} else { | ||
console.log("Invalid tunnel option " + argv.t); | ||
console.log(optimist.help()); | ||
process.exit(); | ||
} | ||
localPort = parseInt(localPort); | ||
const wsHostUrl = argv._[0]; | ||
client.start(`${localHost}:${localPort}`, remoteAddr, wsHostUrl, (err) => | ||
err ? console.log(err) : console.log(`Server is listening on ${argv.t}`) | ||
); | ||
} else { | ||
console.log(optimist.help()); | ||
} | ||
})(); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
module.exports = { | ||
Server: require("./server"), | ||
Client: require("./client"), | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,50 @@ | ||
const utils = require("./utils"); | ||
const net = require("net"); | ||
const WebSocket = require("ws"); | ||
|
||
module.exports = BoringClient = class BoringClient { | ||
constructor() { | ||
this.tcpServer = net.createServer(); | ||
} | ||
|
||
start(localAddr, remoteAddr, wsHostUrl, cb) { | ||
const [localHost, localPort] = Array.from(utils.parseAddr(localAddr)); | ||
console.log(`localHost: ${localHost}; localPort: ${localPort}`); | ||
if (remoteAddr) console.log(`remoteAddr: ${remoteAddr}`); | ||
console.log(`wsHostUrl: ${wsHostUrl}`); | ||
if (remoteAddr) { | ||
this.wsHostUrl = `${wsHostUrl}/?dst=${remoteAddr}`; | ||
} else { | ||
this.wsHostUrl = wsHostUrl; | ||
} | ||
this.tcpServer.listen(localPort, localHost, cb); | ||
this.tcpServer.on("connection", (socket) => { | ||
this._connect(this.wsHostUrl, (err, wsStream) => { | ||
if (err) console.log(err); | ||
else { | ||
socket.pipe(wsStream); | ||
wsStream.pipe(socket); | ||
} | ||
}); | ||
}); | ||
} | ||
|
||
_connect(host, cb) { | ||
try { | ||
const re = new RegExp( | ||
"^(?:wss?:)?(?://)?(?:[^@\n]+@)?(?:www.)?([^:/\n]+)", | ||
"im" | ||
); | ||
const ws = new WebSocket(host, { | ||
servername: host.match(re)[1], | ||
perMessageDeflate: false, | ||
rejectUnauthorized: false, | ||
strictSSL: false, | ||
}); | ||
const duplex = WebSocket.createWebSocketStream(ws); | ||
cb(null, duplex); | ||
} catch (e) { | ||
cb(e, null); | ||
} | ||
} | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,122 @@ | ||
const utils = require("./utils"); | ||
const WebSocket = require("ws"); | ||
const net = require("net"); | ||
const url = require("url"); | ||
|
||
module.exports = BoringServer = class BoringServer { | ||
start(localAddr, remoteAddr, cert, key, cb) { | ||
const [localHost, localPort] = Array.from(utils.parseAddr(localAddr)); | ||
if (remoteAddr) { | ||
const [remoteHost, remotePort] = Array.from(utils.parseAddr(remoteAddr)); | ||
this.remoteHost = remoteHost; | ||
this.remotePort = remotePort; | ||
console.log(`Traffic is forwarded to ${remoteAddr}`); | ||
} else { | ||
console.log( | ||
`The remote where the traffic is forwarded to will be defined by clients` | ||
); | ||
} | ||
cb(); | ||
let server; | ||
if (cert && key) { | ||
const https = require("https"); | ||
server = https.createServer({ | ||
cert: fs.readFileSync(cert), | ||
key: fs.readFileSync(key), | ||
}); | ||
} else { | ||
const http = require("http"); | ||
server = http.createServer(); | ||
} | ||
|
||
const wss = new WebSocket.Server({ | ||
server: server, | ||
path: "/", | ||
}); | ||
|
||
wss.on("connection", (ws, req) => { | ||
console.log( | ||
`ws connected, headers: ${JSON.stringify(req.headers)}; url: ${ | ||
req.url | ||
}; ip: ${req.socket.remoteAddress}` | ||
); | ||
ws.isAlive = true; | ||
ws.on("pong", () => { | ||
ws.isAlive = true; | ||
}); | ||
const uri = url.parse(req.url, true); | ||
let dst = uri.query.dst ? uri.query.dst : null; | ||
setupTcpSocket(ws, dst); | ||
ws.on("close", (code, reason) => { | ||
console.log(`ws closed - code: ${code}; reason: ${reason}`); | ||
}); | ||
ws.on("error", (err) => { | ||
console.log(`ws error: `, err); | ||
}); | ||
}); | ||
|
||
const setupTcpSocket = (ws, target) => { | ||
if (!ws.tcpSocket) { | ||
const duplex = WebSocket.createWebSocketStream(ws); | ||
let host, port; | ||
if (this.remoteHost && this.remotePort) { | ||
[host, port] = [this.remoteHost, this.remotePort]; | ||
} else if (target) { | ||
[host, port] = Array.from(utils.parseAddr(target)); | ||
} else { | ||
console.log("Remote address unknown"); | ||
closeWs(ws); | ||
return; | ||
} | ||
|
||
const tcpSocket = net.connect( | ||
{ host, port, allowHalfOpen: true }, | ||
() => { | ||
console.log(`tcpSocket connected`); | ||
duplex.pipe(tcpSocket); | ||
tcpSocket.pipe(duplex); | ||
} | ||
); | ||
tcpSocket.on("end", () => { | ||
console.log("tcpSocket end"); | ||
ws.tcpSocket = null; | ||
}); | ||
tcpSocket.on("error", (err) => { | ||
console.log("tcpSocket error: ", err); | ||
}); | ||
ws.tcpSocket = tcpSocket; | ||
} | ||
}; | ||
|
||
const closeWs = (ws) => { | ||
ws.terminate(); | ||
if (ws.tcpSocket) { | ||
ws.tcpSocket.end(); | ||
ws.tcpSocket = null; | ||
} | ||
}; | ||
|
||
const noop = () => {}; | ||
const cleanup = () => { | ||
try { | ||
wss.clients.forEach((ws) => { | ||
if (!ws.isAlive) { | ||
closeWs(ws); | ||
return; | ||
} | ||
ws.isAlive = false; | ||
ws.ping(noop); | ||
}); | ||
} catch (e) { | ||
console.log(e); | ||
} finally { | ||
setTimeout(() => { | ||
cleanup(); | ||
}, 300000); | ||
} | ||
}; | ||
cleanup(); | ||
|
||
server.listen(localPort, localHost); | ||
} | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
parseAddr = (addr) => { | ||
let host, port; | ||
if (typeof addr === "number") { | ||
port = addr; | ||
} else { | ||
[host, port] = Array.from(addr.split(":")); | ||
if (/^\d+$/.test(host)) { | ||
port = host; | ||
host = null; | ||
} | ||
port = parseInt(port); | ||
} | ||
if (host == null) { | ||
host = "127.0.0.1"; | ||
} | ||
return [host, port]; | ||
}; | ||
|
||
module.exports = { | ||
parseAddr: parseAddr, | ||
}; |
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,31 @@ | ||
{ | ||
"name": "tboring", | ||
"version": "1.0.0", | ||
"description": "Bore a tunnel through WebSocket, Socks5 or HTTP Connection.", | ||
"scripts": { | ||
"prestart": "npx prettier --write '**/*.js'" | ||
}, | ||
"keywords": [ | ||
"tunnel", | ||
"websocket" | ||
], | ||
"author": { | ||
"name": "Damon Yuan", | ||
"email": "damon.yuan.dev@gmail.com" | ||
}, | ||
"repository": { | ||
"type": "git", | ||
"url": "git://github.com:damonYuan/boring.git" | ||
}, | ||
"license": "MIT", | ||
"dependencies": { | ||
"optimist": "^0.6.1", | ||
"ws": "^7.5.0" | ||
}, | ||
"devDependencies": { | ||
"prettier": "^2.3.1" | ||
}, | ||
"bin": { | ||
"boring": "./bin/cli.js" | ||
} | ||
} |