113 lines
		
	
	
		
			2.9 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
			
		
		
	
	
			113 lines
		
	
	
		
			2.9 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
| 'use strict';
 | |
| 
 | |
| var PromiseA = require('bluebird');
 | |
| //var dns = PromiseA.promisifyAll(require('dns'));
 | |
| var os = require('os');
 | |
| var requestAsync = require('./request');
 | |
| 
 | |
| module.exports = function (opts) {
 | |
|   var promises = [];
 | |
|   var interfaces = os.networkInterfaces();
 | |
|   var ifacenames = Object.keys(interfaces).filter(function (ifacename) {
 | |
|     // http://www.freedesktop.org/wiki/Software/systemd/PredictableNetworkInterfaceNames/
 | |
|     // https://wiki.archlinux.org/index.php/Network_configuration#Device_names
 | |
|     // we do not include tun and bridge devices because we're trying
 | |
|     // to see if any physical interface is internet-connected first
 | |
|     return /^(en|sl|wl|ww|eth|net|lan|wifi|inet)/.test(ifacename);
 | |
|   });
 | |
|   var ifaces = ifacenames.reduce(function (all, ifacename) {
 | |
|     var ifs = interfaces[ifacename];
 | |
| 
 | |
|     ifs.forEach(function (iface) {
 | |
|       if (!iface.internal && !/^fe80/.test(iface.address)) {
 | |
|         all.push(iface);
 | |
|       }
 | |
|     });
 | |
| 
 | |
|     return all;
 | |
|   }, []);
 | |
| 
 | |
|   /*
 | |
|   // TODO how to support servername
 | |
|   promises.push(dns.resolve4Async(hostname).then(function (ips) {
 | |
|     return ips.map(function (ip) {
 | |
|       return {
 | |
|         address: ip
 | |
|       , family: 'IPv4'
 | |
|       };
 | |
|     });
 | |
|   }));
 | |
| 
 | |
|   promises.push(dns.resolve6Async(hostname).then(function (ips) {
 | |
|     return ips.map(function (ip) {
 | |
|       return {
 | |
|         address: ip
 | |
|       , family: 'IPv6'
 | |
|       };
 | |
|     });
 | |
|   }));
 | |
|   */
 | |
| 
 | |
|   function parseIp(ip) {
 | |
|     if (!/\d+\.\d+\.\d+\.\d+/.test(ip) && !/\w+\:\w+/.test(ip)) {
 | |
|       return PromiseA.reject(new Error("bad response '" + ip + "'"));
 | |
|     }
 | |
| 
 | |
|     return ip;
 | |
|   }
 | |
| 
 | |
|   function ignoreEinval(err) {
 | |
|     if ('EINVAL' === err.code) {
 | |
|       if (opts.debug) {
 | |
|         console.warn('[HP] tried to bind to invalid address:');
 | |
|         console.warn(err.stack);
 | |
|       }
 | |
|       return null;
 | |
|     }
 | |
| 
 | |
|     return PromiseA.reject(err);
 | |
|   }
 | |
| 
 | |
|   if (opts.debug) {
 | |
|     console.log('[HP] external ip opts:');
 | |
|     console.log(opts);
 | |
| 
 | |
|     console.log('[HP] external ifaces:');
 | |
|     console.log(ifaces);
 | |
|   }
 | |
| 
 | |
|   ifaces.forEach(function (iface) {
 | |
|     promises.push(requestAsync({
 | |
|       family: iface.family
 | |
|     , method: 'GET'
 | |
|     , headers: {
 | |
|         Host: opts.hostname
 | |
|       }
 | |
|     , localAddress: iface.address
 | |
|     , servername: opts.hostname   // is this actually sent to tls.connect()?
 | |
|     , hostname: opts.hostname     // if so we could do the DNS ourselves
 | |
|                                   // and use the ip address here
 | |
|     , port: opts.port || 443
 | |
|     , pathname: opts.pathname || opts.path || '/'
 | |
|     }).then(parseIp, ignoreEinval).then(function (addr) {
 | |
|       return {
 | |
|         family: iface.family
 | |
|       , address: addr
 | |
|       , localAddress: iface.address
 | |
|       };
 | |
|     }));
 | |
|   });
 | |
| 
 | |
|   return PromiseA.all(promises).then(function (results) {
 | |
|     if (opts.debug) {
 | |
|       console.log('[HP] got all ip address types');
 | |
|       console.log(results);
 | |
|     }
 | |
| 
 | |
|     return results;
 | |
|   }, function (err) {
 | |
|     console.error('[HP] error');
 | |
|     console.error(err);
 | |
|   });
 | |
| };
 |