154
common/ktls/ktls_write.go
Normal file
154
common/ktls/ktls_write.go
Normal file
@@ -0,0 +1,154 @@
|
||||
// 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.
|
||||
|
||||
//go:build linux && go1.25 && !without_badtls
|
||||
|
||||
package ktls
|
||||
|
||||
import (
|
||||
"crypto/cipher"
|
||||
"crypto/tls"
|
||||
"errors"
|
||||
"net"
|
||||
)
|
||||
|
||||
func (c *Conn) Write(b []byte) (int, error) {
|
||||
if !c.kernelTx {
|
||||
return c.Conn.Write(b)
|
||||
}
|
||||
// interlock with Close below
|
||||
for {
|
||||
x := c.rawConn.ActiveCall.Load()
|
||||
if x&1 != 0 {
|
||||
return 0, net.ErrClosed
|
||||
}
|
||||
if c.rawConn.ActiveCall.CompareAndSwap(x, x+2) {
|
||||
break
|
||||
}
|
||||
}
|
||||
defer c.rawConn.ActiveCall.Add(-2)
|
||||
|
||||
//if err := c.Conn.HandshakeContext(context.Background()); err != nil {
|
||||
// return 0, err
|
||||
//}
|
||||
|
||||
c.rawConn.Out.Lock()
|
||||
defer c.rawConn.Out.Unlock()
|
||||
|
||||
if err := *c.rawConn.Out.Err; err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
if !c.rawConn.IsHandshakeComplete.Load() {
|
||||
return 0, tls.AlertError(alertInternalError)
|
||||
}
|
||||
|
||||
if *c.rawConn.CloseNotifySent {
|
||||
// return 0, errShutdown
|
||||
return 0, errors.New("tls: protocol is shutdown")
|
||||
}
|
||||
|
||||
// TLS 1.0 is susceptible to a chosen-plaintext
|
||||
// attack when using block mode ciphers due to predictable IVs.
|
||||
// This can be prevented by splitting each Application Data
|
||||
// record into two records, effectively randomizing the RawIV.
|
||||
//
|
||||
// https://www.openssl.org/~bodo/tls-cbc.txt
|
||||
// https://bugzilla.mozilla.org/show_bug.cgi?id=665814
|
||||
// https://www.imperialviolet.org/2012/01/15/beastfollowup.html
|
||||
|
||||
var m int
|
||||
if len(b) > 1 && *c.rawConn.Vers == tls.VersionTLS10 {
|
||||
if _, ok := (*c.rawConn.Out.Cipher).(cipher.BlockMode); ok {
|
||||
n, err := c.writeRecordLocked(recordTypeApplicationData, b[:1])
|
||||
if err != nil {
|
||||
return n, c.rawConn.Out.SetErrorLocked(err)
|
||||
}
|
||||
m, b = 1, b[1:]
|
||||
}
|
||||
}
|
||||
|
||||
n, err := c.writeRecordLocked(recordTypeApplicationData, b)
|
||||
return n + m, c.rawConn.Out.SetErrorLocked(err)
|
||||
}
|
||||
|
||||
func (c *Conn) writeRecordLocked(typ uint16, data []byte) (n int, err error) {
|
||||
if !c.kernelTx {
|
||||
return c.rawConn.WriteRecordLocked(typ, data)
|
||||
}
|
||||
/*for len(data) > 0 {
|
||||
m := len(data)
|
||||
if maxPayload := c.maxPayloadSizeForWrite(typ); m > maxPayload {
|
||||
m = maxPayload
|
||||
}
|
||||
_, err = c.writeKernelRecord(typ, data[:m])
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
n += m
|
||||
data = data[m:]
|
||||
}*/
|
||||
return c.writeKernelRecord(typ, data)
|
||||
}
|
||||
|
||||
const (
|
||||
// tcpMSSEstimate is a conservative estimate of the TCP maximum segment
|
||||
// size (MSS). A constant is used, rather than querying the kernel for
|
||||
// the actual MSS, to avoid complexity. The value here is the IPv6
|
||||
// minimum MTU (1280 bytes) minus the overhead of an IPv6 header (40
|
||||
// bytes) and a TCP header with timestamps (32 bytes).
|
||||
tcpMSSEstimate = 1208
|
||||
|
||||
// recordSizeBoostThreshold is the number of bytes of application data
|
||||
// sent after which the TLS record size will be increased to the
|
||||
// maximum.
|
||||
recordSizeBoostThreshold = 128 * 1024
|
||||
)
|
||||
|
||||
func (c *Conn) maxPayloadSizeForWrite(typ uint16) int {
|
||||
if /*c.config.DynamicRecordSizingDisabled ||*/ typ != recordTypeApplicationData {
|
||||
return maxPlaintext
|
||||
}
|
||||
|
||||
if *c.rawConn.PacketsSent >= recordSizeBoostThreshold {
|
||||
return maxPlaintext
|
||||
}
|
||||
|
||||
// Subtract TLS overheads to get the maximum payload size.
|
||||
payloadBytes := tcpMSSEstimate - recordHeaderLen - c.rawConn.Out.ExplicitNonceLen()
|
||||
if rawCipher := *c.rawConn.Out.Cipher; rawCipher != nil {
|
||||
switch ciph := rawCipher.(type) {
|
||||
case cipher.Stream:
|
||||
payloadBytes -= (*c.rawConn.Out.Mac).Size()
|
||||
case cipher.AEAD:
|
||||
payloadBytes -= ciph.Overhead()
|
||||
/*case cbcMode:
|
||||
blockSize := ciph.BlockSize()
|
||||
// The payload must fit in a multiple of blockSize, with
|
||||
// room for at least one padding byte.
|
||||
payloadBytes = (payloadBytes & ^(blockSize - 1)) - 1
|
||||
// The RawMac is appended before padding so affects the
|
||||
// payload size directly.
|
||||
payloadBytes -= c.out.mac.Size()*/
|
||||
default:
|
||||
panic("unknown cipher type")
|
||||
}
|
||||
}
|
||||
if *c.rawConn.Vers == tls.VersionTLS13 {
|
||||
payloadBytes-- // encrypted ContentType
|
||||
}
|
||||
|
||||
// Allow packet growth in arithmetic progression up to max.
|
||||
pkt := *c.rawConn.PacketsSent
|
||||
*c.rawConn.PacketsSent++
|
||||
if pkt > 1000 {
|
||||
return maxPlaintext // avoid overflow in multiply below
|
||||
}
|
||||
|
||||
n := payloadBytes * int(pkt+1)
|
||||
if n > maxPlaintext {
|
||||
n = maxPlaintext
|
||||
}
|
||||
return n
|
||||
}
|
||||
Reference in New Issue
Block a user