Repository: whitequark/ipaddr.js (js)

Issue:
[BUG] IPv6 Shortening is not right
the below toString() operation is wrong.

> ipaddr.parse("2001:0000:0000:0db8:0000:0000:0000:0001").toString();
'2001::db8:0:0:0:1'  --> This format is not the right one

rfc5952: https://www.rfc-editor.org/rfc/rfc5952.txt

4.2.3. Choice in Placement of "::"

When there is an alternative choice in the placement of a "::", the **longest run of consecutive 16-bit 0 fields MUST be shortened** (i.e., the sequence with three consecutive zero fields is shortened in 2001:0:0:1:0:0:0:1). When the length of the consecutive 16-bit 0 fields are equal (i.e., 2001:db8:0:0:1:0:0:1), the first sequence of zero bits MUST be shortened. For example, 2001:db8::1:0:0:1 is correct representation.

So in our case,
IPv6: 2001:0000:0000:0db8:0000:0000:0000:0001
The right shortened representation should have been: 2001:0:0:db8::1

Pull request description:
Fix IPv6 shortening
A simple fix that solves the IPv6 shortening issue.

Fixing the toString method is useful when printing CIDR addresses.

* Before fix :

```ts
console.log(IPv6.parseCIDR('0:0:0:ff:ff:0:0:0/64').toString()); // prints ::ff:ff:0:0:0

const ipv6: [IPv6, number] = IPv6.parseCIDR(ip);
console.log(`${ipv6[0].toRFC5952String()}/${ipv6[1]}`); // prints ::ff:ff:0:0:0/64
```
* After fix :

```ts
console.log(IPv6.parseCIDR('0:0:0:ff:ff:0:0:0/64').toString()); // prints ::ff:ff:0:0:0/64
```

fixes #155

Patch:
diff --git a/lib/ipaddr.js b/lib/ipaddr.js
index 848f95f..fe82e53 100644
--- a/lib/ipaddr.js
+++ b/lib/ipaddr.js
@@ -736,11 +736,9 @@
 
         // Returns the address in compact, human-readable format like
         // 2001:db8:8:66::1
-        //
-        // Deprecated: use toRFC5952String() instead.
+        // Calls toRFC5952String under the hood.
         IPv6.prototype.toString = function () {
-            // Replace the first sequence of 1 or more '0' parts with '::'
-            return this.toNormalizedString().replace(/((^|:)(0(:|$))+)/, '::');
+            return this.toRFC5952String();
         };
 
         return IPv6;


Test patch:
diff --git a/test/ipaddr.test.js b/test/ipaddr.test.js
index d41cbbc..be7ced3 100644
--- a/test/ipaddr.test.js
+++ b/test/ipaddr.test.js
@@ -1,6 +1,7 @@
 'use strict';
 
 const assert = require('assert');
+const { IPv6, IPv4 } = require('../lib/ipaddr');
 
 const ipaddr = require('../lib/ipaddr');
 
@@ -36,6 +37,11 @@ describe('ipaddr', () => {
         done();
     })
 
+    it('converts IPv4 CIDR to string correctly', (done) => {
+        assert.equal(IPv4.parseCIDR('192.168.1.1/24').toString(), '192.168.1.1/24');
+        done();
+    })
+
     it('returns correct kind for IPv4', (done) => {
         let addr = new ipaddr.IPv4([1, 2, 3, 4]);
         assert.equal(addr.kind(), 'ipv4');
@@ -209,16 +215,24 @@ describe('ipaddr', () => {
         assert.equal(new ipaddr.IPv6([0, 0, 0, 0, 0, 0, 0, 0]).toString(), '::');
         assert.equal(new ipaddr.IPv6([0, 0, 0, 0, 0, 0, 0, 1]).toString(), '::1');
         assert.equal(new ipaddr.IPv6([0x2001, 0xdb8, 0, 0, 0, 0, 0, 0]).toString(), '2001:db8::');
-        assert.equal(new ipaddr.IPv6([0, 0xff, 0, 0, 0, 0, 0, 0]).toString(), '::ff:0:0:0:0:0:0');
+        assert.equal(new ipaddr.IPv6([0, 0xff, 0, 0, 0, 0, 0, 0]).toString(), '0:ff::');
         assert.equal(new ipaddr.IPv6([0, 0, 0, 0, 0, 0, 0xff, 0]).toString(), '::ff:0');
-        assert.equal(new ipaddr.IPv6([0, 0, 0xff, 0, 0, 0, 0, 0]).toString(), '::ff:0:0:0:0:0');
+        assert.equal(new ipaddr.IPv6([0, 0, 0xff, 0, 0, 0, 0, 0]).toString(), '0:0:ff::');
         assert.equal(new ipaddr.IPv6([0, 0, 0, 0, 0, 0xff, 0, 0]).toString(), '::ff:0:0');
+        assert.equal(new ipaddr.IPv6([0, 0, 0, 0xff, 0xff, 0, 0, 0]).toString(), '::ff:ff:0:0:0');
         assert.equal(new ipaddr.IPv6([0x2001, 0xdb8, 0xff, 0xabc, 0xdef, 0x123b, 0x456c, 0x78d]).toString(), '2001:db8:ff:abc:def:123b:456c:78d');
-        assert.equal(new ipaddr.IPv6([0x2001, 0xdb8, 0xff, 0xabc, 0, 0x123b, 0x456c, 0x78d]).toString(), '2001:db8:ff:abc::123b:456c:78d');
+        assert.equal(new ipaddr.IPv6([0x2001, 0xdb8, 0xff, 0xabc, 0, 0x123b, 0x456c, 0x78d]).toString(), '2001:db8:ff:abc:0:123b:456c:78d');
         assert.equal(new ipaddr.IPv6([0x2001, 0xdb8, 0xff, 0xabc, 0, 0, 0x456c, 0x78d]).toString(), '2001:db8:ff:abc::456c:78d');
         done();
     })
 
+    it('converts IPv6 CIDR to string correctly', (done) => {
+        assert.equal(IPv6.parseCIDR('0:0:0:0:0:0:0:0/64').toString(), '::/64');
+        assert.equal(IPv6.parseCIDR('0:0:0:ff:ff:0:0:0/64').toString(), '::ff:ff:0:0:0/64');
+        assert.equal(IPv6.parseCIDR('2001:db8:ff:abc:def:123b:456c:78d/64').toString(), '2001:db8:ff:abc:def:123b:456c:78d/64');
+        done();
+    })
+
     it('converts IPv6 to RFC 5952 string correctly', (done) => {
         // see https://tools.ietf.org/html/rfc5952#section-4
         let addr = new ipaddr.IPv6([0x2001, 0xdb8, 0xf53a, 0, 0, 0, 0, 1]);
@@ -634,7 +648,7 @@ describe('ipaddr', () => {
 
     it('subnetMaskFromPrefixLength returns correct IPv6 subnet mask given prefix length', (done) => {
         assert.equal(ipaddr.IPv6.subnetMaskFromPrefixLength(128), 'ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff');
-        assert.equal(ipaddr.IPv6.subnetMaskFromPrefixLength(112), 'ffff:ffff:ffff:ffff:ffff:ffff:ffff::');
+        assert.equal(ipaddr.IPv6.subnetMaskFromPrefixLength(112), 'ffff:ffff:ffff:ffff:ffff:ffff:ffff:0');
         assert.equal(ipaddr.IPv6.subnetMaskFromPrefixLength(96),  'ffff:ffff:ffff:ffff:ffff:ffff::');
         assert.equal(ipaddr.IPv6.subnetMaskFromPrefixLength(72),  'ffff:ffff:ffff:ffff:ff00::');
         assert.equal(ipaddr.IPv6.subnetMaskFromPrefixLength(64),  'ffff:ffff:ffff:ffff::');
@@ -676,7 +690,7 @@ describe('ipaddr', () => {
 
     it('broadcastAddressFromCIDR returns correct IPv6 broadcast address', (done) => {
         assert.equal(ipaddr.IPv6.broadcastAddressFromCIDR('::/0'),                  'ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff');
-        assert.equal(ipaddr.IPv6.broadcastAddressFromCIDR('2001:db8:f53a::1:1/64'), '2001:db8:f53a::ffff:ffff:ffff:ffff');
+        assert.equal(ipaddr.IPv6.broadcastAddressFromCIDR('2001:db8:f53a::1:1/64'), '2001:db8:f53a:0:ffff:ffff:ffff:ffff');
         assert.equal(ipaddr.IPv6.broadcastAddressFromCIDR('2001:db8:f53b::1:1/48'), '2001:db8:f53b:ffff:ffff:ffff:ffff:ffff');
         assert.equal(ipaddr.IPv6.broadcastAddressFromCIDR('2001:db8:f531::1:1/44'), '2001:db8:f53f:ffff:ffff:ffff:ffff:ffff');
         assert.equal(ipaddr.IPv6.broadcastAddressFromCIDR('2001:db8:f500::1/40'),   '2001:db8:f5ff:ffff:ffff:ffff:ffff:ffff');