Chore: merge branch 'with-tun' into plus-pro

This commit is contained in:
yaling888 2022-05-09 01:56:32 +08:00
commit 934babca85
6 changed files with 67 additions and 95 deletions

View File

@ -20,10 +20,18 @@ jobs:
- name: Check out code into the Go module directory - name: Check out code into the Go module directory
uses: actions/checkout@v3 uses: actions/checkout@v3
- name: Go cache paths
id: go-cache-paths
run: |
echo "::set-output name=go-build::$(go env GOCACHE)"
echo "::set-output name=go-mod::$(go env GOMODCACHE)"
- name: Cache go module - name: Cache go module
uses: actions/cache@v2 uses: actions/cache@v2
with: with:
path: ~/go/pkg/mod path: |
${{ steps.go-cache-paths.outputs.go-mod }}
${{ steps.go-cache-paths.outputs.go-build }}
key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }} key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }}
restore-keys: | restore-keys: |
${{ runner.os }}-go- ${{ runner.os }}-go-
@ -92,6 +100,8 @@ jobs:
with: with:
files: bin/* files: bin/*
draft: true draft: true
prerelease: true
generate_release_notes: true
- name: Delete workflow runs - name: Delete workflow runs
uses: GitRML/delete-workflow-runs@main uses: GitRML/delete-workflow-runs@main

View File

@ -40,7 +40,7 @@ Documentations are now moved to [GitHub Wiki](https://github.com/Dreamacro/clash
A root CA certificate is required, the A root CA certificate is required, the
MITM proxy server will generate a CA certificate file and a CA private key file in your Clash home directory, you can use your own certificate replace it. MITM proxy server will generate a CA certificate file and a CA private key file in your Clash home directory, you can use your own certificate replace it.
Need to install and trust the CA certificate on the client device, open this URL http://mitm.clash/cert.crt by the web browser to install the CA certificate, the host name 'mitm.clash' was always been hijacked. Need to install and trust the CA certificate on the client device, open this URL [http://mitm.clash/cert.crt](http://mitm.clash/cert.crt) by the web browser to install the CA certificate, the host name 'mitm.clash' was always been hijacked.
NOTE: this feature cannot work on tls pinning NOTE: this feature cannot work on tls pinning
@ -329,9 +329,21 @@ $ systemctl start clash
``` ```
### Display Process name ### Display Process name
Add field `Process` to `Metadata` and prepare to get process name for Restful API `GET /connections`. To display process name online by click [https://yaling888.github.io/yacd/](https://yaling888.github.io/yacd/).
To display process name in GUI please use https://yaling888.github.io/yacd/. You can download the [Dashboard](https://github.com/yaling888/yacd/archive/gh-pages.zip) into Clash home directory:
```shell
cd ~/.config/clash
curl -LJ https://github.com/yaling888/yacd/archive/gh-pages.zip -o dashboard.zip
unzip dashboard.zip
```
Add to config file:
```yaml
external-controller: 127.0.0.1:9090
external-ui: dashboard
```
Open [http://127.0.0.1:9090/ui/](http://127.0.0.1:9090/ui/) by web browser.
## Development ## Development
If you want to build an application that uses clash as a library, check out the the [GitHub Wiki](https://github.com/Dreamacro/clash/wiki/use-clash-as-a-library) If you want to build an application that uses clash as a library, check out the the [GitHub Wiki](https://github.com/Dreamacro/clash/wiki/use-clash-as-a-library)

View File

@ -2,32 +2,21 @@ package outbound
import ( import (
"context" "context"
"errors"
"net" "net"
"time" "time"
"github.com/Dreamacro/clash/component/dialer" "github.com/Dreamacro/clash/component/dialer"
"github.com/Dreamacro/clash/component/trie"
C "github.com/Dreamacro/clash/constant" C "github.com/Dreamacro/clash/constant"
) )
var (
errIgnored = errors.New("not match in mitm host lists")
httpProxyClient = NewHttp(HttpOption{})
rewriteHosts *trie.DomainTrie[bool]
)
type Mitm struct { type Mitm struct {
*Base *Base
serverAddr *net.TCPAddr serverAddr *net.TCPAddr
httpProxyClient *Http
} }
// DialContext implements C.ProxyAdapter // DialContext implements C.ProxyAdapter
func (m *Mitm) DialContext(_ 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 (rewriteHosts == nil || rewriteHosts.Search(metadata.String()) == nil) && metadata.DstPort != "80" {
return nil, errIgnored
}
c, err := net.DialTCP("tcp", nil, m.serverAddr) c, err := net.DialTCP("tcp", nil, m.serverAddr)
if err != nil { if err != nil {
return nil, err return nil, err
@ -38,7 +27,7 @@ func (m *Mitm) DialContext(_ context.Context, metadata *C.Metadata, _ ...dialer.
metadata.Type = C.MITM metadata.Type = C.MITM
hc, err := httpProxyClient.StreamConn(c, metadata) hc, err := m.httpProxyClient.StreamConn(c, metadata)
if err != nil { if err != nil {
_ = c.Close() _ = c.Close()
return nil, err return nil, err
@ -54,10 +43,7 @@ func NewMitm(serverAddr string) *Mitm {
name: "Mitm", name: "Mitm",
tp: C.Mitm, tp: C.Mitm,
}, },
serverAddr: tcpAddr, serverAddr: tcpAddr,
httpProxyClient: NewHttp(HttpOption{}),
} }
} }
func UpdateRewriteHosts(hosts *trie.DomainTrie[bool]) {
rewriteHosts = hosts
}

View File

@ -8,7 +8,6 @@ import (
"sync" "sync"
"github.com/Dreamacro/clash/adapter" "github.com/Dreamacro/clash/adapter"
"github.com/Dreamacro/clash/adapter/outbound"
"github.com/Dreamacro/clash/adapter/outboundgroup" "github.com/Dreamacro/clash/adapter/outboundgroup"
"github.com/Dreamacro/clash/component/auth" "github.com/Dreamacro/clash/component/auth"
"github.com/Dreamacro/clash/component/dialer" "github.com/Dreamacro/clash/component/dialer"
@ -339,8 +338,7 @@ func updateIPTables(cfg *config.Config) {
} }
func updateMitm(mitm *config.Mitm) { func updateMitm(mitm *config.Mitm) {
outbound.UpdateRewriteHosts(mitm.Hosts) tunnel.UpdateRewrites(mitm.Hosts, mitm.Rules)
tunnel.UpdateRewrites(mitm.Rules)
} }
func Shutdown() { func Shutdown() {

View File

@ -1,37 +0,0 @@
package rewrites
import (
"regexp"
"testing"
"github.com/Dreamacro/clash/constant"
"github.com/stretchr/testify/assert"
)
func TestParseRewrite(t *testing.T) {
line0 := `^https?://example\.com/resource1/3/ url reject-dict`
line1 := `^https?://example\.com/(resource2)/ url 307 https://example.com/new-$1`
line2 := `^https?://example\.com/resource4/ url request-header (\r\n)User-Agent:.+(\r\n) request-header $1User-Agent: Fuck-Who$2`
line3 := `should be error`
c0, err0 := ParseRewrite(line0)
c1, err1 := ParseRewrite(line1)
c2, err2 := ParseRewrite(line2)
_, err3 := ParseRewrite(line3)
assert.NotNil(t, err3)
assert.Nil(t, err0)
assert.Equal(t, c0.RuleType(), constant.MitmRejectDict)
assert.Nil(t, err1)
assert.Equal(t, c1.RuleType(), constant.Mitm307)
assert.Equal(t, c1.URLRegx(), regexp.MustCompile(`^https?://example\.com/(resource2)/`))
assert.Equal(t, c1.RulePayload(), "https://example.com/new-$1")
assert.Nil(t, err2)
assert.Equal(t, c2.RuleType(), constant.MitmRequestHeader)
assert.Equal(t, c2.RuleRegx(), regexp.MustCompile(`(\r\n)User-Agent:.+(\r\n)`))
assert.Equal(t, c2.RulePayload(), "$1User-Agent: Fuck-Who$2")
}

View File

@ -11,11 +11,13 @@ import (
"sync" "sync"
"time" "time"
A "github.com/Dreamacro/clash/adapter"
"github.com/Dreamacro/clash/adapter/inbound" "github.com/Dreamacro/clash/adapter/inbound"
"github.com/Dreamacro/clash/component/nat" "github.com/Dreamacro/clash/component/nat"
P "github.com/Dreamacro/clash/component/process" P "github.com/Dreamacro/clash/component/process"
"github.com/Dreamacro/clash/component/resolver" "github.com/Dreamacro/clash/component/resolver"
S "github.com/Dreamacro/clash/component/script" S "github.com/Dreamacro/clash/component/script"
"github.com/Dreamacro/clash/component/trie"
C "github.com/Dreamacro/clash/constant" C "github.com/Dreamacro/clash/constant"
"github.com/Dreamacro/clash/constant/provider" "github.com/Dreamacro/clash/constant/provider"
icontext "github.com/Dreamacro/clash/context" icontext "github.com/Dreamacro/clash/context"
@ -24,14 +26,15 @@ import (
) )
var ( var (
tcpQueue = make(chan C.ConnContext, 200) tcpQueue = make(chan C.ConnContext, 200)
udpQueue = make(chan *inbound.PacketAdapter, 200) udpQueue = make(chan *inbound.PacketAdapter, 200)
natTable = nat.New() natTable = nat.New()
rules []C.Rule rules []C.Rule
rewrites C.RewriteRule proxies = make(map[string]C.Proxy)
proxies = make(map[string]C.Proxy) providers map[string]provider.ProxyProvider
providers map[string]provider.ProxyProvider rewrites C.RewriteRule
configMux sync.RWMutex rewriteHosts *trie.DomainTrie[bool]
configMux sync.RWMutex
// Outbound Rule // Outbound Rule
mode = Rule mode = Rule
@ -39,8 +42,8 @@ var (
// default timeout for UDP session // default timeout for UDP session
udpTimeout = 60 * time.Second udpTimeout = 60 * time.Second
// mitmOutbound mitm proxy adapter // mitmProxy mitm proxy
mitmOutbound C.ProxyAdapter mitmProxy C.Proxy
) )
func init() { func init() {
@ -99,7 +102,7 @@ func SetMode(m TunnelMode) {
// SetMitmOutbound set the MITM outbound // SetMitmOutbound set the MITM outbound
func SetMitmOutbound(outbound C.ProxyAdapter) { func SetMitmOutbound(outbound C.ProxyAdapter) {
mitmOutbound = outbound mitmProxy = A.NewProxy(outbound)
} }
// Rewrites return all rewrites // Rewrites return all rewrites
@ -108,9 +111,10 @@ func Rewrites() C.RewriteRule {
} }
// UpdateRewrites handle update rewrites // UpdateRewrites handle update rewrites
func UpdateRewrites(newRewrites C.RewriteRule) { func UpdateRewrites(hosts *trie.DomainTrie[bool], rules C.RewriteRule) {
configMux.Lock() configMux.Lock()
rewrites = newRewrites rewriteHosts = hosts
rewrites = rules
configMux.Unlock() configMux.Unlock()
} }
@ -179,7 +183,7 @@ func preHandleMetadata(metadata *C.Metadata) error {
if err != nil { if err != nil {
log.Debugln("[Process] find process %s: %v", metadata.String(), err) log.Debugln("[Process] find process %s: %v", metadata.String(), err)
} else { } else {
log.Debugln("[Process] %s from process %s", metadata.String(), path) // log.Debugln("[Process] %s from process %s", metadata.String(), path)
metadata.Process = filepath.Base(path) metadata.Process = filepath.Base(path)
metadata.ProcessPath = path metadata.ProcessPath = path
} }
@ -189,6 +193,12 @@ func preHandleMetadata(metadata *C.Metadata) error {
} }
func resolveMetadata(_ C.PlainContext, metadata *C.Metadata) (proxy C.Proxy, rule C.Rule, err error) { func resolveMetadata(_ C.PlainContext, metadata *C.Metadata) (proxy C.Proxy, rule C.Rule, err error) {
if metadata.NetWork == C.TCP && mitmProxy != nil && metadata.Type != C.MITM &&
((rewriteHosts != nil && rewriteHosts.Search(metadata.String()) != nil) || metadata.DstPort == "80") {
proxy = mitmProxy
return
}
switch mode { switch mode {
case Direct: case Direct:
proxy = proxies["DIRECT"] proxy = proxies["DIRECT"]
@ -310,29 +320,20 @@ func handleTCPConn(connCtx C.ConnContext) {
return return
} }
ctx, cancel := context.WithTimeout(context.Background(), C.DefaultTCPTimeout)
defer cancel()
if mitmOutbound != nil && metadata.Type != C.MITM {
if remoteConn, err1 := mitmOutbound.DialContext(ctx, metadata); err1 == nil {
remoteConn = statistic.NewSniffing(remoteConn, metadata, nil)
defer func(remoteConn C.Conn) {
_ = remoteConn.Close()
}(remoteConn)
handleSocket(connCtx, remoteConn)
return
}
}
proxy, rule, err := resolveMetadata(connCtx, metadata) proxy, rule, err := resolveMetadata(connCtx, metadata)
if err != nil { if err != nil {
log.Warnln("[Metadata] parse failed: %s", err.Error()) log.Warnln("[Metadata] parse failed: %s", err.Error())
return return
} }
remoteConn, err := proxy.DialContext(ctx, metadata.Pure()) mtd := metadata
if proxy != mitmProxy {
mtd = metadata.Pure()
}
ctx, cancel := context.WithTimeout(context.Background(), C.DefaultTCPTimeout)
defer cancel()
remoteConn, err := proxy.DialContext(ctx, mtd)
if err != nil { if err != nil {
if rule == nil { if rule == nil {
log.Warnln("[TCP] dial %s to %s error: %s", proxy.Name(), metadata.RemoteAddress(), err.Error()) log.Warnln("[TCP] dial %s to %s error: %s", proxy.Name(), metadata.RemoteAddress(), err.Error())
@ -342,7 +343,7 @@ func handleTCPConn(connCtx C.ConnContext) {
return return
} }
if remoteConn.Chains().Last() != "REJECT" { if remoteConn.Chains().Last() != "REJECT" && proxy != mitmProxy {
remoteConn = statistic.NewTCPTracker(remoteConn, statistic.DefaultManager, metadata, rule) remoteConn = statistic.NewTCPTracker(remoteConn, statistic.DefaultManager, metadata, rule)
} }
@ -351,6 +352,8 @@ func handleTCPConn(connCtx C.ConnContext) {
}(remoteConn) }(remoteConn)
switch true { switch true {
case proxy == mitmProxy:
break
case rule != nil: case rule != nil:
log.Infoln("[TCP] %s(%s) --> %s match %s(%s) using %s", metadata.SourceAddress(), metadata.Process, metadata.RemoteAddress(), rule.RuleType().String(), rule.Payload(), remoteConn.Chains().String()) log.Infoln("[TCP] %s(%s) --> %s match %s(%s) using %s", metadata.SourceAddress(), metadata.Process, metadata.RemoteAddress(), rule.RuleType().String(), rule.Payload(), remoteConn.Chains().String())
case mode == Script: case mode == Script: