mirror of
https://github.com/SagerNet/sing-box.git
synced 2025-06-13 21:54:13 +08:00
Compare commits
11 Commits
dev-next
...
v1.6.0-bet
Author | SHA1 | Date | |
---|---|---|---|
![]() |
a808fee9ef | ||
![]() |
2ad54b0aab | ||
![]() |
533225833b | ||
![]() |
34691c9fea | ||
![]() |
209e68b783 | ||
![]() |
97e3792bbe | ||
![]() |
30492d1c90 | ||
![]() |
071a352baf | ||
![]() |
b393366f0d | ||
![]() |
2ac21bd93f | ||
![]() |
efecd67e29 |
@ -11,7 +11,6 @@ import (
|
|||||||
|
|
||||||
"github.com/gofrs/uuid/v5"
|
"github.com/gofrs/uuid/v5"
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
"golang.zx2c4.com/wireguard/wgctrl/wgtypes"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
var commandGenerate = &cobra.Command{
|
var commandGenerate = &cobra.Command{
|
||||||
@ -22,8 +21,7 @@ var commandGenerate = &cobra.Command{
|
|||||||
func init() {
|
func init() {
|
||||||
commandGenerate.AddCommand(commandGenerateUUID)
|
commandGenerate.AddCommand(commandGenerateUUID)
|
||||||
commandGenerate.AddCommand(commandGenerateRandom)
|
commandGenerate.AddCommand(commandGenerateRandom)
|
||||||
commandGenerate.AddCommand(commandGenerateWireGuardKeyPair)
|
|
||||||
commandGenerate.AddCommand(commandGenerateRealityKeyPair)
|
|
||||||
mainCommand.AddCommand(commandGenerate)
|
mainCommand.AddCommand(commandGenerate)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -92,48 +90,3 @@ func generateUUID() error {
|
|||||||
_, err = os.Stdout.WriteString(newUUID.String() + "\n")
|
_, err = os.Stdout.WriteString(newUUID.String() + "\n")
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
var commandGenerateWireGuardKeyPair = &cobra.Command{
|
|
||||||
Use: "wg-keypair",
|
|
||||||
Short: "Generate WireGuard key pair",
|
|
||||||
Args: cobra.NoArgs,
|
|
||||||
Run: func(cmd *cobra.Command, args []string) {
|
|
||||||
err := generateWireGuardKey()
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal(err)
|
|
||||||
}
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
func generateWireGuardKey() error {
|
|
||||||
privateKey, err := wgtypes.GeneratePrivateKey()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
os.Stdout.WriteString("PrivateKey: " + privateKey.String() + "\n")
|
|
||||||
os.Stdout.WriteString("PublicKey: " + privateKey.PublicKey().String() + "\n")
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
var commandGenerateRealityKeyPair = &cobra.Command{
|
|
||||||
Use: "reality-keypair",
|
|
||||||
Short: "Generate reality key pair",
|
|
||||||
Args: cobra.NoArgs,
|
|
||||||
Run: func(cmd *cobra.Command, args []string) {
|
|
||||||
err := generateRealityKey()
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal(err)
|
|
||||||
}
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
func generateRealityKey() error {
|
|
||||||
privateKey, err := wgtypes.GeneratePrivateKey()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
publicKey := privateKey.PublicKey()
|
|
||||||
os.Stdout.WriteString("PrivateKey: " + base64.RawURLEncoding.EncodeToString(privateKey[:]) + "\n")
|
|
||||||
os.Stdout.WriteString("PublicKey: " + base64.RawURLEncoding.EncodeToString(publicKey[:]) + "\n")
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
40
cmd/sing-box/cmd_generate_tls.go
Normal file
40
cmd/sing-box/cmd_generate_tls.go
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/sagernet/sing-box/common/tls"
|
||||||
|
"github.com/sagernet/sing-box/log"
|
||||||
|
|
||||||
|
"github.com/spf13/cobra"
|
||||||
|
)
|
||||||
|
|
||||||
|
var flagGenerateTLSKeyPairMonths int
|
||||||
|
|
||||||
|
var commandGenerateTLSKeyPair = &cobra.Command{
|
||||||
|
Use: "tls-keypair <server_name>",
|
||||||
|
Short: "Generate TLS self sign key pair",
|
||||||
|
Args: cobra.ExactArgs(1),
|
||||||
|
Run: func(cmd *cobra.Command, args []string) {
|
||||||
|
err := generateTLSKeyPair(args[0])
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
commandGenerateTLSKeyPair.Flags().IntVarP(&flagGenerateTLSKeyPairMonths, "months", "m", 1, "Valid months")
|
||||||
|
commandGenerate.AddCommand(commandGenerateTLSKeyPair)
|
||||||
|
}
|
||||||
|
|
||||||
|
func generateTLSKeyPair(serverName string) error {
|
||||||
|
privateKeyPem, publicKeyPem, err := tls.GenerateKeyPair(time.Now, serverName, time.Now().AddDate(0, flagGenerateTLSKeyPairMonths, 0))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
os.Stdout.WriteString(string(privateKeyPem) + "\n")
|
||||||
|
os.Stdout.WriteString(string(publicKeyPem) + "\n")
|
||||||
|
return nil
|
||||||
|
}
|
40
cmd/sing-box/cmd_generate_vapid.go
Normal file
40
cmd/sing-box/cmd_generate_vapid.go
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
//go:build go1.20
|
||||||
|
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/ecdh"
|
||||||
|
"crypto/rand"
|
||||||
|
"encoding/base64"
|
||||||
|
"os"
|
||||||
|
|
||||||
|
"github.com/sagernet/sing-box/log"
|
||||||
|
|
||||||
|
"github.com/spf13/cobra"
|
||||||
|
)
|
||||||
|
|
||||||
|
var commandGenerateVAPIDKeyPair = &cobra.Command{
|
||||||
|
Use: "vapid-keypair",
|
||||||
|
Short: "Generate VAPID key pair",
|
||||||
|
Run: func(cmd *cobra.Command, args []string) {
|
||||||
|
err := generateVAPIDKeyPair()
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
commandGenerate.AddCommand(commandGenerateVAPIDKeyPair)
|
||||||
|
}
|
||||||
|
|
||||||
|
func generateVAPIDKeyPair() error {
|
||||||
|
privateKey, err := ecdh.P256().GenerateKey(rand.Reader)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
publicKey := privateKey.PublicKey()
|
||||||
|
os.Stdout.WriteString("PrivateKey: " + base64.RawURLEncoding.EncodeToString(privateKey.Bytes()) + "\n")
|
||||||
|
os.Stdout.WriteString("PublicKey: " + base64.RawURLEncoding.EncodeToString(publicKey.Bytes()) + "\n")
|
||||||
|
return nil
|
||||||
|
}
|
61
cmd/sing-box/cmd_generate_wireguard.go
Normal file
61
cmd/sing-box/cmd_generate_wireguard.go
Normal file
@ -0,0 +1,61 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/base64"
|
||||||
|
"os"
|
||||||
|
|
||||||
|
"github.com/sagernet/sing-box/log"
|
||||||
|
|
||||||
|
"github.com/spf13/cobra"
|
||||||
|
"golang.zx2c4.com/wireguard/wgctrl/wgtypes"
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
commandGenerate.AddCommand(commandGenerateWireGuardKeyPair)
|
||||||
|
commandGenerate.AddCommand(commandGenerateRealityKeyPair)
|
||||||
|
}
|
||||||
|
|
||||||
|
var commandGenerateWireGuardKeyPair = &cobra.Command{
|
||||||
|
Use: "wg-keypair",
|
||||||
|
Short: "Generate WireGuard key pair",
|
||||||
|
Args: cobra.NoArgs,
|
||||||
|
Run: func(cmd *cobra.Command, args []string) {
|
||||||
|
err := generateWireGuardKey()
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
func generateWireGuardKey() error {
|
||||||
|
privateKey, err := wgtypes.GeneratePrivateKey()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
os.Stdout.WriteString("PrivateKey: " + privateKey.String() + "\n")
|
||||||
|
os.Stdout.WriteString("PublicKey: " + privateKey.PublicKey().String() + "\n")
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
var commandGenerateRealityKeyPair = &cobra.Command{
|
||||||
|
Use: "reality-keypair",
|
||||||
|
Short: "Generate reality key pair",
|
||||||
|
Args: cobra.NoArgs,
|
||||||
|
Run: func(cmd *cobra.Command, args []string) {
|
||||||
|
err := generateRealityKey()
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
func generateRealityKey() error {
|
||||||
|
privateKey, err := wgtypes.GeneratePrivateKey()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
publicKey := privateKey.PublicKey()
|
||||||
|
os.Stdout.WriteString("PrivateKey: " + base64.RawURLEncoding.EncodeToString(privateKey[:]) + "\n")
|
||||||
|
os.Stdout.WriteString("PublicKey: " + base64.RawURLEncoding.EncodeToString(publicKey[:]) + "\n")
|
||||||
|
return nil
|
||||||
|
}
|
@ -1,50 +0,0 @@
|
|||||||
package proxyproto
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"net"
|
|
||||||
"net/netip"
|
|
||||||
|
|
||||||
"github.com/sagernet/sing-box/adapter"
|
|
||||||
E "github.com/sagernet/sing/common/exceptions"
|
|
||||||
M "github.com/sagernet/sing/common/metadata"
|
|
||||||
N "github.com/sagernet/sing/common/network"
|
|
||||||
|
|
||||||
"github.com/pires/go-proxyproto"
|
|
||||||
)
|
|
||||||
|
|
||||||
var _ N.Dialer = (*Dialer)(nil)
|
|
||||||
|
|
||||||
type Dialer struct {
|
|
||||||
N.Dialer
|
|
||||||
}
|
|
||||||
|
|
||||||
func (d *Dialer) DialContext(ctx context.Context, network string, destination M.Socksaddr) (net.Conn, error) {
|
|
||||||
switch N.NetworkName(network) {
|
|
||||||
case N.NetworkTCP:
|
|
||||||
conn, err := d.Dialer.DialContext(ctx, network, destination)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
var source M.Socksaddr
|
|
||||||
metadata := adapter.ContextFrom(ctx)
|
|
||||||
if metadata != nil {
|
|
||||||
source = metadata.Source
|
|
||||||
}
|
|
||||||
if !source.IsValid() {
|
|
||||||
source = M.SocksaddrFromNet(conn.LocalAddr())
|
|
||||||
}
|
|
||||||
if destination.Addr.Is6() {
|
|
||||||
source = M.SocksaddrFrom(netip.AddrFrom16(source.Addr.As16()), source.Port)
|
|
||||||
}
|
|
||||||
h := proxyproto.HeaderProxyFromAddrs(1, source.TCPAddr(), destination.TCPAddr())
|
|
||||||
_, err = h.WriteTo(conn)
|
|
||||||
if err != nil {
|
|
||||||
conn.Close()
|
|
||||||
return nil, E.Cause(err, "write proxy protocol header")
|
|
||||||
}
|
|
||||||
return conn, nil
|
|
||||||
default:
|
|
||||||
return d.Dialer.DialContext(ctx, network, destination)
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,62 +0,0 @@
|
|||||||
package proxyproto
|
|
||||||
|
|
||||||
import (
|
|
||||||
std_bufio "bufio"
|
|
||||||
"net"
|
|
||||||
|
|
||||||
"github.com/sagernet/sing/common/buf"
|
|
||||||
"github.com/sagernet/sing/common/bufio"
|
|
||||||
M "github.com/sagernet/sing/common/metadata"
|
|
||||||
|
|
||||||
"github.com/pires/go-proxyproto"
|
|
||||||
)
|
|
||||||
|
|
||||||
type Listener struct {
|
|
||||||
net.Listener
|
|
||||||
AcceptNoHeader bool
|
|
||||||
}
|
|
||||||
|
|
||||||
func (l *Listener) Accept() (net.Conn, error) {
|
|
||||||
conn, err := l.Listener.Accept()
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
bufReader := std_bufio.NewReader(conn)
|
|
||||||
header, err := proxyproto.Read(bufReader)
|
|
||||||
if err != nil && !(l.AcceptNoHeader && err == proxyproto.ErrNoProxyProtocol) {
|
|
||||||
return nil, &Error{err}
|
|
||||||
}
|
|
||||||
if bufReader.Buffered() > 0 {
|
|
||||||
cache := buf.NewSize(bufReader.Buffered())
|
|
||||||
_, err = cache.ReadFullFrom(bufReader, cache.FreeLen())
|
|
||||||
if err != nil {
|
|
||||||
return nil, &Error{err}
|
|
||||||
}
|
|
||||||
conn = bufio.NewCachedConn(conn, cache)
|
|
||||||
}
|
|
||||||
if header != nil {
|
|
||||||
return &bufio.AddrConn{Conn: conn, Metadata: M.Metadata{
|
|
||||||
Source: M.SocksaddrFromNet(header.SourceAddr).Unwrap(),
|
|
||||||
Destination: M.SocksaddrFromNet(header.DestinationAddr).Unwrap(),
|
|
||||||
}}, nil
|
|
||||||
}
|
|
||||||
return conn, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
var _ net.Error = (*Error)(nil)
|
|
||||||
|
|
||||||
type Error struct {
|
|
||||||
error
|
|
||||||
}
|
|
||||||
|
|
||||||
func (e *Error) Unwrap() error {
|
|
||||||
return e.error
|
|
||||||
}
|
|
||||||
|
|
||||||
func (e *Error) Timeout() bool {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
func (e *Error) Temporary() bool {
|
|
||||||
return true
|
|
||||||
}
|
|
@ -11,22 +11,34 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
func GenerateKeyPair(timeFunc func() time.Time, serverName string) (*tls.Certificate, error) {
|
func GenerateCertificate(timeFunc func() time.Time, serverName string) (*tls.Certificate, error) {
|
||||||
|
privateKeyPem, publicKeyPem, err := GenerateKeyPair(timeFunc, serverName, timeFunc().Add(time.Hour))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
certificate, err := tls.X509KeyPair(publicKeyPem, privateKeyPem)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &certificate, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func GenerateKeyPair(timeFunc func() time.Time, serverName string, expire time.Time) (privateKeyPem []byte, publicKeyPem []byte, err error) {
|
||||||
if timeFunc == nil {
|
if timeFunc == nil {
|
||||||
timeFunc = time.Now
|
timeFunc = time.Now
|
||||||
}
|
}
|
||||||
key, err := rsa.GenerateKey(rand.Reader, 2048)
|
key, err := rsa.GenerateKey(rand.Reader, 2048)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return
|
||||||
}
|
}
|
||||||
serialNumber, err := rand.Int(rand.Reader, new(big.Int).Lsh(big.NewInt(1), 128))
|
serialNumber, err := rand.Int(rand.Reader, new(big.Int).Lsh(big.NewInt(1), 128))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return
|
||||||
}
|
}
|
||||||
template := &x509.Certificate{
|
template := &x509.Certificate{
|
||||||
SerialNumber: serialNumber,
|
SerialNumber: serialNumber,
|
||||||
NotBefore: timeFunc().Add(time.Hour * -1),
|
NotBefore: timeFunc().Add(time.Hour * -1),
|
||||||
NotAfter: timeFunc().Add(time.Hour),
|
NotAfter: expire,
|
||||||
KeyUsage: x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature,
|
KeyUsage: x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature,
|
||||||
ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth},
|
ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth},
|
||||||
BasicConstraintsValid: true,
|
BasicConstraintsValid: true,
|
||||||
@ -37,17 +49,13 @@ func GenerateKeyPair(timeFunc func() time.Time, serverName string) (*tls.Certifi
|
|||||||
}
|
}
|
||||||
publicDer, err := x509.CreateCertificate(rand.Reader, template, template, key.Public(), key)
|
publicDer, err := x509.CreateCertificate(rand.Reader, template, template, key.Public(), key)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return
|
||||||
}
|
}
|
||||||
privateDer, err := x509.MarshalPKCS8PrivateKey(key)
|
privateDer, err := x509.MarshalPKCS8PrivateKey(key)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return
|
||||||
}
|
}
|
||||||
publicPem := pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE", Bytes: publicDer})
|
publicKeyPem = pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE", Bytes: publicDer})
|
||||||
privPem := pem.EncodeToMemory(&pem.Block{Type: "PRIVATE KEY", Bytes: privateDer})
|
privateKeyPem = pem.EncodeToMemory(&pem.Block{Type: "PRIVATE KEY", Bytes: privateDer})
|
||||||
keyPair, err := tls.X509KeyPair(publicPem, privPem)
|
return
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return &keyPair, err
|
|
||||||
}
|
}
|
||||||
|
@ -233,7 +233,7 @@ func NewSTDServer(ctx context.Context, logger log.Logger, options option.Inbound
|
|||||||
}
|
}
|
||||||
if certificate == nil && key == nil && options.Insecure {
|
if certificate == nil && key == nil && options.Insecure {
|
||||||
tlsConfig.GetCertificate = func(info *tls.ClientHelloInfo) (*tls.Certificate, error) {
|
tlsConfig.GetCertificate = func(info *tls.ClientHelloInfo) (*tls.Certificate, error) {
|
||||||
return GenerateKeyPair(ntp.TimeFuncFromContext(ctx), info.ServerName)
|
return GenerateCertificate(ntp.TimeFuncFromContext(ctx), info.ServerName)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if certificate == nil {
|
if certificate == nil {
|
||||||
|
@ -1,22 +1,91 @@
|
|||||||
|
#### 1.6.0-beta.4
|
||||||
|
|
||||||
|
* Fix IPv6 `auto_route` for Linux **1**
|
||||||
|
* Fixes and improvements
|
||||||
|
|
||||||
|
**1**:
|
||||||
|
|
||||||
|
When `auto_route` is enabled and `strict_route` is disabled, the device can now be reached from external IPv6 addresses.
|
||||||
|
|
||||||
#### 1.5.4
|
#### 1.5.4
|
||||||
|
|
||||||
* Fix Clash cache crash on arm32 devices
|
* Fix Clash cache crash on arm32 devices
|
||||||
* Fixes and improvements
|
* Fixes and improvements
|
||||||
|
|
||||||
|
#### 1.6.0-beta.3
|
||||||
|
|
||||||
|
* Update the legacy Hysteria protocol **1**
|
||||||
|
* Fixes and improvements
|
||||||
|
|
||||||
|
**1**
|
||||||
|
|
||||||
|
Based on discussions with the original author, the brutal CC and QUIC protocol parameters of
|
||||||
|
the old protocol (Hysteria 1) have been updated to be consistent with Hysteria 2
|
||||||
|
|
||||||
|
#### 1.6.0-beta.2
|
||||||
|
|
||||||
|
* Add TLS self sign key pair generate command
|
||||||
|
* Update brutal congestion control for Hysteria2
|
||||||
|
* Fix Clash cache crash on arm32 devices
|
||||||
|
* Update golang.org/x/net to v0.17.0
|
||||||
|
* Fixes and improvements
|
||||||
|
|
||||||
#### 1.5.3
|
#### 1.5.3
|
||||||
|
|
||||||
* Fix compatibility with Android 14
|
* Fix compatibility with Android 14
|
||||||
* Fixes and improvements
|
* Fixes and improvements
|
||||||
|
|
||||||
|
#### 1.6.0-beta.1
|
||||||
|
|
||||||
|
* Fixes and improvements
|
||||||
|
|
||||||
|
#### 1.6.0-alpha.5
|
||||||
|
|
||||||
|
* Fix compatibility with Android 14
|
||||||
|
* Update BBR congestion control for TUIC and Hysteria2 **1**
|
||||||
|
* Fixes and improvements
|
||||||
|
|
||||||
|
**1**:
|
||||||
|
|
||||||
|
None of the existing Golang BBR congestion control implementations have been reviewed or unit tested.
|
||||||
|
This update is intended to fix a memory leak flaw in the new implementation introduced in 1.6.0-alpha.1 and may
|
||||||
|
introduce new issues.
|
||||||
|
|
||||||
|
#### 1.6.0-alpha.4
|
||||||
|
|
||||||
|
* Add `brutal_debug` option for Hysteria2
|
||||||
|
* Fixes and improvements
|
||||||
|
|
||||||
#### 1.5.2
|
#### 1.5.2
|
||||||
|
|
||||||
* Our [Apple tvOS client](/installation/clients/sft) is now available in the App Store 🍎
|
* Our [Apple tvOS client](/installation/clients/sft) is now available in the App Store 🍎
|
||||||
* Fixes and improvements
|
* Fixes and improvements
|
||||||
|
|
||||||
|
#### 1.6.0-alpha.3
|
||||||
|
|
||||||
|
* Fixes and improvements
|
||||||
|
|
||||||
|
#### 1.6.0-alpha.2
|
||||||
|
|
||||||
|
* Fixes and improvements
|
||||||
|
|
||||||
#### 1.5.1
|
#### 1.5.1
|
||||||
|
|
||||||
* Fixes and improvements
|
* Fixes and improvements
|
||||||
|
|
||||||
|
#### 1.6.0-alpha.1
|
||||||
|
|
||||||
|
* Update BBR congestion control for TUIC and Hysteria2 **1**
|
||||||
|
* Update quic-go to v0.39.0
|
||||||
|
* Update gVisor to 20230814.0
|
||||||
|
* Remove [Deprecated Features](/deprecated) by agreement
|
||||||
|
* Fixes and improvements
|
||||||
|
|
||||||
|
**1**:
|
||||||
|
|
||||||
|
None of the existing Golang BBR congestion control implementations have been reviewed or unit tested.
|
||||||
|
This update is intended to address the multi-send defects of the old implementation and may introduce new issues.
|
||||||
|
|
||||||
#### 1.5.0
|
#### 1.5.0
|
||||||
|
|
||||||
* Fixes and improvements
|
* Fixes and improvements
|
||||||
|
@ -20,8 +20,9 @@
|
|||||||
}
|
}
|
||||||
],
|
],
|
||||||
"ignore_client_bandwidth": false,
|
"ignore_client_bandwidth": false,
|
||||||
|
"tls": {},
|
||||||
"masquerade": "",
|
"masquerade": "",
|
||||||
"tls": {}
|
"brutal_debug": false
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -67,6 +68,12 @@ Commands the client to use the BBR flow control algorithm instead of Hysteria CC
|
|||||||
|
|
||||||
Conflict with `up_mbps` and `down_mbps`.
|
Conflict with `up_mbps` and `down_mbps`.
|
||||||
|
|
||||||
|
#### tls
|
||||||
|
|
||||||
|
==Required==
|
||||||
|
|
||||||
|
TLS configuration, see [TLS](/configuration/shared/tls/#inbound).
|
||||||
|
|
||||||
#### masquerade
|
#### masquerade
|
||||||
|
|
||||||
HTTP3 server behavior when authentication fails.
|
HTTP3 server behavior when authentication fails.
|
||||||
@ -78,8 +85,6 @@ HTTP3 server behavior when authentication fails.
|
|||||||
|
|
||||||
A 404 page will be returned if empty.
|
A 404 page will be returned if empty.
|
||||||
|
|
||||||
#### tls
|
#### brutal_debug
|
||||||
|
|
||||||
==Required==
|
Enable debug information logging for Hysteria Brutal CC.
|
||||||
|
|
||||||
TLS configuration, see [TLS](/configuration/shared/tls/#inbound).
|
|
||||||
|
@ -20,8 +20,9 @@
|
|||||||
}
|
}
|
||||||
],
|
],
|
||||||
"ignore_client_bandwidth": false,
|
"ignore_client_bandwidth": false,
|
||||||
|
"tls": {},
|
||||||
"masquerade": "",
|
"masquerade": "",
|
||||||
"tls": {}
|
"brutal_debug": false
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -65,6 +66,12 @@ Hysteria 用户
|
|||||||
|
|
||||||
与 `up_mbps` 和 `down_mbps` 冲突。
|
与 `up_mbps` 和 `down_mbps` 冲突。
|
||||||
|
|
||||||
|
#### tls
|
||||||
|
|
||||||
|
==必填==
|
||||||
|
|
||||||
|
TLS 配置, 参阅 [TLS](/zh/configuration/shared/tls/#inbound)。
|
||||||
|
|
||||||
#### masquerade
|
#### masquerade
|
||||||
|
|
||||||
HTTP3 服务器认证失败时的行为。
|
HTTP3 服务器认证失败时的行为。
|
||||||
@ -76,8 +83,6 @@ HTTP3 服务器认证失败时的行为。
|
|||||||
|
|
||||||
如果为空,则返回 404 页。
|
如果为空,则返回 404 页。
|
||||||
|
|
||||||
#### tls
|
#### brutal_debug
|
||||||
|
|
||||||
==必填==
|
启用 Hysteria Brutal CC 的调试信息日志记录。
|
||||||
|
|
||||||
TLS 配置, 参阅 [TLS](/zh/configuration/shared/tls/#inbound)。
|
|
||||||
|
@ -16,6 +16,7 @@
|
|||||||
"password": "goofy_ahh_password",
|
"password": "goofy_ahh_password",
|
||||||
"network": "tcp",
|
"network": "tcp",
|
||||||
"tls": {},
|
"tls": {},
|
||||||
|
"brutal_debug": false,
|
||||||
|
|
||||||
... // Dial Fields
|
... // Dial Fields
|
||||||
}
|
}
|
||||||
@ -73,6 +74,10 @@ Both is enabled by default.
|
|||||||
|
|
||||||
TLS configuration, see [TLS](/configuration/shared/tls/#outbound).
|
TLS configuration, see [TLS](/configuration/shared/tls/#outbound).
|
||||||
|
|
||||||
|
#### brutal_debug
|
||||||
|
|
||||||
|
Enable debug information logging for Hysteria Brutal CC.
|
||||||
|
|
||||||
### Dial Fields
|
### Dial Fields
|
||||||
|
|
||||||
See [Dial Fields](/configuration/shared/dial) for details.
|
See [Dial Fields](/configuration/shared/dial) for details.
|
||||||
|
@ -16,6 +16,7 @@
|
|||||||
"password": "goofy_ahh_password",
|
"password": "goofy_ahh_password",
|
||||||
"network": "tcp",
|
"network": "tcp",
|
||||||
"tls": {},
|
"tls": {},
|
||||||
|
"brutal_debug": false,
|
||||||
|
|
||||||
... // 拨号字段
|
... // 拨号字段
|
||||||
}
|
}
|
||||||
@ -73,6 +74,9 @@ QUIC 流量混淆器密码.
|
|||||||
|
|
||||||
TLS 配置, 参阅 [TLS](/zh/configuration/shared/tls/#outbound)。
|
TLS 配置, 参阅 [TLS](/zh/configuration/shared/tls/#outbound)。
|
||||||
|
|
||||||
|
#### brutal_debug
|
||||||
|
|
||||||
|
启用 Hysteria Brutal CC 的调试信息日志记录。
|
||||||
|
|
||||||
### 拨号字段
|
### 拨号字段
|
||||||
|
|
||||||
|
@ -65,6 +65,17 @@ func (m *platformDefaultInterfaceMonitor) DefaultInterfaceIndex(destination neti
|
|||||||
return m.defaultInterfaceIndex
|
return m.defaultInterfaceIndex
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (m *platformDefaultInterfaceMonitor) DefaultInterface(destination netip.Addr) (string, int) {
|
||||||
|
for _, address := range m.networkAddresses {
|
||||||
|
for _, prefix := range address.addresses {
|
||||||
|
if prefix.Contains(destination) {
|
||||||
|
return address.interfaceName, address.interfaceIndex
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return m.defaultInterfaceName, m.defaultInterfaceIndex
|
||||||
|
}
|
||||||
|
|
||||||
func (m *platformDefaultInterfaceMonitor) OverrideAndroidVPN() bool {
|
func (m *platformDefaultInterfaceMonitor) OverrideAndroidVPN() bool {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
17
go.mod
17
go.mod
@ -4,11 +4,10 @@ go 1.20
|
|||||||
|
|
||||||
require (
|
require (
|
||||||
berty.tech/go-libtor v1.0.385
|
berty.tech/go-libtor v1.0.385
|
||||||
github.com/Dreamacro/clash v1.17.0
|
|
||||||
github.com/caddyserver/certmagic v0.19.2
|
github.com/caddyserver/certmagic v0.19.2
|
||||||
github.com/cloudflare/circl v1.3.3
|
github.com/cloudflare/circl v1.3.3
|
||||||
github.com/cretz/bine v0.2.0
|
github.com/cretz/bine v0.2.0
|
||||||
github.com/fsnotify/fsnotify v1.6.0
|
github.com/fsnotify/fsnotify v1.7.0
|
||||||
github.com/go-chi/chi/v5 v5.0.10
|
github.com/go-chi/chi/v5 v5.0.10
|
||||||
github.com/go-chi/cors v1.2.1
|
github.com/go-chi/cors v1.2.1
|
||||||
github.com/go-chi/render v1.0.3
|
github.com/go-chi/render v1.0.3
|
||||||
@ -21,21 +20,20 @@ require (
|
|||||||
github.com/miekg/dns v1.1.56
|
github.com/miekg/dns v1.1.56
|
||||||
github.com/ooni/go-libtor v1.1.8
|
github.com/ooni/go-libtor v1.1.8
|
||||||
github.com/oschwald/maxminddb-golang v1.12.0
|
github.com/oschwald/maxminddb-golang v1.12.0
|
||||||
github.com/pires/go-proxyproto v0.7.0
|
|
||||||
github.com/sagernet/bbolt v0.0.0-20231014093535-ea5cb2fe9f0a
|
github.com/sagernet/bbolt v0.0.0-20231014093535-ea5cb2fe9f0a
|
||||||
github.com/sagernet/cloudflare-tls v0.0.0-20230829051644-4a68352d0c4a
|
github.com/sagernet/cloudflare-tls v0.0.0-20230829051644-4a68352d0c4a
|
||||||
github.com/sagernet/gomobile v0.0.0-20230915142329-c6740b6d2950
|
github.com/sagernet/gomobile v0.0.0-20230915142329-c6740b6d2950
|
||||||
github.com/sagernet/gvisor v0.0.0-20230627031050-1ab0276e0dd2
|
github.com/sagernet/gvisor v0.0.0-20230930141345-5fef6f2e17ab
|
||||||
github.com/sagernet/quic-go v0.0.0-20230919101909-0cc6c5dcecee
|
github.com/sagernet/quic-go v0.0.0-20231008035953-32727fef9460
|
||||||
github.com/sagernet/reality v0.0.0-20230406110435-ee17307e7691
|
github.com/sagernet/reality v0.0.0-20230406110435-ee17307e7691
|
||||||
github.com/sagernet/sing v0.2.15-0.20231021083548-570295cd12f5
|
github.com/sagernet/sing v0.2.15-0.20231021090846-8002db54c028
|
||||||
github.com/sagernet/sing-dns v0.1.10
|
github.com/sagernet/sing-dns v0.1.10
|
||||||
github.com/sagernet/sing-mux v0.1.3
|
github.com/sagernet/sing-mux v0.1.3
|
||||||
github.com/sagernet/sing-quic v0.1.2
|
github.com/sagernet/sing-quic v0.1.3-0.20231021083253-6c13506e2fb2
|
||||||
github.com/sagernet/sing-shadowsocks v0.2.5
|
github.com/sagernet/sing-shadowsocks v0.2.5
|
||||||
github.com/sagernet/sing-shadowsocks2 v0.1.4
|
github.com/sagernet/sing-shadowsocks2 v0.1.4
|
||||||
github.com/sagernet/sing-shadowtls v0.1.4
|
github.com/sagernet/sing-shadowtls v0.1.4
|
||||||
github.com/sagernet/sing-tun v0.1.15
|
github.com/sagernet/sing-tun v0.1.16-0.20231022145846-fe9891a78413
|
||||||
github.com/sagernet/sing-vmess v0.1.8
|
github.com/sagernet/sing-vmess v0.1.8
|
||||||
github.com/sagernet/smux v0.0.0-20230312102458-337ec2a5af37
|
github.com/sagernet/smux v0.0.0-20230312102458-337ec2a5af37
|
||||||
github.com/sagernet/tfo-go v0.0.0-20230816093905-5a5c285d44a6
|
github.com/sagernet/tfo-go v0.0.0-20230816093905-5a5c285d44a6
|
||||||
@ -47,7 +45,6 @@ require (
|
|||||||
go.uber.org/zap v1.26.0
|
go.uber.org/zap v1.26.0
|
||||||
go4.org/netipx v0.0.0-20230824141953-6213f710f925
|
go4.org/netipx v0.0.0-20230824141953-6213f710f925
|
||||||
golang.org/x/crypto v0.14.0
|
golang.org/x/crypto v0.14.0
|
||||||
golang.org/x/exp v0.0.0-20231005195138-3e424a577f31
|
|
||||||
golang.org/x/net v0.17.0
|
golang.org/x/net v0.17.0
|
||||||
golang.org/x/sys v0.13.0
|
golang.org/x/sys v0.13.0
|
||||||
golang.zx2c4.com/wireguard/wgctrl v0.0.0-20230429144221-925a1e7659e6
|
golang.zx2c4.com/wireguard/wgctrl v0.0.0-20230429144221-925a1e7659e6
|
||||||
@ -59,7 +56,6 @@ require (
|
|||||||
//replace github.com/sagernet/sing => ../sing
|
//replace github.com/sagernet/sing => ../sing
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/Dreamacro/protobytes v0.0.0-20230617041236-6500a9f4f158 // indirect
|
|
||||||
github.com/ajg/form v1.5.1 // indirect
|
github.com/ajg/form v1.5.1 // indirect
|
||||||
github.com/andybalholm/brotli v1.0.5 // indirect
|
github.com/andybalholm/brotli v1.0.5 // indirect
|
||||||
github.com/davecgh/go-spew v1.1.1 // indirect
|
github.com/davecgh/go-spew v1.1.1 // indirect
|
||||||
@ -88,6 +84,7 @@ require (
|
|||||||
github.com/vishvananda/netns v0.0.0-20211101163701-50045581ed74 // indirect
|
github.com/vishvananda/netns v0.0.0-20211101163701-50045581ed74 // indirect
|
||||||
github.com/zeebo/blake3 v0.2.3 // indirect
|
github.com/zeebo/blake3 v0.2.3 // indirect
|
||||||
go.uber.org/multierr v1.11.0 // indirect
|
go.uber.org/multierr v1.11.0 // indirect
|
||||||
|
golang.org/x/exp v0.0.0-20231005195138-3e424a577f31 // indirect
|
||||||
golang.org/x/mod v0.12.0 // indirect
|
golang.org/x/mod v0.12.0 // indirect
|
||||||
golang.org/x/text v0.13.0 // indirect
|
golang.org/x/text v0.13.0 // indirect
|
||||||
golang.org/x/time v0.3.0 // indirect
|
golang.org/x/time v0.3.0 // indirect
|
||||||
|
31
go.sum
31
go.sum
@ -1,9 +1,5 @@
|
|||||||
berty.tech/go-libtor v1.0.385 h1:RWK94C3hZj6Z2GdvePpHJLnWYobFr3bY/OdUJ5aoEXw=
|
berty.tech/go-libtor v1.0.385 h1:RWK94C3hZj6Z2GdvePpHJLnWYobFr3bY/OdUJ5aoEXw=
|
||||||
berty.tech/go-libtor v1.0.385/go.mod h1:9swOOQVb+kmvuAlsgWUK/4c52pm69AdbJsxLzk+fJEw=
|
berty.tech/go-libtor v1.0.385/go.mod h1:9swOOQVb+kmvuAlsgWUK/4c52pm69AdbJsxLzk+fJEw=
|
||||||
github.com/Dreamacro/clash v1.17.0 h1:LWtp6KcnrCiujY58ufI8pylI+hbCBgSCsLI90EWhpi4=
|
|
||||||
github.com/Dreamacro/clash v1.17.0/go.mod h1:PtcAft7sdsK325BD6uwm8wvhOkMV3TCeED6dfZ/lnfE=
|
|
||||||
github.com/Dreamacro/protobytes v0.0.0-20230617041236-6500a9f4f158 h1:JFnwKplz9hj8ubqYjm8HkgZS1Rvz9yW+u/XCNNTxr0k=
|
|
||||||
github.com/Dreamacro/protobytes v0.0.0-20230617041236-6500a9f4f158/go.mod h1:QvmEZ/h6KXszPOr2wUFl7Zn3hfFNYdfbXwPVDTyZs6k=
|
|
||||||
github.com/ajg/form v1.5.1 h1:t9c7v8JUKu/XxOGBU0yjNpaMloxGEJhUkqFRq0ibGeU=
|
github.com/ajg/form v1.5.1 h1:t9c7v8JUKu/XxOGBU0yjNpaMloxGEJhUkqFRq0ibGeU=
|
||||||
github.com/ajg/form v1.5.1/go.mod h1:uL1WgH+h2mgNtvBq0339dVnzXdBETtL2LeUXaIv25UY=
|
github.com/ajg/form v1.5.1/go.mod h1:uL1WgH+h2mgNtvBq0339dVnzXdBETtL2LeUXaIv25UY=
|
||||||
github.com/andybalholm/brotli v1.0.5 h1:8uQZIdzKmjc/iuPu7O2ioW48L81FgatrcpfFmiq/cCs=
|
github.com/andybalholm/brotli v1.0.5 h1:8uQZIdzKmjc/iuPu7O2ioW48L81FgatrcpfFmiq/cCs=
|
||||||
@ -22,8 +18,8 @@ github.com/cretz/bine v0.2.0/go.mod h1:WU4o9QR9wWp8AVKtTM1XD5vUHkEqnf2vVSo6dBqbe
|
|||||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
github.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4HY=
|
github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA=
|
||||||
github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw=
|
github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM=
|
||||||
github.com/go-chi/chi/v5 v5.0.10 h1:rLz5avzKpjqxrYwXNfmjkrYYXOyLJd37pz53UFHC6vk=
|
github.com/go-chi/chi/v5 v5.0.10 h1:rLz5avzKpjqxrYwXNfmjkrYYXOyLJd37pz53UFHC6vk=
|
||||||
github.com/go-chi/chi/v5 v5.0.10/go.mod h1:DslCQbL2OYiznFReuXYUmQ2hGd1aDpCnlMNITLSKoi8=
|
github.com/go-chi/chi/v5 v5.0.10/go.mod h1:DslCQbL2OYiznFReuXYUmQ2hGd1aDpCnlMNITLSKoi8=
|
||||||
github.com/go-chi/cors v1.2.1 h1:xEC8UT3Rlp2QuWNEr4Fs/c2EAGVKBwy/1vHx3bppil4=
|
github.com/go-chi/cors v1.2.1 h1:xEC8UT3Rlp2QuWNEr4Fs/c2EAGVKBwy/1vHx3bppil4=
|
||||||
@ -89,8 +85,6 @@ github.com/oschwald/maxminddb-golang v1.12.0 h1:9FnTOD0YOhP7DGxGsq4glzpGy5+w7pq5
|
|||||||
github.com/oschwald/maxminddb-golang v1.12.0/go.mod h1:q0Nob5lTCqyQ8WT6FYgS1L7PXKVVbgiymefNwIjPzgY=
|
github.com/oschwald/maxminddb-golang v1.12.0/go.mod h1:q0Nob5lTCqyQ8WT6FYgS1L7PXKVVbgiymefNwIjPzgY=
|
||||||
github.com/pierrec/lz4/v4 v4.1.14 h1:+fL8AQEZtz/ijeNnpduH0bROTu0O3NZAlPjQxGn8LwE=
|
github.com/pierrec/lz4/v4 v4.1.14 h1:+fL8AQEZtz/ijeNnpduH0bROTu0O3NZAlPjQxGn8LwE=
|
||||||
github.com/pierrec/lz4/v4 v4.1.14/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4=
|
github.com/pierrec/lz4/v4 v4.1.14/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4=
|
||||||
github.com/pires/go-proxyproto v0.7.0 h1:IukmRewDQFWC7kfnb66CSomk2q/seBuilHBYFwyq0Hs=
|
|
||||||
github.com/pires/go-proxyproto v0.7.0/go.mod h1:Vz/1JPY/OACxWGQNIRY2BeyDmpoaWmEP40O9LbuiFR4=
|
|
||||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||||
github.com/quic-go/qpack v0.4.0 h1:Cr9BXA1sQS2SmDUWjSofMPNKmvF6IiIfDRmgU0w1ZCo=
|
github.com/quic-go/qpack v0.4.0 h1:Cr9BXA1sQS2SmDUWjSofMPNKmvF6IiIfDRmgU0w1ZCo=
|
||||||
@ -106,32 +100,32 @@ github.com/sagernet/go-tun2socks v1.16.12-0.20220818015926-16cb67876a61 h1:5+m7c
|
|||||||
github.com/sagernet/go-tun2socks v1.16.12-0.20220818015926-16cb67876a61/go.mod h1:QUQ4RRHD6hGGHdFMEtR8T2P6GS6R3D/CXKdaYHKKXms=
|
github.com/sagernet/go-tun2socks v1.16.12-0.20220818015926-16cb67876a61/go.mod h1:QUQ4RRHD6hGGHdFMEtR8T2P6GS6R3D/CXKdaYHKKXms=
|
||||||
github.com/sagernet/gomobile v0.0.0-20230915142329-c6740b6d2950 h1:hUz/2mJLgi7l2H36JGpDY+jou9FmI6kAm0ZkU+xPpgE=
|
github.com/sagernet/gomobile v0.0.0-20230915142329-c6740b6d2950 h1:hUz/2mJLgi7l2H36JGpDY+jou9FmI6kAm0ZkU+xPpgE=
|
||||||
github.com/sagernet/gomobile v0.0.0-20230915142329-c6740b6d2950/go.mod h1:5YE39YkJkCcMsfq1jMKkjsrM2GfBoF9JVWnvU89hmvU=
|
github.com/sagernet/gomobile v0.0.0-20230915142329-c6740b6d2950/go.mod h1:5YE39YkJkCcMsfq1jMKkjsrM2GfBoF9JVWnvU89hmvU=
|
||||||
github.com/sagernet/gvisor v0.0.0-20230627031050-1ab0276e0dd2 h1:dnkKrzapqtAwjTSWt6hdPrARORfoYvuUczynvRLrueo=
|
github.com/sagernet/gvisor v0.0.0-20230930141345-5fef6f2e17ab h1:u+xQoi/Yc6bNUvTfrDD6HhGRybn2lzrhf5vmS+wb4Ho=
|
||||||
github.com/sagernet/gvisor v0.0.0-20230627031050-1ab0276e0dd2/go.mod h1:1JUiV7nGuf++YFm9eWZ8q2lrwHmhcUGzptMl/vL1+LA=
|
github.com/sagernet/gvisor v0.0.0-20230930141345-5fef6f2e17ab/go.mod h1:3akUhSHSVtLuJaYcW5JPepUraBOW06Ibz2HKwaK5rOk=
|
||||||
github.com/sagernet/netlink v0.0.0-20220905062125-8043b4a9aa97 h1:iL5gZI3uFp0X6EslacyapiRz7LLSJyr4RajF/BhMVyE=
|
github.com/sagernet/netlink v0.0.0-20220905062125-8043b4a9aa97 h1:iL5gZI3uFp0X6EslacyapiRz7LLSJyr4RajF/BhMVyE=
|
||||||
github.com/sagernet/netlink v0.0.0-20220905062125-8043b4a9aa97/go.mod h1:xLnfdiJbSp8rNqYEdIW/6eDO4mVoogml14Bh2hSiFpM=
|
github.com/sagernet/netlink v0.0.0-20220905062125-8043b4a9aa97/go.mod h1:xLnfdiJbSp8rNqYEdIW/6eDO4mVoogml14Bh2hSiFpM=
|
||||||
github.com/sagernet/quic-go v0.0.0-20230919101909-0cc6c5dcecee h1:ykuhl9jCS638N+jw1vC9AvT9bbQn6xRNScP2FWPV9dM=
|
github.com/sagernet/quic-go v0.0.0-20231008035953-32727fef9460 h1:dAe4OIJAtE0nHOzTHhAReQteh3+sa63rvXbuIpbeOTY=
|
||||||
github.com/sagernet/quic-go v0.0.0-20230919101909-0cc6c5dcecee/go.mod h1:0CfhWwZAeXGYM9+Nkkw1zcQtFHQC8KWjbpeDv7pu8iw=
|
github.com/sagernet/quic-go v0.0.0-20231008035953-32727fef9460/go.mod h1:uJGpmJCOcMQqMlHKc3P1Vz6uygmpz4bPeVIoOhdVQnM=
|
||||||
github.com/sagernet/reality v0.0.0-20230406110435-ee17307e7691 h1:5Th31OC6yj8byLGkEnIYp6grlXfo1QYUfiYFGjewIdc=
|
github.com/sagernet/reality v0.0.0-20230406110435-ee17307e7691 h1:5Th31OC6yj8byLGkEnIYp6grlXfo1QYUfiYFGjewIdc=
|
||||||
github.com/sagernet/reality v0.0.0-20230406110435-ee17307e7691/go.mod h1:B8lp4WkQ1PwNnrVMM6KyuFR20pU8jYBD+A4EhJovEXU=
|
github.com/sagernet/reality v0.0.0-20230406110435-ee17307e7691/go.mod h1:B8lp4WkQ1PwNnrVMM6KyuFR20pU8jYBD+A4EhJovEXU=
|
||||||
github.com/sagernet/sing v0.0.0-20220817130738-ce854cda8522/go.mod h1:QVsS5L/ZA2Q5UhQwLrn0Trw+msNd/NPGEhBKR/ioWiY=
|
github.com/sagernet/sing v0.0.0-20220817130738-ce854cda8522/go.mod h1:QVsS5L/ZA2Q5UhQwLrn0Trw+msNd/NPGEhBKR/ioWiY=
|
||||||
github.com/sagernet/sing v0.1.8/go.mod h1:jt1w2u7lJQFFSGLiRrRIs5YWmx4kAPfWuOejuDW9qMk=
|
github.com/sagernet/sing v0.1.8/go.mod h1:jt1w2u7lJQFFSGLiRrRIs5YWmx4kAPfWuOejuDW9qMk=
|
||||||
github.com/sagernet/sing v0.2.15-0.20231021083548-570295cd12f5 h1:yab4g52MbW5MVzUkZwlh0632xwqO9dBWBT4jlr8hTZ0=
|
github.com/sagernet/sing v0.2.15-0.20231021090846-8002db54c028 h1:KRTzVdo7UPsygO235wsB705z6oWEM/L9HEJMOh4AsGI=
|
||||||
github.com/sagernet/sing v0.2.15-0.20231021083548-570295cd12f5/go.mod h1:AhNEHu0GXrpqkuzvTwvC8+j2cQUU/dh+zLEmq4C99pg=
|
github.com/sagernet/sing v0.2.15-0.20231021090846-8002db54c028/go.mod h1:AhNEHu0GXrpqkuzvTwvC8+j2cQUU/dh+zLEmq4C99pg=
|
||||||
github.com/sagernet/sing-dns v0.1.10 h1:iIU7nRBlUYj+fF2TaktGIvRiTFFrHwSMedLQsvlTZCI=
|
github.com/sagernet/sing-dns v0.1.10 h1:iIU7nRBlUYj+fF2TaktGIvRiTFFrHwSMedLQsvlTZCI=
|
||||||
github.com/sagernet/sing-dns v0.1.10/go.mod h1:vtUimtf7Nq9EdvD5WTpfCr69KL1M7bcgOVKiYBiAY/c=
|
github.com/sagernet/sing-dns v0.1.10/go.mod h1:vtUimtf7Nq9EdvD5WTpfCr69KL1M7bcgOVKiYBiAY/c=
|
||||||
github.com/sagernet/sing-mux v0.1.3 h1:fAf7PZa2A55mCeh0KKM02f1k2Y4vEmxuZZ/51ahkkLA=
|
github.com/sagernet/sing-mux v0.1.3 h1:fAf7PZa2A55mCeh0KKM02f1k2Y4vEmxuZZ/51ahkkLA=
|
||||||
github.com/sagernet/sing-mux v0.1.3/go.mod h1:wGeIeiiFLx4HUM5LAg65wrNZ/X1muOimqK0PEhNbPi0=
|
github.com/sagernet/sing-mux v0.1.3/go.mod h1:wGeIeiiFLx4HUM5LAg65wrNZ/X1muOimqK0PEhNbPi0=
|
||||||
github.com/sagernet/sing-quic v0.1.2 h1:+u9CRf0KHi5HgXmJ3eB0CtqpWXtF0lx2QlWq+ZFZ+XY=
|
github.com/sagernet/sing-quic v0.1.3-0.20231021083253-6c13506e2fb2 h1:wcvxA4P/rnF2dokdRAL+Mr45GVFitVoWJqRzBt1Ut0s=
|
||||||
github.com/sagernet/sing-quic v0.1.2/go.mod h1:H1TX0/y9UUM43wyaLQ+qjg2+o901ibYtwWX2rWG+a3o=
|
github.com/sagernet/sing-quic v0.1.3-0.20231021083253-6c13506e2fb2/go.mod h1:1M7xP4802K9Kz6BQ7LlA7UeCapWvWlH1Htmk2bAqkWc=
|
||||||
github.com/sagernet/sing-shadowsocks v0.2.5 h1:qxIttos4xu6ii7MTVJYA8EFQR7Q3KG6xMqmLJIFtBaY=
|
github.com/sagernet/sing-shadowsocks v0.2.5 h1:qxIttos4xu6ii7MTVJYA8EFQR7Q3KG6xMqmLJIFtBaY=
|
||||||
github.com/sagernet/sing-shadowsocks v0.2.5/go.mod h1:MGWGkcU2xW2G2mfArT9/QqpVLOGU+dBaahZCtPHdt7A=
|
github.com/sagernet/sing-shadowsocks v0.2.5/go.mod h1:MGWGkcU2xW2G2mfArT9/QqpVLOGU+dBaahZCtPHdt7A=
|
||||||
github.com/sagernet/sing-shadowsocks2 v0.1.4 h1:vht2M8t3m5DTgXR2j24KbYOygG5aOp+MUhpQnAux728=
|
github.com/sagernet/sing-shadowsocks2 v0.1.4 h1:vht2M8t3m5DTgXR2j24KbYOygG5aOp+MUhpQnAux728=
|
||||||
github.com/sagernet/sing-shadowsocks2 v0.1.4/go.mod h1:Mgdee99NxxNd5Zld3ixIs18yVs4x2dI2VTDDE1N14Wc=
|
github.com/sagernet/sing-shadowsocks2 v0.1.4/go.mod h1:Mgdee99NxxNd5Zld3ixIs18yVs4x2dI2VTDDE1N14Wc=
|
||||||
github.com/sagernet/sing-shadowtls v0.1.4 h1:aTgBSJEgnumzFenPvc+kbD9/W0PywzWevnVpEx6Tw3k=
|
github.com/sagernet/sing-shadowtls v0.1.4 h1:aTgBSJEgnumzFenPvc+kbD9/W0PywzWevnVpEx6Tw3k=
|
||||||
github.com/sagernet/sing-shadowtls v0.1.4/go.mod h1:F8NBgsY5YN2beQavdgdm1DPlhaKQlaL6lpDdcBglGK4=
|
github.com/sagernet/sing-shadowtls v0.1.4/go.mod h1:F8NBgsY5YN2beQavdgdm1DPlhaKQlaL6lpDdcBglGK4=
|
||||||
github.com/sagernet/sing-tun v0.1.15 h1:XfHQD/dhCCQeespPojB4gRhADI1A/4mSLLJCnh5qUnQ=
|
github.com/sagernet/sing-tun v0.1.16-0.20231022145846-fe9891a78413 h1:0jLVkf1w93i0qns6UA5tzu2NblDLZ2L4uaG2nnDQfwI=
|
||||||
github.com/sagernet/sing-tun v0.1.15/go.mod h1:zgRoBAtOM24QXx0IKYFEnuTtXPq1Z4rDYRWkP8kJm+g=
|
github.com/sagernet/sing-tun v0.1.16-0.20231022145846-fe9891a78413/go.mod h1:aDReDW4yL0AzGDyH1fMvv8gKbJC/e4u4qi01oO+Zf2w=
|
||||||
github.com/sagernet/sing-vmess v0.1.8 h1:XVWad1RpTy9b5tPxdm5MCU8cGfrTGdR8qCq6HV2aCNc=
|
github.com/sagernet/sing-vmess v0.1.8 h1:XVWad1RpTy9b5tPxdm5MCU8cGfrTGdR8qCq6HV2aCNc=
|
||||||
github.com/sagernet/sing-vmess v0.1.8/go.mod h1:vhx32UNzTDUkNwOyIjcZQohre1CaytquC5mPplId8uA=
|
github.com/sagernet/sing-vmess v0.1.8/go.mod h1:vhx32UNzTDUkNwOyIjcZQohre1CaytquC5mPplId8uA=
|
||||||
github.com/sagernet/smux v0.0.0-20230312102458-337ec2a5af37 h1:HuE6xSwco/Xed8ajZ+coeYLmioq0Qp1/Z2zczFaV8as=
|
github.com/sagernet/smux v0.0.0-20230312102458-337ec2a5af37 h1:HuE6xSwco/Xed8ajZ+coeYLmioq0Qp1/Z2zczFaV8as=
|
||||||
@ -193,7 +187,6 @@ golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7w
|
|||||||
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20220622161953-175b2fd9d664/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20220622161953-175b2fd9d664/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.0.0-20220731174439-a90be440212d/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20220731174439-a90be440212d/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
|
||||||
golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.13.0 h1:Af8nKPmuFypiUBjVoU9V20FiaFXOcuZI21p0ycVYYGE=
|
golang.org/x/sys v0.13.0 h1:Af8nKPmuFypiUBjVoU9V20FiaFXOcuZI21p0ycVYYGE=
|
||||||
|
@ -5,7 +5,6 @@ import (
|
|||||||
"net"
|
"net"
|
||||||
|
|
||||||
"github.com/sagernet/sing-box/adapter"
|
"github.com/sagernet/sing-box/adapter"
|
||||||
"github.com/sagernet/sing-box/common/proxyproto"
|
|
||||||
"github.com/sagernet/sing-box/log"
|
"github.com/sagernet/sing-box/log"
|
||||||
E "github.com/sagernet/sing/common/exceptions"
|
E "github.com/sagernet/sing/common/exceptions"
|
||||||
M "github.com/sagernet/sing/common/metadata"
|
M "github.com/sagernet/sing/common/metadata"
|
||||||
@ -34,9 +33,8 @@ func (a *myInboundAdapter) ListenTCP() (net.Listener, error) {
|
|||||||
if err == nil {
|
if err == nil {
|
||||||
a.logger.Info("tcp server started at ", tcpListener.Addr())
|
a.logger.Info("tcp server started at ", tcpListener.Addr())
|
||||||
}
|
}
|
||||||
if a.listenOptions.ProxyProtocol {
|
if a.listenOptions.ProxyProtocol || a.listenOptions.ProxyProtocolAcceptNoHeader {
|
||||||
a.logger.Warn("Proxy Protocol is deprecated, see https://sing-box.sagernet.org/deprecated")
|
return nil, E.New("Proxy Protocol is deprecated and removed in sing-box 1.6.0")
|
||||||
tcpListener = &proxyproto.Listener{Listener: tcpListener, AcceptNoHeader: a.listenOptions.ProxyProtocolAcceptNoHeader}
|
|
||||||
}
|
}
|
||||||
a.tcpListener = tcpListener
|
a.tcpListener = tcpListener
|
||||||
return tcpListener, err
|
return tcpListener, err
|
||||||
|
@ -4,104 +4,38 @@ package inbound
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"sync"
|
"net"
|
||||||
|
|
||||||
"github.com/sagernet/quic-go"
|
|
||||||
"github.com/sagernet/sing-box/adapter"
|
"github.com/sagernet/sing-box/adapter"
|
||||||
|
"github.com/sagernet/sing-box/common/humanize"
|
||||||
"github.com/sagernet/sing-box/common/tls"
|
"github.com/sagernet/sing-box/common/tls"
|
||||||
C "github.com/sagernet/sing-box/constant"
|
C "github.com/sagernet/sing-box/constant"
|
||||||
"github.com/sagernet/sing-box/log"
|
"github.com/sagernet/sing-box/log"
|
||||||
"github.com/sagernet/sing-box/option"
|
"github.com/sagernet/sing-box/option"
|
||||||
"github.com/sagernet/sing-box/transport/hysteria"
|
"github.com/sagernet/sing-quic/hysteria"
|
||||||
"github.com/sagernet/sing-quic"
|
|
||||||
hyCC "github.com/sagernet/sing-quic/hysteria2/congestion"
|
|
||||||
"github.com/sagernet/sing/common"
|
"github.com/sagernet/sing/common"
|
||||||
"github.com/sagernet/sing/common/auth"
|
"github.com/sagernet/sing/common/auth"
|
||||||
E "github.com/sagernet/sing/common/exceptions"
|
E "github.com/sagernet/sing/common/exceptions"
|
||||||
F "github.com/sagernet/sing/common/format"
|
|
||||||
M "github.com/sagernet/sing/common/metadata"
|
|
||||||
N "github.com/sagernet/sing/common/network"
|
N "github.com/sagernet/sing/common/network"
|
||||||
|
|
||||||
"golang.org/x/exp/slices"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
var _ adapter.Inbound = (*Hysteria)(nil)
|
var _ adapter.Inbound = (*Hysteria)(nil)
|
||||||
|
|
||||||
type Hysteria struct {
|
type Hysteria struct {
|
||||||
myInboundAdapter
|
myInboundAdapter
|
||||||
quicConfig *quic.Config
|
|
||||||
tlsConfig tls.ServerConfig
|
tlsConfig tls.ServerConfig
|
||||||
authKey []string
|
service *hysteria.Service[int]
|
||||||
authUser []string
|
userNameList []string
|
||||||
xplusKey []byte
|
|
||||||
sendBPS uint64
|
|
||||||
recvBPS uint64
|
|
||||||
listener qtls.Listener
|
|
||||||
udpAccess sync.RWMutex
|
|
||||||
udpSessionId uint32
|
|
||||||
udpSessions map[uint32]chan *hysteria.UDPMessage
|
|
||||||
udpDefragger hysteria.Defragger
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewHysteria(ctx context.Context, router adapter.Router, logger log.ContextLogger, tag string, options option.HysteriaInboundOptions) (*Hysteria, error) {
|
func NewHysteria(ctx context.Context, router adapter.Router, logger log.ContextLogger, tag string, options option.HysteriaInboundOptions) (*Hysteria, error) {
|
||||||
options.UDPFragmentDefault = true
|
options.UDPFragmentDefault = true
|
||||||
quicConfig := &quic.Config{
|
if options.TLS == nil || !options.TLS.Enabled {
|
||||||
InitialStreamReceiveWindow: options.ReceiveWindowConn,
|
return nil, C.ErrTLSRequired
|
||||||
MaxStreamReceiveWindow: options.ReceiveWindowConn,
|
|
||||||
InitialConnectionReceiveWindow: options.ReceiveWindowClient,
|
|
||||||
MaxConnectionReceiveWindow: options.ReceiveWindowClient,
|
|
||||||
MaxIncomingStreams: int64(options.MaxConnClient),
|
|
||||||
KeepAlivePeriod: hysteria.KeepAlivePeriod,
|
|
||||||
DisablePathMTUDiscovery: options.DisableMTUDiscovery || !(C.IsLinux || C.IsWindows),
|
|
||||||
EnableDatagrams: true,
|
|
||||||
}
|
}
|
||||||
if options.ReceiveWindowConn == 0 {
|
tlsConfig, err := tls.NewServer(ctx, logger, common.PtrValueOrDefault(options.TLS))
|
||||||
quicConfig.InitialStreamReceiveWindow = hysteria.DefaultStreamReceiveWindow
|
if err != nil {
|
||||||
quicConfig.MaxStreamReceiveWindow = hysteria.DefaultStreamReceiveWindow
|
return nil, err
|
||||||
}
|
|
||||||
if options.ReceiveWindowClient == 0 {
|
|
||||||
quicConfig.InitialConnectionReceiveWindow = hysteria.DefaultConnectionReceiveWindow
|
|
||||||
quicConfig.MaxConnectionReceiveWindow = hysteria.DefaultConnectionReceiveWindow
|
|
||||||
}
|
|
||||||
if quicConfig.MaxIncomingStreams == 0 {
|
|
||||||
quicConfig.MaxIncomingStreams = hysteria.DefaultMaxIncomingStreams
|
|
||||||
}
|
|
||||||
authKey := common.Map(options.Users, func(it option.HysteriaUser) string {
|
|
||||||
if len(it.Auth) > 0 {
|
|
||||||
return string(it.Auth)
|
|
||||||
} else {
|
|
||||||
return it.AuthString
|
|
||||||
}
|
|
||||||
})
|
|
||||||
authUser := common.Map(options.Users, func(it option.HysteriaUser) string {
|
|
||||||
return it.Name
|
|
||||||
})
|
|
||||||
var xplus []byte
|
|
||||||
if options.Obfs != "" {
|
|
||||||
xplus = []byte(options.Obfs)
|
|
||||||
}
|
|
||||||
var up, down uint64
|
|
||||||
if len(options.Up) > 0 {
|
|
||||||
up = hysteria.StringToBps(options.Up)
|
|
||||||
if up == 0 {
|
|
||||||
return nil, E.New("invalid up speed format: ", options.Up)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
up = uint64(options.UpMbps) * hysteria.MbpsToBps
|
|
||||||
}
|
|
||||||
if len(options.Down) > 0 {
|
|
||||||
down = hysteria.StringToBps(options.Down)
|
|
||||||
if down == 0 {
|
|
||||||
return nil, E.New("invalid down speed format: ", options.Down)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
down = uint64(options.DownMbps) * hysteria.MbpsToBps
|
|
||||||
}
|
|
||||||
if up < hysteria.MinSpeedBPS {
|
|
||||||
return nil, E.New("invalid up speed")
|
|
||||||
}
|
|
||||||
if down < hysteria.MinSpeedBPS {
|
|
||||||
return nil, E.New("invalid down speed")
|
|
||||||
}
|
}
|
||||||
inbound := &Hysteria{
|
inbound := &Hysteria{
|
||||||
myInboundAdapter: myInboundAdapter{
|
myInboundAdapter: myInboundAdapter{
|
||||||
@ -113,224 +47,108 @@ func NewHysteria(ctx context.Context, router adapter.Router, logger log.ContextL
|
|||||||
tag: tag,
|
tag: tag,
|
||||||
listenOptions: options.ListenOptions,
|
listenOptions: options.ListenOptions,
|
||||||
},
|
},
|
||||||
quicConfig: quicConfig,
|
tlsConfig: tlsConfig,
|
||||||
authKey: authKey,
|
|
||||||
authUser: authUser,
|
|
||||||
xplusKey: xplus,
|
|
||||||
sendBPS: up,
|
|
||||||
recvBPS: down,
|
|
||||||
udpSessions: make(map[uint32]chan *hysteria.UDPMessage),
|
|
||||||
}
|
}
|
||||||
if options.TLS == nil || !options.TLS.Enabled {
|
var sendBps, receiveBps uint64
|
||||||
return nil, C.ErrTLSRequired
|
if len(options.Up) > 0 {
|
||||||
|
sendBps, err = humanize.ParseBytes(options.Up)
|
||||||
|
if err != nil {
|
||||||
|
return nil, E.Cause(err, "invalid up speed format: ", options.Up)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
sendBps = uint64(options.UpMbps) * hysteria.MbpsToBps
|
||||||
}
|
}
|
||||||
if len(options.TLS.ALPN) == 0 {
|
if len(options.Down) > 0 {
|
||||||
options.TLS.ALPN = []string{hysteria.DefaultALPN}
|
receiveBps, err = humanize.ParseBytes(options.Down)
|
||||||
|
if receiveBps == 0 {
|
||||||
|
return nil, E.New("invalid down speed format: ", options.Down)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
receiveBps = uint64(options.DownMbps) * hysteria.MbpsToBps
|
||||||
}
|
}
|
||||||
tlsConfig, err := tls.NewServer(ctx, logger, common.PtrValueOrDefault(options.TLS))
|
service, err := hysteria.NewService[int](hysteria.ServiceOptions{
|
||||||
|
Context: ctx,
|
||||||
|
Logger: logger,
|
||||||
|
SendBPS: sendBps,
|
||||||
|
ReceiveBPS: receiveBps,
|
||||||
|
XPlusPassword: options.Obfs,
|
||||||
|
TLSConfig: tlsConfig,
|
||||||
|
Handler: adapter.NewUpstreamHandler(adapter.InboundContext{}, inbound.newConnection, inbound.newPacketConnection, nil),
|
||||||
|
|
||||||
|
// Legacy options
|
||||||
|
|
||||||
|
ConnReceiveWindow: options.ReceiveWindowConn,
|
||||||
|
StreamReceiveWindow: options.ReceiveWindowClient,
|
||||||
|
MaxIncomingStreams: int64(options.MaxConnClient),
|
||||||
|
DisableMTUDiscovery: options.DisableMTUDiscovery,
|
||||||
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
inbound.tlsConfig = tlsConfig
|
userList := make([]int, 0, len(options.Users))
|
||||||
|
userNameList := make([]string, 0, len(options.Users))
|
||||||
|
userPasswordList := make([]string, 0, len(options.Users))
|
||||||
|
for index, user := range options.Users {
|
||||||
|
userList = append(userList, index)
|
||||||
|
userNameList = append(userNameList, user.Name)
|
||||||
|
var password string
|
||||||
|
if user.AuthString != "" {
|
||||||
|
password = user.AuthString
|
||||||
|
} else {
|
||||||
|
password = string(user.Auth)
|
||||||
|
}
|
||||||
|
userPasswordList = append(userPasswordList, password)
|
||||||
|
}
|
||||||
|
service.UpdateUsers(userList, userPasswordList)
|
||||||
|
inbound.service = service
|
||||||
|
inbound.userNameList = userNameList
|
||||||
return inbound, nil
|
return inbound, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (h *Hysteria) newConnection(ctx context.Context, conn net.Conn, metadata adapter.InboundContext) error {
|
||||||
|
ctx = log.ContextWithNewID(ctx)
|
||||||
|
metadata = h.createMetadata(conn, metadata)
|
||||||
|
userID, _ := auth.UserFromContext[int](ctx)
|
||||||
|
if userName := h.userNameList[userID]; userName != "" {
|
||||||
|
metadata.User = userName
|
||||||
|
h.logger.InfoContext(ctx, "[", userName, "] inbound connection to ", metadata.Destination)
|
||||||
|
} else {
|
||||||
|
h.logger.InfoContext(ctx, "inbound connection to ", metadata.Destination)
|
||||||
|
}
|
||||||
|
return h.router.RouteConnection(ctx, conn, metadata)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *Hysteria) newPacketConnection(ctx context.Context, conn N.PacketConn, metadata adapter.InboundContext) error {
|
||||||
|
ctx = log.ContextWithNewID(ctx)
|
||||||
|
metadata = h.createPacketMetadata(conn, metadata)
|
||||||
|
userID, _ := auth.UserFromContext[int](ctx)
|
||||||
|
if userName := h.userNameList[userID]; userName != "" {
|
||||||
|
metadata.User = userName
|
||||||
|
h.logger.InfoContext(ctx, "[", userName, "] inbound packet connection to ", metadata.Destination)
|
||||||
|
} else {
|
||||||
|
h.logger.InfoContext(ctx, "inbound packet connection to ", metadata.Destination)
|
||||||
|
}
|
||||||
|
return h.router.RoutePacketConnection(ctx, conn, metadata)
|
||||||
|
}
|
||||||
|
|
||||||
func (h *Hysteria) Start() error {
|
func (h *Hysteria) Start() error {
|
||||||
|
if h.tlsConfig != nil {
|
||||||
|
err := h.tlsConfig.Start()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
packetConn, err := h.myInboundAdapter.ListenUDP()
|
packetConn, err := h.myInboundAdapter.ListenUDP()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if len(h.xplusKey) > 0 {
|
return h.service.Start(packetConn)
|
||||||
packetConn = hysteria.NewXPlusPacketConn(packetConn, h.xplusKey)
|
|
||||||
packetConn = &hysteria.PacketConnWrapper{PacketConn: packetConn}
|
|
||||||
}
|
|
||||||
err = h.tlsConfig.Start()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
listener, err := qtls.Listen(packetConn, h.tlsConfig, h.quicConfig)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
h.listener = listener
|
|
||||||
h.logger.Info("udp server started at ", listener.Addr())
|
|
||||||
go h.acceptLoop()
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (h *Hysteria) acceptLoop() {
|
|
||||||
for {
|
|
||||||
ctx := log.ContextWithNewID(h.ctx)
|
|
||||||
conn, err := h.listener.Accept(ctx)
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
go func() {
|
|
||||||
hErr := h.accept(ctx, conn)
|
|
||||||
if hErr != nil {
|
|
||||||
conn.CloseWithError(0, "")
|
|
||||||
NewError(h.logger, ctx, E.Cause(hErr, "process connection from ", conn.RemoteAddr()))
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (h *Hysteria) accept(ctx context.Context, conn quic.Connection) error {
|
|
||||||
controlStream, err := conn.AcceptStream(ctx)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
clientHello, err := hysteria.ReadClientHello(controlStream)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if len(h.authKey) > 0 {
|
|
||||||
userIndex := slices.Index(h.authKey, string(clientHello.Auth))
|
|
||||||
if userIndex == -1 {
|
|
||||||
err = hysteria.WriteServerHello(controlStream, hysteria.ServerHello{
|
|
||||||
Message: "wrong password",
|
|
||||||
})
|
|
||||||
return E.Errors(E.New("wrong password: ", string(clientHello.Auth)), err)
|
|
||||||
}
|
|
||||||
user := h.authUser[userIndex]
|
|
||||||
if user == "" {
|
|
||||||
user = F.ToString(userIndex)
|
|
||||||
} else {
|
|
||||||
ctx = auth.ContextWithUser(ctx, user)
|
|
||||||
}
|
|
||||||
h.logger.InfoContext(ctx, "[", user, "] inbound connection from ", conn.RemoteAddr())
|
|
||||||
} else {
|
|
||||||
h.logger.InfoContext(ctx, "inbound connection from ", conn.RemoteAddr())
|
|
||||||
}
|
|
||||||
h.logger.DebugContext(ctx, "peer send speed: ", clientHello.SendBPS/1024/1024, " MBps, peer recv speed: ", clientHello.RecvBPS/1024/1024, " MBps")
|
|
||||||
if clientHello.SendBPS == 0 || clientHello.RecvBPS == 0 {
|
|
||||||
return E.New("invalid rate from client")
|
|
||||||
}
|
|
||||||
serverSendBPS, serverRecvBPS := clientHello.RecvBPS, clientHello.SendBPS
|
|
||||||
if h.sendBPS > 0 && serverSendBPS > h.sendBPS {
|
|
||||||
serverSendBPS = h.sendBPS
|
|
||||||
}
|
|
||||||
if h.recvBPS > 0 && serverRecvBPS > h.recvBPS {
|
|
||||||
serverRecvBPS = h.recvBPS
|
|
||||||
}
|
|
||||||
err = hysteria.WriteServerHello(controlStream, hysteria.ServerHello{
|
|
||||||
OK: true,
|
|
||||||
SendBPS: serverSendBPS,
|
|
||||||
RecvBPS: serverRecvBPS,
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
conn.SetCongestionControl(hyCC.NewBrutalSender(serverSendBPS))
|
|
||||||
go h.udpRecvLoop(conn)
|
|
||||||
for {
|
|
||||||
var stream quic.Stream
|
|
||||||
stream, err = conn.AcceptStream(ctx)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
go func() {
|
|
||||||
hErr := h.acceptStream(ctx, conn /*&hysteria.StreamWrapper{Stream: stream}*/, stream)
|
|
||||||
if hErr != nil {
|
|
||||||
stream.Close()
|
|
||||||
NewError(h.logger, ctx, E.Cause(hErr, "process stream from ", conn.RemoteAddr()))
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (h *Hysteria) udpRecvLoop(conn quic.Connection) {
|
|
||||||
for {
|
|
||||||
packet, err := conn.ReceiveMessage(h.ctx)
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
message, err := hysteria.ParseUDPMessage(packet)
|
|
||||||
if err != nil {
|
|
||||||
h.logger.Error("parse udp message: ", err)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
dfMsg := h.udpDefragger.Feed(message)
|
|
||||||
if dfMsg == nil {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
h.udpAccess.RLock()
|
|
||||||
ch, ok := h.udpSessions[dfMsg.SessionID]
|
|
||||||
if ok {
|
|
||||||
select {
|
|
||||||
case ch <- dfMsg:
|
|
||||||
// OK
|
|
||||||
default:
|
|
||||||
// Silently drop the message when the channel is full
|
|
||||||
}
|
|
||||||
}
|
|
||||||
h.udpAccess.RUnlock()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (h *Hysteria) acceptStream(ctx context.Context, conn quic.Connection, stream quic.Stream) error {
|
|
||||||
request, err := hysteria.ReadClientRequest(stream)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
var metadata adapter.InboundContext
|
|
||||||
metadata.Inbound = h.tag
|
|
||||||
metadata.InboundType = C.TypeHysteria
|
|
||||||
metadata.InboundOptions = h.listenOptions.InboundOptions
|
|
||||||
metadata.Source = M.SocksaddrFromNet(conn.RemoteAddr()).Unwrap()
|
|
||||||
metadata.OriginDestination = M.SocksaddrFromNet(conn.LocalAddr()).Unwrap()
|
|
||||||
metadata.Destination = M.ParseSocksaddrHostPort(request.Host, request.Port).Unwrap()
|
|
||||||
metadata.User, _ = auth.UserFromContext[string](ctx)
|
|
||||||
|
|
||||||
if !request.UDP {
|
|
||||||
err = hysteria.WriteServerResponse(stream, hysteria.ServerResponse{
|
|
||||||
OK: true,
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
h.logger.InfoContext(ctx, "inbound connection to ", metadata.Destination)
|
|
||||||
return h.router.RouteConnection(ctx, hysteria.NewConn(stream, metadata.Destination, false), metadata)
|
|
||||||
} else {
|
|
||||||
h.logger.InfoContext(ctx, "inbound packet connection to ", metadata.Destination)
|
|
||||||
var id uint32
|
|
||||||
h.udpAccess.Lock()
|
|
||||||
id = h.udpSessionId
|
|
||||||
nCh := make(chan *hysteria.UDPMessage, 1024)
|
|
||||||
h.udpSessions[id] = nCh
|
|
||||||
h.udpSessionId += 1
|
|
||||||
h.udpAccess.Unlock()
|
|
||||||
err = hysteria.WriteServerResponse(stream, hysteria.ServerResponse{
|
|
||||||
OK: true,
|
|
||||||
UDPSessionID: id,
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
packetConn := hysteria.NewPacketConn(conn, stream, id, metadata.Destination, nCh, common.Closer(func() error {
|
|
||||||
h.udpAccess.Lock()
|
|
||||||
if ch, ok := h.udpSessions[id]; ok {
|
|
||||||
close(ch)
|
|
||||||
delete(h.udpSessions, id)
|
|
||||||
}
|
|
||||||
h.udpAccess.Unlock()
|
|
||||||
return nil
|
|
||||||
}))
|
|
||||||
go packetConn.Hold()
|
|
||||||
return h.router.RoutePacketConnection(ctx, packetConn, metadata)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *Hysteria) Close() error {
|
func (h *Hysteria) Close() error {
|
||||||
h.udpAccess.Lock()
|
|
||||||
for _, session := range h.udpSessions {
|
|
||||||
close(session)
|
|
||||||
}
|
|
||||||
h.udpSessions = make(map[uint32]chan *hysteria.UDPMessage)
|
|
||||||
h.udpAccess.Unlock()
|
|
||||||
return common.Close(
|
return common.Close(
|
||||||
&h.myInboundAdapter,
|
&h.myInboundAdapter,
|
||||||
h.listener,
|
|
||||||
h.tlsConfig,
|
h.tlsConfig,
|
||||||
|
common.PtrOrNil(h.service),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -14,7 +14,7 @@ import (
|
|||||||
C "github.com/sagernet/sing-box/constant"
|
C "github.com/sagernet/sing-box/constant"
|
||||||
"github.com/sagernet/sing-box/log"
|
"github.com/sagernet/sing-box/log"
|
||||||
"github.com/sagernet/sing-box/option"
|
"github.com/sagernet/sing-box/option"
|
||||||
"github.com/sagernet/sing-box/transport/hysteria"
|
"github.com/sagernet/sing-quic/hysteria"
|
||||||
"github.com/sagernet/sing-quic/hysteria2"
|
"github.com/sagernet/sing-quic/hysteria2"
|
||||||
"github.com/sagernet/sing/common"
|
"github.com/sagernet/sing/common"
|
||||||
"github.com/sagernet/sing/common/auth"
|
"github.com/sagernet/sing/common/auth"
|
||||||
@ -32,6 +32,7 @@ type Hysteria2 struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func NewHysteria2(ctx context.Context, router adapter.Router, logger log.ContextLogger, tag string, options option.Hysteria2InboundOptions) (*Hysteria2, error) {
|
func NewHysteria2(ctx context.Context, router adapter.Router, logger log.ContextLogger, tag string, options option.Hysteria2InboundOptions) (*Hysteria2, error) {
|
||||||
|
options.UDPFragmentDefault = true
|
||||||
if options.TLS == nil || !options.TLS.Enabled {
|
if options.TLS == nil || !options.TLS.Enabled {
|
||||||
return nil, C.ErrTLSRequired
|
return nil, C.ErrTLSRequired
|
||||||
}
|
}
|
||||||
@ -89,6 +90,7 @@ func NewHysteria2(ctx context.Context, router adapter.Router, logger log.Context
|
|||||||
service, err := hysteria2.NewService[int](hysteria2.ServiceOptions{
|
service, err := hysteria2.NewService[int](hysteria2.ServiceOptions{
|
||||||
Context: ctx,
|
Context: ctx,
|
||||||
Logger: logger,
|
Logger: logger,
|
||||||
|
BrutalDebug: options.BrutalDebug,
|
||||||
SendBPS: uint64(options.UpMbps * hysteria.MbpsToBps),
|
SendBPS: uint64(options.UpMbps * hysteria.MbpsToBps),
|
||||||
ReceiveBPS: uint64(options.DownMbps * hysteria.MbpsToBps),
|
ReceiveBPS: uint64(options.DownMbps * hysteria.MbpsToBps),
|
||||||
SalamanderPassword: salamanderPassword,
|
SalamanderPassword: salamanderPassword,
|
||||||
|
@ -2,7 +2,6 @@ package inbound
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"encoding/base64"
|
|
||||||
"encoding/binary"
|
"encoding/binary"
|
||||||
"io"
|
"io"
|
||||||
"math/rand"
|
"math/rand"
|
||||||
@ -139,14 +138,9 @@ func (n *Naive) ServeHTTP(writer http.ResponseWriter, request *http.Request) {
|
|||||||
n.badRequest(ctx, request, E.New("missing naive padding"))
|
n.badRequest(ctx, request, E.New("missing naive padding"))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
var authOk bool
|
userName, password, authOk := sHttp.ParseBasicAuth(request.Header.Get("Proxy-Authorization"))
|
||||||
var userName string
|
if authOk {
|
||||||
authorization := request.Header.Get("Proxy-Authorization")
|
authOk = n.authenticator.Verify(userName, password)
|
||||||
if strings.HasPrefix(authorization, "BASIC ") || strings.HasPrefix(authorization, "Basic ") {
|
|
||||||
userPassword, _ := base64.URLEncoding.DecodeString(authorization[6:])
|
|
||||||
userPswdArr := strings.SplitN(string(userPassword), ":", 2)
|
|
||||||
userName = userPswdArr[0]
|
|
||||||
authOk = n.authenticator.Verify(userPswdArr[0], userPswdArr[1])
|
|
||||||
}
|
}
|
||||||
if !authOk {
|
if !authOk {
|
||||||
rejectHTTP(writer, http.StatusProxyAuthRequired)
|
rejectHTTP(writer, http.StatusProxyAuthRequired)
|
||||||
|
@ -73,14 +73,14 @@ func NewTun(ctx context.Context, router adapter.Router, logger log.ContextLogger
|
|||||||
tunOptions: tun.Options{
|
tunOptions: tun.Options{
|
||||||
Name: options.InterfaceName,
|
Name: options.InterfaceName,
|
||||||
MTU: tunMTU,
|
MTU: tunMTU,
|
||||||
Inet4Address: common.Map(options.Inet4Address, option.ListenPrefix.Build),
|
Inet4Address: options.Inet4Address,
|
||||||
Inet6Address: common.Map(options.Inet6Address, option.ListenPrefix.Build),
|
Inet6Address: options.Inet6Address,
|
||||||
AutoRoute: options.AutoRoute,
|
AutoRoute: options.AutoRoute,
|
||||||
StrictRoute: options.StrictRoute,
|
StrictRoute: options.StrictRoute,
|
||||||
IncludeInterface: options.IncludeInterface,
|
IncludeInterface: options.IncludeInterface,
|
||||||
ExcludeInterface: options.ExcludeInterface,
|
ExcludeInterface: options.ExcludeInterface,
|
||||||
Inet4RouteAddress: common.Map(options.Inet4RouteAddress, option.ListenPrefix.Build),
|
Inet4RouteAddress: options.Inet4RouteAddress,
|
||||||
Inet6RouteAddress: common.Map(options.Inet6RouteAddress, option.ListenPrefix.Build),
|
Inet6RouteAddress: options.Inet6RouteAddress,
|
||||||
IncludeUID: includeUID,
|
IncludeUID: includeUID,
|
||||||
ExcludeUID: excludeUID,
|
ExcludeUID: excludeUID,
|
||||||
IncludeAndroidUser: options.IncludeAndroidUser,
|
IncludeAndroidUser: options.IncludeAndroidUser,
|
||||||
|
@ -1,5 +1,7 @@
|
|||||||
package option
|
package option
|
||||||
|
|
||||||
|
import "net/netip"
|
||||||
|
|
||||||
type DNSOptions struct {
|
type DNSOptions struct {
|
||||||
Servers []DNSServerOptions `json:"servers,omitempty"`
|
Servers []DNSServerOptions `json:"servers,omitempty"`
|
||||||
Rules []DNSRule `json:"rules,omitempty"`
|
Rules []DNSRule `json:"rules,omitempty"`
|
||||||
@ -28,6 +30,6 @@ type DNSClientOptions struct {
|
|||||||
|
|
||||||
type DNSFakeIPOptions struct {
|
type DNSFakeIPOptions struct {
|
||||||
Enabled bool `json:"enabled,omitempty"`
|
Enabled bool `json:"enabled,omitempty"`
|
||||||
Inet4Range *ListenPrefix `json:"inet4_range,omitempty"`
|
Inet4Range *netip.Prefix `json:"inet4_range,omitempty"`
|
||||||
Inet6Range *ListenPrefix `json:"inet6_range,omitempty"`
|
Inet6Range *netip.Prefix `json:"inet6_range,omitempty"`
|
||||||
}
|
}
|
||||||
|
@ -9,6 +9,7 @@ type Hysteria2InboundOptions struct {
|
|||||||
IgnoreClientBandwidth bool `json:"ignore_client_bandwidth,omitempty"`
|
IgnoreClientBandwidth bool `json:"ignore_client_bandwidth,omitempty"`
|
||||||
TLS *InboundTLSOptions `json:"tls,omitempty"`
|
TLS *InboundTLSOptions `json:"tls,omitempty"`
|
||||||
Masquerade string `json:"masquerade,omitempty"`
|
Masquerade string `json:"masquerade,omitempty"`
|
||||||
|
BrutalDebug bool `json:"brutal_debug,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type Hysteria2Obfs struct {
|
type Hysteria2Obfs struct {
|
||||||
@ -24,10 +25,11 @@ type Hysteria2User struct {
|
|||||||
type Hysteria2OutboundOptions struct {
|
type Hysteria2OutboundOptions struct {
|
||||||
DialerOptions
|
DialerOptions
|
||||||
ServerOptions
|
ServerOptions
|
||||||
UpMbps int `json:"up_mbps,omitempty"`
|
UpMbps int `json:"up_mbps,omitempty"`
|
||||||
DownMbps int `json:"down_mbps,omitempty"`
|
DownMbps int `json:"down_mbps,omitempty"`
|
||||||
Obfs *Hysteria2Obfs `json:"obfs,omitempty"`
|
Obfs *Hysteria2Obfs `json:"obfs,omitempty"`
|
||||||
Password string `json:"password,omitempty"`
|
Password string `json:"password,omitempty"`
|
||||||
Network NetworkList `json:"network,omitempty"`
|
Network NetworkList `json:"network,omitempty"`
|
||||||
TLS *OutboundTLSOptions `json:"tls,omitempty"`
|
TLS *OutboundTLSOptions `json:"tls,omitempty"`
|
||||||
|
BrutalDebug bool `json:"brutal_debug,omitempty"`
|
||||||
}
|
}
|
||||||
|
@ -27,9 +27,9 @@ type SocksOutboundOptions struct {
|
|||||||
type HTTPOutboundOptions struct {
|
type HTTPOutboundOptions struct {
|
||||||
DialerOptions
|
DialerOptions
|
||||||
ServerOptions
|
ServerOptions
|
||||||
Username string `json:"username,omitempty"`
|
Username string `json:"username,omitempty"`
|
||||||
Password string `json:"password,omitempty"`
|
Password string `json:"password,omitempty"`
|
||||||
TLS *OutboundTLSOptions `json:"tls,omitempty"`
|
TLS *OutboundTLSOptions `json:"tls,omitempty"`
|
||||||
Path string `json:"path,omitempty"`
|
Path string `json:"path,omitempty"`
|
||||||
Headers map[string]Listable[string] `json:"headers,omitempty"`
|
Headers HTTPHeader `json:"headers,omitempty"`
|
||||||
}
|
}
|
||||||
|
@ -1,14 +1,16 @@
|
|||||||
package option
|
package option
|
||||||
|
|
||||||
|
import "net/netip"
|
||||||
|
|
||||||
type TunInboundOptions struct {
|
type TunInboundOptions struct {
|
||||||
InterfaceName string `json:"interface_name,omitempty"`
|
InterfaceName string `json:"interface_name,omitempty"`
|
||||||
MTU uint32 `json:"mtu,omitempty"`
|
MTU uint32 `json:"mtu,omitempty"`
|
||||||
Inet4Address Listable[ListenPrefix] `json:"inet4_address,omitempty"`
|
Inet4Address Listable[netip.Prefix] `json:"inet4_address,omitempty"`
|
||||||
Inet6Address Listable[ListenPrefix] `json:"inet6_address,omitempty"`
|
Inet6Address Listable[netip.Prefix] `json:"inet6_address,omitempty"`
|
||||||
AutoRoute bool `json:"auto_route,omitempty"`
|
AutoRoute bool `json:"auto_route,omitempty"`
|
||||||
StrictRoute bool `json:"strict_route,omitempty"`
|
StrictRoute bool `json:"strict_route,omitempty"`
|
||||||
Inet4RouteAddress Listable[ListenPrefix] `json:"inet4_route_address,omitempty"`
|
Inet4RouteAddress Listable[netip.Prefix] `json:"inet4_route_address,omitempty"`
|
||||||
Inet6RouteAddress Listable[ListenPrefix] `json:"inet6_route_address,omitempty"`
|
Inet6RouteAddress Listable[netip.Prefix] `json:"inet6_route_address,omitempty"`
|
||||||
IncludeInterface Listable[string] `json:"include_interface,omitempty"`
|
IncludeInterface Listable[string] `json:"include_interface,omitempty"`
|
||||||
ExcludeInterface Listable[string] `json:"exclude_interface,omitempty"`
|
ExcludeInterface Listable[string] `json:"exclude_interface,omitempty"`
|
||||||
IncludeUID Listable[uint32] `json:"include_uid,omitempty"`
|
IncludeUID Listable[uint32] `json:"include_uid,omitempty"`
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
package option
|
package option
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"net/http"
|
||||||
"net/netip"
|
"net/netip"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
@ -171,34 +172,6 @@ func (d *Duration) UnmarshalJSON(bytes []byte) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
type ListenPrefix netip.Prefix
|
|
||||||
|
|
||||||
func (p ListenPrefix) MarshalJSON() ([]byte, error) {
|
|
||||||
prefix := netip.Prefix(p)
|
|
||||||
if !prefix.IsValid() {
|
|
||||||
return json.Marshal(nil)
|
|
||||||
}
|
|
||||||
return json.Marshal(prefix.String())
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *ListenPrefix) UnmarshalJSON(bytes []byte) error {
|
|
||||||
var value string
|
|
||||||
err := json.Unmarshal(bytes, &value)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
prefix, err := netip.ParsePrefix(value)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
*p = ListenPrefix(prefix)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p ListenPrefix) Build() netip.Prefix {
|
|
||||||
return netip.Prefix(p)
|
|
||||||
}
|
|
||||||
|
|
||||||
type DNSQueryType uint16
|
type DNSQueryType uint16
|
||||||
|
|
||||||
func (t DNSQueryType) MarshalJSON() ([]byte, error) {
|
func (t DNSQueryType) MarshalJSON() ([]byte, error) {
|
||||||
@ -235,3 +208,15 @@ func DNSQueryTypeToString(queryType uint16) string {
|
|||||||
}
|
}
|
||||||
return F.ToString(queryType)
|
return F.ToString(queryType)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type HTTPHeader map[string]Listable[string]
|
||||||
|
|
||||||
|
func (h HTTPHeader) Build() http.Header {
|
||||||
|
header := make(http.Header)
|
||||||
|
for name, values := range h {
|
||||||
|
for _, value := range values {
|
||||||
|
header.Add(name, value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return header
|
||||||
|
}
|
||||||
|
@ -61,19 +61,19 @@ func (o *V2RayTransportOptions) UnmarshalJSON(bytes []byte) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type V2RayHTTPOptions struct {
|
type V2RayHTTPOptions struct {
|
||||||
Host Listable[string] `json:"host,omitempty"`
|
Host Listable[string] `json:"host,omitempty"`
|
||||||
Path string `json:"path,omitempty"`
|
Path string `json:"path,omitempty"`
|
||||||
Method string `json:"method,omitempty"`
|
Method string `json:"method,omitempty"`
|
||||||
Headers map[string]Listable[string] `json:"headers,omitempty"`
|
Headers HTTPHeader `json:"headers,omitempty"`
|
||||||
IdleTimeout Duration `json:"idle_timeout,omitempty"`
|
IdleTimeout Duration `json:"idle_timeout,omitempty"`
|
||||||
PingTimeout Duration `json:"ping_timeout,omitempty"`
|
PingTimeout Duration `json:"ping_timeout,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type V2RayWebsocketOptions struct {
|
type V2RayWebsocketOptions struct {
|
||||||
Path string `json:"path,omitempty"`
|
Path string `json:"path,omitempty"`
|
||||||
Headers map[string]Listable[string] `json:"headers,omitempty"`
|
Headers HTTPHeader `json:"headers,omitempty"`
|
||||||
MaxEarlyData uint32 `json:"max_early_data,omitempty"`
|
MaxEarlyData uint32 `json:"max_early_data,omitempty"`
|
||||||
EarlyDataHeaderName string `json:"early_data_header_name,omitempty"`
|
EarlyDataHeaderName string `json:"early_data_header_name,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type V2RayQUICOptions struct{}
|
type V2RayQUICOptions struct{}
|
||||||
|
@ -1,10 +1,12 @@
|
|||||||
package option
|
package option
|
||||||
|
|
||||||
|
import "net/netip"
|
||||||
|
|
||||||
type WireGuardOutboundOptions struct {
|
type WireGuardOutboundOptions struct {
|
||||||
DialerOptions
|
DialerOptions
|
||||||
SystemInterface bool `json:"system_interface,omitempty"`
|
SystemInterface bool `json:"system_interface,omitempty"`
|
||||||
InterfaceName string `json:"interface_name,omitempty"`
|
InterfaceName string `json:"interface_name,omitempty"`
|
||||||
LocalAddress Listable[ListenPrefix] `json:"local_address"`
|
LocalAddress Listable[netip.Prefix] `json:"local_address"`
|
||||||
PrivateKey string `json:"private_key"`
|
PrivateKey string `json:"private_key"`
|
||||||
Peers []WireGuardPeer `json:"peers,omitempty"`
|
Peers []WireGuardPeer `json:"peers,omitempty"`
|
||||||
ServerOptions
|
ServerOptions
|
||||||
|
@ -17,8 +17,6 @@ import (
|
|||||||
E "github.com/sagernet/sing/common/exceptions"
|
E "github.com/sagernet/sing/common/exceptions"
|
||||||
M "github.com/sagernet/sing/common/metadata"
|
M "github.com/sagernet/sing/common/metadata"
|
||||||
N "github.com/sagernet/sing/common/network"
|
N "github.com/sagernet/sing/common/network"
|
||||||
|
|
||||||
"github.com/pires/go-proxyproto"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
@ -33,7 +31,6 @@ type Direct struct {
|
|||||||
fallbackDelay time.Duration
|
fallbackDelay time.Duration
|
||||||
overrideOption int
|
overrideOption int
|
||||||
overrideDestination M.Socksaddr
|
overrideDestination M.Socksaddr
|
||||||
proxyProto uint8
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewDirect(router adapter.Router, logger log.ContextLogger, tag string, options option.DirectOutboundOptions) (*Direct, error) {
|
func NewDirect(router adapter.Router, logger log.ContextLogger, tag string, options option.DirectOutboundOptions) (*Direct, error) {
|
||||||
@ -54,10 +51,9 @@ func NewDirect(router adapter.Router, logger log.ContextLogger, tag string, opti
|
|||||||
domainStrategy: dns.DomainStrategy(options.DomainStrategy),
|
domainStrategy: dns.DomainStrategy(options.DomainStrategy),
|
||||||
fallbackDelay: time.Duration(options.FallbackDelay),
|
fallbackDelay: time.Duration(options.FallbackDelay),
|
||||||
dialer: outboundDialer,
|
dialer: outboundDialer,
|
||||||
proxyProto: options.ProxyProtocol,
|
|
||||||
}
|
}
|
||||||
if options.ProxyProtocol > 2 {
|
if options.ProxyProtocol != 0 {
|
||||||
return nil, E.New("invalid proxy protocol option: ", options.ProxyProtocol)
|
return nil, E.New("Proxy Protocol is deprecated and removed in sing-box 1.6.0")
|
||||||
}
|
}
|
||||||
if options.OverrideAddress != "" && options.OverridePort != 0 {
|
if options.OverrideAddress != "" && options.OverridePort != 0 {
|
||||||
outbound.overrideOption = 1
|
outbound.overrideOption = 1
|
||||||
@ -74,7 +70,6 @@ func NewDirect(router adapter.Router, logger log.ContextLogger, tag string, opti
|
|||||||
|
|
||||||
func (h *Direct) DialContext(ctx context.Context, network string, destination M.Socksaddr) (net.Conn, error) {
|
func (h *Direct) DialContext(ctx context.Context, network string, destination M.Socksaddr) (net.Conn, error) {
|
||||||
ctx, metadata := adapter.AppendContext(ctx)
|
ctx, metadata := adapter.AppendContext(ctx)
|
||||||
originDestination := metadata.Destination
|
|
||||||
metadata.Outbound = h.tag
|
metadata.Outbound = h.tag
|
||||||
metadata.Destination = destination
|
metadata.Destination = destination
|
||||||
switch h.overrideOption {
|
switch h.overrideOption {
|
||||||
@ -94,31 +89,11 @@ func (h *Direct) DialContext(ctx context.Context, network string, destination M.
|
|||||||
case N.NetworkUDP:
|
case N.NetworkUDP:
|
||||||
h.logger.InfoContext(ctx, "outbound packet connection to ", destination)
|
h.logger.InfoContext(ctx, "outbound packet connection to ", destination)
|
||||||
}
|
}
|
||||||
conn, err := h.dialer.DialContext(ctx, network, destination)
|
return h.dialer.DialContext(ctx, network, destination)
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
if h.proxyProto > 0 {
|
|
||||||
source := metadata.Source
|
|
||||||
if !source.IsValid() {
|
|
||||||
source = M.SocksaddrFromNet(conn.LocalAddr())
|
|
||||||
}
|
|
||||||
if originDestination.Addr.Is6() {
|
|
||||||
source = M.SocksaddrFrom(netip.AddrFrom16(source.Addr.As16()), source.Port)
|
|
||||||
}
|
|
||||||
header := proxyproto.HeaderProxyFromAddrs(h.proxyProto, source.TCPAddr(), originDestination.TCPAddr())
|
|
||||||
_, err = header.WriteTo(conn)
|
|
||||||
if err != nil {
|
|
||||||
conn.Close()
|
|
||||||
return nil, E.Cause(err, "write proxy protocol header")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return conn, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *Direct) DialParallel(ctx context.Context, network string, destination M.Socksaddr, destinationAddresses []netip.Addr) (net.Conn, error) {
|
func (h *Direct) DialParallel(ctx context.Context, network string, destination M.Socksaddr, destinationAddresses []netip.Addr) (net.Conn, error) {
|
||||||
ctx, metadata := adapter.AppendContext(ctx)
|
ctx, metadata := adapter.AppendContext(ctx)
|
||||||
originDestination := metadata.Destination
|
|
||||||
metadata.Outbound = h.tag
|
metadata.Outbound = h.tag
|
||||||
metadata.Destination = destination
|
metadata.Destination = destination
|
||||||
switch h.overrideOption {
|
switch h.overrideOption {
|
||||||
@ -141,26 +116,7 @@ func (h *Direct) DialParallel(ctx context.Context, network string, destination M
|
|||||||
} else {
|
} else {
|
||||||
domainStrategy = dns.DomainStrategy(metadata.InboundOptions.DomainStrategy)
|
domainStrategy = dns.DomainStrategy(metadata.InboundOptions.DomainStrategy)
|
||||||
}
|
}
|
||||||
conn, err := N.DialParallel(ctx, h.dialer, network, destination, destinationAddresses, domainStrategy == dns.DomainStrategyPreferIPv6, h.fallbackDelay)
|
return N.DialParallel(ctx, h.dialer, network, destination, destinationAddresses, domainStrategy == dns.DomainStrategyPreferIPv6, h.fallbackDelay)
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
if h.proxyProto > 0 {
|
|
||||||
source := metadata.Source
|
|
||||||
if !source.IsValid() {
|
|
||||||
source = M.SocksaddrFromNet(conn.LocalAddr())
|
|
||||||
}
|
|
||||||
if originDestination.Addr.Is6() {
|
|
||||||
source = M.SocksaddrFrom(netip.AddrFrom16(source.Addr.As16()), source.Port)
|
|
||||||
}
|
|
||||||
header := proxyproto.HeaderProxyFromAddrs(h.proxyProto, source.TCPAddr(), originDestination.TCPAddr())
|
|
||||||
_, err = header.WriteTo(conn)
|
|
||||||
if err != nil {
|
|
||||||
conn.Close()
|
|
||||||
return nil, E.Cause(err, "write proxy protocol header")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return conn, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *Direct) ListenPacket(ctx context.Context, destination M.Socksaddr) (net.PacketConn, error) {
|
func (h *Direct) ListenPacket(ctx context.Context, destination M.Socksaddr) (net.PacketConn, error) {
|
||||||
|
@ -3,7 +3,6 @@ package outbound
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"net"
|
"net"
|
||||||
"net/http"
|
|
||||||
"os"
|
"os"
|
||||||
|
|
||||||
"github.com/sagernet/sing-box/adapter"
|
"github.com/sagernet/sing-box/adapter"
|
||||||
@ -34,13 +33,6 @@ func NewHTTP(ctx context.Context, router adapter.Router, logger log.ContextLogge
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
var headers http.Header
|
|
||||||
if options.Headers != nil {
|
|
||||||
headers = make(http.Header)
|
|
||||||
for key, values := range options.Headers {
|
|
||||||
headers[key] = values
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return &HTTP{
|
return &HTTP{
|
||||||
myOutboundAdapter{
|
myOutboundAdapter{
|
||||||
protocol: C.TypeHTTP,
|
protocol: C.TypeHTTP,
|
||||||
@ -56,7 +48,7 @@ func NewHTTP(ctx context.Context, router adapter.Router, logger log.ContextLogge
|
|||||||
Username: options.Username,
|
Username: options.Username,
|
||||||
Password: options.Password,
|
Password: options.Password,
|
||||||
Path: options.Path,
|
Path: options.Path,
|
||||||
Headers: headers,
|
Headers: options.Headers.Build(),
|
||||||
}),
|
}),
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
@ -5,18 +5,16 @@ package outbound
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"net"
|
"net"
|
||||||
"sync"
|
"os"
|
||||||
|
|
||||||
"github.com/sagernet/quic-go"
|
|
||||||
"github.com/sagernet/sing-box/adapter"
|
"github.com/sagernet/sing-box/adapter"
|
||||||
"github.com/sagernet/sing-box/common/dialer"
|
"github.com/sagernet/sing-box/common/dialer"
|
||||||
|
"github.com/sagernet/sing-box/common/humanize"
|
||||||
"github.com/sagernet/sing-box/common/tls"
|
"github.com/sagernet/sing-box/common/tls"
|
||||||
C "github.com/sagernet/sing-box/constant"
|
C "github.com/sagernet/sing-box/constant"
|
||||||
"github.com/sagernet/sing-box/log"
|
"github.com/sagernet/sing-box/log"
|
||||||
"github.com/sagernet/sing-box/option"
|
"github.com/sagernet/sing-box/option"
|
||||||
"github.com/sagernet/sing-box/transport/hysteria"
|
"github.com/sagernet/sing-quic/hysteria"
|
||||||
"github.com/sagernet/sing-quic"
|
|
||||||
hyCC "github.com/sagernet/sing-quic/hysteria2/congestion"
|
|
||||||
"github.com/sagernet/sing/common"
|
"github.com/sagernet/sing/common"
|
||||||
"github.com/sagernet/sing/common/bufio"
|
"github.com/sagernet/sing/common/bufio"
|
||||||
E "github.com/sagernet/sing/common/exceptions"
|
E "github.com/sagernet/sing/common/exceptions"
|
||||||
@ -25,27 +23,13 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
_ adapter.Outbound = (*Hysteria)(nil)
|
_ adapter.Outbound = (*TUIC)(nil)
|
||||||
_ adapter.InterfaceUpdateListener = (*Hysteria)(nil)
|
_ adapter.InterfaceUpdateListener = (*TUIC)(nil)
|
||||||
)
|
)
|
||||||
|
|
||||||
type Hysteria struct {
|
type Hysteria struct {
|
||||||
myOutboundAdapter
|
myOutboundAdapter
|
||||||
ctx context.Context
|
client *hysteria.Client
|
||||||
dialer N.Dialer
|
|
||||||
serverAddr M.Socksaddr
|
|
||||||
tlsConfig tls.Config
|
|
||||||
quicConfig *quic.Config
|
|
||||||
authKey []byte
|
|
||||||
xplusKey []byte
|
|
||||||
sendBPS uint64
|
|
||||||
recvBPS uint64
|
|
||||||
connAccess sync.Mutex
|
|
||||||
conn quic.Connection
|
|
||||||
rawConn net.Conn
|
|
||||||
udpAccess sync.RWMutex
|
|
||||||
udpSessions map[uint32]chan *hysteria.UDPMessage
|
|
||||||
udpDefragger hysteria.Defragger
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewHysteria(ctx context.Context, router adapter.Router, logger log.ContextLogger, tag string, options option.HysteriaOutboundOptions) (*Hysteria, error) {
|
func NewHysteria(ctx context.Context, router adapter.Router, logger log.ContextLogger, tag string, options option.HysteriaOutboundOptions) (*Hysteria, error) {
|
||||||
@ -57,252 +41,77 @@ func NewHysteria(ctx context.Context, router adapter.Router, logger log.ContextL
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
if len(tlsConfig.NextProtos()) == 0 {
|
outboundDialer, err := dialer.New(router, options.DialerOptions)
|
||||||
tlsConfig.SetNextProtos([]string{hysteria.DefaultALPN})
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
}
|
}
|
||||||
quicConfig := &quic.Config{
|
networkList := options.Network.Build()
|
||||||
InitialStreamReceiveWindow: options.ReceiveWindowConn,
|
var password string
|
||||||
MaxStreamReceiveWindow: options.ReceiveWindowConn,
|
if options.AuthString != "" {
|
||||||
InitialConnectionReceiveWindow: options.ReceiveWindow,
|
password = options.AuthString
|
||||||
MaxConnectionReceiveWindow: options.ReceiveWindow,
|
|
||||||
KeepAlivePeriod: hysteria.KeepAlivePeriod,
|
|
||||||
DisablePathMTUDiscovery: options.DisableMTUDiscovery,
|
|
||||||
EnableDatagrams: true,
|
|
||||||
}
|
|
||||||
if options.ReceiveWindowConn == 0 {
|
|
||||||
quicConfig.InitialStreamReceiveWindow = hysteria.DefaultStreamReceiveWindow
|
|
||||||
quicConfig.MaxStreamReceiveWindow = hysteria.DefaultStreamReceiveWindow
|
|
||||||
}
|
|
||||||
if options.ReceiveWindow == 0 {
|
|
||||||
quicConfig.InitialConnectionReceiveWindow = hysteria.DefaultConnectionReceiveWindow
|
|
||||||
quicConfig.MaxConnectionReceiveWindow = hysteria.DefaultConnectionReceiveWindow
|
|
||||||
}
|
|
||||||
if quicConfig.MaxIncomingStreams == 0 {
|
|
||||||
quicConfig.MaxIncomingStreams = hysteria.DefaultMaxIncomingStreams
|
|
||||||
}
|
|
||||||
var auth []byte
|
|
||||||
if len(options.Auth) > 0 {
|
|
||||||
auth = options.Auth
|
|
||||||
} else {
|
} else {
|
||||||
auth = []byte(options.AuthString)
|
password = string(options.Auth)
|
||||||
}
|
}
|
||||||
var xplus []byte
|
var sendBps, receiveBps uint64
|
||||||
if options.Obfs != "" {
|
|
||||||
xplus = []byte(options.Obfs)
|
|
||||||
}
|
|
||||||
var up, down uint64
|
|
||||||
if len(options.Up) > 0 {
|
if len(options.Up) > 0 {
|
||||||
up = hysteria.StringToBps(options.Up)
|
sendBps, err = humanize.ParseBytes(options.Up)
|
||||||
if up == 0 {
|
if err != nil {
|
||||||
return nil, E.New("invalid up speed format: ", options.Up)
|
return nil, E.Cause(err, "invalid up speed format: ", options.Up)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
up = uint64(options.UpMbps) * hysteria.MbpsToBps
|
sendBps = uint64(options.UpMbps) * hysteria.MbpsToBps
|
||||||
}
|
}
|
||||||
if len(options.Down) > 0 {
|
if len(options.Down) > 0 {
|
||||||
down = hysteria.StringToBps(options.Down)
|
receiveBps, err = humanize.ParseBytes(options.Down)
|
||||||
if down == 0 {
|
if receiveBps == 0 {
|
||||||
return nil, E.New("invalid down speed format: ", options.Down)
|
return nil, E.New("invalid down speed format: ", options.Down)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
down = uint64(options.DownMbps) * hysteria.MbpsToBps
|
receiveBps = uint64(options.DownMbps) * hysteria.MbpsToBps
|
||||||
}
|
}
|
||||||
if up < hysteria.MinSpeedBPS {
|
client, err := hysteria.NewClient(hysteria.ClientOptions{
|
||||||
return nil, E.New("invalid up speed")
|
Context: ctx,
|
||||||
}
|
Dialer: outboundDialer,
|
||||||
if down < hysteria.MinSpeedBPS {
|
Logger: logger,
|
||||||
return nil, E.New("invalid down speed")
|
ServerAddress: options.ServerOptions.Build(),
|
||||||
}
|
SendBPS: sendBps,
|
||||||
outboundDialer, err := dialer.New(router, options.DialerOptions)
|
ReceiveBPS: receiveBps,
|
||||||
|
XPlusPassword: options.Obfs,
|
||||||
|
Password: password,
|
||||||
|
TLSConfig: tlsConfig,
|
||||||
|
UDPDisabled: !common.Contains(networkList, N.NetworkUDP),
|
||||||
|
|
||||||
|
ConnReceiveWindow: options.ReceiveWindowConn,
|
||||||
|
StreamReceiveWindow: options.ReceiveWindow,
|
||||||
|
DisableMTUDiscovery: options.DisableMTUDiscovery,
|
||||||
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
return &Hysteria{
|
return &Hysteria{
|
||||||
myOutboundAdapter: myOutboundAdapter{
|
myOutboundAdapter: myOutboundAdapter{
|
||||||
protocol: C.TypeHysteria,
|
protocol: C.TypeHysteria,
|
||||||
network: options.Network.Build(),
|
network: networkList,
|
||||||
router: router,
|
router: router,
|
||||||
logger: logger,
|
logger: logger,
|
||||||
tag: tag,
|
tag: tag,
|
||||||
dependencies: withDialerDependency(options.DialerOptions),
|
dependencies: withDialerDependency(options.DialerOptions),
|
||||||
},
|
},
|
||||||
ctx: ctx,
|
client: client,
|
||||||
dialer: outboundDialer,
|
|
||||||
serverAddr: options.ServerOptions.Build(),
|
|
||||||
tlsConfig: tlsConfig,
|
|
||||||
quicConfig: quicConfig,
|
|
||||||
authKey: auth,
|
|
||||||
xplusKey: xplus,
|
|
||||||
sendBPS: up,
|
|
||||||
recvBPS: down,
|
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *Hysteria) offer(ctx context.Context) (quic.Connection, error) {
|
|
||||||
conn := h.conn
|
|
||||||
if conn != nil && !common.Done(conn.Context()) {
|
|
||||||
return conn, nil
|
|
||||||
}
|
|
||||||
h.connAccess.Lock()
|
|
||||||
defer h.connAccess.Unlock()
|
|
||||||
h.udpAccess.Lock()
|
|
||||||
defer h.udpAccess.Unlock()
|
|
||||||
conn = h.conn
|
|
||||||
if conn != nil && !common.Done(conn.Context()) {
|
|
||||||
return conn, nil
|
|
||||||
}
|
|
||||||
common.Close(h.rawConn)
|
|
||||||
conn, err := h.offerNew(ctx)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
if common.Contains(h.network, N.NetworkUDP) {
|
|
||||||
for _, session := range h.udpSessions {
|
|
||||||
close(session)
|
|
||||||
}
|
|
||||||
h.udpSessions = make(map[uint32]chan *hysteria.UDPMessage)
|
|
||||||
h.udpDefragger = hysteria.Defragger{}
|
|
||||||
go h.udpRecvLoop(conn)
|
|
||||||
}
|
|
||||||
return conn, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (h *Hysteria) offerNew(ctx context.Context) (quic.Connection, error) {
|
|
||||||
udpConn, err := h.dialer.DialContext(h.ctx, "udp", h.serverAddr)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
var packetConn net.PacketConn
|
|
||||||
packetConn = bufio.NewUnbindPacketConn(udpConn)
|
|
||||||
if h.xplusKey != nil {
|
|
||||||
packetConn = hysteria.NewXPlusPacketConn(packetConn, h.xplusKey)
|
|
||||||
}
|
|
||||||
packetConn = &hysteria.PacketConnWrapper{PacketConn: packetConn}
|
|
||||||
quicConn, err := qtls.Dial(h.ctx, packetConn, udpConn.RemoteAddr(), h.tlsConfig, h.quicConfig)
|
|
||||||
if err != nil {
|
|
||||||
packetConn.Close()
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
controlStream, err := quicConn.OpenStreamSync(ctx)
|
|
||||||
if err != nil {
|
|
||||||
packetConn.Close()
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
err = hysteria.WriteClientHello(controlStream, hysteria.ClientHello{
|
|
||||||
SendBPS: h.sendBPS,
|
|
||||||
RecvBPS: h.recvBPS,
|
|
||||||
Auth: h.authKey,
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
packetConn.Close()
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
serverHello, err := hysteria.ReadServerHello(controlStream)
|
|
||||||
if err != nil {
|
|
||||||
packetConn.Close()
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
if !serverHello.OK {
|
|
||||||
packetConn.Close()
|
|
||||||
return nil, E.New("remote error: ", serverHello.Message)
|
|
||||||
}
|
|
||||||
quicConn.SetCongestionControl(hyCC.NewBrutalSender(serverHello.RecvBPS))
|
|
||||||
h.conn = quicConn
|
|
||||||
h.rawConn = udpConn
|
|
||||||
return quicConn, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (h *Hysteria) udpRecvLoop(conn quic.Connection) {
|
|
||||||
for {
|
|
||||||
packet, err := conn.ReceiveMessage(h.ctx)
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
message, err := hysteria.ParseUDPMessage(packet)
|
|
||||||
if err != nil {
|
|
||||||
h.logger.Error("parse udp message: ", err)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
dfMsg := h.udpDefragger.Feed(message)
|
|
||||||
if dfMsg == nil {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
h.udpAccess.RLock()
|
|
||||||
ch, ok := h.udpSessions[dfMsg.SessionID]
|
|
||||||
if ok {
|
|
||||||
select {
|
|
||||||
case ch <- dfMsg:
|
|
||||||
// OK
|
|
||||||
default:
|
|
||||||
// Silently drop the message when the channel is full
|
|
||||||
}
|
|
||||||
}
|
|
||||||
h.udpAccess.RUnlock()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (h *Hysteria) InterfaceUpdated() {
|
|
||||||
h.Close()
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func (h *Hysteria) Close() error {
|
|
||||||
h.connAccess.Lock()
|
|
||||||
defer h.connAccess.Unlock()
|
|
||||||
h.udpAccess.Lock()
|
|
||||||
defer h.udpAccess.Unlock()
|
|
||||||
if h.conn != nil {
|
|
||||||
h.conn.CloseWithError(0, "")
|
|
||||||
h.rawConn.Close()
|
|
||||||
}
|
|
||||||
for _, session := range h.udpSessions {
|
|
||||||
close(session)
|
|
||||||
}
|
|
||||||
h.udpSessions = make(map[uint32]chan *hysteria.UDPMessage)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (h *Hysteria) open(ctx context.Context, reconnect bool) (quic.Connection, quic.Stream, error) {
|
|
||||||
conn, err := h.offer(ctx)
|
|
||||||
if err != nil {
|
|
||||||
if nErr, ok := err.(net.Error); ok && !nErr.Temporary() && reconnect {
|
|
||||||
return h.open(ctx, false)
|
|
||||||
}
|
|
||||||
return nil, nil, err
|
|
||||||
}
|
|
||||||
stream, err := conn.OpenStream()
|
|
||||||
if err != nil {
|
|
||||||
if nErr, ok := err.(net.Error); ok && !nErr.Temporary() && reconnect {
|
|
||||||
return h.open(ctx, false)
|
|
||||||
}
|
|
||||||
return nil, nil, err
|
|
||||||
}
|
|
||||||
return conn, &hysteria.StreamWrapper{Stream: stream}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (h *Hysteria) DialContext(ctx context.Context, network string, destination M.Socksaddr) (net.Conn, error) {
|
func (h *Hysteria) DialContext(ctx context.Context, network string, destination M.Socksaddr) (net.Conn, error) {
|
||||||
switch N.NetworkName(network) {
|
switch N.NetworkName(network) {
|
||||||
case N.NetworkTCP:
|
case N.NetworkTCP:
|
||||||
h.logger.InfoContext(ctx, "outbound connection to ", destination)
|
h.logger.InfoContext(ctx, "outbound connection to ", destination)
|
||||||
_, stream, err := h.open(ctx, true)
|
return h.client.DialConn(ctx, destination)
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
err = hysteria.WriteClientRequest(stream, hysteria.ClientRequest{
|
|
||||||
Host: destination.AddrString(),
|
|
||||||
Port: destination.Port,
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
stream.Close()
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return hysteria.NewConn(stream, destination, true), nil
|
|
||||||
case N.NetworkUDP:
|
case N.NetworkUDP:
|
||||||
conn, err := h.ListenPacket(ctx, destination)
|
conn, err := h.ListenPacket(ctx, destination)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
return conn.(*hysteria.PacketConn), nil
|
return bufio.NewBindPacketConn(conn, destination), nil
|
||||||
default:
|
default:
|
||||||
return nil, E.New("unsupported network: ", network)
|
return nil, E.New("unsupported network: ", network)
|
||||||
}
|
}
|
||||||
@ -310,44 +119,7 @@ func (h *Hysteria) DialContext(ctx context.Context, network string, destination
|
|||||||
|
|
||||||
func (h *Hysteria) ListenPacket(ctx context.Context, destination M.Socksaddr) (net.PacketConn, error) {
|
func (h *Hysteria) ListenPacket(ctx context.Context, destination M.Socksaddr) (net.PacketConn, error) {
|
||||||
h.logger.InfoContext(ctx, "outbound packet connection to ", destination)
|
h.logger.InfoContext(ctx, "outbound packet connection to ", destination)
|
||||||
conn, stream, err := h.open(ctx, true)
|
return h.client.ListenPacket(ctx, destination)
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
err = hysteria.WriteClientRequest(stream, hysteria.ClientRequest{
|
|
||||||
UDP: true,
|
|
||||||
Host: destination.AddrString(),
|
|
||||||
Port: destination.Port,
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
stream.Close()
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
var response *hysteria.ServerResponse
|
|
||||||
response, err = hysteria.ReadServerResponse(stream)
|
|
||||||
if err != nil {
|
|
||||||
stream.Close()
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
if !response.OK {
|
|
||||||
stream.Close()
|
|
||||||
return nil, E.New("remote error: ", response.Message)
|
|
||||||
}
|
|
||||||
h.udpAccess.Lock()
|
|
||||||
nCh := make(chan *hysteria.UDPMessage, 1024)
|
|
||||||
h.udpSessions[response.UDPSessionID] = nCh
|
|
||||||
h.udpAccess.Unlock()
|
|
||||||
packetConn := hysteria.NewPacketConn(conn, stream, response.UDPSessionID, destination, nCh, common.Closer(func() error {
|
|
||||||
h.udpAccess.Lock()
|
|
||||||
if ch, ok := h.udpSessions[response.UDPSessionID]; ok {
|
|
||||||
close(ch)
|
|
||||||
delete(h.udpSessions, response.UDPSessionID)
|
|
||||||
}
|
|
||||||
h.udpAccess.Unlock()
|
|
||||||
return nil
|
|
||||||
}))
|
|
||||||
go packetConn.Hold()
|
|
||||||
return packetConn, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *Hysteria) NewConnection(ctx context.Context, conn net.Conn, metadata adapter.InboundContext) error {
|
func (h *Hysteria) NewConnection(ctx context.Context, conn net.Conn, metadata adapter.InboundContext) error {
|
||||||
@ -357,3 +129,11 @@ func (h *Hysteria) NewConnection(ctx context.Context, conn net.Conn, metadata ad
|
|||||||
func (h *Hysteria) NewPacketConnection(ctx context.Context, conn N.PacketConn, metadata adapter.InboundContext) error {
|
func (h *Hysteria) NewPacketConnection(ctx context.Context, conn N.PacketConn, metadata adapter.InboundContext) error {
|
||||||
return NewPacketConnection(ctx, h, conn, metadata)
|
return NewPacketConnection(ctx, h, conn, metadata)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (h *Hysteria) InterfaceUpdated() error {
|
||||||
|
return h.client.CloseWithError(E.New("network changed"))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *Hysteria) Close() error {
|
||||||
|
return h.client.CloseWithError(os.ErrClosed)
|
||||||
|
}
|
||||||
|
@ -13,7 +13,7 @@ import (
|
|||||||
C "github.com/sagernet/sing-box/constant"
|
C "github.com/sagernet/sing-box/constant"
|
||||||
"github.com/sagernet/sing-box/log"
|
"github.com/sagernet/sing-box/log"
|
||||||
"github.com/sagernet/sing-box/option"
|
"github.com/sagernet/sing-box/option"
|
||||||
"github.com/sagernet/sing-box/transport/hysteria"
|
"github.com/sagernet/sing-quic/hysteria"
|
||||||
"github.com/sagernet/sing-quic/hysteria2"
|
"github.com/sagernet/sing-quic/hysteria2"
|
||||||
"github.com/sagernet/sing/common"
|
"github.com/sagernet/sing/common"
|
||||||
"github.com/sagernet/sing/common/bufio"
|
"github.com/sagernet/sing/common/bufio"
|
||||||
@ -61,6 +61,8 @@ func NewHysteria2(ctx context.Context, router adapter.Router, logger log.Context
|
|||||||
client, err := hysteria2.NewClient(hysteria2.ClientOptions{
|
client, err := hysteria2.NewClient(hysteria2.ClientOptions{
|
||||||
Context: ctx,
|
Context: ctx,
|
||||||
Dialer: outboundDialer,
|
Dialer: outboundDialer,
|
||||||
|
Logger: logger,
|
||||||
|
BrutalDebug: options.BrutalDebug,
|
||||||
ServerAddress: options.ServerOptions.Build(),
|
ServerAddress: options.ServerOptions.Build(),
|
||||||
SendBPS: uint64(options.UpMbps * hysteria.MbpsToBps),
|
SendBPS: uint64(options.UpMbps * hysteria.MbpsToBps),
|
||||||
ReceiveBPS: uint64(options.DownMbps * hysteria.MbpsToBps),
|
ReceiveBPS: uint64(options.DownMbps * hysteria.MbpsToBps),
|
||||||
|
@ -4,192 +4,15 @@ package outbound
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"errors"
|
"os"
|
||||||
"fmt"
|
|
||||||
"net"
|
|
||||||
|
|
||||||
"github.com/sagernet/sing-box/adapter"
|
"github.com/sagernet/sing-box/adapter"
|
||||||
"github.com/sagernet/sing-box/common/dialer"
|
|
||||||
C "github.com/sagernet/sing-box/constant"
|
|
||||||
"github.com/sagernet/sing-box/log"
|
"github.com/sagernet/sing-box/log"
|
||||||
"github.com/sagernet/sing-box/option"
|
"github.com/sagernet/sing-box/option"
|
||||||
"github.com/sagernet/sing-box/transport/clashssr/obfs"
|
|
||||||
"github.com/sagernet/sing-box/transport/clashssr/protocol"
|
|
||||||
"github.com/sagernet/sing/common/bufio"
|
|
||||||
E "github.com/sagernet/sing/common/exceptions"
|
|
||||||
M "github.com/sagernet/sing/common/metadata"
|
|
||||||
N "github.com/sagernet/sing/common/network"
|
|
||||||
|
|
||||||
"github.com/Dreamacro/clash/transport/shadowsocks/core"
|
|
||||||
"github.com/Dreamacro/clash/transport/shadowsocks/shadowstream"
|
|
||||||
"github.com/Dreamacro/clash/transport/socks5"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
var _ adapter.Outbound = (*ShadowsocksR)(nil)
|
var _ int = "ShadowsocksR is deprecated and removed in sing-box 1.6.0"
|
||||||
|
|
||||||
type ShadowsocksR struct {
|
func NewShadowsocksR(ctx context.Context, router adapter.Router, logger log.ContextLogger, tag string, options option.ShadowsocksROutboundOptions) (adapter.Outbound, error) {
|
||||||
myOutboundAdapter
|
return nil, os.ErrInvalid
|
||||||
dialer N.Dialer
|
|
||||||
serverAddr M.Socksaddr
|
|
||||||
cipher core.Cipher
|
|
||||||
obfs obfs.Obfs
|
|
||||||
protocol protocol.Protocol
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewShadowsocksR(ctx context.Context, router adapter.Router, logger log.ContextLogger, tag string, options option.ShadowsocksROutboundOptions) (*ShadowsocksR, error) {
|
|
||||||
logger.Warn("ShadowsocksR is deprecated, see https://sing-box.sagernet.org/deprecated")
|
|
||||||
outboundDialer, err := dialer.New(router, options.DialerOptions)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
outbound := &ShadowsocksR{
|
|
||||||
myOutboundAdapter: myOutboundAdapter{
|
|
||||||
protocol: C.TypeShadowsocksR,
|
|
||||||
network: options.Network.Build(),
|
|
||||||
router: router,
|
|
||||||
logger: logger,
|
|
||||||
tag: tag,
|
|
||||||
dependencies: withDialerDependency(options.DialerOptions),
|
|
||||||
},
|
|
||||||
dialer: outboundDialer,
|
|
||||||
serverAddr: options.ServerOptions.Build(),
|
|
||||||
}
|
|
||||||
var cipher string
|
|
||||||
switch options.Method {
|
|
||||||
case "none":
|
|
||||||
cipher = "dummy"
|
|
||||||
default:
|
|
||||||
cipher = options.Method
|
|
||||||
}
|
|
||||||
outbound.cipher, err = core.PickCipher(cipher, nil, options.Password)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
var (
|
|
||||||
ivSize int
|
|
||||||
key []byte
|
|
||||||
)
|
|
||||||
if cipher == "dummy" {
|
|
||||||
ivSize = 0
|
|
||||||
key = core.Kdf(options.Password, 16)
|
|
||||||
} else {
|
|
||||||
streamCipher, ok := outbound.cipher.(*core.StreamCipher)
|
|
||||||
if !ok {
|
|
||||||
return nil, fmt.Errorf("%s is not none or a supported stream cipher in ssr", cipher)
|
|
||||||
}
|
|
||||||
ivSize = streamCipher.IVSize()
|
|
||||||
key = streamCipher.Key
|
|
||||||
}
|
|
||||||
obfs, obfsOverhead, err := obfs.PickObfs(options.Obfs, &obfs.Base{
|
|
||||||
Host: options.Server,
|
|
||||||
Port: int(options.ServerPort),
|
|
||||||
Key: key,
|
|
||||||
IVSize: ivSize,
|
|
||||||
Param: options.ObfsParam,
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
return nil, E.Cause(err, "initialize obfs")
|
|
||||||
}
|
|
||||||
protocol, err := protocol.PickProtocol(options.Protocol, &protocol.Base{
|
|
||||||
Key: key,
|
|
||||||
Overhead: obfsOverhead,
|
|
||||||
Param: options.ProtocolParam,
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
return nil, E.Cause(err, "initialize protocol")
|
|
||||||
}
|
|
||||||
outbound.obfs = obfs
|
|
||||||
outbound.protocol = protocol
|
|
||||||
return outbound, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (h *ShadowsocksR) DialContext(ctx context.Context, network string, destination M.Socksaddr) (net.Conn, error) {
|
|
||||||
ctx, metadata := adapter.AppendContext(ctx)
|
|
||||||
metadata.Outbound = h.tag
|
|
||||||
metadata.Destination = destination
|
|
||||||
switch network {
|
|
||||||
case N.NetworkTCP:
|
|
||||||
h.logger.InfoContext(ctx, "outbound connection to ", destination)
|
|
||||||
conn, err := h.dialer.DialContext(ctx, network, h.serverAddr)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
conn = h.cipher.StreamConn(h.obfs.StreamConn(conn))
|
|
||||||
writeIv, err := conn.(*shadowstream.Conn).ObtainWriteIV()
|
|
||||||
if err != nil {
|
|
||||||
conn.Close()
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
conn = h.protocol.StreamConn(conn, writeIv)
|
|
||||||
err = M.SocksaddrSerializer.WriteAddrPort(conn, destination)
|
|
||||||
if err != nil {
|
|
||||||
conn.Close()
|
|
||||||
return nil, E.Cause(err, "write request")
|
|
||||||
}
|
|
||||||
return conn, nil
|
|
||||||
case N.NetworkUDP:
|
|
||||||
conn, err := h.ListenPacket(ctx, destination)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return bufio.NewBindPacketConn(conn, destination), nil
|
|
||||||
default:
|
|
||||||
return nil, E.Extend(N.ErrUnknownNetwork, network)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (h *ShadowsocksR) ListenPacket(ctx context.Context, destination M.Socksaddr) (net.PacketConn, error) {
|
|
||||||
ctx, metadata := adapter.AppendContext(ctx)
|
|
||||||
metadata.Outbound = h.tag
|
|
||||||
metadata.Destination = destination
|
|
||||||
h.logger.InfoContext(ctx, "outbound packet connection to ", destination)
|
|
||||||
outConn, err := h.dialer.DialContext(ctx, N.NetworkUDP, h.serverAddr)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
packetConn := h.cipher.PacketConn(bufio.NewUnbindPacketConn(outConn))
|
|
||||||
packetConn = h.protocol.PacketConn(packetConn)
|
|
||||||
packetConn = &ssPacketConn{packetConn, outConn.RemoteAddr()}
|
|
||||||
return packetConn, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (h *ShadowsocksR) NewConnection(ctx context.Context, conn net.Conn, metadata adapter.InboundContext) error {
|
|
||||||
return NewConnection(ctx, h, conn, metadata)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (h *ShadowsocksR) NewPacketConnection(ctx context.Context, conn N.PacketConn, metadata adapter.InboundContext) error {
|
|
||||||
return NewPacketConnection(ctx, h, conn, metadata)
|
|
||||||
}
|
|
||||||
|
|
||||||
type ssPacketConn struct {
|
|
||||||
net.PacketConn
|
|
||||||
rAddr net.Addr
|
|
||||||
}
|
|
||||||
|
|
||||||
func (spc *ssPacketConn) WriteTo(b []byte, addr net.Addr) (n int, err error) {
|
|
||||||
packet, err := socks5.EncodeUDPPacket(socks5.ParseAddrToSocksAddr(addr), b)
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
return spc.PacketConn.WriteTo(packet[3:], spc.rAddr)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (spc *ssPacketConn) ReadFrom(b []byte) (int, net.Addr, error) {
|
|
||||||
n, _, e := spc.PacketConn.ReadFrom(b)
|
|
||||||
if e != nil {
|
|
||||||
return 0, nil, e
|
|
||||||
}
|
|
||||||
|
|
||||||
addr := socks5.SplitAddr(b[:n])
|
|
||||||
if addr == nil {
|
|
||||||
return 0, nil, errors.New("parse addr error")
|
|
||||||
}
|
|
||||||
|
|
||||||
udpAddr := addr.UDPAddr()
|
|
||||||
if udpAddr == nil {
|
|
||||||
return 0, nil, errors.New("parse addr error")
|
|
||||||
}
|
|
||||||
|
|
||||||
copy(b, b[len(addr):])
|
|
||||||
return n - len(addr), udpAddr, e
|
|
||||||
}
|
}
|
||||||
|
@ -12,5 +12,5 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func NewShadowsocksR(ctx context.Context, router adapter.Router, logger log.ContextLogger, tag string, options option.ShadowsocksROutboundOptions) (adapter.Outbound, error) {
|
func NewShadowsocksR(ctx context.Context, router adapter.Router, logger log.ContextLogger, tag string, options option.ShadowsocksROutboundOptions) (adapter.Outbound, error) {
|
||||||
return nil, E.New(`ShadowsocksR is not included in this build, rebuild with -tags with_shadowsocksr`)
|
return nil, E.New("ShadowsocksR is deprecated and removed in sing-box 1.6.0")
|
||||||
}
|
}
|
||||||
|
@ -18,7 +18,6 @@ import (
|
|||||||
"github.com/sagernet/sing-box/transport/wireguard"
|
"github.com/sagernet/sing-box/transport/wireguard"
|
||||||
"github.com/sagernet/sing-dns"
|
"github.com/sagernet/sing-dns"
|
||||||
"github.com/sagernet/sing-tun"
|
"github.com/sagernet/sing-tun"
|
||||||
"github.com/sagernet/sing/common"
|
|
||||||
"github.com/sagernet/sing/common/debug"
|
"github.com/sagernet/sing/common/debug"
|
||||||
E "github.com/sagernet/sing/common/exceptions"
|
E "github.com/sagernet/sing/common/exceptions"
|
||||||
M "github.com/sagernet/sing/common/metadata"
|
M "github.com/sagernet/sing/common/metadata"
|
||||||
@ -71,8 +70,7 @@ func NewWireGuard(ctx context.Context, router adapter.Router, logger log.Context
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
outbound.bind = wireguard.NewClientBind(ctx, outbound, outboundDialer, isConnect, connectAddr, reserved)
|
outbound.bind = wireguard.NewClientBind(ctx, outbound, outboundDialer, isConnect, connectAddr, reserved)
|
||||||
localPrefixes := common.Map(options.LocalAddress, option.ListenPrefix.Build)
|
if len(options.LocalAddress) == 0 {
|
||||||
if len(localPrefixes) == 0 {
|
|
||||||
return nil, E.New("missing local address")
|
return nil, E.New("missing local address")
|
||||||
}
|
}
|
||||||
var privateKey string
|
var privateKey string
|
||||||
@ -143,7 +141,7 @@ func NewWireGuard(ctx context.Context, router adapter.Router, logger log.Context
|
|||||||
ipcConf += "\npreshared_key=" + preSharedKey
|
ipcConf += "\npreshared_key=" + preSharedKey
|
||||||
}
|
}
|
||||||
var has4, has6 bool
|
var has4, has6 bool
|
||||||
for _, address := range localPrefixes {
|
for _, address := range options.LocalAddress {
|
||||||
if address.Addr().Is4() {
|
if address.Addr().Is4() {
|
||||||
has4 = true
|
has4 = true
|
||||||
} else {
|
} else {
|
||||||
@ -163,9 +161,9 @@ func NewWireGuard(ctx context.Context, router adapter.Router, logger log.Context
|
|||||||
}
|
}
|
||||||
var wireTunDevice wireguard.Device
|
var wireTunDevice wireguard.Device
|
||||||
if !options.SystemInterface && tun.WithGVisor {
|
if !options.SystemInterface && tun.WithGVisor {
|
||||||
wireTunDevice, err = wireguard.NewStackDevice(localPrefixes, mtu)
|
wireTunDevice, err = wireguard.NewStackDevice(options.LocalAddress, mtu)
|
||||||
} else {
|
} else {
|
||||||
wireTunDevice, err = wireguard.NewSystemDevice(router, options.InterfaceName, localPrefixes, mtu)
|
wireTunDevice, err = wireguard.NewSystemDevice(router, options.InterfaceName, options.LocalAddress, mtu)
|
||||||
}
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, E.Cause(err, "create WireGuard device")
|
return nil, E.Cause(err, "create WireGuard device")
|
||||||
|
@ -253,10 +253,10 @@ func NewRouter(
|
|||||||
var inet4Range netip.Prefix
|
var inet4Range netip.Prefix
|
||||||
var inet6Range netip.Prefix
|
var inet6Range netip.Prefix
|
||||||
if fakeIPOptions.Inet4Range != nil {
|
if fakeIPOptions.Inet4Range != nil {
|
||||||
inet4Range = fakeIPOptions.Inet4Range.Build()
|
inet4Range = *fakeIPOptions.Inet4Range
|
||||||
}
|
}
|
||||||
if fakeIPOptions.Inet6Range != nil {
|
if fakeIPOptions.Inet6Range != nil {
|
||||||
inet6Range = fakeIPOptions.Inet6Range.Build()
|
inet6Range = *fakeIPOptions.Inet6Range
|
||||||
}
|
}
|
||||||
router.fakeIPStore = fakeip.NewStore(router, router.logger, inet4Range, inet6Range)
|
router.fakeIPStore = fakeip.NewStore(router, router.logger, inet4Range, inet6Range)
|
||||||
}
|
}
|
||||||
@ -930,9 +930,8 @@ func (r *Router) AutoDetectInterfaceFunc() control.Func {
|
|||||||
return control.BindToInterfaceFunc(r.InterfaceFinder(), func(network string, address string) (interfaceName string, interfaceIndex int, err error) {
|
return control.BindToInterfaceFunc(r.InterfaceFinder(), func(network string, address string) (interfaceName string, interfaceIndex int, err error) {
|
||||||
remoteAddr := M.ParseSocksaddr(address).Addr
|
remoteAddr := M.ParseSocksaddr(address).Addr
|
||||||
if C.IsLinux {
|
if C.IsLinux {
|
||||||
interfaceName = r.InterfaceMonitor().DefaultInterfaceName(remoteAddr)
|
interfaceName, interfaceIndex = r.InterfaceMonitor().DefaultInterface(remoteAddr)
|
||||||
interfaceIndex = -1
|
if interfaceIndex == -1 {
|
||||||
if interfaceName == "" {
|
|
||||||
err = tun.ErrNoRoute
|
err = tun.ErrNoRoute
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
@ -36,7 +36,6 @@ const (
|
|||||||
ImageHysteria2 = "tobyxdd/hysteria:v2"
|
ImageHysteria2 = "tobyxdd/hysteria:v2"
|
||||||
ImageNginx = "nginx:stable"
|
ImageNginx = "nginx:stable"
|
||||||
ImageShadowTLS = "ghcr.io/ihciah/shadow-tls:latest"
|
ImageShadowTLS = "ghcr.io/ihciah/shadow-tls:latest"
|
||||||
ImageShadowsocksR = "teddysun/shadowsocks-r:latest"
|
|
||||||
ImageXRayCore = "teddysun/xray:latest"
|
ImageXRayCore = "teddysun/xray:latest"
|
||||||
ImageShadowsocksLegacy = "mritd/shadowsocks:latest"
|
ImageShadowsocksLegacy = "mritd/shadowsocks:latest"
|
||||||
ImageTUICServer = "kilvn/tuic-server:latest"
|
ImageTUICServer = "kilvn/tuic-server:latest"
|
||||||
@ -54,7 +53,6 @@ var allImages = []string{
|
|||||||
ImageHysteria2,
|
ImageHysteria2,
|
||||||
ImageNginx,
|
ImageNginx,
|
||||||
ImageShadowTLS,
|
ImageShadowTLS,
|
||||||
ImageShadowsocksR,
|
|
||||||
ImageXRayCore,
|
ImageXRayCore,
|
||||||
ImageShadowsocksLegacy,
|
ImageShadowsocksLegacy,
|
||||||
ImageTUICServer,
|
ImageTUICServer,
|
||||||
|
25
test/go.mod
25
test/go.mod
@ -6,26 +6,22 @@ require github.com/sagernet/sing-box v0.0.0
|
|||||||
|
|
||||||
replace github.com/sagernet/sing-box => ../
|
replace github.com/sagernet/sing-box => ../
|
||||||
|
|
||||||
replace github.com/sagernet/sing-quic => ../../sing-quic
|
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/docker/docker v24.0.6+incompatible
|
github.com/docker/docker v24.0.6+incompatible
|
||||||
github.com/docker/go-connections v0.4.0
|
github.com/docker/go-connections v0.4.0
|
||||||
github.com/gofrs/uuid/v5 v5.0.0
|
github.com/gofrs/uuid/v5 v5.0.0
|
||||||
github.com/sagernet/sing v0.2.12
|
github.com/sagernet/sing v0.2.15-0.20231021090846-8002db54c028
|
||||||
github.com/sagernet/sing-quic v0.1.1
|
github.com/sagernet/sing-quic v0.1.3-0.20231021083253-6c13506e2fb2
|
||||||
github.com/sagernet/sing-shadowsocks v0.2.5
|
github.com/sagernet/sing-shadowsocks v0.2.5
|
||||||
github.com/sagernet/sing-shadowsocks2 v0.1.4
|
github.com/sagernet/sing-shadowsocks2 v0.1.4
|
||||||
github.com/spyzhov/ajson v0.9.0
|
github.com/spyzhov/ajson v0.9.0
|
||||||
github.com/stretchr/testify v1.8.4
|
github.com/stretchr/testify v1.8.4
|
||||||
go.uber.org/goleak v1.2.1
|
go.uber.org/goleak v1.2.1
|
||||||
golang.org/x/net v0.15.0
|
golang.org/x/net v0.17.0
|
||||||
)
|
)
|
||||||
|
|
||||||
require (
|
require (
|
||||||
berty.tech/go-libtor v1.0.385 // indirect
|
berty.tech/go-libtor v1.0.385 // indirect
|
||||||
github.com/Dreamacro/clash v1.17.0 // indirect
|
|
||||||
github.com/Dreamacro/protobytes v0.0.0-20230617041236-6500a9f4f158 // indirect
|
|
||||||
github.com/Microsoft/go-winio v0.6.0 // indirect
|
github.com/Microsoft/go-winio v0.6.0 // indirect
|
||||||
github.com/ajg/form v1.5.1 // indirect
|
github.com/ajg/form v1.5.1 // indirect
|
||||||
github.com/andybalholm/brotli v1.0.5 // indirect
|
github.com/andybalholm/brotli v1.0.5 // indirect
|
||||||
@ -64,21 +60,21 @@ require (
|
|||||||
github.com/opencontainers/image-spec v1.0.2 // indirect
|
github.com/opencontainers/image-spec v1.0.2 // indirect
|
||||||
github.com/oschwald/maxminddb-golang v1.12.0 // indirect
|
github.com/oschwald/maxminddb-golang v1.12.0 // indirect
|
||||||
github.com/pierrec/lz4/v4 v4.1.14 // indirect
|
github.com/pierrec/lz4/v4 v4.1.14 // indirect
|
||||||
github.com/pires/go-proxyproto v0.7.0 // indirect
|
|
||||||
github.com/pkg/errors v0.9.1 // indirect
|
github.com/pkg/errors v0.9.1 // indirect
|
||||||
github.com/pmezard/go-difflib v1.0.0 // indirect
|
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||||
github.com/quic-go/qpack v0.4.0 // indirect
|
github.com/quic-go/qpack v0.4.0 // indirect
|
||||||
github.com/quic-go/qtls-go1-20 v0.3.4 // indirect
|
github.com/quic-go/qtls-go1-20 v0.3.4 // indirect
|
||||||
|
github.com/sagernet/bbolt v0.0.0-20231014093535-ea5cb2fe9f0a // indirect
|
||||||
github.com/sagernet/cloudflare-tls v0.0.0-20230829051644-4a68352d0c4a // indirect
|
github.com/sagernet/cloudflare-tls v0.0.0-20230829051644-4a68352d0c4a // indirect
|
||||||
github.com/sagernet/go-tun2socks v1.16.12-0.20220818015926-16cb67876a61 // indirect
|
github.com/sagernet/go-tun2socks v1.16.12-0.20220818015926-16cb67876a61 // indirect
|
||||||
github.com/sagernet/gvisor v0.0.0-20230627031050-1ab0276e0dd2 // indirect
|
github.com/sagernet/gvisor v0.0.0-20230930141345-5fef6f2e17ab // indirect
|
||||||
github.com/sagernet/netlink v0.0.0-20220905062125-8043b4a9aa97 // indirect
|
github.com/sagernet/netlink v0.0.0-20220905062125-8043b4a9aa97 // indirect
|
||||||
github.com/sagernet/quic-go v0.0.0-20231001051131-0fc736a289bb // indirect
|
github.com/sagernet/quic-go v0.0.0-20231008035953-32727fef9460 // indirect
|
||||||
github.com/sagernet/reality v0.0.0-20230406110435-ee17307e7691 // indirect
|
github.com/sagernet/reality v0.0.0-20230406110435-ee17307e7691 // indirect
|
||||||
github.com/sagernet/sing-dns v0.1.10 // indirect
|
github.com/sagernet/sing-dns v0.1.10 // indirect
|
||||||
github.com/sagernet/sing-mux v0.1.3 // indirect
|
github.com/sagernet/sing-mux v0.1.3 // indirect
|
||||||
github.com/sagernet/sing-shadowtls v0.1.4 // indirect
|
github.com/sagernet/sing-shadowtls v0.1.4 // indirect
|
||||||
github.com/sagernet/sing-tun v0.1.14 // indirect
|
github.com/sagernet/sing-tun v0.1.16-0.20231006112722-19cc8b9e81aa // indirect
|
||||||
github.com/sagernet/sing-vmess v0.1.8 // indirect
|
github.com/sagernet/sing-vmess v0.1.8 // indirect
|
||||||
github.com/sagernet/smux v0.0.0-20230312102458-337ec2a5af37 // indirect
|
github.com/sagernet/smux v0.0.0-20230312102458-337ec2a5af37 // indirect
|
||||||
github.com/sagernet/tfo-go v0.0.0-20230816093905-5a5c285d44a6 // indirect
|
github.com/sagernet/tfo-go v0.0.0-20230816093905-5a5c285d44a6 // indirect
|
||||||
@ -89,14 +85,13 @@ require (
|
|||||||
github.com/u-root/uio v0.0.0-20230220225925-ffce2a382923 // indirect
|
github.com/u-root/uio v0.0.0-20230220225925-ffce2a382923 // indirect
|
||||||
github.com/vishvananda/netns v0.0.0-20211101163701-50045581ed74 // indirect
|
github.com/vishvananda/netns v0.0.0-20211101163701-50045581ed74 // indirect
|
||||||
github.com/zeebo/blake3 v0.2.3 // indirect
|
github.com/zeebo/blake3 v0.2.3 // indirect
|
||||||
go.etcd.io/bbolt v1.3.7 // indirect
|
|
||||||
go.uber.org/multierr v1.11.0 // indirect
|
go.uber.org/multierr v1.11.0 // indirect
|
||||||
go.uber.org/zap v1.26.0 // indirect
|
go.uber.org/zap v1.26.0 // indirect
|
||||||
go4.org/netipx v0.0.0-20230824141953-6213f710f925 // indirect
|
go4.org/netipx v0.0.0-20230824141953-6213f710f925 // indirect
|
||||||
golang.org/x/crypto v0.13.0 // indirect
|
golang.org/x/crypto v0.14.0 // indirect
|
||||||
golang.org/x/exp v0.0.0-20230905200255-921286631fa9 // indirect
|
golang.org/x/exp v0.0.0-20231005195138-3e424a577f31 // indirect
|
||||||
golang.org/x/mod v0.12.0 // indirect
|
golang.org/x/mod v0.12.0 // indirect
|
||||||
golang.org/x/sys v0.12.0 // indirect
|
golang.org/x/sys v0.13.0 // indirect
|
||||||
golang.org/x/text v0.13.0 // indirect
|
golang.org/x/text v0.13.0 // indirect
|
||||||
golang.org/x/time v0.3.0 // indirect
|
golang.org/x/time v0.3.0 // indirect
|
||||||
golang.org/x/tools v0.13.0 // indirect
|
golang.org/x/tools v0.13.0 // indirect
|
||||||
|
46
test/go.sum
46
test/go.sum
@ -1,10 +1,6 @@
|
|||||||
berty.tech/go-libtor v1.0.385 h1:RWK94C3hZj6Z2GdvePpHJLnWYobFr3bY/OdUJ5aoEXw=
|
berty.tech/go-libtor v1.0.385 h1:RWK94C3hZj6Z2GdvePpHJLnWYobFr3bY/OdUJ5aoEXw=
|
||||||
berty.tech/go-libtor v1.0.385/go.mod h1:9swOOQVb+kmvuAlsgWUK/4c52pm69AdbJsxLzk+fJEw=
|
berty.tech/go-libtor v1.0.385/go.mod h1:9swOOQVb+kmvuAlsgWUK/4c52pm69AdbJsxLzk+fJEw=
|
||||||
github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 h1:UQHMgLO+TxOElx5B5HZ4hJQsoJ/PvUvKRhJHDQXO8P8=
|
github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 h1:UQHMgLO+TxOElx5B5HZ4hJQsoJ/PvUvKRhJHDQXO8P8=
|
||||||
github.com/Dreamacro/clash v1.17.0 h1:LWtp6KcnrCiujY58ufI8pylI+hbCBgSCsLI90EWhpi4=
|
|
||||||
github.com/Dreamacro/clash v1.17.0/go.mod h1:PtcAft7sdsK325BD6uwm8wvhOkMV3TCeED6dfZ/lnfE=
|
|
||||||
github.com/Dreamacro/protobytes v0.0.0-20230617041236-6500a9f4f158 h1:JFnwKplz9hj8ubqYjm8HkgZS1Rvz9yW+u/XCNNTxr0k=
|
|
||||||
github.com/Dreamacro/protobytes v0.0.0-20230617041236-6500a9f4f158/go.mod h1:QvmEZ/h6KXszPOr2wUFl7Zn3hfFNYdfbXwPVDTyZs6k=
|
|
||||||
github.com/Microsoft/go-winio v0.6.0 h1:slsWYD/zyx7lCXoZVlvQrj0hPTM1HI4+v1sIda2yDvg=
|
github.com/Microsoft/go-winio v0.6.0 h1:slsWYD/zyx7lCXoZVlvQrj0hPTM1HI4+v1sIda2yDvg=
|
||||||
github.com/Microsoft/go-winio v0.6.0/go.mod h1:cTAf44im0RAYeL23bpB+fzCyDH2MJiz2BO69KH/soAE=
|
github.com/Microsoft/go-winio v0.6.0/go.mod h1:cTAf44im0RAYeL23bpB+fzCyDH2MJiz2BO69KH/soAE=
|
||||||
github.com/ajg/form v1.5.1 h1:t9c7v8JUKu/XxOGBU0yjNpaMloxGEJhUkqFRq0ibGeU=
|
github.com/ajg/form v1.5.1 h1:t9c7v8JUKu/XxOGBU0yjNpaMloxGEJhUkqFRq0ibGeU=
|
||||||
@ -105,8 +101,6 @@ github.com/oschwald/maxminddb-golang v1.12.0 h1:9FnTOD0YOhP7DGxGsq4glzpGy5+w7pq5
|
|||||||
github.com/oschwald/maxminddb-golang v1.12.0/go.mod h1:q0Nob5lTCqyQ8WT6FYgS1L7PXKVVbgiymefNwIjPzgY=
|
github.com/oschwald/maxminddb-golang v1.12.0/go.mod h1:q0Nob5lTCqyQ8WT6FYgS1L7PXKVVbgiymefNwIjPzgY=
|
||||||
github.com/pierrec/lz4/v4 v4.1.14 h1:+fL8AQEZtz/ijeNnpduH0bROTu0O3NZAlPjQxGn8LwE=
|
github.com/pierrec/lz4/v4 v4.1.14 h1:+fL8AQEZtz/ijeNnpduH0bROTu0O3NZAlPjQxGn8LwE=
|
||||||
github.com/pierrec/lz4/v4 v4.1.14/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4=
|
github.com/pierrec/lz4/v4 v4.1.14/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4=
|
||||||
github.com/pires/go-proxyproto v0.7.0 h1:IukmRewDQFWC7kfnb66CSomk2q/seBuilHBYFwyq0Hs=
|
|
||||||
github.com/pires/go-proxyproto v0.7.0/go.mod h1:Vz/1JPY/OACxWGQNIRY2BeyDmpoaWmEP40O9LbuiFR4=
|
|
||||||
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
||||||
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||||
@ -115,34 +109,38 @@ github.com/quic-go/qpack v0.4.0 h1:Cr9BXA1sQS2SmDUWjSofMPNKmvF6IiIfDRmgU0w1ZCo=
|
|||||||
github.com/quic-go/qpack v0.4.0/go.mod h1:UZVnYIfi5GRk+zI9UMaCPsmZ2xKJP7XBUvVyT1Knj9A=
|
github.com/quic-go/qpack v0.4.0/go.mod h1:UZVnYIfi5GRk+zI9UMaCPsmZ2xKJP7XBUvVyT1Knj9A=
|
||||||
github.com/quic-go/qtls-go1-20 v0.3.4 h1:MfFAPULvst4yoMgY9QmtpYmfij/em7O8UUi+bNVm7Cg=
|
github.com/quic-go/qtls-go1-20 v0.3.4 h1:MfFAPULvst4yoMgY9QmtpYmfij/em7O8UUi+bNVm7Cg=
|
||||||
github.com/quic-go/qtls-go1-20 v0.3.4/go.mod h1:X9Nh97ZL80Z+bX/gUXMbipO6OxdiDi58b/fMC9mAL+k=
|
github.com/quic-go/qtls-go1-20 v0.3.4/go.mod h1:X9Nh97ZL80Z+bX/gUXMbipO6OxdiDi58b/fMC9mAL+k=
|
||||||
|
github.com/sagernet/bbolt v0.0.0-20231014093535-ea5cb2fe9f0a h1:+NkI2670SQpQWvkkD2QgdTuzQG263YZ+2emfpeyGqW0=
|
||||||
|
github.com/sagernet/bbolt v0.0.0-20231014093535-ea5cb2fe9f0a/go.mod h1:63s7jpZqcDAIpj8oI/1v4Izok+npJOHACFCU6+huCkM=
|
||||||
github.com/sagernet/cloudflare-tls v0.0.0-20230829051644-4a68352d0c4a h1:wZHruBxZCsQLXHAozWpnJBL3wJ/XufDpz0qKtgpSnA4=
|
github.com/sagernet/cloudflare-tls v0.0.0-20230829051644-4a68352d0c4a h1:wZHruBxZCsQLXHAozWpnJBL3wJ/XufDpz0qKtgpSnA4=
|
||||||
github.com/sagernet/cloudflare-tls v0.0.0-20230829051644-4a68352d0c4a/go.mod h1:dNV1ZP9y3qx5ltULeKaQZTZWTLHflgW5DES+Ses7cMI=
|
github.com/sagernet/cloudflare-tls v0.0.0-20230829051644-4a68352d0c4a/go.mod h1:dNV1ZP9y3qx5ltULeKaQZTZWTLHflgW5DES+Ses7cMI=
|
||||||
github.com/sagernet/go-tun2socks v1.16.12-0.20220818015926-16cb67876a61 h1:5+m7c6AkmAylhauulqN/c5dnh8/KssrE9c93TQrXldA=
|
github.com/sagernet/go-tun2socks v1.16.12-0.20220818015926-16cb67876a61 h1:5+m7c6AkmAylhauulqN/c5dnh8/KssrE9c93TQrXldA=
|
||||||
github.com/sagernet/go-tun2socks v1.16.12-0.20220818015926-16cb67876a61/go.mod h1:QUQ4RRHD6hGGHdFMEtR8T2P6GS6R3D/CXKdaYHKKXms=
|
github.com/sagernet/go-tun2socks v1.16.12-0.20220818015926-16cb67876a61/go.mod h1:QUQ4RRHD6hGGHdFMEtR8T2P6GS6R3D/CXKdaYHKKXms=
|
||||||
github.com/sagernet/gvisor v0.0.0-20230627031050-1ab0276e0dd2 h1:dnkKrzapqtAwjTSWt6hdPrARORfoYvuUczynvRLrueo=
|
github.com/sagernet/gvisor v0.0.0-20230930141345-5fef6f2e17ab h1:u+xQoi/Yc6bNUvTfrDD6HhGRybn2lzrhf5vmS+wb4Ho=
|
||||||
github.com/sagernet/gvisor v0.0.0-20230627031050-1ab0276e0dd2/go.mod h1:1JUiV7nGuf++YFm9eWZ8q2lrwHmhcUGzptMl/vL1+LA=
|
github.com/sagernet/gvisor v0.0.0-20230930141345-5fef6f2e17ab/go.mod h1:3akUhSHSVtLuJaYcW5JPepUraBOW06Ibz2HKwaK5rOk=
|
||||||
github.com/sagernet/netlink v0.0.0-20220905062125-8043b4a9aa97 h1:iL5gZI3uFp0X6EslacyapiRz7LLSJyr4RajF/BhMVyE=
|
github.com/sagernet/netlink v0.0.0-20220905062125-8043b4a9aa97 h1:iL5gZI3uFp0X6EslacyapiRz7LLSJyr4RajF/BhMVyE=
|
||||||
github.com/sagernet/netlink v0.0.0-20220905062125-8043b4a9aa97/go.mod h1:xLnfdiJbSp8rNqYEdIW/6eDO4mVoogml14Bh2hSiFpM=
|
github.com/sagernet/netlink v0.0.0-20220905062125-8043b4a9aa97/go.mod h1:xLnfdiJbSp8rNqYEdIW/6eDO4mVoogml14Bh2hSiFpM=
|
||||||
github.com/sagernet/quic-go v0.0.0-20231001051131-0fc736a289bb h1:jlrVCepGBoob4QsPChIbe1j0d/lZSJkyVj2ukX3D4PE=
|
github.com/sagernet/quic-go v0.0.0-20231008035953-32727fef9460 h1:dAe4OIJAtE0nHOzTHhAReQteh3+sa63rvXbuIpbeOTY=
|
||||||
github.com/sagernet/quic-go v0.0.0-20231001051131-0fc736a289bb/go.mod h1:uJGpmJCOcMQqMlHKc3P1Vz6uygmpz4bPeVIoOhdVQnM=
|
github.com/sagernet/quic-go v0.0.0-20231008035953-32727fef9460/go.mod h1:uJGpmJCOcMQqMlHKc3P1Vz6uygmpz4bPeVIoOhdVQnM=
|
||||||
github.com/sagernet/reality v0.0.0-20230406110435-ee17307e7691 h1:5Th31OC6yj8byLGkEnIYp6grlXfo1QYUfiYFGjewIdc=
|
github.com/sagernet/reality v0.0.0-20230406110435-ee17307e7691 h1:5Th31OC6yj8byLGkEnIYp6grlXfo1QYUfiYFGjewIdc=
|
||||||
github.com/sagernet/reality v0.0.0-20230406110435-ee17307e7691/go.mod h1:B8lp4WkQ1PwNnrVMM6KyuFR20pU8jYBD+A4EhJovEXU=
|
github.com/sagernet/reality v0.0.0-20230406110435-ee17307e7691/go.mod h1:B8lp4WkQ1PwNnrVMM6KyuFR20pU8jYBD+A4EhJovEXU=
|
||||||
github.com/sagernet/sing v0.0.0-20220817130738-ce854cda8522/go.mod h1:QVsS5L/ZA2Q5UhQwLrn0Trw+msNd/NPGEhBKR/ioWiY=
|
github.com/sagernet/sing v0.0.0-20220817130738-ce854cda8522/go.mod h1:QVsS5L/ZA2Q5UhQwLrn0Trw+msNd/NPGEhBKR/ioWiY=
|
||||||
github.com/sagernet/sing v0.1.8/go.mod h1:jt1w2u7lJQFFSGLiRrRIs5YWmx4kAPfWuOejuDW9qMk=
|
github.com/sagernet/sing v0.1.8/go.mod h1:jt1w2u7lJQFFSGLiRrRIs5YWmx4kAPfWuOejuDW9qMk=
|
||||||
github.com/sagernet/sing v0.2.12 h1:wwdLm3c4qvU4hW8tNtadh60V5z2FGlDZSYYGRzHhD74=
|
github.com/sagernet/sing v0.2.15-0.20231021090846-8002db54c028 h1:KRTzVdo7UPsygO235wsB705z6oWEM/L9HEJMOh4AsGI=
|
||||||
github.com/sagernet/sing v0.2.12/go.mod h1:GQ673iPfUnkbK/dIPkfd1Xh1MjOGo36gkl/mkiHY7Jg=
|
github.com/sagernet/sing v0.2.15-0.20231021090846-8002db54c028/go.mod h1:AhNEHu0GXrpqkuzvTwvC8+j2cQUU/dh+zLEmq4C99pg=
|
||||||
github.com/sagernet/sing-dns v0.1.10 h1:iIU7nRBlUYj+fF2TaktGIvRiTFFrHwSMedLQsvlTZCI=
|
github.com/sagernet/sing-dns v0.1.10 h1:iIU7nRBlUYj+fF2TaktGIvRiTFFrHwSMedLQsvlTZCI=
|
||||||
github.com/sagernet/sing-dns v0.1.10/go.mod h1:vtUimtf7Nq9EdvD5WTpfCr69KL1M7bcgOVKiYBiAY/c=
|
github.com/sagernet/sing-dns v0.1.10/go.mod h1:vtUimtf7Nq9EdvD5WTpfCr69KL1M7bcgOVKiYBiAY/c=
|
||||||
github.com/sagernet/sing-mux v0.1.3 h1:fAf7PZa2A55mCeh0KKM02f1k2Y4vEmxuZZ/51ahkkLA=
|
github.com/sagernet/sing-mux v0.1.3 h1:fAf7PZa2A55mCeh0KKM02f1k2Y4vEmxuZZ/51ahkkLA=
|
||||||
github.com/sagernet/sing-mux v0.1.3/go.mod h1:wGeIeiiFLx4HUM5LAg65wrNZ/X1muOimqK0PEhNbPi0=
|
github.com/sagernet/sing-mux v0.1.3/go.mod h1:wGeIeiiFLx4HUM5LAg65wrNZ/X1muOimqK0PEhNbPi0=
|
||||||
|
github.com/sagernet/sing-quic v0.1.3-0.20231021083253-6c13506e2fb2 h1:wcvxA4P/rnF2dokdRAL+Mr45GVFitVoWJqRzBt1Ut0s=
|
||||||
|
github.com/sagernet/sing-quic v0.1.3-0.20231021083253-6c13506e2fb2/go.mod h1:1M7xP4802K9Kz6BQ7LlA7UeCapWvWlH1Htmk2bAqkWc=
|
||||||
github.com/sagernet/sing-shadowsocks v0.2.5 h1:qxIttos4xu6ii7MTVJYA8EFQR7Q3KG6xMqmLJIFtBaY=
|
github.com/sagernet/sing-shadowsocks v0.2.5 h1:qxIttos4xu6ii7MTVJYA8EFQR7Q3KG6xMqmLJIFtBaY=
|
||||||
github.com/sagernet/sing-shadowsocks v0.2.5/go.mod h1:MGWGkcU2xW2G2mfArT9/QqpVLOGU+dBaahZCtPHdt7A=
|
github.com/sagernet/sing-shadowsocks v0.2.5/go.mod h1:MGWGkcU2xW2G2mfArT9/QqpVLOGU+dBaahZCtPHdt7A=
|
||||||
github.com/sagernet/sing-shadowsocks2 v0.1.4 h1:vht2M8t3m5DTgXR2j24KbYOygG5aOp+MUhpQnAux728=
|
github.com/sagernet/sing-shadowsocks2 v0.1.4 h1:vht2M8t3m5DTgXR2j24KbYOygG5aOp+MUhpQnAux728=
|
||||||
github.com/sagernet/sing-shadowsocks2 v0.1.4/go.mod h1:Mgdee99NxxNd5Zld3ixIs18yVs4x2dI2VTDDE1N14Wc=
|
github.com/sagernet/sing-shadowsocks2 v0.1.4/go.mod h1:Mgdee99NxxNd5Zld3ixIs18yVs4x2dI2VTDDE1N14Wc=
|
||||||
github.com/sagernet/sing-shadowtls v0.1.4 h1:aTgBSJEgnumzFenPvc+kbD9/W0PywzWevnVpEx6Tw3k=
|
github.com/sagernet/sing-shadowtls v0.1.4 h1:aTgBSJEgnumzFenPvc+kbD9/W0PywzWevnVpEx6Tw3k=
|
||||||
github.com/sagernet/sing-shadowtls v0.1.4/go.mod h1:F8NBgsY5YN2beQavdgdm1DPlhaKQlaL6lpDdcBglGK4=
|
github.com/sagernet/sing-shadowtls v0.1.4/go.mod h1:F8NBgsY5YN2beQavdgdm1DPlhaKQlaL6lpDdcBglGK4=
|
||||||
github.com/sagernet/sing-tun v0.1.14 h1:Vsval4r78kngCsZsz0FExT6p6akiUeRuiVXjfgnN3Ok=
|
github.com/sagernet/sing-tun v0.1.16-0.20231006112722-19cc8b9e81aa h1:lQVFeYJ916CXunKB3JrsOQqCtT16OAitMXw8Z0lRNvY=
|
||||||
github.com/sagernet/sing-tun v0.1.14/go.mod h1:Z2WibDUoQh/3wwFCfkIzIG0n/NlAlPuEbLTq3rD1aQY=
|
github.com/sagernet/sing-tun v0.1.16-0.20231006112722-19cc8b9e81aa/go.mod h1:d5kKDDW3I8Tr1OSWNaHXzrnsg1LbOx9sYVx83cHPhm8=
|
||||||
github.com/sagernet/sing-vmess v0.1.8 h1:XVWad1RpTy9b5tPxdm5MCU8cGfrTGdR8qCq6HV2aCNc=
|
github.com/sagernet/sing-vmess v0.1.8 h1:XVWad1RpTy9b5tPxdm5MCU8cGfrTGdR8qCq6HV2aCNc=
|
||||||
github.com/sagernet/sing-vmess v0.1.8/go.mod h1:vhx32UNzTDUkNwOyIjcZQohre1CaytquC5mPplId8uA=
|
github.com/sagernet/sing-vmess v0.1.8/go.mod h1:vhx32UNzTDUkNwOyIjcZQohre1CaytquC5mPplId8uA=
|
||||||
github.com/sagernet/smux v0.0.0-20230312102458-337ec2a5af37 h1:HuE6xSwco/Xed8ajZ+coeYLmioq0Qp1/Z2zczFaV8as=
|
github.com/sagernet/smux v0.0.0-20230312102458-337ec2a5af37 h1:HuE6xSwco/Xed8ajZ+coeYLmioq0Qp1/Z2zczFaV8as=
|
||||||
@ -177,8 +175,6 @@ github.com/zeebo/blake3 v0.2.3 h1:TFoLXsjeXqRNFxSbk35Dk4YtszE/MQQGK10BH4ptoTg=
|
|||||||
github.com/zeebo/blake3 v0.2.3/go.mod h1:mjJjZpnsyIVtVgTOSpJ9vmRE4wgDeyt2HU3qXvvKCaQ=
|
github.com/zeebo/blake3 v0.2.3/go.mod h1:mjJjZpnsyIVtVgTOSpJ9vmRE4wgDeyt2HU3qXvvKCaQ=
|
||||||
github.com/zeebo/pcg v1.0.1 h1:lyqfGeWiv4ahac6ttHs+I5hwtH/+1mrhlCtVNQM2kHo=
|
github.com/zeebo/pcg v1.0.1 h1:lyqfGeWiv4ahac6ttHs+I5hwtH/+1mrhlCtVNQM2kHo=
|
||||||
github.com/zeebo/pcg v1.0.1/go.mod h1:09F0S9iiKrwn9rlI5yjLkmrug154/YRW6KnnXVDM/l4=
|
github.com/zeebo/pcg v1.0.1/go.mod h1:09F0S9iiKrwn9rlI5yjLkmrug154/YRW6KnnXVDM/l4=
|
||||||
go.etcd.io/bbolt v1.3.7 h1:j+zJOnnEjF/kyHlDDgGnVL/AIqIJPq8UoB2GSNfkUfQ=
|
|
||||||
go.etcd.io/bbolt v1.3.7/go.mod h1:N9Mkw9X8x5fupy0IKsmuqVtoGDyxsaDlbk4Rd05IAQw=
|
|
||||||
go.uber.org/goleak v1.2.1 h1:NBol2c7O1ZokfZ0LEU9K6Whx/KnwvepVetCUhtKja4A=
|
go.uber.org/goleak v1.2.1 h1:NBol2c7O1ZokfZ0LEU9K6Whx/KnwvepVetCUhtKja4A=
|
||||||
go.uber.org/goleak v1.2.1/go.mod h1:qlT2yGI9QafXHhZZLxlSuNsMw3FFLxBr+tBRlmO1xH4=
|
go.uber.org/goleak v1.2.1/go.mod h1:qlT2yGI9QafXHhZZLxlSuNsMw3FFLxBr+tBRlmO1xH4=
|
||||||
go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0=
|
go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0=
|
||||||
@ -192,10 +188,10 @@ golang.org/x/crypto v0.0.0-20190404164418-38d8ce5564a5/go.mod h1:WFFai1msRO1wXaE
|
|||||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||||
golang.org/x/crypto v0.0.0-20210513164829-c07d793c2f9a/go.mod h1:P+XmwS30IXTQdn5tA2iutPOUgjI07+tq3H3K9MVA1s8=
|
golang.org/x/crypto v0.0.0-20210513164829-c07d793c2f9a/go.mod h1:P+XmwS30IXTQdn5tA2iutPOUgjI07+tq3H3K9MVA1s8=
|
||||||
golang.org/x/crypto v0.13.0 h1:mvySKfSWJ+UKUii46M40LOvyWfN0s2U+46/jDd0e6Ck=
|
golang.org/x/crypto v0.14.0 h1:wBqGXzWJW6m1XrIKlAH0Hs1JJ7+9KBwnIO8v66Q9cHc=
|
||||||
golang.org/x/crypto v0.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliYc=
|
golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf4=
|
||||||
golang.org/x/exp v0.0.0-20230905200255-921286631fa9 h1:GoHiUyI/Tp2nVkLI2mCxVkOjsbSXD66ic0XW0js0R9g=
|
golang.org/x/exp v0.0.0-20231005195138-3e424a577f31 h1:9k5exFQKQglLo+RoP+4zMjOFE14P6+vyR0baDAi0Rcs=
|
||||||
golang.org/x/exp v0.0.0-20230905200255-921286631fa9/go.mod h1:S2oDrQGGwySpoQPVqRShND87VCbxmc6bL1Yd2oYrm6k=
|
golang.org/x/exp v0.0.0-20231005195138-3e424a577f31/go.mod h1:S2oDrQGGwySpoQPVqRShND87VCbxmc6bL1Yd2oYrm6k=
|
||||||
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||||
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||||
golang.org/x/mod v0.12.0 h1:rmsUpXtvNzj340zd98LZ4KntptpfRHwpFOHG188oHXc=
|
golang.org/x/mod v0.12.0 h1:rmsUpXtvNzj340zd98LZ4KntptpfRHwpFOHG188oHXc=
|
||||||
@ -206,8 +202,8 @@ golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLL
|
|||||||
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||||
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||||
golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||||
golang.org/x/net v0.15.0 h1:ugBLEUaxABaB5AJqW9enI0ACdci2RUd4eP51NTBvuJ8=
|
golang.org/x/net v0.17.0 h1:pVaXccu2ozPjCXewfr1S7xza/zcXTity9cCdXQYSjIM=
|
||||||
golang.org/x/net v0.15.0/go.mod h1:idbUs1IY1+zTqbi8yxTbhexhEEk5ur9LInksu6HrEpk=
|
golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE=
|
||||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
@ -226,10 +222,10 @@ golang.org/x/sys v0.0.0-20220731174439-a90be440212d/go.mod h1:oPkhp1MJrh7nUepCBc
|
|||||||
golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.12.0 h1:CM0HF96J0hcLAwsHPJZjfdNzs0gftsLfgKt57wWHJ0o=
|
golang.org/x/sys v0.13.0 h1:Af8nKPmuFypiUBjVoU9V20FiaFXOcuZI21p0ycVYYGE=
|
||||||
golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||||
golang.org/x/term v0.12.0 h1:/ZfYdc3zq+q02Rv9vGqTeSItdzZTSNDmfTi0mBAuidU=
|
golang.org/x/term v0.13.0 h1:bb+I9cTfFazGW51MZqBVmZy7+JEJMouUHTUSKVQLBek=
|
||||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||||
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||||
|
@ -79,7 +79,7 @@ func TestHysteriaSelf(t *testing.T) {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
testSuitSimple1(t, clientPort, testPort)
|
testSuit(t, clientPort, testPort)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestHysteriaInbound(t *testing.T) {
|
func TestHysteriaInbound(t *testing.T) {
|
||||||
@ -118,7 +118,7 @@ func TestHysteriaInbound(t *testing.T) {
|
|||||||
caPem: "/etc/hysteria/ca.pem",
|
caPem: "/etc/hysteria/ca.pem",
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
testSuitSimple1(t, clientPort, testPort)
|
testSuit(t, clientPort, testPort)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestHysteriaOutbound(t *testing.T) {
|
func TestHysteriaOutbound(t *testing.T) {
|
||||||
|
@ -1,48 +0,0 @@
|
|||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"net/netip"
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
C "github.com/sagernet/sing-box/constant"
|
|
||||||
"github.com/sagernet/sing-box/option"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestShadowsocksR(t *testing.T) {
|
|
||||||
startDockerContainer(t, DockerOptions{
|
|
||||||
Image: ImageShadowsocksR,
|
|
||||||
Ports: []uint16{serverPort, testPort},
|
|
||||||
Bind: map[string]string{
|
|
||||||
"shadowsocksr.json": "/etc/shadowsocks-r/config.json",
|
|
||||||
},
|
|
||||||
})
|
|
||||||
startInstance(t, option.Options{
|
|
||||||
Inbounds: []option.Inbound{
|
|
||||||
{
|
|
||||||
Type: C.TypeMixed,
|
|
||||||
MixedOptions: option.HTTPMixedInboundOptions{
|
|
||||||
ListenOptions: option.ListenOptions{
|
|
||||||
Listen: option.NewListenAddress(netip.IPv4Unspecified()),
|
|
||||||
ListenPort: clientPort,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
Outbounds: []option.Outbound{
|
|
||||||
{
|
|
||||||
Type: C.TypeShadowsocksR,
|
|
||||||
ShadowsocksROptions: option.ShadowsocksROutboundOptions{
|
|
||||||
ServerOptions: option.ServerOptions{
|
|
||||||
Server: "127.0.0.1",
|
|
||||||
ServerPort: serverPort,
|
|
||||||
},
|
|
||||||
Method: "aes-256-cfb",
|
|
||||||
Password: "password0",
|
|
||||||
Obfs: "plain",
|
|
||||||
Protocol: "origin",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
})
|
|
||||||
testSuit(t, clientPort, testPort)
|
|
||||||
}
|
|
@ -1,9 +0,0 @@
|
|||||||
package obfs
|
|
||||||
|
|
||||||
type Base struct {
|
|
||||||
Host string
|
|
||||||
Port int
|
|
||||||
Key []byte
|
|
||||||
IVSize int
|
|
||||||
Param string
|
|
||||||
}
|
|
@ -1,9 +0,0 @@
|
|||||||
package obfs
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
register("http_post", newHTTPPost, 0)
|
|
||||||
}
|
|
||||||
|
|
||||||
func newHTTPPost(b *Base) Obfs {
|
|
||||||
return &httpObfs{Base: b, post: true}
|
|
||||||
}
|
|
@ -1,405 +0,0 @@
|
|||||||
package obfs
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"encoding/hex"
|
|
||||||
"io"
|
|
||||||
"math/rand"
|
|
||||||
"net"
|
|
||||||
"strconv"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"github.com/Dreamacro/clash/common/pool"
|
|
||||||
)
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
register("http_simple", newHTTPSimple, 0)
|
|
||||||
}
|
|
||||||
|
|
||||||
type httpObfs struct {
|
|
||||||
*Base
|
|
||||||
post bool
|
|
||||||
}
|
|
||||||
|
|
||||||
func newHTTPSimple(b *Base) Obfs {
|
|
||||||
return &httpObfs{Base: b}
|
|
||||||
}
|
|
||||||
|
|
||||||
type httpConn struct {
|
|
||||||
net.Conn
|
|
||||||
*httpObfs
|
|
||||||
hasSentHeader bool
|
|
||||||
hasRecvHeader bool
|
|
||||||
buf []byte
|
|
||||||
}
|
|
||||||
|
|
||||||
func (h *httpObfs) StreamConn(c net.Conn) net.Conn {
|
|
||||||
return &httpConn{Conn: c, httpObfs: h}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *httpConn) Read(b []byte) (int, error) {
|
|
||||||
if c.buf != nil {
|
|
||||||
n := copy(b, c.buf)
|
|
||||||
if n == len(c.buf) {
|
|
||||||
c.buf = nil
|
|
||||||
} else {
|
|
||||||
c.buf = c.buf[n:]
|
|
||||||
}
|
|
||||||
return n, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
if c.hasRecvHeader {
|
|
||||||
return c.Conn.Read(b)
|
|
||||||
}
|
|
||||||
|
|
||||||
buf := pool.Get(pool.RelayBufferSize)
|
|
||||||
defer pool.Put(buf)
|
|
||||||
n, err := c.Conn.Read(buf)
|
|
||||||
if err != nil {
|
|
||||||
return 0, err
|
|
||||||
}
|
|
||||||
pos := bytes.Index(buf[:n], []byte("\r\n\r\n"))
|
|
||||||
if pos == -1 {
|
|
||||||
return 0, io.EOF
|
|
||||||
}
|
|
||||||
c.hasRecvHeader = true
|
|
||||||
dataLength := n - pos - 4
|
|
||||||
n = copy(b, buf[4+pos:n])
|
|
||||||
if dataLength > n {
|
|
||||||
c.buf = append(c.buf, buf[4+pos+n:4+pos+dataLength]...)
|
|
||||||
}
|
|
||||||
return n, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *httpConn) Write(b []byte) (int, error) {
|
|
||||||
if c.hasSentHeader {
|
|
||||||
return c.Conn.Write(b)
|
|
||||||
}
|
|
||||||
// 30: head length
|
|
||||||
headLength := c.IVSize + 30
|
|
||||||
|
|
||||||
bLength := len(b)
|
|
||||||
headDataLength := bLength
|
|
||||||
if bLength-headLength > 64 {
|
|
||||||
headDataLength = headLength + rand.Intn(65)
|
|
||||||
}
|
|
||||||
headData := b[:headDataLength]
|
|
||||||
b = b[headDataLength:]
|
|
||||||
|
|
||||||
var body string
|
|
||||||
host := c.Host
|
|
||||||
if len(c.Param) > 0 {
|
|
||||||
pos := strings.Index(c.Param, "#")
|
|
||||||
if pos != -1 {
|
|
||||||
body = strings.ReplaceAll(c.Param[pos+1:], "\n", "\r\n")
|
|
||||||
body = strings.ReplaceAll(body, "\\n", "\r\n")
|
|
||||||
host = c.Param[:pos]
|
|
||||||
} else {
|
|
||||||
host = c.Param
|
|
||||||
}
|
|
||||||
}
|
|
||||||
hosts := strings.Split(host, ",")
|
|
||||||
host = hosts[rand.Intn(len(hosts))]
|
|
||||||
|
|
||||||
buf := pool.GetBuffer()
|
|
||||||
defer pool.PutBuffer(buf)
|
|
||||||
if c.post {
|
|
||||||
buf.WriteString("POST /")
|
|
||||||
} else {
|
|
||||||
buf.WriteString("GET /")
|
|
||||||
}
|
|
||||||
packURLEncodedHeadData(buf, headData)
|
|
||||||
buf.WriteString(" HTTP/1.1\r\nHost: " + host)
|
|
||||||
if c.Port != 80 {
|
|
||||||
buf.WriteString(":" + strconv.Itoa(c.Port))
|
|
||||||
}
|
|
||||||
buf.WriteString("\r\n")
|
|
||||||
if len(body) > 0 {
|
|
||||||
buf.WriteString(body + "\r\n\r\n")
|
|
||||||
} else {
|
|
||||||
buf.WriteString("User-Agent: ")
|
|
||||||
buf.WriteString(userAgent[rand.Intn(len(userAgent))])
|
|
||||||
buf.WriteString("\r\nAccept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8\r\nAccept-Language: en-US,en;q=0.8\r\nAccept-Encoding: gzip, deflate\r\n")
|
|
||||||
if c.post {
|
|
||||||
packBoundary(buf)
|
|
||||||
}
|
|
||||||
buf.WriteString("DNT: 1\r\nConnection: keep-alive\r\n\r\n")
|
|
||||||
}
|
|
||||||
buf.Write(b)
|
|
||||||
_, err := c.Conn.Write(buf.Bytes())
|
|
||||||
if err != nil {
|
|
||||||
return 0, nil
|
|
||||||
}
|
|
||||||
c.hasSentHeader = true
|
|
||||||
return bLength, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func packURLEncodedHeadData(buf *bytes.Buffer, data []byte) {
|
|
||||||
dataLength := len(data)
|
|
||||||
for i := 0; i < dataLength; i++ {
|
|
||||||
buf.WriteRune('%')
|
|
||||||
buf.WriteString(hex.EncodeToString(data[i : i+1]))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func packBoundary(buf *bytes.Buffer) {
|
|
||||||
buf.WriteString("Content-Type: multipart/form-data; boundary=")
|
|
||||||
set := "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"
|
|
||||||
for i := 0; i < 32; i++ {
|
|
||||||
buf.WriteByte(set[rand.Intn(62)])
|
|
||||||
}
|
|
||||||
buf.WriteString("\r\n")
|
|
||||||
}
|
|
||||||
|
|
||||||
var userAgent = []string{
|
|
||||||
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/65.0.3325.162 Safari/537.36",
|
|
||||||
"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/45.0.2454.85 Safari/537.36",
|
|
||||||
"Mozilla/5.0 (Linux; Android 7.0; Moto C Build/NRD90M.059) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.100 Mobile Safari/537.36",
|
|
||||||
"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/68.0.3440.106 Safari/537.36",
|
|
||||||
"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.102 Safari/537.36",
|
|
||||||
"Mozilla/5.0 (Linux; Android 6.0.1; SM-G532M Build/MMB29T; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/55.0.2883.91 Mobile Safari/537.36",
|
|
||||||
"Mozilla/5.0 (Windows NT 6.2; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/46.0.2490.80 Safari/537.36",
|
|
||||||
"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/41.0.2272.101 Safari/537.36",
|
|
||||||
"Mozilla/5.0 (Windows NT 6.2; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.102 Safari/537.36",
|
|
||||||
"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/40.0.2214.111 Safari/537.36",
|
|
||||||
"Mozilla/5.0 (Windows NT 6.3; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/62.0.3202.94 Safari/537.36",
|
|
||||||
"Mozilla/5.0 (Linux; Android 5.1.1; SM-J120M Build/LMY47X) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.100 Mobile Safari/537.36",
|
|
||||||
"Mozilla/5.0 (Windows NT 6.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/46.0.2490.80 Safari/537.36",
|
|
||||||
"Mozilla/5.0 (Linux; Android 7.0; Moto G (5) Build/NPPS25.137-93-14) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.100 Mobile Safari/537.36",
|
|
||||||
"Mozilla/5.0 (Windows NT 6.2; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/51.0.2704.106 Safari/537.36",
|
|
||||||
"Mozilla/5.0 (Windows NT 6.3; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/65.0.3325.181 Safari/537.36",
|
|
||||||
"Mozilla/5.0 (Linux; Android 7.0; SM-G570M) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.80 Mobile Safari/537.36",
|
|
||||||
"Mozilla/5.0 (Windows NT 5.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/46.0.2490.80 Safari/537.36",
|
|
||||||
"Mozilla/5.0 (Windows NT 10.0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.102 Safari/537.36",
|
|
||||||
"Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/49.0.2623.112 Safari/537.36",
|
|
||||||
"Mozilla/5.0 (Linux; Android 6.0; CAM-L03 Build/HUAWEICAM-L03) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.100 Mobile Safari/537.36",
|
|
||||||
"Mozilla/5.0 (Windows NT 6.3; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/41.0.2272.76 Safari/537.36",
|
|
||||||
"Mozilla/5.0 (Windows NT 6.3; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/66.0.3359.117 Safari/537.36",
|
|
||||||
"Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US) AppleWebKit/534.3 (KHTML, like Gecko) Chrome/6.0.472.63 Safari/534.3",
|
|
||||||
"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/55.0.2883.87 Safari/537.36",
|
|
||||||
"Mozilla/5.0 (Windows NT 6.3; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.102 Safari/537.36",
|
|
||||||
"Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/47.0.2526.106 Safari/537.36",
|
|
||||||
"Mozilla/5.0 (Windows; U; Windows NT 6.1; en-US) AppleWebKit/534.7 (KHTML, like Gecko) Chrome/7.0.517.44 Safari/534.7",
|
|
||||||
"Mozilla/5.0 (Windows NT 6.3; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/52.0.2743.116 Safari/537.36",
|
|
||||||
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/68.0.3440.75 Safari/537.36",
|
|
||||||
"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/54.0.2840.71 Safari/537.36",
|
|
||||||
"Mozilla/5.0 (Windows; U; Windows NT 6.1; en-US) AppleWebKit/534.3 (KHTML, like Gecko) Chrome/6.0.472.63 Safari/534.3",
|
|
||||||
"Mozilla/5.0 (Linux; Android 8.0.0; FIG-LX3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.80 Mobile Safari/537.36",
|
|
||||||
"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/40.0.2214.115 Safari/537.36",
|
|
||||||
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/52.0.2743.116 Safari/537.36",
|
|
||||||
"Mozilla/5.0 (Windows; U; Windows NT 6.1; en-US) AppleWebKit/534.10 (KHTML, like Gecko) Chrome/8.0.552.237 Safari/534.10",
|
|
||||||
"Mozilla/5.0 (Windows NT 6.3; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/60.0.3112.90 Safari/537.36",
|
|
||||||
"Mozilla/5.0 (X11; U; Linux x86_64; en-US) AppleWebKit/533.2 (KHTML, like Gecko) Chrome/5.0.342.1 Safari/533.2",
|
|
||||||
"Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.102 Safari/537.36",
|
|
||||||
"Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/49.0.2623.110 Safari/537.36",
|
|
||||||
"Mozilla/5.0 (Windows NT 6.3; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/62.0.3202.89 Safari/537.36",
|
|
||||||
"Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.81 Safari/537.36",
|
|
||||||
"Mozilla/5.0 (X11; Datanyze; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/65.0.3325.181 Safari/537.36",
|
|
||||||
"Mozilla/5.0 (Windows NT 6.3; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.132 Safari/537.36",
|
|
||||||
"Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/54.0.2840.71 Safari/537.36",
|
|
||||||
"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/35.0.1916.153 Safari/537.36",
|
|
||||||
"Mozilla/5.0 (Linux; Android 5.1.1; SM-J111M Build/LMY47V) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.100 Mobile Safari/537.36",
|
|
||||||
"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/57.0.2987.133 Safari/537.36",
|
|
||||||
"Mozilla/5.0 (Windows NT 6.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/51.0.2704.106 Safari/537.36",
|
|
||||||
"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/65.0.3325.181 Safari/537.36",
|
|
||||||
"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/37.0.2062.120 Safari/537.36",
|
|
||||||
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.100 Safari/537.36",
|
|
||||||
"Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.84 Safari/537.36",
|
|
||||||
"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/54.0.2840.99 Safari/537.36",
|
|
||||||
"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/32.0.1700.107 Safari/537.36",
|
|
||||||
"Mozilla/5.0 (Windows NT 10.0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.100 Safari/537.36",
|
|
||||||
"Mozilla/5.0 (Linux; Android 6.0.1; SM-J700M Build/MMB29K) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.100 Mobile Safari/537.36",
|
|
||||||
"Mozilla/5.0 (Windows NT 6.3; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/51.0.2704.63 Safari/537.36",
|
|
||||||
"Mozilla/5.0 (X11; Linux i686) AppleWebKit/534.30 (KHTML, like Gecko) Slackware/Chrome/12.0.742.100 Safari/534.30",
|
|
||||||
"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.100 Safari/537.36",
|
|
||||||
"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/46.0.2490.86 Safari/537.36",
|
|
||||||
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/64.0.3282.167 Safari/537.36",
|
|
||||||
"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/48.0.2564.116 Safari/537.36",
|
|
||||||
"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/50.0.2661.102 Safari/537.36",
|
|
||||||
"Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/53.0.2785.143 Safari/537.36",
|
|
||||||
"Mozilla/5.0 (X11; Linux i686) AppleWebKit/534.30 (KHTML, like Gecko) Chrome/12.0.742.100 Safari/534.30",
|
|
||||||
"Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.100 Safari/537.36",
|
|
||||||
"Mozilla/5.0 (Linux; Android 8.0.0; WAS-LX3 Build/HUAWEIWAS-LX3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.100 Mobile Safari/537.36",
|
|
||||||
"Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/54.0.2840.99 Safari/537.36",
|
|
||||||
"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/57.0.2987.133 Safari/537.36",
|
|
||||||
"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/49.0.2623.87 Safari/537.36",
|
|
||||||
"Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/50.0.2661.102 Safari/537.36",
|
|
||||||
"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/31.0.1650.57 Safari/537.36",
|
|
||||||
"Mozilla/5.0 (Windows NT 6.3; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/67.0.3396.99 Safari/537.36",
|
|
||||||
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/60.0.3112.101 Safari/537.36",
|
|
||||||
"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/68.0.3440.1805 Safari/537.36 MVisionPlayer/1.0.0.0",
|
|
||||||
"Mozilla/5.0 (Linux; Android 7.0; TRT-LX3 Build/HUAWEITRT-LX3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.100 Mobile Safari/537.36",
|
|
||||||
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/41.0.2272.89 Safari/537.36",
|
|
||||||
"Mozilla/5.0 (Linux; Android 6.0; vivo 1610 Build/MMB29M) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/53.0.2785.124 Mobile Safari/537.36",
|
|
||||||
"Mozilla/5.0 (Windows NT 6.2; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.100 Safari/537.36",
|
|
||||||
"Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.87 Safari/537.36",
|
|
||||||
"Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/67.0.3396.87 Safari/537.36",
|
|
||||||
"Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/57.0.2987.133 Safari/537.36",
|
|
||||||
"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/47.0.2526.111 Safari/537.36",
|
|
||||||
"Mozilla/5.0 (Linux; Android 4.4.2; de-de; SAMSUNG GT-I9195 Build/KOT49H) AppleWebKit/537.36 (KHTML, like Gecko) Version/1.5 Chrome/28.0.1500.94 Mobile Safari/537.36",
|
|
||||||
"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.110 Safari/537.36",
|
|
||||||
"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36",
|
|
||||||
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/42.0.2311.90 Safari/537.36",
|
|
||||||
"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/49.0.2623.110 Safari/537.36",
|
|
||||||
"Mozilla/5.0 (Linux; Android 8.0.0; ANE-LX3 Build/HUAWEIANE-LX3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.100 Mobile Safari/537.36",
|
|
||||||
"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/49.0.2623.112 Safari/537.36",
|
|
||||||
"Mozilla/5.0 (Windows NT 6.3; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.87 Safari/537.36",
|
|
||||||
"Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/49.0.2623.87 Safari/537.36",
|
|
||||||
"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/53.0.2785.143 Safari/537.36",
|
|
||||||
"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.87 Safari/537.36",
|
|
||||||
"Mozilla/5.0 (X11; U; Linux i586; en-US) AppleWebKit/533.2 (KHTML, like Gecko) Chrome/5.0.342.1 Safari/533.2",
|
|
||||||
"Mozilla/5.0 (Windows NT 6.3; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/43.0.2357.65 Safari/537.36",
|
|
||||||
"Mozilla/5.0 (Linux; Android 7.0; SM-G610M) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.80 Mobile Safari/537.36",
|
|
||||||
"Mozilla/5.0 (Linux; Android 6.0.1; SM-J500M Build/MMB29M) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.100 Mobile Safari/537.36",
|
|
||||||
"Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US) AppleWebKit/534.7 (KHTML, like Gecko) Chrome/7.0.517.44 Safari/534.7",
|
|
||||||
"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/41.0.2272.104 Safari/537.36",
|
|
||||||
"Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/59.0.3071.115 Safari/537.36",
|
|
||||||
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.87 Safari/537.36",
|
|
||||||
"Mozilla/5.0 (Linux; Android 6.0; vivo 1606 Build/MMB29M) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/53.0.2785.124 Mobile Safari/537.36",
|
|
||||||
"Mozilla/5.0 (Windows NT 6.3; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/55.0.2883.87 Safari/537.36",
|
|
||||||
"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/42.0.2311.135 Safari/537.36",
|
|
||||||
"Mozilla/5.0 (Linux; Android 7.0; SM-G610M Build/NRD90M) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.100 Mobile Safari/537.36",
|
|
||||||
"Mozilla/5.0 (Linux; Android 7.1; vivo 1716 Build/N2G47H) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/61.0.3163.98 Mobile Safari/537.36",
|
|
||||||
"Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.87 Safari/537.36",
|
|
||||||
"Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/55.0.2883.87 Safari/537.36",
|
|
||||||
"Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/45.0.2454.93 Safari/537.36",
|
|
||||||
"Mozilla/5.0 (Linux; Android 7.0; SM-G570M Build/NRD90M) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.100 Mobile Safari/537.36",
|
|
||||||
"Mozilla/5.0 (Windows NT 6.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/60.0.3112.90 Safari/537.36",
|
|
||||||
"Mozilla/5.0 (Linux; Android 6.0; MYA-L22 Build/HUAWEIMYA-L22) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/62.0.3202.84 Mobile Safari/537.36",
|
|
||||||
"Mozilla/5.0 (Linux; Android 5.1; A1601 Build/LMY47I) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/52.0.2743.98 Mobile Safari/537.36",
|
|
||||||
"Mozilla/5.0 (Linux; Android 7.0; TRT-LX2 Build/HUAWEITRT-LX2; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/59.0.3071.125 Mobile Safari/537.36",
|
|
||||||
"Mozilla/5.0 (Windows NT 6.2; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/55.0.2883.87 Safari/537.36",
|
|
||||||
"Mozilla/5.0 (Windows NT 6.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/55.0.2883.87 Safari/537.36",
|
|
||||||
"Mozilla/5.0 (Windows NT 5.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/55.0.2883.87 Safari/537.36",
|
|
||||||
"Mozilla/5.0 (Windows NT 6.3; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.102 Safari/537.36",
|
|
||||||
"Mozilla/5.0 (Windows; U; Windows NT 6.1; en-US) AppleWebKit/534.17 (KHTML, like Gecko) Chrome/10.0.649.0 Safari/534.17",
|
|
||||||
"Mozilla/5.0 (Linux; Android 6.0; CAM-L21 Build/HUAWEICAM-L21; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/62.0.3202.84 Mobile Safari/537.36",
|
|
||||||
"Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/62.0.3202.94 Safari/537.36",
|
|
||||||
"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/534.24 (KHTML, like Gecko) Chrome/11.0.696.3 Safari/534.24",
|
|
||||||
"Mozilla/5.0 (Linux; Android 7.1.2; Redmi 4X Build/N2G47H) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.111 Mobile Safari/537.36",
|
|
||||||
"Mozilla/5.0 (Windows NT 6.3; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/51.0.2704.106 Safari/537.36",
|
|
||||||
"Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/66.0.3359.181 Safari/537.36",
|
|
||||||
"Mozilla/5.0 (Linux; Android 4.4.2; SM-G7102 Build/KOT49H) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/62.0.3202.84 Mobile Safari/537.36",
|
|
||||||
"Mozilla/5.0 (Windows NT 6.3; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/48.0.2564.109 Safari/537.36",
|
|
||||||
"Mozilla/5.0 (Linux; Android 5.1; HUAWEI CUN-L22 Build/HUAWEICUN-L22; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/62.0.3202.84 Mobile Safari/537.36",
|
|
||||||
"Mozilla/5.0 (Linux; Android 5.1.1; A37fw Build/LMY47V) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/62.0.3202.84 Mobile Safari/537.36",
|
|
||||||
"Mozilla/5.0 (Linux; Android 7.0; SM-J730GM Build/NRD90M) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.111 Mobile Safari/537.36",
|
|
||||||
"Mozilla/5.0 (Linux; Android 7.0; SM-G610F Build/NRD90M) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.111 Mobile Safari/537.36",
|
|
||||||
"Mozilla/5.0 (Windows NT 6.3; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/41.0.2272.101 Safari/537.36",
|
|
||||||
"Mozilla/5.0 (Linux; Android 7.1.2; Redmi Note 5A Build/N2G47H; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/63.0.3239.111 Mobile Safari/537.36",
|
|
||||||
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.77 Safari/537.36",
|
|
||||||
"Mozilla/5.0 (Linux; Android 7.0; Redmi Note 4 Build/NRD90M) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.111 Mobile Safari/537.36",
|
|
||||||
"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/47.0.2526.106 Safari/537.36",
|
|
||||||
"Mozilla/5.0 (Unknown; Linux) AppleWebKit/538.1 (KHTML, like Gecko) Chrome/v1.0.0 Safari/538.1",
|
|
||||||
"Mozilla/5.0 (Linux; Android 7.0; BLL-L22 Build/HUAWEIBLL-L22) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/55.0.2883.91 Mobile Safari/537.36",
|
|
||||||
"Mozilla/5.0 (Linux; Android 7.0; SM-J710F Build/NRD90M) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/62.0.3202.84 Mobile Safari/537.36",
|
|
||||||
"Mozilla/5.0 (Linux; Android 6.0.1; SM-G532M Build/MMB29T) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/55.0.2883.91 Mobile Safari/537.36",
|
|
||||||
"Mozilla/5.0 (Linux; Android 7.1.1; CPH1723 Build/N6F26Q) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/61.0.3163.98 Mobile Safari/537.36",
|
|
||||||
"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/41.0.2272.118 Safari/537.36",
|
|
||||||
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/61.0.3163.79 Safari/537.36",
|
|
||||||
"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/45.0.2454.101 Safari/537.36",
|
|
||||||
"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/50.0.2661.94 Safari/537.36",
|
|
||||||
"Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/66.0.3359.139 Safari/537.36",
|
|
||||||
"Mozilla/5.0 (Linux; Android 8.0.0; FIG-LX3 Build/HUAWEIFIG-LX3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.100 Mobile Safari/537.36",
|
|
||||||
"Mozilla/5.0 (Windows; U; Windows NT 6.1; de-DE) AppleWebKit/534.17 (KHTML, like Gecko) Chrome/10.0.649.0 Safari/534.17",
|
|
||||||
"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/52.0.2743.116 Safari/537.36",
|
|
||||||
"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/51.0.2704.63 Safari/537.36",
|
|
||||||
"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/41.0.2228.0 Safari/537.36",
|
|
||||||
"Mozilla/5.0 (Windows NT 6.3; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.67 Safari/537.36",
|
|
||||||
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/62.0.3202.94 Safari/537.36",
|
|
||||||
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.110 Safari/537.36",
|
|
||||||
"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/67.0.3396.99 Safari/537.36",
|
|
||||||
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/54.0.2840.99 Safari/537.36",
|
|
||||||
"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.67 Safari/537.36",
|
|
||||||
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.36",
|
|
||||||
"Mozilla/5.0 (Windows NT 10.0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.67 Safari/537.36",
|
|
||||||
"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.77 Safari/537.36",
|
|
||||||
"Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.67 Safari/537.36",
|
|
||||||
"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/43.0.2357.65 Safari/537.36",
|
|
||||||
"Mozilla/5.0 (Linux; Android 7.1; Mi A1 Build/N2G47H) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.83 Mobile Safari/537.36",
|
|
||||||
"Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/66.0.3359.117 Safari/537.36",
|
|
||||||
"Mozilla/5.0 (Windows NT 6.3; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/57.0.2987.133 Safari/537.36",
|
|
||||||
"Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/65.0.3325.181 Safari/537.36",
|
|
||||||
"Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US) AppleWebKit/533.4 (KHTML, like Gecko) Chrome/5.0.375.99 Safari/533.4",
|
|
||||||
"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/36.0.1985.125 Safari/537.36",
|
|
||||||
"Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/62.0.3202.89 Safari/537.36",
|
|
||||||
"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/47.0.2526.111 Safari/537.36 MVisionPlayer/1.0.0.0",
|
|
||||||
"Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/51.0.2704.103 Safari/537.36",
|
|
||||||
"Mozilla/5.0 (Linux; Android 5.1; A37f Build/LMY47V) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/43.0.2357.93 Mobile Safari/537.36",
|
|
||||||
"Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.132 Safari/537.36",
|
|
||||||
"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.87 Safari/537.36",
|
|
||||||
"Mozilla/5.0 (Windows NT 6.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/57.0.2987.133 Safari/537.36",
|
|
||||||
"Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/60.0.3112.113 Safari/537.36",
|
|
||||||
"Mozilla/5.0 (Windows NT 6.2; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/57.0.2987.133 Safari/537.36",
|
|
||||||
"Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/61.0.3163.100 Safari/537.36",
|
|
||||||
"Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/46.0.2490.86 Safari/537.36",
|
|
||||||
"Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.110 Safari/537.36",
|
|
||||||
"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/41.0.2272.76 Safari/537.36",
|
|
||||||
"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/55.0.2883.87 Safari/537.36",
|
|
||||||
"Mozilla/5.0 (Windows NT 5.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/57.0.2987.133 Safari/537.36",
|
|
||||||
"Mozilla/5.0 (Windows NT 6.3; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.77 Safari/537.36",
|
|
||||||
"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.102 Safari/537.36",
|
|
||||||
"Mozilla/5.0 (Windows NT 6.3; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/46.0.2490.80 Safari/537.36",
|
|
||||||
"Mozilla/5.0 (Linux; Android 6.0.1; CPH1607 Build/MMB29M; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/63.0.3239.111 Mobile Safari/537.36",
|
|
||||||
"Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/54.0.2840.99 Safari/537.36",
|
|
||||||
"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36",
|
|
||||||
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/61.0.3163.100 Safari/537.36",
|
|
||||||
"Mozilla/5.0 (Linux; Android 6.0.1; vivo 1603 Build/MMB29M) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.83 Mobile Safari/537.36",
|
|
||||||
"Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/64.0.3282.186 Safari/537.36",
|
|
||||||
"Mozilla/5.0 (Linux; Android 6.0.1; SM-G532M Build/MMB29T) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.100 Mobile Safari/537.36",
|
|
||||||
"Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.36",
|
|
||||||
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/67.0.3396.87 Safari/537.36",
|
|
||||||
"Mozilla/5.0 (Linux; Android 6.0.1; Redmi 4A Build/MMB29M; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/60.0.3112.116 Mobile Safari/537.36",
|
|
||||||
"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/51.0.2704.103 Safari/537.36",
|
|
||||||
"Mozilla/5.0 (Windows NT 6.0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/49.0.2623.112 Safari/537.36",
|
|
||||||
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/60.0.3112.113 Safari/537.36",
|
|
||||||
"Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/60.0.3112.90 Safari/537.36",
|
|
||||||
"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/44.0.2403.157 Safari/537.36",
|
|
||||||
"Mozilla/5.0 (Windows NT 5.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/46.0.2490.71 Safari/537.36",
|
|
||||||
"Mozilla/5.0 (Windows NT 5.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/60.0.3112.90 Safari/537.36",
|
|
||||||
"Mozilla/5.0 (Windows NT 6.2; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/60.0.3112.90 Safari/537.36",
|
|
||||||
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.100 Safari/537.36",
|
|
||||||
"Mozilla/5.0 (Windows NT 6.3; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/60.0.3112.113 Safari/537.36",
|
|
||||||
"Mozilla/5.0 (Windows NT 6.2; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/42.0.2311.90 Safari/537.36",
|
|
||||||
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.132 Safari/537.36",
|
|
||||||
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/57.0.2987.133 Safari/537.36",
|
|
||||||
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/60.0.3112.90 Safari/537.36",
|
|
||||||
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.77 Safari/537.36",
|
|
||||||
"Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.100 Safari/537.36",
|
|
||||||
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/67.0.3396.99 Safari/537.36",
|
|
||||||
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/61.0.3163.100 Safari/537.36",
|
|
||||||
"Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.77 Safari/537.36",
|
|
||||||
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.102 Safari/537.36",
|
|
||||||
"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.31 (KHTML, like Gecko) Chrome/26.0.1410.64 Safari/537.31",
|
|
||||||
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.84 Safari/537.36",
|
|
||||||
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/55.0.2883.87 Safari/537.36",
|
|
||||||
"Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/57.0.2987.133 Safari/537.36",
|
|
||||||
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/64.0.3282.186 Safari/537.36",
|
|
||||||
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/59.0.3071.115 Safari/537.36",
|
|
||||||
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/68.0.3440.106 Safari/537.36",
|
|
||||||
"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.100 Safari/537.36",
|
|
||||||
"Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/55.0.2883.87 Safari/537.36",
|
|
||||||
"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/46.0.2490.80 Safari/537.36",
|
|
||||||
"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/36.0.1985.143 Safari/537.36",
|
|
||||||
"Mozilla/5.0 (Windows NT 5.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/49.0.2623.112 Safari/537.36",
|
|
||||||
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.67 Safari/537.36",
|
|
||||||
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/65.0.3325.181 Safari/537.36",
|
|
||||||
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/66.0.3359.181 Safari/537.36",
|
|
||||||
"Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/67.0.3396.99 Safari/537.36",
|
|
||||||
"Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/52.0.2743.116 Safari/537.36",
|
|
||||||
"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/51.0.2704.106 Safari/537.36",
|
|
||||||
"Mozilla/5.0 (Windows NT 6.3; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.100 Safari/537.36",
|
|
||||||
"Mozilla/5.0 (Linux; Android 6.0.1; SM-G532G Build/MMB29T) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.83 Mobile Safari/537.36",
|
|
||||||
"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/48.0.2564.109 Safari/537.36",
|
|
||||||
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/66.0.3359.117 Safari/537.36",
|
|
||||||
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/66.0.3359.139 Safari/537.36",
|
|
||||||
"Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.102 Safari/537.36",
|
|
||||||
"Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.67 Safari/537.36",
|
|
||||||
"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/34.0.1847.131 Safari/537.36",
|
|
||||||
"Mozilla/5.0 (Linux; Android 6.0; vivo 1713 Build/MRA58K) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/53.0.2785.124 Mobile Safari/537.36",
|
|
||||||
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/62.0.3202.89 Safari/537.36",
|
|
||||||
"Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/46.0.2490.80 Safari/537.36",
|
|
||||||
"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/41.0.2272.101 Safari/537.36",
|
|
||||||
"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.67 Safari/537.36",
|
|
||||||
"Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/68.0.3440.106 Safari/537.36",
|
|
||||||
}
|
|
@ -1,42 +0,0 @@
|
|||||||
package obfs
|
|
||||||
|
|
||||||
import (
|
|
||||||
"errors"
|
|
||||||
"fmt"
|
|
||||||
"net"
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
|
||||||
errTLS12TicketAuthIncorrectMagicNumber = errors.New("tls1.2_ticket_auth incorrect magic number")
|
|
||||||
errTLS12TicketAuthTooShortData = errors.New("tls1.2_ticket_auth too short data")
|
|
||||||
errTLS12TicketAuthHMACError = errors.New("tls1.2_ticket_auth hmac verifying failed")
|
|
||||||
)
|
|
||||||
|
|
||||||
type authData struct {
|
|
||||||
clientID [32]byte
|
|
||||||
}
|
|
||||||
|
|
||||||
type Obfs interface {
|
|
||||||
StreamConn(net.Conn) net.Conn
|
|
||||||
}
|
|
||||||
|
|
||||||
type obfsCreator func(b *Base) Obfs
|
|
||||||
|
|
||||||
var obfsList = make(map[string]struct {
|
|
||||||
overhead int
|
|
||||||
new obfsCreator
|
|
||||||
})
|
|
||||||
|
|
||||||
func register(name string, c obfsCreator, o int) {
|
|
||||||
obfsList[name] = struct {
|
|
||||||
overhead int
|
|
||||||
new obfsCreator
|
|
||||||
}{overhead: o, new: c}
|
|
||||||
}
|
|
||||||
|
|
||||||
func PickObfs(name string, b *Base) (Obfs, int, error) {
|
|
||||||
if choice, ok := obfsList[name]; ok {
|
|
||||||
return choice.new(b), choice.overhead, nil
|
|
||||||
}
|
|
||||||
return nil, 0, fmt.Errorf("Obfs %s not supported", name)
|
|
||||||
}
|
|
@ -1,15 +0,0 @@
|
|||||||
package obfs
|
|
||||||
|
|
||||||
import "net"
|
|
||||||
|
|
||||||
type plain struct{}
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
register("plain", newPlain, 0)
|
|
||||||
}
|
|
||||||
|
|
||||||
func newPlain(b *Base) Obfs {
|
|
||||||
return &plain{}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *plain) StreamConn(c net.Conn) net.Conn { return c }
|
|
@ -1,71 +0,0 @@
|
|||||||
package obfs
|
|
||||||
|
|
||||||
import (
|
|
||||||
"encoding/binary"
|
|
||||||
"hash/crc32"
|
|
||||||
"math/rand"
|
|
||||||
"net"
|
|
||||||
|
|
||||||
"github.com/Dreamacro/clash/common/pool"
|
|
||||||
)
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
register("random_head", newRandomHead, 0)
|
|
||||||
}
|
|
||||||
|
|
||||||
type randomHead struct {
|
|
||||||
*Base
|
|
||||||
}
|
|
||||||
|
|
||||||
func newRandomHead(b *Base) Obfs {
|
|
||||||
return &randomHead{Base: b}
|
|
||||||
}
|
|
||||||
|
|
||||||
type randomHeadConn struct {
|
|
||||||
net.Conn
|
|
||||||
*randomHead
|
|
||||||
hasSentHeader bool
|
|
||||||
rawTransSent bool
|
|
||||||
rawTransRecv bool
|
|
||||||
buf []byte
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *randomHead) StreamConn(c net.Conn) net.Conn {
|
|
||||||
return &randomHeadConn{Conn: c, randomHead: r}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *randomHeadConn) Read(b []byte) (int, error) {
|
|
||||||
if c.rawTransRecv {
|
|
||||||
return c.Conn.Read(b)
|
|
||||||
}
|
|
||||||
buf := pool.Get(pool.RelayBufferSize)
|
|
||||||
defer pool.Put(buf)
|
|
||||||
c.Conn.Read(buf)
|
|
||||||
c.rawTransRecv = true
|
|
||||||
c.Write(nil)
|
|
||||||
return 0, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *randomHeadConn) Write(b []byte) (int, error) {
|
|
||||||
if c.rawTransSent {
|
|
||||||
return c.Conn.Write(b)
|
|
||||||
}
|
|
||||||
c.buf = append(c.buf, b...)
|
|
||||||
if !c.hasSentHeader {
|
|
||||||
c.hasSentHeader = true
|
|
||||||
dataLength := rand.Intn(96) + 4
|
|
||||||
buf := pool.Get(dataLength + 4)
|
|
||||||
defer pool.Put(buf)
|
|
||||||
rand.Read(buf[:dataLength])
|
|
||||||
binary.LittleEndian.PutUint32(buf[dataLength:], 0xffffffff-crc32.ChecksumIEEE(buf[:dataLength]))
|
|
||||||
_, err := c.Conn.Write(buf)
|
|
||||||
return len(b), err
|
|
||||||
}
|
|
||||||
if c.rawTransRecv {
|
|
||||||
_, err := c.Conn.Write(c.buf)
|
|
||||||
c.buf = nil
|
|
||||||
c.rawTransSent = true
|
|
||||||
return len(b), err
|
|
||||||
}
|
|
||||||
return len(b), nil
|
|
||||||
}
|
|
@ -1,226 +0,0 @@
|
|||||||
package obfs
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"crypto/hmac"
|
|
||||||
"encoding/binary"
|
|
||||||
"math/rand"
|
|
||||||
"net"
|
|
||||||
"strings"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/Dreamacro/clash/common/pool"
|
|
||||||
"github.com/Dreamacro/clash/transport/ssr/tools"
|
|
||||||
)
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
register("tls1.2_ticket_auth", newTLS12Ticket, 5)
|
|
||||||
register("tls1.2_ticket_fastauth", newTLS12Ticket, 5)
|
|
||||||
}
|
|
||||||
|
|
||||||
type tls12Ticket struct {
|
|
||||||
*Base
|
|
||||||
*authData
|
|
||||||
}
|
|
||||||
|
|
||||||
func newTLS12Ticket(b *Base) Obfs {
|
|
||||||
r := &tls12Ticket{Base: b, authData: &authData{}}
|
|
||||||
rand.Read(r.clientID[:])
|
|
||||||
return r
|
|
||||||
}
|
|
||||||
|
|
||||||
type tls12TicketConn struct {
|
|
||||||
net.Conn
|
|
||||||
*tls12Ticket
|
|
||||||
handshakeStatus int
|
|
||||||
decoded bytes.Buffer
|
|
||||||
underDecoded bytes.Buffer
|
|
||||||
sendBuf bytes.Buffer
|
|
||||||
}
|
|
||||||
|
|
||||||
func (t *tls12Ticket) StreamConn(c net.Conn) net.Conn {
|
|
||||||
return &tls12TicketConn{Conn: c, tls12Ticket: t}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *tls12TicketConn) Read(b []byte) (int, error) {
|
|
||||||
if c.decoded.Len() > 0 {
|
|
||||||
return c.decoded.Read(b)
|
|
||||||
}
|
|
||||||
|
|
||||||
buf := pool.Get(pool.RelayBufferSize)
|
|
||||||
defer pool.Put(buf)
|
|
||||||
n, err := c.Conn.Read(buf)
|
|
||||||
if err != nil {
|
|
||||||
return 0, err
|
|
||||||
}
|
|
||||||
|
|
||||||
if c.handshakeStatus == 8 {
|
|
||||||
c.underDecoded.Write(buf[:n])
|
|
||||||
for c.underDecoded.Len() > 5 {
|
|
||||||
if !bytes.Equal(c.underDecoded.Bytes()[:3], []byte{0x17, 3, 3}) {
|
|
||||||
c.underDecoded.Reset()
|
|
||||||
return 0, errTLS12TicketAuthIncorrectMagicNumber
|
|
||||||
}
|
|
||||||
size := int(binary.BigEndian.Uint16(c.underDecoded.Bytes()[3:5]))
|
|
||||||
if c.underDecoded.Len() < 5+size {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
c.underDecoded.Next(5)
|
|
||||||
c.decoded.Write(c.underDecoded.Next(size))
|
|
||||||
}
|
|
||||||
n, _ = c.decoded.Read(b)
|
|
||||||
return n, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
if n < 11+32+1+32 {
|
|
||||||
return 0, errTLS12TicketAuthTooShortData
|
|
||||||
}
|
|
||||||
|
|
||||||
if !hmac.Equal(buf[33:43], c.hmacSHA1(buf[11:33])[:10]) || !hmac.Equal(buf[n-10:n], c.hmacSHA1(buf[:n-10])[:10]) {
|
|
||||||
return 0, errTLS12TicketAuthHMACError
|
|
||||||
}
|
|
||||||
|
|
||||||
c.Write(nil)
|
|
||||||
return 0, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *tls12TicketConn) Write(b []byte) (int, error) {
|
|
||||||
length := len(b)
|
|
||||||
if c.handshakeStatus == 8 {
|
|
||||||
buf := pool.GetBuffer()
|
|
||||||
defer pool.PutBuffer(buf)
|
|
||||||
for len(b) > 2048 {
|
|
||||||
size := rand.Intn(4096) + 100
|
|
||||||
if len(b) < size {
|
|
||||||
size = len(b)
|
|
||||||
}
|
|
||||||
packData(buf, b[:size])
|
|
||||||
b = b[size:]
|
|
||||||
}
|
|
||||||
if len(b) > 0 {
|
|
||||||
packData(buf, b)
|
|
||||||
}
|
|
||||||
_, err := c.Conn.Write(buf.Bytes())
|
|
||||||
if err != nil {
|
|
||||||
return 0, err
|
|
||||||
}
|
|
||||||
return length, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(b) > 0 {
|
|
||||||
packData(&c.sendBuf, b)
|
|
||||||
}
|
|
||||||
|
|
||||||
if c.handshakeStatus == 0 {
|
|
||||||
c.handshakeStatus = 1
|
|
||||||
|
|
||||||
data := pool.GetBuffer()
|
|
||||||
defer pool.PutBuffer(data)
|
|
||||||
|
|
||||||
data.Write([]byte{3, 3})
|
|
||||||
c.packAuthData(data)
|
|
||||||
data.WriteByte(0x20)
|
|
||||||
data.Write(c.clientID[:])
|
|
||||||
data.Write([]byte{0x00, 0x1c, 0xc0, 0x2b, 0xc0, 0x2f, 0xcc, 0xa9, 0xcc, 0xa8, 0xcc, 0x14, 0xcc, 0x13, 0xc0, 0x0a, 0xc0, 0x14, 0xc0, 0x09, 0xc0, 0x13, 0x00, 0x9c, 0x00, 0x35, 0x00, 0x2f, 0x00, 0x0a})
|
|
||||||
data.Write([]byte{0x1, 0x0})
|
|
||||||
|
|
||||||
ext := pool.GetBuffer()
|
|
||||||
defer pool.PutBuffer(ext)
|
|
||||||
|
|
||||||
host := c.getHost()
|
|
||||||
ext.Write([]byte{0xff, 0x01, 0x00, 0x01, 0x00})
|
|
||||||
packSNIData(ext, host)
|
|
||||||
ext.Write([]byte{0, 0x17, 0, 0})
|
|
||||||
c.packTicketBuf(ext, host)
|
|
||||||
ext.Write([]byte{0x00, 0x0d, 0x00, 0x16, 0x00, 0x14, 0x06, 0x01, 0x06, 0x03, 0x05, 0x01, 0x05, 0x03, 0x04, 0x01, 0x04, 0x03, 0x03, 0x01, 0x03, 0x03, 0x02, 0x01, 0x02, 0x03})
|
|
||||||
ext.Write([]byte{0x00, 0x05, 0x00, 0x05, 0x01, 0x00, 0x00, 0x00, 0x00})
|
|
||||||
ext.Write([]byte{0x00, 0x12, 0x00, 0x00})
|
|
||||||
ext.Write([]byte{0x75, 0x50, 0x00, 0x00})
|
|
||||||
ext.Write([]byte{0x00, 0x0b, 0x00, 0x02, 0x01, 0x00})
|
|
||||||
ext.Write([]byte{0x00, 0x0a, 0x00, 0x06, 0x00, 0x04, 0x00, 0x17, 0x00, 0x18})
|
|
||||||
|
|
||||||
binary.Write(data, binary.BigEndian, uint16(ext.Len()))
|
|
||||||
data.ReadFrom(ext)
|
|
||||||
|
|
||||||
ret := pool.GetBuffer()
|
|
||||||
defer pool.PutBuffer(ret)
|
|
||||||
|
|
||||||
ret.Write([]byte{0x16, 3, 1})
|
|
||||||
binary.Write(ret, binary.BigEndian, uint16(data.Len()+4))
|
|
||||||
ret.Write([]byte{1, 0})
|
|
||||||
binary.Write(ret, binary.BigEndian, uint16(data.Len()))
|
|
||||||
ret.ReadFrom(data)
|
|
||||||
|
|
||||||
_, err := c.Conn.Write(ret.Bytes())
|
|
||||||
if err != nil {
|
|
||||||
return 0, err
|
|
||||||
}
|
|
||||||
return length, nil
|
|
||||||
} else if c.handshakeStatus == 1 && len(b) == 0 {
|
|
||||||
buf := pool.GetBuffer()
|
|
||||||
defer pool.PutBuffer(buf)
|
|
||||||
|
|
||||||
buf.Write([]byte{0x14, 3, 3, 0, 1, 1, 0x16, 3, 3, 0, 0x20})
|
|
||||||
tools.AppendRandBytes(buf, 22)
|
|
||||||
buf.Write(c.hmacSHA1(buf.Bytes())[:10])
|
|
||||||
buf.ReadFrom(&c.sendBuf)
|
|
||||||
|
|
||||||
c.handshakeStatus = 8
|
|
||||||
|
|
||||||
_, err := c.Conn.Write(buf.Bytes())
|
|
||||||
return 0, err
|
|
||||||
}
|
|
||||||
return length, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func packData(buf *bytes.Buffer, data []byte) {
|
|
||||||
buf.Write([]byte{0x17, 3, 3})
|
|
||||||
binary.Write(buf, binary.BigEndian, uint16(len(data)))
|
|
||||||
buf.Write(data)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (t *tls12Ticket) packAuthData(buf *bytes.Buffer) {
|
|
||||||
binary.Write(buf, binary.BigEndian, uint32(time.Now().Unix()))
|
|
||||||
tools.AppendRandBytes(buf, 18)
|
|
||||||
buf.Write(t.hmacSHA1(buf.Bytes()[buf.Len()-22:])[:10])
|
|
||||||
}
|
|
||||||
|
|
||||||
func packSNIData(buf *bytes.Buffer, u string) {
|
|
||||||
len := uint16(len(u))
|
|
||||||
buf.Write([]byte{0, 0})
|
|
||||||
binary.Write(buf, binary.BigEndian, len+5)
|
|
||||||
binary.Write(buf, binary.BigEndian, len+3)
|
|
||||||
buf.WriteByte(0)
|
|
||||||
binary.Write(buf, binary.BigEndian, len)
|
|
||||||
buf.WriteString(u)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *tls12TicketConn) packTicketBuf(buf *bytes.Buffer, u string) {
|
|
||||||
length := 16 * (rand.Intn(17) + 8)
|
|
||||||
buf.Write([]byte{0, 0x23})
|
|
||||||
binary.Write(buf, binary.BigEndian, uint16(length))
|
|
||||||
tools.AppendRandBytes(buf, length)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (t *tls12Ticket) hmacSHA1(data []byte) []byte {
|
|
||||||
key := pool.Get(len(t.Key) + 32)
|
|
||||||
defer pool.Put(key)
|
|
||||||
copy(key, t.Key)
|
|
||||||
copy(key[len(t.Key):], t.clientID[:])
|
|
||||||
|
|
||||||
sha1Data := tools.HmacSHA1(key, data)
|
|
||||||
return sha1Data[:10]
|
|
||||||
}
|
|
||||||
|
|
||||||
func (t *tls12Ticket) getHost() string {
|
|
||||||
host := t.Param
|
|
||||||
if len(host) == 0 {
|
|
||||||
host = t.Host
|
|
||||||
}
|
|
||||||
if len(host) > 0 && host[len(host)-1] >= '0' && host[len(host)-1] <= '9' {
|
|
||||||
host = ""
|
|
||||||
}
|
|
||||||
hosts := strings.Split(host, ",")
|
|
||||||
host = hosts[rand.Intn(len(hosts))]
|
|
||||||
return host
|
|
||||||
}
|
|
@ -1,18 +0,0 @@
|
|||||||
package protocol
|
|
||||||
|
|
||||||
import "github.com/Dreamacro/clash/transport/ssr/tools"
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
register("auth_aes128_md5", newAuthAES128MD5, 9)
|
|
||||||
}
|
|
||||||
|
|
||||||
func newAuthAES128MD5(b *Base) Protocol {
|
|
||||||
a := &authAES128{
|
|
||||||
Base: b,
|
|
||||||
authData: &authData{},
|
|
||||||
authAES128Function: &authAES128Function{salt: "auth_aes128_md5", hmac: tools.HmacMD5, hashDigest: tools.MD5Sum},
|
|
||||||
userData: &userData{},
|
|
||||||
}
|
|
||||||
a.initUserData()
|
|
||||||
return a
|
|
||||||
}
|
|
@ -1,277 +0,0 @@
|
|||||||
package protocol
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"encoding/binary"
|
|
||||||
"math"
|
|
||||||
"math/rand"
|
|
||||||
"net"
|
|
||||||
"strconv"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"github.com/Dreamacro/clash/common/pool"
|
|
||||||
"github.com/Dreamacro/clash/transport/ssr/tools"
|
|
||||||
)
|
|
||||||
|
|
||||||
type (
|
|
||||||
hmacMethod func(key, data []byte) []byte
|
|
||||||
hashDigestMethod func([]byte) []byte
|
|
||||||
)
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
register("auth_aes128_sha1", newAuthAES128SHA1, 9)
|
|
||||||
}
|
|
||||||
|
|
||||||
type authAES128Function struct {
|
|
||||||
salt string
|
|
||||||
hmac hmacMethod
|
|
||||||
hashDigest hashDigestMethod
|
|
||||||
}
|
|
||||||
|
|
||||||
type authAES128 struct {
|
|
||||||
*Base
|
|
||||||
*authData
|
|
||||||
*authAES128Function
|
|
||||||
*userData
|
|
||||||
iv []byte
|
|
||||||
hasSentHeader bool
|
|
||||||
rawTrans bool
|
|
||||||
packID uint32
|
|
||||||
recvID uint32
|
|
||||||
}
|
|
||||||
|
|
||||||
func newAuthAES128SHA1(b *Base) Protocol {
|
|
||||||
a := &authAES128{
|
|
||||||
Base: b,
|
|
||||||
authData: &authData{},
|
|
||||||
authAES128Function: &authAES128Function{salt: "auth_aes128_sha1", hmac: tools.HmacSHA1, hashDigest: tools.SHA1Sum},
|
|
||||||
userData: &userData{},
|
|
||||||
}
|
|
||||||
a.initUserData()
|
|
||||||
return a
|
|
||||||
}
|
|
||||||
|
|
||||||
func (a *authAES128) initUserData() {
|
|
||||||
params := strings.Split(a.Param, ":")
|
|
||||||
if len(params) > 1 {
|
|
||||||
if userID, err := strconv.ParseUint(params[0], 10, 32); err == nil {
|
|
||||||
binary.LittleEndian.PutUint32(a.userID[:], uint32(userID))
|
|
||||||
a.userKey = a.hashDigest([]byte(params[1]))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if len(a.userKey) == 0 {
|
|
||||||
a.userKey = a.Key
|
|
||||||
rand.Read(a.userID[:])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (a *authAES128) StreamConn(c net.Conn, iv []byte) net.Conn {
|
|
||||||
p := &authAES128{
|
|
||||||
Base: a.Base,
|
|
||||||
authData: a.next(),
|
|
||||||
authAES128Function: a.authAES128Function,
|
|
||||||
userData: a.userData,
|
|
||||||
packID: 1,
|
|
||||||
recvID: 1,
|
|
||||||
}
|
|
||||||
p.iv = iv
|
|
||||||
return &Conn{Conn: c, Protocol: p}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (a *authAES128) PacketConn(c net.PacketConn) net.PacketConn {
|
|
||||||
p := &authAES128{
|
|
||||||
Base: a.Base,
|
|
||||||
authAES128Function: a.authAES128Function,
|
|
||||||
userData: a.userData,
|
|
||||||
}
|
|
||||||
return &PacketConn{PacketConn: c, Protocol: p}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (a *authAES128) Decode(dst, src *bytes.Buffer) error {
|
|
||||||
if a.rawTrans {
|
|
||||||
dst.ReadFrom(src)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
for src.Len() > 4 {
|
|
||||||
macKey := pool.Get(len(a.userKey) + 4)
|
|
||||||
defer pool.Put(macKey)
|
|
||||||
copy(macKey, a.userKey)
|
|
||||||
binary.LittleEndian.PutUint32(macKey[len(a.userKey):], a.recvID)
|
|
||||||
if !bytes.Equal(a.hmac(macKey, src.Bytes()[:2])[:2], src.Bytes()[2:4]) {
|
|
||||||
src.Reset()
|
|
||||||
return errAuthAES128MACError
|
|
||||||
}
|
|
||||||
|
|
||||||
length := int(binary.LittleEndian.Uint16(src.Bytes()[:2]))
|
|
||||||
if length >= 8192 || length < 7 {
|
|
||||||
a.rawTrans = true
|
|
||||||
src.Reset()
|
|
||||||
return errAuthAES128LengthError
|
|
||||||
}
|
|
||||||
if length > src.Len() {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
|
|
||||||
if !bytes.Equal(a.hmac(macKey, src.Bytes()[:length-4])[:4], src.Bytes()[length-4:length]) {
|
|
||||||
a.rawTrans = true
|
|
||||||
src.Reset()
|
|
||||||
return errAuthAES128ChksumError
|
|
||||||
}
|
|
||||||
|
|
||||||
a.recvID++
|
|
||||||
|
|
||||||
pos := int(src.Bytes()[4])
|
|
||||||
if pos < 255 {
|
|
||||||
pos += 4
|
|
||||||
} else {
|
|
||||||
pos = int(binary.LittleEndian.Uint16(src.Bytes()[5:7])) + 4
|
|
||||||
}
|
|
||||||
dst.Write(src.Bytes()[pos : length-4])
|
|
||||||
src.Next(length)
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (a *authAES128) Encode(buf *bytes.Buffer, b []byte) error {
|
|
||||||
fullDataLength := len(b)
|
|
||||||
if !a.hasSentHeader {
|
|
||||||
dataLength := getDataLength(b)
|
|
||||||
a.packAuthData(buf, b[:dataLength])
|
|
||||||
b = b[dataLength:]
|
|
||||||
a.hasSentHeader = true
|
|
||||||
}
|
|
||||||
for len(b) > 8100 {
|
|
||||||
a.packData(buf, b[:8100], fullDataLength)
|
|
||||||
b = b[8100:]
|
|
||||||
}
|
|
||||||
if len(b) > 0 {
|
|
||||||
a.packData(buf, b, fullDataLength)
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (a *authAES128) DecodePacket(b []byte) ([]byte, error) {
|
|
||||||
if len(b) < 4 {
|
|
||||||
return nil, errAuthAES128LengthError
|
|
||||||
}
|
|
||||||
if !bytes.Equal(a.hmac(a.Key, b[:len(b)-4])[:4], b[len(b)-4:]) {
|
|
||||||
return nil, errAuthAES128ChksumError
|
|
||||||
}
|
|
||||||
return b[:len(b)-4], nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (a *authAES128) EncodePacket(buf *bytes.Buffer, b []byte) error {
|
|
||||||
buf.Write(b)
|
|
||||||
buf.Write(a.userID[:])
|
|
||||||
buf.Write(a.hmac(a.userKey, buf.Bytes())[:4])
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (a *authAES128) packData(poolBuf *bytes.Buffer, data []byte, fullDataLength int) {
|
|
||||||
dataLength := len(data)
|
|
||||||
randDataLength := a.getRandDataLengthForPackData(dataLength, fullDataLength)
|
|
||||||
/*
|
|
||||||
2: uint16 LittleEndian packedDataLength
|
|
||||||
2: hmac of packedDataLength
|
|
||||||
3: maxRandDataLengthPrefix (min:1)
|
|
||||||
4: hmac of packedData except the last 4 bytes
|
|
||||||
*/
|
|
||||||
packedDataLength := 2 + 2 + 3 + randDataLength + dataLength + 4
|
|
||||||
if randDataLength < 128 {
|
|
||||||
packedDataLength -= 2
|
|
||||||
}
|
|
||||||
|
|
||||||
macKey := pool.Get(len(a.userKey) + 4)
|
|
||||||
defer pool.Put(macKey)
|
|
||||||
copy(macKey, a.userKey)
|
|
||||||
binary.LittleEndian.PutUint32(macKey[len(a.userKey):], a.packID)
|
|
||||||
a.packID++
|
|
||||||
|
|
||||||
binary.Write(poolBuf, binary.LittleEndian, uint16(packedDataLength))
|
|
||||||
poolBuf.Write(a.hmac(macKey, poolBuf.Bytes()[poolBuf.Len()-2:])[:2])
|
|
||||||
a.packRandData(poolBuf, randDataLength)
|
|
||||||
poolBuf.Write(data)
|
|
||||||
poolBuf.Write(a.hmac(macKey, poolBuf.Bytes()[poolBuf.Len()-packedDataLength+4:])[:4])
|
|
||||||
}
|
|
||||||
|
|
||||||
func trapezoidRandom(max int, d float64) int {
|
|
||||||
base := rand.Float64()
|
|
||||||
if d-0 > 1e-6 {
|
|
||||||
a := 1 - d
|
|
||||||
base = (math.Sqrt(a*a+4*d*base) - a) / (2 * d)
|
|
||||||
}
|
|
||||||
return int(base * float64(max))
|
|
||||||
}
|
|
||||||
|
|
||||||
func (a *authAES128) getRandDataLengthForPackData(dataLength, fullDataLength int) int {
|
|
||||||
if fullDataLength >= 32*1024-a.Overhead {
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
// 1460: tcp_mss
|
|
||||||
revLength := 1460 - dataLength - 9
|
|
||||||
if revLength == 0 {
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
if revLength < 0 {
|
|
||||||
if revLength > -1460 {
|
|
||||||
return trapezoidRandom(revLength+1460, -0.3)
|
|
||||||
}
|
|
||||||
return rand.Intn(32)
|
|
||||||
}
|
|
||||||
if dataLength > 900 {
|
|
||||||
return rand.Intn(revLength)
|
|
||||||
}
|
|
||||||
return trapezoidRandom(revLength, -0.3)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (a *authAES128) packAuthData(poolBuf *bytes.Buffer, data []byte) {
|
|
||||||
if len(data) == 0 {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
dataLength := len(data)
|
|
||||||
randDataLength := a.getRandDataLengthForPackAuthData(dataLength)
|
|
||||||
/*
|
|
||||||
7: checkHead(1) and hmac of checkHead(6)
|
|
||||||
4: userID
|
|
||||||
16: encrypted data of authdata(12), uint16 BigEndian packedDataLength(2) and uint16 BigEndian randDataLength(2)
|
|
||||||
4: hmac of userID and encrypted data
|
|
||||||
4: hmac of packedAuthData except the last 4 bytes
|
|
||||||
*/
|
|
||||||
packedAuthDataLength := 7 + 4 + 16 + 4 + randDataLength + dataLength + 4
|
|
||||||
|
|
||||||
macKey := pool.Get(len(a.iv) + len(a.Key))
|
|
||||||
defer pool.Put(macKey)
|
|
||||||
copy(macKey, a.iv)
|
|
||||||
copy(macKey[len(a.iv):], a.Key)
|
|
||||||
|
|
||||||
poolBuf.WriteByte(byte(rand.Intn(256)))
|
|
||||||
poolBuf.Write(a.hmac(macKey, poolBuf.Bytes())[:6])
|
|
||||||
poolBuf.Write(a.userID[:])
|
|
||||||
err := a.authData.putEncryptedData(poolBuf, a.userKey, [2]int{packedAuthDataLength, randDataLength}, a.salt)
|
|
||||||
if err != nil {
|
|
||||||
poolBuf.Reset()
|
|
||||||
return
|
|
||||||
}
|
|
||||||
poolBuf.Write(a.hmac(macKey, poolBuf.Bytes()[7:])[:4])
|
|
||||||
tools.AppendRandBytes(poolBuf, randDataLength)
|
|
||||||
poolBuf.Write(data)
|
|
||||||
poolBuf.Write(a.hmac(a.userKey, poolBuf.Bytes())[:4])
|
|
||||||
}
|
|
||||||
|
|
||||||
func (a *authAES128) getRandDataLengthForPackAuthData(size int) int {
|
|
||||||
if size > 400 {
|
|
||||||
return rand.Intn(512)
|
|
||||||
}
|
|
||||||
return rand.Intn(1024)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (a *authAES128) packRandData(poolBuf *bytes.Buffer, size int) {
|
|
||||||
if size < 128 {
|
|
||||||
poolBuf.WriteByte(byte(size + 1))
|
|
||||||
tools.AppendRandBytes(poolBuf, size)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
poolBuf.WriteByte(255)
|
|
||||||
binary.Write(poolBuf, binary.LittleEndian, uint16(size+3))
|
|
||||||
tools.AppendRandBytes(poolBuf, size)
|
|
||||||
}
|
|
@ -1,306 +0,0 @@
|
|||||||
package protocol
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"crypto/cipher"
|
|
||||||
"crypto/rand"
|
|
||||||
"crypto/rc4"
|
|
||||||
"encoding/base64"
|
|
||||||
"encoding/binary"
|
|
||||||
"net"
|
|
||||||
"strconv"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"github.com/Dreamacro/clash/common/pool"
|
|
||||||
"github.com/Dreamacro/clash/transport/shadowsocks/core"
|
|
||||||
"github.com/Dreamacro/clash/transport/ssr/tools"
|
|
||||||
)
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
register("auth_chain_a", newAuthChainA, 4)
|
|
||||||
}
|
|
||||||
|
|
||||||
type randDataLengthMethod func(int, []byte, *tools.XorShift128Plus) int
|
|
||||||
|
|
||||||
type authChainA struct {
|
|
||||||
*Base
|
|
||||||
*authData
|
|
||||||
*userData
|
|
||||||
iv []byte
|
|
||||||
salt string
|
|
||||||
hasSentHeader bool
|
|
||||||
rawTrans bool
|
|
||||||
lastClientHash []byte
|
|
||||||
lastServerHash []byte
|
|
||||||
encrypter cipher.Stream
|
|
||||||
decrypter cipher.Stream
|
|
||||||
randomClient tools.XorShift128Plus
|
|
||||||
randomServer tools.XorShift128Plus
|
|
||||||
randDataLength randDataLengthMethod
|
|
||||||
packID uint32
|
|
||||||
recvID uint32
|
|
||||||
}
|
|
||||||
|
|
||||||
func newAuthChainA(b *Base) Protocol {
|
|
||||||
a := &authChainA{
|
|
||||||
Base: b,
|
|
||||||
authData: &authData{},
|
|
||||||
userData: &userData{},
|
|
||||||
salt: "auth_chain_a",
|
|
||||||
}
|
|
||||||
a.initUserData()
|
|
||||||
return a
|
|
||||||
}
|
|
||||||
|
|
||||||
func (a *authChainA) initUserData() {
|
|
||||||
params := strings.Split(a.Param, ":")
|
|
||||||
if len(params) > 1 {
|
|
||||||
if userID, err := strconv.ParseUint(params[0], 10, 32); err == nil {
|
|
||||||
binary.LittleEndian.PutUint32(a.userID[:], uint32(userID))
|
|
||||||
a.userKey = []byte(params[1])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if len(a.userKey) == 0 {
|
|
||||||
a.userKey = a.Key
|
|
||||||
rand.Read(a.userID[:])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (a *authChainA) StreamConn(c net.Conn, iv []byte) net.Conn {
|
|
||||||
p := &authChainA{
|
|
||||||
Base: a.Base,
|
|
||||||
authData: a.next(),
|
|
||||||
userData: a.userData,
|
|
||||||
salt: a.salt,
|
|
||||||
packID: 1,
|
|
||||||
recvID: 1,
|
|
||||||
}
|
|
||||||
p.iv = iv
|
|
||||||
p.randDataLength = p.getRandLength
|
|
||||||
return &Conn{Conn: c, Protocol: p}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (a *authChainA) PacketConn(c net.PacketConn) net.PacketConn {
|
|
||||||
p := &authChainA{
|
|
||||||
Base: a.Base,
|
|
||||||
salt: a.salt,
|
|
||||||
userData: a.userData,
|
|
||||||
}
|
|
||||||
return &PacketConn{PacketConn: c, Protocol: p}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (a *authChainA) Decode(dst, src *bytes.Buffer) error {
|
|
||||||
if a.rawTrans {
|
|
||||||
dst.ReadFrom(src)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
for src.Len() > 4 {
|
|
||||||
macKey := pool.Get(len(a.userKey) + 4)
|
|
||||||
defer pool.Put(macKey)
|
|
||||||
copy(macKey, a.userKey)
|
|
||||||
binary.LittleEndian.PutUint32(macKey[len(a.userKey):], a.recvID)
|
|
||||||
|
|
||||||
dataLength := int(binary.LittleEndian.Uint16(src.Bytes()[:2]) ^ binary.LittleEndian.Uint16(a.lastServerHash[14:16]))
|
|
||||||
randDataLength := a.randDataLength(dataLength, a.lastServerHash, &a.randomServer)
|
|
||||||
length := dataLength + randDataLength
|
|
||||||
|
|
||||||
if length >= 4096 {
|
|
||||||
a.rawTrans = true
|
|
||||||
src.Reset()
|
|
||||||
return errAuthChainLengthError
|
|
||||||
}
|
|
||||||
|
|
||||||
if 4+length > src.Len() {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
|
|
||||||
serverHash := tools.HmacMD5(macKey, src.Bytes()[:length+2])
|
|
||||||
if !bytes.Equal(serverHash[:2], src.Bytes()[length+2:length+4]) {
|
|
||||||
a.rawTrans = true
|
|
||||||
src.Reset()
|
|
||||||
return errAuthChainChksumError
|
|
||||||
}
|
|
||||||
a.lastServerHash = serverHash
|
|
||||||
|
|
||||||
pos := 2
|
|
||||||
if dataLength > 0 && randDataLength > 0 {
|
|
||||||
pos += getRandStartPos(randDataLength, &a.randomServer)
|
|
||||||
}
|
|
||||||
wantedData := src.Bytes()[pos : pos+dataLength]
|
|
||||||
a.decrypter.XORKeyStream(wantedData, wantedData)
|
|
||||||
if a.recvID == 1 {
|
|
||||||
dst.Write(wantedData[2:])
|
|
||||||
} else {
|
|
||||||
dst.Write(wantedData)
|
|
||||||
}
|
|
||||||
a.recvID++
|
|
||||||
src.Next(length + 4)
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (a *authChainA) Encode(buf *bytes.Buffer, b []byte) error {
|
|
||||||
if !a.hasSentHeader {
|
|
||||||
dataLength := getDataLength(b)
|
|
||||||
a.packAuthData(buf, b[:dataLength])
|
|
||||||
b = b[dataLength:]
|
|
||||||
a.hasSentHeader = true
|
|
||||||
}
|
|
||||||
for len(b) > 2800 {
|
|
||||||
a.packData(buf, b[:2800])
|
|
||||||
b = b[2800:]
|
|
||||||
}
|
|
||||||
if len(b) > 0 {
|
|
||||||
a.packData(buf, b)
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (a *authChainA) DecodePacket(b []byte) ([]byte, error) {
|
|
||||||
if len(b) < 9 {
|
|
||||||
return nil, errAuthChainLengthError
|
|
||||||
}
|
|
||||||
if !bytes.Equal(tools.HmacMD5(a.userKey, b[:len(b)-1])[:1], b[len(b)-1:]) {
|
|
||||||
return nil, errAuthChainChksumError
|
|
||||||
}
|
|
||||||
md5Data := tools.HmacMD5(a.Key, b[len(b)-8:len(b)-1])
|
|
||||||
|
|
||||||
randDataLength := udpGetRandLength(md5Data, &a.randomServer)
|
|
||||||
|
|
||||||
key := core.Kdf(base64.StdEncoding.EncodeToString(a.userKey)+base64.StdEncoding.EncodeToString(md5Data), 16)
|
|
||||||
rc4Cipher, err := rc4.NewCipher(key)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
wantedData := b[:len(b)-8-randDataLength]
|
|
||||||
rc4Cipher.XORKeyStream(wantedData, wantedData)
|
|
||||||
return wantedData, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (a *authChainA) EncodePacket(buf *bytes.Buffer, b []byte) error {
|
|
||||||
authData := pool.Get(3)
|
|
||||||
defer pool.Put(authData)
|
|
||||||
rand.Read(authData)
|
|
||||||
|
|
||||||
md5Data := tools.HmacMD5(a.Key, authData)
|
|
||||||
|
|
||||||
randDataLength := udpGetRandLength(md5Data, &a.randomClient)
|
|
||||||
|
|
||||||
key := core.Kdf(base64.StdEncoding.EncodeToString(a.userKey)+base64.StdEncoding.EncodeToString(md5Data), 16)
|
|
||||||
rc4Cipher, err := rc4.NewCipher(key)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
rc4Cipher.XORKeyStream(b, b)
|
|
||||||
|
|
||||||
buf.Write(b)
|
|
||||||
tools.AppendRandBytes(buf, randDataLength)
|
|
||||||
buf.Write(authData)
|
|
||||||
binary.Write(buf, binary.LittleEndian, binary.LittleEndian.Uint32(a.userID[:])^binary.LittleEndian.Uint32(md5Data[:4]))
|
|
||||||
buf.Write(tools.HmacMD5(a.userKey, buf.Bytes())[:1])
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (a *authChainA) packAuthData(poolBuf *bytes.Buffer, data []byte) {
|
|
||||||
/*
|
|
||||||
dataLength := len(data)
|
|
||||||
12: checkHead(4) and hmac of checkHead(8)
|
|
||||||
4: uint32 LittleEndian uid (uid = userID ^ last client hash)
|
|
||||||
16: encrypted data of authdata(12), uint16 LittleEndian overhead(2) and uint16 LittleEndian number zero(2)
|
|
||||||
4: last server hash(4)
|
|
||||||
packedAuthDataLength := 12 + 4 + 16 + 4 + dataLength
|
|
||||||
*/
|
|
||||||
|
|
||||||
macKey := pool.Get(len(a.iv) + len(a.Key))
|
|
||||||
defer pool.Put(macKey)
|
|
||||||
copy(macKey, a.iv)
|
|
||||||
copy(macKey[len(a.iv):], a.Key)
|
|
||||||
|
|
||||||
// check head
|
|
||||||
tools.AppendRandBytes(poolBuf, 4)
|
|
||||||
a.lastClientHash = tools.HmacMD5(macKey, poolBuf.Bytes())
|
|
||||||
a.initRC4Cipher()
|
|
||||||
poolBuf.Write(a.lastClientHash[:8])
|
|
||||||
// uid
|
|
||||||
binary.Write(poolBuf, binary.LittleEndian, binary.LittleEndian.Uint32(a.userID[:])^binary.LittleEndian.Uint32(a.lastClientHash[8:12]))
|
|
||||||
// encrypted data
|
|
||||||
err := a.putEncryptedData(poolBuf, a.userKey, [2]int{a.Overhead, 0}, a.salt)
|
|
||||||
if err != nil {
|
|
||||||
poolBuf.Reset()
|
|
||||||
return
|
|
||||||
}
|
|
||||||
// last server hash
|
|
||||||
a.lastServerHash = tools.HmacMD5(a.userKey, poolBuf.Bytes()[12:])
|
|
||||||
poolBuf.Write(a.lastServerHash[:4])
|
|
||||||
// packed data
|
|
||||||
a.packData(poolBuf, data)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (a *authChainA) packData(poolBuf *bytes.Buffer, data []byte) {
|
|
||||||
a.encrypter.XORKeyStream(data, data)
|
|
||||||
|
|
||||||
macKey := pool.Get(len(a.userKey) + 4)
|
|
||||||
defer pool.Put(macKey)
|
|
||||||
copy(macKey, a.userKey)
|
|
||||||
binary.LittleEndian.PutUint32(macKey[len(a.userKey):], a.packID)
|
|
||||||
a.packID++
|
|
||||||
|
|
||||||
length := uint16(len(data)) ^ binary.LittleEndian.Uint16(a.lastClientHash[14:16])
|
|
||||||
|
|
||||||
originalLength := poolBuf.Len()
|
|
||||||
binary.Write(poolBuf, binary.LittleEndian, length)
|
|
||||||
a.putMixedRandDataAndData(poolBuf, data)
|
|
||||||
a.lastClientHash = tools.HmacMD5(macKey, poolBuf.Bytes()[originalLength:])
|
|
||||||
poolBuf.Write(a.lastClientHash[:2])
|
|
||||||
}
|
|
||||||
|
|
||||||
func (a *authChainA) putMixedRandDataAndData(poolBuf *bytes.Buffer, data []byte) {
|
|
||||||
randDataLength := a.randDataLength(len(data), a.lastClientHash, &a.randomClient)
|
|
||||||
if len(data) == 0 {
|
|
||||||
tools.AppendRandBytes(poolBuf, randDataLength)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if randDataLength > 0 {
|
|
||||||
startPos := getRandStartPos(randDataLength, &a.randomClient)
|
|
||||||
tools.AppendRandBytes(poolBuf, startPos)
|
|
||||||
poolBuf.Write(data)
|
|
||||||
tools.AppendRandBytes(poolBuf, randDataLength-startPos)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
poolBuf.Write(data)
|
|
||||||
}
|
|
||||||
|
|
||||||
func getRandStartPos(length int, random *tools.XorShift128Plus) int {
|
|
||||||
if length == 0 {
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
return int(int64(random.Next()%8589934609) % int64(length))
|
|
||||||
}
|
|
||||||
|
|
||||||
func (a *authChainA) getRandLength(length int, lastHash []byte, random *tools.XorShift128Plus) int {
|
|
||||||
if length > 1440 {
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
random.InitFromBinAndLength(lastHash, length)
|
|
||||||
if length > 1300 {
|
|
||||||
return int(random.Next() % 31)
|
|
||||||
}
|
|
||||||
if length > 900 {
|
|
||||||
return int(random.Next() % 127)
|
|
||||||
}
|
|
||||||
if length > 400 {
|
|
||||||
return int(random.Next() % 521)
|
|
||||||
}
|
|
||||||
return int(random.Next() % 1021)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (a *authChainA) initRC4Cipher() {
|
|
||||||
key := core.Kdf(base64.StdEncoding.EncodeToString(a.userKey)+base64.StdEncoding.EncodeToString(a.lastClientHash), 16)
|
|
||||||
a.encrypter, _ = rc4.NewCipher(key)
|
|
||||||
a.decrypter, _ = rc4.NewCipher(key)
|
|
||||||
}
|
|
||||||
|
|
||||||
func udpGetRandLength(lastHash []byte, random *tools.XorShift128Plus) int {
|
|
||||||
random.InitFromBin(lastHash)
|
|
||||||
return int(random.Next() % 127)
|
|
||||||
}
|
|
@ -1,97 +0,0 @@
|
|||||||
package protocol
|
|
||||||
|
|
||||||
import (
|
|
||||||
"net"
|
|
||||||
"sort"
|
|
||||||
|
|
||||||
"github.com/Dreamacro/clash/transport/ssr/tools"
|
|
||||||
)
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
register("auth_chain_b", newAuthChainB, 4)
|
|
||||||
}
|
|
||||||
|
|
||||||
type authChainB struct {
|
|
||||||
*authChainA
|
|
||||||
dataSizeList []int
|
|
||||||
dataSizeList2 []int
|
|
||||||
}
|
|
||||||
|
|
||||||
func newAuthChainB(b *Base) Protocol {
|
|
||||||
a := &authChainB{
|
|
||||||
authChainA: &authChainA{
|
|
||||||
Base: b,
|
|
||||||
authData: &authData{},
|
|
||||||
userData: &userData{},
|
|
||||||
salt: "auth_chain_b",
|
|
||||||
},
|
|
||||||
}
|
|
||||||
a.initUserData()
|
|
||||||
return a
|
|
||||||
}
|
|
||||||
|
|
||||||
func (a *authChainB) StreamConn(c net.Conn, iv []byte) net.Conn {
|
|
||||||
p := &authChainB{
|
|
||||||
authChainA: &authChainA{
|
|
||||||
Base: a.Base,
|
|
||||||
authData: a.next(),
|
|
||||||
userData: a.userData,
|
|
||||||
salt: a.salt,
|
|
||||||
packID: 1,
|
|
||||||
recvID: 1,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
p.iv = iv
|
|
||||||
p.randDataLength = p.getRandLength
|
|
||||||
p.initDataSize()
|
|
||||||
return &Conn{Conn: c, Protocol: p}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (a *authChainB) initDataSize() {
|
|
||||||
a.dataSizeList = a.dataSizeList[:0]
|
|
||||||
a.dataSizeList2 = a.dataSizeList2[:0]
|
|
||||||
|
|
||||||
a.randomServer.InitFromBin(a.Key)
|
|
||||||
length := a.randomServer.Next()%8 + 4
|
|
||||||
for ; length > 0; length-- {
|
|
||||||
a.dataSizeList = append(a.dataSizeList, int(a.randomServer.Next()%2340%2040%1440))
|
|
||||||
}
|
|
||||||
sort.Ints(a.dataSizeList)
|
|
||||||
|
|
||||||
length = a.randomServer.Next()%16 + 8
|
|
||||||
for ; length > 0; length-- {
|
|
||||||
a.dataSizeList2 = append(a.dataSizeList2, int(a.randomServer.Next()%2340%2040%1440))
|
|
||||||
}
|
|
||||||
sort.Ints(a.dataSizeList2)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (a *authChainB) getRandLength(length int, lashHash []byte, random *tools.XorShift128Plus) int {
|
|
||||||
if length >= 1440 {
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
random.InitFromBinAndLength(lashHash, length)
|
|
||||||
pos := sort.Search(len(a.dataSizeList), func(i int) bool { return a.dataSizeList[i] >= length+a.Overhead })
|
|
||||||
finalPos := pos + int(random.Next()%uint64(len(a.dataSizeList)))
|
|
||||||
if finalPos < len(a.dataSizeList) {
|
|
||||||
return a.dataSizeList[finalPos] - length - a.Overhead
|
|
||||||
}
|
|
||||||
|
|
||||||
pos = sort.Search(len(a.dataSizeList2), func(i int) bool { return a.dataSizeList2[i] >= length+a.Overhead })
|
|
||||||
finalPos = pos + int(random.Next()%uint64(len(a.dataSizeList2)))
|
|
||||||
if finalPos < len(a.dataSizeList2) {
|
|
||||||
return a.dataSizeList2[finalPos] - length - a.Overhead
|
|
||||||
}
|
|
||||||
if finalPos < pos+len(a.dataSizeList2)-1 {
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
if length > 1300 {
|
|
||||||
return int(random.Next() % 31)
|
|
||||||
}
|
|
||||||
if length > 900 {
|
|
||||||
return int(random.Next() % 127)
|
|
||||||
}
|
|
||||||
if length > 400 {
|
|
||||||
return int(random.Next() % 521)
|
|
||||||
}
|
|
||||||
return int(random.Next() % 1021)
|
|
||||||
}
|
|
@ -1,182 +0,0 @@
|
|||||||
package protocol
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"encoding/binary"
|
|
||||||
"hash/adler32"
|
|
||||||
"hash/crc32"
|
|
||||||
"math/rand"
|
|
||||||
"net"
|
|
||||||
|
|
||||||
"github.com/Dreamacro/clash/common/pool"
|
|
||||||
"github.com/Dreamacro/clash/transport/ssr/tools"
|
|
||||||
)
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
register("auth_sha1_v4", newAuthSHA1V4, 7)
|
|
||||||
}
|
|
||||||
|
|
||||||
type authSHA1V4 struct {
|
|
||||||
*Base
|
|
||||||
*authData
|
|
||||||
iv []byte
|
|
||||||
hasSentHeader bool
|
|
||||||
rawTrans bool
|
|
||||||
}
|
|
||||||
|
|
||||||
func newAuthSHA1V4(b *Base) Protocol {
|
|
||||||
return &authSHA1V4{Base: b, authData: &authData{}}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (a *authSHA1V4) StreamConn(c net.Conn, iv []byte) net.Conn {
|
|
||||||
p := &authSHA1V4{Base: a.Base, authData: a.next()}
|
|
||||||
p.iv = iv
|
|
||||||
return &Conn{Conn: c, Protocol: p}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (a *authSHA1V4) PacketConn(c net.PacketConn) net.PacketConn {
|
|
||||||
return c
|
|
||||||
}
|
|
||||||
|
|
||||||
func (a *authSHA1V4) Decode(dst, src *bytes.Buffer) error {
|
|
||||||
if a.rawTrans {
|
|
||||||
dst.ReadFrom(src)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
for src.Len() > 4 {
|
|
||||||
if uint16(crc32.ChecksumIEEE(src.Bytes()[:2])&0xffff) != binary.LittleEndian.Uint16(src.Bytes()[2:4]) {
|
|
||||||
src.Reset()
|
|
||||||
return errAuthSHA1V4CRC32Error
|
|
||||||
}
|
|
||||||
|
|
||||||
length := int(binary.BigEndian.Uint16(src.Bytes()[:2]))
|
|
||||||
if length >= 8192 || length < 7 {
|
|
||||||
a.rawTrans = true
|
|
||||||
src.Reset()
|
|
||||||
return errAuthSHA1V4LengthError
|
|
||||||
}
|
|
||||||
if length > src.Len() {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
|
|
||||||
if adler32.Checksum(src.Bytes()[:length-4]) != binary.LittleEndian.Uint32(src.Bytes()[length-4:length]) {
|
|
||||||
a.rawTrans = true
|
|
||||||
src.Reset()
|
|
||||||
return errAuthSHA1V4Adler32Error
|
|
||||||
}
|
|
||||||
|
|
||||||
pos := int(src.Bytes()[4])
|
|
||||||
if pos < 255 {
|
|
||||||
pos += 4
|
|
||||||
} else {
|
|
||||||
pos = int(binary.BigEndian.Uint16(src.Bytes()[5:7])) + 4
|
|
||||||
}
|
|
||||||
dst.Write(src.Bytes()[pos : length-4])
|
|
||||||
src.Next(length)
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (a *authSHA1V4) Encode(buf *bytes.Buffer, b []byte) error {
|
|
||||||
if !a.hasSentHeader {
|
|
||||||
dataLength := getDataLength(b)
|
|
||||||
|
|
||||||
a.packAuthData(buf, b[:dataLength])
|
|
||||||
b = b[dataLength:]
|
|
||||||
|
|
||||||
a.hasSentHeader = true
|
|
||||||
}
|
|
||||||
for len(b) > 8100 {
|
|
||||||
a.packData(buf, b[:8100])
|
|
||||||
b = b[8100:]
|
|
||||||
}
|
|
||||||
if len(b) > 0 {
|
|
||||||
a.packData(buf, b)
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (a *authSHA1V4) DecodePacket(b []byte) ([]byte, error) { return b, nil }
|
|
||||||
|
|
||||||
func (a *authSHA1V4) EncodePacket(buf *bytes.Buffer, b []byte) error {
|
|
||||||
buf.Write(b)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (a *authSHA1V4) packData(poolBuf *bytes.Buffer, data []byte) {
|
|
||||||
dataLength := len(data)
|
|
||||||
randDataLength := a.getRandDataLength(dataLength)
|
|
||||||
/*
|
|
||||||
2: uint16 BigEndian packedDataLength
|
|
||||||
2: uint16 LittleEndian crc32Data & 0xffff
|
|
||||||
3: maxRandDataLengthPrefix (min:1)
|
|
||||||
4: adler32Data
|
|
||||||
*/
|
|
||||||
packedDataLength := 2 + 2 + 3 + randDataLength + dataLength + 4
|
|
||||||
if randDataLength < 128 {
|
|
||||||
packedDataLength -= 2
|
|
||||||
}
|
|
||||||
|
|
||||||
binary.Write(poolBuf, binary.BigEndian, uint16(packedDataLength))
|
|
||||||
binary.Write(poolBuf, binary.LittleEndian, uint16(crc32.ChecksumIEEE(poolBuf.Bytes()[poolBuf.Len()-2:])&0xffff))
|
|
||||||
a.packRandData(poolBuf, randDataLength)
|
|
||||||
poolBuf.Write(data)
|
|
||||||
binary.Write(poolBuf, binary.LittleEndian, adler32.Checksum(poolBuf.Bytes()[poolBuf.Len()-packedDataLength+4:]))
|
|
||||||
}
|
|
||||||
|
|
||||||
func (a *authSHA1V4) packAuthData(poolBuf *bytes.Buffer, data []byte) {
|
|
||||||
dataLength := len(data)
|
|
||||||
randDataLength := a.getRandDataLength(12 + dataLength)
|
|
||||||
/*
|
|
||||||
2: uint16 BigEndian packedAuthDataLength
|
|
||||||
4: uint32 LittleEndian crc32Data
|
|
||||||
3: maxRandDataLengthPrefix (min: 1)
|
|
||||||
12: authDataLength
|
|
||||||
10: hmacSHA1DataLength
|
|
||||||
*/
|
|
||||||
packedAuthDataLength := 2 + 4 + 3 + randDataLength + 12 + dataLength + 10
|
|
||||||
if randDataLength < 128 {
|
|
||||||
packedAuthDataLength -= 2
|
|
||||||
}
|
|
||||||
|
|
||||||
salt := []byte("auth_sha1_v4")
|
|
||||||
crcData := pool.Get(len(salt) + len(a.Key) + 2)
|
|
||||||
defer pool.Put(crcData)
|
|
||||||
binary.BigEndian.PutUint16(crcData, uint16(packedAuthDataLength))
|
|
||||||
copy(crcData[2:], salt)
|
|
||||||
copy(crcData[2+len(salt):], a.Key)
|
|
||||||
|
|
||||||
key := pool.Get(len(a.iv) + len(a.Key))
|
|
||||||
defer pool.Put(key)
|
|
||||||
copy(key, a.iv)
|
|
||||||
copy(key[len(a.iv):], a.Key)
|
|
||||||
|
|
||||||
poolBuf.Write(crcData[:2])
|
|
||||||
binary.Write(poolBuf, binary.LittleEndian, crc32.ChecksumIEEE(crcData))
|
|
||||||
a.packRandData(poolBuf, randDataLength)
|
|
||||||
a.putAuthData(poolBuf)
|
|
||||||
poolBuf.Write(data)
|
|
||||||
poolBuf.Write(tools.HmacSHA1(key, poolBuf.Bytes()[poolBuf.Len()-packedAuthDataLength+10:])[:10])
|
|
||||||
}
|
|
||||||
|
|
||||||
func (a *authSHA1V4) packRandData(poolBuf *bytes.Buffer, size int) {
|
|
||||||
if size < 128 {
|
|
||||||
poolBuf.WriteByte(byte(size + 1))
|
|
||||||
tools.AppendRandBytes(poolBuf, size)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
poolBuf.WriteByte(255)
|
|
||||||
binary.Write(poolBuf, binary.BigEndian, uint16(size+3))
|
|
||||||
tools.AppendRandBytes(poolBuf, size)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (a *authSHA1V4) getRandDataLength(size int) int {
|
|
||||||
if size > 1200 {
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
if size > 400 {
|
|
||||||
return rand.Intn(256)
|
|
||||||
}
|
|
||||||
return rand.Intn(512)
|
|
||||||
}
|
|
@ -1,75 +0,0 @@
|
|||||||
package protocol
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"crypto/aes"
|
|
||||||
"crypto/cipher"
|
|
||||||
"encoding/base64"
|
|
||||||
"encoding/binary"
|
|
||||||
"math/rand"
|
|
||||||
"sync"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/Dreamacro/clash/common/pool"
|
|
||||||
"github.com/Dreamacro/clash/transport/shadowsocks/core"
|
|
||||||
)
|
|
||||||
|
|
||||||
type Base struct {
|
|
||||||
Key []byte
|
|
||||||
Overhead int
|
|
||||||
Param string
|
|
||||||
}
|
|
||||||
|
|
||||||
type userData struct {
|
|
||||||
userKey []byte
|
|
||||||
userID [4]byte
|
|
||||||
}
|
|
||||||
|
|
||||||
type authData struct {
|
|
||||||
clientID [4]byte
|
|
||||||
connectionID uint32
|
|
||||||
mutex sync.Mutex
|
|
||||||
}
|
|
||||||
|
|
||||||
func (a *authData) next() *authData {
|
|
||||||
r := &authData{}
|
|
||||||
a.mutex.Lock()
|
|
||||||
defer a.mutex.Unlock()
|
|
||||||
if a.connectionID > 0xff000000 || a.connectionID == 0 {
|
|
||||||
rand.Read(a.clientID[:])
|
|
||||||
a.connectionID = rand.Uint32() & 0xffffff
|
|
||||||
}
|
|
||||||
a.connectionID++
|
|
||||||
copy(r.clientID[:], a.clientID[:])
|
|
||||||
r.connectionID = a.connectionID
|
|
||||||
return r
|
|
||||||
}
|
|
||||||
|
|
||||||
func (a *authData) putAuthData(buf *bytes.Buffer) {
|
|
||||||
binary.Write(buf, binary.LittleEndian, uint32(time.Now().Unix()))
|
|
||||||
buf.Write(a.clientID[:])
|
|
||||||
binary.Write(buf, binary.LittleEndian, a.connectionID)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (a *authData) putEncryptedData(b *bytes.Buffer, userKey []byte, paddings [2]int, salt string) error {
|
|
||||||
encrypt := pool.Get(16)
|
|
||||||
defer pool.Put(encrypt)
|
|
||||||
binary.LittleEndian.PutUint32(encrypt, uint32(time.Now().Unix()))
|
|
||||||
copy(encrypt[4:], a.clientID[:])
|
|
||||||
binary.LittleEndian.PutUint32(encrypt[8:], a.connectionID)
|
|
||||||
binary.LittleEndian.PutUint16(encrypt[12:], uint16(paddings[0]))
|
|
||||||
binary.LittleEndian.PutUint16(encrypt[14:], uint16(paddings[1]))
|
|
||||||
|
|
||||||
cipherKey := core.Kdf(base64.StdEncoding.EncodeToString(userKey)+salt, 16)
|
|
||||||
block, err := aes.NewCipher(cipherKey)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
iv := bytes.Repeat([]byte{0}, 16)
|
|
||||||
cbcCipher := cipher.NewCBCEncrypter(block, iv)
|
|
||||||
|
|
||||||
cbcCipher.CryptBlocks(encrypt, encrypt)
|
|
||||||
|
|
||||||
b.Write(encrypt)
|
|
||||||
return nil
|
|
||||||
}
|
|
@ -1,33 +0,0 @@
|
|||||||
package protocol
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"net"
|
|
||||||
)
|
|
||||||
|
|
||||||
type origin struct{}
|
|
||||||
|
|
||||||
func init() { register("origin", newOrigin, 0) }
|
|
||||||
|
|
||||||
func newOrigin(b *Base) Protocol { return &origin{} }
|
|
||||||
|
|
||||||
func (o *origin) StreamConn(c net.Conn, iv []byte) net.Conn { return c }
|
|
||||||
|
|
||||||
func (o *origin) PacketConn(c net.PacketConn) net.PacketConn { return c }
|
|
||||||
|
|
||||||
func (o *origin) Decode(dst, src *bytes.Buffer) error {
|
|
||||||
dst.ReadFrom(src)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (o *origin) Encode(buf *bytes.Buffer, b []byte) error {
|
|
||||||
buf.Write(b)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (o *origin) DecodePacket(b []byte) ([]byte, error) { return b, nil }
|
|
||||||
|
|
||||||
func (o *origin) EncodePacket(buf *bytes.Buffer, b []byte) error {
|
|
||||||
buf.Write(b)
|
|
||||||
return nil
|
|
||||||
}
|
|
@ -1,36 +0,0 @@
|
|||||||
package protocol
|
|
||||||
|
|
||||||
import (
|
|
||||||
"net"
|
|
||||||
|
|
||||||
"github.com/Dreamacro/clash/common/pool"
|
|
||||||
)
|
|
||||||
|
|
||||||
type PacketConn struct {
|
|
||||||
net.PacketConn
|
|
||||||
Protocol
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *PacketConn) WriteTo(b []byte, addr net.Addr) (int, error) {
|
|
||||||
buf := pool.GetBuffer()
|
|
||||||
defer pool.PutBuffer(buf)
|
|
||||||
err := c.EncodePacket(buf, b)
|
|
||||||
if err != nil {
|
|
||||||
return 0, err
|
|
||||||
}
|
|
||||||
_, err = c.PacketConn.WriteTo(buf.Bytes(), addr)
|
|
||||||
return len(b), err
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *PacketConn) ReadFrom(b []byte) (int, net.Addr, error) {
|
|
||||||
n, addr, err := c.PacketConn.ReadFrom(b)
|
|
||||||
if err != nil {
|
|
||||||
return n, addr, err
|
|
||||||
}
|
|
||||||
decoded, err := c.DecodePacket(b[:n])
|
|
||||||
if err != nil {
|
|
||||||
return n, addr, err
|
|
||||||
}
|
|
||||||
copy(b, decoded)
|
|
||||||
return len(decoded), addr, nil
|
|
||||||
}
|
|
@ -1,76 +0,0 @@
|
|||||||
package protocol
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"errors"
|
|
||||||
"fmt"
|
|
||||||
"math/rand"
|
|
||||||
"net"
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
|
||||||
errAuthSHA1V4CRC32Error = errors.New("auth_sha1_v4 decode data wrong crc32")
|
|
||||||
errAuthSHA1V4LengthError = errors.New("auth_sha1_v4 decode data wrong length")
|
|
||||||
errAuthSHA1V4Adler32Error = errors.New("auth_sha1_v4 decode data wrong adler32")
|
|
||||||
errAuthAES128MACError = errors.New("auth_aes128 decode data wrong mac")
|
|
||||||
errAuthAES128LengthError = errors.New("auth_aes128 decode data wrong length")
|
|
||||||
errAuthAES128ChksumError = errors.New("auth_aes128 decode data wrong checksum")
|
|
||||||
errAuthChainLengthError = errors.New("auth_chain decode data wrong length")
|
|
||||||
errAuthChainChksumError = errors.New("auth_chain decode data wrong checksum")
|
|
||||||
)
|
|
||||||
|
|
||||||
type Protocol interface {
|
|
||||||
StreamConn(net.Conn, []byte) net.Conn
|
|
||||||
PacketConn(net.PacketConn) net.PacketConn
|
|
||||||
Decode(dst, src *bytes.Buffer) error
|
|
||||||
Encode(buf *bytes.Buffer, b []byte) error
|
|
||||||
DecodePacket([]byte) ([]byte, error)
|
|
||||||
EncodePacket(buf *bytes.Buffer, b []byte) error
|
|
||||||
}
|
|
||||||
|
|
||||||
type protocolCreator func(b *Base) Protocol
|
|
||||||
|
|
||||||
var protocolList = make(map[string]struct {
|
|
||||||
overhead int
|
|
||||||
new protocolCreator
|
|
||||||
})
|
|
||||||
|
|
||||||
func register(name string, c protocolCreator, o int) {
|
|
||||||
protocolList[name] = struct {
|
|
||||||
overhead int
|
|
||||||
new protocolCreator
|
|
||||||
}{overhead: o, new: c}
|
|
||||||
}
|
|
||||||
|
|
||||||
func PickProtocol(name string, b *Base) (Protocol, error) {
|
|
||||||
if choice, ok := protocolList[name]; ok {
|
|
||||||
b.Overhead += choice.overhead
|
|
||||||
return choice.new(b), nil
|
|
||||||
}
|
|
||||||
return nil, fmt.Errorf("protocol %s not supported", name)
|
|
||||||
}
|
|
||||||
|
|
||||||
func getHeadSize(b []byte, defaultValue int) int {
|
|
||||||
if len(b) < 2 {
|
|
||||||
return defaultValue
|
|
||||||
}
|
|
||||||
headType := b[0] & 7
|
|
||||||
switch headType {
|
|
||||||
case 1:
|
|
||||||
return 7
|
|
||||||
case 4:
|
|
||||||
return 19
|
|
||||||
case 3:
|
|
||||||
return 4 + int(b[1])
|
|
||||||
}
|
|
||||||
return defaultValue
|
|
||||||
}
|
|
||||||
|
|
||||||
func getDataLength(b []byte) int {
|
|
||||||
bLength := len(b)
|
|
||||||
dataLength := getHeadSize(b, 30) + rand.Intn(32)
|
|
||||||
if bLength < dataLength {
|
|
||||||
return bLength
|
|
||||||
}
|
|
||||||
return dataLength
|
|
||||||
}
|
|
@ -1,50 +0,0 @@
|
|||||||
package protocol
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"net"
|
|
||||||
|
|
||||||
"github.com/Dreamacro/clash/common/pool"
|
|
||||||
)
|
|
||||||
|
|
||||||
type Conn struct {
|
|
||||||
net.Conn
|
|
||||||
Protocol
|
|
||||||
decoded bytes.Buffer
|
|
||||||
underDecoded bytes.Buffer
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *Conn) Read(b []byte) (int, error) {
|
|
||||||
if c.decoded.Len() > 0 {
|
|
||||||
return c.decoded.Read(b)
|
|
||||||
}
|
|
||||||
|
|
||||||
buf := pool.Get(pool.RelayBufferSize)
|
|
||||||
defer pool.Put(buf)
|
|
||||||
n, err := c.Conn.Read(buf)
|
|
||||||
if err != nil {
|
|
||||||
return 0, err
|
|
||||||
}
|
|
||||||
c.underDecoded.Write(buf[:n])
|
|
||||||
err = c.Decode(&c.decoded, &c.underDecoded)
|
|
||||||
if err != nil {
|
|
||||||
return 0, err
|
|
||||||
}
|
|
||||||
n, _ = c.decoded.Read(b)
|
|
||||||
return n, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *Conn) Write(b []byte) (int, error) {
|
|
||||||
bLength := len(b)
|
|
||||||
buf := pool.GetBuffer()
|
|
||||||
defer pool.PutBuffer(buf)
|
|
||||||
err := c.Encode(buf, b)
|
|
||||||
if err != nil {
|
|
||||||
return 0, err
|
|
||||||
}
|
|
||||||
_, err = c.Conn.Write(buf.Bytes())
|
|
||||||
if err != nil {
|
|
||||||
return 0, err
|
|
||||||
}
|
|
||||||
return bLength, nil
|
|
||||||
}
|
|
@ -1,65 +0,0 @@
|
|||||||
package hysteria
|
|
||||||
|
|
||||||
func FragUDPMessage(m UDPMessage, maxSize int) []UDPMessage {
|
|
||||||
if m.Size() <= maxSize {
|
|
||||||
return []UDPMessage{m}
|
|
||||||
}
|
|
||||||
fullPayload := m.Data
|
|
||||||
maxPayloadSize := maxSize - m.HeaderSize()
|
|
||||||
off := 0
|
|
||||||
fragID := uint8(0)
|
|
||||||
fragCount := uint8((len(fullPayload) + maxPayloadSize - 1) / maxPayloadSize) // round up
|
|
||||||
var frags []UDPMessage
|
|
||||||
for off < len(fullPayload) {
|
|
||||||
payloadSize := len(fullPayload) - off
|
|
||||||
if payloadSize > maxPayloadSize {
|
|
||||||
payloadSize = maxPayloadSize
|
|
||||||
}
|
|
||||||
frag := m
|
|
||||||
frag.FragID = fragID
|
|
||||||
frag.FragCount = fragCount
|
|
||||||
frag.Data = fullPayload[off : off+payloadSize]
|
|
||||||
frags = append(frags, frag)
|
|
||||||
off += payloadSize
|
|
||||||
fragID++
|
|
||||||
}
|
|
||||||
return frags
|
|
||||||
}
|
|
||||||
|
|
||||||
type Defragger struct {
|
|
||||||
msgID uint16
|
|
||||||
frags []*UDPMessage
|
|
||||||
count uint8
|
|
||||||
}
|
|
||||||
|
|
||||||
func (d *Defragger) Feed(m UDPMessage) *UDPMessage {
|
|
||||||
if m.FragCount <= 1 {
|
|
||||||
return &m
|
|
||||||
}
|
|
||||||
if m.FragID >= m.FragCount {
|
|
||||||
// wtf is this?
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
if m.MsgID != d.msgID {
|
|
||||||
// new message, clear previous state
|
|
||||||
d.msgID = m.MsgID
|
|
||||||
d.frags = make([]*UDPMessage, m.FragCount)
|
|
||||||
d.count = 1
|
|
||||||
d.frags[m.FragID] = &m
|
|
||||||
} else if d.frags[m.FragID] == nil {
|
|
||||||
d.frags[m.FragID] = &m
|
|
||||||
d.count++
|
|
||||||
if int(d.count) == len(d.frags) {
|
|
||||||
// all fragments received, assemble
|
|
||||||
var data []byte
|
|
||||||
for _, frag := range d.frags {
|
|
||||||
data = append(data, frag.Data...)
|
|
||||||
}
|
|
||||||
m.Data = data
|
|
||||||
m.FragID = 0
|
|
||||||
m.FragCount = 1
|
|
||||||
return &m
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
@ -1,539 +0,0 @@
|
|||||||
package hysteria
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"encoding/binary"
|
|
||||||
"io"
|
|
||||||
"math/rand"
|
|
||||||
"net"
|
|
||||||
"os"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/sagernet/quic-go"
|
|
||||||
"github.com/sagernet/sing/common"
|
|
||||||
"github.com/sagernet/sing/common/buf"
|
|
||||||
E "github.com/sagernet/sing/common/exceptions"
|
|
||||||
M "github.com/sagernet/sing/common/metadata"
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
|
||||||
MbpsToBps = 125000
|
|
||||||
MinSpeedBPS = 16384
|
|
||||||
DefaultStreamReceiveWindow = 15728640 // 15 MB/s
|
|
||||||
DefaultConnectionReceiveWindow = 67108864 // 64 MB/s
|
|
||||||
DefaultMaxIncomingStreams = 1024
|
|
||||||
DefaultALPN = "hysteria"
|
|
||||||
KeepAlivePeriod = 10 * time.Second
|
|
||||||
)
|
|
||||||
|
|
||||||
const Version = 3
|
|
||||||
|
|
||||||
type ClientHello struct {
|
|
||||||
SendBPS uint64
|
|
||||||
RecvBPS uint64
|
|
||||||
Auth []byte
|
|
||||||
}
|
|
||||||
|
|
||||||
func WriteClientHello(stream io.Writer, hello ClientHello) error {
|
|
||||||
var requestLen int
|
|
||||||
requestLen += 1 // version
|
|
||||||
requestLen += 8 // sendBPS
|
|
||||||
requestLen += 8 // recvBPS
|
|
||||||
requestLen += 2 // auth len
|
|
||||||
requestLen += len(hello.Auth)
|
|
||||||
request := buf.NewSize(requestLen)
|
|
||||||
defer request.Release()
|
|
||||||
common.Must(
|
|
||||||
request.WriteByte(Version),
|
|
||||||
binary.Write(request, binary.BigEndian, hello.SendBPS),
|
|
||||||
binary.Write(request, binary.BigEndian, hello.RecvBPS),
|
|
||||||
binary.Write(request, binary.BigEndian, uint16(len(hello.Auth))),
|
|
||||||
common.Error(request.Write(hello.Auth)),
|
|
||||||
)
|
|
||||||
return common.Error(stream.Write(request.Bytes()))
|
|
||||||
}
|
|
||||||
|
|
||||||
func ReadClientHello(reader io.Reader) (*ClientHello, error) {
|
|
||||||
var version uint8
|
|
||||||
err := binary.Read(reader, binary.BigEndian, &version)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
if version != Version {
|
|
||||||
return nil, E.New("unsupported client version: ", version)
|
|
||||||
}
|
|
||||||
var clientHello ClientHello
|
|
||||||
err = binary.Read(reader, binary.BigEndian, &clientHello.SendBPS)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
err = binary.Read(reader, binary.BigEndian, &clientHello.RecvBPS)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
var authLen uint16
|
|
||||||
err = binary.Read(reader, binary.BigEndian, &authLen)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
clientHello.Auth = make([]byte, authLen)
|
|
||||||
_, err = io.ReadFull(reader, clientHello.Auth)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return &clientHello, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
type ServerHello struct {
|
|
||||||
OK bool
|
|
||||||
SendBPS uint64
|
|
||||||
RecvBPS uint64
|
|
||||||
Message string
|
|
||||||
}
|
|
||||||
|
|
||||||
func ReadServerHello(stream io.Reader) (*ServerHello, error) {
|
|
||||||
var responseLen int
|
|
||||||
responseLen += 1 // ok
|
|
||||||
responseLen += 8 // sendBPS
|
|
||||||
responseLen += 8 // recvBPS
|
|
||||||
responseLen += 2 // message len
|
|
||||||
response := buf.NewSize(responseLen)
|
|
||||||
defer response.Release()
|
|
||||||
_, err := response.ReadFullFrom(stream, responseLen)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
var serverHello ServerHello
|
|
||||||
serverHello.OK = response.Byte(0) == 1
|
|
||||||
serverHello.SendBPS = binary.BigEndian.Uint64(response.Range(1, 9))
|
|
||||||
serverHello.RecvBPS = binary.BigEndian.Uint64(response.Range(9, 17))
|
|
||||||
messageLen := binary.BigEndian.Uint16(response.Range(17, 19))
|
|
||||||
if messageLen == 0 {
|
|
||||||
return &serverHello, nil
|
|
||||||
}
|
|
||||||
message := make([]byte, messageLen)
|
|
||||||
_, err = io.ReadFull(stream, message)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
serverHello.Message = string(message)
|
|
||||||
return &serverHello, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func WriteServerHello(stream io.Writer, hello ServerHello) error {
|
|
||||||
var responseLen int
|
|
||||||
responseLen += 1 // ok
|
|
||||||
responseLen += 8 // sendBPS
|
|
||||||
responseLen += 8 // recvBPS
|
|
||||||
responseLen += 2 // message len
|
|
||||||
responseLen += len(hello.Message)
|
|
||||||
response := buf.NewSize(responseLen)
|
|
||||||
defer response.Release()
|
|
||||||
if hello.OK {
|
|
||||||
common.Must(response.WriteByte(1))
|
|
||||||
} else {
|
|
||||||
common.Must(response.WriteByte(0))
|
|
||||||
}
|
|
||||||
common.Must(
|
|
||||||
binary.Write(response, binary.BigEndian, hello.SendBPS),
|
|
||||||
binary.Write(response, binary.BigEndian, hello.RecvBPS),
|
|
||||||
binary.Write(response, binary.BigEndian, uint16(len(hello.Message))),
|
|
||||||
common.Error(response.WriteString(hello.Message)),
|
|
||||||
)
|
|
||||||
return common.Error(stream.Write(response.Bytes()))
|
|
||||||
}
|
|
||||||
|
|
||||||
type ClientRequest struct {
|
|
||||||
UDP bool
|
|
||||||
Host string
|
|
||||||
Port uint16
|
|
||||||
}
|
|
||||||
|
|
||||||
func ReadClientRequest(stream io.Reader) (*ClientRequest, error) {
|
|
||||||
var clientRequest ClientRequest
|
|
||||||
err := binary.Read(stream, binary.BigEndian, &clientRequest.UDP)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
var hostLen uint16
|
|
||||||
err = binary.Read(stream, binary.BigEndian, &hostLen)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
host := make([]byte, hostLen)
|
|
||||||
_, err = io.ReadFull(stream, host)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
clientRequest.Host = string(host)
|
|
||||||
err = binary.Read(stream, binary.BigEndian, &clientRequest.Port)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return &clientRequest, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func WriteClientRequest(stream io.Writer, request ClientRequest) error {
|
|
||||||
var requestLen int
|
|
||||||
requestLen += 1 // udp
|
|
||||||
requestLen += 2 // host len
|
|
||||||
requestLen += len(request.Host)
|
|
||||||
requestLen += 2 // port
|
|
||||||
buffer := buf.NewSize(requestLen)
|
|
||||||
defer buffer.Release()
|
|
||||||
if request.UDP {
|
|
||||||
common.Must(buffer.WriteByte(1))
|
|
||||||
} else {
|
|
||||||
common.Must(buffer.WriteByte(0))
|
|
||||||
}
|
|
||||||
common.Must(
|
|
||||||
binary.Write(buffer, binary.BigEndian, uint16(len(request.Host))),
|
|
||||||
common.Error(buffer.WriteString(request.Host)),
|
|
||||||
binary.Write(buffer, binary.BigEndian, request.Port),
|
|
||||||
)
|
|
||||||
return common.Error(stream.Write(buffer.Bytes()))
|
|
||||||
}
|
|
||||||
|
|
||||||
type ServerResponse struct {
|
|
||||||
OK bool
|
|
||||||
UDPSessionID uint32
|
|
||||||
Message string
|
|
||||||
}
|
|
||||||
|
|
||||||
func ReadServerResponse(stream io.Reader) (*ServerResponse, error) {
|
|
||||||
var responseLen int
|
|
||||||
responseLen += 1 // ok
|
|
||||||
responseLen += 4 // udp session id
|
|
||||||
responseLen += 2 // message len
|
|
||||||
response := buf.NewSize(responseLen)
|
|
||||||
defer response.Release()
|
|
||||||
_, err := response.ReadFullFrom(stream, responseLen)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
var serverResponse ServerResponse
|
|
||||||
serverResponse.OK = response.Byte(0) == 1
|
|
||||||
serverResponse.UDPSessionID = binary.BigEndian.Uint32(response.Range(1, 5))
|
|
||||||
messageLen := binary.BigEndian.Uint16(response.Range(5, 7))
|
|
||||||
if messageLen == 0 {
|
|
||||||
return &serverResponse, nil
|
|
||||||
}
|
|
||||||
message := make([]byte, messageLen)
|
|
||||||
_, err = io.ReadFull(stream, message)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
serverResponse.Message = string(message)
|
|
||||||
return &serverResponse, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func WriteServerResponse(stream io.Writer, response ServerResponse) error {
|
|
||||||
var responseLen int
|
|
||||||
responseLen += 1 // ok
|
|
||||||
responseLen += 4 // udp session id
|
|
||||||
responseLen += 2 // message len
|
|
||||||
responseLen += len(response.Message)
|
|
||||||
buffer := buf.NewSize(responseLen)
|
|
||||||
defer buffer.Release()
|
|
||||||
if response.OK {
|
|
||||||
common.Must(buffer.WriteByte(1))
|
|
||||||
} else {
|
|
||||||
common.Must(buffer.WriteByte(0))
|
|
||||||
}
|
|
||||||
common.Must(
|
|
||||||
binary.Write(buffer, binary.BigEndian, response.UDPSessionID),
|
|
||||||
binary.Write(buffer, binary.BigEndian, uint16(len(response.Message))),
|
|
||||||
common.Error(buffer.WriteString(response.Message)),
|
|
||||||
)
|
|
||||||
return common.Error(stream.Write(buffer.Bytes()))
|
|
||||||
}
|
|
||||||
|
|
||||||
type UDPMessage struct {
|
|
||||||
SessionID uint32
|
|
||||||
Host string
|
|
||||||
Port uint16
|
|
||||||
MsgID uint16 // doesn't matter when not fragmented, but must not be 0 when fragmented
|
|
||||||
FragID uint8 // doesn't matter when not fragmented, starts at 0 when fragmented
|
|
||||||
FragCount uint8 // must be 1 when not fragmented
|
|
||||||
Data []byte
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m UDPMessage) HeaderSize() int {
|
|
||||||
return 4 + 2 + len(m.Host) + 2 + 2 + 1 + 1 + 2
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m UDPMessage) Size() int {
|
|
||||||
return m.HeaderSize() + len(m.Data)
|
|
||||||
}
|
|
||||||
|
|
||||||
func ParseUDPMessage(packet []byte) (message UDPMessage, err error) {
|
|
||||||
reader := bytes.NewReader(packet)
|
|
||||||
err = binary.Read(reader, binary.BigEndian, &message.SessionID)
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
var hostLen uint16
|
|
||||||
err = binary.Read(reader, binary.BigEndian, &hostLen)
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
_, err = reader.Seek(int64(hostLen), io.SeekCurrent)
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if 6+int(hostLen) > len(packet) {
|
|
||||||
err = E.New("invalid host length")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
message.Host = string(packet[6 : 6+hostLen])
|
|
||||||
err = binary.Read(reader, binary.BigEndian, &message.Port)
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
err = binary.Read(reader, binary.BigEndian, &message.MsgID)
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
err = binary.Read(reader, binary.BigEndian, &message.FragID)
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
err = binary.Read(reader, binary.BigEndian, &message.FragCount)
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
var dataLen uint16
|
|
||||||
err = binary.Read(reader, binary.BigEndian, &dataLen)
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if reader.Len() != int(dataLen) {
|
|
||||||
err = E.New("invalid data length")
|
|
||||||
}
|
|
||||||
dataOffset := int(reader.Size()) - reader.Len()
|
|
||||||
message.Data = packet[dataOffset:]
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func WriteUDPMessage(conn quic.Connection, message UDPMessage) error {
|
|
||||||
var messageLen int
|
|
||||||
messageLen += 4 // session id
|
|
||||||
messageLen += 2 // host len
|
|
||||||
messageLen += len(message.Host)
|
|
||||||
messageLen += 2 // port
|
|
||||||
messageLen += 2 // msg id
|
|
||||||
messageLen += 1 // frag id
|
|
||||||
messageLen += 1 // frag count
|
|
||||||
messageLen += 2 // data len
|
|
||||||
messageLen += len(message.Data)
|
|
||||||
buffer := buf.NewSize(messageLen)
|
|
||||||
defer buffer.Release()
|
|
||||||
err := writeUDPMessage(conn, message, buffer)
|
|
||||||
if errSize, ok := err.(quic.ErrMessageTooLarge); ok {
|
|
||||||
// need to frag
|
|
||||||
message.MsgID = uint16(rand.Intn(0xFFFF)) + 1 // msgID must be > 0 when fragCount > 1
|
|
||||||
fragMsgs := FragUDPMessage(message, int(errSize))
|
|
||||||
for _, fragMsg := range fragMsgs {
|
|
||||||
buffer.FullReset()
|
|
||||||
err = writeUDPMessage(conn, fragMsg, buffer)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
func writeUDPMessage(conn quic.Connection, message UDPMessage, buffer *buf.Buffer) error {
|
|
||||||
common.Must(
|
|
||||||
binary.Write(buffer, binary.BigEndian, message.SessionID),
|
|
||||||
binary.Write(buffer, binary.BigEndian, uint16(len(message.Host))),
|
|
||||||
common.Error(buffer.WriteString(message.Host)),
|
|
||||||
binary.Write(buffer, binary.BigEndian, message.Port),
|
|
||||||
binary.Write(buffer, binary.BigEndian, message.MsgID),
|
|
||||||
binary.Write(buffer, binary.BigEndian, message.FragID),
|
|
||||||
binary.Write(buffer, binary.BigEndian, message.FragCount),
|
|
||||||
binary.Write(buffer, binary.BigEndian, uint16(len(message.Data))),
|
|
||||||
common.Error(buffer.Write(message.Data)),
|
|
||||||
)
|
|
||||||
return conn.SendMessage(buffer.Bytes())
|
|
||||||
}
|
|
||||||
|
|
||||||
var _ net.Conn = (*Conn)(nil)
|
|
||||||
|
|
||||||
type Conn struct {
|
|
||||||
quic.Stream
|
|
||||||
destination M.Socksaddr
|
|
||||||
needReadResponse bool
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewConn(stream quic.Stream, destination M.Socksaddr, isClient bool) *Conn {
|
|
||||||
return &Conn{
|
|
||||||
Stream: stream,
|
|
||||||
destination: destination,
|
|
||||||
needReadResponse: isClient,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *Conn) Read(p []byte) (n int, err error) {
|
|
||||||
if c.needReadResponse {
|
|
||||||
var response *ServerResponse
|
|
||||||
response, err = ReadServerResponse(c.Stream)
|
|
||||||
if err != nil {
|
|
||||||
c.Close()
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if !response.OK {
|
|
||||||
c.Close()
|
|
||||||
return 0, E.New("remote error: ", response.Message)
|
|
||||||
}
|
|
||||||
c.needReadResponse = false
|
|
||||||
}
|
|
||||||
return c.Stream.Read(p)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *Conn) LocalAddr() net.Addr {
|
|
||||||
return M.Socksaddr{}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *Conn) RemoteAddr() net.Addr {
|
|
||||||
return c.destination.TCPAddr()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *Conn) ReaderReplaceable() bool {
|
|
||||||
return !c.needReadResponse
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *Conn) WriterReplaceable() bool {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *Conn) Upstream() any {
|
|
||||||
return c.Stream
|
|
||||||
}
|
|
||||||
|
|
||||||
type PacketConn struct {
|
|
||||||
session quic.Connection
|
|
||||||
stream quic.Stream
|
|
||||||
sessionId uint32
|
|
||||||
destination M.Socksaddr
|
|
||||||
msgCh <-chan *UDPMessage
|
|
||||||
closer io.Closer
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewPacketConn(session quic.Connection, stream quic.Stream, sessionId uint32, destination M.Socksaddr, msgCh <-chan *UDPMessage, closer io.Closer) *PacketConn {
|
|
||||||
return &PacketConn{
|
|
||||||
session: session,
|
|
||||||
stream: stream,
|
|
||||||
sessionId: sessionId,
|
|
||||||
destination: destination,
|
|
||||||
msgCh: msgCh,
|
|
||||||
closer: closer,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *PacketConn) Hold() {
|
|
||||||
// Hold the stream until it's closed
|
|
||||||
buf := make([]byte, 1024)
|
|
||||||
for {
|
|
||||||
_, err := c.stream.Read(buf)
|
|
||||||
if err != nil {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
_ = c.Close()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *PacketConn) ReadPacket(buffer *buf.Buffer) (destination M.Socksaddr, err error) {
|
|
||||||
msg := <-c.msgCh
|
|
||||||
if msg == nil {
|
|
||||||
err = net.ErrClosed
|
|
||||||
return
|
|
||||||
}
|
|
||||||
err = common.Error(buffer.Write(msg.Data))
|
|
||||||
destination = M.ParseSocksaddrHostPort(msg.Host, msg.Port).Unwrap()
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *PacketConn) ReadPacketThreadSafe() (buffer *buf.Buffer, destination M.Socksaddr, err error) {
|
|
||||||
msg := <-c.msgCh
|
|
||||||
if msg == nil {
|
|
||||||
err = net.ErrClosed
|
|
||||||
return
|
|
||||||
}
|
|
||||||
buffer = buf.As(msg.Data)
|
|
||||||
destination = M.ParseSocksaddrHostPort(msg.Host, msg.Port).Unwrap()
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *PacketConn) WritePacket(buffer *buf.Buffer, destination M.Socksaddr) error {
|
|
||||||
return WriteUDPMessage(c.session, UDPMessage{
|
|
||||||
SessionID: c.sessionId,
|
|
||||||
Host: destination.AddrString(),
|
|
||||||
Port: destination.Port,
|
|
||||||
FragCount: 1,
|
|
||||||
Data: buffer.Bytes(),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *PacketConn) ReadFrom(p []byte) (n int, addr net.Addr, err error) {
|
|
||||||
msg := <-c.msgCh
|
|
||||||
if msg == nil {
|
|
||||||
err = net.ErrClosed
|
|
||||||
return
|
|
||||||
}
|
|
||||||
n = copy(p, msg.Data)
|
|
||||||
destination := M.ParseSocksaddrHostPort(msg.Host, msg.Port)
|
|
||||||
if destination.IsFqdn() {
|
|
||||||
addr = destination
|
|
||||||
} else {
|
|
||||||
addr = destination.UDPAddr()
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *PacketConn) WriteTo(p []byte, addr net.Addr) (n int, err error) {
|
|
||||||
err = c.WritePacket(buf.As(p), M.SocksaddrFromNet(addr))
|
|
||||||
if err == nil {
|
|
||||||
n = len(p)
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *PacketConn) LocalAddr() net.Addr {
|
|
||||||
return M.Socksaddr{}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *PacketConn) RemoteAddr() net.Addr {
|
|
||||||
return c.destination.UDPAddr()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *PacketConn) SetDeadline(t time.Time) error {
|
|
||||||
return os.ErrInvalid
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *PacketConn) SetReadDeadline(t time.Time) error {
|
|
||||||
return os.ErrInvalid
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *PacketConn) SetWriteDeadline(t time.Time) error {
|
|
||||||
return os.ErrInvalid
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *PacketConn) NeedAdditionalReadDeadline() bool {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *PacketConn) Read(b []byte) (n int, err error) {
|
|
||||||
n, _, err = c.ReadFrom(b)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *PacketConn) Write(b []byte) (n int, err error) {
|
|
||||||
return c.WriteTo(b, c.destination)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *PacketConn) Close() error {
|
|
||||||
return common.Close(c.stream, c.closer)
|
|
||||||
}
|
|
@ -1,36 +0,0 @@
|
|||||||
package hysteria
|
|
||||||
|
|
||||||
import (
|
|
||||||
"regexp"
|
|
||||||
"strconv"
|
|
||||||
)
|
|
||||||
|
|
||||||
func StringToBps(s string) uint64 {
|
|
||||||
if s == "" {
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
m := regexp.MustCompile(`^(\d+)\s*([KMGT]?)([Bb])ps$`).FindStringSubmatch(s)
|
|
||||||
if m == nil {
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
var n uint64
|
|
||||||
switch m[2] {
|
|
||||||
case "K":
|
|
||||||
n = 1 << 10
|
|
||||||
case "M":
|
|
||||||
n = 1 << 20
|
|
||||||
case "G":
|
|
||||||
n = 1 << 30
|
|
||||||
case "T":
|
|
||||||
n = 1 << 40
|
|
||||||
default:
|
|
||||||
n = 1
|
|
||||||
}
|
|
||||||
v, _ := strconv.ParseUint(m[1], 10, 64)
|
|
||||||
n = v * n
|
|
||||||
if m[3] == "b" {
|
|
||||||
// Bits, need to convert to bytes
|
|
||||||
n = n >> 3
|
|
||||||
}
|
|
||||||
return n
|
|
||||||
}
|
|
@ -1,68 +0,0 @@
|
|||||||
package hysteria
|
|
||||||
|
|
||||||
import (
|
|
||||||
"net"
|
|
||||||
"os"
|
|
||||||
"syscall"
|
|
||||||
|
|
||||||
"github.com/sagernet/quic-go"
|
|
||||||
"github.com/sagernet/sing/common"
|
|
||||||
"github.com/sagernet/sing/common/baderror"
|
|
||||||
)
|
|
||||||
|
|
||||||
type PacketConnWrapper struct {
|
|
||||||
net.PacketConn
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *PacketConnWrapper) SetReadBuffer(bytes int) error {
|
|
||||||
return common.MustCast[*net.UDPConn](c.PacketConn).SetReadBuffer(bytes)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *PacketConnWrapper) SetWriteBuffer(bytes int) error {
|
|
||||||
return common.MustCast[*net.UDPConn](c.PacketConn).SetWriteBuffer(bytes)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *PacketConnWrapper) SyscallConn() (syscall.RawConn, error) {
|
|
||||||
return common.MustCast[*net.UDPConn](c.PacketConn).SyscallConn()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *PacketConnWrapper) File() (f *os.File, err error) {
|
|
||||||
return common.MustCast[*net.UDPConn](c.PacketConn).File()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *PacketConnWrapper) Upstream() any {
|
|
||||||
return c.PacketConn
|
|
||||||
}
|
|
||||||
|
|
||||||
type StreamWrapper struct {
|
|
||||||
Conn quic.Connection
|
|
||||||
quic.Stream
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *StreamWrapper) Read(p []byte) (n int, err error) {
|
|
||||||
n, err = s.Stream.Read(p)
|
|
||||||
return n, baderror.WrapQUIC(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *StreamWrapper) Write(p []byte) (n int, err error) {
|
|
||||||
n, err = s.Stream.Write(p)
|
|
||||||
return n, baderror.WrapQUIC(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *StreamWrapper) LocalAddr() net.Addr {
|
|
||||||
return s.Conn.LocalAddr()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *StreamWrapper) RemoteAddr() net.Addr {
|
|
||||||
return s.Conn.RemoteAddr()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *StreamWrapper) Upstream() any {
|
|
||||||
return s.Stream
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *StreamWrapper) Close() error {
|
|
||||||
s.CancelRead(0)
|
|
||||||
s.Stream.Close()
|
|
||||||
return nil
|
|
||||||
}
|
|
@ -1,118 +0,0 @@
|
|||||||
package hysteria
|
|
||||||
|
|
||||||
import (
|
|
||||||
"crypto/sha256"
|
|
||||||
"math/rand"
|
|
||||||
"net"
|
|
||||||
"sync"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/sagernet/sing/common"
|
|
||||||
"github.com/sagernet/sing/common/buf"
|
|
||||||
"github.com/sagernet/sing/common/bufio"
|
|
||||||
M "github.com/sagernet/sing/common/metadata"
|
|
||||||
N "github.com/sagernet/sing/common/network"
|
|
||||||
)
|
|
||||||
|
|
||||||
const xplusSaltLen = 16
|
|
||||||
|
|
||||||
func NewXPlusPacketConn(conn net.PacketConn, key []byte) net.PacketConn {
|
|
||||||
vectorisedWriter, isVectorised := bufio.CreateVectorisedPacketWriter(conn)
|
|
||||||
if isVectorised {
|
|
||||||
return &VectorisedXPlusConn{
|
|
||||||
XPlusPacketConn: XPlusPacketConn{
|
|
||||||
PacketConn: conn,
|
|
||||||
key: key,
|
|
||||||
rand: rand.New(rand.NewSource(time.Now().UnixNano())),
|
|
||||||
},
|
|
||||||
writer: vectorisedWriter,
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
return &XPlusPacketConn{
|
|
||||||
PacketConn: conn,
|
|
||||||
key: key,
|
|
||||||
rand: rand.New(rand.NewSource(time.Now().UnixNano())),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
type XPlusPacketConn struct {
|
|
||||||
net.PacketConn
|
|
||||||
key []byte
|
|
||||||
randAccess sync.Mutex
|
|
||||||
rand *rand.Rand
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *XPlusPacketConn) ReadFrom(p []byte) (n int, addr net.Addr, err error) {
|
|
||||||
n, addr, err = c.PacketConn.ReadFrom(p)
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
} else if n < xplusSaltLen {
|
|
||||||
n = 0
|
|
||||||
return
|
|
||||||
}
|
|
||||||
key := sha256.Sum256(append(c.key, p[:xplusSaltLen]...))
|
|
||||||
for i := range p[xplusSaltLen:] {
|
|
||||||
p[i] = p[xplusSaltLen+i] ^ key[i%sha256.Size]
|
|
||||||
}
|
|
||||||
n -= xplusSaltLen
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *XPlusPacketConn) WriteTo(p []byte, addr net.Addr) (n int, err error) {
|
|
||||||
// can't use unsafe buffer on WriteTo
|
|
||||||
buffer := buf.NewSize(len(p) + xplusSaltLen)
|
|
||||||
defer buffer.Release()
|
|
||||||
salt := buffer.Extend(xplusSaltLen)
|
|
||||||
c.randAccess.Lock()
|
|
||||||
_, _ = c.rand.Read(salt)
|
|
||||||
c.randAccess.Unlock()
|
|
||||||
key := sha256.Sum256(append(c.key, salt...))
|
|
||||||
for i := range p {
|
|
||||||
common.Must(buffer.WriteByte(p[i] ^ key[i%sha256.Size]))
|
|
||||||
}
|
|
||||||
return c.PacketConn.WriteTo(buffer.Bytes(), addr)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *XPlusPacketConn) Upstream() any {
|
|
||||||
return c.PacketConn
|
|
||||||
}
|
|
||||||
|
|
||||||
type VectorisedXPlusConn struct {
|
|
||||||
XPlusPacketConn
|
|
||||||
writer N.VectorisedPacketWriter
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *VectorisedXPlusConn) WriteTo(p []byte, addr net.Addr) (n int, err error) {
|
|
||||||
header := buf.NewSize(xplusSaltLen)
|
|
||||||
defer header.Release()
|
|
||||||
salt := header.Extend(xplusSaltLen)
|
|
||||||
c.randAccess.Lock()
|
|
||||||
_, _ = c.rand.Read(salt)
|
|
||||||
c.randAccess.Unlock()
|
|
||||||
key := sha256.Sum256(append(c.key, salt...))
|
|
||||||
for i := range p {
|
|
||||||
p[i] ^= key[i%sha256.Size]
|
|
||||||
}
|
|
||||||
return bufio.WriteVectorisedPacket(c.writer, [][]byte{header.Bytes(), p}, M.SocksaddrFromNet(addr))
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *VectorisedXPlusConn) WriteVectorisedPacket(buffers []*buf.Buffer, destination M.Socksaddr) error {
|
|
||||||
header := buf.NewSize(xplusSaltLen)
|
|
||||||
defer header.Release()
|
|
||||||
salt := header.Extend(xplusSaltLen)
|
|
||||||
c.randAccess.Lock()
|
|
||||||
_, _ = c.rand.Read(salt)
|
|
||||||
c.randAccess.Unlock()
|
|
||||||
key := sha256.Sum256(append(c.key, salt...))
|
|
||||||
var index int
|
|
||||||
for _, buffer := range buffers {
|
|
||||||
data := buffer.Bytes()
|
|
||||||
for i := range data {
|
|
||||||
data[i] ^= key[index%sha256.Size]
|
|
||||||
index++
|
|
||||||
}
|
|
||||||
}
|
|
||||||
buffers = append([]*buf.Buffer{header}, buffers...)
|
|
||||||
return c.writer.WriteVectorisedPacket(buffers, destination)
|
|
||||||
}
|
|
@ -64,7 +64,7 @@ func NewClient(ctx context.Context, dialer N.Dialer, serverAddr M.Socksaddr, opt
|
|||||||
serverAddr: serverAddr,
|
serverAddr: serverAddr,
|
||||||
host: options.Host,
|
host: options.Host,
|
||||||
method: options.Method,
|
method: options.Method,
|
||||||
headers: make(http.Header),
|
headers: options.Headers.Build(),
|
||||||
transport: transport,
|
transport: transport,
|
||||||
http2: tlsConfig != nil,
|
http2: tlsConfig != nil,
|
||||||
}
|
}
|
||||||
@ -83,9 +83,6 @@ func NewClient(ctx context.Context, dialer N.Dialer, serverAddr M.Socksaddr, opt
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, E.New("failed to set path: " + err.Error())
|
return nil, E.New("failed to set path: " + err.Error())
|
||||||
}
|
}
|
||||||
for key, valueList := range options.Headers {
|
|
||||||
client.headers[key] = valueList
|
|
||||||
}
|
|
||||||
client.url = &uri
|
client.url = &uri
|
||||||
return client, nil
|
return client, nil
|
||||||
}
|
}
|
||||||
|
@ -55,7 +55,7 @@ func NewServer(ctx context.Context, options option.V2RayHTTPOptions, tlsConfig t
|
|||||||
host: options.Host,
|
host: options.Host,
|
||||||
path: options.Path,
|
path: options.Path,
|
||||||
method: options.Method,
|
method: options.Method,
|
||||||
headers: make(http.Header),
|
headers: options.Headers.Build(),
|
||||||
}
|
}
|
||||||
if server.method == "" {
|
if server.method == "" {
|
||||||
server.method = "PUT"
|
server.method = "PUT"
|
||||||
@ -63,9 +63,6 @@ func NewServer(ctx context.Context, options option.V2RayHTTPOptions, tlsConfig t
|
|||||||
if !strings.HasPrefix(server.path, "/") {
|
if !strings.HasPrefix(server.path, "/") {
|
||||||
server.path = "/" + server.path
|
server.path = "/" + server.path
|
||||||
}
|
}
|
||||||
for key, value := range options.Headers {
|
|
||||||
server.headers[key] = value
|
|
||||||
}
|
|
||||||
server.httpServer = &http.Server{
|
server.httpServer = &http.Server{
|
||||||
Handler: server,
|
Handler: server,
|
||||||
ReadHeaderTimeout: C.TCPTimeout,
|
ReadHeaderTimeout: C.TCPTimeout,
|
||||||
|
@ -12,7 +12,6 @@ import (
|
|||||||
"github.com/sagernet/sing-box/common/tls"
|
"github.com/sagernet/sing-box/common/tls"
|
||||||
C "github.com/sagernet/sing-box/constant"
|
C "github.com/sagernet/sing-box/constant"
|
||||||
"github.com/sagernet/sing-box/option"
|
"github.com/sagernet/sing-box/option"
|
||||||
"github.com/sagernet/sing-box/transport/hysteria"
|
|
||||||
"github.com/sagernet/sing-quic"
|
"github.com/sagernet/sing-quic"
|
||||||
"github.com/sagernet/sing/common"
|
"github.com/sagernet/sing/common"
|
||||||
"github.com/sagernet/sing/common/bufio"
|
"github.com/sagernet/sing/common/bufio"
|
||||||
@ -93,7 +92,7 @@ func (c *Client) DialContext(ctx context.Context) (net.Conn, error) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
return &hysteria.StreamWrapper{Conn: conn, Stream: stream}, nil
|
return &StreamWrapper{Conn: conn, Stream: stream}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Client) Close() error {
|
func (c *Client) Close() error {
|
||||||
|
@ -12,7 +12,6 @@ import (
|
|||||||
"github.com/sagernet/sing-box/common/tls"
|
"github.com/sagernet/sing-box/common/tls"
|
||||||
C "github.com/sagernet/sing-box/constant"
|
C "github.com/sagernet/sing-box/constant"
|
||||||
"github.com/sagernet/sing-box/option"
|
"github.com/sagernet/sing-box/option"
|
||||||
"github.com/sagernet/sing-box/transport/hysteria"
|
|
||||||
"github.com/sagernet/sing-quic"
|
"github.com/sagernet/sing-quic"
|
||||||
"github.com/sagernet/sing/common"
|
"github.com/sagernet/sing/common"
|
||||||
M "github.com/sagernet/sing/common/metadata"
|
M "github.com/sagernet/sing/common/metadata"
|
||||||
@ -86,7 +85,7 @@ func (s *Server) streamAcceptLoop(conn quic.Connection) error {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
go s.handler.NewConnection(conn.Context(), &hysteria.StreamWrapper{Conn: conn, Stream: stream}, M.Metadata{})
|
go s.handler.NewConnection(conn.Context(), &StreamWrapper{Conn: conn, Stream: stream}, M.Metadata{})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
41
transport/v2rayquic/stream.go
Normal file
41
transport/v2rayquic/stream.go
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
package v2rayquic
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net"
|
||||||
|
|
||||||
|
"github.com/sagernet/quic-go"
|
||||||
|
"github.com/sagernet/sing/common/baderror"
|
||||||
|
)
|
||||||
|
|
||||||
|
type StreamWrapper struct {
|
||||||
|
Conn quic.Connection
|
||||||
|
quic.Stream
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *StreamWrapper) Read(p []byte) (n int, err error) {
|
||||||
|
n, err = s.Stream.Read(p)
|
||||||
|
return n, baderror.WrapQUIC(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *StreamWrapper) Write(p []byte) (n int, err error) {
|
||||||
|
n, err = s.Stream.Write(p)
|
||||||
|
return n, baderror.WrapQUIC(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *StreamWrapper) LocalAddr() net.Addr {
|
||||||
|
return s.Conn.LocalAddr()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *StreamWrapper) RemoteAddr() net.Addr {
|
||||||
|
return s.Conn.RemoteAddr()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *StreamWrapper) Upstream() any {
|
||||||
|
return s.Stream
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *StreamWrapper) Close() error {
|
||||||
|
s.CancelRead(0)
|
||||||
|
s.Stream.Close()
|
||||||
|
return nil
|
||||||
|
}
|
@ -286,6 +286,10 @@ func (ep *wireEndpoint) ARPHardwareType() header.ARPHardwareType {
|
|||||||
func (ep *wireEndpoint) AddHeader(buffer stack.PacketBufferPtr) {
|
func (ep *wireEndpoint) AddHeader(buffer stack.PacketBufferPtr) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (ep *wireEndpoint) ParseHeader(ptr stack.PacketBufferPtr) bool {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
func (ep *wireEndpoint) WritePackets(list stack.PacketBufferList) (int, tcpip.Error) {
|
func (ep *wireEndpoint) WritePackets(list stack.PacketBufferList) (int, tcpip.Error) {
|
||||||
for _, packetBuffer := range list.AsSlice() {
|
for _, packetBuffer := range list.AsSlice() {
|
||||||
packetBuffer.IncRef()
|
packetBuffer.IncRef()
|
||||||
|
Loading…
x
Reference in New Issue
Block a user