github.com/MerlinKodo/sing-shadowsocks2@v0.1.6/internal/shadowio/writer.go (about)

     1  package shadowio
     2  
     3  import (
     4  	"crypto/cipher"
     5  	"encoding/binary"
     6  	"io"
     7  	"sync"
     8  
     9  	"github.com/sagernet/sing/common"
    10  	"github.com/sagernet/sing/common/buf"
    11  	"github.com/sagernet/sing/common/bufio"
    12  	N "github.com/sagernet/sing/common/network"
    13  )
    14  
    15  type Writer struct {
    16  	WriterInterface
    17  	writer        N.ExtendedWriter
    18  	cipher        cipher.AEAD
    19  	maxPacketSize int
    20  	nonce         []byte
    21  	access        sync.Mutex
    22  }
    23  
    24  func NewWriter(writer io.Writer, cipher cipher.AEAD, nonce []byte, maxPacketSize int) *Writer {
    25  	if len(nonce) == 0 {
    26  		nonce = make([]byte, cipher.NonceSize())
    27  	}
    28  	return &Writer{
    29  		writer:        bufio.NewExtendedWriter(writer),
    30  		cipher:        cipher,
    31  		nonce:         nonce,
    32  		maxPacketSize: maxPacketSize,
    33  	}
    34  }
    35  
    36  func (w *Writer) Encrypt(destination []byte, source []byte) {
    37  	w.cipher.Seal(destination, w.nonce, source, nil)
    38  	increaseNonce(w.nonce)
    39  }
    40  
    41  func (w *Writer) Write(p []byte) (n int, err error) {
    42  	if len(p) == 0 {
    43  		return
    44  	}
    45  	w.access.Lock()
    46  	defer w.access.Unlock()
    47  	for pLen := len(p); pLen > 0; {
    48  		var data []byte
    49  		if pLen > w.maxPacketSize {
    50  			data = p[:w.maxPacketSize]
    51  			p = p[w.maxPacketSize:]
    52  			pLen -= w.maxPacketSize
    53  		} else {
    54  			data = p
    55  			pLen = 0
    56  		}
    57  		bufferSize := PacketLengthBufferSize + 2*Overhead + len(data)
    58  		buffer := buf.NewSize(bufferSize)
    59  		common.Must(binary.Write(buffer, binary.BigEndian, uint16(len(data))))
    60  		w.cipher.Seal(buffer.Index(0), w.nonce, buffer.To(PacketLengthBufferSize), nil)
    61  		increaseNonce(w.nonce)
    62  		buffer.Extend(Overhead)
    63  		w.cipher.Seal(buffer.Index(buffer.Len()), w.nonce, data, nil)
    64  		buffer.Extend(len(data) + Overhead)
    65  		increaseNonce(w.nonce)
    66  		_, err = w.writer.Write(buffer.Bytes())
    67  		buffer.Release()
    68  		if err != nil {
    69  			return
    70  		}
    71  		n += len(data)
    72  	}
    73  	return
    74  }
    75  
    76  func (w *Writer) WriteBuffer(buffer *buf.Buffer) error {
    77  	if buffer.Len() > w.maxPacketSize {
    78  		defer buffer.Release()
    79  		return common.Error(w.Write(buffer.Bytes()))
    80  	}
    81  	pLen := buffer.Len()
    82  	headerOffset := PacketLengthBufferSize + Overhead
    83  	header := buffer.ExtendHeader(headerOffset)
    84  	binary.BigEndian.PutUint16(header, uint16(pLen))
    85  	w.cipher.Seal(header[:0], w.nonce, header[:PacketLengthBufferSize], nil)
    86  	increaseNonce(w.nonce)
    87  	w.cipher.Seal(buffer.Index(headerOffset), w.nonce, buffer.From(headerOffset), nil)
    88  	increaseNonce(w.nonce)
    89  	buffer.Extend(Overhead)
    90  	return w.writer.WriteBuffer(buffer)
    91  }
    92  
    93  func (w *Writer) TakeNonce() []byte {
    94  	return w.nonce
    95  }
    96  
    97  func (w *Writer) Upstream() any {
    98  	return w.writer
    99  }
   100  
   101  type WriterInterface struct{}
   102  
   103  func (w *WriterInterface) FrontHeadroom() int {
   104  	return PacketLengthBufferSize + Overhead
   105  }
   106  
   107  func (w *WriterInterface) RearHeadroom() int {
   108  	return Overhead
   109  }