chore: Something update from clash :) (#606)
This commit is contained in:
parent
1f592c43de
commit
a0c7641ad5
11 changed files with 100 additions and 31 deletions
5
Makefile
5
Makefile
|
@ -31,6 +31,8 @@ PLATFORM_LIST = \
|
|||
linux-mips-hardfloat \
|
||||
linux-mipsle-softfloat \
|
||||
linux-mipsle-hardfloat \
|
||||
linux-riscv64 \
|
||||
linux-loong64 \
|
||||
android-arm64 \
|
||||
freebsd-386 \
|
||||
freebsd-amd64 \
|
||||
|
@ -104,6 +106,9 @@ linux-mips64le:
|
|||
linux-riscv64:
|
||||
GOARCH=riscv64 GOOS=linux $(GOBUILD) -o $(BINDIR)/$(NAME)-$@
|
||||
|
||||
linux-loong64:
|
||||
GOARCH=loong64 GOOS=linux $(GOBUILD) -o $(BINDIR)/$(NAME)-$@
|
||||
|
||||
android-arm64:
|
||||
GOARCH=arm64 GOOS=android $(GOBUILD) -o $(BINDIR)/$(NAME)-$@
|
||||
|
||||
|
|
|
@ -56,12 +56,12 @@ func ParseProxyGroup(config map[string]any, proxyMap map[string]C.Proxy, provide
|
|||
providers := []types.ProxyProvider{}
|
||||
|
||||
if len(groupOption.Proxies) == 0 && len(groupOption.Use) == 0 {
|
||||
return nil, errMissProxy
|
||||
return nil, fmt.Errorf("%s: %w", groupName, errMissProxy)
|
||||
}
|
||||
|
||||
expectedStatus, err := utils.NewIntRanges[uint16](groupOption.ExpectedStatus)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, fmt.Errorf("%s: %w", groupName, err)
|
||||
}
|
||||
|
||||
status := strings.TrimSpace(groupOption.ExpectedStatus)
|
||||
|
@ -74,17 +74,17 @@ func ParseProxyGroup(config map[string]any, proxyMap map[string]C.Proxy, provide
|
|||
if len(groupOption.Proxies) != 0 {
|
||||
ps, err := getProxies(proxyMap, groupOption.Proxies)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, fmt.Errorf("%s: %w", groupName, err)
|
||||
}
|
||||
|
||||
if _, ok := providersMap[groupName]; ok {
|
||||
return nil, errDuplicateProvider
|
||||
return nil, fmt.Errorf("%s: %w", groupName, errDuplicateProvider)
|
||||
}
|
||||
|
||||
hc := provider.NewHealthCheck(ps, "", 0, true, nil)
|
||||
pd, err := provider.NewCompatibleProvider(groupName, ps, hc)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, fmt.Errorf("%s: %w", groupName, err)
|
||||
}
|
||||
|
||||
// select don't need health check
|
||||
|
@ -107,7 +107,7 @@ func ParseProxyGroup(config map[string]any, proxyMap map[string]C.Proxy, provide
|
|||
if len(groupOption.Use) != 0 {
|
||||
list, err := getProviders(providersMap, groupOption.Use)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, fmt.Errorf("%s: %w", groupName, err)
|
||||
}
|
||||
|
||||
// different proxy groups use different test URL
|
||||
|
|
|
@ -12,7 +12,10 @@ import (
|
|||
types "github.com/Dreamacro/clash/constant/provider"
|
||||
)
|
||||
|
||||
var errVehicleType = errors.New("unsupport vehicle type")
|
||||
var (
|
||||
errVehicleType = errors.New("unsupport vehicle type")
|
||||
errSubPath = errors.New("path is not subpath of home directory")
|
||||
)
|
||||
|
||||
type healthCheckSchema struct {
|
||||
Enable bool `provider:"enable"`
|
||||
|
@ -64,6 +67,9 @@ func ParseProxyProvider(name string, mapping map[string]any) (types.ProxyProvide
|
|||
case "file":
|
||||
vehicle = resource.NewFileVehicle(path)
|
||||
case "http":
|
||||
if !C.Path.IsSubPath(path) {
|
||||
return nil, fmt.Errorf("%w: %s", errSubPath, path)
|
||||
}
|
||||
vehicle = resource.NewHTTPVehicle(schema.URL, path)
|
||||
default:
|
||||
return nil, fmt.Errorf("%w: %s", errVehicleType, schema.Type)
|
||||
|
|
|
@ -67,7 +67,7 @@ func findProcessName(network string, ip netip.Addr, srcPort int) (uint32, string
|
|||
err := initWin32API()
|
||||
if err != nil {
|
||||
log.Errorln("Initialize PROCESS-NAME failed: %s", err.Error())
|
||||
log.Warnln("All PROCESS-NAMES rules will be skiped")
|
||||
log.Warnln("All PROCESS-NAMES rules will be skipped")
|
||||
return
|
||||
}
|
||||
})
|
||||
|
|
|
@ -913,7 +913,7 @@ func parseNameServer(servers []string, preferH3 bool) ([]dns.NameServer, error)
|
|||
addr, err = hostWithDefaultPort(u.Host, "443")
|
||||
if err == nil {
|
||||
proxyName = ""
|
||||
clearURL := url.URL{Scheme: "https", Host: addr, Path: u.Path}
|
||||
clearURL := url.URL{Scheme: "https", Host: addr, Path: u.Path, User: u.User}
|
||||
addr = clearURL.String()
|
||||
dnsNetType = "https" // DNS over HTTPS
|
||||
if len(u.Fragment) != 0 {
|
||||
|
|
|
@ -56,6 +56,18 @@ func (p *path) Resolve(path string) string {
|
|||
return path
|
||||
}
|
||||
|
||||
// IsSubPath return true if path is a subpath of homedir
|
||||
func (p *path) IsSubPath(path string) bool {
|
||||
homedir := p.HomeDir()
|
||||
path = p.Resolve(path)
|
||||
rel, err := filepath.Rel(homedir, path)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
|
||||
return !strings.Contains(rel, "..")
|
||||
}
|
||||
|
||||
func (p *path) MMDB() string {
|
||||
files, err := os.ReadDir(p.homeDir)
|
||||
if err != nil {
|
||||
|
|
|
@ -165,7 +165,8 @@ func (r *Resolver) ExchangeContext(ctx context.Context, m *D.Msg) (msg *D.Msg, e
|
|||
setMsgTTL(msg, uint32(1)) // Continue fetch
|
||||
continueFetch = true
|
||||
} else {
|
||||
setMsgTTL(msg, uint32(time.Until(expireTime).Seconds()))
|
||||
// updating TTL by subtracting common delta time from each DNS record
|
||||
updateMsgTTL(msg, uint32(time.Until(expireTime).Seconds()))
|
||||
}
|
||||
return
|
||||
}
|
||||
|
|
29
dns/util.go
29
dns/util.go
|
@ -21,12 +21,29 @@ import (
|
|||
"github.com/Dreamacro/clash/tunnel"
|
||||
|
||||
D "github.com/miekg/dns"
|
||||
"github.com/samber/lo"
|
||||
)
|
||||
|
||||
const (
|
||||
MaxMsgSize = 65535
|
||||
)
|
||||
|
||||
func minimalTTL(records []D.RR) uint32 {
|
||||
return lo.MinBy(records, func(r1 D.RR, r2 D.RR) bool {
|
||||
return r1.Header().Ttl < r2.Header().Ttl
|
||||
}).Header().Ttl
|
||||
}
|
||||
|
||||
func updateTTL(records []D.RR, ttl uint32) {
|
||||
if len(records) == 0 {
|
||||
return
|
||||
}
|
||||
delta := minimalTTL(records) - ttl
|
||||
for i := range records {
|
||||
records[i].Header().Ttl = lo.Clamp(records[i].Header().Ttl-delta, 1, records[i].Header().Ttl)
|
||||
}
|
||||
}
|
||||
|
||||
func putMsgToCache(c *cache.LruCache[string, *D.Msg], key string, msg *D.Msg) {
|
||||
// skip dns cache for acme challenge
|
||||
if len(msg.Question) != 0 {
|
||||
|
@ -38,11 +55,11 @@ func putMsgToCache(c *cache.LruCache[string, *D.Msg], key string, msg *D.Msg) {
|
|||
var ttl uint32
|
||||
switch {
|
||||
case len(msg.Answer) != 0:
|
||||
ttl = msg.Answer[0].Header().Ttl
|
||||
ttl = minimalTTL(msg.Answer)
|
||||
case len(msg.Ns) != 0:
|
||||
ttl = msg.Ns[0].Header().Ttl
|
||||
ttl = minimalTTL(msg.Ns)
|
||||
case len(msg.Extra) != 0:
|
||||
ttl = msg.Extra[0].Header().Ttl
|
||||
ttl = minimalTTL(msg.Extra)
|
||||
default:
|
||||
log.Debugln("[DNS] response msg empty: %#v", msg)
|
||||
return
|
||||
|
@ -65,6 +82,12 @@ func setMsgTTL(msg *D.Msg, ttl uint32) {
|
|||
}
|
||||
}
|
||||
|
||||
func updateMsgTTL(msg *D.Msg, ttl uint32) {
|
||||
updateTTL(msg.Answer, ttl)
|
||||
updateTTL(msg.Ns, ttl)
|
||||
updateTTL(msg.Extra, ttl)
|
||||
}
|
||||
|
||||
func isIPRequest(q D.Question) bool {
|
||||
return q.Qclass == D.ClassINET && (q.Qtype == D.TypeA || q.Qtype == D.TypeAAAA || q.Qtype == D.TypeCNAME)
|
||||
}
|
||||
|
|
2
go.mod
2
go.mod
|
@ -1,6 +1,6 @@
|
|||
module github.com/Dreamacro/clash
|
||||
|
||||
go 1.19
|
||||
go 1.20
|
||||
|
||||
require (
|
||||
github.com/3andne/restls-client-go v0.1.4
|
||||
|
|
|
@ -2,12 +2,14 @@ package route
|
|||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/subtle"
|
||||
"crypto/tls"
|
||||
"encoding/json"
|
||||
"net/http"
|
||||
"runtime/debug"
|
||||
"strings"
|
||||
"time"
|
||||
"unsafe"
|
||||
|
||||
"github.com/Dreamacro/clash/adapter/inbound"
|
||||
CN "github.com/Dreamacro/clash/common/net"
|
||||
|
@ -149,6 +151,12 @@ func Start(addr string, tlsAddr string, secret string,
|
|||
|
||||
}
|
||||
|
||||
func safeEuqal(a, b string) bool {
|
||||
aBuf := unsafe.Slice(unsafe.StringData(a), len(a))
|
||||
bBuf := unsafe.Slice(unsafe.StringData(b), len(b))
|
||||
return subtle.ConstantTimeCompare(aBuf, bBuf) == 1
|
||||
}
|
||||
|
||||
func authentication(next http.Handler) http.Handler {
|
||||
fn := func(w http.ResponseWriter, r *http.Request) {
|
||||
if serverSecret == "" {
|
||||
|
@ -159,7 +167,7 @@ func authentication(next http.Handler) http.Handler {
|
|||
// Browser websocket not support custom header
|
||||
if websocket.IsWebSocketUpgrade(r) && r.URL.Query().Get("token") != "" {
|
||||
token := r.URL.Query().Get("token")
|
||||
if token != serverSecret {
|
||||
if !safeEuqal(token, serverSecret) {
|
||||
render.Status(r, http.StatusUnauthorized)
|
||||
render.JSON(w, r, ErrUnauthorized)
|
||||
return
|
||||
|
@ -172,7 +180,7 @@ func authentication(next http.Handler) http.Handler {
|
|||
bearer, token, found := strings.Cut(header, " ")
|
||||
|
||||
hasInvalidHeader := bearer != "Bearer"
|
||||
hasInvalidSecret := !found || token != serverSecret
|
||||
hasInvalidSecret := !found || !safeEuqal(token, serverSecret)
|
||||
if hasInvalidHeader || hasInvalidSecret {
|
||||
render.Status(r, http.StatusUnauthorized)
|
||||
render.JSON(w, r, ErrUnauthorized)
|
||||
|
|
|
@ -1,12 +1,16 @@
|
|||
package redir
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
"net"
|
||||
"net/netip"
|
||||
"syscall"
|
||||
"unsafe"
|
||||
|
||||
"github.com/Dreamacro/clash/transport/socks5"
|
||||
|
||||
"golang.org/x/sys/unix"
|
||||
)
|
||||
|
||||
const (
|
||||
|
@ -25,28 +29,38 @@ func parserPacket(conn net.Conn) (socks5.Addr, error) {
|
|||
return nil, err
|
||||
}
|
||||
|
||||
var addr socks5.Addr
|
||||
var addr netip.AddrPort
|
||||
|
||||
rc.Control(func(fd uintptr) {
|
||||
if ip4 := c.LocalAddr().(*net.TCPAddr).IP.To4(); ip4 != nil {
|
||||
addr, err = getorigdst(fd)
|
||||
} else {
|
||||
addr, err = getorigdst6(fd)
|
||||
}
|
||||
})
|
||||
|
||||
return addr, err
|
||||
return socks5.AddrFromStdAddrPort(addr), err
|
||||
}
|
||||
|
||||
// Call getorigdst() from linux/net/ipv4/netfilter/nf_conntrack_l3proto_ipv4.c
|
||||
func getorigdst(fd uintptr) (socks5.Addr, error) {
|
||||
raw := syscall.RawSockaddrInet4{}
|
||||
siz := unsafe.Sizeof(raw)
|
||||
_, _, err := syscall.Syscall6(syscall.SYS_GETSOCKOPT, fd, syscall.IPPROTO_IP, SO_ORIGINAL_DST, uintptr(unsafe.Pointer(&raw)), uintptr(unsafe.Pointer(&siz)), 0)
|
||||
func getorigdst(fd uintptr) (netip.AddrPort, error) {
|
||||
addr := unix.RawSockaddrInet4{}
|
||||
size := uint32(unsafe.Sizeof(addr))
|
||||
_, _, err := syscall.Syscall6(syscall.SYS_GETSOCKOPT, fd, syscall.IPPROTO_IP, SO_ORIGINAL_DST, uintptr(unsafe.Pointer(&addr)), uintptr(unsafe.Pointer(&size)), 0)
|
||||
if err != 0 {
|
||||
return nil, err
|
||||
return netip.AddrPort{}, err
|
||||
}
|
||||
port := binary.BigEndian.Uint16((*(*[2]byte)(unsafe.Pointer(&addr.Port)))[:])
|
||||
return netip.AddrPortFrom(netip.AddrFrom4(addr.Addr), port), nil
|
||||
}
|
||||
|
||||
addr := make([]byte, 1+net.IPv4len+2)
|
||||
addr[0] = socks5.AtypIPv4
|
||||
copy(addr[1:1+net.IPv4len], raw.Addr[:])
|
||||
port := (*[2]byte)(unsafe.Pointer(&raw.Port)) // big-endian
|
||||
addr[1+net.IPv4len], addr[1+net.IPv4len+1] = port[0], port[1]
|
||||
return addr, nil
|
||||
func getorigdst6(fd uintptr) (netip.AddrPort, error) {
|
||||
addr := unix.RawSockaddrInet6{}
|
||||
size := uint32(unsafe.Sizeof(addr))
|
||||
_, _, err := syscall.Syscall6(syscall.SYS_GETSOCKOPT, fd, syscall.IPPROTO_IPV6, IP6T_SO_ORIGINAL_DST, uintptr(unsafe.Pointer(&addr)), uintptr(unsafe.Pointer(&size)), 0)
|
||||
if err != 0 {
|
||||
return netip.AddrPort{}, err
|
||||
}
|
||||
port := binary.BigEndian.Uint16((*(*[2]byte)(unsafe.Pointer(&addr.Port)))[:])
|
||||
return netip.AddrPortFrom(netip.AddrFrom16(addr.Addr), port), nil
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue