Compare commits

...

9 Commits
master ... v1

5 changed files with 100 additions and 36 deletions

View File

@ -790,6 +790,9 @@ var parsers = {
answers[parts[0]] = parts[1];
});
if (answers.relay) {
console.info("using --relay " + answers.relay);
}
// things that aren't straight-forward copy-over
if (!answers.advanced && !answers.relay) {
answers.relay = 'telebit.cloud';

View File

@ -52,10 +52,15 @@ function _connect(state) {
+ "(" + clientHandlers.count() + " clients)");
conn.tunnelCid = cid;
conn.tunnelRead = tun.data.byteLength;
conn.tunnelWritten = 0;
if (tun.data) {
conn.tunnelRead = tun.data.byteLength;
} else {
conn.tunnelRead = 0;
}
conn.tunnelWritten = 0;
conn.on('data', function onLocalData(chunk) {
//var chunk = conn.read();
if (conn.tunnelClosing) {
console.warn("[onLocalData] received data for '"+cid+"' over socket after connection was ended");
return;
@ -67,8 +72,10 @@ function _connect(state) {
// down the data we are getting to send over. We also want to pause all active connections
// if any connections are paused to make things more fair so one connection doesn't get
// stuff waiting for all other connections to finish because it tried writing near the border.
var bufSize = wsHandlers.sendMessage(Packer.pack(tun, chunk));
if (pausedClients.length || bufSize > 1024*1024) {
var bufSize = wsHandlers.sendMessage(Packer.packHeader(tun, chunk));
// Sending 2 messages instead of copying the buffer
var bufSize2 = wsHandlers.sendMessage(chunk);
if (pausedClients.length || (bufSize + bufSize2) > 1024*1024) {
// console.log('[onLocalData] paused connection', cid, 'to allow websocket to catch up');
conn.pause();
pausedClients.push(conn);
@ -80,14 +87,15 @@ function _connect(state) {
console.info("[onLocalEnd] connection '" + cid + "' ended, will probably close soon");
conn.tunnelClosing = true;
if (!sentEnd) {
wsHandlers.sendMessage(Packer.pack(tun, null, 'end'));
wsHandlers.sendMessage(Packer.packHeader(tun, null, 'end'));
sentEnd = true;
}
});
conn.on('error', function onLocalError(err) {
console.info("[onLocalError] connection '" + cid + "' errored:", err);
if (!sentEnd) {
wsHandlers.sendMessage(Packer.pack(tun, {message: err.message, code: err.code}, 'error'));
var packBody = true;
wsHandlers.sendMessage(Packer.packHeader(tun, {message: err.message, code: err.code}, 'error', packBody));
sentEnd = true;
}
});
@ -95,7 +103,7 @@ function _connect(state) {
delete localclients[cid];
console.log('[onLocalClose] closed "' + cid + '" read:'+conn.tunnelRead+', wrote:'+conn.tunnelWritten+' (' + clientHandlers.count() + ' clients)');
if (!sentEnd) {
wsHandlers.sendMessage(Packer.pack(tun, null, hadErr && 'error' || 'end'));
wsHandlers.sendMessage(Packer.packHeader(tun, null, hadErr && 'error' || 'end'));
sentEnd = true;
}
});
@ -119,11 +127,13 @@ function _connect(state) {
conn.tunnelRead += opts.data.byteLength;
if (!conn.remotePaused && conn.bufferSize > 1024*1024) {
wsHandlers.sendMessage(Packer.pack(opts, conn.tunnelRead, 'pause'));
var packBody = true;
wsHandlers.sendMessage(Packer.packHeader(opts, conn.tunnelRead, 'pause', packBody));
conn.remotePaused = true;
conn.once('drain', function () {
wsHandlers.sendMessage(Packer.pack(opts, conn.tunnelRead, 'resume'));
var packBody = true;
wsHandlers.sendMessage(Packer.packHeader(opts, conn.tunnelRead, 'resume', packBody));
conn.remotePaused = false;
});
}
@ -188,7 +198,8 @@ function _connect(state) {
var cmd = [id, name].concat(Array.prototype.slice.call(arguments, 1));
if (state.debug) { console.log('[DEBUG] command sending', cmd); }
wsHandlers.sendMessage(Packer.pack(null, cmd, 'control'));
var packBody = true;
wsHandlers.sendMessage(Packer.packHeader(null, cmd, 'control', packBody));
setTimeout(function () {
if (pendingCommands[id]) {
console.warn('command', name, id, 'timed out');
@ -236,6 +247,23 @@ function _connect(state) {
var connCallback;
function hyperPeek(tun) {
var m;
var str;
if (tun.data) {
if ('http' === tun.service) {
str = tun.data.toString();
m = str.match(/(?:^|[\r\n])Host: ([^\r\n]+)[\r\n]*/im);
tun._name = tun._hostname = (m && m[1].toLowerCase() || '').split(':')[0];
}
else if ('https' === tun.service || 'tls' === tun.service) {
tun._name = tun._servername = sni(tun.data);
} else {
tun._name = '';
}
}
}
var packerHandlers = {
oncontrol: function (opts) {
var cmd, err;
@ -294,27 +322,18 @@ function _connect(state) {
err = { message: 'unknown command "'+cmd[1]+'"', code: 'E_UNKNOWN_COMMAND' };
}
wsHandlers.sendMessage(Packer.pack(null, [-cmd[0], err], 'control'));
var packBody = true;
wsHandlers.sendMessage(Packer.packHeader(null, [-cmd[0], err], 'control', packBody));
}
, onmessage: function (tun) {
, onconnection: function (tun) {
var cid = tun._id = Packer.addrToId(tun);
var str;
var m;
if ('http' === tun.service) {
str = tun.data.toString();
m = str.match(/(?:^|[\r\n])Host: ([^\r\n]+)[\r\n]*/im);
tun._name = tun._hostname = (m && m[1].toLowerCase() || '').split(':')[0];
}
else if ('https' === tun.service || 'tls' === tun.service) {
tun._name = tun._servername = sni(tun.data);
} else {
tun._name = '';
}
if (clientHandlers.write(cid, tun)) { return; }
// this data should have been gathered already as part of the proxy protocol
// but if it's available again here we can double check
hyperPeek(tun);
// TODO use readable streams instead
wstunneler.pause();
require(state.sortingHat).assign(state, tun, function (err, conn) {
if (err) {
@ -328,6 +347,18 @@ function _connect(state) {
});
}
, onmessage: function (tun) {
var cid = tun._id = Packer.addrToId(tun);
var handled;
hyperPeek(tun);
handled = clientHandlers.write(cid, tun);
// quasi backwards compat
if (!handled) { console.log("[debug] did not get 'connection' event"); packerHandlers.onconnection(tun); }
}
, onpause: function (opts) {
var cid = Packer.addrToId(opts);
if (localclients[cid]) {
@ -338,7 +369,8 @@ function _connect(state) {
console.log('[TunnelPause] remote tried pausing finished connection', cid);
// Often we have enough latency that we've finished sending before we're told to pause, so
// don't worry about sending back errors, since we won't be sending data over anyway.
// wsHandlers.sendMessage(Packer.pack(opts, {message: 'no matching connection', code: 'E_NO_CONN'}, 'error'));
// var packBody = true;
// wsHandlers.sendMessage(Packer.packHeader(opts, {message: 'no matching connection', code: 'E_NO_CONN'}, 'error', packBody));
}
}
, onresume: function (opts) {
@ -349,7 +381,8 @@ function _connect(state) {
localclients[cid].resume();
} else {
console.log('[TunnelResume] remote tried resuming finished connection', cid);
// wsHandlers.sendMessage(Packer.pack(opts, {message: 'no matching connection', code: 'E_NO_CONN'}, 'error'));
// var packBody = true;
// wsHandlers.sendMessage(Packer.packHeader(opts, {message: 'no matching connection', code: 'E_NO_CONN'}, 'error', packBody));
}
}
@ -366,7 +399,7 @@ function _connect(state) {
, _onConnectError: function (cid, opts, err) {
console.info("[_onConnectError] opening '" + cid + "' failed because " + err.message);
wsHandlers.sendMessage(Packer.pack(opts, null, 'error'));
wsHandlers.sendMessage(Packer.packHeader(opts, null, 'error'));
}
};

View File

@ -362,21 +362,47 @@ module.exports.assign = function (state, tun, cb) {
state._serveIndex = require('serve-index');
var serveIndex;
var serveStatic;
var dlStatic;
if (isFile) {
serveStatic = state._serveStatic(path.dirname(conf.handler), { dotfiles: 'allow', index: [ 'index.html' ] });
dlStatic = state._serveStatic(path.dirname(conf.handler), { acceptRanges: false, dotfiles: 'allow', index: [ 'index.html' ] });
serveIndex = function (req, res, next) { next(); };
isFile = path.basename(conf.handler);
} else {
serveStatic = state._serveStatic(conf.handler, { dotfiles: 'allow', index: [ 'index.html' ] });
serveIndex = state._serveIndex(conf.handler, { hidden: true, icons: true, view: 'tiles' });
dlStatic = state._serveStatic(conf.handler, { acceptRanges: false, dotfiles: 'allow', index: [ 'index.html' ] });
serveIndex = state._serveIndex(conf.handler, {
hidden: true, icons: true
, template: require('serve-tpl-attachment')({ privatefiles: 'ignore' })
});
}
handler = function (req, res) {
var qIndex = req.url.indexOf('?');
var fIndex;
var fname;
if (-1 === qIndex) {
qIndex = req.url.length;
}
req.querystring = req.url.substr(qIndex);
req.url = req.url.substr(0, qIndex);
req.query = require('querystring').parse(req.querystring.substr(1));
if (isFile) {
req.url = '/' + isFile;
}
serveStatic(req, res, function () {
serveIndex(req, res, state._finalHandler(req, res));
});
//console.log('[req.query]', req.url, req.query);
if (req.query.download) {
fIndex = req.url.lastIndexOf('/');
fname = req.url.substr(fIndex + 1);
res.setHeader('Content-Disposition', 'attachment; filename="'+decodeURIComponent(fname)+'"');
res.setHeader('Content-Type', 'application/octet-stream');
dlStatic(req, res, function () {
serveIndex(req, res, state._finalHandler(req, res));
});
} else {
serveStatic(req, res, function () {
serveIndex(req, res, state._finalHandler(req, res));
});
}
};
handlerservers[conf.handler] = http.createServer(handler);
handlerservers[conf.handler].emit('connection', tlsSocket);

View File

@ -8,6 +8,7 @@ module.exports = function (pkg) {
https.get(url, function (resp) {
var str = '';
resp.on('data', function (chunk) {
//var chunk = conn.read();
str += chunk.toString('utf8');
});
resp.on('end', function () {

View File

@ -1,6 +1,6 @@
{
"name": "telebit",
"version": "0.19.28",
"version": "0.20.0-wip",
"description": "Break out of localhost. Connect to any device from anywhere over any tcp port or securely in a browser. A secure tunnel. A poor man's reverse VPN.",
"main": "lib/remote.js",
"files": [
@ -59,15 +59,16 @@
"js-yaml": "^3.11.0",
"jsonwebtoken": "^7.1.9",
"mkdirp": "^0.5.1",
"proxy-packer": "^1.4.3",
"proxy-packer": "^2.0.2",
"ps-list": "^5.0.0",
"recase": "^1.0.4",
"redirect-https": "^1.1.5",
"serve-index": "^1.9.1",
"serve-static": "^1.13.2",
"serve-tpl-attachment": "^1.0.4",
"sni": "^1.0.0",
"socket-pair": "^1.0.3",
"ws": "^2.2.3"
"ws": "^2.3.1"
},
"trulyOptionalDependencies": {
"bluebird": "^3.5.1"