mirror of
https://github.com/MetaCubeX/mihomo.git
synced 2024-12-25 11:54:25 +08:00
235 lines
4.9 KiB
Go
235 lines
4.9 KiB
Go
package tuic
|
|
|
|
import (
|
|
"bufio"
|
|
"context"
|
|
"crypto/tls"
|
|
"net"
|
|
"time"
|
|
|
|
"github.com/Dreamacro/clash/adapter/inbound"
|
|
N "github.com/Dreamacro/clash/common/net"
|
|
"github.com/Dreamacro/clash/common/utils"
|
|
C "github.com/Dreamacro/clash/constant"
|
|
"github.com/Dreamacro/clash/transport/socks5"
|
|
"github.com/Dreamacro/clash/transport/tuic/common"
|
|
v4 "github.com/Dreamacro/clash/transport/tuic/v4"
|
|
v5 "github.com/Dreamacro/clash/transport/tuic/v5"
|
|
|
|
"github.com/gofrs/uuid/v5"
|
|
"github.com/metacubex/quic-go"
|
|
)
|
|
|
|
type ServerOption struct {
|
|
HandleTcpFn func(conn net.Conn, addr socks5.Addr, additions ...inbound.Addition) error
|
|
HandleUdpFn func(addr socks5.Addr, packet C.UDPPacket, additions ...inbound.Addition) error
|
|
|
|
TlsConfig *tls.Config
|
|
QuicConfig *quic.Config
|
|
Tokens [][32]byte // V4 special
|
|
Users map[[16]byte]string // V5 special
|
|
CongestionController string
|
|
AuthenticationTimeout time.Duration
|
|
MaxUdpRelayPacketSize int
|
|
CWND int
|
|
}
|
|
|
|
type Server struct {
|
|
*ServerOption
|
|
optionV4 *v4.ServerOption
|
|
optionV5 *v5.ServerOption
|
|
listener *quic.EarlyListener
|
|
}
|
|
|
|
func (s *Server) Serve() error {
|
|
for {
|
|
conn, err := s.listener.Accept(context.Background())
|
|
if err != nil {
|
|
return err
|
|
}
|
|
common.SetCongestionController(conn, s.CongestionController, s.CWND)
|
|
h := &serverHandler{
|
|
Server: s,
|
|
quicConn: conn,
|
|
uuid: utils.NewUUIDV4(),
|
|
}
|
|
if h.optionV4 != nil {
|
|
h.v4Handler = v4.NewServerHandler(h.optionV4, conn, h.uuid)
|
|
}
|
|
if h.optionV5 != nil {
|
|
h.v5Handler = v5.NewServerHandler(h.optionV5, conn, h.uuid)
|
|
}
|
|
go h.handle()
|
|
}
|
|
}
|
|
|
|
func (s *Server) Close() error {
|
|
return s.listener.Close()
|
|
}
|
|
|
|
type serverHandler struct {
|
|
*Server
|
|
quicConn quic.EarlyConnection
|
|
uuid uuid.UUID
|
|
|
|
v4Handler common.ServerHandler
|
|
v5Handler common.ServerHandler
|
|
}
|
|
|
|
func (s *serverHandler) handle() {
|
|
go func() {
|
|
_ = s.handleUniStream()
|
|
}()
|
|
go func() {
|
|
_ = s.handleStream()
|
|
}()
|
|
go func() {
|
|
_ = s.handleMessage()
|
|
}()
|
|
|
|
<-s.quicConn.HandshakeComplete()
|
|
time.AfterFunc(s.AuthenticationTimeout, func() {
|
|
if s.v4Handler != nil {
|
|
if s.v4Handler.AuthOk() {
|
|
return
|
|
}
|
|
}
|
|
|
|
if s.v5Handler != nil {
|
|
if s.v5Handler.AuthOk() {
|
|
return
|
|
}
|
|
}
|
|
|
|
if s.v4Handler != nil {
|
|
s.v4Handler.HandleTimeout()
|
|
}
|
|
|
|
if s.v5Handler != nil {
|
|
s.v5Handler.HandleTimeout()
|
|
}
|
|
})
|
|
}
|
|
|
|
func (s *serverHandler) handleMessage() (err error) {
|
|
for {
|
|
var message []byte
|
|
message, err = s.quicConn.ReceiveMessage(context.Background())
|
|
if err != nil {
|
|
return err
|
|
}
|
|
go func() (err error) {
|
|
if len(message) > 0 {
|
|
switch message[0] {
|
|
case v4.VER:
|
|
if s.v4Handler != nil {
|
|
return s.v4Handler.HandleMessage(message)
|
|
}
|
|
case v5.VER:
|
|
if s.v5Handler != nil {
|
|
return s.v5Handler.HandleMessage(message)
|
|
}
|
|
}
|
|
}
|
|
return
|
|
}()
|
|
}
|
|
}
|
|
|
|
func (s *serverHandler) handleStream() (err error) {
|
|
for {
|
|
var quicStream quic.Stream
|
|
quicStream, err = s.quicConn.AcceptStream(context.Background())
|
|
if err != nil {
|
|
return err
|
|
}
|
|
go func() (err error) {
|
|
stream := common.NewQuicStreamConn(
|
|
quicStream,
|
|
s.quicConn.LocalAddr(),
|
|
s.quicConn.RemoteAddr(),
|
|
nil,
|
|
)
|
|
conn := N.NewBufferedConn(stream)
|
|
|
|
verBytes, err := conn.Peek(1)
|
|
if err != nil {
|
|
_ = conn.Close()
|
|
return err
|
|
}
|
|
|
|
switch verBytes[0] {
|
|
case v4.VER:
|
|
if s.v4Handler != nil {
|
|
return s.v4Handler.HandleStream(conn)
|
|
}
|
|
case v5.VER:
|
|
if s.v5Handler != nil {
|
|
return s.v5Handler.HandleStream(conn)
|
|
}
|
|
}
|
|
return
|
|
}()
|
|
}
|
|
}
|
|
|
|
func (s *serverHandler) handleUniStream() (err error) {
|
|
for {
|
|
var stream quic.ReceiveStream
|
|
stream, err = s.quicConn.AcceptUniStream(context.Background())
|
|
if err != nil {
|
|
return err
|
|
}
|
|
go func() (err error) {
|
|
defer func() {
|
|
stream.CancelRead(0)
|
|
}()
|
|
reader := bufio.NewReader(stream)
|
|
verBytes, err := reader.Peek(1)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
switch verBytes[0] {
|
|
case v4.VER:
|
|
if s.v4Handler != nil {
|
|
return s.v4Handler.HandleUniStream(reader)
|
|
}
|
|
case v5.VER:
|
|
if s.v5Handler != nil {
|
|
return s.v5Handler.HandleUniStream(reader)
|
|
}
|
|
}
|
|
return
|
|
}()
|
|
}
|
|
}
|
|
|
|
func NewServer(option *ServerOption, pc net.PacketConn) (*Server, error) {
|
|
listener, err := quic.ListenEarly(pc, option.TlsConfig, option.QuicConfig)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
server := &Server{
|
|
ServerOption: option,
|
|
listener: listener,
|
|
}
|
|
if len(option.Tokens) > 0 {
|
|
server.optionV4 = &v4.ServerOption{
|
|
HandleTcpFn: option.HandleTcpFn,
|
|
HandleUdpFn: option.HandleUdpFn,
|
|
Tokens: option.Tokens,
|
|
MaxUdpRelayPacketSize: option.MaxUdpRelayPacketSize,
|
|
}
|
|
}
|
|
if len(option.Users) > 0 {
|
|
server.optionV5 = &v5.ServerOption{
|
|
HandleTcpFn: option.HandleTcpFn,
|
|
HandleUdpFn: option.HandleUdpFn,
|
|
Users: option.Users,
|
|
MaxUdpRelayPacketSize: option.MaxUdpRelayPacketSize,
|
|
}
|
|
}
|
|
return server, nil
|
|
}
|