فهرست منبع

Merge pull request #2160 from euanh/portbinding-ipv6

types: Handle IPv6 literals correctly in port bindings
Flavio Crisciani 7 سال پیش
والد
کامیت
c7300fec17
2فایلهای تغییر یافته به همراه67 افزوده شده و 24 حذف شده
  1. 14 11
      libnetwork/types/types.go
  2. 53 13
      libnetwork/types/types_test.go

+ 14 - 11
libnetwork/types/types.go

@@ -145,7 +145,12 @@ func (p *PortBinding) String() string {
 	return ret
 }
 
-// FromString reads the PortBinding structure from string
+// FromString reads the PortBinding structure from string s.
+// String s is a triple of "protocol/containerIP:port/hostIP:port"
+// containerIP and hostIP can be in dotted decimal ("192.0.2.1") or IPv6 ("2001:db8::68") form.
+// Zoned addresses ("169.254.0.23%eth0" or "fe80::1ff:fe23:4567:890a%eth0") are not supported.
+// If string s is incorrectly formatted or the IP addresses or ports cannot be parsed, FromString
+// returns an error.
 func (p *PortBinding) FromString(s string) error {
 	ps := strings.Split(s, "/")
 	if len(ps) != 3 {
@@ -167,21 +172,19 @@ func (p *PortBinding) FromString(s string) error {
 }
 
 func parseIPPort(s string) (net.IP, uint16, error) {
-	pp := strings.Split(s, ":")
-	if len(pp) != 2 {
-		return nil, 0, BadRequestErrorf("invalid format: %s", s)
+	hoststr, portstr, err := net.SplitHostPort(s)
+	if err != nil {
+		return nil, 0, err
 	}
 
-	var ip net.IP
-	if pp[0] != "" {
-		if ip = net.ParseIP(pp[0]); ip == nil {
-			return nil, 0, BadRequestErrorf("invalid ip: %s", pp[0])
-		}
+	ip := net.ParseIP(hoststr)
+	if ip == nil {
+		return nil, 0, BadRequestErrorf("invalid ip: %s", hoststr)
 	}
 
-	port, err := strconv.ParseUint(pp[1], 10, 16)
+	port, err := strconv.ParseUint(portstr, 10, 16)
 	if err != nil {
-		return nil, 0, BadRequestErrorf("invalid port: %s", pp[1])
+		return nil, 0, BadRequestErrorf("invalid port: %s", portstr)
 	}
 
 	return ip, uint16(port), nil

+ 53 - 13
libnetwork/types/types_test.go

@@ -2,6 +2,7 @@ package types
 
 import (
 	"flag"
+	"github.com/stretchr/testify/require"
 	"net"
 	"testing"
 )
@@ -26,21 +27,60 @@ func TestTransportPortConv(t *testing.T) {
 }
 
 func TestTransportPortBindingConv(t *testing.T) {
-	sform := "tcp/172.28.30.23:80/112.0.43.56:8001"
-	pb := &PortBinding{
-		Proto:    TCP,
-		IP:       net.IPv4(172, 28, 30, 23),
-		Port:     uint16(80),
-		HostIP:   net.IPv4(112, 0, 43, 56),
-		HostPort: uint16(8001),
+	input := []struct {
+		sform      string
+		pb         PortBinding
+		shouldFail bool
+	}{
+		{ // IPv4 -> IPv4
+			sform: "tcp/172.28.30.23:80/112.0.43.56:8001",
+			pb: PortBinding{
+				Proto:    TCP,
+				IP:       net.IPv4(172, 28, 30, 23),
+				Port:     uint16(80),
+				HostIP:   net.IPv4(112, 0, 43, 56),
+				HostPort: uint16(8001),
+			},
+		},
+		{ // IPv6 -> IPv4
+			sform: "tcp/[2001:db8::1]:80/112.0.43.56:8001",
+			pb: PortBinding{
+				Proto:    TCP,
+				IP:       net.IP{0x20, 0x01, 0x0d, 0xb8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1},
+				Port:     uint16(80),
+				HostIP:   net.IPv4(112, 0, 43, 56),
+				HostPort: uint16(8001),
+			},
+		},
+		{ // IPv4inIPv6 -> IPv4
+			sform: "tcp/[::ffff:172.28.30.23]:80/112.0.43.56:8001",
+			pb: PortBinding{
+				Proto:    TCP,
+				IP:       net.IPv4(172, 28, 30, 23),
+				Port:     uint16(80),
+				HostIP:   net.IPv4(112, 0, 43, 56),
+				HostPort: uint16(8001),
+			},
+		},
+		{ // IPv4 -> IPv4 zoned
+			sform:      "tcp/172.28.30.23:80/169.254.0.23%eth0:8001",
+			shouldFail: true,
+		},
+		{ // IPv4 -> IPv6 zoned
+			sform:      "tcp/172.28.30.23:80/[fe80::1ff:fe23:4567:890a%eth0]:8001",
+			shouldFail: true,
+		},
 	}
 
-	rc := new(PortBinding)
-	if err := rc.FromString(sform); err != nil {
-		t.Fatal(err)
-	}
-	if !pb.Equal(rc) {
-		t.Fatalf("FromString() method failed")
+	for _, in := range input {
+		rc := new(PortBinding)
+		err := rc.FromString(in.sform)
+		if in.shouldFail {
+			require.Error(t, err, "Unexpected success parsing %s", in.sform)
+		} else {
+			require.NoError(t, err)
+			require.Equal(t, in.pb, *rc, "input %s: expected %#v, got %#v", in.sform, in.pb, rc)
+		}
 	}
 }