transcoders.go 4.76 KB
Newer Older
1 2 3
package multiaddr

import (
Steven Allen's avatar
Steven Allen committed
4
	"bytes"
5 6 7 8 9 10 11 12 13 14 15 16 17
	"encoding/base32"
	"encoding/binary"
	"fmt"
	"net"
	"strconv"
	"strings"

	mh "github.com/multiformats/go-multihash"
)

type Transcoder interface {
	StringToBytes(string) ([]byte, error)
	BytesToString([]byte) (string, error)
18
	ValidateBytes([]byte) error
19 20
}

21 22 23 24 25 26
func NewTranscoderFromFunctions(
	s2b func(string) ([]byte, error),
	b2s func([]byte) (string, error),
	val func([]byte) error,
) Transcoder {
	return twrp{s2b, b2s, val}
27 28
}

29 30 31
type twrp struct {
	strtobyte func(string) ([]byte, error)
	bytetostr func([]byte) (string, error)
32
	validbyte func([]byte) error
33 34 35 36 37 38 39 40 41
}

func (t twrp) StringToBytes(s string) ([]byte, error) {
	return t.strtobyte(s)
}
func (t twrp) BytesToString(b []byte) (string, error) {
	return t.bytetostr(b)
}

42 43 44 45 46 47 48
func (t twrp) ValidateBytes(b []byte) error {
	if t.validbyte == nil {
		return nil
	}
	return t.validbyte(b)
}

49 50
var TranscoderIP4 = NewTranscoderFromFunctions(ip4StB, ip4BtS, nil)
var TranscoderIP6 = NewTranscoderFromFunctions(ip6StB, ip6BtS, nil)
Steven Allen's avatar
Steven Allen committed
51
var TranscoderIP6Zone = NewTranscoderFromFunctions(ip6zoneStB, ip6zoneBtS, ip6zoneVal)
52 53 54 55 56 57 58 59 60

func ip4StB(s string) ([]byte, error) {
	i := net.ParseIP(s).To4()
	if i == nil {
		return nil, fmt.Errorf("failed to parse ip4 addr: %s", s)
	}
	return i, nil
}

mwnx's avatar
mwnx committed
61 62 63 64 65 66 67 68 69 70 71 72 73 74
func ip6zoneStB(s string) ([]byte, error) {
	if len(s) == 0 {
		return nil, fmt.Errorf("empty ip6zone")
	}
	return []byte(s), nil
}

func ip6zoneBtS(b []byte) (string, error) {
	if len(b) == 0 {
		return "", fmt.Errorf("invalid length (should be > 0)")
	}
	return string(b), nil
}

Steven Allen's avatar
Steven Allen committed
75
func ip6zoneVal(b []byte) error {
Steven Allen's avatar
Steven Allen committed
76 77 78
	if len(b) == 0 {
		return fmt.Errorf("invalid length (should be > 0)")
	}
Steven Allen's avatar
Steven Allen committed
79 80 81 82 83 84 85
	// Not supported as this would break multiaddrs.
	if bytes.IndexByte(b, '/') >= 0 {
		return fmt.Errorf("IPv6 zone ID contains '/': %s", string(b))
	}
	return nil
}

86 87 88
func ip6StB(s string) ([]byte, error) {
	i := net.ParseIP(s).To16()
	if i == nil {
89
		return nil, fmt.Errorf("failed to parse ip6 addr: %s", s)
90 91 92 93
	}
	return i, nil
}

94 95 96 97 98 99 100 101 102 103
func ip6BtS(b []byte) (string, error) {
	ip := net.IP(b)
	if ip4 := ip.To4(); ip4 != nil {
		// Go fails to prepend the `::ffff:` part.
		return "::ffff:" + ip4.String(), nil
	}
	return ip.String(), nil
}

func ip4BtS(b []byte) (string, error) {
104 105 106
	return net.IP(b).String(), nil
}

107
var TranscoderPort = NewTranscoderFromFunctions(portStB, portBtS, nil)
108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126

func portStB(s string) ([]byte, error) {
	i, err := strconv.Atoi(s)
	if err != nil {
		return nil, fmt.Errorf("failed to parse port addr: %s", err)
	}
	if i >= 65536 {
		return nil, fmt.Errorf("failed to parse port addr: %s", "greater than 65536")
	}
	b := make([]byte, 2)
	binary.BigEndian.PutUint16(b, uint16(i))
	return b, nil
}

func portBtS(b []byte) (string, error) {
	i := binary.BigEndian.Uint16(b)
	return strconv.Itoa(int(i)), nil
}

127
var TranscoderOnion = NewTranscoderFromFunctions(onionStB, onionBtS, nil)
128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169

func onionStB(s string) ([]byte, error) {
	addr := strings.Split(s, ":")
	if len(addr) != 2 {
		return nil, fmt.Errorf("failed to parse onion addr: %s does not contain a port number.", s)
	}

	// onion address without the ".onion" substring
	if len(addr[0]) != 16 {
		return nil, fmt.Errorf("failed to parse onion addr: %s not a Tor onion address.", s)
	}
	onionHostBytes, err := base32.StdEncoding.DecodeString(strings.ToUpper(addr[0]))
	if err != nil {
		return nil, fmt.Errorf("failed to decode base32 onion addr: %s %s", s, err)
	}

	// onion port number
	i, err := strconv.Atoi(addr[1])
	if err != nil {
		return nil, fmt.Errorf("failed to parse onion addr: %s", err)
	}
	if i >= 65536 {
		return nil, fmt.Errorf("failed to parse onion addr: %s", "port greater than 65536")
	}
	if i < 1 {
		return nil, fmt.Errorf("failed to parse onion addr: %s", "port less than 1")
	}

	onionPortBytes := make([]byte, 2)
	binary.BigEndian.PutUint16(onionPortBytes, uint16(i))
	bytes := []byte{}
	bytes = append(bytes, onionHostBytes...)
	bytes = append(bytes, onionPortBytes...)
	return bytes, nil
}

func onionBtS(b []byte) (string, error) {
	addr := strings.ToLower(base32.StdEncoding.EncodeToString(b[0:10]))
	port := binary.BigEndian.Uint16(b[10:12])
	return addr + ":" + strconv.Itoa(int(port)), nil
}

170
var TranscoderP2P = NewTranscoderFromFunctions(p2pStB, p2pBtS, p2pVal)
171

172
func p2pStB(s string) ([]byte, error) {
173 174 175
	// the address is a varint prefixed multihash string representation
	m, err := mh.FromB58String(s)
	if err != nil {
176
		return nil, fmt.Errorf("failed to parse p2p addr: %s %s", s, err)
177
	}
178
	return m, nil
179 180
}

181 182 183 184 185
func p2pVal(b []byte) error {
	_, err := mh.Cast(b)
	return err
}

186
func p2pBtS(b []byte) (string, error) {
187 188 189 190 191 192 193
	m, err := mh.Cast(b)
	if err != nil {
		return "", err
	}
	return m.B58String(), nil
}

194
var TranscoderUnix = NewTranscoderFromFunctions(unixStB, unixBtS, nil)
195 196

func unixStB(s string) ([]byte, error) {
197
	return []byte(s), nil
198 199 200
}

func unixBtS(b []byte) (string, error) {
201
	return string(b), nil
202
}