tunnel: add write mutex for concurrent safety
WriteFrames and keepalive both write multi-part messages to the TLS connection. Without synchronization, their writes could interleave and corrupt the framing. Add writeMu to serialize all tunnel writes. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
6416159164
commit
b3f4c5f42b
1 changed files with 18 additions and 7 deletions
|
|
@ -28,6 +28,7 @@ const (
|
||||||
// SoftEther VPN session. Each "block" is one Ethernet frame.
|
// SoftEther VPN session. Each "block" is one Ethernet frame.
|
||||||
type Tunnel struct {
|
type Tunnel struct {
|
||||||
sess *Session
|
sess *Session
|
||||||
|
writeMu sync.Mutex
|
||||||
stopCh chan struct{}
|
stopCh chan struct{}
|
||||||
stopped sync.Once
|
stopped sync.Once
|
||||||
}
|
}
|
||||||
|
|
@ -83,7 +84,11 @@ func (t *Tunnel) ReadFrames() ([][]byte, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// WriteFrames sends a batch of Ethernet frames to the server.
|
// WriteFrames sends a batch of Ethernet frames to the server.
|
||||||
|
// Safe for concurrent use from multiple goroutines.
|
||||||
func (t *Tunnel) WriteFrames(frames [][]byte) error {
|
func (t *Tunnel) WriteFrames(frames [][]byte) error {
|
||||||
|
t.writeMu.Lock()
|
||||||
|
defer t.writeMu.Unlock()
|
||||||
|
|
||||||
if err := binary.Write(t.sess.Conn, binary.BigEndian, uint32(len(frames))); err != nil {
|
if err := binary.Write(t.sess.Conn, binary.BigEndian, uint32(len(frames))); err != nil {
|
||||||
return fmt.Errorf("write num blocks: %w", err)
|
return fmt.Errorf("write num blocks: %w", err)
|
||||||
}
|
}
|
||||||
|
|
@ -170,14 +175,20 @@ func (t *Tunnel) StartKeepalive() {
|
||||||
return
|
return
|
||||||
case <-ticker.C:
|
case <-ticker.C:
|
||||||
size := uint32(rng.Intn(int(maxKeepaliveSize))) + 1
|
size := uint32(rng.Intn(int(maxKeepaliveSize))) + 1
|
||||||
if err := binary.Write(t.sess.Conn, binary.BigEndian, keepAliveMagic); err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if err := binary.Write(t.sess.Conn, binary.BigEndian, size); err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
rng.Read(randBuf[:size])
|
rng.Read(randBuf[:size])
|
||||||
if _, err := t.sess.Conn.Write(randBuf[:size]); err != nil {
|
|
||||||
|
t.writeMu.Lock()
|
||||||
|
err1 := binary.Write(t.sess.Conn, binary.BigEndian, keepAliveMagic)
|
||||||
|
var err2, err3 error
|
||||||
|
if err1 == nil {
|
||||||
|
err2 = binary.Write(t.sess.Conn, binary.BigEndian, size)
|
||||||
|
}
|
||||||
|
if err2 == nil {
|
||||||
|
_, err3 = t.sess.Conn.Write(randBuf[:size])
|
||||||
|
}
|
||||||
|
t.writeMu.Unlock()
|
||||||
|
|
||||||
|
if err1 != nil || err2 != nil || err3 != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue