| long2ip = (long) -> |
| a = (long & (0xff << 24)) >>> 24; |
| b = (long & (0xff << 16)) >>> 16; |
| c = (long & (0xff << 8)) >>> 8; |
| d = long & 0xff; |
| return [a, b, c, d].join('.') |
| |
| ip2long = (ip) -> |
| b = [] |
| for i in [0..3] |
| if ip.length == 0 |
| break |
| if i > 0 |
| if ip[0] != '.' |
| throw new Error('Invalid IP') |
| ip = ip.substring(1) |
| [n, c] = atob(ip) |
| ip = ip.substring(c) |
| b.push(n) |
| if ip.length != 0 |
| throw new Error('Invalid IP') |
| switch b.length |
| when 1 |
| # Long input notation |
| if b[0] > 0xFFFFFFFF |
| throw new Error('Invalid IP') |
| return b[0] >>> 0 |
| when 2 |
| # Class A notation |
| if b[0] > 0xFF or b[1] > 0xFFFFFF |
| throw new Error('Invalid IP') |
| return (b[0] << 24 | b[1]) >>> 0 |
| when 3 |
| # Class B notation |
| if b[0] > 0xFF or b[1] > 0xFF or b[2] > 0xFFFF |
| throw new Error('Invalid IP') |
| return (b[0] << 24 | b[1] << 16 | b[2]) >>> 0 |
| when 4 |
| # Dotted quad notation |
| if b[0] > 0xFF or b[1] > 0xFF or b[2] > 0xFF or b[3] > 0xFF |
| throw new Error('Invalid IP') |
| return (b[0] << 24 | b[1] << 16 | b[2] << 8 | b[3]) >>> 0 |
| else |
| throw new Error('Invalid IP') |
| |
| chr = (b) -> |
| return b.charCodeAt(0) |
| |
| chr0 = chr('0') |
| chra = chr('a') |
| chrA = chr('A') |
| |
| atob = (s) -> |
| n = 0 |
| base = 10 |
| dmax = '9' |
| i = 0 |
| if s.length > 1 and s[i] == '0' |
| if s[i+1] == 'x' or s[i+1] == 'X' |
| i += 2 |
| base = 16 |
| else if '0' <= s[i+1] and s[i+1] <= '9' |
| i++ |
| base = 8 |
| dmax = '7' |
| start = i |
| while i < s.length |
| if '0' <= s[i] and s[i] <= dmax |
| n = (n*base + (chr(s[i])-chr0)) >>> 0 |
| else if base == 16 |
| if 'a' <= s[i] and s[i] <= 'f' |
| n = (n*base + (10+chr(s[i])-chra)) >>> 0 |
| else if 'A' <= s[i] and s[i] <= 'F' |
| n = (n*base + (10+chr(s[i])-chrA)) >>> 0 |
| else |
| break |
| else |
| break |
| if n > 0xFFFFFFFF |
| throw new Error('too large') |
| i++ |
| if i == start |
| throw new Error('empty octet') |
| return [n, i] |
| |
| class Netmask |
| constructor: (net, mask) -> |
| throw new Error("Missing `net' parameter") unless typeof net is 'string' |
| unless mask |
| # try to find the mask in the net (i.e.: 1.2.3.4/24 or 1.2.3.4/255.255.255.0) |
| [net, mask] = net.split('/', 2) |
| unless mask |
| mask = 32 |
| if typeof mask is 'string' and mask.indexOf('.') > -1 |
| # Compute bitmask, the netmask as a number of bits in the network portion of the address for this block (eg.: 24) |
| try |
| @maskLong = ip2long(mask) |
| catch error |
| throw new Error("Invalid mask: #{mask}") |
| for i in [32..0] |
| if @maskLong == (0xffffffff << (32 - i)) >>> 0 |
| @bitmask = i |
| break |
| else if mask or mask == 0 |
| # The mask was passed as bitmask, compute the mask as long from it |
| @bitmask = parseInt(mask, 10) |
| @maskLong = 0 |
| if @bitmask > 0 |
| @maskLong = (0xffffffff << (32 - @bitmask)) >>> 0 |
| else |
| throw new Error("Invalid mask: empty") |
| |
| try |
| @netLong = (ip2long(net) & @maskLong) >>> 0 |
| catch error |
| throw new Error("Invalid net address: #{net}") |
| |
| throw new Error("Invalid mask for ip4: #{mask}") unless @bitmask <= 32 |
| |
| # The number of IP address in the block (eg.: 254) |
| @size = Math.pow(2, 32 - @bitmask) |
| # The address of the network block as a string (eg.: 216.240.32.0) |
| @base = long2ip(@netLong) |
| # The netmask as a string (eg.: 255.255.255.0) |
| @mask = long2ip(@maskLong) |
| # The host mask, the opposite of the netmask (eg.: 0.0.0.255) |
| @hostmask = long2ip(~@maskLong) |
| # The first usable address of the block |
| @first = if @bitmask <= 30 then long2ip(@netLong + 1) else @base |
| # The last usable address of the block |
| @last = if @bitmask <= 30 then long2ip(@netLong + @size - 2) else long2ip(@netLong + @size - 1) |
| # The block's broadcast address: the last address of the block (eg.: 192.168.1.255) |
| @broadcast = if @bitmask <= 30 then long2ip(@netLong + @size - 1) |
| |
| # Returns true if the given ip or netmask is contained in the block |
| contains: (ip) -> |
| if typeof ip is 'string' and (ip.indexOf('/') > 0 or ip.split('.').length isnt 4) |
| ip = new Netmask(ip) |
| |
| if ip instanceof Netmask |
| return @contains(ip.base) and @contains((ip.broadcast || ip.last)) |
| else |
| return (ip2long(ip) & @maskLong) >>> 0 == ((@netLong & @maskLong)) >>> 0 |
| |
| # Returns the Netmask object for the block which follow this one |
| next: (count=1) -> |
| return new Netmask(long2ip(@netLong + (@size * count)), @mask) |
| |
| forEach: (fn) -> |
| # this implementation is not idiomatic but avoids large memory allocations (2 arrays, one for range and one for the results) in cases when then netmask is large |
| long = ip2long(@first) |
| lastLong = ip2long(@last) |
| index = 0 |
| while long <= lastLong |
| fn long2ip(long), long, index |
| index++ |
| long++ |
| return |
| |
| # Returns the complete netmask formatted as `base/bitmask` |
| toString: -> |
| return @base + "/" + @bitmask |
| |
| |
| exports.ip2long = ip2long |
| exports.long2ip = long2ip |
| exports.Netmask = Netmask |