Closes #14621
This one grew to be much more than I expected so here's the story... :-)
- when a bad port string (e.g. xxx80) is passed into container.create()
via the API it wasn't being checked until we tried to start the container.
- While starting the container we trid to parse 'xxx80' in nat.Int()
and would panic on the strconv.ParseUint(). We should (almost) never panic.
- In trying to remove the panic I decided to make it so that we, instead,
checked the string during the NewPort() constructor. This means that
I had to change all casts from 'string' to 'Port' to use NewPort() instead.
Which is a good thing anyway, people shouldn't assume they know the
internal format of types like that, in general.
- This meant I had to go and add error checks on all calls to NewPort().
To avoid changing the testcases too much I create newPortNoError() **JUST**
for the testcase uses where we know the port string is ok.
- After all of that I then went back and added a check during container.create()
to check the port string so we'll report the error as soon as we get the
data.
- If, somehow, the bad string does get into the metadata we will generate
an error during container.start() but I can't test for that because
the container.create() catches it now. But I did add a testcase for that.
Signed-off-by: Doug Davis <dug@us.ibm.com>
| ... | ... |
@@ -48,7 +48,11 @@ func (cli *DockerCli) CmdPort(args ...string) error {
|
| 48 | 48 |
proto = parts[1] |
| 49 | 49 |
} |
| 50 | 50 |
natPort := port + "/" + proto |
| 51 |
- if frontends, exists := c.NetworkSettings.Ports[nat.Port(port+"/"+proto)]; exists && frontends != nil {
|
|
| 51 |
+ newP, err := nat.NewPort(proto, port) |
|
| 52 |
+ if err != nil {
|
|
| 53 |
+ return err |
|
| 54 |
+ } |
|
| 55 |
+ if frontends, exists := c.NetworkSettings.Ports[newP]; exists && frontends != nil {
|
|
| 52 | 56 |
for _, frontend := range frontends {
|
| 53 | 57 |
fmt.Fprintf(cli.out, "%s:%s\n", frontend.HostIp, frontend.HostPort) |
| 54 | 58 |
} |
| ... | ... |
@@ -521,7 +521,10 @@ func (container *Container) buildPortMapInfo(n libnetwork.Network, ep libnetwork |
| 521 | 521 |
if expData, ok := driverInfo[netlabel.ExposedPorts]; ok {
|
| 522 | 522 |
if exposedPorts, ok := expData.([]types.TransportPort); ok {
|
| 523 | 523 |
for _, tp := range exposedPorts {
|
| 524 |
- natPort := nat.NewPort(tp.Proto.String(), strconv.Itoa(int(tp.Port))) |
|
| 524 |
+ natPort, err := nat.NewPort(tp.Proto.String(), strconv.Itoa(int(tp.Port))) |
|
| 525 |
+ if err != nil {
|
|
| 526 |
+ return nil, fmt.Errorf("Error parsing Port value(%s):%v", tp.Port, err)
|
|
| 527 |
+ } |
|
| 525 | 528 |
networkSettings.Ports[natPort] = nil |
| 526 | 529 |
} |
| 527 | 530 |
} |
| ... | ... |
@@ -534,7 +537,10 @@ func (container *Container) buildPortMapInfo(n libnetwork.Network, ep libnetwork |
| 534 | 534 |
|
| 535 | 535 |
if portMapping, ok := mapData.([]types.PortBinding); ok {
|
| 536 | 536 |
for _, pp := range portMapping {
|
| 537 |
- natPort := nat.NewPort(pp.Proto.String(), strconv.Itoa(int(pp.Port))) |
|
| 537 |
+ natPort, err := nat.NewPort(pp.Proto.String(), strconv.Itoa(int(pp.Port))) |
|
| 538 |
+ if err != nil {
|
|
| 539 |
+ return nil, err |
|
| 540 |
+ } |
|
| 538 | 541 |
natBndg := nat.PortBinding{HostIp: pp.HostIP.String(), HostPort: strconv.Itoa(int(pp.HostPort))}
|
| 539 | 542 |
networkSettings.Ports[natPort] = append(networkSettings.Ports[natPort], natBndg) |
| 540 | 543 |
} |
| ... | ... |
@@ -710,7 +716,11 @@ func (container *Container) buildCreateEndpointOptions() ([]libnetwork.EndpointO |
| 710 | 710 |
binding := bindings[port] |
| 711 | 711 |
for i := 0; i < len(binding); i++ {
|
| 712 | 712 |
pbCopy := pb.GetCopy() |
| 713 |
- pbCopy.HostPort = uint16(nat.Port(binding[i].HostPort).Int()) |
|
| 713 |
+ newP, err := nat.NewPort(nat.SplitProtoPort(binding[i].HostPort)) |
|
| 714 |
+ if err != nil {
|
|
| 715 |
+ return nil, fmt.Errorf("Error parsing HostPort value(%s):%v", binding[i].HostPort, err)
|
|
| 716 |
+ } |
|
| 717 |
+ pbCopy.HostPort = uint16(newP.Int()) |
|
| 714 | 718 |
pbCopy.HostIP = net.ParseIP(binding[i].HostIp) |
| 715 | 719 |
pbList = append(pbList, pbCopy) |
| 716 | 720 |
} |
| ... | ... |
@@ -132,7 +132,13 @@ func (daemon *Daemon) verifyContainerSettings(hostConfig *runconfig.HostConfig, |
| 132 | 132 |
for port := range hostConfig.PortBindings {
|
| 133 | 133 |
_, portStr := nat.SplitProtoPort(string(port)) |
| 134 | 134 |
if _, err := nat.ParsePort(portStr); err != nil {
|
| 135 |
- return warnings, fmt.Errorf("Invalid port specification: %s", portStr)
|
|
| 135 |
+ return warnings, fmt.Errorf("Invalid port specification: %q", portStr)
|
|
| 136 |
+ } |
|
| 137 |
+ for _, pb := range hostConfig.PortBindings[port] {
|
|
| 138 |
+ _, err := nat.NewPort(nat.SplitProtoPort(pb.HostPort)) |
|
| 139 |
+ if err != nil {
|
|
| 140 |
+ return warnings, fmt.Errorf("Invalid port specification: %q", pb.HostPort)
|
|
| 141 |
+ } |
|
| 136 | 142 |
} |
| 137 | 143 |
} |
| 138 | 144 |
if hostConfig.LxcConf.Len() > 0 && !strings.Contains(daemon.ExecutionDriver().Name(), "lxc") {
|
| ... | ... |
@@ -158,7 +158,10 @@ func (daemon *Daemon) Containers(config *ContainersConfig) ([]*types.Container, |
| 158 | 158 |
|
| 159 | 159 |
newC.Ports = []types.Port{}
|
| 160 | 160 |
for port, bindings := range container.NetworkSettings.Ports {
|
| 161 |
- p, _ := nat.ParsePort(port.Port()) |
|
| 161 |
+ p, err := nat.ParsePort(port.Port()) |
|
| 162 |
+ if err != nil {
|
|
| 163 |
+ return err |
|
| 164 |
+ } |
|
| 162 | 165 |
if len(bindings) == 0 {
|
| 163 | 166 |
newC.Ports = append(newC.Ports, types.Port{
|
| 164 | 167 |
PrivatePort: p, |
| ... | ... |
@@ -167,7 +170,10 @@ func (daemon *Daemon) Containers(config *ContainersConfig) ([]*types.Container, |
| 167 | 167 |
continue |
| 168 | 168 |
} |
| 169 | 169 |
for _, binding := range bindings {
|
| 170 |
- h, _ := nat.ParsePort(binding.HostPort) |
|
| 170 |
+ h, err := nat.ParsePort(binding.HostPort) |
|
| 171 |
+ if err != nil {
|
|
| 172 |
+ return err |
|
| 173 |
+ } |
|
| 171 | 174 |
newC.Ports = append(newC.Ports, types.Port{
|
| 172 | 175 |
PrivatePort: p, |
| 173 | 176 |
PublicPort: h, |
| ... | ... |
@@ -872,6 +872,32 @@ func (s *DockerSuite) TestContainerApiCommitWithLabelInConfig(c *check.C) {
|
| 872 | 872 |
dockerCmd(c, "run", img.Id, "ls", "/test") |
| 873 | 873 |
} |
| 874 | 874 |
|
| 875 |
+func (s *DockerSuite) TestContainerApiBadPort(c *check.C) {
|
|
| 876 |
+ config := map[string]interface{}{
|
|
| 877 |
+ "Image": "busybox", |
|
| 878 |
+ "Cmd": []string{"/bin/sh", "-c", "echo test"},
|
|
| 879 |
+ "PortBindings": map[string]interface{}{
|
|
| 880 |
+ "8080/tcp": []map[string]interface{}{
|
|
| 881 |
+ {
|
|
| 882 |
+ "HostIp": "", |
|
| 883 |
+ "HostPort": "aa80", |
|
| 884 |
+ }, |
|
| 885 |
+ }, |
|
| 886 |
+ }, |
|
| 887 |
+ } |
|
| 888 |
+ |
|
| 889 |
+ jsonData := bytes.NewBuffer(nil) |
|
| 890 |
+ json.NewEncoder(jsonData).Encode(config) |
|
| 891 |
+ |
|
| 892 |
+ status, b, err := sockRequest("POST", "/containers/create", config)
|
|
| 893 |
+ c.Assert(err, check.IsNil) |
|
| 894 |
+ c.Assert(status, check.Equals, http.StatusInternalServerError) |
|
| 895 |
+ |
|
| 896 |
+ if strings.TrimSpace(string(b)) != `Invalid port specification: "aa80"` {
|
|
| 897 |
+ c.Fatalf("Incorrect error msg: %s", string(b))
|
|
| 898 |
+ } |
|
| 899 |
+} |
|
| 900 |
+ |
|
| 875 | 901 |
func (s *DockerSuite) TestContainerApiCreate(c *check.C) {
|
| 876 | 902 |
config := map[string]interface{}{
|
| 877 | 903 |
"Image": "busybox", |
| ... | ... |
@@ -8,9 +8,15 @@ import ( |
| 8 | 8 |
"github.com/docker/docker/pkg/nat" |
| 9 | 9 |
) |
| 10 | 10 |
|
| 11 |
+// Just to make life easier |
|
| 12 |
+func newPortNoError(proto, port string) nat.Port {
|
|
| 13 |
+ p, _ := nat.NewPort(proto, port) |
|
| 14 |
+ return p |
|
| 15 |
+} |
|
| 16 |
+ |
|
| 11 | 17 |
func TestLinkNaming(t *testing.T) {
|
| 12 | 18 |
ports := make(nat.PortSet) |
| 13 |
- ports[nat.Port("6379/tcp")] = struct{}{}
|
|
| 19 |
+ ports[newPortNoError("tcp", "6379")] = struct{}{}
|
|
| 14 | 20 |
|
| 15 | 21 |
link, err := NewLink("172.0.17.3", "172.0.17.2", "/db/docker-1", nil, ports)
|
| 16 | 22 |
if err != nil {
|
| ... | ... |
@@ -40,7 +46,7 @@ func TestLinkNaming(t *testing.T) {
|
| 40 | 40 |
|
| 41 | 41 |
func TestLinkNew(t *testing.T) {
|
| 42 | 42 |
ports := make(nat.PortSet) |
| 43 |
- ports[nat.Port("6379/tcp")] = struct{}{}
|
|
| 43 |
+ ports[newPortNoError("tcp", "6379")] = struct{}{}
|
|
| 44 | 44 |
|
| 45 | 45 |
link, err := NewLink("172.0.17.3", "172.0.17.2", "/db/docker", nil, ports)
|
| 46 | 46 |
if err != nil {
|
| ... | ... |
@@ -63,7 +69,7 @@ func TestLinkNew(t *testing.T) {
|
| 63 | 63 |
t.Fail() |
| 64 | 64 |
} |
| 65 | 65 |
for _, p := range link.Ports {
|
| 66 |
- if p != nat.Port("6379/tcp") {
|
|
| 66 |
+ if p != newPortNoError("tcp", "6379") {
|
|
| 67 | 67 |
t.Fail() |
| 68 | 68 |
} |
| 69 | 69 |
} |
| ... | ... |
@@ -71,7 +77,7 @@ func TestLinkNew(t *testing.T) {
|
| 71 | 71 |
|
| 72 | 72 |
func TestLinkEnv(t *testing.T) {
|
| 73 | 73 |
ports := make(nat.PortSet) |
| 74 |
- ports[nat.Port("6379/tcp")] = struct{}{}
|
|
| 74 |
+ ports[newPortNoError("tcp", "6379")] = struct{}{}
|
|
| 75 | 75 |
|
| 76 | 76 |
link, err := NewLink("172.0.17.3", "172.0.17.2", "/db/docker", []string{"PASSWORD=gordon"}, ports)
|
| 77 | 77 |
if err != nil {
|
| ... | ... |
@@ -112,9 +118,9 @@ func TestLinkEnv(t *testing.T) {
|
| 112 | 112 |
|
| 113 | 113 |
func TestLinkMultipleEnv(t *testing.T) {
|
| 114 | 114 |
ports := make(nat.PortSet) |
| 115 |
- ports[nat.Port("6379/tcp")] = struct{}{}
|
|
| 116 |
- ports[nat.Port("6380/tcp")] = struct{}{}
|
|
| 117 |
- ports[nat.Port("6381/tcp")] = struct{}{}
|
|
| 115 |
+ ports[newPortNoError("tcp", "6379")] = struct{}{}
|
|
| 116 |
+ ports[newPortNoError("tcp", "6380")] = struct{}{}
|
|
| 117 |
+ ports[newPortNoError("tcp", "6381")] = struct{}{}
|
|
| 118 | 118 |
|
| 119 | 119 |
link, err := NewLink("172.0.17.3", "172.0.17.2", "/db/docker", []string{"PASSWORD=gordon"}, ports)
|
| 120 | 120 |
if err != nil {
|
| ... | ... |
@@ -161,9 +167,9 @@ func TestLinkMultipleEnv(t *testing.T) {
|
| 161 | 161 |
|
| 162 | 162 |
func TestLinkPortRangeEnv(t *testing.T) {
|
| 163 | 163 |
ports := make(nat.PortSet) |
| 164 |
- ports[nat.Port("6379/tcp")] = struct{}{}
|
|
| 165 |
- ports[nat.Port("6380/tcp")] = struct{}{}
|
|
| 166 |
- ports[nat.Port("6381/tcp")] = struct{}{}
|
|
| 164 |
+ ports[newPortNoError("tcp", "6379")] = struct{}{}
|
|
| 165 |
+ ports[newPortNoError("tcp", "6380")] = struct{}{}
|
|
| 166 |
+ ports[newPortNoError("tcp", "6381")] = struct{}{}
|
|
| 167 | 167 |
|
| 168 | 168 |
link, err := NewLink("172.0.17.3", "172.0.17.2", "/db/docker", []string{"PASSWORD=gordon"}, ports)
|
| 169 | 169 |
if err != nil {
|
| ... | ... |
@@ -29,8 +29,16 @@ type PortSet map[Port]struct{}
|
| 29 | 29 |
// 80/tcp |
| 30 | 30 |
type Port string |
| 31 | 31 |
|
| 32 |
-func NewPort(proto, port string) Port {
|
|
| 33 |
- return Port(fmt.Sprintf("%s/%s", port, proto))
|
|
| 32 |
+func NewPort(proto, port string) (Port, error) {
|
|
| 33 |
+ // Check for parsing issues on "port" now so we can avoid having |
|
| 34 |
+ // to check it later on. |
|
| 35 |
+ |
|
| 36 |
+ portInt, err := ParsePort(port) |
|
| 37 |
+ if err != nil {
|
|
| 38 |
+ return "", err |
|
| 39 |
+ } |
|
| 40 |
+ |
|
| 41 |
+ return Port(fmt.Sprintf("%d/%s", portInt, proto)), nil
|
|
| 34 | 42 |
} |
| 35 | 43 |
|
| 36 | 44 |
func ParsePort(rawPort string) (int, error) {
|
| ... | ... |
@@ -55,11 +63,15 @@ func (p Port) Port() string {
|
| 55 | 55 |
} |
| 56 | 56 |
|
| 57 | 57 |
func (p Port) Int() int {
|
| 58 |
- port, err := ParsePort(p.Port()) |
|
| 59 |
- if err != nil {
|
|
| 60 |
- panic(err) |
|
| 58 |
+ portStr := p.Port() |
|
| 59 |
+ if len(portStr) == 0 {
|
|
| 60 |
+ return 0 |
|
| 61 | 61 |
} |
| 62 |
- return port |
|
| 62 |
+ |
|
| 63 |
+ // We don't need to check for an error because we're going to |
|
| 64 |
+ // assume that any error would have been found, and reported, in NewPort() |
|
| 65 |
+ port, _ := strconv.ParseUint(portStr, 10, 16) |
|
| 66 |
+ return int(port) |
|
| 63 | 67 |
} |
| 64 | 68 |
|
| 65 | 69 |
// Splits a port in the format of proto/port |
| ... | ... |
@@ -152,7 +164,10 @@ func ParsePortSpecs(ports []string) (map[Port]struct{}, map[Port][]PortBinding,
|
| 152 | 152 |
if len(hostPort) > 0 {
|
| 153 | 153 |
hostPort = strconv.FormatUint(startHostPort+i, 10) |
| 154 | 154 |
} |
| 155 |
- port := NewPort(strings.ToLower(proto), containerPort) |
|
| 155 |
+ port, err := NewPort(strings.ToLower(proto), containerPort) |
|
| 156 |
+ if err != nil {
|
|
| 157 |
+ return nil, nil, err |
|
| 158 |
+ } |
|
| 156 | 159 |
if _, exists := exposedPorts[port]; !exists {
|
| 157 | 160 |
exposedPorts[port] = struct{}{}
|
| 158 | 161 |
} |
| ... | ... |
@@ -42,7 +42,11 @@ func TestParsePort(t *testing.T) {
|
| 42 | 42 |
} |
| 43 | 43 |
|
| 44 | 44 |
func TestPort(t *testing.T) {
|
| 45 |
- p := NewPort("tcp", "1234")
|
|
| 45 |
+ p, err := NewPort("tcp", "1234")
|
|
| 46 |
+ |
|
| 47 |
+ if err != nil {
|
|
| 48 |
+ t.Fatalf("tcp, 1234 had a parsing issue: %v", err)
|
|
| 49 |
+ } |
|
| 46 | 50 |
|
| 47 | 51 |
if string(p) != "1234/tcp" {
|
| 48 | 52 |
t.Fatal("tcp, 1234 did not result in the string 1234/tcp")
|
| ... | ... |
@@ -59,6 +63,11 @@ func TestPort(t *testing.T) {
|
| 59 | 59 |
if p.Int() != 1234 {
|
| 60 | 60 |
t.Fatal("port int value was not 1234")
|
| 61 | 61 |
} |
| 62 |
+ |
|
| 63 |
+ p, err = NewPort("tcp", "asd1234")
|
|
| 64 |
+ if err == nil {
|
|
| 65 |
+ t.Fatal("tcp, asd1234 was supposed to fail")
|
|
| 66 |
+ } |
|
| 62 | 67 |
} |
| 63 | 68 |
|
| 64 | 69 |
func TestSplitProtoPort(t *testing.T) {
|
| ... | ... |
@@ -6,17 +6,23 @@ import ( |
| 6 | 6 |
"github.com/docker/docker/pkg/nat" |
| 7 | 7 |
) |
| 8 | 8 |
|
| 9 |
+// Just to make life easier |
|
| 10 |
+func newPortNoError(proto, port string) nat.Port {
|
|
| 11 |
+ p, _ := nat.NewPort(proto, port) |
|
| 12 |
+ return p |
|
| 13 |
+} |
|
| 14 |
+ |
|
| 9 | 15 |
func TestCompare(t *testing.T) {
|
| 10 | 16 |
ports1 := make(nat.PortSet) |
| 11 |
- ports1[nat.Port("1111/tcp")] = struct{}{}
|
|
| 12 |
- ports1[nat.Port("2222/tcp")] = struct{}{}
|
|
| 17 |
+ ports1[newPortNoError("tcp", "1111")] = struct{}{}
|
|
| 18 |
+ ports1[newPortNoError("tcp", "2222")] = struct{}{}
|
|
| 13 | 19 |
ports2 := make(nat.PortSet) |
| 14 |
- ports2[nat.Port("3333/tcp")] = struct{}{}
|
|
| 15 |
- ports2[nat.Port("4444/tcp")] = struct{}{}
|
|
| 20 |
+ ports2[newPortNoError("tcp", "3333")] = struct{}{}
|
|
| 21 |
+ ports2[newPortNoError("tcp", "4444")] = struct{}{}
|
|
| 16 | 22 |
ports3 := make(nat.PortSet) |
| 17 |
- ports3[nat.Port("1111/tcp")] = struct{}{}
|
|
| 18 |
- ports3[nat.Port("2222/tcp")] = struct{}{}
|
|
| 19 |
- ports3[nat.Port("5555/tcp")] = struct{}{}
|
|
| 23 |
+ ports3[newPortNoError("tcp", "1111")] = struct{}{}
|
|
| 24 |
+ ports3[newPortNoError("tcp", "2222")] = struct{}{}
|
|
| 25 |
+ ports3[newPortNoError("tcp", "5555")] = struct{}{}
|
|
| 20 | 26 |
volumes1 := make(map[string]struct{})
|
| 21 | 27 |
volumes1["/test1"] = struct{}{}
|
| 22 | 28 |
volumes2 := make(map[string]struct{})
|
| ... | ... |
@@ -11,8 +11,8 @@ func TestMerge(t *testing.T) {
|
| 11 | 11 |
volumesImage["/test1"] = struct{}{}
|
| 12 | 12 |
volumesImage["/test2"] = struct{}{}
|
| 13 | 13 |
portsImage := make(nat.PortSet) |
| 14 |
- portsImage[nat.Port("1111/tcp")] = struct{}{}
|
|
| 15 |
- portsImage[nat.Port("2222/tcp")] = struct{}{}
|
|
| 14 |
+ portsImage[newPortNoError("tcp", "1111")] = struct{}{}
|
|
| 15 |
+ portsImage[newPortNoError("tcp", "2222")] = struct{}{}
|
|
| 16 | 16 |
configImage := &Config{
|
| 17 | 17 |
ExposedPorts: portsImage, |
| 18 | 18 |
Env: []string{"VAR1=1", "VAR2=2"},
|
| ... | ... |
@@ -20,8 +20,8 @@ func TestMerge(t *testing.T) {
|
| 20 | 20 |
} |
| 21 | 21 |
|
| 22 | 22 |
portsUser := make(nat.PortSet) |
| 23 |
- portsUser[nat.Port("2222/tcp")] = struct{}{}
|
|
| 24 |
- portsUser[nat.Port("3333/tcp")] = struct{}{}
|
|
| 23 |
+ portsUser[newPortNoError("tcp", "2222")] = struct{}{}
|
|
| 24 |
+ portsUser[newPortNoError("tcp", "3333")] = struct{}{}
|
|
| 25 | 25 |
volumesUser := make(map[string]struct{})
|
| 26 | 26 |
volumesUser["/test3"] = struct{}{}
|
| 27 | 27 |
configUser := &Config{
|
| ... | ... |
@@ -267,7 +267,10 @@ func Parse(cmd *flag.FlagSet, args []string) (*Config, *HostConfig, *flag.FlagSe |
| 267 | 267 |
return nil, nil, cmd, fmt.Errorf("Invalid range format for --expose: %s, error: %s", e, err)
|
| 268 | 268 |
} |
| 269 | 269 |
for i := start; i <= end; i++ {
|
| 270 |
- p := nat.NewPort(proto, strconv.FormatUint(i, 10)) |
|
| 270 |
+ p, err := nat.NewPort(proto, strconv.FormatUint(i, 10)) |
|
| 271 |
+ if err != nil {
|
|
| 272 |
+ return nil, nil, cmd, err |
|
| 273 |
+ } |
|
| 271 | 274 |
if _, exists := ports[p]; !exists {
|
| 272 | 275 |
ports[p] = struct{}{}
|
| 273 | 276 |
} |