WARNING: THIS SITE IS A MIRROR OF GITHUB.COM / IT CANNOT LOGIN OR REGISTER ACCOUNTS / THE CONTENTS ARE PROVIDED AS-IS / THIS SITE ASSUMES NO RESPONSIBILITY FOR ANY DISPLAYED CONTENT OR LINKS / IF YOU FOUND SOMETHING MAY NOT GOOD FOR EVERYONE, CONTACT ADMIN AT ilovescratch@foxmail.com
Skip to content

Commit 9354edd

Browse files
Merge pull request docker#144 from thaJeztah/nat_cleanups
nat: cleanup, optimize, and improve errors
2 parents cf3fce2 + 8b7a212 commit 9354edd

File tree

4 files changed

+173
-65
lines changed

4 files changed

+173
-65
lines changed

nat/nat.go

Lines changed: 32 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -27,69 +27,69 @@ type PortSet map[Port]struct{}
2727
type Port string
2828

2929
// NewPort creates a new instance of a Port given a protocol and port number or port range
30-
func NewPort(proto, port string) (Port, error) {
31-
// Check for parsing issues on "port" now so we can avoid having
32-
// to check it later on.
33-
34-
portStartInt, portEndInt, err := ParsePortRangeToInt(port)
30+
func NewPort(proto, portOrRange string) (Port, error) {
31+
start, end, err := parsePortRange(portOrRange)
3532
if err != nil {
3633
return "", err
3734
}
38-
39-
if portStartInt == portEndInt {
40-
return Port(fmt.Sprintf("%d/%s", portStartInt, proto)), nil
35+
if start == end {
36+
return Port(fmt.Sprintf("%d/%s", start, proto)), nil
4137
}
42-
return Port(fmt.Sprintf("%d-%d/%s", portStartInt, portEndInt, proto)), nil
38+
return Port(fmt.Sprintf("%d-%d/%s", start, end, proto)), nil
4339
}
4440

4541
// ParsePort parses the port number string and returns an int
4642
func ParsePort(rawPort string) (int, error) {
4743
if rawPort == "" {
4844
return 0, nil
4945
}
50-
port, err := strconv.ParseUint(rawPort, 10, 16)
46+
port, err := parsePortNumber(rawPort)
5147
if err != nil {
52-
return 0, fmt.Errorf("invalid port '%s': %w", rawPort, errors.Unwrap(err))
48+
return 0, fmt.Errorf("invalid port '%s': %w", rawPort, err)
5349
}
54-
return int(port), nil
50+
return port, nil
5551
}
5652

5753
// ParsePortRangeToInt parses the port range string and returns start/end ints
58-
func ParsePortRangeToInt(rawPort string) (int, int, error) {
54+
func ParsePortRangeToInt(rawPort string) (startPort, endPort int, _ error) {
5955
if rawPort == "" {
56+
// TODO(thaJeztah): consider making this an error; this was kept to keep existing behavior.
6057
return 0, 0, nil
6158
}
62-
start, end, err := ParsePortRange(rawPort)
63-
if err != nil {
64-
return 0, 0, err
65-
}
66-
return int(start), int(end), nil
59+
return parsePortRange(rawPort)
6760
}
6861

6962
// Proto returns the protocol of a Port
7063
func (p Port) Proto() string {
71-
proto, _ := SplitProtoPort(string(p))
64+
_, proto, _ := strings.Cut(string(p), "/")
65+
if proto == "" {
66+
proto = "tcp"
67+
}
7268
return proto
7369
}
7470

7571
// Port returns the port number of a Port
7672
func (p Port) Port() string {
77-
_, port := SplitProtoPort(string(p))
73+
port, _, _ := strings.Cut(string(p), "/")
7874
return port
7975
}
8076

81-
// Int returns the port number of a Port as an int
77+
// Int returns the port number of a Port as an int. It assumes [Port]
78+
// is valid, and returns 0 otherwise.
8279
func (p Port) Int() int {
83-
portStr := p.Port()
8480
// We don't need to check for an error because we're going to
85-
// assume that any error would have been found, and reported, in NewPort()
86-
port, _ := ParsePort(portStr)
81+
// assume that any error would have been found, and reported, in [NewPort]
82+
port, _ := parsePortNumber(p.Port())
8783
return port
8884
}
8985

9086
// Range returns the start/end port numbers of a Port range as ints
9187
func (p Port) Range() (int, int, error) {
92-
return ParsePortRangeToInt(p.Port())
88+
portRange := p.Port()
89+
if portRange == "" {
90+
return 0, 0, nil
91+
}
92+
return parsePortRange(portRange)
9393
}
9494

9595
// SplitProtoPort splits a port(range) and protocol, formatted as "<portnum>/[<proto>]"
@@ -194,14 +194,14 @@ func ParsePortSpec(rawPort string) ([]PortMapping, error) {
194194
return nil, errors.New("invalid IP address: " + ip)
195195
}
196196

197-
startPort, endPort, err := ParsePortRange(containerPort)
197+
startPort, endPort, err := parsePortRange(containerPort)
198198
if err != nil {
199199
return nil, errors.New("invalid containerPort: " + containerPort)
200200
}
201201

202-
var startHostPort, endHostPort uint64
202+
var startHostPort, endHostPort int
203203
if hostPort != "" {
204-
startHostPort, endHostPort, err = ParsePortRange(hostPort)
204+
startHostPort, endHostPort, err = parsePortRange(hostPort)
205205
if err != nil {
206206
return nil, errors.New("invalid hostPort: " + hostPort)
207207
}
@@ -218,19 +218,18 @@ func ParsePortSpec(rawPort string) ([]PortMapping, error) {
218218
count := endPort - startPort + 1
219219
ports := make([]PortMapping, 0, count)
220220

221-
for i := uint64(0); i < count; i++ {
222-
cPort := Port(strconv.FormatUint(startPort+i, 10) + "/" + proto)
221+
for i := 0; i < count; i++ {
223222
hPort := ""
224223
if hostPort != "" {
225-
hPort = strconv.FormatUint(startHostPort+i, 10)
224+
hPort = strconv.Itoa(startHostPort + i)
226225
// Set hostPort to a range only if there is a single container port
227226
// and a dynamic host port.
228227
if count == 1 && startHostPort != endHostPort {
229-
hPort += "-" + strconv.FormatUint(endHostPort, 10)
228+
hPort += "-" + strconv.Itoa(endHostPort)
230229
}
231230
}
232231
ports = append(ports, PortMapping{
233-
Port: cPort,
232+
Port: Port(strconv.Itoa(startPort+i) + "/" + proto),
234233
Binding: PortBinding{HostIP: ip, HostPort: hPort},
235234
})
236235
}

nat/nat_test.go

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ func TestParsePort(t *testing.T) {
3838
doc: "negative value",
3939
input: "-1",
4040
expPort: 0,
41-
expErr: `invalid port '-1': invalid syntax`,
41+
expErr: `invalid port '-1': value out of range (0–65535)`,
4242
},
4343
// FIXME currently this is a valid port. I don't think it should be.
4444
// I'm leaving this test until we make a decision.
@@ -57,7 +57,7 @@ func TestParsePort(t *testing.T) {
5757
doc: "value out of range",
5858
input: "65536",
5959
expPort: 0,
60-
expErr: `invalid port '65536': value out of range`,
60+
expErr: `invalid port '65536': value out of range (0–65535)`,
6161
},
6262
}
6363

@@ -707,11 +707,11 @@ func TestParseNetworkOptsNegativePorts(t *testing.T) {
707707
t.Fail()
708708
}
709709
if len(ports) != 0 {
710-
t.Logf("Expected nil got %d", len(ports))
710+
t.Logf("Expected 0 got %d: %#v", len(ports), ports)
711711
t.Fail()
712712
}
713713
if len(bindings) != 0 {
714-
t.Logf("Expected 0 got %d", len(bindings))
714+
t.Logf("Expected 0 got %d: %#v", len(bindings), bindings)
715715
t.Fail()
716716
}
717717
}
@@ -722,11 +722,11 @@ func TestParseNetworkOptsUdp(t *testing.T) {
722722
t.Fatal(err)
723723
}
724724
if len(ports) != 1 {
725-
t.Logf("Expected 1 got %d", len(ports))
725+
t.Logf("Expected 1 got %d: %#v", len(ports), ports)
726726
t.FailNow()
727727
}
728728
if len(bindings) != 1 {
729-
t.Logf("Expected 1 got %d", len(bindings))
729+
t.Logf("Expected 1 got %d: %#v", len(bindings), bindings)
730730
t.FailNow()
731731
}
732732
for k := range ports {
@@ -764,11 +764,11 @@ func TestParseNetworkOptsSctp(t *testing.T) {
764764
t.Fatal(err)
765765
}
766766
if len(ports) != 1 {
767-
t.Logf("Expected 1 got %d", len(ports))
767+
t.Logf("Expected 1 got %d: %#v", len(ports), ports)
768768
t.FailNow()
769769
}
770770
if len(bindings) != 1 {
771-
t.Logf("Expected 1 got %d", len(bindings))
771+
t.Logf("Expected 1 got %d: %#v", len(bindings), bindings)
772772
t.FailNow()
773773
}
774774
for k := range ports {

nat/parse.go

Lines changed: 41 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -2,32 +2,59 @@ package nat
22

33
import (
44
"errors"
5+
"fmt"
56
"strconv"
67
"strings"
78
)
89

9-
// ParsePortRange parses and validates the specified string as a port-range (8000-9000)
10-
func ParsePortRange(ports string) (uint64, uint64, error) {
10+
// ParsePortRange parses and validates the specified string as a port range (e.g., "8000-9000").
11+
func ParsePortRange(ports string) (startPort, endPort uint64, _ error) {
12+
start, end, err := parsePortRange(ports)
13+
return uint64(start), uint64(end), err
14+
}
15+
16+
// parsePortRange parses and validates the specified string as a port range (e.g., "8000-9000").
17+
func parsePortRange(ports string) (startPort, endPort int, _ error) {
1118
if ports == "" {
1219
return 0, 0, errors.New("empty string specified for ports")
1320
}
14-
if !strings.Contains(ports, "-") {
15-
start, err := strconv.ParseUint(ports, 10, 16)
16-
end := start
17-
return start, end, err
21+
start, end, ok := strings.Cut(ports, "-")
22+
23+
startPort, err := parsePortNumber(start)
24+
if err != nil {
25+
return 0, 0, fmt.Errorf("invalid start port '%s': %w", start, err)
26+
}
27+
if !ok || start == end {
28+
return startPort, startPort, nil
1829
}
1930

20-
parts := strings.Split(ports, "-")
21-
start, err := strconv.ParseUint(parts[0], 10, 16)
31+
endPort, err = parsePortNumber(end)
2232
if err != nil {
23-
return 0, 0, err
33+
return 0, 0, fmt.Errorf("invalid end port '%s': %w", end, err)
2434
}
25-
end, err := strconv.ParseUint(parts[1], 10, 16)
35+
if endPort < startPort {
36+
return 0, 0, errors.New("invalid port range: " + ports)
37+
}
38+
return startPort, endPort, nil
39+
}
40+
41+
// parsePortNumber parses rawPort into an int, unwrapping strconv errors
42+
// and returning a single "out of range" error for any value outside 0–65535.
43+
func parsePortNumber(rawPort string) (int, error) {
44+
if rawPort == "" {
45+
return 0, errors.New("value is empty")
46+
}
47+
port, err := strconv.ParseInt(rawPort, 10, 0)
2648
if err != nil {
27-
return 0, 0, err
49+
var numErr *strconv.NumError
50+
if errors.As(err, &numErr) {
51+
err = numErr.Err
52+
}
53+
return 0, err
2854
}
29-
if end < start {
30-
return 0, 0, errors.New("invalid range specified for port: " + ports)
55+
if port < 0 || port > 65535 {
56+
return 0, errors.New("value out of range (0–65535)")
3157
}
32-
return start, end, nil
58+
59+
return int(port), nil
3360
}

0 commit comments

Comments
 (0)