| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249 |
- package tuic
- import (
- "encoding/json"
- "fmt"
- "net"
- "slices"
- "strconv"
- "strings"
- "github.com/mhsanaei/3x-ui/v3/internal/database/model"
- )
- type TuicServerSettings struct {
- Certificate string `json:"certificate"`
- PrivateKey string `json:"private_key"`
- CongestionControl string `json:"congestion_control"`
- ALPN []string `json:"alpn"`
- UDPRelayMode string `json:"udp_relay_mode"`
- ZeroRTTHandshake bool `json:"zero_rtt_handshake"`
- LogLevel string `json:"log_level"`
- MaxIdleTime int `json:"max_idle_time"`
- AuthenticationTimeout int `json:"authentication_timeout"`
- MaxUdpRelayPacketSize int `json:"max_udp_relay_packet_size"`
- SNI string `json:"sni,omitempty"`
- }
- type TuicClientSettings struct {
- UUID string `json:"uuid"`
- Password string `json:"password"`
- Email string `json:"email"`
- }
- type Instance struct {
- Id int
- Tag string
- Listen string
- Port int
- Certificate string
- PrivateKey string
- CongestionControl string
- ALPN []string
- UDPRelayMode string
- ZeroRTTHandshake bool
- LogLevel string
- MaxIdleTime int
- AuthenticationTimeout int
- MaxUdpRelayPacketSize int
- SNI string
- Clients []TuicClientSettings
- }
- func (inst Instance) BindTo() string {
- listen := inst.Listen
- if listen == "" {
- listen = "0.0.0.0"
- }
- return net.JoinHostPort(listen, strconv.Itoa(inst.Port))
- }
- func (inst Instance) StructuralFingerprint() string {
- parts := []string{
- inst.BindTo(),
- inst.Certificate,
- inst.PrivateKey,
- inst.CongestionControl,
- strings.Join(inst.ALPN, ","),
- inst.UDPRelayMode,
- strconv.FormatBool(inst.ZeroRTTHandshake),
- inst.LogLevel,
- strconv.Itoa(inst.MaxIdleTime),
- strconv.Itoa(inst.AuthenticationTimeout),
- strconv.Itoa(inst.MaxUdpRelayPacketSize),
- inst.SNI,
- }
- return strings.Join(parts, "|")
- }
- func (inst Instance) UsersFingerprint() string {
- pairs := make([]string, 0, len(inst.Clients))
- for _, c := range inst.Clients {
- pairs = append(pairs, fmt.Sprintf("%s=%s:%s", c.Email, c.UUID, c.Password))
- }
- slices.Sort(pairs)
- return strings.Join(pairs, "|")
- }
- func (inst Instance) FullFingerprint() string {
- return inst.StructuralFingerprint() + "#" + inst.UsersFingerprint()
- }
- func InstanceFromInbound(ib *model.Inbound) (Instance, bool) {
- if ib == nil || ib.Protocol != model.TUIC {
- return Instance{}, false
- }
- var parsed struct {
- Certificate string `json:"certificate"`
- PrivateKey string `json:"private_key"`
- CongestionControl string `json:"congestion_control"`
- ALPN []string `json:"alpn"`
- UDPRelayMode string `json:"udp_relay_mode"`
- ZeroRTTHandshake *bool `json:"zero_rtt_handshake"`
- LogLevel string `json:"log_level"`
- MaxIdleTime int `json:"max_idle_time"`
- AuthenticationTimeout int `json:"authentication_timeout"`
- MaxUdpRelayPacketSize int `json:"max_udp_relay_packet_size"`
- SNI string `json:"sni"`
- Server *struct {
- Certificate string `json:"certificate"`
- PrivateKey string `json:"private_key"`
- CongestionControl string `json:"congestion_control"`
- ALPN []string `json:"alpn"`
- UDPRelayMode string `json:"udp_relay_mode"`
- ZeroRTTHandshake *bool `json:"zero_rtt_handshake"`
- LogLevel string `json:"log_level"`
- MaxIdleTime int `json:"max_idle_time"`
- AuthenticationTimeout int `json:"authentication_timeout"`
- MaxUdpRelayPacketSize int `json:"max_udp_relay_packet_size"`
- SNI string `json:"sni"`
- } `json:"server"`
- Clients []struct {
- UUID string `json:"uuid"`
- ID string `json:"id"`
- Password string `json:"password"`
- Email string `json:"email"`
- Enable *bool `json:"enable"`
- TotalGB int64 `json:"totalGB"`
- ExpiryTime int64 `json:"expiryTime"`
- } `json:"clients"`
- }
- if ib.Settings != "" {
- if err := json.Unmarshal([]byte(ib.Settings), &parsed); err != nil {
- return Instance{}, false
- }
- }
- cert := parsed.Certificate
- key := parsed.PrivateKey
- cc := parsed.CongestionControl
- alpn := parsed.ALPN
- udpRelayMode := parsed.UDPRelayMode
- zeroRtt := true
- if parsed.ZeroRTTHandshake != nil {
- zeroRtt = *parsed.ZeroRTTHandshake
- }
- logLevel := parsed.LogLevel
- maxIdle := parsed.MaxIdleTime
- authTimeout := parsed.AuthenticationTimeout
- maxPacketSize := parsed.MaxUdpRelayPacketSize
- sni := parsed.SNI
- if parsed.Server != nil {
- if parsed.Server.Certificate != "" {
- cert = parsed.Server.Certificate
- }
- if parsed.Server.PrivateKey != "" {
- key = parsed.Server.PrivateKey
- }
- if parsed.Server.CongestionControl != "" {
- cc = parsed.Server.CongestionControl
- }
- if len(parsed.Server.ALPN) > 0 {
- alpn = parsed.Server.ALPN
- }
- if parsed.Server.UDPRelayMode != "" {
- udpRelayMode = parsed.Server.UDPRelayMode
- }
- if parsed.Server.ZeroRTTHandshake != nil {
- zeroRtt = *parsed.Server.ZeroRTTHandshake
- }
- if parsed.Server.LogLevel != "" {
- logLevel = parsed.Server.LogLevel
- }
- if parsed.Server.MaxIdleTime > 0 {
- maxIdle = parsed.Server.MaxIdleTime
- }
- if parsed.Server.AuthenticationTimeout > 0 {
- authTimeout = parsed.Server.AuthenticationTimeout
- }
- if parsed.Server.MaxUdpRelayPacketSize > 0 {
- maxPacketSize = parsed.Server.MaxUdpRelayPacketSize
- }
- if parsed.Server.SNI != "" {
- sni = parsed.Server.SNI
- }
- }
- if cc == "" {
- cc = "bbr"
- }
- if len(alpn) == 0 {
- alpn = []string{"h3", "spdy/3.1"}
- }
- if udpRelayMode == "" {
- udpRelayMode = "native"
- }
- if logLevel == "" {
- logLevel = "info"
- }
- if maxIdle <= 0 {
- maxIdle = 15
- }
- if authTimeout <= 0 {
- authTimeout = 3
- }
- if maxPacketSize <= 0 {
- maxPacketSize = 1500
- }
- clients := make([]TuicClientSettings, 0, len(parsed.Clients))
- for _, c := range parsed.Clients {
- if c.Enable != nil && !*c.Enable {
- continue
- }
- uuidVal := strings.ToLower(strings.TrimSpace(c.UUID))
- if uuidVal == "" {
- uuidVal = strings.ToLower(strings.TrimSpace(c.ID))
- }
- if uuidVal == "" || c.Password == "" {
- continue
- }
- clients = append(clients, TuicClientSettings{
- UUID: uuidVal,
- Password: c.Password,
- Email: c.Email,
- })
- }
- return Instance{
- Id: ib.Id,
- Tag: ib.Tag,
- Listen: ib.Listen,
- Port: ib.Port,
- Certificate: cert,
- PrivateKey: key,
- CongestionControl: cc,
- ALPN: alpn,
- UDPRelayMode: udpRelayMode,
- ZeroRTTHandshake: zeroRtt,
- LogLevel: logLevel,
- MaxIdleTime: maxIdle,
- AuthenticationTimeout: authTimeout,
- MaxUdpRelayPacketSize: maxPacketSize,
- SNI: sni,
- Clients: clients,
- }, true
- }
|