13 mh "github.com/multiformats/go-multihash"
16 type Transcoder interface {
17 StringToBytes(string) ([]byte, error)
18 BytesToString([]byte) (string, error)
19 ValidateBytes([]byte) error
22 func NewTranscoderFromFunctions(
23 s2b func(string) ([]byte, error),
24 b2s func([]byte) (string, error),
25 val func([]byte) error,
27 return twrp{s2b, b2s, val}
31 strtobyte func(string) ([]byte, error)
32 bytetostr func([]byte) (string, error)
33 validbyte func([]byte) error
36 func (t twrp) StringToBytes(s string) ([]byte, error) {
39 func (t twrp) BytesToString(b []byte) (string, error) {
43 func (t twrp) ValidateBytes(b []byte) error {
44 if t.validbyte == nil {
50 var TranscoderIP4 = NewTranscoderFromFunctions(ip4StB, ip4BtS, nil)
51 var TranscoderIP6 = NewTranscoderFromFunctions(ip6StB, ip6BtS, nil)
52 var TranscoderIP6Zone = NewTranscoderFromFunctions(ip6zoneStB, ip6zoneBtS, ip6zoneVal)
54 func ip4StB(s string) ([]byte, error) {
55 i := net.ParseIP(s).To4()
57 return nil, fmt.Errorf("failed to parse ip4 addr: %s", s)
62 func ip6zoneStB(s string) ([]byte, error) {
64 return nil, fmt.Errorf("empty ip6zone")
69 func ip6zoneBtS(b []byte) (string, error) {
71 return "", fmt.Errorf("invalid length (should be > 0)")
76 func ip6zoneVal(b []byte) error {
78 return fmt.Errorf("invalid length (should be > 0)")
80 // Not supported as this would break multiaddrs.
81 if bytes.IndexByte(b, '/') >= 0 {
82 return fmt.Errorf("IPv6 zone ID contains '/': %s", string(b))
87 func ip6StB(s string) ([]byte, error) {
88 i := net.ParseIP(s).To16()
90 return nil, fmt.Errorf("failed to parse ip6 addr: %s", s)
95 func ip6BtS(b []byte) (string, error) {
97 if ip4 := ip.To4(); ip4 != nil {
98 // Go fails to prepend the `::ffff:` part.
99 return "::ffff:" + ip4.String(), nil
101 return ip.String(), nil
104 func ip4BtS(b []byte) (string, error) {
105 return net.IP(b).String(), nil
108 var TranscoderPort = NewTranscoderFromFunctions(portStB, portBtS, nil)
110 func portStB(s string) ([]byte, error) {
111 i, err := strconv.Atoi(s)
113 return nil, fmt.Errorf("failed to parse port addr: %s", err)
116 return nil, fmt.Errorf("failed to parse port addr: %s", "greater than 65536")
119 binary.BigEndian.PutUint16(b, uint16(i))
123 func portBtS(b []byte) (string, error) {
124 i := binary.BigEndian.Uint16(b)
125 return strconv.Itoa(int(i)), nil
128 var TranscoderOnion = NewTranscoderFromFunctions(onionStB, onionBtS, nil)
130 func onionStB(s string) ([]byte, error) {
131 addr := strings.Split(s, ":")
133 return nil, fmt.Errorf("failed to parse onion addr: %s does not contain a port number.", s)
136 // onion address without the ".onion" substring
137 if len(addr[0]) != 16 {
138 return nil, fmt.Errorf("failed to parse onion addr: %s not a Tor onion address.", s)
140 onionHostBytes, err := base32.StdEncoding.DecodeString(strings.ToUpper(addr[0]))
142 return nil, fmt.Errorf("failed to decode base32 onion addr: %s %s", s, err)
146 i, err := strconv.Atoi(addr[1])
148 return nil, fmt.Errorf("failed to parse onion addr: %s", err)
151 return nil, fmt.Errorf("failed to parse onion addr: %s", "port greater than 65536")
154 return nil, fmt.Errorf("failed to parse onion addr: %s", "port less than 1")
157 onionPortBytes := make([]byte, 2)
158 binary.BigEndian.PutUint16(onionPortBytes, uint16(i))
160 bytes = append(bytes, onionHostBytes...)
161 bytes = append(bytes, onionPortBytes...)
165 func onionBtS(b []byte) (string, error) {
166 addr := strings.ToLower(base32.StdEncoding.EncodeToString(b[0:10]))
167 port := binary.BigEndian.Uint16(b[10:12])
168 return addr + ":" + strconv.Itoa(int(port)), nil
171 var TranscoderOnion3 = NewTranscoderFromFunctions(onion3StB, onion3BtS, nil)
173 func onion3StB(s string) ([]byte, error) {
174 addr := strings.Split(s, ":")
176 return nil, fmt.Errorf("failed to parse onion addr: %s does not contain a port number.", s)
179 // onion address without the ".onion" substring
180 if len(addr[0]) != 56 {
181 return nil, fmt.Errorf("failed to parse onion addr: %s not a Tor onionv3 address. len == %d", s, len(addr[0]))
183 onionHostBytes, err := base32.StdEncoding.DecodeString(strings.ToUpper(addr[0]))
185 return nil, fmt.Errorf("failed to decode base32 onion addr: %s %s", s, err)
189 i, err := strconv.Atoi(addr[1])
191 return nil, fmt.Errorf("failed to parse onion addr: %s", err)
194 return nil, fmt.Errorf("failed to parse onion addr: %s", "port greater than 65536")
197 return nil, fmt.Errorf("failed to parse onion addr: %s", "port less than 1")
200 onionPortBytes := make([]byte, 2)
201 binary.BigEndian.PutUint16(onionPortBytes, uint16(i))
203 bytes = append(bytes, onionHostBytes[0:35]...)
204 bytes = append(bytes, onionPortBytes...)
208 func onion3BtS(b []byte) (string, error) {
209 addr := strings.ToLower(base32.StdEncoding.EncodeToString(b[0:35]))
210 port := binary.BigEndian.Uint16(b[35:37])
211 str := addr + ":" + strconv.Itoa(int(port))
215 var TranscoderGarlic64 = NewTranscoderFromFunctions(garlic64StB, garlic64BtS, garlicValidate)
217 // i2p uses an alternate character set for base64 addresses. This returns an appropriate encoder.
218 var garlicBase64Encoding = base64.NewEncoding("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-~")
220 func garlic64StB(s string) ([]byte, error) {
221 // i2p base64 address
222 if len(s) < 516 || len(s) > 616 {
223 return nil, fmt.Errorf("failed to parse garlic addr: %s not an i2p base64 address. len: %d\n", s, len(s))
225 garlicHostBytes, err := garlicBase64Encoding.DecodeString(s)
227 return nil, fmt.Errorf("failed to decode base64 i2p addr: %s %s", s, err)
230 return garlicHostBytes, nil
233 func garlic64BtS(b []byte) (string, error) {
235 return "", fmt.Errorf("failed to validate garlic addr: %s not an i2p base64 address. len: %d\n", b, len(b))
237 addr := garlicBase64Encoding.EncodeToString(b)
241 func garlicValidate(b []byte) error {
243 return fmt.Errorf("failed to validate garlic addr: %s not an i2p base64 address. len: %d\n", b, len(b))
248 var TranscoderP2P = NewTranscoderFromFunctions(p2pStB, p2pBtS, p2pVal)
250 func p2pStB(s string) ([]byte, error) {
251 // the address is a varint prefixed multihash string representation
252 m, err := mh.FromB58String(s)
254 return nil, fmt.Errorf("failed to parse p2p addr: %s %s", s, err)
259 func p2pVal(b []byte) error {
264 func p2pBtS(b []byte) (string, error) {
269 return m.B58String(), nil
272 var TranscoderUnix = NewTranscoderFromFunctions(unixStB, unixBtS, nil)
274 func unixStB(s string) ([]byte, error) {
275 return []byte(s), nil
278 func unixBtS(b []byte) (string, error) {
279 return string(b), nil