multiaddr_test.go 11.2 KB
Newer Older
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
1 2 3
package multiaddr

import (
Juan Batiz-Benet's avatar
gofmt  
Juan Batiz-Benet committed
4 5
	"bytes"
	"encoding/hex"
6
	"math/rand"
Jeromy's avatar
Jeromy committed
7
	"strings"
Juan Batiz-Benet's avatar
gofmt  
Juan Batiz-Benet committed
8
	"testing"
9
	"time"
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
10 11
)

Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
12
func newMultiaddr(t *testing.T, a string) Multiaddr {
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
13 14 15 16 17 18 19
	m, err := NewMultiaddr(a)
	if err != nil {
		t.Error(err)
	}
	return m
}

20 21 22 23 24 25 26 27 28 29 30
func TestConstructFails(t *testing.T) {
	cases := []string{
		"/ip4",
		"/ip4/::1",
		"/ip4/fdpsofodsajfdoisa",
		"/ip6",
		"/udp",
		"/tcp",
		"/sctp",
		"/udp/65536",
		"/tcp/65536",
Marten Seemann's avatar
Marten Seemann committed
31
		"/quic/65536",
32 33
		"/onion/9imaq4ygg2iegci7:80",
		"/onion/aaimaq4ygg2iegci7:80",
34 35
		"/onion/timaq4ygg2iegci7:0",
		"/onion/timaq4ygg2iegci7:-1",
36
		"/onion/timaq4ygg2iegci7",
37
		"/onion/timaq4ygg2iegci@:666",
38 39 40 41 42 43 44
		"/udp/1234/sctp",
		"/udp/1234/udt/1234",
		"/udp/1234/utp/1234",
		"/ip4/127.0.0.1/udp/jfodsajfidosajfoidsa",
		"/ip4/127.0.0.1/udp",
		"/ip4/127.0.0.1/tcp/jfodsajfidosajfoidsa",
		"/ip4/127.0.0.1/tcp",
Marten Seemann's avatar
Marten Seemann committed
45
		"/ip4/127.0.0.1/quic/1234",
46 47
		"/ip4/127.0.0.1/ipfs",
		"/ip4/127.0.0.1/ipfs/tcp",
jbenet's avatar
jbenet committed
48 49
		"/unix",
		"/ip4/1.2.3.4/tcp/80/unix",
50 51 52 53
	}

	for _, a := range cases {
		if _, err := NewMultiaddr(a); err == nil {
54
			t.Errorf("should have failed: %s - %s", a, err)
55 56 57 58 59 60 61 62 63 64
		}
	}
}

func TestConstructSucceeds(t *testing.T) {
	cases := []string{
		"/ip4/1.2.3.4",
		"/ip4/0.0.0.0",
		"/ip6/::1",
		"/ip6/2601:9:4f81:9700:803e:ca65:66e8:c21",
Marten Seemann's avatar
Marten Seemann committed
65
		"/ip6/2601:9:4f81:9700:803e:ca65:66e8:c21/udp/1234/quic",
66 67
		"/onion/timaq4ygg2iegci7:1234",
		"/onion/timaq4ygg2iegci7:80/http",
68 69 70 71 72 73 74 75
		"/udp/0",
		"/tcp/0",
		"/sctp/0",
		"/udp/1234",
		"/tcp/1234",
		"/sctp/1234",
		"/udp/65535",
		"/tcp/65535",
76
		"/ipfs/QmcgpsyWgH8Y8ajJz1Cu72KnS5uo2Aa2LpzU7kinSupNKC",
77 78 79
		"/udp/1234/sctp/1234",
		"/udp/1234/udt",
		"/udp/1234/utp",
80 81 82
		"/tcp/1234/http",
		"/tcp/1234/https",
		"/ipfs/QmcgpsyWgH8Y8ajJz1Cu72KnS5uo2Aa2LpzU7kinSupNKC/tcp/1234",
83 84 85 86
		"/ip4/127.0.0.1/udp/1234",
		"/ip4/127.0.0.1/udp/0",
		"/ip4/127.0.0.1/tcp/1234",
		"/ip4/127.0.0.1/tcp/1234/",
Marten Seemann's avatar
Marten Seemann committed
87
		"/ip4/127.0.0.1/udp/1234/quic",
88 89
		"/ip4/127.0.0.1/ipfs/QmcgpsyWgH8Y8ajJz1Cu72KnS5uo2Aa2LpzU7kinSupNKC",
		"/ip4/127.0.0.1/ipfs/QmcgpsyWgH8Y8ajJz1Cu72KnS5uo2Aa2LpzU7kinSupNKC/tcp/1234",
jbenet's avatar
jbenet committed
90 91 92 93
		"/unix/a/b/c/d/e",
		"/unix/stdio",
		"/ip4/1.2.3.4/tcp/80/unix/a/b/c/d/e/f",
		"/ip4/127.0.0.1/ipfs/QmcgpsyWgH8Y8ajJz1Cu72KnS5uo2Aa2LpzU7kinSupNKC/tcp/1234/unix/stdio",
94 95 96 97
	}

	for _, a := range cases {
		if _, err := NewMultiaddr(a); err != nil {
98
			t.Errorf("should have succeeded: %s -- %s", a, err)
99 100 101 102
		}
	}
}

Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
103 104 105 106
func TestEqual(t *testing.T) {
	m1 := newMultiaddr(t, "/ip4/127.0.0.1/udp/1234")
	m2 := newMultiaddr(t, "/ip4/127.0.0.1/tcp/1234")
	m3 := newMultiaddr(t, "/ip4/127.0.0.1/tcp/1234")
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
107
	m4 := newMultiaddr(t, "/ip4/127.0.0.1/tcp/1234/")
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127

	if m1.Equal(m2) {
		t.Error("should not be equal")
	}

	if m2.Equal(m1) {
		t.Error("should not be equal")
	}

	if !m2.Equal(m3) {
		t.Error("should be equal")
	}

	if !m3.Equal(m2) {
		t.Error("should be equal")
	}

	if !m1.Equal(m1) {
		t.Error("should be equal")
	}
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
128 129 130 131 132 133 134 135

	if !m2.Equal(m4) {
		t.Error("should be equal")
	}

	if !m4.Equal(m3) {
		t.Error("should be equal")
	}
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
136 137
}

Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
138 139
func TestStringToBytes(t *testing.T) {

Juan Batiz-Benet's avatar
gofmt  
Juan Batiz-Benet committed
140 141 142 143 144
	testString := func(s string, h string) {
		b1, err := hex.DecodeString(h)
		if err != nil {
			t.Error("failed to decode hex", h)
		}
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
145

146
		b2, err := stringToBytes(s)
Juan Batiz-Benet's avatar
gofmt  
Juan Batiz-Benet committed
147 148 149
		if err != nil {
			t.Error("failed to convert", s)
		}
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
150

Juan Batiz-Benet's avatar
gofmt  
Juan Batiz-Benet committed
151 152 153
		if !bytes.Equal(b1, b2) {
			t.Error("failed to convert", s, "to", b1, "got", b2)
		}
Jeromy's avatar
Jeromy committed
154 155 156 157

		if err := validateBytes(b2); err != nil {
			t.Error(err)
		}
Juan Batiz-Benet's avatar
gofmt  
Juan Batiz-Benet committed
158
	}
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
159

160
	testString("/ip4/127.0.0.1/udp/1234", "047f000001910204d2")
161
	testString("/ip4/127.0.0.1/tcp/4321", "047f0000010610e1")
162
	testString("/ip4/127.0.0.1/udp/1234/ip4/127.0.0.1/tcp/4321", "047f000001910204d2047f0000010610e1")
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
163 164 165 166
}

func TestBytesToString(t *testing.T) {

Juan Batiz-Benet's avatar
gofmt  
Juan Batiz-Benet committed
167 168 169 170 171
	testString := func(s1 string, h string) {
		b, err := hex.DecodeString(h)
		if err != nil {
			t.Error("failed to decode hex", h)
		}
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
172

Jeromy's avatar
Jeromy committed
173 174 175 176
		if err := validateBytes(b); err != nil {
			t.Error(err)
		}

177
		s2, err := bytesToString(b)
Juan Batiz-Benet's avatar
gofmt  
Juan Batiz-Benet committed
178
		if err != nil {
179
			t.Error("failed to convert", b, err)
Juan Batiz-Benet's avatar
gofmt  
Juan Batiz-Benet committed
180
		}
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
181

Juan Batiz-Benet's avatar
gofmt  
Juan Batiz-Benet committed
182 183 184 185
		if s1 != s2 {
			t.Error("failed to convert", b, "to", s1, "got", s2)
		}
	}
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
186

187
	testString("/ip4/127.0.0.1/udp/1234", "047f000001910204d2")
188
	testString("/ip4/127.0.0.1/tcp/4321", "047f0000010610e1")
189
	testString("/ip4/127.0.0.1/udp/1234/ip4/127.0.0.1/tcp/4321", "047f000001910204d2047f0000010610e1")
190
	testString("/onion/aaimaq4ygg2iegci:80", "bc030010c0439831b48218480050")
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
191 192
}

Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
193
func TestBytesSplitAndJoin(t *testing.T) {
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
194 195 196 197

	testString := func(s string, res []string) {
		m, err := NewMultiaddr(s)
		if err != nil {
198
			t.Fatal("failed to convert", s, err)
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
199 200
		}

Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
201
		split := Split(m)
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
202 203 204 205 206 207 208 209 210 211 212
		if len(split) != len(res) {
			t.Error("not enough split components", split)
			return
		}

		for i, a := range split {
			if a.String() != res[i] {
				t.Errorf("split component failed: %s != %s", a, res[i])
			}
		}

Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
213 214 215 216 217
		joined := Join(split...)
		if !m.Equal(joined) {
			t.Errorf("joined components failed: %s != %s", m, joined)
		}

Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233
		// modifying underlying bytes is fine.
		m2 := m.(*multiaddr)
		for i := range m2.bytes {
			m2.bytes[i] = 0
		}

		for i, a := range split {
			if a.String() != res[i] {
				t.Errorf("split component failed: %s != %s", a, res[i])
			}
		}
	}

	testString("/ip4/1.2.3.4/udp/1234", []string{"/ip4/1.2.3.4", "/udp/1234"})
	testString("/ip4/1.2.3.4/tcp/1/ip4/2.3.4.5/udp/2",
		[]string{"/ip4/1.2.3.4", "/tcp/1", "/ip4/2.3.4.5", "/udp/2"})
234 235
	testString("/ip4/1.2.3.4/utp/ip4/2.3.4.5/udp/2/udt",
		[]string{"/ip4/1.2.3.4", "/utp", "/ip4/2.3.4.5", "/udp/2", "/udt"})
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
236 237
}

Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
238
func TestProtocols(t *testing.T) {
Juan Batiz-Benet's avatar
gofmt  
Juan Batiz-Benet committed
239 240 241 242 243
	m, err := NewMultiaddr("/ip4/127.0.0.1/udp/1234")
	if err != nil {
		t.Error("failed to construct", "/ip4/127.0.0.1/udp/1234")
	}

Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
244
	ps := m.Protocols()
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
245
	if ps[0].Code != ProtocolWithName("ip4").Code {
Juan Batiz-Benet's avatar
gofmt  
Juan Batiz-Benet committed
246 247 248 249
		t.Error(ps[0], ProtocolWithName("ip4"))
		t.Error("failed to get ip4 protocol")
	}

Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
250
	if ps[1].Code != ProtocolWithName("udp").Code {
Juan Batiz-Benet's avatar
gofmt  
Juan Batiz-Benet committed
251 252 253
		t.Error(ps[1], ProtocolWithName("udp"))
		t.Error("failed to get udp protocol")
	}
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
254 255

}
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
256

Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
257 258 259 260 261 262 263 264 265 266 267 268 269 270
func TestProtocolsWithString(t *testing.T) {
	pwn := ProtocolWithName
	good := map[string][]Protocol{
		"/ip4":                    []Protocol{pwn("ip4")},
		"/ip4/tcp":                []Protocol{pwn("ip4"), pwn("tcp")},
		"ip4/tcp/udp/ip6":         []Protocol{pwn("ip4"), pwn("tcp"), pwn("udp"), pwn("ip6")},
		"////////ip4/tcp":         []Protocol{pwn("ip4"), pwn("tcp")},
		"ip4/udp/////////":        []Protocol{pwn("ip4"), pwn("udp")},
		"////////ip4/tcp////////": []Protocol{pwn("ip4"), pwn("tcp")},
	}

	for s, ps1 := range good {
		ps2, err := ProtocolsWithString(s)
		if err != nil {
Jeromy's avatar
go vet  
Jeromy committed
271
			t.Errorf("ProtocolsWithString(%s) should have succeeded", s)
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290
		}

		for i, ps1p := range ps1 {
			ps2p := ps2[i]
			if ps1p.Code != ps2p.Code {
				t.Errorf("mismatch: %s != %s, %s", ps1p.Name, ps2p.Name, s)
			}
		}
	}

	bad := []string{
		"dsijafd",                           // bogus proto
		"/ip4/tcp/fidosafoidsa",             // bogus proto
		"////////ip4/tcp/21432141/////////", // bogus proto
		"////////ip4///////tcp/////////",    // empty protos in between
	}

	for _, s := range bad {
		if _, err := ProtocolsWithString(s); err == nil {
Jeromy's avatar
go vet  
Jeromy committed
291
			t.Errorf("ProtocolsWithString(%s) should have failed", s)
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
292 293 294 295 296
		}
	}

}

Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
297
func TestEncapsulate(t *testing.T) {
Juan Batiz-Benet's avatar
gofmt  
Juan Batiz-Benet committed
298 299 300 301 302 303 304 305 306 307 308
	m, err := NewMultiaddr("/ip4/127.0.0.1/udp/1234")
	if err != nil {
		t.Error(err)
	}

	m2, err := NewMultiaddr("/udp/5678")
	if err != nil {
		t.Error(err)
	}

	b := m.Encapsulate(m2)
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
309
	if s := b.String(); s != "/ip4/127.0.0.1/udp/1234/udp/5678" {
Juan Batiz-Benet's avatar
gofmt  
Juan Batiz-Benet committed
310 311 312 313
		t.Error("encapsulate /ip4/127.0.0.1/udp/1234/udp/5678 failed.", s)
	}

	m3, _ := NewMultiaddr("/udp/5678")
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
314
	c := b.Decapsulate(m3)
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
315
	if s := c.String(); s != "/ip4/127.0.0.1/udp/1234" {
Juan Batiz-Benet's avatar
gofmt  
Juan Batiz-Benet committed
316 317 318 319
		t.Error("decapsulate /udp failed.", "/ip4/127.0.0.1/udp/1234", s)
	}

	m4, _ := NewMultiaddr("/ip4/127.0.0.1")
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
320
	d := c.Decapsulate(m4)
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
321
	if s := d.String(); s != "" {
Juan Batiz-Benet's avatar
gofmt  
Juan Batiz-Benet committed
322 323
		t.Error("decapsulate /ip4 failed.", "/", s)
	}
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
324
}
325 326 327 328 329 330 331 332 333

func assertValueForProto(t *testing.T, a Multiaddr, p int, exp string) {
	t.Logf("checking for %s in %s", ProtocolWithCode(p).Name, a)
	fv, err := a.ValueForProtocol(p)
	if err != nil {
		t.Fatal(err)
	}

	if fv != exp {
Jeromy's avatar
go vet  
Jeromy committed
334
		t.Fatalf("expected %q for %d in %s, but got %q instead", exp, p, a, fv)
335 336 337 338 339 340 341 342 343 344
	}
}

func TestGetValue(t *testing.T) {
	a := newMultiaddr(t, "/ip4/127.0.0.1/utp/tcp/5555/udp/1234/utp/ipfs/QmbHVEEepCi7rn7VL7Exxpd2Ci9NNB6ifvqwhsrbRMgQFP")
	assertValueForProto(t, a, P_IP4, "127.0.0.1")
	assertValueForProto(t, a, P_UTP, "")
	assertValueForProto(t, a, P_TCP, "5555")
	assertValueForProto(t, a, P_UDP, "1234")
	assertValueForProto(t, a, P_IPFS, "QmbHVEEepCi7rn7VL7Exxpd2Ci9NNB6ifvqwhsrbRMgQFP")
345
	assertValueForProto(t, a, P_P2P, "QmbHVEEepCi7rn7VL7Exxpd2Ci9NNB6ifvqwhsrbRMgQFP")
346 347 348 349 350 351 352 353 354 355

	_, err := a.ValueForProtocol(P_IP6)
	switch err {
	case ErrProtocolNotFound:
		break
	case nil:
		t.Fatal("expected value lookup to fail")
	default:
		t.Fatalf("expected ErrProtocolNotFound but got: %s", err)
	}
356 357 358 359 360 361 362 363 364 365 366

	a = newMultiaddr(t, "/ip4/0.0.0.0") // only one addr
	assertValueForProto(t, a, P_IP4, "0.0.0.0")

	a = newMultiaddr(t, "/ip4/0.0.0.0/ip4/0.0.0.0/ip4/0.0.0.0") // same sub-addr
	assertValueForProto(t, a, P_IP4, "0.0.0.0")

	a = newMultiaddr(t, "/ip4/0.0.0.0/udp/12345/utp") // ending in a no-value one.
	assertValueForProto(t, a, P_IP4, "0.0.0.0")
	assertValueForProto(t, a, P_UDP, "12345")
	assertValueForProto(t, a, P_UTP, "")
jbenet's avatar
jbenet committed
367 368 369 370

	a = newMultiaddr(t, "/ip4/0.0.0.0/unix/a/b/c/d") // ending in a path one.
	assertValueForProto(t, a, P_IP4, "0.0.0.0")
	assertValueForProto(t, a, P_UNIX, "a/b/c/d")
371
}
372 373 374 375 376 377 378 379 380 381 382 383 384

func TestFuzzBytes(t *testing.T) {
	rand.Seed(time.Now().UnixNano())
	// Bump up these numbers if you want to stress this
	buf := make([]byte, 256)
	for i := 0; i < 2000; i++ {
		l := rand.Intn(len(buf))
		rand.Read(buf[:l])

		// just checking that it doesnt panic
		ma, err := NewMultiaddrBytes(buf[:l])
		if err == nil {
			// for any valid multiaddrs, make sure these calls don't panic
Jeromy's avatar
go vet  
Jeromy committed
385
			_ = ma.String()
386 387 388 389
			ma.Protocols()
		}
	}
}
Jeromy's avatar
Jeromy committed
390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418

func randMaddrString() string {
	good_corpus := []string{"tcp", "ip", "udp", "ipfs", "0.0.0.0", "127.0.0.1", "12345", "QmbHVEEepCi7rn7VL7Exxpd2Ci9NNB6ifvqwhsrbRMgQFP"}

	size := rand.Intn(256)
	parts := make([]string, 0, size)
	for i := 0; i < size; i++ {
		switch rand.Intn(5) {
		case 0, 1, 2:
			parts = append(parts, good_corpus[rand.Intn(len(good_corpus))])
		default:
			badbuf := make([]byte, rand.Intn(256))
			rand.Read(badbuf)
			parts = append(parts, string(badbuf))
		}
	}

	return "/" + strings.Join(parts, "/")
}

func TestFuzzString(t *testing.T) {
	rand.Seed(time.Now().UnixNano())
	// Bump up these numbers if you want to stress this
	for i := 0; i < 2000; i++ {

		// just checking that it doesnt panic
		ma, err := NewMultiaddr(randMaddrString())
		if err == nil {
			// for any valid multiaddrs, make sure these calls don't panic
Jeromy's avatar
go vet  
Jeromy committed
419
			_ = ma.String()
Jeromy's avatar
Jeromy committed
420 421 422 423
			ma.Protocols()
		}
	}
}
Lars Gierth's avatar
Lars Gierth committed
424 425 426 427 428 429 430 431 432 433 434 435

func TestBinaryRepresentation(t *testing.T) {
	expected := []byte{0x4, 0x7f, 0x0, 0x0, 0x1, 0x91, 0x2, 0x4, 0xd2}
	ma, err := NewMultiaddr("/ip4/127.0.0.1/udp/1234")
	if err != nil {
		t.Error(err)
	}

	if !bytes.Equal(ma.Bytes(), expected) {
		t.Errorf("expected %x, got %x", expected, ma.Bytes())
	}
}
Steven Allen's avatar
Steven Allen committed
436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455

func TestRoundTrip(t *testing.T) {
	for _, s := range []string{
		"/unix/a/b/c/d",
		"/ip4/127.0.0.1/tcp/123",
		"/ip4/127.0.0.1/udp/123",
		"/ip4/127.0.0.1/udp/123/ip6/::",
		"/ipfs/QmbHVEEepCi7rn7VL7Exxpd2Ci9NNB6ifvqwhsrbRMgQFP",
		"/ipfs/QmbHVEEepCi7rn7VL7Exxpd2Ci9NNB6ifvqwhsrbRMgQFP/unix/a/b/c",
	} {
		ma, err := NewMultiaddr(s)
		if err != nil {
			t.Errorf("error when parsing %q: %s", s, err)
			continue
		}
		if ma.String() != s {
			t.Errorf("failed to round trip %q", s)
		}
	}
}