diff --git a/multiaddr_test.go b/multiaddr_test.go index ffac3bb..14f7dc3 100644 --- a/multiaddr_test.go +++ b/multiaddr_test.go @@ -59,6 +59,76 @@ func TestConstructFails(t *testing.T) { "/garlic32/566niximlxdzpanmn4qouucvua3k7neniwss47li5r6ugoertzu:80", "/garlic32/566niximlxdzpanmn4qouucvua3k7neniwss47li5r6ugoertzuq:-1", "/garlic32/566niximlxdzpanmn4qouucvua3k7neniwss47li5r6ugoertzu@", + "/sam3/8|7|6|6|2|2", + "/sam3/0|8|6|6|2|2", + "/sam3/0|2|0|6|2|2", + "/sam3/0|2|6|0|2|2", + "/sam3/0|2|7|6|2|2", + "/sam3/0|2|6|7|2|2", + "/sam3/0|7|6|6|-1|2", + "/sam3/3|0|6|6|2|-1", + "/sam3/3|0|6|6|3|2", + "/sam3/3|0|6|6|2|3", + "/sam3/-1|0|6|6|2|2", + "/sam3/3|-1|6|6|2|2", + "/sam3/3|2|6|6|-2|2", + "/sam3/3|2|6|6|2|-2", + "/sam3/3|2|6|6|2|2|7", + "/sam3/a|2|6|6|2|2", + "/sam3/2|a|6|6|2|2", + "/sam3/2|2|a|6|2|2", + "/sam3/2|2|6|a|2|2", + "/sam3/2|2|6|6|a|2", + "/sam3/2|2|6|6|2|a", + "/sam3/3|2|6|6|2", + "/sam2/8|7|6|6|2|2", + "/sam2/0|8|6|6|2|2", + "/sam2/0|2|0|6|2|2", + "/sam2/0|2|6|0|2|2", + "/sam2/0|2|7|6|2|2", + "/sam2/0|2|6|7|2|2", + "/sam2/0|7|6|6|-1|2", + "/sam2/3|0|6|6|2|-1", + "/sam2/3|0|6|6|3|2", + "/sam2/3|0|6|6|2|3", + "/sam2/-1|0|6|6|2|2", + "/sam2/3|-1|6|6|2|2", + "/sam2/3|2|6|6|-2|2", + "/sam2/3|2|6|6|2|-2", + "/sam2/3|2|6|6|2|2|7", + "/sam2/3|2|6|6|2", + "/sam1/8|7|6|6|2|2", + "/sam1/0|8|6|6|2|2", + "/sam1/0|2|0|6|2|2", + "/sam1/0|2|6|0|2|2", + "/sam1/0|2|7|6|2|2", + "/sam1/0|2|6|7|2|2", + "/sam1/0|7|6|6|-1|2", + "/sam1/3|0|6|6|2|-1", + "/sam1/3|0|6|6|3|2", + "/sam1/3|0|6|6|2|3", + "/sam1/-1|0|6|6|2|2", + "/sam1/3|-1|6|6|2|2", + "/sam1/3|2|6|6|-2|2", + "/sam1/3|2|6|6|2|-2", + "/sam1/3|2|6|6|2|2|7", + "/sam1/3|2|6|6|2", + "/bob/8|7|6|6|2|2", + "/bob/0|8|6|6|2|2", + "/bob/0|2|0|6|2|2", + "/bob/0|2|6|0|2|2", + "/bob/0|2|7|6|2|2", + "/bob/0|2|6|7|2|2", + "/bob/0|7|6|6|-1|2", + "/bob/3|0|6|6|2|-1", + "/bob/3|0|6|6|3|2", + "/bob/3|0|6|6|2|3", + "/bob/-1|0|6|6|2|2", + "/bob/3|-1|6|6|2|2", + "/bob/3|2|6|6|-2|2", + "/bob/3|2|6|6|2|-2", + "/bob/3|2|6|6|2|2|7", + "/bob/3|2|6|6|2", "/udp/1234/sctp", "/udp/1234/udt/1234", "/udp/1234/utp/1234", @@ -117,6 +187,18 @@ func TestConstructSucceeds(t *testing.T) { "/garlic32/566niximlxdzpanmn4qouucvua3k7neniwss47li5r6ugoertzuq/http", "/garlic32/566niximlxdzpanmn4qouucvua3k7neniwss47li5r6ugoertzuq/tcp/8080", "/garlic32/566niximlxdzpanmn4qouucvua3k7neniwss47li5r6ugoertzuq/udp/8080", + "/sam3/0|0|1|1|0|0", + "/sam3/4|4|6|6|-1|-1", + "/sam3/3|3|6|6|2|2", + "/sam2/0|0|1|1|0|0", + "/sam2/4|4|6|6|-1|-1", + "/sam2/3|3|6|6|2|2", + "/sam1/0|0|1|1|0|0", + "/sam1/4|4|6|6|-1|-1", + "/sam1/3|3|6|6|2|2", + "/bob/0|0|1|1|0|0", + "/bob/4|4|6|6|-1|-1", + "/bob/3|3|6|6|2|2", "/udp/0", "/tcp/0", "/sctp/0", @@ -163,6 +245,14 @@ func TestEqual(t *testing.T) { m2 := newMultiaddr(t, "/ip4/127.0.0.1/tcp/1234") m3 := newMultiaddr(t, "/ip4/127.0.0.1/tcp/1234") m4 := newMultiaddr(t, "/ip4/127.0.0.1/tcp/1234/") + m5 := newMultiaddr(t, "/sam3/4|4|6|6|-1|-1") + m6 := newMultiaddr(t, "/sam3/3|3|6|6|2|2") + m7 := newMultiaddr(t, "/sam2/4|4|6|6|-1|-1") + m8 := newMultiaddr(t, "/sam2/3|3|6|6|2|2") + m9 := newMultiaddr(t, "/sam1/4|4|6|6|-1|-1") + m10 := newMultiaddr(t, "/sam1/3|3|6|6|2|2") + m11 := newMultiaddr(t, "/bob/4|4|6|6|-1|-1") + m12 := newMultiaddr(t, "/bob/3|3|6|6|2|2") if m1.Equal(m2) { t.Error("should not be equal") @@ -191,6 +281,22 @@ func TestEqual(t *testing.T) { if !m4.Equal(m3) { t.Error("should be equal") } + + if !m5.Equal(m6) { + t.Error("should be equal") + } + + if !m7.Equal(m8) { + t.Error("should be equal") + } + + if !m9.Equal(m10) { + t.Error("should be equal") + } + + if !m11.Equal(m12) { + t.Error("should be equal") + } } func TestStringToBytes(t *testing.T) { diff --git a/protocols.go b/protocols.go index cb38a70..c949540 100644 --- a/protocols.go +++ b/protocols.go @@ -22,6 +22,10 @@ const ( P_ONION3 = 0x01BD P_GARLIC64 = 0x01BE P_GARLIC32 = 0x01BF + P_BOB = 0x01FE + P_SAM3 = 0x0205 + P_SAM2 = 0x0204 + P_SAM1 = 0x0203 P_P2P_WEBRTC_DIRECT = 0x0114 ) @@ -109,6 +113,34 @@ var ( Size: LengthPrefixedVarSize, Transcoder: TranscoderGarlic32, } + protoBOB = Protocol{ + Name: "bob", + Code: P_BOB, + VCode: CodeToVarint(P_BOB), + Size: LengthGarlicBridgeSize, + Transcoder: TranscoderGarlicBridge, + } + protoSAM3 = Protocol{ + Name: "sam3", + Code: P_SAM3, + VCode: CodeToVarint(P_SAM3), + Size: LengthGarlicBridgeSize, + Transcoder: TranscoderGarlicBridge, + } + protoSAM2 = Protocol{ + Name: "sam2", + Code: P_SAM2, + VCode: CodeToVarint(P_SAM2), + Size: LengthGarlicBridgeSize, + Transcoder: TranscoderGarlicBridge, + } + protoSAM1 = Protocol{ + Name: "sam1", + Code: P_SAM1, + VCode: CodeToVarint(P_SAM1), + Size: LengthGarlicBridgeSize, + Transcoder: TranscoderGarlicBridge, + } protoUTP = Protocol{ Name: "utp", Code: P_UTP, @@ -169,6 +201,10 @@ func init() { protoONION3, protoGARLIC64, protoGARLIC32, + protoSAM3, + protoSAM2, + protoSAM1, + protoBOB, protoUTP, protoUDT, protoQUIC, diff --git a/transcoders.go b/transcoders.go index 77908b7..354f50d 100644 --- a/transcoders.go +++ b/transcoders.go @@ -284,6 +284,132 @@ func garlic32Validate(b []byte) error { return nil } +var LengthGarlicBridgeSize = 2 +var TranscoderGarlicBridge = NewTranscoderFromFunctions(garlicBridgeStB, garlicBridgeBtS, garlicBridgeValidate) + +var errorGarlicBridgeUpstreamNegativeHop = fmt.Errorf("A garlic bridge can't have a negative number of hops (your alterator is making this), check upstream.") +var errorGarlicBridgeDownstreamNegativeHop = fmt.Errorf("A garlic bridge can't have a negative number of hops (your alterator is making this), check downstream.") + +func garlicBridgeStB(s string) ([]byte, error) { + sl := strings.SplitN(s, "|", 7) + if len(sl) != 6 { + return nil, fmt.Errorf("A garlic bridge should have 6 params separated by 5 \"|\", not %d.", len(sl)) + } + v1, err := strconv.Atoi(sl[0]) + if err != nil { + return nil, err + } + if v1 < 0 || v1 > 7 { + return nil, fmt.Errorf("A garlic bridge can't have more than 7 hops, not %d, check upstream.", v1) + } + v2, err := strconv.Atoi(sl[1]) + if err != nil { + return nil, err + } + if v2 < 0 || v2 > 7 { + return nil, fmt.Errorf("A garlic bridge can't have more than 7 hops, not %d, check downstream.", v2) + } + v3, err := strconv.Atoi(sl[2]) + if err != nil { + return nil, err + } + if v3 < 1 || v3 > 6 { + return nil, fmt.Errorf("A garlic bridge must have 1 up to 6 tunnel, not %d, check upstream.", v3) + } + v4, err := strconv.Atoi(sl[3]) + if err != nil { + return nil, err + } + if v4 < 1 || v4 > 6 { + return nil, fmt.Errorf("A garlic bridge must have 1 up to 6 tunnel, not %d, check downstream.", v4) + } + v5, err := strconv.Atoi(sl[4]) + if err != nil { + return nil, err + } + if v5 < -1 || v5 > 2 { + return nil, fmt.Errorf("A garlic bridge alterator must be between -1 and 2 (included), not %d, check upstream.", v5) + } + if v5 == -1 { + if v1 == 0 { + return nil, errorGarlicBridgeUpstreamNegativeHop + } + v1 -= 1 + v5 = 2 + } + v6, err := strconv.Atoi(sl[5]) + if err != nil { + return nil, err + } + if v6 < -1 || v6 > 2 { + return nil, fmt.Errorf("A garlic bridge alterator must be between -1 and 2 (included), not %d, check downstream.", v6) + } + if v6 == -1 { + if v2 == 0 { + return nil, errorGarlicBridgeDownstreamNegativeHop + } + v2 -= 1 + v6 = 2 + } + + return []byte{ + byte((v1 << 5) + (v2 << 2) + (v3 >> 1)), + byte((v3 << 7) + (v4 << 4) + (v5 << 2) + v6), + }, nil +} + +func garlicExtractor(b []byte) [6]uint8 { + // Don't transform the uint8 in a uint, this stricity is used to colide with + // border in conversion. + u := [2]uint8{uint8(b[0]), uint8(b[1])} + var r [6]uint8 + r[5] = (u[1] << 6) >> 6 + r[4] = (u[1] << 4) >> 6 + r[3] = (u[1] << 1) >> 5 + r[2] = ((u[0] << 6) >> 5) + (u[1] >> 7) + r[1] = (u[0] << 3) >> 5 + r[0] = u[0] >> 5 + + return r +} + +func garlicBridgeBtS(b []byte) (string, error) { + err := garlicBridgeValidate(b) + if err != nil { + return "", err + } + var rs string + for i, e := range garlicExtractor(b) { + rs += strconv.Itoa(int(e)) + if i != 5 { + rs += "|" + } + } + return rs, nil +} + +func garlicBridgeValidate(b []byte) error { + if len(b) != 2 { + return fmt.Errorf("A garlic bridge compiled version length must be 2, not %d.", len(b)) + } + lv := garlicExtractor(b) + + if lv[2] < 1 || lv[2] > 6 { + return fmt.Errorf("A garlic bridge must have 1 up to 6 tunnel, not %d, check upstream.", lv[2]) + } + if lv[3] < 1 || lv[3] > 6 { + return fmt.Errorf("A garlic bridge must have 1 up to 6 tunnel, not %d, check downstream.", lv[3]) + } + if lv[4] > 2 { + return fmt.Errorf("A garlic bridge alterator compiled can't be bigger than 2, not %d, check upstream.", lv[4]) + } + if lv[5] > 2 { + return fmt.Errorf("A garlic bridge alterator compiled can't be bigger than 2, not %d, check downstream.", lv[5]) + } + + return nil +} + var TranscoderP2P = NewTranscoderFromFunctions(p2pStB, p2pBtS, p2pVal) func p2pStB(s string) ([]byte, error) { diff --git a/transcoders_test.go b/transcoders_test.go new file mode 100644 index 0000000..752e6bc --- /dev/null +++ b/transcoders_test.go @@ -0,0 +1,42 @@ +package multiaddr + +import "testing" + +func TestGarlicBridge(t *testing.T) { + // Simple code and decode + fS := "7|7|6|6|2|2" + B, err := garlicBridgeStB(fS) + if err != nil { + t.Error(err) + } + err = garlicBridgeValidate(B) + if err != nil { + t.Error(err) + } + S, err := garlicBridgeBtS(B) + if err != nil { + t.Error(err) + } + if fS != S { + t.Fatalf("Got %v instead of %v.", S, fS) + } + + shouldFail := [][]byte{ // sample (7|7|6|6|2|2) : []byte{0xFF,0x6A}, + []byte{0xFF, 0xEA}, + []byte{0xFF, 0x7A}, + []byte{0xFF, 0x7E}, + []byte{0xFF, 0x7B}, + []byte{0xFC, 0x4A}, + []byte{0xFE, 0x0A}, + []byte{0xFE, 0xDE}, + []byte{0xFE, 0xDB}, + []byte{0xFF, 0x7B, 0xFF}, + []byte{0xFF}, + } + for _, e := range shouldFail { + S, err = garlicBridgeBtS(e) + if err == nil || S != "" { + t.Fatalf("Should fail but works with %v, and got %v.", e, S) + } + } +}