Skip to content

Commit 257e89e

Browse files
jimlambrtmiekg
andauthored
feat: add support for ReuseAddr (#1510)
* feat: add support for ReuseAddr * Update listen_reuseport.go * Update listen_reuseport.go * fixup! feat: add support for ReuseAddr --------- Co-authored-by: Miek Gieben <[email protected]>
1 parent 3d593a6 commit 257e89e

File tree

4 files changed

+210
-11
lines changed

4 files changed

+210
-11
lines changed

listen_no_reuseport.go

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,16 +7,18 @@ import "net"
77

88
const supportsReusePort = false
99

10-
func listenTCP(network, addr string, reuseport bool) (net.Listener, error) {
11-
if reuseport {
10+
func listenTCP(network, addr string, reuseport, reuseaddr bool) (net.Listener, error) {
11+
if reuseport || reuseaddr {
1212
// TODO(tmthrgd): return an error?
1313
}
1414

1515
return net.Listen(network, addr)
1616
}
1717

18-
func listenUDP(network, addr string, reuseport bool) (net.PacketConn, error) {
19-
if reuseport {
18+
const supportsReuseAddr = false
19+
20+
func listenUDP(network, addr string, reuseport, reuseaddr bool) (net.PacketConn, error) {
21+
if reuseport || reuseaddr {
2022
// TODO(tmthrgd): return an error?
2123
}
2224

listen_reuseport.go

Lines changed: 26 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -25,19 +25,41 @@ func reuseportControl(network, address string, c syscall.RawConn) error {
2525
return opErr
2626
}
2727

28-
func listenTCP(network, addr string, reuseport bool) (net.Listener, error) {
28+
const supportsReuseAddr = true
29+
30+
func reuseaddrControl(network, address string, c syscall.RawConn) error {
31+
var opErr error
32+
err := c.Control(func(fd uintptr) {
33+
opErr = unix.SetsockoptInt(int(fd), unix.SOL_SOCKET, unix.SO_REUSEADDR, 1)
34+
})
35+
if err != nil {
36+
return err
37+
}
38+
39+
return opErr
40+
}
41+
42+
func listenTCP(network, addr string, reuseport, reuseaddr bool) (net.Listener, error) {
2943
var lc net.ListenConfig
30-
if reuseport {
44+
switch {
45+
case reuseaddr && reuseport:
46+
case reuseport:
3147
lc.Control = reuseportControl
48+
case reuseaddr:
49+
lc.Control = reuseaddrControl
3250
}
3351

3452
return lc.Listen(context.Background(), network, addr)
3553
}
3654

37-
func listenUDP(network, addr string, reuseport bool) (net.PacketConn, error) {
55+
func listenUDP(network, addr string, reuseport, reuseaddr bool) (net.PacketConn, error) {
3856
var lc net.ListenConfig
39-
if reuseport {
57+
switch {
58+
case reuseaddr && reuseport:
59+
case reuseport:
4060
lc.Control = reuseportControl
61+
case reuseaddr:
62+
lc.Control = reuseaddrControl
4163
}
4264

4365
return lc.ListenPacket(context.Background(), network, addr)

server.go

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -226,6 +226,10 @@ type Server struct {
226226
// Whether to set the SO_REUSEPORT socket option, allowing multiple listeners to be bound to a single address.
227227
// It is only supported on certain GOOSes and when using ListenAndServe.
228228
ReusePort bool
229+
// Whether to set the SO_REUSEADDR socket option, allowing multiple listeners to be bound to a single address.
230+
// Crucially this allows binding when an existing server is listening on `0.0.0.0` or `::`.
231+
// It is only supported on certain GOOSes and when using ListenAndServe.
232+
ReuseAddr bool
229233
// AcceptMsgFunc will check the incoming message and will reject it early in the process.
230234
// By default DefaultMsgAcceptFunc will be used.
231235
MsgAcceptFunc MsgAcceptFunc
@@ -304,7 +308,7 @@ func (srv *Server) ListenAndServe() error {
304308

305309
switch srv.Net {
306310
case "tcp", "tcp4", "tcp6":
307-
l, err := listenTCP(srv.Net, addr, srv.ReusePort)
311+
l, err := listenTCP(srv.Net, addr, srv.ReusePort, srv.ReuseAddr)
308312
if err != nil {
309313
return err
310314
}
@@ -317,7 +321,7 @@ func (srv *Server) ListenAndServe() error {
317321
return errors.New("dns: neither Certificates nor GetCertificate set in Config")
318322
}
319323
network := strings.TrimSuffix(srv.Net, "-tls")
320-
l, err := listenTCP(network, addr, srv.ReusePort)
324+
l, err := listenTCP(network, addr, srv.ReusePort, srv.ReuseAddr)
321325
if err != nil {
322326
return err
323327
}
@@ -327,7 +331,7 @@ func (srv *Server) ListenAndServe() error {
327331
unlock()
328332
return srv.serveTCP(l)
329333
case "udp", "udp4", "udp6":
330-
l, err := listenUDP(srv.Net, addr, srv.ReusePort)
334+
l, err := listenUDP(srv.Net, addr, srv.ReusePort, srv.ReuseAddr)
331335
if err != nil {
332336
return err
333337
}

server_test.go

Lines changed: 171 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package dns
33
import (
44
"context"
55
"crypto/tls"
6+
"errors"
67
"fmt"
78
"io"
89
"net"
@@ -1041,6 +1042,176 @@ func TestServerReuseport(t *testing.T) {
10411042
}
10421043
}
10431044

1045+
func TestServerReuseaddr(t *testing.T) {
1046+
startServerFn := func(t *testing.T, network, addr string, expectSuccess bool) (*Server, chan error) {
1047+
t.Helper()
1048+
wait := make(chan struct{})
1049+
srv := &Server{
1050+
Net: network,
1051+
Addr: addr,
1052+
NotifyStartedFunc: func() { close(wait) },
1053+
ReuseAddr: true,
1054+
}
1055+
1056+
fin := make(chan error, 1)
1057+
go func() {
1058+
fin <- srv.ListenAndServe()
1059+
}()
1060+
1061+
select {
1062+
case <-wait:
1063+
case err := <-fin:
1064+
switch {
1065+
case expectSuccess:
1066+
t.Fatalf("%s: failed to start server: %v", t.Name(), err)
1067+
default:
1068+
fin <- err
1069+
return nil, fin
1070+
}
1071+
}
1072+
return srv, fin
1073+
}
1074+
1075+
externalIPFn := func(t *testing.T) (string, error) {
1076+
t.Helper()
1077+
ifaces, err := net.Interfaces()
1078+
if err != nil {
1079+
return "", err
1080+
}
1081+
for _, iface := range ifaces {
1082+
if iface.Flags&net.FlagUp == 0 {
1083+
continue // interface down
1084+
}
1085+
if iface.Flags&net.FlagLoopback != 0 {
1086+
continue // loopback interface
1087+
}
1088+
addrs, err := iface.Addrs()
1089+
if err != nil {
1090+
return "", err
1091+
}
1092+
for _, addr := range addrs {
1093+
var ip net.IP
1094+
switch v := addr.(type) {
1095+
case *net.IPNet:
1096+
ip = v.IP
1097+
case *net.IPAddr:
1098+
ip = v.IP
1099+
}
1100+
if ip == nil || ip.IsLoopback() {
1101+
continue
1102+
}
1103+
ip = ip.To4()
1104+
if ip == nil {
1105+
continue // not an ipv4 address
1106+
}
1107+
return ip.String(), nil
1108+
}
1109+
}
1110+
return "", errors.New("are you connected to the network?")
1111+
}
1112+
1113+
freePortFn := func(t *testing.T) int {
1114+
t.Helper()
1115+
addr, err := net.ResolveTCPAddr("tcp", "localhost:0")
1116+
if err != nil {
1117+
t.Fatalf("unable resolve tcp addr: %s", err)
1118+
}
1119+
1120+
l, err := net.ListenTCP("tcp", addr)
1121+
if err != nil {
1122+
t.Fatalf("unable listen tcp: %s", err)
1123+
}
1124+
defer l.Close()
1125+
return l.Addr().(*net.TCPAddr).Port
1126+
}
1127+
1128+
t.Run("should-fail-tcp", func(t *testing.T) {
1129+
// ReuseAddr should fail if you try to bind to exactly the same
1130+
// combination of source address and port.
1131+
// This should fail whether or not ReuseAddr is supported on a
1132+
// particular OS
1133+
ip, err := externalIPFn(t)
1134+
if err != nil {
1135+
t.Skip("no external IPs found")
1136+
return
1137+
}
1138+
port := freePortFn(t)
1139+
srv1, fin1 := startServerFn(t, "tcp", fmt.Sprintf("%s:%d", ip, port), true)
1140+
srv2, fin2 := startServerFn(t, "tcp", fmt.Sprintf("%s:%d", ip, port), false)
1141+
switch {
1142+
case srv2 != nil && srv2.started:
1143+
t.Fatalf("second ListenAndServe should not have started")
1144+
default:
1145+
if err := <-fin2; err == nil {
1146+
t.Fatalf("second ListenAndServe should have returned a startup error: %v", err)
1147+
}
1148+
}
1149+
1150+
if err := srv1.Shutdown(); err != nil {
1151+
t.Fatalf("failed to shutdown first server: %v", err)
1152+
}
1153+
if err := <-fin1; err != nil {
1154+
t.Fatalf("first ListenAndServe returned error after Shutdown: %v", err)
1155+
}
1156+
})
1157+
t.Run("should-succeed-tcp", func(t *testing.T) {
1158+
if !supportsReuseAddr {
1159+
t.Skip("reuseaddr is not supported")
1160+
}
1161+
ip, err := externalIPFn(t)
1162+
if err != nil {
1163+
t.Skip("no external IPs found")
1164+
return
1165+
}
1166+
port := freePortFn(t)
1167+
1168+
// ReuseAddr should succeed if you try to bind to the same port but a different source address
1169+
srv1, fin1 := startServerFn(t, "tcp", fmt.Sprintf("localhost:%d", port), true)
1170+
srv2, fin2 := startServerFn(t, "tcp", fmt.Sprintf("%s:%d", ip, port), true)
1171+
1172+
if err := srv1.Shutdown(); err != nil {
1173+
t.Fatalf("failed to shutdown first server: %v", err)
1174+
}
1175+
if err := srv2.Shutdown(); err != nil {
1176+
t.Fatalf("failed to shutdown second server: %v", err)
1177+
}
1178+
if err := <-fin1; err != nil {
1179+
t.Fatalf("first ListenAndServe returned error after Shutdown: %v", err)
1180+
}
1181+
if err := <-fin2; err != nil {
1182+
t.Fatalf("second ListenAndServe returned error after Shutdown: %v", err)
1183+
}
1184+
})
1185+
t.Run("should-succeed-udp", func(t *testing.T) {
1186+
if !supportsReuseAddr {
1187+
t.Skip("reuseaddr is not supported")
1188+
}
1189+
ip, err := externalIPFn(t)
1190+
if err != nil {
1191+
t.Skip("no external IPs found")
1192+
return
1193+
}
1194+
port := freePortFn(t)
1195+
1196+
// ReuseAddr should succeed if you try to bind to the same port but a different source address
1197+
srv1, fin1 := startServerFn(t, "udp", fmt.Sprintf("localhost:%d", port), true)
1198+
srv2, fin2 := startServerFn(t, "udp", fmt.Sprintf("%s:%d", ip, port), true)
1199+
1200+
if err := srv1.Shutdown(); err != nil {
1201+
t.Fatalf("failed to shutdown first server: %v", err)
1202+
}
1203+
if err := srv2.Shutdown(); err != nil {
1204+
t.Fatalf("failed to shutdown second server: %v", err)
1205+
}
1206+
if err := <-fin1; err != nil {
1207+
t.Fatalf("first ListenAndServe returned error after Shutdown: %v", err)
1208+
}
1209+
if err := <-fin2; err != nil {
1210+
t.Fatalf("second ListenAndServe returned error after Shutdown: %v", err)
1211+
}
1212+
})
1213+
}
1214+
10441215
func TestServerRoundtripTsig(t *testing.T) {
10451216
secret := map[string]string{"test.": "so6ZGir4GPAqINNh9U5c3A=="}
10461217

0 commit comments

Comments
 (0)