sing-quic/tuic/service_packet.go
2025-01-07 12:37:17 +08:00

72 lines
1.8 KiB
Go

package tuic
import (
"github.com/sagernet/sing/common"
"github.com/sagernet/sing/common/auth"
"github.com/sagernet/sing/common/canceler"
E "github.com/sagernet/sing/common/exceptions"
)
func (s *serverSession[U]) loopMessages() {
select {
case <-s.connDone:
return
case <-s.authDone:
}
for {
message, err := s.quicConn.ReceiveDatagram(s.ctx)
if err != nil {
s.closeWithError(E.Cause(err, "receive message"))
return
}
hErr := s.handleMessage(message)
if hErr != nil {
s.closeWithError(E.Cause(hErr, "handle message"))
return
}
}
}
func (s *serverSession[U]) handleMessage(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")
}
s.handleUDPMessage(message, false)
return nil
case CommandHeartbeat:
return nil
default:
return E.New("unknown command ", data[0])
}
}
func (s *serverSession[U]) handleUDPMessage(message *udpMessage, udpStream bool) {
s.udpAccess.RLock()
udpConn, loaded := s.udpConnMap[message.sessionID]
s.udpAccess.RUnlock()
if !loaded || common.Done(udpConn.ctx) {
udpConn = newUDPPacketConn(auth.ContextWithUser(s.ctx, s.authUser), s.quicConn, udpStream, true, func() {
s.udpAccess.Lock()
delete(s.udpConnMap, message.sessionID)
s.udpAccess.Unlock()
})
udpConn.sessionID = message.sessionID
s.udpAccess.Lock()
s.udpConnMap[message.sessionID] = udpConn
s.udpAccess.Unlock()
newCtx, newConn := canceler.NewPacketConn(udpConn.ctx, udpConn, s.udpTimeout)
go s.handler.NewPacketConnectionEx(newCtx, newConn, s.source, message.destination, nil)
}
udpConn.inputPacket(message)
}