├── .gitignore ├── ReadMe.md ├── build.sh ├── clash-meta ├── .gitignore ├── .golangci.yaml ├── Dockerfile ├── LICENSE ├── Makefile ├── Meta.png ├── README.md ├── adapter │ ├── adapter.go │ ├── inbound │ │ ├── addition.go │ │ ├── http.go │ │ ├── https.go │ │ ├── listen.go │ │ ├── packet.go │ │ ├── socket.go │ │ └── util.go │ ├── outbound │ │ ├── base.go │ │ ├── direct.go │ │ ├── http.go │ │ ├── hysteria.go │ │ ├── reality.go │ │ ├── reject.go │ │ ├── shadowsocks.go │ │ ├── shadowsocksr.go │ │ ├── singmux.go │ │ ├── snell.go │ │ ├── socks5.go │ │ ├── trojan.go │ │ ├── tuic.go │ │ ├── util.go │ │ ├── vless.go │ │ ├── vmess.go │ │ └── wireguard.go │ ├── outboundgroup │ │ ├── fallback.go │ │ ├── groupbase.go │ │ ├── loadbalance.go │ │ ├── parser.go │ │ ├── relay.go │ │ ├── selector.go │ │ ├── urltest.go │ │ └── util.go │ ├── parser.go │ └── provider │ │ ├── healthcheck.go │ │ ├── parser.go │ │ ├── provider.go │ │ └── subscription_info.go ├── check_amd64.sh ├── common │ ├── atomic │ │ ├── type.go │ │ └── value.go │ ├── batch │ │ ├── batch.go │ │ └── batch_test.go │ ├── buf │ │ └── sing.go │ ├── cache │ │ ├── lrucache.go │ │ └── lrucache_test.go │ ├── callback │ │ └── callback.go │ ├── cmd │ │ ├── cmd.go │ │ ├── cmd_other.go │ │ ├── cmd_test.go │ │ └── cmd_windows.go │ ├── collections │ │ └── stack.go │ ├── convert │ │ ├── base64.go │ │ ├── converter.go │ │ ├── util.go │ │ └── v.go │ ├── generics │ │ └── list │ │ │ └── list.go │ ├── murmur3 │ │ ├── murmur.go │ │ └── murmur32.go │ ├── net │ │ ├── addr.go │ │ ├── bind.go │ │ ├── bufconn.go │ │ ├── deadline │ │ │ ├── packet.go │ │ │ ├── packet_enhance.go │ │ │ ├── packet_sing.go │ │ │ └── pipe.go │ │ ├── io.go │ │ ├── packet.go │ │ ├── packet │ │ │ ├── packet.go │ │ │ ├── packet_posix.go │ │ │ ├── packet_sing.go │ │ │ ├── packet_windows.go │ │ │ ├── ref.go │ │ │ ├── ref_sing.go │ │ │ ├── thread.go │ │ │ └── thread_sing.go │ │ ├── refconn.go │ │ ├── relay.go │ │ ├── sing.go │ │ ├── tcpip.go │ │ ├── tls.go │ │ └── websocket.go │ ├── nnip │ │ └── netip.go │ ├── observable │ │ ├── iterable.go │ │ ├── observable.go │ │ ├── observable_test.go │ │ └── subscriber.go │ ├── picker │ │ ├── picker.go │ │ └── picker_test.go │ ├── pool │ │ ├── alloc.go │ │ ├── alloc_test.go │ │ ├── buffer.go │ │ ├── buffer_low_memory.go │ │ ├── buffer_standard.go │ │ ├── pool.go │ │ └── sing.go │ ├── queue │ │ └── queue.go │ ├── singledo │ │ ├── singledo.go │ │ └── singledo_test.go │ ├── sockopt │ │ ├── reuseaddr_linux.go │ │ └── reuseaddr_other.go │ ├── structure │ │ ├── structure.go │ │ └── structure_test.go │ └── utils │ │ ├── must.go │ │ ├── range.go │ │ ├── slice.go │ │ ├── strings.go │ │ ├── uuid.go │ │ └── uuid_test.go ├── component │ ├── auth │ │ └── auth.go │ ├── dhcp │ │ ├── conn.go │ │ └── dhcp.go │ ├── dialer │ │ ├── bind_darwin.go │ │ ├── bind_linux.go │ │ ├── bind_others.go │ │ ├── bind_windows.go │ │ ├── control.go │ │ ├── control_go119.go │ │ ├── control_go120.go │ │ ├── dialer.go │ │ ├── error.go │ │ ├── mark_linux.go │ │ ├── mark_nonlinux.go │ │ ├── options.go │ │ ├── resolver.go │ │ ├── reuse_others.go │ │ ├── reuse_unix.go │ │ ├── reuse_windows.go │ │ └── tfo.go │ ├── ebpf │ │ ├── bpf │ │ │ ├── bpf_endian.h │ │ │ ├── bpf_helper_defs.h │ │ │ ├── bpf_helpers.h │ │ │ ├── redir.c │ │ │ └── tc.c │ │ ├── byteorder │ │ │ ├── byteorder.go │ │ │ ├── byteorder_bigendian.go │ │ │ └── byteorder_littleendian.go │ │ ├── ebpf.go │ │ ├── ebpf_linux.go │ │ ├── ebpf_others.go │ │ ├── redir │ │ │ ├── auto_redirect.go │ │ │ ├── bpf_bpfeb.go │ │ │ ├── bpf_bpfeb.o │ │ │ ├── bpf_bpfel.go │ │ │ └── bpf_bpfel.o │ │ └── tc │ │ │ ├── bpf_bpfeb.go │ │ │ ├── bpf_bpfeb.o │ │ │ ├── bpf_bpfel.go │ │ │ ├── bpf_bpfel.o │ │ │ └── redirect_to_tun.go │ ├── fakeip │ │ ├── cachefile.go │ │ ├── memory.go │ │ ├── pool.go │ │ └── pool_test.go │ ├── geodata │ │ ├── attr.go │ │ ├── geodata.go │ │ ├── geodataproto.go │ │ ├── init.go │ │ ├── memconservative │ │ │ ├── cache.go │ │ │ ├── decode.go │ │ │ └── memc.go │ │ ├── package_info.go │ │ ├── router │ │ │ ├── condition.go │ │ │ ├── config.pb.go │ │ │ └── config.proto │ │ ├── standard │ │ │ └── standard.go │ │ ├── strmatcher │ │ │ ├── ac_automaton_matcher.go │ │ │ ├── domain_matcher.go │ │ │ ├── full_matcher.go │ │ │ ├── matchers.go │ │ │ ├── mph_matcher.go │ │ │ ├── package_info.go │ │ │ └── strmatcher.go │ │ └── utils.go │ ├── http │ │ └── http.go │ ├── iface │ │ └── iface.go │ ├── mmdb │ │ └── mmdb.go │ ├── nat │ │ └── table.go │ ├── pool │ │ ├── pool.go │ │ └── pool_test.go │ ├── process │ │ ├── find_process_mode.go │ │ ├── process.go │ │ ├── process_darwin.go │ │ ├── process_freebsd_amd64.go │ │ ├── process_linux.go │ │ ├── process_other.go │ │ └── process_windows.go │ ├── profile │ │ ├── cachefile │ │ │ └── cache.go │ │ └── profile.go │ ├── proxydialer │ │ └── proxydialer.go │ ├── resolver │ │ ├── defaults.go │ │ ├── enhancer.go │ │ ├── host.go │ │ ├── local.go │ │ └── resolver.go │ ├── resource │ │ ├── fetcher.go │ │ └── vehicle.go │ ├── sniffer │ │ ├── base_sniffer.go │ │ ├── dispatcher.go │ │ ├── http_sniffer.go │ │ ├── quic_sniffer.go │ │ ├── sniff_test.go │ │ └── tls_sniffer.go │ ├── tls │ │ ├── config.go │ │ ├── reality.go │ │ └── utls.go │ └── trie │ │ ├── domain.go │ │ ├── domain_set.go │ │ ├── domain_set_test.go │ │ ├── domain_test.go │ │ ├── ipcidr_node.go │ │ ├── ipcidr_trie.go │ │ ├── node.go │ │ └── trie_test.go ├── config │ ├── config.go │ ├── initial.go │ ├── updateGeo.go │ └── utils.go ├── constant │ ├── adapters.go │ ├── context.go │ ├── dns.go │ ├── ebpf.go │ ├── features │ │ ├── low_memory.go │ │ ├── no_fake_tcp.go │ │ ├── tags.go │ │ └── with_gvisor.go │ ├── geodata.go │ ├── listener.go │ ├── metadata.go │ ├── path.go │ ├── provider │ │ └── interface.go │ ├── rule.go │ ├── rule_extra.go │ ├── sniffer │ │ └── sniffer.go │ ├── tun.go │ └── version.go ├── context │ ├── conn.go │ ├── dns.go │ └── packetconn.go ├── dns │ ├── client.go │ ├── dhcp.go │ ├── doh.go │ ├── doq.go │ ├── enhancer.go │ ├── filters.go │ ├── middleware.go │ ├── patch.go │ ├── policy.go │ ├── resolver.go │ ├── server.go │ ├── system.go │ ├── system_posix.go │ ├── system_windows.go │ └── util.go ├── docker │ └── file-name.sh ├── docs │ ├── config.yaml │ └── logo.png ├── flake.lock ├── flake.nix ├── go.mod ├── go.sum ├── hub │ ├── executor │ │ ├── concurrent_load_limit.go │ │ ├── concurrent_load_single.go │ │ ├── concurrent_load_unlimit.go │ │ └── executor.go │ ├── hub.go │ ├── route │ │ ├── cache.go │ │ ├── common.go │ │ ├── configs.go │ │ ├── connections.go │ │ ├── ctxkeys.go │ │ ├── dns.go │ │ ├── errors.go │ │ ├── groups.go │ │ ├── provider.go │ │ ├── proxies.go │ │ ├── restart.go │ │ ├── rules.go │ │ ├── server.go │ │ └── upgrade.go │ └── updater │ │ ├── limitedreader.go │ │ └── updater.go ├── listener │ ├── auth │ │ └── auth.go │ ├── autoredir │ │ └── tcp.go │ ├── config │ │ ├── shadowsocks.go │ │ ├── tuic.go │ │ ├── tun.go │ │ ├── tunnel.go │ │ └── vmess.go │ ├── http │ │ ├── client.go │ │ ├── hack.go │ │ ├── proxy.go │ │ ├── server.go │ │ ├── upgrade.go │ │ └── utils.go │ ├── inbound │ │ ├── base.go │ │ ├── http.go │ │ ├── mixed.go │ │ ├── redir.go │ │ ├── shadowsocks.go │ │ ├── socks.go │ │ ├── tproxy.go │ │ ├── tuic.go │ │ ├── tun.go │ │ ├── tunnel.go │ │ └── vmess.go │ ├── inner │ │ └── tcp.go │ ├── listener.go │ ├── mixed │ │ └── mixed.go │ ├── parse.go │ ├── redir │ │ ├── tcp.go │ │ ├── tcp_darwin.go │ │ ├── tcp_freebsd.go │ │ ├── tcp_linux.go │ │ ├── tcp_linux_386.go │ │ ├── tcp_linux_other.go │ │ └── tcp_other.go │ ├── shadowsocks │ │ ├── tcp.go │ │ ├── udp.go │ │ └── utils.go │ ├── sing │ │ ├── context.go │ │ └── sing.go │ ├── sing_shadowsocks │ │ └── server.go │ ├── sing_tun │ │ ├── dns.go │ │ ├── server.go │ │ ├── server_android.go │ │ ├── server_notandroid.go │ │ ├── server_notwindows.go │ │ ├── server_windows.go │ │ ├── tun_name_darwin.go │ │ ├── tun_name_linux.go │ │ └── tun_name_other.go │ ├── sing_vmess │ │ └── server.go │ ├── socks │ │ ├── tcp.go │ │ ├── udp.go │ │ └── utils.go │ ├── tproxy │ │ ├── packet.go │ │ ├── setsockopt_linux.go │ │ ├── setsockopt_other.go │ │ ├── tproxy.go │ │ ├── tproxy_iptables.go │ │ ├── udp.go │ │ ├── udp_linux.go │ │ └── udp_other.go │ ├── tuic │ │ └── server.go │ └── tunnel │ │ ├── packet.go │ │ ├── tcp.go │ │ └── udp.go ├── log │ ├── level.go │ ├── log.go │ └── sing.go ├── main.go ├── rules │ ├── common │ │ ├── base.go │ │ ├── domain.go │ │ ├── domain_keyword.go │ │ ├── domain_suffix.go │ │ ├── final.go │ │ ├── geoip.go │ │ ├── geosite.go │ │ ├── in_name.go │ │ ├── in_type.go │ │ ├── in_user.go │ │ ├── ipcidr.go │ │ ├── ipsuffix.go │ │ ├── network_type.go │ │ ├── port.go │ │ ├── process.go │ │ └── uid.go │ ├── logic │ │ └── logic.go │ ├── logic_test │ │ └── logic_test.go │ ├── parser.go │ └── provider │ │ ├── classical_strategy.go │ │ ├── domain_strategy.go │ │ ├── ipcidr_strategy.go │ │ ├── parse.go │ │ ├── provider.go │ │ └── rule_set.go ├── test │ ├── .golangci.yaml │ ├── Makefile │ ├── README.md │ ├── clash_test.go │ ├── config │ │ ├── example.org-key.pem │ │ ├── example.org.pem │ │ ├── hysteria.json │ │ ├── snell-http.conf │ │ ├── snell-tls.conf │ │ ├── snell.conf │ │ ├── trojan-grpc.json │ │ ├── trojan-ws.json │ │ ├── trojan-xtls.json │ │ ├── trojan.json │ │ ├── vless-tls.json │ │ ├── vless-ws.json │ │ ├── vless-xtls.json │ │ ├── vmess-grpc.json │ │ ├── vmess-http.json │ │ ├── vmess-http2.json │ │ ├── vmess-tls.json │ │ ├── vmess-ws-0rtt.json │ │ ├── vmess-ws-tls.json │ │ ├── vmess-ws.json │ │ ├── vmess.json │ │ └── xray-shadowsocks.json │ ├── dns_test.go │ ├── docker_test.go │ ├── go.mod │ ├── go.sum │ ├── hysteria_test.go │ ├── snell_test.go │ ├── ss_test.go │ ├── trojan_test.go │ ├── util.go │ ├── util_darwin_test.go │ ├── util_other_test.go │ ├── vless_test.go │ └── vmess_test.go ├── transport │ ├── gun │ │ ├── gun.go │ │ ├── transport.go │ │ └── utils.go │ ├── hysteria │ │ ├── acl │ │ │ ├── engine.go │ │ │ ├── engine_test.go │ │ │ ├── entry.go │ │ │ └── entry_test.go │ │ ├── congestion │ │ │ ├── brutal.go │ │ │ └── pacer.go │ │ ├── conns │ │ │ ├── faketcp │ │ │ │ ├── LICENSE │ │ │ │ ├── obfs.go │ │ │ │ ├── tcp_linux.go │ │ │ │ ├── tcp_stub.go │ │ │ │ └── tcp_test.go │ │ │ ├── udp │ │ │ │ ├── hop.go │ │ │ │ └── obfs.go │ │ │ └── wechat │ │ │ │ └── obfs.go │ │ ├── core │ │ │ ├── client.go │ │ │ ├── frag.go │ │ │ ├── frag_test.go │ │ │ ├── protocol.go │ │ │ └── stream.go │ │ ├── obfs │ │ │ ├── dummy.go │ │ │ ├── obfs.go │ │ │ ├── xplus.go │ │ │ └── xplus_test.go │ │ ├── pmtud_fix │ │ │ ├── avail.go │ │ │ └── unavail.go │ │ ├── transport │ │ │ └── client.go │ │ └── utils │ │ │ └── misc.go │ ├── restls │ │ └── restls.go │ ├── shadowsocks │ │ ├── README.md │ │ ├── core │ │ │ └── cipher.go │ │ ├── shadowaead │ │ │ ├── cipher.go │ │ │ ├── packet.go │ │ │ └── stream.go │ │ └── shadowstream │ │ │ ├── cipher.go │ │ │ ├── old_chacha20.go │ │ │ ├── packet.go │ │ │ └── stream.go │ ├── shadowtls │ │ └── shadowtls.go │ ├── simple-obfs │ │ ├── http.go │ │ └── tls.go │ ├── sing-shadowtls │ │ └── shadowtls.go │ ├── snell │ │ ├── cipher.go │ │ ├── pool.go │ │ └── snell.go │ ├── socks4 │ │ └── socks4.go │ ├── socks5 │ │ └── socks5.go │ ├── ssr │ │ ├── obfs │ │ │ ├── base.go │ │ │ ├── http_post.go │ │ │ ├── http_simple.go │ │ │ ├── obfs.go │ │ │ ├── plain.go │ │ │ ├── random_head.go │ │ │ └── tls1.2_ticket_auth.go │ │ ├── protocol │ │ │ ├── auth_aes128_md5.go │ │ │ ├── auth_aes128_sha1.go │ │ │ ├── auth_chain_a.go │ │ │ ├── auth_chain_b.go │ │ │ ├── auth_sha1_v4.go │ │ │ ├── base.go │ │ │ ├── origin.go │ │ │ ├── packet.go │ │ │ ├── protocol.go │ │ │ └── stream.go │ │ └── tools │ │ │ ├── bufPool.go │ │ │ ├── crypto.go │ │ │ └── random.go │ ├── trojan │ │ └── trojan.go │ ├── tuic │ │ ├── client.go │ │ ├── congestion │ │ │ ├── bandwidth.go │ │ │ ├── bandwidth_sampler.go │ │ │ ├── bbr_sender.go │ │ │ ├── clock.go │ │ │ ├── cubic.go │ │ │ ├── cubic_sender.go │ │ │ ├── hybrid_slow_start.go │ │ │ ├── minmax.go │ │ │ ├── pacer.go │ │ │ └── windowed_filter.go │ │ ├── conn.go │ │ ├── pool_client.go │ │ ├── protocol.go │ │ └── server.go │ ├── v2ray-plugin │ │ ├── mux.go │ │ └── websocket.go │ ├── vless │ │ ├── config.pb.go │ │ ├── config.proto │ │ ├── conn.go │ │ ├── vision │ │ │ ├── conn.go │ │ │ ├── filter.go │ │ │ ├── padding.go │ │ │ └── vision.go │ │ ├── vless.go │ │ └── xtls.go │ └── vmess │ │ ├── aead.go │ │ ├── chunk.go │ │ ├── conn.go │ │ ├── h2.go │ │ ├── header.go │ │ ├── http.go │ │ ├── tls.go │ │ ├── user.go │ │ ├── vmess.go │ │ └── websocket.go └── tunnel │ ├── connection.go │ ├── mode.go │ ├── statistic │ ├── manager.go │ └── tracker.go │ ├── status.go │ └── tunnel.go ├── go.mod ├── go.sum ├── run.bat └── wg-ping.go /.gitignore: -------------------------------------------------------------------------------- 1 | # If you prefer the allow list template instead of the deny list, see community template: 2 | # https://github.com/github/gitignore/blob/main/community/Golang/Go.AllowList.gitignore 3 | # 4 | # Binaries for programs and plugins 5 | *.exe 6 | *.exe~ 7 | *.dll 8 | *.so 9 | *.dylib 10 | 11 | # Test binary, built with `go test -c` 12 | *.test 13 | 14 | # Output of the go coverage tool, specifically when used with LiteIDE 15 | *.out 16 | 17 | # Dependency directories (remove the comment below to include it) 18 | # vendor/ 19 | 20 | # Go workspace file 21 | go.work 22 | 23 | build/* 24 | -------------------------------------------------------------------------------- /ReadMe.md: -------------------------------------------------------------------------------- 1 | # Go Warp Scanner 2 | 3 | A warp scanner that scans throat a real wireguard client! 4 | 5 | ### Run in Android in Termux 6 | 7 | ``` 8 | pkg install unzip wget 9 | wget https://github.com/MiSaturo/Go-Warp-Scanner/files/11646039/warp-ping-linux-arm64.zip 10 | unzip warp-ping-linux-arm64.zip 11 | chmod +x warp-ping-linux-arm64 12 | ./warp-ping-linux-arm64 13 | ``` 14 | 15 | ### Set custom endpiont on windows client 16 | 17 | ``` 18 | warp-cli.exe set-custom-endpoint %endpoint% 19 | ``` 20 | -------------------------------------------------------------------------------- /build.sh: -------------------------------------------------------------------------------- 1 | BINDIR=build 2 | NAME="warp-ping" 3 | FILE=wg-ping.go 4 | GOBUILD='go build -tags with_gvisor' 5 | 6 | mkdir $BINDIR 7 | 8 | env GOARCH=amd64 GOOS=linux GOAMD64=v1 $GOBUILD -o build/$NAME-linux-amd64 $FILE 9 | env GOARCH=arm64 GOOS=linux $GOBUILD -o build/$NAME-linux-arm64 $FILE 10 | env GOARCH=amd64 GOOS=windows GOAMD64=v1 $GOBUILD -o build/$NAME-windows.exe $FILE 11 | 12 | read -p "Press any key to resume ..." -------------------------------------------------------------------------------- /clash-meta/.gitignore: -------------------------------------------------------------------------------- 1 | # Binaries for programs and plugins 2 | *.exe 3 | *.exe~ 4 | *.dll 5 | *.so 6 | *.dylib 7 | bin/* 8 | 9 | # Test binary, build with `go test -c` 10 | *.test 11 | 12 | # Output of the go coverage tool, specifically when used with LiteIDE 13 | *.out 14 | 15 | # go mod vendor 16 | vendor 17 | 18 | # GoLand 19 | .idea/* 20 | 21 | # macOS file 22 | .DS_Store 23 | 24 | # test suite 25 | test/config/cache* 26 | /output 27 | .vscode/ 28 | .fleet/ -------------------------------------------------------------------------------- /clash-meta/.golangci.yaml: -------------------------------------------------------------------------------- 1 | linters: 2 | disable-all: true 3 | enable: 4 | - gofumpt 5 | - staticcheck 6 | - govet 7 | - gci 8 | 9 | linters-settings: 10 | gci: 11 | custom-order: true 12 | sections: 13 | - standard 14 | - prefix(github.com/Dreamacro/clash) 15 | - default 16 | staticcheck: 17 | go: '1.19' 18 | -------------------------------------------------------------------------------- /clash-meta/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM alpine:latest as builder 2 | ARG TARGETPLATFORM 3 | RUN echo "I'm building for $TARGETPLATFORM" 4 | 5 | RUN apk add --no-cache gzip && \ 6 | mkdir /clash-config && \ 7 | wget -O /clash-config/Country.mmdb https://raw.githubusercontent.com/Loyalsoldier/geoip/release/Country.mmdb && \ 8 | wget -O /clash-config/geosite.dat https://github.com/Loyalsoldier/v2ray-rules-dat/releases/latest/download/geosite.dat && \ 9 | wget -O /clash-config/geoip.dat https://github.com/Loyalsoldier/v2ray-rules-dat/releases/latest/download/geoip.dat 10 | 11 | COPY docker/file-name.sh /clash/file-name.sh 12 | WORKDIR /clash 13 | COPY bin/ bin/ 14 | RUN FILE_NAME=`sh file-name.sh` && echo $FILE_NAME && \ 15 | FILE_NAME=`ls bin/ | egrep "$FILE_NAME.*"|awk NR==1` && echo $FILE_NAME && \ 16 | mv bin/$FILE_NAME clash.gz && gzip -d clash.gz && echo "$FILE_NAME" > /clash-config/test 17 | FROM alpine:latest 18 | LABEL org.opencontainers.image.source="https://github.com/MetaCubeX/Clash.Meta" 19 | 20 | RUN apk add --no-cache ca-certificates tzdata iptables 21 | 22 | VOLUME ["/root/.config/clash/"] 23 | 24 | COPY --from=builder /clash-config/ /root/.config/clash/ 25 | COPY --from=builder /clash/clash /clash 26 | RUN chmod +x /clash 27 | ENTRYPOINT [ "/clash" ] 28 | -------------------------------------------------------------------------------- /clash-meta/Meta.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MiSaturo/Go-Warp-Scanner/00ac93a0f58dfeb7c7dfdfdfe8b34eb94ce73473/clash-meta/Meta.png -------------------------------------------------------------------------------- /clash-meta/adapter/inbound/addition.go: -------------------------------------------------------------------------------- 1 | package inbound 2 | 3 | import ( 4 | C "github.com/Dreamacro/clash/constant" 5 | ) 6 | 7 | type Addition func(metadata *C.Metadata) 8 | 9 | func (a Addition) Apply(metadata *C.Metadata) { 10 | a(metadata) 11 | } 12 | 13 | func WithInName(name string) Addition { 14 | return func(metadata *C.Metadata) { 15 | metadata.InName = name 16 | } 17 | } 18 | 19 | func WithInUser(user string) Addition { 20 | return func(metadata *C.Metadata) { 21 | metadata.InUser = user 22 | } 23 | } 24 | 25 | func WithSpecialRules(specialRules string) Addition { 26 | return func(metadata *C.Metadata) { 27 | metadata.SpecialRules = specialRules 28 | } 29 | } 30 | 31 | func WithSpecialProxy(specialProxy string) Addition { 32 | return func(metadata *C.Metadata) { 33 | metadata.SpecialProxy = specialProxy 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /clash-meta/adapter/inbound/http.go: -------------------------------------------------------------------------------- 1 | package inbound 2 | 3 | import ( 4 | "net" 5 | 6 | C "github.com/Dreamacro/clash/constant" 7 | "github.com/Dreamacro/clash/context" 8 | "github.com/Dreamacro/clash/transport/socks5" 9 | ) 10 | 11 | // NewHTTP receive normal http request and return HTTPContext 12 | func NewHTTP(target socks5.Addr, source net.Addr, conn net.Conn, additions ...Addition) *context.ConnContext { 13 | metadata := parseSocksAddr(target) 14 | metadata.NetWork = C.TCP 15 | metadata.Type = C.HTTP 16 | for _, addition := range additions { 17 | addition.Apply(metadata) 18 | } 19 | if ip, port, err := parseAddr(source); err == nil { 20 | metadata.SrcIP = ip 21 | metadata.SrcPort = port 22 | } 23 | if ip, port, err := parseAddr(conn.LocalAddr()); err == nil { 24 | metadata.InIP = ip 25 | metadata.InPort = port 26 | } 27 | return context.NewConnContext(conn, metadata) 28 | } 29 | -------------------------------------------------------------------------------- /clash-meta/adapter/inbound/https.go: -------------------------------------------------------------------------------- 1 | package inbound 2 | 3 | import ( 4 | "net" 5 | "net/http" 6 | 7 | C "github.com/Dreamacro/clash/constant" 8 | "github.com/Dreamacro/clash/context" 9 | ) 10 | 11 | // NewHTTPS receive CONNECT request and return ConnContext 12 | func NewHTTPS(request *http.Request, conn net.Conn, additions ...Addition) *context.ConnContext { 13 | metadata := parseHTTPAddr(request) 14 | metadata.Type = C.HTTPS 15 | for _, addition := range additions { 16 | addition.Apply(metadata) 17 | } 18 | if ip, port, err := parseAddr(conn.RemoteAddr()); err == nil { 19 | metadata.SrcIP = ip 20 | metadata.SrcPort = port 21 | } 22 | if ip, port, err := parseAddr(conn.LocalAddr()); err == nil { 23 | metadata.InIP = ip 24 | metadata.InPort = port 25 | } 26 | return context.NewConnContext(conn, metadata) 27 | } 28 | -------------------------------------------------------------------------------- /clash-meta/adapter/inbound/listen.go: -------------------------------------------------------------------------------- 1 | package inbound 2 | 3 | import ( 4 | "context" 5 | "net" 6 | 7 | "github.com/sagernet/tfo-go" 8 | ) 9 | 10 | var ( 11 | lc = tfo.ListenConfig{ 12 | DisableTFO: true, 13 | } 14 | ) 15 | 16 | func SetTfo(open bool) { 17 | lc.DisableTFO = !open 18 | } 19 | 20 | func ListenContext(ctx context.Context, network, address string) (net.Listener, error) { 21 | return lc.Listen(ctx, network, address) 22 | } 23 | 24 | func Listen(network, address string) (net.Listener, error) { 25 | return ListenContext(context.Background(), network, address) 26 | } 27 | -------------------------------------------------------------------------------- /clash-meta/adapter/inbound/packet.go: -------------------------------------------------------------------------------- 1 | package inbound 2 | 3 | import ( 4 | C "github.com/Dreamacro/clash/constant" 5 | "github.com/Dreamacro/clash/transport/socks5" 6 | ) 7 | 8 | // PacketAdapter is a UDP Packet adapter for socks/redir/tun 9 | type PacketAdapter struct { 10 | C.UDPPacket 11 | metadata *C.Metadata 12 | } 13 | 14 | // Metadata returns destination metadata 15 | func (s *PacketAdapter) Metadata() *C.Metadata { 16 | return s.metadata 17 | } 18 | 19 | // NewPacket is PacketAdapter generator 20 | func NewPacket(target socks5.Addr, packet C.UDPPacket, source C.Type, additions ...Addition) C.PacketAdapter { 21 | metadata := parseSocksAddr(target) 22 | metadata.NetWork = C.UDP 23 | metadata.Type = source 24 | for _, addition := range additions { 25 | addition.Apply(metadata) 26 | } 27 | if ip, port, err := parseAddr(packet.LocalAddr()); err == nil { 28 | metadata.SrcIP = ip 29 | metadata.SrcPort = port 30 | } 31 | if p, ok := packet.(C.UDPPacketInAddr); ok { 32 | if ip, port, err := parseAddr(p.InAddr()); err == nil { 33 | metadata.InIP = ip 34 | metadata.InPort = port 35 | } 36 | } 37 | 38 | return &PacketAdapter{ 39 | packet, 40 | metadata, 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /clash-meta/adapter/inbound/socket.go: -------------------------------------------------------------------------------- 1 | package inbound 2 | 3 | import ( 4 | "net" 5 | "net/netip" 6 | 7 | C "github.com/Dreamacro/clash/constant" 8 | "github.com/Dreamacro/clash/context" 9 | "github.com/Dreamacro/clash/transport/socks5" 10 | ) 11 | 12 | // NewSocket receive TCP inbound and return ConnContext 13 | func NewSocket(target socks5.Addr, conn net.Conn, source C.Type, additions ...Addition) *context.ConnContext { 14 | metadata := parseSocksAddr(target) 15 | metadata.NetWork = C.TCP 16 | metadata.Type = source 17 | for _, addition := range additions { 18 | addition.Apply(metadata) 19 | } 20 | 21 | if ip, port, err := parseAddr(conn.RemoteAddr()); err == nil { 22 | metadata.SrcIP = ip 23 | metadata.SrcPort = port 24 | } 25 | if ip, port, err := parseAddr(conn.LocalAddr()); err == nil { 26 | metadata.InIP = ip 27 | metadata.InPort = port 28 | } 29 | 30 | return context.NewConnContext(conn, metadata) 31 | } 32 | 33 | func NewInner(conn net.Conn, address string) *context.ConnContext { 34 | metadata := &C.Metadata{} 35 | metadata.NetWork = C.TCP 36 | metadata.Type = C.INNER 37 | metadata.DNSMode = C.DNSNormal 38 | metadata.Process = C.ClashName 39 | if h, port, err := net.SplitHostPort(address); err == nil { 40 | metadata.DstPort = port 41 | if ip, err := netip.ParseAddr(h); err == nil { 42 | metadata.DstIP = ip 43 | } else { 44 | metadata.Host = h 45 | } 46 | } 47 | 48 | return context.NewConnContext(conn, metadata) 49 | } 50 | -------------------------------------------------------------------------------- /clash-meta/adapter/outbound/reality.go: -------------------------------------------------------------------------------- 1 | package outbound 2 | 3 | import ( 4 | "encoding/base64" 5 | "encoding/hex" 6 | "errors" 7 | 8 | tlsC "github.com/Dreamacro/clash/component/tls" 9 | 10 | "golang.org/x/crypto/curve25519" 11 | ) 12 | 13 | type RealityOptions struct { 14 | PublicKey string `proxy:"public-key"` 15 | ShortID string `proxy:"short-id"` 16 | } 17 | 18 | func (o RealityOptions) Parse() (*tlsC.RealityConfig, error) { 19 | if o.PublicKey != "" { 20 | config := new(tlsC.RealityConfig) 21 | 22 | n, err := base64.RawURLEncoding.Decode(config.PublicKey[:], []byte(o.PublicKey)) 23 | if err != nil || n != curve25519.ScalarSize { 24 | return nil, errors.New("invalid REALITY public key") 25 | } 26 | 27 | n, err = hex.Decode(config.ShortID[:], []byte(o.ShortID)) 28 | if err != nil || n > tlsC.RealityMaxShortIDLen { 29 | return nil, errors.New("invalid REALITY short ID") 30 | } 31 | 32 | return config, nil 33 | } 34 | return nil, nil 35 | } 36 | -------------------------------------------------------------------------------- /clash-meta/adapter/outboundgroup/util.go: -------------------------------------------------------------------------------- 1 | package outboundgroup 2 | 3 | import ( 4 | "net" 5 | "time" 6 | ) 7 | 8 | func tcpKeepAlive(c net.Conn) { 9 | if tcp, ok := c.(*net.TCPConn); ok { 10 | _ = tcp.SetKeepAlive(true) 11 | _ = tcp.SetKeepAlivePeriod(30 * time.Second) 12 | } 13 | } 14 | 15 | type SelectAble interface { 16 | Set(string) error 17 | ForceSet(name string) 18 | } 19 | -------------------------------------------------------------------------------- /clash-meta/adapter/provider/subscription_info.go: -------------------------------------------------------------------------------- 1 | package provider 2 | 3 | import ( 4 | "github.com/dlclark/regexp2" 5 | "strconv" 6 | "strings" 7 | ) 8 | 9 | type SubscriptionInfo struct { 10 | Upload int64 11 | Download int64 12 | Total int64 13 | Expire int64 14 | } 15 | 16 | func NewSubscriptionInfo(str string) (si *SubscriptionInfo, err error) { 17 | si = &SubscriptionInfo{} 18 | str = strings.ToLower(str) 19 | reTraffic := regexp2.MustCompile("upload=(\\d+); download=(\\d+); total=(\\d+)", 0) 20 | reExpire := regexp2.MustCompile("expire=(\\d+)", 0) 21 | 22 | match, err := reTraffic.FindStringMatch(str) 23 | if err != nil || match == nil { 24 | return nil, err 25 | } 26 | group := match.Groups() 27 | si.Upload, err = str2uint64(group[1].String()) 28 | if err != nil { 29 | return nil, err 30 | } 31 | 32 | si.Download, err = str2uint64(group[2].String()) 33 | if err != nil { 34 | return nil, err 35 | } 36 | 37 | si.Total, err = str2uint64(group[3].String()) 38 | if err != nil { 39 | return nil, err 40 | } 41 | 42 | match, _ = reExpire.FindStringMatch(str) 43 | if match != nil { 44 | group = match.Groups() 45 | si.Expire, err = str2uint64(group[1].String()) 46 | if err != nil { 47 | return nil, err 48 | } 49 | } 50 | 51 | return 52 | } 53 | 54 | func str2uint64(str string) (int64, error) { 55 | i, err := strconv.ParseInt(str, 10, 64) 56 | return i, err 57 | } 58 | -------------------------------------------------------------------------------- /clash-meta/check_amd64.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | flags=$(grep '^flags\b' 2 && strings.ContainsAny(cmd, "|") { 32 | suffix := strings.Join(args[2:], " ") 33 | args = append(args[:2], suffix) 34 | } 35 | return args 36 | } 37 | -------------------------------------------------------------------------------- /clash-meta/common/cmd/cmd_other.go: -------------------------------------------------------------------------------- 1 | //go:build !windows 2 | 3 | package cmd 4 | 5 | import ( 6 | "os/exec" 7 | ) 8 | 9 | func prepareBackgroundCommand(cmd *exec.Cmd) { 10 | 11 | } 12 | -------------------------------------------------------------------------------- /clash-meta/common/cmd/cmd_test.go: -------------------------------------------------------------------------------- 1 | package cmd 2 | 3 | import ( 4 | "runtime" 5 | "testing" 6 | 7 | "github.com/stretchr/testify/assert" 8 | ) 9 | 10 | func TestSplitArgs(t *testing.T) { 11 | args := splitArgs("ls") 12 | args1 := splitArgs("ls -la") 13 | args2 := splitArgs("bash -c ls") 14 | args3 := splitArgs("bash -c ls -lahF | grep 'cmd'") 15 | 16 | assert.Equal(t, 1, len(args)) 17 | assert.Equal(t, 2, len(args1)) 18 | assert.Equal(t, 3, len(args2)) 19 | assert.Equal(t, 3, len(args3)) 20 | } 21 | 22 | func TestExecCmd(t *testing.T) { 23 | if runtime.GOOS == "windows" { 24 | _, err := ExecCmd("dir") 25 | assert.Nil(t, err) 26 | return 27 | } 28 | 29 | _, err := ExecCmd("ls") 30 | _, err1 := ExecCmd("ls -la") 31 | _, err2 := ExecCmd("bash -c ls") 32 | _, err3 := ExecCmd("bash -c ls -la") 33 | _, err4 := ExecCmd("bash -c ls -la | grep 'cmd'") 34 | 35 | assert.Nil(t, err) 36 | assert.Nil(t, err1) 37 | assert.Nil(t, err2) 38 | assert.Nil(t, err3) 39 | assert.Nil(t, err4) 40 | } 41 | -------------------------------------------------------------------------------- /clash-meta/common/cmd/cmd_windows.go: -------------------------------------------------------------------------------- 1 | //go:build windows 2 | 3 | package cmd 4 | 5 | import ( 6 | "os/exec" 7 | "syscall" 8 | ) 9 | 10 | func prepareBackgroundCommand(cmd *exec.Cmd) { 11 | cmd.SysProcAttr = &syscall.SysProcAttr{HideWindow: true} 12 | } 13 | -------------------------------------------------------------------------------- /clash-meta/common/collections/stack.go: -------------------------------------------------------------------------------- 1 | package collections 2 | 3 | import "sync" 4 | 5 | type ( 6 | stack struct { 7 | top *node 8 | length int 9 | lock *sync.RWMutex 10 | } 11 | 12 | node struct { 13 | value interface{} 14 | prev *node 15 | } 16 | ) 17 | 18 | // NewStack Create a new stack 19 | func NewStack() *stack { 20 | return &stack{nil, 0, &sync.RWMutex{}} 21 | } 22 | 23 | // Len Return the number of items in the stack 24 | func (this *stack) Len() int { 25 | return this.length 26 | } 27 | 28 | // Peek View the top item on the stack 29 | func (this *stack) Peek() interface{} { 30 | if this.length == 0 { 31 | return nil 32 | } 33 | return this.top.value 34 | } 35 | 36 | // Pop the top item of the stack and return it 37 | func (this *stack) Pop() interface{} { 38 | this.lock.Lock() 39 | defer this.lock.Unlock() 40 | if this.length == 0 { 41 | return nil 42 | } 43 | n := this.top 44 | this.top = n.prev 45 | this.length-- 46 | return n.value 47 | } 48 | 49 | // Push a value onto the top of the stack 50 | func (this *stack) Push(value interface{}) { 51 | this.lock.Lock() 52 | defer this.lock.Unlock() 53 | n := &node{value, this.top} 54 | this.top = n 55 | this.length++ 56 | } 57 | -------------------------------------------------------------------------------- /clash-meta/common/convert/base64.go: -------------------------------------------------------------------------------- 1 | package convert 2 | 3 | import ( 4 | "encoding/base64" 5 | "strings" 6 | ) 7 | 8 | var ( 9 | encRaw = base64.RawStdEncoding 10 | enc = base64.StdEncoding 11 | ) 12 | 13 | // DecodeBase64 try to decode content from the given bytes, 14 | // which can be in base64.RawStdEncoding, base64.StdEncoding or just plaintext. 15 | func DecodeBase64(buf []byte) []byte { 16 | result, err := tryDecodeBase64(buf) 17 | if err != nil { 18 | return buf 19 | } 20 | return result 21 | } 22 | 23 | func tryDecodeBase64(buf []byte) ([]byte, error) { 24 | dBuf := make([]byte, encRaw.DecodedLen(len(buf))) 25 | n, err := encRaw.Decode(dBuf, buf) 26 | if err != nil { 27 | n, err = enc.Decode(dBuf, buf) 28 | if err != nil { 29 | return nil, err 30 | } 31 | } 32 | return dBuf[:n], nil 33 | } 34 | 35 | func urlSafe(data string) string { 36 | return strings.NewReplacer("+", "-", "/", "_").Replace(data) 37 | } 38 | 39 | func decodeUrlSafe(data string) string { 40 | dcBuf, err := base64.RawURLEncoding.DecodeString(data) 41 | if err != nil { 42 | return "" 43 | } 44 | return string(dcBuf) 45 | } 46 | -------------------------------------------------------------------------------- /clash-meta/common/murmur3/murmur.go: -------------------------------------------------------------------------------- 1 | package murmur3 2 | 3 | type bmixer interface { 4 | bmix(p []byte) (tail []byte) 5 | Size() (n int) 6 | reset() 7 | } 8 | 9 | type digest struct { 10 | clen int // Digested input cumulative length. 11 | tail []byte // 0 to Size()-1 bytes view of `buf'. 12 | buf [16]byte // Expected (but not required) to be Size() large. 13 | seed uint32 // Seed for initializing the hash. 14 | bmixer 15 | } 16 | 17 | func (d *digest) BlockSize() int { return 1 } 18 | 19 | func (d *digest) Write(p []byte) (n int, err error) { 20 | n = len(p) 21 | d.clen += n 22 | 23 | if len(d.tail) > 0 { 24 | // Stick back pending bytes. 25 | nfree := d.Size() - len(d.tail) // nfree ∈ [1, d.Size()-1]. 26 | if nfree < len(p) { 27 | // One full block can be formed. 28 | block := append(d.tail, p[:nfree]...) 29 | p = p[nfree:] 30 | _ = d.bmix(block) // No tail. 31 | } else { 32 | // Tail's buf is large enough to prevent reallocs. 33 | p = append(d.tail, p...) 34 | } 35 | } 36 | 37 | d.tail = d.bmix(p) 38 | 39 | // Keep own copy of the 0 to Size()-1 pending bytes. 40 | nn := copy(d.buf[:], d.tail) 41 | d.tail = d.buf[:nn] 42 | 43 | return n, nil 44 | } 45 | 46 | func (d *digest) Reset() { 47 | d.clen = 0 48 | d.tail = nil 49 | d.bmixer.reset() 50 | } 51 | -------------------------------------------------------------------------------- /clash-meta/common/net/addr.go: -------------------------------------------------------------------------------- 1 | package net 2 | 3 | import ( 4 | "net" 5 | ) 6 | 7 | type CustomAddr interface { 8 | net.Addr 9 | RawAddr() net.Addr 10 | } 11 | 12 | type customAddr struct { 13 | networkStr string 14 | addrStr string 15 | rawAddr net.Addr 16 | } 17 | 18 | func (a customAddr) Network() string { 19 | return a.networkStr 20 | } 21 | 22 | func (a customAddr) String() string { 23 | return a.addrStr 24 | } 25 | 26 | func (a customAddr) RawAddr() net.Addr { 27 | return a.rawAddr 28 | } 29 | 30 | func NewCustomAddr(networkStr string, addrStr string, rawAddr net.Addr) CustomAddr { 31 | return customAddr{ 32 | networkStr: networkStr, 33 | addrStr: addrStr, 34 | rawAddr: rawAddr, 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /clash-meta/common/net/bind.go: -------------------------------------------------------------------------------- 1 | package net 2 | 3 | import "net" 4 | 5 | type bindPacketConn struct { 6 | EnhancePacketConn 7 | rAddr net.Addr 8 | } 9 | 10 | func (c *bindPacketConn) Read(b []byte) (n int, err error) { 11 | n, _, err = c.EnhancePacketConn.ReadFrom(b) 12 | return n, err 13 | } 14 | 15 | func (c *bindPacketConn) WaitRead() (data []byte, put func(), err error) { 16 | data, put, _, err = c.EnhancePacketConn.WaitReadFrom() 17 | return 18 | } 19 | 20 | func (c *bindPacketConn) Write(b []byte) (n int, err error) { 21 | return c.EnhancePacketConn.WriteTo(b, c.rAddr) 22 | } 23 | 24 | func (c *bindPacketConn) RemoteAddr() net.Addr { 25 | return c.rAddr 26 | } 27 | 28 | func (c *bindPacketConn) LocalAddr() net.Addr { 29 | if c.EnhancePacketConn.LocalAddr() == nil { 30 | return &net.UDPAddr{IP: net.IPv4zero, Port: 0} 31 | } else { 32 | return c.EnhancePacketConn.LocalAddr() 33 | } 34 | } 35 | 36 | func (c *bindPacketConn) Upstream() any { 37 | return c.EnhancePacketConn 38 | } 39 | 40 | func NewBindPacketConn(pc net.PacketConn, rAddr net.Addr) net.Conn { 41 | return &bindPacketConn{ 42 | EnhancePacketConn: NewEnhancePacketConn(pc), 43 | rAddr: rAddr, 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /clash-meta/common/net/io.go: -------------------------------------------------------------------------------- 1 | package net 2 | 3 | import "io" 4 | 5 | type ReadOnlyReader struct { 6 | io.Reader 7 | } 8 | 9 | type WriteOnlyWriter struct { 10 | io.Writer 11 | } 12 | -------------------------------------------------------------------------------- /clash-meta/common/net/packet.go: -------------------------------------------------------------------------------- 1 | package net 2 | 3 | import ( 4 | "github.com/Dreamacro/clash/common/net/deadline" 5 | "github.com/Dreamacro/clash/common/net/packet" 6 | ) 7 | 8 | type EnhancePacketConn = packet.EnhancePacketConn 9 | type WaitReadFrom = packet.WaitReadFrom 10 | 11 | var NewEnhancePacketConn = packet.NewEnhancePacketConn 12 | var NewThreadSafePacketConn = packet.NewThreadSafePacketConn 13 | var NewRefPacketConn = packet.NewRefPacketConn 14 | 15 | var NewDeadlineNetPacketConn = deadline.NewNetPacketConn 16 | var NewDeadlineEnhancePacketConn = deadline.NewEnhancePacketConn 17 | var NewDeadlineSingPacketConn = deadline.NewSingPacketConn 18 | var NewDeadlineEnhanceSingPacketConn = deadline.NewEnhanceSingPacketConn 19 | -------------------------------------------------------------------------------- /clash-meta/common/net/packet/packet_windows.go: -------------------------------------------------------------------------------- 1 | //go:build windows 2 | 3 | package packet 4 | 5 | import ( 6 | "net" 7 | ) 8 | 9 | type enhanceUDPConn struct { 10 | *net.UDPConn 11 | } 12 | 13 | func (c *enhanceUDPConn) WaitReadFrom() (data []byte, put func(), addr net.Addr, err error) { 14 | return waitReadFrom(c.UDPConn) 15 | } 16 | -------------------------------------------------------------------------------- /clash-meta/common/net/packet/ref_sing.go: -------------------------------------------------------------------------------- 1 | package packet 2 | 3 | import ( 4 | "runtime" 5 | 6 | "github.com/sagernet/sing/common/buf" 7 | M "github.com/sagernet/sing/common/metadata" 8 | N "github.com/sagernet/sing/common/network" 9 | ) 10 | 11 | type refSingPacketConn struct { 12 | *refPacketConn 13 | singPacketConn SingPacketConn 14 | } 15 | 16 | var _ N.NetPacketConn = (*refSingPacketConn)(nil) 17 | 18 | func (c *refSingPacketConn) WritePacket(buffer *buf.Buffer, destination M.Socksaddr) error { 19 | defer runtime.KeepAlive(c.ref) 20 | return c.singPacketConn.WritePacket(buffer, destination) 21 | } 22 | 23 | func (c *refSingPacketConn) ReadPacket(buffer *buf.Buffer) (destination M.Socksaddr, err error) { 24 | defer runtime.KeepAlive(c.ref) 25 | return c.singPacketConn.ReadPacket(buffer) 26 | } 27 | -------------------------------------------------------------------------------- /clash-meta/common/net/packet/thread.go: -------------------------------------------------------------------------------- 1 | package packet 2 | 3 | import ( 4 | "net" 5 | "sync" 6 | ) 7 | 8 | type threadSafePacketConn struct { 9 | EnhancePacketConn 10 | access sync.Mutex 11 | } 12 | 13 | func (c *threadSafePacketConn) WriteTo(b []byte, addr net.Addr) (int, error) { 14 | c.access.Lock() 15 | defer c.access.Unlock() 16 | return c.EnhancePacketConn.WriteTo(b, addr) 17 | } 18 | 19 | func (c *threadSafePacketConn) Upstream() any { 20 | return c.EnhancePacketConn 21 | } 22 | 23 | func (c *threadSafePacketConn) ReaderReplaceable() bool { 24 | return true 25 | } 26 | 27 | func NewThreadSafePacketConn(pc net.PacketConn) EnhancePacketConn { 28 | tsPC := &threadSafePacketConn{EnhancePacketConn: NewEnhancePacketConn(pc)} 29 | if singPC, isSingPC := pc.(SingPacketConn); isSingPC { 30 | return &threadSafeSingPacketConn{ 31 | threadSafePacketConn: tsPC, 32 | singPacketConn: singPC, 33 | } 34 | } 35 | return tsPC 36 | } 37 | -------------------------------------------------------------------------------- /clash-meta/common/net/packet/thread_sing.go: -------------------------------------------------------------------------------- 1 | package packet 2 | 3 | import ( 4 | "github.com/sagernet/sing/common/buf" 5 | M "github.com/sagernet/sing/common/metadata" 6 | N "github.com/sagernet/sing/common/network" 7 | ) 8 | 9 | type threadSafeSingPacketConn struct { 10 | *threadSafePacketConn 11 | singPacketConn SingPacketConn 12 | } 13 | 14 | var _ N.NetPacketConn = (*threadSafeSingPacketConn)(nil) 15 | 16 | func (c *threadSafeSingPacketConn) WritePacket(buffer *buf.Buffer, destination M.Socksaddr) error { 17 | c.access.Lock() 18 | defer c.access.Unlock() 19 | return c.singPacketConn.WritePacket(buffer, destination) 20 | } 21 | 22 | func (c *threadSafeSingPacketConn) ReadPacket(buffer *buf.Buffer) (destination M.Socksaddr, err error) { 23 | return c.singPacketConn.ReadPacket(buffer) 24 | } 25 | -------------------------------------------------------------------------------- /clash-meta/common/net/relay.go: -------------------------------------------------------------------------------- 1 | package net 2 | 3 | //import ( 4 | // "io" 5 | // "net" 6 | // "time" 7 | //) 8 | // 9 | //// Relay copies between left and right bidirectionally. 10 | //func Relay(leftConn, rightConn net.Conn) { 11 | // ch := make(chan error) 12 | // 13 | // go func() { 14 | // // Wrapping to avoid using *net.TCPConn.(ReadFrom) 15 | // // See also https://github.com/Dreamacro/clash/pull/1209 16 | // _, err := io.Copy(WriteOnlyWriter{Writer: leftConn}, ReadOnlyReader{Reader: rightConn}) 17 | // leftConn.SetReadDeadline(time.Now()) 18 | // ch <- err 19 | // }() 20 | // 21 | // _, _ = io.Copy(WriteOnlyWriter{Writer: rightConn}, ReadOnlyReader{Reader: leftConn}) 22 | // rightConn.SetReadDeadline(time.Now()) 23 | // <-ch 24 | //} 25 | -------------------------------------------------------------------------------- /clash-meta/common/net/sing.go: -------------------------------------------------------------------------------- 1 | package net 2 | 3 | import ( 4 | "context" 5 | "net" 6 | "runtime" 7 | 8 | "github.com/sagernet/sing/common" 9 | "github.com/sagernet/sing/common/bufio" 10 | "github.com/sagernet/sing/common/bufio/deadline" 11 | "github.com/sagernet/sing/common/network" 12 | ) 13 | 14 | var NewExtendedConn = bufio.NewExtendedConn 15 | var NewExtendedWriter = bufio.NewExtendedWriter 16 | var NewExtendedReader = bufio.NewExtendedReader 17 | 18 | type ExtendedConn = network.ExtendedConn 19 | type ExtendedWriter = network.ExtendedWriter 20 | type ExtendedReader = network.ExtendedReader 21 | 22 | func NewDeadlineConn(conn net.Conn) ExtendedConn { 23 | return deadline.NewFallbackConn(conn) 24 | } 25 | 26 | func NeedHandshake(conn any) bool { 27 | if earlyConn, isEarlyConn := common.Cast[network.EarlyConn](conn); isEarlyConn && earlyConn.NeedHandshake() { 28 | return true 29 | } 30 | return false 31 | } 32 | 33 | type CountFunc = network.CountFunc 34 | 35 | // Relay copies between left and right bidirectionally. 36 | func Relay(leftConn, rightConn net.Conn) { 37 | defer runtime.KeepAlive(leftConn) 38 | defer runtime.KeepAlive(rightConn) 39 | _ = bufio.CopyConn(context.TODO(), leftConn, rightConn) 40 | } 41 | -------------------------------------------------------------------------------- /clash-meta/common/net/tcpip.go: -------------------------------------------------------------------------------- 1 | package net 2 | 3 | import ( 4 | "fmt" 5 | "net" 6 | "strings" 7 | ) 8 | 9 | func SplitNetworkType(s string) (string, string, error) { 10 | var ( 11 | shecme string 12 | hostPort string 13 | ) 14 | result := strings.Split(s, "://") 15 | if len(result) == 2 { 16 | shecme = result[0] 17 | hostPort = result[1] 18 | } else if len(result) == 1 { 19 | hostPort = result[0] 20 | } else { 21 | return "", "", fmt.Errorf("tcp/udp style error") 22 | } 23 | 24 | if len(shecme) == 0 { 25 | shecme = "udp" 26 | } 27 | 28 | if shecme != "tcp" && shecme != "udp" { 29 | return "", "", fmt.Errorf("scheme should be tcp:// or udp://") 30 | } else { 31 | return shecme, hostPort, nil 32 | } 33 | } 34 | 35 | func SplitHostPort(s string) (host, port string, hasPort bool, err error) { 36 | temp := s 37 | hasPort = true 38 | 39 | if !strings.Contains(s, ":") && !strings.Contains(s, "]:") { 40 | temp += ":0" 41 | hasPort = false 42 | } 43 | 44 | host, port, err = net.SplitHostPort(temp) 45 | return 46 | } 47 | -------------------------------------------------------------------------------- /clash-meta/common/net/tls.go: -------------------------------------------------------------------------------- 1 | package net 2 | 3 | import ( 4 | "crypto/rand" 5 | "crypto/rsa" 6 | "crypto/tls" 7 | "crypto/x509" 8 | "encoding/pem" 9 | "fmt" 10 | "math/big" 11 | ) 12 | 13 | func ParseCert(certificate, privateKey string) (tls.Certificate, error) { 14 | if certificate == "" && privateKey == "" { 15 | return newRandomTLSKeyPair() 16 | } 17 | cert, painTextErr := tls.X509KeyPair([]byte(certificate), []byte(privateKey)) 18 | if painTextErr == nil { 19 | return cert, nil 20 | } 21 | 22 | cert, loadErr := tls.LoadX509KeyPair(certificate, privateKey) 23 | if loadErr != nil { 24 | return tls.Certificate{}, fmt.Errorf("parse certificate failed, maybe format error:%s, or path error: %s", painTextErr.Error(), loadErr.Error()) 25 | } 26 | return cert, nil 27 | } 28 | 29 | func newRandomTLSKeyPair() (tls.Certificate, error) { 30 | key, err := rsa.GenerateKey(rand.Reader, 2048) 31 | if err != nil { 32 | return tls.Certificate{}, err 33 | } 34 | template := x509.Certificate{SerialNumber: big.NewInt(1)} 35 | certDER, err := x509.CreateCertificate( 36 | rand.Reader, 37 | &template, 38 | &template, 39 | &key.PublicKey, 40 | key) 41 | if err != nil { 42 | return tls.Certificate{}, err 43 | } 44 | keyPEM := pem.EncodeToMemory(&pem.Block{Type: "RSA PRIVATE KEY", Bytes: x509.MarshalPKCS1PrivateKey(key)}) 45 | certPEM := pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE", Bytes: certDER}) 46 | 47 | tlsCert, err := tls.X509KeyPair(certPEM, keyPEM) 48 | if err != nil { 49 | return tls.Certificate{}, err 50 | } 51 | return tlsCert, nil 52 | } 53 | -------------------------------------------------------------------------------- /clash-meta/common/nnip/netip.go: -------------------------------------------------------------------------------- 1 | package nnip 2 | 3 | import ( 4 | "encoding/binary" 5 | "net" 6 | "net/netip" 7 | ) 8 | 9 | // IpToAddr converts the net.IP to netip.Addr. 10 | // If slice's length is not 4 or 16, IpToAddr returns netip.Addr{} 11 | func IpToAddr(slice net.IP) netip.Addr { 12 | ip := slice 13 | if len(ip) != 4 { 14 | if ip = slice.To4(); ip == nil { 15 | ip = slice 16 | } 17 | } 18 | 19 | if addr, ok := netip.AddrFromSlice(ip); ok { 20 | return addr 21 | } 22 | return netip.Addr{} 23 | } 24 | 25 | // UnMasked returns p's last IP address. 26 | // If p is invalid, UnMasked returns netip.Addr{} 27 | func UnMasked(p netip.Prefix) netip.Addr { 28 | if !p.IsValid() { 29 | return netip.Addr{} 30 | } 31 | 32 | buf := p.Addr().As16() 33 | 34 | hi := binary.BigEndian.Uint64(buf[:8]) 35 | lo := binary.BigEndian.Uint64(buf[8:]) 36 | 37 | bits := p.Bits() 38 | if bits <= 32 { 39 | bits += 96 40 | } 41 | 42 | hi = hi | ^uint64(0)>>bits 43 | lo = lo | ^(^uint64(0) << (128 - bits)) 44 | 45 | binary.BigEndian.PutUint64(buf[:8], hi) 46 | binary.BigEndian.PutUint64(buf[8:], lo) 47 | 48 | addr := netip.AddrFrom16(buf) 49 | if p.Addr().Is4() { 50 | return addr.Unmap() 51 | } 52 | return addr 53 | } 54 | -------------------------------------------------------------------------------- /clash-meta/common/observable/iterable.go: -------------------------------------------------------------------------------- 1 | package observable 2 | 3 | type Iterable[T any] <-chan T 4 | -------------------------------------------------------------------------------- /clash-meta/common/observable/observable.go: -------------------------------------------------------------------------------- 1 | package observable 2 | 3 | import ( 4 | "errors" 5 | "sync" 6 | ) 7 | 8 | type Observable[T any] struct { 9 | iterable Iterable[T] 10 | listener map[Subscription[T]]*Subscriber[T] 11 | mux sync.Mutex 12 | done bool 13 | } 14 | 15 | func (o *Observable[T]) process() { 16 | for item := range o.iterable { 17 | o.mux.Lock() 18 | for _, sub := range o.listener { 19 | sub.Emit(item) 20 | } 21 | o.mux.Unlock() 22 | } 23 | o.close() 24 | } 25 | 26 | func (o *Observable[T]) close() { 27 | o.mux.Lock() 28 | defer o.mux.Unlock() 29 | 30 | o.done = true 31 | for _, sub := range o.listener { 32 | sub.Close() 33 | } 34 | } 35 | 36 | func (o *Observable[T]) Subscribe() (Subscription[T], error) { 37 | o.mux.Lock() 38 | defer o.mux.Unlock() 39 | if o.done { 40 | return nil, errors.New("observable is closed") 41 | } 42 | subscriber := newSubscriber[T]() 43 | o.listener[subscriber.Out()] = subscriber 44 | return subscriber.Out(), nil 45 | } 46 | 47 | func (o *Observable[T]) UnSubscribe(sub Subscription[T]) { 48 | o.mux.Lock() 49 | defer o.mux.Unlock() 50 | subscriber, exist := o.listener[sub] 51 | if !exist { 52 | return 53 | } 54 | delete(o.listener, sub) 55 | subscriber.Close() 56 | } 57 | 58 | func NewObservable[T any](iter Iterable[T]) *Observable[T] { 59 | observable := &Observable[T]{ 60 | iterable: iter, 61 | listener: map[Subscription[T]]*Subscriber[T]{}, 62 | } 63 | go observable.process() 64 | return observable 65 | } 66 | -------------------------------------------------------------------------------- /clash-meta/common/observable/subscriber.go: -------------------------------------------------------------------------------- 1 | package observable 2 | 3 | import ( 4 | "sync" 5 | ) 6 | 7 | type Subscription[T any] <-chan T 8 | 9 | type Subscriber[T any] struct { 10 | buffer chan T 11 | once sync.Once 12 | } 13 | 14 | func (s *Subscriber[T]) Emit(item T) { 15 | s.buffer <- item 16 | } 17 | 18 | func (s *Subscriber[T]) Out() Subscription[T] { 19 | return s.buffer 20 | } 21 | 22 | func (s *Subscriber[T]) Close() { 23 | s.once.Do(func() { 24 | close(s.buffer) 25 | }) 26 | } 27 | 28 | func newSubscriber[T any]() *Subscriber[T] { 29 | sub := &Subscriber[T]{ 30 | buffer: make(chan T, 200), 31 | } 32 | return sub 33 | } 34 | -------------------------------------------------------------------------------- /clash-meta/common/picker/picker_test.go: -------------------------------------------------------------------------------- 1 | package picker 2 | 3 | import ( 4 | "context" 5 | "testing" 6 | "time" 7 | 8 | "github.com/stretchr/testify/assert" 9 | ) 10 | 11 | func sleepAndSend[T any](ctx context.Context, delay int, input T) func() (T, error) { 12 | return func() (T, error) { 13 | timer := time.NewTimer(time.Millisecond * time.Duration(delay)) 14 | select { 15 | case <-timer.C: 16 | return input, nil 17 | case <-ctx.Done(): 18 | return getZero[T](), ctx.Err() 19 | } 20 | } 21 | } 22 | 23 | func TestPicker_Basic(t *testing.T) { 24 | picker, ctx := WithContext[int](context.Background()) 25 | picker.Go(sleepAndSend(ctx, 30, 2)) 26 | picker.Go(sleepAndSend(ctx, 20, 1)) 27 | 28 | number := picker.Wait() 29 | assert.NotNil(t, number) 30 | assert.Equal(t, number, 1) 31 | } 32 | 33 | func TestPicker_Timeout(t *testing.T) { 34 | picker, ctx := WithTimeout[int](context.Background(), time.Millisecond*5) 35 | picker.Go(sleepAndSend(ctx, 20, 1)) 36 | 37 | number := picker.Wait() 38 | assert.Equal(t, number, getZero[int]()) 39 | assert.NotNil(t, picker.Error()) 40 | } 41 | 42 | func getZero[T any]() T { 43 | var result T 44 | return result 45 | } 46 | -------------------------------------------------------------------------------- /clash-meta/common/pool/alloc_test.go: -------------------------------------------------------------------------------- 1 | package pool 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/stretchr/testify/assert" 7 | "github.com/zhangyunhao116/fastrand" 8 | ) 9 | 10 | func TestAllocGet(t *testing.T) { 11 | alloc := NewAllocator() 12 | assert.Nil(t, alloc.Get(0)) 13 | assert.Equal(t, 1, len(alloc.Get(1))) 14 | assert.Equal(t, 2, len(alloc.Get(2))) 15 | assert.Equal(t, 3, len(alloc.Get(3))) 16 | assert.Equal(t, 4, cap(alloc.Get(3))) 17 | assert.Equal(t, 4, cap(alloc.Get(4))) 18 | assert.Equal(t, 1023, len(alloc.Get(1023))) 19 | assert.Equal(t, 1024, cap(alloc.Get(1023))) 20 | assert.Equal(t, 1024, len(alloc.Get(1024))) 21 | assert.Equal(t, 65536, len(alloc.Get(65536))) 22 | assert.Nil(t, alloc.Get(65537)) 23 | } 24 | 25 | func TestAllocPut(t *testing.T) { 26 | alloc := NewAllocator() 27 | assert.NotNil(t, alloc.Put(nil), "put nil misbehavior") 28 | assert.NotNil(t, alloc.Put(make([]byte, 3)), "put elem:3 []bytes misbehavior") 29 | assert.Nil(t, alloc.Put(make([]byte, 4)), "put elem:4 []bytes misbehavior") 30 | assert.Nil(t, alloc.Put(make([]byte, 1023, 1024)), "put elem:1024 []bytes misbehavior") 31 | assert.Nil(t, alloc.Put(make([]byte, 65536)), "put elem:65536 []bytes misbehavior") 32 | assert.NotNil(t, alloc.Put(make([]byte, 65537)), "put elem:65537 []bytes misbehavior") 33 | } 34 | 35 | func TestAllocPutThenGet(t *testing.T) { 36 | alloc := NewAllocator() 37 | data := alloc.Get(4) 38 | alloc.Put(data) 39 | newData := alloc.Get(4) 40 | 41 | assert.Equal(t, cap(data), cap(newData), "different cap while alloc.Get()") 42 | } 43 | 44 | func BenchmarkMSB(b *testing.B) { 45 | for i := 0; i < b.N; i++ { 46 | msb(fastrand.Int()) 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /clash-meta/common/pool/buffer.go: -------------------------------------------------------------------------------- 1 | package pool 2 | 3 | import ( 4 | "bytes" 5 | "sync" 6 | ) 7 | 8 | var bufferPool = sync.Pool{New: func() any { return &bytes.Buffer{} }} 9 | 10 | func GetBuffer() *bytes.Buffer { 11 | return bufferPool.Get().(*bytes.Buffer) 12 | } 13 | 14 | func PutBuffer(buf *bytes.Buffer) { 15 | buf.Reset() 16 | bufferPool.Put(buf) 17 | } 18 | -------------------------------------------------------------------------------- /clash-meta/common/pool/buffer_low_memory.go: -------------------------------------------------------------------------------- 1 | //go:build with_low_memory 2 | 3 | package pool 4 | 5 | const ( 6 | // io.Copy default buffer size is 32 KiB 7 | // but the maximum packet size of vmess/shadowsocks is about 16 KiB 8 | // so define a buffer of 20 KiB to reduce the memory of each TCP relay 9 | RelayBufferSize = 16 * 1024 10 | 11 | // RelayBufferSize uses 20KiB, but due to the allocator it will actually 12 | // request 32Kib. Most UDPs are smaller than the MTU, and the TUN's MTU 13 | // set to 9000, so the UDP Buffer size set to 16Kib 14 | UDPBufferSize = 8 * 1024 15 | ) 16 | -------------------------------------------------------------------------------- /clash-meta/common/pool/buffer_standard.go: -------------------------------------------------------------------------------- 1 | //go:build !with_low_memory 2 | 3 | package pool 4 | 5 | const ( 6 | // io.Copy default buffer size is 32 KiB 7 | // but the maximum packet size of vmess/shadowsocks is about 16 KiB 8 | // so define a buffer of 20 KiB to reduce the memory of each TCP relay 9 | RelayBufferSize = 20 * 1024 10 | 11 | // RelayBufferSize uses 20KiB, but due to the allocator it will actually 12 | // request 32Kib. Most UDPs are smaller than the MTU, and the TUN's MTU 13 | // set to 9000, so the UDP Buffer size set to 16Kib 14 | UDPBufferSize = 16 * 1024 15 | ) 16 | -------------------------------------------------------------------------------- /clash-meta/common/pool/pool.go: -------------------------------------------------------------------------------- 1 | package pool 2 | 3 | func Get(size int) []byte { 4 | return defaultAllocator.Get(size) 5 | } 6 | 7 | func Put(buf []byte) error { 8 | return defaultAllocator.Put(buf) 9 | } 10 | -------------------------------------------------------------------------------- /clash-meta/common/pool/sing.go: -------------------------------------------------------------------------------- 1 | package pool 2 | 3 | import "github.com/sagernet/sing/common/buf" 4 | 5 | func init() { 6 | buf.DefaultAllocator = defaultAllocator 7 | } 8 | -------------------------------------------------------------------------------- /clash-meta/common/queue/queue.go: -------------------------------------------------------------------------------- 1 | package queue 2 | 3 | import ( 4 | "sync" 5 | ) 6 | 7 | // Queue is a simple concurrent safe queue 8 | type Queue[T any] struct { 9 | items []T 10 | lock sync.RWMutex 11 | } 12 | 13 | // Put add the item to the queue. 14 | func (q *Queue[T]) Put(items ...T) { 15 | if len(items) == 0 { 16 | return 17 | } 18 | 19 | q.lock.Lock() 20 | q.items = append(q.items, items...) 21 | q.lock.Unlock() 22 | } 23 | 24 | // Pop returns the head of items. 25 | func (q *Queue[T]) Pop() T { 26 | if len(q.items) == 0 { 27 | return GetZero[T]() 28 | } 29 | 30 | q.lock.Lock() 31 | head := q.items[0] 32 | q.items = q.items[1:] 33 | q.lock.Unlock() 34 | return head 35 | } 36 | 37 | // Last returns the last of item. 38 | func (q *Queue[T]) Last() T { 39 | if len(q.items) == 0 { 40 | return GetZero[T]() 41 | } 42 | 43 | q.lock.RLock() 44 | last := q.items[len(q.items)-1] 45 | q.lock.RUnlock() 46 | return last 47 | } 48 | 49 | // Copy get the copy of queue. 50 | func (q *Queue[T]) Copy() []T { 51 | items := []T{} 52 | q.lock.RLock() 53 | items = append(items, q.items...) 54 | q.lock.RUnlock() 55 | return items 56 | } 57 | 58 | // Len returns the number of items in this queue. 59 | func (q *Queue[T]) Len() int64 { 60 | q.lock.Lock() 61 | defer q.lock.Unlock() 62 | 63 | return int64(len(q.items)) 64 | } 65 | 66 | // New is a constructor for a new concurrent safe queue. 67 | func New[T any](hint int64) *Queue[T] { 68 | return &Queue[T]{ 69 | items: make([]T, 0, hint), 70 | } 71 | } 72 | 73 | func GetZero[T any]() T { 74 | var result T 75 | return result 76 | } 77 | -------------------------------------------------------------------------------- /clash-meta/common/singledo/singledo.go: -------------------------------------------------------------------------------- 1 | package singledo 2 | 3 | import ( 4 | "sync" 5 | "time" 6 | ) 7 | 8 | type call[T any] struct { 9 | wg sync.WaitGroup 10 | val T 11 | err error 12 | } 13 | 14 | type Single[T any] struct { 15 | mux sync.Mutex 16 | last time.Time 17 | wait time.Duration 18 | call *call[T] 19 | result *Result[T] 20 | } 21 | 22 | type Result[T any] struct { 23 | Val T 24 | Err error 25 | } 26 | 27 | // Do single.Do likes sync.singleFlight 28 | func (s *Single[T]) Do(fn func() (T, error)) (v T, err error, shared bool) { 29 | s.mux.Lock() 30 | now := time.Now() 31 | if now.Before(s.last.Add(s.wait)) { 32 | s.mux.Unlock() 33 | return s.result.Val, s.result.Err, true 34 | } 35 | 36 | if callM := s.call; callM != nil { 37 | s.mux.Unlock() 38 | callM.wg.Wait() 39 | return callM.val, callM.err, true 40 | } 41 | 42 | callM := &call[T]{} 43 | callM.wg.Add(1) 44 | s.call = callM 45 | s.mux.Unlock() 46 | callM.val, callM.err = fn() 47 | callM.wg.Done() 48 | 49 | s.mux.Lock() 50 | s.call = nil 51 | s.result = &Result[T]{callM.val, callM.err} 52 | s.last = now 53 | s.mux.Unlock() 54 | return callM.val, callM.err, false 55 | } 56 | 57 | func (s *Single[T]) Reset() { 58 | s.last = time.Time{} 59 | } 60 | 61 | func NewSingle[T any](wait time.Duration) *Single[T] { 62 | return &Single[T]{wait: wait} 63 | } 64 | -------------------------------------------------------------------------------- /clash-meta/common/singledo/singledo_test.go: -------------------------------------------------------------------------------- 1 | package singledo 2 | 3 | import ( 4 | "sync" 5 | "testing" 6 | "time" 7 | 8 | "github.com/Dreamacro/clash/common/atomic" 9 | 10 | "github.com/stretchr/testify/assert" 11 | ) 12 | 13 | func TestBasic(t *testing.T) { 14 | single := NewSingle[int](time.Millisecond * 30) 15 | foo := 0 16 | shardCount := atomic.NewInt32(0) 17 | call := func() (int, error) { 18 | foo++ 19 | time.Sleep(time.Millisecond * 5) 20 | return 0, nil 21 | } 22 | 23 | var wg sync.WaitGroup 24 | const n = 5 25 | wg.Add(n) 26 | for i := 0; i < n; i++ { 27 | go func() { 28 | _, _, shard := single.Do(call) 29 | if shard { 30 | shardCount.Add(1) 31 | } 32 | wg.Done() 33 | }() 34 | } 35 | 36 | wg.Wait() 37 | assert.Equal(t, 1, foo) 38 | assert.Equal(t, int32(4), shardCount.Load()) 39 | } 40 | 41 | func TestTimer(t *testing.T) { 42 | single := NewSingle[int](time.Millisecond * 30) 43 | foo := 0 44 | callM := func() (int, error) { 45 | foo++ 46 | return 0, nil 47 | } 48 | 49 | _, _, _ = single.Do(callM) 50 | time.Sleep(10 * time.Millisecond) 51 | _, _, shard := single.Do(callM) 52 | 53 | assert.Equal(t, 1, foo) 54 | assert.True(t, shard) 55 | } 56 | 57 | func TestReset(t *testing.T) { 58 | single := NewSingle[int](time.Millisecond * 30) 59 | foo := 0 60 | callM := func() (int, error) { 61 | foo++ 62 | return 0, nil 63 | } 64 | 65 | _, _, _ = single.Do(callM) 66 | single.Reset() 67 | _, _, _ = single.Do(callM) 68 | 69 | assert.Equal(t, 2, foo) 70 | } 71 | -------------------------------------------------------------------------------- /clash-meta/common/sockopt/reuseaddr_linux.go: -------------------------------------------------------------------------------- 1 | package sockopt 2 | 3 | import ( 4 | "net" 5 | "syscall" 6 | ) 7 | 8 | func UDPReuseaddr(c *net.UDPConn) (err error) { 9 | rc, err := c.SyscallConn() 10 | if err != nil { 11 | return 12 | } 13 | 14 | rc.Control(func(fd uintptr) { 15 | err = syscall.SetsockoptInt(int(fd), syscall.SOL_SOCKET, syscall.SO_REUSEADDR, 1) 16 | }) 17 | 18 | return 19 | } 20 | -------------------------------------------------------------------------------- /clash-meta/common/sockopt/reuseaddr_other.go: -------------------------------------------------------------------------------- 1 | //go:build !linux 2 | 3 | package sockopt 4 | 5 | import ( 6 | "net" 7 | ) 8 | 9 | func UDPReuseaddr(c *net.UDPConn) (err error) { 10 | return 11 | } 12 | -------------------------------------------------------------------------------- /clash-meta/common/utils/must.go: -------------------------------------------------------------------------------- 1 | package utils 2 | 3 | func MustOK[T any](result T, ok bool) T { 4 | if ok { 5 | return result 6 | } 7 | panic("operation failed") 8 | } 9 | -------------------------------------------------------------------------------- /clash-meta/common/utils/range.go: -------------------------------------------------------------------------------- 1 | package utils 2 | 3 | import ( 4 | "golang.org/x/exp/constraints" 5 | ) 6 | 7 | type Range[T constraints.Ordered] struct { 8 | start T 9 | end T 10 | } 11 | 12 | func NewRange[T constraints.Ordered](start, end T) *Range[T] { 13 | if start > end { 14 | return &Range[T]{ 15 | start: end, 16 | end: start, 17 | } 18 | } 19 | 20 | return &Range[T]{ 21 | start: start, 22 | end: end, 23 | } 24 | } 25 | 26 | func (r *Range[T]) Contains(t T) bool { 27 | return t >= r.start && t <= r.end 28 | } 29 | 30 | func (r *Range[T]) LeftContains(t T) bool { 31 | return t >= r.start && t < r.end 32 | } 33 | 34 | func (r *Range[T]) RightContains(t T) bool { 35 | return t > r.start && t <= r.end 36 | } 37 | 38 | func (r *Range[T]) Start() T { 39 | return r.start 40 | } 41 | 42 | func (r *Range[T]) End() T { 43 | return r.end 44 | } 45 | -------------------------------------------------------------------------------- /clash-meta/common/utils/slice.go: -------------------------------------------------------------------------------- 1 | package utils 2 | 3 | import ( 4 | "errors" 5 | "fmt" 6 | "reflect" 7 | ) 8 | 9 | func Filter[T comparable](tSlice []T, filter func(t T) bool) []T { 10 | result := make([]T, 0) 11 | for _, t := range tSlice { 12 | if filter(t) { 13 | result = append(result, t) 14 | } 15 | } 16 | return result 17 | } 18 | 19 | func ToStringSlice(value any) ([]string, error) { 20 | strArr := make([]string, 0) 21 | switch reflect.TypeOf(value).Kind() { 22 | case reflect.Slice, reflect.Array: 23 | origin := reflect.ValueOf(value) 24 | for i := 0; i < origin.Len(); i++ { 25 | item := fmt.Sprintf("%v", origin.Index(i)) 26 | strArr = append(strArr, item) 27 | } 28 | case reflect.String: 29 | strArr = append(strArr, fmt.Sprintf("%v", value)) 30 | default: 31 | return nil, errors.New("value format error, must be string or array") 32 | } 33 | return strArr, nil 34 | } 35 | -------------------------------------------------------------------------------- /clash-meta/common/utils/strings.go: -------------------------------------------------------------------------------- 1 | package utils 2 | 3 | func Reverse(s string) string { 4 | a := []rune(s) 5 | for i, j := 0, len(a)-1; i < j; i, j = i+1, j-1 { 6 | a[i], a[j] = a[j], a[i] 7 | } 8 | return string(a) 9 | } 10 | -------------------------------------------------------------------------------- /clash-meta/common/utils/uuid.go: -------------------------------------------------------------------------------- 1 | package utils 2 | 3 | import ( 4 | "github.com/gofrs/uuid/v5" 5 | "github.com/zhangyunhao116/fastrand" 6 | ) 7 | 8 | type fastRandReader struct{} 9 | 10 | func (r fastRandReader) Read(p []byte) (int, error) { 11 | return fastrand.Read(p) 12 | } 13 | 14 | var UnsafeUUIDGenerator = uuid.NewGenWithOptions(uuid.WithRandomReader(fastRandReader{})) 15 | 16 | func NewUUIDV1() uuid.UUID { 17 | u, _ := UnsafeUUIDGenerator.NewV1() // fastrand.Read wouldn't cause error, so ignore err is safe 18 | return u 19 | } 20 | 21 | func NewUUIDV3(ns uuid.UUID, name string) uuid.UUID { 22 | return UnsafeUUIDGenerator.NewV3(ns, name) 23 | } 24 | 25 | func NewUUIDV4() uuid.UUID { 26 | u, _ := UnsafeUUIDGenerator.NewV4() // fastrand.Read wouldn't cause error, so ignore err is safe 27 | return u 28 | } 29 | 30 | func NewUUIDV5(ns uuid.UUID, name string) uuid.UUID { 31 | return UnsafeUUIDGenerator.NewV5(ns, name) 32 | } 33 | 34 | func NewUUIDV6() uuid.UUID { 35 | u, _ := UnsafeUUIDGenerator.NewV6() // fastrand.Read wouldn't cause error, so ignore err is safe 36 | return u 37 | } 38 | 39 | func NewUUIDV7() uuid.UUID { 40 | u, _ := UnsafeUUIDGenerator.NewV7() // fastrand.Read wouldn't cause error, so ignore err is safe 41 | return u 42 | } 43 | 44 | // UUIDMap https://github.com/XTLS/Xray-core/issues/158#issue-783294090 45 | func UUIDMap(str string) (uuid.UUID, error) { 46 | u, err := uuid.FromString(str) 47 | if err != nil { 48 | return NewUUIDV5(uuid.Nil, str), nil 49 | } 50 | return u, nil 51 | } 52 | -------------------------------------------------------------------------------- /clash-meta/component/auth/auth.go: -------------------------------------------------------------------------------- 1 | package auth 2 | 3 | import ( 4 | "sync" 5 | ) 6 | 7 | type Authenticator interface { 8 | Verify(user string, pass string) bool 9 | Users() []string 10 | } 11 | 12 | type AuthUser struct { 13 | User string 14 | Pass string 15 | } 16 | 17 | type inMemoryAuthenticator struct { 18 | storage *sync.Map 19 | usernames []string 20 | } 21 | 22 | func (au *inMemoryAuthenticator) Verify(user string, pass string) bool { 23 | realPass, ok := au.storage.Load(user) 24 | return ok && realPass == pass 25 | } 26 | 27 | func (au *inMemoryAuthenticator) Users() []string { return au.usernames } 28 | 29 | func NewAuthenticator(users []AuthUser) Authenticator { 30 | if len(users) == 0 { 31 | return nil 32 | } 33 | 34 | au := &inMemoryAuthenticator{storage: &sync.Map{}} 35 | for _, user := range users { 36 | au.storage.Store(user.User, user.Pass) 37 | } 38 | usernames := make([]string, 0, len(users)) 39 | au.storage.Range(func(key, value any) bool { 40 | usernames = append(usernames, key.(string)) 41 | return true 42 | }) 43 | au.usernames = usernames 44 | 45 | return au 46 | } 47 | -------------------------------------------------------------------------------- /clash-meta/component/dhcp/conn.go: -------------------------------------------------------------------------------- 1 | package dhcp 2 | 3 | import ( 4 | "context" 5 | "net" 6 | "runtime" 7 | 8 | "github.com/Dreamacro/clash/component/dialer" 9 | ) 10 | 11 | func ListenDHCPClient(ctx context.Context, ifaceName string) (net.PacketConn, error) { 12 | listenAddr := "0.0.0.0:68" 13 | if runtime.GOOS == "linux" || runtime.GOOS == "android" { 14 | listenAddr = "255.255.255.255:68" 15 | } 16 | 17 | return dialer.ListenPacket(ctx, "udp4", listenAddr, dialer.WithInterface(ifaceName), dialer.WithAddrReuse(true)) 18 | } 19 | -------------------------------------------------------------------------------- /clash-meta/component/dialer/bind_darwin.go: -------------------------------------------------------------------------------- 1 | package dialer 2 | 3 | import ( 4 | "context" 5 | "net" 6 | "net/netip" 7 | "syscall" 8 | 9 | "github.com/Dreamacro/clash/component/iface" 10 | 11 | "golang.org/x/sys/unix" 12 | ) 13 | 14 | func bindControl(ifaceIdx int) controlFn { 15 | return func(ctx context.Context, network, address string, c syscall.RawConn) (err error) { 16 | addrPort, err := netip.ParseAddrPort(address) 17 | if err == nil && !addrPort.Addr().IsGlobalUnicast() { 18 | return 19 | } 20 | 21 | var innerErr error 22 | err = c.Control(func(fd uintptr) { 23 | switch network { 24 | case "tcp4", "udp4": 25 | innerErr = unix.SetsockoptInt(int(fd), unix.IPPROTO_IP, unix.IP_BOUND_IF, ifaceIdx) 26 | case "tcp6", "udp6": 27 | innerErr = unix.SetsockoptInt(int(fd), unix.IPPROTO_IPV6, unix.IPV6_BOUND_IF, ifaceIdx) 28 | } 29 | }) 30 | 31 | if innerErr != nil { 32 | err = innerErr 33 | } 34 | 35 | return 36 | } 37 | } 38 | 39 | func bindIfaceToDialer(ifaceName string, dialer *net.Dialer, _ string, _ netip.Addr) error { 40 | ifaceObj, err := iface.ResolveInterface(ifaceName) 41 | if err != nil { 42 | return err 43 | } 44 | 45 | addControlToDialer(dialer, bindControl(ifaceObj.Index)) 46 | return nil 47 | } 48 | 49 | func bindIfaceToListenConfig(ifaceName string, lc *net.ListenConfig, _, address string) (string, error) { 50 | ifaceObj, err := iface.ResolveInterface(ifaceName) 51 | if err != nil { 52 | return "", err 53 | } 54 | 55 | addControlToListenConfig(lc, bindControl(ifaceObj.Index)) 56 | return address, nil 57 | } 58 | 59 | func ParseNetwork(network string, addr netip.Addr) string { 60 | return network 61 | } 62 | -------------------------------------------------------------------------------- /clash-meta/component/dialer/bind_linux.go: -------------------------------------------------------------------------------- 1 | package dialer 2 | 3 | import ( 4 | "context" 5 | "net" 6 | "net/netip" 7 | "syscall" 8 | 9 | "golang.org/x/sys/unix" 10 | ) 11 | 12 | func bindControl(ifaceName string) controlFn { 13 | return func(ctx context.Context, network, address string, c syscall.RawConn) (err error) { 14 | addrPort, err := netip.ParseAddrPort(address) 15 | if err == nil && !addrPort.Addr().IsGlobalUnicast() { 16 | return 17 | } 18 | 19 | var innerErr error 20 | err = c.Control(func(fd uintptr) { 21 | innerErr = unix.BindToDevice(int(fd), ifaceName) 22 | }) 23 | 24 | if innerErr != nil { 25 | err = innerErr 26 | } 27 | 28 | return 29 | } 30 | } 31 | 32 | func bindIfaceToDialer(ifaceName string, dialer *net.Dialer, _ string, _ netip.Addr) error { 33 | addControlToDialer(dialer, bindControl(ifaceName)) 34 | 35 | return nil 36 | } 37 | 38 | func bindIfaceToListenConfig(ifaceName string, lc *net.ListenConfig, _, address string) (string, error) { 39 | addControlToListenConfig(lc, bindControl(ifaceName)) 40 | 41 | return address, nil 42 | } 43 | 44 | func ParseNetwork(network string, addr netip.Addr) string { 45 | return network 46 | } 47 | -------------------------------------------------------------------------------- /clash-meta/component/dialer/control.go: -------------------------------------------------------------------------------- 1 | package dialer 2 | 3 | import ( 4 | "context" 5 | "net" 6 | "syscall" 7 | ) 8 | 9 | type controlFn = func(ctx context.Context, network, address string, c syscall.RawConn) error 10 | 11 | func addControlToListenConfig(lc *net.ListenConfig, fn controlFn) { 12 | llc := *lc 13 | lc.Control = func(network, address string, c syscall.RawConn) (err error) { 14 | switch { 15 | case llc.Control != nil: 16 | if err = llc.Control(network, address, c); err != nil { 17 | return 18 | } 19 | } 20 | return fn(context.Background(), network, address, c) 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /clash-meta/component/dialer/control_go119.go: -------------------------------------------------------------------------------- 1 | //go:build !go1.20 2 | 3 | package dialer 4 | 5 | import ( 6 | "context" 7 | "net" 8 | "syscall" 9 | ) 10 | 11 | func addControlToDialer(d *net.Dialer, fn controlFn) { 12 | ld := *d 13 | d.Control = func(network, address string, c syscall.RawConn) (err error) { 14 | switch { 15 | case ld.Control != nil: 16 | if err = ld.Control(network, address, c); err != nil { 17 | return 18 | } 19 | } 20 | return fn(context.Background(), network, address, c) 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /clash-meta/component/dialer/control_go120.go: -------------------------------------------------------------------------------- 1 | //go:build go1.20 2 | 3 | package dialer 4 | 5 | import ( 6 | "context" 7 | "net" 8 | "syscall" 9 | ) 10 | 11 | func addControlToDialer(d *net.Dialer, fn controlFn) { 12 | ld := *d 13 | d.ControlContext = func(ctx context.Context, network, address string, c syscall.RawConn) (err error) { 14 | switch { 15 | case ld.ControlContext != nil: 16 | if err = ld.ControlContext(ctx, network, address, c); err != nil { 17 | return 18 | } 19 | case ld.Control != nil: 20 | if err = ld.Control(network, address, c); err != nil { 21 | return 22 | } 23 | } 24 | return fn(ctx, network, address, c) 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /clash-meta/component/dialer/error.go: -------------------------------------------------------------------------------- 1 | package dialer 2 | 3 | import ( 4 | "errors" 5 | 6 | E "github.com/sagernet/sing/common/exceptions" 7 | ) 8 | 9 | var ( 10 | ErrorNoIpAddress = errors.New("no ip address") 11 | ErrorInvalidedNetworkStack = errors.New("invalided network stack") 12 | ) 13 | 14 | func errorsJoin(errs ...error) error { 15 | // compatibility with golang<1.20 16 | // maybe use errors.Join(errs...) is better after we drop the old version's support 17 | return E.Errors(errs...) 18 | } 19 | -------------------------------------------------------------------------------- /clash-meta/component/dialer/mark_linux.go: -------------------------------------------------------------------------------- 1 | //go:build linux 2 | 3 | package dialer 4 | 5 | import ( 6 | "context" 7 | "net" 8 | "net/netip" 9 | "syscall" 10 | ) 11 | 12 | func bindMarkToDialer(mark int, dialer *net.Dialer, _ string, _ netip.Addr) { 13 | addControlToDialer(dialer, bindMarkToControl(mark)) 14 | } 15 | 16 | func bindMarkToListenConfig(mark int, lc *net.ListenConfig, _, _ string) { 17 | addControlToListenConfig(lc, bindMarkToControl(mark)) 18 | } 19 | 20 | func bindMarkToControl(mark int) controlFn { 21 | return func(ctx context.Context, network, address string, c syscall.RawConn) (err error) { 22 | 23 | addrPort, err := netip.ParseAddrPort(address) 24 | if err == nil && !addrPort.Addr().IsGlobalUnicast() { 25 | return 26 | } 27 | 28 | var innerErr error 29 | err = c.Control(func(fd uintptr) { 30 | innerErr = syscall.SetsockoptInt(int(fd), syscall.SOL_SOCKET, syscall.SO_MARK, mark) 31 | }) 32 | if innerErr != nil { 33 | err = innerErr 34 | } 35 | return 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /clash-meta/component/dialer/mark_nonlinux.go: -------------------------------------------------------------------------------- 1 | //go:build !linux 2 | 3 | package dialer 4 | 5 | import ( 6 | "net" 7 | "net/netip" 8 | "sync" 9 | 10 | "github.com/Dreamacro/clash/log" 11 | ) 12 | 13 | var printMarkWarnOnce sync.Once 14 | 15 | func printMarkWarn() { 16 | printMarkWarnOnce.Do(func() { 17 | log.Warnln("Routing mark on socket is not supported on current platform") 18 | }) 19 | } 20 | 21 | func bindMarkToDialer(mark int, dialer *net.Dialer, _ string, _ netip.Addr) { 22 | printMarkWarn() 23 | } 24 | 25 | func bindMarkToListenConfig(mark int, lc *net.ListenConfig, _, _ string) { 26 | printMarkWarn() 27 | } 28 | -------------------------------------------------------------------------------- /clash-meta/component/dialer/resolver.go: -------------------------------------------------------------------------------- 1 | package dialer 2 | 3 | import ( 4 | "context" 5 | "net" 6 | "net/netip" 7 | ) 8 | 9 | func init() { 10 | // We must use this DialContext to query DNS 11 | // when using net default resolver. 12 | net.DefaultResolver.PreferGo = true 13 | net.DefaultResolver.Dial = resolverDialContext 14 | } 15 | 16 | func resolverDialContext(ctx context.Context, network, address string) (net.Conn, error) { 17 | d := &net.Dialer{} 18 | 19 | interfaceName := DefaultInterface.Load() 20 | 21 | if interfaceName != "" { 22 | dstIP, err := netip.ParseAddr(address) 23 | if err == nil { 24 | _ = bindIfaceToDialer(interfaceName, d, network, dstIP) 25 | } 26 | } 27 | 28 | return d.DialContext(ctx, network, address) 29 | } 30 | -------------------------------------------------------------------------------- /clash-meta/component/dialer/reuse_others.go: -------------------------------------------------------------------------------- 1 | //go:build !darwin && !dragonfly && !freebsd && !linux && !netbsd && !openbsd && !solaris && !windows 2 | 3 | package dialer 4 | 5 | import ( 6 | "net" 7 | ) 8 | 9 | func addrReuseToListenConfig(*net.ListenConfig) {} 10 | -------------------------------------------------------------------------------- /clash-meta/component/dialer/reuse_unix.go: -------------------------------------------------------------------------------- 1 | //go:build darwin || dragonfly || freebsd || linux || netbsd || openbsd || solaris 2 | 3 | package dialer 4 | 5 | import ( 6 | "context" 7 | "net" 8 | "syscall" 9 | 10 | "golang.org/x/sys/unix" 11 | ) 12 | 13 | func addrReuseToListenConfig(lc *net.ListenConfig) { 14 | addControlToListenConfig(lc, func(ctx context.Context, network, address string, c syscall.RawConn) error { 15 | return c.Control(func(fd uintptr) { 16 | unix.SetsockoptInt(int(fd), unix.SOL_SOCKET, unix.SO_REUSEADDR, 1) 17 | unix.SetsockoptInt(int(fd), unix.SOL_SOCKET, unix.SO_REUSEPORT, 1) 18 | }) 19 | }) 20 | } 21 | -------------------------------------------------------------------------------- /clash-meta/component/dialer/reuse_windows.go: -------------------------------------------------------------------------------- 1 | package dialer 2 | 3 | import ( 4 | "context" 5 | "net" 6 | "syscall" 7 | 8 | "golang.org/x/sys/windows" 9 | ) 10 | 11 | func addrReuseToListenConfig(lc *net.ListenConfig) { 12 | addControlToListenConfig(lc, func(ctx context.Context, network, address string, c syscall.RawConn) error { 13 | return c.Control(func(fd uintptr) { 14 | windows.SetsockoptInt(windows.Handle(fd), windows.SOL_SOCKET, windows.SO_REUSEADDR, 1) 15 | }) 16 | }) 17 | } 18 | -------------------------------------------------------------------------------- /clash-meta/component/ebpf/byteorder/byteorder.go: -------------------------------------------------------------------------------- 1 | package byteorder 2 | 3 | import ( 4 | "net" 5 | ) 6 | 7 | // NetIPv4ToHost32 converts an net.IP to a uint32 in host byte order. ip 8 | // must be a IPv4 address, otherwise the function will panic. 9 | func NetIPv4ToHost32(ip net.IP) uint32 { 10 | ipv4 := ip.To4() 11 | _ = ipv4[3] // Assert length of ipv4. 12 | return Native.Uint32(ipv4) 13 | } 14 | -------------------------------------------------------------------------------- /clash-meta/component/ebpf/byteorder/byteorder_bigendian.go: -------------------------------------------------------------------------------- 1 | //go:build arm64be || armbe || mips || mips64 || mips64p32 || ppc64 || s390 || s390x || sparc || sparc64 2 | 3 | package byteorder 4 | 5 | import "encoding/binary" 6 | 7 | var Native binary.ByteOrder = binary.BigEndian 8 | 9 | func HostToNetwork16(u uint16) uint16 { return u } 10 | func HostToNetwork32(u uint32) uint32 { return u } 11 | func NetworkToHost16(u uint16) uint16 { return u } 12 | func NetworkToHost32(u uint32) uint32 { return u } 13 | -------------------------------------------------------------------------------- /clash-meta/component/ebpf/byteorder/byteorder_littleendian.go: -------------------------------------------------------------------------------- 1 | //go:build 386 || amd64 || amd64p32 || arm || arm64 || mips64le || mips64p32le || mipsle || ppc64le || riscv64 2 | 3 | package byteorder 4 | 5 | import ( 6 | "encoding/binary" 7 | "math/bits" 8 | ) 9 | 10 | var Native binary.ByteOrder = binary.LittleEndian 11 | 12 | func HostToNetwork16(u uint16) uint16 { return bits.ReverseBytes16(u) } 13 | func HostToNetwork32(u uint32) uint32 { return bits.ReverseBytes32(u) } 14 | func NetworkToHost16(u uint16) uint16 { return bits.ReverseBytes16(u) } 15 | func NetworkToHost32(u uint32) uint32 { return bits.ReverseBytes32(u) } 16 | -------------------------------------------------------------------------------- /clash-meta/component/ebpf/ebpf.go: -------------------------------------------------------------------------------- 1 | package ebpf 2 | 3 | import ( 4 | "net/netip" 5 | 6 | C "github.com/Dreamacro/clash/constant" 7 | "github.com/Dreamacro/clash/transport/socks5" 8 | ) 9 | 10 | type TcEBpfProgram struct { 11 | pros []C.EBpf 12 | rawNICs []string 13 | } 14 | 15 | func (t *TcEBpfProgram) RawNICs() []string { 16 | return t.rawNICs 17 | } 18 | 19 | func (t *TcEBpfProgram) Close() { 20 | for _, p := range t.pros { 21 | p.Close() 22 | } 23 | } 24 | 25 | func (t *TcEBpfProgram) Lookup(srcAddrPort netip.AddrPort) (addr socks5.Addr, err error) { 26 | for _, p := range t.pros { 27 | addr, err = p.Lookup(srcAddrPort) 28 | if err == nil { 29 | return 30 | } 31 | } 32 | return 33 | } 34 | -------------------------------------------------------------------------------- /clash-meta/component/ebpf/ebpf_others.go: -------------------------------------------------------------------------------- 1 | //go:build !linux || android 2 | 3 | package ebpf 4 | 5 | import ( 6 | "fmt" 7 | ) 8 | 9 | // NewTcEBpfProgram new ebpf tc program 10 | func NewTcEBpfProgram(_ []string, _ string) (*TcEBpfProgram, error) { 11 | return nil, fmt.Errorf("system not supported") 12 | } 13 | 14 | // NewRedirEBpfProgram new ebpf redirect program 15 | func NewRedirEBpfProgram(_ []string, _ uint16, _ string) (*TcEBpfProgram, error) { 16 | return nil, fmt.Errorf("system not supported") 17 | } 18 | 19 | func GetAutoDetectInterface() (string, error) { 20 | return "", fmt.Errorf("system not supported") 21 | } 22 | -------------------------------------------------------------------------------- /clash-meta/component/ebpf/redir/bpf_bpfeb.o: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MiSaturo/Go-Warp-Scanner/00ac93a0f58dfeb7c7dfdfdfe8b34eb94ce73473/clash-meta/component/ebpf/redir/bpf_bpfeb.o -------------------------------------------------------------------------------- /clash-meta/component/ebpf/redir/bpf_bpfel.o: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MiSaturo/Go-Warp-Scanner/00ac93a0f58dfeb7c7dfdfdfe8b34eb94ce73473/clash-meta/component/ebpf/redir/bpf_bpfel.o -------------------------------------------------------------------------------- /clash-meta/component/ebpf/tc/bpf_bpfeb.o: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MiSaturo/Go-Warp-Scanner/00ac93a0f58dfeb7c7dfdfdfe8b34eb94ce73473/clash-meta/component/ebpf/tc/bpf_bpfeb.o -------------------------------------------------------------------------------- /clash-meta/component/ebpf/tc/bpf_bpfel.o: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MiSaturo/Go-Warp-Scanner/00ac93a0f58dfeb7c7dfdfdfe8b34eb94ce73473/clash-meta/component/ebpf/tc/bpf_bpfel.o -------------------------------------------------------------------------------- /clash-meta/component/geodata/attr.go: -------------------------------------------------------------------------------- 1 | package geodata 2 | 3 | import ( 4 | "strings" 5 | 6 | "github.com/Dreamacro/clash/component/geodata/router" 7 | ) 8 | 9 | type AttributeList struct { 10 | matcher []AttributeMatcher 11 | } 12 | 13 | func (al *AttributeList) Match(domain *router.Domain) bool { 14 | for _, matcher := range al.matcher { 15 | if !matcher.Match(domain) { 16 | return false 17 | } 18 | } 19 | return true 20 | } 21 | 22 | func (al *AttributeList) IsEmpty() bool { 23 | return len(al.matcher) == 0 24 | } 25 | 26 | func parseAttrs(attrs []string) *AttributeList { 27 | al := new(AttributeList) 28 | for _, attr := range attrs { 29 | trimmedAttr := strings.ToLower(strings.TrimSpace(attr)) 30 | if len(trimmedAttr) == 0 { 31 | continue 32 | } 33 | al.matcher = append(al.matcher, BooleanMatcher(trimmedAttr)) 34 | } 35 | return al 36 | } 37 | 38 | type AttributeMatcher interface { 39 | Match(*router.Domain) bool 40 | } 41 | 42 | type BooleanMatcher string 43 | 44 | func (m BooleanMatcher) Match(domain *router.Domain) bool { 45 | for _, attr := range domain.Attribute { 46 | if strings.EqualFold(attr.GetKey(), string(m)) { 47 | return true 48 | } 49 | } 50 | return false 51 | } 52 | -------------------------------------------------------------------------------- /clash-meta/component/geodata/geodata.go: -------------------------------------------------------------------------------- 1 | package geodata 2 | 3 | import ( 4 | "fmt" 5 | 6 | "github.com/Dreamacro/clash/component/geodata/router" 7 | C "github.com/Dreamacro/clash/constant" 8 | ) 9 | 10 | type loader struct { 11 | LoaderImplementation 12 | } 13 | 14 | func (l *loader) LoadGeoSite(list string) ([]*router.Domain, error) { 15 | return l.LoadSiteByPath(C.GeositeName, list) 16 | } 17 | 18 | func (l *loader) LoadGeoIP(country string) ([]*router.CIDR, error) { 19 | return l.LoadIPByPath(C.GeoipName, country) 20 | } 21 | 22 | var loaders map[string]func() LoaderImplementation 23 | 24 | func RegisterGeoDataLoaderImplementationCreator(name string, loader func() LoaderImplementation) { 25 | if loaders == nil { 26 | loaders = map[string]func() LoaderImplementation{} 27 | } 28 | loaders[name] = loader 29 | } 30 | 31 | func getGeoDataLoaderImplementation(name string) (LoaderImplementation, error) { 32 | if geoLoader, ok := loaders[name]; ok { 33 | return geoLoader(), nil 34 | } 35 | return nil, fmt.Errorf("unable to locate GeoData loader %s", name) 36 | } 37 | 38 | func GetGeoDataLoader(name string) (Loader, error) { 39 | loadImpl, err := getGeoDataLoaderImplementation(name) 40 | if err == nil { 41 | return &loader{loadImpl}, nil 42 | } 43 | return nil, err 44 | } 45 | -------------------------------------------------------------------------------- /clash-meta/component/geodata/geodataproto.go: -------------------------------------------------------------------------------- 1 | package geodata 2 | 3 | import ( 4 | "github.com/Dreamacro/clash/component/geodata/router" 5 | ) 6 | 7 | type LoaderImplementation interface { 8 | LoadSiteByPath(filename, list string) ([]*router.Domain, error) 9 | LoadSiteByBytes(geositeBytes []byte, list string) ([]*router.Domain, error) 10 | LoadIPByPath(filename, country string) ([]*router.CIDR, error) 11 | LoadIPByBytes(geoipBytes []byte, country string) ([]*router.CIDR, error) 12 | } 13 | 14 | type Loader interface { 15 | LoaderImplementation 16 | LoadGeoSite(list string) ([]*router.Domain, error) 17 | LoadGeoIP(country string) ([]*router.CIDR, error) 18 | } 19 | -------------------------------------------------------------------------------- /clash-meta/component/geodata/package_info.go: -------------------------------------------------------------------------------- 1 | // Modified from: https://github.com/v2fly/v2ray-core/tree/master/infra/conf/geodata 2 | // License: MIT 3 | 4 | package geodata 5 | -------------------------------------------------------------------------------- /clash-meta/component/geodata/router/config.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | 3 | package clash.component.geodata.router; 4 | option csharp_namespace = "Clash.Component.Geodata.Router"; 5 | option go_package = "github.com/Dreamacro/clash/component/geodata/router"; 6 | option java_package = "com.clash.component.geodata.router"; 7 | option java_multiple_files = true; 8 | 9 | // Domain for routing decision. 10 | message Domain { 11 | // Type of domain value. 12 | enum Type { 13 | // The value is used as is. 14 | Plain = 0; 15 | // The value is used as a regular expression. 16 | Regex = 1; 17 | // The value is a root domain. 18 | Domain = 2; 19 | // The value is a domain. 20 | Full = 3; 21 | } 22 | 23 | // Domain matching type. 24 | Type type = 1; 25 | 26 | // Domain value. 27 | string value = 2; 28 | 29 | message Attribute { 30 | string key = 1; 31 | 32 | oneof typed_value { 33 | bool bool_value = 2; 34 | int64 int_value = 3; 35 | } 36 | } 37 | 38 | // Attributes of this domain. May be used for filtering. 39 | repeated Attribute attribute = 3; 40 | } 41 | 42 | // IP for routing decision, in CIDR form. 43 | message CIDR { 44 | // IP address, should be either 4 or 16 bytes. 45 | bytes ip = 1; 46 | 47 | // Number of leading ones in the network mask. 48 | uint32 prefix = 2; 49 | } 50 | 51 | message GeoIP { 52 | string country_code = 1; 53 | repeated CIDR cidr = 2; 54 | bool reverse_match = 3; 55 | } 56 | 57 | message GeoIPList { 58 | repeated GeoIP entry = 1; 59 | } 60 | 61 | message GeoSite { 62 | string country_code = 1; 63 | repeated Domain domain = 2; 64 | } 65 | 66 | message GeoSiteList { 67 | repeated GeoSite entry = 1; 68 | } 69 | -------------------------------------------------------------------------------- /clash-meta/component/geodata/strmatcher/full_matcher.go: -------------------------------------------------------------------------------- 1 | package strmatcher 2 | 3 | type FullMatcherGroup struct { 4 | matchers map[string][]uint32 5 | } 6 | 7 | func (g *FullMatcherGroup) Add(domain string, value uint32) { 8 | if g.matchers == nil { 9 | g.matchers = make(map[string][]uint32) 10 | } 11 | 12 | g.matchers[domain] = append(g.matchers[domain], value) 13 | } 14 | 15 | func (g *FullMatcherGroup) addMatcher(m fullMatcher, value uint32) { 16 | g.Add(string(m), value) 17 | } 18 | 19 | func (g *FullMatcherGroup) Match(str string) []uint32 { 20 | if g.matchers == nil { 21 | return nil 22 | } 23 | 24 | return g.matchers[str] 25 | } 26 | -------------------------------------------------------------------------------- /clash-meta/component/geodata/strmatcher/matchers.go: -------------------------------------------------------------------------------- 1 | package strmatcher 2 | 3 | import ( 4 | "regexp" 5 | "strings" 6 | ) 7 | 8 | type fullMatcher string 9 | 10 | func (m fullMatcher) Match(s string) bool { 11 | return string(m) == s 12 | } 13 | 14 | func (m fullMatcher) String() string { 15 | return "full:" + string(m) 16 | } 17 | 18 | type substrMatcher string 19 | 20 | func (m substrMatcher) Match(s string) bool { 21 | return strings.Contains(s, string(m)) 22 | } 23 | 24 | func (m substrMatcher) String() string { 25 | return "keyword:" + string(m) 26 | } 27 | 28 | type domainMatcher string 29 | 30 | func (m domainMatcher) Match(s string) bool { 31 | pattern := string(m) 32 | if !strings.HasSuffix(s, pattern) { 33 | return false 34 | } 35 | return len(s) == len(pattern) || s[len(s)-len(pattern)-1] == '.' 36 | } 37 | 38 | func (m domainMatcher) String() string { 39 | return "domain:" + string(m) 40 | } 41 | 42 | type regexMatcher struct { 43 | pattern *regexp.Regexp 44 | } 45 | 46 | func (m *regexMatcher) Match(s string) bool { 47 | return m.pattern.MatchString(s) 48 | } 49 | 50 | func (m *regexMatcher) String() string { 51 | return "regexp:" + m.pattern.String() 52 | } 53 | -------------------------------------------------------------------------------- /clash-meta/component/geodata/strmatcher/package_info.go: -------------------------------------------------------------------------------- 1 | // Modified from: https://github.com/v2fly/v2ray-core/tree/master/common/strmatcher 2 | // License: MIT 3 | 4 | package strmatcher 5 | -------------------------------------------------------------------------------- /clash-meta/component/mmdb/mmdb.go: -------------------------------------------------------------------------------- 1 | package mmdb 2 | 3 | import ( 4 | "context" 5 | "io" 6 | "net/http" 7 | "os" 8 | "sync" 9 | "time" 10 | 11 | clashHttp "github.com/Dreamacro/clash/component/http" 12 | C "github.com/Dreamacro/clash/constant" 13 | "github.com/Dreamacro/clash/log" 14 | 15 | "github.com/oschwald/geoip2-golang" 16 | ) 17 | 18 | var ( 19 | mmdb *geoip2.Reader 20 | once sync.Once 21 | ) 22 | 23 | func LoadFromBytes(buffer []byte) { 24 | once.Do(func() { 25 | var err error 26 | mmdb, err = geoip2.FromBytes(buffer) 27 | if err != nil { 28 | log.Fatalln("Can't load mmdb: %s", err.Error()) 29 | } 30 | }) 31 | } 32 | 33 | func Verify() bool { 34 | instance, err := geoip2.Open(C.Path.MMDB()) 35 | if err == nil { 36 | instance.Close() 37 | } 38 | return err == nil 39 | } 40 | 41 | func Instance() *geoip2.Reader { 42 | once.Do(func() { 43 | var err error 44 | mmdb, err = geoip2.Open(C.Path.MMDB()) 45 | if err != nil { 46 | log.Fatalln("Can't load mmdb: %s", err.Error()) 47 | } 48 | }) 49 | 50 | return mmdb 51 | } 52 | 53 | func DownloadMMDB(path string) (err error) { 54 | ctx, cancel := context.WithTimeout(context.Background(), time.Second*90) 55 | defer cancel() 56 | resp, err := clashHttp.HttpRequest(ctx, C.MmdbUrl, http.MethodGet, http.Header{"User-Agent": {"clash"}}, nil) 57 | if err != nil { 58 | return 59 | } 60 | defer resp.Body.Close() 61 | 62 | f, err := os.OpenFile(path, os.O_CREATE|os.O_WRONLY, 0o644) 63 | if err != nil { 64 | return err 65 | } 66 | defer f.Close() 67 | _, err = io.Copy(f, resp.Body) 68 | 69 | return err 70 | } 71 | -------------------------------------------------------------------------------- /clash-meta/component/pool/pool_test.go: -------------------------------------------------------------------------------- 1 | package pool 2 | 3 | import ( 4 | "context" 5 | "testing" 6 | "time" 7 | 8 | "github.com/stretchr/testify/assert" 9 | ) 10 | 11 | func lg() Factory[int] { 12 | initial := -1 13 | return func(context.Context) (int, error) { 14 | initial++ 15 | return initial, nil 16 | } 17 | } 18 | 19 | func TestPool_Basic(t *testing.T) { 20 | g := lg() 21 | pool := New[int](g) 22 | 23 | elm, _ := pool.Get() 24 | assert.Equal(t, 0, elm) 25 | pool.Put(elm) 26 | elm, _ = pool.Get() 27 | assert.Equal(t, 0, elm) 28 | elm, _ = pool.Get() 29 | assert.Equal(t, 1, elm) 30 | } 31 | 32 | func TestPool_MaxSize(t *testing.T) { 33 | g := lg() 34 | size := 5 35 | pool := New[int](g, WithSize[int](size)) 36 | 37 | var items []int 38 | 39 | for i := 0; i < size; i++ { 40 | item, _ := pool.Get() 41 | items = append(items, item) 42 | } 43 | 44 | extra, _ := pool.Get() 45 | assert.Equal(t, size, extra) 46 | 47 | for _, item := range items { 48 | pool.Put(item) 49 | } 50 | 51 | pool.Put(extra) 52 | 53 | for _, item := range items { 54 | elm, _ := pool.Get() 55 | assert.Equal(t, item, elm) 56 | } 57 | } 58 | 59 | func TestPool_MaxAge(t *testing.T) { 60 | g := lg() 61 | pool := New[int](g, WithAge[int](20)) 62 | 63 | elm, _ := pool.Get() 64 | pool.Put(elm) 65 | 66 | elm, _ = pool.Get() 67 | assert.Equal(t, 0, elm) 68 | pool.Put(elm) 69 | 70 | time.Sleep(time.Millisecond * 22) 71 | elm, _ = pool.Get() 72 | assert.Equal(t, 1, elm) 73 | } 74 | -------------------------------------------------------------------------------- /clash-meta/component/process/find_process_mode.go: -------------------------------------------------------------------------------- 1 | package process 2 | 3 | import ( 4 | "encoding/json" 5 | "errors" 6 | "strings" 7 | ) 8 | 9 | const ( 10 | FindProcessAlways = "always" 11 | FindProcessStrict = "strict" 12 | FindProcessOff = "off" 13 | ) 14 | 15 | var ( 16 | validModes = map[string]struct{}{ 17 | FindProcessAlways: {}, 18 | FindProcessOff: {}, 19 | FindProcessStrict: {}, 20 | } 21 | ) 22 | 23 | type FindProcessMode string 24 | 25 | func (m FindProcessMode) Always() bool { 26 | return m == FindProcessAlways 27 | } 28 | 29 | func (m FindProcessMode) Off() bool { 30 | return m == FindProcessOff 31 | } 32 | 33 | func (m *FindProcessMode) UnmarshalYAML(unmarshal func(any) error) error { 34 | var tp string 35 | if err := unmarshal(&tp); err != nil { 36 | return err 37 | } 38 | return m.Set(tp) 39 | } 40 | 41 | func (m *FindProcessMode) UnmarshalJSON(data []byte) error { 42 | var tp string 43 | if err := json.Unmarshal(data, &tp); err != nil { 44 | return err 45 | } 46 | return m.Set(tp) 47 | } 48 | 49 | func (m *FindProcessMode) Set(value string) error { 50 | mode := strings.ToLower(value) 51 | _, exist := validModes[mode] 52 | if !exist { 53 | return errors.New("invalid find process mode") 54 | } 55 | *m = FindProcessMode(mode) 56 | return nil 57 | } 58 | -------------------------------------------------------------------------------- /clash-meta/component/process/process.go: -------------------------------------------------------------------------------- 1 | package process 2 | 3 | import ( 4 | "errors" 5 | "net/netip" 6 | ) 7 | 8 | var ( 9 | ErrInvalidNetwork = errors.New("invalid network") 10 | ErrPlatformNotSupport = errors.New("not support on this platform") 11 | ErrNotFound = errors.New("process not found") 12 | ) 13 | 14 | const ( 15 | TCP = "tcp" 16 | UDP = "udp" 17 | ) 18 | 19 | func FindProcessName(network string, srcIP netip.Addr, srcPort int) (uint32, string, error) { 20 | return findProcessName(network, srcIP, srcPort) 21 | } 22 | -------------------------------------------------------------------------------- /clash-meta/component/process/process_other.go: -------------------------------------------------------------------------------- 1 | //go:build !darwin && !linux && !windows && (!freebsd || !amd64) 2 | 3 | package process 4 | 5 | import "net/netip" 6 | 7 | func findProcessName(network string, ip netip.Addr, srcPort int) (uint32, string, error) { 8 | return 0, "", ErrPlatformNotSupport 9 | } 10 | 11 | func resolveSocketByNetlink(network string, ip netip.Addr, srcPort int) (uint32, uint32, error) { 12 | return 0, 0, ErrPlatformNotSupport 13 | } 14 | -------------------------------------------------------------------------------- /clash-meta/component/profile/profile.go: -------------------------------------------------------------------------------- 1 | package profile 2 | 3 | import ( 4 | "github.com/Dreamacro/clash/common/atomic" 5 | ) 6 | 7 | // StoreSelected is a global switch for storing selected proxy to cache 8 | var StoreSelected = atomic.NewBool(true) 9 | -------------------------------------------------------------------------------- /clash-meta/component/resolver/defaults.go: -------------------------------------------------------------------------------- 1 | //go:build aix || darwin || dragonfly || freebsd || linux || netbsd || openbsd || solaris 2 | 3 | package resolver 4 | 5 | import _ "unsafe" 6 | 7 | //go:linkname defaultNS net.defaultNS 8 | var defaultNS []string 9 | 10 | func init() { 11 | defaultNS = []string{"114.114.114.114:53", "8.8.8.8:53"} 12 | } 13 | -------------------------------------------------------------------------------- /clash-meta/component/resolver/local.go: -------------------------------------------------------------------------------- 1 | package resolver 2 | 3 | import ( 4 | "context" 5 | 6 | D "github.com/miekg/dns" 7 | ) 8 | 9 | var DefaultLocalServer LocalServer 10 | 11 | type LocalServer interface { 12 | ServeMsg(ctx context.Context, msg *D.Msg) (*D.Msg, error) 13 | } 14 | 15 | // ServeMsg with a dns.Msg, return resolve dns.Msg 16 | func ServeMsg(ctx context.Context, msg *D.Msg) (*D.Msg, error) { 17 | if server := DefaultLocalServer; server != nil { 18 | return server.ServeMsg(ctx, msg) 19 | } 20 | 21 | return nil, ErrIPNotFound 22 | } 23 | -------------------------------------------------------------------------------- /clash-meta/component/resource/vehicle.go: -------------------------------------------------------------------------------- 1 | package resource 2 | 3 | import ( 4 | "context" 5 | clashHttp "github.com/Dreamacro/clash/component/http" 6 | types "github.com/Dreamacro/clash/constant/provider" 7 | "io" 8 | "net/http" 9 | "os" 10 | "time" 11 | ) 12 | 13 | type FileVehicle struct { 14 | path string 15 | } 16 | 17 | func (f *FileVehicle) Type() types.VehicleType { 18 | return types.File 19 | } 20 | 21 | func (f *FileVehicle) Path() string { 22 | return f.path 23 | } 24 | 25 | func (f *FileVehicle) Read() ([]byte, error) { 26 | return os.ReadFile(f.path) 27 | } 28 | 29 | func NewFileVehicle(path string) *FileVehicle { 30 | return &FileVehicle{path: path} 31 | } 32 | 33 | type HTTPVehicle struct { 34 | url string 35 | path string 36 | } 37 | 38 | func (h *HTTPVehicle) Url() string { 39 | return h.url 40 | } 41 | 42 | func (h *HTTPVehicle) Type() types.VehicleType { 43 | return types.HTTP 44 | } 45 | 46 | func (h *HTTPVehicle) Path() string { 47 | return h.path 48 | } 49 | 50 | func (h *HTTPVehicle) Read() ([]byte, error) { 51 | ctx, cancel := context.WithTimeout(context.Background(), time.Second*20) 52 | defer cancel() 53 | resp, err := clashHttp.HttpRequest(ctx, h.url, http.MethodGet, nil, nil) 54 | if err != nil { 55 | return nil, err 56 | } 57 | 58 | defer resp.Body.Close() 59 | buf, err := io.ReadAll(resp.Body) 60 | if err != nil { 61 | return nil, err 62 | } 63 | return buf, nil 64 | } 65 | 66 | func NewHTTPVehicle(url string, path string) *HTTPVehicle { 67 | return &HTTPVehicle{url, path} 68 | } 69 | -------------------------------------------------------------------------------- /clash-meta/component/sniffer/base_sniffer.go: -------------------------------------------------------------------------------- 1 | package sniffer 2 | 3 | import ( 4 | "errors" 5 | 6 | "github.com/Dreamacro/clash/common/utils" 7 | "github.com/Dreamacro/clash/constant" 8 | "github.com/Dreamacro/clash/constant/sniffer" 9 | ) 10 | 11 | type SnifferConfig struct { 12 | OverrideDest bool 13 | Ports []utils.Range[uint16] 14 | } 15 | 16 | type BaseSniffer struct { 17 | ports []utils.Range[uint16] 18 | supportNetworkType constant.NetWork 19 | } 20 | 21 | // Protocol implements sniffer.Sniffer 22 | func (*BaseSniffer) Protocol() string { 23 | return "unknown" 24 | } 25 | 26 | // SniffTCP implements sniffer.Sniffer 27 | func (*BaseSniffer) SniffTCP(bytes []byte) (string, error) { 28 | return "", errors.New("TODO") 29 | } 30 | 31 | // SupportNetwork implements sniffer.Sniffer 32 | func (bs *BaseSniffer) SupportNetwork() constant.NetWork { 33 | return bs.supportNetworkType 34 | } 35 | 36 | // SupportPort implements sniffer.Sniffer 37 | func (bs *BaseSniffer) SupportPort(port uint16) bool { 38 | for _, portRange := range bs.ports { 39 | if portRange.Contains(port) { 40 | return true 41 | } 42 | } 43 | return false 44 | } 45 | 46 | func NewBaseSniffer(ports []utils.Range[uint16], networkType constant.NetWork) *BaseSniffer { 47 | return &BaseSniffer{ 48 | ports: ports, 49 | supportNetworkType: networkType, 50 | } 51 | } 52 | 53 | var _ sniffer.Sniffer = (*BaseSniffer)(nil) 54 | -------------------------------------------------------------------------------- /clash-meta/component/sniffer/quic_sniffer.go: -------------------------------------------------------------------------------- 1 | package sniffer 2 | 3 | //TODO 4 | -------------------------------------------------------------------------------- /clash-meta/component/trie/ipcidr_node.go: -------------------------------------------------------------------------------- 1 | package trie 2 | 3 | import "errors" 4 | 5 | var ( 6 | ErrorOverMaxValue = errors.New("the value don't over max value") 7 | ) 8 | 9 | type IpCidrNode struct { 10 | Mark bool 11 | child map[uint32]*IpCidrNode 12 | maxValue uint32 13 | } 14 | 15 | func NewIpCidrNode(mark bool, maxValue uint32) *IpCidrNode { 16 | ipCidrNode := &IpCidrNode{ 17 | Mark: mark, 18 | child: map[uint32]*IpCidrNode{}, 19 | maxValue: maxValue, 20 | } 21 | 22 | return ipCidrNode 23 | } 24 | 25 | func (n *IpCidrNode) addChild(value uint32) error { 26 | if value > n.maxValue { 27 | return ErrorOverMaxValue 28 | } 29 | 30 | n.child[value] = NewIpCidrNode(false, n.maxValue) 31 | return nil 32 | } 33 | 34 | func (n *IpCidrNode) hasChild(value uint32) bool { 35 | return n.getChild(value) != nil 36 | } 37 | 38 | func (n *IpCidrNode) getChild(value uint32) *IpCidrNode { 39 | if value <= n.maxValue { 40 | return n.child[value] 41 | } 42 | 43 | return nil 44 | } 45 | -------------------------------------------------------------------------------- /clash-meta/config/initial.go: -------------------------------------------------------------------------------- 1 | package config 2 | 3 | import ( 4 | "fmt" 5 | "github.com/Dreamacro/clash/component/geodata" 6 | "os" 7 | 8 | C "github.com/Dreamacro/clash/constant" 9 | "github.com/Dreamacro/clash/log" 10 | ) 11 | 12 | // Init prepare necessary files 13 | func Init(dir string) error { 14 | // initial homedir 15 | if _, err := os.Stat(dir); os.IsNotExist(err) { 16 | if err := os.MkdirAll(dir, 0o777); err != nil { 17 | return fmt.Errorf("can't create config directory %s: %s", dir, err.Error()) 18 | } 19 | } 20 | 21 | // initial config.yaml 22 | if _, err := os.Stat(C.Path.Config()); os.IsNotExist(err) { 23 | log.Infoln("Can't find config, create a initial config file") 24 | f, err := os.OpenFile(C.Path.Config(), os.O_CREATE|os.O_WRONLY, 0o644) 25 | if err != nil { 26 | return fmt.Errorf("can't create file %s: %s", C.Path.Config(), err.Error()) 27 | } 28 | f.Write([]byte(`mixed-port: 7890`)) 29 | f.Close() 30 | } 31 | buf, _ := os.ReadFile(C.Path.Config()) 32 | rawCfg, err := UnmarshalRawConfig(buf) 33 | if err != nil { 34 | log.Errorln(err.Error()) 35 | fmt.Printf("configuration file %s test failed\n", C.Path.Config()) 36 | os.Exit(1) 37 | } 38 | if !C.GeodataMode { 39 | C.GeodataMode = rawCfg.GeodataMode 40 | } 41 | C.GeoIpUrl = rawCfg.GeoXUrl.GeoIp 42 | C.GeoSiteUrl = rawCfg.GeoXUrl.GeoSite 43 | C.MmdbUrl = rawCfg.GeoXUrl.Mmdb 44 | // initial GeoIP 45 | if err := geodata.InitGeoIP(); err != nil { 46 | return fmt.Errorf("can't initial GeoIP: %w", err) 47 | } 48 | 49 | return nil 50 | } 51 | -------------------------------------------------------------------------------- /clash-meta/constant/context.go: -------------------------------------------------------------------------------- 1 | package constant 2 | 3 | import ( 4 | "net" 5 | 6 | N "github.com/Dreamacro/clash/common/net" 7 | 8 | "github.com/gofrs/uuid/v5" 9 | ) 10 | 11 | type PlainContext interface { 12 | ID() uuid.UUID 13 | } 14 | 15 | type ConnContext interface { 16 | PlainContext 17 | Metadata() *Metadata 18 | Conn() *N.BufferedConn 19 | } 20 | 21 | type PacketConnContext interface { 22 | PlainContext 23 | Metadata() *Metadata 24 | PacketConn() net.PacketConn 25 | } 26 | -------------------------------------------------------------------------------- /clash-meta/constant/ebpf.go: -------------------------------------------------------------------------------- 1 | package constant 2 | 3 | import ( 4 | "net/netip" 5 | 6 | "github.com/Dreamacro/clash/transport/socks5" 7 | ) 8 | 9 | const ( 10 | BpfFSPath = "/sys/fs/bpf/clash" 11 | 12 | TcpAutoRedirPort = 't'<<8 | 'r'<<0 13 | ClashTrafficMark = 'c'<<24 | 'l'<<16 | 't'<<8 | 'm'<<0 14 | ) 15 | 16 | type EBpf interface { 17 | Start() error 18 | Close() 19 | Lookup(srcAddrPort netip.AddrPort) (socks5.Addr, error) 20 | } 21 | -------------------------------------------------------------------------------- /clash-meta/constant/features/low_memory.go: -------------------------------------------------------------------------------- 1 | //go:build with_low_memory 2 | package features 3 | 4 | func init() { 5 | TAGS = append(TAGS, "with_low_memory") 6 | } 7 | -------------------------------------------------------------------------------- /clash-meta/constant/features/no_fake_tcp.go: -------------------------------------------------------------------------------- 1 | //go:build no_fake_tcp 2 | 3 | package features 4 | 5 | func init() { 6 | TAGS = append(TAGS, "no_fake_tcp") 7 | } 8 | -------------------------------------------------------------------------------- /clash-meta/constant/features/tags.go: -------------------------------------------------------------------------------- 1 | package features 2 | 3 | var TAGS = make([]string, 0, 0) 4 | -------------------------------------------------------------------------------- /clash-meta/constant/features/with_gvisor.go: -------------------------------------------------------------------------------- 1 | //go:build with_gvisor 2 | 3 | package features 4 | 5 | func init() { 6 | TAGS = append(TAGS, "with_gvisor") 7 | } 8 | -------------------------------------------------------------------------------- /clash-meta/constant/geodata.go: -------------------------------------------------------------------------------- 1 | package constant 2 | 3 | var ( 4 | GeodataMode bool 5 | GeoIpUrl string 6 | MmdbUrl string 7 | GeoSiteUrl string 8 | ) 9 | -------------------------------------------------------------------------------- /clash-meta/constant/listener.go: -------------------------------------------------------------------------------- 1 | package constant 2 | 3 | import "net" 4 | 5 | type Listener interface { 6 | RawAddress() string 7 | Address() string 8 | Close() error 9 | } 10 | 11 | type MultiAddrListener interface { 12 | Close() error 13 | Config() string 14 | AddrList() (addrList []net.Addr) 15 | } 16 | 17 | type InboundListener interface { 18 | Name() string 19 | Listen(tcpIn chan<- ConnContext, udpIn chan<- PacketAdapter, natTable NatTable) error 20 | Close() error 21 | Address() string 22 | RawAddress() string 23 | Config() InboundConfig 24 | } 25 | 26 | type InboundConfig interface { 27 | Name() string 28 | Equal(config InboundConfig) bool 29 | } 30 | -------------------------------------------------------------------------------- /clash-meta/constant/rule_extra.go: -------------------------------------------------------------------------------- 1 | package constant 2 | 3 | import ( 4 | "github.com/Dreamacro/clash/component/geodata/router" 5 | ) 6 | 7 | type RuleGeoSite interface { 8 | GetDomainMatcher() *router.DomainMatcher 9 | } 10 | 11 | type RuleGeoIP interface { 12 | GetIPMatcher() *router.GeoIPMatcher 13 | } 14 | 15 | type RuleGroup interface { 16 | GetRecodeSize() int 17 | } 18 | -------------------------------------------------------------------------------- /clash-meta/constant/sniffer/sniffer.go: -------------------------------------------------------------------------------- 1 | package sniffer 2 | 3 | import "github.com/Dreamacro/clash/constant" 4 | 5 | type Sniffer interface { 6 | SupportNetwork() constant.NetWork 7 | SniffTCP(bytes []byte) (string, error) 8 | Protocol() string 9 | SupportPort(port uint16) bool 10 | } 11 | 12 | const ( 13 | TLS Type = iota 14 | HTTP 15 | ) 16 | 17 | var ( 18 | List = []Type{TLS, HTTP} 19 | ) 20 | 21 | type Type int 22 | 23 | func (rt Type) String() string { 24 | switch rt { 25 | case TLS: 26 | return "TLS" 27 | case HTTP: 28 | return "HTTP" 29 | default: 30 | return "Unknown" 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /clash-meta/constant/tun.go: -------------------------------------------------------------------------------- 1 | package constant 2 | 3 | import ( 4 | "encoding/json" 5 | "errors" 6 | "strings" 7 | ) 8 | 9 | var StackTypeMapping = map[string]TUNStack{ 10 | strings.ToLower(TunGvisor.String()): TunGvisor, 11 | strings.ToLower(TunSystem.String()): TunSystem, 12 | strings.ToLower(TunLWIP.String()): TunLWIP, 13 | } 14 | 15 | const ( 16 | TunGvisor TUNStack = iota 17 | TunSystem 18 | TunLWIP 19 | ) 20 | 21 | type TUNStack int 22 | 23 | // UnmarshalYAML unserialize TUNStack with yaml 24 | func (e *TUNStack) UnmarshalYAML(unmarshal func(any) error) error { 25 | var tp string 26 | if err := unmarshal(&tp); err != nil { 27 | return err 28 | } 29 | mode, exist := StackTypeMapping[strings.ToLower(tp)] 30 | if !exist { 31 | return errors.New("invalid tun stack") 32 | } 33 | *e = mode 34 | return nil 35 | } 36 | 37 | // MarshalYAML serialize TUNStack with yaml 38 | func (e TUNStack) MarshalYAML() (any, error) { 39 | return e.String(), nil 40 | } 41 | 42 | // UnmarshalJSON unserialize TUNStack with json 43 | func (e *TUNStack) UnmarshalJSON(data []byte) error { 44 | var tp string 45 | json.Unmarshal(data, &tp) 46 | mode, exist := StackTypeMapping[strings.ToLower(tp)] 47 | if !exist { 48 | return errors.New("invalid tun stack") 49 | } 50 | *e = mode 51 | return nil 52 | } 53 | 54 | // MarshalJSON serialize TUNStack with json 55 | func (e TUNStack) MarshalJSON() ([]byte, error) { 56 | return json.Marshal(e.String()) 57 | } 58 | 59 | func (e TUNStack) String() string { 60 | switch e { 61 | case TunGvisor: 62 | return "gVisor" 63 | case TunSystem: 64 | return "System" 65 | case TunLWIP: 66 | return "LWIP" 67 | default: 68 | return "unknown" 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /clash-meta/constant/version.go: -------------------------------------------------------------------------------- 1 | package constant 2 | 3 | var ( 4 | Meta = true 5 | Version = "1.10.0" 6 | BuildTime = "unknown time" 7 | ClashName = "clash.meta" 8 | ) 9 | -------------------------------------------------------------------------------- /clash-meta/context/conn.go: -------------------------------------------------------------------------------- 1 | package context 2 | 3 | import ( 4 | "github.com/Dreamacro/clash/common/utils" 5 | "net" 6 | 7 | N "github.com/Dreamacro/clash/common/net" 8 | C "github.com/Dreamacro/clash/constant" 9 | 10 | "github.com/gofrs/uuid/v5" 11 | ) 12 | 13 | type ConnContext struct { 14 | id uuid.UUID 15 | metadata *C.Metadata 16 | conn *N.BufferedConn 17 | } 18 | 19 | func NewConnContext(conn net.Conn, metadata *C.Metadata) *ConnContext { 20 | return &ConnContext{ 21 | id: utils.NewUUIDV4(), 22 | metadata: metadata, 23 | conn: N.NewBufferedConn(conn), 24 | } 25 | } 26 | 27 | // ID implement C.ConnContext ID 28 | func (c *ConnContext) ID() uuid.UUID { 29 | return c.id 30 | } 31 | 32 | // Metadata implement C.ConnContext Metadata 33 | func (c *ConnContext) Metadata() *C.Metadata { 34 | return c.metadata 35 | } 36 | 37 | // Conn implement C.ConnContext Conn 38 | func (c *ConnContext) Conn() *N.BufferedConn { 39 | return c.conn 40 | } 41 | -------------------------------------------------------------------------------- /clash-meta/context/dns.go: -------------------------------------------------------------------------------- 1 | package context 2 | 3 | import ( 4 | "context" 5 | "github.com/Dreamacro/clash/common/utils" 6 | 7 | "github.com/gofrs/uuid/v5" 8 | "github.com/miekg/dns" 9 | ) 10 | 11 | const ( 12 | DNSTypeHost = "host" 13 | DNSTypeFakeIP = "fakeip" 14 | DNSTypeRaw = "raw" 15 | ) 16 | 17 | type DNSContext struct { 18 | context.Context 19 | 20 | id uuid.UUID 21 | msg *dns.Msg 22 | tp string 23 | } 24 | 25 | func NewDNSContext(ctx context.Context, msg *dns.Msg) *DNSContext { 26 | return &DNSContext{ 27 | Context: ctx, 28 | 29 | id: utils.NewUUIDV4(), 30 | msg: msg, 31 | } 32 | } 33 | 34 | // ID implement C.PlainContext ID 35 | func (c *DNSContext) ID() uuid.UUID { 36 | return c.id 37 | } 38 | 39 | // SetType set type of response 40 | func (c *DNSContext) SetType(tp string) { 41 | c.tp = tp 42 | } 43 | 44 | // Type return type of response 45 | func (c *DNSContext) Type() string { 46 | return c.tp 47 | } 48 | -------------------------------------------------------------------------------- /clash-meta/context/packetconn.go: -------------------------------------------------------------------------------- 1 | package context 2 | 3 | import ( 4 | "net" 5 | 6 | "github.com/Dreamacro/clash/common/utils" 7 | C "github.com/Dreamacro/clash/constant" 8 | 9 | "github.com/gofrs/uuid/v5" 10 | ) 11 | 12 | type PacketConnContext struct { 13 | id uuid.UUID 14 | metadata *C.Metadata 15 | packetConn net.PacketConn 16 | } 17 | 18 | func NewPacketConnContext(metadata *C.Metadata) *PacketConnContext { 19 | return &PacketConnContext{ 20 | id: utils.NewUUIDV4(), 21 | metadata: metadata, 22 | } 23 | } 24 | 25 | // ID implement C.PacketConnContext ID 26 | func (pc *PacketConnContext) ID() uuid.UUID { 27 | return pc.id 28 | } 29 | 30 | // Metadata implement C.PacketConnContext Metadata 31 | func (pc *PacketConnContext) Metadata() *C.Metadata { 32 | return pc.metadata 33 | } 34 | 35 | // PacketConn implement C.PacketConnContext PacketConn 36 | func (pc *PacketConnContext) PacketConn() net.PacketConn { 37 | return pc.packetConn 38 | } 39 | 40 | // InjectPacketConn injectPacketConn manually 41 | func (pc *PacketConnContext) InjectPacketConn(pconn C.PacketConn) { 42 | pc.packetConn = pconn 43 | } 44 | -------------------------------------------------------------------------------- /clash-meta/dns/patch.go: -------------------------------------------------------------------------------- 1 | package dns 2 | 3 | import ( 4 | "context" 5 | 6 | D "github.com/miekg/dns" 7 | ) 8 | 9 | type LocalServer struct { 10 | handler handler 11 | } 12 | 13 | // ServeMsg implement resolver.LocalServer ResolveMsg 14 | func (s *LocalServer) ServeMsg(ctx context.Context, msg *D.Msg) (*D.Msg, error) { 15 | return handlerWithContext(ctx, s.handler, msg) 16 | } 17 | 18 | func NewLocalServer(resolver *Resolver, mapper *ResolverEnhancer) *LocalServer { 19 | return &LocalServer{handler: NewHandler(resolver, mapper)} 20 | } 21 | -------------------------------------------------------------------------------- /clash-meta/dns/policy.go: -------------------------------------------------------------------------------- 1 | package dns 2 | 3 | type Policy struct { 4 | data []dnsClient 5 | } 6 | 7 | func (p *Policy) GetData() []dnsClient { 8 | return p.data 9 | } 10 | 11 | func (p *Policy) Compare(p2 *Policy) int { 12 | if p2 == nil { 13 | return 1 14 | } 15 | l1 := len(p.data) 16 | l2 := len(p2.data) 17 | if l1 == l2 { 18 | return 0 19 | } 20 | if l1 > l2 { 21 | return 1 22 | } 23 | return -1 24 | } 25 | 26 | func NewPolicy(data []dnsClient) *Policy { 27 | return &Policy{ 28 | data: data, 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /clash-meta/dns/system.go: -------------------------------------------------------------------------------- 1 | package dns 2 | 3 | import ( 4 | "net" 5 | ) 6 | 7 | func loadSystemResolver() (clients []dnsClient, err error) { 8 | nameservers, err := dnsReadConfig() 9 | if err != nil { 10 | return 11 | } 12 | if len(nameservers) == 0 { 13 | return 14 | } 15 | servers := make([]NameServer, 0, len(nameservers)) 16 | for _, addr := range nameservers { 17 | servers = append(servers, NameServer{ 18 | Addr: net.JoinHostPort(addr, "53"), 19 | Net: "udp", 20 | }) 21 | } 22 | return transform(servers, nil), nil 23 | } 24 | -------------------------------------------------------------------------------- /clash-meta/dns/system_posix.go: -------------------------------------------------------------------------------- 1 | //go:build !windows 2 | 3 | package dns 4 | 5 | import ( 6 | "fmt" 7 | "os" 8 | "regexp" 9 | ) 10 | 11 | var ( 12 | // nameserver xxx.xxx.xxx.xxx 13 | nameserverPattern = regexp.MustCompile(`nameserver\s+(?P\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})`) 14 | ) 15 | 16 | func dnsReadConfig() (servers []string, err error) { 17 | content, err := os.ReadFile("/etc/resolv.conf") 18 | if err != nil { 19 | err = fmt.Errorf("failed to read /etc/resolv.conf: %w", err) 20 | return 21 | } 22 | for _, line := range nameserverPattern.FindAllStringSubmatch(string(content), -1) { 23 | addr := line[1] 24 | servers = append(servers, addr) 25 | } 26 | return 27 | } 28 | -------------------------------------------------------------------------------- /clash-meta/docker/file-name.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | os="clash.meta-linux-" 3 | case $TARGETPLATFORM in 4 | "linux/amd64") 5 | arch="amd64-compatible" 6 | ;; 7 | "linux/386") 8 | arch="386" 9 | ;; 10 | "linux/arm64") 11 | arch="arm64" 12 | ;; 13 | "linux/arm/v7") 14 | arch="armv7" 15 | ;; 16 | "riscv64") 17 | arch="riscv64" 18 | ;; 19 | *) 20 | echo "Unknown architecture" 21 | exit 1 22 | ;; 23 | esac 24 | file_name="$os$arch-$(cat bin/version.txt)" 25 | echo $file_name -------------------------------------------------------------------------------- /clash-meta/docs/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MiSaturo/Go-Warp-Scanner/00ac93a0f58dfeb7c7dfdfdfe8b34eb94ce73473/clash-meta/docs/logo.png -------------------------------------------------------------------------------- /clash-meta/flake.lock: -------------------------------------------------------------------------------- 1 | { 2 | "nodes": { 3 | "nixpkgs": { 4 | "locked": { 5 | "lastModified": 1671072901, 6 | "narHash": "sha256-eyFdLtfxYyZnbJorRiZ2kP2kW4gEU76hLzpZGW9mcZg=", 7 | "owner": "NixOS", 8 | "repo": "nixpkgs", 9 | "rev": "69ce4fbad877f91d4b9bc4cfedfb0ff1fe5043d5", 10 | "type": "github" 11 | }, 12 | "original": { 13 | "owner": "NixOS", 14 | "ref": "master", 15 | "repo": "nixpkgs", 16 | "type": "github" 17 | } 18 | }, 19 | "root": { 20 | "inputs": { 21 | "nixpkgs": "nixpkgs", 22 | "utils": "utils" 23 | } 24 | }, 25 | "utils": { 26 | "locked": { 27 | "lastModified": 1667395993, 28 | "narHash": "sha256-nuEHfE/LcWyuSWnS8t12N1wc105Qtau+/OdUAjtQ0rA=", 29 | "owner": "numtide", 30 | "repo": "flake-utils", 31 | "rev": "5aed5285a952e0b949eb3ba02c12fa4fcfef535f", 32 | "type": "github" 33 | }, 34 | "original": { 35 | "owner": "numtide", 36 | "repo": "flake-utils", 37 | "type": "github" 38 | } 39 | } 40 | }, 41 | "root": "root", 42 | "version": 7 43 | } 44 | -------------------------------------------------------------------------------- /clash-meta/flake.nix: -------------------------------------------------------------------------------- 1 | { 2 | description = "Another Clash Kernel"; 3 | 4 | inputs.nixpkgs.url = "github:NixOS/nixpkgs/master"; 5 | 6 | inputs.utils.url = "github:numtide/flake-utils"; 7 | 8 | outputs = { self, nixpkgs, utils }: 9 | utils.lib.eachDefaultSystem 10 | (system: 11 | let 12 | pkgs = import nixpkgs { 13 | inherit system; 14 | overlays = [ self.overlay ]; 15 | }; 16 | in 17 | rec { 18 | packages.default = pkgs.clash-meta; 19 | } 20 | ) // 21 | ( 22 | let version = nixpkgs.lib.substring 0 8 self.lastModifiedDate or self.lastModified or "19700101"; in 23 | { 24 | overlay = final: prev: { 25 | 26 | clash-meta = final.buildGo119Module { 27 | pname = "clash-meta"; 28 | inherit version; 29 | src = ./.; 30 | 31 | vendorSha256 = "sha256-W5oiPtTRin0731QQWr98xZ2Vpk97HYcBtKoi1OKZz+w="; 32 | 33 | # Do not build testing suit 34 | excludedPackages = [ "./test" ]; 35 | 36 | CGO_ENABLED = 0; 37 | 38 | ldflags = [ 39 | "-s" 40 | "-w" 41 | "-X github.com/Dreamacro/clash/constant.Version=dev-${version}" 42 | "-X github.com/Dreamacro/clash/constant.BuildTime=${version}" 43 | ]; 44 | 45 | tags = [ 46 | "with_gvisor" 47 | ]; 48 | 49 | # Network required 50 | doCheck = false; 51 | 52 | postInstall = '' 53 | mv $out/bin/clash $out/bin/clash-meta 54 | ''; 55 | 56 | }; 57 | }; 58 | } 59 | ); 60 | } 61 | 62 | -------------------------------------------------------------------------------- /clash-meta/hub/executor/concurrent_load_limit.go: -------------------------------------------------------------------------------- 1 | //go:build !386 && !amd64 && !arm64 && !arm64be && !mipsle && !mips 2 | 3 | package executor 4 | 5 | const concurrentCount = 5 6 | -------------------------------------------------------------------------------- /clash-meta/hub/executor/concurrent_load_single.go: -------------------------------------------------------------------------------- 1 | //go:build mips || mipsle 2 | 3 | package executor 4 | 5 | const concurrentCount = 1 6 | -------------------------------------------------------------------------------- /clash-meta/hub/executor/concurrent_load_unlimit.go: -------------------------------------------------------------------------------- 1 | //go:build 386 || amd64 || arm64 || arm64be 2 | 3 | package executor 4 | 5 | import "math" 6 | 7 | const concurrentCount = math.MaxInt 8 | -------------------------------------------------------------------------------- /clash-meta/hub/hub.go: -------------------------------------------------------------------------------- 1 | package hub 2 | 3 | import ( 4 | "github.com/Dreamacro/clash/config" 5 | "github.com/Dreamacro/clash/hub/executor" 6 | "github.com/Dreamacro/clash/hub/route" 7 | "github.com/Dreamacro/clash/log" 8 | ) 9 | 10 | type Option func(*config.Config) 11 | 12 | func WithExternalUI(externalUI string) Option { 13 | return func(cfg *config.Config) { 14 | cfg.General.ExternalUI = externalUI 15 | } 16 | } 17 | 18 | func WithExternalController(externalController string) Option { 19 | return func(cfg *config.Config) { 20 | cfg.General.ExternalController = externalController 21 | } 22 | } 23 | 24 | func WithSecret(secret string) Option { 25 | return func(cfg *config.Config) { 26 | cfg.General.Secret = secret 27 | } 28 | } 29 | 30 | // Parse call at the beginning of clash 31 | func Parse(options ...Option) error { 32 | cfg, err := executor.Parse() 33 | if err != nil { 34 | return err 35 | } 36 | 37 | for _, option := range options { 38 | option(cfg) 39 | } 40 | 41 | if cfg.General.ExternalUI != "" { 42 | route.SetUIPath(cfg.General.ExternalUI) 43 | } 44 | 45 | if cfg.General.ExternalController != "" { 46 | go route.Start(cfg.General.ExternalController, cfg.General.ExternalControllerTLS, 47 | cfg.General.Secret, cfg.TLS.Certificate, cfg.TLS.PrivateKey, cfg.General.LogLevel == log.DEBUG) 48 | } 49 | 50 | executor.ApplyConfig(cfg, true) 51 | return nil 52 | } 53 | -------------------------------------------------------------------------------- /clash-meta/hub/route/cache.go: -------------------------------------------------------------------------------- 1 | package route 2 | 3 | import ( 4 | "net/http" 5 | 6 | "github.com/Dreamacro/clash/component/resolver" 7 | 8 | "github.com/go-chi/chi/v5" 9 | "github.com/go-chi/render" 10 | ) 11 | 12 | func cacheRouter() http.Handler { 13 | r := chi.NewRouter() 14 | r.Post("/fakeip/flush", flushFakeIPPool) 15 | return r 16 | } 17 | 18 | func flushFakeIPPool(w http.ResponseWriter, r *http.Request) { 19 | err := resolver.FlushFakeIP() 20 | if err != nil { 21 | render.Status(r, http.StatusBadRequest) 22 | render.JSON(w, r, newError(err.Error())) 23 | return 24 | } 25 | render.NoContent(w, r) 26 | } 27 | -------------------------------------------------------------------------------- /clash-meta/hub/route/common.go: -------------------------------------------------------------------------------- 1 | package route 2 | 3 | import ( 4 | "net/http" 5 | "net/url" 6 | 7 | "github.com/go-chi/chi/v5" 8 | ) 9 | 10 | // When name is composed of a partial escape string, Golang does not unescape it 11 | func getEscapeParam(r *http.Request, paramName string) string { 12 | param := chi.URLParam(r, paramName) 13 | if newParam, err := url.PathUnescape(param); err == nil { 14 | param = newParam 15 | } 16 | return param 17 | } 18 | -------------------------------------------------------------------------------- /clash-meta/hub/route/ctxkeys.go: -------------------------------------------------------------------------------- 1 | package route 2 | 3 | var ( 4 | CtxKeyProxyName = contextKey("proxy name") 5 | CtxKeyProviderName = contextKey("provider name") 6 | CtxKeyProxy = contextKey("proxy") 7 | CtxKeyProvider = contextKey("provider") 8 | ) 9 | 10 | type contextKey string 11 | 12 | func (c contextKey) String() string { 13 | return "clash context key " + string(c) 14 | } 15 | -------------------------------------------------------------------------------- /clash-meta/hub/route/errors.go: -------------------------------------------------------------------------------- 1 | package route 2 | 3 | var ( 4 | ErrUnauthorized = newError("Unauthorized") 5 | ErrBadRequest = newError("Body invalid") 6 | ErrForbidden = newError("Forbidden") 7 | ErrNotFound = newError("Resource not found") 8 | ErrRequestTimeout = newError("Timeout") 9 | ) 10 | 11 | // HTTPError is custom HTTP error for API 12 | type HTTPError struct { 13 | Message string `json:"message"` 14 | } 15 | 16 | func (e *HTTPError) Error() string { 17 | return e.Message 18 | } 19 | 20 | func newError(msg string) *HTTPError { 21 | return &HTTPError{Message: msg} 22 | } 23 | -------------------------------------------------------------------------------- /clash-meta/hub/route/rules.go: -------------------------------------------------------------------------------- 1 | package route 2 | 3 | import ( 4 | "github.com/Dreamacro/clash/constant" 5 | "net/http" 6 | 7 | "github.com/Dreamacro/clash/tunnel" 8 | 9 | "github.com/go-chi/chi/v5" 10 | "github.com/go-chi/render" 11 | ) 12 | 13 | func ruleRouter() http.Handler { 14 | r := chi.NewRouter() 15 | r.Get("/", getRules) 16 | return r 17 | } 18 | 19 | type Rule struct { 20 | Type string `json:"type"` 21 | Payload string `json:"payload"` 22 | Proxy string `json:"proxy"` 23 | Size int `json:"size"` 24 | } 25 | 26 | func getRules(w http.ResponseWriter, r *http.Request) { 27 | rawRules := tunnel.Rules() 28 | rules := []Rule{} 29 | for _, rule := range rawRules { 30 | r := Rule{ 31 | Type: rule.RuleType().String(), 32 | Payload: rule.Payload(), 33 | Proxy: rule.Adapter(), 34 | Size: -1, 35 | } 36 | if rule.RuleType() == constant.GEOIP || rule.RuleType() == constant.GEOSITE { 37 | r.Size = rule.(constant.RuleGroup).GetRecodeSize() 38 | } 39 | rules = append(rules, r) 40 | 41 | } 42 | 43 | render.JSON(w, r, render.M{ 44 | "rules": rules, 45 | }) 46 | } 47 | -------------------------------------------------------------------------------- /clash-meta/hub/route/upgrade.go: -------------------------------------------------------------------------------- 1 | package route 2 | 3 | import ( 4 | "fmt" 5 | "net/http" 6 | "os" 7 | 8 | "github.com/Dreamacro/clash/hub/updater" 9 | "github.com/Dreamacro/clash/log" 10 | 11 | "github.com/go-chi/chi/v5" 12 | "github.com/go-chi/render" 13 | ) 14 | 15 | func upgradeRouter() http.Handler { 16 | r := chi.NewRouter() 17 | r.Post("/", upgrade) 18 | return r 19 | } 20 | 21 | func upgrade(w http.ResponseWriter, r *http.Request) { 22 | // modify from https://github.com/AdguardTeam/AdGuardHome/blob/595484e0b3fb4c457f9bb727a6b94faa78a66c5f/internal/home/controlupdate.go#L108 23 | log.Infoln("start update") 24 | execPath, err := os.Executable() 25 | if err != nil { 26 | render.Status(r, http.StatusInternalServerError) 27 | render.JSON(w, r, newError(fmt.Sprintf("getting path: %s", err))) 28 | return 29 | } 30 | 31 | err = updater.Update(execPath) 32 | if err != nil { 33 | log.Warnln("%s", err) 34 | render.Status(r, http.StatusInternalServerError) 35 | render.JSON(w, r, newError(fmt.Sprintf("%s", err))) 36 | return 37 | } 38 | 39 | render.JSON(w, r, render.M{"status": "ok"}) 40 | if f, ok := w.(http.Flusher); ok { 41 | f.Flush() 42 | } 43 | 44 | go runRestart(execPath) 45 | } 46 | -------------------------------------------------------------------------------- /clash-meta/hub/updater/limitedreader.go: -------------------------------------------------------------------------------- 1 | package updater 2 | 3 | import ( 4 | "fmt" 5 | "io" 6 | 7 | "golang.org/x/exp/constraints" 8 | ) 9 | 10 | // LimitReachedError records the limit and the operation that caused it. 11 | type LimitReachedError struct { 12 | Limit int64 13 | } 14 | 15 | // Error implements the [error] interface for *LimitReachedError. 16 | // 17 | // TODO(a.garipov): Think about error string format. 18 | func (lre *LimitReachedError) Error() string { 19 | return fmt.Sprintf("attempted to read more than %d bytes", lre.Limit) 20 | } 21 | 22 | // limitedReader is a wrapper for [io.Reader] limiting the input and dealing 23 | // with errors package. 24 | type limitedReader struct { 25 | r io.Reader 26 | limit int64 27 | n int64 28 | } 29 | 30 | // Read implements the [io.Reader] interface. 31 | func (lr *limitedReader) Read(p []byte) (n int, err error) { 32 | if lr.n == 0 { 33 | return 0, &LimitReachedError{ 34 | Limit: lr.limit, 35 | } 36 | } 37 | 38 | p = p[:Min(lr.n, int64(len(p)))] 39 | 40 | n, err = lr.r.Read(p) 41 | lr.n -= int64(n) 42 | 43 | return n, err 44 | } 45 | 46 | // LimitReader wraps Reader to make it's Reader stop with ErrLimitReached after 47 | // n bytes read. 48 | func LimitReader(r io.Reader, n int64) (limited io.Reader, err error) { 49 | if n < 0 { 50 | return nil, &updateError{Message: "limit must be non-negative"} 51 | } 52 | 53 | return &limitedReader{ 54 | r: r, 55 | limit: n, 56 | n: n, 57 | }, nil 58 | } 59 | 60 | // Min returns the smaller of x or y. 61 | func Min[T constraints.Integer | ~string](x, y T) (res T) { 62 | if x < y { 63 | return x 64 | } 65 | 66 | return y 67 | } 68 | -------------------------------------------------------------------------------- /clash-meta/listener/auth/auth.go: -------------------------------------------------------------------------------- 1 | package auth 2 | 3 | import ( 4 | "github.com/Dreamacro/clash/component/auth" 5 | ) 6 | 7 | var authenticator auth.Authenticator 8 | 9 | func Authenticator() auth.Authenticator { 10 | return authenticator 11 | } 12 | 13 | func SetAuthenticator(au auth.Authenticator) { 14 | authenticator = au 15 | } 16 | -------------------------------------------------------------------------------- /clash-meta/listener/config/shadowsocks.go: -------------------------------------------------------------------------------- 1 | package config 2 | 3 | import ( 4 | "encoding/json" 5 | ) 6 | 7 | type ShadowsocksServer struct { 8 | Enable bool 9 | Listen string 10 | Password string 11 | Cipher string 12 | Udp bool 13 | } 14 | 15 | func (t ShadowsocksServer) String() string { 16 | b, _ := json.Marshal(t) 17 | return string(b) 18 | } 19 | -------------------------------------------------------------------------------- /clash-meta/listener/config/tuic.go: -------------------------------------------------------------------------------- 1 | package config 2 | 3 | import ( 4 | "encoding/json" 5 | ) 6 | 7 | type TuicServer struct { 8 | Enable bool `yaml:"enable" json:"enable"` 9 | Listen string `yaml:"listen" json:"listen"` 10 | Token []string `yaml:"token" json:"token"` 11 | Certificate string `yaml:"certificate" json:"certificate"` 12 | PrivateKey string `yaml:"private-key" json:"private-key"` 13 | CongestionController string `yaml:"congestion-controller" json:"congestion-controller,omitempty"` 14 | MaxIdleTime int `yaml:"max-idle-time" json:"max-idle-time,omitempty"` 15 | AuthenticationTimeout int `yaml:"authentication-timeout" json:"authentication-timeout,omitempty"` 16 | ALPN []string `yaml:"alpn" json:"alpn,omitempty"` 17 | MaxUdpRelayPacketSize int `yaml:"max-udp-relay-packet-size" json:"max-udp-relay-packet-size,omitempty"` 18 | MaxDatagramFrameSize int `yaml:"max-datagram-frame-size" json:"max-datagram-frame-size,omitempty"` 19 | } 20 | 21 | func (t TuicServer) String() string { 22 | b, _ := json.Marshal(t) 23 | return string(b) 24 | } 25 | -------------------------------------------------------------------------------- /clash-meta/listener/config/tunnel.go: -------------------------------------------------------------------------------- 1 | package config 2 | 3 | import ( 4 | "fmt" 5 | "net" 6 | "strings" 7 | 8 | "github.com/samber/lo" 9 | ) 10 | 11 | type tunnel struct { 12 | Network []string `yaml:"network"` 13 | Address string `yaml:"address"` 14 | Target string `yaml:"target"` 15 | Proxy string `yaml:"proxy"` 16 | } 17 | 18 | type Tunnel tunnel 19 | 20 | // UnmarshalYAML implements yaml.Unmarshaler 21 | func (t *Tunnel) UnmarshalYAML(unmarshal func(any) error) error { 22 | var tp string 23 | if err := unmarshal(&tp); err != nil { 24 | var inner tunnel 25 | if err := unmarshal(&inner); err != nil { 26 | return err 27 | } 28 | 29 | *t = Tunnel(inner) 30 | return nil 31 | } 32 | 33 | // parse udp/tcp,address,target,proxy 34 | parts := lo.Map(strings.Split(tp, ","), func(s string, _ int) string { 35 | return strings.TrimSpace(s) 36 | }) 37 | if len(parts) != 3 && len(parts) != 4 { 38 | return fmt.Errorf("invalid tunnel config %s", tp) 39 | } 40 | network := strings.Split(parts[0], "/") 41 | 42 | // validate network 43 | for _, n := range network { 44 | switch n { 45 | case "tcp", "udp": 46 | default: 47 | return fmt.Errorf("invalid tunnel network %s", n) 48 | } 49 | } 50 | 51 | // validate address and target 52 | address := parts[1] 53 | target := parts[2] 54 | for _, addr := range []string{address, target} { 55 | if _, _, err := net.SplitHostPort(addr); err != nil { 56 | return fmt.Errorf("invalid tunnel target or address %s", addr) 57 | } 58 | } 59 | 60 | *t = Tunnel(tunnel{ 61 | Network: network, 62 | Address: address, 63 | Target: target, 64 | }) 65 | if len(parts) == 4 { 66 | t.Proxy = parts[3] 67 | } 68 | return nil 69 | } 70 | -------------------------------------------------------------------------------- /clash-meta/listener/config/vmess.go: -------------------------------------------------------------------------------- 1 | package config 2 | 3 | import ( 4 | "encoding/json" 5 | ) 6 | 7 | type VmessUser struct { 8 | Username string 9 | UUID string 10 | AlterID int 11 | } 12 | 13 | type VmessServer struct { 14 | Enable bool 15 | Listen string 16 | Users []VmessUser 17 | } 18 | 19 | func (t VmessServer) String() string { 20 | b, _ := json.Marshal(t) 21 | return string(b) 22 | } 23 | -------------------------------------------------------------------------------- /clash-meta/listener/http/client.go: -------------------------------------------------------------------------------- 1 | package http 2 | 3 | import ( 4 | "context" 5 | "errors" 6 | "net" 7 | "net/http" 8 | "time" 9 | 10 | "github.com/Dreamacro/clash/adapter/inbound" 11 | C "github.com/Dreamacro/clash/constant" 12 | "github.com/Dreamacro/clash/transport/socks5" 13 | ) 14 | 15 | func newClient(source net.Addr, in chan<- C.ConnContext, additions ...inbound.Addition) *http.Client { 16 | return &http.Client{ 17 | Transport: &http.Transport{ 18 | // from http.DefaultTransport 19 | MaxIdleConns: 100, 20 | IdleConnTimeout: 90 * time.Second, 21 | TLSHandshakeTimeout: 10 * time.Second, 22 | ExpectContinueTimeout: 1 * time.Second, 23 | DialContext: func(context context.Context, network, address string) (net.Conn, error) { 24 | if network != "tcp" && network != "tcp4" && network != "tcp6" { 25 | return nil, errors.New("unsupported network " + network) 26 | } 27 | 28 | dstAddr := socks5.ParseAddr(address) 29 | if dstAddr == nil { 30 | return nil, socks5.ErrAddressNotSupported 31 | } 32 | 33 | left, right := net.Pipe() 34 | 35 | in <- inbound.NewHTTP(dstAddr, source, right, additions...) 36 | 37 | return left, nil 38 | }, 39 | }, 40 | CheckRedirect: func(req *http.Request, via []*http.Request) error { 41 | return http.ErrUseLastResponse 42 | }, 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /clash-meta/listener/http/hack.go: -------------------------------------------------------------------------------- 1 | package http 2 | 3 | import ( 4 | "bufio" 5 | "net/http" 6 | _ "unsafe" 7 | ) 8 | 9 | //go:linkname ReadRequest net/http.readRequest 10 | func ReadRequest(b *bufio.Reader) (req *http.Request, err error) 11 | -------------------------------------------------------------------------------- /clash-meta/listener/inbound/http.go: -------------------------------------------------------------------------------- 1 | package inbound 2 | 3 | import ( 4 | C "github.com/Dreamacro/clash/constant" 5 | "github.com/Dreamacro/clash/listener/http" 6 | "github.com/Dreamacro/clash/log" 7 | ) 8 | 9 | type HTTPOption struct { 10 | BaseOption 11 | } 12 | 13 | func (o HTTPOption) Equal(config C.InboundConfig) bool { 14 | return optionToString(o) == optionToString(config) 15 | } 16 | 17 | type HTTP struct { 18 | *Base 19 | config *HTTPOption 20 | l *http.Listener 21 | } 22 | 23 | func NewHTTP(options *HTTPOption) (*HTTP, error) { 24 | base, err := NewBase(&options.BaseOption) 25 | if err != nil { 26 | return nil, err 27 | } 28 | return &HTTP{ 29 | Base: base, 30 | config: options, 31 | }, nil 32 | } 33 | 34 | // Config implements constant.InboundListener 35 | func (h *HTTP) Config() C.InboundConfig { 36 | return h.config 37 | } 38 | 39 | // Address implements constant.InboundListener 40 | func (h *HTTP) Address() string { 41 | return h.l.Address() 42 | } 43 | 44 | // Listen implements constant.InboundListener 45 | func (h *HTTP) Listen(tcpIn chan<- C.ConnContext, udpIn chan<- C.PacketAdapter, natTable C.NatTable) error { 46 | var err error 47 | h.l, err = http.New(h.RawAddress(), tcpIn, h.Additions()...) 48 | if err != nil { 49 | return err 50 | } 51 | log.Infoln("HTTP[%s] proxy listening at: %s", h.Name(), h.Address()) 52 | return nil 53 | } 54 | 55 | // Close implements constant.InboundListener 56 | func (h *HTTP) Close() error { 57 | if h.l != nil { 58 | return h.l.Close() 59 | } 60 | return nil 61 | } 62 | 63 | var _ C.InboundListener = (*HTTP)(nil) 64 | -------------------------------------------------------------------------------- /clash-meta/listener/inbound/redir.go: -------------------------------------------------------------------------------- 1 | package inbound 2 | 3 | import ( 4 | C "github.com/Dreamacro/clash/constant" 5 | "github.com/Dreamacro/clash/listener/redir" 6 | "github.com/Dreamacro/clash/log" 7 | ) 8 | 9 | type RedirOption struct { 10 | BaseOption 11 | } 12 | 13 | func (o RedirOption) Equal(config C.InboundConfig) bool { 14 | return optionToString(o) == optionToString(config) 15 | } 16 | 17 | type Redir struct { 18 | *Base 19 | config *RedirOption 20 | l *redir.Listener 21 | } 22 | 23 | func NewRedir(options *RedirOption) (*Redir, error) { 24 | base, err := NewBase(&options.BaseOption) 25 | if err != nil { 26 | return nil, err 27 | } 28 | return &Redir{ 29 | Base: base, 30 | config: options, 31 | }, nil 32 | } 33 | 34 | // Config implements constant.InboundListener 35 | func (r *Redir) Config() C.InboundConfig { 36 | return r.config 37 | } 38 | 39 | // Address implements constant.InboundListener 40 | func (r *Redir) Address() string { 41 | return r.l.Address() 42 | } 43 | 44 | // Listen implements constant.InboundListener 45 | func (r *Redir) Listen(tcpIn chan<- C.ConnContext, udpIn chan<- C.PacketAdapter, natTable C.NatTable) error { 46 | var err error 47 | r.l, err = redir.New(r.RawAddress(), tcpIn, r.Additions()...) 48 | if err != nil { 49 | return err 50 | } 51 | log.Infoln("Redir[%s] proxy listening at: %s", r.Name(), r.Address()) 52 | return nil 53 | } 54 | 55 | // Close implements constant.InboundListener 56 | func (r *Redir) Close() error { 57 | if r.l != nil { 58 | r.l.Close() 59 | } 60 | return nil 61 | } 62 | 63 | var _ C.InboundListener = (*Redir)(nil) 64 | -------------------------------------------------------------------------------- /clash-meta/listener/inner/tcp.go: -------------------------------------------------------------------------------- 1 | package inner 2 | 3 | import ( 4 | "errors" 5 | "net" 6 | 7 | "github.com/Dreamacro/clash/adapter/inbound" 8 | C "github.com/Dreamacro/clash/constant" 9 | ) 10 | 11 | var tcpIn chan<- C.ConnContext 12 | 13 | func New(in chan<- C.ConnContext) { 14 | tcpIn = in 15 | } 16 | 17 | func HandleTcp(address string) (conn net.Conn, err error) { 18 | if tcpIn == nil { 19 | return nil, errors.New("tcp uninitialized") 20 | } 21 | // executor Parsed 22 | conn1, conn2 := net.Pipe() 23 | context := inbound.NewInner(conn2, address) 24 | tcpIn <- context 25 | return conn1, nil 26 | } 27 | -------------------------------------------------------------------------------- /clash-meta/listener/redir/tcp.go: -------------------------------------------------------------------------------- 1 | package redir 2 | 3 | import ( 4 | "net" 5 | 6 | "github.com/Dreamacro/clash/adapter/inbound" 7 | C "github.com/Dreamacro/clash/constant" 8 | ) 9 | 10 | type Listener struct { 11 | listener net.Listener 12 | addr string 13 | closed bool 14 | } 15 | 16 | // RawAddress implements C.Listener 17 | func (l *Listener) RawAddress() string { 18 | return l.addr 19 | } 20 | 21 | // Address implements C.Listener 22 | func (l *Listener) Address() string { 23 | return l.listener.Addr().String() 24 | } 25 | 26 | // Close implements C.Listener 27 | func (l *Listener) Close() error { 28 | l.closed = true 29 | return l.listener.Close() 30 | } 31 | 32 | func New(addr string, in chan<- C.ConnContext, additions ...inbound.Addition) (*Listener, error) { 33 | if len(additions) == 0 { 34 | additions = []inbound.Addition{ 35 | inbound.WithInName("DEFAULT-REDIR"), 36 | inbound.WithSpecialRules(""), 37 | } 38 | } 39 | l, err := net.Listen("tcp", addr) 40 | if err != nil { 41 | return nil, err 42 | } 43 | rl := &Listener{ 44 | listener: l, 45 | addr: addr, 46 | } 47 | 48 | go func() { 49 | for { 50 | c, err := l.Accept() 51 | if err != nil { 52 | if rl.closed { 53 | break 54 | } 55 | continue 56 | } 57 | go handleRedir(c, in, additions...) 58 | } 59 | }() 60 | 61 | return rl, nil 62 | } 63 | func handleRedir(conn net.Conn, in chan<- C.ConnContext, additions ...inbound.Addition) { 64 | target, err := parserPacket(conn) 65 | if err != nil { 66 | conn.Close() 67 | return 68 | } 69 | conn.(*net.TCPConn).SetKeepAlive(true) 70 | in <- inbound.NewSocket(target, conn, C.REDIR, additions...) 71 | } 72 | -------------------------------------------------------------------------------- /clash-meta/listener/redir/tcp_freebsd.go: -------------------------------------------------------------------------------- 1 | package redir 2 | 3 | import ( 4 | "errors" 5 | "net" 6 | "syscall" 7 | "unsafe" 8 | 9 | "github.com/Dreamacro/clash/transport/socks5" 10 | ) 11 | 12 | const ( 13 | SO_ORIGINAL_DST = 80 // from linux/include/uapi/linux/netfilter_ipv4.h 14 | IP6T_SO_ORIGINAL_DST = 80 // from linux/include/uapi/linux/netfilter_ipv6/ip6_tables.h 15 | ) 16 | 17 | func parserPacket(conn net.Conn) (socks5.Addr, error) { 18 | c, ok := conn.(*net.TCPConn) 19 | if !ok { 20 | return nil, errors.New("only work with TCP connection") 21 | } 22 | 23 | rc, err := c.SyscallConn() 24 | if err != nil { 25 | return nil, err 26 | } 27 | 28 | var addr socks5.Addr 29 | 30 | rc.Control(func(fd uintptr) { 31 | addr, err = getorigdst(fd) 32 | }) 33 | 34 | return addr, err 35 | } 36 | 37 | // Call getorigdst() from linux/net/ipv4/netfilter/nf_conntrack_l3proto_ipv4.c 38 | func getorigdst(fd uintptr) (socks5.Addr, error) { 39 | raw := syscall.RawSockaddrInet4{} 40 | siz := unsafe.Sizeof(raw) 41 | _, _, err := syscall.Syscall6(syscall.SYS_GETSOCKOPT, fd, syscall.IPPROTO_IP, SO_ORIGINAL_DST, uintptr(unsafe.Pointer(&raw)), uintptr(unsafe.Pointer(&siz)), 0) 42 | if err != 0 { 43 | return nil, err 44 | } 45 | 46 | addr := make([]byte, 1+net.IPv4len+2) 47 | addr[0] = socks5.AtypIPv4 48 | copy(addr[1:1+net.IPv4len], raw.Addr[:]) 49 | port := (*[2]byte)(unsafe.Pointer(&raw.Port)) // big-endian 50 | addr[1+net.IPv4len], addr[1+net.IPv4len+1] = port[0], port[1] 51 | return addr, nil 52 | } 53 | -------------------------------------------------------------------------------- /clash-meta/listener/redir/tcp_linux_386.go: -------------------------------------------------------------------------------- 1 | package redir 2 | 3 | import ( 4 | "syscall" 5 | "unsafe" 6 | ) 7 | 8 | const GETSOCKOPT = 15 // https://golang.org/src/syscall/syscall_linux_386.go#L183 9 | 10 | func socketcall(call, a0, a1, a2, a3, a4, a5 uintptr) error { 11 | var a [6]uintptr 12 | a[0], a[1], a[2], a[3], a[4], a[5] = a0, a1, a2, a3, a4, a5 13 | if _, _, errno := syscall.Syscall6(syscall.SYS_SOCKETCALL, call, uintptr(unsafe.Pointer(&a)), 0, 0, 0, 0); errno != 0 { 14 | return errno 15 | } 16 | return nil 17 | } 18 | -------------------------------------------------------------------------------- /clash-meta/listener/redir/tcp_linux_other.go: -------------------------------------------------------------------------------- 1 | //go:build linux && !386 2 | 3 | package redir 4 | 5 | import "syscall" 6 | 7 | const GETSOCKOPT = syscall.SYS_GETSOCKOPT 8 | 9 | func socketcall(call, a0, a1, a2, a3, a4, a5 uintptr) error { 10 | if _, _, errno := syscall.Syscall6(call, a0, a1, a2, a3, a4, a5); errno != 0 { 11 | return errno 12 | } 13 | return nil 14 | } 15 | -------------------------------------------------------------------------------- /clash-meta/listener/redir/tcp_other.go: -------------------------------------------------------------------------------- 1 | //go:build !darwin && !linux && !freebsd 2 | 3 | package redir 4 | 5 | import ( 6 | "errors" 7 | "net" 8 | 9 | "github.com/Dreamacro/clash/transport/socks5" 10 | ) 11 | 12 | func parserPacket(conn net.Conn) (socks5.Addr, error) { 13 | return nil, errors.New("system not support yet") 14 | } 15 | -------------------------------------------------------------------------------- /clash-meta/listener/shadowsocks/utils.go: -------------------------------------------------------------------------------- 1 | package shadowsocks 2 | 3 | import ( 4 | "bytes" 5 | "errors" 6 | "net" 7 | "net/url" 8 | 9 | "github.com/Dreamacro/clash/transport/socks5" 10 | ) 11 | 12 | type packet struct { 13 | pc net.PacketConn 14 | rAddr net.Addr 15 | payload []byte 16 | put func() 17 | } 18 | 19 | func (c *packet) Data() []byte { 20 | return c.payload 21 | } 22 | 23 | // WriteBack wirtes UDP packet with source(ip, port) = `addr` 24 | func (c *packet) WriteBack(b []byte, addr net.Addr) (n int, err error) { 25 | if addr == nil { 26 | err = errors.New("address is invalid") 27 | return 28 | } 29 | packet := bytes.Join([][]byte{socks5.ParseAddrToSocksAddr(addr), b}, []byte{}) 30 | return c.pc.WriteTo(packet, c.rAddr) 31 | } 32 | 33 | // LocalAddr returns the source IP/Port of UDP Packet 34 | func (c *packet) LocalAddr() net.Addr { 35 | return c.rAddr 36 | } 37 | 38 | func (c *packet) Drop() { 39 | if c.put != nil { 40 | c.put() 41 | } 42 | } 43 | 44 | func (c *packet) InAddr() net.Addr { 45 | return c.pc.LocalAddr() 46 | } 47 | 48 | func ParseSSURL(s string) (addr, cipher, password string, err error) { 49 | u, err := url.Parse(s) 50 | if err != nil { 51 | return 52 | } 53 | 54 | addr = u.Host 55 | if u.User != nil { 56 | cipher = u.User.Username() 57 | password, _ = u.User.Password() 58 | } 59 | return 60 | } 61 | -------------------------------------------------------------------------------- /clash-meta/listener/sing/context.go: -------------------------------------------------------------------------------- 1 | package sing 2 | 3 | import ( 4 | "context" 5 | "golang.org/x/exp/slices" 6 | 7 | "github.com/Dreamacro/clash/adapter/inbound" 8 | 9 | "github.com/sagernet/sing/common/auth" 10 | ) 11 | 12 | type contextKey string 13 | 14 | var ctxKeyAdditions = contextKey("Additions") 15 | 16 | func WithAdditions(ctx context.Context, additions ...inbound.Addition) context.Context { 17 | return context.WithValue(ctx, ctxKeyAdditions, additions) 18 | } 19 | 20 | func getAdditions(ctx context.Context) []inbound.Addition { 21 | if v := ctx.Value(ctxKeyAdditions); v != nil { 22 | if a, ok := v.([]inbound.Addition); ok { 23 | return a 24 | } 25 | } 26 | return nil 27 | } 28 | 29 | func combineAdditions(ctx context.Context, additions []inbound.Addition) []inbound.Addition { 30 | additionsCloned := false 31 | if ctxAdditions := getAdditions(ctx); len(ctxAdditions) > 0 { 32 | additions = slices.Clone(additions) 33 | additionsCloned = true 34 | additions = append(additions, ctxAdditions...) 35 | } 36 | if user, ok := auth.UserFromContext[string](ctx); ok { 37 | if !additionsCloned { 38 | additions = slices.Clone(additions) 39 | additionsCloned = true 40 | } 41 | additions = append(additions, inbound.WithInUser(user)) 42 | } 43 | return additions 44 | } 45 | -------------------------------------------------------------------------------- /clash-meta/listener/sing_tun/server_android.go: -------------------------------------------------------------------------------- 1 | package sing_tun 2 | 3 | import ( 4 | "github.com/Dreamacro/clash/log" 5 | tun "github.com/metacubex/sing-tun" 6 | "github.com/sagernet/netlink" 7 | "golang.org/x/sys/unix" 8 | "runtime" 9 | ) 10 | 11 | func (l *Listener) buildAndroidRules(tunOptions *tun.Options) error { 12 | packageManager, err := tun.NewPackageManager(l.handler) 13 | if err != nil { 14 | return err 15 | } 16 | err = packageManager.Start() 17 | if err != nil { 18 | return err 19 | } 20 | l.packageManager = packageManager 21 | tunOptions.BuildAndroidRules(packageManager, l.handler) 22 | return nil 23 | } 24 | 25 | func (h *ListenerHandler) OnPackagesUpdated(packages int, sharedUsers int) { 26 | return 27 | } 28 | 29 | func (l *Listener) openAndroidHotspot(tunOptions tun.Options) { 30 | if runtime.GOOS == "android" && tunOptions.AutoRoute { 31 | priority := 9000 32 | if len(tunOptions.ExcludedRanges()) > 0 { 33 | priority++ 34 | } 35 | if tunOptions.InterfaceMonitor.AndroidVPNEnabled() { 36 | priority++ 37 | } 38 | it := netlink.NewRule() 39 | it.Priority = priority 40 | it.IifName = tunOptions.Name 41 | it.Table = 254 //main 42 | it.Family = unix.AF_INET 43 | it.SuppressPrefixlen = 0 44 | err := netlink.RuleAdd(it) 45 | if err != nil { 46 | log.Warnln("[TUN] add AndroidHotspot rule error") 47 | } 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /clash-meta/listener/sing_tun/server_notandroid.go: -------------------------------------------------------------------------------- 1 | //go:build !android 2 | 3 | package sing_tun 4 | 5 | import ( 6 | tun "github.com/metacubex/sing-tun" 7 | ) 8 | 9 | func (l *Listener) buildAndroidRules(tunOptions *tun.Options) error { 10 | return nil 11 | } 12 | func (l *Listener) openAndroidHotspot(tunOptions tun.Options) {} 13 | -------------------------------------------------------------------------------- /clash-meta/listener/sing_tun/server_notwindows.go: -------------------------------------------------------------------------------- 1 | //go:build !windows 2 | 3 | package sing_tun 4 | 5 | import ( 6 | tun "github.com/metacubex/sing-tun" 7 | ) 8 | 9 | func tunNew(options tun.Options) (tun.Tun, error) { 10 | return tun.New(options) 11 | } 12 | -------------------------------------------------------------------------------- /clash-meta/listener/sing_tun/server_windows.go: -------------------------------------------------------------------------------- 1 | package sing_tun 2 | 3 | import ( 4 | "time" 5 | 6 | "github.com/Dreamacro/clash/log" 7 | 8 | tun "github.com/metacubex/sing-tun" 9 | ) 10 | 11 | func tunNew(options tun.Options) (tunIf tun.Tun, err error) { 12 | maxRetry := 3 13 | for i := 0; i < maxRetry; i++ { 14 | timeBegin := time.Now() 15 | tunIf, err = tun.New(options) 16 | if err == nil { 17 | return 18 | } 19 | timeEnd := time.Now() 20 | if timeEnd.Sub(timeBegin) < 1*time.Second { // retrying for "Cannot create a file when that file already exists." 21 | return 22 | } 23 | log.Warnln("Start Tun interface timeout: %s [retrying %d/%d]", err, i+1, maxRetry) 24 | } 25 | return 26 | } 27 | 28 | func init() { 29 | tun.TunnelType = InterfaceName 30 | } 31 | -------------------------------------------------------------------------------- /clash-meta/listener/sing_tun/tun_name_darwin.go: -------------------------------------------------------------------------------- 1 | package sing_tun 2 | 3 | import "golang.org/x/sys/unix" 4 | 5 | func getTunnelName(fd int32) (string, error) { 6 | return unix.GetsockoptString( 7 | int(fd), 8 | 2, /* #define SYSPROTO_CONTROL 2 */ 9 | 2, /* #define UTUN_OPT_IFNAME 2 */ 10 | ) 11 | } 12 | -------------------------------------------------------------------------------- /clash-meta/listener/sing_tun/tun_name_linux.go: -------------------------------------------------------------------------------- 1 | package sing_tun 2 | 3 | import ( 4 | "fmt" 5 | "golang.org/x/sys/unix" 6 | "syscall" 7 | "unsafe" 8 | ) 9 | 10 | const ifReqSize = unix.IFNAMSIZ + 64 11 | 12 | func getTunnelName(fd int32) (string, error) { 13 | var ifr [ifReqSize]byte 14 | var errno syscall.Errno 15 | _, _, errno = unix.Syscall( 16 | unix.SYS_IOCTL, 17 | uintptr(fd), 18 | uintptr(unix.TUNGETIFF), 19 | uintptr(unsafe.Pointer(&ifr[0])), 20 | ) 21 | if errno != 0 { 22 | return "", fmt.Errorf("failed to get name of TUN device: %w", errno) 23 | } 24 | return unix.ByteSliceToString(ifr[:]), nil 25 | } 26 | -------------------------------------------------------------------------------- /clash-meta/listener/sing_tun/tun_name_other.go: -------------------------------------------------------------------------------- 1 | //go:build !(darwin || linux) 2 | 3 | package sing_tun 4 | 5 | import "os" 6 | 7 | func getTunnelName(fd int32) (string, error) { 8 | return "", os.ErrInvalid 9 | } 10 | -------------------------------------------------------------------------------- /clash-meta/listener/socks/utils.go: -------------------------------------------------------------------------------- 1 | package socks 2 | 3 | import ( 4 | "net" 5 | 6 | "github.com/Dreamacro/clash/common/pool" 7 | "github.com/Dreamacro/clash/transport/socks5" 8 | ) 9 | 10 | type packet struct { 11 | pc net.PacketConn 12 | rAddr net.Addr 13 | payload []byte 14 | bufRef []byte 15 | } 16 | 17 | func (c *packet) Data() []byte { 18 | return c.payload 19 | } 20 | 21 | // WriteBack write UDP packet with source(ip, port) = `addr` 22 | func (c *packet) WriteBack(b []byte, addr net.Addr) (n int, err error) { 23 | packet, err := socks5.EncodeUDPPacket(socks5.ParseAddrToSocksAddr(addr), b) 24 | if err != nil { 25 | return 26 | } 27 | return c.pc.WriteTo(packet, c.rAddr) 28 | } 29 | 30 | // LocalAddr returns the source IP/Port of UDP Packet 31 | func (c *packet) LocalAddr() net.Addr { 32 | return c.rAddr 33 | } 34 | 35 | func (c *packet) Drop() { 36 | pool.Put(c.bufRef) 37 | } 38 | 39 | func (c *packet) InAddr() net.Addr { 40 | return c.pc.LocalAddr() 41 | } 42 | -------------------------------------------------------------------------------- /clash-meta/listener/tproxy/setsockopt_linux.go: -------------------------------------------------------------------------------- 1 | //go:build linux 2 | 3 | package tproxy 4 | 5 | import ( 6 | "net" 7 | "syscall" 8 | ) 9 | 10 | func setsockopt(rc syscall.RawConn, addr string) error { 11 | isIPv6 := true 12 | host, _, err := net.SplitHostPort(addr) 13 | if err != nil { 14 | return err 15 | } 16 | ip := net.ParseIP(host) 17 | if ip != nil && ip.To4() != nil { 18 | isIPv6 = false 19 | } 20 | 21 | rc.Control(func(fd uintptr) { 22 | err = syscall.SetsockoptInt(int(fd), syscall.SOL_SOCKET, syscall.SO_REUSEADDR, 1) 23 | 24 | if err == nil { 25 | err = syscall.SetsockoptInt(int(fd), syscall.SOL_IP, syscall.IP_TRANSPARENT, 1) 26 | } 27 | if err == nil && isIPv6 { 28 | err = syscall.SetsockoptInt(int(fd), syscall.SOL_IPV6, IPV6_TRANSPARENT, 1) 29 | } 30 | 31 | if err == nil { 32 | err = syscall.SetsockoptInt(int(fd), syscall.SOL_IP, syscall.IP_RECVORIGDSTADDR, 1) 33 | } 34 | if err == nil && isIPv6 { 35 | err = syscall.SetsockoptInt(int(fd), syscall.SOL_IPV6, IPV6_RECVORIGDSTADDR, 1) 36 | } 37 | }) 38 | 39 | return err 40 | } 41 | -------------------------------------------------------------------------------- /clash-meta/listener/tproxy/setsockopt_other.go: -------------------------------------------------------------------------------- 1 | //go:build !linux 2 | 3 | package tproxy 4 | 5 | import ( 6 | "errors" 7 | "syscall" 8 | ) 9 | 10 | func setsockopt(rc syscall.RawConn, addr string) error { 11 | return errors.New("not supported on current platform") 12 | } 13 | -------------------------------------------------------------------------------- /clash-meta/listener/tproxy/udp_other.go: -------------------------------------------------------------------------------- 1 | //go:build !linux 2 | 3 | package tproxy 4 | 5 | import ( 6 | "errors" 7 | "net" 8 | "net/netip" 9 | ) 10 | 11 | func getOrigDst(oob []byte) (netip.AddrPort, error) { 12 | return netip.AddrPort{}, errors.New("UDP redir not supported on current platform") 13 | } 14 | 15 | func dialUDP(network string, lAddr, rAddr netip.AddrPort) (*net.UDPConn, error) { 16 | return nil, errors.New("UDP redir not supported on current platform") 17 | } 18 | -------------------------------------------------------------------------------- /clash-meta/listener/tunnel/packet.go: -------------------------------------------------------------------------------- 1 | package tunnel 2 | 3 | import ( 4 | "net" 5 | 6 | "github.com/Dreamacro/clash/common/pool" 7 | ) 8 | 9 | type packet struct { 10 | pc net.PacketConn 11 | rAddr net.Addr 12 | payload []byte 13 | } 14 | 15 | func (c *packet) Data() []byte { 16 | return c.payload 17 | } 18 | 19 | // WriteBack write UDP packet with source(ip, port) = `addr` 20 | func (c *packet) WriteBack(b []byte, addr net.Addr) (n int, err error) { 21 | return c.pc.WriteTo(b, c.rAddr) 22 | } 23 | 24 | // LocalAddr returns the source IP/Port of UDP Packet 25 | func (c *packet) LocalAddr() net.Addr { 26 | return c.rAddr 27 | } 28 | 29 | func (c *packet) Drop() { 30 | pool.Put(c.payload) 31 | } 32 | 33 | func (c *packet) InAddr() net.Addr { 34 | return c.pc.LocalAddr() 35 | } 36 | -------------------------------------------------------------------------------- /clash-meta/log/level.go: -------------------------------------------------------------------------------- 1 | package log 2 | 3 | import ( 4 | "encoding/json" 5 | "errors" 6 | ) 7 | 8 | // LogLevelMapping is a mapping for LogLevel enum 9 | var LogLevelMapping = map[string]LogLevel{ 10 | ERROR.String(): ERROR, 11 | WARNING.String(): WARNING, 12 | INFO.String(): INFO, 13 | DEBUG.String(): DEBUG, 14 | SILENT.String(): SILENT, 15 | } 16 | 17 | const ( 18 | DEBUG LogLevel = iota 19 | INFO 20 | WARNING 21 | ERROR 22 | SILENT 23 | ) 24 | 25 | type LogLevel int 26 | 27 | // UnmarshalYAML unserialize LogLevel with yaml 28 | func (l *LogLevel) UnmarshalYAML(unmarshal func(any) error) error { 29 | var tp string 30 | unmarshal(&tp) 31 | level, exist := LogLevelMapping[tp] 32 | if !exist { 33 | return errors.New("invalid mode") 34 | } 35 | *l = level 36 | return nil 37 | } 38 | 39 | // UnmarshalJSON unserialize LogLevel with json 40 | func (l *LogLevel) UnmarshalJSON(data []byte) error { 41 | var tp string 42 | json.Unmarshal(data, &tp) 43 | level, exist := LogLevelMapping[tp] 44 | if !exist { 45 | return errors.New("invalid mode") 46 | } 47 | *l = level 48 | return nil 49 | } 50 | 51 | // MarshalJSON serialize LogLevel with json 52 | func (l LogLevel) MarshalJSON() ([]byte, error) { 53 | return json.Marshal(l.String()) 54 | } 55 | 56 | // MarshalYAML serialize LogLevel with yaml 57 | func (l LogLevel) MarshalYAML() (any, error) { 58 | return l.String(), nil 59 | } 60 | 61 | func (l LogLevel) String() string { 62 | switch l { 63 | case INFO: 64 | return "info" 65 | case WARNING: 66 | return "warning" 67 | case ERROR: 68 | return "error" 69 | case DEBUG: 70 | return "debug" 71 | case SILENT: 72 | return "silent" 73 | default: 74 | return "unknown" 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /clash-meta/log/sing.go: -------------------------------------------------------------------------------- 1 | package log 2 | 3 | import ( 4 | "context" 5 | "fmt" 6 | 7 | L "github.com/sagernet/sing/common/logger" 8 | ) 9 | 10 | type singLogger struct{} 11 | 12 | func (l singLogger) TraceContext(ctx context.Context, args ...any) { 13 | Debugln(fmt.Sprint(args...)) 14 | } 15 | 16 | func (l singLogger) DebugContext(ctx context.Context, args ...any) { 17 | Debugln(fmt.Sprint(args...)) 18 | } 19 | 20 | func (l singLogger) InfoContext(ctx context.Context, args ...any) { 21 | Infoln(fmt.Sprint(args...)) 22 | } 23 | 24 | func (l singLogger) WarnContext(ctx context.Context, args ...any) { 25 | Warnln(fmt.Sprint(args...)) 26 | } 27 | 28 | func (l singLogger) ErrorContext(ctx context.Context, args ...any) { 29 | Errorln(fmt.Sprint(args...)) 30 | } 31 | 32 | func (l singLogger) FatalContext(ctx context.Context, args ...any) { 33 | Fatalln(fmt.Sprint(args...)) 34 | } 35 | 36 | func (l singLogger) PanicContext(ctx context.Context, args ...any) { 37 | Fatalln(fmt.Sprint(args...)) 38 | } 39 | 40 | func (l singLogger) Trace(args ...any) { 41 | Debugln(fmt.Sprint(args...)) 42 | } 43 | 44 | func (l singLogger) Debug(args ...any) { 45 | Debugln(fmt.Sprint(args...)) 46 | } 47 | 48 | func (l singLogger) Info(args ...any) { 49 | Infoln(fmt.Sprint(args...)) 50 | } 51 | 52 | func (l singLogger) Warn(args ...any) { 53 | Warnln(fmt.Sprint(args...)) 54 | } 55 | 56 | func (l singLogger) Error(args ...any) { 57 | Errorln(fmt.Sprint(args...)) 58 | } 59 | 60 | func (l singLogger) Fatal(args ...any) { 61 | Fatalln(fmt.Sprint(args...)) 62 | } 63 | 64 | func (l singLogger) Panic(args ...any) { 65 | Fatalln(fmt.Sprint(args...)) 66 | } 67 | 68 | var SingLogger L.ContextLogger = singLogger{} 69 | -------------------------------------------------------------------------------- /clash-meta/rules/common/base.go: -------------------------------------------------------------------------------- 1 | package common 2 | 3 | import ( 4 | "errors" 5 | ) 6 | 7 | var ( 8 | errPayload = errors.New("payloadRule error") 9 | noResolve = "no-resolve" 10 | ) 11 | 12 | type Base struct { 13 | } 14 | 15 | func (b *Base) ShouldFindProcess() bool { 16 | return false 17 | } 18 | 19 | func (b *Base) ShouldResolveIP() bool { 20 | return false 21 | } 22 | 23 | func HasNoResolve(params []string) bool { 24 | for _, p := range params { 25 | if p == noResolve { 26 | return true 27 | } 28 | } 29 | return false 30 | } 31 | -------------------------------------------------------------------------------- /clash-meta/rules/common/domain.go: -------------------------------------------------------------------------------- 1 | package common 2 | 3 | import ( 4 | "strings" 5 | 6 | C "github.com/Dreamacro/clash/constant" 7 | ) 8 | 9 | type Domain struct { 10 | *Base 11 | domain string 12 | adapter string 13 | } 14 | 15 | func (d *Domain) RuleType() C.RuleType { 16 | return C.Domain 17 | } 18 | 19 | func (d *Domain) Match(metadata *C.Metadata) (bool, string) { 20 | return metadata.RuleHost() == d.domain, d.adapter 21 | } 22 | 23 | func (d *Domain) Adapter() string { 24 | return d.adapter 25 | } 26 | 27 | func (d *Domain) Payload() string { 28 | return d.domain 29 | } 30 | 31 | func NewDomain(domain string, adapter string) *Domain { 32 | return &Domain{ 33 | Base: &Base{}, 34 | domain: strings.ToLower(domain), 35 | adapter: adapter, 36 | } 37 | } 38 | 39 | //var _ C.Rule = (*Domain)(nil) 40 | -------------------------------------------------------------------------------- /clash-meta/rules/common/domain_keyword.go: -------------------------------------------------------------------------------- 1 | package common 2 | 3 | import ( 4 | "strings" 5 | 6 | C "github.com/Dreamacro/clash/constant" 7 | ) 8 | 9 | type DomainKeyword struct { 10 | *Base 11 | keyword string 12 | adapter string 13 | } 14 | 15 | func (dk *DomainKeyword) RuleType() C.RuleType { 16 | return C.DomainKeyword 17 | } 18 | 19 | func (dk *DomainKeyword) Match(metadata *C.Metadata) (bool, string) { 20 | domain := metadata.RuleHost() 21 | return strings.Contains(domain, dk.keyword), dk.adapter 22 | } 23 | 24 | func (dk *DomainKeyword) Adapter() string { 25 | return dk.adapter 26 | } 27 | 28 | func (dk *DomainKeyword) Payload() string { 29 | return dk.keyword 30 | } 31 | 32 | func NewDomainKeyword(keyword string, adapter string) *DomainKeyword { 33 | return &DomainKeyword{ 34 | Base: &Base{}, 35 | keyword: strings.ToLower(keyword), 36 | adapter: adapter, 37 | } 38 | } 39 | 40 | //var _ C.Rule = (*DomainKeyword)(nil) 41 | -------------------------------------------------------------------------------- /clash-meta/rules/common/domain_suffix.go: -------------------------------------------------------------------------------- 1 | package common 2 | 3 | import ( 4 | "strings" 5 | 6 | C "github.com/Dreamacro/clash/constant" 7 | ) 8 | 9 | type DomainSuffix struct { 10 | *Base 11 | suffix string 12 | adapter string 13 | } 14 | 15 | func (ds *DomainSuffix) RuleType() C.RuleType { 16 | return C.DomainSuffix 17 | } 18 | 19 | func (ds *DomainSuffix) Match(metadata *C.Metadata) (bool, string) { 20 | domain := metadata.RuleHost() 21 | return strings.HasSuffix(domain, "."+ds.suffix) || domain == ds.suffix, ds.adapter 22 | } 23 | 24 | func (ds *DomainSuffix) Adapter() string { 25 | return ds.adapter 26 | } 27 | 28 | func (ds *DomainSuffix) Payload() string { 29 | return ds.suffix 30 | } 31 | 32 | func NewDomainSuffix(suffix string, adapter string) *DomainSuffix { 33 | return &DomainSuffix{ 34 | Base: &Base{}, 35 | suffix: strings.ToLower(suffix), 36 | adapter: adapter, 37 | } 38 | } 39 | 40 | //var _ C.Rule = (*DomainSuffix)(nil) 41 | -------------------------------------------------------------------------------- /clash-meta/rules/common/final.go: -------------------------------------------------------------------------------- 1 | package common 2 | 3 | import ( 4 | C "github.com/Dreamacro/clash/constant" 5 | ) 6 | 7 | type Match struct { 8 | *Base 9 | adapter string 10 | } 11 | 12 | func (f *Match) RuleType() C.RuleType { 13 | return C.MATCH 14 | } 15 | 16 | func (f *Match) Match(metadata *C.Metadata) (bool, string) { 17 | return true, f.adapter 18 | } 19 | 20 | func (f *Match) Adapter() string { 21 | return f.adapter 22 | } 23 | 24 | func (f *Match) Payload() string { 25 | return "" 26 | } 27 | 28 | func NewMatch(adapter string) *Match { 29 | return &Match{ 30 | Base: &Base{}, 31 | adapter: adapter, 32 | } 33 | } 34 | 35 | //var _ C.Rule = (*Match)(nil) 36 | -------------------------------------------------------------------------------- /clash-meta/rules/common/in_name.go: -------------------------------------------------------------------------------- 1 | package common 2 | 3 | import ( 4 | "fmt" 5 | C "github.com/Dreamacro/clash/constant" 6 | "strings" 7 | ) 8 | 9 | type InName struct { 10 | *Base 11 | names []string 12 | adapter string 13 | payload string 14 | } 15 | 16 | func (u *InName) Match(metadata *C.Metadata) (bool, string) { 17 | for _, name := range u.names { 18 | if metadata.InName == name { 19 | return true, u.adapter 20 | } 21 | } 22 | return false, "" 23 | } 24 | 25 | func (u *InName) RuleType() C.RuleType { 26 | return C.InName 27 | } 28 | 29 | func (u *InName) Adapter() string { 30 | return u.adapter 31 | } 32 | 33 | func (u *InName) Payload() string { 34 | return u.payload 35 | } 36 | 37 | func NewInName(iNames, adapter string) (*InName, error) { 38 | names := strings.Split(iNames, "/") 39 | if len(names) == 0 { 40 | return nil, fmt.Errorf("in name couldn't be empty") 41 | } 42 | 43 | return &InName{ 44 | Base: &Base{}, 45 | names: names, 46 | adapter: adapter, 47 | payload: iNames, 48 | }, nil 49 | } 50 | -------------------------------------------------------------------------------- /clash-meta/rules/common/in_type.go: -------------------------------------------------------------------------------- 1 | package common 2 | 3 | import ( 4 | "fmt" 5 | C "github.com/Dreamacro/clash/constant" 6 | "strings" 7 | ) 8 | 9 | type InType struct { 10 | *Base 11 | types []C.Type 12 | adapter string 13 | payload string 14 | } 15 | 16 | func (u *InType) Match(metadata *C.Metadata) (bool, string) { 17 | for _, tp := range u.types { 18 | if metadata.Type == tp { 19 | return true, u.adapter 20 | } 21 | } 22 | return false, "" 23 | } 24 | 25 | func (u *InType) RuleType() C.RuleType { 26 | return C.InType 27 | } 28 | 29 | func (u *InType) Adapter() string { 30 | return u.adapter 31 | } 32 | 33 | func (u *InType) Payload() string { 34 | return u.payload 35 | } 36 | 37 | func NewInType(iTypes, adapter string) (*InType, error) { 38 | types := strings.Split(iTypes, "/") 39 | if len(types) == 0 { 40 | return nil, fmt.Errorf("in type couldn't be empty") 41 | } 42 | 43 | tps, err := parseInTypes(types) 44 | if err != nil { 45 | return nil, err 46 | } 47 | 48 | return &InType{ 49 | Base: &Base{}, 50 | types: tps, 51 | adapter: adapter, 52 | payload: strings.ToUpper(iTypes), 53 | }, nil 54 | } 55 | 56 | func parseInTypes(tps []string) (res []C.Type, err error) { 57 | for _, tp := range tps { 58 | utp := strings.ToUpper(tp) 59 | var r *C.Type 60 | if utp == "SOCKS" { 61 | r, _ = C.ParseType("SOCKS4") 62 | res = append(res, *r) 63 | r, _ = C.ParseType("SOCKS5") 64 | res = append(res, *r) 65 | } else { 66 | r, err = C.ParseType(utp) 67 | if err != nil { 68 | return 69 | } 70 | res = append(res, *r) 71 | } 72 | } 73 | return 74 | } 75 | -------------------------------------------------------------------------------- /clash-meta/rules/common/in_user.go: -------------------------------------------------------------------------------- 1 | package common 2 | 3 | import ( 4 | "fmt" 5 | C "github.com/Dreamacro/clash/constant" 6 | "strings" 7 | ) 8 | 9 | type InUser struct { 10 | *Base 11 | users []string 12 | adapter string 13 | payload string 14 | } 15 | 16 | func (u *InUser) Match(metadata *C.Metadata) (bool, string) { 17 | for _, user := range u.users { 18 | if metadata.InUser == user { 19 | return true, u.adapter 20 | } 21 | } 22 | return false, "" 23 | } 24 | 25 | func (u *InUser) RuleType() C.RuleType { 26 | return C.InUser 27 | } 28 | 29 | func (u *InUser) Adapter() string { 30 | return u.adapter 31 | } 32 | 33 | func (u *InUser) Payload() string { 34 | return u.payload 35 | } 36 | 37 | func NewInUser(iUsers, adapter string) (*InUser, error) { 38 | users := strings.Split(iUsers, "/") 39 | if len(users) == 0 { 40 | return nil, fmt.Errorf("in user couldn't be empty") 41 | } 42 | 43 | return &InUser{ 44 | Base: &Base{}, 45 | users: users, 46 | adapter: adapter, 47 | payload: iUsers, 48 | }, nil 49 | } 50 | -------------------------------------------------------------------------------- /clash-meta/rules/common/ipcidr.go: -------------------------------------------------------------------------------- 1 | package common 2 | 3 | import ( 4 | "net/netip" 5 | 6 | C "github.com/Dreamacro/clash/constant" 7 | ) 8 | 9 | type IPCIDROption func(*IPCIDR) 10 | 11 | func WithIPCIDRSourceIP(b bool) IPCIDROption { 12 | return func(i *IPCIDR) { 13 | i.isSourceIP = b 14 | } 15 | } 16 | 17 | func WithIPCIDRNoResolve(noResolve bool) IPCIDROption { 18 | return func(i *IPCIDR) { 19 | i.noResolveIP = noResolve 20 | } 21 | } 22 | 23 | type IPCIDR struct { 24 | *Base 25 | ipnet *netip.Prefix 26 | adapter string 27 | isSourceIP bool 28 | noResolveIP bool 29 | } 30 | 31 | func (i *IPCIDR) RuleType() C.RuleType { 32 | if i.isSourceIP { 33 | return C.SrcIPCIDR 34 | } 35 | return C.IPCIDR 36 | } 37 | 38 | func (i *IPCIDR) Match(metadata *C.Metadata) (bool, string) { 39 | ip := metadata.DstIP 40 | if i.isSourceIP { 41 | ip = metadata.SrcIP 42 | } 43 | return ip.IsValid() && i.ipnet.Contains(ip), i.adapter 44 | } 45 | 46 | func (i *IPCIDR) Adapter() string { 47 | return i.adapter 48 | } 49 | 50 | func (i *IPCIDR) Payload() string { 51 | return i.ipnet.String() 52 | } 53 | 54 | func (i *IPCIDR) ShouldResolveIP() bool { 55 | return !i.noResolveIP 56 | } 57 | 58 | func NewIPCIDR(s string, adapter string, opts ...IPCIDROption) (*IPCIDR, error) { 59 | ipnet, err := netip.ParsePrefix(s) 60 | if err != nil { 61 | return nil, errPayload 62 | } 63 | 64 | ipcidr := &IPCIDR{ 65 | Base: &Base{}, 66 | ipnet: &ipnet, 67 | adapter: adapter, 68 | } 69 | 70 | for _, o := range opts { 71 | o(ipcidr) 72 | } 73 | 74 | return ipcidr, nil 75 | } 76 | 77 | //var _ C.Rule = (*IPCIDR)(nil) 78 | -------------------------------------------------------------------------------- /clash-meta/rules/common/network_type.go: -------------------------------------------------------------------------------- 1 | package common 2 | 3 | import ( 4 | "fmt" 5 | C "github.com/Dreamacro/clash/constant" 6 | "strings" 7 | ) 8 | 9 | type NetworkType struct { 10 | *Base 11 | network C.NetWork 12 | adapter string 13 | } 14 | 15 | func NewNetworkType(network, adapter string) (*NetworkType, error) { 16 | ntType := NetworkType{ 17 | Base: &Base{}, 18 | } 19 | 20 | ntType.adapter = adapter 21 | switch strings.ToUpper(network) { 22 | case "TCP": 23 | ntType.network = C.TCP 24 | case "UDP": 25 | ntType.network = C.UDP 26 | default: 27 | return nil, fmt.Errorf("unsupported network type, only TCP/UDP") 28 | } 29 | 30 | return &ntType, nil 31 | } 32 | 33 | func (n *NetworkType) RuleType() C.RuleType { 34 | return C.Network 35 | } 36 | 37 | func (n *NetworkType) Match(metadata *C.Metadata) (bool, string) { 38 | return n.network == metadata.NetWork, n.adapter 39 | } 40 | 41 | func (n *NetworkType) Adapter() string { 42 | return n.adapter 43 | } 44 | 45 | func (n *NetworkType) Payload() string { 46 | return n.network.String() 47 | } 48 | -------------------------------------------------------------------------------- /clash-meta/rules/common/process.go: -------------------------------------------------------------------------------- 1 | package common 2 | 3 | import ( 4 | "strings" 5 | 6 | C "github.com/Dreamacro/clash/constant" 7 | ) 8 | 9 | type Process struct { 10 | *Base 11 | adapter string 12 | process string 13 | nameOnly bool 14 | } 15 | 16 | func (ps *Process) RuleType() C.RuleType { 17 | if ps.nameOnly { 18 | return C.Process 19 | } 20 | 21 | return C.ProcessPath 22 | } 23 | 24 | func (ps *Process) Match(metadata *C.Metadata) (bool, string) { 25 | if ps.nameOnly { 26 | return strings.EqualFold(metadata.Process, ps.process), ps.adapter 27 | } 28 | 29 | return strings.EqualFold(metadata.ProcessPath, ps.process), ps.adapter 30 | } 31 | 32 | func (ps *Process) Adapter() string { 33 | return ps.adapter 34 | } 35 | 36 | func (ps *Process) Payload() string { 37 | return ps.process 38 | } 39 | 40 | func (ps *Process) ShouldFindProcess() bool { 41 | return true 42 | } 43 | 44 | func NewProcess(process string, adapter string, nameOnly bool) (*Process, error) { 45 | return &Process{ 46 | Base: &Base{}, 47 | adapter: adapter, 48 | process: process, 49 | nameOnly: nameOnly, 50 | }, nil 51 | } 52 | -------------------------------------------------------------------------------- /clash-meta/rules/provider/domain_strategy.go: -------------------------------------------------------------------------------- 1 | package provider 2 | 3 | import ( 4 | "github.com/Dreamacro/clash/component/trie" 5 | C "github.com/Dreamacro/clash/constant" 6 | "github.com/Dreamacro/clash/log" 7 | ) 8 | 9 | type domainStrategy struct { 10 | count int 11 | domainTrie *trie.DomainTrie[struct{}] 12 | domainSet *trie.DomainSet 13 | } 14 | 15 | func (d *domainStrategy) ShouldFindProcess() bool { 16 | return false 17 | } 18 | 19 | func (d *domainStrategy) Match(metadata *C.Metadata) bool { 20 | return d.domainSet != nil && d.domainSet.Has(metadata.RuleHost()) 21 | } 22 | 23 | func (d *domainStrategy) Count() int { 24 | return d.count 25 | } 26 | 27 | func (d *domainStrategy) ShouldResolveIP() bool { 28 | return false 29 | } 30 | 31 | func (d *domainStrategy) Reset() { 32 | d.domainTrie = trie.New[struct{}]() 33 | d.domainSet = nil 34 | d.count = 0 35 | } 36 | 37 | func (d *domainStrategy) Insert(rule string) { 38 | err := d.domainTrie.Insert(rule, struct{}{}) 39 | if err != nil { 40 | log.Warnln("invalid domain:[%s]", rule) 41 | } else { 42 | d.count++ 43 | } 44 | } 45 | 46 | func (d *domainStrategy) FinishInsert() { 47 | d.domainSet = d.domainTrie.NewDomainSet() 48 | d.domainTrie = nil 49 | } 50 | 51 | func NewDomainStrategy() *domainStrategy { 52 | return &domainStrategy{} 53 | } 54 | -------------------------------------------------------------------------------- /clash-meta/rules/provider/ipcidr_strategy.go: -------------------------------------------------------------------------------- 1 | package provider 2 | 3 | import ( 4 | "github.com/Dreamacro/clash/component/trie" 5 | C "github.com/Dreamacro/clash/constant" 6 | "github.com/Dreamacro/clash/log" 7 | ) 8 | 9 | type ipcidrStrategy struct { 10 | count int 11 | shouldResolveIP bool 12 | trie *trie.IpCidrTrie 13 | } 14 | 15 | func (i *ipcidrStrategy) ShouldFindProcess() bool { 16 | return false 17 | } 18 | 19 | func (i *ipcidrStrategy) Match(metadata *C.Metadata) bool { 20 | return i.trie != nil && i.trie.IsContain(metadata.DstIP.AsSlice()) 21 | } 22 | 23 | func (i *ipcidrStrategy) Count() int { 24 | return i.count 25 | } 26 | 27 | func (i *ipcidrStrategy) ShouldResolveIP() bool { 28 | return i.shouldResolveIP 29 | } 30 | 31 | func (i *ipcidrStrategy) Reset() { 32 | i.trie = trie.NewIpCidrTrie() 33 | i.count = 0 34 | i.shouldResolveIP = false 35 | } 36 | 37 | func (i *ipcidrStrategy) Insert(rule string) { 38 | err := i.trie.AddIpCidrForString(rule) 39 | if err != nil { 40 | log.Warnln("invalid Ipcidr:[%s]", rule) 41 | } else { 42 | i.shouldResolveIP = true 43 | i.count++ 44 | } 45 | } 46 | 47 | func (i *ipcidrStrategy) FinishInsert() {} 48 | 49 | func NewIPCidrStrategy() *ipcidrStrategy { 50 | return &ipcidrStrategy{} 51 | } 52 | -------------------------------------------------------------------------------- /clash-meta/test/.golangci.yaml: -------------------------------------------------------------------------------- 1 | linters: 2 | disable-all: true 3 | enable: 4 | - gofumpt 5 | - govet 6 | - gci 7 | - staticcheck 8 | 9 | linters-settings: 10 | gci: 11 | sections: 12 | - standard 13 | - prefix(github.com/Dreamacro/clash) 14 | - default 15 | staticcheck: 16 | go: '1.19' 17 | -------------------------------------------------------------------------------- /clash-meta/test/Makefile: -------------------------------------------------------------------------------- 1 | lint: 2 | GOOS=darwin golangci-lint run ./... 3 | GOOS=linux golangci-lint run ./... 4 | 5 | test: 6 | go test -p 1 -v ./... 7 | 8 | benchmark: 9 | go test -benchmem -run=^$$ -bench . 10 | -------------------------------------------------------------------------------- /clash-meta/test/README.md: -------------------------------------------------------------------------------- 1 | ## Clash testing suit 2 | 3 | ### Protocol testing suit 4 | 5 | * TCP pingpong test 6 | * UDP pingpong test 7 | * TCP large data test 8 | * UDP large data test 9 | 10 | ### Protocols 11 | 12 | - [x] Shadowsocks 13 | - [x] Normal 14 | - [x] ObfsHTTP 15 | - [x] ObfsTLS 16 | - [x] ObfsV2rayPlugin 17 | - [x] Vmess 18 | - [x] Normal 19 | - [x] AEAD 20 | - [x] HTTP 21 | - [x] HTTP2 22 | - [x] TLS 23 | - [x] Websocket 24 | - [x] Websocket TLS 25 | - [x] gRPC 26 | - [x] Trojan 27 | - [x] Normal 28 | - [x] gRPC 29 | - [x] Snell 30 | - [x] Normal 31 | - [x] ObfsHTTP 32 | - [x] ObfsTLS 33 | 34 | ### Features 35 | 36 | - [ ] DNS 37 | - [x] DNS Server 38 | - [x] FakeIP 39 | - [x] Host 40 | 41 | ### Command 42 | 43 | Prerequisite 44 | 45 | * docker (support Linux and macOS) 46 | 47 | ``` 48 | $ make test 49 | ``` 50 | 51 | benchmark (Linux) 52 | 53 | > Cannot represent the throughput of the protocol on your machine 54 | > but you can compare the corresponding throughput of the protocol on clash 55 | > (change chunkSize to measure the maximum throughput of clash on your machine) 56 | 57 | ``` 58 | $ make benchmark 59 | ``` 60 | -------------------------------------------------------------------------------- /clash-meta/test/config/example.org.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIIESzCCArOgAwIBAgIQIi5xRZvFZaSweWU9Y5mExjANBgkqhkiG9w0BAQsFADCB 3 | hzEeMBwGA1UEChMVbWtjZXJ0IGRldmVsb3BtZW50IENBMS4wLAYDVQQLDCVkcmVh 4 | bWFjcm9ARHJlYW1hY3JvLmxvY2FsIChEcmVhbWFjcm8pMTUwMwYDVQQDDCxta2Nl 5 | cnQgZHJlYW1hY3JvQERyZWFtYWNyby5sb2NhbCAoRHJlYW1hY3JvKTAeFw0yMTAz 6 | MTcxNDQwMzZaFw0yMzA2MTcxNDQwMzZaMFkxJzAlBgNVBAoTHm1rY2VydCBkZXZl 7 | bG9wbWVudCBjZXJ0aWZpY2F0ZTEuMCwGA1UECwwlZHJlYW1hY3JvQERyZWFtYWNy 8 | by5sb2NhbCAoRHJlYW1hY3JvKTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoC 9 | ggEBAND5z74uQNN1rDnmykK70xYxy9V2tgFlnmpnLsOySmFJRJCnblwi7fgYldU8 10 | 3G1tbHUJAKZ0uXCpe+g99S3n+nP2qx+F0BvMV25lCAZ3sZNA5MkEOsCGbb6JWxDI 11 | YceEXrya435KBP4C1ESwnJmgc1i2ONYQgdD74UcQOrBRWePpS330seXJ8KM5nn5b 12 | IGIoKPIw2i2z+AN6ntGLhfaQxOo0wgio9RwBo/kRCTjVslsWEp73Xmo31vV1EhIo 13 | UqaJZ46zGBMH878RovkuwFjBn8GIgwOTfRpm/NEl6WhqwhHfPiGspCoF8HSV94h2 14 | I+kkizmtclASJYfz2B6CmWwtt+sCAwEAAaNgMF4wDgYDVR0PAQH/BAQDAgWgMBMG 15 | A1UdJQQMMAoGCCsGAQUFBwMBMB8GA1UdIwQYMBaAFO800LQ6Pa85RH4EbMmFH6ln 16 | F150MBYGA1UdEQQPMA2CC2V4YW1wbGUub3JnMA0GCSqGSIb3DQEBCwUAA4IBgQAP 17 | TsF53h7bvJcUXT3Y9yZ2vnW6xr9r92tNnM1Gfo3D2Yyn9oLf2YrfJng6WZ04Fhqa 18 | Wh0HOvE0n6yPNpm/Q7mh64DrgolZ8Ce5H4RTJDAabHU9XhEzfGSVtzRSFsz+szu1 19 | Y30IV+08DxxqMmNPspYdpAET2Lwyk2WhnARGiGw11CRkQCEkVEe6d702vS9UGBUz 20 | Du6lmCYCm0SbFrZ0CGgmHSHoTcCtf3EjVam7dPg3yWiPbWjvhXxgip6hz9sCqkhG 21 | WA5f+fPgSZ1I9U4i+uYnqjfrzwgC08RwUYordm15F6gPvXw+KVwDO8yUYQoEH0b6 22 | AFJtbzoAXDysvBC6kWYFFOr62EaisaEkELTS/NrPD9ux1eKbxcxHCwEtVjgC0CL6 23 | gAxEAQ+9maJMbrAFhsOBbGGFC+mMCGg4eEyx6+iMB0oQe0W7QFeRUAFi7Ptc/ocS 24 | tZ9lbrfX1/wrcTTWIYWE+xH6oeb4fhs29kxjHcf2l+tQzmpl0aP3Z/bMW4BSB+w= 25 | -----END CERTIFICATE----- 26 | -------------------------------------------------------------------------------- /clash-meta/test/config/hysteria.json: -------------------------------------------------------------------------------- 1 | { 2 | "listen": ":10002", 3 | "cert": "/home/ubuntu/my.crt", 4 | "key": "/home/ubuntu/my.key", 5 | "obfs": "fuck me till the daylight", 6 | "up_mbps": 100, 7 | "down_mbps": 100 8 | } -------------------------------------------------------------------------------- /clash-meta/test/config/snell-http.conf: -------------------------------------------------------------------------------- 1 | [snell-server] 2 | listen = 0.0.0.0:10002 3 | psk = password 4 | obfs = http 5 | -------------------------------------------------------------------------------- /clash-meta/test/config/snell-tls.conf: -------------------------------------------------------------------------------- 1 | [snell-server] 2 | listen = 0.0.0.0:10002 3 | psk = password 4 | obfs = tls 5 | -------------------------------------------------------------------------------- /clash-meta/test/config/snell.conf: -------------------------------------------------------------------------------- 1 | [snell-server] 2 | listen = 0.0.0.0:10002 3 | psk = password 4 | -------------------------------------------------------------------------------- /clash-meta/test/config/trojan-grpc.json: -------------------------------------------------------------------------------- 1 | { 2 | "inbounds": [ 3 | { 4 | "port": 10002, 5 | "listen": "0.0.0.0", 6 | "protocol": "trojan", 7 | "settings": { 8 | "clients": [ 9 | { 10 | "password": "example", 11 | "email": "grpc@example.com" 12 | } 13 | ] 14 | }, 15 | "streamSettings": { 16 | "network": "grpc", 17 | "security": "tls", 18 | "tlsSettings": { 19 | "certificates": [ 20 | { 21 | "certificateFile": "/etc/ssl/v2ray/fullchain.pem", 22 | "keyFile": "/etc/ssl/v2ray/privkey.pem" 23 | } 24 | ] 25 | }, 26 | "grpcSettings": { 27 | "serviceName": "example" 28 | } 29 | } 30 | } 31 | ], 32 | "outbounds": [ 33 | { 34 | "protocol": "freedom" 35 | } 36 | ], 37 | "log": { 38 | "loglevel": "debug" 39 | } 40 | } -------------------------------------------------------------------------------- /clash-meta/test/config/trojan-ws.json: -------------------------------------------------------------------------------- 1 | { 2 | "run_type": "server", 3 | "local_addr": "0.0.0.0", 4 | "local_port": 10002, 5 | "disable_http_check": true, 6 | "password": [ 7 | "example" 8 | ], 9 | "websocket": { 10 | "enabled": true, 11 | "path": "/", 12 | "host": "example.org" 13 | }, 14 | "ssl": { 15 | "verify": true, 16 | "cert": "/fullchain.pem", 17 | "key": "/privkey.pem", 18 | "sni": "example.org" 19 | } 20 | } -------------------------------------------------------------------------------- /clash-meta/test/config/trojan-xtls.json: -------------------------------------------------------------------------------- 1 | { 2 | "inbounds": [ 3 | { 4 | "port": 10002, 5 | "listen": "0.0.0.0", 6 | "protocol": "trojan", 7 | "settings": { 8 | "clients": [ 9 | { 10 | "password": "example", 11 | "email": "xtls@example.com", 12 | "flow": "xtls-rprx-direct", 13 | "level": 0 14 | } 15 | ] 16 | }, 17 | "streamSettings": { 18 | "network": "tcp", 19 | "security": "xtls", 20 | "xtlsSettings": { 21 | "certificates": [ 22 | { 23 | "certificateFile": "/etc/ssl/v2ray/fullchain.pem", 24 | "keyFile": "/etc/ssl/v2ray/privkey.pem" 25 | } 26 | ] 27 | } 28 | } 29 | } 30 | ], 31 | "outbounds": [ 32 | { 33 | "protocol": "freedom" 34 | } 35 | ], 36 | "log": { 37 | "loglevel": "debug" 38 | } 39 | } -------------------------------------------------------------------------------- /clash-meta/test/config/trojan.json: -------------------------------------------------------------------------------- 1 | { 2 | "run_type": "server", 3 | "local_addr": "0.0.0.0", 4 | "local_port": 10002, 5 | "password": [ 6 | "password" 7 | ], 8 | "log_level": 1, 9 | "ssl": { 10 | "cert": "/path/to/certificate.crt", 11 | "key": "/path/to/private.key", 12 | "key_password": "", 13 | "cipher": "ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384", 14 | "cipher_tls13": "TLS_AES_128_GCM_SHA256:TLS_CHACHA20_POLY1305_SHA256:TLS_AES_256_GCM_SHA384", 15 | "prefer_server_cipher": true, 16 | "alpn": [ 17 | "http/1.1" 18 | ], 19 | "alpn_port_override": { 20 | "h2": 81 21 | }, 22 | "reuse_session": true, 23 | "session_ticket": false, 24 | "session_timeout": 600, 25 | "plain_http_response": "", 26 | "curves": "", 27 | "dhparam": "" 28 | }, 29 | "tcp": { 30 | "prefer_ipv4": false, 31 | "no_delay": true, 32 | "keep_alive": true, 33 | "reuse_port": false, 34 | "fast_open": false, 35 | "fast_open_qlen": 20 36 | }, 37 | "mysql": { 38 | "enabled": false 39 | } 40 | } -------------------------------------------------------------------------------- /clash-meta/test/config/vless-tls.json: -------------------------------------------------------------------------------- 1 | { 2 | "inbounds": [ 3 | { 4 | "port": 10002, 5 | "listen": "0.0.0.0", 6 | "protocol": "vless", 7 | "settings": { 8 | "clients": [ 9 | { 10 | "id": "b831381d-6324-4d53-ad4f-8cda48b30811", 11 | "level": 0, 12 | "email": "love@example.com" 13 | } 14 | ], 15 | "decryption": "none" 16 | }, 17 | "streamSettings": { 18 | "network": "tcp", 19 | "security": "tls", 20 | "tlsSettings": { 21 | "certificates": [ 22 | { 23 | "certificateFile": "/etc/ssl/v2ray/fullchain.pem", 24 | "keyFile": "/etc/ssl/v2ray/privkey.pem" 25 | } 26 | ] 27 | } 28 | } 29 | } 30 | ], 31 | "outbounds": [ 32 | { 33 | "protocol": "freedom" 34 | } 35 | ], 36 | "log": { 37 | "loglevel": "debug" 38 | } 39 | } -------------------------------------------------------------------------------- /clash-meta/test/config/vless-ws.json: -------------------------------------------------------------------------------- 1 | { 2 | "inbounds": [ 3 | { 4 | "port": 10002, 5 | "listen": "0.0.0.0", 6 | "protocol": "vless", 7 | "settings": { 8 | "clients": [ 9 | { 10 | "id": "b831381d-6324-4d53-ad4f-8cda48b30811", 11 | "level": 0, 12 | "email": "ws@example.com" 13 | } 14 | ], 15 | "decryption": "none" 16 | }, 17 | "streamSettings": { 18 | "network": "ws", 19 | "security": "tls", 20 | "tlsSettings": { 21 | "certificates": [ 22 | { 23 | "certificateFile": "/etc/ssl/v2ray/fullchain.pem", 24 | "keyFile": "/etc/ssl/v2ray/privkey.pem" 25 | } 26 | ] 27 | } 28 | } 29 | } 30 | ], 31 | "outbounds": [ 32 | { 33 | "protocol": "freedom" 34 | } 35 | ] 36 | } -------------------------------------------------------------------------------- /clash-meta/test/config/vless-xtls.json: -------------------------------------------------------------------------------- 1 | { 2 | "inbounds": [ 3 | { 4 | "port": 10002, 5 | "listen": "0.0.0.0", 6 | "protocol": "vless", 7 | "settings": { 8 | "clients": [ 9 | { 10 | "id": "b831381d-6324-4d53-ad4f-8cda48b30811", 11 | "email": "xtls@example.com", 12 | "flow": "xtls-rprx-direct", 13 | "level": 0 14 | } 15 | ], 16 | "decryption": "none" 17 | }, 18 | "streamSettings": { 19 | "network": "tcp", 20 | "security": "xtls", 21 | "xtlsSettings": { 22 | "certificates": [ 23 | { 24 | "certificateFile": "/etc/ssl/v2ray/fullchain.pem", 25 | "keyFile": "/etc/ssl/v2ray/privkey.pem" 26 | } 27 | ] 28 | } 29 | } 30 | } 31 | ], 32 | "outbounds": [ 33 | { 34 | "protocol": "freedom" 35 | } 36 | ], 37 | "log": { 38 | "loglevel": "debug" 39 | } 40 | } -------------------------------------------------------------------------------- /clash-meta/test/config/vmess-grpc.json: -------------------------------------------------------------------------------- 1 | { 2 | "inbounds": [ 3 | { 4 | "port": 10002, 5 | "listen": "0.0.0.0", 6 | "protocol": "vmess", 7 | "settings": { 8 | "clients": [ 9 | { 10 | "id": "b831381d-6324-4d53-ad4f-8cda48b30811" 11 | } 12 | ] 13 | }, 14 | "streamSettings": { 15 | "network": "grpc", 16 | "security": "tls", 17 | "tlsSettings": { 18 | "certificates": [ 19 | { 20 | "certificateFile": "/etc/ssl/v2ray/fullchain.pem", 21 | "keyFile": "/etc/ssl/v2ray/privkey.pem" 22 | } 23 | ] 24 | }, 25 | "grpcSettings": { 26 | "serviceName": "example!" 27 | } 28 | } 29 | } 30 | ], 31 | "outbounds": [ 32 | { 33 | "protocol": "freedom" 34 | } 35 | ], 36 | "log": { 37 | "loglevel": "debug" 38 | } 39 | } -------------------------------------------------------------------------------- /clash-meta/test/config/vmess-http2.json: -------------------------------------------------------------------------------- 1 | { 2 | "inbounds": [ 3 | { 4 | "port": 10002, 5 | "listen": "0.0.0.0", 6 | "protocol": "vmess", 7 | "settings": { 8 | "clients": [ 9 | { 10 | "id": "b831381d-6324-4d53-ad4f-8cda48b30811" 11 | } 12 | ] 13 | }, 14 | "streamSettings": { 15 | "network": "http", 16 | "security": "tls", 17 | "tlsSettings": { 18 | "certificates": [ 19 | { 20 | "certificateFile": "/etc/ssl/v2ray/fullchain.pem", 21 | "keyFile": "/etc/ssl/v2ray/privkey.pem" 22 | } 23 | ] 24 | }, 25 | "httpSettings": { 26 | "host": [ 27 | "example.org" 28 | ], 29 | "path": "/test" 30 | } 31 | } 32 | } 33 | ], 34 | "outbounds": [ 35 | { 36 | "protocol": "freedom" 37 | } 38 | ], 39 | "log": { 40 | "loglevel": "debug" 41 | } 42 | } -------------------------------------------------------------------------------- /clash-meta/test/config/vmess-tls.json: -------------------------------------------------------------------------------- 1 | { 2 | "inbounds": [ 3 | { 4 | "port": 10002, 5 | "listen": "0.0.0.0", 6 | "protocol": "vmess", 7 | "settings": { 8 | "clients": [ 9 | { 10 | "id": "b831381d-6324-4d53-ad4f-8cda48b30811" 11 | } 12 | ] 13 | }, 14 | "streamSettings": { 15 | "network": "tcp", 16 | "security": "tls", 17 | "tlsSettings": { 18 | "certificates": [ 19 | { 20 | "certificateFile": "/etc/ssl/v2ray/fullchain.pem", 21 | "keyFile": "/etc/ssl/v2ray/privkey.pem" 22 | } 23 | ] 24 | } 25 | } 26 | } 27 | ], 28 | "outbounds": [ 29 | { 30 | "protocol": "freedom" 31 | } 32 | ], 33 | "log": { 34 | "loglevel": "debug" 35 | } 36 | } -------------------------------------------------------------------------------- /clash-meta/test/config/vmess-ws-0rtt.json: -------------------------------------------------------------------------------- 1 | { 2 | "inbounds": [ 3 | { 4 | "port": 10002, 5 | "listen": "0.0.0.0", 6 | "protocol": "vmess", 7 | "settings": { 8 | "clients": [ 9 | { 10 | "id": "b831381d-6324-4d53-ad4f-8cda48b30811" 11 | } 12 | ] 13 | }, 14 | "streamSettings": { 15 | "network": "ws", 16 | "security": "none", 17 | "wsSettings": { 18 | "maxEarlyData": 128, 19 | "earlyDataHeaderName": "Sec-WebSocket-Protocol" 20 | } 21 | } 22 | } 23 | ], 24 | "outbounds": [ 25 | { 26 | "protocol": "freedom" 27 | } 28 | ] 29 | } -------------------------------------------------------------------------------- /clash-meta/test/config/vmess-ws-tls.json: -------------------------------------------------------------------------------- 1 | { 2 | "inbounds": [ 3 | { 4 | "port": 10002, 5 | "listen": "0.0.0.0", 6 | "protocol": "vmess", 7 | "settings": { 8 | "clients": [ 9 | { 10 | "id": "b831381d-6324-4d53-ad4f-8cda48b30811" 11 | } 12 | ] 13 | }, 14 | "streamSettings": { 15 | "network": "ws", 16 | "security": "tls", 17 | "tlsSettings": { 18 | "certificates": [ 19 | { 20 | "certificateFile": "/etc/ssl/v2ray/fullchain.pem", 21 | "keyFile": "/etc/ssl/v2ray/privkey.pem" 22 | } 23 | ] 24 | } 25 | } 26 | } 27 | ], 28 | "outbounds": [ 29 | { 30 | "protocol": "freedom" 31 | } 32 | ] 33 | } -------------------------------------------------------------------------------- /clash-meta/test/config/vmess-ws.json: -------------------------------------------------------------------------------- 1 | { 2 | "inbounds": [ 3 | { 4 | "port": 10002, 5 | "listen": "0.0.0.0", 6 | "protocol": "vmess", 7 | "settings": { 8 | "clients": [ 9 | { 10 | "id": "b831381d-6324-4d53-ad4f-8cda48b30811" 11 | } 12 | ] 13 | }, 14 | "streamSettings": { 15 | "network": "ws", 16 | "security": "none" 17 | } 18 | } 19 | ], 20 | "outbounds": [ 21 | { 22 | "protocol": "freedom" 23 | } 24 | ] 25 | } -------------------------------------------------------------------------------- /clash-meta/test/config/vmess.json: -------------------------------------------------------------------------------- 1 | { 2 | "inbounds": [ 3 | { 4 | "port": 10002, 5 | "listen": "0.0.0.0", 6 | "protocol": "vmess", 7 | "settings": { 8 | "clients": [ 9 | { 10 | "id": "b831381d-6324-4d53-ad4f-8cda48b30811" 11 | } 12 | ] 13 | }, 14 | "streamSettings": { 15 | "network": "tcp" 16 | } 17 | } 18 | ], 19 | "outbounds": [ 20 | { 21 | "protocol": "freedom" 22 | } 23 | ], 24 | "log": { 25 | "loglevel": "debug" 26 | } 27 | } -------------------------------------------------------------------------------- /clash-meta/test/config/xray-shadowsocks.json: -------------------------------------------------------------------------------- 1 | { 2 | "inbounds": [ 3 | { 4 | "port": 10002, 5 | "listen": "0.0.0.0", 6 | "protocol": "shadowsocks", 7 | "settings": { 8 | "network": "tcp,udp", 9 | "clients": [ 10 | { 11 | "method": "aes-128-gcm", 12 | "level": 0, 13 | "password": "FzcLbKs2dY9mhL" 14 | } 15 | ] 16 | } 17 | } 18 | ], 19 | "outbounds": [ 20 | { 21 | "protocol": "freedom" 22 | } 23 | ], 24 | "log": { 25 | "loglevel": "debug" 26 | } 27 | } -------------------------------------------------------------------------------- /clash-meta/test/docker_test.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "context" 5 | "os" 6 | 7 | "github.com/docker/docker/api/types" 8 | "github.com/docker/docker/api/types/container" 9 | "github.com/docker/docker/client" 10 | ) 11 | 12 | func startContainer(cfg *container.Config, hostCfg *container.HostConfig, name string) (string, error) { 13 | c, err := client.NewClientWithOpts(client.FromEnv, client.WithAPIVersionNegotiation()) 14 | if err != nil { 15 | return "", err 16 | } 17 | defer c.Close() 18 | 19 | if !isDarwin { 20 | hostCfg.NetworkMode = "host" 21 | } 22 | 23 | container, err := c.ContainerCreate(context.Background(), cfg, hostCfg, nil, nil, name) 24 | if err != nil { 25 | return "", err 26 | } 27 | 28 | if err = c.ContainerStart(context.Background(), container.ID, types.ContainerStartOptions{}); err != nil { 29 | return "", err 30 | } 31 | 32 | response, err := c.ContainerAttach(context.Background(), container.ID, types.ContainerAttachOptions{ 33 | Stdout: true, 34 | Stderr: true, 35 | Logs: true, 36 | }) 37 | if err != nil { 38 | return "", err 39 | } 40 | 41 | go func() { 42 | response.Reader.WriteTo(os.Stderr) 43 | }() 44 | 45 | return container.ID, nil 46 | } 47 | 48 | func cleanContainer(id string) error { 49 | c, err := client.NewClientWithOpts(client.FromEnv, client.WithAPIVersionNegotiation()) 50 | if err != nil { 51 | return err 52 | } 53 | defer c.Close() 54 | 55 | removeOpts := types.ContainerRemoveOptions{Force: true} 56 | return c.ContainerRemove(context.Background(), id, removeOpts) 57 | } 58 | -------------------------------------------------------------------------------- /clash-meta/test/hysteria_test.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "testing" 6 | "time" 7 | 8 | "github.com/Dreamacro/clash/adapter/outbound" 9 | C "github.com/Dreamacro/clash/constant" 10 | "github.com/docker/docker/api/types/container" 11 | "github.com/stretchr/testify/assert" 12 | ) 13 | 14 | func TestClash_Hysteria(t *testing.T) { 15 | cfg := &container.Config{ 16 | Image: ImageHysteria, 17 | ExposedPorts: defaultExposedPorts, 18 | Cmd: []string{"server"}, 19 | } 20 | hostCfg := &container.HostConfig{ 21 | PortBindings: defaultPortBindings, 22 | Binds: []string{ 23 | fmt.Sprintf("%s:/config.json", C.Path.Resolve("hysteria.json")), 24 | fmt.Sprintf("%s:/home/ubuntu/my.crt", C.Path.Resolve("example.org.pem")), 25 | fmt.Sprintf("%s:/home/ubuntu/my.key", C.Path.Resolve("example.org-key.pem")), 26 | }, 27 | } 28 | 29 | id, err := startContainer(cfg, hostCfg, "hysteria") 30 | if err != nil { 31 | assert.FailNow(t, err.Error()) 32 | } 33 | 34 | t.Cleanup(func() { 35 | cleanContainer(id) 36 | }) 37 | 38 | proxy, err := outbound.NewHysteria(outbound.HysteriaOption{ 39 | Name: "hysteria", 40 | Server: localIP.String(), 41 | Port: 10002, 42 | Obfs: "fuck me till the daylight", 43 | Up: "100", 44 | Down: "100", 45 | SkipCertVerify: true, 46 | }) 47 | if err != nil { 48 | assert.FailNow(t, err.Error()) 49 | } 50 | 51 | time.Sleep(waitTime) 52 | testSuit(t, proxy) 53 | } 54 | -------------------------------------------------------------------------------- /clash-meta/test/util.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "context" 5 | "net" 6 | "time" 7 | ) 8 | 9 | func Listen(network, address string) (net.Listener, error) { 10 | lc := net.ListenConfig{} 11 | 12 | var lastErr error 13 | for i := 0; i < 5; i++ { 14 | l, err := lc.Listen(context.Background(), network, address) 15 | if err == nil { 16 | return l, nil 17 | } 18 | 19 | lastErr = err 20 | time.Sleep(time.Millisecond * 200) 21 | } 22 | return nil, lastErr 23 | } 24 | 25 | func ListenPacket(network, address string) (net.PacketConn, error) { 26 | var lastErr error 27 | for i := 0; i < 5; i++ { 28 | l, err := net.ListenPacket(network, address) 29 | if err == nil { 30 | return l, nil 31 | } 32 | 33 | lastErr = err 34 | time.Sleep(time.Millisecond * 200) 35 | } 36 | return nil, lastErr 37 | } 38 | 39 | func TCPing(addr string) bool { 40 | for i := 0; i < 10; i++ { 41 | conn, err := net.Dial("tcp", addr) 42 | if err == nil { 43 | conn.Close() 44 | return true 45 | } 46 | time.Sleep(time.Millisecond * 500) 47 | } 48 | 49 | return false 50 | } 51 | -------------------------------------------------------------------------------- /clash-meta/test/util_other_test.go: -------------------------------------------------------------------------------- 1 | //go:build !darwin 2 | 3 | package main 4 | 5 | import ( 6 | "errors" 7 | "net/netip" 8 | ) 9 | 10 | func defaultRouteIP() (netip.Addr, error) { 11 | return netip.Addr{}, errors.New("not supported") 12 | } 13 | -------------------------------------------------------------------------------- /clash-meta/transport/gun/transport.go: -------------------------------------------------------------------------------- 1 | package gun 2 | 3 | import ( 4 | "golang.org/x/net/http2" 5 | "net" 6 | ) 7 | 8 | type TransportWrap struct { 9 | *http2.Transport 10 | remoteAddr net.Addr 11 | localAddr net.Addr 12 | } 13 | 14 | func (tw *TransportWrap) RemoteAddr() net.Addr { 15 | return tw.remoteAddr 16 | } 17 | 18 | func (tw *TransportWrap) LocalAddr() net.Addr { 19 | return tw.localAddr 20 | } 21 | -------------------------------------------------------------------------------- /clash-meta/transport/gun/utils.go: -------------------------------------------------------------------------------- 1 | package gun 2 | 3 | func UVarintLen(x uint64) int { 4 | i := 0 5 | for x >= 0x80 { 6 | x >>= 7 7 | i++ 8 | } 9 | return i + 1 10 | } 11 | -------------------------------------------------------------------------------- /clash-meta/transport/hysteria/conns/faketcp/LICENSE: -------------------------------------------------------------------------------- 1 | Grabbed from https://github.com/xtaci/tcpraw with modifications -------------------------------------------------------------------------------- /clash-meta/transport/hysteria/conns/faketcp/tcp_stub.go: -------------------------------------------------------------------------------- 1 | //go:build !linux || no_fake_tcp 2 | // +build !linux no_fake_tcp 3 | 4 | package faketcp 5 | 6 | import ( 7 | "errors" 8 | "net" 9 | ) 10 | 11 | type TCPConn struct{ *net.UDPConn } 12 | 13 | // Dial connects to the remote TCP port, 14 | // and returns a single packet-oriented connection 15 | func Dial(network, address string) (*TCPConn, error) { 16 | return nil, errors.New("faketcp is not supported on this platform") 17 | } 18 | 19 | func Listen(network, address string) (*TCPConn, error) { 20 | return nil, errors.New("faketcp is not supported on this platform") 21 | } 22 | -------------------------------------------------------------------------------- /clash-meta/transport/hysteria/core/stream.go: -------------------------------------------------------------------------------- 1 | package core 2 | 3 | import ( 4 | "context" 5 | "github.com/metacubex/quic-go" 6 | "time" 7 | ) 8 | 9 | // Handle stream close properly 10 | // Ref: https://github.com/libp2p/go-libp2p-quic-transport/blob/master/stream.go 11 | type wrappedQUICStream struct { 12 | Stream quic.Stream 13 | } 14 | 15 | func (s *wrappedQUICStream) StreamID() quic.StreamID { 16 | return s.Stream.StreamID() 17 | } 18 | 19 | func (s *wrappedQUICStream) Read(p []byte) (n int, err error) { 20 | return s.Stream.Read(p) 21 | } 22 | 23 | func (s *wrappedQUICStream) CancelRead(code quic.StreamErrorCode) { 24 | s.Stream.CancelRead(code) 25 | } 26 | 27 | func (s *wrappedQUICStream) SetReadDeadline(t time.Time) error { 28 | return s.Stream.SetReadDeadline(t) 29 | } 30 | 31 | func (s *wrappedQUICStream) Write(p []byte) (n int, err error) { 32 | return s.Stream.Write(p) 33 | } 34 | 35 | func (s *wrappedQUICStream) Close() error { 36 | s.Stream.CancelRead(0) 37 | return s.Stream.Close() 38 | } 39 | 40 | func (s *wrappedQUICStream) CancelWrite(code quic.StreamErrorCode) { 41 | s.Stream.CancelWrite(code) 42 | } 43 | 44 | func (s *wrappedQUICStream) Context() context.Context { 45 | return s.Stream.Context() 46 | } 47 | 48 | func (s *wrappedQUICStream) SetWriteDeadline(t time.Time) error { 49 | return s.Stream.SetWriteDeadline(t) 50 | } 51 | 52 | func (s *wrappedQUICStream) SetDeadline(t time.Time) error { 53 | return s.Stream.SetDeadline(t) 54 | } 55 | -------------------------------------------------------------------------------- /clash-meta/transport/hysteria/obfs/dummy.go: -------------------------------------------------------------------------------- 1 | package obfs 2 | 3 | type DummyObfuscator struct{} 4 | 5 | func NewDummyObfuscator() *DummyObfuscator { 6 | return &DummyObfuscator{} 7 | } 8 | 9 | func (x *DummyObfuscator) Deobfuscate(in []byte, out []byte) int { 10 | if len(out) < len(in) { 11 | return 0 12 | } 13 | return copy(out, in) 14 | } 15 | 16 | func (x *DummyObfuscator) Obfuscate(in []byte, out []byte) int { 17 | return copy(out, in) 18 | } 19 | -------------------------------------------------------------------------------- /clash-meta/transport/hysteria/obfs/obfs.go: -------------------------------------------------------------------------------- 1 | package obfs 2 | 3 | type Obfuscator interface { 4 | Deobfuscate(in []byte, out []byte) int 5 | Obfuscate(in []byte, out []byte) int 6 | } 7 | -------------------------------------------------------------------------------- /clash-meta/transport/hysteria/obfs/xplus.go: -------------------------------------------------------------------------------- 1 | package obfs 2 | 3 | import ( 4 | "crypto/sha256" 5 | 6 | "github.com/zhangyunhao116/fastrand" 7 | ) 8 | 9 | // [salt][obfuscated payload] 10 | 11 | const saltLen = 16 12 | 13 | type XPlusObfuscator struct { 14 | Key []byte 15 | } 16 | 17 | func NewXPlusObfuscator(key []byte) *XPlusObfuscator { 18 | return &XPlusObfuscator{ 19 | Key: key, 20 | } 21 | } 22 | 23 | func (x *XPlusObfuscator) Deobfuscate(in []byte, out []byte) int { 24 | pLen := len(in) - saltLen 25 | if pLen <= 0 || len(out) < pLen { 26 | // Invalid 27 | return 0 28 | } 29 | key := sha256.Sum256(append(x.Key, in[:saltLen]...)) 30 | // Deobfuscate the payload 31 | for i, c := range in[saltLen:] { 32 | out[i] = c ^ key[i%sha256.Size] 33 | } 34 | return pLen 35 | } 36 | 37 | func (x *XPlusObfuscator) Obfuscate(in []byte, out []byte) int { 38 | _, _ = fastrand.Read(out[:saltLen]) // salt 39 | // Obfuscate the payload 40 | key := sha256.Sum256(append(x.Key, out[:saltLen]...)) 41 | for i, c := range in { 42 | out[i+saltLen] = c ^ key[i%sha256.Size] 43 | } 44 | return len(in) + saltLen 45 | } 46 | -------------------------------------------------------------------------------- /clash-meta/transport/hysteria/obfs/xplus_test.go: -------------------------------------------------------------------------------- 1 | package obfs 2 | 3 | import ( 4 | "bytes" 5 | "testing" 6 | ) 7 | 8 | func TestXPlusObfuscator(t *testing.T) { 9 | x := NewXPlusObfuscator([]byte("Vaundy")) 10 | tests := []struct { 11 | name string 12 | p []byte 13 | }{ 14 | {name: "1", p: []byte("HelloWorld")}, 15 | {name: "2", p: []byte("Regret is just a horrible attempt at time travel that ends with you feeling like crap")}, 16 | {name: "3", p: []byte("To be, or not to be, that is the question:\nWhether 'tis nobler in the mind to suffer\n" + 17 | "The slings and arrows of outrageous fortune,\nOr to take arms against a sea of troubles\n" + 18 | "And by opposing end them. To die—to sleep,\nNo more; and by a sleep to say we end")}, 19 | {name: "empty", p: []byte("")}, 20 | } 21 | for _, tt := range tests { 22 | t.Run(tt.name, func(t *testing.T) { 23 | buf := make([]byte, 10240) 24 | n := x.Obfuscate(tt.p, buf) 25 | n2 := x.Deobfuscate(buf[:n], buf[n:]) 26 | if !bytes.Equal(tt.p, buf[n:n+n2]) { 27 | t.Errorf("Inconsistent deobfuscate result: got %v, want %v", buf[n:n+n2], tt.p) 28 | } 29 | }) 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /clash-meta/transport/hysteria/pmtud_fix/avail.go: -------------------------------------------------------------------------------- 1 | //go:build linux || windows 2 | // +build linux windows 3 | 4 | package pmtud_fix 5 | 6 | const ( 7 | DisablePathMTUDiscovery = false 8 | ) 9 | -------------------------------------------------------------------------------- /clash-meta/transport/hysteria/pmtud_fix/unavail.go: -------------------------------------------------------------------------------- 1 | //go:build !linux && !windows 2 | // +build !linux,!windows 3 | 4 | package pmtud_fix 5 | 6 | const ( 7 | DisablePathMTUDiscovery = true 8 | ) 9 | -------------------------------------------------------------------------------- /clash-meta/transport/hysteria/utils/misc.go: -------------------------------------------------------------------------------- 1 | package utils 2 | 3 | import ( 4 | "context" 5 | "net" 6 | "strconv" 7 | ) 8 | 9 | func SplitHostPort(hostport string) (string, uint16, error) { 10 | host, port, err := net.SplitHostPort(hostport) 11 | if err != nil { 12 | return "", 0, err 13 | } 14 | portUint, err := strconv.ParseUint(port, 10, 16) 15 | if err != nil { 16 | return "", 0, err 17 | } 18 | return host, uint16(portUint), err 19 | } 20 | 21 | func ParseIPZone(s string) (net.IP, string) { 22 | s, zone := splitHostZone(s) 23 | return net.ParseIP(s), zone 24 | } 25 | 26 | func splitHostZone(s string) (host, zone string) { 27 | if i := last(s, '%'); i > 0 { 28 | host, zone = s[:i], s[i+1:] 29 | } else { 30 | host = s 31 | } 32 | return 33 | } 34 | 35 | func last(s string, b byte) int { 36 | i := len(s) 37 | for i--; i >= 0; i-- { 38 | if s[i] == b { 39 | break 40 | } 41 | } 42 | return i 43 | } 44 | 45 | type PacketDialer interface { 46 | ListenPacket(rAddr net.Addr) (net.PacketConn, error) 47 | Context() context.Context 48 | RemoteAddr(host string) (net.Addr, error) 49 | } 50 | -------------------------------------------------------------------------------- /clash-meta/transport/restls/restls.go: -------------------------------------------------------------------------------- 1 | package restls 2 | 3 | import ( 4 | "context" 5 | "net" 6 | 7 | tls "github.com/3andne/restls-client-go" 8 | ) 9 | 10 | const ( 11 | Mode string = "restls" 12 | ) 13 | 14 | type Restls struct { 15 | *tls.UConn 16 | } 17 | 18 | func (r *Restls) Upstream() any { 19 | return r.UConn.NetConn() 20 | } 21 | 22 | // NewRestls return a Restls Connection 23 | func NewRestls(ctx context.Context, conn net.Conn, config *tls.Config) (net.Conn, error) { 24 | clientHellowID := tls.HelloChrome_Auto 25 | if config != nil { 26 | clientIDPtr := config.ClientID.Load() 27 | if clientIDPtr != nil { 28 | clientHellowID = *clientIDPtr 29 | } 30 | } 31 | restls := &Restls{ 32 | UConn: tls.UClient(conn, config, clientHellowID), 33 | } 34 | if err := restls.HandshakeContext(ctx); err != nil { 35 | return nil, err 36 | } 37 | 38 | return restls, nil 39 | } 40 | -------------------------------------------------------------------------------- /clash-meta/transport/shadowsocks/README.md: -------------------------------------------------------------------------------- 1 | ## Embedded go-shadowsocks2 2 | 3 | from https://github.com/Dreamacro/go-shadowsocks2 4 | 5 | origin https://github.com/riobard/go-shadowsocks2 6 | -------------------------------------------------------------------------------- /clash-meta/transport/shadowsocks/shadowstream/old_chacha20.go: -------------------------------------------------------------------------------- 1 | package shadowstream 2 | 3 | import ( 4 | "crypto/cipher" 5 | "github.com/aead/chacha20/chacha" 6 | ) 7 | 8 | type chacha20key []byte 9 | 10 | func (k chacha20key) IVSize() int { 11 | return chacha.NonceSize 12 | } 13 | func (k chacha20key) Encrypter(iv []byte) cipher.Stream { 14 | c, _ := chacha.NewCipher(iv, k, 20) 15 | return c 16 | } 17 | func (k chacha20key) Decrypter(iv []byte) cipher.Stream { 18 | return k.Encrypter(iv) 19 | } 20 | func ChaCha20(key []byte) (Cipher, error) { 21 | return chacha20key(key), nil 22 | } 23 | -------------------------------------------------------------------------------- /clash-meta/transport/snell/cipher.go: -------------------------------------------------------------------------------- 1 | package snell 2 | 3 | import ( 4 | "crypto/aes" 5 | "crypto/cipher" 6 | 7 | "github.com/Dreamacro/clash/transport/shadowsocks/shadowaead" 8 | 9 | "golang.org/x/crypto/argon2" 10 | "golang.org/x/crypto/chacha20poly1305" 11 | ) 12 | 13 | type snellCipher struct { 14 | psk []byte 15 | keySize int 16 | makeAEAD func(key []byte) (cipher.AEAD, error) 17 | } 18 | 19 | func (sc *snellCipher) KeySize() int { return sc.keySize } 20 | func (sc *snellCipher) SaltSize() int { return 16 } 21 | func (sc *snellCipher) Encrypter(salt []byte) (cipher.AEAD, error) { 22 | return sc.makeAEAD(snellKDF(sc.psk, salt, sc.KeySize())) 23 | } 24 | 25 | func (sc *snellCipher) Decrypter(salt []byte) (cipher.AEAD, error) { 26 | return sc.makeAEAD(snellKDF(sc.psk, salt, sc.KeySize())) 27 | } 28 | 29 | func snellKDF(psk, salt []byte, keySize int) []byte { 30 | // snell use a special kdf function 31 | return argon2.IDKey(psk, salt, 3, 8, 1, 32)[:keySize] 32 | } 33 | 34 | func aesGCM(key []byte) (cipher.AEAD, error) { 35 | blk, err := aes.NewCipher(key) 36 | if err != nil { 37 | return nil, err 38 | } 39 | return cipher.NewGCM(blk) 40 | } 41 | 42 | func NewAES128GCM(psk []byte) shadowaead.Cipher { 43 | return &snellCipher{ 44 | psk: psk, 45 | keySize: 16, 46 | makeAEAD: aesGCM, 47 | } 48 | } 49 | 50 | func NewChacha20Poly1305(psk []byte) shadowaead.Cipher { 51 | return &snellCipher{ 52 | psk: psk, 53 | keySize: 32, 54 | makeAEAD: chacha20poly1305.New, 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /clash-meta/transport/ssr/obfs/base.go: -------------------------------------------------------------------------------- 1 | package obfs 2 | 3 | type Base struct { 4 | Host string 5 | Port int 6 | Key []byte 7 | IVSize int 8 | Param string 9 | } 10 | -------------------------------------------------------------------------------- /clash-meta/transport/ssr/obfs/http_post.go: -------------------------------------------------------------------------------- 1 | package obfs 2 | 3 | func init() { 4 | register("http_post", newHTTPPost, 0) 5 | } 6 | 7 | func newHTTPPost(b *Base) Obfs { 8 | return &httpObfs{Base: b, post: true} 9 | } 10 | -------------------------------------------------------------------------------- /clash-meta/transport/ssr/obfs/obfs.go: -------------------------------------------------------------------------------- 1 | package obfs 2 | 3 | import ( 4 | "errors" 5 | "fmt" 6 | "net" 7 | ) 8 | 9 | var ( 10 | errTLS12TicketAuthIncorrectMagicNumber = errors.New("tls1.2_ticket_auth incorrect magic number") 11 | errTLS12TicketAuthTooShortData = errors.New("tls1.2_ticket_auth too short data") 12 | errTLS12TicketAuthHMACError = errors.New("tls1.2_ticket_auth hmac verifying failed") 13 | ) 14 | 15 | type authData struct { 16 | clientID [32]byte 17 | } 18 | 19 | type Obfs interface { 20 | StreamConn(net.Conn) net.Conn 21 | } 22 | 23 | type obfsCreator func(b *Base) Obfs 24 | 25 | var obfsList = make(map[string]struct { 26 | overhead int 27 | new obfsCreator 28 | }) 29 | 30 | func register(name string, c obfsCreator, o int) { 31 | obfsList[name] = struct { 32 | overhead int 33 | new obfsCreator 34 | }{overhead: o, new: c} 35 | } 36 | 37 | func PickObfs(name string, b *Base) (Obfs, int, error) { 38 | if choice, ok := obfsList[name]; ok { 39 | return choice.new(b), choice.overhead, nil 40 | } 41 | return nil, 0, fmt.Errorf("Obfs %s not supported", name) 42 | } 43 | -------------------------------------------------------------------------------- /clash-meta/transport/ssr/obfs/plain.go: -------------------------------------------------------------------------------- 1 | package obfs 2 | 3 | import "net" 4 | 5 | type plain struct{} 6 | 7 | func init() { 8 | register("plain", newPlain, 0) 9 | } 10 | 11 | func newPlain(b *Base) Obfs { 12 | return &plain{} 13 | } 14 | 15 | func (p *plain) StreamConn(c net.Conn) net.Conn { return c } 16 | -------------------------------------------------------------------------------- /clash-meta/transport/ssr/obfs/random_head.go: -------------------------------------------------------------------------------- 1 | package obfs 2 | 3 | import ( 4 | "encoding/binary" 5 | "hash/crc32" 6 | "net" 7 | 8 | "github.com/Dreamacro/clash/common/pool" 9 | 10 | "github.com/zhangyunhao116/fastrand" 11 | ) 12 | 13 | func init() { 14 | register("random_head", newRandomHead, 0) 15 | } 16 | 17 | type randomHead struct { 18 | *Base 19 | } 20 | 21 | func newRandomHead(b *Base) Obfs { 22 | return &randomHead{Base: b} 23 | } 24 | 25 | type randomHeadConn struct { 26 | net.Conn 27 | *randomHead 28 | hasSentHeader bool 29 | rawTransSent bool 30 | rawTransRecv bool 31 | buf []byte 32 | } 33 | 34 | func (r *randomHead) StreamConn(c net.Conn) net.Conn { 35 | return &randomHeadConn{Conn: c, randomHead: r} 36 | } 37 | 38 | func (c *randomHeadConn) Read(b []byte) (int, error) { 39 | if c.rawTransRecv { 40 | return c.Conn.Read(b) 41 | } 42 | buf := pool.Get(pool.RelayBufferSize) 43 | defer pool.Put(buf) 44 | c.Conn.Read(buf) 45 | c.rawTransRecv = true 46 | c.Write(nil) 47 | return 0, nil 48 | } 49 | 50 | func (c *randomHeadConn) Write(b []byte) (int, error) { 51 | if c.rawTransSent { 52 | return c.Conn.Write(b) 53 | } 54 | c.buf = append(c.buf, b...) 55 | if !c.hasSentHeader { 56 | c.hasSentHeader = true 57 | dataLength := fastrand.Intn(96) + 4 58 | buf := pool.Get(dataLength + 4) 59 | defer pool.Put(buf) 60 | fastrand.Read(buf[:dataLength]) 61 | binary.LittleEndian.PutUint32(buf[dataLength:], 0xffffffff-crc32.ChecksumIEEE(buf[:dataLength])) 62 | _, err := c.Conn.Write(buf) 63 | return len(b), err 64 | } 65 | if c.rawTransRecv { 66 | _, err := c.Conn.Write(c.buf) 67 | c.buf = nil 68 | c.rawTransSent = true 69 | return len(b), err 70 | } 71 | return len(b), nil 72 | } 73 | -------------------------------------------------------------------------------- /clash-meta/transport/ssr/protocol/auth_aes128_md5.go: -------------------------------------------------------------------------------- 1 | package protocol 2 | 3 | import "github.com/Dreamacro/clash/transport/ssr/tools" 4 | 5 | func init() { 6 | register("auth_aes128_md5", newAuthAES128MD5, 9) 7 | } 8 | 9 | func newAuthAES128MD5(b *Base) Protocol { 10 | a := &authAES128{ 11 | Base: b, 12 | authData: &authData{}, 13 | authAES128Function: &authAES128Function{salt: "auth_aes128_md5", hmac: tools.HmacMD5, hashDigest: tools.MD5Sum}, 14 | userData: &userData{}, 15 | } 16 | a.initUserData() 17 | return a 18 | } 19 | -------------------------------------------------------------------------------- /clash-meta/transport/ssr/protocol/origin.go: -------------------------------------------------------------------------------- 1 | package protocol 2 | 3 | import ( 4 | "bytes" 5 | "net" 6 | 7 | N "github.com/Dreamacro/clash/common/net" 8 | ) 9 | 10 | type origin struct{} 11 | 12 | func init() { register("origin", newOrigin, 0) } 13 | 14 | func newOrigin(b *Base) Protocol { return &origin{} } 15 | 16 | func (o *origin) StreamConn(c net.Conn, iv []byte) net.Conn { return c } 17 | 18 | func (o *origin) PacketConn(c N.EnhancePacketConn) N.EnhancePacketConn { return c } 19 | 20 | func (o *origin) Decode(dst, src *bytes.Buffer) error { 21 | dst.ReadFrom(src) 22 | return nil 23 | } 24 | 25 | func (o *origin) Encode(buf *bytes.Buffer, b []byte) error { 26 | buf.Write(b) 27 | return nil 28 | } 29 | 30 | func (o *origin) DecodePacket(b []byte) ([]byte, error) { return b, nil } 31 | 32 | func (o *origin) EncodePacket(buf *bytes.Buffer, b []byte) error { 33 | buf.Write(b) 34 | return nil 35 | } 36 | -------------------------------------------------------------------------------- /clash-meta/transport/ssr/protocol/packet.go: -------------------------------------------------------------------------------- 1 | package protocol 2 | 3 | import ( 4 | "net" 5 | 6 | N "github.com/Dreamacro/clash/common/net" 7 | "github.com/Dreamacro/clash/common/pool" 8 | ) 9 | 10 | type PacketConn struct { 11 | N.EnhancePacketConn 12 | Protocol 13 | } 14 | 15 | func (c *PacketConn) WriteTo(b []byte, addr net.Addr) (int, error) { 16 | buf := pool.GetBuffer() 17 | defer pool.PutBuffer(buf) 18 | err := c.EncodePacket(buf, b) 19 | if err != nil { 20 | return 0, err 21 | } 22 | _, err = c.EnhancePacketConn.WriteTo(buf.Bytes(), addr) 23 | return len(b), err 24 | } 25 | 26 | func (c *PacketConn) ReadFrom(b []byte) (int, net.Addr, error) { 27 | n, addr, err := c.EnhancePacketConn.ReadFrom(b) 28 | if err != nil { 29 | return n, addr, err 30 | } 31 | decoded, err := c.DecodePacket(b[:n]) 32 | if err != nil { 33 | return n, addr, err 34 | } 35 | copy(b, decoded) 36 | return len(decoded), addr, nil 37 | } 38 | 39 | func (c *PacketConn) WaitReadFrom() (data []byte, put func(), addr net.Addr, err error) { 40 | data, put, addr, err = c.EnhancePacketConn.WaitReadFrom() 41 | if err != nil { 42 | return 43 | } 44 | data, err = c.DecodePacket(data) 45 | if err != nil { 46 | if put != nil { 47 | put() 48 | } 49 | data = nil 50 | put = nil 51 | return 52 | } 53 | return 54 | } 55 | -------------------------------------------------------------------------------- /clash-meta/transport/ssr/protocol/stream.go: -------------------------------------------------------------------------------- 1 | package protocol 2 | 3 | import ( 4 | "bytes" 5 | "net" 6 | 7 | "github.com/Dreamacro/clash/common/pool" 8 | ) 9 | 10 | type Conn struct { 11 | net.Conn 12 | Protocol 13 | decoded bytes.Buffer 14 | underDecoded bytes.Buffer 15 | } 16 | 17 | func (c *Conn) Read(b []byte) (int, error) { 18 | if c.decoded.Len() > 0 { 19 | return c.decoded.Read(b) 20 | } 21 | 22 | buf := pool.Get(pool.RelayBufferSize) 23 | defer pool.Put(buf) 24 | n, err := c.Conn.Read(buf) 25 | if err != nil { 26 | return 0, err 27 | } 28 | c.underDecoded.Write(buf[:n]) 29 | err = c.Decode(&c.decoded, &c.underDecoded) 30 | if err != nil { 31 | return 0, err 32 | } 33 | n, _ = c.decoded.Read(b) 34 | return n, nil 35 | } 36 | 37 | func (c *Conn) Write(b []byte) (int, error) { 38 | bLength := len(b) 39 | buf := pool.GetBuffer() 40 | defer pool.PutBuffer(buf) 41 | err := c.Encode(buf, b) 42 | if err != nil { 43 | return 0, err 44 | } 45 | _, err = c.Conn.Write(buf.Bytes()) 46 | if err != nil { 47 | return 0, err 48 | } 49 | return bLength, nil 50 | } 51 | -------------------------------------------------------------------------------- /clash-meta/transport/ssr/tools/bufPool.go: -------------------------------------------------------------------------------- 1 | package tools 2 | 3 | import ( 4 | "bytes" 5 | "crypto/rand" 6 | "io" 7 | ) 8 | 9 | func AppendRandBytes(b *bytes.Buffer, length int) { 10 | b.ReadFrom(io.LimitReader(rand.Reader, int64(length))) 11 | } 12 | -------------------------------------------------------------------------------- /clash-meta/transport/ssr/tools/crypto.go: -------------------------------------------------------------------------------- 1 | package tools 2 | 3 | import ( 4 | "crypto/hmac" 5 | "crypto/md5" 6 | "crypto/sha1" 7 | ) 8 | 9 | const HmacSHA1Len = 10 10 | 11 | func HmacMD5(key, data []byte) []byte { 12 | hmacMD5 := hmac.New(md5.New, key) 13 | hmacMD5.Write(data) 14 | return hmacMD5.Sum(nil) 15 | } 16 | 17 | func HmacSHA1(key, data []byte) []byte { 18 | hmacSHA1 := hmac.New(sha1.New, key) 19 | hmacSHA1.Write(data) 20 | return hmacSHA1.Sum(nil) 21 | } 22 | 23 | func MD5Sum(b []byte) []byte { 24 | h := md5.New() 25 | h.Write(b) 26 | return h.Sum(nil) 27 | } 28 | 29 | func SHA1Sum(b []byte) []byte { 30 | h := sha1.New() 31 | h.Write(b) 32 | return h.Sum(nil) 33 | } 34 | -------------------------------------------------------------------------------- /clash-meta/transport/ssr/tools/random.go: -------------------------------------------------------------------------------- 1 | package tools 2 | 3 | import ( 4 | "encoding/binary" 5 | 6 | "github.com/Dreamacro/clash/common/pool" 7 | ) 8 | 9 | // XorShift128Plus - a pseudorandom number generator 10 | type XorShift128Plus struct { 11 | s [2]uint64 12 | } 13 | 14 | func (r *XorShift128Plus) Next() uint64 { 15 | x := r.s[0] 16 | y := r.s[1] 17 | r.s[0] = y 18 | x ^= x << 23 19 | x ^= y ^ (x >> 17) ^ (y >> 26) 20 | r.s[1] = x 21 | return x + y 22 | } 23 | 24 | func (r *XorShift128Plus) InitFromBin(bin []byte) { 25 | var full []byte 26 | if len(bin) < 16 { 27 | full := pool.Get(16)[:0] 28 | defer pool.Put(full) 29 | full = append(full, bin...) 30 | for len(full) < 16 { 31 | full = append(full, 0) 32 | } 33 | } else { 34 | full = bin 35 | } 36 | r.s[0] = binary.LittleEndian.Uint64(full[:8]) 37 | r.s[1] = binary.LittleEndian.Uint64(full[8:16]) 38 | } 39 | 40 | func (r *XorShift128Plus) InitFromBinAndLength(bin []byte, length int) { 41 | var full []byte 42 | if len(bin) < 16 { 43 | full := pool.Get(16)[:0] 44 | defer pool.Put(full) 45 | full = append(full, bin...) 46 | for len(full) < 16 { 47 | full = append(full, 0) 48 | } 49 | } 50 | full = bin 51 | binary.LittleEndian.PutUint16(full, uint16(length)) 52 | r.s[0] = binary.LittleEndian.Uint64(full[:8]) 53 | r.s[1] = binary.LittleEndian.Uint64(full[8:16]) 54 | for i := 0; i < 4; i++ { 55 | r.Next() 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /clash-meta/transport/tuic/congestion/bandwidth.go: -------------------------------------------------------------------------------- 1 | package congestion 2 | 3 | import ( 4 | "math" 5 | "time" 6 | 7 | "github.com/metacubex/quic-go/congestion" 8 | ) 9 | 10 | // Bandwidth of a connection 11 | type Bandwidth uint64 12 | 13 | const infBandwidth Bandwidth = math.MaxUint64 14 | 15 | const ( 16 | // BitsPerSecond is 1 bit per second 17 | BitsPerSecond Bandwidth = 1 18 | // BytesPerSecond is 1 byte per second 19 | BytesPerSecond = 8 * BitsPerSecond 20 | ) 21 | 22 | // BandwidthFromDelta calculates the bandwidth from a number of bytes and a time delta 23 | func BandwidthFromDelta(bytes congestion.ByteCount, delta time.Duration) Bandwidth { 24 | return Bandwidth(bytes) * Bandwidth(time.Second) / Bandwidth(delta) * BytesPerSecond 25 | } 26 | -------------------------------------------------------------------------------- /clash-meta/transport/tuic/congestion/clock.go: -------------------------------------------------------------------------------- 1 | package congestion 2 | 3 | import "time" 4 | 5 | // A Clock returns the current time 6 | type Clock interface { 7 | Now() time.Time 8 | } 9 | 10 | // DefaultClock implements the Clock interface using the Go stdlib clock. 11 | type DefaultClock struct{} 12 | 13 | var _ Clock = DefaultClock{} 14 | 15 | // Now gets the current time 16 | func (DefaultClock) Now() time.Time { 17 | return time.Now() 18 | } 19 | -------------------------------------------------------------------------------- /clash-meta/transport/tuic/congestion/minmax.go: -------------------------------------------------------------------------------- 1 | package congestion 2 | 3 | import ( 4 | "math" 5 | "time" 6 | 7 | "golang.org/x/exp/constraints" 8 | ) 9 | 10 | // InfDuration is a duration of infinite length 11 | const InfDuration = time.Duration(math.MaxInt64) 12 | 13 | func Max[T constraints.Ordered](a, b T) T { 14 | if a < b { 15 | return b 16 | } 17 | return a 18 | } 19 | 20 | func Min[T constraints.Ordered](a, b T) T { 21 | if a < b { 22 | return a 23 | } 24 | return b 25 | } 26 | 27 | // MinNonZeroDuration return the minimum duration that's not zero. 28 | func MinNonZeroDuration(a, b time.Duration) time.Duration { 29 | if a == 0 { 30 | return b 31 | } 32 | if b == 0 { 33 | return a 34 | } 35 | return Min(a, b) 36 | } 37 | 38 | // AbsDuration returns the absolute value of a time duration 39 | func AbsDuration(d time.Duration) time.Duration { 40 | if d >= 0 { 41 | return d 42 | } 43 | return -d 44 | } 45 | 46 | // MinTime returns the earlier time 47 | func MinTime(a, b time.Time) time.Time { 48 | if a.After(b) { 49 | return b 50 | } 51 | return a 52 | } 53 | 54 | // MinNonZeroTime returns the earlist time that is not time.Time{} 55 | // If both a and b are time.Time{}, it returns time.Time{} 56 | func MinNonZeroTime(a, b time.Time) time.Time { 57 | if a.IsZero() { 58 | return b 59 | } 60 | if b.IsZero() { 61 | return a 62 | } 63 | return MinTime(a, b) 64 | } 65 | 66 | // MaxTime returns the later time 67 | func MaxTime(a, b time.Time) time.Time { 68 | if a.After(b) { 69 | return a 70 | } 71 | return b 72 | } 73 | -------------------------------------------------------------------------------- /clash-meta/transport/vless/config.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | 3 | package clash.transport.vless; 4 | option csharp_namespace = "Clash.Transport.Vless"; 5 | option go_package = "github.com/Dreamacro/clash/transport/vless"; 6 | option java_package = "com.clash.transport.vless"; 7 | option java_multiple_files = true; 8 | 9 | message Addons { 10 | string Flow = 1; 11 | bytes Seed = 2; 12 | } -------------------------------------------------------------------------------- /clash-meta/transport/vless/vless.go: -------------------------------------------------------------------------------- 1 | package vless 2 | 3 | import ( 4 | "net" 5 | 6 | "github.com/Dreamacro/clash/common/utils" 7 | 8 | "github.com/gofrs/uuid/v5" 9 | ) 10 | 11 | const ( 12 | XRO = "xtls-rprx-origin" 13 | XRD = "xtls-rprx-direct" 14 | XRS = "xtls-rprx-splice" 15 | XRV = "xtls-rprx-vision" 16 | 17 | Version byte = 0 // protocol version. preview version is 0 18 | ) 19 | 20 | // Command types 21 | const ( 22 | CommandTCP byte = 1 23 | CommandUDP byte = 2 24 | CommandMux byte = 3 25 | ) 26 | 27 | // Addr types 28 | const ( 29 | AtypIPv4 byte = 1 30 | AtypDomainName byte = 2 31 | AtypIPv6 byte = 3 32 | ) 33 | 34 | // DstAddr store destination address 35 | type DstAddr struct { 36 | UDP bool 37 | AddrType byte 38 | Addr []byte 39 | Port uint16 40 | Mux bool // currently used for XUDP only 41 | } 42 | 43 | // Client is vless connection generator 44 | type Client struct { 45 | uuid *uuid.UUID 46 | Addons *Addons 47 | XTLSShow bool 48 | } 49 | 50 | // StreamConn return a Conn with net.Conn and DstAddr 51 | func (c *Client) StreamConn(conn net.Conn, dst *DstAddr) (net.Conn, error) { 52 | return newConn(conn, c, dst) 53 | } 54 | 55 | // NewClient return Client instance 56 | func NewClient(uuidStr string, addons *Addons, xtlsShow bool) (*Client, error) { 57 | uid, err := utils.UUIDMap(uuidStr) 58 | if err != nil { 59 | return nil, err 60 | } 61 | 62 | return &Client{ 63 | uuid: &uid, 64 | Addons: addons, 65 | XTLSShow: xtlsShow, 66 | }, nil 67 | } 68 | -------------------------------------------------------------------------------- /clash-meta/transport/vless/xtls.go: -------------------------------------------------------------------------------- 1 | package vless 2 | 3 | import ( 4 | "context" 5 | "net" 6 | 7 | tlsC "github.com/Dreamacro/clash/component/tls" 8 | xtls "github.com/xtls/go" 9 | ) 10 | 11 | type XTLSConfig struct { 12 | Host string 13 | SkipCertVerify bool 14 | Fingerprint string 15 | NextProtos []string 16 | } 17 | 18 | func StreamXTLSConn(ctx context.Context, conn net.Conn, cfg *XTLSConfig) (net.Conn, error) { 19 | xtlsConfig := &xtls.Config{ 20 | ServerName: cfg.Host, 21 | InsecureSkipVerify: cfg.SkipCertVerify, 22 | NextProtos: cfg.NextProtos, 23 | } 24 | if len(cfg.Fingerprint) == 0 { 25 | xtlsConfig = tlsC.GetGlobalXTLSConfig(xtlsConfig) 26 | } else { 27 | var err error 28 | if xtlsConfig, err = tlsC.GetSpecifiedFingerprintXTLSConfig(xtlsConfig, cfg.Fingerprint); err != nil { 29 | return nil, err 30 | } 31 | } 32 | 33 | xtlsConn := xtls.Client(conn, xtlsConfig) 34 | 35 | err := xtlsConn.HandshakeContext(ctx) 36 | return xtlsConn, err 37 | } 38 | -------------------------------------------------------------------------------- /clash-meta/transport/vmess/user.go: -------------------------------------------------------------------------------- 1 | package vmess 2 | 3 | import ( 4 | "bytes" 5 | "crypto/md5" 6 | 7 | "github.com/gofrs/uuid/v5" 8 | ) 9 | 10 | // ID cmdKey length 11 | const ( 12 | IDBytesLen = 16 13 | ) 14 | 15 | // The ID of en entity, in the form of a UUID. 16 | type ID struct { 17 | UUID *uuid.UUID 18 | CmdKey []byte 19 | } 20 | 21 | // newID returns an ID with given UUID. 22 | func newID(uuid *uuid.UUID) *ID { 23 | id := &ID{UUID: uuid, CmdKey: make([]byte, IDBytesLen)} 24 | md5hash := md5.New() 25 | md5hash.Write(uuid.Bytes()) 26 | md5hash.Write([]byte("c48619fe-8f02-49e0-b9e9-edf763e17e21")) 27 | md5hash.Sum(id.CmdKey[:0]) 28 | return id 29 | } 30 | 31 | func nextID(u *uuid.UUID) *uuid.UUID { 32 | md5hash := md5.New() 33 | md5hash.Write(u.Bytes()) 34 | md5hash.Write([]byte("16167dc8-16b6-4e6d-b8bb-65dd68113a81")) 35 | var newid uuid.UUID 36 | for { 37 | md5hash.Sum(newid[:0]) 38 | if !bytes.Equal(newid.Bytes(), u.Bytes()) { 39 | return &newid 40 | } 41 | md5hash.Write([]byte("533eff8a-4113-4b10-b5ce-0f5d76b98cd2")) 42 | } 43 | } 44 | 45 | func newAlterIDs(primary *ID, alterIDCount uint16) []*ID { 46 | alterIDs := make([]*ID, alterIDCount) 47 | prevID := primary.UUID 48 | for idx := range alterIDs { 49 | newid := nextID(prevID) 50 | alterIDs[idx] = &ID{UUID: newid, CmdKey: primary.CmdKey[:]} 51 | prevID = newid 52 | } 53 | alterIDs = append(alterIDs, primary) 54 | return alterIDs 55 | } 56 | -------------------------------------------------------------------------------- /clash-meta/tunnel/mode.go: -------------------------------------------------------------------------------- 1 | package tunnel 2 | 3 | import ( 4 | "encoding/json" 5 | "errors" 6 | "strings" 7 | ) 8 | 9 | type TunnelMode int 10 | 11 | // ModeMapping is a mapping for Mode enum 12 | var ModeMapping = map[string]TunnelMode{ 13 | Global.String(): Global, 14 | Rule.String(): Rule, 15 | Direct.String(): Direct, 16 | } 17 | 18 | const ( 19 | Global TunnelMode = iota 20 | Rule 21 | Direct 22 | ) 23 | 24 | // UnmarshalJSON unserialize Mode 25 | func (m *TunnelMode) UnmarshalJSON(data []byte) error { 26 | var tp string 27 | json.Unmarshal(data, &tp) 28 | mode, exist := ModeMapping[strings.ToLower(tp)] 29 | if !exist { 30 | return errors.New("invalid mode") 31 | } 32 | *m = mode 33 | return nil 34 | } 35 | 36 | // UnmarshalYAML unserialize Mode with yaml 37 | func (m *TunnelMode) UnmarshalYAML(unmarshal func(any) error) error { 38 | var tp string 39 | unmarshal(&tp) 40 | mode, exist := ModeMapping[strings.ToLower(tp)] 41 | if !exist { 42 | return errors.New("invalid mode") 43 | } 44 | *m = mode 45 | return nil 46 | } 47 | 48 | // MarshalJSON serialize Mode 49 | func (m TunnelMode) MarshalJSON() ([]byte, error) { 50 | return json.Marshal(m.String()) 51 | } 52 | 53 | // MarshalYAML serialize TunnelMode with yaml 54 | func (m TunnelMode) MarshalYAML() (any, error) { 55 | return m.String(), nil 56 | } 57 | 58 | func (m TunnelMode) String() string { 59 | switch m { 60 | case Global: 61 | return "global" 62 | case Rule: 63 | return "rule" 64 | case Direct: 65 | return "direct" 66 | default: 67 | return "Unknown" 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /run.bat: -------------------------------------------------------------------------------- 1 | @go run -tags with_gvisor wg-ping.go 2 | --------------------------------------------------------------------------------