convert.go 5.62 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"
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
9 10 11 12
)

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

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

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

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

Jeromy's avatar
Jeromy committed
31 32 33 34
// 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
35 36 37
	return defaultCodecs.ToNetAddr(maddr)
}

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

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

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

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

Jeromy's avatar
Jeromy committed
57 58 59 60 61 62 63
	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
64 65
	case "unix":
		return net.ResolveUnixAddr(network, host)
Jeromy's avatar
Jeromy committed
66 67
	}

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

mwnx's avatar
mwnx committed
71
func FromIPAndZone(ip net.IP, zone string) (ma.Multiaddr, error) {
Jeromy's avatar
Jeromy committed
72 73
	switch {
	case ip.To4() != nil:
mwnx's avatar
mwnx committed
74
		return ma.NewComponent("ip4", ip.String())
Jeromy's avatar
Jeromy committed
75
	case ip.To16() != nil:
mwnx's avatar
mwnx committed
76 77 78 79 80 81 82 83 84 85 86 87 88
		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
89 90 91
	default:
		return nil, errIncorrectNetAddr
	}
mwnx's avatar
mwnx committed
92 93 94 95 96
}

// 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
97 98
}

99 100
// 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
101 102
// possible return values (we do not support the unixpacket ones yet). Unix
// addresses do not, at present, compose.
Jeromy's avatar
Jeromy committed
103
func DialArgs(m ma.Multiaddr) (string, string, error) {
Steven Allen's avatar
Steven Allen committed
104 105
	var (
		zone, network, ip, port string
mwnx's avatar
mwnx committed
106
		err                     error
107
		hostname                bool
Steven Allen's avatar
Steven Allen committed
108 109 110 111 112 113 114
	)

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

Steven Allen's avatar
Steven Allen committed
176
	switch network {
Jeromy's avatar
Jeromy committed
177
	case "ip6":
Steven Allen's avatar
Steven Allen committed
178 179 180 181 182 183 184 185 186 187 188 189
		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
		}
190 191 192
		if hostname {
			return network, ip + ":" + port, nil
		}
Steven Allen's avatar
Steven Allen committed
193
		return network, "[" + ip + "]" + ":" + port, nil
Cole Brown's avatar
Cole Brown committed
194 195
	case "unix":
		return network, ip, nil
Steven Allen's avatar
Steven Allen committed
196 197
	default:
		return "", "", fmt.Errorf("%s is not a 'thin waist' address", m)
Jeromy's avatar
Jeromy committed
198
	}
Jeromy's avatar
Jeromy committed
199 200
}

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

	// Get IP Addr
mwnx's avatar
mwnx committed
208
	ipm, err := FromIPAndZone(ac.IP, ac.Zone)
Jeromy's avatar
Jeromy committed
209 210 211 212 213 214 215 216 217 218 219 220 221 222
	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
}

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

	// Get IP Addr
mwnx's avatar
mwnx committed
230
	ipm, err := FromIPAndZone(ac.IP, ac.Zone)
Jeromy's avatar
Jeromy committed
231 232 233 234 235 236 237 238 239 240 241 242 243 244
	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
}

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

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

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)
}