fcs
2025-04-10 0163870d662dc5b7d69ceff977490ede594f7d82
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
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