codec.go 4.42 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 173 174 175 176 177 178
	case P_TOR:
		fields := strings.Split(s, ".onion")
		if len(fields) != 2 {
			return nil, fmt.Errorf("failed to parse ipfs addr: %s not a Tor .onion address.", s)
		}
		b, err := base32.StdEncoding.DecodeString(strings.ToUpper(fields[0]))
		if err != nil {
			return nil, fmt.Errorf("failed to parse ipfs addr: %s %s", s, err)
		}
		return b, nil
179
	case P_IPFS: // ipfs
180
		// the address is a varint prefixed multihash string representation
181 182 183 184
		m, err := mh.FromB58String(s)
		if err != nil {
			return nil, fmt.Errorf("failed to parse ipfs addr: %s %s", s, err)
		}
185 186 187
		size := CodeToVarint(len(m))
		b := append(size, m...)
		return b, nil
Juan Batiz-Benet's avatar
gofmt  
Juan Batiz-Benet committed
188 189
	}

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

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

Juan Batiz-Benet's avatar
gofmt  
Juan Batiz-Benet committed
196
	// ipv4,6
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
197
	case P_IP4, P_IP6:
198
		return net.IP(b).String(), nil
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
199

Juan Batiz-Benet's avatar
gofmt  
Juan Batiz-Benet committed
200
	// tcp udp dccp sctp
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
201
	case P_TCP, P_UDP, P_DCCP, P_SCTP:
Juan Batiz-Benet's avatar
gofmt  
Juan Batiz-Benet committed
202
		i := binary.BigEndian.Uint16(b)
203 204 205
		return strconv.Itoa(int(i)), nil

	case P_IPFS: // ipfs
206 207 208 209 210 211
		// the address is a varint-prefixed multihash string representation
		size, n := ReadVarintCode(b)
		b = b[n:]
		if len(b) != size {
			panic("inconsistent lengths")
		}
212 213 214 215 216
		m, err := mh.Cast(b)
		if err != nil {
			return "", err
		}
		return m.B58String(), nil
Juan Batiz-Benet's avatar
gofmt  
Juan Batiz-Benet committed
217
	}
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
218

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