Compare commits

..

39 Commits

Author SHA1 Message Date
世界
8a138e34cc documentation: Bump version 2024-12-10 13:21:54 +08:00
世界
b79eb6a0ad clash-api: Fix missing endpoints 2024-12-10 13:21:54 +08:00
世界
72c45c0860 hysteria2: Add more masquerade options 2024-12-10 13:21:54 +08:00
世界
4b77ab7fca Improve timeouts 2024-12-10 13:21:54 +08:00
世界
9122250b3a Add UDP timeout route option 2024-12-10 13:21:54 +08:00
世界
9abc3aaa90 Make GSO adaptive 2024-12-10 13:21:53 +08:00
世界
2a48c837bb Fix tests 2024-12-10 13:21:53 +08:00
世界
23e0f2ddb8 Fix lint 2024-12-10 13:21:53 +08:00
世界
cad629ee02 refactor: WireGuard endpoint 2024-12-10 13:21:53 +08:00
世界
1c9bc2ccef refactor: connection manager 2024-12-10 13:21:53 +08:00
世界
24612691b0 documentation: Fix typo 2024-12-10 13:21:52 +08:00
世界
b88b03d9c3 Add override destination to route options 2024-12-10 13:21:52 +08:00
世界
9893258af8 Add dns.cache_capacity 2024-12-10 13:21:52 +08:00
世界
1630dbab44 Refactor multi networks strategy 2024-12-10 13:21:52 +08:00
世界
840a3e3442 documentation: Remove unused titles 2024-12-10 13:21:52 +08:00
世界
d6b6771746 Add multi network dialing 2024-12-10 13:21:51 +08:00
世界
c1ac3fbd15 documentation: Merge route options to route actions 2024-12-10 13:21:51 +08:00
世界
ef93657e4c Add network_[type/is_expensive/is_constrained] rule items 2024-12-10 13:21:51 +08:00
世界
21eac13d01 Merge route options to route actions 2024-12-10 13:21:51 +08:00
世界
9bdd5981b0 refactor: Platform Interfaces 2024-12-10 13:21:51 +08:00
世界
88d5254d4a refactor: Extract services form router 2024-12-10 13:21:50 +08:00
世界
8f1fe484a5 refactor: Modular network manager 2024-12-10 13:21:50 +08:00
世界
3f6ceff3e8 refactor: Modular inbound/outbound manager 2024-12-10 13:21:49 +08:00
世界
6f67bf7438 documentation: Add rule action 2024-12-10 13:21:49 +08:00
世界
3585e623b3 documentation: Update the scheduled removal time of deprecated features 2024-12-10 13:21:49 +08:00
世界
cd5c10fc30 documentation: Remove outdated icons 2024-12-10 13:21:48 +08:00
世界
5cf20fb74a Migrate bad options to library 2024-12-10 13:21:48 +08:00
世界
18d0295240 Implement udp connect 2024-12-10 13:21:47 +08:00
世界
9d6eb2546e Implement new deprecated warnings 2024-12-10 13:21:47 +08:00
世界
bbdc6b43b0 Improve rule actions 2024-12-10 13:21:47 +08:00
世界
d12228213c Remove unused reject methods 2024-12-10 13:21:46 +08:00
世界
da47df6037 refactor: Modular inbounds/outbounds 2024-12-10 13:21:46 +08:00
世界
755ea39843 Implement dns-hijack 2024-12-10 13:21:46 +08:00
世界
cb9ee9caa1 Implement resolve(server) 2024-12-10 13:21:45 +08:00
世界
04b1a35a52 Implement TCP and ICMP rejects 2024-12-10 13:21:45 +08:00
世界
cbdf9e0104 Crazy sekai overturns the small pond 2024-12-10 13:21:45 +08:00
世界
2e124a55be Add workaround for bulkBarrierPreWrite: unaligned arguments panic 2024-12-10 13:21:26 +08:00
世界
20b9e9f258 Update debug iOS library build 2024-12-10 13:21:26 +08:00
世界
8850611fb6 Fix play release 2024-12-10 13:10:34 +08:00
14 changed files with 25 additions and 156 deletions

View File

@@ -347,7 +347,7 @@ jobs:
mkdir clients/android/app/libs
cp libbox.aar clients/android/app/libs
cd clients/android
echo -n "$SERVICE_ACCOUNT_CREDENTIALS" | base64 --decode > service-account-credentials.json
echo -n "$SERVICE_ACCOUNT_CREDENTIALS" | base64 --decode -o "service-account-credentials.json"
./gradlew :app:publishPlayReleaseBundle
env:
JAVA_HOME: /usr/lib/jvm/java-17-openjdk-amd64
@@ -557,7 +557,7 @@ jobs:
path: 'dist'
upload:
name: Upload builds
if: always() && github.event_name == 'workflow_dispatch' && inputs.build != 'publish-android'
if: always() && github.event_name == 'workflow_dispatch'
runs-on: ubuntu-latest
needs:
- calculate_version

View File

@@ -30,15 +30,14 @@ func NewClient(ctx context.Context, serverAddress string, options option.Outboun
return nil, nil
}
if options.ECH != nil && options.ECH.Enabled {
if options.ECH.PQSignatureSchemesEnabled || options.ECH.DynamicRecordSizingDisabled {
return NewECHClient(ctx, serverAddress, options)
}
return NewECHClient(ctx, serverAddress, options)
} else if options.Reality != nil && options.Reality.Enabled {
return NewRealityClient(ctx, serverAddress, options)
} else if options.UTLS != nil && options.UTLS.Enabled {
return NewUTLSClient(ctx, serverAddress, options)
} else {
return NewSTDClient(ctx, serverAddress, options)
}
return NewSTDClient(ctx, serverAddress, options)
}
func ClientHandshake(ctx context.Context, conn net.Conn, config Config) (Conn, error) {

View File

@@ -7,6 +7,7 @@ import (
"encoding/binary"
"encoding/pem"
cftls "github.com/sagernet/cloudflare-tls"
E "github.com/sagernet/sing/common/exceptions"
"github.com/cloudflare/circl/hpke"
@@ -58,6 +59,7 @@ func ECHKeygenDefault(serverName string, pqSignatureSchemesEnabled bool) (config
type echKeyConfigPair struct {
id uint8
key cftls.EXP_ECHKey
rawKey []byte
conf myECHKeyConfig
rawConf []byte
@@ -151,13 +153,14 @@ func echKeygen(version uint16, serverName string, conf []myECHKeyConfig, suite [
sk = be.AppendUint16(sk, uint16(len(b)))
sk = append(sk, b...)
cfECHKeys, err := UnmarshalECHKeys(sk)
cfECHKeys, err := cftls.EXP_UnmarshalECHKeys(sk)
if err != nil {
return nil, E.Cause(err, "bug: can't parse generated ECH server key")
}
if len(cfECHKeys) != 1 {
return nil, E.New("bug: unexpected server key count")
}
pair.key = cfECHKeys[0]
pair.rawKey = sk
pairs = append(pairs, pair)

View File

@@ -17,13 +17,12 @@ func NewServer(ctx context.Context, logger log.Logger, options option.InboundTLS
return nil, nil
}
if options.ECH != nil && options.ECH.Enabled {
if options.ECH.PQSignatureSchemesEnabled || options.ECH.DynamicRecordSizingDisabled {
return NewECHServer(ctx, logger, options)
}
return NewECHServer(ctx, logger, options)
} else if options.Reality != nil && options.Reality.Enabled {
return NewRealityServer(ctx, logger, options)
} else {
return NewSTDServer(ctx, logger, options)
}
return NewSTDServer(ctx, logger, options)
}
func ServerHandshake(ctx context.Context, conn net.Conn, config ServerConfig) (Conn, error) {

View File

@@ -4,25 +4,16 @@ import (
"context"
"crypto/tls"
"crypto/x509"
"encoding/base64"
"net"
"net/netip"
"os"
"strings"
"github.com/sagernet/sing-box/adapter"
"github.com/sagernet/sing-box/option"
"github.com/sagernet/sing-dns"
E "github.com/sagernet/sing/common/exceptions"
"github.com/sagernet/sing/common/ntp"
aTLS "github.com/sagernet/sing/common/tls"
"github.com/sagernet/sing/service"
mDNS "github.com/miekg/dns"
)
var _ ConfigCompat = (*STDClientConfig)(nil)
type STDClientConfig struct {
config *tls.Config
}
@@ -55,63 +46,6 @@ func (s *STDClientConfig) Clone() Config {
return &STDClientConfig{s.config.Clone()}
}
type STDECHClientConfig struct {
STDClientConfig
}
func (s *STDClientConfig) ClientHandshake(ctx context.Context, conn net.Conn) (aTLS.Conn, error) {
if len(s.config.EncryptedClientHelloConfigList) == 0 {
message := &mDNS.Msg{
MsgHdr: mDNS.MsgHdr{
RecursionDesired: true,
},
Question: []mDNS.Question{
{
Name: mDNS.Fqdn(s.config.ServerName),
Qtype: mDNS.TypeHTTPS,
Qclass: mDNS.ClassINET,
},
},
}
dnsRouter := service.FromContext[adapter.Router](ctx)
response, err := dnsRouter.Exchange(ctx, message)
if err != nil {
return nil, E.Cause(err, "fetch ECH config list")
}
if response.Rcode != mDNS.RcodeSuccess {
return nil, E.Cause(dns.RCodeError(response.Rcode), "fetch ECH config list")
}
for _, rr := range response.Answer {
switch resource := rr.(type) {
case *mDNS.HTTPS:
for _, value := range resource.Value {
if value.Key().String() == "ech" {
echConfigList, err := base64.StdEncoding.DecodeString(value.String())
if err != nil {
return nil, E.Cause(err, "decode ECH config")
}
s.config.EncryptedClientHelloConfigList = echConfigList
}
}
}
}
return nil, E.New("no ECH config found in DNS records")
}
tlsConn, err := s.Client(conn)
if err != nil {
return nil, err
}
err = tlsConn.HandshakeContext(ctx)
if err != nil {
return nil, err
}
return tlsConn, nil
}
func (s *STDECHClientConfig) Clone() Config {
return &STDECHClientConfig{STDClientConfig{s.config.Clone()}}
}
func NewSTDClient(ctx context.Context, serverAddress string, options option.OutboundTLSOptions) (Config, error) {
var serverName string
if options.ServerName != "" {
@@ -194,21 +128,5 @@ func NewSTDClient(ctx context.Context, serverAddress string, options option.Outb
}
tlsConfig.RootCAs = certPool
}
if options.ECH != nil && options.ECH.Enabled {
var echConfig []byte
if len(options.ECH.Config) > 0 {
echConfig = []byte(strings.Join(options.ECH.Config, "\n"))
} else if options.ECH.ConfigPath != "" {
content, err := os.ReadFile(options.ECH.ConfigPath)
if err != nil {
return nil, E.Cause(err, "read ECH config")
}
echConfig = content
}
if echConfig != nil {
tlsConfig.EncryptedClientHelloConfigList = echConfig
}
return &STDECHClientConfig{STDClientConfig{&tlsConfig}}, nil
}
return &STDClientConfig{&tlsConfig}, nil
}

View File

@@ -3,7 +3,6 @@ package tls
import (
"context"
"crypto/tls"
"encoding/pem"
"net"
"os"
"strings"
@@ -15,8 +14,6 @@ import (
"github.com/sagernet/sing/common"
E "github.com/sagernet/sing/common/exceptions"
"github.com/sagernet/sing/common/ntp"
"golang.org/x/crypto/cryptobyte"
)
var errInsecureUnused = E.New("tls: insecure unused")
@@ -241,31 +238,6 @@ func NewSTDServer(ctx context.Context, logger log.Logger, options option.Inbound
tlsConfig.Certificates = []tls.Certificate{keyPair}
}
}
if options.ECH != nil && options.ECH.Enabled {
var echKey []byte
if len(options.ECH.Key) > 0 {
echKey = []byte(strings.Join(options.ECH.Key, "\n"))
} else if options.ECH.KeyPath != "" {
content, err := os.ReadFile(options.ECH.KeyPath)
if err != nil {
return nil, E.Cause(err, "read ECH key")
}
echKey = content
} else {
return nil, E.New("missing ECH key")
}
block, rest := pem.Decode(echKey)
if block == nil || block.Type != "ECH KEYS" || len(rest) > 0 {
return nil, E.New("invalid ECH keys pem")
}
echKeys, err := UnmarshalECHKeys(block.Bytes)
if err != nil {
return nil, E.Cause(err, "parse ECH keys")
}
tlsConfig.EncryptedClientHelloKeys = echKeys
}
return &STDServerConfig{
config: tlsConfig,
logger: logger,
@@ -276,22 +248,3 @@ func NewSTDServer(ctx context.Context, logger log.Logger, options option.Inbound
keyPath: options.KeyPath,
}, nil
}
func UnmarshalECHKeys(raw []byte) ([]tls.EncryptedClientHelloKey, error) {
var keys []tls.EncryptedClientHelloKey
rawString := cryptobyte.String(raw)
for !rawString.Empty() {
var key tls.EncryptedClientHelloKey
if !rawString.ReadUint16LengthPrefixed((*cryptobyte.String)(&key.PrivateKey)) {
return nil, E.New("error parsing private key")
}
if !rawString.ReadUint16LengthPrefixed((*cryptobyte.String)(&key.Config)) {
return nil, E.New("error parsing config")
}
keys = append(keys, key)
}
if len(keys) == 0 {
return nil, E.New("empty ECH keys")
}
return keys, nil
}

View File

@@ -2,7 +2,7 @@
icon: material/alert-decagram
---
#### 1.11.0-beta.9
#### 1.11.0-beta.8
* Fixes and improvements

2
go.mod
View File

@@ -25,7 +25,7 @@ require (
github.com/sagernet/gvisor v0.0.0-20241123041152-536d05261cff
github.com/sagernet/quic-go v0.48.2-beta.1
github.com/sagernet/reality v0.0.0-20230406110435-ee17307e7691
github.com/sagernet/sing v0.6.0-beta.6
github.com/sagernet/sing v0.6.0-beta.5
github.com/sagernet/sing-dns v0.4.0-beta.1
github.com/sagernet/sing-mux v0.3.0-alpha.1
github.com/sagernet/sing-quic v0.4.0-alpha.4

4
go.sum
View File

@@ -110,8 +110,8 @@ github.com/sagernet/quic-go v0.48.2-beta.1/go.mod h1:1WgdDIVD1Gybp40JTWketeSfKA/
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/sing v0.2.18/go.mod h1:OL6k2F0vHmEzXz2KW19qQzu172FDgSbUSODylighuVo=
github.com/sagernet/sing v0.6.0-beta.6 h1:IFnTCG06Z5rLMZJqw1ZmDncDl2N9gsVw0MGvgakrpg8=
github.com/sagernet/sing v0.6.0-beta.6/go.mod h1:ARkL0gM13/Iv5VCZmci/NuoOlePoIsW0m7BWfln/Hak=
github.com/sagernet/sing v0.6.0-beta.5 h1:RD2j8WmJsvAbbBkAlJWaiYmnd+v/JohBiweoew7kMwo=
github.com/sagernet/sing v0.6.0-beta.5/go.mod h1:ARkL0gM13/Iv5VCZmci/NuoOlePoIsW0m7BWfln/Hak=
github.com/sagernet/sing-dns v0.4.0-beta.1 h1:W1XkdhigwxDOMgMDVB+9kdomCpb7ExsZfB4acPcTZFY=
github.com/sagernet/sing-dns v0.4.0-beta.1/go.mod h1:8wuFcoFkWM4vJuQyg8e97LyvDwe0/Vl7G839WLcKDs8=
github.com/sagernet/sing-mux v0.3.0-alpha.1 h1:IgNX5bJBpL41gGbp05pdDOvh/b5eUQ6cv9240+Ngipg=

View File

@@ -91,7 +91,7 @@ func (h *Inbound) NewConnectionEx(ctx context.Context, conn net.Conn, metadata a
return
}
}
err = http.HandleConnectionEx(ctx, conn, std_bufio.NewReader(conn), h.authenticator, adapter.NewUpstreamHandlerEx(metadata, h.newUserConnection, h.streamUserPacketConnection), metadata.Source, onClose)
err = http.HandleConnectionEx(ctx, conn, std_bufio.NewReader(conn), h.authenticator, nil, adapter.NewUpstreamHandlerEx(metadata, h.newUserConnection, h.streamUserPacketConnection), metadata.Source, onClose)
if err != nil {
N.CloseOnHandshakeFailure(conn, onClose, err)
h.logger.ErrorContext(ctx, E.Cause(err, "process connection from ", metadata.Source))

View File

@@ -85,9 +85,9 @@ func (h *Inbound) newConnection(ctx context.Context, conn net.Conn, metadata ada
}
switch headerBytes[0] {
case socks4.Version, socks5.Version:
return socks.HandleConnectionEx(ctx, conn, reader, h.authenticator, adapter.NewUpstreamHandlerEx(metadata, h.newUserConnection, h.streamUserPacketConnection), metadata.Source, onClose)
return socks.HandleConnectionEx(ctx, conn, reader, h.authenticator, nil, adapter.NewUpstreamHandlerEx(metadata, h.newUserConnection, h.streamUserPacketConnection), metadata.Source, metadata.Destination, onClose)
default:
return http.HandleConnectionEx(ctx, conn, reader, h.authenticator, adapter.NewUpstreamHandlerEx(metadata, h.newUserConnection, h.streamUserPacketConnection), metadata.Source, onClose)
return http.HandleConnectionEx(ctx, conn, reader, h.authenticator, nil, adapter.NewUpstreamHandlerEx(metadata, h.newUserConnection, h.streamUserPacketConnection), metadata.Source, onClose)
}
}

View File

@@ -62,7 +62,7 @@ func (h *Inbound) Close() error {
}
func (h *Inbound) NewConnectionEx(ctx context.Context, conn net.Conn, metadata adapter.InboundContext, onClose N.CloseHandlerFunc) {
err := socks.HandleConnectionEx(ctx, conn, std_bufio.NewReader(conn), h.authenticator, adapter.NewUpstreamHandlerEx(metadata, h.newUserConnection, h.streamUserPacketConnection), metadata.Source, onClose)
err := socks.HandleConnectionEx(ctx, conn, std_bufio.NewReader(conn), h.authenticator, nil, adapter.NewUpstreamHandlerEx(metadata, h.newUserConnection, h.streamUserPacketConnection), metadata.Source, metadata.Destination, onClose)
N.CloseOnHandshakeFailure(conn, onClose, err)
if err != nil {
if E.IsClosedOrCanceled(err) {

View File

@@ -99,7 +99,7 @@ func (l *ProxyListener) acceptLoop() {
}
func (l *ProxyListener) accept(ctx context.Context, conn *net.TCPConn) error {
return socks.HandleConnectionEx(ctx, conn, std_bufio.NewReader(conn), l.authenticator, l, M.SocksaddrFromNet(conn.RemoteAddr()), nil)
return socks.HandleConnectionEx(ctx, conn, std_bufio.NewReader(conn), l.authenticator, nil, l, M.SocksaddrFromNet(conn.RemoteAddr()), M.Socksaddr{}, nil)
}
func (l *ProxyListener) NewConnectionEx(ctx context.Context, conn net.Conn, source M.Socksaddr, destination M.Socksaddr, onClose N.CloseHandlerFunc) {

View File

@@ -461,12 +461,8 @@ match:
break match
}
}
if !preMatch && inputPacketConn != nil && !metadata.Destination.IsFqdn() && !metadata.Destination.Addr.IsGlobalUnicast() {
var timeout time.Duration
if metadata.InboundType == C.TypeSOCKS {
timeout = C.TCPTimeout
}
newBuffer, newPacketBuffers, newErr := r.actionSniff(ctx, metadata, &rule.RuleActionSniff{Timeout: timeout}, inputConn, inputPacketConn)
if !preMatch && metadata.Destination.Addr.IsUnspecified() {
newBuffer, newPacketBuffers, newErr := r.actionSniff(ctx, metadata, &rule.RuleActionSniff{}, inputConn, inputPacketConn)
if newErr != nil {
fatalErr = newErr
return
@@ -562,7 +558,8 @@ func (r *Router) actionSniff(
return
}
} else {
if !metadata.Destination.Addr.IsGlobalUnicast() {
// TODO: maybe always override destination
if metadata.Destination.Addr.IsUnspecified() {
metadata.Destination = destination
}
if len(packetBuffers) > 0 {