mirror of
https://github.com/cloudflare/cloudflared
synced 2026-06-08 13:33:07 +00:00
Compare commits
12 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 4ff215ee8c | |||
| 17e3073a17 | |||
| 071d595371 | |||
| b6c85401a5 | |||
| 3ef3e7a99a | |||
| 12ad264eb3 | |||
| 1082ac1c36 | |||
| 5f6e867685 | |||
| 78a9454023 | |||
| ca85df10ff | |||
| b8333b44a2 | |||
| e49a7a4389 |
@@ -1,5 +1,10 @@
|
||||
**Experimental**: This is a new format for release notes. The format and availability is subject to change.
|
||||
|
||||
## 2021.8.2
|
||||
### Improvements
|
||||
- Because Equinox os shutting down, all cloudflared releases are now present [here](https://github.com/cloudflare/cloudflared/releases).
|
||||
[Equinox](https://dl.equinox.io/cloudflare/cloudflared/stable) will no longer receive updates.
|
||||
|
||||
## 2021.8.0
|
||||
### Bug fixes
|
||||
- Prevents tunnel from accidentally running when only proxy-dns should run.
|
||||
|
||||
@@ -26,16 +26,6 @@ PACKAGE_DIR := $(CURDIR)/packaging
|
||||
INSTALL_BINDIR := /usr/bin/
|
||||
MAN_DIR := /usr/share/man/man1/
|
||||
|
||||
EQUINOX_FLAGS = --version="$(VERSION)" \
|
||||
--platforms="$(EQUINOX_BUILD_PLATFORMS)" \
|
||||
--app="$(EQUINOX_APP_ID)" \
|
||||
--token="$(EQUINOX_TOKEN)" \
|
||||
--channel="$(EQUINOX_CHANNEL)"
|
||||
|
||||
ifeq ($(EQUINOX_IS_DRAFT), true)
|
||||
EQUINOX_FLAGS := --draft $(EQUINOX_FLAGS)
|
||||
endif
|
||||
|
||||
LOCAL_ARCH ?= $(shell uname -m)
|
||||
ifneq ($(GOARCH),)
|
||||
TARGET_ARCH ?= $(GOARCH)
|
||||
@@ -231,10 +221,6 @@ homebrew-upload: cloudflared-darwin-amd64.tgz
|
||||
homebrew-release: homebrew-upload
|
||||
./publish-homebrew-formula.sh cloudflared-darwin-amd64.tgz $(VERSION) homebrew-cloudflare
|
||||
|
||||
.PHONY: release
|
||||
release: bin/equinox
|
||||
bin/equinox release $(EQUINOX_FLAGS) -- $(VERSION_FLAGS) $(IMPORT_PATH)/cmd/cloudflared
|
||||
|
||||
.PHONY: github-release
|
||||
github-release: cloudflared
|
||||
python3 github_release.py --path $(EXECUTABLE_PATH) --release-version $(VERSION)
|
||||
@@ -252,11 +238,6 @@ github-mac-upload:
|
||||
python3 github_release.py --path artifacts/cloudflared-darwin-amd64.tgz --release-version $(VERSION) --name cloudflared-darwin-amd64.tgz
|
||||
python3 github_release.py --path artifacts/cloudflared-amd64.pkg --release-version $(VERSION) --name cloudflared-amd64.pkg
|
||||
|
||||
bin/equinox:
|
||||
mkdir -p bin
|
||||
curl -s https://bin.equinox.io/c/75JtLRTsJ3n/release-tool-beta-$(EQUINOX_PLATFORM).tgz | tar xz -C bin/
|
||||
|
||||
|
||||
.PHONY: tunnelrpc-deps
|
||||
tunnelrpc-deps:
|
||||
which capnp # https://capnproto.org/install.html
|
||||
|
||||
@@ -1,3 +1,17 @@
|
||||
2021.8.3
|
||||
- 2021-08-23 TUN-4889: Add back appendtagheaders function
|
||||
- 2021-08-21 TUN-4940: Fix cloudflared not picking up correct NextProtos for quic
|
||||
- 2021-08-21 TUN-4613: Add a no-op protocol version slot
|
||||
- 2021-08-13 TUN-4922: Downgrade quic-go library to 0.20.0
|
||||
- 2021-08-17 TUN-4866: Add Control Stream for QUIC
|
||||
- 2021-08-17 TUN-4927: Parameterize region in edge discovery code
|
||||
- 2021-08-06 TUN-4602: Added UDP resolves to Edge discovery
|
||||
|
||||
2021.8.2
|
||||
- 2021-08-03 TUN-4597: Added HTTPProxy for QUIC
|
||||
- 2021-08-04 TUN-4795: Remove Equinox releases
|
||||
- 2021-08-09 TUN-4911: Append Environment variable to Path instead of overwriting it
|
||||
|
||||
2021.8.1
|
||||
- 2021-08-02 TUN-4855: Added CHANGES.md for release 2021.8.0
|
||||
- 2021-08-03 TUN-4597: Add a QUIC server skeleton
|
||||
|
||||
@@ -98,56 +98,6 @@ stretch: &stretch
|
||||
- export GOARCH=amd64
|
||||
- export FIPS=true
|
||||
- make publish-deb
|
||||
release-linux-amd64:
|
||||
build_dir: *build_dir
|
||||
builddeps:
|
||||
- *pinned_go_fips
|
||||
- build-essential
|
||||
post-cache:
|
||||
- export GOOS=linux
|
||||
- export GOARCH=amd64
|
||||
- export FIPS=true
|
||||
- make release
|
||||
release-linux-armv6:
|
||||
build_dir: *build_dir
|
||||
builddeps:
|
||||
- *pinned_go
|
||||
- crossbuild-essential-armhf
|
||||
- gcc-arm-linux-gnueabihf
|
||||
post-cache:
|
||||
- export GOOS=linux
|
||||
- export GOARCH=arm
|
||||
- export CC=arm-linux-gnueabihf-gcc
|
||||
- make release
|
||||
release-linux-386:
|
||||
build_dir: *build_dir
|
||||
builddeps:
|
||||
- *pinned_go
|
||||
- gcc-multilib
|
||||
post-cache:
|
||||
- export GOOS=linux
|
||||
- export GOARCH=386
|
||||
- make release
|
||||
release-windows-amd64:
|
||||
build_dir: *build_dir
|
||||
builddeps:
|
||||
- *pinned_go
|
||||
- gcc-mingw-w64
|
||||
post-cache:
|
||||
- export GOOS=windows
|
||||
- export GOARCH=amd64
|
||||
- export CC=x86_64-w64-mingw32-gcc
|
||||
- make release
|
||||
release-windows-386:
|
||||
build_dir: *build_dir
|
||||
builddeps:
|
||||
- *pinned_go
|
||||
- gcc-mingw-w64
|
||||
post-cache:
|
||||
- export GOOS=windows
|
||||
- export GOARCH=386
|
||||
- export CC=i686-w64-mingw32-gcc-win32
|
||||
- make release
|
||||
github-release-macos-amd64:
|
||||
build_dir: *build_dir
|
||||
builddeps:
|
||||
|
||||
+2
-2
@@ -46,8 +46,8 @@
|
||||
Name="PATH"
|
||||
Value="[INSTALLDIR]."
|
||||
Permanent="no"
|
||||
Part="all"
|
||||
Action="set"
|
||||
Part="last"
|
||||
Action="create"
|
||||
System="yes" />
|
||||
</Component>
|
||||
</Directory>
|
||||
|
||||
@@ -248,10 +248,17 @@ func prepareTunnelConfig(
|
||||
|
||||
edgeTLSConfigs := make(map[connection.Protocol]*tls.Config, len(connection.ProtocolList))
|
||||
for _, p := range connection.ProtocolList {
|
||||
edgeTLSConfig, err := tlsconfig.CreateTunnelConfig(c, p.ServerName())
|
||||
tlsSettings := p.TLSSettings()
|
||||
if tlsSettings == nil {
|
||||
return nil, ingress.Ingress{}, fmt.Errorf("%s has unknown TLS settings", p)
|
||||
}
|
||||
edgeTLSConfig, err := tlsconfig.CreateTunnelConfig(c, tlsSettings.ServerName)
|
||||
if err != nil {
|
||||
return nil, ingress.Ingress{}, errors.Wrap(err, "unable to create TLS config to connect with edge")
|
||||
}
|
||||
if len(tlsSettings.NextProtos) > 0 {
|
||||
edgeTLSConfig.NextProtos = tlsSettings.NextProtos
|
||||
}
|
||||
edgeTLSConfigs[p] = edgeTLSConfig
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,89 @@
|
||||
package connection
|
||||
|
||||
import (
|
||||
"context"
|
||||
"io"
|
||||
"time"
|
||||
|
||||
"github.com/rs/zerolog"
|
||||
|
||||
tunnelpogs "github.com/cloudflare/cloudflared/tunnelrpc/pogs"
|
||||
)
|
||||
|
||||
// RPCClientFunc derives a named tunnel rpc client that can then be used to register and unregister connections.
|
||||
type RPCClientFunc func(context.Context, io.ReadWriteCloser, *zerolog.Logger) NamedTunnelRPCClient
|
||||
|
||||
type controlStream struct {
|
||||
observer *Observer
|
||||
|
||||
connectedFuse ConnectedFuse
|
||||
namedTunnelConfig *NamedTunnelConfig
|
||||
connIndex uint8
|
||||
|
||||
newRPCClientFunc RPCClientFunc
|
||||
|
||||
gracefulShutdownC <-chan struct{}
|
||||
gracePeriod time.Duration
|
||||
stoppedGracefully bool
|
||||
}
|
||||
|
||||
// ControlStreamHandler registers connections with origintunneld and initiates graceful shutdown.
|
||||
type ControlStreamHandler interface {
|
||||
ServeControlStream(ctx context.Context, rw io.ReadWriteCloser, connOptions *tunnelpogs.ConnectionOptions) error
|
||||
IsStopped() bool
|
||||
}
|
||||
|
||||
// NewControlStream returns a new instance of ControlStreamHandler
|
||||
func NewControlStream(
|
||||
observer *Observer,
|
||||
connectedFuse ConnectedFuse,
|
||||
namedTunnelConfig *NamedTunnelConfig,
|
||||
connIndex uint8,
|
||||
newRPCClientFunc RPCClientFunc,
|
||||
gracefulShutdownC <-chan struct{},
|
||||
gracePeriod time.Duration,
|
||||
) ControlStreamHandler {
|
||||
if newRPCClientFunc == nil {
|
||||
newRPCClientFunc = newRegistrationRPCClient
|
||||
}
|
||||
return &controlStream{
|
||||
observer: observer,
|
||||
connectedFuse: connectedFuse,
|
||||
namedTunnelConfig: namedTunnelConfig,
|
||||
newRPCClientFunc: newRPCClientFunc,
|
||||
connIndex: connIndex,
|
||||
gracefulShutdownC: gracefulShutdownC,
|
||||
gracePeriod: gracePeriod,
|
||||
}
|
||||
}
|
||||
|
||||
func (c *controlStream) ServeControlStream(
|
||||
ctx context.Context,
|
||||
rw io.ReadWriteCloser,
|
||||
connOptions *tunnelpogs.ConnectionOptions,
|
||||
) error {
|
||||
rpcClient := c.newRPCClientFunc(ctx, rw, c.observer.log)
|
||||
defer rpcClient.Close()
|
||||
|
||||
if err := rpcClient.RegisterConnection(ctx, c.namedTunnelConfig, connOptions, c.connIndex, c.observer); err != nil {
|
||||
return err
|
||||
}
|
||||
c.connectedFuse.Connected()
|
||||
|
||||
// wait for connection termination or start of graceful shutdown
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
break
|
||||
case <-c.gracefulShutdownC:
|
||||
c.stoppedGracefully = true
|
||||
}
|
||||
|
||||
c.observer.sendUnregisteringEvent(c.connIndex)
|
||||
rpcClient.GracefulShutdown(ctx, c.gracePeriod)
|
||||
c.observer.log.Info().Uint8(LogFieldConnIndex, c.connIndex).Msg("Unregistered tunnel connection")
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *controlStream) IsStopped() bool {
|
||||
return c.stoppedGracefully
|
||||
}
|
||||
+21
-52
@@ -30,52 +30,44 @@ var errEdgeConnectionClosed = fmt.Errorf("connection with edge closed")
|
||||
// HTTP2Connection represents a net.Conn that uses HTTP2 frames to proxy traffic from the edge to cloudflared on the
|
||||
// origin.
|
||||
type HTTP2Connection struct {
|
||||
conn net.Conn
|
||||
server *http2.Server
|
||||
config *Config
|
||||
namedTunnel *NamedTunnelConfig
|
||||
connOptions *tunnelpogs.ConnectionOptions
|
||||
observer *Observer
|
||||
connIndexStr string
|
||||
connIndex uint8
|
||||
conn net.Conn
|
||||
server *http2.Server
|
||||
config *Config
|
||||
connOptions *tunnelpogs.ConnectionOptions
|
||||
observer *Observer
|
||||
connIndex uint8
|
||||
// newRPCClientFunc allows us to mock RPCs during testing
|
||||
newRPCClientFunc func(context.Context, io.ReadWriteCloser, *zerolog.Logger) NamedTunnelRPCClient
|
||||
|
||||
log *zerolog.Logger
|
||||
activeRequestsWG sync.WaitGroup
|
||||
connectedFuse ConnectedFuse
|
||||
gracefulShutdownC <-chan struct{}
|
||||
stoppedGracefully bool
|
||||
controlStreamErr error // result of running control stream handler
|
||||
log *zerolog.Logger
|
||||
activeRequestsWG sync.WaitGroup
|
||||
controlStreamHandler ControlStreamHandler
|
||||
stoppedGracefully bool
|
||||
controlStreamErr error // result of running control stream handler
|
||||
}
|
||||
|
||||
// NewHTTP2Connection returns a new instance of HTTP2Connection.
|
||||
func NewHTTP2Connection(
|
||||
conn net.Conn,
|
||||
config *Config,
|
||||
namedTunnelConfig *NamedTunnelConfig,
|
||||
connOptions *tunnelpogs.ConnectionOptions,
|
||||
observer *Observer,
|
||||
connIndex uint8,
|
||||
connectedFuse ConnectedFuse,
|
||||
controlStreamHandler ControlStreamHandler,
|
||||
log *zerolog.Logger,
|
||||
gracefulShutdownC <-chan struct{},
|
||||
) *HTTP2Connection {
|
||||
return &HTTP2Connection{
|
||||
conn: conn,
|
||||
server: &http2.Server{
|
||||
MaxConcurrentStreams: math.MaxUint32,
|
||||
},
|
||||
config: config,
|
||||
namedTunnel: namedTunnelConfig,
|
||||
connOptions: connOptions,
|
||||
observer: observer,
|
||||
connIndexStr: uint8ToString(connIndex),
|
||||
connIndex: connIndex,
|
||||
newRPCClientFunc: newRegistrationRPCClient,
|
||||
connectedFuse: connectedFuse,
|
||||
log: log,
|
||||
gracefulShutdownC: gracefulShutdownC,
|
||||
config: config,
|
||||
connOptions: connOptions,
|
||||
observer: observer,
|
||||
connIndex: connIndex,
|
||||
newRPCClientFunc: newRegistrationRPCClient,
|
||||
controlStreamHandler: controlStreamHandler,
|
||||
log: log,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -91,7 +83,7 @@ func (c *HTTP2Connection) Serve(ctx context.Context) error {
|
||||
})
|
||||
|
||||
switch {
|
||||
case c.stoppedGracefully:
|
||||
case c.controlStreamHandler.IsStopped():
|
||||
return nil
|
||||
case c.controlStreamErr != nil:
|
||||
return c.controlStreamErr
|
||||
@@ -116,7 +108,7 @@ func (c *HTTP2Connection) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
switch connType {
|
||||
case TypeControlStream:
|
||||
if err := c.serveControlStream(r.Context(), respWriter); err != nil {
|
||||
if err := c.controlStreamHandler.ServeControlStream(r.Context(), respWriter, c.connOptions); err != nil {
|
||||
c.controlStreamErr = err
|
||||
c.log.Error().Err(err)
|
||||
respWriter.WriteErrorResponse()
|
||||
@@ -154,29 +146,6 @@ func (c *HTTP2Connection) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
}
|
||||
|
||||
func (c *HTTP2Connection) serveControlStream(ctx context.Context, respWriter *http2RespWriter) error {
|
||||
rpcClient := c.newRPCClientFunc(ctx, respWriter, c.observer.log)
|
||||
defer rpcClient.Close()
|
||||
|
||||
if err := rpcClient.RegisterConnection(ctx, c.namedTunnel, c.connOptions, c.connIndex, c.observer); err != nil {
|
||||
return err
|
||||
}
|
||||
c.connectedFuse.Connected()
|
||||
|
||||
// wait for connection termination or start of graceful shutdown
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
break
|
||||
case <-c.gracefulShutdownC:
|
||||
c.stoppedGracefully = true
|
||||
}
|
||||
|
||||
c.observer.sendUnregisteringEvent(c.connIndex)
|
||||
rpcClient.GracefulShutdown(ctx, c.config.GracePeriod)
|
||||
c.observer.log.Info().Uint8(LogFieldConnIndex, c.connIndex).Msg("Unregistered tunnel connection")
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *HTTP2Connection) close() {
|
||||
// Wait for all serve HTTP handlers to return
|
||||
c.activeRequestsWG.Wait()
|
||||
|
||||
+51
-10
@@ -30,16 +30,24 @@ func newTestHTTP2Connection() (*HTTP2Connection, net.Conn) {
|
||||
edgeConn, originConn := net.Pipe()
|
||||
var connIndex = uint8(0)
|
||||
log := zerolog.Nop()
|
||||
obs := NewObserver(&log, &log, false)
|
||||
controlStream := NewControlStream(
|
||||
obs,
|
||||
mockConnectedFuse{},
|
||||
&NamedTunnelConfig{},
|
||||
connIndex,
|
||||
nil,
|
||||
nil,
|
||||
1*time.Second,
|
||||
)
|
||||
return NewHTTP2Connection(
|
||||
originConn,
|
||||
testConfig,
|
||||
&NamedTunnelConfig{},
|
||||
&pogs.ConnectionOptions{},
|
||||
NewObserver(&log, &log, false),
|
||||
obs,
|
||||
connIndex,
|
||||
mockConnectedFuse{},
|
||||
controlStream,
|
||||
&log,
|
||||
nil,
|
||||
), edgeConn
|
||||
}
|
||||
|
||||
@@ -225,7 +233,18 @@ func TestServeControlStream(t *testing.T) {
|
||||
registered: make(chan struct{}),
|
||||
unregistered: make(chan struct{}),
|
||||
}
|
||||
http2Conn.newRPCClientFunc = rpcClientFactory.newMockRPCClient
|
||||
|
||||
obs := NewObserver(&log, &log, false)
|
||||
controlStream := NewControlStream(
|
||||
obs,
|
||||
mockConnectedFuse{},
|
||||
&NamedTunnelConfig{},
|
||||
1,
|
||||
rpcClientFactory.newMockRPCClient,
|
||||
nil,
|
||||
1*time.Second,
|
||||
)
|
||||
http2Conn.controlStreamHandler = controlStream
|
||||
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
var wg sync.WaitGroup
|
||||
@@ -264,7 +283,18 @@ func TestFailRegistration(t *testing.T) {
|
||||
registered: make(chan struct{}),
|
||||
unregistered: make(chan struct{}),
|
||||
}
|
||||
http2Conn.newRPCClientFunc = rpcClientFactory.newMockRPCClient
|
||||
|
||||
obs := NewObserver(&log, &log, false)
|
||||
controlStream := NewControlStream(
|
||||
obs,
|
||||
mockConnectedFuse{},
|
||||
&NamedTunnelConfig{},
|
||||
http2Conn.connIndex,
|
||||
rpcClientFactory.newMockRPCClient,
|
||||
nil,
|
||||
1*time.Second,
|
||||
)
|
||||
http2Conn.controlStreamHandler = controlStream
|
||||
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
var wg sync.WaitGroup
|
||||
@@ -297,10 +327,21 @@ func TestGracefulShutdownHTTP2(t *testing.T) {
|
||||
unregistered: make(chan struct{}),
|
||||
}
|
||||
events := &eventCollectorSink{}
|
||||
http2Conn.newRPCClientFunc = rpcClientFactory.newMockRPCClient
|
||||
http2Conn.observer.RegisterSink(events)
|
||||
|
||||
shutdownC := make(chan struct{})
|
||||
http2Conn.gracefulShutdownC = shutdownC
|
||||
obs := NewObserver(&log, &log, false)
|
||||
obs.RegisterSink(events)
|
||||
controlStream := NewControlStream(
|
||||
obs,
|
||||
mockConnectedFuse{},
|
||||
&NamedTunnelConfig{},
|
||||
http2Conn.connIndex,
|
||||
rpcClientFactory.newMockRPCClient,
|
||||
shutdownC,
|
||||
1*time.Second,
|
||||
)
|
||||
|
||||
http2Conn.controlStreamHandler = controlStream
|
||||
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
var wg sync.WaitGroup
|
||||
@@ -339,7 +380,7 @@ func TestGracefulShutdownHTTP2(t *testing.T) {
|
||||
case <-time.Tick(time.Second):
|
||||
t.Fatal("timeout out waiting for unregistered signal")
|
||||
}
|
||||
assert.True(t, http2Conn.stoppedGracefully)
|
||||
assert.True(t, controlStream.IsStopped())
|
||||
|
||||
cancel()
|
||||
wg.Wait()
|
||||
|
||||
+41
-12
@@ -15,33 +15,29 @@ const (
|
||||
edgeH2muxTLSServerName = "cftunnel.com"
|
||||
// edgeH2TLSServerName is the server name to establish http2 connection with edge
|
||||
edgeH2TLSServerName = "h2.cftunnel.com"
|
||||
// edgeQUICServerName is the server name to establish quic connection with edge.
|
||||
edgeQUICServerName = "quic.cftunnel.com"
|
||||
// threshold to switch back to h2mux when the user intentionally pick --protocol http2
|
||||
explicitHTTP2FallbackThreshold = -1
|
||||
autoSelectFlag = "auto"
|
||||
)
|
||||
|
||||
var (
|
||||
ProtocolList = []Protocol{H2mux, HTTP2}
|
||||
// ProtocolList represents a list of supported protocols for communication with the edge.
|
||||
ProtocolList = []Protocol{H2mux, HTTP2, QUIC}
|
||||
)
|
||||
|
||||
type Protocol int64
|
||||
|
||||
const (
|
||||
// H2mux protocol can be used both with Classic and Named Tunnels. .
|
||||
H2mux Protocol = iota
|
||||
// HTTP2 is used only with named tunnels. It's more efficient than H2mux for L4 proxying.
|
||||
HTTP2
|
||||
// QUIC is used only with named tunnels.
|
||||
QUIC
|
||||
)
|
||||
|
||||
func (p Protocol) ServerName() string {
|
||||
switch p {
|
||||
case H2mux:
|
||||
return edgeH2muxTLSServerName
|
||||
case HTTP2:
|
||||
return edgeH2TLSServerName
|
||||
default:
|
||||
return ""
|
||||
}
|
||||
}
|
||||
|
||||
// Fallback returns the fallback protocol and whether the protocol has a fallback
|
||||
func (p Protocol) fallback() (Protocol, bool) {
|
||||
switch p {
|
||||
@@ -49,6 +45,8 @@ func (p Protocol) fallback() (Protocol, bool) {
|
||||
return 0, false
|
||||
case HTTP2:
|
||||
return H2mux, true
|
||||
case QUIC:
|
||||
return HTTP2, true
|
||||
default:
|
||||
return 0, false
|
||||
}
|
||||
@@ -60,11 +58,38 @@ func (p Protocol) String() string {
|
||||
return "h2mux"
|
||||
case HTTP2:
|
||||
return "http2"
|
||||
case QUIC:
|
||||
return "quic"
|
||||
default:
|
||||
return fmt.Sprintf("unknown protocol")
|
||||
}
|
||||
}
|
||||
|
||||
func (p Protocol) TLSSettings() *TLSSettings {
|
||||
switch p {
|
||||
case H2mux:
|
||||
return &TLSSettings{
|
||||
ServerName: edgeH2muxTLSServerName,
|
||||
}
|
||||
case HTTP2:
|
||||
return &TLSSettings{
|
||||
ServerName: edgeH2TLSServerName,
|
||||
}
|
||||
case QUIC:
|
||||
return &TLSSettings{
|
||||
ServerName: edgeQUICServerName,
|
||||
NextProtos: []string{"argotunnel"},
|
||||
}
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
type TLSSettings struct {
|
||||
ServerName string
|
||||
NextProtos []string
|
||||
}
|
||||
|
||||
type ProtocolSelector interface {
|
||||
Current() Protocol
|
||||
Fallback() (Protocol, bool)
|
||||
@@ -184,6 +209,10 @@ func NewProtocolSelector(
|
||||
return newAutoProtocolSelector(HTTP2, explicitHTTP2FallbackThreshold, fetchFunc, ttl, log), nil
|
||||
}
|
||||
|
||||
if protocolFlag == QUIC.String() {
|
||||
return newAutoProtocolSelector(QUIC, explicitHTTP2FallbackThreshold, fetchFunc, ttl, log), nil
|
||||
}
|
||||
|
||||
if protocolFlag != autoSelectFlag {
|
||||
return nil, fmt.Errorf("Unknown protocol %s, %s", protocolFlag, AvailableProtocolFlagMessage)
|
||||
}
|
||||
|
||||
+104
-11
@@ -3,19 +3,37 @@ package connection
|
||||
import (
|
||||
"context"
|
||||
"crypto/tls"
|
||||
"fmt"
|
||||
"io"
|
||||
"net"
|
||||
"net/http"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/lucas-clemente/quic-go"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/rs/zerolog"
|
||||
|
||||
quicpogs "github.com/cloudflare/cloudflared/quic"
|
||||
tunnelpogs "github.com/cloudflare/cloudflared/tunnelrpc/pogs"
|
||||
)
|
||||
|
||||
const (
|
||||
// HTTPHeaderKey is used to get or set http headers in QUIC ALPN if the underlying proxy connection type is HTTP.
|
||||
HTTPHeaderKey = "HttpHeader"
|
||||
// HTTPMethodKey is used to get or set http method in QUIC ALPN if the underlying proxy connection type is HTTP.
|
||||
HTTPMethodKey = "HttpMethod"
|
||||
// HTTPHostKey is used to get or set http Method in QUIC ALPN if the underlying proxy connection type is HTTP.
|
||||
HTTPHostKey = "HttpHost"
|
||||
)
|
||||
|
||||
// QUICConnection represents the type that facilitates Proxying via QUIC streams.
|
||||
type QUICConnection struct {
|
||||
session quic.Session
|
||||
logger zerolog.Logger
|
||||
session quic.Session
|
||||
logger *zerolog.Logger
|
||||
httpProxy OriginProxy
|
||||
gracefulShutdownC <-chan struct{}
|
||||
stoppedGracefully bool
|
||||
}
|
||||
|
||||
// NewQUICConnection returns a new instance of QUICConnection.
|
||||
@@ -24,18 +42,27 @@ func NewQUICConnection(
|
||||
quicConfig *quic.Config,
|
||||
edgeAddr net.Addr,
|
||||
tlsConfig *tls.Config,
|
||||
logger zerolog.Logger,
|
||||
httpProxy OriginProxy,
|
||||
connOptions *tunnelpogs.ConnectionOptions,
|
||||
controlStreamHandler ControlStreamHandler,
|
||||
observer *Observer,
|
||||
) (*QUICConnection, error) {
|
||||
session, err := quic.DialAddr(edgeAddr.String(), tlsConfig, quicConfig)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to dial to edge")
|
||||
}
|
||||
|
||||
//TODO: RegisterConnectionRPC here.
|
||||
registrationStream, err := session.OpenStream()
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to open a registration stream")
|
||||
}
|
||||
|
||||
go controlStreamHandler.ServeControlStream(ctx, registrationStream, connOptions)
|
||||
|
||||
return &QUICConnection{
|
||||
session: session,
|
||||
logger: logger,
|
||||
session: session,
|
||||
httpProxy: httpProxy,
|
||||
logger: observer.log,
|
||||
}, nil
|
||||
}
|
||||
|
||||
@@ -58,7 +85,7 @@ func (q *QUICConnection) Serve(ctx context.Context) error {
|
||||
}
|
||||
}
|
||||
|
||||
// Close calls this to close the QuicConnection stream.
|
||||
// Close closes the session with no errors specified.
|
||||
func (q *QUICConnection) Close() {
|
||||
q.session.CloseWithError(0, "")
|
||||
}
|
||||
@@ -71,14 +98,80 @@ func (q *QUICConnection) handleStream(stream quic.Stream) error {
|
||||
|
||||
switch connectRequest.Type {
|
||||
case quicpogs.ConnectionTypeHTTP, quicpogs.ConnectionTypeWebsocket:
|
||||
// Temporary dummy code for the unit test.
|
||||
if err := quicpogs.WriteConnectResponseData(stream, nil, quicpogs.Metadata{Key: "HTTPStatus", Val: "200"}); err != nil {
|
||||
req, err := buildHTTPRequest(connectRequest, stream)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
stream.Write([]byte("OK"))
|
||||
w := newHTTPResponseAdapter(stream)
|
||||
return q.httpProxy.ProxyHTTP(w, req, connectRequest.Type == quicpogs.ConnectionTypeWebsocket)
|
||||
case quicpogs.ConnectionTypeTCP:
|
||||
|
||||
rwa := &streamReadWriteAcker{
|
||||
ReadWriter: stream,
|
||||
}
|
||||
return q.httpProxy.ProxyTCP(context.Background(), rwa, &TCPRequest{Dest: connectRequest.Dest})
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// streamReadWriteAcker is a light wrapper over QUIC streams with a callback to send response back to
|
||||
// the client.
|
||||
type streamReadWriteAcker struct {
|
||||
io.ReadWriter
|
||||
}
|
||||
|
||||
// AckConnection acks response back to the proxy.
|
||||
func (s *streamReadWriteAcker) AckConnection() error {
|
||||
return quicpogs.WriteConnectResponseData(s, nil)
|
||||
}
|
||||
|
||||
// httpResponseAdapter translates responses written by the HTTP Proxy into ones that can be used in QUIC.
|
||||
type httpResponseAdapter struct {
|
||||
io.Writer
|
||||
}
|
||||
|
||||
func newHTTPResponseAdapter(w io.Writer) httpResponseAdapter {
|
||||
return httpResponseAdapter{w}
|
||||
}
|
||||
|
||||
func (hrw httpResponseAdapter) WriteRespHeaders(status int, header http.Header) error {
|
||||
metadata := make([]quicpogs.Metadata, 0)
|
||||
metadata = append(metadata, quicpogs.Metadata{Key: "HttpStatus", Val: strconv.Itoa(status)})
|
||||
for k, vv := range header {
|
||||
for _, v := range vv {
|
||||
httpHeaderKey := fmt.Sprintf("%s:%s", HTTPHeaderKey, k)
|
||||
metadata = append(metadata, quicpogs.Metadata{Key: httpHeaderKey, Val: v})
|
||||
}
|
||||
}
|
||||
return quicpogs.WriteConnectResponseData(hrw, nil, metadata...)
|
||||
}
|
||||
|
||||
func (hrw httpResponseAdapter) WriteErrorResponse(err error) {
|
||||
quicpogs.WriteConnectResponseData(hrw, err, quicpogs.Metadata{Key: "HttpStatus", Val: strconv.Itoa(http.StatusBadGateway)})
|
||||
}
|
||||
|
||||
func buildHTTPRequest(connectRequest *quicpogs.ConnectRequest, body io.Reader) (*http.Request, error) {
|
||||
metadata := connectRequest.MetadataMap()
|
||||
dest := connectRequest.Dest
|
||||
method := metadata[HTTPMethodKey]
|
||||
host := metadata[HTTPHostKey]
|
||||
|
||||
req, err := http.NewRequest(method, dest, body)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
req.Host = host
|
||||
for _, metadata := range connectRequest.Metadata {
|
||||
if strings.Contains(metadata.Key, HTTPHeaderKey) {
|
||||
// metadata.Key is off the format httpHeaderKey:<HTTPHeader>
|
||||
httpHeaderKey := strings.Split(metadata.Key, ":")
|
||||
if len(httpHeaderKey) != 2 {
|
||||
return nil, fmt.Errorf("Header Key: %s malformed", metadata.Key)
|
||||
}
|
||||
req.Header.Add(httpHeaderKey[1], metadata.Val)
|
||||
}
|
||||
}
|
||||
stripWebsocketUpgradeHeader(req)
|
||||
return req, err
|
||||
}
|
||||
|
||||
+177
-28
@@ -1,25 +1,32 @@
|
||||
package connection
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"crypto/rand"
|
||||
"crypto/rsa"
|
||||
"crypto/tls"
|
||||
"crypto/x509"
|
||||
"encoding/pem"
|
||||
"io/ioutil"
|
||||
"fmt"
|
||||
"io"
|
||||
"math/big"
|
||||
"net"
|
||||
"net/http"
|
||||
"os"
|
||||
"sync"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/gobwas/ws/wsutil"
|
||||
"github.com/lucas-clemente/quic-go"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/rs/zerolog"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
quicpogs "github.com/cloudflare/cloudflared/quic"
|
||||
"github.com/cloudflare/cloudflared/tunnelrpc/pogs"
|
||||
)
|
||||
|
||||
// TestQUICServer tests if a quic server accepts and responds to a quic client with the acceptance protocol.
|
||||
@@ -29,65 +36,166 @@ func TestQUICServer(t *testing.T) {
|
||||
KeepAlive: true,
|
||||
}
|
||||
|
||||
// Setup test.
|
||||
log := zerolog.New(os.Stdout)
|
||||
|
||||
// Start a UDP Listener for QUIC.
|
||||
udpAddr, err := net.ResolveUDPAddr("udp", "127.0.0.1:0")
|
||||
|
||||
require.NoError(t, err)
|
||||
udpListener, err := net.ListenUDP(udpAddr.Network(), udpAddr)
|
||||
require.NoError(t, err)
|
||||
defer udpListener.Close()
|
||||
|
||||
// Create a simple tls config.
|
||||
tlsConfig := generateTLSConfig()
|
||||
tlsConfClient := &tls.Config{
|
||||
|
||||
// Create a client config
|
||||
tlsClientConfig := &tls.Config{
|
||||
InsecureSkipVerify: true,
|
||||
NextProtos: []string{"argotunnel"},
|
||||
}
|
||||
|
||||
// Start a mock httpProxy
|
||||
originProxy := &mockOriginProxyWithRequest{}
|
||||
|
||||
// This is simply a sample websocket frame message.
|
||||
wsBuf := &bytes.Buffer{}
|
||||
wsutil.WriteClientText(wsBuf, []byte("Hello"))
|
||||
|
||||
var tests = []struct {
|
||||
desc string
|
||||
dest string
|
||||
connectionType quicpogs.ConnectionType
|
||||
metadata []quicpogs.Metadata
|
||||
message []byte
|
||||
expectedMessage []byte
|
||||
desc string
|
||||
dest string
|
||||
connectionType quicpogs.ConnectionType
|
||||
metadata []quicpogs.Metadata
|
||||
message []byte
|
||||
expectedResponse []byte
|
||||
}{
|
||||
{
|
||||
desc: "",
|
||||
dest: "somehost.com",
|
||||
desc: "test http proxy",
|
||||
dest: "/ok",
|
||||
connectionType: quicpogs.ConnectionTypeHTTP,
|
||||
metadata: []quicpogs.Metadata{
|
||||
quicpogs.Metadata{
|
||||
Key: "HttpHeader:Cf-Ray",
|
||||
Val: "123123123",
|
||||
},
|
||||
quicpogs.Metadata{
|
||||
Key: "HttpHost",
|
||||
Val: "cf.host",
|
||||
},
|
||||
quicpogs.Metadata{
|
||||
Key: "HttpMethod",
|
||||
Val: "GET",
|
||||
},
|
||||
},
|
||||
expectedResponse: []byte("OK"),
|
||||
},
|
||||
{
|
||||
desc: "test http body request streaming",
|
||||
dest: "/echo_body",
|
||||
connectionType: quicpogs.ConnectionTypeHTTP,
|
||||
metadata: []quicpogs.Metadata{
|
||||
quicpogs.Metadata{
|
||||
Key: "HttpHeader:Cf-Ray",
|
||||
Val: "123123123",
|
||||
},
|
||||
quicpogs.Metadata{
|
||||
Key: "HttpHost",
|
||||
Val: "cf.host",
|
||||
},
|
||||
quicpogs.Metadata{
|
||||
Key: "HttpMethod",
|
||||
Val: "POST",
|
||||
},
|
||||
},
|
||||
message: []byte("This is the message body"),
|
||||
expectedResponse: []byte("This is the message body"),
|
||||
},
|
||||
{
|
||||
desc: "test ws proxy",
|
||||
dest: "/ok",
|
||||
connectionType: quicpogs.ConnectionTypeWebsocket,
|
||||
metadata: []quicpogs.Metadata{
|
||||
quicpogs.Metadata{
|
||||
Key: "key",
|
||||
Val: "value",
|
||||
Key: "HttpHeader:Cf-Cloudflared-Proxy-Connection-Upgrade",
|
||||
Val: "Websocket",
|
||||
},
|
||||
quicpogs.Metadata{
|
||||
Key: "HttpHeader:Another-Header",
|
||||
Val: "Misc",
|
||||
},
|
||||
quicpogs.Metadata{
|
||||
Key: "HttpHost",
|
||||
Val: "cf.host",
|
||||
},
|
||||
quicpogs.Metadata{
|
||||
Key: "HttpMethod",
|
||||
Val: "get",
|
||||
},
|
||||
},
|
||||
expectedMessage: []byte("OK"),
|
||||
message: wsBuf.Bytes(),
|
||||
expectedResponse: []byte{0x81, 0x5, 0x48, 0x65, 0x6c, 0x6c, 0x6f},
|
||||
},
|
||||
{
|
||||
desc: "test tcp proxy",
|
||||
connectionType: quicpogs.ConnectionTypeTCP,
|
||||
metadata: []quicpogs.Metadata{},
|
||||
message: []byte("Here is some tcp data"),
|
||||
expectedResponse: []byte("Here is some tcp data"),
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
t.Run(test.desc, func(t *testing.T) {
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
|
||||
var wg sync.WaitGroup
|
||||
go func() {
|
||||
wg.Add(1)
|
||||
defer wg.Done()
|
||||
quicServer(
|
||||
t, udpListener, tlsConfig, quicConfig,
|
||||
test.dest, test.connectionType, test.metadata, test.message, test.expectedMessage,
|
||||
test.dest, test.connectionType, test.metadata, test.message, test.expectedResponse,
|
||||
)
|
||||
wg.Done()
|
||||
}()
|
||||
|
||||
qC, err := NewQUICConnection(context.Background(), quicConfig, udpListener.LocalAddr(), tlsConfClient, log)
|
||||
rpcClientFactory := mockRPCClientFactory{
|
||||
registered: make(chan struct{}),
|
||||
unregistered: make(chan struct{}),
|
||||
}
|
||||
|
||||
obs := NewObserver(&log, &log, false)
|
||||
controlStream := NewControlStream(
|
||||
obs,
|
||||
mockConnectedFuse{},
|
||||
&NamedTunnelConfig{},
|
||||
1,
|
||||
rpcClientFactory.newMockRPCClient,
|
||||
nil,
|
||||
1*time.Second,
|
||||
)
|
||||
|
||||
qC, err := NewQUICConnection(
|
||||
ctx,
|
||||
quicConfig,
|
||||
udpListener.LocalAddr(),
|
||||
tlsClientConfig,
|
||||
originProxy,
|
||||
&pogs.ConnectionOptions{},
|
||||
controlStream,
|
||||
NewObserver(&log, &log, false),
|
||||
)
|
||||
require.NoError(t, err)
|
||||
go qC.Serve(ctx)
|
||||
|
||||
go func() {
|
||||
wg.Wait()
|
||||
cancel()
|
||||
}()
|
||||
|
||||
qC.Serve(ctx)
|
||||
wg.Wait()
|
||||
|
||||
select {
|
||||
case <-rpcClientFactory.registered:
|
||||
break //ok
|
||||
case <-time.Tick(time.Second):
|
||||
t.Fatal("timeout out waiting for registration")
|
||||
}
|
||||
cancel()
|
||||
})
|
||||
}
|
||||
|
||||
@@ -107,7 +215,7 @@ func quicServer(
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
|
||||
earlyListener, err := quic.ListenEarly(conn, tlsConf, config)
|
||||
earlyListener, err := quic.Listen(conn, tlsConf, config)
|
||||
require.NoError(t, err)
|
||||
|
||||
session, err := earlyListener.Accept(ctx)
|
||||
@@ -116,7 +224,6 @@ func quicServer(
|
||||
stream, err := session.OpenStreamSync(context.Background())
|
||||
require.NoError(t, err)
|
||||
|
||||
// Start off ALPN
|
||||
err = quicpogs.WriteConnectRequestData(stream, dest, connectionType, metadata...)
|
||||
require.NoError(t, err)
|
||||
|
||||
@@ -125,11 +232,12 @@ func quicServer(
|
||||
|
||||
if message != nil {
|
||||
// ALPN successful. Write data.
|
||||
_, err = stream.Write([]byte(message))
|
||||
_, err := stream.Write([]byte(message))
|
||||
require.NoError(t, err)
|
||||
}
|
||||
|
||||
response, err := ioutil.ReadAll(stream)
|
||||
response := make([]byte, len(expectedResponse))
|
||||
stream.Read(response)
|
||||
require.NoError(t, err)
|
||||
|
||||
// For now it is an echo server. Verify if the same data is returned.
|
||||
@@ -159,3 +267,44 @@ func generateTLSConfig() *tls.Config {
|
||||
NextProtos: []string{"argotunnel"},
|
||||
}
|
||||
}
|
||||
|
||||
type mockOriginProxyWithRequest struct{}
|
||||
|
||||
func (moc *mockOriginProxyWithRequest) ProxyHTTP(w ResponseWriter, r *http.Request, isWebsocket bool) error {
|
||||
// These are a series of crude tests to ensure the headers and http related data is transferred from
|
||||
// metadata.
|
||||
if r.Method == "" {
|
||||
return errors.New("method not sent")
|
||||
}
|
||||
if r.Host == "" {
|
||||
return errors.New("host not sent")
|
||||
}
|
||||
if len(r.Header) == 0 {
|
||||
return errors.New("headers not set")
|
||||
}
|
||||
|
||||
if isWebsocket {
|
||||
return wsEndpoint(w, r)
|
||||
}
|
||||
switch r.URL.Path {
|
||||
case "/ok":
|
||||
originRespEndpoint(w, http.StatusOK, []byte(http.StatusText(http.StatusOK)))
|
||||
case "/echo_body":
|
||||
resp := &http.Response{
|
||||
StatusCode: http.StatusOK,
|
||||
}
|
||||
_ = w.WriteRespHeaders(resp.StatusCode, resp.Header)
|
||||
io.Copy(w, r.Body)
|
||||
case "/error":
|
||||
return fmt.Errorf("Failed to proxy to origin")
|
||||
default:
|
||||
originRespEndpoint(w, http.StatusNotFound, []byte("page not found"))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (moc *mockOriginProxyWithRequest) ProxyTCP(ctx context.Context, rwa ReadWriteAcker, tcpRequest *TCPRequest) error {
|
||||
rwa.AckConnection()
|
||||
io.Copy(rwa, rwa)
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -22,6 +22,8 @@ const (
|
||||
dotServerName = "cloudflare-dns.com"
|
||||
dotServerAddr = "1.1.1.1:853"
|
||||
dotTimeout = 15 * time.Second
|
||||
|
||||
logFieldAddress = "address"
|
||||
)
|
||||
|
||||
// Redeclare network functions so they can be overridden in tests.
|
||||
@@ -30,6 +32,12 @@ var (
|
||||
netLookupIP = net.LookupIP
|
||||
)
|
||||
|
||||
// EdgeAddr is a representation of possible ways to refer an edge location.
|
||||
type EdgeAddr struct {
|
||||
TCP *net.TCPAddr
|
||||
UDP *net.UDPAddr
|
||||
}
|
||||
|
||||
// If the call to net.LookupSRV fails, try to fall back to DoT from Cloudflare directly.
|
||||
//
|
||||
// Note: Instead of DoT, we could also have used DoH. Either of these:
|
||||
@@ -53,7 +61,9 @@ var friendlyDNSErrorLines = []string{
|
||||
}
|
||||
|
||||
// EdgeDiscovery implements HA service discovery lookup.
|
||||
func edgeDiscovery(log *zerolog.Logger) ([][]*net.TCPAddr, error) {
|
||||
func edgeDiscovery(log *zerolog.Logger, srvService string) ([][]*EdgeAddr, error) {
|
||||
log.Debug().Str("domain", "_"+srvService+"._"+srvProto+"."+srvName).Msg("looking up edge SRV record")
|
||||
|
||||
_, addrs, err := netLookupSRV(srvService, srvProto, srvName)
|
||||
if err != nil {
|
||||
_, fallbackAddrs, fallbackErr := fallbackLookupSRV(srvService, srvProto, srvName)
|
||||
@@ -69,19 +79,19 @@ func edgeDiscovery(log *zerolog.Logger) ([][]*net.TCPAddr, error) {
|
||||
addrs = fallbackAddrs
|
||||
}
|
||||
|
||||
var resolvedIPsPerCNAME [][]*net.TCPAddr
|
||||
var resolvedAddrPerCNAME [][]*EdgeAddr
|
||||
for _, addr := range addrs {
|
||||
ips, err := resolveSRVToTCP(addr)
|
||||
edgeAddrs, err := resolveSRV(addr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
resolvedIPsPerCNAME = append(resolvedIPsPerCNAME, ips)
|
||||
resolvedAddrPerCNAME = append(resolvedAddrPerCNAME, edgeAddrs)
|
||||
}
|
||||
|
||||
return resolvedIPsPerCNAME, nil
|
||||
return resolvedAddrPerCNAME, nil
|
||||
}
|
||||
|
||||
func lookupSRVWithDOT(service, proto, name string) (cname string, addrs []*net.SRV, err error) {
|
||||
func lookupSRVWithDOT(string, string, string) (cname string, addrs []*net.SRV, err error) {
|
||||
// Inspiration: https://github.com/artyom/dot/blob/master/dot.go
|
||||
r := &net.Resolver{
|
||||
PreferGo: true,
|
||||
@@ -100,7 +110,7 @@ func lookupSRVWithDOT(service, proto, name string) (cname string, addrs []*net.S
|
||||
return r.LookupSRV(ctx, srvService, srvProto, srvName)
|
||||
}
|
||||
|
||||
func resolveSRVToTCP(srv *net.SRV) ([]*net.TCPAddr, error) {
|
||||
func resolveSRV(srv *net.SRV) ([]*EdgeAddr, error) {
|
||||
ips, err := netLookupIP(srv.Target)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "Couldn't resolve SRV record %v", srv)
|
||||
@@ -108,23 +118,36 @@ func resolveSRVToTCP(srv *net.SRV) ([]*net.TCPAddr, error) {
|
||||
if len(ips) == 0 {
|
||||
return nil, fmt.Errorf("SRV record %v had no IPs", srv)
|
||||
}
|
||||
addrs := make([]*net.TCPAddr, len(ips))
|
||||
addrs := make([]*EdgeAddr, len(ips))
|
||||
for i, ip := range ips {
|
||||
addrs[i] = &net.TCPAddr{IP: ip, Port: int(srv.Port)}
|
||||
addrs[i] = &EdgeAddr{
|
||||
TCP: &net.TCPAddr{IP: ip, Port: int(srv.Port)},
|
||||
UDP: &net.UDPAddr{IP: ip, Port: int(srv.Port)},
|
||||
}
|
||||
}
|
||||
return addrs, nil
|
||||
}
|
||||
|
||||
// ResolveAddrs resolves TCP address given a list of addresses. Address can be a hostname, however, it will return at most one
|
||||
// of the hostname's IP addresses.
|
||||
func ResolveAddrs(addrs []string, log *zerolog.Logger) (resolved []*net.TCPAddr) {
|
||||
func ResolveAddrs(addrs []string, log *zerolog.Logger) (resolved []*EdgeAddr) {
|
||||
for _, addr := range addrs {
|
||||
tcpAddr, err := net.ResolveTCPAddr("tcp", addr)
|
||||
if err != nil {
|
||||
log.Err(err).Msgf("Failed to resolve %s", addr)
|
||||
} else {
|
||||
resolved = append(resolved, tcpAddr)
|
||||
log.Error().Str(logFieldAddress, addr).Err(err).Msg("failed to resolve to TCP address")
|
||||
continue
|
||||
}
|
||||
|
||||
udpAddr, err := net.ResolveUDPAddr("udp", addr)
|
||||
if err != nil {
|
||||
log.Error().Str(logFieldAddress, addr).Err(err).Msg("failed to resolve to UDP address")
|
||||
continue
|
||||
}
|
||||
resolved = append(resolved, &EdgeAddr{
|
||||
TCP: tcpAddr,
|
||||
UDP: udpAddr,
|
||||
})
|
||||
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
@@ -1,12 +1,17 @@
|
||||
package allregions
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"github.com/rs/zerolog"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func (ea *EdgeAddr) String() string {
|
||||
return fmt.Sprintf("%s-%s", ea.TCP, ea.UDP)
|
||||
}
|
||||
|
||||
func TestEdgeDiscovery(t *testing.T) {
|
||||
mockAddrs := newMockAddrs(19, 2, 5)
|
||||
netLookupSRV = mockNetLookupSRV(mockAddrs)
|
||||
@@ -20,7 +25,7 @@ func TestEdgeDiscovery(t *testing.T) {
|
||||
}
|
||||
|
||||
l := zerolog.Nop()
|
||||
addrLists, err := edgeDiscovery(&l)
|
||||
addrLists, err := edgeDiscovery(&l, "")
|
||||
assert.NoError(t, err)
|
||||
actualAddrSet := map[string]bool{}
|
||||
for _, addrs := range addrLists {
|
||||
|
||||
@@ -11,7 +11,7 @@ import (
|
||||
|
||||
type mockAddrs struct {
|
||||
// a set of synthetic SRV records
|
||||
addrMap map[net.SRV][]*net.TCPAddr
|
||||
addrMap map[net.SRV][]*EdgeAddr
|
||||
// the total number of addresses, aggregated across addrMap.
|
||||
// For the convenience of test code that would otherwise have to compute
|
||||
// this by hand every time.
|
||||
@@ -19,19 +19,24 @@ type mockAddrs struct {
|
||||
}
|
||||
|
||||
func newMockAddrs(port uint16, numRegions uint8, numAddrsPerRegion uint8) mockAddrs {
|
||||
addrMap := make(map[net.SRV][]*net.TCPAddr)
|
||||
addrMap := make(map[net.SRV][]*EdgeAddr)
|
||||
numAddrs := 0
|
||||
|
||||
for r := uint8(0); r < numRegions; r++ {
|
||||
var (
|
||||
srv = net.SRV{Target: fmt.Sprintf("test-region-%v.example.com", r), Port: port}
|
||||
addrs []*net.TCPAddr
|
||||
addrs []*EdgeAddr
|
||||
)
|
||||
for a := uint8(0); a < numAddrsPerRegion; a++ {
|
||||
addrs = append(addrs, &net.TCPAddr{
|
||||
tcpAddr := &net.TCPAddr{
|
||||
IP: net.ParseIP(fmt.Sprintf("10.0.%v.%v", r, a)),
|
||||
Port: int(port),
|
||||
})
|
||||
}
|
||||
udpAddr := &net.UDPAddr{
|
||||
IP: net.ParseIP(fmt.Sprintf("10.0.%v.%v", r, a)),
|
||||
Port: int(port),
|
||||
}
|
||||
addrs = append(addrs, &EdgeAddr{tcpAddr, udpAddr})
|
||||
}
|
||||
addrMap[srv] = addrs
|
||||
numAddrs += len(addrs)
|
||||
@@ -74,13 +79,13 @@ func mockNetLookupIP(
|
||||
m mockAddrs,
|
||||
) func(host string) ([]net.IP, error) {
|
||||
return func(host string) ([]net.IP, error) {
|
||||
for srv, tcpAddrs := range m.addrMap {
|
||||
for srv, addrs := range m.addrMap {
|
||||
if srv.Target != host {
|
||||
continue
|
||||
}
|
||||
result := make([]net.IP, len(tcpAddrs))
|
||||
for i, tcpAddr := range tcpAddrs {
|
||||
result[i] = tcpAddr.IP
|
||||
result := make([]net.IP, len(addrs))
|
||||
for i, addr := range addrs {
|
||||
result[i] = addr.TCP.IP
|
||||
}
|
||||
return result, nil
|
||||
}
|
||||
|
||||
@@ -1,29 +1,27 @@
|
||||
package allregions
|
||||
|
||||
import (
|
||||
"net"
|
||||
)
|
||||
|
||||
// Region contains cloudflared edge addresses. The edge is partitioned into several regions for
|
||||
// redundancy purposes.
|
||||
type Region struct {
|
||||
connFor map[*net.TCPAddr]UsedBy
|
||||
connFor map[*EdgeAddr]UsedBy
|
||||
}
|
||||
|
||||
// NewRegion creates a region with the given addresses, which are all unused.
|
||||
func NewRegion(addrs []*net.TCPAddr) Region {
|
||||
func NewRegion(addrs []*EdgeAddr) Region {
|
||||
// The zero value of UsedBy is Unused(), so we can just initialize the map's values with their
|
||||
// zero values.
|
||||
m := make(map[*net.TCPAddr]UsedBy)
|
||||
connFor := make(map[*EdgeAddr]UsedBy)
|
||||
for _, addr := range addrs {
|
||||
m[addr] = Unused()
|
||||
connFor[addr] = Unused()
|
||||
}
|
||||
return Region{
|
||||
connFor: connFor,
|
||||
}
|
||||
return Region{connFor: m}
|
||||
}
|
||||
|
||||
// AddrUsedBy finds the address used by the given connection in this region.
|
||||
// Returns nil if the connection isn't using any IP.
|
||||
func (r *Region) AddrUsedBy(connID int) *net.TCPAddr {
|
||||
func (r *Region) AddrUsedBy(connID int) *EdgeAddr {
|
||||
for addr, used := range r.connFor {
|
||||
if used.Used && used.ConnID == connID {
|
||||
return addr
|
||||
@@ -45,7 +43,7 @@ func (r Region) AvailableAddrs() int {
|
||||
|
||||
// GetUnusedIP returns a random unused address in this region.
|
||||
// Returns nil if all addresses are in use.
|
||||
func (r Region) GetUnusedIP(excluding *net.TCPAddr) *net.TCPAddr {
|
||||
func (r Region) GetUnusedIP(excluding *EdgeAddr) *EdgeAddr {
|
||||
for addr, usedby := range r.connFor {
|
||||
if !usedby.Used && addr != excluding {
|
||||
return addr
|
||||
@@ -55,7 +53,7 @@ func (r Region) GetUnusedIP(excluding *net.TCPAddr) *net.TCPAddr {
|
||||
}
|
||||
|
||||
// Use the address, assigning it to a proxy connection.
|
||||
func (r Region) Use(addr *net.TCPAddr, connID int) {
|
||||
func (r Region) Use(addr *EdgeAddr, connID int) {
|
||||
if addr == nil {
|
||||
return
|
||||
}
|
||||
@@ -63,7 +61,7 @@ func (r Region) Use(addr *net.TCPAddr, connID int) {
|
||||
}
|
||||
|
||||
// GetAnyAddress returns an arbitrary address from the region.
|
||||
func (r Region) GetAnyAddress() *net.TCPAddr {
|
||||
func (r Region) GetAnyAddress() *EdgeAddr {
|
||||
for addr := range r.connFor {
|
||||
return addr
|
||||
}
|
||||
@@ -72,7 +70,7 @@ func (r Region) GetAnyAddress() *net.TCPAddr {
|
||||
|
||||
// GiveBack the address, ensuring it is no longer assigned to an IP.
|
||||
// Returns true if the address is in this region.
|
||||
func (r Region) GiveBack(addr *net.TCPAddr) (ok bool) {
|
||||
func (r Region) GiveBack(addr *EdgeAddr) (ok bool) {
|
||||
if _, ok := r.connFor[addr]; !ok {
|
||||
return false
|
||||
}
|
||||
|
||||
@@ -1,15 +1,12 @@
|
||||
package allregions
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net"
|
||||
"reflect"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestRegion_New(t *testing.T) {
|
||||
r := NewRegion([]*net.TCPAddr{&addr0, &addr1, &addr2})
|
||||
fmt.Println(r.connFor)
|
||||
r := NewRegion([]*EdgeAddr{&addr0, &addr1, &addr2})
|
||||
if r.AvailableAddrs() != 3 {
|
||||
t.Errorf("r.AvailableAddrs() == %v but want 3", r.AvailableAddrs())
|
||||
}
|
||||
@@ -17,7 +14,7 @@ func TestRegion_New(t *testing.T) {
|
||||
|
||||
func TestRegion_AddrUsedBy(t *testing.T) {
|
||||
type fields struct {
|
||||
connFor map[*net.TCPAddr]UsedBy
|
||||
connFor map[*EdgeAddr]UsedBy
|
||||
}
|
||||
type args struct {
|
||||
connID int
|
||||
@@ -26,11 +23,11 @@ func TestRegion_AddrUsedBy(t *testing.T) {
|
||||
name string
|
||||
fields fields
|
||||
args args
|
||||
want *net.TCPAddr
|
||||
want *EdgeAddr
|
||||
}{
|
||||
{
|
||||
name: "happy trivial test",
|
||||
fields: fields{connFor: map[*net.TCPAddr]UsedBy{
|
||||
fields: fields{connFor: map[*EdgeAddr]UsedBy{
|
||||
&addr0: InUse(0),
|
||||
}},
|
||||
args: args{connID: 0},
|
||||
@@ -38,7 +35,7 @@ func TestRegion_AddrUsedBy(t *testing.T) {
|
||||
},
|
||||
{
|
||||
name: "sad trivial test",
|
||||
fields: fields{connFor: map[*net.TCPAddr]UsedBy{
|
||||
fields: fields{connFor: map[*EdgeAddr]UsedBy{
|
||||
&addr0: InUse(0),
|
||||
}},
|
||||
args: args{connID: 1},
|
||||
@@ -46,7 +43,7 @@ func TestRegion_AddrUsedBy(t *testing.T) {
|
||||
},
|
||||
{
|
||||
name: "sad test",
|
||||
fields: fields{connFor: map[*net.TCPAddr]UsedBy{
|
||||
fields: fields{connFor: map[*EdgeAddr]UsedBy{
|
||||
&addr0: InUse(0),
|
||||
&addr1: InUse(1),
|
||||
&addr2: InUse(2),
|
||||
@@ -56,7 +53,7 @@ func TestRegion_AddrUsedBy(t *testing.T) {
|
||||
},
|
||||
{
|
||||
name: "happy test",
|
||||
fields: fields{connFor: map[*net.TCPAddr]UsedBy{
|
||||
fields: fields{connFor: map[*EdgeAddr]UsedBy{
|
||||
&addr0: InUse(0),
|
||||
&addr1: InUse(1),
|
||||
&addr2: InUse(2),
|
||||
@@ -79,7 +76,7 @@ func TestRegion_AddrUsedBy(t *testing.T) {
|
||||
|
||||
func TestRegion_AvailableAddrs(t *testing.T) {
|
||||
type fields struct {
|
||||
connFor map[*net.TCPAddr]UsedBy
|
||||
connFor map[*EdgeAddr]UsedBy
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
@@ -88,7 +85,7 @@ func TestRegion_AvailableAddrs(t *testing.T) {
|
||||
}{
|
||||
{
|
||||
name: "contains addresses",
|
||||
fields: fields{connFor: map[*net.TCPAddr]UsedBy{
|
||||
fields: fields{connFor: map[*EdgeAddr]UsedBy{
|
||||
&addr0: InUse(0),
|
||||
&addr1: Unused(),
|
||||
&addr2: InUse(2),
|
||||
@@ -97,7 +94,7 @@ func TestRegion_AvailableAddrs(t *testing.T) {
|
||||
},
|
||||
{
|
||||
name: "all free",
|
||||
fields: fields{connFor: map[*net.TCPAddr]UsedBy{
|
||||
fields: fields{connFor: map[*EdgeAddr]UsedBy{
|
||||
&addr0: Unused(),
|
||||
&addr1: Unused(),
|
||||
&addr2: Unused(),
|
||||
@@ -106,7 +103,7 @@ func TestRegion_AvailableAddrs(t *testing.T) {
|
||||
},
|
||||
{
|
||||
name: "all used",
|
||||
fields: fields{connFor: map[*net.TCPAddr]UsedBy{
|
||||
fields: fields{connFor: map[*EdgeAddr]UsedBy{
|
||||
&addr0: InUse(0),
|
||||
&addr1: InUse(1),
|
||||
&addr2: InUse(2),
|
||||
@@ -115,7 +112,7 @@ func TestRegion_AvailableAddrs(t *testing.T) {
|
||||
},
|
||||
{
|
||||
name: "empty",
|
||||
fields: fields{connFor: map[*net.TCPAddr]UsedBy{}},
|
||||
fields: fields{connFor: map[*EdgeAddr]UsedBy{}},
|
||||
want: 0,
|
||||
},
|
||||
}
|
||||
@@ -133,20 +130,20 @@ func TestRegion_AvailableAddrs(t *testing.T) {
|
||||
|
||||
func TestRegion_GetUnusedIP(t *testing.T) {
|
||||
type fields struct {
|
||||
connFor map[*net.TCPAddr]UsedBy
|
||||
connFor map[*EdgeAddr]UsedBy
|
||||
}
|
||||
type args struct {
|
||||
excluding *net.TCPAddr
|
||||
excluding *EdgeAddr
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
fields fields
|
||||
args args
|
||||
want *net.TCPAddr
|
||||
want *EdgeAddr
|
||||
}{
|
||||
{
|
||||
name: "happy test with excluding set",
|
||||
fields: fields{connFor: map[*net.TCPAddr]UsedBy{
|
||||
fields: fields{connFor: map[*EdgeAddr]UsedBy{
|
||||
&addr0: Unused(),
|
||||
&addr1: Unused(),
|
||||
&addr2: InUse(2),
|
||||
@@ -156,7 +153,7 @@ func TestRegion_GetUnusedIP(t *testing.T) {
|
||||
},
|
||||
{
|
||||
name: "happy test with no excluding",
|
||||
fields: fields{connFor: map[*net.TCPAddr]UsedBy{
|
||||
fields: fields{connFor: map[*EdgeAddr]UsedBy{
|
||||
&addr0: InUse(0),
|
||||
&addr1: Unused(),
|
||||
&addr2: InUse(2),
|
||||
@@ -166,7 +163,7 @@ func TestRegion_GetUnusedIP(t *testing.T) {
|
||||
},
|
||||
{
|
||||
name: "sad test with no excluding",
|
||||
fields: fields{connFor: map[*net.TCPAddr]UsedBy{
|
||||
fields: fields{connFor: map[*EdgeAddr]UsedBy{
|
||||
&addr0: InUse(0),
|
||||
&addr1: InUse(1),
|
||||
&addr2: InUse(2),
|
||||
@@ -176,7 +173,7 @@ func TestRegion_GetUnusedIP(t *testing.T) {
|
||||
},
|
||||
{
|
||||
name: "sad test with excluding",
|
||||
fields: fields{connFor: map[*net.TCPAddr]UsedBy{
|
||||
fields: fields{connFor: map[*EdgeAddr]UsedBy{
|
||||
&addr0: Unused(),
|
||||
&addr1: InUse(1),
|
||||
&addr2: InUse(2),
|
||||
@@ -199,10 +196,10 @@ func TestRegion_GetUnusedIP(t *testing.T) {
|
||||
|
||||
func TestRegion_GiveBack(t *testing.T) {
|
||||
type fields struct {
|
||||
connFor map[*net.TCPAddr]UsedBy
|
||||
connFor map[*EdgeAddr]UsedBy
|
||||
}
|
||||
type args struct {
|
||||
addr *net.TCPAddr
|
||||
addr *EdgeAddr
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
@@ -213,7 +210,7 @@ func TestRegion_GiveBack(t *testing.T) {
|
||||
}{
|
||||
{
|
||||
name: "sad test with excluding",
|
||||
fields: fields{connFor: map[*net.TCPAddr]UsedBy{
|
||||
fields: fields{connFor: map[*EdgeAddr]UsedBy{
|
||||
&addr1: InUse(1),
|
||||
}},
|
||||
args: args{addr: &addr1},
|
||||
@@ -222,7 +219,7 @@ func TestRegion_GiveBack(t *testing.T) {
|
||||
},
|
||||
{
|
||||
name: "sad test with excluding",
|
||||
fields: fields{connFor: map[*net.TCPAddr]UsedBy{
|
||||
fields: fields{connFor: map[*EdgeAddr]UsedBy{
|
||||
&addr1: InUse(1),
|
||||
}},
|
||||
args: args{addr: &addr2},
|
||||
@@ -247,7 +244,7 @@ func TestRegion_GiveBack(t *testing.T) {
|
||||
|
||||
func TestRegion_GetAnyAddress(t *testing.T) {
|
||||
type fields struct {
|
||||
connFor map[*net.TCPAddr]UsedBy
|
||||
connFor map[*EdgeAddr]UsedBy
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
@@ -256,19 +253,19 @@ func TestRegion_GetAnyAddress(t *testing.T) {
|
||||
}{
|
||||
{
|
||||
name: "Sad test -- GetAnyAddress should only fail if the region is empty",
|
||||
fields: fields{connFor: map[*net.TCPAddr]UsedBy{}},
|
||||
fields: fields{connFor: map[*EdgeAddr]UsedBy{}},
|
||||
wantNil: true,
|
||||
},
|
||||
{
|
||||
name: "Happy test (all addresses unused)",
|
||||
fields: fields{connFor: map[*net.TCPAddr]UsedBy{
|
||||
fields: fields{connFor: map[*EdgeAddr]UsedBy{
|
||||
&addr0: Unused(),
|
||||
}},
|
||||
wantNil: false,
|
||||
},
|
||||
{
|
||||
name: "Happy test (GetAnyAddress can still return addresses used by proxy conns)",
|
||||
fields: fields{connFor: map[*net.TCPAddr]UsedBy{
|
||||
fields: fields{connFor: map[*EdgeAddr]UsedBy{
|
||||
&addr0: InUse(2),
|
||||
}},
|
||||
wantNil: false,
|
||||
|
||||
@@ -2,7 +2,6 @@ package allregions
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net"
|
||||
|
||||
"github.com/rs/zerolog"
|
||||
)
|
||||
@@ -19,17 +18,17 @@ type Regions struct {
|
||||
// ------------------------------------
|
||||
|
||||
// ResolveEdge resolves the Cloudflare edge, returning all regions discovered.
|
||||
func ResolveEdge(log *zerolog.Logger) (*Regions, error) {
|
||||
addrLists, err := edgeDiscovery(log)
|
||||
func ResolveEdge(log *zerolog.Logger, region string) (*Regions, error) {
|
||||
edgeAddrs, err := edgeDiscovery(log, getRegionalServiceName(region))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if len(addrLists) < 2 {
|
||||
return nil, fmt.Errorf("expected at least 2 Cloudflare Regions regions, but SRV only returned %v", len(addrLists))
|
||||
if len(edgeAddrs) < 2 {
|
||||
return nil, fmt.Errorf("expected at least 2 Cloudflare Regions regions, but SRV only returned %v", len(edgeAddrs))
|
||||
}
|
||||
return &Regions{
|
||||
region1: NewRegion(addrLists[0]),
|
||||
region2: NewRegion(addrLists[1]),
|
||||
region1: NewRegion(edgeAddrs[0]),
|
||||
region2: NewRegion(edgeAddrs[1]),
|
||||
}, nil
|
||||
}
|
||||
|
||||
@@ -45,9 +44,9 @@ func StaticEdge(hostnames []string, log *zerolog.Logger) (*Regions, error) {
|
||||
|
||||
// NewNoResolve doesn't resolve the edge. Instead it just uses the given addresses.
|
||||
// You probably only need this for testing.
|
||||
func NewNoResolve(addrs []*net.TCPAddr) *Regions {
|
||||
region1 := make([]*net.TCPAddr, 0)
|
||||
region2 := make([]*net.TCPAddr, 0)
|
||||
func NewNoResolve(addrs []*EdgeAddr) *Regions {
|
||||
region1 := make([]*EdgeAddr, 0)
|
||||
region2 := make([]*EdgeAddr, 0)
|
||||
for i, v := range addrs {
|
||||
if i%2 == 0 {
|
||||
region1 = append(region1, v)
|
||||
@@ -67,7 +66,7 @@ func NewNoResolve(addrs []*net.TCPAddr) *Regions {
|
||||
// ------------------------------------
|
||||
|
||||
// GetAnyAddress returns an arbitrary address from the larger region.
|
||||
func (rs *Regions) GetAnyAddress() *net.TCPAddr {
|
||||
func (rs *Regions) GetAnyAddress() *EdgeAddr {
|
||||
if addr := rs.region1.GetAnyAddress(); addr != nil {
|
||||
return addr
|
||||
}
|
||||
@@ -76,7 +75,7 @@ func (rs *Regions) GetAnyAddress() *net.TCPAddr {
|
||||
|
||||
// AddrUsedBy finds the address used by the given connection.
|
||||
// Returns nil if the connection isn't using an address.
|
||||
func (rs *Regions) AddrUsedBy(connID int) *net.TCPAddr {
|
||||
func (rs *Regions) AddrUsedBy(connID int) *EdgeAddr {
|
||||
if addr := rs.region1.AddrUsedBy(connID); addr != nil {
|
||||
return addr
|
||||
}
|
||||
@@ -85,7 +84,7 @@ func (rs *Regions) AddrUsedBy(connID int) *net.TCPAddr {
|
||||
|
||||
// GetUnusedAddr gets an unused addr from the edge, excluding the given addr. Prefer to use addresses
|
||||
// evenly across both regions.
|
||||
func (rs *Regions) GetUnusedAddr(excluding *net.TCPAddr, connID int) *net.TCPAddr {
|
||||
func (rs *Regions) GetUnusedAddr(excluding *EdgeAddr, connID int) *EdgeAddr {
|
||||
if rs.region1.AvailableAddrs() > rs.region2.AvailableAddrs() {
|
||||
return getAddrs(excluding, connID, &rs.region1, &rs.region2)
|
||||
}
|
||||
@@ -95,7 +94,7 @@ func (rs *Regions) GetUnusedAddr(excluding *net.TCPAddr, connID int) *net.TCPAdd
|
||||
|
||||
// getAddrs tries to grab address form `first` region, then `second` region
|
||||
// this is an unrolled loop over 2 element array
|
||||
func getAddrs(excluding *net.TCPAddr, connID int, first *Region, second *Region) *net.TCPAddr {
|
||||
func getAddrs(excluding *EdgeAddr, connID int, first *Region, second *Region) *EdgeAddr {
|
||||
addr := first.GetUnusedIP(excluding)
|
||||
if addr != nil {
|
||||
first.Use(addr, connID)
|
||||
@@ -117,9 +116,18 @@ func (rs *Regions) AvailableAddrs() int {
|
||||
|
||||
// GiveBack the address so that other connections can use it.
|
||||
// Returns true if the address is in this edge.
|
||||
func (rs *Regions) GiveBack(addr *net.TCPAddr) bool {
|
||||
func (rs *Regions) GiveBack(addr *EdgeAddr) bool {
|
||||
if found := rs.region1.GiveBack(addr); found {
|
||||
return found
|
||||
}
|
||||
return rs.region2.GiveBack(addr)
|
||||
}
|
||||
|
||||
// Return regionalized service name if `region` isn't empty, otherwise return the global service name for origintunneld
|
||||
func getRegionalServiceName(region string) string {
|
||||
if region != "" {
|
||||
return region + "-" + srvService // Example: `us-origintunneld`
|
||||
}
|
||||
|
||||
return srvService // Global service is just `origintunneld`
|
||||
}
|
||||
|
||||
@@ -8,31 +8,59 @@ import (
|
||||
)
|
||||
|
||||
var (
|
||||
addr0 = net.TCPAddr{
|
||||
IP: net.ParseIP("123.4.5.0"),
|
||||
Port: 8000,
|
||||
Zone: "",
|
||||
addr0 = EdgeAddr{
|
||||
TCP: &net.TCPAddr{
|
||||
IP: net.ParseIP("123.4.5.0"),
|
||||
Port: 8000,
|
||||
Zone: "",
|
||||
},
|
||||
UDP: &net.UDPAddr{
|
||||
IP: net.ParseIP("123.4.5.0"),
|
||||
Port: 8000,
|
||||
Zone: "",
|
||||
},
|
||||
}
|
||||
addr1 = net.TCPAddr{
|
||||
IP: net.ParseIP("123.4.5.1"),
|
||||
Port: 8000,
|
||||
Zone: "",
|
||||
addr1 = EdgeAddr{
|
||||
TCP: &net.TCPAddr{
|
||||
IP: net.ParseIP("123.4.5.1"),
|
||||
Port: 8000,
|
||||
Zone: "",
|
||||
},
|
||||
UDP: &net.UDPAddr{
|
||||
IP: net.ParseIP("123.4.5.1"),
|
||||
Port: 8000,
|
||||
Zone: "",
|
||||
},
|
||||
}
|
||||
addr2 = net.TCPAddr{
|
||||
IP: net.ParseIP("123.4.5.2"),
|
||||
Port: 8000,
|
||||
Zone: "",
|
||||
addr2 = EdgeAddr{
|
||||
TCP: &net.TCPAddr{
|
||||
IP: net.ParseIP("123.4.5.2"),
|
||||
Port: 8000,
|
||||
Zone: "",
|
||||
},
|
||||
UDP: &net.UDPAddr{
|
||||
IP: net.ParseIP("123.4.5.2"),
|
||||
Port: 8000,
|
||||
Zone: "",
|
||||
},
|
||||
}
|
||||
addr3 = net.TCPAddr{
|
||||
IP: net.ParseIP("123.4.5.3"),
|
||||
Port: 8000,
|
||||
Zone: "",
|
||||
addr3 = EdgeAddr{
|
||||
TCP: &net.TCPAddr{
|
||||
IP: net.ParseIP("123.4.5.3"),
|
||||
Port: 8000,
|
||||
Zone: "",
|
||||
},
|
||||
UDP: &net.UDPAddr{
|
||||
IP: net.ParseIP("123.4.5.3"),
|
||||
Port: 8000,
|
||||
Zone: "",
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
func makeRegions() Regions {
|
||||
r1 := NewRegion([]*net.TCPAddr{&addr0, &addr1})
|
||||
r2 := NewRegion([]*net.TCPAddr{&addr2, &addr3})
|
||||
r1 := NewRegion([]*EdgeAddr{&addr0, &addr1})
|
||||
r2 := NewRegion([]*EdgeAddr{&addr2, &addr3})
|
||||
return Regions{region1: r1, region2: r2}
|
||||
}
|
||||
|
||||
@@ -105,7 +133,7 @@ func TestRegions_GetUnusedAddr_Excluding_Region2(t *testing.T) {
|
||||
|
||||
func TestNewNoResolveBalancesRegions(t *testing.T) {
|
||||
type args struct {
|
||||
addrs []*net.TCPAddr
|
||||
addrs []*EdgeAddr
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
@@ -113,11 +141,11 @@ func TestNewNoResolveBalancesRegions(t *testing.T) {
|
||||
}{
|
||||
{
|
||||
name: "one address",
|
||||
args: args{addrs: []*net.TCPAddr{&addr0}},
|
||||
args: args{addrs: []*EdgeAddr{&addr0}},
|
||||
},
|
||||
{
|
||||
name: "two addresses",
|
||||
args: args{addrs: []*net.TCPAddr{&addr0, &addr1}},
|
||||
args: args{addrs: []*EdgeAddr{&addr0, &addr1}},
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
@@ -128,6 +156,18 @@ func TestNewNoResolveBalancesRegions(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetRegionalServiceName(t *testing.T) {
|
||||
// Empty region should just go to origintunneld
|
||||
globalServiceName := getRegionalServiceName("")
|
||||
assert.Equal(t, srvService, globalServiceName)
|
||||
|
||||
// Non-empty region should go to the regional origintunneld variant
|
||||
for _, region := range []string{"us", "pt", "am"} {
|
||||
regionalServiceName := getRegionalServiceName(region)
|
||||
assert.Equal(t, region+"-"+srvService, regionalServiceName)
|
||||
}
|
||||
}
|
||||
|
||||
func RegionsIsBalanced(t *testing.T, rs *Regions) {
|
||||
delta := rs.region1.AvailableAddrs() - rs.region2.AvailableAddrs()
|
||||
assert.True(t, abs(delta) <= 1)
|
||||
|
||||
@@ -2,10 +2,10 @@ package edgediscovery
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net"
|
||||
"sync"
|
||||
|
||||
"github.com/rs/zerolog"
|
||||
"github.com/rs/zerolog/log"
|
||||
|
||||
"github.com/cloudflare/cloudflared/edgediscovery/allregions"
|
||||
)
|
||||
@@ -30,8 +30,8 @@ type Edge struct {
|
||||
|
||||
// ResolveEdge runs the initial discovery of the Cloudflare edge, finding Addrs that can be allocated
|
||||
// to connections.
|
||||
func ResolveEdge(log *zerolog.Logger) (*Edge, error) {
|
||||
regions, err := allregions.ResolveEdge(log)
|
||||
func ResolveEdge(log *zerolog.Logger, region string) (*Edge, error) {
|
||||
regions, err := allregions.ResolveEdge(log, region)
|
||||
if err != nil {
|
||||
return new(Edge), err
|
||||
}
|
||||
@@ -54,7 +54,7 @@ func StaticEdge(log *zerolog.Logger, hostnames []string) (*Edge, error) {
|
||||
}
|
||||
|
||||
// MockEdge creates a Cloudflare Edge from arbitrary TCP addresses. Used for testing.
|
||||
func MockEdge(log *zerolog.Logger, addrs []*net.TCPAddr) *Edge {
|
||||
func MockEdge(log *zerolog.Logger, addrs []*allregions.EdgeAddr) *Edge {
|
||||
regions := allregions.NewNoResolve(addrs)
|
||||
return &Edge{
|
||||
log: log,
|
||||
@@ -67,7 +67,7 @@ func MockEdge(log *zerolog.Logger, addrs []*net.TCPAddr) *Edge {
|
||||
// ------------------------------------
|
||||
|
||||
// GetAddrForRPC gives this connection an edge Addr.
|
||||
func (ed *Edge) GetAddrForRPC() (*net.TCPAddr, error) {
|
||||
func (ed *Edge) GetAddrForRPC() (*allregions.EdgeAddr, error) {
|
||||
ed.Lock()
|
||||
defer ed.Unlock()
|
||||
addr := ed.regions.GetAnyAddress()
|
||||
@@ -78,9 +78,8 @@ func (ed *Edge) GetAddrForRPC() (*net.TCPAddr, error) {
|
||||
}
|
||||
|
||||
// GetAddr gives this proxy connection an edge Addr. Prefer Addrs this connection has already used.
|
||||
func (ed *Edge) GetAddr(connIndex int) (*net.TCPAddr, error) {
|
||||
func (ed *Edge) GetAddr(connIndex int) (*allregions.EdgeAddr, error) {
|
||||
log := ed.log.With().Int(LogFieldConnIndex, connIndex).Logger()
|
||||
|
||||
ed.Lock()
|
||||
defer ed.Unlock()
|
||||
|
||||
@@ -96,14 +95,12 @@ func (ed *Edge) GetAddr(connIndex int) (*net.TCPAddr, error) {
|
||||
log.Debug().Msg("edgediscovery - GetAddr: No addresses left to give proxy connection")
|
||||
return nil, errNoAddressesLeft
|
||||
}
|
||||
log.Debug().Str(LogFieldAddress, addr.String()).Msg("edgediscovery - GetAddr: Giving connection its new address")
|
||||
log.Debug().Msg("edgediscovery - GetAddr: Giving connection its new address")
|
||||
return addr, nil
|
||||
}
|
||||
|
||||
// GetDifferentAddr gives back the proxy connection's edge Addr and uses a new one.
|
||||
func (ed *Edge) GetDifferentAddr(connIndex int) (*net.TCPAddr, error) {
|
||||
log := ed.log.With().Int(LogFieldConnIndex, connIndex).Logger()
|
||||
|
||||
func (ed *Edge) GetDifferentAddr(connIndex int) (*allregions.EdgeAddr, error) {
|
||||
ed.Lock()
|
||||
defer ed.Unlock()
|
||||
|
||||
@@ -117,7 +114,7 @@ func (ed *Edge) GetDifferentAddr(connIndex int) (*net.TCPAddr, error) {
|
||||
// note: if oldAddr were not nil, it will become available on the next iteration
|
||||
return nil, errNoAddressesLeft
|
||||
}
|
||||
log.Debug().Str(LogFieldAddress, addr.String()).Msg("edgediscovery - GetDifferentAddr: Giving connection its new address")
|
||||
log.Debug().Msg("edgediscovery - GetDifferentAddr: Giving connection its new address")
|
||||
return addr, nil
|
||||
}
|
||||
|
||||
@@ -130,7 +127,7 @@ func (ed *Edge) AvailableAddrs() int {
|
||||
|
||||
// GiveBack the address so that other connections can use it.
|
||||
// Returns true if the address is in this edge.
|
||||
func (ed *Edge) GiveBack(addr *net.TCPAddr) bool {
|
||||
func (ed *Edge) GiveBack(addr *allregions.EdgeAddr) bool {
|
||||
ed.Lock()
|
||||
defer ed.Unlock()
|
||||
ed.log.Debug().Msg("edgediscovery - GiveBack: Address now unused")
|
||||
|
||||
@@ -6,35 +6,65 @@ import (
|
||||
|
||||
"github.com/rs/zerolog"
|
||||
"github.com/stretchr/testify/assert"
|
||||
|
||||
"github.com/cloudflare/cloudflared/edgediscovery/allregions"
|
||||
)
|
||||
|
||||
var (
|
||||
addr0 = net.TCPAddr{
|
||||
IP: net.ParseIP("123.0.0.0"),
|
||||
Port: 8000,
|
||||
Zone: "",
|
||||
addr0 = allregions.EdgeAddr{
|
||||
TCP: &net.TCPAddr{
|
||||
IP: net.ParseIP("123.0.0.0"),
|
||||
Port: 8000,
|
||||
Zone: "",
|
||||
},
|
||||
UDP: &net.UDPAddr{
|
||||
IP: net.ParseIP("123.0.0.0"),
|
||||
Port: 8000,
|
||||
Zone: "",
|
||||
},
|
||||
}
|
||||
addr1 = net.TCPAddr{
|
||||
IP: net.ParseIP("123.0.0.1"),
|
||||
Port: 8000,
|
||||
Zone: "",
|
||||
addr1 = allregions.EdgeAddr{
|
||||
TCP: &net.TCPAddr{
|
||||
IP: net.ParseIP("123.0.0.1"),
|
||||
Port: 8000,
|
||||
Zone: "",
|
||||
},
|
||||
UDP: &net.UDPAddr{
|
||||
IP: net.ParseIP("123.0.0.1"),
|
||||
Port: 8000,
|
||||
Zone: "",
|
||||
},
|
||||
}
|
||||
addr2 = net.TCPAddr{
|
||||
IP: net.ParseIP("123.0.0.2"),
|
||||
Port: 8000,
|
||||
Zone: "",
|
||||
addr2 = allregions.EdgeAddr{
|
||||
TCP: &net.TCPAddr{
|
||||
IP: net.ParseIP("123.0.0.2"),
|
||||
Port: 8000,
|
||||
Zone: "",
|
||||
},
|
||||
UDP: &net.UDPAddr{
|
||||
IP: net.ParseIP("123.0.0.2"),
|
||||
Port: 8000,
|
||||
Zone: "",
|
||||
},
|
||||
}
|
||||
addr3 = net.TCPAddr{
|
||||
IP: net.ParseIP("123.0.0.3"),
|
||||
Port: 8000,
|
||||
Zone: "",
|
||||
addr3 = allregions.EdgeAddr{
|
||||
TCP: &net.TCPAddr{
|
||||
IP: net.ParseIP("123.0.0.3"),
|
||||
Port: 8000,
|
||||
Zone: "",
|
||||
},
|
||||
UDP: &net.UDPAddr{
|
||||
IP: net.ParseIP("123.0.0.3"),
|
||||
Port: 8000,
|
||||
Zone: "",
|
||||
},
|
||||
}
|
||||
|
||||
log = zerolog.Nop()
|
||||
testLogger = zerolog.Nop()
|
||||
)
|
||||
|
||||
func TestGiveBack(t *testing.T) {
|
||||
edge := MockEdge(&log, []*net.TCPAddr{&addr0, &addr1, &addr2, &addr3})
|
||||
edge := MockEdge(&testLogger, []*allregions.EdgeAddr{&addr0, &addr1, &addr2, &addr3})
|
||||
|
||||
// Give this connection an address
|
||||
assert.Equal(t, 4, edge.AvailableAddrs())
|
||||
@@ -51,7 +81,7 @@ func TestGiveBack(t *testing.T) {
|
||||
|
||||
func TestRPCAndProxyShareSingleEdgeIP(t *testing.T) {
|
||||
// Make an edge with a single IP
|
||||
edge := MockEdge(&log, []*net.TCPAddr{&addr0})
|
||||
edge := MockEdge(&testLogger, []*allregions.EdgeAddr{&addr0})
|
||||
tunnelConnID := 0
|
||||
|
||||
// Use the IP for a tunnel
|
||||
@@ -65,7 +95,7 @@ func TestRPCAndProxyShareSingleEdgeIP(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestGetAddrForRPC(t *testing.T) {
|
||||
edge := MockEdge(&log, []*net.TCPAddr{&addr0, &addr1, &addr2, &addr3})
|
||||
edge := MockEdge(&testLogger, []*allregions.EdgeAddr{&addr0, &addr1, &addr2, &addr3})
|
||||
|
||||
// Get a connection
|
||||
assert.Equal(t, 4, edge.AvailableAddrs())
|
||||
@@ -83,7 +113,7 @@ func TestGetAddrForRPC(t *testing.T) {
|
||||
|
||||
func TestOnePerRegion(t *testing.T) {
|
||||
// Make an edge with only one address
|
||||
edge := MockEdge(&log, []*net.TCPAddr{&addr0, &addr1})
|
||||
edge := MockEdge(&testLogger, []*allregions.EdgeAddr{&addr0, &addr1})
|
||||
|
||||
// Use the only address
|
||||
const connID = 0
|
||||
@@ -105,7 +135,7 @@ func TestOnePerRegion(t *testing.T) {
|
||||
|
||||
func TestOnlyOneAddrLeft(t *testing.T) {
|
||||
// Make an edge with only one address
|
||||
edge := MockEdge(&log, []*net.TCPAddr{&addr0})
|
||||
edge := MockEdge(&testLogger, []*allregions.EdgeAddr{&addr0})
|
||||
|
||||
// Use the only address
|
||||
const connID = 0
|
||||
@@ -125,7 +155,7 @@ func TestOnlyOneAddrLeft(t *testing.T) {
|
||||
|
||||
func TestNoAddrsLeft(t *testing.T) {
|
||||
// Make an edge with no addresses
|
||||
edge := MockEdge(&log, []*net.TCPAddr{})
|
||||
edge := MockEdge(&testLogger, []*allregions.EdgeAddr{})
|
||||
|
||||
_, err := edge.GetAddr(2)
|
||||
assert.Error(t, err)
|
||||
@@ -134,7 +164,7 @@ func TestNoAddrsLeft(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestGetAddr(t *testing.T) {
|
||||
edge := MockEdge(&log, []*net.TCPAddr{&addr0, &addr1, &addr2, &addr3})
|
||||
edge := MockEdge(&testLogger, []*allregions.EdgeAddr{&addr0, &addr1, &addr2, &addr3})
|
||||
|
||||
// Give this connection an address
|
||||
const connID = 0
|
||||
@@ -149,7 +179,7 @@ func TestGetAddr(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestGetDifferentAddr(t *testing.T) {
|
||||
edge := MockEdge(&log, []*net.TCPAddr{&addr0, &addr1, &addr2, &addr3})
|
||||
edge := MockEdge(&testLogger, []*allregions.EdgeAddr{&addr0, &addr1, &addr2, &addr3})
|
||||
|
||||
// Give this connection an address
|
||||
assert.Equal(t, 4, edge.AvailableAddrs())
|
||||
|
||||
@@ -29,7 +29,7 @@ require (
|
||||
github.com/json-iterator/go v1.1.10
|
||||
github.com/kr/text v0.2.0 // indirect
|
||||
github.com/kylelemons/godebug v1.1.0 // indirect
|
||||
github.com/lucas-clemente/quic-go v0.21.1
|
||||
github.com/lucas-clemente/quic-go v0.20.0
|
||||
github.com/mattn/go-colorable v0.1.8
|
||||
github.com/miekg/dns v1.1.31
|
||||
github.com/mitchellh/go-homedir v1.1.0
|
||||
@@ -38,6 +38,7 @@ require (
|
||||
github.com/pkg/errors v0.9.1
|
||||
github.com/pquerna/cachecontrol v0.0.0-20180517163645-1555304b9b35 // indirect
|
||||
github.com/prometheus/client_golang v1.7.1
|
||||
github.com/prometheus/client_model v0.2.0
|
||||
github.com/prometheus/common v0.13.0 // indirect
|
||||
github.com/rivo/tview v0.0.0-20200712113419-c65badfc3d92
|
||||
github.com/rs/zerolog v1.20.0
|
||||
@@ -46,9 +47,9 @@ require (
|
||||
github.com/urfave/cli/v2 v2.2.0
|
||||
go.uber.org/automaxprocs v1.4.0
|
||||
golang.org/x/crypto v0.0.0-20200820211705-5c72a883971a
|
||||
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4
|
||||
golang.org/x/net v0.0.0-20200904194848-62affa334b73
|
||||
golang.org/x/oauth2 v0.0.0-20200902213428-5d25da1a8d43 // indirect
|
||||
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c
|
||||
golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208
|
||||
golang.org/x/sys v0.0.0-20210510120138-977fb7262007
|
||||
golang.org/x/term v0.0.0-20201210144234-2321bbc49cbf
|
||||
google.golang.org/genproto v0.0.0-20200904004341-0bd0a958aa1d // indirect
|
||||
|
||||
@@ -263,7 +263,8 @@ github.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt
|
||||
github.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=
|
||||
github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=
|
||||
github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4=
|
||||
github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs=
|
||||
github.com/golang/mock v1.5.0 h1:jlYHihg//f7RRwuPfptm04yp4s7O6Kw8EZiVYIGcH0g=
|
||||
github.com/golang/mock v1.5.0/go.mod h1:CWnOUgYIOo4TcNZ0wHX3YZCqsaM1I1Jvs6v3mP3KVu8=
|
||||
github.com/golang/protobuf v0.0.0-20161109072736-4bd1920723d7/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/golang/protobuf v1.3.0/go.mod h1:Qd/q+1AKNOZr9uGQzbzCmRO6sUih6GTPZv6a1/R87v0=
|
||||
@@ -414,8 +415,8 @@ github.com/lightstep/lightstep-tracer-go v0.18.1/go.mod h1:jlF1pusYV4pidLvZ+XD0U
|
||||
github.com/linode/linodego v0.10.0/go.mod h1:cziNP7pbvE3mXIPneHj0oRY8L1WtGEIKlZ8LANE4eXA=
|
||||
github.com/liquidweb/liquidweb-go v1.6.0/go.mod h1:UDcVnAMDkZxpw4Y7NOHkqoeiGacVLEIG/i5J9cyixzQ=
|
||||
github.com/lucas-clemente/quic-go v0.13.1/go.mod h1:Vn3/Fb0/77b02SGhQk36KzOUmXgVpFfizUfW5WMaqyU=
|
||||
github.com/lucas-clemente/quic-go v0.21.1 h1:uuhCcu885TE9u/piPYMChI/yqA1lXfaLUEx8uCMxf8w=
|
||||
github.com/lucas-clemente/quic-go v0.21.1/go.mod h1:U9kFi5LKbNIlU30dkuM9vxmTxWq4Bvzee/MjBI+07UA=
|
||||
github.com/lucas-clemente/quic-go v0.20.0 h1:FSU3YN5VnLafHR27Ejs1r1CYMS7XMyIVDzRewkDLNBw=
|
||||
github.com/lucas-clemente/quic-go v0.20.0/go.mod h1:fZq/HUDIM+mW6X6wtzORjC0E/WDBMKe5Hf9bgjISwLk=
|
||||
github.com/lucasb-eyer/go-colorful v1.0.2/go.mod h1:0MS4r+7BZKSJ5mw4/S5MPN+qHFF1fYclkSPilDOKW0s=
|
||||
github.com/lucasb-eyer/go-colorful v1.0.3 h1:QIbQXiugsb+q10B+MI+7DI1oQLdmnep86tWFlaaUAac=
|
||||
github.com/lucasb-eyer/go-colorful v1.0.3/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0=
|
||||
@@ -431,8 +432,6 @@ github.com/marten-seemann/qtls-go1-15 v0.1.4 h1:RehYMOyRW8hPVEja1KBVsFVNSm35Jj9M
|
||||
github.com/marten-seemann/qtls-go1-15 v0.1.4/go.mod h1:GyFwywLKkRt+6mfU99csTEY1joMZz5vmB1WNZH3P81I=
|
||||
github.com/marten-seemann/qtls-go1-16 v0.1.3 h1:XEZ1xGorVy9u+lJq+WXNE+hiqRYLNvJGYmwfwKQN2gU=
|
||||
github.com/marten-seemann/qtls-go1-16 v0.1.3/go.mod h1:gNpI2Ol+lRS3WwSOtIUUtRwZEQMXjYK+dQSBFbethAk=
|
||||
github.com/marten-seemann/qtls-go1-17 v0.1.0-beta.1.2 h1:SficYjyOthSrliKI+EaFuXS6HqSsX3dkY9AqxAAjBjw=
|
||||
github.com/marten-seemann/qtls-go1-17 v0.1.0-beta.1.2/go.mod h1:fz4HIxByo+LlWcreM4CZOYNuz3taBQ8rN2X6FqvaWo8=
|
||||
github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
|
||||
github.com/mattn/go-colorable v0.1.8 h1:c1ghPdyEDarC70ftn0y+A/Ee++9zz8ljHG1b13eJ0s8=
|
||||
github.com/mattn/go-colorable v0.1.8/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
|
||||
@@ -492,6 +491,7 @@ github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLA
|
||||
github.com/nrdcg/auroradns v1.0.0/go.mod h1:6JPXKzIRzZzMqtTDgueIhTi6rFf1QvYE/HzqidhOhjw=
|
||||
github.com/nrdcg/goinwx v0.6.1/go.mod h1:XPiut7enlbEdntAqalBIqcYcTEVhpv/dKWgDCX2SwKQ=
|
||||
github.com/nrdcg/namesilo v0.2.1/go.mod h1:lwMvfQTyYq+BbjJd30ylEG4GPSS6PII0Tia4rRpRiyw=
|
||||
github.com/nxadm/tail v1.4.4 h1:DQuhQpB1tVlglWS2hLQ5OV6B5r8aGxSrPc5Qo6uTN78=
|
||||
github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A=
|
||||
github.com/oklog/oklog v0.3.2/go.mod h1:FCV+B7mhrz4o+ueLpx+KqkyXRGMWOYEvfiXtdGtbWGs=
|
||||
github.com/oklog/run v1.0.0/go.mod h1:dlhp/R75TPv97u0XWUtDeV/lRKWPKSdTuV0TZvrmrQA=
|
||||
@@ -502,11 +502,13 @@ github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+W
|
||||
github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
||||
github.com/onsi/ginkgo v1.11.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
||||
github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk=
|
||||
github.com/onsi/ginkgo v1.14.0 h1:2mOpI4JVVPBN+WQRa0WKH2eXR+Ey+uK4n7Zj0aYpIQA=
|
||||
github.com/onsi/ginkgo v1.14.0/go.mod h1:iSB4RoI2tjJc9BBv4NKIKWKya62Rps+oPG/Lv9klQyY=
|
||||
github.com/onsi/gomega v0.0.0-20170829124025-dcabb60a477c/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA=
|
||||
github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
|
||||
github.com/onsi/gomega v1.7.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
|
||||
github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY=
|
||||
github.com/onsi/gomega v1.10.1 h1:o0+MgICZLuZ7xjH7Vx6zS/zcu93/BEp1VwkIW1mEXCE=
|
||||
github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo=
|
||||
github.com/op/go-logging v0.0.0-20160315200505-970db520ece7/go.mod h1:HzydrMdWErDVzsI23lYNej1Htcns9BCg93Dk0bBINWk=
|
||||
github.com/opentracing-contrib/go-observer v0.0.0-20170622124052-a52f23424492/go.mod h1:Ngi6UdF0k5OKD5t5wlmGhe/EDKPoUM3BXZSSfIuJbis=
|
||||
@@ -588,7 +590,6 @@ github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFR
|
||||
github.com/rs/xid v1.2.1/go.mod h1:+uKXf+4Djp6Md1KODXJxgGQPKngRmWyn10oCKFzNHOQ=
|
||||
github.com/rs/zerolog v1.20.0 h1:38k9hgtUBdxFwE34yS8rTHmHBa4eN16E4DJlv177LNs=
|
||||
github.com/rs/zerolog v1.20.0/go.mod h1:IzD0RJ65iWH0w97OQQebJEvTZYvsCUm9WVLWBQrJRjo=
|
||||
github.com/russross/blackfriday v0.0.0-20170610170232-067529f716f4 h1:S9YlS71UNJIyS61OqGAmLXv3w5zclSidN+qwr80XxKs=
|
||||
github.com/russross/blackfriday v0.0.0-20170610170232-067529f716f4/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g=
|
||||
github.com/russross/blackfriday v1.5.2 h1:HyvC0ARfnZBqnXwABFeSZHpKvJHJJfPz81GNueLj0oo=
|
||||
github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g=
|
||||
@@ -671,7 +672,6 @@ github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q
|
||||
github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
|
||||
go.etcd.io/bbolt v1.3.3/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=
|
||||
go.etcd.io/etcd v0.0.0-20191023171146-3cf2f69b5738/go.mod h1:dnLIgRNXwCJa5e+c6mIZCrds/GIG4ncV9HhK5PX7jPg=
|
||||
go.etcd.io/etcd v0.5.0-alpha.5.0.20200306183522-221f0cc107cb/go.mod h1:VZB9Yx4s43MHItytoe8jcvaEFEgF2QzHDZGfQ/XQjvQ=
|
||||
@@ -752,7 +752,6 @@ golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzB
|
||||
golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
|
||||
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/net v0.0.0-20170114055629-f2499483f923/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20180611182652-db08ff08e862/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
@@ -881,8 +880,7 @@ golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7w
|
||||
golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4 h1:myAQVi0cGEoqQVR5POX+8RR2mrocKqNN1hmeMqhX27k=
|
||||
golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20201231184435-2d18734c6014/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210510120138-977fb7262007 h1:gG67DSER+11cZvqIMb8S8bt0vZtiN6xWYARwirrOSfE=
|
||||
golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
@@ -953,7 +951,6 @@ golang.org/x/tools v0.0.0-20200618134242-20370b0cb4b2/go.mod h1:EkVYQZoAsY45+roY
|
||||
golang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
|
||||
golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
|
||||
golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
|
||||
golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
|
||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
@@ -1084,6 +1081,7 @@ gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo=
|
||||
gopkg.in/square/go-jose.v2 v2.3.1/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI=
|
||||
gopkg.in/square/go-jose.v2 v2.4.0 h1:0kXPskUMGAXXWJlP05ktEMOV0vmzFQUWw6d+aZJQU8A=
|
||||
gopkg.in/square/go-jose.v2 v2.4.0/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI=
|
||||
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ=
|
||||
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
|
||||
gopkg.in/warnings.v0 v0.1.2/go.mod h1:jksf8JmL6Qr/oQM2OXTHunEvvTAsrWBLb6OOjuVWRNI=
|
||||
gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74=
|
||||
|
||||
@@ -63,6 +63,7 @@ func (p *Proxy) ProxyHTTP(
|
||||
|
||||
cfRay := connection.FindCfRayHeader(req)
|
||||
lbProbe := connection.IsLBProbeRequest(req)
|
||||
p.appendTagHeaders(req)
|
||||
|
||||
rule, ruleNum := p.ingressRules.FindMatchingRule(req.Host, req.URL.Path)
|
||||
logFields := logFields{
|
||||
|
||||
@@ -30,7 +30,7 @@ import (
|
||||
)
|
||||
|
||||
var (
|
||||
testTags = []tunnelpogs.Tag(nil)
|
||||
testTags = []tunnelpogs.Tag{tunnelpogs.Tag{Name: "Name", Value: "value"}}
|
||||
unusedWarpRoutingService = (*ingress.WarpRoutingService)(nil)
|
||||
)
|
||||
|
||||
@@ -150,6 +150,9 @@ func testProxyHTTP(proxy connection.OriginProxy) func(t *testing.T) {
|
||||
|
||||
err = proxy.ProxyHTTP(responseWriter, req, false)
|
||||
require.NoError(t, err)
|
||||
for _, tag := range testTags {
|
||||
assert.Equal(t, tag.Value, req.Header.Get(TagHeaderNamePrefix+tag.Name))
|
||||
}
|
||||
|
||||
assert.Equal(t, http.StatusOK, responseWriter.Code)
|
||||
}
|
||||
|
||||
@@ -4,7 +4,6 @@ import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net"
|
||||
"time"
|
||||
|
||||
"github.com/google/uuid"
|
||||
@@ -12,6 +11,7 @@ import (
|
||||
|
||||
"github.com/cloudflare/cloudflared/connection"
|
||||
"github.com/cloudflare/cloudflared/edgediscovery"
|
||||
"github.com/cloudflare/cloudflared/edgediscovery/allregions"
|
||||
"github.com/cloudflare/cloudflared/h2mux"
|
||||
"github.com/cloudflare/cloudflared/retry"
|
||||
"github.com/cloudflare/cloudflared/signal"
|
||||
@@ -60,7 +60,7 @@ var errEarlyShutdown = errors.New("shutdown started")
|
||||
|
||||
type tunnelError struct {
|
||||
index int
|
||||
addr *net.TCPAddr
|
||||
addr *allregions.EdgeAddr
|
||||
err error
|
||||
}
|
||||
|
||||
@@ -74,7 +74,7 @@ func NewSupervisor(config *TunnelConfig, reconnectCh chan ReconnectSignal, grace
|
||||
if len(config.EdgeAddrs) > 0 {
|
||||
edgeIPs, err = edgediscovery.StaticEdge(config.Log, config.EdgeAddrs)
|
||||
} else {
|
||||
edgeIPs, err = edgediscovery.ResolveEdge(config.Log)
|
||||
edgeIPs, err = edgediscovery.ResolveEdge(config.Log, "")
|
||||
}
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -226,7 +226,7 @@ func (s *Supervisor) startFirstTunnel(
|
||||
connectedSignal *signal.Signal,
|
||||
) {
|
||||
var (
|
||||
addr *net.TCPAddr
|
||||
addr *allregions.EdgeAddr
|
||||
err error
|
||||
)
|
||||
const firstConnIndex = 0
|
||||
@@ -294,7 +294,7 @@ func (s *Supervisor) startTunnel(
|
||||
connectedSignal *signal.Signal,
|
||||
) {
|
||||
var (
|
||||
addr *net.TCPAddr
|
||||
addr *allregions.EdgeAddr
|
||||
err error
|
||||
)
|
||||
defer func() {
|
||||
@@ -347,7 +347,7 @@ func (s *Supervisor) authenticate(ctx context.Context, numPreviousAttempts int)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
edgeConn, err := edgediscovery.DialEdge(ctx, dialTimeout, s.config.EdgeTLSConfigs[connection.H2mux], arbitraryEdgeIP)
|
||||
edgeConn, err := edgediscovery.DialEdge(ctx, dialTimeout, s.config.EdgeTLSConfigs[connection.H2mux], arbitraryEdgeIP.TCP)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
+106
-14
@@ -11,12 +11,14 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/google/uuid"
|
||||
"github.com/lucas-clemente/quic-go"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/rs/zerolog"
|
||||
"golang.org/x/sync/errgroup"
|
||||
|
||||
"github.com/cloudflare/cloudflared/connection"
|
||||
"github.com/cloudflare/cloudflared/edgediscovery"
|
||||
"github.com/cloudflare/cloudflared/edgediscovery/allregions"
|
||||
"github.com/cloudflare/cloudflared/h2mux"
|
||||
"github.com/cloudflare/cloudflared/retry"
|
||||
"github.com/cloudflare/cloudflared/signal"
|
||||
@@ -125,7 +127,7 @@ func ServeTunnelLoop(
|
||||
ctx context.Context,
|
||||
credentialManager *reconnectCredentialManager,
|
||||
config *TunnelConfig,
|
||||
addr *net.TCPAddr,
|
||||
addr *allregions.EdgeAddr,
|
||||
connIndex uint8,
|
||||
connectedSignal *signal.Signal,
|
||||
cloudflaredUUID uuid.UUID,
|
||||
@@ -246,7 +248,7 @@ func ServeTunnel(
|
||||
connLog *zerolog.Logger,
|
||||
credentialManager *reconnectCredentialManager,
|
||||
config *TunnelConfig,
|
||||
addr *net.TCPAddr,
|
||||
addr *allregions.EdgeAddr,
|
||||
connIndex uint8,
|
||||
fuse *h2mux.BooleanFuse,
|
||||
backoff *protocolFallback,
|
||||
@@ -270,16 +272,39 @@ func ServeTunnel(
|
||||
|
||||
defer config.Observer.SendDisconnect(connIndex)
|
||||
|
||||
edgeConn, err := edgediscovery.DialEdge(ctx, dialTimeout, config.EdgeTLSConfigs[protocol], addr)
|
||||
if err != nil {
|
||||
connLog.Err(err).Msg("Unable to establish connection with Cloudflare edge")
|
||||
return err, true
|
||||
}
|
||||
connectedFuse := &connectedFuse{
|
||||
fuse: fuse,
|
||||
backoff: backoff,
|
||||
}
|
||||
|
||||
controlStream := connection.NewControlStream(
|
||||
config.Observer,
|
||||
connectedFuse,
|
||||
config.NamedTunnel,
|
||||
connIndex,
|
||||
nil,
|
||||
gracefulShutdownC,
|
||||
config.ConnectionConfig.GracePeriod,
|
||||
)
|
||||
|
||||
if protocol == connection.QUIC {
|
||||
connOptions := config.ConnectionOptions(addr.UDP.String(), uint8(backoff.Retries()))
|
||||
return ServeQUIC(ctx,
|
||||
addr.UDP,
|
||||
config,
|
||||
connOptions,
|
||||
controlStream,
|
||||
connectedFuse,
|
||||
reconnectCh,
|
||||
gracefulShutdownC)
|
||||
}
|
||||
|
||||
edgeConn, err := edgediscovery.DialEdge(ctx, dialTimeout, config.EdgeTLSConfigs[protocol], addr.TCP)
|
||||
if err != nil {
|
||||
connLog.Err(err).Msg("Unable to establish connection with Cloudflare edge")
|
||||
return err, true
|
||||
}
|
||||
|
||||
if protocol == connection.HTTP2 {
|
||||
connOptions := config.ConnectionOptions(edgeConn.LocalAddr().String(), uint8(backoff.Retries()))
|
||||
err = ServeHTTP2(
|
||||
@@ -288,10 +313,10 @@ func ServeTunnel(
|
||||
config,
|
||||
edgeConn,
|
||||
connOptions,
|
||||
controlStream,
|
||||
connIndex,
|
||||
connectedFuse,
|
||||
reconnectCh,
|
||||
gracefulShutdownC,
|
||||
reconnectCh,
|
||||
)
|
||||
} else {
|
||||
err = ServeH2mux(
|
||||
@@ -402,22 +427,20 @@ func ServeHTTP2(
|
||||
config *TunnelConfig,
|
||||
tlsServerConn net.Conn,
|
||||
connOptions *tunnelpogs.ConnectionOptions,
|
||||
controlStreamHandler connection.ControlStreamHandler,
|
||||
connIndex uint8,
|
||||
connectedFuse connection.ConnectedFuse,
|
||||
reconnectCh chan ReconnectSignal,
|
||||
gracefulShutdownC <-chan struct{},
|
||||
reconnectCh chan ReconnectSignal,
|
||||
) error {
|
||||
connLog.Debug().Msgf("Connecting via http2")
|
||||
h2conn := connection.NewHTTP2Connection(
|
||||
tlsServerConn,
|
||||
config.ConnectionConfig,
|
||||
config.NamedTunnel,
|
||||
connOptions,
|
||||
config.Observer,
|
||||
connIndex,
|
||||
connectedFuse,
|
||||
controlStreamHandler,
|
||||
config.Log,
|
||||
gracefulShutdownC,
|
||||
)
|
||||
|
||||
errGroup, serveCtx := errgroup.WithContext(ctx)
|
||||
@@ -437,6 +460,75 @@ func ServeHTTP2(
|
||||
return errGroup.Wait()
|
||||
}
|
||||
|
||||
func ServeQUIC(
|
||||
ctx context.Context,
|
||||
edgeAddr *net.UDPAddr,
|
||||
config *TunnelConfig,
|
||||
connOptions *tunnelpogs.ConnectionOptions,
|
||||
controlStreamHandler connection.ControlStreamHandler,
|
||||
connectedFuse connection.ConnectedFuse,
|
||||
reconnectCh chan ReconnectSignal,
|
||||
gracefulShutdownC <-chan struct{},
|
||||
) (err error, recoverable bool) {
|
||||
tlsConfig := config.EdgeTLSConfigs[connection.QUIC]
|
||||
quicConfig := &quic.Config{
|
||||
HandshakeIdleTimeout: time.Second * 10,
|
||||
KeepAlive: true,
|
||||
}
|
||||
for {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return
|
||||
default:
|
||||
quicConn, err := connection.NewQUICConnection(
|
||||
ctx,
|
||||
quicConfig,
|
||||
edgeAddr,
|
||||
tlsConfig,
|
||||
config.ConnectionConfig.OriginProxy,
|
||||
connOptions,
|
||||
controlStreamHandler,
|
||||
config.Observer)
|
||||
if err != nil {
|
||||
config.Log.Error().Msgf("Failed to create new quic connection, err: %v", err)
|
||||
return err, true
|
||||
}
|
||||
|
||||
errGroup, serveCtx := errgroup.WithContext(ctx)
|
||||
errGroup.Go(func() error {
|
||||
err := quicConn.Serve(ctx)
|
||||
if err != nil {
|
||||
config.Log.Error().Msgf("Failed to serve quic connection, err: %v", err)
|
||||
}
|
||||
return fmt.Errorf("Connection with edge closed")
|
||||
})
|
||||
|
||||
errGroup.Go(func() error {
|
||||
return listenReconnect(serveCtx, reconnectCh, gracefulShutdownC)
|
||||
})
|
||||
|
||||
err = errGroup.Wait()
|
||||
if err == nil {
|
||||
return nil, false
|
||||
}
|
||||
config.Log.Info().Msg("Reconnecting with the same udp conn")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
type quicLogger struct {
|
||||
*zerolog.Logger
|
||||
}
|
||||
|
||||
func (ql *quicLogger) Write(p []byte) (n int, err error) {
|
||||
ql.Debug().Msgf("quic log: %v", string(p))
|
||||
return len(p), nil
|
||||
}
|
||||
|
||||
func (ql *quicLogger) Close() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func listenReconnect(ctx context.Context, reconnectCh <-chan ReconnectSignal, gracefulShutdownCh <-chan struct{}) error {
|
||||
select {
|
||||
case reconnect := <-reconnectCh:
|
||||
|
||||
@@ -29,6 +29,15 @@ type Metadata struct {
|
||||
Val string `capnp:"val"`
|
||||
}
|
||||
|
||||
// MetadataMap returns a map format of []Metadata.
|
||||
func (r *ConnectRequest) MetadataMap() map[string]string {
|
||||
metadataMap := make(map[string]string)
|
||||
for _, metadata := range r.Metadata {
|
||||
metadataMap[metadata.Key] = metadata.Val
|
||||
}
|
||||
return metadataMap
|
||||
}
|
||||
|
||||
func (r *ConnectRequest) fromPogs(msg *capnp.Message) error {
|
||||
metadata, err := schema.ReadRootConnectRequest(msg)
|
||||
if err != nil {
|
||||
|
||||
+34
-2
@@ -12,12 +12,25 @@ import (
|
||||
// before writing the metadata.
|
||||
var protocolSignature = []byte{0x0A, 0x36, 0xCD, 0x12, 0xA1, 0x3E}
|
||||
|
||||
const protocolVersionLength = 2
|
||||
|
||||
type protocolVersion string
|
||||
|
||||
const (
|
||||
protocolV1 protocolVersion = "01"
|
||||
)
|
||||
|
||||
// ReadConnectRequestData reads the handshake data from a QUIC stream.
|
||||
func ReadConnectRequestData(stream io.Reader) (*ConnectRequest, error) {
|
||||
if err := verifySignature(stream); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// This is a NO-OP for now. We could cause a branching if we wanted to use multiple versions.
|
||||
if _, err := readVersion(stream); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
msg, err := capnp.NewDecoder(stream).Decode()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -55,6 +68,11 @@ func ReadConnectResponseData(stream io.Reader) (*ConnectResponse, error) {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// This is a NO-OP for now. We could cause a branching if we wanted to use multiple versions.
|
||||
if _, err := readVersion(stream); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
msg, err := capnp.NewDecoder(stream).Decode()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -92,8 +110,22 @@ func WriteConnectResponseData(stream io.Writer, respErr error, metadata ...Metad
|
||||
}
|
||||
|
||||
func writePreamble(stream io.Writer) error {
|
||||
return writeSignature(stream)
|
||||
// TODO : TUN-4613 Write protocol version here
|
||||
if err := writeSignature(stream); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return writeVersion(stream)
|
||||
}
|
||||
|
||||
func writeVersion(stream io.Writer) error {
|
||||
_, err := stream.Write([]byte(protocolV1)[:protocolVersionLength])
|
||||
return err
|
||||
}
|
||||
|
||||
func readVersion(stream io.Reader) (string, error) {
|
||||
version := make([]byte, protocolVersionLength)
|
||||
_, err := stream.Read(version)
|
||||
return string(version), err
|
||||
}
|
||||
|
||||
func writeSignature(stream io.Writer) error {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
FROM python:3-buster
|
||||
|
||||
RUN wget https://bin.equinox.io/c/VdrWdbjqyF/cloudflared-stable-linux-amd64.deb \
|
||||
&& dpkg -i cloudflared-stable-linux-amd64.deb
|
||||
RUN wget https://github.com/cloudflare/cloudflared/releases/latest/download/cloudflared-linux-amd64.deb \
|
||||
&& dpkg -i cloudflared-linux-amd64.deb
|
||||
|
||||
RUN pip install pexpect
|
||||
|
||||
|
||||
+5
@@ -0,0 +1,5 @@
|
||||
root = true
|
||||
|
||||
[*]
|
||||
indent_style = tab
|
||||
indent_size = 2
|
||||
+8
-3
@@ -8,12 +8,17 @@
|
||||
[](https://ci.appveyor.com/project/lucas-clemente/quic-go/branch/master)
|
||||
[](https://codecov.io/gh/lucas-clemente/quic-go/)
|
||||
|
||||
quic-go is an implementation of the [QUIC protocol, RFC 9000](https://datatracker.ietf.org/doc/html/rfc9000) protocol in Go.
|
||||
In addition to RFC 9000, it currently implements the [IETF QUIC draft-29](https://tools.ietf.org/html/draft-ietf-quic-transport-29), [draft-32](https://tools.ietf.org/html/draft-ietf-quic-transport-32) and [draft-34](https://tools.ietf.org/html/draft-ietf-quic-transport-34). Support for draft versions will be eventually be dropped, as these are phased out of the ecosystem.
|
||||
quic-go is an implementation of the [QUIC](https://en.wikipedia.org/wiki/QUIC) protocol in Go. It implements the [IETF QUIC draft-29](https://tools.ietf.org/html/draft-ietf-quic-transport-29), [draft-32](https://tools.ietf.org/html/draft-ietf-quic-transport-32) and [draft-34](https://tools.ietf.org/html/draft-ietf-quic-transport-34).
|
||||
|
||||
## Version compatibility
|
||||
|
||||
Since quic-go is under active development, there's no guarantee that two builds of different commits are interoperable. The QUIC version used in the *master* branch is just a placeholder, and should not be considered stable.
|
||||
|
||||
When using quic-go as a library, please always use a [tagged release](https://github.com/lucas-clemente/quic-go/releases). Only these releases use the official draft version numbers.
|
||||
|
||||
## Guides
|
||||
|
||||
*We currently support Go 1.15.x, Go 1.16.x and Go 1.17 Beta 1, with [Go modules](https://github.com/golang/go/wiki/Modules) support enabled.*
|
||||
*We currently support Go 1.15+, with [Go modules](https://github.com/golang/go/wiki/Modules) support enabled.*
|
||||
|
||||
Running tests:
|
||||
|
||||
|
||||
+7
-14
@@ -37,9 +37,8 @@ type client struct {
|
||||
|
||||
session quicSession
|
||||
|
||||
tracer logging.ConnectionTracer
|
||||
tracingID uint64
|
||||
logger utils.Logger
|
||||
tracer logging.ConnectionTracer
|
||||
logger utils.Logger
|
||||
}
|
||||
|
||||
var (
|
||||
@@ -203,16 +202,8 @@ func dialContext(
|
||||
}
|
||||
c.packetHandlers = packetHandlers
|
||||
|
||||
c.tracingID = nextSessionTracingID()
|
||||
if c.config.Tracer != nil {
|
||||
c.tracer = c.config.Tracer.TracerForConnection(
|
||||
context.WithValue(ctx, SessionTracingKey, c.tracingID),
|
||||
protocol.PerspectiveClient,
|
||||
c.destConnID,
|
||||
)
|
||||
}
|
||||
if c.tracer != nil {
|
||||
c.tracer.StartedConnection(c.conn.LocalAddr(), c.conn.RemoteAddr(), c.srcConnID, c.destConnID)
|
||||
c.tracer = c.config.Tracer.TracerForConnection(protocol.PerspectiveClient, c.destConnID)
|
||||
}
|
||||
if err := c.dial(ctx); err != nil {
|
||||
return nil, err
|
||||
@@ -279,6 +270,9 @@ func newClient(
|
||||
|
||||
func (c *client) dial(ctx context.Context) error {
|
||||
c.logger.Infof("Starting new connection to %s (%s -> %s), source connection ID %s, destination connection ID %s, version %s", c.tlsConf.ServerName, c.conn.LocalAddr(), c.conn.RemoteAddr(), c.srcConnID, c.destConnID, c.version)
|
||||
if c.tracer != nil {
|
||||
c.tracer.StartedConnection(c.conn.LocalAddr(), c.conn.RemoteAddr(), c.version, c.srcConnID, c.destConnID)
|
||||
}
|
||||
|
||||
c.session = newClientSession(
|
||||
c.conn,
|
||||
@@ -291,7 +285,6 @@ func (c *client) dial(ctx context.Context) error {
|
||||
c.use0RTT,
|
||||
c.hasNegotiatedVersion,
|
||||
c.tracer,
|
||||
c.tracingID,
|
||||
c.logger,
|
||||
c.version,
|
||||
)
|
||||
@@ -300,7 +293,7 @@ func (c *client) dial(ctx context.Context) error {
|
||||
errorChan := make(chan error, 1)
|
||||
go func() {
|
||||
err := c.session.run() // returns as soon as the session is closed
|
||||
if !errors.Is(err, &errCloseForRecreating{}) && c.createdPacketConn {
|
||||
if !errors.Is(err, errCloseForRecreating{}) && c.createdPacketConn {
|
||||
c.packetHandlers.Destroy()
|
||||
}
|
||||
errorChan <- err
|
||||
|
||||
+2
-8
@@ -73,10 +73,7 @@ func (m *connIDGenerator) SetMaxActiveConnIDs(limit uint64) error {
|
||||
|
||||
func (m *connIDGenerator) Retire(seq uint64, sentWithDestConnID protocol.ConnectionID) error {
|
||||
if seq > m.highestSeq {
|
||||
return &qerr.TransportError{
|
||||
ErrorCode: qerr.ProtocolViolation,
|
||||
ErrorMessage: fmt.Sprintf("retired connection ID %d (highest issued: %d)", seq, m.highestSeq),
|
||||
}
|
||||
return qerr.NewError(qerr.ProtocolViolation, fmt.Sprintf("tried to retire connection ID %d. Highest issued: %d", seq, m.highestSeq))
|
||||
}
|
||||
connID, ok := m.activeSrcConnIDs[seq]
|
||||
// We might already have deleted this connection ID, if this is a duplicate frame.
|
||||
@@ -84,10 +81,7 @@ func (m *connIDGenerator) Retire(seq uint64, sentWithDestConnID protocol.Connect
|
||||
return nil
|
||||
}
|
||||
if connID.Equal(sentWithDestConnID) && !protocol.UseRetireBugBackwardsCompatibilityMode(RetireBugBackwardsCompatibilityMode, m.version) {
|
||||
return &qerr.TransportError{
|
||||
ErrorCode: qerr.ProtocolViolation,
|
||||
ErrorMessage: fmt.Sprintf("retired connection ID %d (%s), which was used as the Destination Connection ID on this packet", seq, connID),
|
||||
}
|
||||
return qerr.NewError(qerr.ProtocolViolation, fmt.Sprintf("tried to retire connection ID %d (%s), which was used as the Destination Connection ID on this packet", seq, connID))
|
||||
}
|
||||
m.retireConnectionID(connID)
|
||||
delete(m.activeSrcConnIDs, seq)
|
||||
|
||||
+1
-1
@@ -53,7 +53,7 @@ func (h *connIDManager) Add(f *wire.NewConnectionIDFrame) error {
|
||||
return err
|
||||
}
|
||||
if h.queue.Len() >= protocol.MaxActiveConnectionIDs {
|
||||
return &qerr.TransportError{ErrorCode: qerr.ConnectionIDLimitError}
|
||||
return qerr.ConnectionIDLimitError
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
+1
-15
@@ -11,21 +11,7 @@ import (
|
||||
"golang.org/x/sys/windows"
|
||||
)
|
||||
|
||||
const IP_DONTFRAGMENT = 14
|
||||
|
||||
func newConn(c OOBCapablePacketConn) (connection, error) {
|
||||
rawConn, err := c.SyscallConn()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("couldn't get syscall.RawConn: %w", err)
|
||||
}
|
||||
if err := rawConn.Control(func(fd uintptr) {
|
||||
// This should succeed if the connection is a IPv4 or a dual-stack connection.
|
||||
// It will fail for IPv6 connections.
|
||||
// TODO: properly handle error.
|
||||
_ = windows.SetsockoptInt(windows.Handle(fd), windows.IPPROTO_IP, IP_DONTFRAGMENT, 1)
|
||||
}); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
func newConn(c net.PacketConn) (connection, error) {
|
||||
return &basicConn{PacketConn: c}, nil
|
||||
}
|
||||
|
||||
|
||||
+3
-12
@@ -39,18 +39,12 @@ func newCryptoStream() cryptoStream {
|
||||
func (s *cryptoStreamImpl) HandleCryptoFrame(f *wire.CryptoFrame) error {
|
||||
highestOffset := f.Offset + protocol.ByteCount(len(f.Data))
|
||||
if maxOffset := highestOffset; maxOffset > protocol.MaxCryptoStreamOffset {
|
||||
return &qerr.TransportError{
|
||||
ErrorCode: qerr.CryptoBufferExceeded,
|
||||
ErrorMessage: fmt.Sprintf("received invalid offset %d on crypto stream, maximum allowed %d", maxOffset, protocol.MaxCryptoStreamOffset),
|
||||
}
|
||||
return qerr.NewError(qerr.CryptoBufferExceeded, fmt.Sprintf("received invalid offset %d on crypto stream, maximum allowed %d", maxOffset, protocol.MaxCryptoStreamOffset))
|
||||
}
|
||||
if s.finished {
|
||||
if highestOffset > s.highestOffset {
|
||||
// reject crypto data received after this stream was already finished
|
||||
return &qerr.TransportError{
|
||||
ErrorCode: qerr.ProtocolViolation,
|
||||
ErrorMessage: "received crypto data after change of encryption level",
|
||||
}
|
||||
return qerr.NewError(qerr.ProtocolViolation, "received crypto data after change of encryption level")
|
||||
}
|
||||
// ignore data with a smaller offset than the highest received
|
||||
// could e.g. be a retransmission
|
||||
@@ -86,10 +80,7 @@ func (s *cryptoStreamImpl) GetCryptoData() []byte {
|
||||
|
||||
func (s *cryptoStreamImpl) Finish() error {
|
||||
if s.queue.HasMoreData() {
|
||||
return &qerr.TransportError{
|
||||
ErrorCode: qerr.ProtocolViolation,
|
||||
ErrorMessage: "encryption level changed, but crypto stream has more data to read",
|
||||
}
|
||||
return qerr.NewError(qerr.ProtocolViolation, "encryption level changed, but crypto stream has more data to read")
|
||||
}
|
||||
s.finished = true
|
||||
return nil
|
||||
|
||||
-58
@@ -1,58 +0,0 @@
|
||||
package quic
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/lucas-clemente/quic-go/internal/qerr"
|
||||
)
|
||||
|
||||
type (
|
||||
TransportError = qerr.TransportError
|
||||
ApplicationError = qerr.ApplicationError
|
||||
VersionNegotiationError = qerr.VersionNegotiationError
|
||||
StatelessResetError = qerr.StatelessResetError
|
||||
IdleTimeoutError = qerr.IdleTimeoutError
|
||||
HandshakeTimeoutError = qerr.HandshakeTimeoutError
|
||||
)
|
||||
|
||||
type (
|
||||
TransportErrorCode = qerr.TransportErrorCode
|
||||
ApplicationErrorCode = qerr.ApplicationErrorCode
|
||||
StreamErrorCode = qerr.StreamErrorCode
|
||||
)
|
||||
|
||||
const (
|
||||
NoError = qerr.NoError
|
||||
InternalError = qerr.InternalError
|
||||
ConnectionRefused = qerr.ConnectionRefused
|
||||
FlowControlError = qerr.FlowControlError
|
||||
StreamLimitError = qerr.StreamLimitError
|
||||
StreamStateError = qerr.StreamStateError
|
||||
FinalSizeError = qerr.FinalSizeError
|
||||
FrameEncodingError = qerr.FrameEncodingError
|
||||
TransportParameterError = qerr.TransportParameterError
|
||||
ConnectionIDLimitError = qerr.ConnectionIDLimitError
|
||||
ProtocolViolation = qerr.ProtocolViolation
|
||||
InvalidToken = qerr.InvalidToken
|
||||
ApplicationErrorErrorCode = qerr.ApplicationErrorErrorCode
|
||||
CryptoBufferExceeded = qerr.CryptoBufferExceeded
|
||||
KeyUpdateError = qerr.KeyUpdateError
|
||||
AEADLimitReached = qerr.AEADLimitReached
|
||||
NoViablePathError = qerr.NoViablePathError
|
||||
)
|
||||
|
||||
// A StreamError is used for Stream.CancelRead and Stream.CancelWrite.
|
||||
// It is also returned from Stream.Read and Stream.Write if the peer canceled reading or writing.
|
||||
type StreamError struct {
|
||||
StreamID StreamID
|
||||
ErrorCode StreamErrorCode
|
||||
}
|
||||
|
||||
func (e *StreamError) Is(target error) bool {
|
||||
_, ok := target.(*StreamError)
|
||||
return ok
|
||||
}
|
||||
|
||||
func (e *StreamError) Error() string {
|
||||
return fmt.Sprintf("stream %d canceled with error code %d", e.StreamID, e.ErrorCode)
|
||||
}
|
||||
+5
-6
@@ -1,20 +1,19 @@
|
||||
module github.com/lucas-clemente/quic-go
|
||||
|
||||
go 1.15
|
||||
go 1.14
|
||||
|
||||
require (
|
||||
github.com/cheekybits/genny v1.0.0
|
||||
github.com/francoispqt/gojay v1.2.13
|
||||
github.com/golang/mock v1.6.0
|
||||
github.com/golang/mock v1.5.0
|
||||
github.com/marten-seemann/qpack v0.2.1
|
||||
github.com/marten-seemann/qtls-go1-15 v0.1.4
|
||||
github.com/marten-seemann/qtls-go1-16 v0.1.3
|
||||
github.com/marten-seemann/qtls-go1-17 v0.1.0-beta.1.2
|
||||
github.com/onsi/ginkgo v1.14.0
|
||||
github.com/onsi/gomega v1.10.1
|
||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9
|
||||
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4
|
||||
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c
|
||||
golang.org/x/sys v0.0.0-20210510120138-977fb7262007
|
||||
golang.org/x/net v0.0.0-20200707034311-ab3426394381
|
||||
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e
|
||||
golang.org/x/sys v0.0.0-20201231184435-2d18734c6014
|
||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 // indirect
|
||||
)
|
||||
|
||||
+10
-20
@@ -34,8 +34,8 @@ github.com/golang/lint v0.0.0-20180702182130-06c8688daad7/go.mod h1:tluoj9z5200j
|
||||
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
|
||||
github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
|
||||
github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4=
|
||||
github.com/golang/mock v1.6.0 h1:ErTB+efbowRARo13NNdxyJji2egdxLGQhRaY+DUumQc=
|
||||
github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs=
|
||||
github.com/golang/mock v1.5.0 h1:jlYHihg//f7RRwuPfptm04yp4s7O6Kw8EZiVYIGcH0g=
|
||||
github.com/golang/mock v1.5.0/go.mod h1:CWnOUgYIOo4TcNZ0wHX3YZCqsaM1I1Jvs6v3mP3KVu8=
|
||||
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=
|
||||
@@ -79,8 +79,6 @@ github.com/marten-seemann/qtls-go1-15 v0.1.4 h1:RehYMOyRW8hPVEja1KBVsFVNSm35Jj9M
|
||||
github.com/marten-seemann/qtls-go1-15 v0.1.4/go.mod h1:GyFwywLKkRt+6mfU99csTEY1joMZz5vmB1WNZH3P81I=
|
||||
github.com/marten-seemann/qtls-go1-16 v0.1.3 h1:XEZ1xGorVy9u+lJq+WXNE+hiqRYLNvJGYmwfwKQN2gU=
|
||||
github.com/marten-seemann/qtls-go1-16 v0.1.3/go.mod h1:gNpI2Ol+lRS3WwSOtIUUtRwZEQMXjYK+dQSBFbethAk=
|
||||
github.com/marten-seemann/qtls-go1-17 v0.1.0-beta.1.2 h1:SficYjyOthSrliKI+EaFuXS6HqSsX3dkY9AqxAAjBjw=
|
||||
github.com/marten-seemann/qtls-go1-17 v0.1.0-beta.1.2/go.mod h1:fz4HIxByo+LlWcreM4CZOYNuz3taBQ8rN2X6FqvaWo8=
|
||||
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
|
||||
github.com/microcosm-cc/bluemonday v1.0.1/go.mod h1:hsXNsILzKxV+sX77C5b8FSuKF00vh2OMYv+xgHpAMF4=
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||
@@ -135,7 +133,6 @@ github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXf
|
||||
github.com/tarm/serial v0.0.0-20180830185346-98f6abe2eb07/go.mod h1:kDXzergiv9cbyO7IOYJZWg1U88JhDg3PB6klq9Hg2pA=
|
||||
github.com/viant/assertly v0.4.8/go.mod h1:aGifi++jvCrUaklKEKT0BU95igDNaqkvz+49uaYMPRU=
|
||||
github.com/viant/toolbox v0.24.0/go.mod h1:OxMCG57V0PXuIP2HNQrtJf2CjqdmbrOx5EkMILuUhzM=
|
||||
github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
|
||||
go.opencensus.io v0.18.0/go.mod h1:vKdFvxhtzZ9onBp9VKHK8z/sRpBMnKAsufL7wlDrCOA=
|
||||
go4.org v0.0.0-20180809161055-417644f6feb5/go.mod h1:MkTOUMDaeVYJUOUsaDXIhWPZYa1yOyC1qaOBpL57BhE=
|
||||
golang.org/x/build v0.0.0-20190111050920-041ab4dc3f9d/go.mod h1:OWs+y06UdEOHN4y+MfF/py+xQ/tYqIWW03b70/CG9Rw=
|
||||
@@ -150,7 +147,7 @@ golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL
|
||||
golang.org/x/lint v0.0.0-20180702182130-06c8688daad7/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
||||
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
||||
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
|
||||
golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
@@ -163,9 +160,8 @@ golang.org/x/net v0.0.0-20190313220215-9f648a60d977/go.mod h1:t9HGtf8HONx5eT2rtn
|
||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
||||
golang.org/x/net v0.0.0-20200707034311-ab3426394381 h1:VXak5I6aEWmAXeQjA+QSZzlgNrpq9mjcfDemuexIKsU=
|
||||
golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
|
||||
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4 h1:4nGaVu0QrbjT/AK2PRLuQfQuh6DJve+pELhqTdAj3x0=
|
||||
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
|
||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||
golang.org/x/oauth2 v0.0.0-20181017192945-9dcd33a902f4/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||
golang.org/x/oauth2 v0.0.0-20181203162652-d668ce993890/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||
@@ -176,8 +172,8 @@ golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJ
|
||||
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c h1:5KslGYwFpkhGh+Q16bwMP3cOontH8FOep7tGV86Y7SQ=
|
||||
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e h1:vcxGaoTs7kV8m5Np9uUNQin4BrLOthgV7252N8V+FwY=
|
||||
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20181029174526-d69651ed3497/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
@@ -190,16 +186,12 @@ golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7w
|
||||
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200519105757-fe76b779f299/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210510120138-977fb7262007 h1:gG67DSER+11cZvqIMb8S8bt0vZtiN6xWYARwirrOSfE=
|
||||
golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/sys v0.0.0-20201231184435-2d18734c6014 h1:joucsQqXmyBVxViHCPFjG3hx8JzIFSaym3l3MM/Jsdg=
|
||||
golang.org/x/sys v0.0.0-20201231184435-2d18734c6014/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs=
|
||||
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
||||
golang.org/x/text v0.3.3 h1:cokOdA+Jmi5PJGXLlLllQSgYigAEfHXJAERHVMaCc2k=
|
||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
@@ -209,12 +201,10 @@ golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGm
|
||||
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
|
||||
golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
|
||||
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
|
||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE=
|
||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
google.golang.org/api v0.0.0-20180910000450-7ca32eb868bf/go.mod h1:4mhQ8q/RsB7i+udVvVy5NUi08OU8ZlA0gRVgrF7VFY0=
|
||||
google.golang.org/api v0.0.0-20181030000543-1d582fd0359e/go.mod h1:4mhQ8q/RsB7i+udVvVy5NUi08OU8ZlA0gRVgrF7VFY0=
|
||||
google.golang.org/api v0.1.0/go.mod h1:UGEZY7KEX120AnNLIHFMKIo4obdJhkp2tPbaPlQx13Y=
|
||||
|
||||
+15
-24
@@ -33,10 +33,6 @@ const (
|
||||
VersionDraft29 = protocol.VersionDraft29
|
||||
// VersionDraft32 is IETF QUIC draft-32
|
||||
VersionDraft32 = protocol.VersionDraft32
|
||||
// VersionDraft34 is IETF QUIC draft-34
|
||||
VersionDraft34 = protocol.VersionDraft34
|
||||
// Version1 is RFC 9000
|
||||
Version1 = protocol.Version1
|
||||
)
|
||||
|
||||
// A Token can be used to verify the ownership of the client address.
|
||||
@@ -66,6 +62,10 @@ type TokenStore interface {
|
||||
Put(key string, token *ClientToken)
|
||||
}
|
||||
|
||||
// An ErrorCode is an application-defined error code.
|
||||
// Valid values range between 0 and MAX_UINT62.
|
||||
type ErrorCode = protocol.ApplicationErrorCode
|
||||
|
||||
// Err0RTTRejected is the returned from:
|
||||
// * Open{Uni}Stream{Sync}
|
||||
// * Accept{Uni}Stream
|
||||
@@ -73,16 +73,7 @@ type TokenStore interface {
|
||||
// when the server rejects a 0-RTT connection attempt.
|
||||
var Err0RTTRejected = errors.New("0-RTT rejected")
|
||||
|
||||
// SessionTracingKey can be used to associate a ConnectionTracer with a Session.
|
||||
// It is set on the Session.Context() context,
|
||||
// as well as on the context passed to logging.Tracer.NewConnectionTracer.
|
||||
var SessionTracingKey = sessionTracingCtxKey{}
|
||||
|
||||
type sessionTracingCtxKey struct{}
|
||||
|
||||
// Stream is the interface implemented by QUIC streams
|
||||
// In addition to the errors listed on the Session,
|
||||
// calls to stream functions can return a StreamError if the stream is canceled.
|
||||
type Stream interface {
|
||||
ReceiveStream
|
||||
SendStream
|
||||
@@ -108,7 +99,7 @@ type ReceiveStream interface {
|
||||
// It will ask the peer to stop transmitting stream data.
|
||||
// Read will unblock immediately, and future Read calls will fail.
|
||||
// When called multiple times or after reading the io.EOF it is a no-op.
|
||||
CancelRead(StreamErrorCode)
|
||||
CancelRead(ErrorCode)
|
||||
// SetReadDeadline sets the deadline for future Read calls and
|
||||
// any currently-blocked Read call.
|
||||
// A zero value for t means Read will not time out.
|
||||
@@ -137,7 +128,7 @@ type SendStream interface {
|
||||
// Data already written, but not yet delivered to the peer is not guaranteed to be delivered reliably.
|
||||
// Write will unblock immediately, and future calls to Write will fail.
|
||||
// When called multiple times or after closing the stream it is a no-op.
|
||||
CancelWrite(StreamErrorCode)
|
||||
CancelWrite(ErrorCode)
|
||||
// The context is canceled as soon as the write-side of the stream is closed.
|
||||
// This happens when Close() or CancelWrite() is called, or when the peer
|
||||
// cancels the read-side of their stream.
|
||||
@@ -151,14 +142,14 @@ type SendStream interface {
|
||||
SetWriteDeadline(t time.Time) error
|
||||
}
|
||||
|
||||
// StreamError is returned by Read and Write when the peer cancels the stream.
|
||||
type StreamError interface {
|
||||
error
|
||||
Canceled() bool
|
||||
ErrorCode() ErrorCode
|
||||
}
|
||||
|
||||
// A Session is a QUIC connection between two peers.
|
||||
// Calls to the session (and to streams) can return the following types of errors:
|
||||
// * ApplicationError: for errors triggered by the application running on top of QUIC
|
||||
// * TransportError: for errors triggered by the QUIC transport (in many cases a misbehaving peer)
|
||||
// * IdleTimeoutError: when the peer goes away unexpectedly (this is a net.Error timeout error)
|
||||
// * HandshakeTimeoutError: when the cryptographic handshake takes too long (this is a net.Error timeout error)
|
||||
// * StatelessResetError: when we receive a stateless reset (this is a net.Error temporary error)
|
||||
// * VersionNegotiationError: returned by the client, when there's no version overlap between the peers
|
||||
type Session interface {
|
||||
// AcceptStream returns the next stream opened by the peer, blocking until one is available.
|
||||
// If the session was closed due to a timeout, the error satisfies
|
||||
@@ -194,9 +185,9 @@ type Session interface {
|
||||
LocalAddr() net.Addr
|
||||
// RemoteAddr returns the address of the peer.
|
||||
RemoteAddr() net.Addr
|
||||
// CloseWithError closes the connection with an error.
|
||||
// Close the connection with an error.
|
||||
// The error string will be sent to the peer.
|
||||
CloseWithError(ApplicationErrorCode, string) error
|
||||
CloseWithError(ErrorCode, string) error
|
||||
// The context is cancelled when the session is closed.
|
||||
// Warning: This API should not be considered stable and might change soon.
|
||||
Context() context.Context
|
||||
|
||||
+1
-2
@@ -9,13 +9,12 @@ import (
|
||||
// NewAckHandler creates a new SentPacketHandler and a new ReceivedPacketHandler
|
||||
func NewAckHandler(
|
||||
initialPacketNumber protocol.PacketNumber,
|
||||
initialMaxDatagramSize protocol.ByteCount,
|
||||
rttStats *utils.RTTStats,
|
||||
pers protocol.Perspective,
|
||||
tracer logging.ConnectionTracer,
|
||||
logger utils.Logger,
|
||||
version protocol.VersionNumber,
|
||||
) (SentPacketHandler, ReceivedPacketHandler) {
|
||||
sph := newSentPacketHandler(initialPacketNumber, initialMaxDatagramSize, rttStats, pers, tracer, logger)
|
||||
sph := newSentPacketHandler(initialPacketNumber, rttStats, pers, tracer, logger)
|
||||
return sph, newReceivedPacketHandler(sph, rttStats, logger, version)
|
||||
}
|
||||
|
||||
+1
-1
@@ -27,7 +27,7 @@ type Packet struct {
|
||||
type SentPacketHandler interface {
|
||||
// SentPacket may modify the packet
|
||||
SentPacket(packet *Packet)
|
||||
ReceivedAck(ackFrame *wire.AckFrame, encLevel protocol.EncryptionLevel, recvTime time.Time) (bool /* 1-RTT packet acked */, error)
|
||||
ReceivedAck(ackFrame *wire.AckFrame, encLevel protocol.EncryptionLevel, recvTime time.Time) error
|
||||
ReceivedBytes(protocol.ByteCount)
|
||||
DropPackets(protocol.EncryptionLevel)
|
||||
ResetForRetry() error
|
||||
|
||||
Generated
Vendored
+79
-112
@@ -21,8 +21,6 @@ const (
|
||||
packetThreshold = 3
|
||||
// Before validating the client's address, the server won't send more than 3x bytes than it received.
|
||||
amplificationFactor = 3
|
||||
// We use Retry packets to derive an RTT estimate. Make sure we don't set the RTT to a super low value yet.
|
||||
minRTTAfterRetry = 5 * time.Millisecond
|
||||
)
|
||||
|
||||
type packetNumberSpace struct {
|
||||
@@ -103,7 +101,6 @@ var (
|
||||
|
||||
func newSentPacketHandler(
|
||||
initialPN protocol.PacketNumber,
|
||||
initialMaxDatagramSize protocol.ByteCount,
|
||||
rttStats *utils.RTTStats,
|
||||
pers protocol.Perspective,
|
||||
tracer logging.ConnectionTracer,
|
||||
@@ -112,7 +109,6 @@ func newSentPacketHandler(
|
||||
congestion := congestion.NewCubicSender(
|
||||
congestion.DefaultClock{},
|
||||
rttStats,
|
||||
initialMaxDatagramSize,
|
||||
true, // use Reno
|
||||
tracer,
|
||||
)
|
||||
@@ -198,17 +194,12 @@ func (h *sentPacketHandler) dropPackets(encLevel protocol.EncryptionLevel) {
|
||||
}
|
||||
|
||||
func (h *sentPacketHandler) ReceivedBytes(n protocol.ByteCount) {
|
||||
wasAmplificationLimit := h.isAmplificationLimited()
|
||||
h.bytesReceived += n
|
||||
if wasAmplificationLimit && !h.isAmplificationLimited() {
|
||||
h.setLossDetectionTimer()
|
||||
}
|
||||
}
|
||||
|
||||
func (h *sentPacketHandler) ReceivedPacket(l protocol.EncryptionLevel) {
|
||||
if h.perspective == protocol.PerspectiveServer && l == protocol.EncryptionHandshake && !h.peerAddressValidated {
|
||||
func (h *sentPacketHandler) ReceivedPacket(encLevel protocol.EncryptionLevel) {
|
||||
if h.perspective == protocol.PerspectiveServer && encLevel == protocol.EncryptionHandshake {
|
||||
h.peerAddressValidated = true
|
||||
h.setLossDetectionTimer()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -277,15 +268,12 @@ func (h *sentPacketHandler) sentPacketImpl(packet *Packet) bool /* is ack-elicit
|
||||
return isAckEliciting
|
||||
}
|
||||
|
||||
func (h *sentPacketHandler) ReceivedAck(ack *wire.AckFrame, encLevel protocol.EncryptionLevel, rcvTime time.Time) (bool /* contained 1-RTT packet */, error) {
|
||||
func (h *sentPacketHandler) ReceivedAck(ack *wire.AckFrame, encLevel protocol.EncryptionLevel, rcvTime time.Time) error {
|
||||
pnSpace := h.getPacketNumberSpace(encLevel)
|
||||
|
||||
largestAcked := ack.LargestAcked()
|
||||
if largestAcked > pnSpace.largestSent {
|
||||
return false, &qerr.TransportError{
|
||||
ErrorCode: qerr.ProtocolViolation,
|
||||
ErrorMessage: "received ACK for an unsent packet",
|
||||
}
|
||||
return qerr.NewError(qerr.ProtocolViolation, "Received ACK for an unsent packet")
|
||||
}
|
||||
|
||||
pnSpace.largestAcked = utils.MaxPacketNumber(pnSpace.largestAcked, largestAcked)
|
||||
@@ -302,7 +290,7 @@ func (h *sentPacketHandler) ReceivedAck(ack *wire.AckFrame, encLevel protocol.En
|
||||
priorInFlight := h.bytesInFlight
|
||||
ackedPackets, err := h.detectAndRemoveAckedPackets(ack, encLevel)
|
||||
if err != nil || len(ackedPackets) == 0 {
|
||||
return false, err
|
||||
return err
|
||||
}
|
||||
// update the RTT, if the largest acked is newly acknowledged
|
||||
if len(ackedPackets) > 0 {
|
||||
@@ -320,16 +308,15 @@ func (h *sentPacketHandler) ReceivedAck(ack *wire.AckFrame, encLevel protocol.En
|
||||
}
|
||||
}
|
||||
if err := h.detectLostPackets(rcvTime, encLevel); err != nil {
|
||||
return false, err
|
||||
return err
|
||||
}
|
||||
var acked1RTTPacket bool
|
||||
for _, p := range ackedPackets {
|
||||
if p.skippedPacket {
|
||||
return fmt.Errorf("received an ACK for skipped packet number: %d (%s)", p.PacketNumber, encLevel)
|
||||
}
|
||||
if p.includedInBytesInFlight && !p.declaredLost {
|
||||
h.congestion.OnPacketAcked(p.PacketNumber, p.Length, priorInFlight, rcvTime)
|
||||
}
|
||||
if p.EncryptionLevel == protocol.Encryption1RTT {
|
||||
acked1RTTPacket = true
|
||||
}
|
||||
h.removeFromBytesInFlight(p)
|
||||
}
|
||||
|
||||
@@ -348,7 +335,7 @@ func (h *sentPacketHandler) ReceivedAck(ack *wire.AckFrame, encLevel protocol.En
|
||||
|
||||
pnSpace.history.DeleteOldPackets(rcvTime)
|
||||
h.setLossDetectionTimer()
|
||||
return acked1RTTPacket, nil
|
||||
return nil
|
||||
}
|
||||
|
||||
func (h *sentPacketHandler) GetLowestPacketNotConfirmedAcked() protocol.PacketNumber {
|
||||
@@ -380,20 +367,15 @@ func (h *sentPacketHandler) detectAndRemoveAckedPackets(ack *wire.AckFrame, encL
|
||||
ackRange = ack.AckRanges[len(ack.AckRanges)-1-ackRangeIndex]
|
||||
}
|
||||
|
||||
if p.PacketNumber < ackRange.Smallest { // packet not contained in ACK range
|
||||
return true, nil
|
||||
}
|
||||
if p.PacketNumber > ackRange.Largest {
|
||||
return false, fmt.Errorf("BUG: ackhandler would have acked wrong packet %d, while evaluating range %d -> %d", p.PacketNumber, ackRange.Smallest, ackRange.Largest)
|
||||
if p.PacketNumber >= ackRange.Smallest { // packet i contained in ACK range
|
||||
if p.PacketNumber > ackRange.Largest {
|
||||
return false, fmt.Errorf("BUG: ackhandler would have acked wrong packet %d, while evaluating range %d -> %d", p.PacketNumber, ackRange.Smallest, ackRange.Largest)
|
||||
}
|
||||
h.ackedPackets = append(h.ackedPackets, p)
|
||||
}
|
||||
} else {
|
||||
h.ackedPackets = append(h.ackedPackets, p)
|
||||
}
|
||||
if p.skippedPacket {
|
||||
return false, &qerr.TransportError{
|
||||
ErrorCode: qerr.ProtocolViolation,
|
||||
ErrorMessage: fmt.Sprintf("received an ACK for skipped packet number: %d (%s)", p.PacketNumber, encLevel),
|
||||
}
|
||||
}
|
||||
h.ackedPackets = append(h.ackedPackets, p)
|
||||
return true, nil
|
||||
})
|
||||
if h.logger.Debug() && len(h.ackedPackets) > 0 {
|
||||
@@ -417,9 +399,6 @@ func (h *sentPacketHandler) detectAndRemoveAckedPackets(ack *wire.AckFrame, encL
|
||||
if err := pnSpace.history.Remove(p.PacketNumber); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if h.tracer != nil {
|
||||
h.tracer.AcknowledgedPacket(encLevel, p.PacketNumber)
|
||||
}
|
||||
}
|
||||
|
||||
return h.ackedPackets, err
|
||||
@@ -445,20 +424,20 @@ func (h *sentPacketHandler) getLossTimeAndSpace() (time.Time, protocol.Encryptio
|
||||
}
|
||||
|
||||
// same logic as getLossTimeAndSpace, but for lastAckElicitingPacketTime instead of lossTime
|
||||
func (h *sentPacketHandler) getPTOTimeAndSpace() (pto time.Time, encLevel protocol.EncryptionLevel, ok bool) {
|
||||
// We only send application data probe packets once the handshake is confirmed,
|
||||
// because before that, we don't have the keys to decrypt ACKs sent in 1-RTT packets.
|
||||
if !h.handshakeConfirmed && !h.hasOutstandingCryptoPackets() {
|
||||
if h.peerCompletedAddressValidation {
|
||||
return
|
||||
}
|
||||
func (h *sentPacketHandler) getPTOTimeAndSpace() (time.Time, protocol.EncryptionLevel) {
|
||||
if !h.hasOutstandingPackets() {
|
||||
t := time.Now().Add(h.rttStats.PTO(false) << h.ptoCount)
|
||||
if h.initialPackets != nil {
|
||||
return t, protocol.EncryptionInitial, true
|
||||
return t, protocol.EncryptionInitial
|
||||
}
|
||||
return t, protocol.EncryptionHandshake, true
|
||||
return t, protocol.EncryptionHandshake
|
||||
}
|
||||
|
||||
var (
|
||||
encLevel protocol.EncryptionLevel
|
||||
pto time.Time
|
||||
)
|
||||
|
||||
if h.initialPackets != nil {
|
||||
encLevel = protocol.EncryptionInitial
|
||||
if t := h.initialPackets.lastAckElicitingPacketTime; !t.IsZero() {
|
||||
@@ -479,7 +458,7 @@ func (h *sentPacketHandler) getPTOTimeAndSpace() (pto time.Time, encLevel protoc
|
||||
encLevel = protocol.Encryption1RTT
|
||||
}
|
||||
}
|
||||
return pto, encLevel, true
|
||||
return pto, encLevel
|
||||
}
|
||||
|
||||
func (h *sentPacketHandler) hasOutstandingCryptoPackets() bool {
|
||||
@@ -494,13 +473,15 @@ func (h *sentPacketHandler) hasOutstandingCryptoPackets() bool {
|
||||
}
|
||||
|
||||
func (h *sentPacketHandler) hasOutstandingPackets() bool {
|
||||
return h.appDataPackets.history.HasOutstandingPackets() || h.hasOutstandingCryptoPackets()
|
||||
// We only send application data probe packets once the handshake completes,
|
||||
// because before that, we don't have the keys to decrypt ACKs sent in 1-RTT packets.
|
||||
return (h.handshakeConfirmed && h.appDataPackets.history.HasOutstandingPackets()) ||
|
||||
h.hasOutstandingCryptoPackets()
|
||||
}
|
||||
|
||||
func (h *sentPacketHandler) setLossDetectionTimer() {
|
||||
oldAlarm := h.alarm // only needed in case tracing is enabled
|
||||
lossTime, encLevel := h.getLossTimeAndSpace()
|
||||
if !lossTime.IsZero() {
|
||||
if lossTime, encLevel := h.getLossTimeAndSpace(); !lossTime.IsZero() {
|
||||
// Early retransmit timer or time loss detection.
|
||||
h.alarm = lossTime
|
||||
if h.tracer != nil && h.alarm != oldAlarm {
|
||||
@@ -509,35 +490,18 @@ func (h *sentPacketHandler) setLossDetectionTimer() {
|
||||
return
|
||||
}
|
||||
|
||||
// Cancel the alarm if amplification limited.
|
||||
if h.isAmplificationLimited() {
|
||||
h.alarm = time.Time{}
|
||||
if !oldAlarm.IsZero() {
|
||||
h.logger.Debugf("Canceling loss detection timer. Amplification limited.")
|
||||
if h.tracer != nil {
|
||||
h.tracer.LossTimerCanceled()
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Cancel the alarm if no packets are outstanding
|
||||
if !h.hasOutstandingPackets() && h.peerCompletedAddressValidation {
|
||||
h.alarm = time.Time{}
|
||||
if !oldAlarm.IsZero() {
|
||||
h.logger.Debugf("Canceling loss detection timer. No packets in flight.")
|
||||
if h.tracer != nil {
|
||||
h.tracer.LossTimerCanceled()
|
||||
}
|
||||
h.logger.Debugf("Canceling loss detection timer. No packets in flight.")
|
||||
if h.tracer != nil && !oldAlarm.IsZero() {
|
||||
h.tracer.LossTimerCanceled()
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// PTO alarm
|
||||
ptoTime, encLevel, ok := h.getPTOTimeAndSpace()
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
ptoTime, encLevel := h.getPTOTimeAndSpace()
|
||||
h.alarm = ptoTime
|
||||
if h.tracer != nil && h.alarm != oldAlarm {
|
||||
h.tracer.SetLossTimer(logging.TimerTypePTO, encLevel, h.alarm)
|
||||
@@ -605,7 +569,20 @@ func (h *sentPacketHandler) detectLostPackets(now time.Time, encLevel protocol.E
|
||||
}
|
||||
|
||||
func (h *sentPacketHandler) OnLossDetectionTimeout() error {
|
||||
defer h.setLossDetectionTimer()
|
||||
// When all outstanding are acknowledged, the alarm is canceled in
|
||||
// setLossDetectionTimer. This doesn't reset the timer in the session though.
|
||||
// When OnAlarm is called, we therefore need to make sure that there are
|
||||
// actually packets outstanding.
|
||||
if h.hasOutstandingPackets() || !h.peerCompletedAddressValidation {
|
||||
if err := h.onVerifiedLossDetectionTimeout(); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
h.setLossDetectionTimer()
|
||||
return nil
|
||||
}
|
||||
|
||||
func (h *sentPacketHandler) onVerifiedLossDetectionTimeout() error {
|
||||
earliestLossTime, encLevel := h.getLossTimeAndSpace()
|
||||
if !earliestLossTime.IsZero() {
|
||||
if h.logger.Debug() {
|
||||
@@ -619,12 +596,34 @@ func (h *sentPacketHandler) OnLossDetectionTimeout() error {
|
||||
}
|
||||
|
||||
// PTO
|
||||
// When all outstanding are acknowledged, the alarm is canceled in
|
||||
// setLossDetectionTimer. This doesn't reset the timer in the session though.
|
||||
// When OnAlarm is called, we therefore need to make sure that there are
|
||||
// actually packets outstanding.
|
||||
if h.bytesInFlight == 0 && !h.peerCompletedAddressValidation {
|
||||
h.ptoCount++
|
||||
h.ptoCount++
|
||||
if h.bytesInFlight > 0 {
|
||||
_, encLevel = h.getPTOTimeAndSpace()
|
||||
if h.logger.Debug() {
|
||||
h.logger.Debugf("Loss detection alarm for %s fired in PTO mode. PTO count: %d", encLevel, h.ptoCount)
|
||||
}
|
||||
if h.tracer != nil {
|
||||
h.tracer.LossTimerExpired(logging.TimerTypePTO, encLevel)
|
||||
h.tracer.UpdatedPTOCount(h.ptoCount)
|
||||
}
|
||||
h.numProbesToSend += 2
|
||||
//nolint:exhaustive // We never arm a PTO timer for 0-RTT packets.
|
||||
switch encLevel {
|
||||
case protocol.EncryptionInitial:
|
||||
h.ptoMode = SendPTOInitial
|
||||
case protocol.EncryptionHandshake:
|
||||
h.ptoMode = SendPTOHandshake
|
||||
case protocol.Encryption1RTT:
|
||||
// skip a packet number in order to elicit an immediate ACK
|
||||
_ = h.PopPacketNumber(protocol.Encryption1RTT)
|
||||
h.ptoMode = SendPTOAppData
|
||||
default:
|
||||
return fmt.Errorf("PTO timer in unexpected encryption level: %s", encLevel)
|
||||
}
|
||||
} else {
|
||||
if h.perspective == protocol.PerspectiveServer {
|
||||
return errors.New("sentPacketHandler BUG: PTO fired, but bytes_in_flight is 0")
|
||||
}
|
||||
h.numProbesToSend++
|
||||
if h.initialPackets != nil {
|
||||
h.ptoMode = SendPTOInitial
|
||||
@@ -633,37 +632,6 @@ func (h *sentPacketHandler) OnLossDetectionTimeout() error {
|
||||
} else {
|
||||
return errors.New("sentPacketHandler BUG: PTO fired, but bytes_in_flight is 0 and Initial and Handshake already dropped")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
_, encLevel, ok := h.getPTOTimeAndSpace()
|
||||
if !ok {
|
||||
return nil
|
||||
}
|
||||
if ps := h.getPacketNumberSpace(encLevel); !ps.history.HasOutstandingPackets() && !h.peerCompletedAddressValidation {
|
||||
return nil
|
||||
}
|
||||
h.ptoCount++
|
||||
if h.logger.Debug() {
|
||||
h.logger.Debugf("Loss detection alarm for %s fired in PTO mode. PTO count: %d", encLevel, h.ptoCount)
|
||||
}
|
||||
if h.tracer != nil {
|
||||
h.tracer.LossTimerExpired(logging.TimerTypePTO, encLevel)
|
||||
h.tracer.UpdatedPTOCount(h.ptoCount)
|
||||
}
|
||||
h.numProbesToSend += 2
|
||||
//nolint:exhaustive // We never arm a PTO timer for 0-RTT packets.
|
||||
switch encLevel {
|
||||
case protocol.EncryptionInitial:
|
||||
h.ptoMode = SendPTOInitial
|
||||
case protocol.EncryptionHandshake:
|
||||
h.ptoMode = SendPTOHandshake
|
||||
case protocol.Encryption1RTT:
|
||||
// skip a packet number in order to elicit an immediate ACK
|
||||
_ = h.PopPacketNumber(protocol.Encryption1RTT)
|
||||
h.ptoMode = SendPTOAppData
|
||||
default:
|
||||
return fmt.Errorf("PTO timer in unexpected encryption level: %s", encLevel)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -800,9 +768,8 @@ func (h *sentPacketHandler) ResetForRetry() error {
|
||||
// Only use the Retry to estimate the RTT if we didn't send any retransmission for the Initial.
|
||||
// Otherwise, we don't know which Initial the Retry was sent in response to.
|
||||
if h.ptoCount == 0 {
|
||||
// Don't set the RTT to a value lower than 5ms here.
|
||||
now := time.Now()
|
||||
h.rttStats.UpdateRTT(utils.MaxDuration(minRTTAfterRetry, now.Sub(firstPacketSendTime)), 0, now)
|
||||
h.rttStats.UpdateRTT(now.Sub(firstPacketSendTime), 0, now)
|
||||
if h.logger.Debug() {
|
||||
h.logger.Debugf("\tupdated RTT: %s (σ: %s)", h.rttStats.SmoothedRTT(), h.rttStats.MeanDeviation())
|
||||
}
|
||||
|
||||
Generated
Vendored
+2
-3
@@ -64,9 +64,8 @@ func (h *sentPacketHistory) Iterate(cb func(*Packet) (cont bool, err error)) err
|
||||
// FirstOutStanding returns the first outstanding packet.
|
||||
func (h *sentPacketHistory) FirstOutstanding() *Packet {
|
||||
for el := h.packetList.Front(); el != nil; el = el.Next() {
|
||||
p := &el.Value
|
||||
if !p.declaredLost && !p.skippedPacket && !p.IsPathMTUProbePacket {
|
||||
return p
|
||||
if !el.Value.declaredLost && !el.Value.skippedPacket {
|
||||
return &el.Value
|
||||
}
|
||||
}
|
||||
return nil
|
||||
|
||||
+6
-28
@@ -1,7 +1,6 @@
|
||||
package congestion
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/lucas-clemente/quic-go/internal/protocol"
|
||||
@@ -15,8 +14,9 @@ const (
|
||||
initialMaxDatagramSize = protocol.ByteCount(protocol.InitialPacketSizeIPv4)
|
||||
maxBurstPackets = 3
|
||||
renoBeta = 0.7 // Reno backoff factor.
|
||||
initialMaxCongestionWindow = protocol.MaxCongestionWindowPackets * initialMaxDatagramSize
|
||||
minCongestionWindowPackets = 2
|
||||
initialCongestionWindow = 32
|
||||
initialCongestionWindow = 32 * initialMaxDatagramSize
|
||||
)
|
||||
|
||||
type cubicSender struct {
|
||||
@@ -65,33 +65,11 @@ var (
|
||||
)
|
||||
|
||||
// NewCubicSender makes a new cubic sender
|
||||
func NewCubicSender(
|
||||
clock Clock,
|
||||
rttStats *utils.RTTStats,
|
||||
initialMaxDatagramSize protocol.ByteCount,
|
||||
reno bool,
|
||||
tracer logging.ConnectionTracer,
|
||||
) *cubicSender {
|
||||
return newCubicSender(
|
||||
clock,
|
||||
rttStats,
|
||||
reno,
|
||||
initialMaxDatagramSize,
|
||||
initialCongestionWindow*initialMaxDatagramSize,
|
||||
protocol.MaxCongestionWindowPackets*initialMaxDatagramSize,
|
||||
tracer,
|
||||
)
|
||||
func NewCubicSender(clock Clock, rttStats *utils.RTTStats, reno bool, tracer logging.ConnectionTracer) *cubicSender {
|
||||
return newCubicSender(clock, rttStats, reno, initialCongestionWindow, initialMaxCongestionWindow, tracer)
|
||||
}
|
||||
|
||||
func newCubicSender(
|
||||
clock Clock,
|
||||
rttStats *utils.RTTStats,
|
||||
reno bool,
|
||||
initialMaxDatagramSize,
|
||||
initialCongestionWindow,
|
||||
initialMaxCongestionWindow protocol.ByteCount,
|
||||
tracer logging.ConnectionTracer,
|
||||
) *cubicSender {
|
||||
func newCubicSender(clock Clock, rttStats *utils.RTTStats, reno bool, initialCongestionWindow, initialMaxCongestionWindow protocol.ByteCount, tracer logging.ConnectionTracer) *cubicSender {
|
||||
c := &cubicSender{
|
||||
rttStats: rttStats,
|
||||
largestSentPacketNumber: protocol.InvalidPacketNumber,
|
||||
@@ -305,7 +283,7 @@ func (c *cubicSender) maybeTraceStateChange(new logging.CongestionState) {
|
||||
|
||||
func (c *cubicSender) SetMaxDatagramSize(s protocol.ByteCount) {
|
||||
if s < c.maxDatagramSize {
|
||||
panic(fmt.Sprintf("congestion BUG: decreased max datagram size from %d to %d", c.maxDatagramSize, s))
|
||||
panic("congestion BUG: decreased max datagram size")
|
||||
}
|
||||
cwndIsMinCwnd := c.congestionWindow == c.minCongestionWindow()
|
||||
c.maxDatagramSize = s
|
||||
|
||||
Generated
Vendored
+1
-4
@@ -50,10 +50,7 @@ func (c *connectionFlowController) IncrementHighestReceived(increment protocol.B
|
||||
|
||||
c.highestReceived += increment
|
||||
if c.checkFlowControlViolation() {
|
||||
return &qerr.TransportError{
|
||||
ErrorCode: qerr.FlowControlError,
|
||||
ErrorMessage: fmt.Sprintf("received %d bytes for the connection, allowed %d bytes", c.highestReceived, c.receiveWindow),
|
||||
}
|
||||
return qerr.NewError(qerr.FlowControlError, fmt.Sprintf("Received %d bytes for the connection, allowed %d bytes", c.highestReceived, c.receiveWindow))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
Generated
Vendored
+4
-16
@@ -54,17 +54,11 @@ func (c *streamFlowController) UpdateHighestReceived(offset protocol.ByteCount,
|
||||
if c.receivedFinalOffset {
|
||||
// If we receive another final offset, check that it's the same.
|
||||
if final && offset != c.highestReceived {
|
||||
return &qerr.TransportError{
|
||||
ErrorCode: qerr.FinalSizeError,
|
||||
ErrorMessage: fmt.Sprintf("received inconsistent final offset for stream %d (old: %d, new: %d bytes)", c.streamID, c.highestReceived, offset),
|
||||
}
|
||||
return qerr.NewError(qerr.FinalSizeError, fmt.Sprintf("Received inconsistent final offset for stream %d (old: %d, new: %d bytes)", c.streamID, c.highestReceived, offset))
|
||||
}
|
||||
// Check that the offset is below the final offset.
|
||||
if offset > c.highestReceived {
|
||||
return &qerr.TransportError{
|
||||
ErrorCode: qerr.FinalSizeError,
|
||||
ErrorMessage: fmt.Sprintf("received offset %d for stream %d, but final offset was already received at %d", offset, c.streamID, c.highestReceived),
|
||||
}
|
||||
return qerr.NewError(qerr.FinalSizeError, fmt.Sprintf("Received offset %d for stream %d. Final offset was already received at %d", offset, c.streamID, c.highestReceived))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -78,10 +72,7 @@ func (c *streamFlowController) UpdateHighestReceived(offset protocol.ByteCount,
|
||||
// This can happen due to reordering.
|
||||
if offset <= c.highestReceived {
|
||||
if final {
|
||||
return &qerr.TransportError{
|
||||
ErrorCode: qerr.FinalSizeError,
|
||||
ErrorMessage: fmt.Sprintf("received final offset %d for stream %d, but already received offset %d before", offset, c.streamID, c.highestReceived),
|
||||
}
|
||||
return qerr.NewError(qerr.FinalSizeError, fmt.Sprintf("Received final offset %d for stream %d, but already received offset %d before", offset, c.streamID, c.highestReceived))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -89,10 +80,7 @@ func (c *streamFlowController) UpdateHighestReceived(offset protocol.ByteCount,
|
||||
increment := offset - c.highestReceived
|
||||
c.highestReceived = offset
|
||||
if c.checkFlowControlViolation() {
|
||||
return &qerr.TransportError{
|
||||
ErrorCode: qerr.FlowControlError,
|
||||
ErrorMessage: fmt.Sprintf("received %d bytes on stream %d, allowed %d bytes", offset, c.streamID, c.receiveWindow),
|
||||
}
|
||||
return qerr.NewError(qerr.FlowControlError, fmt.Sprintf("Received %d bytes on stream %d, allowed %d bytes", offset, c.streamID, c.receiveWindow))
|
||||
}
|
||||
return c.connection.IncrementHighestReceived(increment)
|
||||
}
|
||||
|
||||
+7
-10
@@ -403,10 +403,7 @@ func (h *cryptoSetup) checkEncryptionLevel(msgType messageType, encLevel protoco
|
||||
func (h *cryptoSetup) handleTransportParameters(data []byte) {
|
||||
var tp wire.TransportParameters
|
||||
if err := tp.Unmarshal(data, h.perspective.Opposite()); err != nil {
|
||||
h.runner.OnError(&qerr.TransportError{
|
||||
ErrorCode: qerr.TransportParameterError,
|
||||
ErrorMessage: err.Error(),
|
||||
})
|
||||
h.runner.OnError(qerr.NewError(qerr.TransportParameterError, err.Error()))
|
||||
}
|
||||
h.peerParams = &tp
|
||||
h.runner.OnReceivedParams(h.peerParams)
|
||||
@@ -558,7 +555,7 @@ func (h *cryptoSetup) SetReadKey(encLevel qtls.EncryptionLevel, suite *qtls.Ciph
|
||||
newHeaderProtector(suite, trafficSecret, true),
|
||||
)
|
||||
h.mutex.Unlock()
|
||||
h.logger.Debugf("Installed 0-RTT Read keys (using %s)", tls.CipherSuiteName(suite.ID))
|
||||
h.logger.Debugf("Installed 0-RTT Read keys (using %s)", qtls.CipherSuiteName(suite.ID))
|
||||
if h.tracer != nil {
|
||||
h.tracer.UpdatedKeyFromTLS(protocol.Encryption0RTT, h.perspective.Opposite())
|
||||
}
|
||||
@@ -571,12 +568,12 @@ func (h *cryptoSetup) SetReadKey(encLevel qtls.EncryptionLevel, suite *qtls.Ciph
|
||||
h.dropInitialKeys,
|
||||
h.perspective,
|
||||
)
|
||||
h.logger.Debugf("Installed Handshake Read keys (using %s)", tls.CipherSuiteName(suite.ID))
|
||||
h.logger.Debugf("Installed Handshake Read keys (using %s)", qtls.CipherSuiteName(suite.ID))
|
||||
case qtls.EncryptionApplication:
|
||||
h.readEncLevel = protocol.Encryption1RTT
|
||||
h.aead.SetReadKey(suite, trafficSecret)
|
||||
h.has1RTTOpener = true
|
||||
h.logger.Debugf("Installed 1-RTT Read keys (using %s)", tls.CipherSuiteName(suite.ID))
|
||||
h.logger.Debugf("Installed 1-RTT Read keys (using %s)", qtls.CipherSuiteName(suite.ID))
|
||||
default:
|
||||
panic("unexpected read encryption level")
|
||||
}
|
||||
@@ -598,7 +595,7 @@ func (h *cryptoSetup) SetWriteKey(encLevel qtls.EncryptionLevel, suite *qtls.Cip
|
||||
newHeaderProtector(suite, trafficSecret, true),
|
||||
)
|
||||
h.mutex.Unlock()
|
||||
h.logger.Debugf("Installed 0-RTT Write keys (using %s)", tls.CipherSuiteName(suite.ID))
|
||||
h.logger.Debugf("Installed 0-RTT Write keys (using %s)", qtls.CipherSuiteName(suite.ID))
|
||||
if h.tracer != nil {
|
||||
h.tracer.UpdatedKeyFromTLS(protocol.Encryption0RTT, h.perspective)
|
||||
}
|
||||
@@ -611,12 +608,12 @@ func (h *cryptoSetup) SetWriteKey(encLevel qtls.EncryptionLevel, suite *qtls.Cip
|
||||
h.dropInitialKeys,
|
||||
h.perspective,
|
||||
)
|
||||
h.logger.Debugf("Installed Handshake Write keys (using %s)", tls.CipherSuiteName(suite.ID))
|
||||
h.logger.Debugf("Installed Handshake Write keys (using %s)", qtls.CipherSuiteName(suite.ID))
|
||||
case qtls.EncryptionApplication:
|
||||
h.writeEncLevel = protocol.Encryption1RTT
|
||||
h.aead.SetWriteKey(suite, trafficSecret)
|
||||
h.has1RTTSealer = true
|
||||
h.logger.Debugf("Installed 1-RTT Write keys (using %s)", tls.CipherSuiteName(suite.ID))
|
||||
h.logger.Debugf("Installed 1-RTT Write keys (using %s)", qtls.CipherSuiteName(suite.ID))
|
||||
if h.zeroRTTSealer != nil {
|
||||
h.zeroRTTSealer = nil
|
||||
h.logger.Debugf("Dropping 0-RTT keys.")
|
||||
|
||||
+2
-4
@@ -4,8 +4,6 @@ import (
|
||||
"crypto"
|
||||
"crypto/tls"
|
||||
|
||||
"golang.org/x/crypto/hkdf"
|
||||
|
||||
"github.com/lucas-clemente/quic-go/internal/protocol"
|
||||
"github.com/lucas-clemente/quic-go/internal/qtls"
|
||||
)
|
||||
@@ -16,7 +14,7 @@ var (
|
||||
)
|
||||
|
||||
func getSalt(v protocol.VersionNumber) []byte {
|
||||
if v == protocol.VersionDraft34 || v == protocol.Version1 {
|
||||
if v == protocol.VersionDraft34 {
|
||||
return quicSaltDraft34
|
||||
}
|
||||
return quicSaltOld
|
||||
@@ -51,7 +49,7 @@ func NewInitialAEAD(connID protocol.ConnectionID, pers protocol.Perspective, v p
|
||||
}
|
||||
|
||||
func computeSecrets(connID protocol.ConnectionID, v protocol.VersionNumber) (clientSecret, serverSecret []byte) {
|
||||
initialSecret := hkdf.Extract(crypto.SHA256.New, connID, getSalt(v))
|
||||
initialSecret := qtls.HkdfExtract(crypto.SHA256, connID, getSalt(v))
|
||||
clientSecret = hkdfExpandLabel(crypto.SHA256, initialSecret, []byte{}, "client in", crypto.SHA256.Size())
|
||||
serverSecret = hkdfExpandLabel(crypto.SHA256, initialSecret, []byte{}, "server in", crypto.SHA256.Size())
|
||||
return
|
||||
|
||||
+1
-1
@@ -48,7 +48,7 @@ func GetRetryIntegrityTag(retry []byte, origDestConnID protocol.ConnectionID, ve
|
||||
|
||||
var tag [16]byte
|
||||
var sealed []byte
|
||||
if version != protocol.VersionDraft34 && version != protocol.Version1 {
|
||||
if version != protocol.VersionDraft34 {
|
||||
sealed = oldRetryAEAD.Seal(tag[:0], oldRetryNonce[:], nil, retryBuf.Bytes())
|
||||
} else {
|
||||
sealed = retryAEAD.Seal(tag[:0], retryNonce[:], nil, retryBuf.Bytes())
|
||||
|
||||
Generated
Vendored
+1
-1
@@ -24,7 +24,7 @@ var _ tlsExtensionHandler = &extensionHandler{}
|
||||
// newExtensionHandler creates a new extension handler
|
||||
func newExtensionHandler(params []byte, pers protocol.Perspective, v protocol.VersionNumber) tlsExtensionHandler {
|
||||
et := uint16(quicTLSExtensionType)
|
||||
if v != protocol.VersionDraft34 && v != protocol.Version1 {
|
||||
if v != protocol.VersionDraft34 {
|
||||
et = quicTLSExtensionTypeOldDrafts
|
||||
}
|
||||
return &extensionHandler{
|
||||
|
||||
+3
-9
@@ -163,7 +163,7 @@ func (a *updatableAEAD) Open(dst, src []byte, rcvTime time.Time, pn protocol.Pac
|
||||
if err == ErrDecryptionFailed {
|
||||
a.invalidPacketCount++
|
||||
if a.invalidPacketCount >= a.invalidPacketLimit {
|
||||
return nil, &qerr.TransportError{ErrorCode: qerr.AEADLimitReached}
|
||||
return nil, qerr.AEADLimitReached
|
||||
}
|
||||
}
|
||||
if err == nil {
|
||||
@@ -201,10 +201,7 @@ func (a *updatableAEAD) open(dst, src []byte, rcvTime time.Time, pn protocol.Pac
|
||||
}
|
||||
// Opening succeeded. Check if the peer was allowed to update.
|
||||
if a.keyPhase > 0 && a.firstSentWithCurrentKey == protocol.InvalidPacketNumber {
|
||||
return nil, &qerr.TransportError{
|
||||
ErrorCode: qerr.KeyUpdateError,
|
||||
ErrorMessage: "keys updated too quickly",
|
||||
}
|
||||
return nil, qerr.NewError(qerr.KeyUpdateError, "keys updated too quickly")
|
||||
}
|
||||
a.rollKeys()
|
||||
a.logger.Debugf("Peer updated keys to %d", a.keyPhase)
|
||||
@@ -253,10 +250,7 @@ func (a *updatableAEAD) Seal(dst, src []byte, pn protocol.PacketNumber, ad []byt
|
||||
func (a *updatableAEAD) SetLargestAcked(pn protocol.PacketNumber) error {
|
||||
if a.firstSentWithCurrentKey != protocol.InvalidPacketNumber &&
|
||||
pn >= a.firstSentWithCurrentKey && a.numRcvdWithCurrentKey == 0 {
|
||||
return &qerr.TransportError{
|
||||
ErrorCode: qerr.KeyUpdateError,
|
||||
ErrorMessage: fmt.Sprintf("received ACK for key phase %d, but peer didn't update keys", a.keyPhase),
|
||||
}
|
||||
return qerr.NewError(qerr.KeyUpdateError, fmt.Sprintf("received ACK for key phase %d, but peer didn't update keys", a.keyPhase))
|
||||
}
|
||||
a.largestAcked = pn
|
||||
return nil
|
||||
|
||||
+3
@@ -52,6 +52,9 @@ const MaxByteCount = ByteCount(1<<62 - 1)
|
||||
// InvalidByteCount is an invalid byte count
|
||||
const InvalidByteCount ByteCount = -1
|
||||
|
||||
// An ApplicationErrorCode is an application-defined error code.
|
||||
type ApplicationErrorCode uint64
|
||||
|
||||
// A StatelessResetToken is a stateless reset token.
|
||||
type StatelessResetToken [16]byte
|
||||
|
||||
|
||||
+5
-8
@@ -18,18 +18,17 @@ const (
|
||||
|
||||
// The version numbers, making grepping easier
|
||||
const (
|
||||
VersionTLS VersionNumber = 0x1
|
||||
VersionWhatever VersionNumber = math.MaxUint32 - 1 // for when the version doesn't matter
|
||||
VersionTLS VersionNumber = 0xff00001d // draft-29
|
||||
VersionWhatever VersionNumber = 1 // for when the version doesn't matter
|
||||
VersionUnknown VersionNumber = math.MaxUint32
|
||||
VersionDraft29 VersionNumber = 0xff00001d
|
||||
VersionDraft32 VersionNumber = 0xff000020
|
||||
VersionDraft34 VersionNumber = 0xff000022
|
||||
Version1 VersionNumber = 0x1
|
||||
VersionDraft34 VersionNumber = 0xff000022 // If everything goes according to plan at the IETF, this will one day be QUIC v1.
|
||||
)
|
||||
|
||||
// SupportedVersions lists the versions that the server supports
|
||||
// must be in sorted descending order
|
||||
var SupportedVersions = []VersionNumber{Version1, VersionDraft34, VersionDraft32, VersionDraft29}
|
||||
var SupportedVersions = []VersionNumber{VersionDraft29, VersionDraft34, VersionDraft32}
|
||||
|
||||
// IsValidVersion says if the version is known to quic-go
|
||||
func IsValidVersion(v VersionNumber) bool {
|
||||
@@ -39,7 +38,7 @@ func IsValidVersion(v VersionNumber) bool {
|
||||
func (vn VersionNumber) String() string {
|
||||
// For releases, VersionTLS will be set to a draft version.
|
||||
// A switch statement can't contain duplicate cases.
|
||||
if vn == VersionTLS && VersionTLS != VersionDraft29 && VersionTLS != VersionDraft32 && VersionTLS != Version1 {
|
||||
if vn == VersionTLS && VersionTLS != VersionDraft29 && VersionTLS != VersionDraft32 {
|
||||
return "TLS dev version (WIP)"
|
||||
}
|
||||
//nolint:exhaustive
|
||||
@@ -54,8 +53,6 @@ func (vn VersionNumber) String() string {
|
||||
return "draft-32"
|
||||
case VersionDraft34:
|
||||
return "draft-34"
|
||||
case Version1:
|
||||
return "v1"
|
||||
default:
|
||||
if vn.isGQUIC() {
|
||||
return fmt.Sprintf("gQUIC %d", vn.toGQUICVersion())
|
||||
|
||||
+32
-25
@@ -6,44 +6,51 @@ import (
|
||||
"github.com/lucas-clemente/quic-go/internal/qtls"
|
||||
)
|
||||
|
||||
// TransportErrorCode is a QUIC transport error.
|
||||
type TransportErrorCode uint64
|
||||
// ErrorCode can be used as a normal error without reason.
|
||||
type ErrorCode uint64
|
||||
|
||||
// The error codes defined by QUIC
|
||||
const (
|
||||
NoError TransportErrorCode = 0x0
|
||||
InternalError TransportErrorCode = 0x1
|
||||
ConnectionRefused TransportErrorCode = 0x2
|
||||
FlowControlError TransportErrorCode = 0x3
|
||||
StreamLimitError TransportErrorCode = 0x4
|
||||
StreamStateError TransportErrorCode = 0x5
|
||||
FinalSizeError TransportErrorCode = 0x6
|
||||
FrameEncodingError TransportErrorCode = 0x7
|
||||
TransportParameterError TransportErrorCode = 0x8
|
||||
ConnectionIDLimitError TransportErrorCode = 0x9
|
||||
ProtocolViolation TransportErrorCode = 0xa
|
||||
InvalidToken TransportErrorCode = 0xb
|
||||
ApplicationErrorErrorCode TransportErrorCode = 0xc
|
||||
CryptoBufferExceeded TransportErrorCode = 0xd
|
||||
KeyUpdateError TransportErrorCode = 0xe
|
||||
AEADLimitReached TransportErrorCode = 0xf
|
||||
NoViablePathError TransportErrorCode = 0x10
|
||||
NoError ErrorCode = 0x0
|
||||
InternalError ErrorCode = 0x1
|
||||
ConnectionRefused ErrorCode = 0x2
|
||||
FlowControlError ErrorCode = 0x3
|
||||
StreamLimitError ErrorCode = 0x4
|
||||
StreamStateError ErrorCode = 0x5
|
||||
FinalSizeError ErrorCode = 0x6
|
||||
FrameEncodingError ErrorCode = 0x7
|
||||
TransportParameterError ErrorCode = 0x8
|
||||
ConnectionIDLimitError ErrorCode = 0x9
|
||||
ProtocolViolation ErrorCode = 0xa
|
||||
InvalidToken ErrorCode = 0xb
|
||||
ApplicationError ErrorCode = 0xc
|
||||
CryptoBufferExceeded ErrorCode = 0xd
|
||||
KeyUpdateError ErrorCode = 0xe
|
||||
AEADLimitReached ErrorCode = 0xf
|
||||
NoViablePathError ErrorCode = 0x10
|
||||
)
|
||||
|
||||
func (e TransportErrorCode) IsCryptoError() bool {
|
||||
func (e ErrorCode) isCryptoError() bool {
|
||||
return e >= 0x100 && e < 0x200
|
||||
}
|
||||
|
||||
func (e ErrorCode) Error() string {
|
||||
if e.isCryptoError() {
|
||||
return fmt.Sprintf("%s: %s", e.String(), e.Message())
|
||||
}
|
||||
return e.String()
|
||||
}
|
||||
|
||||
// Message is a description of the error.
|
||||
// It only returns a non-empty string for crypto errors.
|
||||
func (e TransportErrorCode) Message() string {
|
||||
if !e.IsCryptoError() {
|
||||
func (e ErrorCode) Message() string {
|
||||
if !e.isCryptoError() {
|
||||
return ""
|
||||
}
|
||||
return qtls.Alert(e - 0x100).Error()
|
||||
}
|
||||
|
||||
func (e TransportErrorCode) String() string {
|
||||
func (e ErrorCode) String() string {
|
||||
switch e {
|
||||
case NoError:
|
||||
return "NO_ERROR"
|
||||
@@ -69,7 +76,7 @@ func (e TransportErrorCode) String() string {
|
||||
return "PROTOCOL_VIOLATION"
|
||||
case InvalidToken:
|
||||
return "INVALID_TOKEN"
|
||||
case ApplicationErrorErrorCode:
|
||||
case ApplicationError:
|
||||
return "APPLICATION_ERROR"
|
||||
case CryptoBufferExceeded:
|
||||
return "CRYPTO_BUFFER_EXCEEDED"
|
||||
@@ -80,7 +87,7 @@ func (e TransportErrorCode) String() string {
|
||||
case NoViablePathError:
|
||||
return "NO_VIABLE_PATH"
|
||||
default:
|
||||
if e.IsCryptoError() {
|
||||
if e.isCryptoError() {
|
||||
return fmt.Sprintf("CRYPTO_ERROR (%#x)", uint16(e))
|
||||
}
|
||||
return fmt.Sprintf("unknown error code: %#x", uint16(e))
|
||||
|
||||
-106
@@ -1,106 +0,0 @@
|
||||
package qerr
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net"
|
||||
|
||||
"github.com/lucas-clemente/quic-go/internal/protocol"
|
||||
)
|
||||
|
||||
var (
|
||||
ErrHandshakeTimeout = &HandshakeTimeoutError{}
|
||||
ErrIdleTimeout = &IdleTimeoutError{}
|
||||
)
|
||||
|
||||
type TransportError struct {
|
||||
Remote bool
|
||||
FrameType uint64
|
||||
ErrorCode TransportErrorCode
|
||||
ErrorMessage string
|
||||
}
|
||||
|
||||
var _ error = &TransportError{}
|
||||
|
||||
// NewCryptoError create a new TransportError instance for a crypto error
|
||||
func NewCryptoError(tlsAlert uint8, errorMessage string) *TransportError {
|
||||
return &TransportError{
|
||||
ErrorCode: 0x100 + TransportErrorCode(tlsAlert),
|
||||
ErrorMessage: errorMessage,
|
||||
}
|
||||
}
|
||||
|
||||
func (e *TransportError) Error() string {
|
||||
str := e.ErrorCode.String()
|
||||
if e.FrameType != 0 {
|
||||
str += fmt.Sprintf(" (frame type: %#x)", e.FrameType)
|
||||
}
|
||||
msg := e.ErrorMessage
|
||||
if len(msg) == 0 {
|
||||
msg = e.ErrorCode.Message()
|
||||
}
|
||||
if len(msg) == 0 {
|
||||
return str
|
||||
}
|
||||
return str + ": " + msg
|
||||
}
|
||||
|
||||
// An ApplicationErrorCode is an application-defined error code.
|
||||
type ApplicationErrorCode uint64
|
||||
|
||||
// A StreamErrorCode is an error code used to cancel streams.
|
||||
type StreamErrorCode uint64
|
||||
|
||||
type ApplicationError struct {
|
||||
Remote bool
|
||||
ErrorCode ApplicationErrorCode
|
||||
ErrorMessage string
|
||||
}
|
||||
|
||||
var _ error = &ApplicationError{}
|
||||
|
||||
func (e *ApplicationError) Error() string {
|
||||
if len(e.ErrorMessage) == 0 {
|
||||
return fmt.Sprintf("Application error %#x", e.ErrorCode)
|
||||
}
|
||||
return fmt.Sprintf("Application error %#x: %s", e.ErrorCode, e.ErrorMessage)
|
||||
}
|
||||
|
||||
type IdleTimeoutError struct{}
|
||||
|
||||
var _ error = &IdleTimeoutError{}
|
||||
|
||||
func (e *IdleTimeoutError) Timeout() bool { return true }
|
||||
func (e *IdleTimeoutError) Temporary() bool { return false }
|
||||
func (e *IdleTimeoutError) Error() string { return "timeout: no recent network activity" }
|
||||
|
||||
type HandshakeTimeoutError struct{}
|
||||
|
||||
var _ error = &HandshakeTimeoutError{}
|
||||
|
||||
func (e *HandshakeTimeoutError) Timeout() bool { return true }
|
||||
func (e *HandshakeTimeoutError) Temporary() bool { return false }
|
||||
func (e *HandshakeTimeoutError) Error() string { return "timeout: handshake did not complete in time" }
|
||||
|
||||
// A VersionNegotiationError occurs when the client and the server can't agree on a QUIC version.
|
||||
type VersionNegotiationError struct {
|
||||
Ours []protocol.VersionNumber
|
||||
Theirs []protocol.VersionNumber
|
||||
}
|
||||
|
||||
func (e *VersionNegotiationError) Error() string {
|
||||
return fmt.Sprintf("no compatible QUIC version found (we support %s, server offered %s)", e.Ours, e.Theirs)
|
||||
}
|
||||
|
||||
// A StatelessResetError occurs when we receive a stateless reset.
|
||||
type StatelessResetError struct {
|
||||
Token protocol.StatelessResetToken
|
||||
}
|
||||
|
||||
var _ net.Error = &StatelessResetError{}
|
||||
|
||||
func (e *StatelessResetError) Error() string {
|
||||
return fmt.Sprintf("received a stateless reset with token %x", e.Token)
|
||||
}
|
||||
|
||||
func (e *StatelessResetError) Timeout() bool { return false }
|
||||
func (e *StatelessResetError) Temporary() bool { return true }
|
||||
-55
@@ -1,55 +0,0 @@
|
||||
// +build go1.16
|
||||
|
||||
package qerr
|
||||
|
||||
import (
|
||||
"net"
|
||||
)
|
||||
|
||||
func (e *TransportError) Is(target error) bool {
|
||||
_, ok := target.(*TransportError)
|
||||
if ok {
|
||||
return true
|
||||
}
|
||||
return target == net.ErrClosed
|
||||
}
|
||||
|
||||
func (e *ApplicationError) Is(target error) bool {
|
||||
_, ok := target.(*ApplicationError)
|
||||
if ok {
|
||||
return true
|
||||
}
|
||||
return target == net.ErrClosed
|
||||
}
|
||||
|
||||
func (e *IdleTimeoutError) Is(target error) bool {
|
||||
_, ok := target.(*IdleTimeoutError)
|
||||
if ok {
|
||||
return true
|
||||
}
|
||||
return target == net.ErrClosed
|
||||
}
|
||||
|
||||
func (e *HandshakeTimeoutError) Is(target error) bool {
|
||||
_, ok := target.(*HandshakeTimeoutError)
|
||||
if ok {
|
||||
return true
|
||||
}
|
||||
return target == net.ErrClosed
|
||||
}
|
||||
|
||||
func (e *VersionNegotiationError) Is(target error) bool {
|
||||
_, ok := target.(*VersionNegotiationError)
|
||||
if ok {
|
||||
return true
|
||||
}
|
||||
return target == net.ErrClosed
|
||||
}
|
||||
|
||||
func (e *StatelessResetError) Is(target error) bool {
|
||||
_, ok := target.(*StatelessResetError)
|
||||
if ok {
|
||||
return true
|
||||
}
|
||||
return target == net.ErrClosed
|
||||
}
|
||||
-33
@@ -1,33 +0,0 @@
|
||||
// +build !go1.16
|
||||
|
||||
package qerr
|
||||
|
||||
func (e *TransportError) Is(target error) bool {
|
||||
_, ok := target.(*TransportError)
|
||||
return ok
|
||||
}
|
||||
|
||||
func (e *ApplicationError) Is(target error) bool {
|
||||
_, ok := target.(*ApplicationError)
|
||||
return ok
|
||||
}
|
||||
|
||||
func (e *IdleTimeoutError) Is(target error) bool {
|
||||
_, ok := target.(*IdleTimeoutError)
|
||||
return ok
|
||||
}
|
||||
|
||||
func (e *HandshakeTimeoutError) Is(target error) bool {
|
||||
_, ok := target.(*HandshakeTimeoutError)
|
||||
return ok
|
||||
}
|
||||
|
||||
func (e *VersionNegotiationError) Is(target error) bool {
|
||||
_, ok := target.(*VersionNegotiationError)
|
||||
return ok
|
||||
}
|
||||
|
||||
func (e *StatelessResetError) Is(target error) bool {
|
||||
_, ok := target.(*StatelessResetError)
|
||||
return ok
|
||||
}
|
||||
+112
@@ -0,0 +1,112 @@
|
||||
package qerr
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net"
|
||||
)
|
||||
|
||||
// A QuicError consists of an error code plus a error reason
|
||||
type QuicError struct {
|
||||
ErrorCode ErrorCode
|
||||
FrameType uint64 // only valid if this not an application error
|
||||
ErrorMessage string
|
||||
isTimeout bool
|
||||
isApplicationError bool
|
||||
}
|
||||
|
||||
var _ net.Error = &QuicError{}
|
||||
|
||||
// NewError creates a new QuicError instance
|
||||
func NewError(errorCode ErrorCode, errorMessage string) *QuicError {
|
||||
return &QuicError{
|
||||
ErrorCode: errorCode,
|
||||
ErrorMessage: errorMessage,
|
||||
}
|
||||
}
|
||||
|
||||
// NewErrorWithFrameType creates a new QuicError instance for a specific frame type
|
||||
func NewErrorWithFrameType(errorCode ErrorCode, frameType uint64, errorMessage string) *QuicError {
|
||||
return &QuicError{
|
||||
ErrorCode: errorCode,
|
||||
FrameType: frameType,
|
||||
ErrorMessage: errorMessage,
|
||||
}
|
||||
}
|
||||
|
||||
// NewTimeoutError creates a new QuicError instance for a timeout error
|
||||
func NewTimeoutError(errorMessage string) *QuicError {
|
||||
return &QuicError{
|
||||
ErrorMessage: errorMessage,
|
||||
isTimeout: true,
|
||||
}
|
||||
}
|
||||
|
||||
// NewCryptoError create a new QuicError instance for a crypto error
|
||||
func NewCryptoError(tlsAlert uint8, errorMessage string) *QuicError {
|
||||
return &QuicError{
|
||||
ErrorCode: 0x100 + ErrorCode(tlsAlert),
|
||||
ErrorMessage: errorMessage,
|
||||
}
|
||||
}
|
||||
|
||||
// NewApplicationError creates a new QuicError instance for an application error
|
||||
func NewApplicationError(errorCode ErrorCode, errorMessage string) *QuicError {
|
||||
return &QuicError{
|
||||
ErrorCode: errorCode,
|
||||
ErrorMessage: errorMessage,
|
||||
isApplicationError: true,
|
||||
}
|
||||
}
|
||||
|
||||
func (e *QuicError) Error() string {
|
||||
if e.isApplicationError {
|
||||
if len(e.ErrorMessage) == 0 {
|
||||
return fmt.Sprintf("Application error %#x", uint64(e.ErrorCode))
|
||||
}
|
||||
return fmt.Sprintf("Application error %#x: %s", uint64(e.ErrorCode), e.ErrorMessage)
|
||||
}
|
||||
str := e.ErrorCode.String()
|
||||
if e.FrameType != 0 {
|
||||
str += fmt.Sprintf(" (frame type: %#x)", e.FrameType)
|
||||
}
|
||||
msg := e.ErrorMessage
|
||||
if len(msg) == 0 {
|
||||
msg = e.ErrorCode.Message()
|
||||
}
|
||||
if len(msg) == 0 {
|
||||
return str
|
||||
}
|
||||
return str + ": " + msg
|
||||
}
|
||||
|
||||
// IsCryptoError says if this error is a crypto error
|
||||
func (e *QuicError) IsCryptoError() bool {
|
||||
return e.ErrorCode.isCryptoError()
|
||||
}
|
||||
|
||||
// IsApplicationError says if this error is an application error
|
||||
func (e *QuicError) IsApplicationError() bool {
|
||||
return e.isApplicationError
|
||||
}
|
||||
|
||||
// Temporary says if the error is temporary.
|
||||
func (e *QuicError) Temporary() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
// Timeout says if this error is a timeout.
|
||||
func (e *QuicError) Timeout() bool {
|
||||
return e.isTimeout
|
||||
}
|
||||
|
||||
// ToQuicError converts an arbitrary error to a QuicError. It leaves QuicErrors
|
||||
// unchanged, and properly handles `ErrorCode`s.
|
||||
func ToQuicError(err error) *QuicError {
|
||||
switch e := err.(type) {
|
||||
case *QuicError:
|
||||
return e
|
||||
case ErrorCode:
|
||||
return NewError(e, "")
|
||||
}
|
||||
return NewError(InternalError, err.Error())
|
||||
}
|
||||
+16
-1
@@ -10,7 +10,7 @@ import (
|
||||
"net"
|
||||
"unsafe"
|
||||
|
||||
"github.com/marten-seemann/qtls-go1-15"
|
||||
qtls "github.com/marten-seemann/qtls-go1-15"
|
||||
)
|
||||
|
||||
type (
|
||||
@@ -53,6 +53,21 @@ const (
|
||||
EncryptionApplication = qtls.EncryptionApplication
|
||||
)
|
||||
|
||||
// CipherSuiteName gets the name of a cipher suite.
|
||||
func CipherSuiteName(id uint16) string {
|
||||
return qtls.CipherSuiteName(id)
|
||||
}
|
||||
|
||||
// HkdfExtract generates a pseudorandom key for use with Expand from an input secret and an optional independent salt.
|
||||
func HkdfExtract(hash crypto.Hash, newSecret, currentSecret []byte) []byte {
|
||||
return qtls.HkdfExtract(hash, newSecret, currentSecret)
|
||||
}
|
||||
|
||||
// HkdfExpandLabel HKDF expands a label
|
||||
func HkdfExpandLabel(hash crypto.Hash, secret, hashValue []byte, label string, L int) []byte {
|
||||
return qtls.HkdfExpandLabel(hash, secret, hashValue, label, L)
|
||||
}
|
||||
|
||||
// AEADAESGCMTLS13 creates a new AES-GCM AEAD for TLS 1.3
|
||||
func AEADAESGCMTLS13(key, fixedNonce []byte) cipher.AEAD {
|
||||
return qtls.AEADAESGCMTLS13(key, fixedNonce)
|
||||
|
||||
+16
-2
@@ -1,5 +1,4 @@
|
||||
// +build go1.16
|
||||
// +build !go1.17
|
||||
|
||||
package qtls
|
||||
|
||||
@@ -10,7 +9,7 @@ import (
|
||||
"net"
|
||||
"unsafe"
|
||||
|
||||
"github.com/marten-seemann/qtls-go1-16"
|
||||
qtls "github.com/marten-seemann/qtls-go1-16"
|
||||
)
|
||||
|
||||
type (
|
||||
@@ -53,6 +52,21 @@ const (
|
||||
EncryptionApplication = qtls.EncryptionApplication
|
||||
)
|
||||
|
||||
// CipherSuiteName gets the name of a cipher suite.
|
||||
func CipherSuiteName(id uint16) string {
|
||||
return qtls.CipherSuiteName(id)
|
||||
}
|
||||
|
||||
// HkdfExtract generates a pseudorandom key for use with Expand from an input secret and an optional independent salt.
|
||||
func HkdfExtract(hash crypto.Hash, newSecret, currentSecret []byte) []byte {
|
||||
return qtls.HkdfExtract(hash, newSecret, currentSecret)
|
||||
}
|
||||
|
||||
// HkdfExpandLabel HKDF expands a label
|
||||
func HkdfExpandLabel(hash crypto.Hash, secret, hashValue []byte, label string, L int) []byte {
|
||||
return qtls.HkdfExpandLabel(hash, secret, hashValue, label, L)
|
||||
}
|
||||
|
||||
// AEADAESGCMTLS13 creates a new AES-GCM AEAD for TLS 1.3
|
||||
func AEADAESGCMTLS13(key, fixedNonce []byte) cipher.AEAD {
|
||||
return qtls.AEADAESGCMTLS13(key, fixedNonce)
|
||||
|
||||
-99
@@ -1,99 +0,0 @@
|
||||
// +build go1.17
|
||||
|
||||
package qtls
|
||||
|
||||
import (
|
||||
"crypto"
|
||||
"crypto/cipher"
|
||||
"crypto/tls"
|
||||
"net"
|
||||
"unsafe"
|
||||
|
||||
"github.com/marten-seemann/qtls-go1-17"
|
||||
)
|
||||
|
||||
type (
|
||||
// Alert is a TLS alert
|
||||
Alert = qtls.Alert
|
||||
// A Certificate is qtls.Certificate.
|
||||
Certificate = qtls.Certificate
|
||||
// CertificateRequestInfo contains inforamtion about a certificate request.
|
||||
CertificateRequestInfo = qtls.CertificateRequestInfo
|
||||
// A CipherSuiteTLS13 is a cipher suite for TLS 1.3
|
||||
CipherSuiteTLS13 = qtls.CipherSuiteTLS13
|
||||
// ClientHelloInfo contains information about a ClientHello.
|
||||
ClientHelloInfo = qtls.ClientHelloInfo
|
||||
// ClientSessionCache is a cache used for session resumption.
|
||||
ClientSessionCache = qtls.ClientSessionCache
|
||||
// ClientSessionState is a state needed for session resumption.
|
||||
ClientSessionState = qtls.ClientSessionState
|
||||
// A Config is a qtls.Config.
|
||||
Config = qtls.Config
|
||||
// A Conn is a qtls.Conn.
|
||||
Conn = qtls.Conn
|
||||
// ConnectionState contains information about the state of the connection.
|
||||
ConnectionState = qtls.ConnectionStateWith0RTT
|
||||
// EncryptionLevel is the encryption level of a message.
|
||||
EncryptionLevel = qtls.EncryptionLevel
|
||||
// Extension is a TLS extension
|
||||
Extension = qtls.Extension
|
||||
// ExtraConfig is the qtls.ExtraConfig
|
||||
ExtraConfig = qtls.ExtraConfig
|
||||
// RecordLayer is a qtls RecordLayer.
|
||||
RecordLayer = qtls.RecordLayer
|
||||
)
|
||||
|
||||
const (
|
||||
// EncryptionHandshake is the Handshake encryption level
|
||||
EncryptionHandshake = qtls.EncryptionHandshake
|
||||
// Encryption0RTT is the 0-RTT encryption level
|
||||
Encryption0RTT = qtls.Encryption0RTT
|
||||
// EncryptionApplication is the application data encryption level
|
||||
EncryptionApplication = qtls.EncryptionApplication
|
||||
)
|
||||
|
||||
// AEADAESGCMTLS13 creates a new AES-GCM AEAD for TLS 1.3
|
||||
func AEADAESGCMTLS13(key, fixedNonce []byte) cipher.AEAD {
|
||||
return qtls.AEADAESGCMTLS13(key, fixedNonce)
|
||||
}
|
||||
|
||||
// Client returns a new TLS client side connection.
|
||||
func Client(conn net.Conn, config *Config, extraConfig *ExtraConfig) *Conn {
|
||||
return qtls.Client(conn, config, extraConfig)
|
||||
}
|
||||
|
||||
// Server returns a new TLS server side connection.
|
||||
func Server(conn net.Conn, config *Config, extraConfig *ExtraConfig) *Conn {
|
||||
return qtls.Server(conn, config, extraConfig)
|
||||
}
|
||||
|
||||
func GetConnectionState(conn *Conn) ConnectionState {
|
||||
return conn.ConnectionStateWith0RTT()
|
||||
}
|
||||
|
||||
// ToTLSConnectionState extracts the tls.ConnectionState
|
||||
func ToTLSConnectionState(cs ConnectionState) tls.ConnectionState {
|
||||
return cs.ConnectionState
|
||||
}
|
||||
|
||||
type cipherSuiteTLS13 struct {
|
||||
ID uint16
|
||||
KeyLen int
|
||||
AEAD func(key, fixedNonce []byte) cipher.AEAD
|
||||
Hash crypto.Hash
|
||||
}
|
||||
|
||||
//go:linkname cipherSuiteTLS13ByID github.com/marten-seemann/qtls-go1-17.cipherSuiteTLS13ByID
|
||||
func cipherSuiteTLS13ByID(id uint16) *cipherSuiteTLS13
|
||||
|
||||
// CipherSuiteTLS13ByID gets a TLS 1.3 cipher suite.
|
||||
func CipherSuiteTLS13ByID(id uint16) *CipherSuiteTLS13 {
|
||||
val := cipherSuiteTLS13ByID(id)
|
||||
cs := (*cipherSuiteTLS13)(unsafe.Pointer(val))
|
||||
return &qtls.CipherSuiteTLS13{
|
||||
ID: cs.ID,
|
||||
KeyLen: cs.KeyLen,
|
||||
AEAD: cs.AEAD,
|
||||
Hash: cs.Hash,
|
||||
}
|
||||
}
|
||||
-5
@@ -1,5 +0,0 @@
|
||||
// +build go1.18
|
||||
|
||||
package qtls
|
||||
|
||||
var _ int = "quic-go doesn't build on Go 1.18 yet."
|
||||
+6
-5
@@ -5,13 +5,14 @@ import (
|
||||
"io"
|
||||
|
||||
"github.com/lucas-clemente/quic-go/internal/protocol"
|
||||
"github.com/lucas-clemente/quic-go/internal/qerr"
|
||||
"github.com/lucas-clemente/quic-go/quicvarint"
|
||||
)
|
||||
|
||||
// A ConnectionCloseFrame is a CONNECTION_CLOSE frame
|
||||
type ConnectionCloseFrame struct {
|
||||
IsApplicationError bool
|
||||
ErrorCode uint64
|
||||
ErrorCode qerr.ErrorCode
|
||||
FrameType uint64
|
||||
ReasonPhrase string
|
||||
}
|
||||
@@ -27,7 +28,7 @@ func parseConnectionCloseFrame(r *bytes.Reader, _ protocol.VersionNumber) (*Conn
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
f.ErrorCode = ec
|
||||
f.ErrorCode = qerr.ErrorCode(ec)
|
||||
// read the Frame Type, if this is not an application error
|
||||
if !f.IsApplicationError {
|
||||
ft, err := quicvarint.Read(r)
|
||||
@@ -58,8 +59,8 @@ func parseConnectionCloseFrame(r *bytes.Reader, _ protocol.VersionNumber) (*Conn
|
||||
}
|
||||
|
||||
// Length of a written frame
|
||||
func (f *ConnectionCloseFrame) Length(protocol.VersionNumber) protocol.ByteCount {
|
||||
length := 1 + quicvarint.Len(f.ErrorCode) + quicvarint.Len(uint64(len(f.ReasonPhrase))) + protocol.ByteCount(len(f.ReasonPhrase))
|
||||
func (f *ConnectionCloseFrame) Length(version protocol.VersionNumber) protocol.ByteCount {
|
||||
length := 1 + quicvarint.Len(uint64(f.ErrorCode)) + quicvarint.Len(uint64(len(f.ReasonPhrase))) + protocol.ByteCount(len(f.ReasonPhrase))
|
||||
if !f.IsApplicationError {
|
||||
length += quicvarint.Len(f.FrameType) // for the frame type
|
||||
}
|
||||
@@ -73,7 +74,7 @@ func (f *ConnectionCloseFrame) Write(b *bytes.Buffer, version protocol.VersionNu
|
||||
b.WriteByte(0x1c)
|
||||
}
|
||||
|
||||
quicvarint.Write(b, f.ErrorCode)
|
||||
quicvarint.Write(b, uint64(f.ErrorCode))
|
||||
if !f.IsApplicationError {
|
||||
quicvarint.Write(b, f.FrameType)
|
||||
}
|
||||
|
||||
+2
-6
@@ -26,7 +26,7 @@ func NewFrameParser(supportsDatagrams bool, v protocol.VersionNumber) FrameParse
|
||||
}
|
||||
}
|
||||
|
||||
// ParseNext parses the next frame.
|
||||
// ParseNextFrame parses the next frame
|
||||
// It skips PADDING frames.
|
||||
func (p *frameParser) ParseNext(r *bytes.Reader, encLevel protocol.EncryptionLevel) (Frame, error) {
|
||||
for r.Len() != 0 {
|
||||
@@ -38,11 +38,7 @@ func (p *frameParser) ParseNext(r *bytes.Reader, encLevel protocol.EncryptionLev
|
||||
|
||||
f, err := p.parseFrame(r, typeByte, encLevel)
|
||||
if err != nil {
|
||||
return nil, &qerr.TransportError{
|
||||
FrameType: uint64(typeByte),
|
||||
ErrorCode: qerr.FrameEncodingError,
|
||||
ErrorMessage: err.Error(),
|
||||
}
|
||||
return nil, qerr.NewErrorWithFrameType(qerr.FrameEncodingError, uint64(typeByte), err.Error())
|
||||
}
|
||||
return f, nil
|
||||
}
|
||||
|
||||
+2
-3
@@ -4,14 +4,13 @@ import (
|
||||
"bytes"
|
||||
|
||||
"github.com/lucas-clemente/quic-go/internal/protocol"
|
||||
"github.com/lucas-clemente/quic-go/internal/qerr"
|
||||
"github.com/lucas-clemente/quic-go/quicvarint"
|
||||
)
|
||||
|
||||
// A ResetStreamFrame is a RESET_STREAM frame in QUIC
|
||||
type ResetStreamFrame struct {
|
||||
StreamID protocol.StreamID
|
||||
ErrorCode qerr.StreamErrorCode
|
||||
ErrorCode protocol.ApplicationErrorCode
|
||||
FinalSize protocol.ByteCount
|
||||
}
|
||||
|
||||
@@ -39,7 +38,7 @@ func parseResetStreamFrame(r *bytes.Reader, _ protocol.VersionNumber) (*ResetStr
|
||||
|
||||
return &ResetStreamFrame{
|
||||
StreamID: streamID,
|
||||
ErrorCode: qerr.StreamErrorCode(errorCode),
|
||||
ErrorCode: protocol.ApplicationErrorCode(errorCode),
|
||||
FinalSize: byteOffset,
|
||||
}, nil
|
||||
}
|
||||
|
||||
+2
-3
@@ -4,14 +4,13 @@ import (
|
||||
"bytes"
|
||||
|
||||
"github.com/lucas-clemente/quic-go/internal/protocol"
|
||||
"github.com/lucas-clemente/quic-go/internal/qerr"
|
||||
"github.com/lucas-clemente/quic-go/quicvarint"
|
||||
)
|
||||
|
||||
// A StopSendingFrame is a STOP_SENDING frame
|
||||
type StopSendingFrame struct {
|
||||
StreamID protocol.StreamID
|
||||
ErrorCode qerr.StreamErrorCode
|
||||
ErrorCode protocol.ApplicationErrorCode
|
||||
}
|
||||
|
||||
// parseStopSendingFrame parses a STOP_SENDING frame
|
||||
@@ -31,7 +30,7 @@ func parseStopSendingFrame(r *bytes.Reader, _ protocol.VersionNumber) (*StopSend
|
||||
|
||||
return &StopSendingFrame{
|
||||
StreamID: protocol.StreamID(streamID),
|
||||
ErrorCode: qerr.StreamErrorCode(errorCode),
|
||||
ErrorCode: protocol.ApplicationErrorCode(errorCode),
|
||||
}, nil
|
||||
}
|
||||
|
||||
|
||||
+2
-1
@@ -6,6 +6,7 @@ import (
|
||||
"io"
|
||||
|
||||
"github.com/lucas-clemente/quic-go/internal/protocol"
|
||||
"github.com/lucas-clemente/quic-go/internal/qerr"
|
||||
"github.com/lucas-clemente/quic-go/quicvarint"
|
||||
)
|
||||
|
||||
@@ -78,7 +79,7 @@ func parseStreamFrame(r *bytes.Reader, _ protocol.VersionNumber) (*StreamFrame,
|
||||
}
|
||||
}
|
||||
if frame.Offset+frame.DataLen() > protocol.MaxByteCount {
|
||||
return nil, errors.New("stream data overflows maximum offset")
|
||||
return nil, qerr.NewError(qerr.FrameEncodingError, "stream data overflows maximum offset")
|
||||
}
|
||||
return frame, nil
|
||||
}
|
||||
|
||||
+2
-5
@@ -90,10 +90,7 @@ type TransportParameters struct {
|
||||
// Unmarshal the transport parameters
|
||||
func (p *TransportParameters) Unmarshal(data []byte, sentBy protocol.Perspective) error {
|
||||
if err := p.unmarshal(bytes.NewReader(data), sentBy, false); err != nil {
|
||||
return &qerr.TransportError{
|
||||
ErrorCode: qerr.TransportParameterError,
|
||||
ErrorMessage: err.Error(),
|
||||
}
|
||||
return qerr.NewError(qerr.TransportParameterError, err.Error())
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -262,7 +259,7 @@ func (p *TransportParameters) readNumericTransportParameter(
|
||||
return fmt.Errorf("error while reading transport parameter %d: %s", paramID, err)
|
||||
}
|
||||
if remainingLen-r.Len() != expectedLen {
|
||||
return fmt.Errorf("inconsistent transport parameter length for transport parameter %#x", paramID)
|
||||
return fmt.Errorf("inconsistent transport parameter length for %d", paramID)
|
||||
}
|
||||
//nolint:exhaustive // This only covers the numeric transport parameters.
|
||||
switch paramID {
|
||||
|
||||
+78
@@ -0,0 +1,78 @@
|
||||
package logging
|
||||
|
||||
// A CloseReason is the reason why a QUIC connection is closed.
|
||||
// It falls in one of 4 categories:
|
||||
// 1. The application closed the connection (with an application-specific error code).
|
||||
// 2. The transport closed the connection with a transport-error code.
|
||||
// 3. The connection timed out, either during the handshake, or due to an idle timeout.
|
||||
// 4. A stateless reset was received.
|
||||
type CloseReason struct {
|
||||
remote bool
|
||||
applicationError *ApplicationError
|
||||
transportError *TransportError
|
||||
|
||||
timeout *TimeoutReason
|
||||
statelessResetToken *StatelessResetToken
|
||||
versions []VersionNumber
|
||||
}
|
||||
|
||||
// NewApplicationCloseReason creates a new CloseReason for an application error.
|
||||
func NewApplicationCloseReason(errorCode ApplicationError, remote bool) CloseReason {
|
||||
return CloseReason{remote: remote, applicationError: &errorCode}
|
||||
}
|
||||
|
||||
// NewTransportCloseReason creates a new CloseReason for a transport error.
|
||||
func NewTransportCloseReason(errorCode TransportError, remote bool) CloseReason {
|
||||
return CloseReason{remote: remote, transportError: &errorCode}
|
||||
}
|
||||
|
||||
// NewTimeoutCloseReason creates a new CloseReason for a connection timeout.
|
||||
func NewTimeoutCloseReason(r TimeoutReason) CloseReason {
|
||||
return CloseReason{timeout: &r}
|
||||
}
|
||||
|
||||
// NewStatelessResetCloseReason creates a new CloseReason for a stateless reset.
|
||||
func NewStatelessResetCloseReason(token StatelessResetToken) CloseReason {
|
||||
return CloseReason{statelessResetToken: &token}
|
||||
}
|
||||
|
||||
// NewVersionNegotiationError creates a new CloseReason for a version negotiation error.
|
||||
func NewVersionNegotiationError(versions []VersionNumber) CloseReason {
|
||||
return CloseReason{versions: versions}
|
||||
}
|
||||
|
||||
// ApplicationError gets the application error.
|
||||
func (r *CloseReason) ApplicationError() (errorCode ApplicationError, remote bool, ok bool) {
|
||||
if r.applicationError == nil {
|
||||
return
|
||||
}
|
||||
return *r.applicationError, r.remote, true
|
||||
}
|
||||
|
||||
// TransportError gets the transport error.
|
||||
func (r *CloseReason) TransportError() (errorCode TransportError, remote bool, ok bool) {
|
||||
if r.transportError == nil {
|
||||
return
|
||||
}
|
||||
return *r.transportError, r.remote, true
|
||||
}
|
||||
|
||||
// Timeout gets the timeout error.
|
||||
func (r *CloseReason) Timeout() (reason TimeoutReason, ok bool) {
|
||||
if r.timeout == nil {
|
||||
return
|
||||
}
|
||||
return *r.timeout, true
|
||||
}
|
||||
|
||||
// StatelessReset gets the stateless reset token.
|
||||
func (r *CloseReason) StatelessReset() (token StatelessResetToken, ok bool) {
|
||||
if r.statelessResetToken == nil {
|
||||
return
|
||||
}
|
||||
return *r.statelessResetToken, true
|
||||
}
|
||||
|
||||
func (r *CloseReason) VersionNegotiation() (versions []VersionNumber, ok bool) {
|
||||
return r.versions, len(r.versions) > 0
|
||||
}
|
||||
+6
-9
@@ -3,7 +3,6 @@
|
||||
package logging
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net"
|
||||
"time"
|
||||
|
||||
@@ -50,9 +49,9 @@ type (
|
||||
PreferredAddress = wire.PreferredAddress
|
||||
|
||||
// A TransportError is a transport-level error code.
|
||||
TransportError = qerr.TransportErrorCode
|
||||
TransportError = qerr.ErrorCode
|
||||
// An ApplicationError is an application-defined error code.
|
||||
ApplicationError = qerr.TransportErrorCode
|
||||
ApplicationError = qerr.ErrorCode
|
||||
|
||||
// The RTTStats contain statistics used by the congestion controller.
|
||||
RTTStats = utils.RTTStats
|
||||
@@ -92,11 +91,11 @@ const (
|
||||
|
||||
// A Tracer traces events.
|
||||
type Tracer interface {
|
||||
// TracerForConnection requests a new tracer for a connection.
|
||||
// ConnectionTracer requests a new tracer for a connection.
|
||||
// The ODCID is the original destination connection ID:
|
||||
// The destination connection ID that the client used on the first Initial packet it sent on this connection.
|
||||
// If nil is returned, tracing will be disabled for this connection.
|
||||
TracerForConnection(ctx context.Context, p Perspective, odcid ConnectionID) ConnectionTracer
|
||||
TracerForConnection(p Perspective, odcid ConnectionID) ConnectionTracer
|
||||
|
||||
SentPacket(net.Addr, *Header, ByteCount, []Frame)
|
||||
DroppedPacket(net.Addr, PacketType, ByteCount, PacketDropReason)
|
||||
@@ -104,9 +103,8 @@ type Tracer interface {
|
||||
|
||||
// A ConnectionTracer records events.
|
||||
type ConnectionTracer interface {
|
||||
StartedConnection(local, remote net.Addr, srcConnID, destConnID ConnectionID)
|
||||
NegotiatedVersion(chosen VersionNumber, clientVersions, serverVersions []VersionNumber)
|
||||
ClosedConnection(error)
|
||||
StartedConnection(local, remote net.Addr, version VersionNumber, srcConnID, destConnID ConnectionID)
|
||||
ClosedConnection(CloseReason)
|
||||
SentTransportParameters(*TransportParameters)
|
||||
ReceivedTransportParameters(*TransportParameters)
|
||||
RestoredTransportParameters(parameters *TransportParameters) // for 0-RTT
|
||||
@@ -117,7 +115,6 @@ type ConnectionTracer interface {
|
||||
BufferedPacket(PacketType)
|
||||
DroppedPacket(PacketType, ByteCount, PacketDropReason)
|
||||
UpdatedMetrics(rttStats *RTTStats, cwnd, bytesInFlight ByteCount, packetsInFlight int)
|
||||
AcknowledgedPacket(EncryptionLevel, PacketNumber)
|
||||
LostPacket(EncryptionLevel, PacketNumber, PacketLossReason)
|
||||
UpdatedCongestionState(CongestionState)
|
||||
UpdatedPTOCount(value uint32)
|
||||
|
||||
+6
-19
@@ -1,7 +1,6 @@
|
||||
package logging
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net"
|
||||
"time"
|
||||
)
|
||||
@@ -23,10 +22,10 @@ func NewMultiplexedTracer(tracers ...Tracer) Tracer {
|
||||
return &tracerMultiplexer{tracers}
|
||||
}
|
||||
|
||||
func (m *tracerMultiplexer) TracerForConnection(ctx context.Context, p Perspective, odcid ConnectionID) ConnectionTracer {
|
||||
func (m *tracerMultiplexer) TracerForConnection(p Perspective, odcid ConnectionID) ConnectionTracer {
|
||||
var connTracers []ConnectionTracer
|
||||
for _, t := range m.tracers {
|
||||
if ct := t.TracerForConnection(ctx, p, odcid); ct != nil {
|
||||
if ct := t.TracerForConnection(p, odcid); ct != nil {
|
||||
connTracers = append(connTracers, ct)
|
||||
}
|
||||
}
|
||||
@@ -62,21 +61,15 @@ func NewMultiplexedConnectionTracer(tracers ...ConnectionTracer) ConnectionTrace
|
||||
return &connTracerMultiplexer{tracers: tracers}
|
||||
}
|
||||
|
||||
func (m *connTracerMultiplexer) StartedConnection(local, remote net.Addr, srcConnID, destConnID ConnectionID) {
|
||||
func (m *connTracerMultiplexer) StartedConnection(local, remote net.Addr, version VersionNumber, srcConnID, destConnID ConnectionID) {
|
||||
for _, t := range m.tracers {
|
||||
t.StartedConnection(local, remote, srcConnID, destConnID)
|
||||
t.StartedConnection(local, remote, version, srcConnID, destConnID)
|
||||
}
|
||||
}
|
||||
|
||||
func (m *connTracerMultiplexer) NegotiatedVersion(chosen VersionNumber, clientVersions, serverVersions []VersionNumber) {
|
||||
func (m *connTracerMultiplexer) ClosedConnection(reason CloseReason) {
|
||||
for _, t := range m.tracers {
|
||||
t.NegotiatedVersion(chosen, clientVersions, serverVersions)
|
||||
}
|
||||
}
|
||||
|
||||
func (m *connTracerMultiplexer) ClosedConnection(e error) {
|
||||
for _, t := range m.tracers {
|
||||
t.ClosedConnection(e)
|
||||
t.ClosedConnection(reason)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -146,12 +139,6 @@ func (m *connTracerMultiplexer) UpdatedMetrics(rttStats *RTTStats, cwnd, bytesIn
|
||||
}
|
||||
}
|
||||
|
||||
func (m *connTracerMultiplexer) AcknowledgedPacket(encLevel EncryptionLevel, pn PacketNumber) {
|
||||
for _, t := range m.tracers {
|
||||
t.AcknowledgedPacket(encLevel, pn)
|
||||
}
|
||||
}
|
||||
|
||||
func (m *connTracerMultiplexer) LostPacket(encLevel EncryptionLevel, pn PacketNumber, reason PacketLossReason) {
|
||||
for _, t := range m.tracers {
|
||||
t.LostPacket(encLevel, pn, reason)
|
||||
|
||||
+3
-1
@@ -1,7 +1,9 @@
|
||||
package logging
|
||||
|
||||
import "github.com/lucas-clemente/quic-go/internal/protocol"
|
||||
|
||||
// PacketType is the packet type of a QUIC packet
|
||||
type PacketType uint8
|
||||
type PacketType = protocol.PacketType
|
||||
|
||||
const (
|
||||
// PacketTypeInitial is the packet type of an Initial packet
|
||||
|
||||
+9
-1
@@ -18,6 +18,14 @@ import (
|
||||
"github.com/lucas-clemente/quic-go/logging"
|
||||
)
|
||||
|
||||
type statelessResetErr struct {
|
||||
token protocol.StatelessResetToken
|
||||
}
|
||||
|
||||
func (e statelessResetErr) Error() string {
|
||||
return fmt.Sprintf("received a stateless reset with token %x", e.token)
|
||||
}
|
||||
|
||||
type zeroRTTQueue struct {
|
||||
queue []*receivedPacket
|
||||
retireTimer *time.Timer
|
||||
@@ -422,7 +430,7 @@ func (h *packetHandlerMap) maybeHandleStatelessReset(data []byte) bool {
|
||||
copy(token[:], data[len(data)-16:])
|
||||
if sess, ok := h.resetTokens[token]; ok {
|
||||
h.logger.Debugf("Received a stateless reset with token %#x. Closing session.", token)
|
||||
go sess.destroy(&StatelessResetError{Token: token})
|
||||
go sess.destroy(statelessResetErr{token: token})
|
||||
return true
|
||||
}
|
||||
return false
|
||||
|
||||
+18
-29
@@ -20,8 +20,7 @@ type packer interface {
|
||||
PackPacket() (*packedPacket, error)
|
||||
MaybePackProbePacket(protocol.EncryptionLevel) (*packedPacket, error)
|
||||
MaybePackAckPacket(handshakeConfirmed bool) (*packedPacket, error)
|
||||
PackConnectionClose(*qerr.TransportError) (*coalescedPacket, error)
|
||||
PackApplicationClose(*qerr.ApplicationError) (*coalescedPacket, error)
|
||||
PackConnectionClose(*qerr.QuicError) (*coalescedPacket, error)
|
||||
|
||||
SetMaxPacketSize(protocol.ByteCount)
|
||||
PackMTUProbePacket(ping ackhandler.Frame, size protocol.ByteCount) (*packedPacket, error)
|
||||
@@ -204,27 +203,14 @@ func newPacketPacker(
|
||||
}
|
||||
}
|
||||
|
||||
// PackConnectionClose packs a packet that closes the connection with a transport error.
|
||||
func (p *packetPacker) PackConnectionClose(e *qerr.TransportError) (*coalescedPacket, error) {
|
||||
// PackConnectionClose packs a packet that ONLY contains a ConnectionCloseFrame
|
||||
func (p *packetPacker) PackConnectionClose(quicErr *qerr.QuicError) (*coalescedPacket, error) {
|
||||
var reason string
|
||||
// don't send details of crypto errors
|
||||
if !e.ErrorCode.IsCryptoError() {
|
||||
reason = e.ErrorMessage
|
||||
if !quicErr.IsCryptoError() {
|
||||
reason = quicErr.ErrorMessage
|
||||
}
|
||||
return p.packConnectionClose(false, uint64(e.ErrorCode), e.FrameType, reason)
|
||||
}
|
||||
|
||||
// PackApplicationClose packs a packet that closes the connection with an application error.
|
||||
func (p *packetPacker) PackApplicationClose(e *qerr.ApplicationError) (*coalescedPacket, error) {
|
||||
return p.packConnectionClose(true, uint64(e.ErrorCode), 0, e.ErrorMessage)
|
||||
}
|
||||
|
||||
func (p *packetPacker) packConnectionClose(
|
||||
isApplicationError bool,
|
||||
errorCode uint64,
|
||||
frameType uint64,
|
||||
reason string,
|
||||
) (*coalescedPacket, error) {
|
||||
var sealers [4]sealer
|
||||
var hdrs [4]*wire.ExtendedHeader
|
||||
var payloads [4]*payload
|
||||
@@ -235,17 +221,20 @@ func (p *packetPacker) packConnectionClose(
|
||||
if p.perspective == protocol.PerspectiveServer && encLevel == protocol.Encryption0RTT {
|
||||
continue
|
||||
}
|
||||
ccf := &wire.ConnectionCloseFrame{
|
||||
IsApplicationError: isApplicationError,
|
||||
ErrorCode: errorCode,
|
||||
FrameType: frameType,
|
||||
ReasonPhrase: reason,
|
||||
quicErrToSend := quicErr
|
||||
reasonPhrase := reason
|
||||
if encLevel == protocol.EncryptionInitial || encLevel == protocol.EncryptionHandshake {
|
||||
// don't send application errors in Initial or Handshake packets
|
||||
if quicErr.IsApplicationError() {
|
||||
quicErrToSend = qerr.NewError(qerr.ApplicationError, "")
|
||||
reasonPhrase = ""
|
||||
}
|
||||
}
|
||||
// don't send application errors in Initial or Handshake packets
|
||||
if isApplicationError && (encLevel == protocol.EncryptionInitial || encLevel == protocol.EncryptionHandshake) {
|
||||
ccf.IsApplicationError = false
|
||||
ccf.ErrorCode = uint64(qerr.ApplicationErrorErrorCode)
|
||||
ccf.ReasonPhrase = ""
|
||||
ccf := &wire.ConnectionCloseFrame{
|
||||
IsApplicationError: quicErrToSend.IsApplicationError(),
|
||||
ErrorCode: quicErrToSend.ErrorCode,
|
||||
FrameType: quicErrToSend.FrameType,
|
||||
ReasonPhrase: reasonPhrase,
|
||||
}
|
||||
payload := &payload{
|
||||
frames: []ackhandler.Frame{{Frame: ccf}},
|
||||
|
||||
+6
-7
@@ -8,7 +8,6 @@ import (
|
||||
|
||||
"github.com/lucas-clemente/quic-go/internal/flowcontrol"
|
||||
"github.com/lucas-clemente/quic-go/internal/protocol"
|
||||
"github.com/lucas-clemente/quic-go/internal/qerr"
|
||||
"github.com/lucas-clemente/quic-go/internal/utils"
|
||||
"github.com/lucas-clemente/quic-go/internal/wire"
|
||||
)
|
||||
@@ -39,7 +38,7 @@ type receiveStream struct {
|
||||
|
||||
closeForShutdownErr error
|
||||
cancelReadErr error
|
||||
resetRemotelyErr *StreamError
|
||||
resetRemotelyErr StreamError
|
||||
|
||||
closedForShutdown bool // set when CloseForShutdown() is called
|
||||
finRead bool // set once we read a frame with a Fin
|
||||
@@ -197,7 +196,7 @@ func (s *receiveStream) dequeueNextFrame() {
|
||||
s.readPosInFrame = 0
|
||||
}
|
||||
|
||||
func (s *receiveStream) CancelRead(errorCode StreamErrorCode) {
|
||||
func (s *receiveStream) CancelRead(errorCode protocol.ApplicationErrorCode) {
|
||||
s.mutex.Lock()
|
||||
completed := s.cancelReadImpl(errorCode)
|
||||
s.mutex.Unlock()
|
||||
@@ -208,7 +207,7 @@ func (s *receiveStream) CancelRead(errorCode StreamErrorCode) {
|
||||
}
|
||||
}
|
||||
|
||||
func (s *receiveStream) cancelReadImpl(errorCode qerr.StreamErrorCode) bool /* completed */ {
|
||||
func (s *receiveStream) cancelReadImpl(errorCode protocol.ApplicationErrorCode) bool /* completed */ {
|
||||
if s.finRead || s.canceledRead || s.resetRemotely {
|
||||
return false
|
||||
}
|
||||
@@ -282,9 +281,9 @@ func (s *receiveStream) handleResetStreamFrameImpl(frame *wire.ResetStreamFrame)
|
||||
return false, nil
|
||||
}
|
||||
s.resetRemotely = true
|
||||
s.resetRemotelyErr = &StreamError{
|
||||
StreamID: s.streamID,
|
||||
ErrorCode: frame.ErrorCode,
|
||||
s.resetRemotelyErr = streamCanceledError{
|
||||
errorCode: frame.ErrorCode,
|
||||
error: fmt.Errorf("stream %d was reset with error code %d", s.streamID, frame.ErrorCode),
|
||||
}
|
||||
s.signalRead()
|
||||
return newlyRcvdFinalOffset, nil
|
||||
|
||||
+8
-7
@@ -7,9 +7,9 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/lucas-clemente/quic-go/internal/ackhandler"
|
||||
|
||||
"github.com/lucas-clemente/quic-go/internal/flowcontrol"
|
||||
"github.com/lucas-clemente/quic-go/internal/protocol"
|
||||
"github.com/lucas-clemente/quic-go/internal/qerr"
|
||||
"github.com/lucas-clemente/quic-go/internal/utils"
|
||||
"github.com/lucas-clemente/quic-go/internal/wire"
|
||||
)
|
||||
@@ -407,12 +407,12 @@ func (s *sendStream) Close() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *sendStream) CancelWrite(errorCode StreamErrorCode) {
|
||||
func (s *sendStream) CancelWrite(errorCode protocol.ApplicationErrorCode) {
|
||||
s.cancelWriteImpl(errorCode, fmt.Errorf("Write on stream %d canceled with error code %d", s.streamID, errorCode))
|
||||
}
|
||||
|
||||
// must be called after locking the mutex
|
||||
func (s *sendStream) cancelWriteImpl(errorCode qerr.StreamErrorCode, writeErr error) {
|
||||
func (s *sendStream) cancelWriteImpl(errorCode protocol.ApplicationErrorCode, writeErr error) {
|
||||
s.mutex.Lock()
|
||||
if s.canceledWrite {
|
||||
s.mutex.Unlock()
|
||||
@@ -449,10 +449,11 @@ func (s *sendStream) updateSendWindow(limit protocol.ByteCount) {
|
||||
}
|
||||
|
||||
func (s *sendStream) handleStopSendingFrame(frame *wire.StopSendingFrame) {
|
||||
s.cancelWriteImpl(frame.ErrorCode, &StreamError{
|
||||
StreamID: s.streamID,
|
||||
ErrorCode: frame.ErrorCode,
|
||||
})
|
||||
writeErr := streamCanceledError{
|
||||
errorCode: frame.ErrorCode,
|
||||
error: fmt.Errorf("stream %d was reset with error code %d", s.streamID, frame.ErrorCode),
|
||||
}
|
||||
s.cancelWriteImpl(frame.ErrorCode, writeErr)
|
||||
}
|
||||
|
||||
func (s *sendStream) Context() context.Context {
|
||||
|
||||
+3
-10
@@ -87,7 +87,6 @@ type baseServer struct {
|
||||
*handshake.TokenGenerator,
|
||||
bool, /* enable 0-RTT */
|
||||
logging.ConnectionTracer,
|
||||
uint64,
|
||||
utils.Logger,
|
||||
protocol.VersionNumber,
|
||||
) quicSession
|
||||
@@ -451,7 +450,6 @@ func (s *baseServer) handleInitialImpl(p *receivedPacket, hdr *wire.Header) erro
|
||||
}
|
||||
s.logger.Debugf("Changing connection ID to %s.", connID)
|
||||
var sess quicSession
|
||||
tracingID := nextSessionTracingID()
|
||||
if added := s.sessionHandler.AddWithConnID(hdr.DestConnectionID, connID, func() packetHandler {
|
||||
var tracer logging.ConnectionTracer
|
||||
if s.config.Tracer != nil {
|
||||
@@ -460,11 +458,7 @@ func (s *baseServer) handleInitialImpl(p *receivedPacket, hdr *wire.Header) erro
|
||||
if origDestConnID.Len() > 0 {
|
||||
connID = origDestConnID
|
||||
}
|
||||
tracer = s.config.Tracer.TracerForConnection(
|
||||
context.WithValue(context.Background(), SessionTracingKey, tracingID),
|
||||
protocol.PerspectiveServer,
|
||||
connID,
|
||||
)
|
||||
tracer = s.config.Tracer.TracerForConnection(protocol.PerspectiveServer, connID)
|
||||
}
|
||||
sess = s.newSession(
|
||||
newSendConn(s.conn, p.remoteAddr, p.info),
|
||||
@@ -480,7 +474,6 @@ func (s *baseServer) handleInitialImpl(p *receivedPacket, hdr *wire.Header) erro
|
||||
s.tokenGenerator,
|
||||
s.acceptEarlySessions,
|
||||
tracer,
|
||||
tracingID,
|
||||
s.logger,
|
||||
hdr.Version,
|
||||
)
|
||||
@@ -600,12 +593,12 @@ func (s *baseServer) sendConnectionRefused(remoteAddr net.Addr, hdr *wire.Header
|
||||
}
|
||||
|
||||
// sendError sends the error as a response to the packet received with header hdr
|
||||
func (s *baseServer) sendError(remoteAddr net.Addr, hdr *wire.Header, sealer handshake.LongHeaderSealer, errorCode qerr.TransportErrorCode, info *packetInfo) error {
|
||||
func (s *baseServer) sendError(remoteAddr net.Addr, hdr *wire.Header, sealer handshake.LongHeaderSealer, errorCode qerr.ErrorCode, info *packetInfo) error {
|
||||
packetBuffer := getPacketBuffer()
|
||||
defer packetBuffer.Release()
|
||||
buf := bytes.NewBuffer(packetBuffer.Data)
|
||||
|
||||
ccf := &wire.ConnectionCloseFrame{ErrorCode: uint64(errorCode)}
|
||||
ccf := &wire.ConnectionCloseFrame{ErrorCode: errorCode}
|
||||
|
||||
replyHdr := &wire.ExtendedHeader{}
|
||||
replyHdr.IsLongHeader = true
|
||||
|
||||
+109
-170
@@ -10,7 +10,6 @@ import (
|
||||
"net"
|
||||
"reflect"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
|
||||
"github.com/lucas-clemente/quic-go/internal/ackhandler"
|
||||
@@ -123,17 +122,23 @@ type errCloseForRecreating struct {
|
||||
nextVersion protocol.VersionNumber
|
||||
}
|
||||
|
||||
func (e *errCloseForRecreating) Error() string {
|
||||
func (errCloseForRecreating) Error() string {
|
||||
return "closing session in order to recreate it"
|
||||
}
|
||||
|
||||
func (e *errCloseForRecreating) Is(target error) bool {
|
||||
_, ok := target.(*errCloseForRecreating)
|
||||
func (errCloseForRecreating) Is(target error) bool {
|
||||
_, ok := target.(errCloseForRecreating)
|
||||
return ok
|
||||
}
|
||||
|
||||
var sessionTracingID uint64 // to be accessed atomically
|
||||
func nextSessionTracingID() uint64 { return atomic.AddUint64(&sessionTracingID, 1) }
|
||||
type errVersionNegotiation struct {
|
||||
ourVersions []protocol.VersionNumber
|
||||
theirVersions []protocol.VersionNumber
|
||||
}
|
||||
|
||||
func (e errVersionNegotiation) Error() string {
|
||||
return fmt.Sprintf("no compatible QUIC version found (we support %s, server offered %s)", e.ourVersions, e.theirVersions)
|
||||
}
|
||||
|
||||
// A Session is a QUIC session
|
||||
type session struct {
|
||||
@@ -247,7 +252,6 @@ var newSession = func(
|
||||
tokenGenerator *handshake.TokenGenerator,
|
||||
enable0RTT bool,
|
||||
tracer logging.ConnectionTracer,
|
||||
tracingID uint64,
|
||||
logger utils.Logger,
|
||||
v protocol.VersionNumber,
|
||||
) quicSession {
|
||||
@@ -287,10 +291,8 @@ var newSession = func(
|
||||
s.version,
|
||||
)
|
||||
s.preSetup()
|
||||
s.ctx, s.ctxCancel = context.WithCancel(context.WithValue(context.Background(), SessionTracingKey, tracingID))
|
||||
s.sentPacketHandler, s.receivedPacketHandler = ackhandler.NewAckHandler(
|
||||
0,
|
||||
getMaxPacketSize(s.conn.RemoteAddr()),
|
||||
s.rttStats,
|
||||
s.perspective,
|
||||
s.tracer,
|
||||
@@ -378,7 +380,6 @@ var newClientSession = func(
|
||||
enable0RTT bool,
|
||||
hasNegotiatedVersion bool,
|
||||
tracer logging.ConnectionTracer,
|
||||
tracingID uint64,
|
||||
logger utils.Logger,
|
||||
v protocol.VersionNumber,
|
||||
) quicSession {
|
||||
@@ -414,10 +415,8 @@ var newClientSession = func(
|
||||
s.version,
|
||||
)
|
||||
s.preSetup()
|
||||
s.ctx, s.ctxCancel = context.WithCancel(context.WithValue(context.Background(), SessionTracingKey, tracingID))
|
||||
s.sentPacketHandler, s.receivedPacketHandler = ackhandler.NewAckHandler(
|
||||
initialPacketNumber,
|
||||
getMaxPacketSize(s.conn.RemoteAddr()),
|
||||
s.rttStats,
|
||||
s.perspective,
|
||||
s.tracer,
|
||||
@@ -523,6 +522,7 @@ func (s *session) preSetup() {
|
||||
s.receivedPackets = make(chan *receivedPacket, protocol.MaxSessionUnprocessedPackets)
|
||||
s.closeChan = make(chan closeError, 1)
|
||||
s.sendingScheduled = make(chan struct{}, 1)
|
||||
s.ctx, s.ctxCancel = context.WithCancel(context.Background())
|
||||
s.handshakeCtx, s.handshakeCtxCancel = context.WithCancel(context.Background())
|
||||
|
||||
now := time.Now()
|
||||
@@ -608,6 +608,7 @@ runLoop:
|
||||
// nothing to see here.
|
||||
case <-sendQueueAvailable:
|
||||
case firstPacket := <-s.receivedPackets:
|
||||
s.sentPacketHandler.ReceivedBytes(firstPacket.Size())
|
||||
wasProcessed := s.handlePacketImpl(firstPacket)
|
||||
// Don't set timers and send packets if the packet made us close the session.
|
||||
select {
|
||||
@@ -663,13 +664,19 @@ runLoop:
|
||||
s.framer.QueueControlFrame(&wire.PingFrame{})
|
||||
s.keepAlivePingSent = true
|
||||
} else if !s.handshakeComplete && now.Sub(s.sessionCreationTime) >= s.config.handshakeTimeout() {
|
||||
s.destroyImpl(qerr.ErrHandshakeTimeout)
|
||||
if s.tracer != nil {
|
||||
s.tracer.ClosedConnection(logging.NewTimeoutCloseReason(logging.TimeoutReasonHandshake))
|
||||
}
|
||||
s.destroyImpl(qerr.NewTimeoutError("Handshake did not complete in time"))
|
||||
continue
|
||||
} else {
|
||||
idleTimeoutStartTime := s.idleTimeoutStartTime()
|
||||
if (!s.handshakeComplete && now.Sub(idleTimeoutStartTime) >= s.config.HandshakeIdleTimeout) ||
|
||||
(s.handshakeComplete && now.Sub(idleTimeoutStartTime) >= s.idleTimeout) {
|
||||
s.destroyImpl(qerr.ErrIdleTimeout)
|
||||
if s.tracer != nil {
|
||||
s.tracer.ClosedConnection(logging.NewTimeoutCloseReason(logging.TimeoutReasonIdle))
|
||||
}
|
||||
s.destroyImpl(qerr.NewTimeoutError("No recent network activity"))
|
||||
continue
|
||||
}
|
||||
}
|
||||
@@ -690,8 +697,8 @@ runLoop:
|
||||
}
|
||||
}
|
||||
|
||||
s.handleCloseError(&closeErr)
|
||||
if !errors.Is(closeErr.err, &errCloseForRecreating{}) && s.tracer != nil {
|
||||
s.handleCloseError(closeErr)
|
||||
if !errors.Is(closeErr.err, errCloseForRecreating{}) && s.tracer != nil {
|
||||
s.tracer.Close()
|
||||
}
|
||||
s.logger.Infof("Connection %s closed.", s.logID)
|
||||
@@ -731,26 +738,23 @@ func (s *session) nextKeepAliveTime() time.Time {
|
||||
if !s.config.KeepAlive || s.keepAlivePingSent || !s.firstAckElicitingPacketAfterIdleSentTime.IsZero() {
|
||||
return time.Time{}
|
||||
}
|
||||
return s.lastPacketReceivedTime.Add(s.keepAliveInterval)
|
||||
return s.lastPacketReceivedTime.Add(s.keepAliveInterval / 2)
|
||||
}
|
||||
|
||||
func (s *session) maybeResetTimer() {
|
||||
var deadline time.Time
|
||||
if !s.handshakeComplete {
|
||||
deadline = utils.MinTime(
|
||||
s.sessionCreationTime.Add(s.config.handshakeTimeout()),
|
||||
s.idleTimeoutStartTime().Add(s.config.HandshakeIdleTimeout),
|
||||
)
|
||||
deadline = s.sessionCreationTime.Add(utils.MinDuration(s.config.handshakeTimeout(), s.config.HandshakeIdleTimeout))
|
||||
} else {
|
||||
if keepAliveTime := s.nextKeepAliveTime(); !keepAliveTime.IsZero() {
|
||||
deadline = keepAliveTime
|
||||
} else {
|
||||
deadline = s.idleTimeoutStartTime().Add(s.idleTimeout)
|
||||
}
|
||||
}
|
||||
if s.handshakeConfirmed && !s.config.DisablePathMTUDiscovery {
|
||||
if probeTime := s.mtuDiscoverer.NextProbeTime(); !probeTime.IsZero() {
|
||||
deadline = utils.MinTime(deadline, probeTime)
|
||||
if !s.config.DisablePathMTUDiscovery {
|
||||
if probeTime := s.mtuDiscoverer.NextProbeTime(); !probeTime.IsZero() {
|
||||
deadline = utils.MinTime(deadline, probeTime)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -782,36 +786,6 @@ func (s *session) handleHandshakeComplete() {
|
||||
s.connIDManager.SetHandshakeComplete()
|
||||
s.connIDGenerator.SetHandshakeComplete()
|
||||
|
||||
if s.perspective == protocol.PerspectiveClient {
|
||||
s.applyTransportParameters()
|
||||
return
|
||||
}
|
||||
|
||||
s.handleHandshakeConfirmed()
|
||||
|
||||
ticket, err := s.cryptoStreamHandler.GetSessionTicket()
|
||||
if err != nil {
|
||||
s.closeLocal(err)
|
||||
}
|
||||
if ticket != nil {
|
||||
s.oneRTTStream.Write(ticket)
|
||||
for s.oneRTTStream.HasData() {
|
||||
s.queueControlFrame(s.oneRTTStream.PopCryptoFrame(protocol.MaxPostHandshakeCryptoFrameSize))
|
||||
}
|
||||
}
|
||||
token, err := s.tokenGenerator.NewToken(s.conn.RemoteAddr())
|
||||
if err != nil {
|
||||
s.closeLocal(err)
|
||||
}
|
||||
s.queueControlFrame(&wire.NewTokenFrame{Token: token})
|
||||
s.queueControlFrame(&wire.HandshakeDoneFrame{})
|
||||
}
|
||||
|
||||
func (s *session) handleHandshakeConfirmed() {
|
||||
s.handshakeConfirmed = true
|
||||
s.sentPacketHandler.SetHandshakeConfirmed()
|
||||
s.cryptoStreamHandler.SetHandshakeConfirmed()
|
||||
|
||||
if !s.config.DisablePathMTUDiscovery {
|
||||
maxPacketSize := s.peerParams.MaxUDPPayloadSize
|
||||
if maxPacketSize == 0 {
|
||||
@@ -828,11 +802,34 @@ func (s *session) handleHandshakeConfirmed() {
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
if s.perspective == protocol.PerspectiveClient {
|
||||
s.applyTransportParameters()
|
||||
return
|
||||
}
|
||||
|
||||
s.handshakeConfirmed = true
|
||||
s.sentPacketHandler.SetHandshakeConfirmed()
|
||||
ticket, err := s.cryptoStreamHandler.GetSessionTicket()
|
||||
if err != nil {
|
||||
s.closeLocal(err)
|
||||
}
|
||||
if ticket != nil {
|
||||
s.oneRTTStream.Write(ticket)
|
||||
for s.oneRTTStream.HasData() {
|
||||
s.queueControlFrame(s.oneRTTStream.PopCryptoFrame(protocol.MaxPostHandshakeCryptoFrameSize))
|
||||
}
|
||||
}
|
||||
token, err := s.tokenGenerator.NewToken(s.conn.RemoteAddr())
|
||||
if err != nil {
|
||||
s.closeLocal(err)
|
||||
}
|
||||
s.queueControlFrame(&wire.NewTokenFrame{Token: token})
|
||||
s.cryptoStreamHandler.SetHandshakeConfirmed()
|
||||
s.queueControlFrame(&wire.HandshakeDoneFrame{})
|
||||
}
|
||||
|
||||
func (s *session) handlePacketImpl(rp *receivedPacket) bool {
|
||||
s.sentPacketHandler.ReceivedBytes(rp.Size())
|
||||
|
||||
if wire.IsVersionNegotiationPacket(rp.data) {
|
||||
s.handleVersionNegotiationPacket(rp)
|
||||
return false
|
||||
@@ -943,10 +940,7 @@ func (s *session) handleSinglePacket(p *receivedPacket, hdr *wire.Header) bool /
|
||||
wasQueued = true
|
||||
s.tryQueueingUndecryptablePacket(p, hdr)
|
||||
case wire.ErrInvalidReservedBits:
|
||||
s.closeLocal(&qerr.TransportError{
|
||||
ErrorCode: qerr.ProtocolViolation,
|
||||
ErrorMessage: err.Error(),
|
||||
})
|
||||
s.closeLocal(qerr.NewError(qerr.ProtocolViolation, err.Error()))
|
||||
case handshake.ErrDecryptionFailed:
|
||||
// This might be a packet injected by an attacker. Drop it.
|
||||
if s.tracer != nil {
|
||||
@@ -1087,16 +1081,13 @@ func (s *session) handleVersionNegotiationPacket(p *receivedPacket) {
|
||||
}
|
||||
newVersion, ok := protocol.ChooseSupportedVersion(s.config.Versions, supportedVersions)
|
||||
if !ok {
|
||||
s.destroyImpl(&VersionNegotiationError{
|
||||
Ours: s.config.Versions,
|
||||
Theirs: supportedVersions,
|
||||
s.destroyImpl(errVersionNegotiation{
|
||||
ourVersions: s.config.Versions,
|
||||
theirVersions: supportedVersions,
|
||||
})
|
||||
s.logger.Infof("No compatible QUIC version found.")
|
||||
return
|
||||
}
|
||||
if s.tracer != nil {
|
||||
s.tracer.NegotiatedVersion(newVersion, s.config.Versions, supportedVersions)
|
||||
}
|
||||
|
||||
s.logger.Infof("Switching to QUIC version %s.", newVersion)
|
||||
nextPN, _ := s.sentPacketHandler.PeekPacketNumber(protocol.EncryptionInitial)
|
||||
@@ -1113,24 +1104,11 @@ func (s *session) handleUnpackedPacket(
|
||||
packetSize protocol.ByteCount, // only for logging
|
||||
) error {
|
||||
if len(packet.data) == 0 {
|
||||
return &qerr.TransportError{
|
||||
ErrorCode: qerr.ProtocolViolation,
|
||||
ErrorMessage: "empty packet",
|
||||
}
|
||||
return qerr.NewError(qerr.ProtocolViolation, "empty packet")
|
||||
}
|
||||
|
||||
if !s.receivedFirstPacket {
|
||||
s.receivedFirstPacket = true
|
||||
if !s.versionNegotiated && s.tracer != nil {
|
||||
var clientVersions, serverVersions []protocol.VersionNumber
|
||||
switch s.perspective {
|
||||
case protocol.PerspectiveClient:
|
||||
clientVersions = s.config.Versions
|
||||
case protocol.PerspectiveServer:
|
||||
serverVersions = s.config.Versions
|
||||
}
|
||||
s.tracer.NegotiatedVersion(s.version, clientVersions, serverVersions)
|
||||
}
|
||||
// The server can change the source connection ID with the first Handshake packet.
|
||||
if s.perspective == protocol.PerspectiveClient && packet.hdr.IsLongHeader && !packet.hdr.SrcConnectionID.Equal(s.handshakeDestConnID) {
|
||||
cid := packet.hdr.SrcConnectionID
|
||||
@@ -1152,6 +1130,7 @@ func (s *session) handleUnpackedPacket(
|
||||
s.tracer.StartedConnection(
|
||||
s.conn.LocalAddr(),
|
||||
s.conn.RemoteAddr(),
|
||||
s.version,
|
||||
packet.hdr.SrcConnectionID,
|
||||
packet.hdr.DestConnectionID,
|
||||
)
|
||||
@@ -1267,20 +1246,13 @@ func (s *session) handlePacket(p *receivedPacket) {
|
||||
}
|
||||
|
||||
func (s *session) handleConnectionCloseFrame(frame *wire.ConnectionCloseFrame) {
|
||||
var e error
|
||||
if frame.IsApplicationError {
|
||||
s.closeRemote(&qerr.ApplicationError{
|
||||
Remote: true,
|
||||
ErrorCode: qerr.ApplicationErrorCode(frame.ErrorCode),
|
||||
ErrorMessage: frame.ReasonPhrase,
|
||||
})
|
||||
return
|
||||
e = qerr.NewApplicationError(frame.ErrorCode, frame.ReasonPhrase)
|
||||
} else {
|
||||
e = qerr.NewError(frame.ErrorCode, frame.ReasonPhrase)
|
||||
}
|
||||
s.closeRemote(&qerr.TransportError{
|
||||
Remote: true,
|
||||
ErrorCode: qerr.TransportErrorCode(frame.ErrorCode),
|
||||
FrameType: frame.FrameType,
|
||||
ErrorMessage: frame.ReasonPhrase,
|
||||
})
|
||||
s.closeRemote(e)
|
||||
}
|
||||
|
||||
func (s *session) handleCryptoFrame(frame *wire.CryptoFrame, encLevel protocol.EncryptionLevel) error {
|
||||
@@ -1361,10 +1333,7 @@ func (s *session) handlePathChallengeFrame(frame *wire.PathChallengeFrame) {
|
||||
|
||||
func (s *session) handleNewTokenFrame(frame *wire.NewTokenFrame) error {
|
||||
if s.perspective == protocol.PerspectiveServer {
|
||||
return &qerr.TransportError{
|
||||
ErrorCode: qerr.ProtocolViolation,
|
||||
ErrorMessage: "received NEW_TOKEN frame from the client",
|
||||
}
|
||||
return qerr.NewError(qerr.ProtocolViolation, "Received NEW_TOKEN frame from the client.")
|
||||
}
|
||||
if s.config.TokenStore != nil {
|
||||
s.config.TokenStore.Put(s.tokenStoreKey, &ClientToken{data: frame.Token})
|
||||
@@ -1382,37 +1351,27 @@ func (s *session) handleRetireConnectionIDFrame(f *wire.RetireConnectionIDFrame,
|
||||
|
||||
func (s *session) handleHandshakeDoneFrame() error {
|
||||
if s.perspective == protocol.PerspectiveServer {
|
||||
return &qerr.TransportError{
|
||||
ErrorCode: qerr.ProtocolViolation,
|
||||
ErrorMessage: "received a HANDSHAKE_DONE frame",
|
||||
}
|
||||
}
|
||||
if !s.handshakeConfirmed {
|
||||
s.handleHandshakeConfirmed()
|
||||
return qerr.NewError(qerr.ProtocolViolation, "received a HANDSHAKE_DONE frame")
|
||||
}
|
||||
s.handshakeConfirmed = true
|
||||
s.sentPacketHandler.SetHandshakeConfirmed()
|
||||
s.cryptoStreamHandler.SetHandshakeConfirmed()
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *session) handleAckFrame(frame *wire.AckFrame, encLevel protocol.EncryptionLevel) error {
|
||||
acked1RTTPacket, err := s.sentPacketHandler.ReceivedAck(frame, encLevel, s.lastPacketReceivedTime)
|
||||
if err != nil {
|
||||
if err := s.sentPacketHandler.ReceivedAck(frame, encLevel, s.lastPacketReceivedTime); err != nil {
|
||||
return err
|
||||
}
|
||||
if !acked1RTTPacket {
|
||||
if encLevel != protocol.Encryption1RTT {
|
||||
return nil
|
||||
}
|
||||
if s.perspective == protocol.PerspectiveClient && !s.handshakeConfirmed {
|
||||
s.handleHandshakeConfirmed()
|
||||
}
|
||||
return s.cryptoStreamHandler.SetLargest1RTTAcked(frame.LargestAcked())
|
||||
}
|
||||
|
||||
func (s *session) handleDatagramFrame(f *wire.DatagramFrame) error {
|
||||
if f.Length(s.version) > protocol.MaxDatagramFrameSize {
|
||||
return &qerr.TransportError{
|
||||
ErrorCode: qerr.ProtocolViolation,
|
||||
ErrorMessage: "DATAGRAM frame too large",
|
||||
}
|
||||
return qerr.NewError(qerr.ProtocolViolation, "DATAGRAM frame too large")
|
||||
}
|
||||
s.datagramQueue.HandleDatagramFrame(f)
|
||||
return nil
|
||||
@@ -1461,48 +1420,44 @@ func (s *session) shutdown() {
|
||||
<-s.ctx.Done()
|
||||
}
|
||||
|
||||
func (s *session) CloseWithError(code ApplicationErrorCode, desc string) error {
|
||||
s.closeLocal(&qerr.ApplicationError{
|
||||
ErrorCode: code,
|
||||
ErrorMessage: desc,
|
||||
})
|
||||
func (s *session) CloseWithError(code protocol.ApplicationErrorCode, desc string) error {
|
||||
s.closeLocal(qerr.NewApplicationError(qerr.ErrorCode(code), desc))
|
||||
<-s.ctx.Done()
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *session) handleCloseError(closeErr *closeError) {
|
||||
e := closeErr.err
|
||||
if e == nil {
|
||||
e = &qerr.ApplicationError{}
|
||||
} else {
|
||||
defer func() {
|
||||
closeErr.err = e
|
||||
}()
|
||||
func (s *session) handleCloseError(closeErr closeError) {
|
||||
if closeErr.err == nil {
|
||||
closeErr.err = qerr.NewApplicationError(0, "")
|
||||
}
|
||||
|
||||
switch {
|
||||
case errors.Is(e, qerr.ErrIdleTimeout),
|
||||
errors.Is(e, qerr.ErrHandshakeTimeout),
|
||||
errors.Is(e, &StatelessResetError{}),
|
||||
errors.Is(e, &VersionNegotiationError{}),
|
||||
errors.Is(e, &errCloseForRecreating{}),
|
||||
errors.Is(e, &qerr.ApplicationError{}),
|
||||
errors.Is(e, &qerr.TransportError{}):
|
||||
default:
|
||||
e = &qerr.TransportError{
|
||||
ErrorCode: qerr.InternalError,
|
||||
ErrorMessage: e.Error(),
|
||||
}
|
||||
var quicErr *qerr.QuicError
|
||||
var ok bool
|
||||
if quicErr, ok = closeErr.err.(*qerr.QuicError); !ok {
|
||||
quicErr = qerr.ToQuicError(closeErr.err)
|
||||
}
|
||||
|
||||
s.streamsMap.CloseWithError(e)
|
||||
s.streamsMap.CloseWithError(quicErr)
|
||||
s.connIDManager.Close()
|
||||
if s.datagramQueue != nil {
|
||||
s.datagramQueue.CloseWithError(e)
|
||||
s.datagramQueue.CloseWithError(quicErr)
|
||||
}
|
||||
|
||||
if s.tracer != nil && !errors.Is(e, &errCloseForRecreating{}) {
|
||||
s.tracer.ClosedConnection(e)
|
||||
if s.tracer != nil {
|
||||
// timeout errors are logged as soon as they occur (to distinguish between handshake and idle timeouts)
|
||||
if nerr, ok := closeErr.err.(net.Error); !ok || !nerr.Timeout() {
|
||||
var resetErr statelessResetErr
|
||||
var vnErr errVersionNegotiation
|
||||
if errors.As(closeErr.err, &resetErr) {
|
||||
s.tracer.ClosedConnection(logging.NewStatelessResetCloseReason(resetErr.token))
|
||||
} else if errors.As(closeErr.err, &vnErr) {
|
||||
s.tracer.ClosedConnection(logging.NewVersionNegotiationError(vnErr.theirVersions))
|
||||
} else if quicErr.IsApplicationError() {
|
||||
s.tracer.ClosedConnection(logging.NewApplicationCloseReason(quicErr.ErrorCode, closeErr.remote))
|
||||
} else {
|
||||
s.tracer.ClosedConnection(logging.NewTransportCloseReason(quicErr.ErrorCode, closeErr.remote))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// If this is a remote close we're done here
|
||||
@@ -1514,7 +1469,7 @@ func (s *session) handleCloseError(closeErr *closeError) {
|
||||
s.connIDGenerator.RemoveAll()
|
||||
return
|
||||
}
|
||||
connClosePacket, err := s.sendConnectionClose(e)
|
||||
connClosePacket, err := s.sendConnectionClose(quicErr)
|
||||
if err != nil {
|
||||
s.logger.Debugf("Error sending CONNECTION_CLOSE: %s", err)
|
||||
}
|
||||
@@ -1553,10 +1508,7 @@ func (s *session) restoreTransportParameters(params *wire.TransportParameters) {
|
||||
|
||||
func (s *session) handleTransportParameters(params *wire.TransportParameters) {
|
||||
if err := s.checkTransportParameters(params); err != nil {
|
||||
s.closeLocal(&qerr.TransportError{
|
||||
ErrorCode: qerr.TransportParameterError,
|
||||
ErrorMessage: err.Error(),
|
||||
})
|
||||
s.closeLocal(err)
|
||||
}
|
||||
s.peerParams = params
|
||||
// On the client side we have to wait for handshake completion.
|
||||
@@ -1579,7 +1531,7 @@ func (s *session) checkTransportParameters(params *wire.TransportParameters) err
|
||||
|
||||
// check the initial_source_connection_id
|
||||
if !params.InitialSourceConnectionID.Equal(s.handshakeDestConnID) {
|
||||
return fmt.Errorf("expected initial_source_connection_id to equal %s, is %s", s.handshakeDestConnID, params.InitialSourceConnectionID)
|
||||
return qerr.NewError(qerr.TransportParameterError, fmt.Sprintf("expected initial_source_connection_id to equal %s, is %s", s.handshakeDestConnID, params.InitialSourceConnectionID))
|
||||
}
|
||||
|
||||
if s.perspective == protocol.PerspectiveServer {
|
||||
@@ -1587,17 +1539,17 @@ func (s *session) checkTransportParameters(params *wire.TransportParameters) err
|
||||
}
|
||||
// check the original_destination_connection_id
|
||||
if !params.OriginalDestinationConnectionID.Equal(s.origDestConnID) {
|
||||
return fmt.Errorf("expected original_destination_connection_id to equal %s, is %s", s.origDestConnID, params.OriginalDestinationConnectionID)
|
||||
return qerr.NewError(qerr.TransportParameterError, fmt.Sprintf("expected original_destination_connection_id to equal %s, is %s", s.origDestConnID, params.OriginalDestinationConnectionID))
|
||||
}
|
||||
if s.retrySrcConnID != nil { // a Retry was performed
|
||||
if params.RetrySourceConnectionID == nil {
|
||||
return errors.New("missing retry_source_connection_id")
|
||||
return qerr.NewError(qerr.TransportParameterError, "missing retry_source_connection_id")
|
||||
}
|
||||
if !(*params.RetrySourceConnectionID).Equal(*s.retrySrcConnID) {
|
||||
return fmt.Errorf("expected retry_source_connection_id to equal %s, is %s", s.retrySrcConnID, *params.RetrySourceConnectionID)
|
||||
return qerr.NewError(qerr.TransportParameterError, fmt.Sprintf("expected retry_source_connection_id to equal %s, is %s", s.retrySrcConnID, *params.RetrySourceConnectionID))
|
||||
}
|
||||
} else if params.RetrySourceConnectionID != nil {
|
||||
return errors.New("received retry_source_connection_id, although no Retry was performed")
|
||||
return qerr.NewError(qerr.TransportParameterError, "received retry_source_connection_id, although no Retry was performed")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -1766,7 +1718,7 @@ func (s *session) sendPacket() (bool, error) {
|
||||
s.sendQueue.Send(packet.buffer)
|
||||
return true, nil
|
||||
}
|
||||
if !s.config.DisablePathMTUDiscovery && s.mtuDiscoverer.ShouldSendProbe(now) {
|
||||
if !s.config.DisablePathMTUDiscovery && s.handshakeComplete && s.mtuDiscoverer.ShouldSendProbe(now) {
|
||||
packet, err := s.packer.PackMTUProbePacket(s.mtuDiscoverer.GetPing())
|
||||
if err != nil {
|
||||
return false, err
|
||||
@@ -1792,21 +1744,8 @@ func (s *session) sendPackedPacket(packet *packedPacket, now time.Time) {
|
||||
s.sendQueue.Send(packet.buffer)
|
||||
}
|
||||
|
||||
func (s *session) sendConnectionClose(e error) ([]byte, error) {
|
||||
var packet *coalescedPacket
|
||||
var err error
|
||||
var transportErr *qerr.TransportError
|
||||
var applicationErr *qerr.ApplicationError
|
||||
if errors.As(e, &transportErr) {
|
||||
packet, err = s.packer.PackConnectionClose(transportErr)
|
||||
} else if errors.As(e, &applicationErr) {
|
||||
packet, err = s.packer.PackApplicationClose(applicationErr)
|
||||
} else {
|
||||
packet, err = s.packer.PackConnectionClose(&qerr.TransportError{
|
||||
ErrorCode: qerr.InternalError,
|
||||
ErrorMessage: fmt.Sprintf("session BUG: unspecified error type (msg: %s)", e.Error()),
|
||||
})
|
||||
}
|
||||
func (s *session) sendConnectionClose(quicErr *qerr.QuicError) ([]byte, error) {
|
||||
packet, err := s.packer.PackConnectionClose(quicErr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
+10
@@ -86,6 +86,16 @@ type stream struct {
|
||||
|
||||
var _ Stream = &stream{}
|
||||
|
||||
type streamCanceledError struct {
|
||||
error
|
||||
errorCode protocol.ApplicationErrorCode
|
||||
}
|
||||
|
||||
func (streamCanceledError) Canceled() bool { return true }
|
||||
func (e streamCanceledError) ErrorCode() protocol.ApplicationErrorCode { return e.errorCode }
|
||||
|
||||
var _ StreamError = &streamCanceledError{}
|
||||
|
||||
// newStream creates a new Stream
|
||||
func newStream(streamID protocol.StreamID,
|
||||
sender streamSender,
|
||||
|
||||
+2
-8
@@ -209,10 +209,7 @@ func (m *streamsMap) DeleteStream(id protocol.StreamID) error {
|
||||
func (m *streamsMap) GetOrOpenReceiveStream(id protocol.StreamID) (receiveStreamI, error) {
|
||||
str, err := m.getOrOpenReceiveStream(id)
|
||||
if err != nil {
|
||||
return nil, &qerr.TransportError{
|
||||
ErrorCode: qerr.StreamStateError,
|
||||
ErrorMessage: err.Error(),
|
||||
}
|
||||
return nil, qerr.NewError(qerr.StreamStateError, err.Error())
|
||||
}
|
||||
return str, nil
|
||||
}
|
||||
@@ -243,10 +240,7 @@ func (m *streamsMap) getOrOpenReceiveStream(id protocol.StreamID) (receiveStream
|
||||
func (m *streamsMap) GetOrOpenSendStream(id protocol.StreamID) (sendStreamI, error) {
|
||||
str, err := m.getOrOpenSendStream(id)
|
||||
if err != nil {
|
||||
return nil, &qerr.TransportError{
|
||||
ErrorCode: qerr.StreamStateError,
|
||||
ErrorMessage: err.Error(),
|
||||
}
|
||||
return nil, qerr.NewError(qerr.StreamStateError, err.Error())
|
||||
}
|
||||
return str, nil
|
||||
}
|
||||
|
||||
+2
-2
@@ -145,7 +145,7 @@ func (m *incomingBidiStreamsMap) DeleteStream(num protocol.StreamNum) error {
|
||||
func (m *incomingBidiStreamsMap) deleteStream(num protocol.StreamNum) error {
|
||||
if _, ok := m.streams[num]; !ok {
|
||||
return streamError{
|
||||
message: "tried to delete unknown incoming stream %d",
|
||||
message: "Tried to delete unknown incoming stream %d",
|
||||
nums: []protocol.StreamNum{num},
|
||||
}
|
||||
}
|
||||
@@ -156,7 +156,7 @@ func (m *incomingBidiStreamsMap) deleteStream(num protocol.StreamNum) error {
|
||||
entry, ok := m.streams[num]
|
||||
if ok && entry.shouldDelete {
|
||||
return streamError{
|
||||
message: "tried to delete incoming stream %d multiple times",
|
||||
message: "Tried to delete incoming stream %d multiple times",
|
||||
nums: []protocol.StreamNum{num},
|
||||
}
|
||||
}
|
||||
|
||||
+2
-2
@@ -143,7 +143,7 @@ func (m *incomingItemsMap) DeleteStream(num protocol.StreamNum) error {
|
||||
func (m *incomingItemsMap) deleteStream(num protocol.StreamNum) error {
|
||||
if _, ok := m.streams[num]; !ok {
|
||||
return streamError{
|
||||
message: "tried to delete unknown incoming stream %d",
|
||||
message: "Tried to delete unknown incoming stream %d",
|
||||
nums: []protocol.StreamNum{num},
|
||||
}
|
||||
}
|
||||
@@ -154,7 +154,7 @@ func (m *incomingItemsMap) deleteStream(num protocol.StreamNum) error {
|
||||
entry, ok := m.streams[num]
|
||||
if ok && entry.shouldDelete {
|
||||
return streamError{
|
||||
message: "tried to delete incoming stream %d multiple times",
|
||||
message: "Tried to delete incoming stream %d multiple times",
|
||||
nums: []protocol.StreamNum{num},
|
||||
}
|
||||
}
|
||||
|
||||
+2
-2
@@ -145,7 +145,7 @@ func (m *incomingUniStreamsMap) DeleteStream(num protocol.StreamNum) error {
|
||||
func (m *incomingUniStreamsMap) deleteStream(num protocol.StreamNum) error {
|
||||
if _, ok := m.streams[num]; !ok {
|
||||
return streamError{
|
||||
message: "tried to delete unknown incoming stream %d",
|
||||
message: "Tried to delete unknown incoming stream %d",
|
||||
nums: []protocol.StreamNum{num},
|
||||
}
|
||||
}
|
||||
@@ -156,7 +156,7 @@ func (m *incomingUniStreamsMap) deleteStream(num protocol.StreamNum) error {
|
||||
entry, ok := m.streams[num]
|
||||
if ok && entry.shouldDelete {
|
||||
return streamError{
|
||||
message: "tried to delete incoming stream %d multiple times",
|
||||
message: "Tried to delete incoming stream %d multiple times",
|
||||
nums: []protocol.StreamNum{num},
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -157,7 +157,7 @@ func (m *outgoingBidiStreamsMap) DeleteStream(num protocol.StreamNum) error {
|
||||
|
||||
if _, ok := m.streams[num]; !ok {
|
||||
return streamError{
|
||||
message: "tried to delete unknown outgoing stream %d",
|
||||
message: "Tried to delete unknown outgoing stream %d",
|
||||
nums: []protocol.StreamNum{num},
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -155,7 +155,7 @@ func (m *outgoingItemsMap) DeleteStream(num protocol.StreamNum) error {
|
||||
|
||||
if _, ok := m.streams[num]; !ok {
|
||||
return streamError{
|
||||
message: "tried to delete unknown outgoing stream %d",
|
||||
message: "Tried to delete unknown outgoing stream %d",
|
||||
nums: []protocol.StreamNum{num},
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -157,7 +157,7 @@ func (m *outgoingUniStreamsMap) DeleteStream(num protocol.StreamNum) error {
|
||||
|
||||
if _, ok := m.streams[num]; !ok {
|
||||
return streamError{
|
||||
message: "tried to delete unknown outgoing stream %d",
|
||||
message: "Tried to delete unknown outgoing stream %d",
|
||||
nums: []protocol.StreamNum{num},
|
||||
}
|
||||
}
|
||||
|
||||
-27
@@ -1,27 +0,0 @@
|
||||
Copyright (c) 2009 The Go Authors. All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are
|
||||
met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above
|
||||
copyright notice, this list of conditions and the following disclaimer
|
||||
in the documentation and/or other materials provided with the
|
||||
distribution.
|
||||
* Neither the name of Google Inc. nor the names of its
|
||||
contributors may be used to endorse or promote products derived from
|
||||
this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
-6
@@ -1,6 +0,0 @@
|
||||
# qtls
|
||||
|
||||
[](https://pkg.go.dev/github.com/marten-seemann/qtls-go1-17)
|
||||
[](https://github.com/marten-seemann/qtls-go1-17/actions/workflows/go-test.yml)
|
||||
|
||||
This repository contains a modified version of the standard library's TLS implementation, modified for the QUIC protocol. It is used by [quic-go](https://github.com/lucas-clemente/quic-go).
|
||||
-102
@@ -1,102 +0,0 @@
|
||||
// Copyright 2009 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package qtls
|
||||
|
||||
import "strconv"
|
||||
|
||||
type alert uint8
|
||||
|
||||
// Alert is a TLS alert
|
||||
type Alert = alert
|
||||
|
||||
const (
|
||||
// alert level
|
||||
alertLevelWarning = 1
|
||||
alertLevelError = 2
|
||||
)
|
||||
|
||||
const (
|
||||
alertCloseNotify alert = 0
|
||||
alertUnexpectedMessage alert = 10
|
||||
alertBadRecordMAC alert = 20
|
||||
alertDecryptionFailed alert = 21
|
||||
alertRecordOverflow alert = 22
|
||||
alertDecompressionFailure alert = 30
|
||||
alertHandshakeFailure alert = 40
|
||||
alertBadCertificate alert = 42
|
||||
alertUnsupportedCertificate alert = 43
|
||||
alertCertificateRevoked alert = 44
|
||||
alertCertificateExpired alert = 45
|
||||
alertCertificateUnknown alert = 46
|
||||
alertIllegalParameter alert = 47
|
||||
alertUnknownCA alert = 48
|
||||
alertAccessDenied alert = 49
|
||||
alertDecodeError alert = 50
|
||||
alertDecryptError alert = 51
|
||||
alertExportRestriction alert = 60
|
||||
alertProtocolVersion alert = 70
|
||||
alertInsufficientSecurity alert = 71
|
||||
alertInternalError alert = 80
|
||||
alertInappropriateFallback alert = 86
|
||||
alertUserCanceled alert = 90
|
||||
alertNoRenegotiation alert = 100
|
||||
alertMissingExtension alert = 109
|
||||
alertUnsupportedExtension alert = 110
|
||||
alertCertificateUnobtainable alert = 111
|
||||
alertUnrecognizedName alert = 112
|
||||
alertBadCertificateStatusResponse alert = 113
|
||||
alertBadCertificateHashValue alert = 114
|
||||
alertUnknownPSKIdentity alert = 115
|
||||
alertCertificateRequired alert = 116
|
||||
alertNoApplicationProtocol alert = 120
|
||||
)
|
||||
|
||||
var alertText = map[alert]string{
|
||||
alertCloseNotify: "close notify",
|
||||
alertUnexpectedMessage: "unexpected message",
|
||||
alertBadRecordMAC: "bad record MAC",
|
||||
alertDecryptionFailed: "decryption failed",
|
||||
alertRecordOverflow: "record overflow",
|
||||
alertDecompressionFailure: "decompression failure",
|
||||
alertHandshakeFailure: "handshake failure",
|
||||
alertBadCertificate: "bad certificate",
|
||||
alertUnsupportedCertificate: "unsupported certificate",
|
||||
alertCertificateRevoked: "revoked certificate",
|
||||
alertCertificateExpired: "expired certificate",
|
||||
alertCertificateUnknown: "unknown certificate",
|
||||
alertIllegalParameter: "illegal parameter",
|
||||
alertUnknownCA: "unknown certificate authority",
|
||||
alertAccessDenied: "access denied",
|
||||
alertDecodeError: "error decoding message",
|
||||
alertDecryptError: "error decrypting message",
|
||||
alertExportRestriction: "export restriction",
|
||||
alertProtocolVersion: "protocol version not supported",
|
||||
alertInsufficientSecurity: "insufficient security level",
|
||||
alertInternalError: "internal error",
|
||||
alertInappropriateFallback: "inappropriate fallback",
|
||||
alertUserCanceled: "user canceled",
|
||||
alertNoRenegotiation: "no renegotiation",
|
||||
alertMissingExtension: "missing extension",
|
||||
alertUnsupportedExtension: "unsupported extension",
|
||||
alertCertificateUnobtainable: "certificate unobtainable",
|
||||
alertUnrecognizedName: "unrecognized name",
|
||||
alertBadCertificateStatusResponse: "bad certificate status response",
|
||||
alertBadCertificateHashValue: "bad certificate hash value",
|
||||
alertUnknownPSKIdentity: "unknown PSK identity",
|
||||
alertCertificateRequired: "certificate required",
|
||||
alertNoApplicationProtocol: "no application protocol",
|
||||
}
|
||||
|
||||
func (e alert) String() string {
|
||||
s, ok := alertText[e]
|
||||
if ok {
|
||||
return "tls: " + s
|
||||
}
|
||||
return "tls: alert(" + strconv.Itoa(int(e)) + ")"
|
||||
}
|
||||
|
||||
func (e alert) Error() string {
|
||||
return e.String()
|
||||
}
|
||||
-289
@@ -1,289 +0,0 @@
|
||||
// Copyright 2017 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package qtls
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto"
|
||||
"crypto/ecdsa"
|
||||
"crypto/ed25519"
|
||||
"crypto/elliptic"
|
||||
"crypto/rsa"
|
||||
"errors"
|
||||
"fmt"
|
||||
"hash"
|
||||
"io"
|
||||
)
|
||||
|
||||
// verifyHandshakeSignature verifies a signature against pre-hashed
|
||||
// (if required) handshake contents.
|
||||
func verifyHandshakeSignature(sigType uint8, pubkey crypto.PublicKey, hashFunc crypto.Hash, signed, sig []byte) error {
|
||||
switch sigType {
|
||||
case signatureECDSA:
|
||||
pubKey, ok := pubkey.(*ecdsa.PublicKey)
|
||||
if !ok {
|
||||
return fmt.Errorf("expected an ECDSA public key, got %T", pubkey)
|
||||
}
|
||||
if !ecdsa.VerifyASN1(pubKey, signed, sig) {
|
||||
return errors.New("ECDSA verification failure")
|
||||
}
|
||||
case signatureEd25519:
|
||||
pubKey, ok := pubkey.(ed25519.PublicKey)
|
||||
if !ok {
|
||||
return fmt.Errorf("expected an Ed25519 public key, got %T", pubkey)
|
||||
}
|
||||
if !ed25519.Verify(pubKey, signed, sig) {
|
||||
return errors.New("Ed25519 verification failure")
|
||||
}
|
||||
case signaturePKCS1v15:
|
||||
pubKey, ok := pubkey.(*rsa.PublicKey)
|
||||
if !ok {
|
||||
return fmt.Errorf("expected an RSA public key, got %T", pubkey)
|
||||
}
|
||||
if err := rsa.VerifyPKCS1v15(pubKey, hashFunc, signed, sig); err != nil {
|
||||
return err
|
||||
}
|
||||
case signatureRSAPSS:
|
||||
pubKey, ok := pubkey.(*rsa.PublicKey)
|
||||
if !ok {
|
||||
return fmt.Errorf("expected an RSA public key, got %T", pubkey)
|
||||
}
|
||||
signOpts := &rsa.PSSOptions{SaltLength: rsa.PSSSaltLengthEqualsHash}
|
||||
if err := rsa.VerifyPSS(pubKey, hashFunc, signed, sig, signOpts); err != nil {
|
||||
return err
|
||||
}
|
||||
default:
|
||||
return errors.New("internal error: unknown signature type")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
const (
|
||||
serverSignatureContext = "TLS 1.3, server CertificateVerify\x00"
|
||||
clientSignatureContext = "TLS 1.3, client CertificateVerify\x00"
|
||||
)
|
||||
|
||||
var signaturePadding = []byte{
|
||||
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
|
||||
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
|
||||
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
|
||||
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
|
||||
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
|
||||
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
|
||||
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
|
||||
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
|
||||
}
|
||||
|
||||
// signedMessage returns the pre-hashed (if necessary) message to be signed by
|
||||
// certificate keys in TLS 1.3. See RFC 8446, Section 4.4.3.
|
||||
func signedMessage(sigHash crypto.Hash, context string, transcript hash.Hash) []byte {
|
||||
if sigHash == directSigning {
|
||||
b := &bytes.Buffer{}
|
||||
b.Write(signaturePadding)
|
||||
io.WriteString(b, context)
|
||||
b.Write(transcript.Sum(nil))
|
||||
return b.Bytes()
|
||||
}
|
||||
h := sigHash.New()
|
||||
h.Write(signaturePadding)
|
||||
io.WriteString(h, context)
|
||||
h.Write(transcript.Sum(nil))
|
||||
return h.Sum(nil)
|
||||
}
|
||||
|
||||
// typeAndHashFromSignatureScheme returns the corresponding signature type and
|
||||
// crypto.Hash for a given TLS SignatureScheme.
|
||||
func typeAndHashFromSignatureScheme(signatureAlgorithm SignatureScheme) (sigType uint8, hash crypto.Hash, err error) {
|
||||
switch signatureAlgorithm {
|
||||
case PKCS1WithSHA1, PKCS1WithSHA256, PKCS1WithSHA384, PKCS1WithSHA512:
|
||||
sigType = signaturePKCS1v15
|
||||
case PSSWithSHA256, PSSWithSHA384, PSSWithSHA512:
|
||||
sigType = signatureRSAPSS
|
||||
case ECDSAWithSHA1, ECDSAWithP256AndSHA256, ECDSAWithP384AndSHA384, ECDSAWithP521AndSHA512:
|
||||
sigType = signatureECDSA
|
||||
case Ed25519:
|
||||
sigType = signatureEd25519
|
||||
default:
|
||||
return 0, 0, fmt.Errorf("unsupported signature algorithm: %v", signatureAlgorithm)
|
||||
}
|
||||
switch signatureAlgorithm {
|
||||
case PKCS1WithSHA1, ECDSAWithSHA1:
|
||||
hash = crypto.SHA1
|
||||
case PKCS1WithSHA256, PSSWithSHA256, ECDSAWithP256AndSHA256:
|
||||
hash = crypto.SHA256
|
||||
case PKCS1WithSHA384, PSSWithSHA384, ECDSAWithP384AndSHA384:
|
||||
hash = crypto.SHA384
|
||||
case PKCS1WithSHA512, PSSWithSHA512, ECDSAWithP521AndSHA512:
|
||||
hash = crypto.SHA512
|
||||
case Ed25519:
|
||||
hash = directSigning
|
||||
default:
|
||||
return 0, 0, fmt.Errorf("unsupported signature algorithm: %v", signatureAlgorithm)
|
||||
}
|
||||
return sigType, hash, nil
|
||||
}
|
||||
|
||||
// legacyTypeAndHashFromPublicKey returns the fixed signature type and crypto.Hash for
|
||||
// a given public key used with TLS 1.0 and 1.1, before the introduction of
|
||||
// signature algorithm negotiation.
|
||||
func legacyTypeAndHashFromPublicKey(pub crypto.PublicKey) (sigType uint8, hash crypto.Hash, err error) {
|
||||
switch pub.(type) {
|
||||
case *rsa.PublicKey:
|
||||
return signaturePKCS1v15, crypto.MD5SHA1, nil
|
||||
case *ecdsa.PublicKey:
|
||||
return signatureECDSA, crypto.SHA1, nil
|
||||
case ed25519.PublicKey:
|
||||
// RFC 8422 specifies support for Ed25519 in TLS 1.0 and 1.1,
|
||||
// but it requires holding on to a handshake transcript to do a
|
||||
// full signature, and not even OpenSSL bothers with the
|
||||
// complexity, so we can't even test it properly.
|
||||
return 0, 0, fmt.Errorf("tls: Ed25519 public keys are not supported before TLS 1.2")
|
||||
default:
|
||||
return 0, 0, fmt.Errorf("tls: unsupported public key: %T", pub)
|
||||
}
|
||||
}
|
||||
|
||||
var rsaSignatureSchemes = []struct {
|
||||
scheme SignatureScheme
|
||||
minModulusBytes int
|
||||
maxVersion uint16
|
||||
}{
|
||||
// RSA-PSS is used with PSSSaltLengthEqualsHash, and requires
|
||||
// emLen >= hLen + sLen + 2
|
||||
{PSSWithSHA256, crypto.SHA256.Size()*2 + 2, VersionTLS13},
|
||||
{PSSWithSHA384, crypto.SHA384.Size()*2 + 2, VersionTLS13},
|
||||
{PSSWithSHA512, crypto.SHA512.Size()*2 + 2, VersionTLS13},
|
||||
// PKCS #1 v1.5 uses prefixes from hashPrefixes in crypto/rsa, and requires
|
||||
// emLen >= len(prefix) + hLen + 11
|
||||
// TLS 1.3 dropped support for PKCS #1 v1.5 in favor of RSA-PSS.
|
||||
{PKCS1WithSHA256, 19 + crypto.SHA256.Size() + 11, VersionTLS12},
|
||||
{PKCS1WithSHA384, 19 + crypto.SHA384.Size() + 11, VersionTLS12},
|
||||
{PKCS1WithSHA512, 19 + crypto.SHA512.Size() + 11, VersionTLS12},
|
||||
{PKCS1WithSHA1, 15 + crypto.SHA1.Size() + 11, VersionTLS12},
|
||||
}
|
||||
|
||||
// signatureSchemesForCertificate returns the list of supported SignatureSchemes
|
||||
// for a given certificate, based on the public key and the protocol version,
|
||||
// and optionally filtered by its explicit SupportedSignatureAlgorithms.
|
||||
//
|
||||
// This function must be kept in sync with supportedSignatureAlgorithms.
|
||||
func signatureSchemesForCertificate(version uint16, cert *Certificate) []SignatureScheme {
|
||||
priv, ok := cert.PrivateKey.(crypto.Signer)
|
||||
if !ok {
|
||||
return nil
|
||||
}
|
||||
|
||||
var sigAlgs []SignatureScheme
|
||||
switch pub := priv.Public().(type) {
|
||||
case *ecdsa.PublicKey:
|
||||
if version != VersionTLS13 {
|
||||
// In TLS 1.2 and earlier, ECDSA algorithms are not
|
||||
// constrained to a single curve.
|
||||
sigAlgs = []SignatureScheme{
|
||||
ECDSAWithP256AndSHA256,
|
||||
ECDSAWithP384AndSHA384,
|
||||
ECDSAWithP521AndSHA512,
|
||||
ECDSAWithSHA1,
|
||||
}
|
||||
break
|
||||
}
|
||||
switch pub.Curve {
|
||||
case elliptic.P256():
|
||||
sigAlgs = []SignatureScheme{ECDSAWithP256AndSHA256}
|
||||
case elliptic.P384():
|
||||
sigAlgs = []SignatureScheme{ECDSAWithP384AndSHA384}
|
||||
case elliptic.P521():
|
||||
sigAlgs = []SignatureScheme{ECDSAWithP521AndSHA512}
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
case *rsa.PublicKey:
|
||||
size := pub.Size()
|
||||
sigAlgs = make([]SignatureScheme, 0, len(rsaSignatureSchemes))
|
||||
for _, candidate := range rsaSignatureSchemes {
|
||||
if size >= candidate.minModulusBytes && version <= candidate.maxVersion {
|
||||
sigAlgs = append(sigAlgs, candidate.scheme)
|
||||
}
|
||||
}
|
||||
case ed25519.PublicKey:
|
||||
sigAlgs = []SignatureScheme{Ed25519}
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
|
||||
if cert.SupportedSignatureAlgorithms != nil {
|
||||
var filteredSigAlgs []SignatureScheme
|
||||
for _, sigAlg := range sigAlgs {
|
||||
if isSupportedSignatureAlgorithm(sigAlg, cert.SupportedSignatureAlgorithms) {
|
||||
filteredSigAlgs = append(filteredSigAlgs, sigAlg)
|
||||
}
|
||||
}
|
||||
return filteredSigAlgs
|
||||
}
|
||||
return sigAlgs
|
||||
}
|
||||
|
||||
// selectSignatureScheme picks a SignatureScheme from the peer's preference list
|
||||
// that works with the selected certificate. It's only called for protocol
|
||||
// versions that support signature algorithms, so TLS 1.2 and 1.3.
|
||||
func selectSignatureScheme(vers uint16, c *Certificate, peerAlgs []SignatureScheme) (SignatureScheme, error) {
|
||||
supportedAlgs := signatureSchemesForCertificate(vers, c)
|
||||
if len(supportedAlgs) == 0 {
|
||||
return 0, unsupportedCertificateError(c)
|
||||
}
|
||||
if len(peerAlgs) == 0 && vers == VersionTLS12 {
|
||||
// For TLS 1.2, if the client didn't send signature_algorithms then we
|
||||
// can assume that it supports SHA1. See RFC 5246, Section 7.4.1.4.1.
|
||||
peerAlgs = []SignatureScheme{PKCS1WithSHA1, ECDSAWithSHA1}
|
||||
}
|
||||
// Pick signature scheme in the peer's preference order, as our
|
||||
// preference order is not configurable.
|
||||
for _, preferredAlg := range peerAlgs {
|
||||
if isSupportedSignatureAlgorithm(preferredAlg, supportedAlgs) {
|
||||
return preferredAlg, nil
|
||||
}
|
||||
}
|
||||
return 0, errors.New("tls: peer doesn't support any of the certificate's signature algorithms")
|
||||
}
|
||||
|
||||
// unsupportedCertificateError returns a helpful error for certificates with
|
||||
// an unsupported private key.
|
||||
func unsupportedCertificateError(cert *Certificate) error {
|
||||
switch cert.PrivateKey.(type) {
|
||||
case rsa.PrivateKey, ecdsa.PrivateKey:
|
||||
return fmt.Errorf("tls: unsupported certificate: private key is %T, expected *%T",
|
||||
cert.PrivateKey, cert.PrivateKey)
|
||||
case *ed25519.PrivateKey:
|
||||
return fmt.Errorf("tls: unsupported certificate: private key is *ed25519.PrivateKey, expected ed25519.PrivateKey")
|
||||
}
|
||||
|
||||
signer, ok := cert.PrivateKey.(crypto.Signer)
|
||||
if !ok {
|
||||
return fmt.Errorf("tls: certificate private key (%T) does not implement crypto.Signer",
|
||||
cert.PrivateKey)
|
||||
}
|
||||
|
||||
switch pub := signer.Public().(type) {
|
||||
case *ecdsa.PublicKey:
|
||||
switch pub.Curve {
|
||||
case elliptic.P256():
|
||||
case elliptic.P384():
|
||||
case elliptic.P521():
|
||||
default:
|
||||
return fmt.Errorf("tls: unsupported certificate curve (%s)", pub.Curve.Params().Name)
|
||||
}
|
||||
case *rsa.PublicKey:
|
||||
return fmt.Errorf("tls: certificate RSA key size too small for supported signature algorithms")
|
||||
case ed25519.PublicKey:
|
||||
default:
|
||||
return fmt.Errorf("tls: unsupported certificate key (%T)", pub)
|
||||
}
|
||||
|
||||
if cert.SupportedSignatureAlgorithms != nil {
|
||||
return fmt.Errorf("tls: peer doesn't support the certificate custom signature algorithms")
|
||||
}
|
||||
|
||||
return fmt.Errorf("tls: internal error: unsupported key (%T)", cert.PrivateKey)
|
||||
}
|
||||
-705
@@ -1,705 +0,0 @@
|
||||
// Copyright 2010 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package qtls
|
||||
|
||||
import (
|
||||
"crypto"
|
||||
"crypto/aes"
|
||||
"crypto/cipher"
|
||||
"crypto/des"
|
||||
"crypto/hmac"
|
||||
"crypto/rc4"
|
||||
"crypto/sha1"
|
||||
"crypto/sha256"
|
||||
"fmt"
|
||||
"hash"
|
||||
"runtime"
|
||||
|
||||
"golang.org/x/crypto/chacha20poly1305"
|
||||
"golang.org/x/sys/cpu"
|
||||
)
|
||||
|
||||
// CipherSuite is a TLS cipher suite. Note that most functions in this package
|
||||
// accept and expose cipher suite IDs instead of this type.
|
||||
type CipherSuite struct {
|
||||
ID uint16
|
||||
Name string
|
||||
|
||||
// Supported versions is the list of TLS protocol versions that can
|
||||
// negotiate this cipher suite.
|
||||
SupportedVersions []uint16
|
||||
|
||||
// Insecure is true if the cipher suite has known security issues
|
||||
// due to its primitives, design, or implementation.
|
||||
Insecure bool
|
||||
}
|
||||
|
||||
var (
|
||||
supportedUpToTLS12 = []uint16{VersionTLS10, VersionTLS11, VersionTLS12}
|
||||
supportedOnlyTLS12 = []uint16{VersionTLS12}
|
||||
supportedOnlyTLS13 = []uint16{VersionTLS13}
|
||||
)
|
||||
|
||||
// CipherSuites returns a list of cipher suites currently implemented by this
|
||||
// package, excluding those with security issues, which are returned by
|
||||
// InsecureCipherSuites.
|
||||
//
|
||||
// The list is sorted by ID. Note that the default cipher suites selected by
|
||||
// this package might depend on logic that can't be captured by a static list,
|
||||
// and might not match those returned by this function.
|
||||
func CipherSuites() []*CipherSuite {
|
||||
return []*CipherSuite{
|
||||
{TLS_RSA_WITH_AES_128_CBC_SHA, "TLS_RSA_WITH_AES_128_CBC_SHA", supportedUpToTLS12, false},
|
||||
{TLS_RSA_WITH_AES_256_CBC_SHA, "TLS_RSA_WITH_AES_256_CBC_SHA", supportedUpToTLS12, false},
|
||||
{TLS_RSA_WITH_AES_128_GCM_SHA256, "TLS_RSA_WITH_AES_128_GCM_SHA256", supportedOnlyTLS12, false},
|
||||
{TLS_RSA_WITH_AES_256_GCM_SHA384, "TLS_RSA_WITH_AES_256_GCM_SHA384", supportedOnlyTLS12, false},
|
||||
|
||||
{TLS_AES_128_GCM_SHA256, "TLS_AES_128_GCM_SHA256", supportedOnlyTLS13, false},
|
||||
{TLS_AES_256_GCM_SHA384, "TLS_AES_256_GCM_SHA384", supportedOnlyTLS13, false},
|
||||
{TLS_CHACHA20_POLY1305_SHA256, "TLS_CHACHA20_POLY1305_SHA256", supportedOnlyTLS13, false},
|
||||
|
||||
{TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA, "TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA", supportedUpToTLS12, false},
|
||||
{TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA, "TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA", supportedUpToTLS12, false},
|
||||
{TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA, "TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA", supportedUpToTLS12, false},
|
||||
{TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA, "TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA", supportedUpToTLS12, false},
|
||||
{TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, "TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256", supportedOnlyTLS12, false},
|
||||
{TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384, "TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384", supportedOnlyTLS12, false},
|
||||
{TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256", supportedOnlyTLS12, false},
|
||||
{TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384, "TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384", supportedOnlyTLS12, false},
|
||||
{TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256, "TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256", supportedOnlyTLS12, false},
|
||||
{TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256, "TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256", supportedOnlyTLS12, false},
|
||||
}
|
||||
}
|
||||
|
||||
// InsecureCipherSuites returns a list of cipher suites currently implemented by
|
||||
// this package and which have security issues.
|
||||
//
|
||||
// Most applications should not use the cipher suites in this list, and should
|
||||
// only use those returned by CipherSuites.
|
||||
func InsecureCipherSuites() []*CipherSuite {
|
||||
// This list includes RC4, CBC_SHA256, and 3DES cipher suites. See
|
||||
// cipherSuitesPreferenceOrder for details.
|
||||
return []*CipherSuite{
|
||||
{TLS_RSA_WITH_RC4_128_SHA, "TLS_RSA_WITH_RC4_128_SHA", supportedUpToTLS12, true},
|
||||
{TLS_RSA_WITH_3DES_EDE_CBC_SHA, "TLS_RSA_WITH_3DES_EDE_CBC_SHA", supportedUpToTLS12, true},
|
||||
{TLS_RSA_WITH_AES_128_CBC_SHA256, "TLS_RSA_WITH_AES_128_CBC_SHA256", supportedOnlyTLS12, true},
|
||||
{TLS_ECDHE_ECDSA_WITH_RC4_128_SHA, "TLS_ECDHE_ECDSA_WITH_RC4_128_SHA", supportedUpToTLS12, true},
|
||||
{TLS_ECDHE_RSA_WITH_RC4_128_SHA, "TLS_ECDHE_RSA_WITH_RC4_128_SHA", supportedUpToTLS12, true},
|
||||
{TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA, "TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA", supportedUpToTLS12, true},
|
||||
{TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256, "TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256", supportedOnlyTLS12, true},
|
||||
{TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256, "TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256", supportedOnlyTLS12, true},
|
||||
}
|
||||
}
|
||||
|
||||
// CipherSuiteName returns the standard name for the passed cipher suite ID
|
||||
// (e.g. "TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256"), or a fallback representation
|
||||
// of the ID value if the cipher suite is not implemented by this package.
|
||||
func CipherSuiteName(id uint16) string {
|
||||
for _, c := range CipherSuites() {
|
||||
if c.ID == id {
|
||||
return c.Name
|
||||
}
|
||||
}
|
||||
for _, c := range InsecureCipherSuites() {
|
||||
if c.ID == id {
|
||||
return c.Name
|
||||
}
|
||||
}
|
||||
return fmt.Sprintf("0x%04X", id)
|
||||
}
|
||||
|
||||
const (
|
||||
// suiteECDHE indicates that the cipher suite involves elliptic curve
|
||||
// Diffie-Hellman. This means that it should only be selected when the
|
||||
// client indicates that it supports ECC with a curve and point format
|
||||
// that we're happy with.
|
||||
suiteECDHE = 1 << iota
|
||||
// suiteECSign indicates that the cipher suite involves an ECDSA or
|
||||
// EdDSA signature and therefore may only be selected when the server's
|
||||
// certificate is ECDSA or EdDSA. If this is not set then the cipher suite
|
||||
// is RSA based.
|
||||
suiteECSign
|
||||
// suiteTLS12 indicates that the cipher suite should only be advertised
|
||||
// and accepted when using TLS 1.2.
|
||||
suiteTLS12
|
||||
// suiteSHA384 indicates that the cipher suite uses SHA384 as the
|
||||
// handshake hash.
|
||||
suiteSHA384
|
||||
)
|
||||
|
||||
// A cipherSuite is a TLS 1.0–1.2 cipher suite, and defines the key exchange
|
||||
// mechanism, as well as the cipher+MAC pair or the AEAD.
|
||||
type cipherSuite struct {
|
||||
id uint16
|
||||
// the lengths, in bytes, of the key material needed for each component.
|
||||
keyLen int
|
||||
macLen int
|
||||
ivLen int
|
||||
ka func(version uint16) keyAgreement
|
||||
// flags is a bitmask of the suite* values, above.
|
||||
flags int
|
||||
cipher func(key, iv []byte, isRead bool) interface{}
|
||||
mac func(key []byte) hash.Hash
|
||||
aead func(key, fixedNonce []byte) aead
|
||||
}
|
||||
|
||||
var cipherSuites = []*cipherSuite{ // TODO: replace with a map, since the order doesn't matter.
|
||||
{TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305, 32, 0, 12, ecdheRSAKA, suiteECDHE | suiteTLS12, nil, nil, aeadChaCha20Poly1305},
|
||||
{TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305, 32, 0, 12, ecdheECDSAKA, suiteECDHE | suiteECSign | suiteTLS12, nil, nil, aeadChaCha20Poly1305},
|
||||
{TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, 16, 0, 4, ecdheRSAKA, suiteECDHE | suiteTLS12, nil, nil, aeadAESGCM},
|
||||
{TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, 16, 0, 4, ecdheECDSAKA, suiteECDHE | suiteECSign | suiteTLS12, nil, nil, aeadAESGCM},
|
||||
{TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384, 32, 0, 4, ecdheRSAKA, suiteECDHE | suiteTLS12 | suiteSHA384, nil, nil, aeadAESGCM},
|
||||
{TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384, 32, 0, 4, ecdheECDSAKA, suiteECDHE | suiteECSign | suiteTLS12 | suiteSHA384, nil, nil, aeadAESGCM},
|
||||
{TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256, 16, 32, 16, ecdheRSAKA, suiteECDHE | suiteTLS12, cipherAES, macSHA256, nil},
|
||||
{TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA, 16, 20, 16, ecdheRSAKA, suiteECDHE, cipherAES, macSHA1, nil},
|
||||
{TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256, 16, 32, 16, ecdheECDSAKA, suiteECDHE | suiteECSign | suiteTLS12, cipherAES, macSHA256, nil},
|
||||
{TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA, 16, 20, 16, ecdheECDSAKA, suiteECDHE | suiteECSign, cipherAES, macSHA1, nil},
|
||||
{TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA, 32, 20, 16, ecdheRSAKA, suiteECDHE, cipherAES, macSHA1, nil},
|
||||
{TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA, 32, 20, 16, ecdheECDSAKA, suiteECDHE | suiteECSign, cipherAES, macSHA1, nil},
|
||||
{TLS_RSA_WITH_AES_128_GCM_SHA256, 16, 0, 4, rsaKA, suiteTLS12, nil, nil, aeadAESGCM},
|
||||
{TLS_RSA_WITH_AES_256_GCM_SHA384, 32, 0, 4, rsaKA, suiteTLS12 | suiteSHA384, nil, nil, aeadAESGCM},
|
||||
{TLS_RSA_WITH_AES_128_CBC_SHA256, 16, 32, 16, rsaKA, suiteTLS12, cipherAES, macSHA256, nil},
|
||||
{TLS_RSA_WITH_AES_128_CBC_SHA, 16, 20, 16, rsaKA, 0, cipherAES, macSHA1, nil},
|
||||
{TLS_RSA_WITH_AES_256_CBC_SHA, 32, 20, 16, rsaKA, 0, cipherAES, macSHA1, nil},
|
||||
{TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA, 24, 20, 8, ecdheRSAKA, suiteECDHE, cipher3DES, macSHA1, nil},
|
||||
{TLS_RSA_WITH_3DES_EDE_CBC_SHA, 24, 20, 8, rsaKA, 0, cipher3DES, macSHA1, nil},
|
||||
{TLS_RSA_WITH_RC4_128_SHA, 16, 20, 0, rsaKA, 0, cipherRC4, macSHA1, nil},
|
||||
{TLS_ECDHE_RSA_WITH_RC4_128_SHA, 16, 20, 0, ecdheRSAKA, suiteECDHE, cipherRC4, macSHA1, nil},
|
||||
{TLS_ECDHE_ECDSA_WITH_RC4_128_SHA, 16, 20, 0, ecdheECDSAKA, suiteECDHE | suiteECSign, cipherRC4, macSHA1, nil},
|
||||
}
|
||||
|
||||
// selectCipherSuite returns the first TLS 1.0–1.2 cipher suite from ids which
|
||||
// is also in supportedIDs and passes the ok filter.
|
||||
func selectCipherSuite(ids, supportedIDs []uint16, ok func(*cipherSuite) bool) *cipherSuite {
|
||||
for _, id := range ids {
|
||||
candidate := cipherSuiteByID(id)
|
||||
if candidate == nil || !ok(candidate) {
|
||||
continue
|
||||
}
|
||||
|
||||
for _, suppID := range supportedIDs {
|
||||
if id == suppID {
|
||||
return candidate
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// A cipherSuiteTLS13 defines only the pair of the AEAD algorithm and hash
|
||||
// algorithm to be used with HKDF. See RFC 8446, Appendix B.4.
|
||||
type cipherSuiteTLS13 struct {
|
||||
id uint16
|
||||
keyLen int
|
||||
aead func(key, fixedNonce []byte) aead
|
||||
hash crypto.Hash
|
||||
}
|
||||
|
||||
type CipherSuiteTLS13 struct {
|
||||
ID uint16
|
||||
KeyLen int
|
||||
Hash crypto.Hash
|
||||
AEAD func(key, fixedNonce []byte) cipher.AEAD
|
||||
}
|
||||
|
||||
func (c *CipherSuiteTLS13) IVLen() int {
|
||||
return aeadNonceLength
|
||||
}
|
||||
|
||||
var cipherSuitesTLS13 = []*cipherSuiteTLS13{ // TODO: replace with a map.
|
||||
{TLS_AES_128_GCM_SHA256, 16, aeadAESGCMTLS13, crypto.SHA256},
|
||||
{TLS_CHACHA20_POLY1305_SHA256, 32, aeadChaCha20Poly1305, crypto.SHA256},
|
||||
{TLS_AES_256_GCM_SHA384, 32, aeadAESGCMTLS13, crypto.SHA384},
|
||||
}
|
||||
|
||||
// cipherSuitesPreferenceOrder is the order in which we'll select (on the
|
||||
// server) or advertise (on the client) TLS 1.0–1.2 cipher suites.
|
||||
//
|
||||
// Cipher suites are filtered but not reordered based on the application and
|
||||
// peer's preferences, meaning we'll never select a suite lower in this list if
|
||||
// any higher one is available. This makes it more defensible to keep weaker
|
||||
// cipher suites enabled, especially on the server side where we get the last
|
||||
// word, since there are no known downgrade attacks on cipher suites selection.
|
||||
//
|
||||
// The list is sorted by applying the following priority rules, stopping at the
|
||||
// first (most important) applicable one:
|
||||
//
|
||||
// - Anything else comes before RC4
|
||||
//
|
||||
// RC4 has practically exploitable biases. See https://www.rc4nomore.com.
|
||||
//
|
||||
// - Anything else comes before CBC_SHA256
|
||||
//
|
||||
// SHA-256 variants of the CBC ciphersuites don't implement any Lucky13
|
||||
// countermeasures. See http://www.isg.rhul.ac.uk/tls/Lucky13.html and
|
||||
// https://www.imperialviolet.org/2013/02/04/luckythirteen.html.
|
||||
//
|
||||
// - Anything else comes before 3DES
|
||||
//
|
||||
// 3DES has 64-bit blocks, which makes it fundamentally susceptible to
|
||||
// birthday attacks. See https://sweet32.info.
|
||||
//
|
||||
// - ECDHE comes before anything else
|
||||
//
|
||||
// Once we got the broken stuff out of the way, the most important
|
||||
// property a cipher suite can have is forward secrecy. We don't
|
||||
// implement FFDHE, so that means ECDHE.
|
||||
//
|
||||
// - AEADs come before CBC ciphers
|
||||
//
|
||||
// Even with Lucky13 countermeasures, MAC-then-Encrypt CBC cipher suites
|
||||
// are fundamentally fragile, and suffered from an endless sequence of
|
||||
// padding oracle attacks. See https://eprint.iacr.org/2015/1129,
|
||||
// https://www.imperialviolet.org/2014/12/08/poodleagain.html, and
|
||||
// https://blog.cloudflare.com/yet-another-padding-oracle-in-openssl-cbc-ciphersuites/.
|
||||
//
|
||||
// - AES comes before ChaCha20
|
||||
//
|
||||
// When AES hardware is available, AES-128-GCM and AES-256-GCM are faster
|
||||
// than ChaCha20Poly1305.
|
||||
//
|
||||
// When AES hardware is not available, AES-128-GCM is one or more of: much
|
||||
// slower, way more complex, and less safe (because not constant time)
|
||||
// than ChaCha20Poly1305.
|
||||
//
|
||||
// We use this list if we think both peers have AES hardware, and
|
||||
// cipherSuitesPreferenceOrderNoAES otherwise.
|
||||
//
|
||||
// - AES-128 comes before AES-256
|
||||
//
|
||||
// The only potential advantages of AES-256 are better multi-target
|
||||
// margins, and hypothetical post-quantum properties. Neither apply to
|
||||
// TLS, and AES-256 is slower due to its four extra rounds (which don't
|
||||
// contribute to the advantages above).
|
||||
//
|
||||
// - ECDSA comes before RSA
|
||||
//
|
||||
// The relative order of ECDSA and RSA cipher suites doesn't matter,
|
||||
// as they depend on the certificate. Pick one to get a stable order.
|
||||
//
|
||||
var cipherSuitesPreferenceOrder = []uint16{
|
||||
// AEADs w/ ECDHE
|
||||
TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
|
||||
TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384, TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,
|
||||
TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305, TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305,
|
||||
|
||||
// CBC w/ ECDHE
|
||||
TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA, TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA,
|
||||
TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA, TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA,
|
||||
|
||||
// AEADs w/o ECDHE
|
||||
TLS_RSA_WITH_AES_128_GCM_SHA256,
|
||||
TLS_RSA_WITH_AES_256_GCM_SHA384,
|
||||
|
||||
// CBC w/o ECDHE
|
||||
TLS_RSA_WITH_AES_128_CBC_SHA,
|
||||
TLS_RSA_WITH_AES_256_CBC_SHA,
|
||||
|
||||
// 3DES
|
||||
TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA,
|
||||
TLS_RSA_WITH_3DES_EDE_CBC_SHA,
|
||||
|
||||
// CBC_SHA256
|
||||
TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256, TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256,
|
||||
TLS_RSA_WITH_AES_128_CBC_SHA256,
|
||||
|
||||
// RC4
|
||||
TLS_ECDHE_ECDSA_WITH_RC4_128_SHA, TLS_ECDHE_RSA_WITH_RC4_128_SHA,
|
||||
TLS_RSA_WITH_RC4_128_SHA,
|
||||
}
|
||||
|
||||
var cipherSuitesPreferenceOrderNoAES = []uint16{
|
||||
// ChaCha20Poly1305
|
||||
TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305, TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305,
|
||||
|
||||
// AES-GCM w/ ECDHE
|
||||
TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
|
||||
TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384, TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,
|
||||
|
||||
// The rest of cipherSuitesPreferenceOrder.
|
||||
TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA, TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA,
|
||||
TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA, TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA,
|
||||
TLS_RSA_WITH_AES_128_GCM_SHA256,
|
||||
TLS_RSA_WITH_AES_256_GCM_SHA384,
|
||||
TLS_RSA_WITH_AES_128_CBC_SHA,
|
||||
TLS_RSA_WITH_AES_256_CBC_SHA,
|
||||
TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA,
|
||||
TLS_RSA_WITH_3DES_EDE_CBC_SHA,
|
||||
TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256, TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256,
|
||||
TLS_RSA_WITH_AES_128_CBC_SHA256,
|
||||
TLS_ECDHE_ECDSA_WITH_RC4_128_SHA, TLS_ECDHE_RSA_WITH_RC4_128_SHA,
|
||||
TLS_RSA_WITH_RC4_128_SHA,
|
||||
}
|
||||
|
||||
// disabledCipherSuites are not used unless explicitly listed in
|
||||
// Config.CipherSuites. They MUST be at the end of cipherSuitesPreferenceOrder.
|
||||
var disabledCipherSuites = []uint16{
|
||||
// CBC_SHA256
|
||||
TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256, TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256,
|
||||
TLS_RSA_WITH_AES_128_CBC_SHA256,
|
||||
|
||||
// RC4
|
||||
TLS_ECDHE_ECDSA_WITH_RC4_128_SHA, TLS_ECDHE_RSA_WITH_RC4_128_SHA,
|
||||
TLS_RSA_WITH_RC4_128_SHA,
|
||||
}
|
||||
|
||||
var (
|
||||
defaultCipherSuitesLen = len(cipherSuitesPreferenceOrder) - len(disabledCipherSuites)
|
||||
defaultCipherSuites = cipherSuitesPreferenceOrder[:defaultCipherSuitesLen]
|
||||
)
|
||||
|
||||
// defaultCipherSuitesTLS13 is also the preference order, since there are no
|
||||
// disabled by default TLS 1.3 cipher suites. The same AES vs ChaCha20 logic as
|
||||
// cipherSuitesPreferenceOrder applies.
|
||||
var defaultCipherSuitesTLS13 = []uint16{
|
||||
TLS_AES_128_GCM_SHA256,
|
||||
TLS_AES_256_GCM_SHA384,
|
||||
TLS_CHACHA20_POLY1305_SHA256,
|
||||
}
|
||||
|
||||
var defaultCipherSuitesTLS13NoAES = []uint16{
|
||||
TLS_CHACHA20_POLY1305_SHA256,
|
||||
TLS_AES_128_GCM_SHA256,
|
||||
TLS_AES_256_GCM_SHA384,
|
||||
}
|
||||
|
||||
var (
|
||||
hasGCMAsmAMD64 = cpu.X86.HasAES && cpu.X86.HasPCLMULQDQ
|
||||
hasGCMAsmARM64 = cpu.ARM64.HasAES && cpu.ARM64.HasPMULL
|
||||
// Keep in sync with crypto/aes/cipher_s390x.go.
|
||||
hasGCMAsmS390X = cpu.S390X.HasAES && cpu.S390X.HasAESCBC && cpu.S390X.HasAESCTR &&
|
||||
(cpu.S390X.HasGHASH || cpu.S390X.HasAESGCM)
|
||||
|
||||
hasAESGCMHardwareSupport = runtime.GOARCH == "amd64" && hasGCMAsmAMD64 ||
|
||||
runtime.GOARCH == "arm64" && hasGCMAsmARM64 ||
|
||||
runtime.GOARCH == "s390x" && hasGCMAsmS390X
|
||||
)
|
||||
|
||||
var aesgcmCiphers = map[uint16]bool{
|
||||
// TLS 1.2
|
||||
TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256: true,
|
||||
TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384: true,
|
||||
TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256: true,
|
||||
TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384: true,
|
||||
// TLS 1.3
|
||||
TLS_AES_128_GCM_SHA256: true,
|
||||
TLS_AES_256_GCM_SHA384: true,
|
||||
}
|
||||
|
||||
var nonAESGCMAEADCiphers = map[uint16]bool{
|
||||
// TLS 1.2
|
||||
TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305: true,
|
||||
TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305: true,
|
||||
// TLS 1.3
|
||||
TLS_CHACHA20_POLY1305_SHA256: true,
|
||||
}
|
||||
|
||||
// aesgcmPreferred returns whether the first known cipher in the preference list
|
||||
// is an AES-GCM cipher, implying the peer has hardware support for it.
|
||||
func aesgcmPreferred(ciphers []uint16) bool {
|
||||
for _, cID := range ciphers {
|
||||
if c := cipherSuiteByID(cID); c != nil {
|
||||
return aesgcmCiphers[cID]
|
||||
}
|
||||
if c := cipherSuiteTLS13ByID(cID); c != nil {
|
||||
return aesgcmCiphers[cID]
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func cipherRC4(key, iv []byte, isRead bool) interface{} {
|
||||
cipher, _ := rc4.NewCipher(key)
|
||||
return cipher
|
||||
}
|
||||
|
||||
func cipher3DES(key, iv []byte, isRead bool) interface{} {
|
||||
block, _ := des.NewTripleDESCipher(key)
|
||||
if isRead {
|
||||
return cipher.NewCBCDecrypter(block, iv)
|
||||
}
|
||||
return cipher.NewCBCEncrypter(block, iv)
|
||||
}
|
||||
|
||||
func cipherAES(key, iv []byte, isRead bool) interface{} {
|
||||
block, _ := aes.NewCipher(key)
|
||||
if isRead {
|
||||
return cipher.NewCBCDecrypter(block, iv)
|
||||
}
|
||||
return cipher.NewCBCEncrypter(block, iv)
|
||||
}
|
||||
|
||||
// macSHA1 returns a SHA-1 based constant time MAC.
|
||||
func macSHA1(key []byte) hash.Hash {
|
||||
return hmac.New(newConstantTimeHash(sha1.New), key)
|
||||
}
|
||||
|
||||
// macSHA256 returns a SHA-256 based MAC. This is only supported in TLS 1.2 and
|
||||
// is currently only used in disabled-by-default cipher suites.
|
||||
func macSHA256(key []byte) hash.Hash {
|
||||
return hmac.New(sha256.New, key)
|
||||
}
|
||||
|
||||
type aead interface {
|
||||
cipher.AEAD
|
||||
|
||||
// explicitNonceLen returns the number of bytes of explicit nonce
|
||||
// included in each record. This is eight for older AEADs and
|
||||
// zero for modern ones.
|
||||
explicitNonceLen() int
|
||||
}
|
||||
|
||||
const (
|
||||
aeadNonceLength = 12
|
||||
noncePrefixLength = 4
|
||||
)
|
||||
|
||||
// prefixNonceAEAD wraps an AEAD and prefixes a fixed portion of the nonce to
|
||||
// each call.
|
||||
type prefixNonceAEAD struct {
|
||||
// nonce contains the fixed part of the nonce in the first four bytes.
|
||||
nonce [aeadNonceLength]byte
|
||||
aead cipher.AEAD
|
||||
}
|
||||
|
||||
func (f *prefixNonceAEAD) NonceSize() int { return aeadNonceLength - noncePrefixLength }
|
||||
func (f *prefixNonceAEAD) Overhead() int { return f.aead.Overhead() }
|
||||
func (f *prefixNonceAEAD) explicitNonceLen() int { return f.NonceSize() }
|
||||
|
||||
func (f *prefixNonceAEAD) Seal(out, nonce, plaintext, additionalData []byte) []byte {
|
||||
copy(f.nonce[4:], nonce)
|
||||
return f.aead.Seal(out, f.nonce[:], plaintext, additionalData)
|
||||
}
|
||||
|
||||
func (f *prefixNonceAEAD) Open(out, nonce, ciphertext, additionalData []byte) ([]byte, error) {
|
||||
copy(f.nonce[4:], nonce)
|
||||
return f.aead.Open(out, f.nonce[:], ciphertext, additionalData)
|
||||
}
|
||||
|
||||
// xoredNonceAEAD wraps an AEAD by XORing in a fixed pattern to the nonce
|
||||
// before each call.
|
||||
type xorNonceAEAD struct {
|
||||
nonceMask [aeadNonceLength]byte
|
||||
aead cipher.AEAD
|
||||
}
|
||||
|
||||
func (f *xorNonceAEAD) NonceSize() int { return 8 } // 64-bit sequence number
|
||||
func (f *xorNonceAEAD) Overhead() int { return f.aead.Overhead() }
|
||||
func (f *xorNonceAEAD) explicitNonceLen() int { return 0 }
|
||||
|
||||
func (f *xorNonceAEAD) Seal(out, nonce, plaintext, additionalData []byte) []byte {
|
||||
for i, b := range nonce {
|
||||
f.nonceMask[4+i] ^= b
|
||||
}
|
||||
result := f.aead.Seal(out, f.nonceMask[:], plaintext, additionalData)
|
||||
for i, b := range nonce {
|
||||
f.nonceMask[4+i] ^= b
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
func (f *xorNonceAEAD) Open(out, nonce, ciphertext, additionalData []byte) ([]byte, error) {
|
||||
for i, b := range nonce {
|
||||
f.nonceMask[4+i] ^= b
|
||||
}
|
||||
result, err := f.aead.Open(out, f.nonceMask[:], ciphertext, additionalData)
|
||||
for i, b := range nonce {
|
||||
f.nonceMask[4+i] ^= b
|
||||
}
|
||||
|
||||
return result, err
|
||||
}
|
||||
|
||||
func aeadAESGCM(key, noncePrefix []byte) aead {
|
||||
if len(noncePrefix) != noncePrefixLength {
|
||||
panic("tls: internal error: wrong nonce length")
|
||||
}
|
||||
aes, err := aes.NewCipher(key)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
aead, err := cipher.NewGCM(aes)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
ret := &prefixNonceAEAD{aead: aead}
|
||||
copy(ret.nonce[:], noncePrefix)
|
||||
return ret
|
||||
}
|
||||
|
||||
// AEADAESGCMTLS13 creates a new AES-GCM AEAD for TLS 1.3
|
||||
func AEADAESGCMTLS13(key, fixedNonce []byte) cipher.AEAD {
|
||||
return aeadAESGCMTLS13(key, fixedNonce)
|
||||
}
|
||||
|
||||
func aeadAESGCMTLS13(key, nonceMask []byte) aead {
|
||||
if len(nonceMask) != aeadNonceLength {
|
||||
panic("tls: internal error: wrong nonce length")
|
||||
}
|
||||
aes, err := aes.NewCipher(key)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
aead, err := cipher.NewGCM(aes)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
ret := &xorNonceAEAD{aead: aead}
|
||||
copy(ret.nonceMask[:], nonceMask)
|
||||
return ret
|
||||
}
|
||||
|
||||
func aeadChaCha20Poly1305(key, nonceMask []byte) aead {
|
||||
if len(nonceMask) != aeadNonceLength {
|
||||
panic("tls: internal error: wrong nonce length")
|
||||
}
|
||||
aead, err := chacha20poly1305.New(key)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
ret := &xorNonceAEAD{aead: aead}
|
||||
copy(ret.nonceMask[:], nonceMask)
|
||||
return ret
|
||||
}
|
||||
|
||||
type constantTimeHash interface {
|
||||
hash.Hash
|
||||
ConstantTimeSum(b []byte) []byte
|
||||
}
|
||||
|
||||
// cthWrapper wraps any hash.Hash that implements ConstantTimeSum, and replaces
|
||||
// with that all calls to Sum. It's used to obtain a ConstantTimeSum-based HMAC.
|
||||
type cthWrapper struct {
|
||||
h constantTimeHash
|
||||
}
|
||||
|
||||
func (c *cthWrapper) Size() int { return c.h.Size() }
|
||||
func (c *cthWrapper) BlockSize() int { return c.h.BlockSize() }
|
||||
func (c *cthWrapper) Reset() { c.h.Reset() }
|
||||
func (c *cthWrapper) Write(p []byte) (int, error) { return c.h.Write(p) }
|
||||
func (c *cthWrapper) Sum(b []byte) []byte { return c.h.ConstantTimeSum(b) }
|
||||
|
||||
func newConstantTimeHash(h func() hash.Hash) func() hash.Hash {
|
||||
return func() hash.Hash {
|
||||
return &cthWrapper{h().(constantTimeHash)}
|
||||
}
|
||||
}
|
||||
|
||||
// tls10MAC implements the TLS 1.0 MAC function. RFC 2246, Section 6.2.3.
|
||||
func tls10MAC(h hash.Hash, out, seq, header, data, extra []byte) []byte {
|
||||
h.Reset()
|
||||
h.Write(seq)
|
||||
h.Write(header)
|
||||
h.Write(data)
|
||||
res := h.Sum(out)
|
||||
if extra != nil {
|
||||
h.Write(extra)
|
||||
}
|
||||
return res
|
||||
}
|
||||
|
||||
func rsaKA(version uint16) keyAgreement {
|
||||
return rsaKeyAgreement{}
|
||||
}
|
||||
|
||||
func ecdheECDSAKA(version uint16) keyAgreement {
|
||||
return &ecdheKeyAgreement{
|
||||
isRSA: false,
|
||||
version: version,
|
||||
}
|
||||
}
|
||||
|
||||
func ecdheRSAKA(version uint16) keyAgreement {
|
||||
return &ecdheKeyAgreement{
|
||||
isRSA: true,
|
||||
version: version,
|
||||
}
|
||||
}
|
||||
|
||||
// mutualCipherSuite returns a cipherSuite given a list of supported
|
||||
// ciphersuites and the id requested by the peer.
|
||||
func mutualCipherSuite(have []uint16, want uint16) *cipherSuite {
|
||||
for _, id := range have {
|
||||
if id == want {
|
||||
return cipherSuiteByID(id)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func cipherSuiteByID(id uint16) *cipherSuite {
|
||||
for _, cipherSuite := range cipherSuites {
|
||||
if cipherSuite.id == id {
|
||||
return cipherSuite
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func mutualCipherSuiteTLS13(have []uint16, want uint16) *cipherSuiteTLS13 {
|
||||
for _, id := range have {
|
||||
if id == want {
|
||||
return cipherSuiteTLS13ByID(id)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func cipherSuiteTLS13ByID(id uint16) *cipherSuiteTLS13 {
|
||||
for _, cipherSuite := range cipherSuitesTLS13 {
|
||||
if cipherSuite.id == id {
|
||||
return cipherSuite
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// A list of cipher suite IDs that are, or have been, implemented by this
|
||||
// package.
|
||||
//
|
||||
// See https://www.iana.org/assignments/tls-parameters/tls-parameters.xml
|
||||
const (
|
||||
// TLS 1.0 - 1.2 cipher suites.
|
||||
TLS_RSA_WITH_RC4_128_SHA uint16 = 0x0005
|
||||
TLS_RSA_WITH_3DES_EDE_CBC_SHA uint16 = 0x000a
|
||||
TLS_RSA_WITH_AES_128_CBC_SHA uint16 = 0x002f
|
||||
TLS_RSA_WITH_AES_256_CBC_SHA uint16 = 0x0035
|
||||
TLS_RSA_WITH_AES_128_CBC_SHA256 uint16 = 0x003c
|
||||
TLS_RSA_WITH_AES_128_GCM_SHA256 uint16 = 0x009c
|
||||
TLS_RSA_WITH_AES_256_GCM_SHA384 uint16 = 0x009d
|
||||
TLS_ECDHE_ECDSA_WITH_RC4_128_SHA uint16 = 0xc007
|
||||
TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA uint16 = 0xc009
|
||||
TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA uint16 = 0xc00a
|
||||
TLS_ECDHE_RSA_WITH_RC4_128_SHA uint16 = 0xc011
|
||||
TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA uint16 = 0xc012
|
||||
TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA uint16 = 0xc013
|
||||
TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA uint16 = 0xc014
|
||||
TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256 uint16 = 0xc023
|
||||
TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256 uint16 = 0xc027
|
||||
TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 uint16 = 0xc02f
|
||||
TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256 uint16 = 0xc02b
|
||||
TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 uint16 = 0xc030
|
||||
TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384 uint16 = 0xc02c
|
||||
TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256 uint16 = 0xcca8
|
||||
TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256 uint16 = 0xcca9
|
||||
|
||||
// TLS 1.3 cipher suites.
|
||||
TLS_AES_128_GCM_SHA256 uint16 = 0x1301
|
||||
TLS_AES_256_GCM_SHA384 uint16 = 0x1302
|
||||
TLS_CHACHA20_POLY1305_SHA256 uint16 = 0x1303
|
||||
|
||||
// TLS_FALLBACK_SCSV isn't a standard cipher suite but an indicator
|
||||
// that the client is doing version fallback. See RFC 7507.
|
||||
TLS_FALLBACK_SCSV uint16 = 0x5600
|
||||
|
||||
// Legacy names for the corresponding cipher suites with the correct _SHA256
|
||||
// suffix, retained for backward compatibility.
|
||||
TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305 = TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256
|
||||
TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305 = TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256
|
||||
)
|
||||
-1485
File diff suppressed because it is too large
Load Diff
-1590
File diff suppressed because it is too large
Load Diff
-10
@@ -1,10 +0,0 @@
|
||||
module github.com/marten-seemann/qtls-go1-17
|
||||
|
||||
go 1.17
|
||||
|
||||
require (
|
||||
github.com/golang/mock v1.6.0
|
||||
github.com/marten-seemann/qtls-go1-15 v0.1.4 // indirect
|
||||
golang.org/x/crypto v0.0.0-20200221231518-2aa609cf4a9d
|
||||
golang.org/x/sys v0.0.0-20210510120138-977fb7262007
|
||||
)
|
||||
-34
@@ -1,34 +0,0 @@
|
||||
github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4=
|
||||
github.com/golang/mock v1.6.0 h1:ErTB+efbowRARo13NNdxyJji2egdxLGQhRaY+DUumQc=
|
||||
github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs=
|
||||
github.com/marten-seemann/qtls-go1-15 v0.1.4 h1:RehYMOyRW8hPVEja1KBVsFVNSm35Jj9Mvs5yNoZZ28A=
|
||||
github.com/marten-seemann/qtls-go1-15 v0.1.4/go.mod h1:GyFwywLKkRt+6mfU99csTEY1joMZz5vmB1WNZH3P81I=
|
||||
github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20200221231518-2aa609cf4a9d h1:1ZiEyfaQIg3Qh0EoqpwAakHVhecoE5wlSg5GjnafJGw=
|
||||
golang.org/x/crypto v0.0.0-20200221231518-2aa609cf4a9d/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210510120138-977fb7262007 h1:gG67DSER+11cZvqIMb8S8bt0vZtiN6xWYARwirrOSfE=
|
||||
golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
|
||||
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
|
||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
-1111
File diff suppressed because it is too large
Load Diff
-732
@@ -1,732 +0,0 @@
|
||||
// Copyright 2018 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package qtls
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"crypto"
|
||||
"crypto/hmac"
|
||||
"crypto/rsa"
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
"hash"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
|
||||
"golang.org/x/crypto/cryptobyte"
|
||||
)
|
||||
|
||||
type clientHandshakeStateTLS13 struct {
|
||||
c *Conn
|
||||
ctx context.Context
|
||||
serverHello *serverHelloMsg
|
||||
hello *clientHelloMsg
|
||||
ecdheParams ecdheParameters
|
||||
|
||||
session *clientSessionState
|
||||
earlySecret []byte
|
||||
binderKey []byte
|
||||
|
||||
certReq *certificateRequestMsgTLS13
|
||||
usingPSK bool
|
||||
sentDummyCCS bool
|
||||
suite *cipherSuiteTLS13
|
||||
transcript hash.Hash
|
||||
masterSecret []byte
|
||||
trafficSecret []byte // client_application_traffic_secret_0
|
||||
}
|
||||
|
||||
// handshake requires hs.c, hs.hello, hs.serverHello, hs.ecdheParams, and,
|
||||
// optionally, hs.session, hs.earlySecret and hs.binderKey to be set.
|
||||
func (hs *clientHandshakeStateTLS13) handshake() error {
|
||||
c := hs.c
|
||||
|
||||
// The server must not select TLS 1.3 in a renegotiation. See RFC 8446,
|
||||
// sections 4.1.2 and 4.1.3.
|
||||
if c.handshakes > 0 {
|
||||
c.sendAlert(alertProtocolVersion)
|
||||
return errors.New("tls: server selected TLS 1.3 in a renegotiation")
|
||||
}
|
||||
|
||||
// Consistency check on the presence of a keyShare and its parameters.
|
||||
if hs.ecdheParams == nil || len(hs.hello.keyShares) != 1 {
|
||||
return c.sendAlert(alertInternalError)
|
||||
}
|
||||
|
||||
if err := hs.checkServerHelloOrHRR(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
hs.transcript = hs.suite.hash.New()
|
||||
hs.transcript.Write(hs.hello.marshal())
|
||||
|
||||
if bytes.Equal(hs.serverHello.random, helloRetryRequestRandom) {
|
||||
if err := hs.sendDummyChangeCipherSpec(); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := hs.processHelloRetryRequest(); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
hs.transcript.Write(hs.serverHello.marshal())
|
||||
|
||||
c.buffering = true
|
||||
if err := hs.processServerHello(); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := hs.sendDummyChangeCipherSpec(); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := hs.establishHandshakeKeys(); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := hs.readServerParameters(); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := hs.readServerCertificate(); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := hs.readServerFinished(); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := hs.sendClientCertificate(); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := hs.sendClientFinished(); err != nil {
|
||||
return err
|
||||
}
|
||||
if _, err := c.flush(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
atomic.StoreUint32(&c.handshakeStatus, 1)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// checkServerHelloOrHRR does validity checks that apply to both ServerHello and
|
||||
// HelloRetryRequest messages. It sets hs.suite.
|
||||
func (hs *clientHandshakeStateTLS13) checkServerHelloOrHRR() error {
|
||||
c := hs.c
|
||||
|
||||
if hs.serverHello.supportedVersion == 0 {
|
||||
c.sendAlert(alertMissingExtension)
|
||||
return errors.New("tls: server selected TLS 1.3 using the legacy version field")
|
||||
}
|
||||
|
||||
if hs.serverHello.supportedVersion != VersionTLS13 {
|
||||
c.sendAlert(alertIllegalParameter)
|
||||
return errors.New("tls: server selected an invalid version after a HelloRetryRequest")
|
||||
}
|
||||
|
||||
if hs.serverHello.vers != VersionTLS12 {
|
||||
c.sendAlert(alertIllegalParameter)
|
||||
return errors.New("tls: server sent an incorrect legacy version")
|
||||
}
|
||||
|
||||
if hs.serverHello.ocspStapling ||
|
||||
hs.serverHello.ticketSupported ||
|
||||
hs.serverHello.secureRenegotiationSupported ||
|
||||
len(hs.serverHello.secureRenegotiation) != 0 ||
|
||||
len(hs.serverHello.alpnProtocol) != 0 ||
|
||||
len(hs.serverHello.scts) != 0 {
|
||||
c.sendAlert(alertUnsupportedExtension)
|
||||
return errors.New("tls: server sent a ServerHello extension forbidden in TLS 1.3")
|
||||
}
|
||||
|
||||
if !bytes.Equal(hs.hello.sessionId, hs.serverHello.sessionId) {
|
||||
c.sendAlert(alertIllegalParameter)
|
||||
return errors.New("tls: server did not echo the legacy session ID")
|
||||
}
|
||||
|
||||
if hs.serverHello.compressionMethod != compressionNone {
|
||||
c.sendAlert(alertIllegalParameter)
|
||||
return errors.New("tls: server selected unsupported compression format")
|
||||
}
|
||||
|
||||
selectedSuite := mutualCipherSuiteTLS13(hs.hello.cipherSuites, hs.serverHello.cipherSuite)
|
||||
if hs.suite != nil && selectedSuite != hs.suite {
|
||||
c.sendAlert(alertIllegalParameter)
|
||||
return errors.New("tls: server changed cipher suite after a HelloRetryRequest")
|
||||
}
|
||||
if selectedSuite == nil {
|
||||
c.sendAlert(alertIllegalParameter)
|
||||
return errors.New("tls: server chose an unconfigured cipher suite")
|
||||
}
|
||||
hs.suite = selectedSuite
|
||||
c.cipherSuite = hs.suite.id
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// sendDummyChangeCipherSpec sends a ChangeCipherSpec record for compatibility
|
||||
// with middleboxes that didn't implement TLS correctly. See RFC 8446, Appendix D.4.
|
||||
func (hs *clientHandshakeStateTLS13) sendDummyChangeCipherSpec() error {
|
||||
if hs.sentDummyCCS {
|
||||
return nil
|
||||
}
|
||||
hs.sentDummyCCS = true
|
||||
|
||||
_, err := hs.c.writeRecord(recordTypeChangeCipherSpec, []byte{1})
|
||||
return err
|
||||
}
|
||||
|
||||
// processHelloRetryRequest handles the HRR in hs.serverHello, modifies and
|
||||
// resends hs.hello, and reads the new ServerHello into hs.serverHello.
|
||||
func (hs *clientHandshakeStateTLS13) processHelloRetryRequest() error {
|
||||
c := hs.c
|
||||
|
||||
// The first ClientHello gets double-hashed into the transcript upon a
|
||||
// HelloRetryRequest. (The idea is that the server might offload transcript
|
||||
// storage to the client in the cookie.) See RFC 8446, Section 4.4.1.
|
||||
chHash := hs.transcript.Sum(nil)
|
||||
hs.transcript.Reset()
|
||||
hs.transcript.Write([]byte{typeMessageHash, 0, 0, uint8(len(chHash))})
|
||||
hs.transcript.Write(chHash)
|
||||
hs.transcript.Write(hs.serverHello.marshal())
|
||||
|
||||
// The only HelloRetryRequest extensions we support are key_share and
|
||||
// cookie, and clients must abort the handshake if the HRR would not result
|
||||
// in any change in the ClientHello.
|
||||
if hs.serverHello.selectedGroup == 0 && hs.serverHello.cookie == nil {
|
||||
c.sendAlert(alertIllegalParameter)
|
||||
return errors.New("tls: server sent an unnecessary HelloRetryRequest message")
|
||||
}
|
||||
|
||||
if hs.serverHello.cookie != nil {
|
||||
hs.hello.cookie = hs.serverHello.cookie
|
||||
}
|
||||
|
||||
if hs.serverHello.serverShare.group != 0 {
|
||||
c.sendAlert(alertDecodeError)
|
||||
return errors.New("tls: received malformed key_share extension")
|
||||
}
|
||||
|
||||
// If the server sent a key_share extension selecting a group, ensure it's
|
||||
// a group we advertised but did not send a key share for, and send a key
|
||||
// share for it this time.
|
||||
if curveID := hs.serverHello.selectedGroup; curveID != 0 {
|
||||
curveOK := false
|
||||
for _, id := range hs.hello.supportedCurves {
|
||||
if id == curveID {
|
||||
curveOK = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !curveOK {
|
||||
c.sendAlert(alertIllegalParameter)
|
||||
return errors.New("tls: server selected unsupported group")
|
||||
}
|
||||
if hs.ecdheParams.CurveID() == curveID {
|
||||
c.sendAlert(alertIllegalParameter)
|
||||
return errors.New("tls: server sent an unnecessary HelloRetryRequest key_share")
|
||||
}
|
||||
if _, ok := curveForCurveID(curveID); curveID != X25519 && !ok {
|
||||
c.sendAlert(alertInternalError)
|
||||
return errors.New("tls: CurvePreferences includes unsupported curve")
|
||||
}
|
||||
params, err := generateECDHEParameters(c.config.rand(), curveID)
|
||||
if err != nil {
|
||||
c.sendAlert(alertInternalError)
|
||||
return err
|
||||
}
|
||||
hs.ecdheParams = params
|
||||
hs.hello.keyShares = []keyShare{{group: curveID, data: params.PublicKey()}}
|
||||
}
|
||||
|
||||
hs.hello.raw = nil
|
||||
if len(hs.hello.pskIdentities) > 0 {
|
||||
pskSuite := cipherSuiteTLS13ByID(hs.session.cipherSuite)
|
||||
if pskSuite == nil {
|
||||
return c.sendAlert(alertInternalError)
|
||||
}
|
||||
if pskSuite.hash == hs.suite.hash {
|
||||
// Update binders and obfuscated_ticket_age.
|
||||
ticketAge := uint32(c.config.time().Sub(hs.session.receivedAt) / time.Millisecond)
|
||||
hs.hello.pskIdentities[0].obfuscatedTicketAge = ticketAge + hs.session.ageAdd
|
||||
|
||||
transcript := hs.suite.hash.New()
|
||||
transcript.Write([]byte{typeMessageHash, 0, 0, uint8(len(chHash))})
|
||||
transcript.Write(chHash)
|
||||
transcript.Write(hs.serverHello.marshal())
|
||||
transcript.Write(hs.hello.marshalWithoutBinders())
|
||||
pskBinders := [][]byte{hs.suite.finishedHash(hs.binderKey, transcript)}
|
||||
hs.hello.updateBinders(pskBinders)
|
||||
} else {
|
||||
// Server selected a cipher suite incompatible with the PSK.
|
||||
hs.hello.pskIdentities = nil
|
||||
hs.hello.pskBinders = nil
|
||||
}
|
||||
}
|
||||
|
||||
if hs.hello.earlyData && c.extraConfig != nil && c.extraConfig.Rejected0RTT != nil {
|
||||
c.extraConfig.Rejected0RTT()
|
||||
}
|
||||
hs.hello.earlyData = false // disable 0-RTT
|
||||
|
||||
hs.transcript.Write(hs.hello.marshal())
|
||||
if _, err := c.writeRecord(recordTypeHandshake, hs.hello.marshal()); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
msg, err := c.readHandshake()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
serverHello, ok := msg.(*serverHelloMsg)
|
||||
if !ok {
|
||||
c.sendAlert(alertUnexpectedMessage)
|
||||
return unexpectedMessageError(serverHello, msg)
|
||||
}
|
||||
hs.serverHello = serverHello
|
||||
|
||||
if err := hs.checkServerHelloOrHRR(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (hs *clientHandshakeStateTLS13) processServerHello() error {
|
||||
c := hs.c
|
||||
|
||||
if bytes.Equal(hs.serverHello.random, helloRetryRequestRandom) {
|
||||
c.sendAlert(alertUnexpectedMessage)
|
||||
return errors.New("tls: server sent two HelloRetryRequest messages")
|
||||
}
|
||||
|
||||
if len(hs.serverHello.cookie) != 0 {
|
||||
c.sendAlert(alertUnsupportedExtension)
|
||||
return errors.New("tls: server sent a cookie in a normal ServerHello")
|
||||
}
|
||||
|
||||
if hs.serverHello.selectedGroup != 0 {
|
||||
c.sendAlert(alertDecodeError)
|
||||
return errors.New("tls: malformed key_share extension")
|
||||
}
|
||||
|
||||
if hs.serverHello.serverShare.group == 0 {
|
||||
c.sendAlert(alertIllegalParameter)
|
||||
return errors.New("tls: server did not send a key share")
|
||||
}
|
||||
if hs.serverHello.serverShare.group != hs.ecdheParams.CurveID() {
|
||||
c.sendAlert(alertIllegalParameter)
|
||||
return errors.New("tls: server selected unsupported group")
|
||||
}
|
||||
|
||||
if !hs.serverHello.selectedIdentityPresent {
|
||||
return nil
|
||||
}
|
||||
|
||||
if int(hs.serverHello.selectedIdentity) >= len(hs.hello.pskIdentities) {
|
||||
c.sendAlert(alertIllegalParameter)
|
||||
return errors.New("tls: server selected an invalid PSK")
|
||||
}
|
||||
|
||||
if len(hs.hello.pskIdentities) != 1 || hs.session == nil {
|
||||
return c.sendAlert(alertInternalError)
|
||||
}
|
||||
pskSuite := cipherSuiteTLS13ByID(hs.session.cipherSuite)
|
||||
if pskSuite == nil {
|
||||
return c.sendAlert(alertInternalError)
|
||||
}
|
||||
if pskSuite.hash != hs.suite.hash {
|
||||
c.sendAlert(alertIllegalParameter)
|
||||
return errors.New("tls: server selected an invalid PSK and cipher suite pair")
|
||||
}
|
||||
|
||||
hs.usingPSK = true
|
||||
c.didResume = true
|
||||
c.peerCertificates = hs.session.serverCertificates
|
||||
c.verifiedChains = hs.session.verifiedChains
|
||||
c.ocspResponse = hs.session.ocspResponse
|
||||
c.scts = hs.session.scts
|
||||
return nil
|
||||
}
|
||||
|
||||
func (hs *clientHandshakeStateTLS13) establishHandshakeKeys() error {
|
||||
c := hs.c
|
||||
|
||||
sharedKey := hs.ecdheParams.SharedKey(hs.serverHello.serverShare.data)
|
||||
if sharedKey == nil {
|
||||
c.sendAlert(alertIllegalParameter)
|
||||
return errors.New("tls: invalid server key share")
|
||||
}
|
||||
|
||||
earlySecret := hs.earlySecret
|
||||
if !hs.usingPSK {
|
||||
earlySecret = hs.suite.extract(nil, nil)
|
||||
}
|
||||
handshakeSecret := hs.suite.extract(sharedKey,
|
||||
hs.suite.deriveSecret(earlySecret, "derived", nil))
|
||||
|
||||
clientSecret := hs.suite.deriveSecret(handshakeSecret,
|
||||
clientHandshakeTrafficLabel, hs.transcript)
|
||||
c.out.exportKey(EncryptionHandshake, hs.suite, clientSecret)
|
||||
c.out.setTrafficSecret(hs.suite, clientSecret)
|
||||
serverSecret := hs.suite.deriveSecret(handshakeSecret,
|
||||
serverHandshakeTrafficLabel, hs.transcript)
|
||||
c.in.exportKey(EncryptionHandshake, hs.suite, serverSecret)
|
||||
c.in.setTrafficSecret(hs.suite, serverSecret)
|
||||
|
||||
err := c.config.writeKeyLog(keyLogLabelClientHandshake, hs.hello.random, clientSecret)
|
||||
if err != nil {
|
||||
c.sendAlert(alertInternalError)
|
||||
return err
|
||||
}
|
||||
err = c.config.writeKeyLog(keyLogLabelServerHandshake, hs.hello.random, serverSecret)
|
||||
if err != nil {
|
||||
c.sendAlert(alertInternalError)
|
||||
return err
|
||||
}
|
||||
|
||||
hs.masterSecret = hs.suite.extract(nil,
|
||||
hs.suite.deriveSecret(handshakeSecret, "derived", nil))
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (hs *clientHandshakeStateTLS13) readServerParameters() error {
|
||||
c := hs.c
|
||||
|
||||
msg, err := c.readHandshake()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
encryptedExtensions, ok := msg.(*encryptedExtensionsMsg)
|
||||
if !ok {
|
||||
c.sendAlert(alertUnexpectedMessage)
|
||||
return unexpectedMessageError(encryptedExtensions, msg)
|
||||
}
|
||||
// Notify the caller if 0-RTT was rejected.
|
||||
if !encryptedExtensions.earlyData && hs.hello.earlyData && c.extraConfig != nil && c.extraConfig.Rejected0RTT != nil {
|
||||
c.extraConfig.Rejected0RTT()
|
||||
}
|
||||
c.used0RTT = encryptedExtensions.earlyData
|
||||
if hs.c.extraConfig != nil && hs.c.extraConfig.ReceivedExtensions != nil {
|
||||
hs.c.extraConfig.ReceivedExtensions(typeEncryptedExtensions, encryptedExtensions.additionalExtensions)
|
||||
}
|
||||
hs.transcript.Write(encryptedExtensions.marshal())
|
||||
|
||||
if err := checkALPN(hs.hello.alpnProtocols, encryptedExtensions.alpnProtocol); err != nil {
|
||||
c.sendAlert(alertUnsupportedExtension)
|
||||
return err
|
||||
}
|
||||
c.clientProtocol = encryptedExtensions.alpnProtocol
|
||||
|
||||
if c.extraConfig != nil && c.extraConfig.EnforceNextProtoSelection {
|
||||
if len(encryptedExtensions.alpnProtocol) == 0 {
|
||||
// the server didn't select an ALPN
|
||||
c.sendAlert(alertNoApplicationProtocol)
|
||||
return errors.New("ALPN negotiation failed. Server didn't offer any protocols")
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (hs *clientHandshakeStateTLS13) readServerCertificate() error {
|
||||
c := hs.c
|
||||
|
||||
// Either a PSK or a certificate is always used, but not both.
|
||||
// See RFC 8446, Section 4.1.1.
|
||||
if hs.usingPSK {
|
||||
// Make sure the connection is still being verified whether or not this
|
||||
// is a resumption. Resumptions currently don't reverify certificates so
|
||||
// they don't call verifyServerCertificate. See Issue 31641.
|
||||
if c.config.VerifyConnection != nil {
|
||||
if err := c.config.VerifyConnection(c.connectionStateLocked()); err != nil {
|
||||
c.sendAlert(alertBadCertificate)
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
msg, err := c.readHandshake()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
certReq, ok := msg.(*certificateRequestMsgTLS13)
|
||||
if ok {
|
||||
hs.transcript.Write(certReq.marshal())
|
||||
|
||||
hs.certReq = certReq
|
||||
|
||||
msg, err = c.readHandshake()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
certMsg, ok := msg.(*certificateMsgTLS13)
|
||||
if !ok {
|
||||
c.sendAlert(alertUnexpectedMessage)
|
||||
return unexpectedMessageError(certMsg, msg)
|
||||
}
|
||||
if len(certMsg.certificate.Certificate) == 0 {
|
||||
c.sendAlert(alertDecodeError)
|
||||
return errors.New("tls: received empty certificates message")
|
||||
}
|
||||
hs.transcript.Write(certMsg.marshal())
|
||||
|
||||
c.scts = certMsg.certificate.SignedCertificateTimestamps
|
||||
c.ocspResponse = certMsg.certificate.OCSPStaple
|
||||
|
||||
if err := c.verifyServerCertificate(certMsg.certificate.Certificate); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
msg, err = c.readHandshake()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
certVerify, ok := msg.(*certificateVerifyMsg)
|
||||
if !ok {
|
||||
c.sendAlert(alertUnexpectedMessage)
|
||||
return unexpectedMessageError(certVerify, msg)
|
||||
}
|
||||
|
||||
// See RFC 8446, Section 4.4.3.
|
||||
if !isSupportedSignatureAlgorithm(certVerify.signatureAlgorithm, supportedSignatureAlgorithms) {
|
||||
c.sendAlert(alertIllegalParameter)
|
||||
return errors.New("tls: certificate used with invalid signature algorithm")
|
||||
}
|
||||
sigType, sigHash, err := typeAndHashFromSignatureScheme(certVerify.signatureAlgorithm)
|
||||
if err != nil {
|
||||
return c.sendAlert(alertInternalError)
|
||||
}
|
||||
if sigType == signaturePKCS1v15 || sigHash == crypto.SHA1 {
|
||||
c.sendAlert(alertIllegalParameter)
|
||||
return errors.New("tls: certificate used with invalid signature algorithm")
|
||||
}
|
||||
signed := signedMessage(sigHash, serverSignatureContext, hs.transcript)
|
||||
if err := verifyHandshakeSignature(sigType, c.peerCertificates[0].PublicKey,
|
||||
sigHash, signed, certVerify.signature); err != nil {
|
||||
c.sendAlert(alertDecryptError)
|
||||
return errors.New("tls: invalid signature by the server certificate: " + err.Error())
|
||||
}
|
||||
|
||||
hs.transcript.Write(certVerify.marshal())
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (hs *clientHandshakeStateTLS13) readServerFinished() error {
|
||||
c := hs.c
|
||||
|
||||
msg, err := c.readHandshake()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
finished, ok := msg.(*finishedMsg)
|
||||
if !ok {
|
||||
c.sendAlert(alertUnexpectedMessage)
|
||||
return unexpectedMessageError(finished, msg)
|
||||
}
|
||||
|
||||
expectedMAC := hs.suite.finishedHash(c.in.trafficSecret, hs.transcript)
|
||||
if !hmac.Equal(expectedMAC, finished.verifyData) {
|
||||
c.sendAlert(alertDecryptError)
|
||||
return errors.New("tls: invalid server finished hash")
|
||||
}
|
||||
|
||||
hs.transcript.Write(finished.marshal())
|
||||
|
||||
// Derive secrets that take context through the server Finished.
|
||||
|
||||
hs.trafficSecret = hs.suite.deriveSecret(hs.masterSecret,
|
||||
clientApplicationTrafficLabel, hs.transcript)
|
||||
serverSecret := hs.suite.deriveSecret(hs.masterSecret,
|
||||
serverApplicationTrafficLabel, hs.transcript)
|
||||
c.in.exportKey(EncryptionApplication, hs.suite, serverSecret)
|
||||
c.in.setTrafficSecret(hs.suite, serverSecret)
|
||||
|
||||
err = c.config.writeKeyLog(keyLogLabelClientTraffic, hs.hello.random, hs.trafficSecret)
|
||||
if err != nil {
|
||||
c.sendAlert(alertInternalError)
|
||||
return err
|
||||
}
|
||||
err = c.config.writeKeyLog(keyLogLabelServerTraffic, hs.hello.random, serverSecret)
|
||||
if err != nil {
|
||||
c.sendAlert(alertInternalError)
|
||||
return err
|
||||
}
|
||||
|
||||
c.ekm = hs.suite.exportKeyingMaterial(hs.masterSecret, hs.transcript)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (hs *clientHandshakeStateTLS13) sendClientCertificate() error {
|
||||
c := hs.c
|
||||
|
||||
if hs.certReq == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
cert, err := c.getClientCertificate(toCertificateRequestInfo(&certificateRequestInfo{
|
||||
AcceptableCAs: hs.certReq.certificateAuthorities,
|
||||
SignatureSchemes: hs.certReq.supportedSignatureAlgorithms,
|
||||
Version: c.vers,
|
||||
ctx: hs.ctx,
|
||||
}))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
certMsg := new(certificateMsgTLS13)
|
||||
|
||||
certMsg.certificate = *cert
|
||||
certMsg.scts = hs.certReq.scts && len(cert.SignedCertificateTimestamps) > 0
|
||||
certMsg.ocspStapling = hs.certReq.ocspStapling && len(cert.OCSPStaple) > 0
|
||||
|
||||
hs.transcript.Write(certMsg.marshal())
|
||||
if _, err := c.writeRecord(recordTypeHandshake, certMsg.marshal()); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// If we sent an empty certificate message, skip the CertificateVerify.
|
||||
if len(cert.Certificate) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
certVerifyMsg := new(certificateVerifyMsg)
|
||||
certVerifyMsg.hasSignatureAlgorithm = true
|
||||
|
||||
certVerifyMsg.signatureAlgorithm, err = selectSignatureScheme(c.vers, cert, hs.certReq.supportedSignatureAlgorithms)
|
||||
if err != nil {
|
||||
// getClientCertificate returned a certificate incompatible with the
|
||||
// CertificateRequestInfo supported signature algorithms.
|
||||
c.sendAlert(alertHandshakeFailure)
|
||||
return err
|
||||
}
|
||||
|
||||
sigType, sigHash, err := typeAndHashFromSignatureScheme(certVerifyMsg.signatureAlgorithm)
|
||||
if err != nil {
|
||||
return c.sendAlert(alertInternalError)
|
||||
}
|
||||
|
||||
signed := signedMessage(sigHash, clientSignatureContext, hs.transcript)
|
||||
signOpts := crypto.SignerOpts(sigHash)
|
||||
if sigType == signatureRSAPSS {
|
||||
signOpts = &rsa.PSSOptions{SaltLength: rsa.PSSSaltLengthEqualsHash, Hash: sigHash}
|
||||
}
|
||||
sig, err := cert.PrivateKey.(crypto.Signer).Sign(c.config.rand(), signed, signOpts)
|
||||
if err != nil {
|
||||
c.sendAlert(alertInternalError)
|
||||
return errors.New("tls: failed to sign handshake: " + err.Error())
|
||||
}
|
||||
certVerifyMsg.signature = sig
|
||||
|
||||
hs.transcript.Write(certVerifyMsg.marshal())
|
||||
if _, err := c.writeRecord(recordTypeHandshake, certVerifyMsg.marshal()); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (hs *clientHandshakeStateTLS13) sendClientFinished() error {
|
||||
c := hs.c
|
||||
|
||||
finished := &finishedMsg{
|
||||
verifyData: hs.suite.finishedHash(c.out.trafficSecret, hs.transcript),
|
||||
}
|
||||
|
||||
hs.transcript.Write(finished.marshal())
|
||||
if _, err := c.writeRecord(recordTypeHandshake, finished.marshal()); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
c.out.exportKey(EncryptionApplication, hs.suite, hs.trafficSecret)
|
||||
c.out.setTrafficSecret(hs.suite, hs.trafficSecret)
|
||||
|
||||
if !c.config.SessionTicketsDisabled && c.config.ClientSessionCache != nil {
|
||||
c.resumptionSecret = hs.suite.deriveSecret(hs.masterSecret,
|
||||
resumptionLabel, hs.transcript)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *Conn) handleNewSessionTicket(msg *newSessionTicketMsgTLS13) error {
|
||||
if !c.isClient {
|
||||
c.sendAlert(alertUnexpectedMessage)
|
||||
return errors.New("tls: received new session ticket from a client")
|
||||
}
|
||||
|
||||
if c.config.SessionTicketsDisabled || c.config.ClientSessionCache == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
// See RFC 8446, Section 4.6.1.
|
||||
if msg.lifetime == 0 {
|
||||
return nil
|
||||
}
|
||||
lifetime := time.Duration(msg.lifetime) * time.Second
|
||||
if lifetime > maxSessionTicketLifetime {
|
||||
c.sendAlert(alertIllegalParameter)
|
||||
return errors.New("tls: received a session ticket with invalid lifetime")
|
||||
}
|
||||
|
||||
cipherSuite := cipherSuiteTLS13ByID(c.cipherSuite)
|
||||
if cipherSuite == nil || c.resumptionSecret == nil {
|
||||
return c.sendAlert(alertInternalError)
|
||||
}
|
||||
|
||||
// We need to save the max_early_data_size that the server sent us, in order
|
||||
// to decide if we're going to try 0-RTT with this ticket.
|
||||
// However, at the same time, the qtls.ClientSessionTicket needs to be equal to
|
||||
// the tls.ClientSessionTicket, so we can't just add a new field to the struct.
|
||||
// We therefore abuse the nonce field (which is a byte slice)
|
||||
nonceWithEarlyData := make([]byte, len(msg.nonce)+4)
|
||||
binary.BigEndian.PutUint32(nonceWithEarlyData, msg.maxEarlyData)
|
||||
copy(nonceWithEarlyData[4:], msg.nonce)
|
||||
|
||||
var appData []byte
|
||||
if c.extraConfig != nil && c.extraConfig.GetAppDataForSessionState != nil {
|
||||
appData = c.extraConfig.GetAppDataForSessionState()
|
||||
}
|
||||
var b cryptobyte.Builder
|
||||
b.AddUint16(clientSessionStateVersion) // revision
|
||||
b.AddUint32(msg.maxEarlyData)
|
||||
b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) {
|
||||
b.AddBytes(appData)
|
||||
})
|
||||
b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) {
|
||||
b.AddBytes(msg.nonce)
|
||||
})
|
||||
|
||||
// Save the resumption_master_secret and nonce instead of deriving the PSK
|
||||
// to do the least amount of work on NewSessionTicket messages before we
|
||||
// know if the ticket will be used. Forward secrecy of resumed connections
|
||||
// is guaranteed by the requirement for pskModeDHE.
|
||||
session := &clientSessionState{
|
||||
sessionTicket: msg.label,
|
||||
vers: c.vers,
|
||||
cipherSuite: c.cipherSuite,
|
||||
masterSecret: c.resumptionSecret,
|
||||
serverCertificates: c.peerCertificates,
|
||||
verifiedChains: c.verifiedChains,
|
||||
receivedAt: c.config.time(),
|
||||
nonce: b.BytesOrPanic(),
|
||||
useBy: c.config.time().Add(lifetime),
|
||||
ageAdd: msg.ageAdd,
|
||||
ocspResponse: c.ocspResponse,
|
||||
scts: c.scts,
|
||||
}
|
||||
|
||||
cacheKey := clientSessionCacheKey(c.conn.RemoteAddr(), c.config)
|
||||
c.config.ClientSessionCache.Put(cacheKey, toClientSessionState(session))
|
||||
|
||||
return nil
|
||||
}
|
||||
-1832
File diff suppressed because it is too large
Load Diff
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user