convert.go 5.7 KB
Newer Older
1
package manet
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
2 3 4 5

import (
	"fmt"
	"net"
Cole Brown's avatar
Cole Brown committed
6
	"path/filepath"
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
7

8
	ma "github.com/multiformats/go-multiaddr"
9
	madns "github.com/multiformats/go-multiaddr-dns"
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
10 11 12 13
)

var errIncorrectNetAddr = fmt.Errorf("incorrect network addr conversion")

Jeromy's avatar
Jeromy committed
14 15
// FromNetAddr converts a net.Addr type to a Multiaddr.
func FromNetAddr(a net.Addr) (ma.Multiaddr, error) {
Jeromy's avatar
Jeromy committed
16 17 18
	return defaultCodecs.FromNetAddr(a)
}

19
// FromNetAddr converts a net.Addr to Multiaddress.
Jeromy's avatar
Jeromy committed
20
func (cm *CodecMap) FromNetAddr(a net.Addr) (ma.Multiaddr, error) {
Jeromy's avatar
Jeromy committed
21 22 23
	if a == nil {
		return nil, fmt.Errorf("nil multiaddr")
	}
Jeromy's avatar
Jeromy committed
24
	p, err := cm.getAddrParser(a.Network())
Jeromy's avatar
Jeromy committed
25 26 27
	if err != nil {
		return nil, err
	}
Jeromy's avatar
Jeromy committed
28

Jeromy's avatar
Jeromy committed
29 30
	return p(a)
}
Jeromy's avatar
Jeromy committed
31

Jeromy's avatar
Jeromy committed
32 33 34 35
// ToNetAddr converts a Multiaddr to a net.Addr
// Must be ThinWaist. acceptable protocol stacks are:
// /ip{4,6}/{tcp, udp}
func ToNetAddr(maddr ma.Multiaddr) (net.Addr, error) {
Jeromy's avatar
Jeromy committed
36 37 38
	return defaultCodecs.ToNetAddr(maddr)
}

39
// ToNetAddr converts a Multiaddress to a standard net.Addr.
Jeromy's avatar
Jeromy committed
40
func (cm *CodecMap) ToNetAddr(maddr ma.Multiaddr) (net.Addr, error) {
Jeromy's avatar
Jeromy committed
41 42
	protos := maddr.Protocols()
	final := protos[len(protos)-1]
Jeromy's avatar
Jeromy committed
43

Jeromy's avatar
Jeromy committed
44
	p, err := cm.getMaddrParser(final.Name)
Jeromy's avatar
Jeromy committed
45 46 47 48 49
	if err != nil {
		return nil, err
	}

	return p(maddr)
Jeromy's avatar
Jeromy committed
50 51
}

Jeromy's avatar
Jeromy committed
52 53 54 55
func parseBasicNetMaddr(maddr ma.Multiaddr) (net.Addr, error) {
	network, host, err := DialArgs(maddr)
	if err != nil {
		return nil, err
Jeromy's avatar
Jeromy committed
56 57
	}

Jeromy's avatar
Jeromy committed
58 59 60 61 62 63 64
	switch network {
	case "tcp", "tcp4", "tcp6":
		return net.ResolveTCPAddr(network, host)
	case "udp", "udp4", "udp6":
		return net.ResolveUDPAddr(network, host)
	case "ip", "ip4", "ip6":
		return net.ResolveIPAddr(network, host)
Cole Brown's avatar
Cole Brown committed
65 66
	case "unix":
		return net.ResolveUnixAddr(network, host)
Jeromy's avatar
Jeromy committed
67 68
	}

Jeromy's avatar
Jeromy committed
69
	return nil, fmt.Errorf("network not supported: %s", network)
Jeromy's avatar
Jeromy committed
70 71
}

mwnx's avatar
mwnx committed
72
func FromIPAndZone(ip net.IP, zone string) (ma.Multiaddr, error) {
Jeromy's avatar
Jeromy committed
73 74
	switch {
	case ip.To4() != nil:
mwnx's avatar
mwnx committed
75
		return ma.NewComponent("ip4", ip.String())
Jeromy's avatar
Jeromy committed
76
	case ip.To16() != nil:
mwnx's avatar
mwnx committed
77 78 79 80 81 82 83 84 85 86 87 88 89
		ip6, err := ma.NewComponent("ip6", ip.String())
		if err != nil {
			return nil, err
		}
		if zone == "" {
			return ip6, nil
		} else {
			zone, err := ma.NewComponent("ip6zone", zone)
			if err != nil {
				return nil, err
			}
			return zone.Encapsulate(ip6), nil
		}
Jeromy's avatar
Jeromy committed
90 91 92
	default:
		return nil, errIncorrectNetAddr
	}
mwnx's avatar
mwnx committed
93 94 95 96 97
}

// FromIP converts a net.IP type to a Multiaddr.
func FromIP(ip net.IP) (ma.Multiaddr, error) {
	return FromIPAndZone(ip, "")
Jeromy's avatar
Jeromy committed
98 99
}

100 101
// DialArgs is a convenience function that returns network and address as
// expected by net.Dial. See https://godoc.org/net#Dial for an overview of
Cole Brown's avatar
Cole Brown committed
102 103
// possible return values (we do not support the unixpacket ones yet). Unix
// addresses do not, at present, compose.
Jeromy's avatar
Jeromy committed
104
func DialArgs(m ma.Multiaddr) (string, string, error) {
Steven Allen's avatar
Steven Allen committed
105 106
	var (
		zone, network, ip, port string
mwnx's avatar
mwnx committed
107
		err                     error
108
		hostname                bool
Steven Allen's avatar
Steven Allen committed
109 110 111 112 113 114 115
	)

	ma.ForEach(m, func(c ma.Component) bool {
		switch network {
		case "":
			switch c.Protocol().Code {
			case ma.P_IP6ZONE:
mwnx's avatar
mwnx committed
116 117 118 119
				if zone != "" {
					err = fmt.Errorf("%s has multiple zones", m)
					return false
				}
Steven Allen's avatar
Steven Allen committed
120 121 122 123 124 125 126
				zone = c.Value()
				return true
			case ma.P_IP6:
				network = "ip6"
				ip = c.Value()
				return true
			case ma.P_IP4:
mwnx's avatar
mwnx committed
127 128 129 130
				if zone != "" {
					err = fmt.Errorf("%s has ip4 with zone", m)
					return false
				}
Steven Allen's avatar
Steven Allen committed
131 132 133
				network = "ip4"
				ip = c.Value()
				return true
134 135 136 137 138 139 140 141 142 143
			case madns.Dns4Protocol.Code:
				network = "ip4"
				hostname = true
				ip = c.Value()
				return true
			case madns.Dns6Protocol.Code:
				network = "ip6"
				hostname = true
				ip = c.Value()
				return true
Cole Brown's avatar
Cole Brown committed
144 145 146 147
			case ma.P_UNIX:
				network = "unix"
				ip = c.Value()
				return false
Steven Allen's avatar
Steven Allen committed
148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172
			}
		case "ip4":
			switch c.Protocol().Code {
			case ma.P_UDP:
				network = "udp4"
			case ma.P_TCP:
				network = "tcp4"
			default:
				return false
			}
			port = c.Value()
		case "ip6":
			switch c.Protocol().Code {
			case ma.P_UDP:
				network = "udp6"
			case ma.P_TCP:
				network = "tcp6"
			default:
				return false
			}
			port = c.Value()
		}
		// Done.
		return false
	})
mwnx's avatar
mwnx committed
173 174 175
	if err != nil {
		return "", "", err
	}
176

Steven Allen's avatar
Steven Allen committed
177
	switch network {
Jeromy's avatar
Jeromy committed
178
	case "ip6":
Steven Allen's avatar
Steven Allen committed
179 180 181 182 183 184 185 186 187 188 189 190
		if zone != "" {
			ip += "%" + zone
		}
		fallthrough
	case "ip4":
		return network, ip, nil
	case "tcp4", "udp4":
		return network, ip + ":" + port, nil
	case "tcp6", "udp6":
		if zone != "" {
			ip += "%" + zone
		}
191 192 193
		if hostname {
			return network, ip + ":" + port, nil
		}
Steven Allen's avatar
Steven Allen committed
194
		return network, "[" + ip + "]" + ":" + port, nil
Cole Brown's avatar
Cole Brown committed
195 196
	case "unix":
		return network, ip, nil
Steven Allen's avatar
Steven Allen committed
197 198
	default:
		return "", "", fmt.Errorf("%s is not a 'thin waist' address", m)
Jeromy's avatar
Jeromy committed
199
	}
Jeromy's avatar
Jeromy committed
200 201
}

202
func parseTCPNetAddr(a net.Addr) (ma.Multiaddr, error) {
Jeromy's avatar
Jeromy committed
203 204 205 206 207 208
	ac, ok := a.(*net.TCPAddr)
	if !ok {
		return nil, errIncorrectNetAddr
	}

	// Get IP Addr
mwnx's avatar
mwnx committed
209
	ipm, err := FromIPAndZone(ac.IP, ac.Zone)
Jeromy's avatar
Jeromy committed
210 211 212 213 214 215 216 217 218 219 220 221 222 223
	if err != nil {
		return nil, errIncorrectNetAddr
	}

	// Get TCP Addr
	tcpm, err := ma.NewMultiaddr(fmt.Sprintf("/tcp/%d", ac.Port))
	if err != nil {
		return nil, errIncorrectNetAddr
	}

	// Encapsulate
	return ipm.Encapsulate(tcpm), nil
}

224
func parseUDPNetAddr(a net.Addr) (ma.Multiaddr, error) {
Jeromy's avatar
Jeromy committed
225 226 227 228 229 230
	ac, ok := a.(*net.UDPAddr)
	if !ok {
		return nil, errIncorrectNetAddr
	}

	// Get IP Addr
mwnx's avatar
mwnx committed
231
	ipm, err := FromIPAndZone(ac.IP, ac.Zone)
Jeromy's avatar
Jeromy committed
232 233 234 235 236 237 238 239 240 241 242 243 244 245
	if err != nil {
		return nil, errIncorrectNetAddr
	}

	// Get UDP Addr
	udpm, err := ma.NewMultiaddr(fmt.Sprintf("/udp/%d", ac.Port))
	if err != nil {
		return nil, errIncorrectNetAddr
	}

	// Encapsulate
	return ipm.Encapsulate(udpm), nil
}

246
func parseIPNetAddr(a net.Addr) (ma.Multiaddr, error) {
Jeromy's avatar
Jeromy committed
247 248 249 250
	ac, ok := a.(*net.IPAddr)
	if !ok {
		return nil, errIncorrectNetAddr
	}
mwnx's avatar
mwnx committed
251
	return FromIPAndZone(ac.IP, ac.Zone)
Jeromy's avatar
Jeromy committed
252 253
}

254
func parseIPPlusNetAddr(a net.Addr) (ma.Multiaddr, error) {
Jeromy's avatar
Jeromy committed
255 256 257 258 259 260
	ac, ok := a.(*net.IPNet)
	if !ok {
		return nil, errIncorrectNetAddr
	}
	return FromIP(ac.IP)
}
Cole Brown's avatar
Cole Brown committed
261 262 263 264 265 266 267 268 269

func parseUnixNetAddr(a net.Addr) (ma.Multiaddr, error) {
	ac, ok := a.(*net.UnixAddr)
	if !ok {
		return nil, errIncorrectNetAddr
	}
	cleaned := filepath.Clean(ac.Name)
	return ma.NewComponent("unix", cleaned)
}