diff --git a/codec.go b/codec.go index d08544b..cc8a2ed 100644 --- a/codec.go +++ b/codec.go @@ -28,12 +28,14 @@ func stringToBytes(s string) ([]byte, error) { if p == nil { return nil, fmt.Errorf("no protocol with name %s", sp[0]) } - b = append(b, byte(p.Code)) + b = append(b, CodeToVarint(p.Code)...) + sp = sp[1:] - a := addressStringToBytes(p, sp[1]) - b = append(b, a...) - - sp = sp[2:] + if p.Size > 0 { + a := addressStringToBytes(p, sp[0]) + b = append(b, a...) + sp = sp[1:] + } } return b, nil } @@ -50,18 +52,22 @@ func bytesToString(b []byte) (ret string, err error) { s := "" for len(b) > 0 { - p := ProtocolWithCode(int(b[0])) + + code, n := ReadVarintCode(b) + b = b[n:] + p := ProtocolWithCode(code) if p == nil { - return "", fmt.Errorf("no protocol with code %d", b[0]) + return "", fmt.Errorf("no protocol with code %d", code) } s = strings.Join([]string{s, "/", p.Name}, "") - b = b[1:] - a := addressBytesToString(p, b[:(p.Size/8)]) - if len(a) > 0 { - s = strings.Join([]string{s, "/", a}, "") + if p.Size > 0 { + a := addressBytesToString(p, b[:(p.Size/8)]) + if len(a) > 0 { + s = strings.Join([]string{s, "/", a}, "") + } + b = b[(p.Size / 8):] } - b = b[(p.Size / 8):] } return s, nil @@ -78,12 +84,13 @@ func bytesSplit(b []byte) (ret [][]byte, err error) { ret = [][]byte{} for len(b) > 0 { - p := ProtocolWithCode(int(b[0])) + code, n := ReadVarintCode(b) + p := ProtocolWithCode(code) if p == nil { return [][]byte{}, fmt.Errorf("no protocol with code %d", b[0]) } - length := 1 + (p.Size / 8) + length := n + (p.Size / 8) ret = append(ret, b[:length]) b = b[length:] } diff --git a/multiaddr.go b/multiaddr.go index a2bea09..3071fa9 100644 --- a/multiaddr.go +++ b/multiaddr.go @@ -67,14 +67,15 @@ func (m *multiaddr) Protocols() []*Protocol { ps := []*Protocol{} b := m.bytes[:] for len(b) > 0 { - p := ProtocolWithCode(int(b[0])) + code, n := ReadVarintCode(b) + p := ProtocolWithCode(code) if p == nil { // this is a panic (and not returning err) because this should've been // caught on constructing the Multiaddr panic(fmt.Errorf("no protocol with code %d", b[0])) } ps = append(ps, p) - b = b[1+(p.Size/8):] + b = b[n+(p.Size/8):] } return ps } diff --git a/multiaddr_test.go b/multiaddr_test.go index a5cb666..15e2be0 100644 --- a/multiaddr_test.go +++ b/multiaddr_test.go @@ -68,6 +68,8 @@ func TestStringToBytes(t *testing.T) { } testString("/ip4/127.0.0.1/udp/1234", "047f0000011104d2") + testString("/ip4/127.0.0.1/tcp/4321", "047f0000010610e1") + testString("/ip4/127.0.0.1/udp/1234/ip4/127.0.0.1/tcp/4321", "047f0000011104d2047f0000010610e1") } func TestBytesToString(t *testing.T) { @@ -89,6 +91,8 @@ func TestBytesToString(t *testing.T) { } testString("/ip4/127.0.0.1/udp/1234", "047f0000011104d2") + testString("/ip4/127.0.0.1/tcp/4321", "047f0000010610e1") + testString("/ip4/127.0.0.1/udp/1234/ip4/127.0.0.1/tcp/4321", "047f0000011104d2047f0000010610e1") } func TestBytesSplitAndJoin(t *testing.T) { @@ -96,7 +100,7 @@ func TestBytesSplitAndJoin(t *testing.T) { testString := func(s string, res []string) { m, err := NewMultiaddr(s) if err != nil { - t.Error("failed to convert", s) + t.Fatal("failed to convert", s, err) } split := Split(m) @@ -132,6 +136,8 @@ func TestBytesSplitAndJoin(t *testing.T) { 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"}) + 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"}) } func TestProtocols(t *testing.T) { diff --git a/protocols.csv b/protocols.csv index 62bc5c2..a8b1e3a 100644 --- a/protocols.csv +++ b/protocols.csv @@ -5,5 +5,7 @@ code size name 33 16 dccp 41 128 ip6 132 16 sctp +301 0 udt +302 0 utp 480 0 http 443 0 https diff --git a/protocols.go b/protocols.go index ed4d29f..49051be 100644 --- a/protocols.go +++ b/protocols.go @@ -1,10 +1,15 @@ package multiaddr +import ( + "encoding/binary" +) + // Protocol is a Multiaddr protocol description structure. type Protocol struct { - Code int - Size int - Name string + Code int + Size int + Name string + VCode []byte } // replicating table here to: @@ -18,17 +23,21 @@ const ( P_DCCP = 33 P_IP6 = 41 P_SCTP = 132 + P_UTP = 301 + P_UDT = 302 ) // Protocols is the list of multiaddr protocols supported by this module. var Protocols = []*Protocol{ - &Protocol{P_IP4, 32, "ip4"}, - &Protocol{P_TCP, 16, "tcp"}, - &Protocol{P_UDP, 16, "udp"}, - &Protocol{P_DCCP, 16, "dccp"}, - &Protocol{P_IP6, 128, "ip6"}, + &Protocol{P_IP4, 32, "ip4", CodeToVarint(P_IP4)}, + &Protocol{P_TCP, 16, "tcp", CodeToVarint(P_TCP)}, + &Protocol{P_UDP, 16, "udp", CodeToVarint(P_UDP)}, + &Protocol{P_DCCP, 16, "dccp", CodeToVarint(P_DCCP)}, + &Protocol{P_IP6, 128, "ip6", CodeToVarint(P_IP6)}, // these require varint: - &Protocol{P_SCTP, 16, "sctp"}, + &Protocol{P_SCTP, 16, "sctp", CodeToVarint(P_SCTP)}, + &Protocol{P_UTP, 0, "utp", CodeToVarint(P_UTP)}, + &Protocol{P_UDT, 0, "udt", CodeToVarint(P_UDT)}, // {480, 0, "http"}, // {443, 0, "https"}, } @@ -52,3 +61,26 @@ func ProtocolWithCode(c int) *Protocol { } return nil } + +// CodeToVarint converts an integer to a varint-encoded []byte +func CodeToVarint(num int) []byte { + buf := make([]byte, (num/7)+1) // varint package is uint64 + n := binary.PutUvarint(buf, uint64(num)) + return buf[:n] +} + +// VarintToCode converts a varint-encoded []byte to an integer protocol code +func VarintToCode(buf []byte) int { + num, _ := ReadVarintCode(buf) + return num +} + +// ReadVarintCode reads a varint code from the beginning of buf. +// returns the code, and the number of bytes read. +func ReadVarintCode(buf []byte) (int, int) { + num, n := binary.Uvarint(buf) + if n < 0 { + panic("varints larger than uint64 not yet supported") + } + return int(num), n +}