protocols.go 5.83 KB
Newer Older
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
1 2
package multiaddr

3 4
import (
	"encoding/binary"
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
5
	"fmt"
mwnx's avatar
mwnx committed
6
	"math/bits"
Steven Allen's avatar
go fmt  
Steven Allen committed
7
	"strings"
8 9
)

10
// Protocol is a Multiaddr protocol description structure.
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
11
type Protocol struct {
12 13 14 15 16 17
	Code       int
	Size       int // a size of -1 indicates a length-prefixed variable size
	Name       string
	VCode      []byte
	Path       bool // indicates a path protocol (eg unix, http)
	Transcoder Transcoder
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
18 19
}

20 21 22 23 24
// You **MUST** register your multicodecs with
// https://github.com/multiformats/multicodec before adding them here.
//
// TODO: Use a single source of truth for all multicodecs instead of
// distributing them like this...
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
25
const (
26 27 28 29 30
	P_IP4   = 0x0004
	P_TCP   = 0x0006
	P_UDP   = 0x0111
	P_DCCP  = 0x0021
	P_IP6   = 0x0029
31
	P_QUIC  = 0x01CC
32 33 34 35
	P_SCTP  = 0x0084
	P_UDT   = 0x012D
	P_UTP   = 0x012E
	P_UNIX  = 0x0190
36 37
	P_P2P   = 0x01A5
	P_IPFS  = 0x01A5 // alias for backwards compatability
38 39 40
	P_HTTP  = 0x01E0
	P_HTTPS = 0x01BB
	P_ONION = 0x01BC
41 42 43 44 45
)

// These are special sizes
const (
	LengthPrefixedVarSize = -1
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
46 47
)

48
// Protocols is the list of multiaddr protocols supported by this module.
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
49
var Protocols = []Protocol{
50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83
	protoIP4,
	protoTCP,
	protoUDP,
	protoDCCP,
	protoIP6,
	protoSCTP,
	protoONION,
	protoUTP,
	protoUDT,
	protoQUIC,
	protoHTTP,
	protoHTTPS,
	protoP2P,
	protoUNIX,
}

var (
	protoIP4  = Protocol{P_IP4, 32, "ip4", CodeToVarint(P_IP4), false, TranscoderIP4}
	protoTCP  = Protocol{P_TCP, 16, "tcp", CodeToVarint(P_TCP), false, TranscoderPort}
	protoUDP  = Protocol{P_UDP, 16, "udp", CodeToVarint(P_UDP), false, TranscoderPort}
	protoDCCP = Protocol{P_DCCP, 16, "dccp", CodeToVarint(P_DCCP), false, TranscoderPort}
	protoIP6  = Protocol{P_IP6, 128, "ip6", CodeToVarint(P_IP6), false, TranscoderIP6}
	// these require varint
	protoSCTP  = Protocol{P_SCTP, 16, "sctp", CodeToVarint(P_SCTP), false, TranscoderPort}
	protoONION = Protocol{P_ONION, 96, "onion", CodeToVarint(P_ONION), false, TranscoderOnion}
	protoUTP   = Protocol{P_UTP, 0, "utp", CodeToVarint(P_UTP), false, nil}
	protoUDT   = Protocol{P_UDT, 0, "udt", CodeToVarint(P_UDT), false, nil}
	protoQUIC  = Protocol{P_QUIC, 0, "quic", CodeToVarint(P_QUIC), false, nil}
	protoHTTP  = Protocol{P_HTTP, 0, "http", CodeToVarint(P_HTTP), false, nil}
	protoHTTPS = Protocol{P_HTTPS, 0, "https", CodeToVarint(P_HTTPS), false, nil}
	protoP2P   = Protocol{P_P2P, LengthPrefixedVarSize, "ipfs", CodeToVarint(P_P2P), false, TranscoderP2P}
	protoUNIX  = Protocol{P_UNIX, LengthPrefixedVarSize, "unix", CodeToVarint(P_UNIX), true, TranscoderUnix}
)

84 85
var protocolsByName = map[string]Protocol{}
var protocolsByCode = map[int]Protocol{}
86 87 88

func init() {
	for _, p := range Protocols {
89 90
		protocolsByName[p.Name] = p
		protocolsByCode[p.Code] = p
91 92 93
	}

	// explicitly set both of these
94 95
	protocolsByName["p2p"] = protoP2P
	protocolsByName["ipfs"] = protoP2P
96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119
}

// SwapToP2pMultiaddrs is a function to make the transition from /ipfs/...
// multiaddrs to /p2p/... multiaddrs easier
// The first stage of the rollout is to ship this package to all users so
// that all users of multiaddr can parse both /ipfs/ and /p2p/ multiaddrs
// as the same code (P_P2P). During this stage of the rollout, all addresses
// with P_P2P will continue printing as /ipfs/, so that older clients without
// the new parsing code won't break.
// Once the network has adopted the new parsing code broadly enough, users of
// multiaddr can add a call to this method to an init function in their codebase.
// This will cause any P_P2P multiaddr to print out as /p2p/ instead of /ipfs/.
// Note that the binary serialization of this multiaddr does not change at any
// point. This means that this code is not a breaking network change at any point
func SwapToP2pMultiaddrs() {
	for i := range Protocols {
		if Protocols[i].Code == P_P2P {
			Protocols[i].Name = "p2p"
			break
		}
	}

	protoP2P.Name = "p2p"

120 121 122
	protocolsByName["ipfs"] = protoP2P
	protocolsByName["p2p"] = protoP2P
	protocolsByCode[protoP2P.Code] = protoP2P
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
123 124
}

Jeromy's avatar
Jeromy committed
125
func AddProtocol(p Protocol) error {
126 127 128 129 130 131 132 133
	if _, ok := protocolsByName[p.Name]; ok {
		return fmt.Errorf("protocol by the name %q already exists", p.Name)
	}

	if _, ok := protocolsByCode[p.Code]; ok {
		return fmt.Errorf("protocol code %d already taken by %q", p.Code, p.Code)
	}

134 135 136 137 138 139
	if p.Size != 0 && p.Transcoder == nil {
		return fmt.Errorf("protocols with arguments must define transcoders")
	}
	if p.Path && p.Size >= 0 {
		return fmt.Errorf("path protocols must have variable-length sizes")
	}
Jeromy's avatar
Jeromy committed
140 141

	Protocols = append(Protocols, p)
142 143
	protocolsByName[p.Name] = p
	protocolsByCode[p.Code] = p
Jeromy's avatar
Jeromy committed
144 145 146
	return nil
}

147
// ProtocolWithName returns the Protocol description with given string name.
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
148
func ProtocolWithName(s string) Protocol {
149
	return protocolsByName[s]
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
150 151
}

152
// ProtocolWithCode returns the Protocol description with given protocol code.
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
153
func ProtocolWithCode(c int) Protocol {
154
	return protocolsByCode[c]
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
155
}
156

Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175
// ProtocolsWithString returns a slice of protocols matching given string.
func ProtocolsWithString(s string) ([]Protocol, error) {
	s = strings.Trim(s, "/")
	sp := strings.Split(s, "/")
	if len(sp) == 0 {
		return nil, nil
	}

	t := make([]Protocol, len(sp))
	for i, name := range sp {
		p := ProtocolWithName(name)
		if p.Code == 0 {
			return nil, fmt.Errorf("no protocol with name: %s", name)
		}
		t[i] = p
	}
	return t, nil
}

176 177
// CodeToVarint converts an integer to a varint-encoded []byte
func CodeToVarint(num int) []byte {
Steven Allen's avatar
go fmt  
Steven Allen committed
178
	buf := make([]byte, bits.Len(uint(num))/7+1)
179 180 181 182 183 184
	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 {
185 186 187 188
	num, _, err := ReadVarintCode(buf)
	if err != nil {
		panic(err)
	}
189 190 191 192 193
	return num
}

// ReadVarintCode reads a varint code from the beginning of buf.
// returns the code, and the number of bytes read.
194
func ReadVarintCode(buf []byte) (int, int, error) {
195 196
	num, n := binary.Uvarint(buf)
	if n < 0 {
197
		return 0, 0, fmt.Errorf("varints larger than uint64 not yet supported")
198
	}
199
	return int(num), n, nil
200
}