1// SPDX-License-Identifier: ISC
2// Copyright (c) 2014-2020 Bitmark Inc.
3// Use of this source code is governed by an ISC
4// license that can be found in the LICENSE file.
5
6package util
7
8import (
9	"encoding/hex"
10	"strings"
11	"testing"
12
13	"github.com/bitmark-inc/bitmarkd/fault"
14)
15
16// Test IP address detection
17func TestCanonical(t *testing.T) {
18
19	type item struct {
20		in  string
21		out string
22	}
23
24	testData := []item{
25		{"127.0.0.1:1234", "127.0.0.1:1234"},
26		{"127.0.0.1:1", "127.0.0.1:1"},
27		{" 127.0.0.1 : 1 ", "127.0.0.1:1"},
28		{"127.0.0.1:65535", "127.0.0.1:65535"},
29		{"0.0.0.0:1234", "0.0.0.0:1234"},
30		{"[::1]:1234", "[::1]:1234"},
31		{"[::]:1234", "[::]:1234"},
32		{"*:1234", "[::]:1234"},
33		{"[0:0::0:0]:1234", "[::]:1234"},
34		{"[0:0:0:0::1]:1234", "[::1]:1234"},
35		{"[2404:6800:4008:c07::66]:443", "[2404:6800:4008:c07::66]:443"},
36		{"[2404:6800:4008:0c07:0000:0000:0000:0066]:443", "[2404:6800:4008:c07::66]:443"},
37	}
38
39	for i, d := range testData {
40
41		// create a connection item
42		c, err := NewConnection(d.in)
43		if nil != err {
44			t.Fatalf("NewConnection failed on:[%d] %q  error: %s", i, d.in, err)
45		}
46
47		// convert to text
48		s, v6 := c.CanonicalIPandPort("")
49		if s != d.out {
50			t.Fatalf("failed on:[%d] %q  actual: %q  expected: %q", i, d.in, s, d.out)
51		}
52
53		t.Logf("converted:[%d]: %q  to(%t): %q", i, d.in, v6, s)
54
55		// check packing/unpacking
56		pk := c.Pack()
57		cu, n := pk.Unpack()
58		if len(pk) != n {
59			t.Fatalf("Unpack failed on:[%d] %q  only read: %d of: %d bytes", i, d.in, n, len(pk))
60		}
61		su, v6u := cu.CanonicalIPandPort("")
62		if su != s {
63			t.Fatalf("failed on:[%d] %x  actual: %q  expected: %q", i, pk, su, s)
64		}
65		if v6u != v6 {
66			t.Fatalf("failed on:[%d] %x  actual v6: %t  expected v6: %t", i, pk, v6u, v6)
67		}
68	}
69}
70
71// Test IP address
72func TestCanonicalIP(t *testing.T) {
73
74	type item struct {
75		ip  string
76		err string
77	}
78
79	testData := []item{
80		{"256.0.0.0:1234", "no such host"},
81		{"0.256.0.0:1234", "no such host"},
82		{"0.0.256.0:1234", "no such host"},
83		{"0.0.0.256:1234", "no such host"},
84		{"0:0:1234", "too many colons in address"},
85		{"[]:1234", "no such host"},
86		{"[as34::]:1234", "no such host"},
87		{"[1ffff::]:1234", "no such host"},
88		{"[2404:6800:4008:0c07:0000:0000:0000:0066:1234]:443", "no such host"},
89		{"!:1234", "no such host"},
90		{"#:1234", "no such host"},
91		{"127.0.0.1", "missing port in address"},
92		{"[2001:0db8:85a3:08d3:1319:8a2e:0370:7344]", "missing port in address"},
93	}
94
95	for i, d := range testData {
96		c, err := NewConnection(d.ip)
97		if nil == err {
98			s, v6 := c.CanonicalIPandPort("")
99			t.Fatalf("eroneoulssly converted:[%d]: %q  to(%t): %q", i, d.ip, v6, s)
100		}
101		if !strings.Contains(err.Error(), d.err) {
102			t.Fatalf("NewConnection failed on:[%d] %q  error: %s", i, d, err)
103		}
104	}
105}
106
107// Test port range
108func TestCanonicalPort(t *testing.T) {
109
110	testData := []string{
111		"127.0.0.1:0",
112		"127.0.0.1:65536",
113		"127.0.0.1:-1",
114	}
115
116	for i, d := range testData {
117		c, err := NewConnection(d)
118		if nil == err {
119			s, v6 := c.CanonicalIPandPort("")
120			t.Fatalf("eroneoulssly converted:[%d]: %q  to(%t): %q", i, d, v6, s)
121		}
122		if fault.InvalidPortNumber != err {
123			t.Fatalf("NewConnection failed on:[%d] %q  error: %s", i, d, err)
124		}
125	}
126}
127
128// helper
129func makePacked(h string) PackedConnection {
130	b, err := hex.DecodeString(h)
131	if nil != err {
132		panic(err)
133	}
134	return b
135}
136
137// Test of unpack
138func TestCanonicalUnpack(t *testing.T) {
139
140	type item struct {
141		packed    PackedConnection
142		addresses []string
143		v4        string
144		v6        string
145	}
146
147	testData := []item{
148		{
149			packed: makePacked("1304d200000000000000000000ffff7f0000011304d200000000000000000000000000000001"),
150			addresses: []string{
151				"127.0.0.1:1234",
152				"[::1]:1234",
153			},
154			v4: "127.0.0.1:1234",
155			v6: "[::1]:1234",
156		},
157		{
158			packed: makePacked("1304d2000000000000000000000000000000011304d200000000000000000000ffff7f000001"),
159			addresses: []string{
160				"[::1]:1234",
161				"127.0.0.1:1234",
162			},
163			v4: "127.0.0.1:1234",
164			v6: "[::1]:1234",
165		},
166		{
167			packed: makePacked("1301bb2404680040080c0700000000000000661301bb2404680040080c070000000000000066"),
168			addresses: []string{
169				"[2404:6800:4008:c07::66]:443",
170				"[2404:6800:4008:c07::66]:443",
171			},
172			v4: "<nil>",
173			v6: "[2404:6800:4008:c07::66]:443",
174		},
175		{ // extraneous data
176			packed: makePacked("1301bb2404680040080c0700000000000000661301bb2404680040080c0700000000000000660000000000000000000000000000000000000000"),
177			addresses: []string{
178				"[2404:6800:4008:c07::66]:443",
179				"[2404:6800:4008:c07::66]:443",
180			},
181			v4: "<nil>",
182			v6: "[2404:6800:4008:c07::66]:443",
183		},
184		{ // bad data -> no items
185			packed:    makePacked("1401bb2404680040080c0700000000000000661001bb2404680040080c0700000000000000660000000000000000000000000000000000000000"),
186			addresses: []string{},
187			v4:        "<nil>",
188			v6:        "<nil>",
189		},
190		{ // bad data followed by good addresses -> consider as all bad
191			packed:    makePacked("01221304d200000000000000000000ffff7f0000011304d200000000000000000000000000000001"),
192			addresses: []string{},
193			v4:        "<nil>",
194			v6:        "<nil>",
195		},
196	}
197
198	for i, data := range testData {
199		p := data.packed
200		a := data.addresses
201		al := len(a)
202
203		v4, v6 := p.Unpack46()
204		v4s := "<nil>"
205		if nil != v4 {
206			v4s, _ = v4.CanonicalIPandPort("")
207		}
208		v6s := "<nil>"
209		if nil != v6 {
210			v6s, _ = v6.CanonicalIPandPort("")
211		}
212		if data.v4 != v4s {
213			t.Errorf("unpack46:[%d]: v4 actual: %q  expected: %q", i, v4s, data.v4)
214		}
215		if data.v6 != v6s {
216			t.Errorf("unpack66:[%d]: v6 actual: %q  expected: %q", i, v6s, data.v6)
217		}
218
219	inner:
220		for k := 0; k < 10; k += 1 {
221			l := len(p)
222			c, n := p.Unpack()
223			p = p[n:]
224
225			if nil == c {
226				// only signal error if nil was not just after last address
227				if k != al {
228					t.Errorf("unpack:[%d]: nil connection, n: %d", i, n)
229				}
230
231			} else {
232				s, v6 := c.CanonicalIPandPort("")
233				if k >= al {
234					t.Errorf("unpack:[%d]: bytes: %d of %d result: (%t) %q", i, n, l, v6, s)
235				} else if s != a[k] {
236					t.Errorf("unpack:[%d]: bytes: %d of %d result: (%t) %q  expected: %s", i, n, l, v6, s, a[k])
237				}
238			}
239			if n <= 0 {
240				break inner
241			}
242		}
243	}
244}
245