diff --git a/pkg/client/tunnel.go b/pkg/client/tunnel.go index f8d43f0..5a36c44 100644 --- a/pkg/client/tunnel.go +++ b/pkg/client/tunnel.go @@ -85,20 +85,31 @@ func (t *Tunnel) ReadFrames() ([][]byte, error) { // WriteFrames sends a batch of Ethernet frames to the server. // Safe for concurrent use from multiple goroutines. +// Assembles the entire message into one buffer for a single TLS write. 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 { - return fmt.Errorf("write num blocks: %w", err) + // Calculate total size: 4 (numBlocks) + per frame: 4 (size) + len(data) + total := 4 + for _, f := range frames { + total += 4 + len(f) } - for _, frame := range frames { - if err := binary.Write(t.sess.Conn, binary.BigEndian, uint32(len(frame))); err != nil { - return fmt.Errorf("write block size: %w", err) - } - if _, err := t.sess.Conn.Write(frame); err != nil { - return fmt.Errorf("write block data: %w", err) - } + + buf := make([]byte, total) + off := 0 + binary.BigEndian.PutUint32(buf[off:], uint32(len(frames))) + off += 4 + for _, f := range frames { + binary.BigEndian.PutUint32(buf[off:], uint32(len(f))) + off += 4 + copy(buf[off:], f) + off += len(f) + } + + t.writeMu.Lock() + _, err := t.sess.Conn.Write(buf) + t.writeMu.Unlock() + + if err != nil { + return fmt.Errorf("write frames: %w", err) } return nil }