codec.go 6.6 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
	"bytes"
5
	"encoding/base32"
Juan Batiz-Benet's avatar
gofmt  
Juan Batiz-Benet committed
6
	"encoding/binary"
Lars Gierth's avatar
Lars Gierth committed
7
	"errors"
Juan Batiz-Benet's avatar
gofmt  
Juan Batiz-Benet committed
8 9 10 11
	"fmt"
	"net"
	"strconv"
	"strings"
12

Lars Gierth's avatar
Lars Gierth committed
13
	mh "github.com/multiformats/go-multihash"
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
14 15
)

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

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

21
	b := new(bytes.Buffer)
Juan Batiz-Benet's avatar
gofmt  
Juan Batiz-Benet committed
22
	sp := strings.Split(s, "/")
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
23

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

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

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

39 40 41 42 43 44 45
		if p.Size == 0 { // no length.
			continue
		}

		if len(sp) < 1 {
			return nil, fmt.Errorf("protocol requires address, none given: %s", p.Name)
		}
46

jbenet's avatar
jbenet committed
47 48 49 50 51 52
		if p.Path {
			// it's a path protocol (terminal).
			// consume the rest of the address as the next component.
			sp = []string{"/" + strings.Join(sp, "/")}
		}

53 54 55
		a, err := addressStringToBytes(p, sp[0])
		if err != nil {
			return nil, fmt.Errorf("failed to parse %s: %s %s", p.Name, sp[0], err)
56
		}
57
		b.Write(a)
58
		sp = sp[1:]
Juan Batiz-Benet's avatar
gofmt  
Juan Batiz-Benet committed
59
	}
60 61

	return b.Bytes(), nil
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
62 63
}

64 65 66
func validateBytes(b []byte) (err error) {
	for len(b) > 0 {
		code, n, err := ReadVarintCode(b)
Jeromy's avatar
Jeromy committed
67 68 69 70
		if err != nil {
			return err
		}

71 72 73 74 75 76 77 78 79
		b = b[n:]
		p := ProtocolWithCode(code)
		if p.Code == 0 {
			return fmt.Errorf("no protocol with code %d", code)
		}

		if p.Size == 0 {
			continue
		}
Juan Batiz-Benet's avatar
gofmt  
Juan Batiz-Benet committed
80

81 82 83 84 85
		size, err := sizeForAddr(p, b)
		if err != nil {
			return err
		}

Jeromy's avatar
Jeromy committed
86
		if len(b) < size || size < 0 {
87 88
			return fmt.Errorf("invalid value for size")
		}
Jeromy's avatar
Jeromy committed
89

90 91 92 93 94
		b = b[size:]
	}

	return nil
}
Jeromy's avatar
Jeromy committed
95

96
func bytesToString(b []byte) (ret string, err error) {
Juan Batiz-Benet's avatar
gofmt  
Juan Batiz-Benet committed
97 98 99
	s := ""

	for len(b) > 0 {
100 101 102 103
		code, n, err := ReadVarintCode(b)
		if err != nil {
			return "", err
		}
104 105 106

		b = b[n:]
		p := ProtocolWithCode(code)
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
107
		if p.Code == 0 {
108
			return "", fmt.Errorf("no protocol with code %d", code)
Juan Batiz-Benet's avatar
gofmt  
Juan Batiz-Benet committed
109
		}
110
		s += "/" + p.Name
Juan Batiz-Benet's avatar
gofmt  
Juan Batiz-Benet committed
111

112 113 114 115
		if p.Size == 0 {
			continue
		}

116 117 118 119 120
		size, err := sizeForAddr(p, b)
		if err != nil {
			return "", err
		}

121 122 123 124
		if len(b) < size || size < 0 {
			return "", fmt.Errorf("invalid value for size")
		}

125
		a, err := addressBytesToString(p, b[:size])
126 127 128 129
		if err != nil {
			return "", err
		}
		if len(a) > 0 {
130
			s += "/" + a
Juan Batiz-Benet's avatar
gofmt  
Juan Batiz-Benet committed
131
		}
132
		b = b[size:]
Juan Batiz-Benet's avatar
gofmt  
Juan Batiz-Benet committed
133 134 135
	}

	return s, nil
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
136
}
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
137

138
func sizeForAddr(p Protocol, b []byte) (int, error) {
139 140
	switch {
	case p.Size > 0:
141
		return (p.Size / 8), nil
142
	case p.Size == 0:
143
		return 0, nil
jbenet's avatar
jbenet committed
144 145 146 147 148 149
	case p.Path:
		size, n, err := ReadVarintCode(b)
		if err != nil {
			return 0, err
		}
		return size + n, nil
150
	default:
151 152 153 154 155
		size, n, err := ReadVarintCode(b)
		if err != nil {
			return 0, err
		}
		return size + n, nil
156 157 158
	}
}

159 160 161 162 163 164
func bytesSplit(b []byte) ([][]byte, error) {
	var ret [][]byte
	for len(b) > 0 {
		code, n, err := ReadVarintCode(b)
		if err != nil {
			return nil, err
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
165 166
		}

167
		p := ProtocolWithCode(code)
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
168
		if p.Code == 0 {
169 170 171 172 173 174
			return nil, fmt.Errorf("no protocol with code %d", b[0])
		}

		size, err := sizeForAddr(p, b[n:])
		if err != nil {
			return nil, err
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
175 176
		}

177
		length := n + size
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
178 179 180 181 182 183 184
		ret = append(ret, b[:length])
		b = b[length:]
	}

	return ret, nil
}

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

Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
188
	case P_IP4: // ipv4
189 190 191 192 193
		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
194

Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
195
	case P_IP6: // ipv6
196 197 198 199 200
		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
201

Juan Batiz-Benet's avatar
gofmt  
Juan Batiz-Benet committed
202
	// tcp udp dccp sctp
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
203
	case P_TCP, P_UDP, P_DCCP, P_SCTP:
Juan Batiz-Benet's avatar
gofmt  
Juan Batiz-Benet committed
204
		i, err := strconv.Atoi(s)
205 206 207 208 209
		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
210
		}
211 212 213
		b := make([]byte, 2)
		binary.BigEndian.PutUint16(b, uint16(i))
		return b, nil
214

215 216 217 218
	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)
219
		}
220 221 222 223 224 225

		// 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]))
226
		if err != nil {
227
			return nil, fmt.Errorf("failed to decode base32 %s addr: %s %s", p.Name, s, err)
228
		}
229 230 231 232 233 234 235

		// 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 {
236 237 238 239
			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")
240
		}
241

242 243 244 245 246 247 248
		onionPortBytes := make([]byte, 2)
		binary.BigEndian.PutUint16(onionPortBytes, uint16(i))
		bytes := []byte{}
		bytes = append(bytes, onionHostBytes...)
		bytes = append(bytes, onionPortBytes...)
		return bytes, nil

249
	case P_IPFS: // ipfs
250
		// the address is a varint prefixed multihash string representation
251 252 253 254
		m, err := mh.FromB58String(s)
		if err != nil {
			return nil, fmt.Errorf("failed to parse ipfs addr: %s %s", s, err)
		}
255 256 257
		size := CodeToVarint(len(m))
		b := append(size, m...)
		return b, nil
jbenet's avatar
jbenet committed
258 259 260 261 262 263

	case P_UNIX:
		// the address is the whole remaining string, prefixed by a varint len
		size := CodeToVarint(len(s))
		b := append(size, []byte(s)...)
		return b, nil
Juan Batiz-Benet's avatar
gofmt  
Juan Batiz-Benet committed
264 265
	}

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

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

Juan Batiz-Benet's avatar
gofmt  
Juan Batiz-Benet committed
272
	// ipv4,6
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
273
	case P_IP4, P_IP6:
274
		return net.IP(b).String(), nil
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
275

Juan Batiz-Benet's avatar
gofmt  
Juan Batiz-Benet committed
276
	// tcp udp dccp sctp
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
277
	case P_TCP, P_UDP, P_DCCP, P_SCTP:
Juan Batiz-Benet's avatar
gofmt  
Juan Batiz-Benet committed
278
		i := binary.BigEndian.Uint16(b)
279 280 281
		return strconv.Itoa(int(i)), nil

	case P_IPFS: // ipfs
282
		// the address is a varint-prefixed multihash string representation
283 284 285 286 287
		size, n, err := ReadVarintCode(b)
		if err != nil {
			return "", err
		}

288 289
		b = b[n:]
		if len(b) != size {
jbenet's avatar
jbenet committed
290
			return "", errors.New("inconsistent lengths")
291
		}
292 293 294 295 296
		m, err := mh.Cast(b)
		if err != nil {
			return "", err
		}
		return m.B58String(), nil
297 298 299 300

	case P_ONION:
		addr := strings.ToLower(base32.StdEncoding.EncodeToString(b[0:10]))
		port := binary.BigEndian.Uint16(b[10:12])
Lars Gierth's avatar
Lars Gierth committed
301
		return addr + ":" + strconv.Itoa(int(port)), nil
302

jbenet's avatar
jbenet committed
303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320
	case P_UNIX:
		// the address is a varint len prefixed string
		size, n, err := ReadVarintCode(b)
		if err != nil {
			return "", err
		}

		b = b[n:]
		if len(b) != size {
			return "", errors.New("inconsistent lengths")
		}
		if size == 0 {
			return "", errors.New("invalid length")
		}
		s := string(b)
		s = s[1:] // remove starting slash
		return s, nil

321
	default:
jbenet's avatar
jbenet committed
322
		return "", errors.New("unknown protocol")
Juan Batiz-Benet's avatar
gofmt  
Juan Batiz-Benet committed
323
	}
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
324
}