Socket
Socket
Sign inDemoInstall

@leichtgewicht/ip-codec

Package Overview
Dependencies
0
Maintainers
1
Versions
7
Alerts
File Explorer

Advanced tools

Install Socket

Detect and block malicious and high-risk dependencies

Install

Comparing version 2.0.2 to 2.0.3

177

index.js

@@ -12,27 +12,23 @@ const v4Regex = /^(\d{1,3}\.){3,3}\d{1,3}$/

offset = ~~offset
const result = buff || new Uint8Array(offset + v4Size)
ip.split(/\./g).forEach((byte, index) => {
result[offset + index] = parseInt(byte, 10) & 0xff
})
return result
buff = buff || new Uint8Array(offset + v4Size)
const max = ip.length
let n = 0
for (let i = 0; i < max;) {
const c = ip.charCodeAt(i++)
if (c === 46) { // "."
buff[offset++] = n
n = 0
} else {
n = n * 10 + (c - 48)
}
}
buff[offset] = n
return buff
},
decode (buff, offset) {
offset = ~~offset
return [
buff[offset],
buff[offset + 1],
buff[offset + 2],
buff[offset + 3]
].join('.')
return `${buff[offset++]}.${buff[offset++]}.${buff[offset++]}.${buff[offset]}`
}
}
function hex (byte) {
byte = byte.toString(16)
if (byte.length === 1) {
return '0' + byte
}
return byte
}
const v6 = {

@@ -44,43 +40,126 @@ name: 'v6',

offset = ~~offset
const sections = ip.split(':', 8)
for (let i = 0; i < sections.length; i++) {
if (v4.isFormat(sections[i])) {
const v4Buffer = v4.encode(sections[i])
sections[i] = hex(v4Buffer[0]) + hex(v4Buffer[1])
if (++i < 8) {
sections.splice(i, 0, hex(v4Buffer[2]) + hex(v4Buffer[3]))
let end = offset + v6Size
let fill = -1
let hexN = 0
let decN = 0
let prevColon = true
let useDec = false
buff = buff || new Uint8Array(offset + v6Size)
// Note: This algorithm needs to check if the offset
// could exceed the buffer boundaries as it supports
// non-standard compliant encodings that may go beyond
// the boundary limits. if (offset < end) checks should
// not be necessary...
for (let i = 0; i < ip.length; i++) {
let c = ip.charCodeAt(i)
if (c === 58) { // :
if (prevColon) {
if (fill !== -1) {
// Not Standard! (standard doesn't allow multiple ::)
// We need to treat
if (offset < end) buff[offset] = 0
if (offset < end - 1) buff[offset + 1] = 0
offset += 2
} else if (offset < end) {
// :: in the middle
fill = offset
}
} else {
// : ends the previous number
if (useDec === true) {
// Non-standard! (ipv4 should be at end only)
// A ipv4 address should not be found anywhere else but at
// the end. This codec also support putting characters
// after the ipv4 address..
if (offset < end) buff[offset] = decN
offset++
} else {
if (offset < end) buff[offset] = hexN >> 8
if (offset < end - 1) buff[offset + 1] = hexN & 0xff
offset += 2
}
hexN = 0
decN = 0
}
prevColon = true
useDec = false
} else if (c === 46) { // . indicates IPV4 notation
if (offset < end) buff[offset] = decN
offset++
decN = 0
hexN = 0
prevColon = false
useDec = true
} else {
prevColon = false
if (c >= 97) {
c -= 87 // a-f ... 97~102 -87 => 10~15
} else if (c >= 65) {
c -= 55 // A-F ... 65~70 -55 => 10~15
} else {
c -= 48 // 0-9 ... starting from charCode 48
decN = decN * 10 + c
}
// We don't know yet if its a dec or hex number
hexN = (hexN << 4) + c
}
}
if (sections[0] === '') {
while (sections.length < 8) sections.unshift('0')
} else if (sections[sections.length - 1] === '') {
while (sections.length < 8) sections.push('0')
} else if (sections.length < 8) {
let i = 0
while (i < sections.length && sections[i] !== '') i++
const argv = [i, 1]
for (i = 9 - sections.length; i > 0; i--) {
argv.push('0')
if (prevColon === false) {
// Commiting last number
if (useDec === true) {
if (offset < end) buff[offset] = decN
offset++
} else {
if (offset < end) buff[offset] = hexN >> 8
if (offset < end - 1) buff[offset + 1] = hexN & 0xff
offset += 2
}
sections.splice.apply(sections, argv)
} else if (fill === 0) {
// Not Standard! (standard doesn't allow multiple ::)
// This means that a : was found at the start AND end which means the
// end needs to be treated as 0 entry...
if (offset < end) buff[offset] = 0
if (offset < end - 1) buff[offset + 1] = 0
offset += 2
} else if (fill !== -1) {
// Non-standard! (standard doens't allow multiple ::)
// Here we find that there has been a :: somewhere in the middle
// and the end. To treat the end with priority we need to move all
// written data two bytes to the right.
offset += 2
for (let i = Math.min(offset - 1, end - 1); i >= fill + 2; i--) {
buff[i] = buff[i - 2]
}
buff[fill] = 0
buff[fill + 1] = 0
fill = offset
}
const result = buff || new Uint8Array(offset + v6Size)
for (const section of sections) {
const word = parseInt(section, 16)
result[offset++] = (word >> 8) & 0xff
result[offset++] = word & 0xff
if (fill !== offset && fill !== -1) {
// Move the written numbers to the end while filling the everything
// "fill" to the bytes with zeros.
if (offset > end - 2) {
// Non Standard support, when the cursor exceeds bounds.
offset = end - 2
}
while (end > fill) {
buff[--end] = offset < end && offset > fill ? buff[--offset] : 0
}
} else {
// Fill the rest with zeros
while (offset < end) {
buff[offset++] = 0
}
}
return result
return buff
},
decode (buff, offset) {
offset = ~~offset
const result = []
let result = ''
for (let i = 0; i < v6Size; i += 2) {
result.push((buff[offset + i] << 8 | buff[offset + i + 1]).toString(16))
if (i !== 0) {
result += ':'
}
result += (buff[offset + i] << 8 | buff[offset + i + 1]).toString(16)
}
return result.join(':')
return result
.replace(/(^|:)0(:0)*:0(:|$)/, '$1::$3')

@@ -87,0 +166,0 @@ .replace(/:{3,4}/, '::')

{
"name": "@leichtgewicht/ip-codec",
"version": "2.0.2",
"version": "2.0.3",
"description": "Small package to encode or decode IP addresses from buffers to strings.",

@@ -5,0 +5,0 @@ "main": "index.js",

const test = require('fresh-tape')
const Buffer = require('buffer').Buffer
const { encode, decode, sizeOf, familyOf, v4, v6 } = require('.')
const crypto = require('crypto')
test('IPv4 addresses tests', t => {
t.ok(v4.isFormat('0.0.0.0'))
t.ok(v4.isFormat('1.1.1.1'))
t.ok(v4.isFormat('1.1.1.1'))
t.ok(v4.isFormat('11.11.11.11'))
t.ok(v4.isFormat('111.111.111.111'))
t.ok(v4.isFormat('255.255.255.255'))
t.notOk(v4.isFormat(''))
t.notOk(v4.isFormat('1'))
t.notOk(v4.isFormat('1.1.1'))
t.notOk(v4.isFormat(' 1.1.1.1 '))
t.notOk(v4.isFormat('1.1.1.1 '))
t.notOk(v4.isFormat(' 1.1.1.1'))
t.notOk(v4.isFormat('1000.1.1.1'))
t.notOk(v4.isFormat('1.1000.1.1'))
t.notOk(v4.isFormat('1.1.1000.1'))
t.notOk(v4.isFormat('1.1.1.1000'))
t.end()
})
test('IPv6 addresses tests', t => {
t.ok(v6.isFormat('0:0:0:0:0:0:0:0'), '8 octets')
t.ok(v6.isFormat('ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff'), '8 full octets')
t.ok(v6.isFormat('::'), 'expanding ::')
t.ok(v6.isFormat('::1'), '0-9')
t.ok(v6.isFormat('::f'), 'a-f')
t.ok(v6.isFormat('::F'), 'upper characters need to match')
t.ok(v6.isFormat('::ff'), '2 chars')
t.ok(v6.isFormat('::fff'), '3 chars')
t.ok(v6.isFormat('::ffff'), '4 chars')
t.ok(v6.isFormat('::0:0:0:0:0:0:0'), 'expanding left')
t.ok(v6.isFormat('0:0:0:0:0:0:0::'), 'expanding right')
t.ok(v6.isFormat('0:0:0:0::0:0:0'), 'expanding middle')
t.ok(v6.isFormat('::ffff:127.0.0.1'), 'ipv4 in v6')
t.ok(v6.isFormat('::127.0.0.1')) // TODO: Likely non-standard?!
t.notOk(v6.isFormat(' 0:0:0:0:0:0:0:0'), 'spaces')
t.notOk(v6.isFormat('0:0:0:0:0:0:0:0 '), 'spaces')
t.notOk(v6.isFormat('::g'), 'invalid v6')
t.notOk(v6.isFormat('::g0'), 'invalid v6')
t.notOk(v6.isFormat('::gg'), 'invalid v6')
t.notOk(v6.isFormat('::g'), 'invalid v6')
t.notOk(v6.isFormat('::ffff:127.0.0.1.1'), 'too many ipv4 numbers')
t.notOk(v6.isFormat('::ffff:127.0.0'), 'incomplete ipv4')
t.notOk(v6.isFormat('::ffff:127.0'), 'less complete ipv4')
t.notOk(v6.isFormat('0:0:0:0:::0:0:0:0'), 'too much expansion')
t.notOk(v6.isFormat('0:0:0:0:0:0:0:0:0'), 'too many octets')
t.notOk(v6.isFormat('0:0:0:0:0:0:0:10000'), 'too many digits')
t.notOk(v6.isFormat(':0:0:0:0:0:0:0:0'), '8 octets + expanding left')
t.end()
})
test('should convert to buffer IPv4 address', t => {

@@ -18,2 +70,22 @@ const buf = encode('127.0.0.1')

t.equal(decode(buf, offset, 4), '127.0.0.1')
// Non-standard encodings that are technically working
// Changing these would mean a breaking change...
t.equal(decode(encode('256.0.0.0')), '0.0.0.0')
t.equal(decode(encode('258.0.0.0')), '2.0.0.0')
t.equal(decode(encode('0.534.0.0')), '0.22.0.0')
t.equal(decode(encode('0.0.990.0')), '0.0.222.0')
t.equal(decode(encode('0.0.0.671')), '0.0.0.159')
// Zero prefixing is allowed, as the encoder supports it.
// However we need to be aware that
// https://datatracker.ietf.org/doc/html/rfc3986#section-7.4
// clearly specifies that this is not a valid URI format.
t.equal(decode(encode('01.0.0.0')), '1.0.0.0')
t.equal(decode(encode('0.01.0.0')), '0.1.0.0')
t.equal(decode(encode('0.0.01.0')), '0.0.1.0')
t.equal(decode(encode('0.0.0.01')), '0.0.0.1')
t.equal(decode(encode('001.0.0.0')), '1.0.0.0')
t.equal(decode(encode('0.001.0.0')), '0.1.0.0')
t.equal(decode(encode('0.0.001.0')), '0.0.1.0')
t.equal(decode(encode('0.0.0.001')), '0.0.0.1')
t.end()

@@ -26,6 +98,48 @@ })

t.equal(decode(buf), '::1')
t.equal(decode(encode('1::')), '1::')
t.equal(decode(encode('abcd::dcba')), 'abcd::dcba')
t.equal(decode(encode('::ffff:c0a8:100')), '::ffff:c0a8:100')
t.equal(decode(encode('::ffff:ff00')), '::ffff:ff00')
testV6(t, '1::', '1::')
testV6(t, 'abcd::dcba', 'abcd::dcba')
testV6(t, 'ABCD::DCBA', 'abcd::dcba')
testV6(t, '::ffff:c0a8:100', '::ffff:c0a8:100')
testV6(t, '::ffff:ff00', '::ffff:ff00')
// Non-standard encodings that are technically working
// Changing these would mean a breaking change...
testV6(t, ':', '::', 'dangling colon')
testV6(t, ':::', '::', 'colon parade?!')
testV6(t, '::::::::::::::::::', '::', 'colon parade++')
testV6(t, ':12', '::12', 'dangling colon at start')
testV6(t, '12:', '12::', 'dangling colon at end')
testV6(t, '1', '1::', '1 octet')
testV6(t, '1:2', '1:2::', '2 octets')
testV6(t, '1:2:3', '1:2:3::', '3 octets')
testV6(t, '1:2:3:4', '1:2:3:4::', '4 octets')
testV6(t, '1:2:3:4:5', '1:2:3:4:5::', '5 octets')
testV6(t, '1:2:3:4:5:6', '1:2:3:4:5:6::', '6 octets')
testV6(t, '1:2:3:4:5:6:7', '1:2:3:4:5:6:7:0', '7 octets')
testV6(t, '1:2:3:4::5:6:7:8', '1:2:3:4:0:5:6:7', '8 octets + expanding')
testV6(t, '1:2:3:4:5:6:7:8:', '1:2:3:4:5:6:7:8', '8 octets + expanding right')
testV6(t, '12345678', '5678::', 'no colon between numbers')
testV6(t, '::12345', '::2345', 'too many digits')
testV6(t, '0111:0:0:0:0:0:0', '111::', '0 prefixing')
testV6(t, '0011:0:0:0:0:0:0', '11::', '0 prefixing')
testV6(t, '011:0:0:0:0:0:0', '11::', '0 prefixing')
testV6(t, '001:0:0:0:0:0:0', '1::', '0 prefixing')
testV6(t, '01:0:0:0:0:0:0', '1::', '0 prefixing')
testV6(t, '0:01:0:0:0:0:0', '0:1::', '0 prefixing')
testV6(t, '0:0:01:0:0:0:0', '::1:0:0:0:0:0', '0 prefixing')
testV6(t, '0:0:0:01:0:0:0', '::1:0:0:0:0', '0 prefixing')
testV6(t, '0:0:0:0:01:0:0', '::1:0:0:0', '0 prefixing')
testV6(t, '0:0:0:0:0:01:0', '::1:0:0', '0 prefixing')
testV6(t, '0:0:0:0:0:0:01', '::1:0', '0 prefixing')
testV6(t, '0:0:0:0:0:0:001', '::1:0', '0 prefixing')
testV6(t, '0:0:0:0:0:0:011', '::11:0', '0 prefixing')
testV6(t, '0:0:0:0:0:0:0011', '::11:0', '0 prefixing')
testV6(t, '0:0:0:0:0:0:0111', '::111:0', '0 prefixing')
testV6(t, '::abcd::', '::abcd:0:0', ':: at end and start')
testV6(t, '::abcd::9876', '::abcd:0:9876', ':: at middle and start')
testV6(t, 'abcd::9876::', 'abcd:0:9876::', ':: at middle and end')
testV6(t, '::abcd::9876::', '::abcd:0:9876:0:0', ':: everywhere')
testV6(t, 'abcd::9876:123:', 'abcd:0:9876:123::', 'dangling colon + expansion')
testV6(t, '123:::789::::456::::::', '123::789:0:0:0:456')
testV6(t, 'abcd98764532', '4532::')
t.end()

@@ -59,5 +173,60 @@ })

t.equal(decode(buf), '::ffff:7f00:1')
// Non-standard encodings that are technically working
// Changing these would mean a breaking change...
testV6(t, '127.0.0.1', '7f00:1::', 'ipv4 standalone')
testV6(t, '127.0.0.1::', '7f00:1::', 'endling ::')
testV6(t, '::127.0.0.1::', '::7f00:1:0:0', ':: start and end ::')
testV6(t, ':0:ff::127.0.0.1::', '::ff:0:7f00:1:0:0', 'dangling double colon')
testV6(t, ':0:ff::127.0.0.1:', '::ff:0:7f00:1:0', 'dangling colon')
testV6(t, '127.0.0.1:::::::::::::', '7f00:1::', 'colon parade')
testV6(t, '::ffff:999.0.0.1', '::ffff:e700:1', 'wrong ipv4')
testV6(t, '::ffff:1.999.0.1', '::ffff:1e7:1', 'wrong ipv4')
testV6(t, '::ffff:1.0.999.1', '::ffff:100:e701', 'wrong ipv4')
testV6(t, '::ffff:1.0.0.999', '::ffff:100:e7', 'wrong ipv4')
testV6(t, ':0:ff::999.1.1.1:', '::ff:0:e701:101:0', 'alt wrong ipv4')
testV6(t, ':0:ff::1.999.1.1:', '::ff:0:1e7:101:0', 'alt wrong ipv4')
testV6(t, ':0:ff::1.1.999.1:', '::ff:0:101:e701:0', 'alt wrong ipv4')
testV6(t, ':0:ff::1.1.1.999:', '::ff:0:101:1e7:0', 'alt wrong ipv4')
testV6(t, '127.0.0.1:0:0', '7f00:1::', 'ipv4 at the start')
// the regular encoder would identify the ip as ipv4 here, using ipv6 instead
t.equal(decode(v6.encode('127.0.0.1')), '7f00:1::', 'ipv4 as is shouldnt work')
t.end()
})
function testV6 (t, input, output, message) {
message = `${input} → ${output}${message ? `- ${message}` : ''}`
let result = v6.decode(v6.encode(input))
// First, lets do a general en- & decode
if (result === output) {
// Then, use a own input buffer to make sure that all bytes are written
result = v6.decode(v6.encode(input, crypto.randomBytes(v6.size), 0), 0)
if (result === output) {
// Then use a buffer that has random data before & after and write in the
// middle to make sure that the implementation doesn't exceed bounds.
// (Do this more than once to make sure randomness doesn't hide error
let buffSlice, safeSlice
for (let i = 0; i < 4; i++) {
const buff = crypto.randomBytes(v6.size + 8)
const safe = Buffer.from(buff)
v6.decode(v6.encode(input, buff, 4), 4)
buffSlice = sliceBounds(buff)
safeSlice = sliceBounds(safe)
if (buffSlice !== safeSlice) {
break
}
}
t.equal(buffSlice, safeSlice, `${message} (verified)`)
} else {
t.equal(result, output, `${message} (0 offset byob)`)
}
} else {
t.equal(result, output, message)
}
}
function sliceBounds (buff) {
return Buffer.concat([buff.slice(0, 4), buff.slice(buff.length - 4)]).toString('hex')
}
test('error decoding a buffer of unexpected length', t => {

@@ -64,0 +233,0 @@ t.throws(() => decode(Buffer.alloc(0)))

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc