Chore: delay reject

This commit is contained in:
yaling888 2022-05-04 19:49:04 +08:00
parent 3c07ba6b56
commit 045b67524c
6 changed files with 71 additions and 50 deletions

View File

@ -3,6 +3,8 @@ package outbound
import ( import (
"context" "context"
"errors" "errors"
"net"
"time"
"github.com/Dreamacro/clash/component/dialer" "github.com/Dreamacro/clash/component/dialer"
"github.com/Dreamacro/clash/component/trie" "github.com/Dreamacro/clash/component/trie"
@ -12,50 +14,50 @@ import (
var ( var (
errIgnored = errors.New("not match in mitm host lists") errIgnored = errors.New("not match in mitm host lists")
httpProxyClient = NewHttp(HttpOption{}) httpProxyClient = NewHttp(HttpOption{})
rewriteHosts *trie.DomainTrie[bool]
MiddlemanRewriteHosts *trie.DomainTrie[bool]
) )
type Mitm struct { type Mitm struct {
*Base *Base
serverAddr string serverAddr *net.TCPAddr
} }
// DialContext implements C.ProxyAdapter // DialContext implements C.ProxyAdapter
func (m *Mitm) DialContext(ctx context.Context, metadata *C.Metadata, _ ...dialer.Option) (C.Conn, error) { func (m *Mitm) DialContext(_ context.Context, metadata *C.Metadata, _ ...dialer.Option) (C.Conn, error) {
if MiddlemanRewriteHosts == nil { if (rewriteHosts == nil || rewriteHosts.Search(metadata.String()) == nil) && metadata.DstPort != "80" {
return nil, errIgnored return nil, errIgnored
} }
if MiddlemanRewriteHosts.Search(metadata.String()) == nil && metadata.DstPort != "80" { c, err := net.DialTCP("tcp", nil, m.serverAddr)
return nil, errIgnored if err != nil {
return nil, err
} }
_ = c.SetKeepAlive(true)
_ = c.SetKeepAlivePeriod(60 * time.Second)
metadata.Type = C.MITM metadata.Type = C.MITM
c, err := dialer.DialContext(ctx, "tcp", m.serverAddr, []dialer.Option{dialer.WithInterface(""), dialer.WithRoutingMark(0), dialer.WithDirect()}...) hc, err := httpProxyClient.StreamConn(c, metadata)
if err != nil { if err != nil {
_ = c.Close()
return nil, err return nil, err
} }
tcpKeepAlive(c) return NewConn(hc, m), nil
defer safeConnClose(c, err)
c, err = httpProxyClient.StreamConn(c, metadata)
if err != nil {
return nil, err
}
return NewConn(c, m), nil
} }
func NewMitm(serverAddr string) *Mitm { func NewMitm(serverAddr string) *Mitm {
tcpAddr, _ := net.ResolveTCPAddr("tcp", serverAddr)
return &Mitm{ return &Mitm{
Base: &Base{ Base: &Base{
name: "Mitm", name: "Mitm",
tp: C.Mitm, tp: C.Mitm,
}, },
serverAddr: serverAddr, serverAddr: tcpAddr,
} }
} }
func UpdateRewriteHosts(hosts *trie.DomainTrie[bool]) {
rewriteHosts = hosts
}

View File

@ -6,16 +6,43 @@ import (
"net" "net"
"time" "time"
"github.com/Dreamacro/clash/common/cache"
"github.com/Dreamacro/clash/component/dialer" "github.com/Dreamacro/clash/component/dialer"
C "github.com/Dreamacro/clash/constant" C "github.com/Dreamacro/clash/constant"
) )
const (
rejectCountLimit = 50
rejectDelay = time.Second * 35
)
var rejectCounter = cache.NewLRUCache[string, int](cache.WithAge[string, int](15), cache.WithStale[string, int](false), cache.WithSize[string, int](512))
type Reject struct { type Reject struct {
*Base *Base
} }
// DialContext implements C.ProxyAdapter // DialContext implements C.ProxyAdapter
func (r *Reject) DialContext(ctx context.Context, metadata *C.Metadata, opts ...dialer.Option) (C.Conn, error) { func (r *Reject) DialContext(ctx context.Context, metadata *C.Metadata, opts ...dialer.Option) (C.Conn, error) {
key := metadata.RemoteAddress()
count, existed := rejectCounter.Get(key)
if !existed {
count = 0
}
count = count + 1
rejectCounter.Set(key, count)
if count > rejectCountLimit {
c, _ := net.Pipe()
_ = c.SetDeadline(time.Now().Add(rejectDelay))
return NewConn(c, r), nil
}
return NewConn(&nopConn{}, r), nil return NewConn(&nopConn{}, r), nil
} }

View File

@ -333,7 +333,7 @@ func updateIPTables(cfg *config.Config) {
} }
func updateMitm(mitm *config.Mitm) { func updateMitm(mitm *config.Mitm) {
outbound.MiddlemanRewriteHosts = mitm.Hosts outbound.UpdateRewriteHosts(mitm.Hosts)
tunnel.UpdateRewrites(mitm.Rules) tunnel.UpdateRewrites(mitm.Rules)
} }

View File

@ -347,11 +347,11 @@ func ReCreateTun(tunConf *config.Tun, tunAddressPrefix *netip.Prefix, tcpIn chan
tunAddressPrefix = lastTunAddressPrefix tunAddressPrefix = lastTunAddressPrefix
} }
if tunStackListener != nil {
if !hasTunConfigChange(tunConf, tunAddressPrefix) { if !hasTunConfigChange(tunConf, tunAddressPrefix) {
return return
} }
if tunStackListener != nil {
_ = tunStackListener.Close() _ = tunStackListener.Close()
tunStackListener = nil tunStackListener = nil
lastTunConf = nil lastTunConf = nil
@ -388,9 +388,9 @@ func ReCreateMitm(port int, tcpIn chan<- C.ConnContext) {
if mitmListener.RawAddress() == addr { if mitmListener.RawAddress() == addr {
return return
} }
tunnel.MitmOutbound = nil
_ = mitmListener.Close() _ = mitmListener.Close()
mitmListener = nil mitmListener = nil
tunnel.SetMitmOutbound(nil)
} }
if portIsZero(addr) { if portIsZero(addr) {
@ -442,7 +442,7 @@ func ReCreateMitm(port int, tcpIn chan<- C.ConnContext) {
return return
} }
tunnel.MitmOutbound = outbound.NewMitm(mitmListener.Address()) tunnel.SetMitmOutbound(outbound.NewMitm(mitmListener.Address()))
log.Infoln("Mitm proxy listening at: %s", mitmListener.Address()) log.Infoln("Mitm proxy listening at: %s", mitmListener.Address())
} }

View File

@ -1,12 +1,6 @@
package rewrites package rewrites
import ( import (
"bytes"
"fmt"
"image"
"image/color"
"image/draw"
"image/png"
"regexp" "regexp"
"testing" "testing"
@ -41,16 +35,3 @@ func TestParseRewrite(t *testing.T) {
assert.Equal(t, c2.RuleRegx(), regexp.MustCompile(`(\r\n)User-Agent:.+(\r\n)`)) assert.Equal(t, c2.RuleRegx(), regexp.MustCompile(`(\r\n)User-Agent:.+(\r\n)`))
assert.Equal(t, c2.RulePayload(), "$1User-Agent: Fuck-Who$2") assert.Equal(t, c2.RulePayload(), "$1User-Agent: Fuck-Who$2")
} }
func Test1PxPNG(t *testing.T) {
m := image.NewRGBA(image.Rect(0, 0, 1, 1))
draw.Draw(m, m.Bounds(), &image.Uniform{C: color.Transparent}, image.Point{}, draw.Src)
buf := &bytes.Buffer{}
assert.Nil(t, png.Encode(buf, m))
fmt.Printf("len: %d\n", buf.Len())
fmt.Printf("% #x\n", buf.Bytes())
}

View File

@ -38,8 +38,8 @@ var (
// default timeout for UDP session // default timeout for UDP session
udpTimeout = 60 * time.Second udpTimeout = 60 * time.Second
// MitmOutbound mitm proxy adapter // mitmOutbound mitm proxy adapter
MitmOutbound C.ProxyAdapter mitmOutbound C.ProxyAdapter
) )
func init() { func init() {
@ -96,6 +96,11 @@ func SetMode(m TunnelMode) {
mode = m mode = m
} }
// SetMitmOutbound set the MITM outbound
func SetMitmOutbound(outbound C.ProxyAdapter) {
mitmOutbound = outbound
}
// Rewrites return all rewrites // Rewrites return all rewrites
func Rewrites() C.RewriteRule { func Rewrites() C.RewriteRule {
return rewrites return rewrites
@ -302,9 +307,11 @@ func handleTCPConn(connCtx C.ConnContext) {
ctx, cancel := context.WithTimeout(context.Background(), C.DefaultTCPTimeout) ctx, cancel := context.WithTimeout(context.Background(), C.DefaultTCPTimeout)
defer cancel() defer cancel()
if MitmOutbound != nil && metadata.Type != C.MITM {
if remoteConn, err1 := MitmOutbound.DialContext(ctx, metadata); err1 == nil { if mitmOutbound != nil && metadata.Type != C.MITM {
if remoteConn, err1 := mitmOutbound.DialContext(ctx, metadata); err1 == nil {
remoteConn = statistic.NewSniffing(remoteConn, metadata, nil) remoteConn = statistic.NewSniffing(remoteConn, metadata, nil)
defer func(remoteConn C.Conn) { defer func(remoteConn C.Conn) {
_ = remoteConn.Close() _ = remoteConn.Close()
}(remoteConn) }(remoteConn)
@ -329,7 +336,11 @@ func handleTCPConn(connCtx C.ConnContext) {
} }
return return
} }
if remoteConn.Chains().Last() != "REJECT" {
remoteConn = statistic.NewTCPTracker(remoteConn, statistic.DefaultManager, metadata, rule) remoteConn = statistic.NewTCPTracker(remoteConn, statistic.DefaultManager, metadata, rule)
}
defer func(remoteConn C.Conn) { defer func(remoteConn C.Conn) {
_ = remoteConn.Close() _ = remoteConn.Close()
}(remoteConn) }(remoteConn)