forked from coolaj86/telebit.js
		
	more command and control
This commit is contained in:
		
							parent
							
								
									ce7854b79d
								
							
						
					
					
						commit
						b97239b252
					
				
							
								
								
									
										300
									
								
								bin/telebit.js
									
									
									
									
									
								
							
							
						
						
									
										300
									
								
								bin/telebit.js
									
									
									
									
									
								
							| @ -3,12 +3,12 @@ | ||||
| 'use strict'; | ||||
| 
 | ||||
| var pkg = require('../package.json'); | ||||
| console.log(pkg.name, pkg.version); | ||||
| console.info(pkg.name, pkg.version); | ||||
| 
 | ||||
| var url = require('url'); | ||||
| var path = require('path'); | ||||
| var remote = require('../'); | ||||
| var state = {}; | ||||
| var http = require('http'); | ||||
| var state = { servernames: {}, ports: {} }; | ||||
| 
 | ||||
| var argv = process.argv.slice(2); | ||||
| 
 | ||||
| @ -29,11 +29,26 @@ function help() { | ||||
|   console.info(''); | ||||
|   console.info('Usage:'); | ||||
|   console.info(''); | ||||
|   console.info('\ttelebit --config <path>'); | ||||
|   console.info('\ttelebit [--config <path>] <module> <module-option>'); | ||||
|   console.info(''); | ||||
|   console.info('Example:'); | ||||
|   console.info('Examples:'); | ||||
|   console.info(''); | ||||
|   console.info('\ttelebit --config /etc/telebit/telebit.yml'); | ||||
|   console.info('\ttelebit --config ~/.config/telebit/telebit.yml status'); | ||||
|   console.info(''); | ||||
|   console.info('\ttelebit status'); | ||||
|   console.info('\ttelebit enable'); | ||||
|   console.info('\ttelebit disable'); | ||||
|   console.info(''); | ||||
|   console.info('\ttelebit list'); | ||||
|   console.info(''); | ||||
|   console.info('\ttelebit http 3000'); | ||||
|   console.info('\ttelebit tcp 5050'); | ||||
|   console.info(''); | ||||
|   console.info('\ttelebit http default'); | ||||
|   console.info('\ttelebit tcp default'); | ||||
|   console.info(''); | ||||
|   console.info('\ttelebit http /path/to/module'); | ||||
|   console.info('\ttelebit tcp /path/to/module'); | ||||
|   console.info(''); | ||||
|   console.info('Config:'); | ||||
|   console.info(''); | ||||
| @ -63,11 +78,13 @@ try { | ||||
| } catch(e) { | ||||
|   // ignore
 | ||||
| } | ||||
| var controlServer; | ||||
| require('fs').readFile(confpath, 'utf8', function (err, text) { | ||||
|   var config; | ||||
| 
 | ||||
|   var recase = require('recase').create({}); | ||||
|   var camelCopy = recase.camelCopy.bind(recase); | ||||
|   var snakeCopy = recase.snakeCopy.bind(recase); | ||||
| 
 | ||||
|   if (err) { | ||||
|     console.error("\nCouldn't load config:\n\n\t" + err.message + "\n"); | ||||
| @ -100,21 +117,49 @@ require('fs').readFile(confpath, 'utf8', function (err, text) { | ||||
|     console.warn("Choosing the first."); | ||||
|     console.warn(); | ||||
|   } | ||||
|   state.config.token = token; | ||||
|   state.token = token; | ||||
| 
 | ||||
|   function restartCmd() { | ||||
|   if (!state.config.servernames) { | ||||
|     state.config.servernames = {}; | ||||
|   } | ||||
|   if (!state.config.ports) { | ||||
|     state.config.ports = {}; | ||||
|   } | ||||
|   state.servernames = JSON.parse(JSON.stringify(state.config.servernames)); | ||||
|   state.ports = JSON.parse(JSON.stringify(state.config.ports)); | ||||
| 
 | ||||
|   function putConfig(service, args) { | ||||
|     var http = require('http'); | ||||
|     var req = http.get({ | ||||
|       socketPath: state.config.sock || defaultSockname | ||||
|     , method: 'POST' | ||||
|     , path: '/rpc/restart' | ||||
|     , path: '/rpc/' + service + '?_body=' + JSON.stringify(args) | ||||
|     }, function (resp) { | ||||
|       console.log('statusCode', resp.statusCode); | ||||
| 
 | ||||
|       function finish() { | ||||
|         if (200 !== resp.statusCode) { | ||||
|         console.warn("May not have restarted." | ||||
|           console.warn("'" + service + "' may have failed." | ||||
|            + " Consider peaking at the logs either with 'journalctl -xeu telebit' or /opt/telebit/var/log/error.log"); | ||||
|         } else { | ||||
|         console.log("restarted"); | ||||
|           if (body) { | ||||
|             console.info('Response'); | ||||
|             console.info(body); | ||||
|           } else { | ||||
|             console.info("👌"); | ||||
|           } | ||||
|         } | ||||
|       } | ||||
| 
 | ||||
|       var body = ''; | ||||
|       if (resp.headers['content-length']) { | ||||
|         resp.on('data', function (chunk) { | ||||
|           body += chunk.toString(); | ||||
|         }); | ||||
|         resp.on('end', function () { | ||||
|           finish(); | ||||
|         }); | ||||
|       } else { | ||||
|         finish(); | ||||
|       } | ||||
|     }); | ||||
|     req.on('error', function (err) { | ||||
| @ -124,30 +169,136 @@ require('fs').readFile(confpath, 'utf8', function (err, text) { | ||||
|     }); | ||||
|   } | ||||
| 
 | ||||
|   function controlServer() { | ||||
|     var http = require('http'); | ||||
|     var server = http.createServer(function (req, res) { | ||||
|   var tun; | ||||
|   function serveControls() { | ||||
|     if (!state.config.disable) { | ||||
|       tun = rawTunnel(); | ||||
|     } | ||||
|     controlServer = http.createServer(function (req, res) { | ||||
|       var opts = url.parse(req.url, true); | ||||
|       if (opts.query._body) { | ||||
|         try { | ||||
|           opts.body = JSON.parse(opts.query._body, true); | ||||
|         } catch(e) { | ||||
|           res.statusCode = 500; | ||||
|           res.end('{"error":{"message":"?_body={{bad_format}}"}}'); | ||||
|           return; | ||||
|         } | ||||
|       } | ||||
| 
 | ||||
|       if (/restart/.test(req.url)) { | ||||
|       if (/enable/.test(opts.path)) { | ||||
|         state.config.disable = undefined; | ||||
|         if (!tun) { tun = rawTunnel(); } | ||||
|         fs.writeFile(confpath, require('js-yaml').safeDump(snakeCopy(state.config)), function () { | ||||
|           if (err) { | ||||
|             res.statusCode = 500; | ||||
|             res.end('{"error":{"message":"Could not save config file. Perhaps you\'re not running as root?"}}'); | ||||
|             return; | ||||
|           } | ||||
|           res.end('{"success":true}'); | ||||
|         }); | ||||
|         return; | ||||
|       } | ||||
| 
 | ||||
|       if (/disable/.test(opts.path)) { | ||||
|         state.config.disable = true; | ||||
|         if (tun) { tun.end(); tun = null; } | ||||
|         fs.writeFile(confpath, require('js-yaml').safeDump(snakeCopy(state.config)), function () { | ||||
|           if (err) { | ||||
|             res.statusCode = 500; | ||||
|             res.end('{"error":{"message":"Could not save config file. Perhaps you\'re not running as root?"}}'); | ||||
|             return; | ||||
|           } | ||||
|           res.end('{"success":true}'); | ||||
|         }); | ||||
|         return; | ||||
|       } | ||||
| 
 | ||||
|       if (/status/.test(opts.path)) { | ||||
|         res.end('{"status":' + (state.config.disable ? 'disabled' : 'enabled') + '}'); | ||||
|         return; | ||||
|       } | ||||
| 
 | ||||
|       if (/restart/.test(opts.path)) { | ||||
|         tun.end(); | ||||
|         process.nextTick(function () { | ||||
|           server.close(function () { | ||||
|         res.end('{"success":true}'); | ||||
|         controlServer.close(function () { | ||||
|           // TODO closeAll other things
 | ||||
|           process.nextTick(function () { | ||||
|             // system daemon will restart the process
 | ||||
|             process.exit(); | ||||
|           }); | ||||
|         }); | ||||
|         return; | ||||
|       } | ||||
| 
 | ||||
|       if (/list/.test(opts.path)) { | ||||
|         res.end(JSON.stringify({ | ||||
|           servernames: state.servernames | ||||
|         , ports: state.ports | ||||
|         })); | ||||
|         return; | ||||
|       } | ||||
| 
 | ||||
|       if (/http/.test(opts.path)) { | ||||
|         if (!opts.body) { | ||||
|           res.statusCode = 422; | ||||
|           res.end('{"error":{"message":"needs more arguments"}}'); | ||||
|           return; | ||||
|         } | ||||
|         if (opts.body[1]) { | ||||
|           if (!state.servernames[opts.body[1]]) { | ||||
|             res.statusCode = 400; | ||||
|             res.end('{"error":{"message":"bad servername \'' + opts.body[1] + '\'"'); | ||||
|             return; | ||||
|           } | ||||
|           state.servernames[opts.body[1]].handler = opts.body[0]; | ||||
|         } else { | ||||
|           Object.keys(state.servernames).forEach(function (key) { | ||||
|             state.servernames[key].handler = opts.body[0]; | ||||
|           }); | ||||
|         } | ||||
|         res.end('{"success":true}'); | ||||
|         return; | ||||
|       } | ||||
| 
 | ||||
|       if (/tcp/.test(opts.path)) { | ||||
|         if (!opts.body) { | ||||
|           res.statusCode = 422; | ||||
|           res.end('{"error":{"message":"needs more arguments"}}'); | ||||
|           return; | ||||
|         } | ||||
| 
 | ||||
|         if (opts.body[1]) { | ||||
|           if (!state.servernames[opts.body[1]]) { | ||||
|             res.statusCode = 400; | ||||
|             res.end('{"error":{"message":"bad servername \'' + opts.body[1] + '\'"'); | ||||
|             return; | ||||
|           } | ||||
|           state.servernames[opts.body[1]].handler = opts.body[0]; | ||||
|         } else { | ||||
|           Object.keys(state.servernames).forEach(function (key) { | ||||
|             state.servernames[key].handler = opts.body[0]; | ||||
|           }); | ||||
|         } | ||||
|         res.end('{"success":true}'); | ||||
|         return; | ||||
|       } | ||||
| 
 | ||||
|       res.end('{"error":{"message":"unrecognized rpc"}}'); | ||||
|     }); | ||||
|     var pipename = (state.config.sock || defaultSockname); | ||||
|     var fs = require('fs'); | ||||
|     if (fs.existsSync(pipename)) { | ||||
|       fs.unlinkSync(pipename); | ||||
|     } | ||||
|     if (/^win/i.test(require('os').platform())) { | ||||
|       pipename = '\\\\?\\pipe' + pipename.replace(/\//, '\\'); | ||||
|     } | ||||
|     // mask is so that processes owned by other users
 | ||||
|     // can speak to this process, which is probably root-owned
 | ||||
|     var oldUmask = process.umask(0x0000); | ||||
|     server.listen({ | ||||
|     controlServer.listen({ | ||||
|       path: pipename | ||||
|     , writableAll: true | ||||
|     , readableAll: true | ||||
| @ -157,18 +308,46 @@ require('fs').readFile(confpath, 'utf8', function (err, text) { | ||||
|     }); | ||||
|   } | ||||
| 
 | ||||
|   console.log('argv', argv); | ||||
|   if (-1 !== argv.indexOf('restart')) { | ||||
|     restartCmd(); | ||||
|     return; | ||||
|   // Two styles:
 | ||||
|   //     http 3000
 | ||||
|   //     http modulename
 | ||||
|   function makeRpc(key) { | ||||
|     var cmdIndex = argv.indexOf(key); | ||||
|     if (-1 !== cmdIndex) { | ||||
|       putConfig(argv[cmdIndex], argv.slice(1)); | ||||
|       return true; | ||||
|     } | ||||
|   } | ||||
| 
 | ||||
|   controlServer(); | ||||
|   var tun = rawTunnel(); | ||||
|   if ([ 'status', 'enable', 'disable', 'restart', 'list' ].some(makeRpc)) { | ||||
|     return; | ||||
|   } | ||||
|   if ([ 'http', 'tcp' ].some(function (key) { | ||||
|     var cmdIndex = argv.indexOf(key); | ||||
|     if (-1 !== cmdIndex && argv[cmdIndex + 1]) { | ||||
|       putConfig(argv[cmdIndex], argv.slice(1)); | ||||
|       return true; | ||||
|     } | ||||
|   })) { | ||||
|     return true; | ||||
|   } | ||||
| 
 | ||||
|   serveControls(); | ||||
| }); | ||||
| 
 | ||||
| function connectTunnel() { | ||||
|   state.net = { | ||||
|   function sigHandler() { | ||||
|     console.info('Received kill signal. Attempting to exit cleanly...'); | ||||
| 
 | ||||
|     // We want to handle cleanup properly unless something is broken in our cleanup process
 | ||||
|     // that prevents us from exitting, in which case we want the user to be able to send
 | ||||
|     // the signal again and exit the way it normally would.
 | ||||
|     process.removeListener('SIGINT', sigHandler); | ||||
|     tun.end(); | ||||
|     controlServer.close(); | ||||
|   } | ||||
|   process.on('SIGINT', sigHandler); | ||||
|   state.net = state.net || { | ||||
|     createConnection: function (info, cb) { | ||||
|       // data is the hello packet / first chunk
 | ||||
|       // info = { data, servername, port, host, remoteFamily, remoteAddress, remotePort }
 | ||||
| @ -180,9 +359,8 @@ function connectTunnel() { | ||||
|   }; | ||||
| 
 | ||||
|   state.greenlock = state.config.greenlock || {}; | ||||
|   if (!state.config.sortingHat) { | ||||
|     state.config.sortingHat = path.resolve(__dirname, '..', 'lib/sorting-hat.js'); | ||||
|   } | ||||
|   state.sortingHat = state.config.sortingHat || path.resolve(__dirname, '..', 'lib/sorting-hat.js'); | ||||
| 
 | ||||
|   // TODO sortingHat.print(); ?
 | ||||
| 
 | ||||
|   if (state.config.email && !state.token) { | ||||
| @ -199,20 +377,23 @@ function connectTunnel() { | ||||
|     console.info(); | ||||
|   } | ||||
|   // TODO Check undefined vs false for greenlock config
 | ||||
|   var tun = remote.connect({ | ||||
|     relay: state.config.relay | ||||
|   , config: state.config | ||||
|   , _confpath: confpath | ||||
|   , sortingHat: state.config.sortingHat | ||||
|   , net: state.net | ||||
|   , insecure: state.config.relay_ignore_invalid_certificates | ||||
|   , token: state.token | ||||
|   , handlers: { | ||||
|   var remote = require('../'); | ||||
|   state.handlers = { | ||||
|     grant: function (grants) { | ||||
|       console.info(""); | ||||
|       console.info("Connect to your device by any of the following means:"); | ||||
|       console.info(""); | ||||
|       grants.forEach(function (arr) { | ||||
|         if ('https' === arr[0]) { | ||||
|           if (!state.servernames[arr[1]]) { | ||||
|             state.servernames[arr[1]] = {}; | ||||
|           } | ||||
|         } else if ('tcp' === arr[0]) { | ||||
|           if (!state.ports[arr[2]]) { | ||||
|             state.ports[arr[2]] = {}; | ||||
|           } | ||||
|         } | ||||
| 
 | ||||
|         if ('ssh+https' === arr[0]) { | ||||
|           console.info("SSH+HTTPS"); | ||||
|         } else if ('ssh' === arr[0]) { | ||||
| @ -222,7 +403,7 @@ function connectTunnel() { | ||||
|         } else if ('https' === arr[0]) { | ||||
|           console.info("HTTPS"); | ||||
|         } | ||||
|           console.log('\t' + arr[0] + '://' + arr[1] + (arr[2] ? (':' + arr[2]) : '')); | ||||
|         console.info('\t' + arr[0] + '://' + arr[1] + (arr[2] ? (':' + arr[2]) : '')); | ||||
|         if ('ssh+https' === arr[0]) { | ||||
|           console.info("\tex: ssh -o ProxyCommand='openssl s_client -connect %h:%p -quiet' " + arr[1] + " -p 443\n"); | ||||
|         } else if ('ssh' === arr[0]) { | ||||
| @ -243,8 +424,8 @@ function connectTunnel() { | ||||
|         console.error(e); | ||||
|       } | ||||
|     } | ||||
|     } | ||||
|   , greenlockConfig: { | ||||
|   }; | ||||
|   state.greenlockConfig = { | ||||
|     version: state.greenlock.version || 'draft-11' | ||||
|   , server: state.greenlock.server || 'https://acme-v02.api.letsencrypt.org/directory' | ||||
|   , communityMember: state.greenlock.communityMember || state.config.communityMember | ||||
| @ -270,24 +451,29 @@ function connectTunnel() { | ||||
| 
 | ||||
|       //cb(new Error("servername not found in allowed list"));
 | ||||
|     } | ||||
|     } | ||||
|   }; | ||||
|   state.insecure = state.config.relay_ignore_invalid_certificates; | ||||
|   // { relay, config, servernames, ports, sortingHat, net, insecure, token, handlers, greenlockConfig }
 | ||||
| 
 | ||||
|   var tun = remote.connect({ | ||||
|     relay: state.relay | ||||
|   , config: state.config | ||||
|   , sortingHat: state.sortingHat | ||||
|   , net: state.net | ||||
|   , insecure: state.insecure | ||||
|   , token: state.token | ||||
|   , servernames: state.servernames | ||||
|   , ports: state.ports | ||||
|   , handlers: state.handlers | ||||
|   , greenlockConfig: state.greenlockConfig | ||||
|   }); | ||||
| 
 | ||||
|   function sigHandler() { | ||||
|     console.info('Received kill signal. Attempting to exit cleanly...'); | ||||
| 
 | ||||
|     // We want to handle cleanup properly unless something is broken in our cleanup process
 | ||||
|     // that prevents us from exitting, in which case we want the user to be able to send
 | ||||
|     // the signal again and exit the way it normally would.
 | ||||
|     process.removeListener('SIGINT', sigHandler); | ||||
|     tun.end(); | ||||
|   } | ||||
|   process.on('SIGINT', sigHandler); | ||||
|   return tun; | ||||
| } | ||||
| 
 | ||||
| function rawTunnel() { | ||||
|   if (!state.config.relay) { | ||||
|   state.relay = state.config.relay; | ||||
|   if (!state.relay) { | ||||
|     throw new Error("'" + state._confpath + "' is missing 'relay'"); | ||||
|   } | ||||
| 
 | ||||
| @ -299,13 +485,13 @@ function rawTunnel() { | ||||
|   } | ||||
|   */ | ||||
| 
 | ||||
|   var location = url.parse(state.config.relay); | ||||
|   var location = url.parse(state.relay); | ||||
|   if (!location.protocol || /\./.test(location.protocol)) { | ||||
|     state.config.relay = 'wss://' + state.config.relay; | ||||
|     location = url.parse(state.config.relay); | ||||
|     state.relay = 'wss://' + state.relay; | ||||
|     location = url.parse(state.relay); | ||||
|   } | ||||
|   var aud = location.hostname + (location.port ? ':' + location.port : ''); | ||||
|   state.config.relay = location.protocol + '//' + aud; | ||||
|   state.relay = location.protocol + '//' + aud; | ||||
| 
 | ||||
|   if (!state.config.token && state.config.secret) { | ||||
|     var jwt = require('jsonwebtoken'); | ||||
|  | ||||
| @ -7,8 +7,13 @@ secret: ''                      # Shared Secret with Telebit Relay for authoriza | ||||
| #token: ''                       # Token created by Telebit Relay for authorization | ||||
| ssh_auto: 22                    # forward ssh-looking packets, from any connection, to port 22 | ||||
| servernames:                    # hostnames that direct to the Telebit Relay admin console | ||||
|   example.com: {} | ||||
|   example.net: {} | ||||
|   example.com: | ||||
|     handler: 3000 | ||||
|   example.net: | ||||
|     handler: /path/to/module | ||||
| ports: | ||||
|   5050: | ||||
|     handler: 54321 | ||||
| greenlock: | ||||
|   version: 'draft-11' | ||||
|   server: 'https://acme-staging-v02.api.letsencrypt.org/directory' | ||||
|  | ||||
							
								
								
									
										22
									
								
								lib/html/index.html
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										22
									
								
								lib/html/index.html
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,22 @@ | ||||
| <!DOCTYPE html> | ||||
| <html> | ||||
|   <head> | ||||
|     <title>Telebit</title> | ||||
|     <meta charset="utf-8"> | ||||
|   </head> | ||||
|   <body> | ||||
|     <script>document.body.hidden = true;</script> | ||||
| 
 | ||||
|     <h1>Welcome Home <!-- as in 127.0.0.1, y'know ;) --></h1> | ||||
| 
 | ||||
|     <h2>You've claimed <span class="js-servername">{{servername}}</span></h2> | ||||
|     <p>Here's same ways you can use it:</p> | ||||
|     <pre><code>telebit http 3000</code></pre> | ||||
| 
 | ||||
|     <h2>You've claimed <span class="js-serviceport">{{serviceport}}</span></h2> | ||||
|     <p>Here's same ways you can use it:</p> | ||||
|     <pre><code>#telebit tcp 3000</code></pre> | ||||
| 
 | ||||
|     <script src="js/app.js"></script> | ||||
|   </body> | ||||
| </html> | ||||
							
								
								
									
										36
									
								
								lib/html/js/app.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										36
									
								
								lib/html/js/app.js
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,36 @@ | ||||
| (function () { | ||||
| 'use strict'; | ||||
| 
 | ||||
| document.body.hidden = false; | ||||
| 
 | ||||
| var hash = window.location.hash.slice(1); | ||||
| 
 | ||||
| function parseQuery(search) { | ||||
|     var args = search.substring(1).split('&'); | ||||
|     var argsParsed = {}; | ||||
|     var i, arg, kvp, key, value; | ||||
| 
 | ||||
|     for (i=0; i < args.length; i++) { | ||||
| 
 | ||||
|         arg = args[i]; | ||||
| 
 | ||||
|         if (-1 === arg.indexOf('=')) { | ||||
| 
 | ||||
|             argsParsed[decodeURIComponent(arg).trim()] = true; | ||||
| 
 | ||||
|         } else { | ||||
| 
 | ||||
|             kvp = arg.split('='); | ||||
|             key = decodeURIComponent(kvp[0]).trim(); | ||||
|             value = decodeURIComponent(kvp[1]).trim(); | ||||
|             argsParsed[key] = value; | ||||
| 
 | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     return argsParsed; | ||||
| } | ||||
| 
 | ||||
| console.log(parseQuery(hash)); | ||||
| 
 | ||||
| }()); | ||||
| @ -1,3 +1,7 @@ | ||||
| 'use strict'; | ||||
| var os = require('os'); | ||||
| var path = require('path'); | ||||
| 
 | ||||
| module.exports.print = function (config) { | ||||
|   var services = { https: {}, http: {}, tcp: {} }; | ||||
|   // Note: the remote needs to know:
 | ||||
| @ -78,11 +82,13 @@ module.exports.assign = function (state, tun, cb) { | ||||
|     state.httpRedirectServer.emit('connection', socket); | ||||
|   }; | ||||
|   handlers.https = function (tlsSocket) { | ||||
|     console.log('Enccrypted', tlsSocket.encrypted, tlsSocket.remoteAddress, tlsSocket.remotePort); | ||||
|     console.log('Encrypted', tlsSocket.encrypted, tlsSocket.remoteAddress, tlsSocket.remotePort); | ||||
|     if (!state.defaultHttpServer) { | ||||
|       state._finalHandler = require('finalhandler'); | ||||
|       state._serveStatic = require('serve-static'); | ||||
|       state._defaultServe = state._serveStatic(path.join(__dirname, 'html')); | ||||
|       state.defaultHttpServer = require('http').createServer(function (req, res) { | ||||
|         console.log('[hit http/s server]'); | ||||
|         res.end('Hello, Encrypted Tunnel World!'); | ||||
|         state._defaultServe(req, res, state._finalHandler(req, res)); | ||||
|       }); | ||||
|     } | ||||
|     state.defaultHttpServer.emit('connection', tlsSocket); | ||||
| @ -183,26 +189,27 @@ module.exports.assign = function (state, tun, cb) { | ||||
|     } | ||||
|     var handle = tun.name || tun.port; | ||||
|     var handler; | ||||
|     var handlerpath = conf.handler; | ||||
|     var path = require('path'); | ||||
|     var homedir = require('os').homedir(); | ||||
|     var homedir = os.homedir(); | ||||
|     var localshare = path.join(homedir, '.local/share/telebit/apps'); | ||||
| 
 | ||||
|     if (/^~/.test(conf.handler)) { | ||||
|       conf.handler = require('path').join(require('os').homedir(), conf.handler.replace(/^~(\/?)/, '')); | ||||
|     if (/^~/.test(handlerpath)) { | ||||
|       handlerpath = path.join(homedir, handlerpath.replace(/^~(\/?)/, '')); | ||||
|     } | ||||
| 
 | ||||
|     try { | ||||
|       handler = require(conf.handler); | ||||
|       console.info("Handling '" + handle + ":" + id + "' with '" + conf.handler + "'"); | ||||
|       handler = require(handlerpath); | ||||
|       console.info("Handling '" + handle + ":" + id + "' with '" + handlerpath + "'"); | ||||
|       handler(tlsSocket, tun, id); | ||||
|     } catch(e1) { | ||||
|       try { | ||||
|         handler = require(path.join(localshare, conf.handler)); | ||||
|         console.info("Handling '" + handle + ":" + id + "' with '" + conf.handler + "'"); | ||||
|         handler = require(path.join(localshare, handlerpath)); | ||||
|         console.info("Handling '" + handle + ":" + id + "' with '" + handlerpath + "'"); | ||||
|         handler(tlsSocket, tun, id); | ||||
|       } catch(e2) { | ||||
|         console.error("Failed to load '" + conf.handler + "':", e1.message); | ||||
|         console.error("Failed to load '" + path.join(localshare, conf.handler) + "':", e2.message); | ||||
|         console.error("Failed to load '" + handlerpath + "':", e1.message); | ||||
|         console.error("Failed to load '" + path.join(localshare, handlerpath) + "':", e2.message); | ||||
|         console.warn("Using default handler for '" + handle + ":" + id + "'"); | ||||
|         handlers.https(tlsSocket, tun, id); | ||||
|       } | ||||
| @ -231,7 +238,7 @@ module.exports.assign = function (state, tun, cb) { | ||||
|           defineProps(tlsSocket, addr); | ||||
|           //console.log('[hit tls server]', tlsSocket.remoteFamily, tlsSocket.remoteAddress, tlsSocket.remotePort, tlsSocket.localPort);
 | ||||
|           //console.log(addr);
 | ||||
|           var conf = state.config.servernames[tlsSocket.servername]; | ||||
|           var conf = state.servernames[tlsSocket.servername]; | ||||
|           tlsSocket.once('data', function (firstChunk) { | ||||
|             tlsSocket.pause(); | ||||
|             //tlsSocket.unshift(firstChunk);
 | ||||
| @ -246,7 +253,7 @@ module.exports.assign = function (state, tun, cb) { | ||||
|                 return; | ||||
|               } | ||||
| 
 | ||||
|               if (!conf || !conf.handler) { | ||||
|               if (!conf || !conf.handler || 'none' === conf.handler) { | ||||
|                 console.log('https default handler'); | ||||
|                 handlers.https(tlsSocket); | ||||
|                 return; | ||||
| @ -276,11 +283,6 @@ module.exports.assign = function (state, tun, cb) { | ||||
|     return; | ||||
|   } | ||||
| 
 | ||||
|   if (!state.config.servernames) { | ||||
|     state.config.servernames = {}; | ||||
|   } | ||||
| 
 | ||||
| 
 | ||||
|   if ('http' === tun.service || 'https' === tun.service) { | ||||
|     if (!tun.name) { | ||||
|       cb(new Error("No routing information for ':tun_id'. Service '" + tun.service + "' is missing 'name'.")); | ||||
| @ -290,13 +292,13 @@ module.exports.assign = function (state, tun, cb) { | ||||
| 
 | ||||
|   if ('http' === tun.service) { | ||||
|     // TODO match *.example.com
 | ||||
|     handled = Object.keys(state.config.servernames).some(function (sn) { | ||||
|     handled = Object.keys(state.servernames).some(function (sn) { | ||||
|       if (sn !== tun.name) { return; } | ||||
| 
 | ||||
|       console.log('Found config match for PLAIN', tun.name); | ||||
|       if (!state.config.servernames[sn]) { return; } | ||||
|       if (!state.servernames[sn]) { return; } | ||||
| 
 | ||||
|       if (false === state.config.servernames[sn].terminate) { | ||||
|       if (false === state.servernames[sn].terminate) { | ||||
|         cb(new Error("insecure http not supported yet")); | ||||
|         return true; | ||||
|       } | ||||
| @ -313,13 +315,13 @@ module.exports.assign = function (state, tun, cb) { | ||||
| 
 | ||||
|   if ('https' === tun.service) { | ||||
|     // TODO match *.example.com
 | ||||
|     handled = Object.keys(state.config.servernames).some(function (sn) { | ||||
|     handled = Object.keys(state.servernames).some(function (sn) { | ||||
|       if (sn !== tun.name) { return; } | ||||
| 
 | ||||
|       console.log('Found config match for TLS', tun.name); | ||||
|       if (!state.config.servernames[sn]) { return; } | ||||
|       if (!state.servernames[sn]) { return; } | ||||
| 
 | ||||
|       if (false === state.config.servernames[sn].terminate) { | ||||
|       if (false === state.servernames[sn].terminate) { | ||||
|         cb(new Error("insecure http not supported yet")); | ||||
|         return true; | ||||
|       } | ||||
| @ -339,7 +341,15 @@ module.exports.assign = function (state, tun, cb) { | ||||
|       if (conn) { cb(null, conn); return; } | ||||
|       // TODO add TCP handlers
 | ||||
|       console.log('Using echo server for tcp'); | ||||
|       var conf = state.ports[tun.serviceport]; | ||||
|       if (!conf || !conf.handler || 'none' === conf.handler) { | ||||
|         echoTcp(cb); | ||||
|       } | ||||
| 
 | ||||
|       var Packer = require('proxy-packer'); | ||||
|       //var addr = Packer.socketToAddr(conn);
 | ||||
|       var id = Packer.addrToId(tun); | ||||
|       invokeHandler(conf, conn, tun, id); | ||||
|     }); | ||||
|     return; | ||||
|   } | ||||
|  | ||||
| @ -49,12 +49,14 @@ | ||||
|   "dependencies": { | ||||
|     "bluebird": "^3.5.1", | ||||
|     "commander": "^2.9.0", | ||||
|     "finalhandler": "^1.1.1", | ||||
|     "greenlock": "^2.2.19", | ||||
|     "js-yaml": "^3.11.0", | ||||
|     "jsonwebtoken": "^7.1.9", | ||||
|     "proxy-packer": "^1.4.3", | ||||
|     "recase": "^1.0.4", | ||||
|     "redirect-https": "^1.1.5", | ||||
|     "serve-static": "^1.13.2", | ||||
|     "sni": "^1.0.0", | ||||
|     "socket-pair": "^1.0.3", | ||||
|     "ws": "^2.2.3" | ||||
|  | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user