package manet
import (
"errors"
"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" )
func FromNetAddr (a net .Addr ) (ma .Multiaddr , error ) {
return defaultCodecs .FromNetAddr (a )
}
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 )
}
func ToNetAddr (maddr ma .Multiaddr ) (net .Addr , error ) {
return defaultCodecs .ToNetAddr (maddr )
}
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 MultiaddrToIPNet (m ma .Multiaddr ) (*net .IPNet , error ) {
var ipString string
var mask string
for _ , c := range m {
if c .Protocol ().Code == ma .P_IP4 || c .Protocol ().Code == ma .P_IP6 {
ipString = c .Value ()
}
if c .Protocol ().Code == ma .P_IPCIDR {
mask = c .Value ()
}
if ipString != "" && mask != "" {
break
}
}
if ipString == "" {
return nil , errors .New ("no ip protocol found" )
}
if mask == "" {
return nil , errors .New ("no mask found" )
}
_ , ipnet , err := net .ParseCIDR (ipString + "/" + string (mask ))
return ipnet , err
}
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 :
c , err := ma .NewComponent ("ip4" , ip .String ())
if err != nil {
return nil , err
}
return c .Multiaddr (), nil
case ip .To16 () != nil :
ip6C , err := ma .NewComponent ("ip6" , ip .String ())
if err != nil {
return nil , err
}
ip6 := ip6C .Multiaddr ()
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
}
}
func FromIP (ip net .IP ) (ma .Multiaddr , error ) {
return FromIPAndZone (ip , "" )
}
func ToIP (addr ma .Multiaddr ) (net .IP , error ) {
var ip net .IP
for _ , c := range addr {
switch c .Protocol ().Code {
case ma .P_IP6ZONE :
continue
case ma .P_IP6 , ma .P_IP4 :
ip = net .IP (c .RawValue ())
return ip , nil
}
return nil , errNotIP
}
return nil , errNotIP
}
func DialArgs (m ma .Multiaddr ) (string , string , error ) {
zone , network , ip , port , hostname , err := dialArgComponents (m )
if err != nil {
return "" , "" , err
}
if hostname {
switch network {
case "ip" , "ip4" , "ip6" :
return network , ip , nil
case "tcp" , "tcp4" , "tcp6" , "udp" , "udp4" , "udp6" :
return network , ip + ":" + port , nil
}
return "" , "" , errors .New ("no hostname" )
}
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" {
ip = filepath .FromSlash (strings .TrimLeft (ip , "/" ))
}
return network , ip , nil
default :
return "" , "" , fmt .Errorf ("%s is not a 'thin waist' address" , m )
}
}
func dialArgComponents(m ma .Multiaddr ) (zone , network , ip , port string , hostname bool , err error ) {
for _ , c := range m {
switch network {
case "" :
switch c .Protocol ().Code {
case ma .P_IP6ZONE :
if zone != "" {
err = fmt .Errorf ("%s has multiple zones" , m )
return
}
zone = c .Value ()
continue
case ma .P_IP6 :
network = "ip6"
ip = c .Value ()
continue
case ma .P_IP4 :
if zone != "" {
err = fmt .Errorf ("%s has ip4 with zone" , m )
return
}
network = "ip4"
ip = c .Value ()
continue
case ma .P_DNS :
network = "ip"
hostname = true
ip = c .Value ()
continue
case ma .P_DNS4 :
network = "ip4"
hostname = true
ip = c .Value ()
continue
case ma .P_DNS6 :
network = "ip6"
hostname = true
ip = c .Value ()
continue
case ma .P_UNIX :
network = "unix"
ip = c .Value ()
return
}
case "ip" :
switch c .Protocol ().Code {
case ma .P_UDP :
network = "udp"
case ma .P_TCP :
network = "tcp"
default :
return
}
port = c .Value ()
case "ip4" :
switch c .Protocol ().Code {
case ma .P_UDP :
network = "udp4"
case ma .P_TCP :
network = "tcp4"
default :
return
}
port = c .Value ()
case "ip6" :
switch c .Protocol ().Code {
case ma .P_UDP :
network = "udp6"
case ma .P_TCP :
network = "tcp6"
default :
return
}
port = c .Value ()
}
return
}
return
}
func parseTCPNetAddr(a net .Addr ) (ma .Multiaddr , error ) {
ac , ok := a .(*net .TCPAddr )
if !ok {
return nil , errIncorrectNetAddr
}
ipm , err := FromIPAndZone (ac .IP , ac .Zone )
if err != nil {
return nil , errIncorrectNetAddr
}
tcpm , err := ma .NewMultiaddr (fmt .Sprintf ("/tcp/%d" , ac .Port ))
if err != nil {
return nil , errIncorrectNetAddr
}
return ipm .Encapsulate (tcpm ), nil
}
func parseUDPNetAddr(a net .Addr ) (ma .Multiaddr , error ) {
ac , ok := a .(*net .UDPAddr )
if !ok {
return nil , errIncorrectNetAddr
}
ipm , err := FromIPAndZone (ac .IP , ac .Zone )
if err != nil {
return nil , errIncorrectNetAddr
}
udpm , err := ma .NewMultiaddr (fmt .Sprintf ("/udp/%d" , ac .Port ))
if err != nil {
return nil , errIncorrectNetAddr
}
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" {
path = filepath .ToSlash (path )
}
if len (path ) == 0 || path [0 ] != '/' {
path = "/" + path
}
c , err := ma .NewComponent ("unix" , path )
if err != nil {
return nil , err
}
return c .Multiaddr (), nil
}
The pages are generated with Golds v0.8.2 . (GOOS=linux GOARCH=amd64)
Golds is a Go 101 project developed by Tapir Liu .
PR and bug reports are welcome and can be submitted to the issue list .
Please follow @zigo_101 (reachable from the left QR code) to get the latest news of Golds .