Merge remote-tracking branch 'clash/dev' into Alpha

# Conflicts:
#	.github/workflows/codeql-analysis.yml
#	.github/workflows/docker.yml
#	.github/workflows/linter.yml
#	.github/workflows/stale.yml
#	Makefile
#	component/dialer/dialer.go
#	config/config.go
#	constant/metadata.go
#	constant/rule.go
#	rule/common/domain.go
#	rule/common/domain_keyword.go
#	rule/common/domain_suffix.go
#	rule/common/final.go
#	rule/common/ipcidr.go
#	rule/geoip.go
#	rule/parser.go
#	rule/port.go
#	rule/process.go
This commit is contained in:
MetaCubeX 2022-03-15 23:13:41 +08:00
commit f01ac69654
32 changed files with 123 additions and 175 deletions

View file

@ -15,7 +15,7 @@ jobs:
go-version: ${{ steps.version.outputs.go_version }} go-version: ${{ steps.version.outputs.go_version }}
- name: Check out code into the Go module directory - name: Check out code into the Go module directory
uses: actions/checkout@v2 uses: actions/checkout@v3
- name: Cache go module - name: Cache go module
uses: actions/cache@v2 uses: actions/cache@v2

14
.golangci.yaml Normal file
View file

@ -0,0 +1,14 @@
linters:
disable-all: true
enable:
- gofumpt
- megacheck
- govet
- gci
linters-settings:
gci:
sections:
- standard
- prefix(github.com/Dreamacro/clash)
- default

View file

@ -12,7 +12,7 @@ RUN go mod download && \
FROM alpine:latest FROM alpine:latest
LABEL org.opencontainers.image.source="https://github.com/Dreamacro/clash" LABEL org.opencontainers.image.source="https://github.com/Dreamacro/clash"
RUN apk add --no-cache ca-certificates RUN apk add --no-cache ca-certificates tzdata
COPY --from=builder /Country.mmdb /root/.config/clash/ COPY --from=builder /Country.mmdb /root/.config/clash/
COPY --from=builder /clash / COPY --from=builder /clash /
ENTRYPOINT ["/clash"] ENTRYPOINT ["/clash"]

View file

@ -4,9 +4,9 @@ import (
"net" "net"
"syscall" "syscall"
"golang.org/x/sys/unix"
"github.com/Dreamacro/clash/component/iface" "github.com/Dreamacro/clash/component/iface"
"golang.org/x/sys/unix"
) )
type controlFn = func(network, address string, c syscall.RawConn) error type controlFn = func(network, address string, c syscall.RawConn) error

View file

@ -3,7 +3,6 @@ package process
import ( import (
"encoding/binary" "encoding/binary"
"net" "net"
"path/filepath"
"syscall" "syscall"
"unsafe" "unsafe"
@ -96,7 +95,7 @@ func getExecPathFromPID(pid uint32) (string, error) {
return "", errno return "", errno
} }
return filepath.Base(unix.ByteSliceToString(buf)), nil return unix.ByteSliceToString(buf), nil
} }
func readNativeUint32(b []byte) uint32 { func readNativeUint32(b []byte) uint32 {

View file

@ -4,7 +4,6 @@ import (
"encoding/binary" "encoding/binary"
"fmt" "fmt"
"net" "net"
"path/filepath"
"strconv" "strconv"
"strings" "strings"
"sync" "sync"
@ -77,7 +76,7 @@ func getExecPathFromPID(pid uint32) (string, error) {
return "", errno return "", errno
} }
return filepath.Base(string(buf[:size-1])), nil return string(buf[:size-1]), nil
} }
func readNativeUint32(b []byte) uint32 { func readNativeUint32(b []byte) uint32 {

View file

@ -4,12 +4,12 @@ import (
"bytes" "bytes"
"encoding/binary" "encoding/binary"
"fmt" "fmt"
"io"
"net" "net"
"os" "os"
"path" "path"
"path/filepath" "strings"
"syscall" "syscall"
"unicode"
"unsafe" "unsafe"
"github.com/Dreamacro/clash/common/pool" "github.com/Dreamacro/clash/common/pool"
@ -25,17 +25,6 @@ var nativeEndian = func() binary.ByteOrder {
return binary.LittleEndian return binary.LittleEndian
}() }()
type (
SocketResolver func(network string, ip net.IP, srcPort int) (inode, uid int, err error)
ProcessNameResolver func(inode, uid int) (name string, err error)
)
// export for android
var (
DefaultSocketResolver SocketResolver = resolveSocketByNetlink
DefaultProcessNameResolver ProcessNameResolver = resolveProcessNameByProcSearch
)
const ( const (
sizeOfSocketDiagRequest = syscall.SizeofNlMsghdr + 8 + 48 sizeOfSocketDiagRequest = syscall.SizeofNlMsghdr + 8 + 48
socketDiagByFamily = 20 socketDiagByFamily = 20
@ -43,15 +32,15 @@ const (
) )
func findProcessName(network string, ip net.IP, srcPort int) (string, error) { func findProcessName(network string, ip net.IP, srcPort int) (string, error) {
inode, uid, err := DefaultSocketResolver(network, ip, srcPort) inode, uid, err := resolveSocketByNetlink(network, ip, srcPort)
if err != nil { if err != nil {
return "", err return "", err
} }
return DefaultProcessNameResolver(inode, uid) return resolveProcessNameByProcSearch(inode, uid)
} }
func resolveSocketByNetlink(network string, ip net.IP, srcPort int) (int, int, error) { func resolveSocketByNetlink(network string, ip net.IP, srcPort int) (int32, int32, error) {
var family byte var family byte
var protocol byte var protocol byte
@ -74,13 +63,12 @@ func resolveSocketByNetlink(network string, ip net.IP, srcPort int) (int, int, e
socket, err := syscall.Socket(syscall.AF_NETLINK, syscall.SOCK_DGRAM, syscall.NETLINK_INET_DIAG) socket, err := syscall.Socket(syscall.AF_NETLINK, syscall.SOCK_DGRAM, syscall.NETLINK_INET_DIAG)
if err != nil { if err != nil {
return 0, 0, err return 0, 0, fmt.Errorf("dial netlink: %w", err)
} }
defer syscall.Close(socket) defer syscall.Close(socket)
syscall.SetNonblock(socket, true) syscall.SetsockoptTimeval(socket, syscall.SOL_SOCKET, syscall.SO_SNDTIMEO, &syscall.Timeval{Usec: 100})
syscall.SetsockoptTimeval(socket, syscall.SOL_SOCKET, syscall.SO_SNDTIMEO, &syscall.Timeval{Usec: 50}) syscall.SetsockoptTimeval(socket, syscall.SOL_SOCKET, syscall.SO_RCVTIMEO, &syscall.Timeval{Usec: 100})
syscall.SetsockoptTimeval(socket, syscall.SOL_SOCKET, syscall.SO_RCVTIMEO, &syscall.Timeval{Usec: 50})
if err := syscall.Connect(socket, &syscall.SockaddrNetlink{ if err := syscall.Connect(socket, &syscall.SockaddrNetlink{
Family: syscall.AF_NETLINK, Family: syscall.AF_NETLINK,
@ -92,7 +80,7 @@ func resolveSocketByNetlink(network string, ip net.IP, srcPort int) (int, int, e
} }
if _, err := syscall.Write(socket, req); err != nil { if _, err := syscall.Write(socket, req); err != nil {
return 0, 0, err return 0, 0, fmt.Errorf("write request: %w", err)
} }
rb := pool.Get(pool.RelayBufferSize) rb := pool.Get(pool.RelayBufferSize)
@ -100,24 +88,27 @@ func resolveSocketByNetlink(network string, ip net.IP, srcPort int) (int, int, e
n, err := syscall.Read(socket, rb) n, err := syscall.Read(socket, rb)
if err != nil { if err != nil {
return 0, 0, err return 0, 0, fmt.Errorf("read response: %w", err)
} }
messages, err := syscall.ParseNetlinkMessage(rb[:n]) messages, err := syscall.ParseNetlinkMessage(rb[:n])
if err != nil { if err != nil {
return 0, 0, err return 0, 0, fmt.Errorf("parse netlink message: %w", err)
} else if len(messages) == 0 { } else if len(messages) == 0 {
return 0, 0, io.ErrUnexpectedEOF return 0, 0, fmt.Errorf("unexcepted netlink response")
} }
message := messages[0] message := messages[0]
if message.Header.Type&syscall.NLMSG_ERROR != 0 { if message.Header.Type&syscall.NLMSG_ERROR != 0 {
return 0, 0, syscall.ESRCH return 0, 0, fmt.Errorf("netlink message: NLMSG_ERROR")
} }
uid, inode := unpackSocketDiagResponse(&messages[0]) uid, inode := unpackSocketDiagResponse(&messages[0])
if uid < 0 || inode < 0 {
return 0, 0, fmt.Errorf("invalid uid(%d) or inode(%d)", uid, inode)
}
return int(uid), int(inode), nil return uid, inode, nil
} }
func packSocketDiagRequest(family, protocol byte, source net.IP, sourcePort uint16) []byte { func packSocketDiagRequest(family, protocol byte, source net.IP, sourcePort uint16) []byte {
@ -155,20 +146,20 @@ func packSocketDiagRequest(family, protocol byte, source net.IP, sourcePort uint
return buf return buf
} }
func unpackSocketDiagResponse(msg *syscall.NetlinkMessage) (inode, uid uint32) { func unpackSocketDiagResponse(msg *syscall.NetlinkMessage) (inode, uid int32) {
if len(msg.Data) < 72 { if len(msg.Data) < 72 {
return 0, 0 return 0, 0
} }
data := msg.Data data := msg.Data
uid = nativeEndian.Uint32(data[64:68]) uid = int32(nativeEndian.Uint32(data[64:68]))
inode = nativeEndian.Uint32(data[68:72]) inode = int32(nativeEndian.Uint32(data[68:72]))
return return
} }
func resolveProcessNameByProcSearch(inode, uid int) (string, error) { func resolveProcessNameByProcSearch(inode, uid int32) (string, error) {
files, err := os.ReadDir(pathProc) files, err := os.ReadDir(pathProc)
if err != nil { if err != nil {
return "", err return "", err
@ -205,38 +196,16 @@ func resolveProcessNameByProcSearch(inode, uid int) (string, error) {
} }
if bytes.Equal(buffer[:n], socket) { if bytes.Equal(buffer[:n], socket) {
cmdline, err := os.ReadFile(path.Join(processPath, "cmdline")) return os.Readlink(path.Join(processPath, "exe"))
if err != nil {
return "", err
}
return splitCmdline(cmdline), nil
} }
} }
} }
return "", syscall.ESRCH return "", fmt.Errorf("process of uid(%d),inode(%d) not found", uid, inode)
}
func splitCmdline(cmdline []byte) string {
indexOfEndOfString := len(cmdline)
for i, c := range cmdline {
if c == 0 {
indexOfEndOfString = i
break
}
}
return filepath.Base(string(cmdline[:indexOfEndOfString]))
} }
func isPid(s string) bool { func isPid(s string) bool {
for _, s := range s { return strings.IndexFunc(s, func(r rune) bool {
if s < '0' || s > '9' { return !unicode.IsDigit(r)
return false }) == -1
}
}
return true
} }

View file

@ -3,7 +3,6 @@ package process
import ( import (
"fmt" "fmt"
"net" "net"
"path/filepath"
"sync" "sync"
"syscall" "syscall"
"unsafe" "unsafe"
@ -220,5 +219,5 @@ func getExecPathFromPID(pid uint32) (string, error) {
if r1 == 0 { if r1 == 0 {
return "", err return "", err
} }
return filepath.Base(syscall.UTF16ToString(buf[:size])), nil return syscall.UTF16ToString(buf[:size]), nil
} }

View file

@ -176,6 +176,7 @@ type RawConfig struct {
GeodataMode string `yaml:"geodata-mode"` GeodataMode string `yaml:"geodata-mode"`
GeodataLoader string `yaml:"geodata-loader"` GeodataLoader string `yaml:"geodata-loader"`
AutoIptables bool `yaml:"auto-iptables"` AutoIptables bool `yaml:"auto-iptables"`
RoutingMark int `yaml:"routing-mark"`
ProxyProvider map[string]map[string]interface{} `yaml:"proxy-providers"` ProxyProvider map[string]map[string]interface{} `yaml:"proxy-providers"`
RuleProvider map[string]map[string]interface{} `yaml:"rule-providers"` RuleProvider map[string]map[string]interface{} `yaml:"rule-providers"`
@ -240,7 +241,7 @@ func UnmarshalRawConfig(buf []byte) (*RawConfig, error) {
}, },
NameServer: []string{ NameServer: []string{
"223.5.5.5", "223.5.5.5",
"119.29.29", "119.29.29.29",
}, },
FakeIPFilter: []string{ FakeIPFilter: []string{
"dns.msftnsci.com", "dns.msftnsci.com",
@ -351,6 +352,7 @@ func parseGeneral(cfg *RawConfig) (*General, error) {
Interface: cfg.Interface, Interface: cfg.Interface,
GeodataLoader: cfg.GeodataLoader, GeodataLoader: cfg.GeodataLoader,
AutoIptables: cfg.AutoIptables, AutoIptables: cfg.AutoIptables,
RoutingMark: cfg.RoutingMark,
}, nil }, nil
} }

View file

@ -83,6 +83,7 @@ type Metadata struct {
Host string `json:"host"` Host string `json:"host"`
Process string `json:"process"` Process string `json:"process"`
DNSMode DNSMode `json:"dnsMode"` DNSMode DNSMode `json:"dnsMode"`
ProcessPath string `json:"processPath"`
} }
func (m *Metadata) RemoteAddress() string { func (m *Metadata) RemoteAddress() string {

View file

@ -12,6 +12,7 @@ const (
SrcPort SrcPort
DstPort DstPort
Process Process
ProcessPath
Script Script
RuleSet RuleSet
Network Network
@ -46,6 +47,8 @@ func (rt RuleType) String() string {
return "DstPort" return "DstPort"
case Process: case Process:
return "Process" return "Process"
case ProcessPath:
return "ProcessPath"
case Script: case Script:
return "Script" return "Script"
case MATCH: case MATCH:
@ -71,5 +74,6 @@ type Rule interface {
Adapter() string Adapter() string
Payload() string Payload() string
ShouldResolveIP() bool ShouldResolveIP() bool
ShouldFindProcess() bool
RuleExtra() *RuleExtra RuleExtra() *RuleExtra
} }

View file

@ -232,6 +232,7 @@ func updateGeneral(general *config.General, Tun *config.Tun, force bool) {
} }
dialer.DefaultInterface.Store(general.Interface) dialer.DefaultInterface.Store(general.Interface)
dialer.DefaultRoutingMark.Store(int32(general.RoutingMark))
log.Infoln("Use interface name: %s", general.Interface) log.Infoln("Use interface name: %s", general.Interface)

View file

@ -19,7 +19,12 @@ func HandleConn(c net.Conn, in chan<- C.ConnContext, cache *cache.Cache) {
client := newClient(c.RemoteAddr(), in) client := newClient(c.RemoteAddr(), in)
defer client.CloseIdleConnections() defer client.CloseIdleConnections()
conn := N.NewBufferedConn(c) var conn *N.BufferedConn
if bufConn, ok := c.(*N.BufferedConn); ok {
conn = bufConn
} else {
conn = N.NewBufferedConn(c)
}
keepAlive := true keepAlive := true
trusted := cache == nil // disable authenticate if cache is nil trusted := cache == nil // disable authenticate if cache is nil

View file

@ -64,6 +64,8 @@ func New(addr string, in chan<- C.ConnContext) (*Listener, error) {
} }
func handleConn(conn net.Conn, in chan<- C.ConnContext, cache *cache.Cache) { func handleConn(conn net.Conn, in chan<- C.ConnContext, cache *cache.Cache) {
conn.(*net.TCPConn).SetKeepAlive(true)
bufConn := N.NewBufferedConn(conn) bufConn := N.NewBufferedConn(conn)
head, err := bufConn.Peek(1) head, err := bufConn.Peek(1)
if err != nil { if err != nil {

View file

@ -61,6 +61,7 @@ func New(addr string, in chan<- C.ConnContext) (*Listener, error) {
} }
func handleSocks(conn net.Conn, in chan<- C.ConnContext) { func handleSocks(conn net.Conn, in chan<- C.ConnContext) {
conn.(*net.TCPConn).SetKeepAlive(true)
bufConn := N.NewBufferedConn(conn) bufConn := N.NewBufferedConn(conn)
head, err := bufConn.Peek(1) head, err := bufConn.Peek(1)
if err != nil { if err != nil {
@ -84,9 +85,6 @@ func HandleSocks4(conn net.Conn, in chan<- C.ConnContext) {
conn.Close() conn.Close()
return return
} }
if c, ok := conn.(*net.TCPConn); ok {
c.SetKeepAlive(true)
}
in <- inbound.NewSocket(socks5.ParseAddr(addr), conn, C.SOCKS4) in <- inbound.NewSocket(socks5.ParseAddr(addr), conn, C.SOCKS4)
} }
@ -96,9 +94,6 @@ func HandleSocks5(conn net.Conn, in chan<- C.ConnContext) {
conn.Close() conn.Close()
return return
} }
if c, ok := conn.(*net.TCPConn); ok {
c.SetKeepAlive(true)
}
if command == socks5.CmdUDPAssociate { if command == socks5.CmdUDPAssociate {
defer conn.Close() defer conn.Close()
io.Copy(io.Discard, conn) io.Copy(io.Discard, conn)

View file

@ -39,6 +39,10 @@ func (d *Domain) RuleExtra() *C.RuleExtra {
return d.ruleExtra return d.ruleExtra
} }
func (d *Domain) ShouldFindProcess() bool {
return false
}
func NewDomain(domain string, adapter string, ruleExtra *C.RuleExtra) *Domain { func NewDomain(domain string, adapter string, ruleExtra *C.RuleExtra) *Domain {
return &Domain{ return &Domain{
domain: strings.ToLower(domain), domain: strings.ToLower(domain),

View file

@ -36,11 +36,15 @@ func (dk *DomainKeyword) ShouldResolveIP() bool {
return false return false
} }
func (dk *DomainKeyword) ShouldFindProcess() bool {
return false
}
func (dk *DomainKeyword) RuleExtra() *C.RuleExtra { func (dk *DomainKeyword) RuleExtra() *C.RuleExtra {
return dk.ruleExtra return dk.ruleExtra
} }
func NewDomainKeyword(keyword string, adapter string, ruleExtra *C.RuleExtra) *DomainKeyword { func NewDomainKeyword(keyword string, adapter string) *DomainKeyword {
return &DomainKeyword{ return &DomainKeyword{
keyword: strings.ToLower(keyword), keyword: strings.ToLower(keyword),
adapter: adapter, adapter: adapter,

View file

@ -40,6 +40,11 @@ func (ds *DomainSuffix) RuleExtra() *C.RuleExtra {
return ds.ruleExtra return ds.ruleExtra
} }
func (ds *DomainSuffix) ShouldFindProcess() bool {
return false
}
func NewDomainSuffix(suffix string, adapter string, ruleExtra *C.RuleExtra) *DomainSuffix { func NewDomainSuffix(suffix string, adapter string, ruleExtra *C.RuleExtra) *DomainSuffix {
return &DomainSuffix{ return &DomainSuffix{
suffix: strings.ToLower(suffix), suffix: strings.ToLower(suffix),

View file

@ -29,6 +29,10 @@ func (f *Match) ShouldResolveIP() bool {
return false return false
} }
func (f *Match) ShouldFindProcess() bool {
return false
}
func (f *Match) RuleExtra() *C.RuleExtra { func (f *Match) RuleExtra() *C.RuleExtra {
return f.ruleExtra return f.ruleExtra
} }

View file

@ -55,6 +55,10 @@ func (i *IPCIDR) ShouldResolveIP() bool {
return !i.noResolveIP return !i.noResolveIP
} }
func (i *IPCIDR) ShouldFindProcess() bool {
return false
}
func (i *IPCIDR) RuleExtra() *C.RuleExtra { func (i *IPCIDR) RuleExtra() *C.RuleExtra {
return i.ruleExtra return i.ruleExtra
} }

View file

@ -1,4 +1,4 @@
package rule package rules
import ( import (
"fmt" "fmt"
@ -42,7 +42,9 @@ func ParseRule(tp, payload, target string, params []string) (C.Rule, error) {
case "DST-PORT": case "DST-PORT":
parsed, parseErr = RC.NewPort(payload, target, false, ruleExtra) parsed, parseErr = RC.NewPort(payload, target, false, ruleExtra)
case "PROCESS-NAME": case "PROCESS-NAME":
parsed, parseErr = RC.NewProcess(payload, target, ruleExtra) parsed, parseErr = RC.NewProcess(payload, target, true,ruleExtra)
case "PROCESS-PATH":
parsed, parseErr = RC.NewProcess(payload, target, false,ruleExtra)
case "MATCH": case "MATCH":
parsed = RC.NewMatch(target, ruleExtra) parsed = RC.NewMatch(target, ruleExtra)
case "RULE-SET": case "RULE-SET":

View file

@ -1,28 +0,0 @@
{
"inbounds": [
{
"port": 10002,
"listen": "0.0.0.0",
"protocol": "vmess",
"settings": {
"clients": [
{
"id": "b831381d-6324-4d53-ad4f-8cda48b30811",
"alterId": 0
}
]
},
"streamSettings": {
"network": "tcp"
}
}
],
"outbounds": [
{
"protocol": "freedom"
}
],
"log": {
"loglevel": "debug"
}
}

View file

@ -7,8 +7,7 @@
"settings": { "settings": {
"clients": [ "clients": [
{ {
"id": "b831381d-6324-4d53-ad4f-8cda48b30811", "id": "b831381d-6324-4d53-ad4f-8cda48b30811"
"alterId": 32
} }
] ]
}, },

View file

@ -7,8 +7,7 @@
"settings": { "settings": {
"clients": [ "clients": [
{ {
"id": "b831381d-6324-4d53-ad4f-8cda48b30811", "id": "b831381d-6324-4d53-ad4f-8cda48b30811"
"alterId": 32
} }
] ]
}, },

View file

@ -7,8 +7,7 @@
"settings": { "settings": {
"clients": [ "clients": [
{ {
"id": "b831381d-6324-4d53-ad4f-8cda48b30811", "id": "b831381d-6324-4d53-ad4f-8cda48b30811"
"alterId": 32
} }
] ]
}, },

View file

@ -7,8 +7,7 @@
"settings": { "settings": {
"clients": [ "clients": [
{ {
"id": "b831381d-6324-4d53-ad4f-8cda48b30811", "id": "b831381d-6324-4d53-ad4f-8cda48b30811"
"alterId": 32
} }
] ]
}, },

View file

@ -7,8 +7,7 @@
"settings": { "settings": {
"clients": [ "clients": [
{ {
"id": "b831381d-6324-4d53-ad4f-8cda48b30811", "id": "b831381d-6324-4d53-ad4f-8cda48b30811"
"alterId": 32
} }
] ]
}, },

View file

@ -7,8 +7,7 @@
"settings": { "settings": {
"clients": [ "clients": [
{ {
"id": "b831381d-6324-4d53-ad4f-8cda48b30811", "id": "b831381d-6324-4d53-ad4f-8cda48b30811"
"alterId": 32
} }
] ]
}, },

View file

@ -7,8 +7,7 @@
"settings": { "settings": {
"clients": [ "clients": [
{ {
"id": "b831381d-6324-4d53-ad4f-8cda48b30811", "id": "b831381d-6324-4d53-ad4f-8cda48b30811"
"alterId": 32
} }
] ]
}, },

View file

@ -7,8 +7,7 @@
"settings": { "settings": {
"clients": [ "clients": [
{ {
"id": "b831381d-6324-4d53-ad4f-8cda48b30811", "id": "b831381d-6324-4d53-ad4f-8cda48b30811"
"alterId": 32
} }
] ]
}, },

View file

@ -39,45 +39,6 @@ func TestClash_Vmess(t *testing.T) {
Port: 10002, Port: 10002,
UUID: "b831381d-6324-4d53-ad4f-8cda48b30811", UUID: "b831381d-6324-4d53-ad4f-8cda48b30811",
Cipher: "auto", Cipher: "auto",
AlterID: 32,
UDP: true,
})
if err != nil {
assert.FailNow(t, err.Error())
}
time.Sleep(waitTime)
testSuit(t, proxy)
}
func TestClash_VmessAEAD(t *testing.T) {
configPath := C.Path.Resolve("vmess-aead.json")
cfg := &container.Config{
Image: ImageVmess,
ExposedPorts: defaultExposedPorts,
}
hostCfg := &container.HostConfig{
PortBindings: defaultPortBindings,
Binds: []string{fmt.Sprintf("%s:/etc/v2ray/config.json", configPath)},
}
id, err := startContainer(cfg, hostCfg, "vmess-aead")
if err != nil {
assert.FailNow(t, err.Error())
}
t.Cleanup(func() {
cleanContainer(id)
})
proxy, err := outbound.NewVmess(outbound.VmessOption{
Name: "vmess",
Server: localIP.String(),
Port: 10002,
UUID: "b831381d-6324-4d53-ad4f-8cda48b30811",
Cipher: "auto",
AlterID: 0,
UDP: true, UDP: true,
}) })
if err != nil { if err != nil {
@ -114,7 +75,6 @@ func TestClash_VmessTLS(t *testing.T) {
Port: 10002, Port: 10002,
UUID: "b831381d-6324-4d53-ad4f-8cda48b30811", UUID: "b831381d-6324-4d53-ad4f-8cda48b30811",
Cipher: "auto", Cipher: "auto",
AlterID: 32,
TLS: true, TLS: true,
SkipCertVerify: true, SkipCertVerify: true,
ServerName: "example.org", ServerName: "example.org",
@ -154,7 +114,6 @@ func TestClash_VmessHTTP2(t *testing.T) {
Port: 10002, Port: 10002,
UUID: "b831381d-6324-4d53-ad4f-8cda48b30811", UUID: "b831381d-6324-4d53-ad4f-8cda48b30811",
Cipher: "auto", Cipher: "auto",
AlterID: 32,
Network: "h2", Network: "h2",
TLS: true, TLS: true,
SkipCertVerify: true, SkipCertVerify: true,
@ -197,7 +156,6 @@ func TestClash_VmessHTTP(t *testing.T) {
Port: 10002, Port: 10002,
UUID: "b831381d-6324-4d53-ad4f-8cda48b30811", UUID: "b831381d-6324-4d53-ad4f-8cda48b30811",
Cipher: "auto", Cipher: "auto",
AlterID: 32,
Network: "http", Network: "http",
UDP: true, UDP: true,
HTTPOpts: outbound.HTTPOptions{ HTTPOpts: outbound.HTTPOptions{
@ -250,7 +208,6 @@ func TestClash_VmessWebsocket(t *testing.T) {
Port: 10002, Port: 10002,
UUID: "b831381d-6324-4d53-ad4f-8cda48b30811", UUID: "b831381d-6324-4d53-ad4f-8cda48b30811",
Cipher: "auto", Cipher: "auto",
AlterID: 32,
Network: "ws", Network: "ws",
UDP: true, UDP: true,
}) })
@ -288,7 +245,6 @@ func TestClash_VmessWebsocketTLS(t *testing.T) {
Port: 10002, Port: 10002,
UUID: "b831381d-6324-4d53-ad4f-8cda48b30811", UUID: "b831381d-6324-4d53-ad4f-8cda48b30811",
Cipher: "auto", Cipher: "auto",
AlterID: 32,
Network: "ws", Network: "ws",
TLS: true, TLS: true,
SkipCertVerify: true, SkipCertVerify: true,
@ -328,7 +284,6 @@ func TestClash_VmessGrpc(t *testing.T) {
Port: 10002, Port: 10002,
UUID: "b831381d-6324-4d53-ad4f-8cda48b30811", UUID: "b831381d-6324-4d53-ad4f-8cda48b30811",
Cipher: "auto", Cipher: "auto",
AlterID: 32,
Network: "grpc", Network: "grpc",
TLS: true, TLS: true,
SkipCertVerify: true, SkipCertVerify: true,
@ -370,7 +325,6 @@ func TestClash_VmessWebsocket0RTT(t *testing.T) {
Port: 10002, Port: 10002,
UUID: "b831381d-6324-4d53-ad4f-8cda48b30811", UUID: "b831381d-6324-4d53-ad4f-8cda48b30811",
Cipher: "auto", Cipher: "auto",
AlterID: 32,
Network: "ws", Network: "ws",
UDP: true, UDP: true,
ServerName: "example.org", ServerName: "example.org",
@ -411,7 +365,6 @@ func TestClash_VmessWebsocketXray0RTT(t *testing.T) {
Port: 10002, Port: 10002,
UUID: "b831381d-6324-4d53-ad4f-8cda48b30811", UUID: "b831381d-6324-4d53-ad4f-8cda48b30811",
Cipher: "auto", Cipher: "auto",
AlterID: 32,
Network: "ws", Network: "ws",
UDP: true, UDP: true,
ServerName: "example.org", ServerName: "example.org",

View file

@ -6,11 +6,13 @@ import (
R "github.com/Dreamacro/clash/rule/common" R "github.com/Dreamacro/clash/rule/common"
"net" "net"
"runtime" "runtime"
"strconv"
"sync" "sync"
"time" "time"
"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"
"github.com/Dreamacro/clash/component/resolver" "github.com/Dreamacro/clash/component/resolver"
C "github.com/Dreamacro/clash/constant" C "github.com/Dreamacro/clash/constant"
"github.com/Dreamacro/clash/constant/provider" "github.com/Dreamacro/clash/constant/provider"
@ -329,6 +331,7 @@ func match(metadata *C.Metadata) (C.Proxy, C.Rule, error) {
defer configMux.RUnlock() defer configMux.RUnlock()
var resolved bool var resolved bool
var processFound bool
if node := resolver.DefaultHosts.Search(metadata.Host); node != nil { if node := resolver.DefaultHosts.Search(metadata.Host); node != nil {
ip := node.Data.(net.IP) ip := node.Data.(net.IP)
@ -351,6 +354,21 @@ func match(metadata *C.Metadata) (C.Proxy, C.Rule, error) {
resolved = true resolved = true
} }
if !processFound && rule.ShouldFindProcess() {
processFound = true
srcPort, err := strconv.Atoi(metadata.SrcPort)
if err == nil {
path, err := P.FindProcessName(metadata.NetWork.String(), metadata.SrcIP, srcPort)
if err != nil {
log.Debugln("[Process] find process %s: %v", metadata.String(), err)
} else {
log.Debugln("[Process] %s from process %s", metadata.String(), path)
metadata.ProcessPath = path
}
}
}
if rule.Match(metadata) { if rule.Match(metadata) {
adapter, ok := proxies[rule.Adapter()] adapter, ok := proxies[rule.Adapter()]
if !ok { if !ok {