ip.js 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416
  1. 'use strict';
  2. var ip = exports;
  3. var Buffer = require('buffer').Buffer;
  4. var os = require('os');
  5. ip.toBuffer = function(ip, buff, offset) {
  6. offset = ~~offset;
  7. var result;
  8. if (this.isV4Format(ip)) {
  9. result = buff || new Buffer(offset + 4);
  10. ip.split(/\./g).map(function(byte) {
  11. result[offset++] = parseInt(byte, 10) & 0xff;
  12. });
  13. } else if (this.isV6Format(ip)) {
  14. var sections = ip.split(':', 8);
  15. var i;
  16. for (i = 0; i < sections.length; i++) {
  17. var isv4 = this.isV4Format(sections[i]);
  18. var v4Buffer;
  19. if (isv4) {
  20. v4Buffer = this.toBuffer(sections[i]);
  21. sections[i] = v4Buffer.slice(0, 2).toString('hex');
  22. }
  23. if (v4Buffer && ++i < 8) {
  24. sections.splice(i, 0, v4Buffer.slice(2, 4).toString('hex'));
  25. }
  26. }
  27. if (sections[0] === '') {
  28. while (sections.length < 8) sections.unshift('0');
  29. } else if (sections[sections.length - 1] === '') {
  30. while (sections.length < 8) sections.push('0');
  31. } else if (sections.length < 8) {
  32. for (i = 0; i < sections.length && sections[i] !== ''; i++);
  33. var argv = [ i, 1 ];
  34. for (i = 9 - sections.length; i > 0; i--) {
  35. argv.push('0');
  36. }
  37. sections.splice.apply(sections, argv);
  38. }
  39. result = buff || new Buffer(offset + 16);
  40. for (i = 0; i < sections.length; i++) {
  41. var word = parseInt(sections[i], 16);
  42. result[offset++] = (word >> 8) & 0xff;
  43. result[offset++] = word & 0xff;
  44. }
  45. }
  46. if (!result) {
  47. throw Error('Invalid ip address: ' + ip);
  48. }
  49. return result;
  50. };
  51. ip.toString = function(buff, offset, length) {
  52. offset = ~~offset;
  53. length = length || (buff.length - offset);
  54. var result = [];
  55. if (length === 4) {
  56. // IPv4
  57. for (var i = 0; i < length; i++) {
  58. result.push(buff[offset + i]);
  59. }
  60. result = result.join('.');
  61. } else if (length === 16) {
  62. // IPv6
  63. for (var i = 0; i < length; i += 2) {
  64. result.push(buff.readUInt16BE(offset + i).toString(16));
  65. }
  66. result = result.join(':');
  67. result = result.replace(/(^|:)0(:0)*:0(:|$)/, '$1::$3');
  68. result = result.replace(/:{3,4}/, '::');
  69. }
  70. return result;
  71. };
  72. var ipv4Regex = /^(\d{1,3}\.){3,3}\d{1,3}$/;
  73. var ipv6Regex =
  74. /^(::)?(((\d{1,3}\.){3}(\d{1,3}){1})?([0-9a-f]){0,4}:{0,2}){1,8}(::)?$/i;
  75. ip.isV4Format = function(ip) {
  76. return ipv4Regex.test(ip);
  77. };
  78. ip.isV6Format = function(ip) {
  79. return ipv6Regex.test(ip);
  80. };
  81. function _normalizeFamily(family) {
  82. return family ? family.toLowerCase() : 'ipv4';
  83. }
  84. ip.fromPrefixLen = function(prefixlen, family) {
  85. if (prefixlen > 32) {
  86. family = 'ipv6';
  87. } else {
  88. family = _normalizeFamily(family);
  89. }
  90. var len = 4;
  91. if (family === 'ipv6') {
  92. len = 16;
  93. }
  94. var buff = new Buffer(len);
  95. for (var i = 0, n = buff.length; i < n; ++i) {
  96. var bits = 8;
  97. if (prefixlen < 8) {
  98. bits = prefixlen;
  99. }
  100. prefixlen -= bits;
  101. buff[i] = ~(0xff >> bits) & 0xff;
  102. }
  103. return ip.toString(buff);
  104. };
  105. ip.mask = function(addr, mask) {
  106. addr = ip.toBuffer(addr);
  107. mask = ip.toBuffer(mask);
  108. var result = new Buffer(Math.max(addr.length, mask.length));
  109. var i = 0;
  110. // Same protocol - do bitwise and
  111. if (addr.length === mask.length) {
  112. for (i = 0; i < addr.length; i++) {
  113. result[i] = addr[i] & mask[i];
  114. }
  115. } else if (mask.length === 4) {
  116. // IPv6 address and IPv4 mask
  117. // (Mask low bits)
  118. for (i = 0; i < mask.length; i++) {
  119. result[i] = addr[addr.length - 4 + i] & mask[i];
  120. }
  121. } else {
  122. // IPv6 mask and IPv4 addr
  123. for (var i = 0; i < result.length - 6; i++) {
  124. result[i] = 0;
  125. }
  126. // ::ffff:ipv4
  127. result[10] = 0xff;
  128. result[11] = 0xff;
  129. for (i = 0; i < addr.length; i++) {
  130. result[i + 12] = addr[i] & mask[i + 12];
  131. }
  132. i = i + 12;
  133. }
  134. for (; i < result.length; i++)
  135. result[i] = 0;
  136. return ip.toString(result);
  137. };
  138. ip.cidr = function(cidrString) {
  139. var cidrParts = cidrString.split('/');
  140. var addr = cidrParts[0];
  141. if (cidrParts.length !== 2)
  142. throw new Error('invalid CIDR subnet: ' + addr);
  143. var mask = ip.fromPrefixLen(parseInt(cidrParts[1], 10));
  144. return ip.mask(addr, mask);
  145. };
  146. ip.subnet = function(addr, mask) {
  147. var networkAddress = ip.toLong(ip.mask(addr, mask));
  148. // Calculate the mask's length.
  149. var maskBuffer = ip.toBuffer(mask);
  150. var maskLength = 0;
  151. for (var i = 0; i < maskBuffer.length; i++) {
  152. if (maskBuffer[i] === 0xff) {
  153. maskLength += 8;
  154. } else {
  155. var octet = maskBuffer[i] & 0xff;
  156. while (octet) {
  157. octet = (octet << 1) & 0xff;
  158. maskLength++;
  159. }
  160. }
  161. }
  162. var numberOfAddresses = Math.pow(2, 32 - maskLength);
  163. return {
  164. networkAddress: ip.fromLong(networkAddress),
  165. firstAddress: numberOfAddresses <= 2 ?
  166. ip.fromLong(networkAddress) :
  167. ip.fromLong(networkAddress + 1),
  168. lastAddress: numberOfAddresses <= 2 ?
  169. ip.fromLong(networkAddress + numberOfAddresses - 1) :
  170. ip.fromLong(networkAddress + numberOfAddresses - 2),
  171. broadcastAddress: ip.fromLong(networkAddress + numberOfAddresses - 1),
  172. subnetMask: mask,
  173. subnetMaskLength: maskLength,
  174. numHosts: numberOfAddresses <= 2 ?
  175. numberOfAddresses : numberOfAddresses - 2,
  176. length: numberOfAddresses,
  177. contains: function(other) {
  178. return networkAddress === ip.toLong(ip.mask(other, mask));
  179. }
  180. };
  181. };
  182. ip.cidrSubnet = function(cidrString) {
  183. var cidrParts = cidrString.split('/');
  184. var addr = cidrParts[0];
  185. if (cidrParts.length !== 2)
  186. throw new Error('invalid CIDR subnet: ' + addr);
  187. var mask = ip.fromPrefixLen(parseInt(cidrParts[1], 10));
  188. return ip.subnet(addr, mask);
  189. };
  190. ip.not = function(addr) {
  191. var buff = ip.toBuffer(addr);
  192. for (var i = 0; i < buff.length; i++) {
  193. buff[i] = 0xff ^ buff[i];
  194. }
  195. return ip.toString(buff);
  196. };
  197. ip.or = function(a, b) {
  198. a = ip.toBuffer(a);
  199. b = ip.toBuffer(b);
  200. // same protocol
  201. if (a.length === b.length) {
  202. for (var i = 0; i < a.length; ++i) {
  203. a[i] |= b[i];
  204. }
  205. return ip.toString(a);
  206. // mixed protocols
  207. } else {
  208. var buff = a;
  209. var other = b;
  210. if (b.length > a.length) {
  211. buff = b;
  212. other = a;
  213. }
  214. var offset = buff.length - other.length;
  215. for (var i = offset; i < buff.length; ++i) {
  216. buff[i] |= other[i - offset];
  217. }
  218. return ip.toString(buff);
  219. }
  220. };
  221. ip.isEqual = function(a, b) {
  222. a = ip.toBuffer(a);
  223. b = ip.toBuffer(b);
  224. // Same protocol
  225. if (a.length === b.length) {
  226. for (var i = 0; i < a.length; i++) {
  227. if (a[i] !== b[i]) return false;
  228. }
  229. return true;
  230. }
  231. // Swap
  232. if (b.length === 4) {
  233. var t = b;
  234. b = a;
  235. a = t;
  236. }
  237. // a - IPv4, b - IPv6
  238. for (var i = 0; i < 10; i++) {
  239. if (b[i] !== 0) return false;
  240. }
  241. var word = b.readUInt16BE(10);
  242. if (word !== 0 && word !== 0xffff) return false;
  243. for (var i = 0; i < 4; i++) {
  244. if (a[i] !== b[i + 12]) return false;
  245. }
  246. return true;
  247. };
  248. ip.isPrivate = function(addr) {
  249. return /^(::f{4}:)?10\.([0-9]{1,3})\.([0-9]{1,3})\.([0-9]{1,3})$/i
  250. .test(addr) ||
  251. /^(::f{4}:)?192\.168\.([0-9]{1,3})\.([0-9]{1,3})$/i.test(addr) ||
  252. /^(::f{4}:)?172\.(1[6-9]|2\d|30|31)\.([0-9]{1,3})\.([0-9]{1,3})$/i
  253. .test(addr) ||
  254. /^(::f{4}:)?127\.([0-9]{1,3})\.([0-9]{1,3})\.([0-9]{1,3})$/i.test(addr) ||
  255. /^(::f{4}:)?169\.254\.([0-9]{1,3})\.([0-9]{1,3})$/i.test(addr) ||
  256. /^f[cd][0-9a-f]{2}:/i.test(addr) ||
  257. /^fe80:/i.test(addr) ||
  258. /^::1$/.test(addr) ||
  259. /^::$/.test(addr);
  260. };
  261. ip.isPublic = function(addr) {
  262. return !ip.isPrivate(addr);
  263. };
  264. ip.isLoopback = function(addr) {
  265. return /^(::f{4}:)?127\.([0-9]{1,3})\.([0-9]{1,3})\.([0-9]{1,3})/
  266. .test(addr) ||
  267. /^fe80::1$/.test(addr) ||
  268. /^::1$/.test(addr) ||
  269. /^::$/.test(addr);
  270. };
  271. ip.loopback = function(family) {
  272. //
  273. // Default to `ipv4`
  274. //
  275. family = _normalizeFamily(family);
  276. if (family !== 'ipv4' && family !== 'ipv6') {
  277. throw new Error('family must be ipv4 or ipv6');
  278. }
  279. return family === 'ipv4' ? '127.0.0.1' : 'fe80::1';
  280. };
  281. //
  282. // ### function address (name, family)
  283. // #### @name {string|'public'|'private'} **Optional** Name or security
  284. // of the network interface.
  285. // #### @family {ipv4|ipv6} **Optional** IP family of the address (defaults
  286. // to ipv4).
  287. //
  288. // Returns the address for the network interface on the current system with
  289. // the specified `name`:
  290. // * String: First `family` address of the interface.
  291. // If not found see `undefined`.
  292. // * 'public': the first public ip address of family.
  293. // * 'private': the first private ip address of family.
  294. // * undefined: First address with `ipv4` or loopback address `127.0.0.1`.
  295. //
  296. ip.address = function(name, family) {
  297. var interfaces = os.networkInterfaces();
  298. var all;
  299. //
  300. // Default to `ipv4`
  301. //
  302. family = _normalizeFamily(family);
  303. //
  304. // If a specific network interface has been named,
  305. // return the address.
  306. //
  307. if (name && name !== 'private' && name !== 'public') {
  308. var res = interfaces[name].filter(function(details) {
  309. var itemFamily = details.family.toLowerCase();
  310. return itemFamily === family;
  311. });
  312. if (res.length === 0)
  313. return undefined;
  314. return res[0].address;
  315. }
  316. var all = Object.keys(interfaces).map(function (nic) {
  317. //
  318. // Note: name will only be `public` or `private`
  319. // when this is called.
  320. //
  321. var addresses = interfaces[nic].filter(function (details) {
  322. details.family = details.family.toLowerCase();
  323. if (details.family !== family || ip.isLoopback(details.address)) {
  324. return false;
  325. } else if (!name) {
  326. return true;
  327. }
  328. return name === 'public' ? ip.isPrivate(details.address) :
  329. ip.isPublic(details.address);
  330. });
  331. return addresses.length ? addresses[0].address : undefined;
  332. }).filter(Boolean);
  333. return !all.length ? ip.loopback(family) : all[0];
  334. };
  335. ip.toLong = function(ip) {
  336. var ipl = 0;
  337. ip.split('.').forEach(function(octet) {
  338. ipl <<= 8;
  339. ipl += parseInt(octet);
  340. });
  341. return(ipl >>> 0);
  342. };
  343. ip.fromLong = function(ipl) {
  344. return ((ipl >>> 24) + '.' +
  345. (ipl >> 16 & 255) + '.' +
  346. (ipl >> 8 & 255) + '.' +
  347. (ipl & 255) );
  348. };