diff --git a/codec.go b/codec.go index 9fef2da..64e7d04 100644 --- a/codec.go +++ b/codec.go @@ -1,6 +1,7 @@ package multiaddr import ( + "encoding/base32" "encoding/binary" "errors" "fmt" @@ -165,6 +166,40 @@ func addressStringToBytes(p Protocol, s string) ([]byte, error) { binary.BigEndian.PutUint16(b, uint16(i)) return b, nil + case P_ONION: + addr := strings.Split(s, ":") + if len(addr) != 2 { + return nil, fmt.Errorf("failed to parse %s addr: %s does not contain a port number.", p.Name, s) + } + + // onion address without the ".onion" substring + if len(addr[0]) != 16 { + return nil, fmt.Errorf("failed to parse %s addr: %s not a Tor onion address.", p.Name, s) + } + onionHostBytes, err := base32.StdEncoding.DecodeString(strings.ToUpper(addr[0])) + if err != nil { + return nil, fmt.Errorf("failed to decode base32 %s addr: %s %s", p.Name, s, err) + } + + // onion port number + i, err := strconv.Atoi(addr[1]) + if err != nil { + return nil, fmt.Errorf("failed to parse %s addr: %s", p.Name, err) + } + if i >= 65536 { + return nil, fmt.Errorf("failed to parse %s addr: %s", p.Name, "port greater than 65536") + } + if i < 1 { + return nil, fmt.Errorf("failed to parse %s addr: %s", p.Name, "port less than 1") + } + + onionPortBytes := make([]byte, 2) + binary.BigEndian.PutUint16(onionPortBytes, uint16(i)) + bytes := []byte{} + bytes = append(bytes, onionHostBytes...) + bytes = append(bytes, onionPortBytes...) + return bytes, nil + case P_IPFS: // ipfs // the address is a varint prefixed multihash string representation m, err := mh.FromB58String(s) diff --git a/multiaddr_test.go b/multiaddr_test.go index f0e9c36..3a60751 100644 --- a/multiaddr_test.go +++ b/multiaddr_test.go @@ -25,6 +25,12 @@ func TestConstructFails(t *testing.T) { "/sctp", "/udp/65536", "/tcp/65536", + "/onion/9imaq4ygg2iegci7:80", + "/onion/aaimaq4ygg2iegci7:80", + "/onion/timaq4ygg2iegci7:0", + "/onion/timaq4ygg2iegci7:-1", + "/onion/timaq4ygg2iegci7", + "/onion/timaq4ygg2iegci@:666", "/udp/1234/sctp", "/udp/1234/udt/1234", "/udp/1234/utp/1234", @@ -49,6 +55,8 @@ func TestConstructSucceeds(t *testing.T) { "/ip4/0.0.0.0", "/ip6/::1", "/ip6/2601:9:4f81:9700:803e:ca65:66e8:c21", + "/onion/timaq4ygg2iegci7:1234", + "/onion/timaq4ygg2iegci7:80/http", "/udp/0", "/tcp/0", "/sctp/0", diff --git a/protocols.csv b/protocols.csv index 213e9b5..fa27ba3 100644 --- a/protocols.csv +++ b/protocols.csv @@ -10,3 +10,4 @@ code size name 421 V ipfs 480 0 http 443 0 https +444 10 onion \ No newline at end of file diff --git a/protocols.go b/protocols.go index c4ee5df..c233cef 100644 --- a/protocols.go +++ b/protocols.go @@ -30,6 +30,7 @@ const ( P_IPFS = 421 P_HTTP = 480 P_HTTPS = 443 + P_ONION = 444 ) // These are special sizes @@ -46,6 +47,7 @@ var Protocols = []Protocol{ Protocol{P_IP6, 128, "ip6", CodeToVarint(P_IP6)}, // these require varint: Protocol{P_SCTP, 16, "sctp", CodeToVarint(P_SCTP)}, + Protocol{P_ONION, 80, "onion", CodeToVarint(P_ONION)}, Protocol{P_UTP, 0, "utp", CodeToVarint(P_UTP)}, Protocol{P_UDT, 0, "udt", CodeToVarint(P_UDT)}, Protocol{P_HTTP, 0, "http", CodeToVarint(P_HTTP)},