mirror of
https://github.com/MetaCubeX/Clash.Meta.git
synced 2025-04-20 01:00:56 +00:00
Merge branch 'MetaCubeX:Alpha' into Alpha
This commit is contained in:
commit
753b9bdb47
18 changed files with 389 additions and 45 deletions
25
.github/workflows/build.yml
vendored
25
.github/workflows/build.yml
vendored
|
@ -204,18 +204,18 @@ jobs:
|
|||
alien --to-rpm --scripts mihomo-${{matrix.jobs.goos}}-${{matrix.jobs.output}}-${VERSION}.deb
|
||||
mv mihomo*.rpm mihomo-${{matrix.jobs.goos}}-${{matrix.jobs.output}}-${VERSION}.rpm
|
||||
|
||||
- name: Convert DEB to PKG
|
||||
if: ${{ matrix.jobs.goos == 'linux' && !contains(matrix.jobs.goarch, 'mips') && !contains(matrix.jobs.goarch, 'loong64') }}
|
||||
run: |
|
||||
docker pull archlinux
|
||||
docker run --rm -v ./:/mnt archlinux bash -c "
|
||||
pacman -Syu pkgfile base-devel --noconfirm
|
||||
curl -L https://github.com/helixarch/debtap/raw/master/debtap > /usr/bin/debtap
|
||||
chmod 755 /usr/bin/debtap
|
||||
debtap -u
|
||||
debtap -Q /mnt/mihomo-${{matrix.jobs.goos}}-${{matrix.jobs.output}}-${VERSION}.deb
|
||||
"
|
||||
mv mihomo*.pkg.tar.zst mihomo-${{matrix.jobs.goos}}-${{matrix.jobs.output}}-${VERSION}.pkg.tar.zst
|
||||
# - name: Convert DEB to PKG
|
||||
# if: ${{ matrix.jobs.goos == 'linux' && !contains(matrix.jobs.goarch, 'mips') && !contains(matrix.jobs.goarch, 'loong64') }}
|
||||
# run: |
|
||||
# docker pull archlinux
|
||||
# docker run --rm -v ./:/mnt archlinux bash -c "
|
||||
# pacman -Syu pkgfile base-devel --noconfirm
|
||||
# curl -L https://github.com/helixarch/debtap/raw/master/debtap > /usr/bin/debtap
|
||||
# chmod 755 /usr/bin/debtap
|
||||
# debtap -u
|
||||
# debtap -Q /mnt/mihomo-${{matrix.jobs.goos}}-${{matrix.jobs.output}}-${VERSION}.deb
|
||||
# "
|
||||
# mv mihomo*.pkg.tar.zst mihomo-${{matrix.jobs.goos}}-${{matrix.jobs.output}}-${VERSION}.pkg.tar.zst
|
||||
|
||||
- name: Save version
|
||||
run: |
|
||||
|
@ -230,7 +230,6 @@ jobs:
|
|||
mihomo*.gz
|
||||
mihomo*.deb
|
||||
mihomo*.rpm
|
||||
mihomo*.pkg.tar.zst
|
||||
mihomo*.zip
|
||||
version.txt
|
||||
|
||||
|
|
|
@ -2,10 +2,10 @@ package outbound
|
|||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net"
|
||||
"time"
|
||||
|
||||
N "github.com/metacubex/mihomo/common/net"
|
||||
"github.com/metacubex/mihomo/common/pool"
|
||||
"github.com/metacubex/mihomo/component/dialer"
|
||||
"github.com/metacubex/mihomo/component/resolver"
|
||||
|
@ -24,7 +24,9 @@ type DnsOption struct {
|
|||
|
||||
// DialContext implements C.ProxyAdapter
|
||||
func (d *Dns) DialContext(ctx context.Context, metadata *C.Metadata, opts ...dialer.Option) (C.Conn, error) {
|
||||
return nil, fmt.Errorf("dns outbound does not support tcp")
|
||||
left, right := N.Pipe()
|
||||
go resolver.RelayDnsConn(context.Background(), right, 0)
|
||||
return NewConn(left, d), nil
|
||||
}
|
||||
|
||||
// ListenPacketContext implements C.ProxyAdapter
|
||||
|
@ -76,29 +78,44 @@ func (d *dnsPacketConn) ReadFrom(p []byte) (n int, addr net.Addr, err error) {
|
|||
}
|
||||
|
||||
func (d *dnsPacketConn) WriteTo(p []byte, addr net.Addr) (n int, err error) {
|
||||
select {
|
||||
case <-d.ctx.Done():
|
||||
return 0, net.ErrClosed
|
||||
default:
|
||||
}
|
||||
|
||||
if len(p) > resolver.SafeDnsPacketSize {
|
||||
// wtf???
|
||||
return len(p), nil
|
||||
}
|
||||
|
||||
ctx, cancel := context.WithTimeout(d.ctx, resolver.DefaultDnsRelayTimeout)
|
||||
defer cancel()
|
||||
|
||||
buf := pool.Get(resolver.SafeDnsPacketSize)
|
||||
put := func() { _ = pool.Put(buf) }
|
||||
buf, err = resolver.RelayDnsPacket(ctx, p, buf)
|
||||
if err != nil {
|
||||
put()
|
||||
return 0, err
|
||||
}
|
||||
copy(buf, p) // avoid p be changed after WriteTo returned
|
||||
|
||||
packet := dnsPacket{
|
||||
data: buf,
|
||||
put: put,
|
||||
addr: addr,
|
||||
}
|
||||
select {
|
||||
case d.response <- packet:
|
||||
return len(p), nil
|
||||
case <-d.ctx.Done():
|
||||
put()
|
||||
return 0, net.ErrClosed
|
||||
}
|
||||
go func() { // don't block the WriteTo function
|
||||
buf, err = resolver.RelayDnsPacket(ctx, buf[:len(p)], buf)
|
||||
if err != nil {
|
||||
put()
|
||||
return
|
||||
}
|
||||
|
||||
packet := dnsPacket{
|
||||
data: buf,
|
||||
put: put,
|
||||
addr: addr,
|
||||
}
|
||||
select {
|
||||
case d.response <- packet:
|
||||
break
|
||||
case <-d.ctx.Done():
|
||||
put()
|
||||
}
|
||||
}()
|
||||
return len(p), nil
|
||||
}
|
||||
|
||||
func (d *dnsPacketConn) Close() error {
|
||||
|
|
|
@ -5,15 +5,19 @@ import (
|
|||
"crypto/tls"
|
||||
"errors"
|
||||
"fmt"
|
||||
"math/rand"
|
||||
"net"
|
||||
"runtime"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
CN "github.com/metacubex/mihomo/common/net"
|
||||
"github.com/metacubex/mihomo/component/ca"
|
||||
"github.com/metacubex/mihomo/component/dialer"
|
||||
"github.com/metacubex/mihomo/component/proxydialer"
|
||||
C "github.com/metacubex/mihomo/constant"
|
||||
"github.com/metacubex/mihomo/log"
|
||||
tuicCommon "github.com/metacubex/mihomo/transport/tuic/common"
|
||||
|
||||
"github.com/metacubex/sing-quic/hysteria2"
|
||||
|
@ -25,6 +29,9 @@ func init() {
|
|||
hysteria2.SetCongestionController = tuicCommon.SetCongestionController
|
||||
}
|
||||
|
||||
const minHopInterval = 5
|
||||
const defaultHopInterval = 30
|
||||
|
||||
type Hysteria2 struct {
|
||||
*Base
|
||||
|
||||
|
@ -38,6 +45,8 @@ type Hysteria2Option struct {
|
|||
Name string `proxy:"name"`
|
||||
Server string `proxy:"server"`
|
||||
Port int `proxy:"port"`
|
||||
Ports string `proxy:"ports,omitempty"`
|
||||
HopInterval int `proxy:"hop-interval,omitempty"`
|
||||
Up string `proxy:"up,omitempty"`
|
||||
Down string `proxy:"down,omitempty"`
|
||||
Password string `proxy:"password,omitempty"`
|
||||
|
@ -82,6 +91,41 @@ func closeHysteria2(h *Hysteria2) {
|
|||
}
|
||||
}
|
||||
|
||||
func parsePorts(portStr string) (ports []uint16) {
|
||||
portStrs := strings.Split(portStr, ",")
|
||||
for _, portStr := range portStrs {
|
||||
if strings.Contains(portStr, "-") {
|
||||
// Port range
|
||||
portRange := strings.Split(portStr, "-")
|
||||
if len(portRange) != 2 {
|
||||
return nil
|
||||
}
|
||||
start, err := strconv.ParseUint(portRange[0], 10, 16)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
end, err := strconv.ParseUint(portRange[1], 10, 16)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
if start > end {
|
||||
start, end = end, start
|
||||
}
|
||||
for i := start; i <= end; i++ {
|
||||
ports = append(ports, uint16(i))
|
||||
}
|
||||
} else {
|
||||
// Single port
|
||||
port, err := strconv.ParseUint(portStr, 10, 16)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
ports = append(ports, uint16(port))
|
||||
}
|
||||
}
|
||||
return ports
|
||||
}
|
||||
|
||||
func NewHysteria2(option Hysteria2Option) (*Hysteria2, error) {
|
||||
addr := net.JoinHostPort(option.Server, strconv.Itoa(option.Port))
|
||||
var salamanderPassword string
|
||||
|
@ -129,6 +173,7 @@ func NewHysteria2(option Hysteria2Option) (*Hysteria2, error) {
|
|||
clientOptions := hysteria2.ClientOptions{
|
||||
Context: context.TODO(),
|
||||
Dialer: singDialer,
|
||||
Logger: log.SingLogger,
|
||||
ServerAddress: M.ParseSocksaddrHostPort(option.Server, uint16(option.Port)),
|
||||
SendBPS: StringToBps(option.Up),
|
||||
ReceiveBPS: StringToBps(option.Down),
|
||||
|
@ -140,6 +185,23 @@ func NewHysteria2(option Hysteria2Option) (*Hysteria2, error) {
|
|||
UdpMTU: option.UdpMTU,
|
||||
}
|
||||
|
||||
if option.Ports != "" {
|
||||
ports := parsePorts(option.Ports)
|
||||
if len(ports) > 0 {
|
||||
for _, port := range ports {
|
||||
clientOptions.ServerAddresses = append(clientOptions.ServerAddresses, M.ParseSocksaddrHostPort(option.Server, port))
|
||||
}
|
||||
clientOptions.ServerAddress = clientOptions.ServerAddresses[rand.Intn(len(clientOptions.ServerAddresses))]
|
||||
|
||||
if option.HopInterval == 0 {
|
||||
option.HopInterval = defaultHopInterval
|
||||
} else if option.HopInterval < minHopInterval {
|
||||
option.HopInterval = minHopInterval
|
||||
}
|
||||
clientOptions.HopInterval = time.Duration(option.HopInterval) * time.Second
|
||||
}
|
||||
}
|
||||
|
||||
client, err := hysteria2.NewClient(clientOptions)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
|
173
adapter/outbound/ssh.go
Normal file
173
adapter/outbound/ssh.go
Normal file
|
@ -0,0 +1,173 @@
|
|||
package outbound
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net"
|
||||
"os"
|
||||
"runtime"
|
||||
"strconv"
|
||||
"sync"
|
||||
|
||||
N "github.com/metacubex/mihomo/common/net"
|
||||
"github.com/metacubex/mihomo/component/dialer"
|
||||
"github.com/metacubex/mihomo/component/proxydialer"
|
||||
C "github.com/metacubex/mihomo/constant"
|
||||
|
||||
"github.com/zhangyunhao116/fastrand"
|
||||
"golang.org/x/crypto/ssh"
|
||||
)
|
||||
|
||||
type Ssh struct {
|
||||
*Base
|
||||
|
||||
option *SshOption
|
||||
client *sshClient // using a standalone struct to avoid its inner loop invalidate the Finalizer
|
||||
}
|
||||
|
||||
type SshOption struct {
|
||||
BasicOption
|
||||
Name string `proxy:"name"`
|
||||
Server string `proxy:"server"`
|
||||
Port int `proxy:"port"`
|
||||
UserName string `proxy:"username"`
|
||||
Password string `proxy:"password,omitempty"`
|
||||
PrivateKey string `proxy:"privateKey,omitempty"`
|
||||
}
|
||||
|
||||
func (s *Ssh) DialContext(ctx context.Context, metadata *C.Metadata, opts ...dialer.Option) (_ C.Conn, err error) {
|
||||
var cDialer C.Dialer = dialer.NewDialer(s.Base.DialOptions(opts...)...)
|
||||
if len(s.option.DialerProxy) > 0 {
|
||||
cDialer, err = proxydialer.NewByName(s.option.DialerProxy, cDialer)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
client, err := s.client.connect(ctx, cDialer, s.addr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
c, err := client.DialContext(ctx, "tcp", metadata.RemoteAddress())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return NewConn(N.NewRefConn(c, s), s), nil
|
||||
}
|
||||
|
||||
type sshClient struct {
|
||||
config *ssh.ClientConfig
|
||||
client *ssh.Client
|
||||
cMutex sync.Mutex
|
||||
}
|
||||
|
||||
func (s *sshClient) connect(ctx context.Context, cDialer C.Dialer, addr string) (client *ssh.Client, err error) {
|
||||
s.cMutex.Lock()
|
||||
defer s.cMutex.Unlock()
|
||||
if s.client != nil {
|
||||
return s.client, nil
|
||||
}
|
||||
c, err := cDialer.DialContext(ctx, "tcp", addr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
N.TCPKeepAlive(c)
|
||||
|
||||
defer func(c net.Conn) {
|
||||
safeConnClose(c, err)
|
||||
}(c)
|
||||
|
||||
if ctx.Done() != nil {
|
||||
done := N.SetupContextForConn(ctx, c)
|
||||
defer done(&err)
|
||||
}
|
||||
|
||||
clientConn, chans, reqs, err := ssh.NewClientConn(c, addr, s.config)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
client = ssh.NewClient(clientConn, chans, reqs)
|
||||
|
||||
s.client = client
|
||||
|
||||
go func() {
|
||||
_ = client.Wait() // wait shutdown
|
||||
_ = client.Close()
|
||||
s.cMutex.Lock()
|
||||
defer s.cMutex.Unlock()
|
||||
if s.client == client {
|
||||
s.client = nil
|
||||
}
|
||||
}()
|
||||
|
||||
return client, nil
|
||||
}
|
||||
|
||||
func (s *sshClient) Close() error {
|
||||
s.cMutex.Lock()
|
||||
defer s.cMutex.Unlock()
|
||||
if s.client != nil {
|
||||
return s.client.Close()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func closeSsh(s *Ssh) {
|
||||
_ = s.client.Close()
|
||||
}
|
||||
|
||||
func NewSsh(option SshOption) (*Ssh, error) {
|
||||
addr := net.JoinHostPort(option.Server, strconv.Itoa(option.Port))
|
||||
|
||||
config := ssh.ClientConfig{
|
||||
User: option.UserName,
|
||||
HostKeyCallback: func(hostname string, remote net.Addr, key ssh.PublicKey) error {
|
||||
return nil
|
||||
},
|
||||
}
|
||||
|
||||
if option.Password == "" {
|
||||
b, err := os.ReadFile(option.PrivateKey)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
pKey, err := ssh.ParsePrivateKey(b)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
config.Auth = []ssh.AuthMethod{
|
||||
ssh.PublicKeys(pKey),
|
||||
}
|
||||
} else {
|
||||
config.Auth = []ssh.AuthMethod{
|
||||
ssh.Password(option.Password),
|
||||
}
|
||||
}
|
||||
|
||||
version := "SSH-2.0-OpenSSH_"
|
||||
if fastrand.Intn(2) == 0 {
|
||||
version += "7." + strconv.Itoa(fastrand.Intn(10))
|
||||
} else {
|
||||
version += "8." + strconv.Itoa(fastrand.Intn(9))
|
||||
}
|
||||
config.ClientVersion = version
|
||||
|
||||
outbound := &Ssh{
|
||||
Base: &Base{
|
||||
name: option.Name,
|
||||
addr: addr,
|
||||
tp: C.Ssh,
|
||||
udp: false,
|
||||
iface: option.Interface,
|
||||
rmark: option.RoutingMark,
|
||||
prefer: C.NewDNSPrefer(option.IPVersion),
|
||||
},
|
||||
option: &option,
|
||||
client: &sshClient{
|
||||
config: &config,
|
||||
},
|
||||
}
|
||||
runtime.SetFinalizer(outbound, closeSsh)
|
||||
|
||||
return outbound, nil
|
||||
}
|
|
@ -134,6 +134,13 @@ func ParseProxy(mapping map[string]any) (C.Proxy, error) {
|
|||
break
|
||||
}
|
||||
proxy = outbound.NewRejectWithOption(*rejectOption)
|
||||
case "ssh":
|
||||
sshOption := &outbound.SshOption{}
|
||||
err = decoder.Decode(mapping, sshOption)
|
||||
if err != nil {
|
||||
break
|
||||
}
|
||||
proxy, err = outbound.NewSsh(*sshOption)
|
||||
default:
|
||||
return nil, fmt.Errorf("unsupport proxy type: %s", proxyType)
|
||||
}
|
||||
|
|
|
@ -26,6 +26,11 @@ type Conn struct {
|
|||
resultCh chan *connReadResult
|
||||
}
|
||||
|
||||
func IsConn(conn any) bool {
|
||||
_, ok := conn.(*Conn)
|
||||
return ok
|
||||
}
|
||||
|
||||
func NewConn(conn net.Conn) *Conn {
|
||||
c := &Conn{
|
||||
ExtendedConn: bufio.NewExtendedConn(conn),
|
||||
|
|
|
@ -215,3 +215,8 @@ func (p *pipe) waitReadBuffer() (buffer *buf.Buffer, err error) {
|
|||
return nil, os.ErrDeadlineExceeded
|
||||
}
|
||||
}
|
||||
|
||||
func IsPipe(conn any) bool {
|
||||
_, ok := conn.(*pipe)
|
||||
return ok
|
||||
}
|
||||
|
|
|
@ -23,6 +23,12 @@ type ExtendedReader = network.ExtendedReader
|
|||
var WriteBuffer = bufio.WriteBuffer
|
||||
|
||||
func NewDeadlineConn(conn net.Conn) ExtendedConn {
|
||||
if deadline.IsPipe(conn) || deadline.IsPipe(network.UnwrapReader(conn)) {
|
||||
return NewExtendedConn(conn) // pipe always have correctly deadline implement
|
||||
}
|
||||
if deadline.IsConn(conn) || deadline.IsConn(network.UnwrapReader(conn)) {
|
||||
return NewExtendedConn(conn) // was a *deadline.Conn
|
||||
}
|
||||
return deadline.NewConn(conn)
|
||||
}
|
||||
|
||||
|
|
|
@ -3,6 +3,7 @@ package mmdb
|
|||
import (
|
||||
"fmt"
|
||||
"net"
|
||||
"strings"
|
||||
|
||||
"github.com/oschwald/maxminddb-golang"
|
||||
)
|
||||
|
@ -26,7 +27,7 @@ func (r Reader) LookupCode(ipAddress net.IP) []string {
|
|||
if country.Country.IsoCode == "" {
|
||||
return []string{}
|
||||
}
|
||||
return []string{country.Country.IsoCode}
|
||||
return []string{strings.ToLower(country.Country.IsoCode)}
|
||||
|
||||
case typeSing:
|
||||
var code string
|
||||
|
|
|
@ -17,15 +17,15 @@ const DefaultDnsRelayTimeout = time.Second * 5
|
|||
|
||||
const SafeDnsPacketSize = 2 * 1024 // safe size which is 1232 from https://dnsflagday.net/2020/, so 2048 is enough
|
||||
|
||||
func RelayDnsConn(ctx context.Context, conn net.Conn) error {
|
||||
func RelayDnsConn(ctx context.Context, conn net.Conn, readTimeout time.Duration) error {
|
||||
buff := pool.Get(pool.UDPBufferSize)
|
||||
defer func() {
|
||||
_ = pool.Put(buff)
|
||||
_ = conn.Close()
|
||||
}()
|
||||
for {
|
||||
if conn.SetReadDeadline(time.Now().Add(DefaultDnsReadTimeout)) != nil {
|
||||
break
|
||||
if readTimeout > 0 {
|
||||
_ = conn.SetReadDeadline(time.Now().Add(readTimeout))
|
||||
}
|
||||
|
||||
length := uint16(0)
|
||||
|
|
|
@ -41,6 +41,7 @@ const (
|
|||
Hysteria2
|
||||
WireGuard
|
||||
Tuic
|
||||
Ssh
|
||||
)
|
||||
|
||||
const (
|
||||
|
@ -222,7 +223,8 @@ func (at AdapterType) String() string {
|
|||
return "URLTest"
|
||||
case LoadBalance:
|
||||
return "LoadBalance"
|
||||
|
||||
case Ssh:
|
||||
return "Ssh"
|
||||
default:
|
||||
return "Unknown"
|
||||
}
|
||||
|
|
|
@ -5,6 +5,7 @@ const (
|
|||
Domain RuleType = iota
|
||||
DomainSuffix
|
||||
DomainKeyword
|
||||
DomainRegex
|
||||
GEOSITE
|
||||
GEOIP
|
||||
IPCIDR
|
||||
|
@ -40,6 +41,8 @@ func (rt RuleType) String() string {
|
|||
return "DomainSuffix"
|
||||
case DomainKeyword:
|
||||
return "DomainKeyword"
|
||||
case DomainRegex:
|
||||
return "DomainRegex"
|
||||
case GEOSITE:
|
||||
return "GeoSite"
|
||||
case GEOIP:
|
||||
|
|
4
go.mod
4
go.mod
|
@ -19,8 +19,8 @@ require (
|
|||
github.com/lunixbochs/struc v0.0.0-20200707160740-784aaebc1d40
|
||||
github.com/mdlayher/netlink v1.7.2
|
||||
github.com/metacubex/gopacket v1.1.20-0.20230608035415-7e2f98a3e759
|
||||
github.com/metacubex/quic-go v0.41.1-0.20240120014142-a02f4a533d4a
|
||||
github.com/metacubex/sing-quic v0.0.0-20240130040922-cbe613c88f20
|
||||
github.com/metacubex/quic-go v0.41.1-0.20240307164142-46c6f7cdf2d1
|
||||
github.com/metacubex/sing-quic v0.0.0-20240308143007-4dd80423c25a
|
||||
github.com/metacubex/sing-shadowsocks v0.2.6
|
||||
github.com/metacubex/sing-shadowsocks2 v0.2.0
|
||||
github.com/metacubex/sing-tun v0.2.1-0.20240214100323-23e40bfb9067
|
||||
|
|
9
go.sum
9
go.sum
|
@ -104,12 +104,12 @@ github.com/metacubex/gopacket v1.1.20-0.20230608035415-7e2f98a3e759 h1:cjd4biTvO
|
|||
github.com/metacubex/gopacket v1.1.20-0.20230608035415-7e2f98a3e759/go.mod h1:UHOv2xu+RIgLwpXca7TLrXleEd4oR3sPatW6IF8wU88=
|
||||
github.com/metacubex/gvisor v0.0.0-20240214095142-666a73bcf165 h1:QIQI4gEm+gTwVNdiAyF4EIz5cHm7kSlfDGFpYlAa5dg=
|
||||
github.com/metacubex/gvisor v0.0.0-20240214095142-666a73bcf165/go.mod h1:SKY70wiF1UTSoyuDZyKPMsUC6MsMxh8Y3ZNkIa6J3fU=
|
||||
github.com/metacubex/quic-go v0.41.1-0.20240120014142-a02f4a533d4a h1:IMr75VdMnDUhkANZemUWqmOPLfwnemiIaCHRnGCdAsY=
|
||||
github.com/metacubex/quic-go v0.41.1-0.20240120014142-a02f4a533d4a/go.mod h1:F/t8VnA47xoia8ABlNA4InkZjssvFJ5p6E6jKdbkgAs=
|
||||
github.com/metacubex/quic-go v0.41.1-0.20240307164142-46c6f7cdf2d1 h1:63zKmEWU4MB5MjUSCmeDhm3OzilF7ypXWPq0gAA2GE8=
|
||||
github.com/metacubex/quic-go v0.41.1-0.20240307164142-46c6f7cdf2d1/go.mod h1:F/t8VnA47xoia8ABlNA4InkZjssvFJ5p6E6jKdbkgAs=
|
||||
github.com/metacubex/sing v0.0.0-20240111014253-f1818b6a82b2 h1:upEO8dt9WDBavhgcgkXB3hRcwVNbkTbnd+xyzy6ZQZo=
|
||||
github.com/metacubex/sing v0.0.0-20240111014253-f1818b6a82b2/go.mod h1:9pfuAH6mZfgnz/YjP6xu5sxx882rfyjpcrTdUpd6w3g=
|
||||
github.com/metacubex/sing-quic v0.0.0-20240130040922-cbe613c88f20 h1:wt7ydRxm9Pvw+un6KD97tjLJHMrkzp83HyiGkoz6e7k=
|
||||
github.com/metacubex/sing-quic v0.0.0-20240130040922-cbe613c88f20/go.mod h1:bdHqEysJclB9BzIa5jcKKSZ1qua+YEPjR8fOzzE3vZU=
|
||||
github.com/metacubex/sing-quic v0.0.0-20240308143007-4dd80423c25a h1:ATj0jL+cp7n+NT3T010cXK5KoVvAbeGhZFtUFHvq2BU=
|
||||
github.com/metacubex/sing-quic v0.0.0-20240308143007-4dd80423c25a/go.mod h1:WyY0zYxv+o+18R/Ece+QFontlgXoobKbNqbtYn2zjz8=
|
||||
github.com/metacubex/sing-shadowsocks v0.2.6 h1:6oEB3QcsFYnNiFeoevcXrCwJ3sAablwVSgtE9R3QeFQ=
|
||||
github.com/metacubex/sing-shadowsocks v0.2.6/go.mod h1:zIkMeSnb8Mbf4hdqhw0pjzkn1d99YJ3JQm/VBg5WMTg=
|
||||
github.com/metacubex/sing-shadowsocks2 v0.2.0 h1:hqwT/AfI5d5UdPefIzR6onGHJfDXs5zgOM5QSgaM/9A=
|
||||
|
@ -255,6 +255,7 @@ golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
|||
golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/sys v0.17.0 h1:25cE3gD+tdBA7lp7QfhuV+rJiE9YXTcS3VG1SqssI/Y=
|
||||
golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/term v0.17.0 h1:mkTF7LCd6WGJNL3K1Ad7kwxNfYAW6a8a8QqtMblp/4U=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ=
|
||||
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
|
||||
|
|
|
@ -37,7 +37,7 @@ func (h *ListenerHandler) ShouldHijackDns(targetAddr netip.AddrPort) bool {
|
|||
func (h *ListenerHandler) NewConnection(ctx context.Context, conn net.Conn, metadata M.Metadata) error {
|
||||
if h.ShouldHijackDns(metadata.Destination.AddrPort()) {
|
||||
log.Debugln("[DNS] hijack tcp:%s", metadata.Destination.String())
|
||||
return resolver.RelayDnsConn(ctx, conn)
|
||||
return resolver.RelayDnsConn(ctx, conn, resolver.DefaultDnsReadTimeout)
|
||||
}
|
||||
return h.ListenerHandler.NewConnection(ctx, conn, metadata)
|
||||
}
|
||||
|
|
17
main.go
17
main.go
|
@ -4,6 +4,7 @@ import (
|
|||
"flag"
|
||||
"fmt"
|
||||
"os"
|
||||
"os/exec"
|
||||
"os/signal"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
|
@ -48,6 +49,10 @@ func init() {
|
|||
}
|
||||
|
||||
func main() {
|
||||
if runtime.GOOS == "android" {
|
||||
SetAndroidTZ()
|
||||
}
|
||||
|
||||
_, _ = maxprocs.Set(maxprocs.Logger(func(string, ...any) {}))
|
||||
if version {
|
||||
fmt.Printf("Mihomo Meta %s %s %s with %s %s\n",
|
||||
|
@ -176,3 +181,15 @@ func updateGeoDatabases() {
|
|||
executor.ApplyConfig(cfg, false)
|
||||
}()
|
||||
}
|
||||
|
||||
func SetAndroidTZ() {
|
||||
out, err := exec.Command("getprop", "persist.sys.timezone").Output()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
z, err := time.LoadLocation(strings.TrimSpace(string(out)))
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
time.Local = z
|
||||
}
|
||||
|
|
44
rules/common/domain_regex.go
Normal file
44
rules/common/domain_regex.go
Normal file
|
@ -0,0 +1,44 @@
|
|||
package common
|
||||
|
||||
import (
|
||||
"regexp"
|
||||
|
||||
C "github.com/metacubex/mihomo/constant"
|
||||
)
|
||||
|
||||
type DomainRegex struct {
|
||||
*Base
|
||||
regex *regexp.Regexp
|
||||
adapter string
|
||||
}
|
||||
|
||||
func (dr *DomainRegex) RuleType() C.RuleType {
|
||||
return C.DomainRegex
|
||||
}
|
||||
|
||||
func (dr *DomainRegex) Match(metadata *C.Metadata) (bool, string) {
|
||||
domain := metadata.RuleHost()
|
||||
return dr.regex.MatchString(domain), dr.adapter
|
||||
}
|
||||
|
||||
func (dr *DomainRegex) Adapter() string {
|
||||
return dr.adapter
|
||||
}
|
||||
|
||||
func (dr *DomainRegex) Payload() string {
|
||||
return dr.regex.String()
|
||||
}
|
||||
|
||||
func NewDomainRegex(regex string, adapter string) (*DomainRegex, error) {
|
||||
r, err := regexp.Compile(regex)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &DomainRegex{
|
||||
Base: &Base{},
|
||||
regex: r,
|
||||
adapter: adapter,
|
||||
}, nil
|
||||
}
|
||||
|
||||
//var _ C.Rule = (*DomainRegex)(nil)
|
|
@ -2,7 +2,7 @@ package rules
|
|||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
|
||||
C "github.com/metacubex/mihomo/constant"
|
||||
RC "github.com/metacubex/mihomo/rules/common"
|
||||
"github.com/metacubex/mihomo/rules/logic"
|
||||
|
@ -17,6 +17,8 @@ func ParseRule(tp, payload, target string, params []string, subRules map[string]
|
|||
parsed = RC.NewDomainSuffix(payload, target)
|
||||
case "DOMAIN-KEYWORD":
|
||||
parsed = RC.NewDomainKeyword(payload, target)
|
||||
case "DOMAIN-REGEX":
|
||||
parsed, parseErr = RC.NewDomainRegex(payload, target)
|
||||
case "GEOSITE":
|
||||
parsed, parseErr = RC.NewGEOSITE(payload, target)
|
||||
case "GEOIP":
|
||||
|
|
Loading…
Add table
Reference in a new issue