]> Gitweb @ Texas Instruments - Open Source Git Repositories - git.TI.com/gitweb - apps/tidep0084.git/blob - example/iot-gateway/node_modules/ipaddr.js/src/ipaddr.coffee
Initial commit
[apps/tidep0084.git] / example / iot-gateway / node_modules / ipaddr.js / src / ipaddr.coffee
1 # Define the main object
2 ipaddr = {}
4 root = this
6 # Export for both the CommonJS and browser-like environment
7 if module? && module.exports
8   module.exports = ipaddr
9 else
10   root['ipaddr'] = ipaddr
12 # A generic CIDR (Classless Inter-Domain Routing) RFC1518 range matcher.
13 matchCIDR = (first, second, partSize, cidrBits) ->
14   if first.length != second.length
15     throw new Error "ipaddr: cannot match CIDR for objects with different lengths"
17   part = 0
18   while cidrBits > 0
19     shift = partSize - cidrBits
20     shift = 0 if shift < 0
22     if first[part] >> shift != second[part] >> shift
23       return false
25     cidrBits -= partSize
26     part     += 1
28   return true
30 # An utility function to ease named range matching. See examples below.
31 ipaddr.subnetMatch = (address, rangeList, defaultName='unicast') ->
32   for rangeName, rangeSubnets of rangeList
33     # ECMA5 Array.isArray isn't available everywhere
34     if rangeSubnets[0] && !(rangeSubnets[0] instanceof Array)
35       rangeSubnets = [ rangeSubnets ]
37     for subnet in rangeSubnets
38       return rangeName if address.match.apply(address, subnet)
40   return defaultName
42 # An IPv4 address (RFC791).
43 class ipaddr.IPv4
44   # Constructs a new IPv4 address from an array of four octets.
45   # Verifies the input.
46   constructor: (octets) ->
47     if octets.length != 4
48       throw new Error "ipaddr: ipv4 octet count should be 4"
50     for octet in octets
51       if !(0 <= octet <= 255)
52         throw new Error "ipaddr: ipv4 octet is a byte"
54     @octets = octets
56   # The 'kind' method exists on both IPv4 and IPv6 classes.
57   kind: ->
58     return 'ipv4'
60   # Returns the address in convenient, decimal-dotted format.
61   toString: ->
62     return @octets.join "."
64   # Returns an array of byte-sized values in network order
65   toByteArray: ->
66     return @octets.slice(0) # octets.clone
68   # Checks if this address matches other one within given CIDR range.
69   match: (other, cidrRange) ->
70     if cidrRange == undefined
71       [other, cidrRange] = other
73     if other.kind() != 'ipv4'
74       throw new Error "ipaddr: cannot match ipv4 address with non-ipv4 one"
76     return matchCIDR(this.octets, other.octets, 8, cidrRange)
78   # Special IPv4 address ranges.
79   SpecialRanges:
80     unspecified: [
81       [ new IPv4([0,     0,    0,   0]),  8 ]
82     ]
83     broadcast: [
84       [ new IPv4([255, 255,  255, 255]), 32 ]
85     ]
86     multicast: [ # RFC3171
87       [ new IPv4([224,   0,    0,   0]), 4  ]
88     ]
89     linkLocal: [ # RFC3927
90       [ new IPv4([169,   254,  0,   0]), 16 ]
91     ]
92     loopback: [ # RFC5735
93       [ new IPv4([127,   0,    0,   0]), 8  ]
94     ]
95     private: [ # RFC1918
96       [ new IPv4([10,    0,    0,   0]), 8  ]
97       [ new IPv4([172,   16,   0,   0]), 12 ]
98       [ new IPv4([192,   168,  0,   0]), 16 ]
99     ]
100     reserved: [ # Reserved and testing-only ranges; RFCs 5735, 5737, 2544, 1700
101       [ new IPv4([192,   0,    0,   0]), 24 ]
102       [ new IPv4([192,   0,    2,   0]), 24 ]
103       [ new IPv4([192,  88,   99,   0]), 24 ]
104       [ new IPv4([198,  51,  100,   0]), 24 ]
105       [ new IPv4([203,   0,  113,   0]), 24 ]
106       [ new IPv4([240,   0,    0,   0]), 4  ]
107     ]
109   # Checks if the address corresponds to one of the special ranges.
110   range: ->
111     return ipaddr.subnetMatch(this, @SpecialRanges)
113   # Convrets this IPv4 address to an IPv4-mapped IPv6 address.
114   toIPv4MappedAddress: ->
115     return ipaddr.IPv6.parse "::ffff:#{@toString()}"
117 # A list of regular expressions that match arbitrary IPv4 addresses,
118 # for which a number of weird notations exist.
119 # Note that an address like 0010.0xa5.1.1 is considered legal.
120 ipv4Part = "(0?\\d+|0x[a-f0-9]+)"
121 ipv4Regexes =
122   fourOctet: new RegExp "^#{ipv4Part}\\.#{ipv4Part}\\.#{ipv4Part}\\.#{ipv4Part}$", 'i'
123   longValue: new RegExp "^#{ipv4Part}$", 'i'
125 # Classful variants (like a.b, where a is an octet, and b is a 24-bit
126 # value representing last three octets; this corresponds to a class C
127 # address) are omitted due to classless nature of modern Internet.
128 ipaddr.IPv4.parser = (string) ->
129   parseIntAuto = (string) ->
130     if string[0] == "0" && string[1] != "x"
131       parseInt(string, 8)
132     else
133       parseInt(string)
135   # parseInt recognizes all that octal & hexadecimal weirdness for us
136   if match = string.match(ipv4Regexes.fourOctet)
137     return (parseIntAuto(part) for part in match[1..5])
138   else if match = string.match(ipv4Regexes.longValue)
139     value = parseIntAuto(match[1])
140     if value > 0xffffffff || value < 0
141       throw new Error "ipaddr: address outside defined range"
142     return ((value >> shift) & 0xff for shift in [0..24] by 8).reverse()
143   else
144     return null
146 # An IPv6 address (RFC2460)
147 class ipaddr.IPv6
148   # Constructs an IPv6 address from an array of eight 16-bit parts.
149   # Throws an error if the input is invalid.
150   constructor: (parts) ->
151     if parts.length != 8
152       throw new Error "ipaddr: ipv6 part count should be 8"
154     for part in parts
155       if !(0 <= part <= 0xffff)
156         throw new Error "ipaddr: ipv6 part should fit to two octets"
158     @parts = parts
160   # The 'kind' method exists on both IPv4 and IPv6 classes.
161   kind: ->
162     return 'ipv6'
164   # Returns the address in compact, human-readable format like
165   # 2001:db8:8:66::1
166   toString: ->
167     stringParts = (part.toString(16) for part in @parts)
169     compactStringParts = []
170     pushPart = (part) -> compactStringParts.push part
172     state = 0
173     for part in stringParts
174       switch state
175         when 0
176           if part == '0'
177             pushPart('')
178           else
179             pushPart(part)
181           state = 1
182         when 1
183           if part == '0'
184             state = 2
185           else
186             pushPart(part)
187         when 2
188           unless part == '0'
189             pushPart('')
190             pushPart(part)
191             state = 3
192         when 3
193           pushPart(part)
195     if state == 2
196       pushPart('')
197       pushPart('')
199     return compactStringParts.join ":"
201   # Returns an array of byte-sized values in network order
202   toByteArray: ->
203     bytes = []
204     for part in @parts
205       bytes.push(part >> 8)
206       bytes.push(part & 0xff)
208     return bytes
210   # Returns the address in expanded format with all zeroes included, like
211   # 2001:db8:8:66:0:0:0:1
212   toNormalizedString: ->
213     return (part.toString(16) for part in @parts).join ":"
215   # Checks if this address matches other one within given CIDR range.
216   match: (other, cidrRange) ->
217     if cidrRange == undefined
218       [other, cidrRange] = other
220     if other.kind() != 'ipv6'
221       throw new Error "ipaddr: cannot match ipv6 address with non-ipv6 one"
223     return matchCIDR(this.parts, other.parts, 16, cidrRange)
225   # Special IPv6 ranges
226   SpecialRanges:
227     unspecified: [ new IPv6([0,      0,      0, 0, 0,      0,      0, 0]), 128 ] # RFC4291, here and after
228     linkLocal:   [ new IPv6([0xfe80, 0,      0, 0, 0,      0,      0, 0]), 10  ]
229     multicast:   [ new IPv6([0xff00, 0,      0, 0, 0,      0,      0, 0]), 8   ]
230     loopback:    [ new IPv6([0,      0,      0, 0, 0,      0,      0, 1]), 128 ]
231     uniqueLocal: [ new IPv6([0xfc00, 0,      0, 0, 0,      0,      0, 0]), 7   ]
232     ipv4Mapped:  [ new IPv6([0,      0,      0, 0, 0,      0xffff, 0, 0]), 96  ]
233     rfc6145:     [ new IPv6([0,      0,      0, 0, 0xffff, 0,      0, 0]), 96  ] # RFC6145
234     rfc6052:     [ new IPv6([0x64,   0xff9b, 0, 0, 0,      0,      0, 0]), 96  ] # RFC6052
235     '6to4':      [ new IPv6([0x2002, 0,      0, 0, 0,      0,      0, 0]), 16  ] # RFC3056
236     teredo:      [ new IPv6([0x2001, 0,      0, 0, 0,      0,      0, 0]), 32  ] # RFC6052, RFC6146
237     reserved: [
238       [ new IPv6([ 0x2001, 0xdb8, 0, 0, 0, 0, 0, 0]), 32 ] # RFC4291
239     ]
241   # Checks if the address corresponds to one of the special ranges.
242   range: ->
243     return ipaddr.subnetMatch(this, @SpecialRanges)
245   # Checks if this address is an IPv4-mapped IPv6 address.
246   isIPv4MappedAddress: ->
247     return @range() == 'ipv4Mapped'
249   # Converts this address to IPv4 address if it is an IPv4-mapped IPv6 address.
250   # Throws an error otherwise.
251   toIPv4Address: ->
252     unless @isIPv4MappedAddress()
253       throw new Error "ipaddr: trying to convert a generic ipv6 address to ipv4"
255     [high, low] = @parts[-2..-1]
257     return new ipaddr.IPv4([high >> 8, high & 0xff, low >> 8, low & 0xff])
259 # IPv6-matching regular expressions.
260 # For IPv6, the task is simpler: it is enough to match the colon-delimited
261 # hexadecimal IPv6 and a transitional variant with dotted-decimal IPv4 at
262 # the end.
263 ipv6Part = "(?:[0-9a-f]+::?)+"
264 ipv6Regexes =
265   native:       new RegExp "^(::)?(#{ipv6Part})?([0-9a-f]+)?(::)?$", 'i'
266   transitional: new RegExp "^((?:#{ipv6Part})|(?:::)(?:#{ipv6Part})?)" +
267                            "#{ipv4Part}\\.#{ipv4Part}\\.#{ipv4Part}\\.#{ipv4Part}$", 'i'
269 # Expand :: in an IPv6 address or address part consisting of `parts` groups.
270 expandIPv6 = (string, parts) ->
271   # More than one '::' means invalid adddress
272   if string.indexOf('::') != string.lastIndexOf('::')
273     return null
275   # How many parts do we already have?
276   colonCount = 0
277   lastColon = -1
278   while (lastColon = string.indexOf(':', lastColon + 1)) >= 0
279     colonCount++
281   # 0::0 is two parts more than ::
282   colonCount-- if string.substr(0, 2) == '::'
283   colonCount-- if string.substr(-2, 2) == '::'
285   # The following loop would hang if colonCount > parts
286   if colonCount > parts
287     return null
289   # replacement = ':' + '0:' * (parts - colonCount)
290   replacementCount = parts - colonCount
291   replacement = ':'
292   while replacementCount--
293     replacement += '0:'
295   # Insert the missing zeroes
296   string = string.replace('::', replacement)
298   # Trim any garbage which may be hanging around if :: was at the edge in
299   # the source string
300   string = string[1..-1] if string[0] == ':'
301   string = string[0..-2] if string[string.length-1] == ':'
303   return (parseInt(part, 16) for part in string.split(":"))
305 # Parse an IPv6 address.
306 ipaddr.IPv6.parser = (string) ->
307   if string.match(ipv6Regexes['native'])
308     return expandIPv6(string, 8)
310   else if match = string.match(ipv6Regexes['transitional'])
311     parts = expandIPv6(match[1][0..-2], 6)
312     if parts
313       parts.push(parseInt(match[2]) << 8 | parseInt(match[3]))
314       parts.push(parseInt(match[4]) << 8 | parseInt(match[5]))
315       return parts
317   return null
319 # Checks if a given string is formatted like IPv4/IPv6 address.
320 ipaddr.IPv4.isIPv4 = ipaddr.IPv6.isIPv6 = (string) ->
321   return @parser(string) != null
323 # Checks if a given string is a valid IPv4/IPv6 address.
324 ipaddr.IPv4.isValid = (string) ->
325   try
326     new this(@parser(string))
327     return true
328   catch e
329     return false
331 ipaddr.IPv6.isValid = (string) ->
332   # Since IPv6.isValid is always called first, this shortcut
333   # provides a substantial performance gain.
334   if typeof string == "string" and string.indexOf(":") == -1
335     return false
337   try
338     new this(@parser(string))
339     return true
340   catch e
341     return false
343 # Tries to parse and validate a string with IPv4/IPv6 address.
344 # Throws an error if it fails.
345 ipaddr.IPv4.parse = ipaddr.IPv6.parse = (string) ->
346   parts = @parser(string)
347   if parts == null
348     throw new Error "ipaddr: string is not formatted like ip address"
350   return new this(parts)
352 ipaddr.IPv4.parseCIDR = (string) ->
353   if match = string.match(/^(.+)\/(\d+)$/)
354     maskLength = parseInt(match[2])
355     if maskLength >= 0 and maskLength <= 32
356       return [@parse(match[1]), maskLength]
358   throw new Error "ipaddr: string is not formatted like an IPv4 CIDR range"
360 ipaddr.IPv6.parseCIDR = (string) ->
361   if match = string.match(/^(.+)\/(\d+)$/)
362     maskLength = parseInt(match[2])
363     if maskLength >= 0 and maskLength <= 128
364       return [@parse(match[1]), maskLength]
366   throw new Error "ipaddr: string is not formatted like an IPv6 CIDR range"
368 # Checks if the address is valid IP address
369 ipaddr.isValid = (string) ->
370   return ipaddr.IPv6.isValid(string) || ipaddr.IPv4.isValid(string)
372 # Try to parse an address and throw an error if it is impossible
373 ipaddr.parse = (string) ->
374   if ipaddr.IPv6.isValid(string)
375     return ipaddr.IPv6.parse(string)
376   else if ipaddr.IPv4.isValid(string)
377     return ipaddr.IPv4.parse(string)
378   else
379     throw new Error "ipaddr: the address has neither IPv6 nor IPv4 format"
381 ipaddr.parseCIDR = (string) ->
382   try
383     return ipaddr.IPv6.parseCIDR(string)
384   catch e
385     try
386       return ipaddr.IPv4.parseCIDR(string)
387     catch e
388       throw new Error "ipaddr: the address has neither IPv6 nor IPv4 CIDR format"
390 # Parse an address and return plain IPv4 address if it is an IPv4-mapped address
391 ipaddr.process = (string) ->
392   addr = @parse(string)
393   if addr.kind() == 'ipv6' && addr.isIPv4MappedAddress()
394     return addr.toIPv4Address()
395   else
396     return addr