codec.go 5.79 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 7 8 9 10
	"encoding/binary"
	"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, "/")

20
	b := new(bytes.Buffer)
Juan Batiz-Benet's avatar
gofmt  
Juan Batiz-Benet committed
21
	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
		b.Write(CodeToVarint(p.Code))
36
		sp = sp[1:]
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
37

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

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

46 47 48
		a, err := addressStringToBytes(p, sp[0])
		if err != nil {
			return nil, fmt.Errorf("failed to parse %s: %s %s", p.Name, sp[0], err)
49
		}
50
		b.Write(a)
51
		sp = sp[1:]
Juan Batiz-Benet's avatar
gofmt  
Juan Batiz-Benet committed
52
	}
53 54

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

57 58 59
func validateBytes(b []byte) (err error) {
	for len(b) > 0 {
		code, n, err := ReadVarintCode(b)
Jeromy's avatar
Jeromy committed
60 61 62 63
		if err != nil {
			return err
		}

64 65 66 67 68 69 70 71 72
		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
73

74 75 76 77 78
		size, err := sizeForAddr(p, b)
		if err != nil {
			return err
		}

Jeromy's avatar
Jeromy committed
79
		if len(b) < size || size < 0 {
80 81
			return fmt.Errorf("invalid value for size")
		}
Jeromy's avatar
Jeromy committed
82

83 84 85 86 87
		b = b[size:]
	}

	return nil
}
Jeromy's avatar
Jeromy committed
88

89
func bytesToString(b []byte) (ret string, err error) {
Juan Batiz-Benet's avatar
gofmt  
Juan Batiz-Benet committed
90 91 92
	s := ""

	for len(b) > 0 {
93 94 95 96
		code, n, err := ReadVarintCode(b)
		if err != nil {
			return "", err
		}
97 98 99

		b = b[n:]
		p := ProtocolWithCode(code)
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
100
		if p.Code == 0 {
101
			return "", fmt.Errorf("no protocol with code %d", code)
Juan Batiz-Benet's avatar
gofmt  
Juan Batiz-Benet committed
102
		}
103
		s += "/" + p.Name
Juan Batiz-Benet's avatar
gofmt  
Juan Batiz-Benet committed
104

105 106 107 108
		if p.Size == 0 {
			continue
		}

109 110 111 112 113
		size, err := sizeForAddr(p, b)
		if err != nil {
			return "", err
		}

114 115 116 117
		if len(b) < size || size < 0 {
			return "", fmt.Errorf("invalid value for size")
		}

118
		a, err := addressBytesToString(p, b[:size])
119 120 121 122
		if err != nil {
			return "", err
		}
		if len(a) > 0 {
123
			s += "/" + a
Juan Batiz-Benet's avatar
gofmt  
Juan Batiz-Benet committed
124
		}
125
		b = b[size:]
Juan Batiz-Benet's avatar
gofmt  
Juan Batiz-Benet committed
126 127 128
	}

	return s, nil
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
129
}
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
130

131
func sizeForAddr(p Protocol, b []byte) (int, error) {
132 133
	switch {
	case p.Size > 0:
134
		return (p.Size / 8), nil
135
	case p.Size == 0:
136
		return 0, nil
137
	default:
138 139 140 141 142 143
		size, n, err := ReadVarintCode(b)
		if err != nil {
			return 0, err
		}

		return size + n, nil
144 145 146
	}
}

147 148 149 150 151 152
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
153 154
		}

155
		p := ProtocolWithCode(code)
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
156
		if p.Code == 0 {
157 158 159 160 161 162
			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
163 164
		}

165
		length := n + size
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
166 167 168 169 170 171 172
		ret = append(ret, b[:length])
		b = b[length:]
	}

	return ret, nil
}

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

Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
176
	case P_IP4: // ipv4
177 178 179 180 181
		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
182

Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
183
	case P_IP6: // ipv6
184 185 186 187 188
		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
189

Juan Batiz-Benet's avatar
gofmt  
Juan Batiz-Benet committed
190
	// tcp udp dccp sctp
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
191
	case P_TCP, P_UDP, P_DCCP, P_SCTP:
Juan Batiz-Benet's avatar
gofmt  
Juan Batiz-Benet committed
192
		i, err := strconv.Atoi(s)
193 194 195 196 197
		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
198
		}
199 200 201
		b := make([]byte, 2)
		binary.BigEndian.PutUint16(b, uint16(i))
		return b, nil
202

203 204 205 206
	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)
207
		}
208 209 210 211 212 213

		// 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]))
214
		if err != nil {
215
			return nil, fmt.Errorf("failed to decode base32 %s addr: %s %s", p.Name, s, err)
216
		}
217 218 219 220 221 222 223

		// 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 {
224 225 226 227
			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")
228
		}
229

230 231 232 233 234 235 236
		onionPortBytes := make([]byte, 2)
		binary.BigEndian.PutUint16(onionPortBytes, uint16(i))
		bytes := []byte{}
		bytes = append(bytes, onionHostBytes...)
		bytes = append(bytes, onionPortBytes...)
		return bytes, nil

237
	case P_IPFS: // ipfs
238
		// the address is a varint prefixed multihash string representation
239 240 241 242
		m, err := mh.FromB58String(s)
		if err != nil {
			return nil, fmt.Errorf("failed to parse ipfs addr: %s %s", s, err)
		}
243 244 245
		size := CodeToVarint(len(m))
		b := append(size, m...)
		return b, nil
Juan Batiz-Benet's avatar
gofmt  
Juan Batiz-Benet committed
246 247
	}

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

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

Juan Batiz-Benet's avatar
gofmt  
Juan Batiz-Benet committed
254
	// ipv4,6
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
255
	case P_IP4, P_IP6:
256
		return net.IP(b).String(), nil
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
257

Juan Batiz-Benet's avatar
gofmt  
Juan Batiz-Benet committed
258
	// tcp udp dccp sctp
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
259
	case P_TCP, P_UDP, P_DCCP, P_SCTP:
Juan Batiz-Benet's avatar
gofmt  
Juan Batiz-Benet committed
260
		i := binary.BigEndian.Uint16(b)
261 262 263
		return strconv.Itoa(int(i)), nil

	case P_IPFS: // ipfs
264
		// the address is a varint-prefixed multihash string representation
265 266 267 268 269
		size, n, err := ReadVarintCode(b)
		if err != nil {
			return "", err
		}

270 271
		b = b[n:]
		if len(b) != size {
272
			return "", fmt.Errorf("inconsistent lengths")
273
		}
274 275 276 277 278
		m, err := mh.Cast(b)
		if err != nil {
			return "", err
		}
		return m.B58String(), nil
279 280 281 282 283 284

	case P_ONION:
		addr := strings.ToLower(base32.StdEncoding.EncodeToString(b[0:10]))
		port := binary.BigEndian.Uint16(b[10:12])
		return addr + ":"+ strconv.Itoa(int(port)), nil

285 286
	default:
		return "", fmt.Errorf("unknown protocol")
Juan Batiz-Benet's avatar
gofmt  
Juan Batiz-Benet committed
287
	}
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
288
}