Skip to content

Commit 3f19a05

Browse files
committed
Rewrite byte parsing in full JS without depending on parseInt
The parseInt function is full of surprises. Instead of trying to find all its pitfalls, let's parse bytes manually.
1 parent 6a3169c commit 3f19a05

File tree

2 files changed

+50
-17
lines changed

2 files changed

+50
-17
lines changed

lib/netmask.coffee

Lines changed: 47 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -6,27 +6,57 @@ long2ip = (long) ->
66
return [a, b, c, d].join('.')
77

88
ip2long = (ip) ->
9-
b = (ip + '').split('.');
10-
if b.length is 0 or b.length > 4 then throw new Error('Invalid IP')
11-
for byte, i in b
12-
if byte and byte[0] == '0'
13-
if byte.length > 2 and (byte[1] == 'x' or byte[1] == 'x')
14-
# make sure 0x prefixed bytes are parsed as hex
15-
byte = parseInt(byte, 16)
16-
else
17-
# make sure 0 prefixed bytes are parsed as octal
18-
byte = parseInt(byte, 8)
19-
else if byte and (byte[0] == ' ' or byte[byte.length-1] == ' ')
20-
throw new Error('Invalid IP')
21-
else
22-
byte = parseInt(byte, 10)
23-
if isNaN(byte) then throw new Error("Invalid byte: #{byte}")
24-
if byte < 0 or byte > 255 then throw new Error("Invalid byte: #{byte}")
25-
b[i] = byte
9+
b = []
10+
for i in [0..3]
11+
if ip.length == 0
12+
break
13+
if i > 0
14+
if ip[0] != '.'
15+
throw new Error('Invalid IP')
16+
ip = ip.substring(1)
17+
[n, c] = atob(ip)
18+
ip = ip.substring(c)
19+
b.push(n)
20+
if ip.length != 0
21+
throw new Error('Invalid IP')
2622
while b.length < 4
2723
b.unshift(0)
2824
return (b[0] << 24 | b[1] << 16 | b[2] << 8 | b[3]) >>> 0
2925

26+
atob = (s) ->
27+
n = 0
28+
base = 10
29+
dmax = '9'
30+
i = 0
31+
if s.length > 1 and s[i] == '0'
32+
if s[i+1] == 'x' or s[i+1] == 'X'
33+
i+=2
34+
base = 16
35+
else if '0' <= s[i+1] and s[i+1] <= '7'
36+
i++
37+
base = 8
38+
dmax = '7'
39+
start = i
40+
chr = (b) -> return b.charCodeAt(0)
41+
while s.length > 0
42+
if '0' <= s[i] and s[i] <= dmax
43+
n = n*base + (chr(s[i])-chr('0'))
44+
else if base == 16
45+
if 'a' <= s[i] and s[i] <= 'f'
46+
n = n*base + (10+chr(s[i])-chr('a'))
47+
else if 'A' <= s[i] and s[i] <= 'F'
48+
n = n*base + (10+chr(s[i])-chr('A'))
49+
else
50+
break
51+
else
52+
break
53+
if n > 0xFF
54+
throw new Error('byte overflow')
55+
i++
56+
if i == start
57+
throw new Error('empty octet')
58+
return [n, i]
59+
3060
class Netmask
3161
constructor: (net, mask) ->
3262
throw new Error("Missing `net' parameter") unless typeof net is 'string'

test/badnets.coffee

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,11 +35,14 @@ vows.describe('IPs with bytes greater than 255')
3535
vows.describe('Invalid IP format')
3636
.addBatch
3737
' 1.2.3.4': shouldFailWithError 'Invalid net'
38+
' 1.2.3.4': shouldFailWithError 'Invalid net'
3839
'1. 2.3.4': shouldFailWithError 'Invalid net'
3940
'1.2. 3.4': shouldFailWithError 'Invalid net'
4041
'1.2.3. 4': shouldFailWithError 'Invalid net'
4142
'1.2.3.4 ': shouldFailWithError 'Invalid net'
4243
'1 .2.3.4': shouldFailWithError 'Invalid net'
44+
'018.0.0.0': shouldFailWithError 'Invalid net'
45+
'0xfg.0.0.0': shouldFailWithError 'Invalid net'
4346
.export(module)
4447

4548
vows.describe('Ranges that are a power-of-two big, but are not legal blocks')

0 commit comments

Comments
 (0)