Update On Sat Sep 13 20:34:35 CEST 2025

This commit is contained in:
github-action[bot]
2025-09-13 20:34:36 +02:00
parent f93a1b54e8
commit 8cbef33280
192 changed files with 2148 additions and 1571 deletions

1
.github/update.log vendored
View File

@@ -1119,3 +1119,4 @@ Update On Tue Sep 9 20:33:51 CEST 2025
Update On Wed Sep 10 20:42:57 CEST 2025
Update On Thu Sep 11 20:34:24 CEST 2025
Update On Fri Sep 12 20:36:01 CEST 2025
Update On Sat Sep 13 20:34:27 CEST 2025

View File

@@ -2,7 +2,6 @@ package adapter
import (
"context"
"crypto/tls"
"encoding/json"
"fmt"
"net"
@@ -236,6 +235,11 @@ func (p *Proxy) URLTest(ctx context.Context, url string, expectedStatus utils.In
}
req = req.WithContext(ctx)
tlsConfig, err := ca.GetTLSConfig(ca.Option{})
if err != nil {
return
}
transport := &http.Transport{
DialContext: func(context.Context, string, string) (net.Conn, error) {
return instance, nil
@@ -245,7 +249,7 @@ func (p *Proxy) URLTest(ctx context.Context, url string, expectedStatus utils.In
IdleConnTimeout: 90 * time.Second,
TLSHandshakeTimeout: 10 * time.Second,
ExpectContinueTimeout: 1 * time.Second,
TLSClientConfig: ca.GetGlobalTLSConfig(&tls.Config{}),
TLSClientConfig: tlsConfig,
}
client := http.Client{

View File

@@ -167,10 +167,13 @@ func NewHttp(option HttpOption) (*Http, error) {
sni = option.SNI
}
var err error
tlsConfig, err = ca.GetSpecifiedFingerprintTLSConfig(&tls.Config{
tlsConfig, err = ca.GetTLSConfig(ca.Option{
TLSConfig: &tls.Config{
InsecureSkipVerify: option.SkipCertVerify,
ServerName: sni,
}, option.Fingerprint)
},
Fingerprint: option.Fingerprint,
})
if err != nil {
return nil, err
}

View File

@@ -160,14 +160,16 @@ func NewHysteria(option HysteriaOption) (*Hysteria, error) {
serverName = option.SNI
}
tlsConfig := &tls.Config{
tlsConfig, err := ca.GetTLSConfig(ca.Option{
TLSConfig: &tls.Config{
ServerName: serverName,
InsecureSkipVerify: option.SkipCertVerify,
MinVersion: tls.VersionTLS13,
}
var err error
tlsConfig, err = ca.GetTLSConfig(tlsConfig, option.Fingerprint, option.CustomCA, option.CustomCAString)
},
Fingerprint: option.Fingerprint,
CustomCA: option.CustomCA,
CustomCAString: option.CustomCAString,
})
if err != nil {
return nil, err
}

View File

@@ -141,14 +141,16 @@ func NewHysteria2(option Hysteria2Option) (*Hysteria2, error) {
serverName = option.SNI
}
tlsConfig := &tls.Config{
tlsConfig, err := ca.GetTLSConfig(ca.Option{
TLSConfig: &tls.Config{
ServerName: serverName,
InsecureSkipVerify: option.SkipCertVerify,
MinVersion: tls.VersionTLS13,
}
var err error
tlsConfig, err = ca.GetTLSConfig(tlsConfig, option.Fingerprint, option.CustomCA, option.CustomCAString)
},
Fingerprint: option.Fingerprint,
CustomCA: option.CustomCA,
CustomCAString: option.CustomCAString,
})
if err != nil {
return nil, err
}

View File

@@ -193,13 +193,14 @@ func (ss *Socks5) clientHandshakeContext(ctx context.Context, c net.Conn, addr s
func NewSocks5(option Socks5Option) (*Socks5, error) {
var tlsConfig *tls.Config
if option.TLS {
tlsConfig = &tls.Config{
var err error
tlsConfig, err = ca.GetTLSConfig(ca.Option{
TLSConfig: &tls.Config{
InsecureSkipVerify: option.SkipCertVerify,
ServerName: option.Server,
}
var err error
tlsConfig, err = ca.GetSpecifiedFingerprintTLSConfig(tlsConfig, option.Fingerprint)
},
Fingerprint: option.Fingerprint,
})
if err != nil {
return nil, err
}

View File

@@ -100,14 +100,15 @@ func (t *Trojan) StreamConnContext(ctx context.Context, c net.Conn, metadata *C.
}
wsOpts.TLS = true
tlsConfig := &tls.Config{
wsOpts.TLSConfig, err = ca.GetTLSConfig(ca.Option{
TLSConfig: &tls.Config{
NextProtos: alpn,
MinVersion: tls.VersionTLS12,
InsecureSkipVerify: t.option.SkipCertVerify,
ServerName: t.option.SNI,
}
wsOpts.TLSConfig, err = ca.GetSpecifiedFingerprintTLSConfig(tlsConfig, t.option.Fingerprint)
},
Fingerprint: t.option.Fingerprint,
})
if err != nil {
return nil, err
}
@@ -363,15 +364,15 @@ func NewTrojan(option TrojanOption) (*Trojan, error) {
return c, nil
}
tlsConfig := &tls.Config{
tlsConfig, err := ca.GetTLSConfig(ca.Option{
TLSConfig: &tls.Config{
NextProtos: option.ALPN,
MinVersion: tls.VersionTLS12,
InsecureSkipVerify: option.SkipCertVerify,
ServerName: option.SNI,
}
var err error
tlsConfig, err = ca.GetSpecifiedFingerprintTLSConfig(tlsConfig, option.Fingerprint)
},
Fingerprint: option.Fingerprint,
})
if err != nil {
return nil, err
}

View File

@@ -161,17 +161,20 @@ func (t *Tuic) ProxyInfo() C.ProxyInfo {
func NewTuic(option TuicOption) (*Tuic, error) {
addr := net.JoinHostPort(option.Server, strconv.Itoa(option.Port))
serverName := option.Server
tlsConfig := &tls.Config{
if option.SNI != "" {
serverName = option.SNI
}
tlsConfig, err := ca.GetTLSConfig(ca.Option{
TLSConfig: &tls.Config{
ServerName: serverName,
InsecureSkipVerify: option.SkipCertVerify,
MinVersion: tls.VersionTLS13,
}
if option.SNI != "" {
tlsConfig.ServerName = option.SNI
}
var err error
tlsConfig, err = ca.GetTLSConfig(tlsConfig, option.Fingerprint, option.CustomCA, option.CustomCAString)
},
Fingerprint: option.Fingerprint,
CustomCA: option.CustomCA,
CustomCAString: option.CustomCAString,
})
if err != nil {
return nil, err
}

View File

@@ -95,14 +95,15 @@ func (v *Vless) StreamConnContext(ctx context.Context, c net.Conn, metadata *C.M
}
if v.option.TLS {
wsOpts.TLS = true
tlsConfig := &tls.Config{
wsOpts.TLSConfig, err = ca.GetTLSConfig(ca.Option{
TLSConfig: &tls.Config{
MinVersion: tls.VersionTLS12,
ServerName: host,
InsecureSkipVerify: v.option.SkipCertVerify,
NextProtos: []string{"http/1.1"},
}
wsOpts.TLSConfig, err = ca.GetSpecifiedFingerprintTLSConfig(tlsConfig, v.option.Fingerprint)
},
Fingerprint: v.option.Fingerprint,
})
if err != nil {
return nil, err
}
@@ -498,10 +499,13 @@ func NewVless(option VlessOption) (*Vless, error) {
}
var tlsConfig *tls.Config
if option.TLS {
tlsConfig, err = ca.GetSpecifiedFingerprintTLSConfig(&tls.Config{
tlsConfig, err = ca.GetTLSConfig(ca.Option{
TLSConfig: &tls.Config{
InsecureSkipVerify: v.option.SkipCertVerify,
ServerName: v.option.ServerName,
}, v.option.Fingerprint)
},
Fingerprint: v.option.Fingerprint,
})
if err != nil {
return nil, err
}

View File

@@ -123,13 +123,14 @@ func (v *Vmess) StreamConnContext(ctx context.Context, c net.Conn, metadata *C.M
if v.option.TLS {
wsOpts.TLS = true
tlsConfig := &tls.Config{
wsOpts.TLSConfig, err = ca.GetTLSConfig(ca.Option{
TLSConfig: &tls.Config{
ServerName: host,
InsecureSkipVerify: v.option.SkipCertVerify,
NextProtos: []string{"http/1.1"},
}
wsOpts.TLSConfig, err = ca.GetSpecifiedFingerprintTLSConfig(tlsConfig, v.option.Fingerprint)
},
Fingerprint: v.option.Fingerprint,
})
if err != nil {
return nil, err
}
@@ -501,10 +502,13 @@ func NewVmess(option VmessOption) (*Vmess, error) {
}
var tlsConfig *tls.Config
if option.TLS {
tlsConfig, err = ca.GetSpecifiedFingerprintTLSConfig(&tls.Config{
tlsConfig, err = ca.GetTLSConfig(ca.Option{
TLSConfig: &tls.Config{
InsecureSkipVerify: v.option.SkipCertVerify,
ServerName: v.option.ServerName,
}, v.option.Fingerprint)
},
Fingerprint: v.option.Fingerprint,
})
if err != nil {
return nil, err
}

View File

@@ -10,7 +10,9 @@ import (
"strconv"
"sync"
"github.com/metacubex/mihomo/common/once"
C "github.com/metacubex/mihomo/constant"
"github.com/metacubex/mihomo/ntp"
)
var globalCertPool *x509.CertPool
@@ -65,18 +67,6 @@ func ResetCertificate() {
initializeCertPool()
}
func getCertPool() *x509.CertPool {
if globalCertPool == nil {
mutex.Lock()
defer mutex.Unlock()
if globalCertPool != nil {
return globalCertPool
}
initializeCertPool()
}
return globalCertPool
}
func GetCertPool(customCA string, customCAString string) (*x509.CertPool, error) {
var certificate []byte
var err error
@@ -99,22 +89,41 @@ func GetCertPool(customCA string, customCAString string) (*x509.CertPool, error)
}
return certPool, nil
} else {
return getCertPool(), nil
mutex.Lock()
defer mutex.Unlock()
if globalCertPool == nil {
initializeCertPool()
}
return globalCertPool, nil
}
}
// GetTLSConfig specified fingerprint, customCA and customCAString
func GetTLSConfig(tlsConfig *tls.Config, fingerprint string, customCA string, customCAString string) (_ *tls.Config, err error) {
type Option struct {
TLSConfig *tls.Config
Fingerprint string
CustomCA string
CustomCAString string
ZeroTrust bool
}
func GetTLSConfig(opt Option) (tlsConfig *tls.Config, err error) {
tlsConfig = opt.TLSConfig
if tlsConfig == nil {
tlsConfig = &tls.Config{}
}
tlsConfig.RootCAs, err = GetCertPool(customCA, customCAString)
tlsConfig.Time = ntp.Now
if opt.ZeroTrust {
tlsConfig.RootCAs = zeroTrustCertPool()
} else {
tlsConfig.RootCAs, err = GetCertPool(opt.CustomCA, opt.CustomCAString)
if err != nil {
return nil, err
}
}
if len(fingerprint) > 0 {
tlsConfig.VerifyPeerCertificate, err = NewFingerprintVerifier(fingerprint)
if len(opt.Fingerprint) > 0 {
tlsConfig.VerifyPeerCertificate, err = NewFingerprintVerifier(opt.Fingerprint)
if err != nil {
return nil, err
}
@@ -123,12 +132,12 @@ func GetTLSConfig(tlsConfig *tls.Config, fingerprint string, customCA string, cu
return tlsConfig, nil
}
// GetSpecifiedFingerprintTLSConfig specified fingerprint
func GetSpecifiedFingerprintTLSConfig(tlsConfig *tls.Config, fingerprint string) (*tls.Config, error) {
return GetTLSConfig(tlsConfig, fingerprint, "", "")
}
func GetGlobalTLSConfig(tlsConfig *tls.Config) *tls.Config {
tlsConfig, _ = GetTLSConfig(tlsConfig, "", "", "")
return tlsConfig
}
var zeroTrustCertPool = once.OnceValue(func() *x509.CertPool {
if len(_CaCertificates) != 0 { // always using embed cert first
zeroTrustCertPool := x509.NewCertPool()
if zeroTrustCertPool.AppendCertsFromPEM(_CaCertificates) {
return zeroTrustCertPool
}
}
return nil // fallback to system pool
})

View File

@@ -2,7 +2,6 @@ package http
import (
"context"
"crypto/tls"
"io"
"net"
"net/http"
@@ -28,11 +27,11 @@ func SetUA(UA string) {
ua = UA
}
func HttpRequest(ctx context.Context, url, method string, header map[string][]string, body io.Reader) (*http.Response, error) {
return HttpRequestWithProxy(ctx, url, method, header, body, "")
}
func HttpRequestWithProxy(ctx context.Context, url, method string, header map[string][]string, body io.Reader, specialProxy string) (*http.Response, error) {
func HttpRequest(ctx context.Context, url, method string, header map[string][]string, body io.Reader, options ...Option) (*http.Response, error) {
opt := option{}
for _, o := range options {
o(&opt)
}
method = strings.ToUpper(method)
urlRes, err := URL.Parse(url)
if err != nil {
@@ -40,6 +39,10 @@ func HttpRequestWithProxy(ctx context.Context, url, method string, header map[st
}
req, err := http.NewRequest(method, urlRes.String(), body)
if err != nil {
return nil, err
}
for k, v := range header {
for _, v := range v {
req.Header.Add(k, v)
@@ -50,10 +53,6 @@ func HttpRequestWithProxy(ctx context.Context, url, method string, header map[st
req.Header.Set("User-Agent", UA())
}
if err != nil {
return nil, err
}
if user := urlRes.User; user != nil {
password, _ := user.Password()
req.SetBasicAuth(user.Username(), password)
@@ -61,6 +60,11 @@ func HttpRequestWithProxy(ctx context.Context, url, method string, header map[st
req = req.WithContext(ctx)
tlsConfig, err := ca.GetTLSConfig(opt.caOption)
if err != nil {
return nil, err
}
transport := &http.Transport{
// from http.DefaultTransport
DisableKeepAlives: runtime.GOOS == "android",
@@ -69,15 +73,34 @@ func HttpRequestWithProxy(ctx context.Context, url, method string, header map[st
TLSHandshakeTimeout: 10 * time.Second,
ExpectContinueTimeout: 1 * time.Second,
DialContext: func(ctx context.Context, network, address string) (net.Conn, error) {
if conn, err := inner.HandleTcp(inner.GetTunnel(), address, specialProxy); err == nil {
if conn, err := inner.HandleTcp(inner.GetTunnel(), address, opt.specialProxy); err == nil {
return conn, nil
} else {
return dialer.DialContext(ctx, network, address)
}
},
TLSClientConfig: ca.GetGlobalTLSConfig(&tls.Config{}),
TLSClientConfig: tlsConfig,
}
client := http.Client{Transport: transport}
return client.Do(req)
}
type Option func(opt *option)
type option struct {
specialProxy string
caOption ca.Option
}
func WithSpecialProxy(name string) Option {
return func(opt *option) {
opt.specialProxy = name
}
}
func WithCAOption(caOption ca.Option) Option {
return func(opt *option) {
opt.caOption = caOption
}
}

View File

@@ -135,7 +135,7 @@ func (h *HTTPVehicle) Read(ctx context.Context, oldHash utils.HashType) (buf []b
setIfNoneMatch = true
}
}
resp, err := mihomoHttp.HttpRequestWithProxy(ctx, h.url, http.MethodGet, header, nil, h.proxy)
resp, err := mihomoHttp.HttpRequest(ctx, h.url, http.MethodGet, header, nil, mihomoHttp.WithSpecialProxy(h.proxy))
if err != nil {
return
}

View File

@@ -15,6 +15,7 @@ import (
"sync"
"time"
"github.com/metacubex/mihomo/component/ca"
mihomoHttp "github.com/metacubex/mihomo/component/http"
C "github.com/metacubex/mihomo/constant"
"github.com/metacubex/mihomo/constant/features"
@@ -171,7 +172,7 @@ func (u *CoreUpdater) Update(currentExePath string, channel string, force bool)
func (u *CoreUpdater) getLatestVersion(versionURL string) (version string, err error) {
ctx, cancel := context.WithTimeout(context.Background(), time.Second*5)
defer cancel()
resp, err := mihomoHttp.HttpRequest(ctx, versionURL, http.MethodGet, nil, nil)
resp, err := mihomoHttp.HttpRequest(ctx, versionURL, http.MethodGet, nil, nil, mihomoHttp.WithCAOption(ca.Option{ZeroTrust: true}))
if err != nil {
return "", err
}
@@ -194,7 +195,7 @@ func (u *CoreUpdater) getLatestVersion(versionURL string) (version string, err e
func (u *CoreUpdater) download(updateDir, packagePath, packageURL string) (err error) {
ctx, cancel := context.WithTimeout(context.Background(), time.Second*90)
defer cancel()
resp, err := mihomoHttp.HttpRequest(ctx, packageURL, http.MethodGet, nil, nil)
resp, err := mihomoHttp.HttpRequest(ctx, packageURL, http.MethodGet, nil, nil, mihomoHttp.WithCAOption(ca.Option{ZeroTrust: true}))
if err != nil {
return fmt.Errorf("http request failed: %w", err)
}

View File

@@ -48,6 +48,13 @@ func (c *client) ExchangeContext(ctx context.Context, m *D.Msg) (*D.Msg, error)
network = "tcp"
}
tlsConfig, err := ca.GetTLSConfig(ca.Option{
TLSConfig: c.Client.TLSConfig,
})
if err != nil {
return nil, err
}
addr := net.JoinHostPort(c.host, c.port)
conn, err := c.dialer.DialContext(ctx, network, addr)
if err != nil {
@@ -66,7 +73,7 @@ func (c *client) ExchangeContext(ctx context.Context, m *D.Msg) (*D.Msg, error)
ch := make(chan result, 1)
go func() {
if strings.HasSuffix(c.Client.Net, "tls") {
conn = tls.Client(conn, ca.GetGlobalTLSConfig(c.Client.TLSConfig))
conn = tls.Client(conn, tlsConfig)
}
dConn := &D.Conn{

View File

@@ -397,12 +397,16 @@ func (doh *dnsOverHTTPS) createTransport(ctx context.Context) (t http.RoundTripp
return transport, nil
}
tlsConfig := ca.GetGlobalTLSConfig(
&tls.Config{
tlsConfig, err := ca.GetTLSConfig(ca.Option{
TLSConfig: &tls.Config{
InsecureSkipVerify: doh.skipCertVerify,
MinVersion: tls.VersionTLS12,
SessionTicketsDisabled: false,
},
})
if err != nil {
return nil, err
}
var nextProtos []string
for _, v := range doh.httpVersions {
nextProtos = append(nextProtos, string(v))

View File

@@ -331,15 +331,19 @@ func (doq *dnsOverQUIC) openConnection(ctx context.Context) (conn *quic.Conn, er
return nil, err
}
tlsConfig := ca.GetGlobalTLSConfig(
&tls.Config{
tlsConfig, err := ca.GetTLSConfig(ca.Option{
TLSConfig: &tls.Config{
ServerName: host,
InsecureSkipVerify: doq.skipCertVerify,
NextProtos: []string{
NextProtoDQ,
},
SessionTicketsDisabled: false,
},
})
if err != nil {
return nil, err
}
transport := quic.Transport{Conn: udp}
transport.SetCreatedConn(true) // auto close conn

View File

@@ -6,7 +6,7 @@ require (
github.com/bahlo/generic-list-go v0.2.0
github.com/coreos/go-iptables v0.8.0
github.com/dlclark/regexp2 v1.11.5
github.com/enfein/mieru/v3 v3.19.1
github.com/enfein/mieru/v3 v3.20.0
github.com/go-chi/chi/v5 v5.2.3
github.com/go-chi/render v1.0.3
github.com/gobwas/ws v1.4.0

View File

@@ -25,8 +25,8 @@ github.com/dlclark/regexp2 v1.11.5 h1:Q/sSnsKerHeCkc/jSTNq1oCm7KiVgUMZRDUoRu0JQZ
github.com/dlclark/regexp2 v1.11.5/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8=
github.com/ebitengine/purego v0.8.4 h1:CF7LEKg5FFOsASUj0+QwaXf8Ht6TlFxg09+S9wz0omw=
github.com/ebitengine/purego v0.8.4/go.mod h1:iIjxzd6CiRiOG0UyXP+V1+jWqUXVjPKLAI0mRfJZTmQ=
github.com/enfein/mieru/v3 v3.19.1 h1:19b9kgFC7oJXX9RLEO5Pi1gO6yek5cWlpK7IJVUoE8I=
github.com/enfein/mieru/v3 v3.19.1/go.mod h1:zJBUCsi5rxyvHM8fjFf+GLaEl4OEjjBXr1s5F6Qd3hM=
github.com/enfein/mieru/v3 v3.20.0 h1:1ob7pCIVSH5FYFAfYvim8isLW1vBOS4cFOUF9exJS38=
github.com/enfein/mieru/v3 v3.20.0/go.mod h1:zJBUCsi5rxyvHM8fjFf+GLaEl4OEjjBXr1s5F6Qd3hM=
github.com/ericlagergren/aegis v0.0.0-20250325060835-cd0defd64358 h1:kXYqH/sL8dS/FdoFjr12ePjnLPorPo2FsnrHNuXSDyo=
github.com/ericlagergren/aegis v0.0.0-20250325060835-cd0defd64358/go.mod h1:hkIFzoiIPZYxdFOOLyDho59b7SrDfo+w3h+yWdlg45I=
github.com/ericlagergren/polyval v0.0.0-20220411101811-e25bc10ba391 h1:8j2RH289RJplhA6WfdaPqzg1MjH2K8wX5e0uhAxrw2g=

View File

@@ -20,6 +20,7 @@ import (
tlsC "github.com/metacubex/mihomo/component/tls"
C "github.com/metacubex/mihomo/constant"
"github.com/metacubex/mihomo/log"
"github.com/metacubex/mihomo/ntp"
"github.com/metacubex/mihomo/tunnel/statistic"
"github.com/go-chi/chi/v5"
@@ -201,7 +202,7 @@ func startTLS(cfg *Config) {
}
log.Infoln("RESTful API tls listening at: %s", l.Addr().String())
tlsConfig := &tlsC.Config{}
tlsConfig := &tlsC.Config{Time: ntp.Now}
tlsConfig.NextProtos = []string{"h2", "http/1.1"}
tlsConfig.Certificates = []tlsC.Certificate{tlsC.UCertificate(cert)}

View File

@@ -17,6 +17,7 @@ import (
C "github.com/metacubex/mihomo/constant"
LC "github.com/metacubex/mihomo/listener/config"
"github.com/metacubex/mihomo/listener/sing"
"github.com/metacubex/mihomo/ntp"
"github.com/metacubex/mihomo/transport/anytls/padding"
"github.com/metacubex/mihomo/transport/anytls/session"
@@ -42,7 +43,7 @@ func New(config LC.AnyTLSServer, tunnel C.Tunnel, additions ...inbound.Addition)
}
}
tlsConfig := &tlsC.Config{}
tlsConfig := &tlsC.Config{Time: ntp.Now}
if config.Certificate != "" && config.PrivateKey != "" {
cert, err := ca.LoadTLSKeyPair(config.Certificate, config.PrivateKey, C.Path)
if err != nil {

View File

@@ -12,6 +12,7 @@ import (
authStore "github.com/metacubex/mihomo/listener/auth"
LC "github.com/metacubex/mihomo/listener/config"
"github.com/metacubex/mihomo/listener/reality"
"github.com/metacubex/mihomo/ntp"
)
type Listener struct {
@@ -65,7 +66,7 @@ func NewWithConfig(config LC.AuthServer, tunnel C.Tunnel, additions ...inbound.A
return nil, err
}
tlsConfig := &tlsC.Config{}
tlsConfig := &tlsC.Config{Time: ntp.Now}
var realityBuilder *reality.Builder
if config.Certificate != "" && config.PrivateKey != "" {

View File

@@ -39,7 +39,7 @@ var userUUID = utils.NewUUIDV4().String()
var tlsCertificate, tlsPrivateKey, tlsFingerprint, _ = ca.NewRandomTLSKeyPair(ca.KeyPairTypeP256)
var tlsConfigCert, _ = tls.X509KeyPair([]byte(tlsCertificate), []byte(tlsPrivateKey))
var tlsConfig = &tls.Config{Certificates: []tls.Certificate{tlsConfigCert}, NextProtos: []string{"h2", "http/1.1"}}
var tlsClientConfig, _ = ca.GetTLSConfig(nil, tlsFingerprint, "", "")
var tlsClientConfig, _ = ca.GetTLSConfig(ca.Option{Fingerprint: tlsFingerprint})
var realityPrivateKey, realityPublickey string
var realityDest = "itunes.apple.com"
var realityShortid = "10f897e26c4b9478"

View File

@@ -16,6 +16,7 @@ import (
"github.com/metacubex/mihomo/listener/http"
"github.com/metacubex/mihomo/listener/reality"
"github.com/metacubex/mihomo/listener/socks"
"github.com/metacubex/mihomo/ntp"
"github.com/metacubex/mihomo/transport/socks4"
"github.com/metacubex/mihomo/transport/socks5"
)
@@ -61,7 +62,7 @@ func NewWithConfig(config LC.AuthServer, tunnel C.Tunnel, additions ...inbound.A
return nil, err
}
tlsConfig := &tlsC.Config{}
tlsConfig := &tlsC.Config{Time: ntp.Now}
var realityBuilder *reality.Builder
if config.Certificate != "" && config.PrivateKey != "" {

View File

@@ -20,6 +20,7 @@ import (
LC "github.com/metacubex/mihomo/listener/config"
"github.com/metacubex/mihomo/listener/sing"
"github.com/metacubex/mihomo/log"
"github.com/metacubex/mihomo/ntp"
"github.com/metacubex/sing-quic/hysteria2"
@@ -61,6 +62,7 @@ func New(config LC.Hysteria2Server, tunnel C.Tunnel, additions ...inbound.Additi
return nil, err
}
tlsConfig := &tlsC.Config{
Time: ntp.Now,
MinVersion: tlsC.VersionTLS13,
}
tlsConfig.Certificates = []tlsC.Certificate{tlsC.UCertificate(cert)}

View File

@@ -15,6 +15,7 @@ import (
LC "github.com/metacubex/mihomo/listener/config"
"github.com/metacubex/mihomo/listener/reality"
"github.com/metacubex/mihomo/listener/sing"
"github.com/metacubex/mihomo/ntp"
"github.com/metacubex/mihomo/transport/gun"
"github.com/metacubex/mihomo/transport/vless/encryption"
mihomoVMess "github.com/metacubex/mihomo/transport/vmess"
@@ -75,7 +76,7 @@ func New(config LC.VlessServer, tunnel C.Tunnel, additions ...inbound.Addition)
}()
}
tlsConfig := &tlsC.Config{}
tlsConfig := &tlsC.Config{Time: ntp.Now}
var realityBuilder *reality.Builder
var httpServer http.Server

View File

@@ -76,7 +76,7 @@ func New(config LC.VmessServer, tunnel C.Tunnel, additions ...inbound.Addition)
sl = &Listener{false, config, nil, service}
tlsConfig := &tlsC.Config{}
tlsConfig := &tlsC.Config{Time: ntp.Now}
var realityBuilder *reality.Builder
var httpServer http.Server

View File

@@ -15,6 +15,7 @@ import (
authStore "github.com/metacubex/mihomo/listener/auth"
LC "github.com/metacubex/mihomo/listener/config"
"github.com/metacubex/mihomo/listener/reality"
"github.com/metacubex/mihomo/ntp"
"github.com/metacubex/mihomo/transport/socks4"
"github.com/metacubex/mihomo/transport/socks5"
)
@@ -60,7 +61,7 @@ func NewWithConfig(config LC.AuthServer, tunnel C.Tunnel, additions ...inbound.A
return nil, err
}
tlsConfig := &tlsC.Config{}
tlsConfig := &tlsC.Config{Time: ntp.Now}
var realityBuilder *reality.Builder
if config.Certificate != "" && config.PrivateKey != "" {

View File

@@ -15,6 +15,7 @@ import (
LC "github.com/metacubex/mihomo/listener/config"
"github.com/metacubex/mihomo/listener/reality"
"github.com/metacubex/mihomo/listener/sing"
"github.com/metacubex/mihomo/ntp"
"github.com/metacubex/mihomo/transport/gun"
"github.com/metacubex/mihomo/transport/shadowsocks/core"
"github.com/metacubex/mihomo/transport/socks5"
@@ -70,7 +71,7 @@ func New(config LC.TrojanServer, tunnel C.Tunnel, additions ...inbound.Addition)
}
sl = &Listener{false, config, nil, keys, pickCipher, h}
tlsConfig := &tlsC.Config{}
tlsConfig := &tlsC.Config{Time: ntp.Now}
var realityBuilder *reality.Builder
var httpServer http.Server

View File

@@ -14,6 +14,7 @@ import (
LC "github.com/metacubex/mihomo/listener/config"
"github.com/metacubex/mihomo/listener/sing"
"github.com/metacubex/mihomo/log"
"github.com/metacubex/mihomo/ntp"
"github.com/metacubex/mihomo/transport/socks5"
"github.com/metacubex/mihomo/transport/tuic"
@@ -53,6 +54,7 @@ func New(config LC.TuicServer, tunnel C.Tunnel, additions ...inbound.Addition) (
return nil, err
}
tlsConfig := &tlsC.Config{
Time: ntp.Now,
MinVersion: tlsC.VersionTLS13,
}
tlsConfig.Certificates = []tlsC.Certificate{tlsC.UCertificate(cert)}

View File

@@ -23,24 +23,21 @@ type Service struct {
ticker *time.Ticker
ctx context.Context
cancel context.CancelFunc
mu sync.RWMutex
offset time.Duration
offset atomic.Int64 // [time.Duration]
syncSystemTime bool
running bool
}
func ReCreateNTPService(server string, interval time.Duration, dialerProxy string, syncSystemTime bool) {
globalMu.Lock()
defer globalMu.Unlock()
service := globalSrv.Swap(nil)
if service != nil {
if service := globalSrv.Swap(nil); service != nil {
service.Stop()
}
if server == "" {
if server == "" || interval <= 0 {
return
}
ctx, cancel := context.WithCancel(context.Background())
service = &Service{
service := &Service{
server: M.ParseSocksaddr(server),
dialer: proxydialer.NewByNameSingDialer(dialerProxy, dialer.NewDialer()),
ticker: time.NewTicker(interval * time.Minute),
@@ -53,38 +50,17 @@ func ReCreateNTPService(server string, interval time.Duration, dialerProxy strin
}
func (srv *Service) Start() {
srv.mu.Lock()
defer srv.mu.Unlock()
log.Infoln("NTP service start, sync system time is %t", srv.syncSystemTime)
err := srv.update()
if err != nil {
log.Errorln("Initialize NTP time failed: %s", err)
return
}
srv.running = true
go srv.loopUpdate()
}
func (srv *Service) Stop() {
srv.mu.Lock()
defer srv.mu.Unlock()
if srv.running {
srv.ticker.Stop()
log.Infoln("NTP service stop")
srv.cancel()
srv.running = false
}
}
func (srv *Service) Offset() time.Duration {
if srv == nil {
return 0
}
srv.mu.RLock()
defer srv.mu.RUnlock()
if srv.running {
return srv.offset
}
return 0
return time.Duration(srv.offset.Load())
}
func (srv *Service) update() error {
@@ -93,6 +69,9 @@ func (srv *Service) update() error {
for i := 0; i < 3; i++ {
response, err = ntp.Exchange(srv.ctx, srv.dialer, srv.server)
if err != nil {
if srv.ctx.Err() != nil {
return nil
}
continue
}
offset := response.ClockOffset
@@ -101,9 +80,7 @@ func (srv *Service) update() error {
} else if offset < time.Duration(0) {
log.Infoln("System clock is behind NTP time by %s", -offset)
}
srv.mu.Lock()
srv.offset = offset
srv.mu.Unlock()
srv.offset.Store(int64(offset))
if srv.syncSystemTime {
timeNow := response.Time
syncErr := setSystemTime(timeNow)
@@ -120,23 +97,27 @@ func (srv *Service) update() error {
}
func (srv *Service) loopUpdate() {
defer srv.offset.Store(0)
defer srv.ticker.Stop()
for {
err := srv.update()
if err != nil {
log.Warnln("Sync time failed: %s", err)
}
select {
case <-srv.ctx.Done():
return
case <-srv.ticker.C:
}
err := srv.update()
if err != nil {
log.Warnln("Sync time failed: %s", err)
}
}
}
func Now() time.Time {
now := time.Now()
if offset := globalSrv.Load().Offset(); offset.Abs() > 0 {
if service := globalSrv.Load(); service != nil {
if offset := service.Offset(); offset.Abs() > 0 {
now = now.Add(offset)
}
}
return now
}

View File

@@ -57,15 +57,17 @@ func NewGostWebsocket(ctx context.Context, conn net.Conn, option *Option) (net.C
Headers: header,
}
var err error
if option.TLS {
config.TLS = true
tlsConfig := &tls.Config{
config.TLSConfig, err = ca.GetTLSConfig(ca.Option{
TLSConfig: &tls.Config{
ServerName: option.Host,
InsecureSkipVerify: option.SkipCertVerify,
NextProtos: []string{"http/1.1"},
}
var err error
config.TLSConfig, err = ca.GetSpecifiedFingerprintTLSConfig(tlsConfig, option.Fingerprint)
},
Fingerprint: option.Fingerprint,
})
if err != nil {
return nil, err
}
@@ -75,7 +77,6 @@ func NewGostWebsocket(ctx context.Context, conn net.Conn, option *Option) (net.C
}
}
var err error
conn, err = vmess.StreamWebsocketConn(ctx, conn, config)
if err != nil {
return nil, err

View File

@@ -6,9 +6,9 @@ import (
"encoding/binary"
"io"
"net"
"time"
"github.com/metacubex/mihomo/common/pool"
"github.com/metacubex/mihomo/ntp"
)
const (
@@ -145,7 +145,7 @@ func makeClientHelloMsg(data []byte, server string) []byte {
buf.Write([]byte{0x03, 0x03})
// random with timestamp, sid len, sid
binary.Write(buf, binary.BigEndian, uint32(time.Now().Unix()))
binary.Write(buf, binary.BigEndian, uint32(ntp.Now().Unix()))
buf.Write(random)
buf.WriteByte(32)
buf.Write(sessionID)

View File

@@ -33,22 +33,23 @@ type ShadowTLSOption struct {
}
func NewShadowTLS(ctx context.Context, conn net.Conn, option *ShadowTLSOption) (net.Conn, error) {
tlsConfig := &tls.Config{
tlsConfig, err := ca.GetTLSConfig(ca.Option{
TLSConfig: &tls.Config{
NextProtos: option.ALPN,
MinVersion: tls.VersionTLS12,
InsecureSkipVerify: option.SkipCertVerify,
ServerName: option.Host,
}
if option.Version == 1 {
tlsConfig.MaxVersion = tls.VersionTLS12 // ShadowTLS v1 only support TLS 1.2
}
var err error
tlsConfig, err = ca.GetSpecifiedFingerprintTLSConfig(tlsConfig, option.Fingerprint)
},
Fingerprint: option.Fingerprint,
})
if err != nil {
return nil, err
}
if option.Version == 1 {
tlsConfig.MaxVersion = tls.VersionTLS12 // ShadowTLS v1 only support TLS 1.2
}
tlsHandshake := uTLSHandshakeFunc(tlsConfig, option.ClientFingerprint, option.Version)
client, err := shadowtls.NewClient(shadowtls.ClientConfig{
Version: option.Version,

View File

@@ -7,9 +7,9 @@ import (
"encoding/binary"
"net"
"strings"
"time"
"github.com/metacubex/mihomo/common/pool"
"github.com/metacubex/mihomo/ntp"
"github.com/metacubex/mihomo/transport/ssr/tools"
"github.com/metacubex/randv2"
@@ -182,7 +182,7 @@ func packData(buf *bytes.Buffer, data []byte) {
}
func (t *tls12Ticket) packAuthData(buf *bytes.Buffer) {
binary.Write(buf, binary.BigEndian, uint32(time.Now().Unix()))
binary.Write(buf, binary.BigEndian, uint32(ntp.Now().Unix()))
tools.AppendRandBytes(buf, 18)
buf.Write(t.hmacSHA1(buf.Bytes()[buf.Len()-22:])[:10])
}

View File

@@ -8,10 +8,10 @@ import (
"encoding/base64"
"encoding/binary"
"sync"
"time"
"github.com/metacubex/mihomo/common/pool"
"github.com/metacubex/mihomo/log"
"github.com/metacubex/mihomo/ntp"
"github.com/metacubex/mihomo/transport/shadowsocks/core"
"github.com/metacubex/randv2"
@@ -49,7 +49,7 @@ func (a *authData) next() *authData {
}
func (a *authData) putAuthData(buf *bytes.Buffer) {
binary.Write(buf, binary.LittleEndian, uint32(time.Now().Unix()))
binary.Write(buf, binary.LittleEndian, uint32(ntp.Now().Unix()))
buf.Write(a.clientID[:])
binary.Write(buf, binary.LittleEndian, a.connectionID)
}
@@ -57,7 +57,7 @@ func (a *authData) putAuthData(buf *bytes.Buffer) {
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()))
binary.LittleEndian.PutUint32(encrypt, uint32(ntp.Now().Unix()))
copy(encrypt[4:], a.clientID[:])
binary.LittleEndian.PutUint32(encrypt[8:], a.connectionID)
binary.LittleEndian.PutUint16(encrypt[12:], uint16(paddings[0]))

View File

@@ -43,15 +43,17 @@ func NewV2rayObfs(ctx context.Context, conn net.Conn, option *Option) (net.Conn,
Headers: header,
}
var err error
if option.TLS {
config.TLS = true
tlsConfig := &tls.Config{
config.TLSConfig, err = ca.GetTLSConfig(ca.Option{
TLSConfig: &tls.Config{
ServerName: option.Host,
InsecureSkipVerify: option.SkipCertVerify,
NextProtos: []string{"http/1.1"},
}
var err error
config.TLSConfig, err = ca.GetSpecifiedFingerprintTLSConfig(tlsConfig, option.Fingerprint)
},
Fingerprint: option.Fingerprint,
})
if err != nil {
return nil, err
}
@@ -61,7 +63,6 @@ func NewV2rayObfs(ctx context.Context, conn net.Conn, option *Option) (net.Conn,
}
}
var err error
conn, err = vmess.StreamWebsocketConn(ctx, conn, config)
if err != nil {
return nil, err

View File

@@ -26,14 +26,14 @@ type ECHConfig struct {
}
func StreamTLSConn(ctx context.Context, conn net.Conn, cfg *TLSConfig) (net.Conn, error) {
tlsConfig := &tls.Config{
tlsConfig, err := ca.GetTLSConfig(ca.Option{
TLSConfig: &tls.Config{
ServerName: cfg.Host,
InsecureSkipVerify: cfg.SkipCertVerify,
NextProtos: cfg.NextProtos,
}
var err error
tlsConfig, err = ca.GetSpecifiedFingerprintTLSConfig(tlsConfig, cfg.FingerPrint)
},
Fingerprint: cfg.FingerPrint,
})
if err != nil {
return nil, err
}

View File

@@ -2,7 +2,7 @@
"manifest_version": 1,
"latest": {
"mihomo": "v1.19.13",
"mihomo_alpha": "alpha-909729c",
"mihomo_alpha": "alpha-4f4f13d",
"clash_rs": "v0.9.0",
"clash_premium": "2023-09-05-gdcc8d87",
"clash_rs_alpha": "0.9.0-alpha+sha.50f295d"
@@ -69,5 +69,5 @@
"linux-armv7hf": "clash-armv7-unknown-linux-gnueabihf"
}
},
"updated_at": "2025-09-11T22:20:53.894Z"
"updated_at": "2025-09-12T22:20:41.506Z"
}

View File

@@ -2,6 +2,65 @@
All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines.
## [2.43.0](https://github.com/filebrowser/filebrowser/compare/v2.42.5...v2.43.0) (2025-09-13)
### Features
* "save changes" button to discard changes dialog ([84e8632](https://github.com/filebrowser/filebrowser/commit/84e8632b98e315bfef2da77dd7d1049daec99241))
* Translate frontend/src/i18n/en.json in es ([571ce6c](https://github.com/filebrowser/filebrowser/commit/571ce6cb0d7c8725d1cc1a3238ea506ddc72b060))
* Translate frontend/src/i18n/en.json in fr ([6b1fa87](https://github.com/filebrowser/filebrowser/commit/6b1fa87ad38ebbb1a9c5d0e5fc88ba796c148bcf))
* Updates for project File Browser ([#5427](https://github.com/filebrowser/filebrowser/issues/5427)) ([8950585](https://github.com/filebrowser/filebrowser/commit/89505851414bfcee6b9ff02087eb4cec51c330f6))
### Bug Fixes
* optimize markdown preview height ([783503a](https://github.com/filebrowser/filebrowser/commit/783503aece7fca9e26f7e849b0e7478aba976acb))
### Reverts
* build(deps): bump github.com/ulikunitz/xz from 0.5.12 to 0.5.14 ([0769265](https://github.com/filebrowser/filebrowser/commit/07692653ffe0ea5e517e6dc1fd3961172e931843))
### Build
* **deps-dev:** bump vite from 6.1.6 to 6.3.6 in /frontend ([36c6cc2](https://github.com/filebrowser/filebrowser/commit/36c6cc203e10947439519a0413d5817921a1690d))
* **deps:** bump github.com/go-viper/mapstructure/v2 in /tools ([280fa56](https://github.com/filebrowser/filebrowser/commit/280fa562a67824887ae6e2530a3b73739d6e1bb4))
* **deps:** bump github.com/ulikunitz/xz from 0.5.12 to 0.5.14 ([950028a](https://github.com/filebrowser/filebrowser/commit/950028abebe2898bac4ecfd8715c0967246310cb))
### Refactorings
* to use strings.Lines ([b482a9b](https://github.com/filebrowser/filebrowser/commit/b482a9bf0d292ec6542d2145a4408971e4c985f1))
## [2.43.0](https://github.com/filebrowser/filebrowser/compare/v2.42.5...v2.43.0) (2025-09-13)
### Features
* "save changes" button to discard changes dialog ([84e8632](https://github.com/filebrowser/filebrowser/commit/84e8632b98e315bfef2da77dd7d1049daec99241))
* Translate frontend/src/i18n/en.json in es ([571ce6c](https://github.com/filebrowser/filebrowser/commit/571ce6cb0d7c8725d1cc1a3238ea506ddc72b060))
* Translate frontend/src/i18n/en.json in fr ([6b1fa87](https://github.com/filebrowser/filebrowser/commit/6b1fa87ad38ebbb1a9c5d0e5fc88ba796c148bcf))
* Updates for project File Browser ([#5427](https://github.com/filebrowser/filebrowser/issues/5427)) ([8950585](https://github.com/filebrowser/filebrowser/commit/89505851414bfcee6b9ff02087eb4cec51c330f6))
### Bug Fixes
* optimize markdown preview height ([783503a](https://github.com/filebrowser/filebrowser/commit/783503aece7fca9e26f7e849b0e7478aba976acb))
### Build
* **deps-dev:** bump vite from 6.1.6 to 6.3.6 in /frontend ([36c6cc2](https://github.com/filebrowser/filebrowser/commit/36c6cc203e10947439519a0413d5817921a1690d))
* **deps:** bump github.com/go-viper/mapstructure/v2 in /tools ([280fa56](https://github.com/filebrowser/filebrowser/commit/280fa562a67824887ae6e2530a3b73739d6e1bb4))
* **deps:** bump github.com/ulikunitz/xz from 0.5.12 to 0.5.14 ([950028a](https://github.com/filebrowser/filebrowser/commit/950028abebe2898bac4ecfd8715c0967246310cb))
### Refactorings
* to use strings.Lines ([b482a9b](https://github.com/filebrowser/filebrowser/commit/b482a9bf0d292ec6542d2145a4408971e4c985f1))
### [2.42.5](https://github.com/filebrowser/filebrowser/compare/v2.42.4...v2.42.5) (2025-08-16)

View File

@@ -123,7 +123,7 @@ func (a *HookAuth) GetValues(s string) {
s = strings.ReplaceAll(s, "\r\n", "\n")
// iterate input lines
for _, val := range strings.Split(s, "\n") {
for val := range strings.Lines(s) {
v := strings.SplitN(val, "=", 2)
// skips non key and value format

View File

@@ -11,17 +11,26 @@
@click="closeHovers"
:aria-label="$t('buttons.cancel')"
:title="$t('buttons.cancel')"
tabindex="2"
tabindex="3"
>
{{ $t("buttons.cancel") }}
</button>
<button
class="button button--flat button--blue"
@click="saveAndClose"
:aria-label="$t('buttons.saveChanges')"
:title="$t('buttons.saveChanges')"
tabindex="1"
>
{{ $t("buttons.saveChanges") }}
</button>
<button
id="focus-prompt"
@click="currentPrompt.confirm"
class="button button--flat button--red"
:aria-label="$t('buttons.discardChanges')"
:title="$t('buttons.discardChanges')"
tabindex="1"
tabindex="2"
>
{{ $t("buttons.discardChanges") }}
</button>
@@ -40,6 +49,12 @@ export default {
},
methods: {
...mapActions(useLayoutStore, ["closeHovers"]),
saveAndClose() {
if (this.currentPrompt?.saveAction) {
this.currentPrompt.saveAction();
}
this.closeHovers();
},
},
};
</script>

View File

@@ -1,6 +1,4 @@
.md_preview {
overflow-y: auto;
max-height: 80vh;
padding: 1rem;
border: 1px solid #000;
font-size: 20px;
@@ -9,5 +7,5 @@
#preview-container {
overflow: auto;
max-height: 80vh; /* Match the max-height of md_preview for scrolling */
flex: 1;
}

View File

@@ -42,7 +42,8 @@
"update": "Update",
"upload": "Upload",
"openFile": "Open file",
"discardChanges": "Discard"
"discardChanges": "Discard",
"saveChanges": "Save changes"
},
"download": {
"downloadFile": "Download File",

View File

@@ -7,7 +7,7 @@
"copy": "Copiar",
"copyFile": "Copiar archivo",
"copyToClipboard": "Copiar al portapapeles",
"copyDownloadLinkToClipboard": "Copy download link to clipboard",
"copyDownloadLinkToClipboard": "Copiar enlace de descarga al portapapeles",
"create": "Crear",
"delete": "Borrar",
"download": "Descargar",

View File

@@ -41,6 +41,7 @@ export const useLayoutStore = defineStore("layout", {
prompt: value,
confirm: null,
action: undefined,
saveAction: undefined,
props: null,
close: null,
});
@@ -51,6 +52,7 @@ export const useLayoutStore = defineStore("layout", {
prompt: value.prompt,
confirm: value?.confirm,
action: value?.action,
saveAction: value?.saveAction,
props: value?.props,
close: value?.close,
});

View File

@@ -2,6 +2,7 @@ interface PopupProps {
prompt: string;
confirm?: any;
action?: PopupAction;
saveAction?: () => void;
props?: any;
close?: (() => Promise<string>) | null;
}

View File

@@ -1,5 +1,5 @@
<template>
<div id="editor-container" @wheel.prevent.stop>
<div id="editor-container">
<header-bar>
<action icon="close" :label="t('buttons.close')" @action="close()" />
<title>{{ fileStore.req?.name ?? "" }}</title>
@@ -97,7 +97,6 @@ const isMarkdownFile =
onMounted(() => {
window.addEventListener("keydown", keyEvent);
window.addEventListener("wheel", handleScroll);
window.addEventListener("beforeunload", handlePageChange);
const fileContent = fileStore.req?.content || "";
@@ -111,13 +110,6 @@ onMounted(() => {
console.error("Failed to convert content to HTML:", error);
previewContent.value = "";
}
const previewContainer = document.getElementById("preview-container");
if (previewContainer) {
previewContainer.addEventListener("wheel", handleScroll, {
capture: true,
});
}
}
});
@@ -148,7 +140,6 @@ onMounted(() => {
onBeforeUnmount(() => {
window.removeEventListener("keydown", keyEvent);
window.removeEventListener("wheel", handleScroll);
window.removeEventListener("beforeunload", handlePageChange);
editor.value?.destroy();
});
@@ -166,6 +157,10 @@ onBeforeRouteUpdate((to, from, next) => {
event.preventDefault();
next();
},
saveAction: async () => {
await save();
next();
},
});
});
@@ -186,13 +181,6 @@ const keyEvent = (event: KeyboardEvent) => {
save();
};
const handleScroll = (event: WheelEvent) => {
const editorContainer = document.getElementById("preview-container");
if (editorContainer) {
editorContainer.scrollTop += event.deltaY;
}
};
const handlePageChange = (event: BeforeUnloadEvent) => {
if (!editor.value?.session.getUndoManager().isClean()) {
event.preventDefault();

View File

@@ -65,7 +65,7 @@ require (
github.com/sourcegraph/conc v0.3.0 // indirect
github.com/spf13/cast v1.9.2 // indirect
github.com/subosito/gotenv v1.6.0 // indirect
github.com/ulikunitz/xz v0.5.14 // indirect
github.com/ulikunitz/xz v0.5.12 // indirect
github.com/yusufpapurcu/wmi v1.2.4 // indirect
go.uber.org/multierr v1.11.0 // indirect
go4.org v0.0.0-20230225012048-214862532bf5 // indirect

View File

@@ -232,8 +232,8 @@ github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSW
github.com/tomasen/realip v0.0.0-20180522021738-f0c99a92ddce h1:fb190+cK2Xz/dvi9Hv8eCYJYvIGUTN2/KLq1pT6CjEc=
github.com/tomasen/realip v0.0.0-20180522021738-f0c99a92ddce/go.mod h1:o8v6yHRoik09Xen7gje4m9ERNah1d1PPsVq1VEx9vE4=
github.com/ulikunitz/xz v0.5.8/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14=
github.com/ulikunitz/xz v0.5.14 h1:uv/0Bq533iFdnMHZdRBTOlaNMdb1+ZxXIlHDZHIHcvg=
github.com/ulikunitz/xz v0.5.14/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14=
github.com/ulikunitz/xz v0.5.12 h1:37Nm15o69RwBkXM0J6A5OlE67RZTfzUxTj8fB3dfcsc=
github.com/ulikunitz/xz v0.5.12/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14=
github.com/vmihailenco/msgpack v4.0.4+incompatible h1:dSLoQfGFAo3F6OoNhwUmLwVgaUXK79GlxNBwueZn0xI=
github.com/vmihailenco/msgpack v4.0.4+incompatible/go.mod h1:fy3FlTQTDXWkZ7Bh6AcGMlsjHatGryHQYUTf1ShIgkk=
github.com/xyproto/randomstring v1.0.5 h1:YtlWPoRdgMu3NZtP45drfy1GKoojuR7hmRcnhZqKjWU=

View File

@@ -7,14 +7,12 @@
include $(TOPDIR)/rules.mk
PKG_NAME:=erofs-utils
PKG_VERSION:=1.8.9
PKG_VERSION:=1.8.10
PKG_RELEASE:=1
PKG_SOURCE_PROTO:=git
PKG_SOURCE_URL=https://git.kernel.org/pub/scm/linux/kernel/git/xiang/erofs-utils.git
PKG_MIRROR_HASH:=dfeeca4e3b1337cf05d2b40f6f3956601b98fba0b6a8d7363071ae366a2020e1
PKG_SOURCE_DATE:=2025-06-26
PKG_SOURCE_VERSION:=81169bf3cfd26b8f2b3aa3b20da23971168a90a9
PKG_SOURCE:=$(PKG_NAME)-$(PKG_VERSION).tar.gz
PKG_SOURCE_URL=https://git.kernel.org/pub/scm/linux/kernel/git/xiang/erofs-utils.git/snapshot/
PKG_HASH:=05eb4edebe11decce6ecb34e98d2f80c8cd283c2f2967d8ba7efd58418570514
PKG_FIXUP:=autoreconf

View File

@@ -3,7 +3,7 @@ on: [push, pull_request, workflow_dispatch]
jobs:
run-test:
runs-on: ubuntu-latest
timeout-minutes: 10
timeout-minutes: 15
steps:
- name: Check out repository code
uses: actions/checkout@v4

View File

@@ -32,7 +32,7 @@ PROJECT_NAME=$(shell basename "${ROOT}")
# - pkg/version/current.go
#
# Use `tools/bump_version.sh` script to change all those files at one shot.
VERSION="3.19.2"
VERSION="3.20.0"
# Build binaries and installation packages.
.PHONY: build

View File

@@ -74,6 +74,10 @@ type ClientNetworkService interface {
// DialContext returns a new proxy connection to reach the destination.
// It uses the dialer in ClientConfig to connect to a proxy server endpoint.
//
// If HandshakeMode in ClientConfig is HANDSHAKE_NO_WAIT, handshake is performed
// upon the first write to the network connection. Otherwise, handshake is
// performed before this method returns.
//
// This is a streaming based proxy connection. If the destination is a packet
// endpoint, packets are encapsulated in the streaming connection.
//
@@ -84,6 +88,7 @@ type ClientNetworkService interface {
// ClientConfig stores proxy client configuration.
type ClientConfig struct {
// Main configuration.
Profile *appctlpb.ClientProfile
// A dialer to connect to proxy server via stream-oriented network connections.

View File

@@ -246,6 +246,9 @@ func (mc *mieruClient) DialContext(ctx context.Context, addr net.Addr) (net.Conn
if err != nil {
return nil, err
}
if mc.config.Profile.GetHandshakeMode() == appctlpb.HandshakeMode_HANDSHAKE_NO_WAIT {
return apicommon.NewEarlyConn(conn, netAddrSpec), nil
}
return mc.dialPostHandshake(conn, netAddrSpec)
}

View File

@@ -1,5 +1,5 @@
Package: mieru
Version: 3.19.2
Version: 3.20.0
Section: net
Priority: optional
Architecture: amd64

View File

@@ -1,5 +1,5 @@
Name: mieru
Version: 3.19.2
Version: 3.20.0
Release: 1%{?dist}
Summary: Mieru proxy client
License: GPLv3+

View File

@@ -1,5 +1,5 @@
Package: mieru
Version: 3.19.2
Version: 3.20.0
Section: net
Priority: optional
Architecture: arm64

View File

@@ -1,5 +1,5 @@
Name: mieru
Version: 3.19.2
Version: 3.20.0
Release: 1%{?dist}
Summary: Mieru proxy client
License: GPLv3+

View File

@@ -1,5 +1,5 @@
Package: mita
Version: 3.19.2
Version: 3.20.0
Section: net
Priority: optional
Architecture: amd64

View File

@@ -1,5 +1,5 @@
Name: mita
Version: 3.19.2
Version: 3.20.0
Release: 1%{?dist}
Summary: Mieru proxy server
License: GPLv3+

View File

@@ -1,5 +1,5 @@
Package: mita
Version: 3.19.2
Version: 3.20.0
Section: net
Priority: optional
Architecture: arm64

View File

@@ -1,5 +1,5 @@
Name: mita
Version: 3.19.2
Version: 3.20.0
Release: 1%{?dist}
Summary: Mieru proxy server
License: GPLv3+

View File

@@ -18,32 +18,32 @@ Or you can manually install and configure proxy server using the steps below.
```sh
# Debian / Ubuntu - X86_64
curl -LSO https://github.com/enfein/mieru/releases/download/v3.19.2/mita_3.19.2_amd64.deb
curl -LSO https://github.com/enfein/mieru/releases/download/v3.20.0/mita_3.20.0_amd64.deb
# Debian / Ubuntu - ARM 64
curl -LSO https://github.com/enfein/mieru/releases/download/v3.19.2/mita_3.19.2_arm64.deb
curl -LSO https://github.com/enfein/mieru/releases/download/v3.20.0/mita_3.20.0_arm64.deb
# RedHat / CentOS / Rocky Linux - X86_64
curl -LSO https://github.com/enfein/mieru/releases/download/v3.19.2/mita-3.19.2-1.x86_64.rpm
curl -LSO https://github.com/enfein/mieru/releases/download/v3.20.0/mita-3.20.0-1.x86_64.rpm
# RedHat / CentOS / Rocky Linux - ARM 64
curl -LSO https://github.com/enfein/mieru/releases/download/v3.19.2/mita-3.19.2-1.aarch64.rpm
curl -LSO https://github.com/enfein/mieru/releases/download/v3.20.0/mita-3.20.0-1.aarch64.rpm
```
## Install mita package
```sh
# Debian / Ubuntu - X86_64
sudo dpkg -i mita_3.19.2_amd64.deb
sudo dpkg -i mita_3.20.0_amd64.deb
# Debian / Ubuntu - ARM 64
sudo dpkg -i mita_3.19.2_arm64.deb
sudo dpkg -i mita_3.20.0_arm64.deb
# RedHat / CentOS / Rocky Linux - X86_64
sudo rpm -Uvh --force mita-3.19.2-1.x86_64.rpm
sudo rpm -Uvh --force mita-3.20.0-1.x86_64.rpm
# RedHat / CentOS / Rocky Linux - ARM 64
sudo rpm -Uvh --force mita-3.19.2-1.aarch64.rpm
sudo rpm -Uvh --force mita-3.20.0-1.aarch64.rpm
```
Those instructions can also be used to upgrade the version of mita software package.

View File

@@ -18,32 +18,32 @@ sudo python3 setup.py --lang=zh
```sh
# Debian / Ubuntu - X86_64
curl -LSO https://github.com/enfein/mieru/releases/download/v3.19.2/mita_3.19.2_amd64.deb
curl -LSO https://github.com/enfein/mieru/releases/download/v3.20.0/mita_3.20.0_amd64.deb
# Debian / Ubuntu - ARM 64
curl -LSO https://github.com/enfein/mieru/releases/download/v3.19.2/mita_3.19.2_arm64.deb
curl -LSO https://github.com/enfein/mieru/releases/download/v3.20.0/mita_3.20.0_arm64.deb
# RedHat / CentOS / Rocky Linux - X86_64
curl -LSO https://github.com/enfein/mieru/releases/download/v3.19.2/mita-3.19.2-1.x86_64.rpm
curl -LSO https://github.com/enfein/mieru/releases/download/v3.20.0/mita-3.20.0-1.x86_64.rpm
# RedHat / CentOS / Rocky Linux - ARM 64
curl -LSO https://github.com/enfein/mieru/releases/download/v3.19.2/mita-3.19.2-1.aarch64.rpm
curl -LSO https://github.com/enfein/mieru/releases/download/v3.20.0/mita-3.20.0-1.aarch64.rpm
```
## 安装 mita 软件包
```sh
# Debian / Ubuntu - X86_64
sudo dpkg -i mita_3.19.2_amd64.deb
sudo dpkg -i mita_3.20.0_amd64.deb
# Debian / Ubuntu - ARM 64
sudo dpkg -i mita_3.19.2_arm64.deb
sudo dpkg -i mita_3.20.0_arm64.deb
# RedHat / CentOS / Rocky Linux - X86_64
sudo rpm -Uvh --force mita-3.19.2-1.x86_64.rpm
sudo rpm -Uvh --force mita-3.20.0-1.x86_64.rpm
# RedHat / CentOS / Rocky Linux - ARM 64
sudo rpm -Uvh --force mita-3.19.2-1.aarch64.rpm
sudo rpm -Uvh --force mita-3.20.0-1.aarch64.rpm
```
上述指令也可以用来升级 mita 软件包的版本。

View File

@@ -16,5 +16,5 @@
package version
const (
AppVersion = "3.19.2"
AppVersion = "3.20.0"
)

View File

@@ -41,6 +41,7 @@ var (
serverIP = flag.String("server_ip", "", "IP address of mieru proxy server")
serverPort = flag.Int("server_port", 0, "Port number of mieru proxy server")
serverProtocol = flag.String("server_protocol", "TCP", "Transport protocol: TCP or UDP")
handshakeMode = flag.String("handshake_mode", "HANDSHAKE_STANDARD", "Handshake mode: HANDSHAKE_STANDARD or HANDSHAKE_NO_WAIT")
debug = flag.Bool("debug", false, "Display debug messages")
)
@@ -73,6 +74,15 @@ func main() {
default:
panic(fmt.Sprintf("Transport protocol %q is invalid", *serverProtocol))
}
var handshakeModeConfig appctlpb.HandshakeMode
switch *handshakeMode {
case "HANDSHAKE_STANDARD":
handshakeModeConfig = appctlpb.HandshakeMode_HANDSHAKE_STANDARD
case "HANDSHAKE_NO_WAIT":
handshakeModeConfig = appctlpb.HandshakeMode_HANDSHAKE_NO_WAIT
default:
panic(fmt.Sprintf("Handshake mode %q is invalid", *handshakeMode))
}
c := client.NewClient()
if err := c.Store(&client.ClientConfig{
@@ -94,6 +104,7 @@ func main() {
},
},
Mtu: proto.Int32(1400),
HandshakeMode: &handshakeModeConfig,
},
}); err != nil {
panic(err)

View File

@@ -35,9 +35,18 @@ sleep 1
./mita run &
sleep 1
# Start mieru API client.
# Start mieru API clients.
./exampleapiclient -port=1081 -username=baozi -password=manlianpenfen \
-server_ip=127.0.0.1 -server_port=8964 &
-server_ip=127.0.0.1 -server_port=8964 -server_protocol=TCP &
sleep 1
./exampleapiclient -port=1082 -username=baozi -password=manlianpenfen \
-server_ip=127.0.0.1 -server_port=8964 -server_protocol=UDP &
sleep 1
./exampleapiclient -port=1083 -username=baozi -password=manlianpenfen \
-server_ip=127.0.0.1 -server_port=8964 -server_protocol=TCP -handshake_mode=HANDSHAKE_NO_WAIT &
sleep 1
./exampleapiclient -port=1084 -username=baozi -password=manlianpenfen \
-server_ip=127.0.0.1 -server_port=8964 -server_protocol=UDP -handshake_mode=HANDSHAKE_NO_WAIT &
sleep 1
# Run TCP test.

View File

@@ -105,6 +105,19 @@ if [ "$?" -ne "0" ]; then
exit 1
fi
sleep 1
echo ">>> socks5 UDP associate - TCP with API client - handshake no wait <<<"
./socksudpclient -dst_host=127.0.0.1 -dst_port=9090 \
-local_proxy_host=127.0.0.1 -local_proxy_port=1083 \
-interval_ms=10 -num_request=100 -num_conn=60
if [ "$?" -ne "0" ]; then
print_mieru_client_log
print_mieru_client_thread_dump
print_mieru_server_thread_dump
echo "Test UDP associate - TCP with API client (handshake no wait) failed."
exit 1
fi
# Collect profile with UDP associate.
./mieru get heap-profile /test/mieru.associate.heap.gz
./mita get heap-profile /test/mita.associate.heap.gz

View File

@@ -149,7 +149,7 @@ fi
# Start testing.
sleep 2
echo ">>> socks5 - new connections - TCP <<<"
echo ">>> socks5 - new connections - TCP - handshake no wait <<<"
./sockshttpclient -dst_host=127.0.0.1 -dst_port=8080 \
-local_proxy_host=127.0.0.1 -local_proxy_port=1080 \
-test_case=new_conn -num_request=3000
@@ -161,6 +161,16 @@ if [ "$?" -ne "0" ]; then
exit 1
fi
sleep 1
echo ">>> socks5 - new connections with API client - TCP - handshake no wait <<<"
./sockshttpclient -dst_host=127.0.0.1 -dst_port=8080 \
-local_proxy_host=127.0.0.1 -local_proxy_port=1083 \
-test_case=new_conn -num_request=3000
if [ "$?" -ne "0" ]; then
echo "TCP - test socks5 new_conn (handshake no wait) with API client failed."
exit 1
fi
# Stop mieru client.
./mieru stop
if [[ "$?" -ne 0 ]]; then

View File

@@ -55,10 +55,6 @@ if [[ "$?" -ne 0 ]]; then
fi
./mieru profile cpu start /test/mieru.udp.cpu.gz
# Start mieru API client.
./exampleapiclient -port=1082 -username=baozi -password=manlianpenfen \
-server_ip=127.0.0.1 -server_port=8964 -server_protocol=UDP &
# Start testing.
sleep 2
echo ">>> socks5 - new connections - UDP <<<"
@@ -153,7 +149,7 @@ fi
# Start testing.
sleep 2
echo ">>> socks5 - new connections - UDP <<<"
echo ">>> socks5 - new connections - UDP - handshake no wait <<<"
./sockshttpclient -dst_host=127.0.0.1 -dst_port=8080 \
-local_proxy_host=127.0.0.1 -local_proxy_port=1080 \
-test_case=new_conn -num_request=3000
@@ -165,6 +161,16 @@ if [ "$?" -ne "0" ]; then
exit 1
fi
sleep 1
echo ">>> socks5 - new connections with API client - UDP - handshake no wait <<<"
./sockshttpclient -dst_host=127.0.0.1 -dst_port=8080 \
-local_proxy_host=127.0.0.1 -local_proxy_port=1084 \
-test_case=new_conn -num_request=3000
if [ "$?" -ne "0" ]; then
echo "UDP - test socks5 new_conn (handshake no wait) with API client failed."
exit 1
fi
# Stop mieru client.
./mieru stop
if [[ "$?" -ne 0 ]]; then

View File

@@ -22,6 +22,7 @@ WORKDIR /test
# Copy binaries, data and test script into the container.
COPY mihomo mita httpserver sockshttpclient socksudpclient udpserver \
test/deploy/mihomo/mihomo-config.yaml \
test/deploy/mihomo/mihomo-config-no-wait.yaml \
test/deploy/mihomo/server_tcp.json \
test/deploy/mihomo/libtest.sh \
test/deploy/mihomo/test_tcp.sh \

View File

@@ -0,0 +1,19 @@
dns:
enable: true
nameserver:
- 8.8.8.8
log-level: warning
mixed-port: 1081
mode: rule
proxies:
- name: mieru
type: mieru
server: 127.0.0.1
port: 8964
transport: TCP
udp: true
username: baozi
password: manlianpenfen
handshake-mode: HANDSHAKE_NO_WAIT
rules:
- MATCH,mieru

View File

@@ -40,6 +40,8 @@ fi
# Start mihomo.
./mihomo -f mihomo-config.yaml &
sleep 1
./mihomo -f mihomo-config-no-wait.yaml &
sleep 1
# Start testing.
sleep 2
@@ -86,6 +88,50 @@ if [ "$?" -ne "0" ]; then
exit 1
fi
sleep 1
echo ">>> socks5 - new connections - TCP - handshake no wait <<<"
./sockshttpclient -dst_host=127.0.0.1 -dst_port=8080 \
-local_proxy_host=127.0.0.1 -local_proxy_port=1081 \
-test_case=new_conn -num_request=3000
if [ "$?" -ne "0" ]; then
print_mieru_server_thread_dump
echo "TCP - test socks5 new_conn (handshake no wait) failed."
exit 1
fi
sleep 1
echo ">>> http - new connections - TCP - handshake no wait <<<"
./sockshttpclient -proxy_mode=http -dst_host=127.0.0.1 -dst_port=8080 \
-local_http_host=127.0.0.1 -local_http_port=1081 \
-test_case=new_conn -num_request=1000
if [ "$?" -ne "0" ]; then
print_mieru_server_thread_dump
echo "TCP - test HTTP new_conn (handshake no wait) failed."
exit 1
fi
sleep 1
echo ">>> socks5 - reuse one connection - TCP - handshake no wait <<<"
./sockshttpclient -dst_host=127.0.0.1 -dst_port=8080 \
-local_proxy_host=127.0.0.1 -local_proxy_port=1081 \
-test_case=reuse_conn -test_time_sec=30
if [ "$?" -ne "0" ]; then
print_mieru_server_thread_dump
echo "TCP - test socks5 reuse_conn (handshake no wait) failed."
exit 1
fi
sleep 1
echo ">>> socks5 UDP associate - TCP - handshake no wait <<<"
./socksudpclient -dst_host=127.0.0.1 -dst_port=9090 \
-local_proxy_host=127.0.0.1 -local_proxy_port=1081 \
-interval_ms=10 -num_request=100 -num_conn=60
if [ "$?" -ne "0" ]; then
print_mieru_server_thread_dump
echo "TCP - test socks5 udp_associate (handshake no wait) failed."
exit 1
fi
# Print metrics and memory statistics.
print_mieru_server_metrics
sleep 1

View File

@@ -2,7 +2,6 @@ package adapter
import (
"context"
"crypto/tls"
"encoding/json"
"fmt"
"net"
@@ -236,6 +235,11 @@ func (p *Proxy) URLTest(ctx context.Context, url string, expectedStatus utils.In
}
req = req.WithContext(ctx)
tlsConfig, err := ca.GetTLSConfig(ca.Option{})
if err != nil {
return
}
transport := &http.Transport{
DialContext: func(context.Context, string, string) (net.Conn, error) {
return instance, nil
@@ -245,7 +249,7 @@ func (p *Proxy) URLTest(ctx context.Context, url string, expectedStatus utils.In
IdleConnTimeout: 90 * time.Second,
TLSHandshakeTimeout: 10 * time.Second,
ExpectContinueTimeout: 1 * time.Second,
TLSClientConfig: ca.GetGlobalTLSConfig(&tls.Config{}),
TLSClientConfig: tlsConfig,
}
client := http.Client{

View File

@@ -167,10 +167,13 @@ func NewHttp(option HttpOption) (*Http, error) {
sni = option.SNI
}
var err error
tlsConfig, err = ca.GetSpecifiedFingerprintTLSConfig(&tls.Config{
tlsConfig, err = ca.GetTLSConfig(ca.Option{
TLSConfig: &tls.Config{
InsecureSkipVerify: option.SkipCertVerify,
ServerName: sni,
}, option.Fingerprint)
},
Fingerprint: option.Fingerprint,
})
if err != nil {
return nil, err
}

View File

@@ -160,14 +160,16 @@ func NewHysteria(option HysteriaOption) (*Hysteria, error) {
serverName = option.SNI
}
tlsConfig := &tls.Config{
tlsConfig, err := ca.GetTLSConfig(ca.Option{
TLSConfig: &tls.Config{
ServerName: serverName,
InsecureSkipVerify: option.SkipCertVerify,
MinVersion: tls.VersionTLS13,
}
var err error
tlsConfig, err = ca.GetTLSConfig(tlsConfig, option.Fingerprint, option.CustomCA, option.CustomCAString)
},
Fingerprint: option.Fingerprint,
CustomCA: option.CustomCA,
CustomCAString: option.CustomCAString,
})
if err != nil {
return nil, err
}

View File

@@ -141,14 +141,16 @@ func NewHysteria2(option Hysteria2Option) (*Hysteria2, error) {
serverName = option.SNI
}
tlsConfig := &tls.Config{
tlsConfig, err := ca.GetTLSConfig(ca.Option{
TLSConfig: &tls.Config{
ServerName: serverName,
InsecureSkipVerify: option.SkipCertVerify,
MinVersion: tls.VersionTLS13,
}
var err error
tlsConfig, err = ca.GetTLSConfig(tlsConfig, option.Fingerprint, option.CustomCA, option.CustomCAString)
},
Fingerprint: option.Fingerprint,
CustomCA: option.CustomCA,
CustomCAString: option.CustomCAString,
})
if err != nil {
return nil, err
}

View File

@@ -193,13 +193,14 @@ func (ss *Socks5) clientHandshakeContext(ctx context.Context, c net.Conn, addr s
func NewSocks5(option Socks5Option) (*Socks5, error) {
var tlsConfig *tls.Config
if option.TLS {
tlsConfig = &tls.Config{
var err error
tlsConfig, err = ca.GetTLSConfig(ca.Option{
TLSConfig: &tls.Config{
InsecureSkipVerify: option.SkipCertVerify,
ServerName: option.Server,
}
var err error
tlsConfig, err = ca.GetSpecifiedFingerprintTLSConfig(tlsConfig, option.Fingerprint)
},
Fingerprint: option.Fingerprint,
})
if err != nil {
return nil, err
}

View File

@@ -100,14 +100,15 @@ func (t *Trojan) StreamConnContext(ctx context.Context, c net.Conn, metadata *C.
}
wsOpts.TLS = true
tlsConfig := &tls.Config{
wsOpts.TLSConfig, err = ca.GetTLSConfig(ca.Option{
TLSConfig: &tls.Config{
NextProtos: alpn,
MinVersion: tls.VersionTLS12,
InsecureSkipVerify: t.option.SkipCertVerify,
ServerName: t.option.SNI,
}
wsOpts.TLSConfig, err = ca.GetSpecifiedFingerprintTLSConfig(tlsConfig, t.option.Fingerprint)
},
Fingerprint: t.option.Fingerprint,
})
if err != nil {
return nil, err
}
@@ -363,15 +364,15 @@ func NewTrojan(option TrojanOption) (*Trojan, error) {
return c, nil
}
tlsConfig := &tls.Config{
tlsConfig, err := ca.GetTLSConfig(ca.Option{
TLSConfig: &tls.Config{
NextProtos: option.ALPN,
MinVersion: tls.VersionTLS12,
InsecureSkipVerify: option.SkipCertVerify,
ServerName: option.SNI,
}
var err error
tlsConfig, err = ca.GetSpecifiedFingerprintTLSConfig(tlsConfig, option.Fingerprint)
},
Fingerprint: option.Fingerprint,
})
if err != nil {
return nil, err
}

View File

@@ -161,17 +161,20 @@ func (t *Tuic) ProxyInfo() C.ProxyInfo {
func NewTuic(option TuicOption) (*Tuic, error) {
addr := net.JoinHostPort(option.Server, strconv.Itoa(option.Port))
serverName := option.Server
tlsConfig := &tls.Config{
if option.SNI != "" {
serverName = option.SNI
}
tlsConfig, err := ca.GetTLSConfig(ca.Option{
TLSConfig: &tls.Config{
ServerName: serverName,
InsecureSkipVerify: option.SkipCertVerify,
MinVersion: tls.VersionTLS13,
}
if option.SNI != "" {
tlsConfig.ServerName = option.SNI
}
var err error
tlsConfig, err = ca.GetTLSConfig(tlsConfig, option.Fingerprint, option.CustomCA, option.CustomCAString)
},
Fingerprint: option.Fingerprint,
CustomCA: option.CustomCA,
CustomCAString: option.CustomCAString,
})
if err != nil {
return nil, err
}

View File

@@ -95,14 +95,15 @@ func (v *Vless) StreamConnContext(ctx context.Context, c net.Conn, metadata *C.M
}
if v.option.TLS {
wsOpts.TLS = true
tlsConfig := &tls.Config{
wsOpts.TLSConfig, err = ca.GetTLSConfig(ca.Option{
TLSConfig: &tls.Config{
MinVersion: tls.VersionTLS12,
ServerName: host,
InsecureSkipVerify: v.option.SkipCertVerify,
NextProtos: []string{"http/1.1"},
}
wsOpts.TLSConfig, err = ca.GetSpecifiedFingerprintTLSConfig(tlsConfig, v.option.Fingerprint)
},
Fingerprint: v.option.Fingerprint,
})
if err != nil {
return nil, err
}
@@ -498,10 +499,13 @@ func NewVless(option VlessOption) (*Vless, error) {
}
var tlsConfig *tls.Config
if option.TLS {
tlsConfig, err = ca.GetSpecifiedFingerprintTLSConfig(&tls.Config{
tlsConfig, err = ca.GetTLSConfig(ca.Option{
TLSConfig: &tls.Config{
InsecureSkipVerify: v.option.SkipCertVerify,
ServerName: v.option.ServerName,
}, v.option.Fingerprint)
},
Fingerprint: v.option.Fingerprint,
})
if err != nil {
return nil, err
}

View File

@@ -123,13 +123,14 @@ func (v *Vmess) StreamConnContext(ctx context.Context, c net.Conn, metadata *C.M
if v.option.TLS {
wsOpts.TLS = true
tlsConfig := &tls.Config{
wsOpts.TLSConfig, err = ca.GetTLSConfig(ca.Option{
TLSConfig: &tls.Config{
ServerName: host,
InsecureSkipVerify: v.option.SkipCertVerify,
NextProtos: []string{"http/1.1"},
}
wsOpts.TLSConfig, err = ca.GetSpecifiedFingerprintTLSConfig(tlsConfig, v.option.Fingerprint)
},
Fingerprint: v.option.Fingerprint,
})
if err != nil {
return nil, err
}
@@ -501,10 +502,13 @@ func NewVmess(option VmessOption) (*Vmess, error) {
}
var tlsConfig *tls.Config
if option.TLS {
tlsConfig, err = ca.GetSpecifiedFingerprintTLSConfig(&tls.Config{
tlsConfig, err = ca.GetTLSConfig(ca.Option{
TLSConfig: &tls.Config{
InsecureSkipVerify: v.option.SkipCertVerify,
ServerName: v.option.ServerName,
}, v.option.Fingerprint)
},
Fingerprint: v.option.Fingerprint,
})
if err != nil {
return nil, err
}

View File

@@ -10,7 +10,9 @@ import (
"strconv"
"sync"
"github.com/metacubex/mihomo/common/once"
C "github.com/metacubex/mihomo/constant"
"github.com/metacubex/mihomo/ntp"
)
var globalCertPool *x509.CertPool
@@ -65,18 +67,6 @@ func ResetCertificate() {
initializeCertPool()
}
func getCertPool() *x509.CertPool {
if globalCertPool == nil {
mutex.Lock()
defer mutex.Unlock()
if globalCertPool != nil {
return globalCertPool
}
initializeCertPool()
}
return globalCertPool
}
func GetCertPool(customCA string, customCAString string) (*x509.CertPool, error) {
var certificate []byte
var err error
@@ -99,22 +89,41 @@ func GetCertPool(customCA string, customCAString string) (*x509.CertPool, error)
}
return certPool, nil
} else {
return getCertPool(), nil
mutex.Lock()
defer mutex.Unlock()
if globalCertPool == nil {
initializeCertPool()
}
return globalCertPool, nil
}
}
// GetTLSConfig specified fingerprint, customCA and customCAString
func GetTLSConfig(tlsConfig *tls.Config, fingerprint string, customCA string, customCAString string) (_ *tls.Config, err error) {
type Option struct {
TLSConfig *tls.Config
Fingerprint string
CustomCA string
CustomCAString string
ZeroTrust bool
}
func GetTLSConfig(opt Option) (tlsConfig *tls.Config, err error) {
tlsConfig = opt.TLSConfig
if tlsConfig == nil {
tlsConfig = &tls.Config{}
}
tlsConfig.RootCAs, err = GetCertPool(customCA, customCAString)
tlsConfig.Time = ntp.Now
if opt.ZeroTrust {
tlsConfig.RootCAs = zeroTrustCertPool()
} else {
tlsConfig.RootCAs, err = GetCertPool(opt.CustomCA, opt.CustomCAString)
if err != nil {
return nil, err
}
}
if len(fingerprint) > 0 {
tlsConfig.VerifyPeerCertificate, err = NewFingerprintVerifier(fingerprint)
if len(opt.Fingerprint) > 0 {
tlsConfig.VerifyPeerCertificate, err = NewFingerprintVerifier(opt.Fingerprint)
if err != nil {
return nil, err
}
@@ -123,12 +132,12 @@ func GetTLSConfig(tlsConfig *tls.Config, fingerprint string, customCA string, cu
return tlsConfig, nil
}
// GetSpecifiedFingerprintTLSConfig specified fingerprint
func GetSpecifiedFingerprintTLSConfig(tlsConfig *tls.Config, fingerprint string) (*tls.Config, error) {
return GetTLSConfig(tlsConfig, fingerprint, "", "")
}
func GetGlobalTLSConfig(tlsConfig *tls.Config) *tls.Config {
tlsConfig, _ = GetTLSConfig(tlsConfig, "", "", "")
return tlsConfig
}
var zeroTrustCertPool = once.OnceValue(func() *x509.CertPool {
if len(_CaCertificates) != 0 { // always using embed cert first
zeroTrustCertPool := x509.NewCertPool()
if zeroTrustCertPool.AppendCertsFromPEM(_CaCertificates) {
return zeroTrustCertPool
}
}
return nil // fallback to system pool
})

View File

@@ -2,7 +2,6 @@ package http
import (
"context"
"crypto/tls"
"io"
"net"
"net/http"
@@ -28,11 +27,11 @@ func SetUA(UA string) {
ua = UA
}
func HttpRequest(ctx context.Context, url, method string, header map[string][]string, body io.Reader) (*http.Response, error) {
return HttpRequestWithProxy(ctx, url, method, header, body, "")
}
func HttpRequestWithProxy(ctx context.Context, url, method string, header map[string][]string, body io.Reader, specialProxy string) (*http.Response, error) {
func HttpRequest(ctx context.Context, url, method string, header map[string][]string, body io.Reader, options ...Option) (*http.Response, error) {
opt := option{}
for _, o := range options {
o(&opt)
}
method = strings.ToUpper(method)
urlRes, err := URL.Parse(url)
if err != nil {
@@ -40,6 +39,10 @@ func HttpRequestWithProxy(ctx context.Context, url, method string, header map[st
}
req, err := http.NewRequest(method, urlRes.String(), body)
if err != nil {
return nil, err
}
for k, v := range header {
for _, v := range v {
req.Header.Add(k, v)
@@ -50,10 +53,6 @@ func HttpRequestWithProxy(ctx context.Context, url, method string, header map[st
req.Header.Set("User-Agent", UA())
}
if err != nil {
return nil, err
}
if user := urlRes.User; user != nil {
password, _ := user.Password()
req.SetBasicAuth(user.Username(), password)
@@ -61,6 +60,11 @@ func HttpRequestWithProxy(ctx context.Context, url, method string, header map[st
req = req.WithContext(ctx)
tlsConfig, err := ca.GetTLSConfig(opt.caOption)
if err != nil {
return nil, err
}
transport := &http.Transport{
// from http.DefaultTransport
DisableKeepAlives: runtime.GOOS == "android",
@@ -69,15 +73,34 @@ func HttpRequestWithProxy(ctx context.Context, url, method string, header map[st
TLSHandshakeTimeout: 10 * time.Second,
ExpectContinueTimeout: 1 * time.Second,
DialContext: func(ctx context.Context, network, address string) (net.Conn, error) {
if conn, err := inner.HandleTcp(inner.GetTunnel(), address, specialProxy); err == nil {
if conn, err := inner.HandleTcp(inner.GetTunnel(), address, opt.specialProxy); err == nil {
return conn, nil
} else {
return dialer.DialContext(ctx, network, address)
}
},
TLSClientConfig: ca.GetGlobalTLSConfig(&tls.Config{}),
TLSClientConfig: tlsConfig,
}
client := http.Client{Transport: transport}
return client.Do(req)
}
type Option func(opt *option)
type option struct {
specialProxy string
caOption ca.Option
}
func WithSpecialProxy(name string) Option {
return func(opt *option) {
opt.specialProxy = name
}
}
func WithCAOption(caOption ca.Option) Option {
return func(opt *option) {
opt.caOption = caOption
}
}

View File

@@ -135,7 +135,7 @@ func (h *HTTPVehicle) Read(ctx context.Context, oldHash utils.HashType) (buf []b
setIfNoneMatch = true
}
}
resp, err := mihomoHttp.HttpRequestWithProxy(ctx, h.url, http.MethodGet, header, nil, h.proxy)
resp, err := mihomoHttp.HttpRequest(ctx, h.url, http.MethodGet, header, nil, mihomoHttp.WithSpecialProxy(h.proxy))
if err != nil {
return
}

View File

@@ -15,6 +15,7 @@ import (
"sync"
"time"
"github.com/metacubex/mihomo/component/ca"
mihomoHttp "github.com/metacubex/mihomo/component/http"
C "github.com/metacubex/mihomo/constant"
"github.com/metacubex/mihomo/constant/features"
@@ -171,7 +172,7 @@ func (u *CoreUpdater) Update(currentExePath string, channel string, force bool)
func (u *CoreUpdater) getLatestVersion(versionURL string) (version string, err error) {
ctx, cancel := context.WithTimeout(context.Background(), time.Second*5)
defer cancel()
resp, err := mihomoHttp.HttpRequest(ctx, versionURL, http.MethodGet, nil, nil)
resp, err := mihomoHttp.HttpRequest(ctx, versionURL, http.MethodGet, nil, nil, mihomoHttp.WithCAOption(ca.Option{ZeroTrust: true}))
if err != nil {
return "", err
}
@@ -194,7 +195,7 @@ func (u *CoreUpdater) getLatestVersion(versionURL string) (version string, err e
func (u *CoreUpdater) download(updateDir, packagePath, packageURL string) (err error) {
ctx, cancel := context.WithTimeout(context.Background(), time.Second*90)
defer cancel()
resp, err := mihomoHttp.HttpRequest(ctx, packageURL, http.MethodGet, nil, nil)
resp, err := mihomoHttp.HttpRequest(ctx, packageURL, http.MethodGet, nil, nil, mihomoHttp.WithCAOption(ca.Option{ZeroTrust: true}))
if err != nil {
return fmt.Errorf("http request failed: %w", err)
}

View File

@@ -48,6 +48,13 @@ func (c *client) ExchangeContext(ctx context.Context, m *D.Msg) (*D.Msg, error)
network = "tcp"
}
tlsConfig, err := ca.GetTLSConfig(ca.Option{
TLSConfig: c.Client.TLSConfig,
})
if err != nil {
return nil, err
}
addr := net.JoinHostPort(c.host, c.port)
conn, err := c.dialer.DialContext(ctx, network, addr)
if err != nil {
@@ -66,7 +73,7 @@ func (c *client) ExchangeContext(ctx context.Context, m *D.Msg) (*D.Msg, error)
ch := make(chan result, 1)
go func() {
if strings.HasSuffix(c.Client.Net, "tls") {
conn = tls.Client(conn, ca.GetGlobalTLSConfig(c.Client.TLSConfig))
conn = tls.Client(conn, tlsConfig)
}
dConn := &D.Conn{

View File

@@ -397,12 +397,16 @@ func (doh *dnsOverHTTPS) createTransport(ctx context.Context) (t http.RoundTripp
return transport, nil
}
tlsConfig := ca.GetGlobalTLSConfig(
&tls.Config{
tlsConfig, err := ca.GetTLSConfig(ca.Option{
TLSConfig: &tls.Config{
InsecureSkipVerify: doh.skipCertVerify,
MinVersion: tls.VersionTLS12,
SessionTicketsDisabled: false,
},
})
if err != nil {
return nil, err
}
var nextProtos []string
for _, v := range doh.httpVersions {
nextProtos = append(nextProtos, string(v))

View File

@@ -331,15 +331,19 @@ func (doq *dnsOverQUIC) openConnection(ctx context.Context) (conn *quic.Conn, er
return nil, err
}
tlsConfig := ca.GetGlobalTLSConfig(
&tls.Config{
tlsConfig, err := ca.GetTLSConfig(ca.Option{
TLSConfig: &tls.Config{
ServerName: host,
InsecureSkipVerify: doq.skipCertVerify,
NextProtos: []string{
NextProtoDQ,
},
SessionTicketsDisabled: false,
},
})
if err != nil {
return nil, err
}
transport := quic.Transport{Conn: udp}
transport.SetCreatedConn(true) // auto close conn

View File

@@ -6,7 +6,7 @@ require (
github.com/bahlo/generic-list-go v0.2.0
github.com/coreos/go-iptables v0.8.0
github.com/dlclark/regexp2 v1.11.5
github.com/enfein/mieru/v3 v3.19.1
github.com/enfein/mieru/v3 v3.20.0
github.com/go-chi/chi/v5 v5.2.3
github.com/go-chi/render v1.0.3
github.com/gobwas/ws v1.4.0

View File

@@ -25,8 +25,8 @@ github.com/dlclark/regexp2 v1.11.5 h1:Q/sSnsKerHeCkc/jSTNq1oCm7KiVgUMZRDUoRu0JQZ
github.com/dlclark/regexp2 v1.11.5/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8=
github.com/ebitengine/purego v0.8.4 h1:CF7LEKg5FFOsASUj0+QwaXf8Ht6TlFxg09+S9wz0omw=
github.com/ebitengine/purego v0.8.4/go.mod h1:iIjxzd6CiRiOG0UyXP+V1+jWqUXVjPKLAI0mRfJZTmQ=
github.com/enfein/mieru/v3 v3.19.1 h1:19b9kgFC7oJXX9RLEO5Pi1gO6yek5cWlpK7IJVUoE8I=
github.com/enfein/mieru/v3 v3.19.1/go.mod h1:zJBUCsi5rxyvHM8fjFf+GLaEl4OEjjBXr1s5F6Qd3hM=
github.com/enfein/mieru/v3 v3.20.0 h1:1ob7pCIVSH5FYFAfYvim8isLW1vBOS4cFOUF9exJS38=
github.com/enfein/mieru/v3 v3.20.0/go.mod h1:zJBUCsi5rxyvHM8fjFf+GLaEl4OEjjBXr1s5F6Qd3hM=
github.com/ericlagergren/aegis v0.0.0-20250325060835-cd0defd64358 h1:kXYqH/sL8dS/FdoFjr12ePjnLPorPo2FsnrHNuXSDyo=
github.com/ericlagergren/aegis v0.0.0-20250325060835-cd0defd64358/go.mod h1:hkIFzoiIPZYxdFOOLyDho59b7SrDfo+w3h+yWdlg45I=
github.com/ericlagergren/polyval v0.0.0-20220411101811-e25bc10ba391 h1:8j2RH289RJplhA6WfdaPqzg1MjH2K8wX5e0uhAxrw2g=

View File

@@ -20,6 +20,7 @@ import (
tlsC "github.com/metacubex/mihomo/component/tls"
C "github.com/metacubex/mihomo/constant"
"github.com/metacubex/mihomo/log"
"github.com/metacubex/mihomo/ntp"
"github.com/metacubex/mihomo/tunnel/statistic"
"github.com/go-chi/chi/v5"
@@ -201,7 +202,7 @@ func startTLS(cfg *Config) {
}
log.Infoln("RESTful API tls listening at: %s", l.Addr().String())
tlsConfig := &tlsC.Config{}
tlsConfig := &tlsC.Config{Time: ntp.Now}
tlsConfig.NextProtos = []string{"h2", "http/1.1"}
tlsConfig.Certificates = []tlsC.Certificate{tlsC.UCertificate(cert)}

View File

@@ -17,6 +17,7 @@ import (
C "github.com/metacubex/mihomo/constant"
LC "github.com/metacubex/mihomo/listener/config"
"github.com/metacubex/mihomo/listener/sing"
"github.com/metacubex/mihomo/ntp"
"github.com/metacubex/mihomo/transport/anytls/padding"
"github.com/metacubex/mihomo/transport/anytls/session"
@@ -42,7 +43,7 @@ func New(config LC.AnyTLSServer, tunnel C.Tunnel, additions ...inbound.Addition)
}
}
tlsConfig := &tlsC.Config{}
tlsConfig := &tlsC.Config{Time: ntp.Now}
if config.Certificate != "" && config.PrivateKey != "" {
cert, err := ca.LoadTLSKeyPair(config.Certificate, config.PrivateKey, C.Path)
if err != nil {

View File

@@ -12,6 +12,7 @@ import (
authStore "github.com/metacubex/mihomo/listener/auth"
LC "github.com/metacubex/mihomo/listener/config"
"github.com/metacubex/mihomo/listener/reality"
"github.com/metacubex/mihomo/ntp"
)
type Listener struct {
@@ -65,7 +66,7 @@ func NewWithConfig(config LC.AuthServer, tunnel C.Tunnel, additions ...inbound.A
return nil, err
}
tlsConfig := &tlsC.Config{}
tlsConfig := &tlsC.Config{Time: ntp.Now}
var realityBuilder *reality.Builder
if config.Certificate != "" && config.PrivateKey != "" {

View File

@@ -39,7 +39,7 @@ var userUUID = utils.NewUUIDV4().String()
var tlsCertificate, tlsPrivateKey, tlsFingerprint, _ = ca.NewRandomTLSKeyPair(ca.KeyPairTypeP256)
var tlsConfigCert, _ = tls.X509KeyPair([]byte(tlsCertificate), []byte(tlsPrivateKey))
var tlsConfig = &tls.Config{Certificates: []tls.Certificate{tlsConfigCert}, NextProtos: []string{"h2", "http/1.1"}}
var tlsClientConfig, _ = ca.GetTLSConfig(nil, tlsFingerprint, "", "")
var tlsClientConfig, _ = ca.GetTLSConfig(ca.Option{Fingerprint: tlsFingerprint})
var realityPrivateKey, realityPublickey string
var realityDest = "itunes.apple.com"
var realityShortid = "10f897e26c4b9478"

View File

@@ -16,6 +16,7 @@ import (
"github.com/metacubex/mihomo/listener/http"
"github.com/metacubex/mihomo/listener/reality"
"github.com/metacubex/mihomo/listener/socks"
"github.com/metacubex/mihomo/ntp"
"github.com/metacubex/mihomo/transport/socks4"
"github.com/metacubex/mihomo/transport/socks5"
)
@@ -61,7 +62,7 @@ func NewWithConfig(config LC.AuthServer, tunnel C.Tunnel, additions ...inbound.A
return nil, err
}
tlsConfig := &tlsC.Config{}
tlsConfig := &tlsC.Config{Time: ntp.Now}
var realityBuilder *reality.Builder
if config.Certificate != "" && config.PrivateKey != "" {

View File

@@ -20,6 +20,7 @@ import (
LC "github.com/metacubex/mihomo/listener/config"
"github.com/metacubex/mihomo/listener/sing"
"github.com/metacubex/mihomo/log"
"github.com/metacubex/mihomo/ntp"
"github.com/metacubex/sing-quic/hysteria2"
@@ -61,6 +62,7 @@ func New(config LC.Hysteria2Server, tunnel C.Tunnel, additions ...inbound.Additi
return nil, err
}
tlsConfig := &tlsC.Config{
Time: ntp.Now,
MinVersion: tlsC.VersionTLS13,
}
tlsConfig.Certificates = []tlsC.Certificate{tlsC.UCertificate(cert)}

View File

@@ -15,6 +15,7 @@ import (
LC "github.com/metacubex/mihomo/listener/config"
"github.com/metacubex/mihomo/listener/reality"
"github.com/metacubex/mihomo/listener/sing"
"github.com/metacubex/mihomo/ntp"
"github.com/metacubex/mihomo/transport/gun"
"github.com/metacubex/mihomo/transport/vless/encryption"
mihomoVMess "github.com/metacubex/mihomo/transport/vmess"
@@ -75,7 +76,7 @@ func New(config LC.VlessServer, tunnel C.Tunnel, additions ...inbound.Addition)
}()
}
tlsConfig := &tlsC.Config{}
tlsConfig := &tlsC.Config{Time: ntp.Now}
var realityBuilder *reality.Builder
var httpServer http.Server

View File

@@ -76,7 +76,7 @@ func New(config LC.VmessServer, tunnel C.Tunnel, additions ...inbound.Addition)
sl = &Listener{false, config, nil, service}
tlsConfig := &tlsC.Config{}
tlsConfig := &tlsC.Config{Time: ntp.Now}
var realityBuilder *reality.Builder
var httpServer http.Server

Some files were not shown because too many files have changed in this diff Show More