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