-
Notifications
You must be signed in to change notification settings - Fork 111
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
10 changed files
with
2,092 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,330 @@ | ||
package manet | ||
|
||
import ( | ||
"fmt" | ||
"net" | ||
"path/filepath" | ||
"runtime" | ||
"strings" | ||
|
||
ma "github.com/multiformats/go-multiaddr" | ||
) | ||
|
||
var errIncorrectNetAddr = fmt.Errorf("incorrect network addr conversion") | ||
var errNotIP = fmt.Errorf("multiaddr does not start with an IP address") | ||
|
||
// FromNetAddr converts a net.Addr type to a Multiaddr. | ||
func FromNetAddr(a net.Addr) (ma.Multiaddr, error) { | ||
return defaultCodecs.FromNetAddr(a) | ||
} | ||
|
||
// FromNetAddr converts a net.Addr to Multiaddress. | ||
func (cm *CodecMap) FromNetAddr(a net.Addr) (ma.Multiaddr, error) { | ||
if a == nil { | ||
return nil, fmt.Errorf("nil multiaddr") | ||
} | ||
p, err := cm.getAddrParser(a.Network()) | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
return p(a) | ||
} | ||
|
||
// 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) { | ||
return defaultCodecs.ToNetAddr(maddr) | ||
} | ||
|
||
// ToNetAddr converts a Multiaddress to a standard net.Addr. | ||
func (cm *CodecMap) ToNetAddr(maddr ma.Multiaddr) (net.Addr, error) { | ||
protos := maddr.Protocols() | ||
final := protos[len(protos)-1] | ||
|
||
p, err := cm.getMaddrParser(final.Name) | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
return p(maddr) | ||
} | ||
|
||
func parseBasicNetMaddr(maddr ma.Multiaddr) (net.Addr, error) { | ||
network, host, err := DialArgs(maddr) | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
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) | ||
case "unix": | ||
return net.ResolveUnixAddr(network, host) | ||
} | ||
|
||
return nil, fmt.Errorf("network not supported: %s", network) | ||
} | ||
|
||
func FromIPAndZone(ip net.IP, zone string) (ma.Multiaddr, error) { | ||
switch { | ||
case ip.To4() != nil: | ||
return ma.NewComponent("ip4", ip.String()) | ||
case ip.To16() != nil: | ||
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 | ||
} | ||
default: | ||
return nil, errIncorrectNetAddr | ||
} | ||
} | ||
|
||
// FromIP converts a net.IP type to a Multiaddr. | ||
func FromIP(ip net.IP) (ma.Multiaddr, error) { | ||
return FromIPAndZone(ip, "") | ||
} | ||
|
||
// ToIP converts a Multiaddr to a net.IP when possible | ||
func ToIP(addr ma.Multiaddr) (net.IP, error) { | ||
var ip net.IP | ||
ma.ForEach(addr, func(c ma.Component) bool { | ||
switch c.Protocol().Code { | ||
case ma.P_IP6ZONE: | ||
// we can't return these anyways. | ||
return true | ||
case ma.P_IP6, ma.P_IP4: | ||
ip = net.IP(c.RawValue()) | ||
return false | ||
} | ||
return false | ||
}) | ||
if ip == nil { | ||
return nil, errNotIP | ||
} | ||
return ip, nil | ||
} | ||
|
||
// 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 | ||
// possible return values (we do not support the unixpacket ones yet). Unix | ||
// addresses do not, at present, compose. | ||
func DialArgs(m ma.Multiaddr) (string, string, error) { | ||
zone, network, ip, port, hostname, err := dialArgComponents(m) | ||
if err != nil { | ||
return "", "", err | ||
} | ||
|
||
// If we have a hostname (dns*), we don't want any fancy ipv6 formatting | ||
// logic (zone, brackets, etc.). | ||
if hostname { | ||
switch network { | ||
case "ip", "ip4", "ip6": | ||
return network, ip, nil | ||
case "tcp", "tcp4", "tcp6", "udp", "udp4", "udp6": | ||
return network, ip + ":" + port, nil | ||
} | ||
// Hostname is only true when network is one of the above. | ||
panic("unreachable") | ||
} | ||
|
||
switch network { | ||
case "ip6": | ||
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 | ||
} | ||
return network, "[" + ip + "]" + ":" + port, nil | ||
case "unix": | ||
if runtime.GOOS == "windows" { | ||
// convert /c:/... to c:\... | ||
ip = filepath.FromSlash(strings.TrimLeft(ip, "/")) | ||
} | ||
return network, ip, nil | ||
default: | ||
return "", "", fmt.Errorf("%s is not a 'thin waist' address", m) | ||
} | ||
} | ||
|
||
// dialArgComponents extracts the raw pieces used in dialing a Multiaddr | ||
func dialArgComponents(m ma.Multiaddr) (zone, network, ip, port string, hostname bool, err error) { | ||
ma.ForEach(m, func(c ma.Component) bool { | ||
switch network { | ||
case "": | ||
switch c.Protocol().Code { | ||
case ma.P_IP6ZONE: | ||
if zone != "" { | ||
err = fmt.Errorf("%s has multiple zones", m) | ||
return false | ||
} | ||
zone = c.Value() | ||
return true | ||
case ma.P_IP6: | ||
network = "ip6" | ||
ip = c.Value() | ||
return true | ||
case ma.P_IP4: | ||
if zone != "" { | ||
err = fmt.Errorf("%s has ip4 with zone", m) | ||
return false | ||
} | ||
network = "ip4" | ||
ip = c.Value() | ||
return true | ||
case ma.P_DNS: | ||
network = "ip" | ||
hostname = true | ||
ip = c.Value() | ||
return true | ||
case ma.P_DNS4: | ||
network = "ip4" | ||
hostname = true | ||
ip = c.Value() | ||
return true | ||
case ma.P_DNS6: | ||
network = "ip6" | ||
hostname = true | ||
ip = c.Value() | ||
return true | ||
case ma.P_UNIX: | ||
network = "unix" | ||
ip = c.Value() | ||
return false | ||
} | ||
case "ip": | ||
switch c.Protocol().Code { | ||
case ma.P_UDP: | ||
network = "udp" | ||
case ma.P_TCP: | ||
network = "tcp" | ||
default: | ||
return false | ||
} | ||
port = c.Value() | ||
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 | ||
}) | ||
return | ||
} | ||
|
||
func parseTCPNetAddr(a net.Addr) (ma.Multiaddr, error) { | ||
ac, ok := a.(*net.TCPAddr) | ||
if !ok { | ||
return nil, errIncorrectNetAddr | ||
} | ||
|
||
// Get IP Addr | ||
ipm, err := FromIPAndZone(ac.IP, ac.Zone) | ||
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 | ||
} | ||
|
||
func parseUDPNetAddr(a net.Addr) (ma.Multiaddr, error) { | ||
ac, ok := a.(*net.UDPAddr) | ||
if !ok { | ||
return nil, errIncorrectNetAddr | ||
} | ||
|
||
// Get IP Addr | ||
ipm, err := FromIPAndZone(ac.IP, ac.Zone) | ||
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 | ||
} | ||
|
||
func parseIPNetAddr(a net.Addr) (ma.Multiaddr, error) { | ||
ac, ok := a.(*net.IPAddr) | ||
if !ok { | ||
return nil, errIncorrectNetAddr | ||
} | ||
return FromIPAndZone(ac.IP, ac.Zone) | ||
} | ||
|
||
func parseIPPlusNetAddr(a net.Addr) (ma.Multiaddr, error) { | ||
ac, ok := a.(*net.IPNet) | ||
if !ok { | ||
return nil, errIncorrectNetAddr | ||
} | ||
return FromIP(ac.IP) | ||
} | ||
|
||
func parseUnixNetAddr(a net.Addr) (ma.Multiaddr, error) { | ||
ac, ok := a.(*net.UnixAddr) | ||
if !ok { | ||
return nil, errIncorrectNetAddr | ||
} | ||
|
||
path := ac.Name | ||
if runtime.GOOS == "windows" { | ||
// Convert c:\foobar\... to c:/foobar/... | ||
path = filepath.ToSlash(path) | ||
} | ||
if len(path) == 0 || path[0] != '/' { | ||
// convert "" and "c:/..." to "/..." | ||
path = "/" + path | ||
} | ||
|
||
return ma.NewComponent("unix", path) | ||
} |
Oops, something went wrong.