implemented proxying decrypted TLS streams in raw form
This commit is contained in:
		
							vanhempi
							
								
									0ef845f2d5
								
							
						
					
					
						commit
						138f59bea3
					
				@ -73,6 +73,14 @@ Object.keys(moduleSchemas).forEach(function (name) {
 | 
				
			|||||||
  validator.addSchema(schema, schema.id);
 | 
					  validator.addSchema(schema, schema.id);
 | 
				
			||||||
});
 | 
					});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					function addDomainRequirement(itemSchema) {
 | 
				
			||||||
 | 
					  var result = Object.assign({}, itemSchema);
 | 
				
			||||||
 | 
					  result.required = (result.required || []).concat('domains');
 | 
				
			||||||
 | 
					  result.properties = Object.assign({}, result.properties);
 | 
				
			||||||
 | 
					  result.properties.domains = { type: 'array', items: { type: 'string' }, minLength: 1};
 | 
				
			||||||
 | 
					  return result;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
function toSchemaRef(name) {
 | 
					function toSchemaRef(name) {
 | 
				
			||||||
  return { '$ref': '/modules/'+name };
 | 
					  return { '$ref': '/modules/'+name };
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@ -84,12 +92,11 @@ var moduleRefs = {
 | 
				
			|||||||
, ddns: [ 'dns@oauth3.org' ].map(toSchemaRef)
 | 
					, ddns: [ 'dns@oauth3.org' ].map(toSchemaRef)
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
function addDomainRequirement(itemSchema) {
 | 
					// TCP is a bit special in that it has a module that doesn't operate based on domain name
 | 
				
			||||||
  itemSchema.required = (itemSchema.required || []).concat('domains');
 | 
					// (ie forward), and a modules that does (ie proxy). It therefore has different module
 | 
				
			||||||
  itemSchema.properties = itemSchema.properties || {};
 | 
					// when part of the `domains` config, and when not part of the `domains` config the proxy
 | 
				
			||||||
  itemSchema.properties.domains = { type: 'array', items: { type: 'string' }, minLength: 1};
 | 
					// modules must have the `domains` property while forward should not have it.
 | 
				
			||||||
  return itemSchema;
 | 
					moduleRefs.tcp.push(addDomainRequirement(toSchemaRef('proxy')));
 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
var domainSchema = {
 | 
					var domainSchema = {
 | 
				
			||||||
  type: 'array'
 | 
					  type: 'array'
 | 
				
			||||||
@ -104,6 +111,7 @@ var domainSchema = {
 | 
				
			|||||||
          tls:  { type: 'array', items: { oneOf: moduleRefs.tls }}
 | 
					          tls:  { type: 'array', items: { oneOf: moduleRefs.tls }}
 | 
				
			||||||
        , http: { type: 'array', items: { oneOf: moduleRefs.http }}
 | 
					        , http: { type: 'array', items: { oneOf: moduleRefs.http }}
 | 
				
			||||||
        , ddns: { type: 'array', items: { oneOf: moduleRefs.ddns }}
 | 
					        , ddns: { type: 'array', items: { oneOf: moduleRefs.ddns }}
 | 
				
			||||||
 | 
					        , tcp:  { type: 'array', items: { oneOf: ['proxy'].map(toSchemaRef)}}
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
      , additionalProperties: false
 | 
					      , additionalProperties: false
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
@ -185,7 +193,7 @@ var ddnsSchema = {
 | 
				
			|||||||
      , token_id: { type: 'string'}
 | 
					      , token_id: { type: 'string'}
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  , modules: { type: 'array', items: { oneOf: moduleRefs.ddns }}
 | 
					  , modules: { type: 'array', items: addDomainRequirement({ oneOf: moduleRefs.ddns })}
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
var socks5Schema = {
 | 
					var socks5Schema = {
 | 
				
			||||||
@ -293,6 +301,7 @@ class DomainList extends IdList {
 | 
				
			|||||||
        http: new ModuleList((dom.modules || {}).http)
 | 
					        http: new ModuleList((dom.modules || {}).http)
 | 
				
			||||||
      , tls:  new ModuleList((dom.modules || {}).tls)
 | 
					      , tls:  new ModuleList((dom.modules || {}).tls)
 | 
				
			||||||
      , ddns: new ModuleList((dom.modules || {}).ddns)
 | 
					      , ddns: new ModuleList((dom.modules || {}).ddns)
 | 
				
			||||||
 | 
					      , tcp:  new ModuleList((dom.modules || {}).tcp)
 | 
				
			||||||
      };
 | 
					      };
 | 
				
			||||||
    });
 | 
					    });
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
@ -309,6 +318,7 @@ class DomainList extends IdList {
 | 
				
			|||||||
      http: new ModuleList()
 | 
					      http: new ModuleList()
 | 
				
			||||||
    , tls:  new ModuleList()
 | 
					    , tls:  new ModuleList()
 | 
				
			||||||
    , ddns: new ModuleList()
 | 
					    , ddns: new ModuleList()
 | 
				
			||||||
 | 
					    , tcp:  new ModuleList()
 | 
				
			||||||
    };
 | 
					    };
 | 
				
			||||||
    // We add these after instead of in the constructor to run the validation and manipulation
 | 
					    // We add these after instead of in the constructor to run the validation and manipulation
 | 
				
			||||||
    // in the ModList add function since these are all new modules.
 | 
					    // in the ModList add function since these are all new modules.
 | 
				
			||||||
 | 
				
			|||||||
@ -6,13 +6,75 @@ module.exports.create = function (deps, config) {
 | 
				
			|||||||
  //var PromiseA = global.Promise;
 | 
					  //var PromiseA = global.Promise;
 | 
				
			||||||
  var PromiseA = require('bluebird');
 | 
					  var PromiseA = require('bluebird');
 | 
				
			||||||
  var listeners = require('./servers').listeners;
 | 
					  var listeners = require('./servers').listeners;
 | 
				
			||||||
 | 
					  var domainUtils = require('./domain-utils');
 | 
				
			||||||
  var modules;
 | 
					  var modules;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  var addrProperties = [
 | 
				
			||||||
 | 
					    'remoteAddress'
 | 
				
			||||||
 | 
					  , 'remotePort'
 | 
				
			||||||
 | 
					  , 'remoteFamily'
 | 
				
			||||||
 | 
					  , 'localAddress'
 | 
				
			||||||
 | 
					  , 'localPort'
 | 
				
			||||||
 | 
					  , 'localFamily'
 | 
				
			||||||
 | 
					  ];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  function nameMatchesDomains(name, domainList) {
 | 
				
			||||||
 | 
					    return domainList.some(function (pattern) {
 | 
				
			||||||
 | 
					      return domainUtils.match(pattern, name);
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  function loadModules() {
 | 
					  function loadModules() {
 | 
				
			||||||
    modules = {};
 | 
					    modules = {};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    modules.tls = require('./modules/tls').create(deps, config, netHandler);
 | 
					    modules.tls  = require('./modules/tls').create(deps, config, tcpHandler);
 | 
				
			||||||
    modules.http = require('./modules/http.js').create(deps, config, modules.tls.middleware);
 | 
					    modules.http = require('./modules/http').create(deps, config, modules.tls.middleware);
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  function checkTcpProxy(conn, opts) {
 | 
				
			||||||
 | 
					    var proxied = false;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // TCP Proxying (ie forwarding based on domain name not incoming port) only works for
 | 
				
			||||||
 | 
					    // TLS wrapped connections, so if the opts don't give us a servername or don't tell us
 | 
				
			||||||
 | 
					    // this is the decrypted side of a TLS connection we can't handle it here.
 | 
				
			||||||
 | 
					    if (!opts.servername || !opts.encrypted) { return proxied; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    function proxy(mod) {
 | 
				
			||||||
 | 
					      // First thing we need to add to the connection options is where to proxy the connection to
 | 
				
			||||||
 | 
					      var newConnOpts = domainUtils.separatePort(mod.address || '');
 | 
				
			||||||
 | 
					      newConnOpts.port = newConnOpts.port || mod.port;
 | 
				
			||||||
 | 
					      newConnOpts.host = newConnOpts.host || mod.host || 'localhost';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      // Then we add all of the connection address information. We need to prefix all of the
 | 
				
			||||||
 | 
					      // properties with '_' so we can provide the information to any connection `createConnection`
 | 
				
			||||||
 | 
					      // implementation but not have the default implementation try to bind the same local port.
 | 
				
			||||||
 | 
					      addrProperties.forEach(function (name) {
 | 
				
			||||||
 | 
					        newConnOpts['_' + name] = opts[name] || opts['_'+name] || conn[name] || conn['_'+name];
 | 
				
			||||||
 | 
					      });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      deps.proxy(conn, newConnOpts);
 | 
				
			||||||
 | 
					      return true;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    proxied = config.domains.some(function (dom) {
 | 
				
			||||||
 | 
					      if (!dom.modules || !Array.isArray(dom.modules.tcp)) { return false; }
 | 
				
			||||||
 | 
					      if (!nameMatchesDomains(opts.servername, dom.names)) { return false; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      return dom.modules.tcp.some(function (mod) {
 | 
				
			||||||
 | 
					        if (mod.type !== 'proxy') { return false; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return proxy(mod);
 | 
				
			||||||
 | 
					      });
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    proxied = proxied || config.tcp.modules.some(function (mod) {
 | 
				
			||||||
 | 
					      if (mod.type !== 'proxy') { return false; }
 | 
				
			||||||
 | 
					      if (!nameMatchesDomains(opts.servername, mod.domains)) { return false; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      return proxy(mod);
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return proxied;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  // opts = { servername, encrypted, peek, data, remoteAddress, remotePort }
 | 
					  // opts = { servername, encrypted, peek, data, remoteAddress, remotePort }
 | 
				
			||||||
@ -52,26 +114,28 @@ module.exports.create = function (deps, config) {
 | 
				
			|||||||
    console.warn('failed to identify protocol from first chunk', firstChunk);
 | 
					    console.warn('failed to identify protocol from first chunk', firstChunk);
 | 
				
			||||||
    conn.destroy();
 | 
					    conn.destroy();
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
  function netHandler(conn, opts) {
 | 
					  function tcpHandler(conn, opts) {
 | 
				
			||||||
    function getProp(name) {
 | 
					    function getProp(name) {
 | 
				
			||||||
      return opts[name] || opts['_'+name] || conn[name] || conn['_'+name];
 | 
					      return opts[name] || opts['_'+name] || conn[name] || conn['_'+name];
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    opts = opts || {};
 | 
					    opts = opts || {};
 | 
				
			||||||
    var logName = getProp('remoteAddress') + ':' + getProp('remotePort') + ' -> ' +
 | 
					    var logName = getProp('remoteAddress') + ':' + getProp('remotePort') + ' -> ' +
 | 
				
			||||||
                  getProp('localAddress')  + ':' + getProp('localPort');
 | 
					                  getProp('localAddress')  + ':' + getProp('localPort');
 | 
				
			||||||
    console.log('[netHandler]', logName, 'encrypted: '+opts.encrypted);
 | 
					    console.log('[tcpHandler]', logName, 'connection started - encrypted: ' + (opts.encrypted || false));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    var start = Date.now();
 | 
					    var start = Date.now();
 | 
				
			||||||
    conn.on('timeout', function () {
 | 
					    conn.on('timeout', function () {
 | 
				
			||||||
      console.log('[netHandler]', logName, 'connection timed out', (Date.now()-start)/1000);
 | 
					      console.log('[tcpHandler]', logName, 'connection timed out', (Date.now()-start)/1000);
 | 
				
			||||||
    });
 | 
					    });
 | 
				
			||||||
    conn.on('end', function () {
 | 
					    conn.on('end', function () {
 | 
				
			||||||
      console.log('[netHandler]', logName, 'connection ended', (Date.now()-start)/1000);
 | 
					      console.log('[tcpHandler]', logName, 'connection ended', (Date.now()-start)/1000);
 | 
				
			||||||
    });
 | 
					    });
 | 
				
			||||||
    conn.on('close', function () {
 | 
					    conn.on('close', function () {
 | 
				
			||||||
      console.log('[netHandler]', logName, 'connection closed', (Date.now()-start)/1000);
 | 
					      console.log('[tcpHandler]', logName, 'connection closed', (Date.now()-start)/1000);
 | 
				
			||||||
    });
 | 
					    });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (checkTcpProxy(conn, opts)) { return; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // XXX PEEK COMMENT XXX
 | 
					    // XXX PEEK COMMENT XXX
 | 
				
			||||||
    // TODO we can have our cake and eat it too
 | 
					    // TODO we can have our cake and eat it too
 | 
				
			||||||
    // we can skip the need to wrap the TLS connection twice
 | 
					    // we can skip the need to wrap the TLS connection twice
 | 
				
			||||||
@ -95,7 +159,7 @@ module.exports.create = function (deps, config) {
 | 
				
			|||||||
    });
 | 
					    });
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  function dnsListener(port, msg) {
 | 
					  function udpHandler(port, msg) {
 | 
				
			||||||
    if (!Array.isArray(config.udp.modules)) {
 | 
					    if (!Array.isArray(config.udp.modules)) {
 | 
				
			||||||
      return;
 | 
					      return;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
@ -123,10 +187,8 @@ module.exports.create = function (deps, config) {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    return function (conn) {
 | 
					    return function (conn) {
 | 
				
			||||||
      var newConnOpts = {};
 | 
					      var newConnOpts = {};
 | 
				
			||||||
      ['remote', 'local'].forEach(function (end) {
 | 
					      addrProperties.forEach(function (name) {
 | 
				
			||||||
        ['Family', 'Address', 'Port'].forEach(function (name) {
 | 
					        newConnOpts['_'+name] = conn[name];
 | 
				
			||||||
          newConnOpts['_'+end+name] = conn[end+name];
 | 
					 | 
				
			||||||
        });
 | 
					 | 
				
			||||||
      });
 | 
					      });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      deps.proxy(conn, Object.assign(newConnOpts, dest));
 | 
					      deps.proxy(conn, Object.assign(newConnOpts, dest));
 | 
				
			||||||
@ -176,7 +238,7 @@ module.exports.create = function (deps, config) {
 | 
				
			|||||||
        } catch(e) {
 | 
					        } catch(e) {
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        netHandler(reader, wrapOpts);
 | 
					        tcpHandler(reader, wrapOpts);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        process.nextTick(function () {
 | 
					        process.nextTick(function () {
 | 
				
			||||||
          // this cb will cause the stream to emit its (actually) first data event
 | 
					          // this cb will cause the stream to emit its (actually) first data event
 | 
				
			||||||
@ -217,19 +279,19 @@ module.exports.create = function (deps, config) {
 | 
				
			|||||||
        listenPromises.push(listeners.tcp.add(port, forwarder));
 | 
					        listenPromises.push(listeners.tcp.add(port, forwarder));
 | 
				
			||||||
      });
 | 
					      });
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    else {
 | 
					    else if (mod.type !== 'proxy') {
 | 
				
			||||||
      console.warn('unknown TCP module specified', mod);
 | 
					      console.warn('unknown TCP module specified', mod);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  });
 | 
					  });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  var portList = Object.keys(tcpPortMap).map(Number).sort();
 | 
					  var portList = Object.keys(tcpPortMap).map(Number).sort();
 | 
				
			||||||
  portList.forEach(function (port) {
 | 
					  portList.forEach(function (port) {
 | 
				
			||||||
    listenPromises.push(listeners.tcp.add(port, netHandler));
 | 
					    listenPromises.push(listeners.tcp.add(port, tcpHandler));
 | 
				
			||||||
  });
 | 
					  });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  if (config.udp.bind) {
 | 
					  if (config.udp.bind) {
 | 
				
			||||||
    config.udp.bind.forEach(function (port) {
 | 
					    config.udp.bind.forEach(function (port) {
 | 
				
			||||||
      listenPromises.push(listeners.udp.add(port, dnsListener.bind(port)));
 | 
					      listenPromises.push(listeners.udp.add(port, udpHandler.bind(port)));
 | 
				
			||||||
    });
 | 
					    });
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
		Ladataan…
	
	
			
			x
			
			
		
	
		Viittaa uudesa ongelmassa
	
	Block a user