mirror of
https://github.com/logos-messaging/go-multiaddr.git
synced 2026-01-06 23:13:08 +00:00
Merge pull request #42 from mwnx/ip6z
Add "ip6%" multiaddr support (IPv6 with zone ID)
This commit is contained in:
commit
86e5d145c9
44
convert.go
44
convert.go
@ -65,24 +65,39 @@ func parseBasicNetMaddr(maddr ma.Multiaddr) (net.Addr, error) {
|
|||||||
return nil, fmt.Errorf("network not supported: %s", network)
|
return nil, fmt.Errorf("network not supported: %s", network)
|
||||||
}
|
}
|
||||||
|
|
||||||
// FromIP converts a net.IP type to a Multiaddr.
|
func FromIPAndZone(ip net.IP, zone string) (ma.Multiaddr, error) {
|
||||||
func FromIP(ip net.IP) (ma.Multiaddr, error) {
|
|
||||||
var proto string
|
|
||||||
switch {
|
switch {
|
||||||
case ip.To4() != nil:
|
case ip.To4() != nil:
|
||||||
proto = "ip4"
|
return ma.NewComponent("ip4", ip.String())
|
||||||
case ip.To16() != nil:
|
case ip.To16() != nil:
|
||||||
proto = "ip6"
|
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:
|
default:
|
||||||
return nil, errIncorrectNetAddr
|
return nil, errIncorrectNetAddr
|
||||||
}
|
}
|
||||||
return ma.NewComponent(proto, ip.String())
|
}
|
||||||
|
|
||||||
|
// FromIP converts a net.IP type to a Multiaddr.
|
||||||
|
func FromIP(ip net.IP) (ma.Multiaddr, error) {
|
||||||
|
return FromIPAndZone(ip, "")
|
||||||
}
|
}
|
||||||
|
|
||||||
// DialArgs is a convenience function returning arguments for use in net.Dial
|
// DialArgs is a convenience function returning arguments for use in net.Dial
|
||||||
func DialArgs(m ma.Multiaddr) (string, string, error) {
|
func DialArgs(m ma.Multiaddr) (string, string, error) {
|
||||||
var (
|
var (
|
||||||
zone, network, ip, port string
|
zone, network, ip, port string
|
||||||
|
err error
|
||||||
)
|
)
|
||||||
|
|
||||||
ma.ForEach(m, func(c ma.Component) bool {
|
ma.ForEach(m, func(c ma.Component) bool {
|
||||||
@ -90,6 +105,10 @@ func DialArgs(m ma.Multiaddr) (string, string, error) {
|
|||||||
case "":
|
case "":
|
||||||
switch c.Protocol().Code {
|
switch c.Protocol().Code {
|
||||||
case ma.P_IP6ZONE:
|
case ma.P_IP6ZONE:
|
||||||
|
if zone != "" {
|
||||||
|
err = fmt.Errorf("%s has multiple zones", m)
|
||||||
|
return false
|
||||||
|
}
|
||||||
zone = c.Value()
|
zone = c.Value()
|
||||||
return true
|
return true
|
||||||
case ma.P_IP6:
|
case ma.P_IP6:
|
||||||
@ -97,6 +116,10 @@ func DialArgs(m ma.Multiaddr) (string, string, error) {
|
|||||||
ip = c.Value()
|
ip = c.Value()
|
||||||
return true
|
return true
|
||||||
case ma.P_IP4:
|
case ma.P_IP4:
|
||||||
|
if zone != "" {
|
||||||
|
err = fmt.Errorf("%s has ip4 with zone", m)
|
||||||
|
return false
|
||||||
|
}
|
||||||
network = "ip4"
|
network = "ip4"
|
||||||
ip = c.Value()
|
ip = c.Value()
|
||||||
return true
|
return true
|
||||||
@ -125,6 +148,9 @@ func DialArgs(m ma.Multiaddr) (string, string, error) {
|
|||||||
// Done.
|
// Done.
|
||||||
return false
|
return false
|
||||||
})
|
})
|
||||||
|
if err != nil {
|
||||||
|
return "", "", err
|
||||||
|
}
|
||||||
switch network {
|
switch network {
|
||||||
case "ip6":
|
case "ip6":
|
||||||
if zone != "" {
|
if zone != "" {
|
||||||
@ -152,7 +178,7 @@ func parseTCPNetAddr(a net.Addr) (ma.Multiaddr, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Get IP Addr
|
// Get IP Addr
|
||||||
ipm, err := FromIP(ac.IP)
|
ipm, err := FromIPAndZone(ac.IP, ac.Zone)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, errIncorrectNetAddr
|
return nil, errIncorrectNetAddr
|
||||||
}
|
}
|
||||||
@ -174,7 +200,7 @@ func parseUDPNetAddr(a net.Addr) (ma.Multiaddr, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Get IP Addr
|
// Get IP Addr
|
||||||
ipm, err := FromIP(ac.IP)
|
ipm, err := FromIPAndZone(ac.IP, ac.Zone)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, errIncorrectNetAddr
|
return nil, errIncorrectNetAddr
|
||||||
}
|
}
|
||||||
@ -194,7 +220,7 @@ func parseIPNetAddr(a net.Addr) (ma.Multiaddr, error) {
|
|||||||
if !ok {
|
if !ok {
|
||||||
return nil, errIncorrectNetAddr
|
return nil, errIncorrectNetAddr
|
||||||
}
|
}
|
||||||
return FromIP(ac.IP)
|
return FromIPAndZone(ac.IP, ac.Zone)
|
||||||
}
|
}
|
||||||
|
|
||||||
func parseIPPlusNetAddr(a net.Addr) (ma.Multiaddr, error) {
|
func parseIPPlusNetAddr(a net.Addr) (ma.Multiaddr, error) {
|
||||||
|
|||||||
@ -97,11 +97,18 @@ func TestThinWaist(t *testing.T) {
|
|||||||
"/ip6/::1/tcp/80": true,
|
"/ip6/::1/tcp/80": true,
|
||||||
"/ip6/::1/udp/80": true,
|
"/ip6/::1/udp/80": true,
|
||||||
"/ip6/::1": true,
|
"/ip6/::1": true,
|
||||||
|
"/ip6zone/hello/ip6/fe80::1/tcp/80": true,
|
||||||
|
"/ip6zone/hello/ip6/fe80::1": true,
|
||||||
"/tcp/1234/ip4/1.2.3.4": false,
|
"/tcp/1234/ip4/1.2.3.4": false,
|
||||||
"/tcp/1234": false,
|
"/tcp/1234": false,
|
||||||
"/tcp/1234/udp/1234": false,
|
"/tcp/1234/udp/1234": false,
|
||||||
"/ip4/1.2.3.4/ip4/2.3.4.5": true,
|
"/ip4/1.2.3.4/ip4/2.3.4.5": true,
|
||||||
"/ip6/::1/ip4/2.3.4.5": true,
|
"/ip6/fe80::1/ip4/2.3.4.5": true,
|
||||||
|
"/ip6zone/hello/ip6/fe80::1/ip4/2.3.4.5": true,
|
||||||
|
|
||||||
|
// Invalid ip6zone usage:
|
||||||
|
"/ip6zone/hello": false,
|
||||||
|
"/ip6zone/hello/ip4/1.1.1.1": false,
|
||||||
}
|
}
|
||||||
|
|
||||||
for a, res := range addrs {
|
for a, res := range addrs {
|
||||||
@ -120,7 +127,7 @@ func TestDialArgs(t *testing.T) {
|
|||||||
test := func(e_maddr, e_nw, e_host string) {
|
test := func(e_maddr, e_nw, e_host string) {
|
||||||
m, err := ma.NewMultiaddr(e_maddr)
|
m, err := ma.NewMultiaddr(e_maddr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal("failed to construct", "/ip4/127.0.0.1/udp/1234", e_maddr)
|
t.Fatal("failed to construct", e_maddr)
|
||||||
}
|
}
|
||||||
|
|
||||||
nw, host, err := DialArgs(m)
|
nw, host, err := DialArgs(m)
|
||||||
@ -137,6 +144,18 @@ func TestDialArgs(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
test_error := func(e_maddr string) {
|
||||||
|
m, err := ma.NewMultiaddr(e_maddr)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal("failed to construct", e_maddr)
|
||||||
|
}
|
||||||
|
|
||||||
|
_, _, err = DialArgs(m)
|
||||||
|
if err == nil {
|
||||||
|
t.Fatal("expected DialArgs to fail on", e_maddr)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
test("/ip4/127.0.0.1/udp/1234", "udp4", "127.0.0.1:1234")
|
test("/ip4/127.0.0.1/udp/1234", "udp4", "127.0.0.1:1234")
|
||||||
test("/ip4/127.0.0.1/tcp/4321", "tcp4", "127.0.0.1:4321")
|
test("/ip4/127.0.0.1/tcp/4321", "tcp4", "127.0.0.1:4321")
|
||||||
test("/ip6/::1/udp/1234", "udp6", "[::1]:1234")
|
test("/ip6/::1/udp/1234", "udp6", "[::1]:1234")
|
||||||
@ -144,7 +163,9 @@ func TestDialArgs(t *testing.T) {
|
|||||||
test("/ip6/::1", "ip6", "::1") // Just an IP
|
test("/ip6/::1", "ip6", "::1") // Just an IP
|
||||||
test("/ip4/1.2.3.4", "ip4", "1.2.3.4") // Just an IP
|
test("/ip4/1.2.3.4", "ip4", "1.2.3.4") // Just an IP
|
||||||
test("/ip6zone/foo/ip6/::1/tcp/4321", "tcp6", "[::1%foo]:4321") // zone
|
test("/ip6zone/foo/ip6/::1/tcp/4321", "tcp6", "[::1%foo]:4321") // zone
|
||||||
|
test("/ip6zone/foo/ip6/::1/udp/4321", "udp6", "[::1%foo]:4321") // zone
|
||||||
test("/ip6zone/foo/ip6/::1", "ip6", "::1%foo") // no TCP
|
test("/ip6zone/foo/ip6/::1", "ip6", "::1%foo") // no TCP
|
||||||
|
test_error("/ip6zone/foo/ip4/127.0.0.1") // IP4 doesn't take zone
|
||||||
test("/ip6zone/foo/ip6/::1/ip6zone/bar", "ip6", "::1%foo") // IP over IP
|
test("/ip6zone/foo/ip6/::1/ip6zone/bar", "ip6", "::1%foo") // IP over IP
|
||||||
test("/ip6zone/foo/ip4/127.0.0.1/ip6zone/bar", "ip4", "127.0.0.1") // Skip zones in IP
|
test_error("/ip6zone/foo/ip6zone/bar/ip6/::1") // Only one zone per IP6
|
||||||
}
|
}
|
||||||
|
|||||||
63
ip.go
63
ip.go
@ -27,6 +27,10 @@ var (
|
|||||||
// IsThinWaist returns whether a Multiaddr starts with "Thin Waist" Protocols.
|
// IsThinWaist returns whether a Multiaddr starts with "Thin Waist" Protocols.
|
||||||
// This means: /{IP4, IP6}[/{TCP, UDP}]
|
// This means: /{IP4, IP6}[/{TCP, UDP}]
|
||||||
func IsThinWaist(m ma.Multiaddr) bool {
|
func IsThinWaist(m ma.Multiaddr) bool {
|
||||||
|
m = zoneless(m)
|
||||||
|
if m == nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
p := m.Protocols()
|
p := m.Protocols()
|
||||||
|
|
||||||
// nothing? not even a waist.
|
// nothing? not even a waist.
|
||||||
@ -52,9 +56,14 @@ func IsThinWaist(m ma.Multiaddr) bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// IsIPLoopback returns whether a Multiaddr is a "Loopback" IP address
|
// IsIPLoopback returns whether a Multiaddr is a "Loopback" IP address
|
||||||
// This means either /ip4/127.*.*.*, /ip6/::1, or /ip6/::ffff:127.*.*.*.*
|
// This means either /ip4/127.*.*.*, /ip6/::1, or /ip6/::ffff:127.*.*.*.*,
|
||||||
|
// or /ip6zone/<any value>/ip6/<one of the preceding ip6 values>
|
||||||
func IsIPLoopback(m ma.Multiaddr) bool {
|
func IsIPLoopback(m ma.Multiaddr) bool {
|
||||||
|
m = zoneless(m)
|
||||||
c, rest := ma.SplitFirst(m)
|
c, rest := ma.SplitFirst(m)
|
||||||
|
if c == nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
if rest != nil {
|
if rest != nil {
|
||||||
// Not *just* an IPv4 addr
|
// Not *just* an IPv4 addr
|
||||||
return false
|
return false
|
||||||
@ -66,33 +75,47 @@ func IsIPLoopback(m ma.Multiaddr) bool {
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
// IsIP6LinkLocal returns if a an IPv6 link-local multiaddress (with zero or
|
// IsIP6LinkLocal returns whether a Multiaddr starts with an IPv6 link-local
|
||||||
// more leading zones). These addresses are non routable.
|
// multiaddress (with zero or one leading zone). These addresses are non
|
||||||
|
// routable.
|
||||||
func IsIP6LinkLocal(m ma.Multiaddr) bool {
|
func IsIP6LinkLocal(m ma.Multiaddr) bool {
|
||||||
matched := false
|
m = zoneless(m)
|
||||||
ma.ForEach(m, func(c ma.Component) bool {
|
c, _ := ma.SplitFirst(m)
|
||||||
// Too much.
|
if c == nil || c.Protocol().Code != ma.P_IP6 {
|
||||||
if matched {
|
|
||||||
matched = false
|
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
switch c.Protocol().Code {
|
|
||||||
case ma.P_IP6ZONE:
|
|
||||||
return true
|
|
||||||
case ma.P_IP6:
|
|
||||||
ip := net.IP(c.RawValue())
|
ip := net.IP(c.RawValue())
|
||||||
matched = ip.IsLinkLocalMulticast() || ip.IsLinkLocalUnicast()
|
return ip.IsLinkLocalMulticast() || ip.IsLinkLocalUnicast()
|
||||||
return true
|
|
||||||
default:
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
})
|
|
||||||
return matched
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// IsIPUnspecified returns whether a Multiaddr is am Unspecified IP address
|
// IsIPUnspecified returns whether a Multiaddr is am Unspecified IP address
|
||||||
// This means either /ip4/0.0.0.0 or /ip6/::
|
// This means either /ip4/0.0.0.0 or /ip6/::
|
||||||
func IsIPUnspecified(m ma.Multiaddr) bool {
|
func IsIPUnspecified(m ma.Multiaddr) bool {
|
||||||
|
m = zoneless(m)
|
||||||
|
if m == nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
return IP4Unspecified.Equal(m) || IP6Unspecified.Equal(m)
|
return IP4Unspecified.Equal(m) || IP6Unspecified.Equal(m)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// If m matches [zone,ip6,...], return [ip6,...]
|
||||||
|
// else if m matches [], [zone], or [zone,...], return nil
|
||||||
|
// else return m
|
||||||
|
func zoneless(m ma.Multiaddr) ma.Multiaddr {
|
||||||
|
head, tail := ma.SplitFirst(m)
|
||||||
|
if head == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
if head.Protocol().Code == ma.P_IP6ZONE {
|
||||||
|
if tail == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
tailhead, _ := ma.SplitFirst(tail)
|
||||||
|
if tailhead.Protocol().Code != ma.P_IP6 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return tail
|
||||||
|
} else {
|
||||||
|
return m
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
32
net_test.go
32
net_test.go
@ -175,12 +175,20 @@ func TestListenAddrs(t *testing.T) {
|
|||||||
test("/ip4/0.0.0.0/tcp/4324", "", true)
|
test("/ip4/0.0.0.0/tcp/4324", "", true)
|
||||||
test("/ip4/0.0.0.0/udp/4325", "", false)
|
test("/ip4/0.0.0.0/udp/4325", "", false)
|
||||||
test("/ip4/0.0.0.0/udp/4326/udt", "", false)
|
test("/ip4/0.0.0.0/udp/4326/udt", "", false)
|
||||||
|
|
||||||
test("/ip6/::1/tcp/4324", "", true)
|
test("/ip6/::1/tcp/4324", "", true)
|
||||||
test("/ip6/::1/udp/4325", "", false)
|
test("/ip6/::1/udp/4325", "", false)
|
||||||
test("/ip6/::1/udp/4326/udt", "", false)
|
test("/ip6/::1/udp/4326/udt", "", false)
|
||||||
test("/ip6/::/tcp/4324", "", true)
|
test("/ip6/::/tcp/4324", "", true)
|
||||||
test("/ip6/::/udp/4325", "", false)
|
test("/ip6/::/udp/4325", "", false)
|
||||||
test("/ip6/::/udp/4326/udt", "", false)
|
test("/ip6/::/udp/4326/udt", "", false)
|
||||||
|
|
||||||
|
/* "An implementation should also support the concept of a "default"
|
||||||
|
* zone for each scope. And, when supported, the index value zero
|
||||||
|
* at each scope SHOULD be reserved to mean "use the default zone"."
|
||||||
|
* -- rfc4007. So, this _should_ work everywhere(?). */
|
||||||
|
test("/ip6zone/0/ip6/::1/tcp/4324", "/ip6/::1/tcp/4324", true)
|
||||||
|
test("/ip6zone/0/ip6/::1/udp/4324", "", false)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestListenAndDial(t *testing.T) {
|
func TestListenAndDial(t *testing.T) {
|
||||||
@ -345,6 +353,22 @@ func TestIPLoopback(t *testing.T) {
|
|||||||
if IsIPLoopback(newMultiaddr(t, "/ip6/::fffa:127.99.3.2")) {
|
if IsIPLoopback(newMultiaddr(t, "/ip6/::fffa:127.99.3.2")) {
|
||||||
t.Error("IsIPLoopback false positive (/ip6/::fffa:127.99.3.2)")
|
t.Error("IsIPLoopback false positive (/ip6/::fffa:127.99.3.2)")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if !IsIPLoopback(newMultiaddr(t, "/ip6zone/0/ip6/::1")) {
|
||||||
|
t.Error("IsIPLoopback failed (/ip6zone/0/ip6/::1)")
|
||||||
|
}
|
||||||
|
|
||||||
|
if !IsIPLoopback(newMultiaddr(t, "/ip6zone/xxx/ip6/::1")) {
|
||||||
|
t.Error("IsIPLoopback failed (/ip6zone/xxx/ip6/::1)")
|
||||||
|
}
|
||||||
|
|
||||||
|
if IsIPLoopback(newMultiaddr(t, "/ip6zone/0/ip6/::1/tcp/3333")) {
|
||||||
|
t.Error("IsIPLoopback failed (/ip6zone/0/ip6/::1/tcp/3333)")
|
||||||
|
}
|
||||||
|
|
||||||
|
if IsIPLoopback(newMultiaddr(t, "/ip6zone/0/ip6/1::1")) {
|
||||||
|
t.Errorf("IsIPLoopback false positive (/ip6zone/0/ip6/1::1)")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestIPUnspecified(t *testing.T) {
|
func TestIPUnspecified(t *testing.T) {
|
||||||
@ -363,6 +387,10 @@ func TestIPUnspecified(t *testing.T) {
|
|||||||
if !IsIPUnspecified(IP6Unspecified) {
|
if !IsIPUnspecified(IP6Unspecified) {
|
||||||
t.Error("IsIPUnspecified failed (IP6Unspecified)")
|
t.Error("IsIPUnspecified failed (IP6Unspecified)")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if !IsIPUnspecified(newMultiaddr(t, "/ip6zone/xxx/ip6/::")) {
|
||||||
|
t.Error("IsIPUnspecified failed (/ip6zone/xxx/ip6/::)")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestIP6LinkLocal(t *testing.T) {
|
func TestIP6LinkLocal(t *testing.T) {
|
||||||
@ -373,6 +401,10 @@ func TestIP6LinkLocal(t *testing.T) {
|
|||||||
t.Errorf("IsIP6LinkLocal failed (%s != %v)", m, isLinkLocal)
|
t.Errorf("IsIP6LinkLocal failed (%s != %v)", m, isLinkLocal)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if !IsIP6LinkLocal(newMultiaddr(t, "/ip6zone/hello/ip6/fe80::9999")) {
|
||||||
|
t.Error("IsIP6LinkLocal failed (/ip6/fe80::9999)")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestConvertNetAddr(t *testing.T) {
|
func TestConvertNetAddr(t *testing.T) {
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user