16
17
import (
18
"errors"
19
+ "net"
20
+ "os"
21
"testing"
22
+
23
+ "golang.org/x/sys/unix"
24
)
25
22
-func TestIsPrivilegedPort(t *testing.T) {
26
+func TestIsPortBind(t *testing.T) {
27
for _, tc := range []struct {
28
network, address string
29
want bool
32
{"tcp4", "127.0.0.1:443", true},
33
{"udp6", "[::1]:443", true},
34
{"tcp", ":1023", true},
31
- {"tcp", ":1024", false},
32
- {"tcp", ":8080", false},
35
+ {"tcp", ":1024", true},
36
+ {"tcp", ":8080", true},
37
+ {"tcp", ":65535", true},
38
+ {"tcp", ":65536", false},
39
{"tcp", ":0", false},
40
{"unix", "/run/caddy.sock", false},
41
{"fd", "3", false},
42
{"ip4:icmp", "127.0.0.1", false},
43
} {
38
- if got := isPrivilegedPort(tc.network, tc.address); got != tc.want {
39
- t.Errorf("isPrivilegedPort(%q, %q) = %v, want %v", tc.network, tc.address, got, tc.want)
44
+ if got := isPortBind(tc.network, tc.address); got != tc.want {
45
+ t.Errorf("isPortBind(%q, %q) = %v, want %v", tc.network, tc.address, got, tc.want)
46
}
47
}
48
}
53
t.Fatalf("reading capabilities: %v", err)
54
}
55
56
+ // a privileged-port EACCES is retried only if the capability can be raised
57
+ retryCalls := 1
58
+ if before {
59
+ retryCalls = 2
60
+ }
61
+
62
+ // shaped like the error net.ListenConfig.Listen returns
63
+ eacces := &net.OpError{Op: "listen", Net: "tcp", Err: os.NewSyscallError("bind", unix.EACCES)}
64
sentinel := errors.New("sentinel")
51
- for _, tc := range []struct{ network, address string }{
52
- {"tcp", "localhost:80"},
53
- {"udp", ":8080"},
54
- {"unix", "/tmp/caddy.sock"},
65
+ for _, tc := range []struct {
66
+ network, address string
67
+ err error
68
+ wantCalls int
69
+ }{
70
+ {"tcp", "localhost:80", nil, 1},
71
+ {"tcp", "localhost:80", sentinel, 1},
72
+ {"udp", ":8080", sentinel, 1},
73
+ {"unix", "/tmp/caddy.sock", eacces, 1},
74
+ {"tcp", ":0", eacces, 1},
75
+ {"tcp", "localhost:80", eacces, retryCalls},
76
} {
77
+ calls := 0
78
ln, err := WithBindCapability(tc.network, tc.address, func() (any, error) {
57
- return "listener", sentinel
79
+ calls++
80
+ return "listener", tc.err
81
})
59
- if ln != "listener" || !errors.Is(err, sentinel) {
60
- t.Errorf("%s/%s: got (%v, %v), want listen's return values", tc.network, tc.address, ln, err)
82
+ if calls != tc.wantCalls {
83
+ t.Errorf("%s/%s/%v: listen called %d times, want %d", tc.network, tc.address, tc.err, calls, tc.wantCalls)
84
+ }
85
+ if ln != "listener" || !errors.Is(err, tc.err) {
86
+ t.Errorf("%s/%s/%v: got (%v, %v), want listen's return values", tc.network, tc.address, tc.err, ln, err)
87
}
88
}
89