diff --git a/codec.go b/codec.go index 03502e9..f034a41 100644 --- a/codec.go +++ b/codec.go @@ -6,6 +6,8 @@ import ( "net" "strconv" "strings" + + mh "github.com/jbenet/go-multihash" ) func stringToBytes(s string) ([]byte, error) { @@ -31,17 +33,19 @@ func stringToBytes(s string) ([]byte, error) { b = append(b, CodeToVarint(p.Code)...) sp = sp[1:] - if p.Size > 0 { - if len(sp) < 1 { - return nil, fmt.Errorf("protocol requires address, none given: %s", p.Name) - } - a, err := addressStringToBytes(p, sp[0]) - if err != nil { - return nil, fmt.Errorf("failed to parse %s: %s %s", p.Name, sp[0], err) - } - b = append(b, a...) - sp = sp[1:] + if p.Size == 0 { // no length. + continue } + + if len(sp) < 1 { + return nil, fmt.Errorf("protocol requires address, none given: %s", p.Name) + } + a, err := addressStringToBytes(p, sp[0]) + if err != nil { + return nil, fmt.Errorf("failed to parse %s: %s %s", p.Name, sp[0], err) + } + b = append(b, a...) + sp = sp[1:] } return b, nil } @@ -67,13 +71,18 @@ func bytesToString(b []byte) (ret string, err error) { } s = strings.Join([]string{s, "/", p.Name}, "") - 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):] + if p.Size == 0 { + continue } + + a, err := addressBytesToString(p, b[:(p.Size/8)]) + if err != nil { + return "", err + } + if len(a) > 0 { + s = strings.Join([]string{s, "/", a}, "") + } + b = b[(p.Size / 8):] } return s, nil @@ -133,23 +142,39 @@ func addressStringToBytes(p Protocol, s string) ([]byte, error) { b := make([]byte, 2) binary.BigEndian.PutUint16(b, uint16(i)) return b, nil + + case P_IPFS: // ipfs + // the address is a multihash string representation + m, err := mh.FromB58String(s) + if err != nil { + return nil, fmt.Errorf("failed to parse ipfs addr: %s %s", s, err) + } + return []byte(m), nil } return []byte{}, fmt.Errorf("failed to parse %s addr: unknown", p.Name) } -func addressBytesToString(p Protocol, b []byte) string { +func addressBytesToString(p Protocol, b []byte) (string, error) { switch p.Code { // ipv4,6 case P_IP4, P_IP6: - return net.IP(b).String() + return net.IP(b).String(), nil // tcp udp dccp sctp case P_TCP, P_UDP, P_DCCP, P_SCTP: i := binary.BigEndian.Uint16(b) - return strconv.Itoa(int(i)) + return strconv.Itoa(int(i)), nil + + case P_IPFS: // ipfs + // the address is a multihash string representation + m, err := mh.Cast(b) + if err != nil { + return "", err + } + return m.B58String(), nil } - return "" + return "", fmt.Errorf("unknown protocol") } diff --git a/multiaddr_test.go b/multiaddr_test.go index 6545897..f0e9c36 100644 --- a/multiaddr_test.go +++ b/multiaddr_test.go @@ -32,11 +32,13 @@ func TestConstructFails(t *testing.T) { "/ip4/127.0.0.1/udp", "/ip4/127.0.0.1/tcp/jfodsajfidosajfoidsa", "/ip4/127.0.0.1/tcp", + "/ip4/127.0.0.1/ipfs", + "/ip4/127.0.0.1/ipfs/tcp", } for _, a := range cases { if _, err := NewMultiaddr(a); err == nil { - t.Errorf("should have failed: %s", a) + t.Errorf("should have failed: %s - %s", a, err) } } } @@ -55,18 +57,24 @@ func TestConstructSucceeds(t *testing.T) { "/sctp/1234", "/udp/65535", "/tcp/65535", + "/ipfs/QmcgpsyWgH8Y8ajJz1Cu72KnS5uo2Aa2LpzU7kinSupNKC", "/udp/1234/sctp/1234", "/udp/1234/udt", "/udp/1234/utp", + "/tcp/1234/http", + "/tcp/1234/https", + "/ipfs/QmcgpsyWgH8Y8ajJz1Cu72KnS5uo2Aa2LpzU7kinSupNKC/tcp/1234", "/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/", + "/ip4/127.0.0.1/ipfs/QmcgpsyWgH8Y8ajJz1Cu72KnS5uo2Aa2LpzU7kinSupNKC", + "/ip4/127.0.0.1/ipfs/QmcgpsyWgH8Y8ajJz1Cu72KnS5uo2Aa2LpzU7kinSupNKC/tcp/1234", } for _, a := range cases { if _, err := NewMultiaddr(a); err != nil { - t.Errorf("should have succeeded: %s", a) + t.Errorf("should have succeeded: %s -- %s", a, err) } } } diff --git a/protocols.csv b/protocols.csv index a8b1e3a..213e9b5 100644 --- a/protocols.csv +++ b/protocols.csv @@ -7,5 +7,6 @@ code size name 132 16 sctp 301 0 udt 302 0 utp +421 V ipfs 480 0 http 443 0 https diff --git a/protocols.go b/protocols.go index eaddc61..c4ee5df 100644 --- a/protocols.go +++ b/protocols.go @@ -9,7 +9,7 @@ import ( // Protocol is a Multiaddr protocol description structure. type Protocol struct { Code int - Size int + Size int // a size of -1 indicates a length-prefixed variable size Name string VCode []byte } @@ -19,14 +19,22 @@ type Protocol struct { // 2. ensuring errors in the csv don't screw up code. // 3. changing a number has to happen in two places. const ( - P_IP4 = 4 - P_TCP = 6 - P_UDP = 17 - P_DCCP = 33 - P_IP6 = 41 - P_SCTP = 132 - P_UTP = 301 - P_UDT = 302 + P_IP4 = 4 + P_TCP = 6 + P_UDP = 17 + P_DCCP = 33 + P_IP6 = 41 + P_SCTP = 132 + P_UTP = 301 + P_UDT = 302 + P_IPFS = 421 + P_HTTP = 480 + P_HTTPS = 443 +) + +// These are special sizes +const ( + LengthPrefixedVarSize = -1 ) // Protocols is the list of multiaddr protocols supported by this module. @@ -40,8 +48,9 @@ var Protocols = []Protocol{ 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"}, + Protocol{P_HTTP, 0, "http", CodeToVarint(P_HTTP)}, + Protocol{P_HTTPS, 0, "https", CodeToVarint(P_HTTPS)}, + Protocol{P_IPFS, LengthPrefixedVarSize, "ipfs", CodeToVarint(P_IPFS)}, } // ProtocolWithName returns the Protocol description with given string name.