Update On Wed Feb 19 19:34:37 CET 2025

This commit is contained in:
github-action[bot]
2025-02-19 19:34:38 +01:00
parent 5cd1918265
commit 13ade665d3
105 changed files with 2275 additions and 1007 deletions

View File

@@ -29,7 +29,7 @@ jobs:
- name: Setup Go
uses: actions/setup-go@v5
with:
go-version: "1.23"
go-version: "1.24"
- uses: actions/cache@v4
with:

View File

@@ -28,7 +28,7 @@ jobs:
- name: Setup Go
uses: actions/setup-go@v5
with:
go-version: "1.23"
go-version: "1.24"
- uses: actions/cache@v4
with:

View File

@@ -31,7 +31,7 @@ jobs:
- name: Setup Go
uses: actions/setup-go@v5
with:
go-version: "1.23"
go-version: "1.24"
- uses: actions/cache@v4
with:

View File

@@ -24,7 +24,7 @@ jobs:
- name: Setup Go
uses: actions/setup-go@v5
with:
go-version: "1.23"
go-version: "1.24"
- uses: actions/cache@v4
with:

View File

@@ -23,6 +23,7 @@ require (
github.com/metacubex/gopacket v1.1.20-0.20230608035415-7e2f98a3e759
github.com/metacubex/quic-go v0.49.1-0.20250212162123-c135a4412996
github.com/metacubex/randv2 v0.2.0
github.com/metacubex/reality v0.0.0-20250219003814-74e8d7850629
github.com/metacubex/sing-quic v0.0.0-20250119013740-2a19cce83925
github.com/metacubex/sing-shadowsocks v0.2.8
github.com/metacubex/sing-shadowsocks2 v0.2.2
@@ -40,7 +41,6 @@ require (
github.com/sagernet/cors v1.2.1
github.com/sagernet/fswatch v0.1.1
github.com/sagernet/netlink v0.0.0-20240612041022-b9a21c07ac6a
github.com/sagernet/reality v0.0.0-20230406110435-ee17307e7691
github.com/sagernet/sing v0.5.1
github.com/sagernet/sing-mux v0.2.1
github.com/sagernet/sing-shadowtls v0.1.5

View File

@@ -109,6 +109,8 @@ github.com/metacubex/quic-go v0.49.1-0.20250212162123-c135a4412996 h1:B+AP/Pj2/j
github.com/metacubex/quic-go v0.49.1-0.20250212162123-c135a4412996/go.mod h1:ExVjGyEwTUjCFqx+5uxgV7MOoA3fZI+th4D40H35xmY=
github.com/metacubex/randv2 v0.2.0 h1:uP38uBvV2SxYfLj53kuvAjbND4RUDfFJjwr4UigMiLs=
github.com/metacubex/randv2 v0.2.0/go.mod h1:kFi2SzrQ5WuneuoLLCMkABtiBu6VRrMrWFqSPyj2cxY=
github.com/metacubex/reality v0.0.0-20250219003814-74e8d7850629 h1:aHsYiTvubfgMa3JMTDY//hDXVvFWrHg6ARckR52ttZs=
github.com/metacubex/reality v0.0.0-20250219003814-74e8d7850629/go.mod h1:TTeIOZLdGmzc07Oedn++vWUUfkZoXLF4sEMxWuhBFr8=
github.com/metacubex/sing v0.0.0-20241121030428-33b6ebc52000 h1:gUbMXcQXhXGj0vCpCVFTUyIH7TMpD1dpTcNv/MCS+ok=
github.com/metacubex/sing v0.0.0-20241121030428-33b6ebc52000/go.mod h1:ARkL0gM13/Iv5VCZmci/NuoOlePoIsW0m7BWfln/Hak=
github.com/metacubex/sing-quic v0.0.0-20250119013740-2a19cce83925 h1:UkPoRAnoBQMn7IK5qpoIV3OejU15q+rqel3NrbSCFKA=
@@ -168,8 +170,6 @@ github.com/sagernet/netlink v0.0.0-20240612041022-b9a21c07ac6a h1:ObwtHN2VpqE0ZN
github.com/sagernet/netlink v0.0.0-20240612041022-b9a21c07ac6a/go.mod h1:xLnfdiJbSp8rNqYEdIW/6eDO4mVoogml14Bh2hSiFpM=
github.com/sagernet/nftables v0.3.0-beta.4 h1:kbULlAwAC3jvdGAC1P5Fa3GSxVwQJibNenDW2zaXr8I=
github.com/sagernet/nftables v0.3.0-beta.4/go.mod h1:OQXAjvjNGGFxaTgVCSTRIhYB5/llyVDeapVoENYBDS8=
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-mux v0.2.1 h1:N/3MHymfnFZRd29tE3TaXwPUVVgKvxhtOkiCMLp9HVo=
github.com/sagernet/sing-mux v0.2.1/go.mod h1:dm3BWL6NvES9pbib7llpylrq7Gq+LjlzG+0RacdxcyE=
github.com/sagernet/sing-shadowtls v0.1.5 h1:uXxmq/HXh8DIiBGLzpMjCbWnzIAFs+lIxiTOjdgG5qo=

View File

@@ -135,6 +135,8 @@ func (l *Listener) HandleConn(conn net.Conn, h *sing.ListenerHandler) {
defer conn.Close()
b := buf.NewPacket()
defer b.Release()
_, err := b.ReadOnceFrom(conn)
if err != nil {
return
@@ -177,6 +179,6 @@ func (l *Listener) HandleConn(conn net.Conn, h *sing.ListenerHandler) {
Destination: destination,
})
}, &l.padding)
session.Run(true)
session.Run()
session.Close()
}

View File

@@ -12,7 +12,7 @@ import (
"github.com/metacubex/mihomo/listener/inner"
"github.com/metacubex/mihomo/ntp"
"github.com/sagernet/reality"
"github.com/metacubex/reality"
)
type Conn = reality.Conn

View File

@@ -71,6 +71,8 @@ func (c *Client) CreateOutboundTLSConnection(ctx context.Context) (net.Conn, err
}
b := buf.NewPacket()
defer b.Release()
b.Write(c.passwordSha256)
var paddingLen int
if pad := c.padding.Load().GenerateRecordPayloadSizes(0); len(pad) > 0 {

View File

@@ -0,0 +1,74 @@
package pipe
import (
"sync"
"time"
)
// PipeDeadline is an abstraction for handling timeouts.
type PipeDeadline struct {
mu sync.Mutex // Guards timer and cancel
timer *time.Timer
cancel chan struct{} // Must be non-nil
}
func MakePipeDeadline() PipeDeadline {
return PipeDeadline{cancel: make(chan struct{})}
}
// Set sets the point in time when the deadline will time out.
// A timeout event is signaled by closing the channel returned by waiter.
// Once a timeout has occurred, the deadline can be refreshed by specifying a
// t value in the future.
//
// A zero value for t prevents timeout.
func (d *PipeDeadline) Set(t time.Time) {
d.mu.Lock()
defer d.mu.Unlock()
if d.timer != nil && !d.timer.Stop() {
<-d.cancel // Wait for the timer callback to finish and close cancel
}
d.timer = nil
// Time is zero, then there is no deadline.
closed := isClosedChan(d.cancel)
if t.IsZero() {
if closed {
d.cancel = make(chan struct{})
}
return
}
// Time in the future, setup a timer to cancel in the future.
if dur := time.Until(t); dur > 0 {
if closed {
d.cancel = make(chan struct{})
}
d.timer = time.AfterFunc(dur, func() {
close(d.cancel)
})
return
}
// Time in the past, so close immediately.
if !closed {
close(d.cancel)
}
}
// Wait returns a channel that is closed when the deadline is exceeded.
func (d *PipeDeadline) Wait() chan struct{} {
d.mu.Lock()
defer d.mu.Unlock()
return d.cancel
}
func isClosedChan(c <-chan struct{}) bool {
select {
case <-c:
return true
default:
return false
}
}

View File

@@ -0,0 +1,232 @@
// Copyright 2009 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// Pipe adapter to connect code expecting an io.Reader
// with code expecting an io.Writer.
package pipe
import (
"io"
"os"
"sync"
"time"
)
// onceError is an object that will only store an error once.
type onceError struct {
sync.Mutex // guards following
err error
}
func (a *onceError) Store(err error) {
a.Lock()
defer a.Unlock()
if a.err != nil {
return
}
a.err = err
}
func (a *onceError) Load() error {
a.Lock()
defer a.Unlock()
return a.err
}
// A pipe is the shared pipe structure underlying PipeReader and PipeWriter.
type pipe struct {
wrMu sync.Mutex // Serializes Write operations
wrCh chan []byte
rdCh chan int
once sync.Once // Protects closing done
done chan struct{}
rerr onceError
werr onceError
readDeadline PipeDeadline
writeDeadline PipeDeadline
}
func (p *pipe) read(b []byte) (n int, err error) {
select {
case <-p.done:
return 0, p.readCloseError()
case <-p.readDeadline.Wait():
return 0, os.ErrDeadlineExceeded
default:
}
select {
case bw := <-p.wrCh:
nr := copy(b, bw)
p.rdCh <- nr
return nr, nil
case <-p.done:
return 0, p.readCloseError()
case <-p.readDeadline.Wait():
return 0, os.ErrDeadlineExceeded
}
}
func (p *pipe) closeRead(err error) error {
if err == nil {
err = io.ErrClosedPipe
}
p.rerr.Store(err)
p.once.Do(func() { close(p.done) })
return nil
}
func (p *pipe) write(b []byte) (n int, err error) {
select {
case <-p.done:
return 0, p.writeCloseError()
case <-p.writeDeadline.Wait():
return 0, os.ErrDeadlineExceeded
default:
p.wrMu.Lock()
defer p.wrMu.Unlock()
}
for once := true; once || len(b) > 0; once = false {
select {
case p.wrCh <- b:
nw := <-p.rdCh
b = b[nw:]
n += nw
case <-p.done:
return n, p.writeCloseError()
case <-p.writeDeadline.Wait():
return n, os.ErrDeadlineExceeded
}
}
return n, nil
}
func (p *pipe) closeWrite(err error) error {
if err == nil {
err = io.EOF
}
p.werr.Store(err)
p.once.Do(func() { close(p.done) })
return nil
}
// readCloseError is considered internal to the pipe type.
func (p *pipe) readCloseError() error {
rerr := p.rerr.Load()
if werr := p.werr.Load(); rerr == nil && werr != nil {
return werr
}
return io.ErrClosedPipe
}
// writeCloseError is considered internal to the pipe type.
func (p *pipe) writeCloseError() error {
werr := p.werr.Load()
if rerr := p.rerr.Load(); werr == nil && rerr != nil {
return rerr
}
return io.ErrClosedPipe
}
// A PipeReader is the read half of a pipe.
type PipeReader struct{ pipe }
// Read implements the standard Read interface:
// it reads data from the pipe, blocking until a writer
// arrives or the write end is closed.
// If the write end is closed with an error, that error is
// returned as err; otherwise err is EOF.
func (r *PipeReader) Read(data []byte) (n int, err error) {
return r.pipe.read(data)
}
// Close closes the reader; subsequent writes to the
// write half of the pipe will return the error [ErrClosedPipe].
func (r *PipeReader) Close() error {
return r.CloseWithError(nil)
}
// CloseWithError closes the reader; subsequent writes
// to the write half of the pipe will return the error err.
//
// CloseWithError never overwrites the previous error if it exists
// and always returns nil.
func (r *PipeReader) CloseWithError(err error) error {
return r.pipe.closeRead(err)
}
// A PipeWriter is the write half of a pipe.
type PipeWriter struct{ r PipeReader }
// Write implements the standard Write interface:
// it writes data to the pipe, blocking until one or more readers
// have consumed all the data or the read end is closed.
// If the read end is closed with an error, that err is
// returned as err; otherwise err is [ErrClosedPipe].
func (w *PipeWriter) Write(data []byte) (n int, err error) {
return w.r.pipe.write(data)
}
// Close closes the writer; subsequent reads from the
// read half of the pipe will return no bytes and EOF.
func (w *PipeWriter) Close() error {
return w.CloseWithError(nil)
}
// CloseWithError closes the writer; subsequent reads from the
// read half of the pipe will return no bytes and the error err,
// or EOF if err is nil.
//
// CloseWithError never overwrites the previous error if it exists
// and always returns nil.
func (w *PipeWriter) CloseWithError(err error) error {
return w.r.pipe.closeWrite(err)
}
// Pipe creates a synchronous in-memory pipe.
// It can be used to connect code expecting an [io.Reader]
// with code expecting an [io.Writer].
//
// Reads and Writes on the pipe are matched one to one
// except when multiple Reads are needed to consume a single Write.
// That is, each Write to the [PipeWriter] blocks until it has satisfied
// one or more Reads from the [PipeReader] that fully consume
// the written data.
// The data is copied directly from the Write to the corresponding
// Read (or Reads); there is no internal buffering.
//
// It is safe to call Read and Write in parallel with each other or with Close.
// Parallel calls to Read and parallel calls to Write are also safe:
// the individual calls will be gated sequentially.
//
// Added SetReadDeadline and SetWriteDeadline methods based on `io.Pipe`.
func Pipe() (*PipeReader, *PipeWriter) {
pw := &PipeWriter{r: PipeReader{pipe: pipe{
wrCh: make(chan []byte),
rdCh: make(chan int),
done: make(chan struct{}),
readDeadline: MakePipeDeadline(),
writeDeadline: MakePipeDeadline(),
}}}
return &pw.r, pw
}
func (p *PipeReader) SetReadDeadline(t time.Time) error {
if isClosedChan(p.done) {
return io.ErrClosedPipe
}
p.readDeadline.Set(t)
return nil
}
func (p *PipeWriter) SetWriteDeadline(t time.Time) error {
if isClosedChan(p.r.done) {
return io.ErrClosedPipe
}
p.r.writeDeadline.Set(t)
return nil
}

View File

@@ -123,7 +123,7 @@ func (c *Client) createSession(ctx context.Context) (*Session, error) {
c.idleSession.Remove(math.MaxUint64 - session.seq)
c.idleSessionLock.Unlock()
}
session.Run(false)
session.Run()
return session, nil
}

View File

@@ -36,10 +36,11 @@ type Session struct {
padding *atomic.TypedValue[*padding.PaddingFactory]
// client
isClient bool
buffering bool
buffer []byte
pktCounter atomic.Uint32
isClient bool
sendPadding bool
buffering bool
buffer []byte
pktCounter atomic.Uint32
// server
onNewStream func(stream *Stream)
@@ -47,9 +48,10 @@ type Session struct {
func NewClientSession(conn net.Conn, _padding *atomic.TypedValue[*padding.PaddingFactory]) *Session {
s := &Session{
conn: conn,
isClient: true,
padding: _padding,
conn: conn,
isClient: true,
sendPadding: true,
padding: _padding,
}
s.die = make(chan struct{})
s.streams = make(map[uint32]*Stream)
@@ -60,7 +62,6 @@ func NewServerSession(conn net.Conn, onNewStream func(stream *Stream), _padding
s := &Session{
conn: conn,
onNewStream: onNewStream,
isClient: false,
padding: _padding,
}
s.die = make(chan struct{})
@@ -68,8 +69,8 @@ func NewServerSession(conn net.Conn, onNewStream func(stream *Stream), _padding
return s
}
func (s *Session) Run(isServer bool) {
if isServer {
func (s *Session) Run() {
if !s.isClient {
s.recvLoop()
return
}
@@ -257,19 +258,18 @@ func (s *Session) recvLoop() error {
}
case cmdUpdatePaddingScheme:
if hdr.Length() > 0 {
buffer := pool.Get(int(hdr.Length()))
if _, err := io.ReadFull(s.conn, buffer); err != nil {
pool.Put(buffer)
// `rawScheme` Do not use buffer to prevent subsequent misuse
rawScheme := make([]byte, int(hdr.Length()))
if _, err := io.ReadFull(s.conn, rawScheme); err != nil {
return err
}
if s.isClient {
if padding.UpdatePaddingScheme(buffer, s.padding) {
log.Infoln("[Update padding succeed] %x\n", md5.Sum(buffer))
if padding.UpdatePaddingScheme(rawScheme, s.padding) {
log.Infoln("[Update padding succeed] %x\n", md5.Sum(rawScheme))
} else {
log.Warnln("[Update padding failed] %x\n", md5.Sum(buffer))
log.Warnln("[Update padding failed] %x\n", md5.Sum(rawScheme))
}
}
pool.Put(buffer)
}
default:
// I don't know what command it is (can't have data)
@@ -319,7 +319,7 @@ func (s *Session) writeConn(b []byte) (n int, err error) {
}
// calulate & send padding
if s.isClient {
if s.sendPadding {
pkt := s.pktCounter.Add(1)
paddingF := s.padding.Load()
if pkt < paddingF.Stop {
@@ -333,7 +333,6 @@ func (s *Session) writeConn(b []byte) (n int, err error) {
continue
}
}
// logrus.Debugln(pkt, "write", l, "len", remainPayloadLen, "remain", remainPayloadLen-l)
if remainPayloadLen > l { // this packet is all payload
_, err = s.conn.Write(b[:l])
if err != nil {
@@ -371,7 +370,12 @@ func (s *Session) writeConn(b []byte) (n int, err error) {
// maybe still remain payload to write
if len(b) == 0 {
return
} else {
n2, err := s.conn.Write(b)
return n + n2, err
}
} else {
s.sendPadding = false
}
}

View File

@@ -6,6 +6,8 @@ import (
"os"
"sync"
"time"
"github.com/metacubex/mihomo/transport/anytls/pipe"
)
// Stream implements net.Conn
@@ -14,8 +16,9 @@ type Stream struct {
sess *Session
pipeR *io.PipeReader
pipeW *io.PipeWriter
pipeR *pipe.PipeReader
pipeW *pipe.PipeWriter
writeDeadline pipe.PipeDeadline
dieOnce sync.Once
dieHook func()
@@ -26,7 +29,8 @@ func newStream(id uint32, sess *Session) *Stream {
s := new(Stream)
s.id = id
s.sess = sess
s.pipeR, s.pipeW = io.Pipe()
s.pipeR, s.pipeW = pipe.Pipe()
s.writeDeadline = pipe.MakePipeDeadline()
return s
}
@@ -37,6 +41,11 @@ func (s *Stream) Read(b []byte) (n int, err error) {
// Write implements net.Conn
func (s *Stream) Write(b []byte) (n int, err error) {
select {
case <-s.writeDeadline.Wait():
return 0, os.ErrDeadlineExceeded
default:
}
f := newFrame(cmdPSH, s.id)
f.data = b
n, err = s.sess.writeFrame(f)
@@ -67,15 +76,17 @@ func (s *Stream) sessionClose() (once bool) {
}
func (s *Stream) SetReadDeadline(t time.Time) error {
return os.ErrNotExist
return s.pipeR.SetReadDeadline(t)
}
func (s *Stream) SetWriteDeadline(t time.Time) error {
return os.ErrNotExist
s.writeDeadline.Set(t)
return nil
}
func (s *Stream) SetDeadline(t time.Time) error {
return os.ErrNotExist
s.SetWriteDeadline(t)
return s.SetReadDeadline(t)
}
// LocalAddr satisfies net.Conn interface

View File

@@ -52,6 +52,7 @@ require (
github.com/metacubex/mihomo v1.7.0 // indirect
github.com/metacubex/quic-go v0.49.1-0.20250212162123-c135a4412996 // indirect
github.com/metacubex/randv2 v0.2.0 // indirect
github.com/metacubex/reality v0.0.0-20250219003814-74e8d7850629 // indirect
github.com/metacubex/sing-quic v0.0.0-20250119013740-2a19cce83925 // indirect
github.com/metacubex/sing-shadowsocks v0.2.8 // indirect
github.com/metacubex/sing-shadowsocks2 v0.2.2 // indirect
@@ -76,7 +77,6 @@ require (
github.com/sagernet/fswatch v0.1.1 // indirect
github.com/sagernet/netlink v0.0.0-20240612041022-b9a21c07ac6a // indirect
github.com/sagernet/nftables v0.3.0-beta.4 // indirect
github.com/sagernet/reality v0.0.0-20230406110435-ee17307e7691 // indirect
github.com/sagernet/sing v0.5.1 // indirect
github.com/sagernet/sing-mux v0.2.1 // indirect
github.com/sagernet/sing-shadowtls v0.1.5 // indirect

View File

@@ -109,6 +109,8 @@ github.com/metacubex/quic-go v0.49.1-0.20250212162123-c135a4412996 h1:B+AP/Pj2/j
github.com/metacubex/quic-go v0.49.1-0.20250212162123-c135a4412996/go.mod h1:ExVjGyEwTUjCFqx+5uxgV7MOoA3fZI+th4D40H35xmY=
github.com/metacubex/randv2 v0.2.0 h1:uP38uBvV2SxYfLj53kuvAjbND4RUDfFJjwr4UigMiLs=
github.com/metacubex/randv2 v0.2.0/go.mod h1:kFi2SzrQ5WuneuoLLCMkABtiBu6VRrMrWFqSPyj2cxY=
github.com/metacubex/reality v0.0.0-20250219003814-74e8d7850629 h1:aHsYiTvubfgMa3JMTDY//hDXVvFWrHg6ARckR52ttZs=
github.com/metacubex/reality v0.0.0-20250219003814-74e8d7850629/go.mod h1:TTeIOZLdGmzc07Oedn++vWUUfkZoXLF4sEMxWuhBFr8=
github.com/metacubex/sing v0.0.0-20241121030428-33b6ebc52000 h1:gUbMXcQXhXGj0vCpCVFTUyIH7TMpD1dpTcNv/MCS+ok=
github.com/metacubex/sing v0.0.0-20241121030428-33b6ebc52000/go.mod h1:ARkL0gM13/Iv5VCZmci/NuoOlePoIsW0m7BWfln/Hak=
github.com/metacubex/sing-quic v0.0.0-20250119013740-2a19cce83925 h1:UkPoRAnoBQMn7IK5qpoIV3OejU15q+rqel3NrbSCFKA=
@@ -167,8 +169,6 @@ github.com/sagernet/netlink v0.0.0-20240612041022-b9a21c07ac6a h1:ObwtHN2VpqE0ZN
github.com/sagernet/netlink v0.0.0-20240612041022-b9a21c07ac6a/go.mod h1:xLnfdiJbSp8rNqYEdIW/6eDO4mVoogml14Bh2hSiFpM=
github.com/sagernet/nftables v0.3.0-beta.4 h1:kbULlAwAC3jvdGAC1P5Fa3GSxVwQJibNenDW2zaXr8I=
github.com/sagernet/nftables v0.3.0-beta.4/go.mod h1:OQXAjvjNGGFxaTgVCSTRIhYB5/llyVDeapVoENYBDS8=
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-mux v0.2.1 h1:N/3MHymfnFZRd29tE3TaXwPUVVgKvxhtOkiCMLp9HVo=
github.com/sagernet/sing-mux v0.2.1/go.mod h1:dm3BWL6NvES9pbib7llpylrq7Gq+LjlzG+0RacdxcyE=
github.com/sagernet/sing-shadowtls v0.1.5 h1:uXxmq/HXh8DIiBGLzpMjCbWnzIAFs+lIxiTOjdgG5qo=

View File

@@ -60,6 +60,7 @@ require (
github.com/metacubex/gvisor v0.0.0-20241126021258-5b028898cc5a // indirect
github.com/metacubex/quic-go v0.49.1-0.20250212162123-c135a4412996 // indirect
github.com/metacubex/randv2 v0.2.0 // indirect
github.com/metacubex/reality v0.0.0-20250219003814-74e8d7850629 // indirect
github.com/metacubex/sing-quic v0.0.0-20250119013740-2a19cce83925 // indirect
github.com/metacubex/sing-shadowsocks v0.2.8 // indirect
github.com/metacubex/sing-shadowsocks2 v0.2.2 // indirect
@@ -84,7 +85,6 @@ require (
github.com/sagernet/fswatch v0.1.1 // indirect
github.com/sagernet/netlink v0.0.0-20240612041022-b9a21c07ac6a // indirect
github.com/sagernet/nftables v0.3.0-beta.4 // indirect
github.com/sagernet/reality v0.0.0-20230406110435-ee17307e7691 // indirect
github.com/sagernet/sing v0.5.1 // indirect
github.com/sagernet/sing-mux v0.2.1 // indirect
github.com/sagernet/sing-shadowtls v0.1.5 // indirect

View File

@@ -110,6 +110,8 @@ github.com/metacubex/quic-go v0.49.1-0.20250212162123-c135a4412996 h1:B+AP/Pj2/j
github.com/metacubex/quic-go v0.49.1-0.20250212162123-c135a4412996/go.mod h1:ExVjGyEwTUjCFqx+5uxgV7MOoA3fZI+th4D40H35xmY=
github.com/metacubex/randv2 v0.2.0 h1:uP38uBvV2SxYfLj53kuvAjbND4RUDfFJjwr4UigMiLs=
github.com/metacubex/randv2 v0.2.0/go.mod h1:kFi2SzrQ5WuneuoLLCMkABtiBu6VRrMrWFqSPyj2cxY=
github.com/metacubex/reality v0.0.0-20250219003814-74e8d7850629 h1:aHsYiTvubfgMa3JMTDY//hDXVvFWrHg6ARckR52ttZs=
github.com/metacubex/reality v0.0.0-20250219003814-74e8d7850629/go.mod h1:TTeIOZLdGmzc07Oedn++vWUUfkZoXLF4sEMxWuhBFr8=
github.com/metacubex/sing v0.0.0-20241121030428-33b6ebc52000 h1:gUbMXcQXhXGj0vCpCVFTUyIH7TMpD1dpTcNv/MCS+ok=
github.com/metacubex/sing v0.0.0-20241121030428-33b6ebc52000/go.mod h1:ARkL0gM13/Iv5VCZmci/NuoOlePoIsW0m7BWfln/Hak=
github.com/metacubex/sing-quic v0.0.0-20250119013740-2a19cce83925 h1:UkPoRAnoBQMn7IK5qpoIV3OejU15q+rqel3NrbSCFKA=
@@ -168,8 +170,6 @@ github.com/sagernet/netlink v0.0.0-20240612041022-b9a21c07ac6a h1:ObwtHN2VpqE0ZN
github.com/sagernet/netlink v0.0.0-20240612041022-b9a21c07ac6a/go.mod h1:xLnfdiJbSp8rNqYEdIW/6eDO4mVoogml14Bh2hSiFpM=
github.com/sagernet/nftables v0.3.0-beta.4 h1:kbULlAwAC3jvdGAC1P5Fa3GSxVwQJibNenDW2zaXr8I=
github.com/sagernet/nftables v0.3.0-beta.4/go.mod h1:OQXAjvjNGGFxaTgVCSTRIhYB5/llyVDeapVoENYBDS8=
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-mux v0.2.1 h1:N/3MHymfnFZRd29tE3TaXwPUVVgKvxhtOkiCMLp9HVo=
github.com/sagernet/sing-mux v0.2.1/go.mod h1:dm3BWL6NvES9pbib7llpylrq7Gq+LjlzG+0RacdxcyE=
github.com/sagernet/sing-shadowtls v0.1.5 h1:uXxmq/HXh8DIiBGLzpMjCbWnzIAFs+lIxiTOjdgG5qo=

View File

@@ -179,8 +179,8 @@ data class ConfigurationOverride(
@SerialName("enable")
var enable: Boolean? = null,
@SerialName("sniffing")
var sniffing: List<String>? = null,
@SerialName("sniff")
var sniff: Sniff = Sniff(),
@SerialName("force-dns-mapping")
var forceDnsMapping: Boolean? = null,
@@ -197,8 +197,11 @@ data class ConfigurationOverride(
@SerialName("skip-domain")
var skipDomain: List<String>? = null,
@SerialName("port-whitelist")
var portWhitelist: List<String>? = null,
@SerialName("skip-src-address")
var skipSrcAddress: List<String>? = null,
@SerialName("skip-dst-address")
var skipDstAddress: List<String>? = null,
)
@Serializable
@@ -222,6 +225,27 @@ data class ConfigurationOverride(
var allowPrivateNetwork: Boolean? = null,
)
@Serializable
data class Sniff(
@SerialName("HTTP")
var http: ProtocolConig = ProtocolConig(),
@SerialName("TLS")
var tls: ProtocolConig = ProtocolConig(),
@SerialName("QUIC")
var quic: ProtocolConig = ProtocolConig(),
)
@Serializable
data class ProtocolConig(
@SerialName("ports")
var ports: List<String>? = null,
@SerialName("override-destination")
var overrideDestination: Boolean? = null,
)
override fun writeToParcel(parcel: Parcel, flags: Int) {
Parcelizer.encodeToParcel(serializer(), parcel, this)
}

View File

@@ -137,13 +137,53 @@ class MetaFeatureSettingsDesign(
}
editableTextList(
value = configuration.sniffer::sniffing,
value = configuration.sniffer.sniff.http::ports,
adapter = TextAdapter.String,
title = R.string.sniffing,
title = R.string.sniff_http_ports,
placeholder = R.string.dont_modify,
configure = snifferDependencies::add,
)
selectableList(
value = configuration.sniffer.sniff.http::overrideDestination,
values = booleanValues,
valuesText = booleanValuesText,
title = R.string.sniff_http_override_destination,
configure = snifferDependencies::add,
)
editableTextList(
value = configuration.sniffer.sniff.tls::ports,
adapter = TextAdapter.String,
title = R.string.sniff_tls_ports,
placeholder = R.string.dont_modify,
configure = snifferDependencies::add,
)
selectableList(
value = configuration.sniffer.sniff.tls::overrideDestination,
values = booleanValues,
valuesText = booleanValuesText,
title = R.string.sniff_tls_override_destination,
configure = snifferDependencies::add,
)
editableTextList(
value = configuration.sniffer.sniff.quic::ports,
adapter = TextAdapter.String,
title = R.string.sniff_quic_ports,
placeholder = R.string.dont_modify,
configure = snifferDependencies::add,
)
selectableList(
value = configuration.sniffer.sniff.quic::overrideDestination,
values = booleanValues,
valuesText = booleanValuesText,
title = R.string.sniff_quic_override_destination,
configure = snifferDependencies::add,
)
selectableList(
value = configuration.sniffer::forceDnsMapping,
values = booleanValues,
@@ -185,9 +225,17 @@ class MetaFeatureSettingsDesign(
)
editableTextList(
value = configuration.sniffer::portWhitelist,
value = configuration.sniffer::skipSrcAddress,
adapter = TextAdapter.String,
title = R.string.port_whitelist,
title = R.string.skip_src_address,
placeholder = R.string.dont_modify,
configure = snifferDependencies::add,
)
editableTextList(
value = configuration.sniffer::skipDstAddress,
adapter = TextAdapter.String,
title = R.string.skip_dst_address,
placeholder = R.string.dont_modify,
configure = snifferDependencies::add,
)

View File

@@ -55,8 +55,9 @@ class ProvidersDesign(
fun requestUpdateAll() {
adapter.states.filter { !it.updating }.forEachIndexed { index, state ->
state.updating = true
requests.trySend(Request.Update(index, state.provider))
if (state.provider.vehicleType != Provider.VehicleType.Inline) {
requests.trySend(Request.Update(index, state.provider))
}
}
}
}

View File

@@ -55,10 +55,17 @@ class ProviderAdapter(
holder.binding.provider = state.provider
holder.binding.state = state
holder.binding.update = View.OnClickListener {
state.updating = true
requestUpdate(position, state.provider)
if (state.provider.vehicleType == Provider.VehicleType.Inline) {
holder.binding.endView.visibility = View.GONE
holder.binding.elapsedView.visibility = View.GONE
holder.binding.divider.visibility = View.GONE
} else {
holder.binding.endView.visibility = View.VISIBLE
holder.binding.elapsedView.visibility = View.VISIBLE
holder.binding.update = View.OnClickListener {
state.updating = true
requestUpdate(position, state.provider)
}
}
}

View File

@@ -58,6 +58,7 @@
android:text="@{IntervalKt.elapsedIntervalString(currentTime.value - state.updatedAt, context)}" />
<View
android:id="@+id/divider"
android:layout_width="@dimen/divider_size"
android:layout_height="wrap_content"
android:layout_centerVertical="true"

View File

@@ -133,9 +133,9 @@
<string name="fakeip">Fake-IP 至 域名映射</string>
<string name="blacklist">黑名单</string>
<string name="whitelist">白名单</string>
<string name="off">OFF</string>
<string name="strict">Strict</string>
<string name="always">Always</string>
<string name="off">关闭</string>
<string name="strict">严格</string>
<string name="always">强制开启</string>
<string name="sort">排序</string>
<string name="layout">布局</string>
<string name="single">单列</string>
@@ -220,40 +220,39 @@
<string name="allow_ipv6">允许 Ipv6</string>
<string name="allow_ipv6_summary">通过 VpnService 代理 Ipv6 流量</string>
<string name="clash_meta_wiki">Clash Meta Wiki</string>
<string name="meta_features">Meta Features</string>
<string name="unified_delay">Unified Delay</string>
<string name="geodata_mode">Geodata Mode</string>
<string name="tcp_concurrent">TCP Concurrent</string>
<string name="find_process_mode">Find Process Mode</string>
<string name="sniffer_setting">Sniffer Setting</string>
<string name="sniffer">Sniffer</string>
<string name="sniffing">Sniffer Mode</string>
<string name="force_domain">Force Domain</string>
<string name="skip_domain">Skip Domain</string>
<string name="disable_sniffer">Disable Sniffer</string>
<string name="sniffer_config">Load Sniffer From Config</string>
<string name="sniffer_override">Override Sniffer Config</string>
<string name="geox_url_setting">GeoX Url Setting</string>
<string name="geox_geoip">GeoIp Url</string>
<string name="geox_mmdb">MMDB Url</string>
<string name="geox_geosite">Geosite Url</string>
<string name="prefer_h3">Prefer h3</string>
<string name="port_whitelist">Port Whitelist</string>
<string name="geox_files" >Geo Files</string>
<string name="meta_features">Meta 特性</string>
<string name="unified_delay">统一延迟</string>
<string name="geodata_mode">Geodata 模式</string>
<string name="tcp_concurrent">TCP 并发</string>
<string name="find_process_mode">查找进程模式</string>
<string name="sniffer_setting">嗅探设置</string>
<string name="sniffer">嗅探器</string>
<string name="sniff">嗅探</string>
<string name="force_dns_mapping">强制 DNS 映射</string>
<string name="parse_pure_ip">解析纯 IP 连接</string>
<string name="override_destination">覆盖目标地址</string>
<string name="force_domain">强制解析域名</string>
<string name="skip_domain">跳过域名</string>
<string name="skip_src_address">跳过源 IP</string>
<string name="skip_dst_address">跳过目标 IP</string>
<string name="geox_url_setting">GeoX 链接设置</string>
<string name="geox_geoip">GeoIp 链接</string>
<string name="geox_mmdb">MMDB 链接</string>
<string name="geox_geosite">Geosite 链接</string>
<string name="prefer_h3">H3 优先</string>
<string name="geox_files" >Geo 文件</string>
<string name="import_geoip_file">导入 GeoIP 数据库</string>
<string name="press_to_import">Press to import...</string>
<string name="import_geosite_file">导入 GeoSite 数据库</string>
<string name="import_country_file">导入 Country 数据库</string>
<string name="import_asn_file">导入 ASN 数据库</string>
<string name="press_to_import">点击导入...</string>
<string name="geofile_import_failed">导入失败</string>
<string name="geofile_unknown_db_format">数据库类型错误</string>
<string name="geofile_unknown_db_format_message">只支持 %1$s 格式的 Geo 数据库</string>
<string name="geofile_imported">%1$s 已导入</string>
<string name="toast_profile_updated_complete">更新配置 %s 成功</string>
<string name="toast_profile_updated_failed">更新配置 %1$s 失败:%2$s</string>
<string name="external_control_activity">External Control</string>
<string name="external_control_activity">外部控制</string>
<string name="external_control_started">Clash.Meta 服务已启动</string>
<string name="external_control_stopped">Clash.Meta 服务已停止</string>
<string name="force_dns_mapping">Force DNS Mapping</string>
<string name="parse_pure_ip">Parse Pure IP</string>
<string name="override_destination">Override Destination</string>
</resources>
</resources>

View File

@@ -305,13 +305,19 @@
<string name="sniffer_setting">Sniffer Setting</string>
<string name="sniffer">Sniffer</string>
<string name="sniffing">Sniffer Mode</string>
<string name="force_dns_mapping">Force DNS Mapping</string>
<string name="parse_pure_ip">Parse Pure IP</string>
<string name="override_destination">Override Destination</string>
<string name="force_domain">Force Domain</string>
<string name="skip_domain">Skip Domain</string>
<string name="port_whitelist">Port Whitelist</string>
<string name="disable_sniffer">Disable Sniffer</string>
<string name="sniffer_config">Load Sniffer From Config</string>
<string name="sniffer_override">Override Sniffer Config</string>
<string name="skip_src_address">Skip Src Address</string>
<string name="skip_dst_address">Skip Dst Address</string>
<string name="sniff_http_ports">Sniff HTTP Ports</string>
<string name="sniff_tls_ports">Sniff TLS Ports</string>
<string name="sniff_quic_ports">Sniff QUIC Ports</string>
<string name="sniff_http_override_destination">Sniff HTTP Override Destination</string>
<string name="sniff_tls_override_destination">Sniff TLS Override Destination</string>
<string name="sniff_quic_override_destination">Sniff QUIC Override Destination</string>
<string name="geox_url_setting">GeoX Url Setting</string>
<string name="geox_geoip">GeoIp Url</string>
@@ -322,10 +328,10 @@
<string name="geosite_url" translatable="false">https://raw.githubusercontent.com/MetaCubeX/meta-rules-dat/release/geosite.dat</string>
<string name="geox_files" >Geo Files</string>
<string name="import_geoip_file">Import GeoIP Database</string>
<string name="press_to_import">Press to import...</string>
<string name="import_geosite_file">Import GeoSite Database</string>
<string name="import_country_file">Import Country Database</string>
<string name="import_asn_file">Import ASN Database</string>
<string name="press_to_import">Press to import...</string>
<string name="geofile_import_failed">Import failed</string>
<string name="geofile_unknown_db_format">Unknown Database format</string>
<string name="geofile_unknown_db_format_message">Only %1$s are supported</string>
@@ -335,7 +341,4 @@
<string name="external_control_activity">External Control</string>
<string name="external_control_started">Clash.Meta service started</string>
<string name="external_control_stopped">Clash.Meta service stopped</string>
<string name="force_dns_mapping">Force DNS Mapping</string>
<string name="parse_pure_ip">Parse Pure IP</string>
<string name="override_destination">Override Destination</string>
</resources>