mirror of
https://github.com/SagerNet/sing-box.git
synced 2024-11-29 12:01:29 +00:00
111 lines
2.4 KiB
Go
111 lines
2.4 KiB
Go
package tuic
|
|
|
|
import (
|
|
"io"
|
|
|
|
"github.com/sagernet/quic-go"
|
|
"github.com/sagernet/sing/common/buf"
|
|
"github.com/sagernet/sing/common/bufio"
|
|
E "github.com/sagernet/sing/common/exceptions"
|
|
)
|
|
|
|
func (c *Client) loopMessages(conn *clientQUICConnection) {
|
|
for {
|
|
message, err := conn.quicConn.ReceiveMessage(c.ctx)
|
|
if err != nil {
|
|
conn.closeWithError(E.Cause(err, "receive message"))
|
|
return
|
|
}
|
|
go func() {
|
|
hErr := c.handleMessage(conn, message)
|
|
if hErr != nil {
|
|
conn.closeWithError(E.Cause(hErr, "handle message"))
|
|
}
|
|
}()
|
|
}
|
|
}
|
|
|
|
func (c *Client) handleMessage(conn *clientQUICConnection, data []byte) error {
|
|
if len(data) < 2 {
|
|
return E.New("invalid message")
|
|
}
|
|
if data[0] != Version {
|
|
return E.New("unknown version ", data[0])
|
|
}
|
|
switch data[1] {
|
|
case CommandPacket:
|
|
message := allocMessage()
|
|
err := decodeUDPMessage(message, data[2:])
|
|
if err != nil {
|
|
message.release()
|
|
return E.Cause(err, "decode UDP message")
|
|
}
|
|
conn.handleUDPMessage(message)
|
|
return nil
|
|
case CommandHeartbeat:
|
|
return nil
|
|
default:
|
|
return E.New("unknown command ", data[0])
|
|
}
|
|
}
|
|
|
|
func (c *Client) loopUniStreams(conn *clientQUICConnection) {
|
|
for {
|
|
stream, err := conn.quicConn.AcceptUniStream(c.ctx)
|
|
if err != nil {
|
|
conn.closeWithError(E.Cause(err, "handle uni stream"))
|
|
return
|
|
}
|
|
go func() {
|
|
hErr := c.handleUniStream(conn, stream)
|
|
if hErr != nil {
|
|
conn.closeWithError(hErr)
|
|
}
|
|
}()
|
|
}
|
|
}
|
|
|
|
func (c *Client) handleUniStream(conn *clientQUICConnection, stream quic.ReceiveStream) error {
|
|
defer stream.CancelRead(0)
|
|
buffer := buf.NewPacket()
|
|
defer buffer.Release()
|
|
_, err := buffer.ReadAtLeastFrom(stream, 2)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
version, _ := buffer.ReadByte()
|
|
if version != Version {
|
|
return E.New("unknown version ", version)
|
|
}
|
|
command, _ := buffer.ReadByte()
|
|
if command != CommandPacket {
|
|
return E.New("unknown command ", command)
|
|
}
|
|
reader := io.MultiReader(bufio.NewCachedReader(stream, buffer), stream)
|
|
message := allocMessage()
|
|
err = readUDPMessage(message, reader)
|
|
if err != nil {
|
|
message.release()
|
|
return err
|
|
}
|
|
conn.handleUDPMessage(message)
|
|
return nil
|
|
}
|
|
|
|
func (c *clientQUICConnection) handleUDPMessage(message *udpMessage) {
|
|
c.udpAccess.RLock()
|
|
udpConn, loaded := c.udpConnMap[message.sessionID]
|
|
c.udpAccess.RUnlock()
|
|
if !loaded {
|
|
message.releaseMessage()
|
|
return
|
|
}
|
|
select {
|
|
case <-udpConn.ctx.Done():
|
|
message.releaseMessage()
|
|
return
|
|
default:
|
|
}
|
|
udpConn.inputPacket(message)
|
|
}
|