netmask.coffee 5.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167
  1. long2ip = (long) ->
  2. a = (long & (0xff << 24)) >>> 24;
  3. b = (long & (0xff << 16)) >>> 16;
  4. c = (long & (0xff << 8)) >>> 8;
  5. d = long & 0xff;
  6. return [a, b, c, d].join('.')
  7. ip2long = (ip) ->
  8. b = []
  9. for i in [0..3]
  10. if ip.length == 0
  11. break
  12. if i > 0
  13. if ip[0] != '.'
  14. throw new Error('Invalid IP')
  15. ip = ip.substring(1)
  16. [n, c] = atob(ip)
  17. ip = ip.substring(c)
  18. b.push(n)
  19. if ip.length != 0
  20. throw new Error('Invalid IP')
  21. switch b.length
  22. when 1
  23. # Long input notation
  24. if b[0] > 0xFFFFFFFF
  25. throw new Error('Invalid IP')
  26. return b[0] >>> 0
  27. when 2
  28. # Class A notation
  29. if b[0] > 0xFF or b[1] > 0xFFFFFF
  30. throw new Error('Invalid IP')
  31. return (b[0] << 24 | b[1]) >>> 0
  32. when 3
  33. # Class B notation
  34. if b[0] > 0xFF or b[1] > 0xFF or b[2] > 0xFFFF
  35. throw new Error('Invalid IP')
  36. return (b[0] << 24 | b[1] << 16 | b[2]) >>> 0
  37. when 4
  38. # Dotted quad notation
  39. if b[0] > 0xFF or b[1] > 0xFF or b[2] > 0xFF or b[3] > 0xFF
  40. throw new Error('Invalid IP')
  41. return (b[0] << 24 | b[1] << 16 | b[2] << 8 | b[3]) >>> 0
  42. else
  43. throw new Error('Invalid IP')
  44. chr = (b) ->
  45. return b.charCodeAt(0)
  46. chr0 = chr('0')
  47. chra = chr('a')
  48. chrA = chr('A')
  49. atob = (s) ->
  50. n = 0
  51. base = 10
  52. dmax = '9'
  53. i = 0
  54. if s.length > 1 and s[i] == '0'
  55. if s[i+1] == 'x' or s[i+1] == 'X'
  56. i += 2
  57. base = 16
  58. else if '0' <= s[i+1] and s[i+1] <= '9'
  59. i++
  60. base = 8
  61. dmax = '7'
  62. start = i
  63. while i < s.length
  64. if '0' <= s[i] and s[i] <= dmax
  65. n = (n*base + (chr(s[i])-chr0)) >>> 0
  66. else if base == 16
  67. if 'a' <= s[i] and s[i] <= 'f'
  68. n = (n*base + (10+chr(s[i])-chra)) >>> 0
  69. else if 'A' <= s[i] and s[i] <= 'F'
  70. n = (n*base + (10+chr(s[i])-chrA)) >>> 0
  71. else
  72. break
  73. else
  74. break
  75. if n > 0xFFFFFFFF
  76. throw new Error('too large')
  77. i++
  78. if i == start
  79. throw new Error('empty octet')
  80. return [n, i]
  81. class Netmask
  82. constructor: (net, mask) ->
  83. throw new Error("Missing `net' parameter") unless typeof net is 'string'
  84. unless mask
  85. # try to find the mask in the net (i.e.: 1.2.3.4/24 or 1.2.3.4/255.255.255.0)
  86. [net, mask] = net.split('/', 2)
  87. unless mask
  88. mask = 32
  89. if typeof mask is 'string' and mask.indexOf('.') > -1
  90. # Compute bitmask, the netmask as a number of bits in the network portion of the address for this block (eg.: 24)
  91. try
  92. @maskLong = ip2long(mask)
  93. catch error
  94. throw new Error("Invalid mask: #{mask}")
  95. for i in [32..0]
  96. if @maskLong == (0xffffffff << (32 - i)) >>> 0
  97. @bitmask = i
  98. break
  99. else if mask or mask == 0
  100. # The mask was passed as bitmask, compute the mask as long from it
  101. @bitmask = parseInt(mask, 10)
  102. @maskLong = 0
  103. if @bitmask > 0
  104. @maskLong = (0xffffffff << (32 - @bitmask)) >>> 0
  105. else
  106. throw new Error("Invalid mask: empty")
  107. try
  108. @netLong = (ip2long(net) & @maskLong) >>> 0
  109. catch error
  110. throw new Error("Invalid net address: #{net}")
  111. throw new Error("Invalid mask for ip4: #{mask}") unless @bitmask <= 32
  112. # The number of IP address in the block (eg.: 254)
  113. @size = Math.pow(2, 32 - @bitmask)
  114. # The address of the network block as a string (eg.: 216.240.32.0)
  115. @base = long2ip(@netLong)
  116. # The netmask as a string (eg.: 255.255.255.0)
  117. @mask = long2ip(@maskLong)
  118. # The host mask, the opposite of the netmask (eg.: 0.0.0.255)
  119. @hostmask = long2ip(~@maskLong)
  120. # The first usable address of the block
  121. @first = if @bitmask <= 30 then long2ip(@netLong + 1) else @base
  122. # The last usable address of the block
  123. @last = if @bitmask <= 30 then long2ip(@netLong + @size - 2) else long2ip(@netLong + @size - 1)
  124. # The block's broadcast address: the last address of the block (eg.: 192.168.1.255)
  125. @broadcast = if @bitmask <= 30 then long2ip(@netLong + @size - 1)
  126. # Returns true if the given ip or netmask is contained in the block
  127. contains: (ip) ->
  128. if typeof ip is 'string' and (ip.indexOf('/') > 0 or ip.split('.').length isnt 4)
  129. ip = new Netmask(ip)
  130. if ip instanceof Netmask
  131. return @contains(ip.base) and @contains((ip.broadcast || ip.last))
  132. else
  133. return (ip2long(ip) & @maskLong) >>> 0 == ((@netLong & @maskLong)) >>> 0
  134. # Returns the Netmask object for the block which follow this one
  135. next: (count=1) ->
  136. return new Netmask(long2ip(@netLong + (@size * count)), @mask)
  137. forEach: (fn) ->
  138. # 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
  139. long = ip2long(@first)
  140. lastLong = ip2long(@last)
  141. index = 0
  142. while long <= lastLong
  143. fn long2ip(long), long, index
  144. index++
  145. long++
  146. return
  147. # Returns the complete netmask formatted as `base/bitmask`
  148. toString: ->
  149. return @base + "/" + @bitmask
  150. exports.ip2long = ip2long
  151. exports.long2ip = long2ip
  152. exports.Netmask = Netmask