Skip to content

Commit 22cbde9

Browse files
committed
quic: set ServerName in client connection TLSConfig
Client connections must set tls.Config.ServerName to authenticate the identity of the server. (RFC 9001, Section 4.4.) Previously, we specified a single tls.Config per Endpoint. Change the Config passed to Listen to only apply to client connections accepted by the endpoint. Add a Config parameter to Listener.Dial to allow specifying a separate config per outbound connection, allowing the user to set the ServerName field. When the user does not set ServerName, set it ourselves. For golang/go#58547 Change-Id: Ie2500ae7c7a85400e6cc1c10cefa2bd4c746e313 Reviewed-on: https://go-review.googlesource.com/c/net/+/565796 Reviewed-by: Jonathan Amsterdam <[email protected]> LUCI-TryBot-Result: Go LUCI <[email protected]>
1 parent 57e4cc7 commit 22cbde9

File tree

7 files changed

+81
-42
lines changed

7 files changed

+81
-42
lines changed

internal/quic/cmd/interop/main.go

+3-3
Original file line numberDiff line numberDiff line change
@@ -148,7 +148,7 @@ func basicTest(ctx context.Context, config *quic.Config, urls []string) {
148148
g.Add(1)
149149
go func() {
150150
defer g.Done()
151-
fetchFrom(ctx, l, addr, u)
151+
fetchFrom(ctx, config, l, addr, u)
152152
}()
153153
}
154154

@@ -221,8 +221,8 @@ func parseURL(s string) (u *url.URL, authority string, err error) {
221221
return u, authority, nil
222222
}
223223

224-
func fetchFrom(ctx context.Context, l *quic.Endpoint, addr string, urls []*url.URL) {
225-
conn, err := l.Dial(ctx, "udp", addr)
224+
func fetchFrom(ctx context.Context, config *quic.Config, l *quic.Endpoint, addr string, urls []*url.URL) {
225+
conn, err := l.Dial(ctx, "udp", addr, config)
226226
if err != nil {
227227
log.Printf("%v: %v", addr, err)
228228
return

internal/quic/config.go

+7
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,13 @@ type Config struct {
107107
QLogLogger *slog.Logger
108108
}
109109

110+
// Clone returns a shallow clone of c, or nil if c is nil.
111+
// It is safe to clone a [Config] that is being used concurrently by a QUIC endpoint.
112+
func (c *Config) Clone() *Config {
113+
n := *c
114+
return &n
115+
}
116+
110117
func configDefault[T ~int64](v, def, limit T) T {
111118
switch {
112119
case v == 0:

internal/quic/conn.go

+2-2
Original file line numberDiff line numberDiff line change
@@ -94,7 +94,7 @@ type newServerConnIDs struct {
9494
retrySrcConnID []byte // source from server's Retry
9595
}
9696

97-
func newConn(now time.Time, side connSide, cids newServerConnIDs, peerAddr netip.AddrPort, config *Config, e *Endpoint) (conn *Conn, _ error) {
97+
func newConn(now time.Time, side connSide, cids newServerConnIDs, peerHostname string, peerAddr netip.AddrPort, config *Config, e *Endpoint) (conn *Conn, _ error) {
9898
c := &Conn{
9999
side: side,
100100
endpoint: e,
@@ -146,7 +146,7 @@ func newConn(now time.Time, side connSide, cids newServerConnIDs, peerAddr netip
146146
c.lifetimeInit()
147147
c.restartIdleTimer(now)
148148

149-
if err := c.startTLS(now, initialConnID, transportParameters{
149+
if err := c.startTLS(now, initialConnID, peerHostname, transportParameters{
150150
initialSrcConnID: c.connIDState.srcConnID(),
151151
originalDstConnID: cids.originalDstConnID,
152152
retrySrcConnID: cids.retrySrcConnID,

internal/quic/conn_test.go

+2
Original file line numberDiff line numberDiff line change
@@ -242,8 +242,10 @@ func newTestConn(t *testing.T, side connSide, opts ...any) *testConn {
242242
endpoint.configTestConn = configTestConn
243243
conn, err := endpoint.e.newConn(
244244
endpoint.now,
245+
config,
245246
side,
246247
cids,
248+
"",
247249
netip.MustParseAddrPort("127.0.0.1:443"))
248250
if err != nil {
249251
t.Fatal(err)

internal/quic/endpoint.go

+35-24
Original file line numberDiff line numberDiff line change
@@ -22,11 +22,11 @@ import (
2222
//
2323
// Multiple goroutines may invoke methods on an Endpoint simultaneously.
2424
type Endpoint struct {
25-
config *Config
26-
packetConn packetConn
27-
testHooks endpointTestHooks
28-
resetGen statelessResetTokenGenerator
29-
retry retryState
25+
listenConfig *Config
26+
packetConn packetConn
27+
testHooks endpointTestHooks
28+
resetGen statelessResetTokenGenerator
29+
retry retryState
3030

3131
acceptQueue queue[*Conn] // new inbound connections
3232
connsMap connsMap // only accessed by the listen loop
@@ -51,9 +51,11 @@ type packetConn interface {
5151
}
5252

5353
// Listen listens on a local network address.
54-
// The configuration config must be non-nil.
55-
func Listen(network, address string, config *Config) (*Endpoint, error) {
56-
if config.TLSConfig == nil {
54+
//
55+
// The config is used to for connections accepted by the endpoint.
56+
// If the config is nil, the endpoint will not accept connections.
57+
func Listen(network, address string, listenConfig *Config) (*Endpoint, error) {
58+
if listenConfig != nil && listenConfig.TLSConfig == nil {
5759
return nil, errors.New("TLSConfig is not set")
5860
}
5961
a, err := net.ResolveUDPAddr(network, address)
@@ -68,21 +70,25 @@ func Listen(network, address string, config *Config) (*Endpoint, error) {
6870
if err != nil {
6971
return nil, err
7072
}
71-
return newEndpoint(pc, config, nil)
73+
return newEndpoint(pc, listenConfig, nil)
7274
}
7375

7476
func newEndpoint(pc packetConn, config *Config, hooks endpointTestHooks) (*Endpoint, error) {
7577
e := &Endpoint{
76-
config: config,
77-
packetConn: pc,
78-
testHooks: hooks,
79-
conns: make(map[*Conn]struct{}),
80-
acceptQueue: newQueue[*Conn](),
81-
closec: make(chan struct{}),
82-
}
83-
e.resetGen.init(config.StatelessResetKey)
78+
listenConfig: config,
79+
packetConn: pc,
80+
testHooks: hooks,
81+
conns: make(map[*Conn]struct{}),
82+
acceptQueue: newQueue[*Conn](),
83+
closec: make(chan struct{}),
84+
}
85+
var statelessResetKey [32]byte
86+
if config != nil {
87+
statelessResetKey = config.StatelessResetKey
88+
}
89+
e.resetGen.init(statelessResetKey)
8490
e.connsMap.init()
85-
if config.RequireAddressValidation {
91+
if config != nil && config.RequireAddressValidation {
8692
if err := e.retry.init(); err != nil {
8793
return nil, err
8894
}
@@ -141,14 +147,15 @@ func (e *Endpoint) Accept(ctx context.Context) (*Conn, error) {
141147
}
142148

143149
// Dial creates and returns a connection to a network address.
144-
func (e *Endpoint) Dial(ctx context.Context, network, address string) (*Conn, error) {
150+
// The config cannot be nil.
151+
func (e *Endpoint) Dial(ctx context.Context, network, address string, config *Config) (*Conn, error) {
145152
u, err := net.ResolveUDPAddr(network, address)
146153
if err != nil {
147154
return nil, err
148155
}
149156
addr := u.AddrPort()
150157
addr = netip.AddrPortFrom(addr.Addr().Unmap(), addr.Port())
151-
c, err := e.newConn(time.Now(), clientSide, newServerConnIDs{}, addr)
158+
c, err := e.newConn(time.Now(), config, clientSide, newServerConnIDs{}, address, addr)
152159
if err != nil {
153160
return nil, err
154161
}
@@ -159,13 +166,13 @@ func (e *Endpoint) Dial(ctx context.Context, network, address string) (*Conn, er
159166
return c, nil
160167
}
161168

162-
func (e *Endpoint) newConn(now time.Time, side connSide, cids newServerConnIDs, peerAddr netip.AddrPort) (*Conn, error) {
169+
func (e *Endpoint) newConn(now time.Time, config *Config, side connSide, cids newServerConnIDs, peerHostname string, peerAddr netip.AddrPort) (*Conn, error) {
163170
e.connsMu.Lock()
164171
defer e.connsMu.Unlock()
165172
if e.closing {
166173
return nil, errors.New("endpoint closed")
167174
}
168-
c, err := newConn(now, side, cids, peerAddr, e.config, e)
175+
c, err := newConn(now, side, cids, peerHostname, peerAddr, config, e)
169176
if err != nil {
170177
return nil, err
171178
}
@@ -288,11 +295,15 @@ func (e *Endpoint) handleUnknownDestinationDatagram(m *datagram) {
288295
// https://www.rfc-editor.org/rfc/rfc9000#section-10.3-16
289296
return
290297
}
298+
if e.listenConfig == nil {
299+
// We are not configured to accept connections.
300+
return
301+
}
291302
cids := newServerConnIDs{
292303
srcConnID: p.srcConnID,
293304
dstConnID: p.dstConnID,
294305
}
295-
if e.config.RequireAddressValidation {
306+
if e.listenConfig.RequireAddressValidation {
296307
var ok bool
297308
cids.retrySrcConnID = p.dstConnID
298309
cids.originalDstConnID, ok = e.validateInitialAddress(now, p, m.peerAddr)
@@ -303,7 +314,7 @@ func (e *Endpoint) handleUnknownDestinationDatagram(m *datagram) {
303314
cids.originalDstConnID = p.dstConnID
304315
}
305316
var err error
306-
c, err := e.newConn(now, serverSide, cids, m.peerAddr)
317+
c, err := e.newConn(now, e.listenConfig, serverSide, cids, "", m.peerAddr)
307318
if err != nil {
308319
// The accept queue is probably full.
309320
// We could send a CONNECTION_CLOSE to the peer to reject the connection.

internal/quic/endpoint_test.go

+20-11
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,8 @@ func newLocalConnPair(t testing.TB, conf1, conf2 *Config) (clientConn, serverCon
6767
ctx := context.Background()
6868
e1 := newLocalEndpoint(t, serverSide, conf1)
6969
e2 := newLocalEndpoint(t, clientSide, conf2)
70-
c2, err := e2.Dial(ctx, "udp", e1.LocalAddr().String())
70+
conf2 = makeTestConfig(conf2, clientSide)
71+
c2, err := e2.Dial(ctx, "udp", e1.LocalAddr().String(), conf2)
7172
if err != nil {
7273
t.Fatal(err)
7374
}
@@ -80,9 +81,24 @@ func newLocalConnPair(t testing.TB, conf1, conf2 *Config) (clientConn, serverCon
8081

8182
func newLocalEndpoint(t testing.TB, side connSide, conf *Config) *Endpoint {
8283
t.Helper()
84+
conf = makeTestConfig(conf, side)
85+
e, err := Listen("udp", "127.0.0.1:0", conf)
86+
if err != nil {
87+
t.Fatal(err)
88+
}
89+
t.Cleanup(func() {
90+
e.Close(canceledContext())
91+
})
92+
return e
93+
}
94+
95+
func makeTestConfig(conf *Config, side connSide) *Config {
96+
if conf == nil {
97+
return nil
98+
}
99+
newConf := *conf
100+
conf = &newConf
83101
if conf.TLSConfig == nil {
84-
newConf := *conf
85-
conf = &newConf
86102
conf.TLSConfig = newTestTLSConfig(side)
87103
}
88104
if conf.QLogLogger == nil {
@@ -91,14 +107,7 @@ func newLocalEndpoint(t testing.TB, side connSide, conf *Config) *Endpoint {
91107
Dir: *qlogdir,
92108
}))
93109
}
94-
e, err := Listen("udp", "127.0.0.1:0", conf)
95-
if err != nil {
96-
t.Fatal(err)
97-
}
98-
t.Cleanup(func() {
99-
e.Close(canceledContext())
100-
})
101-
return e
110+
return conf
102111
}
103112

104113
type testEndpoint struct {

internal/quic/tls.go

+12-2
Original file line numberDiff line numberDiff line change
@@ -11,14 +11,24 @@ import (
1111
"crypto/tls"
1212
"errors"
1313
"fmt"
14+
"net"
1415
"time"
1516
)
1617

1718
// startTLS starts the TLS handshake.
18-
func (c *Conn) startTLS(now time.Time, initialConnID []byte, params transportParameters) error {
19+
func (c *Conn) startTLS(now time.Time, initialConnID []byte, peerHostname string, params transportParameters) error {
20+
tlsConfig := c.config.TLSConfig
21+
if a, _, err := net.SplitHostPort(peerHostname); err == nil {
22+
peerHostname = a
23+
}
24+
if tlsConfig.ServerName == "" && peerHostname != "" {
25+
tlsConfig = tlsConfig.Clone()
26+
tlsConfig.ServerName = peerHostname
27+
}
28+
1929
c.keysInitial = initialKeys(initialConnID, c.side)
2030

21-
qconfig := &tls.QUICConfig{TLSConfig: c.config.TLSConfig}
31+
qconfig := &tls.QUICConfig{TLSConfig: tlsConfig}
2232
if c.side == clientSide {
2333
c.tls = tls.QUICClient(qconfig)
2434
} else {

0 commit comments

Comments
 (0)