diff --git a/internal/app/session/session.go b/internal/app/session/session.go index 759f5aa..b66847e 100644 --- a/internal/app/session/session.go +++ b/internal/app/session/session.go @@ -13,10 +13,10 @@ import ( "github.com/openlibrecommunity/olcrtc/internal/auth" "github.com/openlibrecommunity/olcrtc/internal/client" "github.com/openlibrecommunity/olcrtc/internal/control" - "github.com/openlibrecommunity/olcrtc/internal/crypto" enginebuiltin "github.com/openlibrecommunity/olcrtc/internal/engine/builtin" "github.com/openlibrecommunity/olcrtc/internal/logger" "github.com/openlibrecommunity/olcrtc/internal/names" + "github.com/openlibrecommunity/olcrtc/internal/runtime" "github.com/openlibrecommunity/olcrtc/internal/server" "github.com/openlibrecommunity/olcrtc/internal/transport" "github.com/openlibrecommunity/olcrtc/internal/transport/datachannel" @@ -549,7 +549,7 @@ func validateTrafficConfig(cfg Config) error { func trafficConfig(cfg Config) (transport.TrafficConfig, error) { if cfg.TrafficMaxPayloadSize < 0 || (cfg.TrafficMaxPayloadSize > 0 && - cfg.TrafficMaxPayloadSize <= crypto.WireOverhead) { + cfg.TrafficMaxPayloadSize < runtime.MinSmuxWirePayload) { return transport.TrafficConfig{}, ErrTrafficMaxPayloadSizeInvalid } minDelay, err := parseOptionalNonNegativeDuration(cfg.TrafficMinDelay) diff --git a/internal/app/session/session_test.go b/internal/app/session/session_test.go index a3aa21b..ad92906 100644 --- a/internal/app/session/session_test.go +++ b/internal/app/session/session_test.go @@ -8,7 +8,7 @@ import ( "time" "github.com/openlibrecommunity/olcrtc/internal/control" - "github.com/openlibrecommunity/olcrtc/internal/crypto" + "github.com/openlibrecommunity/olcrtc/internal/runtime" ) const testBadDuration = "nope" @@ -511,10 +511,10 @@ func TestValidate(t *testing.T) { want: ErrTrafficMaxPayloadSizeInvalid, }, { - name: "traffic rejects payload smaller than crypto overhead", + name: "traffic rejects payload too small for encrypted smux frame", cfg: func() Config { cfg := base - cfg.TrafficMaxPayloadSize = crypto.WireOverhead + cfg.TrafficMaxPayloadSize = runtime.MinSmuxWirePayload - 1 return cfg }(), want: ErrTrafficMaxPayloadSizeInvalid, diff --git a/internal/client/client_test.go b/internal/client/client_test.go index ed249d7..80ce4a2 100644 --- a/internal/client/client_test.go +++ b/internal/client/client_test.go @@ -52,9 +52,10 @@ func TestSmuxConfig(t *testing.T) { t.Fatalf("smuxConfig(0) = %+v", cfg) } capped := smuxConfig(4096) - if capped.MaxFrameSize != 4096-cryptopkg.WireOverhead { + want := 4096 - runtime.SmuxWireOverhead + if capped.MaxFrameSize != want { t.Fatalf("smuxConfig(4096).MaxFrameSize = %d, want %d", - capped.MaxFrameSize, 4096-cryptopkg.WireOverhead) + capped.MaxFrameSize, want) } } diff --git a/internal/runtime/runtime.go b/internal/runtime/runtime.go index 8a2af3e..9361bb9 100644 --- a/internal/runtime/runtime.go +++ b/internal/runtime/runtime.go @@ -17,6 +17,19 @@ import ( "github.com/xtaci/smux" ) +const ( + // SmuxFrameOverhead is the fixed smux frame header size. MaxFrameSize + // caps only the smux payload, while muxconn encrypts and sends the whole + // smux frame as one transport message. + SmuxFrameOverhead = 8 + // SmuxWireOverhead is the non-payload overhead added around each smux + // frame before it reaches the transport payload limit. + SmuxWireOverhead = crypto.WireOverhead + SmuxFrameOverhead + // MinSmuxWirePayload is the smallest useful encrypted transport payload + // cap that can still carry a non-empty smux frame. + MinSmuxWirePayload = SmuxWireOverhead + 1 +) + // ErrKeyRequired is returned when no encryption key is provided. var ErrKeyRequired = errors.New("key required (use -key )") @@ -44,15 +57,15 @@ func SetupCipher(keyHex string) (*crypto.Cipher, error) { // SmuxConfig returns the tuned smux config used on both ends. Both peers // must agree on Version and MaxFrameSize. maxWirePayload, when > 0, -// constrains the max frame size to fit under the transport's per-message -// payload cap minus the AEAD wire overhead. +// constrains the smux payload size so the encrypted whole smux frame fits +// under the transport's per-message payload cap. func SmuxConfig(maxWirePayload int) *smux.Config { cfg := smux.DefaultConfig() cfg.Version = 2 cfg.KeepAliveDisabled = false cfg.MaxFrameSize = 32768 - if maxWirePayload > crypto.WireOverhead { - maxFrameSize := maxWirePayload - crypto.WireOverhead + if maxWirePayload >= MinSmuxWirePayload { + maxFrameSize := maxWirePayload - SmuxWireOverhead if maxFrameSize < cfg.MaxFrameSize { cfg.MaxFrameSize = maxFrameSize } diff --git a/internal/runtime/runtime_test.go b/internal/runtime/runtime_test.go index 7d18bbe..43032ea 100644 --- a/internal/runtime/runtime_test.go +++ b/internal/runtime/runtime_test.go @@ -44,12 +44,15 @@ func TestSmuxConfigDefault(t *testing.T) { } func TestSmuxConfigShrinks(t *testing.T) { - // 100-byte wire payload minus crypto overhead is far below default 32768, - // so MaxFrameSize must shrink. + // 100-byte wire payload minus smux+crypto overhead is far below default + // 32768, so MaxFrameSize must shrink. cfg := runtime.SmuxConfig(100) if cfg.MaxFrameSize >= 32768 { t.Fatalf("MaxFrameSize = %d, want shrunk", cfg.MaxFrameSize) } + if cfg.MaxFrameSize+runtime.SmuxWireOverhead != 100 { + t.Fatalf("wire size = %d, want 100", cfg.MaxFrameSize+runtime.SmuxWireOverhead) + } } func TestHealthTrackerEmitsOnEveryChange(t *testing.T) { diff --git a/internal/server/server_test.go b/internal/server/server_test.go index 0f14a0a..16816a4 100644 --- a/internal/server/server_test.go +++ b/internal/server/server_test.go @@ -53,9 +53,10 @@ func TestSmuxConfig(t *testing.T) { t.Fatalf("smuxConfig(0) = %+v", cfg) } capped := smuxConfig(4096) - if capped.MaxFrameSize != 4096-cryptopkg.WireOverhead { + want := 4096 - runtime.SmuxWireOverhead + if capped.MaxFrameSize != want { t.Fatalf("smuxConfig(4096).MaxFrameSize = %d, want %d", - capped.MaxFrameSize, 4096-cryptopkg.WireOverhead) + capped.MaxFrameSize, want) } }