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:
commit
f01ac69654
32 changed files with 123 additions and 175 deletions
2
.github/workflows/Alpha.yml
vendored
2
.github/workflows/Alpha.yml
vendored
|
@ -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
14
.golangci.yaml
Normal 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
|
|
@ -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"]
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
|
@ -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)
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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),
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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),
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
|
@ -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":
|
||||||
|
|
|
@ -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"
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -7,8 +7,7 @@
|
||||||
"settings": {
|
"settings": {
|
||||||
"clients": [
|
"clients": [
|
||||||
{
|
{
|
||||||
"id": "b831381d-6324-4d53-ad4f-8cda48b30811",
|
"id": "b831381d-6324-4d53-ad4f-8cda48b30811"
|
||||||
"alterId": 32
|
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
|
|
@ -7,8 +7,7 @@
|
||||||
"settings": {
|
"settings": {
|
||||||
"clients": [
|
"clients": [
|
||||||
{
|
{
|
||||||
"id": "b831381d-6324-4d53-ad4f-8cda48b30811",
|
"id": "b831381d-6324-4d53-ad4f-8cda48b30811"
|
||||||
"alterId": 32
|
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
|
|
@ -7,8 +7,7 @@
|
||||||
"settings": {
|
"settings": {
|
||||||
"clients": [
|
"clients": [
|
||||||
{
|
{
|
||||||
"id": "b831381d-6324-4d53-ad4f-8cda48b30811",
|
"id": "b831381d-6324-4d53-ad4f-8cda48b30811"
|
||||||
"alterId": 32
|
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
|
|
@ -7,8 +7,7 @@
|
||||||
"settings": {
|
"settings": {
|
||||||
"clients": [
|
"clients": [
|
||||||
{
|
{
|
||||||
"id": "b831381d-6324-4d53-ad4f-8cda48b30811",
|
"id": "b831381d-6324-4d53-ad4f-8cda48b30811"
|
||||||
"alterId": 32
|
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
|
|
@ -7,8 +7,7 @@
|
||||||
"settings": {
|
"settings": {
|
||||||
"clients": [
|
"clients": [
|
||||||
{
|
{
|
||||||
"id": "b831381d-6324-4d53-ad4f-8cda48b30811",
|
"id": "b831381d-6324-4d53-ad4f-8cda48b30811"
|
||||||
"alterId": 32
|
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
|
|
@ -7,8 +7,7 @@
|
||||||
"settings": {
|
"settings": {
|
||||||
"clients": [
|
"clients": [
|
||||||
{
|
{
|
||||||
"id": "b831381d-6324-4d53-ad4f-8cda48b30811",
|
"id": "b831381d-6324-4d53-ad4f-8cda48b30811"
|
||||||
"alterId": 32
|
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
|
|
@ -7,8 +7,7 @@
|
||||||
"settings": {
|
"settings": {
|
||||||
"clients": [
|
"clients": [
|
||||||
{
|
{
|
||||||
"id": "b831381d-6324-4d53-ad4f-8cda48b30811",
|
"id": "b831381d-6324-4d53-ad4f-8cda48b30811"
|
||||||
"alterId": 32
|
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
|
|
@ -7,8 +7,7 @@
|
||||||
"settings": {
|
"settings": {
|
||||||
"clients": [
|
"clients": [
|
||||||
{
|
{
|
||||||
"id": "b831381d-6324-4d53-ad4f-8cda48b30811",
|
"id": "b831381d-6324-4d53-ad4f-8cda48b30811"
|
||||||
"alterId": 32
|
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
|
|
@ -34,51 +34,12 @@ func TestClash_Vmess(t *testing.T) {
|
||||||
})
|
})
|
||||||
|
|
||||||
proxy, err := outbound.NewVmess(outbound.VmessOption{
|
proxy, err := outbound.NewVmess(outbound.VmessOption{
|
||||||
Name: "vmess",
|
Name: "vmess",
|
||||||
Server: localIP.String(),
|
Server: localIP.String(),
|
||||||
Port: 10002,
|
Port: 10002,
|
||||||
UUID: "b831381d-6324-4d53-ad4f-8cda48b30811",
|
UUID: "b831381d-6324-4d53-ad4f-8cda48b30811",
|
||||||
Cipher: "auto",
|
Cipher: "auto",
|
||||||
AlterID: 32,
|
UDP: true,
|
||||||
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,
|
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
assert.FailNow(t, err.Error())
|
assert.FailNow(t, err.Error())
|
||||||
|
@ -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",
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
Loading…
Reference in a new issue