diff --git a/codec.go b/codec.go index 1f912f8..7797ad2 100644 --- a/codec.go +++ b/codec.go @@ -4,6 +4,8 @@ import ( "bytes" "fmt" "strings" + + multibase "github.com/multiformats/go-multibase" ) func stringToBytes(s string) ([]byte, error) { @@ -22,6 +24,18 @@ func stringToBytes(s string) ([]byte, error) { sp = sp[1:] for len(sp) > 0 { + if sp[0] == "unknown" { + if len(sp) < 2 { + return nil, fmt.Errorf("unknown protocol requires an argument") + } + _, bts, err := multibase.Decode(sp[1]) + if err != nil { + return nil, err + } + b.Write(bts) + sp = sp[2:] + continue + } p := ProtocolWithName(sp[0]) if p.Code == 0 { return nil, fmt.Errorf("no protocol with name %s", sp[0]) @@ -70,7 +84,7 @@ func validateBytes(b []byte) (err error) { b = b[n:] p := ProtocolWithCode(code) if p.Code == 0 { - return fmt.Errorf("no protocol with code %d", code) + return nil } if p.Size == 0 { @@ -108,11 +122,16 @@ func bytesToString(b []byte) (ret string, err error) { return "", err } - b = b[n:] p := ProtocolWithCode(code) if p.Code == 0 { - return "", fmt.Errorf("no protocol with code %d", code) + encoded, err := multibase.Encode(multibase.Base32, b) + if err != nil { + panic(err) + } + return s + "/unknown/" + encoded, nil } + b = b[n:] + s += "/" + p.Name if p.Size == 0 { @@ -173,8 +192,10 @@ func bytesSplit(b []byte) ([][]byte, error) { } p := ProtocolWithCode(code) + // Unknown protocol. Finish. if p.Code == 0 { - return nil, fmt.Errorf("no protocol with code %d", b[0]) + ret = append(ret, b) + break } n2, size, err := sizeForAddr(p, b[n:]) diff --git a/multiaddr.go b/multiaddr.go index 098e5ed..5b0c24c 100644 --- a/multiaddr.go +++ b/multiaddr.go @@ -67,7 +67,9 @@ func (m *multiaddr) String() string { } // Protocols returns the list of protocols this Multiaddr has. -// will panic in case we access bytes incorrectly. +// +// Will end with an "unknown" protocol if we hit any protocol codes that we +// don't understand. func (m *multiaddr) Protocols() []Protocol { ps := make([]Protocol, 0, 8) b := m.bytes @@ -79,9 +81,12 @@ func (m *multiaddr) Protocols() []Protocol { p := ProtocolWithCode(code) if p.Code == 0 { - // 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, Protocol{ + Name: "unknown", + Size: -1, + Code: -1, + }) + return ps } ps = append(ps, p) b = b[n:] diff --git a/protocol.go b/protocol.go index f9d7036..2311355 100644 --- a/protocol.go +++ b/protocol.go @@ -17,6 +17,8 @@ type Protocol struct { Name string // Code is the protocol's multicodec (a normal, non-varint number). + // + // -1 is reserved for the special "unknown" protocol. Code int // VCode is a precomputed varint encoded version of Code. diff --git a/util.go b/util.go index d1b54af..90ac9a7 100644 --- a/util.go +++ b/util.go @@ -3,6 +3,8 @@ package multiaddr import "fmt" // Split returns the sub-address portions of a multiaddr. +// +// Split stops when it hits a protocol that it doesn't understand. func Split(m Multiaddr) []Multiaddr { split, err := bytesSplit(m.Bytes()) if err != nil {