mirror of
https://github.com/waku-org/go-multiaddr.git
synced 2025-02-23 03:28:12 +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)
|
||||
}
|
||||
|
||||
// FromIP converts a net.IP type to a Multiaddr.
|
||||
func FromIP(ip net.IP) (ma.Multiaddr, error) {
|
||||
var proto string
|
||||
func FromIPAndZone(ip net.IP, zone string) (ma.Multiaddr, error) {
|
||||
switch {
|
||||
case ip.To4() != nil:
|
||||
proto = "ip4"
|
||||
return ma.NewComponent("ip4", ip.String())
|
||||
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:
|
||||
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
|
||||
func DialArgs(m ma.Multiaddr) (string, string, error) {
|
||||
var (
|
||||
zone, network, ip, port string
|
||||
err error
|
||||
)
|
||||
|
||||
ma.ForEach(m, func(c ma.Component) bool {
|
||||
@ -90,6 +105,10 @@ func DialArgs(m ma.Multiaddr) (string, string, error) {
|
||||
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:
|
||||
@ -97,6 +116,10 @@ func DialArgs(m ma.Multiaddr) (string, string, error) {
|
||||
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
|
||||
@ -125,6 +148,9 @@ func DialArgs(m ma.Multiaddr) (string, string, error) {
|
||||
// Done.
|
||||
return false
|
||||
})
|
||||
if err != nil {
|
||||
return "", "", err
|
||||
}
|
||||
switch network {
|
||||
case "ip6":
|
||||
if zone != "" {
|
||||
@ -152,7 +178,7 @@ func parseTCPNetAddr(a net.Addr) (ma.Multiaddr, error) {
|
||||
}
|
||||
|
||||
// Get IP Addr
|
||||
ipm, err := FromIP(ac.IP)
|
||||
ipm, err := FromIPAndZone(ac.IP, ac.Zone)
|
||||
if err != nil {
|
||||
return nil, errIncorrectNetAddr
|
||||
}
|
||||
@ -174,7 +200,7 @@ func parseUDPNetAddr(a net.Addr) (ma.Multiaddr, error) {
|
||||
}
|
||||
|
||||
// Get IP Addr
|
||||
ipm, err := FromIP(ac.IP)
|
||||
ipm, err := FromIPAndZone(ac.IP, ac.Zone)
|
||||
if err != nil {
|
||||
return nil, errIncorrectNetAddr
|
||||
}
|
||||
@ -194,7 +220,7 @@ func parseIPNetAddr(a net.Addr) (ma.Multiaddr, error) {
|
||||
if !ok {
|
||||
return nil, errIncorrectNetAddr
|
||||
}
|
||||
return FromIP(ac.IP)
|
||||
return FromIPAndZone(ac.IP, ac.Zone)
|
||||
}
|
||||
|
||||
func parseIPPlusNetAddr(a net.Addr) (ma.Multiaddr, error) {
|
||||
|
@ -90,18 +90,25 @@ func TestFromUDP(t *testing.T) {
|
||||
|
||||
func TestThinWaist(t *testing.T) {
|
||||
addrs := map[string]bool{
|
||||
"/ip4/127.0.0.1/udp/1234": true,
|
||||
"/ip4/127.0.0.1/tcp/1234": true,
|
||||
"/ip4/127.0.0.1/udp/1234/tcp/1234": true,
|
||||
"/ip4/127.0.0.1/tcp/12345/ip4/1.2.3.4": true,
|
||||
"/ip6/::1/tcp/80": true,
|
||||
"/ip6/::1/udp/80": true,
|
||||
"/ip6/::1": true,
|
||||
"/tcp/1234/ip4/1.2.3.4": false,
|
||||
"/tcp/1234": false,
|
||||
"/tcp/1234/udp/1234": false,
|
||||
"/ip4/1.2.3.4/ip4/2.3.4.5": true,
|
||||
"/ip6/::1/ip4/2.3.4.5": true,
|
||||
"/ip4/127.0.0.1/udp/1234": true,
|
||||
"/ip4/127.0.0.1/tcp/1234": true,
|
||||
"/ip4/127.0.0.1/udp/1234/tcp/1234": true,
|
||||
"/ip4/127.0.0.1/tcp/12345/ip4/1.2.3.4": true,
|
||||
"/ip6/::1/tcp/80": true,
|
||||
"/ip6/::1/udp/80": 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": false,
|
||||
"/tcp/1234/udp/1234": false,
|
||||
"/ip4/1.2.3.4/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 {
|
||||
@ -120,7 +127,7 @@ func TestDialArgs(t *testing.T) {
|
||||
test := func(e_maddr, e_nw, e_host string) {
|
||||
m, err := ma.NewMultiaddr(e_maddr)
|
||||
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)
|
||||
@ -137,14 +144,28 @@ 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/tcp/4321", "tcp4", "127.0.0.1:4321")
|
||||
test("/ip6/::1/udp/1234", "udp6", "[::1]:1234")
|
||||
test("/ip6/::1/tcp/4321", "tcp6", "[::1]:4321")
|
||||
test("/ip6/::1", "ip6", "::1") // 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", "ip6", "::1%foo") // no TCP
|
||||
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("/ip6/::1", "ip6", "::1") // 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/udp/4321", "udp6", "[::1%foo]:4321") // zone
|
||||
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_error("/ip6zone/foo/ip6zone/bar/ip6/::1") // Only one zone per IP6
|
||||
}
|
||||
|
69
ip.go
69
ip.go
@ -27,6 +27,10 @@ var (
|
||||
// IsThinWaist returns whether a Multiaddr starts with "Thin Waist" Protocols.
|
||||
// This means: /{IP4, IP6}[/{TCP, UDP}]
|
||||
func IsThinWaist(m ma.Multiaddr) bool {
|
||||
m = zoneless(m)
|
||||
if m == nil {
|
||||
return false
|
||||
}
|
||||
p := m.Protocols()
|
||||
|
||||
// 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
|
||||
// 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 {
|
||||
m = zoneless(m)
|
||||
c, rest := ma.SplitFirst(m)
|
||||
if c == nil {
|
||||
return false
|
||||
}
|
||||
if rest != nil {
|
||||
// Not *just* an IPv4 addr
|
||||
return false
|
||||
@ -66,33 +75,47 @@ func IsIPLoopback(m ma.Multiaddr) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
// IsIP6LinkLocal returns if a an IPv6 link-local multiaddress (with zero or
|
||||
// more leading zones). These addresses are non routable.
|
||||
// IsIP6LinkLocal returns whether a Multiaddr starts with an IPv6 link-local
|
||||
// multiaddress (with zero or one leading zone). These addresses are non
|
||||
// routable.
|
||||
func IsIP6LinkLocal(m ma.Multiaddr) bool {
|
||||
matched := false
|
||||
ma.ForEach(m, func(c ma.Component) bool {
|
||||
// Too much.
|
||||
if matched {
|
||||
matched = false
|
||||
return false
|
||||
}
|
||||
|
||||
switch c.Protocol().Code {
|
||||
case ma.P_IP6ZONE:
|
||||
return true
|
||||
case ma.P_IP6:
|
||||
ip := net.IP(c.RawValue())
|
||||
matched = ip.IsLinkLocalMulticast() || ip.IsLinkLocalUnicast()
|
||||
return true
|
||||
default:
|
||||
return false
|
||||
}
|
||||
})
|
||||
return matched
|
||||
m = zoneless(m)
|
||||
c, _ := ma.SplitFirst(m)
|
||||
if c == nil || c.Protocol().Code != ma.P_IP6 {
|
||||
return false
|
||||
}
|
||||
ip := net.IP(c.RawValue())
|
||||
return ip.IsLinkLocalMulticast() || ip.IsLinkLocalUnicast()
|
||||
}
|
||||
|
||||
// IsIPUnspecified returns whether a Multiaddr is am Unspecified IP address
|
||||
// This means either /ip4/0.0.0.0 or /ip6/::
|
||||
func IsIPUnspecified(m ma.Multiaddr) bool {
|
||||
m = zoneless(m)
|
||||
if m == nil {
|
||||
return false
|
||||
}
|
||||
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/udp/4325", "", false)
|
||||
test("/ip4/0.0.0.0/udp/4326/udt", "", false)
|
||||
|
||||
test("/ip6/::1/tcp/4324", "", true)
|
||||
test("/ip6/::1/udp/4325", "", false)
|
||||
test("/ip6/::1/udp/4326/udt", "", false)
|
||||
test("/ip6/::/tcp/4324", "", true)
|
||||
test("/ip6/::/udp/4325", "", 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) {
|
||||
@ -345,6 +353,22 @@ func TestIPLoopback(t *testing.T) {
|
||||
if IsIPLoopback(newMultiaddr(t, "/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) {
|
||||
@ -363,6 +387,10 @@ func TestIPUnspecified(t *testing.T) {
|
||||
if !IsIPUnspecified(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) {
|
||||
@ -373,6 +401,10 @@ func TestIP6LinkLocal(t *testing.T) {
|
||||
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) {
|
||||
|
Loading…
x
Reference in New Issue
Block a user