codec.go 4.06 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 4
import (
	"encoding/binary"
5
	"errors"
Juan Batiz-Benet's avatar
gofmt  
Juan Batiz-Benet committed
6 7 8 9
	"fmt"
	"net"
	"strconv"
	"strings"
10 11

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

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

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

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

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

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

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

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

54
func bytesToString(b []byte) (ret string, err error) {
Juan Batiz-Benet's avatar
gofmt  
Juan Batiz-Benet committed
55 56 57 58
	// panic handler, in case we try accessing bytes incorrectly.
	defer func() {
		if e := recover(); e != nil {
			ret = ""
59 60 61 62 63 64 65 66
			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
67 68 69 70 71 72
		}
	}()

	s := ""

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

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

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

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

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

100 101 102 103 104 105 106 107 108 109 110 111
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
112 113 114 115 116 117 118 119 120 121 122
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 {
123 124
		code, n := ReadVarintCode(b)
		p := ProtocolWithCode(code)
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
125
		if p.Code == 0 {
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
126 127 128
			return [][]byte{}, fmt.Errorf("no protocol with code %d", b[0])
		}

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

	return ret, nil
}

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

Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
141
	case P_IP4: // ipv4
142 143 144 145 146
		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
147

Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
148
	case P_IP6: // ipv6
149 150 151 152 153
		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
154

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

	case P_IPFS: // ipfs
169
		// the address is a varint prefixed multihash string representation
170 171 172 173
		m, err := mh.FromB58String(s)
		if err != nil {
			return nil, fmt.Errorf("failed to parse ipfs addr: %s %s", s, err)
		}
174 175 176
		size := CodeToVarint(len(m))
		b := append(size, m...)
		return b, nil
Juan Batiz-Benet's avatar
gofmt  
Juan Batiz-Benet committed
177 178
	}

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

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

Juan Batiz-Benet's avatar
gofmt  
Juan Batiz-Benet committed
185
	// ipv4,6
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
186
	case P_IP4, P_IP6:
187
		return net.IP(b).String(), nil
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
188

Juan Batiz-Benet's avatar
gofmt  
Juan Batiz-Benet committed
189
	// tcp udp dccp sctp
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
190
	case P_TCP, P_UDP, P_DCCP, P_SCTP:
Juan Batiz-Benet's avatar
gofmt  
Juan Batiz-Benet committed
191
		i := binary.BigEndian.Uint16(b)
192 193 194
		return strconv.Itoa(int(i)), nil

	case P_IPFS: // ipfs
195 196 197 198 199 200
		// the address is a varint-prefixed multihash string representation
		size, n := ReadVarintCode(b)
		b = b[n:]
		if len(b) != size {
			panic("inconsistent lengths")
		}
201 202 203 204 205
		m, err := mh.Cast(b)
		if err != nil {
			return "", err
		}
		return m.B58String(), nil
Juan Batiz-Benet's avatar
gofmt  
Juan Batiz-Benet committed
206
	}
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
207

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