Skip to content

Commit

Permalink
chore: Something update from clash :) (MetaCubeX#606)
Browse files Browse the repository at this point in the history
  • Loading branch information
Nep-Timeline authored Jun 6, 2023
1 parent e717486 commit dafeceb
Show file tree
Hide file tree
Showing 11 changed files with 99 additions and 30 deletions.
5 changes: 5 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -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 \
Expand Down Expand Up @@ -103,6 +105,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)-$@
Expand Down
12 changes: 6 additions & 6 deletions adapter/outboundgroup/parser.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand All @@ -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
Expand All @@ -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
Expand Down
8 changes: 7 additions & 1 deletion adapter/provider/parser.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"`
Expand Down Expand Up @@ -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)
Expand Down
2 changes: 1 addition & 1 deletion component/process/process_windows.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
})
Expand Down
2 changes: 1 addition & 1 deletion config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
12 changes: 12 additions & 0 deletions constant/path.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
3 changes: 2 additions & 1 deletion dns/resolver.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
Expand Down
29 changes: 26 additions & 3 deletions dns/util.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand All @@ -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
Expand All @@ -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)
}
Expand Down
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
@@ -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
Expand Down
12 changes: 10 additions & 2 deletions hub/route/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -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 == "" {
Expand All @@ -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
Expand All @@ -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)
Expand Down
42 changes: 28 additions & 14 deletions listener/redir/tcp_freebsd.go
Original file line number Diff line number Diff line change
@@ -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 (
Expand All @@ -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) {
addr, err = getorigdst(fd)
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
}

0 comments on commit dafeceb

Please sign in to comment.