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

import (
	"fmt"
	"net"
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
6
	"strings"
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
7 8

	ma "github.com/jbenet/go-multiaddr"
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
9 10
)

Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
11 12 13 14 15 16 17 18
var (
	// IP4Loopback is the ip4 loopback multiaddr
	IP4Loopback = ma.StringCast("/ip4/127.0.0.1")

	// IP6Loopback is the ip6 loopback multiaddr
	IP6Loopback = ma.StringCast("/ip6/::1")
)

Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
19 20 21
var errIncorrectNetAddr = fmt.Errorf("incorrect network addr conversion")

// FromNetAddr converts a net.Addr type to a Multiaddr.
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
22
func FromNetAddr(a net.Addr) (ma.Multiaddr, error) {
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
23 24 25 26 27 28 29 30 31 32 33 34 35 36
	switch a.Network() {
	case "tcp", "tcp4", "tcp6":
		ac, ok := a.(*net.TCPAddr)
		if !ok {
			return nil, errIncorrectNetAddr
		}

		// Get IP Addr
		ipm, err := FromIP(ac.IP)
		if err != nil {
			return nil, errIncorrectNetAddr
		}

		// Get TCP Addr
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
37
		tcpm, err := ma.NewMultiaddr(fmt.Sprintf("/tcp/%d", ac.Port))
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57
		if err != nil {
			return nil, errIncorrectNetAddr
		}

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

	case "udp", "upd4", "udp6":
		ac, ok := a.(*net.UDPAddr)
		if !ok {
			return nil, errIncorrectNetAddr
		}

		// Get IP Addr
		ipm, err := FromIP(ac.IP)
		if err != nil {
			return nil, errIncorrectNetAddr
		}

		// Get UDP Addr
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
58
		udpm, err := ma.NewMultiaddr(fmt.Sprintf("/udp/%d", ac.Port))
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77
		if err != nil {
			return nil, errIncorrectNetAddr
		}

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

	case "ip", "ip4", "ip6":
		ac, ok := a.(*net.IPAddr)
		if !ok {
			return nil, errIncorrectNetAddr
		}
		return FromIP(ac.IP)

	default:
		return nil, fmt.Errorf("unknown network %v", a.Network())
	}
}

Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
78 79 80
// ToNetAddr converts a Multiaddr to a net.Addr
// Must be ThinWaist. acceptable protocol stacks are:
// /ip{4,6}/{tcp, udp}
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
81 82
func ToNetAddr(maddr ma.Multiaddr) (net.Addr, error) {
	network, host, err := DialArgs(maddr)
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98
	if err != nil {
		return nil, err
	}

	switch network {
	case "tcp":
		return net.ResolveTCPAddr(network, host)
	case "udp":
		return net.ResolveUDPAddr(network, host)
	case "ip":
		return net.ResolveIPAddr(network, host)
	}

	return nil, fmt.Errorf("network not supported: %s", network)
}

Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
99
// FromIP converts a net.IP type to a Multiaddr.
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
100
func FromIP(ip net.IP) (ma.Multiaddr, error) {
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
101 102
	switch {
	case ip.To4() != nil:
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
103
		return ma.NewMultiaddr("/ip4/" + ip.String())
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
104
	case ip.To16() != nil:
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
105
		return ma.NewMultiaddr("/ip6/" + ip.String())
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
106 107 108 109
	default:
		return nil, errIncorrectNetAddr
	}
}
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
110 111

// DialArgs is a convenience function returning arguments for use in net.Dial
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
112
func DialArgs(m ma.Multiaddr) (string, string, error) {
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
113 114 115 116 117 118 119
	if !IsThinWaist(m) {
		return "", "", fmt.Errorf("%s is not a 'thin waist' address", m)
	}

	str := m.String()
	parts := strings.Split(str, "/")[1:]

Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
120 121 122 123 124
	if len(parts) == 2 { // only IP
		return parts[0], parts[1], nil
	}

	network := parts[2]
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
125 126 127 128 129 130 131 132 133 134 135
	var host string
	switch parts[0] {
	case "ip4":
		host = strings.Join([]string{parts[1], parts[3]}, ":")
	case "ip6":
		host = fmt.Sprintf("[%s]:%s", parts[1], parts[3])
	}
	return network, host, nil
}

// IsThinWaist returns whether a Multiaddr starts with "Thin Waist" Protocols.
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
136
// This means: /{IP4, IP6}[/{TCP, UDP}]
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
137
func IsThinWaist(m ma.Multiaddr) bool {
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
138
	p := m.Protocols()
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
139 140 141

	// nothing? not even a waist.
	if len(p) == 0 {
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
142 143 144
		return false
	}

Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
145
	if p[0].Code != ma.P_IP4 && p[0].Code != ma.P_IP6 {
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
146 147 148
		return false
	}

Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
149 150 151 152 153 154
	// only IP? still counts.
	if len(p) == 1 {
		return true
	}

	switch p[1].Code {
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
155
	case ma.P_TCP, ma.P_UDP, ma.P_IP4, ma.P_IP6:
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
156 157 158 159
		return true
	default:
		return false
	}
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
160
}
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
161 162 163 164 165 166

// IsIPLoopback returns whether a Multiaddr is a "Loopback" IP address
// This means either /ip4/127.0.0.1 or /ip6/::1
func IsIPLoopback(m ma.Multiaddr) bool {
	return m.Equal(IP4Loopback) || m.Equal(IP6Loopback)
}