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

Juan Batiz-Benet's avatar
gofmt  
Juan Batiz-Benet committed
3
import (
4
	"encoding/base32"
Juan Batiz-Benet's avatar
gofmt  
Juan Batiz-Benet committed
5
	"encoding/binary"
6
	"errors"
Juan Batiz-Benet's avatar
gofmt  
Juan Batiz-Benet committed
7 8 9 10
	"fmt"
	"net"
	"strconv"
	"strings"
11 12

	mh "github.com/jbenet/go-multihash"
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
13 14
)

15
func stringToBytes(s string) ([]byte, error) {
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
16 17 18 19

	// consume trailing slashes
	s = strings.TrimRight(s, "/")

Juan Batiz-Benet's avatar
gofmt  
Juan Batiz-Benet committed
20 21
	b := []byte{}
	sp := strings.Split(s, "/")
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
22

23 24 25 26
	if sp[0] != "" {
		return nil, fmt.Errorf("invalid multiaddr, must begin with /")
	}

Juan Batiz-Benet's avatar
gofmt  
Juan Batiz-Benet committed
27 28
	// consume first empty elem
	sp = sp[1:]
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
29

Juan Batiz-Benet's avatar
gofmt  
Juan Batiz-Benet committed
30 31
	for len(sp) > 0 {
		p := ProtocolWithName(sp[0])
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
32
		if p.Code == 0 {
Juan Batiz-Benet's avatar
gofmt  
Juan Batiz-Benet committed
33 34
			return nil, fmt.Errorf("no protocol with name %s", sp[0])
		}
35 36
		b = append(b, CodeToVarint(p.Code)...)
		sp = sp[1:]
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
37

38 39 40 41 42 43 44 45 46 47
		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)
48
		}
49 50
		b = append(b, a...)
		sp = sp[1:]
Juan Batiz-Benet's avatar
gofmt  
Juan Batiz-Benet committed
51 52
	}
	return b, nil
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
53 54
}

55
func bytesToString(b []byte) (ret string, err error) {
Juan Batiz-Benet's avatar
gofmt  
Juan Batiz-Benet committed
56 57 58 59
	// panic handler, in case we try accessing bytes incorrectly.
	defer func() {
		if e := recover(); e != nil {
			ret = ""
60 61 62 63 64 65 66 67
			switch e := e.(type) {
			case error:
				err = e
			case string:
				err = errors.New(e)
			default:
				err = fmt.Errorf("%v", e)
			}
Juan Batiz-Benet's avatar
gofmt  
Juan Batiz-Benet committed
68 69 70 71 72 73
		}
	}()

	s := ""

	for len(b) > 0 {
74 75 76 77

		code, n := ReadVarintCode(b)
		b = b[n:]
		p := ProtocolWithCode(code)
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
78
		if p.Code == 0 {
79
			return "", fmt.Errorf("no protocol with code %d", code)
Juan Batiz-Benet's avatar
gofmt  
Juan Batiz-Benet committed
80
		}
81
		s += "/" + p.Name
Juan Batiz-Benet's avatar
gofmt  
Juan Batiz-Benet committed
82

83 84 85 86
		if p.Size == 0 {
			continue
		}

87 88
		size := sizeForAddr(p, b)
		a, err := addressBytesToString(p, b[:size])
89 90 91 92
		if err != nil {
			return "", err
		}
		if len(a) > 0 {
93
			s += "/" + a
Juan Batiz-Benet's avatar
gofmt  
Juan Batiz-Benet committed
94
		}
95
		b = b[size:]
Juan Batiz-Benet's avatar
gofmt  
Juan Batiz-Benet committed
96 97 98
	}

	return s, nil
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
99
}
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
100

101 102 103 104 105 106 107 108 109 110 111 112
func sizeForAddr(p Protocol, b []byte) int {
	switch {
	case p.Size > 0:
		return (p.Size / 8)
	case p.Size == 0:
		return 0
	default:
		size, n := ReadVarintCode(b)
		return size + n
	}
}

Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
113 114 115 116 117 118 119 120 121 122 123
func bytesSplit(b []byte) (ret [][]byte, err error) {
	// panic handler, in case we try accessing bytes incorrectly.
	defer func() {
		if e := recover(); e != nil {
			ret = [][]byte{}
			err = e.(error)
		}
	}()

	ret = [][]byte{}
	for len(b) > 0 {
124 125
		code, n := ReadVarintCode(b)
		p := ProtocolWithCode(code)
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
126
		if p.Code == 0 {
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
127 128 129
			return [][]byte{}, fmt.Errorf("no protocol with code %d", b[0])
		}

130 131
		size := sizeForAddr(p, b[n:])
		length := n + size
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
132 133 134 135 136 137 138
		ret = append(ret, b[:length])
		b = b[length:]
	}

	return ret, nil
}

Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
139
func addressStringToBytes(p Protocol, s string) ([]byte, error) {
Juan Batiz-Benet's avatar
gofmt  
Juan Batiz-Benet committed
140 141
	switch p.Code {

Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
142
	case P_IP4: // ipv4
143 144 145 146 147
		i := net.ParseIP(s).To4()
		if i == nil {
			return nil, fmt.Errorf("failed to parse ip4 addr: %s", s)
		}
		return i, nil
Juan Batiz-Benet's avatar
gofmt  
Juan Batiz-Benet committed
148

Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
149
	case P_IP6: // ipv6
150 151 152 153 154
		i := net.ParseIP(s).To16()
		if i == nil {
			return nil, fmt.Errorf("failed to parse ip6 addr: %s", s)
		}
		return i, nil
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
155

Juan Batiz-Benet's avatar
gofmt  
Juan Batiz-Benet committed
156
	// tcp udp dccp sctp
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
157
	case P_TCP, P_UDP, P_DCCP, P_SCTP:
Juan Batiz-Benet's avatar
gofmt  
Juan Batiz-Benet committed
158
		i, err := strconv.Atoi(s)
159 160 161 162 163
		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, "greater than 65536")
Juan Batiz-Benet's avatar
gofmt  
Juan Batiz-Benet committed
164
		}
165 166 167
		b := make([]byte, 2)
		binary.BigEndian.PutUint16(b, uint16(i))
		return b, nil
168

169 170 171 172
	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)
173
		}
174 175 176 177 178 179

		// 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]))
180
		if err != nil {
181
			return nil, fmt.Errorf("failed to decode base32 %s addr: %s %s", p.Name, s, err)
182
		}
183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198

		// 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, "greater than 65536")
		}
		onionPortBytes := make([]byte, 2)
		binary.BigEndian.PutUint16(onionPortBytes, uint16(i))
		bytes := []byte{}
		bytes = append(bytes, onionHostBytes...)
		bytes = append(bytes, onionPortBytes...)
		return bytes, nil

199
	case P_IPFS: // ipfs
200
		// the address is a varint prefixed multihash string representation
201 202 203 204
		m, err := mh.FromB58String(s)
		if err != nil {
			return nil, fmt.Errorf("failed to parse ipfs addr: %s %s", s, err)
		}
205 206 207
		size := CodeToVarint(len(m))
		b := append(size, m...)
		return b, nil
Juan Batiz-Benet's avatar
gofmt  
Juan Batiz-Benet committed
208 209
	}

210
	return []byte{}, fmt.Errorf("failed to parse %s addr: unknown", p.Name)
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
211 212
}

213
func addressBytesToString(p Protocol, b []byte) (string, error) {
Juan Batiz-Benet's avatar
gofmt  
Juan Batiz-Benet committed
214
	switch p.Code {
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
215

Juan Batiz-Benet's avatar
gofmt  
Juan Batiz-Benet committed
216
	// ipv4,6
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
217
	case P_IP4, P_IP6:
218
		return net.IP(b).String(), nil
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
219

Juan Batiz-Benet's avatar
gofmt  
Juan Batiz-Benet committed
220
	// tcp udp dccp sctp
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
221
	case P_TCP, P_UDP, P_DCCP, P_SCTP:
Juan Batiz-Benet's avatar
gofmt  
Juan Batiz-Benet committed
222
		i := binary.BigEndian.Uint16(b)
223 224 225
		return strconv.Itoa(int(i)), nil

	case P_IPFS: // ipfs
226 227 228 229 230 231
		// the address is a varint-prefixed multihash string representation
		size, n := ReadVarintCode(b)
		b = b[n:]
		if len(b) != size {
			panic("inconsistent lengths")
		}
232 233 234 235 236
		m, err := mh.Cast(b)
		if err != nil {
			return "", err
		}
		return m.B58String(), nil
Juan Batiz-Benet's avatar
gofmt  
Juan Batiz-Benet committed
237
	}
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
238

239
	return "", fmt.Errorf("unknown protocol")
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
240
}