mirror of
https://github.com/daeuniverse/dae.git
synced 2025-02-06 17:19:08 +07:00
153 lines
3.9 KiB
Go
153 lines
3.9 KiB
Go
package tuic
|
|
|
|
import (
|
|
"crypto/tls"
|
|
"fmt"
|
|
"net"
|
|
"net/url"
|
|
"strconv"
|
|
"strings"
|
|
|
|
"github.com/daeuniverse/dae/common"
|
|
"github.com/daeuniverse/dae/component/outbound/dialer"
|
|
"github.com/mzz2017/softwind/protocol"
|
|
"github.com/mzz2017/softwind/protocol/direct"
|
|
)
|
|
|
|
func init() {
|
|
dialer.FromLinkRegister("tuic", NewTuic)
|
|
}
|
|
|
|
type Tuic struct {
|
|
Name string
|
|
Server string
|
|
Port int
|
|
User string
|
|
Password string
|
|
Sni string
|
|
AllowInsecure bool
|
|
DisableSni bool
|
|
CongestionControl string
|
|
Alpn []string
|
|
Protocol string
|
|
UdpRelayMode string
|
|
}
|
|
|
|
func NewTuic(option *dialer.GlobalOption, iOption dialer.InstanceOption, link string) (*dialer.Dialer, error) {
|
|
s, err := ParseTuicURL(link, option)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return s.Dialer(option, iOption)
|
|
}
|
|
|
|
func (s *Tuic) Dialer(option *dialer.GlobalOption, iOption dialer.InstanceOption) (*dialer.Dialer, error) {
|
|
d := direct.FullconeDirect // Tuic Proxy supports full-cone.
|
|
var err error
|
|
var flags protocol.Flags
|
|
if s.UdpRelayMode == "quic" {
|
|
flags |= protocol.Flags_Tuic_UdpRelayModeQuic
|
|
}
|
|
if d, err = protocol.NewDialer("tuic", d, protocol.Header{
|
|
ProxyAddress: net.JoinHostPort(s.Server, strconv.Itoa(s.Port)),
|
|
Feature1: s.CongestionControl,
|
|
TlsConfig: &tls.Config{NextProtos: s.Alpn, MinVersion: tls.VersionTLS13, ServerName: s.Sni, InsecureSkipVerify: s.AllowInsecure},
|
|
User: s.User,
|
|
Password: s.Password,
|
|
IsClient: true,
|
|
Flags: flags,
|
|
}); err != nil {
|
|
return nil, err
|
|
}
|
|
return dialer.NewDialer(d, option, iOption, dialer.Property{
|
|
Name: s.Name,
|
|
Address: net.JoinHostPort(s.Server, strconv.Itoa(s.Port)),
|
|
Protocol: s.Protocol,
|
|
Link: s.ExportToURL(),
|
|
}), nil
|
|
}
|
|
|
|
func ParseTuicURL(u string, option *dialer.GlobalOption) (data *Tuic, err error) {
|
|
//trojan://password@server:port#escape(remarks)
|
|
t, err := url.Parse(u)
|
|
if err != nil {
|
|
err = fmt.Errorf("invalid trojan format")
|
|
return
|
|
}
|
|
var alpn []string
|
|
if t.Query().Has("alpn") {
|
|
alpn = strings.Split(t.Query().Get("alpn"), ",")
|
|
for i := range alpn {
|
|
alpn[i] = strings.TrimSpace(alpn[i])
|
|
}
|
|
}
|
|
allowInsecure, _ := strconv.ParseBool(t.Query().Get("allowInsecure"))
|
|
if !allowInsecure {
|
|
allowInsecure, _ = strconv.ParseBool(t.Query().Get("allow_insecure"))
|
|
}
|
|
if !allowInsecure && option.AllowInsecure {
|
|
allowInsecure = true
|
|
}
|
|
sni := t.Query().Get("peer")
|
|
if sni == "" {
|
|
sni = t.Query().Get("sni")
|
|
}
|
|
if sni == "" {
|
|
sni = t.Hostname()
|
|
}
|
|
disableSni, _ := strconv.ParseBool(t.Query().Get("disable_sni"))
|
|
if disableSni {
|
|
sni = ""
|
|
allowInsecure = true
|
|
}
|
|
port, err := strconv.Atoi(t.Port())
|
|
if err != nil {
|
|
return nil, dialer.InvalidParameterErr
|
|
}
|
|
password, _ := t.User.Password()
|
|
data = &Tuic{
|
|
Name: t.Fragment,
|
|
Server: t.Hostname(),
|
|
Port: port,
|
|
User: t.User.Username(),
|
|
Password: password,
|
|
Sni: sni,
|
|
AllowInsecure: allowInsecure,
|
|
DisableSni: disableSni,
|
|
CongestionControl: t.Query().Get("congestion_control"),
|
|
Alpn: alpn,
|
|
UdpRelayMode: strings.ToLower(t.Query().Get("udp_relay_mode")),
|
|
Protocol: "tuic",
|
|
}
|
|
return data, nil
|
|
}
|
|
|
|
func (t *Tuic) ExportToURL() string {
|
|
u := &url.URL{
|
|
Scheme: "trojan",
|
|
User: url.UserPassword(t.User, t.Password),
|
|
Host: net.JoinHostPort(t.Server, strconv.Itoa(t.Port)),
|
|
Fragment: t.Name,
|
|
}
|
|
q := u.Query()
|
|
if t.AllowInsecure {
|
|
q.Set("allow_insecure", "1")
|
|
}
|
|
common.SetValue(&q, "sni", t.Sni)
|
|
if t.DisableSni {
|
|
common.SetValue(&q, "disable_sni", "1")
|
|
}
|
|
if t.CongestionControl != "" {
|
|
common.SetValue(&q, "congestion_control", t.CongestionControl)
|
|
}
|
|
if len(t.Alpn) > 0 {
|
|
common.SetValue(&q, "alpn", strings.Join(t.Alpn, ","))
|
|
}
|
|
if t.UdpRelayMode != "" {
|
|
common.SetValue(&q, "udp_relay_mode", t.UdpRelayMode)
|
|
}
|
|
|
|
u.RawQuery = q.Encode()
|
|
return u.String()
|
|
}
|