package multiaddr import ( "fmt" "strings" ) // These are special sizes const ( LengthPrefixedVarSize = -1 ) // Protocol is a Multiaddr protocol description structure. type Protocol struct { // Name is the string representation of the protocol code. E.g., ip4, // ip6, tcp, udp, etc. Name string // Code is the protocol's multicodec (a normal, non-varint number). Code int // VCode is a precomputed varint encoded version of Code. VCode []byte // Size is the size of the argument to this protocol. // // * Size == 0 means this protocol takes no argument. // * Size > 0 means this protocol takes a constant sized argument. // * Size < 0 means this protocol takes a variable length, varint // prefixed argument. Size int // a size of -1 indicates a length-prefixed variable size // Path indicates a path protocol (e.g., unix). When parsing multiaddr // strings, path protocols consume the remainder of the address instead // of stopping at the next forward slash. // // Size must be LengthPrefixedVarSize. Path bool // Transcoder converts between the byte representation and the string // representation of this protocol's argument (if any). // // This should only be non-nil if Size != 0 Transcoder Transcoder } var protocolsByName = map[string]Protocol{} var protocolsByCode = map[int]Protocol{} // Protocols is the list of multiaddr protocols supported by this module. var Protocols = []Protocol{} // 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" protocolsByName["ipfs"] = protoP2P protocolsByName["p2p"] = protoP2P protocolsByCode[protoP2P.Code] = protoP2P } func AddProtocol(p Protocol) error { 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) } 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") } Protocols = append(Protocols, p) protocolsByName[p.Name] = p protocolsByCode[p.Code] = p return nil } // ProtocolWithName returns the Protocol description with given string name. func ProtocolWithName(s string) Protocol { return protocolsByName[s] } // ProtocolWithCode returns the Protocol description with given protocol code. func ProtocolWithCode(c int) Protocol { return protocolsByCode[c] } // 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 }