codec.go 3.73 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"
Juan Batiz-Benet's avatar
gofmt  
Juan Batiz-Benet committed
5 6
	"fmt"
	"strings"
Steven Allen's avatar
Steven Allen committed
7 8

	"github.com/multiformats/go-varint"
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
9 10
)

11
func stringToBytes(s string) ([]byte, error) {
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
12 13 14
	// consume trailing slashes
	s = strings.TrimRight(s, "/")

15
	var b bytes.Buffer
Juan Batiz-Benet's avatar
gofmt  
Juan Batiz-Benet committed
16
	sp := strings.Split(s, "/")
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
17

18
	if sp[0] != "" {
Steven Allen's avatar
Steven Allen committed
19
		return nil, fmt.Errorf("failed to parse multiaddr %q: must begin with /", s)
20 21
	}

Juan Batiz-Benet's avatar
gofmt  
Juan Batiz-Benet committed
22 23
	// consume first empty elem
	sp = sp[1:]
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
24

Steven Allen's avatar
Steven Allen committed
25 26 27 28
	if len(sp) == 0 {
		return nil, fmt.Errorf("failed to parse multiaddr %q: empty multiaddr", s)
	}

Juan Batiz-Benet's avatar
gofmt  
Juan Batiz-Benet committed
29
	for len(sp) > 0 {
30 31
		name := sp[0]
		p := ProtocolWithName(name)
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
32
		if p.Code == 0 {
Steven Allen's avatar
Steven Allen committed
33
			return nil, fmt.Errorf("failed to parse multiaddr %q: unknown protocol %s", s, sp[0])
Juan Batiz-Benet's avatar
gofmt  
Juan Batiz-Benet committed
34
		}
Steven Allen's avatar
Steven Allen committed
35
		_, _ = b.Write(p.VCode)
36
		sp = sp[1:]
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
37

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

		if len(sp) < 1 {
Steven Allen's avatar
Steven Allen committed
43
			return nil, fmt.Errorf("failed to parse multiaddr %q: unexpected end of multiaddr", s)
44
		}
45

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

52
		a, err := p.Transcoder.StringToBytes(sp[0])
53
		if err != nil {
Steven Allen's avatar
Steven Allen committed
54
			return nil, fmt.Errorf("failed to parse multiaddr %q: invalid value %q for protocol %s: %s", s, sp[0], p.Name, err)
55
		}
56
		if p.Size < 0 { // varint size.
Steven Allen's avatar
Steven Allen committed
57
			_, _ = b.Write(varint.ToUvarint(uint64(len(a))))
58
		}
59
		b.Write(a)
60
		sp = sp[1:]
Juan Batiz-Benet's avatar
gofmt  
Juan Batiz-Benet committed
61
	}
62 63

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

66
func validateBytes(b []byte) (err error) {
Steven Allen's avatar
Steven Allen committed
67 68 69
	if len(b) == 0 {
		return fmt.Errorf("empty multiaddr")
	}
70 71
	for len(b) > 0 {
		code, n, err := ReadVarintCode(b)
Jeromy's avatar
Jeromy committed
72 73 74 75
		if err != nil {
			return err
		}

76 77 78 79 80 81 82 83 84
		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
85

86
		n, size, err := sizeForAddr(p, b)
87 88 89 90
		if err != nil {
			return err
		}

91 92
		b = b[n:]

Jeromy's avatar
Jeromy committed
93
		if len(b) < size || size < 0 {
94
			return fmt.Errorf("invalid value for size %d", len(b))
95
		}
Jeromy's avatar
Jeromy committed
96

97 98 99 100 101
		err = p.Transcoder.ValidateBytes(b[:size])
		if err != nil {
			return err
		}

102 103 104 105 106
		b = b[size:]
	}

	return nil
}
Jeromy's avatar
Jeromy committed
107

Steven Allen's avatar
Steven Allen committed
108 109 110 111 112 113 114
func readComponent(b []byte) (int, Component, error) {
	var offset int
	code, n, err := ReadVarintCode(b)
	if err != nil {
		return 0, Component{}, err
	}
	offset += n
Juan Batiz-Benet's avatar
gofmt  
Juan Batiz-Benet committed
115

Steven Allen's avatar
Steven Allen committed
116 117 118 119
	p := ProtocolWithCode(code)
	if p.Code == 0 {
		return 0, Component{}, fmt.Errorf("no protocol with code %d", code)
	}
120

Steven Allen's avatar
Steven Allen committed
121 122 123 124 125 126 127
	if p.Size == 0 {
		return offset, Component{
			bytes:    b[:offset],
			offset:   offset,
			protocol: p,
		}, nil
	}
Juan Batiz-Benet's avatar
gofmt  
Juan Batiz-Benet committed
128

Steven Allen's avatar
Steven Allen committed
129 130 131 132
	n, size, err := sizeForAddr(p, b[offset:])
	if err != nil {
		return 0, Component{}, err
	}
133

Steven Allen's avatar
Steven Allen committed
134
	offset += n
135

Steven Allen's avatar
Steven Allen committed
136
	if len(b[offset:]) < size || size < 0 {
137
		return 0, Component{}, fmt.Errorf("invalid value for size %d", len(b[offset:]))
Steven Allen's avatar
Steven Allen committed
138
	}
139

Steven Allen's avatar
Steven Allen committed
140 141 142 143 144 145
	return offset + size, Component{
		bytes:    b[:offset+size],
		protocol: p,
		offset:   offset,
	}, nil
}
146

Steven Allen's avatar
Steven Allen committed
147
func bytesToString(b []byte) (ret string, err error) {
Steven Allen's avatar
Steven Allen committed
148 149 150
	if len(b) == 0 {
		return "", fmt.Errorf("empty multiaddr")
	}
Steven Allen's avatar
Steven Allen committed
151 152 153 154
	var buf strings.Builder

	for len(b) > 0 {
		n, c, err := readComponent(b)
155 156 157
		if err != nil {
			return "", err
		}
Steven Allen's avatar
Steven Allen committed
158 159
		b = b[n:]
		c.writeTo(&buf)
Juan Batiz-Benet's avatar
gofmt  
Juan Batiz-Benet committed
160 161
	}

Steven Allen's avatar
Steven Allen committed
162
	return buf.String(), nil
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
163
}
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
164

165
func sizeForAddr(p Protocol, b []byte) (skip, size int, err error) {
166 167
	switch {
	case p.Size > 0:
168
		return 0, (p.Size / 8), nil
169
	case p.Size == 0:
170
		return 0, 0, nil
171
	default:
172 173
		size, n, err := ReadVarintCode(b)
		if err != nil {
174
			return 0, 0, err
175
		}
176
		return n, size, nil
177 178 179
	}
}

180 181 182 183 184 185
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
186 187
		}

188
		p := ProtocolWithCode(code)
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
189
		if p.Code == 0 {
190 191 192
			return nil, fmt.Errorf("no protocol with code %d", b[0])
		}

193
		n2, size, err := sizeForAddr(p, b[n:])
194 195
		if err != nil {
			return nil, err
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
196 197
		}

198
		length := n + n2 + size
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
199 200 201 202 203 204
		ret = append(ret, b[:length])
		b = b[length:]
	}

	return ret, nil
}