mirror of
https://github.com/MetaCubeX/mihomo.git
synced 2026-03-12 19:59:57 +00:00
Compare commits
52 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5b975275f5 | ||
|
|
166392fe17 | ||
|
|
5c6aa433ca | ||
|
|
2c55dc2557 | ||
|
|
56c0b088e8 | ||
|
|
5344e869a8 | ||
|
|
6cfaf15cbf | ||
|
|
31f0060b30 | ||
|
|
c60750d549 | ||
|
|
ebf5918e94 | ||
|
|
93ca18517c | ||
|
|
32d447ce99 | ||
|
|
617fef84ae | ||
|
|
d19199322d | ||
|
|
87795e3a07 | ||
|
|
85bb40aaf8 | ||
|
|
082bcec281 | ||
|
|
9283cb0f5f | ||
|
|
ae7967f662 | ||
|
|
01f8f2db2f | ||
|
|
255ff5e977 | ||
|
|
939e4109d7 | ||
|
|
40587b62b8 | ||
|
|
85e6d25de5 | ||
|
|
29a37f4f4b | ||
|
|
2f9a3b3469 | ||
|
|
40ea0ba098 | ||
|
|
8d7f947a80 | ||
|
|
71a8705636 | ||
|
|
c0f452b540 | ||
|
|
6c9abe16cc | ||
|
|
213d80c1e2 | ||
|
|
1db89da122 | ||
|
|
689c58f661 | ||
|
|
33590c4066 | ||
|
|
60ae9dce56 | ||
|
|
4741ac6702 | ||
|
|
ef3d7e4dd7 | ||
|
|
a1c7881229 | ||
|
|
12e3952b74 | ||
|
|
88419cbd12 | ||
|
|
4ed830330e | ||
|
|
3ed6ff9402 | ||
|
|
34de62d21d | ||
|
|
d2e255f257 | ||
|
|
a0c46bb4b7 | ||
|
|
9e3bf14b1a | ||
|
|
28c387a9b6 | ||
|
|
15eda703b4 | ||
|
|
b1d12a15db | ||
|
|
5a21bf3642 | ||
|
|
199fb8fd5d |
3
.github/workflows/build.yml
vendored
3
.github/workflows/build.yml
vendored
@@ -198,10 +198,11 @@ jobs:
|
|||||||
- name: Set variables
|
- name: Set variables
|
||||||
run: |
|
run: |
|
||||||
VERSION="${GITHUB_REF_NAME,,}-$(git rev-parse --short HEAD)"
|
VERSION="${GITHUB_REF_NAME,,}-$(git rev-parse --short HEAD)"
|
||||||
|
VERSION="${VERSION//\//-}"
|
||||||
PackageVersion="$(curl -s "https://api.github.com/repos/MetaCubeX/mihomo/releases/latest" | jq -r '.tag_name' | sed 's/v//g' | awk -F '.' '{$NF = $NF + 1; print}' OFS='.').${VERSION/-/.}"
|
PackageVersion="$(curl -s "https://api.github.com/repos/MetaCubeX/mihomo/releases/latest" | jq -r '.tag_name' | sed 's/v//g' | awk -F '.' '{$NF = $NF + 1; print}' OFS='.').${VERSION/-/.}"
|
||||||
if [ -n "${{ github.event.inputs.version }}" ]; then
|
if [ -n "${{ github.event.inputs.version }}" ]; then
|
||||||
VERSION=${{ github.event.inputs.version }}
|
VERSION=${{ github.event.inputs.version }}
|
||||||
PackageVersion="${VERSION#v}" >> $GITHUB_ENV
|
PackageVersion="${VERSION#v}"
|
||||||
fi
|
fi
|
||||||
echo "VERSION=${VERSION}" >> $GITHUB_ENV
|
echo "VERSION=${VERSION}" >> $GITHUB_ENV
|
||||||
echo "PackageVersion=${PackageVersion}" >> $GITHUB_ENV
|
echo "PackageVersion=${PackageVersion}" >> $GITHUB_ENV
|
||||||
|
|||||||
3
.github/workflows/trigger-cmfa-update.yml
vendored
3
.github/workflows/trigger-cmfa-update.yml
vendored
@@ -10,9 +10,6 @@ on:
|
|||||||
- Alpha
|
- Alpha
|
||||||
tags:
|
tags:
|
||||||
- "v*"
|
- "v*"
|
||||||
pull_request_target:
|
|
||||||
branches:
|
|
||||||
- Alpha
|
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
# Send "core-updated" to MetaCubeX/ClashMetaForAndroid to trigger update-dependencies
|
# Send "core-updated" to MetaCubeX/ClashMetaForAndroid to trigger update-dependencies
|
||||||
|
|||||||
@@ -7,9 +7,7 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"net"
|
"net"
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/netip"
|
|
||||||
"net/url"
|
"net/url"
|
||||||
"strconv"
|
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
@@ -316,15 +314,7 @@ func urlToMetadata(rawURL string) (addr C.Metadata, err error) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
uintPort, err := strconv.ParseUint(port, 10, 16)
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
addr = C.Metadata{
|
err = addr.SetRemoteAddress(net.JoinHostPort(u.Hostname(), port))
|
||||||
Host: u.Hostname(),
|
|
||||||
DstIP: netip.Addr{},
|
|
||||||
DstPort: uint16(uintPort),
|
|
||||||
}
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,7 +4,6 @@ import (
|
|||||||
"net"
|
"net"
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/netip"
|
"net/netip"
|
||||||
"strconv"
|
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
C "github.com/metacubex/mihomo/constant"
|
C "github.com/metacubex/mihomo/constant"
|
||||||
@@ -41,23 +40,8 @@ func parseHTTPAddr(request *http.Request) *C.Metadata {
|
|||||||
// trim FQDN (#737)
|
// trim FQDN (#737)
|
||||||
host = strings.TrimRight(host, ".")
|
host = strings.TrimRight(host, ".")
|
||||||
|
|
||||||
var uint16Port uint16
|
metadata := &C.Metadata{}
|
||||||
if port, err := strconv.ParseUint(port, 10, 16); err == nil {
|
_ = metadata.SetRemoteAddress(net.JoinHostPort(host, port))
|
||||||
uint16Port = uint16(port)
|
|
||||||
}
|
|
||||||
|
|
||||||
metadata := &C.Metadata{
|
|
||||||
NetWork: C.TCP,
|
|
||||||
Host: host,
|
|
||||||
DstIP: netip.Addr{},
|
|
||||||
DstPort: uint16Port,
|
|
||||||
}
|
|
||||||
|
|
||||||
ip, err := netip.ParseAddr(host)
|
|
||||||
if err == nil {
|
|
||||||
metadata.DstIP = ip
|
|
||||||
}
|
|
||||||
|
|
||||||
return metadata
|
return metadata
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -2,7 +2,6 @@ package outbound
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"errors"
|
|
||||||
"net"
|
"net"
|
||||||
"strconv"
|
"strconv"
|
||||||
"time"
|
"time"
|
||||||
@@ -10,7 +9,6 @@ import (
|
|||||||
CN "github.com/metacubex/mihomo/common/net"
|
CN "github.com/metacubex/mihomo/common/net"
|
||||||
"github.com/metacubex/mihomo/component/dialer"
|
"github.com/metacubex/mihomo/component/dialer"
|
||||||
"github.com/metacubex/mihomo/component/proxydialer"
|
"github.com/metacubex/mihomo/component/proxydialer"
|
||||||
"github.com/metacubex/mihomo/component/resolver"
|
|
||||||
C "github.com/metacubex/mihomo/constant"
|
C "github.com/metacubex/mihomo/constant"
|
||||||
"github.com/metacubex/mihomo/transport/anytls"
|
"github.com/metacubex/mihomo/transport/anytls"
|
||||||
"github.com/metacubex/mihomo/transport/vmess"
|
"github.com/metacubex/mihomo/transport/vmess"
|
||||||
@@ -53,6 +51,10 @@ func (t *AnyTLS) DialContext(ctx context.Context, metadata *C.Metadata) (_ C.Con
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (t *AnyTLS) ListenPacketContext(ctx context.Context, metadata *C.Metadata) (_ C.PacketConn, err error) {
|
func (t *AnyTLS) ListenPacketContext(ctx context.Context, metadata *C.Metadata) (_ C.PacketConn, err error) {
|
||||||
|
if err = t.ResolveUDP(ctx, metadata); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
// create tcp
|
// create tcp
|
||||||
c, err := t.client.CreateProxy(ctx, uot.RequestDestination(2))
|
c, err := t.client.CreateProxy(ctx, uot.RequestDestination(2))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -60,13 +62,6 @@ func (t *AnyTLS) ListenPacketContext(ctx context.Context, metadata *C.Metadata)
|
|||||||
}
|
}
|
||||||
|
|
||||||
// create uot on tcp
|
// create uot on tcp
|
||||||
if !metadata.Resolved() {
|
|
||||||
ip, err := resolver.ResolveIP(ctx, metadata.Host)
|
|
||||||
if err != nil {
|
|
||||||
return nil, errors.New("can't resolve ip")
|
|
||||||
}
|
|
||||||
metadata.DstIP = ip
|
|
||||||
}
|
|
||||||
destination := M.SocksaddrFromNet(metadata.UDPAddr())
|
destination := M.SocksaddrFromNet(metadata.UDPAddr())
|
||||||
return newPacketConn(CN.NewThreadSafePacketConn(uot.NewLazyConn(c, uot.Request{Destination: destination})), t), nil
|
return newPacketConn(CN.NewThreadSafePacketConn(uot.NewLazyConn(c, uot.Request{Destination: destination})), t), nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,15 +3,16 @@ package outbound
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
"net"
|
"net"
|
||||||
"runtime"
|
"runtime"
|
||||||
"strings"
|
|
||||||
"sync"
|
"sync"
|
||||||
"syscall"
|
"syscall"
|
||||||
|
|
||||||
N "github.com/metacubex/mihomo/common/net"
|
N "github.com/metacubex/mihomo/common/net"
|
||||||
"github.com/metacubex/mihomo/common/utils"
|
"github.com/metacubex/mihomo/common/utils"
|
||||||
"github.com/metacubex/mihomo/component/dialer"
|
"github.com/metacubex/mihomo/component/dialer"
|
||||||
|
"github.com/metacubex/mihomo/component/resolver"
|
||||||
C "github.com/metacubex/mihomo/constant"
|
C "github.com/metacubex/mihomo/constant"
|
||||||
"github.com/metacubex/mihomo/log"
|
"github.com/metacubex/mihomo/log"
|
||||||
)
|
)
|
||||||
@@ -19,6 +20,7 @@ import (
|
|||||||
type ProxyAdapter interface {
|
type ProxyAdapter interface {
|
||||||
C.ProxyAdapter
|
C.ProxyAdapter
|
||||||
DialOptions() []dialer.Option
|
DialOptions() []dialer.Option
|
||||||
|
ResolveUDP(ctx context.Context, metadata *C.Metadata) error
|
||||||
}
|
}
|
||||||
|
|
||||||
type Base struct {
|
type Base struct {
|
||||||
@@ -160,6 +162,17 @@ func (b *Base) DialOptions() (opts []dialer.Option) {
|
|||||||
return opts
|
return opts
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (b *Base) ResolveUDP(ctx context.Context, metadata *C.Metadata) error {
|
||||||
|
if !metadata.Resolved() {
|
||||||
|
ip, err := resolver.ResolveIP(ctx, metadata.Host)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("can't resolve ip: %w", err)
|
||||||
|
}
|
||||||
|
metadata.DstIP = ip
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func (b *Base) Close() error {
|
func (b *Base) Close() error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@@ -203,12 +216,21 @@ func NewBase(opt BaseOption) *Base {
|
|||||||
|
|
||||||
type conn struct {
|
type conn struct {
|
||||||
N.ExtendedConn
|
N.ExtendedConn
|
||||||
chain C.Chain
|
chain C.Chain
|
||||||
actualRemoteDestination string
|
adapterAddr string
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *conn) RemoteDestination() string {
|
func (c *conn) RemoteDestination() string {
|
||||||
return c.actualRemoteDestination
|
if remoteAddr := c.RemoteAddr(); remoteAddr != nil {
|
||||||
|
m := C.Metadata{}
|
||||||
|
if err := m.SetRemoteAddr(remoteAddr); err == nil {
|
||||||
|
if m.Valid() {
|
||||||
|
return m.String()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
host, _, _ := net.SplitHostPort(c.adapterAddr)
|
||||||
|
return host
|
||||||
}
|
}
|
||||||
|
|
||||||
// Chains implements C.Connection
|
// Chains implements C.Connection
|
||||||
@@ -241,19 +263,25 @@ func NewConn(c net.Conn, a C.ProxyAdapter) C.Conn {
|
|||||||
if _, ok := c.(syscall.Conn); !ok { // exclusion system conn like *net.TCPConn
|
if _, ok := c.(syscall.Conn); !ok { // exclusion system conn like *net.TCPConn
|
||||||
c = N.NewDeadlineConn(c) // most conn from outbound can't handle readDeadline correctly
|
c = N.NewDeadlineConn(c) // most conn from outbound can't handle readDeadline correctly
|
||||||
}
|
}
|
||||||
return &conn{N.NewExtendedConn(c), []string{a.Name()}, parseRemoteDestination(a.Addr())}
|
return &conn{N.NewExtendedConn(c), []string{a.Name()}, a.Addr()}
|
||||||
}
|
}
|
||||||
|
|
||||||
type packetConn struct {
|
type packetConn struct {
|
||||||
N.EnhancePacketConn
|
N.EnhancePacketConn
|
||||||
chain C.Chain
|
chain C.Chain
|
||||||
adapterName string
|
adapterName string
|
||||||
connID string
|
connID string
|
||||||
actualRemoteDestination string
|
adapterAddr string
|
||||||
|
resolveUDP func(ctx context.Context, metadata *C.Metadata) error
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *packetConn) ResolveUDP(ctx context.Context, metadata *C.Metadata) error {
|
||||||
|
return c.resolveUDP(ctx, metadata)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *packetConn) RemoteDestination() string {
|
func (c *packetConn) RemoteDestination() string {
|
||||||
return c.actualRemoteDestination
|
host, _, _ := net.SplitHostPort(c.adapterAddr)
|
||||||
|
return host
|
||||||
}
|
}
|
||||||
|
|
||||||
// Chains implements C.Connection
|
// Chains implements C.Connection
|
||||||
@@ -287,24 +315,12 @@ func (c *packetConn) AddRef(ref any) {
|
|||||||
c.EnhancePacketConn = N.NewRefPacketConn(c.EnhancePacketConn, ref) // add ref for autoCloseProxyAdapter
|
c.EnhancePacketConn = N.NewRefPacketConn(c.EnhancePacketConn, ref) // add ref for autoCloseProxyAdapter
|
||||||
}
|
}
|
||||||
|
|
||||||
func newPacketConn(pc net.PacketConn, a C.ProxyAdapter) C.PacketConn {
|
func newPacketConn(pc net.PacketConn, a ProxyAdapter) C.PacketConn {
|
||||||
epc := N.NewEnhancePacketConn(pc)
|
epc := N.NewEnhancePacketConn(pc)
|
||||||
if _, ok := pc.(syscall.Conn); !ok { // exclusion system conn like *net.UDPConn
|
if _, ok := pc.(syscall.Conn); !ok { // exclusion system conn like *net.UDPConn
|
||||||
epc = N.NewDeadlineEnhancePacketConn(epc) // most conn from outbound can't handle readDeadline correctly
|
epc = N.NewDeadlineEnhancePacketConn(epc) // most conn from outbound can't handle readDeadline correctly
|
||||||
}
|
}
|
||||||
return &packetConn{epc, []string{a.Name()}, a.Name(), utils.NewUUIDV4().String(), parseRemoteDestination(a.Addr())}
|
return &packetConn{epc, []string{a.Name()}, a.Name(), utils.NewUUIDV4().String(), a.Addr(), a.ResolveUDP}
|
||||||
}
|
|
||||||
|
|
||||||
func parseRemoteDestination(addr string) string {
|
|
||||||
if dst, _, err := net.SplitHostPort(addr); err == nil {
|
|
||||||
return dst
|
|
||||||
} else {
|
|
||||||
if addrError, ok := err.(*net.AddrError); ok && strings.Contains(addrError.Err, "missing port") {
|
|
||||||
return dst
|
|
||||||
} else {
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type AddRef interface {
|
type AddRef interface {
|
||||||
|
|||||||
@@ -2,7 +2,8 @@ package outbound
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"errors"
|
"fmt"
|
||||||
|
|
||||||
"github.com/metacubex/mihomo/component/dialer"
|
"github.com/metacubex/mihomo/component/dialer"
|
||||||
"github.com/metacubex/mihomo/component/loopback"
|
"github.com/metacubex/mihomo/component/loopback"
|
||||||
"github.com/metacubex/mihomo/component/resolver"
|
"github.com/metacubex/mihomo/component/resolver"
|
||||||
@@ -38,13 +39,8 @@ func (d *Direct) ListenPacketContext(ctx context.Context, metadata *C.Metadata)
|
|||||||
if err := d.loopBack.CheckPacketConn(metadata); err != nil {
|
if err := d.loopBack.CheckPacketConn(metadata); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
// net.UDPConn.WriteTo only working with *net.UDPAddr, so we need a net.UDPAddr
|
if err := d.ResolveUDP(ctx, metadata); err != nil {
|
||||||
if !metadata.Resolved() {
|
return nil, err
|
||||||
ip, err := resolver.ResolveIPWithResolver(ctx, metadata.Host, resolver.DirectHostResolver)
|
|
||||||
if err != nil {
|
|
||||||
return nil, errors.New("can't resolve ip")
|
|
||||||
}
|
|
||||||
metadata.DstIP = ip
|
|
||||||
}
|
}
|
||||||
pc, err := dialer.NewDialer(d.DialOptions()...).ListenPacket(ctx, "udp", "", metadata.AddrPort())
|
pc, err := dialer.NewDialer(d.DialOptions()...).ListenPacket(ctx, "udp", "", metadata.AddrPort())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -53,6 +49,17 @@ func (d *Direct) ListenPacketContext(ctx context.Context, metadata *C.Metadata)
|
|||||||
return d.loopBack.NewPacketConn(newPacketConn(pc, d)), nil
|
return d.loopBack.NewPacketConn(newPacketConn(pc, d)), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (d *Direct) ResolveUDP(ctx context.Context, metadata *C.Metadata) error {
|
||||||
|
if (!metadata.Resolved() || resolver.DirectHostResolver != resolver.DefaultResolver) && metadata.Host != "" {
|
||||||
|
ip, err := resolver.ResolveIPWithResolver(ctx, metadata.Host, resolver.DirectHostResolver)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("can't resolve ip: %w", err)
|
||||||
|
}
|
||||||
|
metadata.DstIP = ip
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func (d *Direct) IsL3Protocol(metadata *C.Metadata) bool {
|
func (d *Direct) IsL3Protocol(metadata *C.Metadata) bool {
|
||||||
return true // tell DNSDialer don't send domain to DialContext, avoid lookback to DefaultResolver
|
return true // tell DNSDialer don't send domain to DialContext, avoid lookback to DefaultResolver
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ package outbound
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"net"
|
"net"
|
||||||
|
"net/netip"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
N "github.com/metacubex/mihomo/common/net"
|
N "github.com/metacubex/mihomo/common/net"
|
||||||
@@ -31,6 +32,9 @@ func (d *Dns) DialContext(ctx context.Context, metadata *C.Metadata) (C.Conn, er
|
|||||||
// ListenPacketContext implements C.ProxyAdapter
|
// ListenPacketContext implements C.ProxyAdapter
|
||||||
func (d *Dns) ListenPacketContext(ctx context.Context, metadata *C.Metadata) (C.PacketConn, error) {
|
func (d *Dns) ListenPacketContext(ctx context.Context, metadata *C.Metadata) (C.PacketConn, error) {
|
||||||
log.Debugln("[DNS] hijack udp:%s from %s", metadata.RemoteAddress(), metadata.SourceAddrPort())
|
log.Debugln("[DNS] hijack udp:%s from %s", metadata.RemoteAddress(), metadata.SourceAddrPort())
|
||||||
|
if err := d.ResolveUDP(ctx, metadata); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
ctx, cancel := context.WithCancel(context.Background())
|
ctx, cancel := context.WithCancel(context.Background())
|
||||||
|
|
||||||
@@ -41,6 +45,13 @@ func (d *Dns) ListenPacketContext(ctx context.Context, metadata *C.Metadata) (C.
|
|||||||
}, d), nil
|
}, d), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (d *Dns) ResolveUDP(ctx context.Context, metadata *C.Metadata) error {
|
||||||
|
if !metadata.Resolved() {
|
||||||
|
metadata.DstIP = netip.AddrFrom4([4]byte{127, 0, 0, 2})
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
type dnsPacket struct {
|
type dnsPacket struct {
|
||||||
data []byte
|
data []byte
|
||||||
put func()
|
put func()
|
||||||
|
|||||||
@@ -1,10 +1,12 @@
|
|||||||
package outbound
|
package outbound
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"encoding/base64"
|
"encoding/base64"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
"github.com/metacubex/mihomo/component/ech"
|
"github.com/metacubex/mihomo/component/ech"
|
||||||
|
"github.com/metacubex/mihomo/component/resolver"
|
||||||
)
|
)
|
||||||
|
|
||||||
type ECHOptions struct {
|
type ECHOptions struct {
|
||||||
@@ -22,7 +24,13 @@ func (o ECHOptions) Parse() (*ech.Config, error) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("base64 decode ech config string failed: %v", err)
|
return nil, fmt.Errorf("base64 decode ech config string failed: %v", err)
|
||||||
}
|
}
|
||||||
echConfig.EncryptedClientHelloConfigList = list
|
echConfig.GetEncryptedClientHelloConfigList = func(ctx context.Context, serverName string) ([]byte, error) {
|
||||||
|
return list, nil
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
echConfig.GetEncryptedClientHelloConfigList = func(ctx context.Context, serverName string) ([]byte, error) {
|
||||||
|
return resolver.ResolveECHWithResolver(ctx, serverName, resolver.ProxyServerHostResolver)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return echConfig, nil
|
return echConfig, nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -60,6 +60,9 @@ func (h *Hysteria) DialContext(ctx context.Context, metadata *C.Metadata) (C.Con
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (h *Hysteria) ListenPacketContext(ctx context.Context, metadata *C.Metadata) (C.PacketConn, error) {
|
func (h *Hysteria) ListenPacketContext(ctx context.Context, metadata *C.Metadata) (C.PacketConn, error) {
|
||||||
|
if err := h.ResolveUDP(ctx, metadata); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
udpConn, err := h.client.DialUDP(h.genHdc(ctx))
|
udpConn, err := h.client.DialUDP(h.genHdc(ctx))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
|||||||
@@ -77,6 +77,9 @@ func (h *Hysteria2) DialContext(ctx context.Context, metadata *C.Metadata) (_ C.
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (h *Hysteria2) ListenPacketContext(ctx context.Context, metadata *C.Metadata) (_ C.PacketConn, err error) {
|
func (h *Hysteria2) ListenPacketContext(ctx context.Context, metadata *C.Metadata) (_ C.PacketConn, err error) {
|
||||||
|
if err = h.ResolveUDP(ctx, metadata); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
pc, err := h.client.ListenPacket(ctx)
|
pc, err := h.client.ListenPacket(ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
|||||||
@@ -54,6 +54,9 @@ func (m *Mieru) DialContext(ctx context.Context, metadata *C.Metadata) (C.Conn,
|
|||||||
|
|
||||||
// ListenPacketContext implements C.ProxyAdapter
|
// ListenPacketContext implements C.ProxyAdapter
|
||||||
func (m *Mieru) ListenPacketContext(ctx context.Context, metadata *C.Metadata) (_ C.PacketConn, err error) {
|
func (m *Mieru) ListenPacketContext(ctx context.Context, metadata *C.Metadata) (_ C.PacketConn, err error) {
|
||||||
|
if err = m.ResolveUDP(ctx, metadata); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
if err := m.ensureClientIsRunning(); err != nil {
|
if err := m.ensureClientIsRunning(); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"io"
|
"io"
|
||||||
"net"
|
"net"
|
||||||
|
"net/netip"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/metacubex/mihomo/common/buf"
|
"github.com/metacubex/mihomo/common/buf"
|
||||||
@@ -29,9 +30,19 @@ func (r *Reject) DialContext(ctx context.Context, metadata *C.Metadata) (C.Conn,
|
|||||||
|
|
||||||
// ListenPacketContext implements C.ProxyAdapter
|
// ListenPacketContext implements C.ProxyAdapter
|
||||||
func (r *Reject) ListenPacketContext(ctx context.Context, metadata *C.Metadata) (C.PacketConn, error) {
|
func (r *Reject) ListenPacketContext(ctx context.Context, metadata *C.Metadata) (C.PacketConn, error) {
|
||||||
|
if err := r.ResolveUDP(ctx, metadata); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
return newPacketConn(&nopPacketConn{}, r), nil
|
return newPacketConn(&nopPacketConn{}, r), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (r *Reject) ResolveUDP(ctx context.Context, metadata *C.Metadata) error {
|
||||||
|
if !metadata.Resolved() {
|
||||||
|
metadata.DstIP = netip.IPv4Unspecified()
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func NewRejectWithOption(option RejectOption) *Reject {
|
func NewRejectWithOption(option RejectOption) *Reject {
|
||||||
return &Reject{
|
return &Reject{
|
||||||
Base: &Base{
|
Base: &Base{
|
||||||
|
|||||||
@@ -2,7 +2,6 @@ package outbound
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"errors"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"net"
|
"net"
|
||||||
"strconv"
|
"strconv"
|
||||||
@@ -11,7 +10,6 @@ import (
|
|||||||
"github.com/metacubex/mihomo/common/structure"
|
"github.com/metacubex/mihomo/common/structure"
|
||||||
"github.com/metacubex/mihomo/component/dialer"
|
"github.com/metacubex/mihomo/component/dialer"
|
||||||
"github.com/metacubex/mihomo/component/proxydialer"
|
"github.com/metacubex/mihomo/component/proxydialer"
|
||||||
"github.com/metacubex/mihomo/component/resolver"
|
|
||||||
C "github.com/metacubex/mihomo/constant"
|
C "github.com/metacubex/mihomo/constant"
|
||||||
gost "github.com/metacubex/mihomo/transport/gost-plugin"
|
gost "github.com/metacubex/mihomo/transport/gost-plugin"
|
||||||
"github.com/metacubex/mihomo/transport/restls"
|
"github.com/metacubex/mihomo/transport/restls"
|
||||||
@@ -202,6 +200,9 @@ func (ss *ShadowSocks) ListenPacketWithDialer(ctx context.Context, dialer C.Dial
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if err = ss.ResolveUDP(ctx, metadata); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
addr, err := resolveUDPAddr(ctx, "udp", ss.addr, ss.prefer)
|
addr, err := resolveUDPAddr(ctx, "udp", ss.addr, ss.prefer)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@@ -230,15 +231,9 @@ func (ss *ShadowSocks) ProxyInfo() C.ProxyInfo {
|
|||||||
// ListenPacketOnStreamConn implements C.ProxyAdapter
|
// ListenPacketOnStreamConn implements C.ProxyAdapter
|
||||||
func (ss *ShadowSocks) ListenPacketOnStreamConn(ctx context.Context, c net.Conn, metadata *C.Metadata) (_ C.PacketConn, err error) {
|
func (ss *ShadowSocks) ListenPacketOnStreamConn(ctx context.Context, c net.Conn, metadata *C.Metadata) (_ C.PacketConn, err error) {
|
||||||
if ss.option.UDPOverTCP {
|
if ss.option.UDPOverTCP {
|
||||||
// ss uot use stream-oriented udp with a special address, so we need a net.UDPAddr
|
if err = ss.ResolveUDP(ctx, metadata); err != nil {
|
||||||
if !metadata.Resolved() {
|
return nil, err
|
||||||
ip, err := resolver.ResolveIP(ctx, metadata.Host)
|
|
||||||
if err != nil {
|
|
||||||
return nil, errors.New("can't resolve ip")
|
|
||||||
}
|
|
||||||
metadata.DstIP = ip
|
|
||||||
}
|
}
|
||||||
|
|
||||||
destination := M.SocksaddrFromNet(metadata.UDPAddr())
|
destination := M.SocksaddrFromNet(metadata.UDPAddr())
|
||||||
if ss.option.UDPOverTCPVersion == uot.LegacyVersion {
|
if ss.option.UDPOverTCPVersion == uot.LegacyVersion {
|
||||||
return newPacketConn(N.NewThreadSafePacketConn(uot.NewConn(c, uot.Request{Destination: destination})), ss), nil
|
return newPacketConn(N.NewThreadSafePacketConn(uot.NewConn(c, uot.Request{Destination: destination})), ss), nil
|
||||||
|
|||||||
@@ -105,6 +105,9 @@ func (ssr *ShadowSocksR) ListenPacketWithDialer(ctx context.Context, dialer C.Di
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if err = ssr.ResolveUDP(ctx, metadata); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
addr, err := resolveUDPAddr(ctx, "udp", ssr.addr, ssr.prefer)
|
addr, err := resolveUDPAddr(ctx, "udp", ssr.addr, ssr.prefer)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
|||||||
@@ -2,12 +2,10 @@ package outbound
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"errors"
|
|
||||||
|
|
||||||
CN "github.com/metacubex/mihomo/common/net"
|
CN "github.com/metacubex/mihomo/common/net"
|
||||||
"github.com/metacubex/mihomo/component/dialer"
|
"github.com/metacubex/mihomo/component/dialer"
|
||||||
"github.com/metacubex/mihomo/component/proxydialer"
|
"github.com/metacubex/mihomo/component/proxydialer"
|
||||||
"github.com/metacubex/mihomo/component/resolver"
|
|
||||||
C "github.com/metacubex/mihomo/constant"
|
C "github.com/metacubex/mihomo/constant"
|
||||||
"github.com/metacubex/mihomo/log"
|
"github.com/metacubex/mihomo/log"
|
||||||
|
|
||||||
@@ -53,16 +51,9 @@ func (s *SingMux) ListenPacketContext(ctx context.Context, metadata *C.Metadata)
|
|||||||
if s.onlyTcp {
|
if s.onlyTcp {
|
||||||
return s.ProxyAdapter.ListenPacketContext(ctx, metadata)
|
return s.ProxyAdapter.ListenPacketContext(ctx, metadata)
|
||||||
}
|
}
|
||||||
|
if err = s.ProxyAdapter.ResolveUDP(ctx, metadata); err != nil {
|
||||||
// sing-mux use stream-oriented udp with a special address, so we need a net.UDPAddr
|
return nil, err
|
||||||
if !metadata.Resolved() {
|
|
||||||
ip, err := resolver.ResolveIP(ctx, metadata.Host)
|
|
||||||
if err != nil {
|
|
||||||
return nil, errors.New("can't resolve ip")
|
|
||||||
}
|
|
||||||
metadata.DstIP = ip
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pc, err := s.client.ListenPacket(ctx, M.SocksaddrFromNet(metadata.UDPAddr()))
|
pc, err := s.client.ListenPacket(ctx, M.SocksaddrFromNet(metadata.UDPAddr()))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
|||||||
@@ -127,6 +127,9 @@ func (s *Snell) ListenPacketWithDialer(ctx context.Context, dialer C.Dialer, met
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if err = s.ResolveUDP(ctx, metadata); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
c, err := dialer.DialContext(ctx, "tcp", s.addr)
|
c, err := dialer.DialContext(ctx, "tcp", s.addr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
|||||||
@@ -109,6 +109,9 @@ func (ss *Socks5) ListenPacketContext(ctx context.Context, metadata *C.Metadata)
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if err = ss.ResolveUDP(ctx, metadata); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
c, err := cDialer.DialContext(ctx, "tcp", ss.addr)
|
c, err := cDialer.DialContext(ctx, "tcp", ss.addr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
err = fmt.Errorf("%s connect error: %w", ss.addr, err)
|
err = fmt.Errorf("%s connect error: %w", ss.addr, err)
|
||||||
|
|||||||
@@ -219,6 +219,10 @@ func (t *Trojan) DialContextWithDialer(ctx context.Context, dialer C.Dialer, met
|
|||||||
|
|
||||||
// ListenPacketContext implements C.ProxyAdapter
|
// ListenPacketContext implements C.ProxyAdapter
|
||||||
func (t *Trojan) ListenPacketContext(ctx context.Context, metadata *C.Metadata) (_ C.PacketConn, err error) {
|
func (t *Trojan) ListenPacketContext(ctx context.Context, metadata *C.Metadata) (_ C.PacketConn, err error) {
|
||||||
|
if err = t.ResolveUDP(ctx, metadata); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
var c net.Conn
|
var c net.Conn
|
||||||
|
|
||||||
// grpc transport
|
// grpc transport
|
||||||
@@ -250,6 +254,9 @@ func (t *Trojan) ListenPacketWithDialer(ctx context.Context, dialer C.Dialer, me
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if err = t.ResolveUDP(ctx, metadata); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
c, err := dialer.DialContext(ctx, "tcp", t.addr)
|
c, err := dialer.DialContext(ctx, "tcp", t.addr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("%s connect error: %w", t.addr, err)
|
return nil, fmt.Errorf("%s connect error: %w", t.addr, err)
|
||||||
@@ -271,12 +278,6 @@ func (t *Trojan) SupportWithDialer() C.NetWork {
|
|||||||
return C.ALLNet
|
return C.ALLNet
|
||||||
}
|
}
|
||||||
|
|
||||||
// ListenPacketOnStreamConn implements C.ProxyAdapter
|
|
||||||
func (t *Trojan) ListenPacketOnStreamConn(c net.Conn, metadata *C.Metadata) (_ C.PacketConn, err error) {
|
|
||||||
pc := trojan.NewPacketConn(c)
|
|
||||||
return newPacketConn(pc, t), err
|
|
||||||
}
|
|
||||||
|
|
||||||
// SupportUOT implements C.ProxyAdapter
|
// SupportUOT implements C.ProxyAdapter
|
||||||
func (t *Trojan) SupportUOT() bool {
|
func (t *Trojan) SupportUOT() bool {
|
||||||
return true
|
return true
|
||||||
|
|||||||
@@ -3,7 +3,6 @@ package outbound
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"crypto/tls"
|
"crypto/tls"
|
||||||
"errors"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"math"
|
"math"
|
||||||
"net"
|
"net"
|
||||||
@@ -14,7 +13,6 @@ import (
|
|||||||
"github.com/metacubex/mihomo/component/dialer"
|
"github.com/metacubex/mihomo/component/dialer"
|
||||||
"github.com/metacubex/mihomo/component/ech"
|
"github.com/metacubex/mihomo/component/ech"
|
||||||
"github.com/metacubex/mihomo/component/proxydialer"
|
"github.com/metacubex/mihomo/component/proxydialer"
|
||||||
"github.com/metacubex/mihomo/component/resolver"
|
|
||||||
tlsC "github.com/metacubex/mihomo/component/tls"
|
tlsC "github.com/metacubex/mihomo/component/tls"
|
||||||
C "github.com/metacubex/mihomo/constant"
|
C "github.com/metacubex/mihomo/constant"
|
||||||
"github.com/metacubex/mihomo/transport/tuic"
|
"github.com/metacubex/mihomo/transport/tuic"
|
||||||
@@ -91,6 +89,10 @@ func (t *Tuic) ListenPacketContext(ctx context.Context, metadata *C.Metadata) (_
|
|||||||
|
|
||||||
// ListenPacketWithDialer implements C.ProxyAdapter
|
// ListenPacketWithDialer implements C.ProxyAdapter
|
||||||
func (t *Tuic) ListenPacketWithDialer(ctx context.Context, dialer C.Dialer, metadata *C.Metadata) (_ C.PacketConn, err error) {
|
func (t *Tuic) ListenPacketWithDialer(ctx context.Context, dialer C.Dialer, metadata *C.Metadata) (_ C.PacketConn, err error) {
|
||||||
|
if err = t.ResolveUDP(ctx, metadata); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
if t.option.UDPOverStream {
|
if t.option.UDPOverStream {
|
||||||
uotDestination := uot.RequestDestination(uint8(t.option.UDPOverStreamVersion))
|
uotDestination := uot.RequestDestination(uint8(t.option.UDPOverStreamVersion))
|
||||||
uotMetadata := *metadata
|
uotMetadata := *metadata
|
||||||
@@ -102,13 +104,6 @@ func (t *Tuic) ListenPacketWithDialer(ctx context.Context, dialer C.Dialer, meta
|
|||||||
}
|
}
|
||||||
|
|
||||||
// tuic uos use stream-oriented udp with a special address, so we need a net.UDPAddr
|
// tuic uos use stream-oriented udp with a special address, so we need a net.UDPAddr
|
||||||
if !metadata.Resolved() {
|
|
||||||
ip, err := resolver.ResolveIP(ctx, metadata.Host)
|
|
||||||
if err != nil {
|
|
||||||
return nil, errors.New("can't resolve ip")
|
|
||||||
}
|
|
||||||
metadata.DstIP = ip
|
|
||||||
}
|
|
||||||
|
|
||||||
destination := M.SocksaddrFromNet(metadata.UDPAddr())
|
destination := M.SocksaddrFromNet(metadata.UDPAddr())
|
||||||
if t.option.UDPOverStreamVersion == uot.LegacyVersion {
|
if t.option.UDPOverStreamVersion == uot.LegacyVersion {
|
||||||
|
|||||||
@@ -19,7 +19,6 @@ import (
|
|||||||
"github.com/metacubex/mihomo/component/dialer"
|
"github.com/metacubex/mihomo/component/dialer"
|
||||||
"github.com/metacubex/mihomo/component/ech"
|
"github.com/metacubex/mihomo/component/ech"
|
||||||
"github.com/metacubex/mihomo/component/proxydialer"
|
"github.com/metacubex/mihomo/component/proxydialer"
|
||||||
"github.com/metacubex/mihomo/component/resolver"
|
|
||||||
tlsC "github.com/metacubex/mihomo/component/tls"
|
tlsC "github.com/metacubex/mihomo/component/tls"
|
||||||
C "github.com/metacubex/mihomo/constant"
|
C "github.com/metacubex/mihomo/constant"
|
||||||
"github.com/metacubex/mihomo/transport/gun"
|
"github.com/metacubex/mihomo/transport/gun"
|
||||||
@@ -277,13 +276,8 @@ func (v *Vless) DialContextWithDialer(ctx context.Context, dialer C.Dialer, meta
|
|||||||
|
|
||||||
// ListenPacketContext implements C.ProxyAdapter
|
// ListenPacketContext implements C.ProxyAdapter
|
||||||
func (v *Vless) ListenPacketContext(ctx context.Context, metadata *C.Metadata) (_ C.PacketConn, err error) {
|
func (v *Vless) ListenPacketContext(ctx context.Context, metadata *C.Metadata) (_ C.PacketConn, err error) {
|
||||||
// vless use stream-oriented udp with a special address, so we need a net.UDPAddr
|
if err = v.ResolveUDP(ctx, metadata); err != nil {
|
||||||
if !metadata.Resolved() {
|
return nil, err
|
||||||
ip, err := resolver.ResolveIP(ctx, metadata.Host)
|
|
||||||
if err != nil {
|
|
||||||
return nil, errors.New("can't resolve ip")
|
|
||||||
}
|
|
||||||
metadata.DstIP = ip
|
|
||||||
}
|
}
|
||||||
var c net.Conn
|
var c net.Conn
|
||||||
// gun transport
|
// gun transport
|
||||||
@@ -315,13 +309,8 @@ func (v *Vless) ListenPacketWithDialer(ctx context.Context, dialer C.Dialer, met
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// vless use stream-oriented udp with a special address, so we need a net.UDPAddr
|
if err = v.ResolveUDP(ctx, metadata); err != nil {
|
||||||
if !metadata.Resolved() {
|
return nil, err
|
||||||
ip, err := resolver.ResolveIP(ctx, metadata.Host)
|
|
||||||
if err != nil {
|
|
||||||
return nil, errors.New("can't resolve ip")
|
|
||||||
}
|
|
||||||
metadata.DstIP = ip
|
|
||||||
}
|
}
|
||||||
|
|
||||||
c, err := dialer.DialContext(ctx, "tcp", v.addr)
|
c, err := dialer.DialContext(ctx, "tcp", v.addr)
|
||||||
@@ -347,13 +336,8 @@ func (v *Vless) SupportWithDialer() C.NetWork {
|
|||||||
|
|
||||||
// ListenPacketOnStreamConn implements C.ProxyAdapter
|
// ListenPacketOnStreamConn implements C.ProxyAdapter
|
||||||
func (v *Vless) ListenPacketOnStreamConn(ctx context.Context, c net.Conn, metadata *C.Metadata) (_ C.PacketConn, err error) {
|
func (v *Vless) ListenPacketOnStreamConn(ctx context.Context, c net.Conn, metadata *C.Metadata) (_ C.PacketConn, err error) {
|
||||||
// vless use stream-oriented udp with a special address, so we need a net.UDPAddr
|
if err = v.ResolveUDP(ctx, metadata); err != nil {
|
||||||
if !metadata.Resolved() {
|
return nil, err
|
||||||
ip, err := resolver.ResolveIP(ctx, metadata.Host)
|
|
||||||
if err != nil {
|
|
||||||
return nil, errors.New("can't resolve ip")
|
|
||||||
}
|
|
||||||
metadata.DstIP = ip
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if v.option.XUDP {
|
if v.option.XUDP {
|
||||||
|
|||||||
@@ -17,7 +17,6 @@ import (
|
|||||||
"github.com/metacubex/mihomo/component/dialer"
|
"github.com/metacubex/mihomo/component/dialer"
|
||||||
"github.com/metacubex/mihomo/component/ech"
|
"github.com/metacubex/mihomo/component/ech"
|
||||||
"github.com/metacubex/mihomo/component/proxydialer"
|
"github.com/metacubex/mihomo/component/proxydialer"
|
||||||
"github.com/metacubex/mihomo/component/resolver"
|
|
||||||
tlsC "github.com/metacubex/mihomo/component/tls"
|
tlsC "github.com/metacubex/mihomo/component/tls"
|
||||||
C "github.com/metacubex/mihomo/constant"
|
C "github.com/metacubex/mihomo/constant"
|
||||||
"github.com/metacubex/mihomo/ntp"
|
"github.com/metacubex/mihomo/ntp"
|
||||||
@@ -330,13 +329,8 @@ func (v *Vmess) DialContextWithDialer(ctx context.Context, dialer C.Dialer, meta
|
|||||||
|
|
||||||
// ListenPacketContext implements C.ProxyAdapter
|
// ListenPacketContext implements C.ProxyAdapter
|
||||||
func (v *Vmess) ListenPacketContext(ctx context.Context, metadata *C.Metadata) (_ C.PacketConn, err error) {
|
func (v *Vmess) ListenPacketContext(ctx context.Context, metadata *C.Metadata) (_ C.PacketConn, err error) {
|
||||||
// vmess use stream-oriented udp with a special address, so we need a net.UDPAddr
|
if err = v.ResolveUDP(ctx, metadata); err != nil {
|
||||||
if !metadata.Resolved() {
|
return nil, err
|
||||||
ip, err := resolver.ResolveIP(ctx, metadata.Host)
|
|
||||||
if err != nil {
|
|
||||||
return nil, errors.New("can't resolve ip")
|
|
||||||
}
|
|
||||||
metadata.DstIP = ip
|
|
||||||
}
|
}
|
||||||
var c net.Conn
|
var c net.Conn
|
||||||
// gun transport
|
// gun transport
|
||||||
@@ -367,13 +361,8 @@ func (v *Vmess) ListenPacketWithDialer(ctx context.Context, dialer C.Dialer, met
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// vmess use stream-oriented udp with a special address, so we need a net.UDPAddr
|
if err = v.ResolveUDP(ctx, metadata); err != nil {
|
||||||
if !metadata.Resolved() {
|
return nil, err
|
||||||
ip, err := resolver.ResolveIP(ctx, metadata.Host)
|
|
||||||
if err != nil {
|
|
||||||
return nil, errors.New("can't resolve ip")
|
|
||||||
}
|
|
||||||
metadata.DstIP = ip
|
|
||||||
}
|
}
|
||||||
|
|
||||||
c, err := dialer.DialContext(ctx, "tcp", v.addr)
|
c, err := dialer.DialContext(ctx, "tcp", v.addr)
|
||||||
@@ -413,13 +402,8 @@ func (v *Vmess) Close() error {
|
|||||||
|
|
||||||
// ListenPacketOnStreamConn implements C.ProxyAdapter
|
// ListenPacketOnStreamConn implements C.ProxyAdapter
|
||||||
func (v *Vmess) ListenPacketOnStreamConn(ctx context.Context, c net.Conn, metadata *C.Metadata) (_ C.PacketConn, err error) {
|
func (v *Vmess) ListenPacketOnStreamConn(ctx context.Context, c net.Conn, metadata *C.Metadata) (_ C.PacketConn, err error) {
|
||||||
// vmess use stream-oriented udp with a special address, so we need a net.UDPAddr
|
if err = v.ResolveUDP(ctx, metadata); err != nil {
|
||||||
if !metadata.Resolved() {
|
return nil, err
|
||||||
ip, err := resolver.ResolveIP(ctx, metadata.Host)
|
|
||||||
if err != nil {
|
|
||||||
return nil, errors.New("can't resolve ip")
|
|
||||||
}
|
|
||||||
metadata.DstIP = ip
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if pc, ok := c.(net.PacketConn); ok {
|
if pc, ok := c.(net.PacketConn); ok {
|
||||||
|
|||||||
@@ -4,7 +4,6 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"encoding/base64"
|
"encoding/base64"
|
||||||
"encoding/hex"
|
"encoding/hex"
|
||||||
"errors"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"net"
|
"net"
|
||||||
"net/netip"
|
"net/netip"
|
||||||
@@ -520,16 +519,8 @@ func (w *WireGuard) ListenPacketContext(ctx context.Context, metadata *C.Metadat
|
|||||||
if err = w.init(ctx); err != nil {
|
if err = w.init(ctx); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
if (!metadata.Resolved() || w.resolver != nil) && metadata.Host != "" {
|
if err = w.ResolveUDP(ctx, metadata); err != nil {
|
||||||
r := resolver.DefaultResolver
|
return nil, err
|
||||||
if w.resolver != nil {
|
|
||||||
r = w.resolver
|
|
||||||
}
|
|
||||||
ip, err := resolver.ResolveIPWithResolver(ctx, metadata.Host, r)
|
|
||||||
if err != nil {
|
|
||||||
return nil, errors.New("can't resolve ip")
|
|
||||||
}
|
|
||||||
metadata.DstIP = ip
|
|
||||||
}
|
}
|
||||||
pc, err = w.tunDevice.ListenPacket(ctx, M.SocksaddrFrom(metadata.DstIP, metadata.DstPort).Unwrap())
|
pc, err = w.tunDevice.ListenPacket(ctx, M.SocksaddrFrom(metadata.DstIP, metadata.DstPort).Unwrap())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -541,6 +532,21 @@ func (w *WireGuard) ListenPacketContext(ctx context.Context, metadata *C.Metadat
|
|||||||
return newPacketConn(pc, w), nil
|
return newPacketConn(pc, w), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (w *WireGuard) ResolveUDP(ctx context.Context, metadata *C.Metadata) error {
|
||||||
|
if (!metadata.Resolved() || w.resolver != nil) && metadata.Host != "" {
|
||||||
|
r := resolver.DefaultResolver
|
||||||
|
if w.resolver != nil {
|
||||||
|
r = w.resolver
|
||||||
|
}
|
||||||
|
ip, err := resolver.ResolveIPWithResolver(ctx, metadata.Host, r)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("can't resolve ip: %w", err)
|
||||||
|
}
|
||||||
|
metadata.DstIP = ip
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
// IsL3Protocol implements C.ProxyAdapter
|
// IsL3Protocol implements C.ProxyAdapter
|
||||||
func (w *WireGuard) IsL3Protocol(metadata *C.Metadata) bool {
|
func (w *WireGuard) IsL3Protocol(metadata *C.Metadata) bool {
|
||||||
return true
|
return true
|
||||||
|
|||||||
63
common/atomic/enum.go
Normal file
63
common/atomic/enum.go
Normal file
@@ -0,0 +1,63 @@
|
|||||||
|
package atomic
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"sync/atomic"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Int32Enum[T ~int32] struct {
|
||||||
|
value atomic.Int32
|
||||||
|
}
|
||||||
|
|
||||||
|
func (i *Int32Enum[T]) MarshalJSON() ([]byte, error) {
|
||||||
|
return json.Marshal(i.Load())
|
||||||
|
}
|
||||||
|
|
||||||
|
func (i *Int32Enum[T]) UnmarshalJSON(b []byte) error {
|
||||||
|
var v T
|
||||||
|
if err := json.Unmarshal(b, &v); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
i.Store(v)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (i *Int32Enum[T]) MarshalYAML() (any, error) {
|
||||||
|
return i.Load(), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (i *Int32Enum[T]) UnmarshalYAML(unmarshal func(any) error) error {
|
||||||
|
var v T
|
||||||
|
if err := unmarshal(&v); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
i.Store(v)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (i *Int32Enum[T]) String() string {
|
||||||
|
return fmt.Sprint(i.Load())
|
||||||
|
}
|
||||||
|
|
||||||
|
func (i *Int32Enum[T]) Store(v T) {
|
||||||
|
i.value.Store(int32(v))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (i *Int32Enum[T]) Load() T {
|
||||||
|
return T(i.value.Load())
|
||||||
|
}
|
||||||
|
|
||||||
|
func (i *Int32Enum[T]) Swap(new T) T {
|
||||||
|
return T(i.value.Swap(int32(new)))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (i *Int32Enum[T]) CompareAndSwap(old, new T) bool {
|
||||||
|
return i.value.CompareAndSwap(int32(old), int32(new))
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewInt32Enum[T ~int32](v T) *Int32Enum[T] {
|
||||||
|
a := &Int32Enum[T]{}
|
||||||
|
a.Store(v)
|
||||||
|
return a
|
||||||
|
}
|
||||||
@@ -29,6 +29,19 @@ func (i *Bool) UnmarshalJSON(b []byte) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (i *Bool) MarshalYAML() (any, error) {
|
||||||
|
return i.Load(), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (i *Bool) UnmarshalYAML(unmarshal func(any) error) error {
|
||||||
|
var v bool
|
||||||
|
if err := unmarshal(&v); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
i.Store(v)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func (i *Bool) String() string {
|
func (i *Bool) String() string {
|
||||||
v := i.Load()
|
v := i.Load()
|
||||||
return strconv.FormatBool(v)
|
return strconv.FormatBool(v)
|
||||||
@@ -58,6 +71,19 @@ func (p *Pointer[T]) UnmarshalJSON(b []byte) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (p *Pointer[T]) MarshalYAML() (any, error) {
|
||||||
|
return p.Load(), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *Pointer[T]) UnmarshalYAML(unmarshal func(any) error) error {
|
||||||
|
var v *T
|
||||||
|
if err := unmarshal(&v); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
p.Store(v)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func (p *Pointer[T]) String() string {
|
func (p *Pointer[T]) String() string {
|
||||||
return fmt.Sprint(p.Load())
|
return fmt.Sprint(p.Load())
|
||||||
}
|
}
|
||||||
@@ -84,6 +110,19 @@ func (i *Int32) UnmarshalJSON(b []byte) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (i *Int32) MarshalYAML() (any, error) {
|
||||||
|
return i.Load(), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (i *Int32) UnmarshalYAML(unmarshal func(any) error) error {
|
||||||
|
var v int32
|
||||||
|
if err := unmarshal(&v); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
i.Store(v)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func (i *Int32) String() string {
|
func (i *Int32) String() string {
|
||||||
v := i.Load()
|
v := i.Load()
|
||||||
return strconv.FormatInt(int64(v), 10)
|
return strconv.FormatInt(int64(v), 10)
|
||||||
@@ -111,6 +150,19 @@ func (i *Int64) UnmarshalJSON(b []byte) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (i *Int64) MarshalYAML() (any, error) {
|
||||||
|
return i.Load(), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (i *Int64) UnmarshalYAML(unmarshal func(any) error) error {
|
||||||
|
var v int64
|
||||||
|
if err := unmarshal(&v); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
i.Store(v)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func (i *Int64) String() string {
|
func (i *Int64) String() string {
|
||||||
v := i.Load()
|
v := i.Load()
|
||||||
return strconv.FormatInt(int64(v), 10)
|
return strconv.FormatInt(int64(v), 10)
|
||||||
@@ -138,6 +190,19 @@ func (i *Uint32) UnmarshalJSON(b []byte) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (i *Uint32) MarshalYAML() (any, error) {
|
||||||
|
return i.Load(), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (i *Uint32) UnmarshalYAML(unmarshal func(any) error) error {
|
||||||
|
var v uint32
|
||||||
|
if err := unmarshal(&v); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
i.Store(v)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func (i *Uint32) String() string {
|
func (i *Uint32) String() string {
|
||||||
v := i.Load()
|
v := i.Load()
|
||||||
return strconv.FormatUint(uint64(v), 10)
|
return strconv.FormatUint(uint64(v), 10)
|
||||||
@@ -165,6 +230,19 @@ func (i *Uint64) UnmarshalJSON(b []byte) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (i *Uint64) MarshalYAML() (any, error) {
|
||||||
|
return i.Load(), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (i *Uint64) UnmarshalYAML(unmarshal func(any) error) error {
|
||||||
|
var v uint64
|
||||||
|
if err := unmarshal(&v); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
i.Store(v)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func (i *Uint64) String() string {
|
func (i *Uint64) String() string {
|
||||||
v := i.Load()
|
v := i.Load()
|
||||||
return strconv.FormatUint(uint64(v), 10)
|
return strconv.FormatUint(uint64(v), 10)
|
||||||
@@ -192,6 +270,19 @@ func (i *Uintptr) UnmarshalJSON(b []byte) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (i *Uintptr) MarshalYAML() (any, error) {
|
||||||
|
return i.Load(), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (i *Uintptr) UnmarshalYAML(unmarshal func(any) error) error {
|
||||||
|
var v uintptr
|
||||||
|
if err := unmarshal(&v); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
i.Store(v)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func (i *Uintptr) String() string {
|
func (i *Uintptr) String() string {
|
||||||
v := i.Load()
|
v := i.Load()
|
||||||
return strconv.FormatUint(uint64(v), 10)
|
return strconv.FormatUint(uint64(v), 10)
|
||||||
|
|||||||
@@ -27,11 +27,16 @@ type tValue[T any] struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (t *TypedValue[T]) Load() T {
|
func (t *TypedValue[T]) Load() T {
|
||||||
|
value, _ := t.LoadOk()
|
||||||
|
return value
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *TypedValue[T]) LoadOk() (_ T, ok bool) {
|
||||||
value := t.value.Load()
|
value := t.value.Load()
|
||||||
if value == nil {
|
if value == nil {
|
||||||
return DefaultValue[T]()
|
return DefaultValue[T](), false
|
||||||
}
|
}
|
||||||
return value.(tValue[T]).value
|
return value.(tValue[T]).value, true
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *TypedValue[T]) Store(value T) {
|
func (t *TypedValue[T]) Store(value T) {
|
||||||
@@ -47,7 +52,11 @@ func (t *TypedValue[T]) Swap(new T) T {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (t *TypedValue[T]) CompareAndSwap(old, new T) bool {
|
func (t *TypedValue[T]) CompareAndSwap(old, new T) bool {
|
||||||
return t.value.CompareAndSwap(tValue[T]{old}, tValue[T]{new})
|
return t.value.CompareAndSwap(tValue[T]{old}, tValue[T]{new}) ||
|
||||||
|
// In the edge-case where [atomic.Value.Store] is uninitialized
|
||||||
|
// and trying to compare with the zero value of T,
|
||||||
|
// then compare-and-swap with the nil any value.
|
||||||
|
(any(old) == any(DefaultValue[T]()) && t.value.CompareAndSwap(any(nil), tValue[T]{new}))
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *TypedValue[T]) MarshalJSON() ([]byte, error) {
|
func (t *TypedValue[T]) MarshalJSON() ([]byte, error) {
|
||||||
@@ -63,6 +72,19 @@ func (t *TypedValue[T]) UnmarshalJSON(b []byte) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (t *TypedValue[T]) MarshalYAML() (any, error) {
|
||||||
|
return t.Load(), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *TypedValue[T]) UnmarshalYAML(unmarshal func(any) error) error {
|
||||||
|
var v T
|
||||||
|
if err := unmarshal(&v); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
t.Store(v)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func NewTypedValue[T any](t T) (v TypedValue[T]) {
|
func NewTypedValue[T any](t T) (v TypedValue[T]) {
|
||||||
v.Store(t)
|
v.Store(t)
|
||||||
return
|
return
|
||||||
|
|||||||
77
common/atomic/value_test.go
Normal file
77
common/atomic/value_test.go
Normal file
@@ -0,0 +1,77 @@
|
|||||||
|
package atomic
|
||||||
|
|
||||||
|
import (
|
||||||
|
"io"
|
||||||
|
"os"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestTypedValue(t *testing.T) {
|
||||||
|
{
|
||||||
|
// Always wrapping should not allocate for simple values
|
||||||
|
// because tValue[T] has the same memory layout as T.
|
||||||
|
var v TypedValue[bool]
|
||||||
|
bools := []bool{true, false}
|
||||||
|
if n := int(testing.AllocsPerRun(1000, func() {
|
||||||
|
for _, b := range bools {
|
||||||
|
v.Store(b)
|
||||||
|
}
|
||||||
|
})); n != 0 {
|
||||||
|
t.Errorf("AllocsPerRun = %d, want 0", n)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
var v TypedValue[int]
|
||||||
|
got, gotOk := v.LoadOk()
|
||||||
|
if got != 0 || gotOk {
|
||||||
|
t.Fatalf("LoadOk = (%v, %v), want (0, false)", got, gotOk)
|
||||||
|
}
|
||||||
|
v.Store(1)
|
||||||
|
got, gotOk = v.LoadOk()
|
||||||
|
if got != 1 || !gotOk {
|
||||||
|
t.Fatalf("LoadOk = (%v, %v), want (1, true)", got, gotOk)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
var v TypedValue[error]
|
||||||
|
got, gotOk := v.LoadOk()
|
||||||
|
if got != nil || gotOk {
|
||||||
|
t.Fatalf("LoadOk = (%v, %v), want (nil, false)", got, gotOk)
|
||||||
|
}
|
||||||
|
v.Store(io.EOF)
|
||||||
|
got, gotOk = v.LoadOk()
|
||||||
|
if got != io.EOF || !gotOk {
|
||||||
|
t.Fatalf("LoadOk = (%v, %v), want (EOF, true)", got, gotOk)
|
||||||
|
}
|
||||||
|
err := &os.PathError{}
|
||||||
|
v.Store(err)
|
||||||
|
got, gotOk = v.LoadOk()
|
||||||
|
if got != err || !gotOk {
|
||||||
|
t.Fatalf("LoadOk = (%v, %v), want (%v, true)", got, gotOk, err)
|
||||||
|
}
|
||||||
|
v.Store(nil)
|
||||||
|
got, gotOk = v.LoadOk()
|
||||||
|
if got != nil || !gotOk {
|
||||||
|
t.Fatalf("LoadOk = (%v, %v), want (nil, true)", got, gotOk)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
c1, c2, c3 := make(chan struct{}), make(chan struct{}), make(chan struct{})
|
||||||
|
var v TypedValue[chan struct{}]
|
||||||
|
if v.CompareAndSwap(c1, c2) != false {
|
||||||
|
t.Fatalf("CompareAndSwap = true, want false")
|
||||||
|
}
|
||||||
|
if v.CompareAndSwap(nil, c1) != true {
|
||||||
|
t.Fatalf("CompareAndSwap = false, want true")
|
||||||
|
}
|
||||||
|
if v.CompareAndSwap(c2, c3) != false {
|
||||||
|
t.Fatalf("CompareAndSwap = true, want false")
|
||||||
|
}
|
||||||
|
if v.CompareAndSwap(c1, c2) != true {
|
||||||
|
t.Fatalf("CompareAndSwap = false, want true")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -2,6 +2,7 @@ package convert
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/base64"
|
"encoding/base64"
|
||||||
|
"fmt"
|
||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -43,3 +44,22 @@ func decodeUrlSafe(data string) string {
|
|||||||
}
|
}
|
||||||
return string(dcBuf)
|
return string(dcBuf)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TryDecodeBase64(s string) (decoded []byte, err error) {
|
||||||
|
if len(s)%4 == 0 {
|
||||||
|
if decoded, err = base64.StdEncoding.DecodeString(s); err == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if decoded, err = base64.URLEncoding.DecodeString(s); err == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if decoded, err = base64.RawStdEncoding.DecodeString(s); err == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if decoded, err = base64.RawURLEncoding.DecodeString(s); err == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil, fmt.Errorf("invalid base64-encoded string")
|
||||||
|
}
|
||||||
|
|||||||
@@ -456,12 +456,12 @@ func ConvertsV2Ray(buf []byte) ([]map[string]any, error) {
|
|||||||
proxies = append(proxies, ss)
|
proxies = append(proxies, ss)
|
||||||
|
|
||||||
case "ssr":
|
case "ssr":
|
||||||
dcBuf, err := encRaw.DecodeString(body)
|
dcBuf, err := TryDecodeBase64(body)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
// ssr://host:port:protocol:method:obfs:urlsafebase64pass/?obfsparam=urlsafebase64&protoparam=&remarks=urlsafebase64&group=urlsafebase64&udpport=0&uot=1
|
// ssr://host:port:protocol:method:obfs:urlsafebase64pass/?obfsparam=urlsafebase64param&protoparam=urlsafebase64param&remarks=urlsafebase64remarks&group=urlsafebase64group&udpport=0&uot=1
|
||||||
|
|
||||||
before, after, ok := strings.Cut(string(dcBuf), "/?")
|
before, after, ok := strings.Cut(string(dcBuf), "/?")
|
||||||
if !ok {
|
if !ok {
|
||||||
@@ -490,7 +490,7 @@ func ConvertsV2Ray(buf []byte) ([]map[string]any, error) {
|
|||||||
name := uniqueName(names, remarks)
|
name := uniqueName(names, remarks)
|
||||||
|
|
||||||
obfsParam := decodeUrlSafe(query.Get("obfsparam"))
|
obfsParam := decodeUrlSafe(query.Get("obfsparam"))
|
||||||
protocolParam := query.Get("protoparam")
|
protocolParam := decodeUrlSafe(query.Get("protoparam"))
|
||||||
|
|
||||||
ssr := make(map[string]any, 20)
|
ssr := make(map[string]any, 20)
|
||||||
|
|
||||||
@@ -513,6 +513,101 @@ func ConvertsV2Ray(buf []byte) ([]map[string]any, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
proxies = append(proxies, ssr)
|
proxies = append(proxies, ssr)
|
||||||
|
|
||||||
|
case "socks", "socks5", "socks5h", "http", "https":
|
||||||
|
link, err := url.Parse(line)
|
||||||
|
if err != nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
server := link.Hostname()
|
||||||
|
if server == "" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
portStr := link.Port()
|
||||||
|
if portStr == "" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
remarks := link.Fragment
|
||||||
|
if remarks == "" {
|
||||||
|
remarks = fmt.Sprintf("%s:%s", server, portStr)
|
||||||
|
}
|
||||||
|
name := uniqueName(names, remarks)
|
||||||
|
encodeStr := link.User.String()
|
||||||
|
var username, password string
|
||||||
|
if encodeStr != "" {
|
||||||
|
decodeStr := string(DecodeBase64([]byte(encodeStr)))
|
||||||
|
splitStr := strings.Split(decodeStr, ":")
|
||||||
|
|
||||||
|
// todo: should use url.QueryUnescape ?
|
||||||
|
username = splitStr[0]
|
||||||
|
if len(splitStr) == 2 {
|
||||||
|
password = splitStr[1]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
socks := make(map[string]any, 10)
|
||||||
|
socks["name"] = name
|
||||||
|
socks["type"] = func() string {
|
||||||
|
switch scheme {
|
||||||
|
case "socks", "socks5", "socks5h":
|
||||||
|
return "socks5"
|
||||||
|
case "http", "https":
|
||||||
|
return "http"
|
||||||
|
}
|
||||||
|
return scheme
|
||||||
|
}()
|
||||||
|
socks["server"] = server
|
||||||
|
socks["port"] = portStr
|
||||||
|
socks["username"] = username
|
||||||
|
socks["password"] = password
|
||||||
|
socks["skip-cert-verify"] = true
|
||||||
|
if scheme == "https" {
|
||||||
|
socks["tls"] = true
|
||||||
|
}
|
||||||
|
|
||||||
|
proxies = append(proxies, socks)
|
||||||
|
|
||||||
|
case "anytls":
|
||||||
|
// https://github.com/anytls/anytls-go/blob/main/docs/uri_scheme.md
|
||||||
|
link, err := url.Parse(line)
|
||||||
|
if err != nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
username := link.User.Username()
|
||||||
|
password, exist := link.User.Password()
|
||||||
|
if !exist {
|
||||||
|
password = username
|
||||||
|
}
|
||||||
|
query := link.Query()
|
||||||
|
server := link.Hostname()
|
||||||
|
if server == "" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
portStr := link.Port()
|
||||||
|
if portStr == "" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
insecure, sni := query.Get("insecure"), query.Get("sni")
|
||||||
|
insecureBool := insecure == "1"
|
||||||
|
fingerprint := query.Get("hpkp")
|
||||||
|
|
||||||
|
remarks := link.Fragment
|
||||||
|
if remarks == "" {
|
||||||
|
remarks = fmt.Sprintf("%s:%s", server, portStr)
|
||||||
|
}
|
||||||
|
name := uniqueName(names, remarks)
|
||||||
|
anytls := make(map[string]any, 10)
|
||||||
|
anytls["name"] = name
|
||||||
|
anytls["type"] = "anytls"
|
||||||
|
anytls["server"] = server
|
||||||
|
anytls["port"] = portStr
|
||||||
|
anytls["username"] = username
|
||||||
|
anytls["password"] = password
|
||||||
|
anytls["sni"] = sni
|
||||||
|
anytls["fingerprint"] = fingerprint
|
||||||
|
anytls["skip-cert-verify"] = insecureBool
|
||||||
|
anytls["udp"] = true
|
||||||
|
|
||||||
|
proxies = append(proxies, anytls)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ type connReadResult struct {
|
|||||||
type Conn struct {
|
type Conn struct {
|
||||||
network.ExtendedConn
|
network.ExtendedConn
|
||||||
deadline atomic.TypedValue[time.Time]
|
deadline atomic.TypedValue[time.Time]
|
||||||
pipeDeadline pipeDeadline
|
pipeDeadline PipeDeadline
|
||||||
disablePipe atomic.Bool
|
disablePipe atomic.Bool
|
||||||
inRead atomic.Bool
|
inRead atomic.Bool
|
||||||
resultCh chan *connReadResult
|
resultCh chan *connReadResult
|
||||||
@@ -34,7 +34,7 @@ func IsConn(conn any) bool {
|
|||||||
func NewConn(conn net.Conn) *Conn {
|
func NewConn(conn net.Conn) *Conn {
|
||||||
c := &Conn{
|
c := &Conn{
|
||||||
ExtendedConn: bufio.NewExtendedConn(conn),
|
ExtendedConn: bufio.NewExtendedConn(conn),
|
||||||
pipeDeadline: makePipeDeadline(),
|
pipeDeadline: MakePipeDeadline(),
|
||||||
resultCh: make(chan *connReadResult, 1),
|
resultCh: make(chan *connReadResult, 1),
|
||||||
}
|
}
|
||||||
c.resultCh <- nil
|
c.resultCh <- nil
|
||||||
@@ -58,7 +58,7 @@ func (c *Conn) Read(p []byte) (n int, err error) {
|
|||||||
c.resultCh <- nil
|
c.resultCh <- nil
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
case <-c.pipeDeadline.wait():
|
case <-c.pipeDeadline.Wait():
|
||||||
return 0, os.ErrDeadlineExceeded
|
return 0, os.ErrDeadlineExceeded
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -104,7 +104,7 @@ func (c *Conn) ReadBuffer(buffer *buf.Buffer) (err error) {
|
|||||||
c.resultCh <- nil
|
c.resultCh <- nil
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
case <-c.pipeDeadline.wait():
|
case <-c.pipeDeadline.Wait():
|
||||||
return os.ErrDeadlineExceeded
|
return os.ErrDeadlineExceeded
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -130,7 +130,7 @@ func (c *Conn) SetReadDeadline(t time.Time) error {
|
|||||||
return c.ExtendedConn.SetReadDeadline(t)
|
return c.ExtendedConn.SetReadDeadline(t)
|
||||||
}
|
}
|
||||||
c.deadline.Store(t)
|
c.deadline.Store(t)
|
||||||
c.pipeDeadline.set(t)
|
c.pipeDeadline.Set(t)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ type readResult struct {
|
|||||||
type NetPacketConn struct {
|
type NetPacketConn struct {
|
||||||
net.PacketConn
|
net.PacketConn
|
||||||
deadline atomic.TypedValue[time.Time]
|
deadline atomic.TypedValue[time.Time]
|
||||||
pipeDeadline pipeDeadline
|
pipeDeadline PipeDeadline
|
||||||
disablePipe atomic.Bool
|
disablePipe atomic.Bool
|
||||||
inRead atomic.Bool
|
inRead atomic.Bool
|
||||||
resultCh chan any
|
resultCh chan any
|
||||||
@@ -28,7 +28,7 @@ type NetPacketConn struct {
|
|||||||
func NewNetPacketConn(pc net.PacketConn) net.PacketConn {
|
func NewNetPacketConn(pc net.PacketConn) net.PacketConn {
|
||||||
npc := &NetPacketConn{
|
npc := &NetPacketConn{
|
||||||
PacketConn: pc,
|
PacketConn: pc,
|
||||||
pipeDeadline: makePipeDeadline(),
|
pipeDeadline: MakePipeDeadline(),
|
||||||
resultCh: make(chan any, 1),
|
resultCh: make(chan any, 1),
|
||||||
}
|
}
|
||||||
npc.resultCh <- nil
|
npc.resultCh <- nil
|
||||||
@@ -83,7 +83,7 @@ FOR:
|
|||||||
c.resultCh <- nil
|
c.resultCh <- nil
|
||||||
break FOR
|
break FOR
|
||||||
}
|
}
|
||||||
case <-c.pipeDeadline.wait():
|
case <-c.pipeDeadline.Wait():
|
||||||
return 0, nil, os.ErrDeadlineExceeded
|
return 0, nil, os.ErrDeadlineExceeded
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -122,7 +122,7 @@ func (c *NetPacketConn) SetReadDeadline(t time.Time) error {
|
|||||||
return c.PacketConn.SetReadDeadline(t)
|
return c.PacketConn.SetReadDeadline(t)
|
||||||
}
|
}
|
||||||
c.deadline.Store(t)
|
c.deadline.Store(t)
|
||||||
c.pipeDeadline.set(t)
|
c.pipeDeadline.Set(t)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -52,7 +52,7 @@ FOR:
|
|||||||
c.netPacketConn.resultCh <- nil
|
c.netPacketConn.resultCh <- nil
|
||||||
break FOR
|
break FOR
|
||||||
}
|
}
|
||||||
case <-c.netPacketConn.pipeDeadline.wait():
|
case <-c.netPacketConn.pipeDeadline.Wait():
|
||||||
return nil, nil, nil, os.ErrDeadlineExceeded
|
return nil, nil, nil, os.ErrDeadlineExceeded
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -69,7 +69,7 @@ FOR:
|
|||||||
c.netPacketConn.resultCh <- nil
|
c.netPacketConn.resultCh <- nil
|
||||||
break FOR
|
break FOR
|
||||||
}
|
}
|
||||||
case <-c.netPacketConn.pipeDeadline.wait():
|
case <-c.netPacketConn.pipeDeadline.Wait():
|
||||||
return M.Socksaddr{}, os.ErrDeadlineExceeded
|
return M.Socksaddr{}, os.ErrDeadlineExceeded
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -146,7 +146,7 @@ FOR:
|
|||||||
c.netPacketConn.resultCh <- nil
|
c.netPacketConn.resultCh <- nil
|
||||||
break FOR
|
break FOR
|
||||||
}
|
}
|
||||||
case <-c.netPacketConn.pipeDeadline.wait():
|
case <-c.netPacketConn.pipeDeadline.Wait():
|
||||||
return nil, M.Socksaddr{}, os.ErrDeadlineExceeded
|
return nil, M.Socksaddr{}, os.ErrDeadlineExceeded
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,24 +9,24 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
// pipeDeadline is an abstraction for handling timeouts.
|
// PipeDeadline is an abstraction for handling timeouts.
|
||||||
type pipeDeadline struct {
|
type PipeDeadline struct {
|
||||||
mu sync.Mutex // Guards timer and cancel
|
mu sync.Mutex // Guards timer and cancel
|
||||||
timer *time.Timer
|
timer *time.Timer
|
||||||
cancel chan struct{} // Must be non-nil
|
cancel chan struct{} // Must be non-nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func makePipeDeadline() pipeDeadline {
|
func MakePipeDeadline() PipeDeadline {
|
||||||
return pipeDeadline{cancel: make(chan struct{})}
|
return PipeDeadline{cancel: make(chan struct{})}
|
||||||
}
|
}
|
||||||
|
|
||||||
// set sets the point in time when the deadline will time out.
|
// Set sets the point in time when the deadline will time out.
|
||||||
// A timeout event is signaled by closing the channel returned by waiter.
|
// A timeout event is signaled by closing the channel returned by waiter.
|
||||||
// Once a timeout has occurred, the deadline can be refreshed by specifying a
|
// Once a timeout has occurred, the deadline can be refreshed by specifying a
|
||||||
// t value in the future.
|
// t value in the future.
|
||||||
//
|
//
|
||||||
// A zero value for t prevents timeout.
|
// A zero value for t prevents timeout.
|
||||||
func (d *pipeDeadline) set(t time.Time) {
|
func (d *PipeDeadline) Set(t time.Time) {
|
||||||
d.mu.Lock()
|
d.mu.Lock()
|
||||||
defer d.mu.Unlock()
|
defer d.mu.Unlock()
|
||||||
|
|
||||||
@@ -61,8 +61,8 @@ func (d *pipeDeadline) set(t time.Time) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// wait returns a channel that is closed when the deadline is exceeded.
|
// Wait returns a channel that is closed when the deadline is exceeded.
|
||||||
func (d *pipeDeadline) wait() chan struct{} {
|
func (d *PipeDeadline) Wait() chan struct{} {
|
||||||
d.mu.Lock()
|
d.mu.Lock()
|
||||||
defer d.mu.Unlock()
|
defer d.mu.Unlock()
|
||||||
return d.cancel
|
return d.cancel
|
||||||
|
|||||||
@@ -33,8 +33,8 @@ type pipe struct {
|
|||||||
localDone chan struct{}
|
localDone chan struct{}
|
||||||
remoteDone <-chan struct{}
|
remoteDone <-chan struct{}
|
||||||
|
|
||||||
readDeadline pipeDeadline
|
readDeadline PipeDeadline
|
||||||
writeDeadline pipeDeadline
|
writeDeadline PipeDeadline
|
||||||
|
|
||||||
readWaitOptions N.ReadWaitOptions
|
readWaitOptions N.ReadWaitOptions
|
||||||
}
|
}
|
||||||
@@ -56,15 +56,15 @@ func Pipe() (net.Conn, net.Conn) {
|
|||||||
rdRx: cb1, rdTx: cn1,
|
rdRx: cb1, rdTx: cn1,
|
||||||
wrTx: cb2, wrRx: cn2,
|
wrTx: cb2, wrRx: cn2,
|
||||||
localDone: done1, remoteDone: done2,
|
localDone: done1, remoteDone: done2,
|
||||||
readDeadline: makePipeDeadline(),
|
readDeadline: MakePipeDeadline(),
|
||||||
writeDeadline: makePipeDeadline(),
|
writeDeadline: MakePipeDeadline(),
|
||||||
}
|
}
|
||||||
p2 := &pipe{
|
p2 := &pipe{
|
||||||
rdRx: cb2, rdTx: cn2,
|
rdRx: cb2, rdTx: cn2,
|
||||||
wrTx: cb1, wrRx: cn1,
|
wrTx: cb1, wrRx: cn1,
|
||||||
localDone: done2, remoteDone: done1,
|
localDone: done2, remoteDone: done1,
|
||||||
readDeadline: makePipeDeadline(),
|
readDeadline: MakePipeDeadline(),
|
||||||
writeDeadline: makePipeDeadline(),
|
writeDeadline: MakePipeDeadline(),
|
||||||
}
|
}
|
||||||
return p1, p2
|
return p1, p2
|
||||||
}
|
}
|
||||||
@@ -86,7 +86,7 @@ func (p *pipe) read(b []byte) (n int, err error) {
|
|||||||
return 0, io.ErrClosedPipe
|
return 0, io.ErrClosedPipe
|
||||||
case isClosedChan(p.remoteDone):
|
case isClosedChan(p.remoteDone):
|
||||||
return 0, io.EOF
|
return 0, io.EOF
|
||||||
case isClosedChan(p.readDeadline.wait()):
|
case isClosedChan(p.readDeadline.Wait()):
|
||||||
return 0, os.ErrDeadlineExceeded
|
return 0, os.ErrDeadlineExceeded
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -99,7 +99,7 @@ func (p *pipe) read(b []byte) (n int, err error) {
|
|||||||
return 0, io.ErrClosedPipe
|
return 0, io.ErrClosedPipe
|
||||||
case <-p.remoteDone:
|
case <-p.remoteDone:
|
||||||
return 0, io.EOF
|
return 0, io.EOF
|
||||||
case <-p.readDeadline.wait():
|
case <-p.readDeadline.Wait():
|
||||||
return 0, os.ErrDeadlineExceeded
|
return 0, os.ErrDeadlineExceeded
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -118,7 +118,7 @@ func (p *pipe) write(b []byte) (n int, err error) {
|
|||||||
return 0, io.ErrClosedPipe
|
return 0, io.ErrClosedPipe
|
||||||
case isClosedChan(p.remoteDone):
|
case isClosedChan(p.remoteDone):
|
||||||
return 0, io.ErrClosedPipe
|
return 0, io.ErrClosedPipe
|
||||||
case isClosedChan(p.writeDeadline.wait()):
|
case isClosedChan(p.writeDeadline.Wait()):
|
||||||
return 0, os.ErrDeadlineExceeded
|
return 0, os.ErrDeadlineExceeded
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -134,7 +134,7 @@ func (p *pipe) write(b []byte) (n int, err error) {
|
|||||||
return n, io.ErrClosedPipe
|
return n, io.ErrClosedPipe
|
||||||
case <-p.remoteDone:
|
case <-p.remoteDone:
|
||||||
return n, io.ErrClosedPipe
|
return n, io.ErrClosedPipe
|
||||||
case <-p.writeDeadline.wait():
|
case <-p.writeDeadline.Wait():
|
||||||
return n, os.ErrDeadlineExceeded
|
return n, os.ErrDeadlineExceeded
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -145,8 +145,8 @@ func (p *pipe) SetDeadline(t time.Time) error {
|
|||||||
if isClosedChan(p.localDone) || isClosedChan(p.remoteDone) {
|
if isClosedChan(p.localDone) || isClosedChan(p.remoteDone) {
|
||||||
return io.ErrClosedPipe
|
return io.ErrClosedPipe
|
||||||
}
|
}
|
||||||
p.readDeadline.set(t)
|
p.readDeadline.Set(t)
|
||||||
p.writeDeadline.set(t)
|
p.writeDeadline.Set(t)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -154,7 +154,7 @@ func (p *pipe) SetReadDeadline(t time.Time) error {
|
|||||||
if isClosedChan(p.localDone) || isClosedChan(p.remoteDone) {
|
if isClosedChan(p.localDone) || isClosedChan(p.remoteDone) {
|
||||||
return io.ErrClosedPipe
|
return io.ErrClosedPipe
|
||||||
}
|
}
|
||||||
p.readDeadline.set(t)
|
p.readDeadline.Set(t)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -162,7 +162,7 @@ func (p *pipe) SetWriteDeadline(t time.Time) error {
|
|||||||
if isClosedChan(p.localDone) || isClosedChan(p.remoteDone) {
|
if isClosedChan(p.localDone) || isClosedChan(p.remoteDone) {
|
||||||
return io.ErrClosedPipe
|
return io.ErrClosedPipe
|
||||||
}
|
}
|
||||||
p.writeDeadline.set(t)
|
p.writeDeadline.Set(t)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -192,7 +192,7 @@ func (p *pipe) waitReadBuffer() (buffer *buf.Buffer, err error) {
|
|||||||
return nil, io.ErrClosedPipe
|
return nil, io.ErrClosedPipe
|
||||||
case isClosedChan(p.remoteDone):
|
case isClosedChan(p.remoteDone):
|
||||||
return nil, io.EOF
|
return nil, io.EOF
|
||||||
case isClosedChan(p.readDeadline.wait()):
|
case isClosedChan(p.readDeadline.Wait()):
|
||||||
return nil, os.ErrDeadlineExceeded
|
return nil, os.ErrDeadlineExceeded
|
||||||
}
|
}
|
||||||
select {
|
select {
|
||||||
@@ -211,7 +211,7 @@ func (p *pipe) waitReadBuffer() (buffer *buf.Buffer, err error) {
|
|||||||
return nil, io.ErrClosedPipe
|
return nil, io.ErrClosedPipe
|
||||||
case <-p.remoteDone:
|
case <-p.remoteDone:
|
||||||
return nil, io.EOF
|
return nil, io.EOF
|
||||||
case <-p.readDeadline.wait():
|
case <-p.readDeadline.Wait():
|
||||||
return nil, os.ErrDeadlineExceeded
|
return nil, os.ErrDeadlineExceeded
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -34,8 +34,8 @@ func (l *handleContextListener) init() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
if c, err := l.handle(l.ctx, c); err == nil {
|
if conn, err := l.handle(l.ctx, c); err == nil {
|
||||||
l.conns <- c
|
l.conns <- conn
|
||||||
} else {
|
} else {
|
||||||
// handle failed, close the underlying connection.
|
// handle failed, close the underlying connection.
|
||||||
_ = c.Close()
|
_ = c.Close()
|
||||||
|
|||||||
@@ -8,18 +8,23 @@ import (
|
|||||||
"sync"
|
"sync"
|
||||||
)
|
)
|
||||||
|
|
||||||
var defaultAllocator = NewAllocator()
|
var DefaultAllocator = NewAllocator()
|
||||||
|
|
||||||
// Allocator for incoming frames, optimized to prevent overwriting after zeroing
|
type Allocator interface {
|
||||||
type Allocator struct {
|
Get(size int) []byte
|
||||||
|
Put(buf []byte) error
|
||||||
|
}
|
||||||
|
|
||||||
|
// defaultAllocator for incoming frames, optimized to prevent overwriting after zeroing
|
||||||
|
type defaultAllocator struct {
|
||||||
buffers [11]sync.Pool
|
buffers [11]sync.Pool
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewAllocator initiates a []byte allocator for frames less than 65536 bytes,
|
// NewAllocator initiates a []byte allocator for frames less than 65536 bytes,
|
||||||
// the waste(memory fragmentation) of space allocation is guaranteed to be
|
// the waste(memory fragmentation) of space allocation is guaranteed to be
|
||||||
// no more than 50%.
|
// no more than 50%.
|
||||||
func NewAllocator() *Allocator {
|
func NewAllocator() Allocator {
|
||||||
return &Allocator{
|
return &defaultAllocator{
|
||||||
buffers: [...]sync.Pool{ // 64B -> 64K
|
buffers: [...]sync.Pool{ // 64B -> 64K
|
||||||
{New: func() any { return new([1 << 6]byte) }},
|
{New: func() any { return new([1 << 6]byte) }},
|
||||||
{New: func() any { return new([1 << 7]byte) }},
|
{New: func() any { return new([1 << 7]byte) }},
|
||||||
@@ -37,7 +42,7 @@ func NewAllocator() *Allocator {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Get a []byte from pool with most appropriate cap
|
// Get a []byte from pool with most appropriate cap
|
||||||
func (alloc *Allocator) Get(size int) []byte {
|
func (alloc *defaultAllocator) Get(size int) []byte {
|
||||||
switch {
|
switch {
|
||||||
case size < 0:
|
case size < 0:
|
||||||
panic("alloc.Get: len out of range")
|
panic("alloc.Get: len out of range")
|
||||||
@@ -87,7 +92,7 @@ func (alloc *Allocator) Get(size int) []byte {
|
|||||||
|
|
||||||
// Put returns a []byte to pool for future use,
|
// Put returns a []byte to pool for future use,
|
||||||
// which the cap must be exactly 2^n
|
// which the cap must be exactly 2^n
|
||||||
func (alloc *Allocator) Put(buf []byte) error {
|
func (alloc *defaultAllocator) Put(buf []byte) error {
|
||||||
if cap(buf) == 0 || cap(buf) > 65536 {
|
if cap(buf) == 0 || cap(buf) > 65536 {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,13 +3,12 @@
|
|||||||
package pool
|
package pool
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
// RelayBufferSize using for tcp
|
||||||
// io.Copy default buffer size is 32 KiB
|
// io.Copy default buffer size is 32 KiB
|
||||||
// but the maximum packet size of vmess/shadowsocks is about 16 KiB
|
|
||||||
// so define a buffer of 20 KiB to reduce the memory of each TCP relay
|
|
||||||
RelayBufferSize = 16 * 1024
|
RelayBufferSize = 16 * 1024
|
||||||
|
|
||||||
// RelayBufferSize uses 20KiB, but due to the allocator it will actually
|
// UDPBufferSize using for udp
|
||||||
// request 32Kib. Most UDPs are smaller than the MTU, and the TUN's MTU
|
// Most UDPs are smaller than the MTU, and the TUN's MTU
|
||||||
// set to 9000, so the UDP Buffer size set to 16Kib
|
// set to 9000, so the UDP Buffer size set to 16Kib
|
||||||
UDPBufferSize = 8 * 1024
|
UDPBufferSize = 8 * 1024
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -3,13 +3,12 @@
|
|||||||
package pool
|
package pool
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
// RelayBufferSize using for tcp
|
||||||
// io.Copy default buffer size is 32 KiB
|
// io.Copy default buffer size is 32 KiB
|
||||||
// but the maximum packet size of vmess/shadowsocks is about 16 KiB
|
RelayBufferSize = 32 * 1024
|
||||||
// so define a buffer of 20 KiB to reduce the memory of each TCP relay
|
|
||||||
RelayBufferSize = 20 * 1024
|
|
||||||
|
|
||||||
// RelayBufferSize uses 20KiB, but due to the allocator it will actually
|
// UDPBufferSize using for udp
|
||||||
// request 32Kib. Most UDPs are smaller than the MTU, and the TUN's MTU
|
// Most UDPs are smaller than the MTU, and the TUN's MTU
|
||||||
// set to 9000, so the UDP Buffer size set to 16Kib
|
// set to 9000, so the UDP Buffer size set to 16Kib
|
||||||
UDPBufferSize = 16 * 1024
|
UDPBufferSize = 16 * 1024
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
package pool
|
package pool
|
||||||
|
|
||||||
func Get(size int) []byte {
|
func Get(size int) []byte {
|
||||||
return defaultAllocator.Get(size)
|
return DefaultAllocator.Get(size)
|
||||||
}
|
}
|
||||||
|
|
||||||
func Put(buf []byte) error {
|
func Put(buf []byte) error {
|
||||||
return defaultAllocator.Put(buf)
|
return DefaultAllocator.Put(buf)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,5 +3,5 @@ package pool
|
|||||||
import "github.com/metacubex/sing/common/buf"
|
import "github.com/metacubex/sing/common/buf"
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
buf.DefaultAllocator = defaultAllocator
|
buf.DefaultAllocator = DefaultAllocator
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -239,13 +239,15 @@ func (n *num) UnmarshalText(text []byte) (err error) {
|
|||||||
|
|
||||||
func TestStructure_TextUnmarshaller(t *testing.T) {
|
func TestStructure_TextUnmarshaller(t *testing.T) {
|
||||||
rawMap := map[string]any{
|
rawMap := map[string]any{
|
||||||
"num": "255",
|
"num": "255",
|
||||||
"num_p": "127",
|
"num_p": "127",
|
||||||
|
"num_arr": []string{"1", "2", "3"},
|
||||||
}
|
}
|
||||||
|
|
||||||
s := &struct {
|
s := &struct {
|
||||||
Num num `test:"num"`
|
Num num `test:"num"`
|
||||||
NumP *num `test:"num_p"`
|
NumP *num `test:"num_p"`
|
||||||
|
NumArr []num `test:"num_arr"`
|
||||||
}{}
|
}{}
|
||||||
|
|
||||||
err := decoder.Decode(rawMap, s)
|
err := decoder.Decode(rawMap, s)
|
||||||
@@ -253,6 +255,7 @@ func TestStructure_TextUnmarshaller(t *testing.T) {
|
|||||||
assert.Equal(t, 255, s.Num.a)
|
assert.Equal(t, 255, s.Num.a)
|
||||||
assert.NotNil(t, s.NumP)
|
assert.NotNil(t, s.NumP)
|
||||||
assert.Equal(t, s.NumP.a, 127)
|
assert.Equal(t, s.NumP.a, 127)
|
||||||
|
assert.Equal(t, s.NumArr, []num{{1}, {2}, {3}})
|
||||||
|
|
||||||
// test WeaklyTypedInput
|
// test WeaklyTypedInput
|
||||||
rawMap["num"] = 256
|
rawMap["num"] = 256
|
||||||
|
|||||||
@@ -41,3 +41,11 @@ func NewAuthenticator(users []AuthUser) Authenticator {
|
|||||||
}
|
}
|
||||||
return au
|
return au
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var AlwaysValid Authenticator = alwaysValid{}
|
||||||
|
|
||||||
|
type alwaysValid struct{}
|
||||||
|
|
||||||
|
func (alwaysValid) Verify(string, string) bool { return true }
|
||||||
|
|
||||||
|
func (alwaysValid) Users() []string { return nil }
|
||||||
|
|||||||
@@ -73,7 +73,7 @@ func ListenPacket(ctx context.Context, network, address string, rAddrPort netip.
|
|||||||
}
|
}
|
||||||
if opt.interfaceName == "" {
|
if opt.interfaceName == "" {
|
||||||
if finder := DefaultInterfaceFinder.Load(); finder != nil {
|
if finder := DefaultInterfaceFinder.Load(); finder != nil {
|
||||||
opt.interfaceName = finder.FindInterfaceName(rAddrPort.Addr())
|
opt.interfaceName = finder.FindInterfaceName(rAddrPort.Addr().Unmap())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if rAddrPort.Addr().Unmap().IsLoopback() {
|
if rAddrPort.Addr().Unmap().IsLoopback() {
|
||||||
|
|||||||
@@ -4,24 +4,20 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
"github.com/metacubex/mihomo/component/resolver"
|
|
||||||
tlsC "github.com/metacubex/mihomo/component/tls"
|
tlsC "github.com/metacubex/mihomo/component/tls"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Config struct {
|
type Config struct {
|
||||||
EncryptedClientHelloConfigList []byte
|
GetEncryptedClientHelloConfigList func(ctx context.Context, serverName string) ([]byte, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (cfg *Config) ClientHandle(ctx context.Context, tlsConfig *tlsC.Config) (err error) {
|
func (cfg *Config) ClientHandle(ctx context.Context, tlsConfig *tlsC.Config) (err error) {
|
||||||
if cfg == nil {
|
if cfg == nil {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
echConfigList := cfg.EncryptedClientHelloConfigList
|
echConfigList, err := cfg.GetEncryptedClientHelloConfigList(ctx, tlsConfig.ServerName)
|
||||||
if len(echConfigList) == 0 {
|
if err != nil {
|
||||||
echConfigList, err = resolver.ResolveECH(ctx, tlsConfig.ServerName)
|
return fmt.Errorf("resolve ECH config error: %w", err)
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("resolve ECH config error: %w", err)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
tlsConfig.EncryptedClientHelloConfigList = echConfigList
|
tlsConfig.EncryptedClientHelloConfigList = echConfigList
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
package geodata
|
package geodata
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
@@ -76,13 +75,13 @@ func LoadGeoSiteMatcher(countryCode string) (router.DomainMatcher, error) {
|
|||||||
if countryCode[0] == '!' {
|
if countryCode[0] == '!' {
|
||||||
not = true
|
not = true
|
||||||
countryCode = countryCode[1:]
|
countryCode = countryCode[1:]
|
||||||
|
if countryCode == "" {
|
||||||
|
return nil, fmt.Errorf("country code could not be empty")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
countryCode = strings.ToLower(countryCode)
|
countryCode = strings.ToLower(countryCode)
|
||||||
|
|
||||||
parts := strings.Split(countryCode, "@")
|
parts := strings.Split(countryCode, "@")
|
||||||
if len(parts) == 0 {
|
|
||||||
return nil, errors.New("empty rule")
|
|
||||||
}
|
|
||||||
listName := strings.TrimSpace(parts[0])
|
listName := strings.TrimSpace(parts[0])
|
||||||
attrVal := parts[1:]
|
attrVal := parts[1:]
|
||||||
attrs := parseAttrs(attrVal)
|
attrs := parseAttrs(attrVal)
|
||||||
|
|||||||
@@ -26,8 +26,9 @@ var (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type ifaceCache struct {
|
type ifaceCache struct {
|
||||||
ifMap map[string]*Interface
|
ifMapByName map[string]*Interface
|
||||||
ifTable bart.Table[*Interface]
|
ifMapByAddr map[netip.Addr]*Interface
|
||||||
|
ifTable bart.Table[*Interface]
|
||||||
}
|
}
|
||||||
|
|
||||||
var caches = singledo.NewSingle[*ifaceCache](time.Second * 20)
|
var caches = singledo.NewSingle[*ifaceCache](time.Second * 20)
|
||||||
@@ -40,7 +41,8 @@ func getCache() (*ifaceCache, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
cache := &ifaceCache{
|
cache := &ifaceCache{
|
||||||
ifMap: make(map[string]*Interface),
|
ifMapByName: make(map[string]*Interface),
|
||||||
|
ifMapByAddr: make(map[netip.Addr]*Interface),
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, iface := range ifaces {
|
for _, iface := range ifaces {
|
||||||
@@ -78,12 +80,13 @@ func getCache() (*ifaceCache, error) {
|
|||||||
Flags: iface.Flags,
|
Flags: iface.Flags,
|
||||||
Addresses: ipNets,
|
Addresses: ipNets,
|
||||||
}
|
}
|
||||||
cache.ifMap[iface.Name] = ifaceObj
|
cache.ifMapByName[iface.Name] = ifaceObj
|
||||||
|
|
||||||
if iface.Flags&net.FlagUp == 0 {
|
if iface.Flags&net.FlagUp == 0 {
|
||||||
continue // interface down
|
continue // interface down
|
||||||
}
|
}
|
||||||
for _, prefix := range ipNets {
|
for _, prefix := range ipNets {
|
||||||
|
cache.ifMapByAddr[prefix.Addr()] = ifaceObj
|
||||||
cache.ifTable.Insert(prefix, ifaceObj)
|
cache.ifTable.Insert(prefix, ifaceObj)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -98,7 +101,7 @@ func Interfaces() (map[string]*Interface, error) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
return cache.ifMap, nil
|
return cache.ifMapByName, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func ResolveInterface(name string) (*Interface, error) {
|
func ResolveInterface(name string) (*Interface, error) {
|
||||||
@@ -120,6 +123,11 @@ func ResolveInterfaceByAddr(addr netip.Addr) (*Interface, error) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
// maybe two interfaces have the same prefix but different address
|
||||||
|
// so direct check address equal before do a route lookup (longest prefix match)
|
||||||
|
if iface, ok := cache.ifMapByAddr[addr]; ok {
|
||||||
|
return iface, nil
|
||||||
|
}
|
||||||
iface, ok := cache.ifTable.Lookup(addr)
|
iface, ok := cache.ifTable.Lookup(addr)
|
||||||
if !ok {
|
if !ok {
|
||||||
return nil, ErrIfaceNotFound
|
return nil, ErrIfaceNotFound
|
||||||
@@ -133,7 +141,8 @@ func IsLocalIp(addr netip.Addr) (bool, error) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return false, err
|
return false, err
|
||||||
}
|
}
|
||||||
return cache.ifTable.Contains(addr), nil
|
_, ok := cache.ifMapByAddr[addr]
|
||||||
|
return ok, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func FlushCache() {
|
func FlushCache() {
|
||||||
|
|||||||
@@ -1,57 +1,52 @@
|
|||||||
package process
|
package process
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
|
||||||
"errors"
|
"errors"
|
||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
FindProcessAlways = "always"
|
FindProcessStrict FindProcessMode = iota
|
||||||
FindProcessStrict = "strict"
|
FindProcessAlways
|
||||||
FindProcessOff = "off"
|
FindProcessOff
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
validModes = map[string]struct{}{
|
validModes = map[string]FindProcessMode{
|
||||||
FindProcessAlways: {},
|
FindProcessStrict.String(): FindProcessStrict,
|
||||||
FindProcessOff: {},
|
FindProcessAlways.String(): FindProcessAlways,
|
||||||
FindProcessStrict: {},
|
FindProcessOff.String(): FindProcessOff,
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
type FindProcessMode string
|
type FindProcessMode int32
|
||||||
|
|
||||||
func (m FindProcessMode) Always() bool {
|
// UnmarshalText unserialize FindProcessMode
|
||||||
return m == FindProcessAlways
|
func (m *FindProcessMode) UnmarshalText(data []byte) error {
|
||||||
}
|
return m.Set(string(data))
|
||||||
|
|
||||||
func (m FindProcessMode) Off() bool {
|
|
||||||
return m == FindProcessOff
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *FindProcessMode) UnmarshalYAML(unmarshal func(any) error) error {
|
|
||||||
var tp string
|
|
||||||
if err := unmarshal(&tp); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return m.Set(tp)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *FindProcessMode) UnmarshalJSON(data []byte) error {
|
|
||||||
var tp string
|
|
||||||
if err := json.Unmarshal(data, &tp); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return m.Set(tp)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *FindProcessMode) Set(value string) error {
|
func (m *FindProcessMode) Set(value string) error {
|
||||||
mode := strings.ToLower(value)
|
mode, exist := validModes[strings.ToLower(value)]
|
||||||
_, exist := validModes[mode]
|
|
||||||
if !exist {
|
if !exist {
|
||||||
return errors.New("invalid find process mode")
|
return errors.New("invalid find process mode")
|
||||||
}
|
}
|
||||||
*m = FindProcessMode(mode)
|
*m = mode
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// MarshalText serialize FindProcessMode
|
||||||
|
func (m FindProcessMode) MarshalText() ([]byte, error) {
|
||||||
|
return []byte(m.String()), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m FindProcessMode) String() string {
|
||||||
|
switch m {
|
||||||
|
case FindProcessAlways:
|
||||||
|
return "always"
|
||||||
|
case FindProcessOff:
|
||||||
|
return "off"
|
||||||
|
default:
|
||||||
|
return "strict"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -2,7 +2,6 @@ package proxydialer
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"errors"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"net"
|
"net"
|
||||||
"net/netip"
|
"net/netip"
|
||||||
@@ -10,7 +9,6 @@ import (
|
|||||||
|
|
||||||
N "github.com/metacubex/mihomo/common/net"
|
N "github.com/metacubex/mihomo/common/net"
|
||||||
"github.com/metacubex/mihomo/component/dialer"
|
"github.com/metacubex/mihomo/component/dialer"
|
||||||
"github.com/metacubex/mihomo/component/resolver"
|
|
||||||
C "github.com/metacubex/mihomo/constant"
|
C "github.com/metacubex/mihomo/constant"
|
||||||
"github.com/metacubex/mihomo/tunnel"
|
"github.com/metacubex/mihomo/tunnel"
|
||||||
"github.com/metacubex/mihomo/tunnel/statistic"
|
"github.com/metacubex/mihomo/tunnel/statistic"
|
||||||
@@ -40,17 +38,16 @@ func (p proxyDialer) DialContext(ctx context.Context, network, address string) (
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
if strings.Contains(network, "udp") { // using in wireguard outbound
|
if strings.Contains(network, "udp") { // using in wireguard outbound
|
||||||
if !currentMeta.Resolved() {
|
|
||||||
ip, err := resolver.ResolveIP(ctx, currentMeta.Host)
|
|
||||||
if err != nil {
|
|
||||||
return nil, errors.New("can't resolve ip")
|
|
||||||
}
|
|
||||||
currentMeta.DstIP = ip
|
|
||||||
}
|
|
||||||
pc, err := p.listenPacket(ctx, currentMeta)
|
pc, err := p.listenPacket(ctx, currentMeta)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
if !currentMeta.Resolved() { // should not happen, maybe by a wrongly implemented proxy, but we can handle this (:
|
||||||
|
err = pc.ResolveUDP(ctx, currentMeta)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
return N.NewBindPacketConn(pc, currentMeta.UDPAddr()), nil
|
return N.NewBindPacketConn(pc, currentMeta.UDPAddr()), nil
|
||||||
}
|
}
|
||||||
var conn C.Conn
|
var conn C.Conn
|
||||||
|
|||||||
@@ -77,7 +77,7 @@ func NewHostValue(value any) (HostValue, error) {
|
|||||||
isDomain = false
|
isDomain = false
|
||||||
for _, str := range valueArr {
|
for _, str := range valueArr {
|
||||||
if ip, err := netip.ParseAddr(str); err == nil {
|
if ip, err := netip.ParseAddr(str); err == nil {
|
||||||
ips = append(ips, ip)
|
ips = append(ips, ip.Unmap())
|
||||||
} else {
|
} else {
|
||||||
return HostValue{}, err
|
return HostValue{}, err
|
||||||
}
|
}
|
||||||
@@ -85,7 +85,7 @@ func NewHostValue(value any) (HostValue, error) {
|
|||||||
} else if len(valueArr) == 1 {
|
} else if len(valueArr) == 1 {
|
||||||
host := valueArr[0]
|
host := valueArr[0]
|
||||||
if ip, err := netip.ParseAddr(host); err == nil {
|
if ip, err := netip.ParseAddr(host); err == nil {
|
||||||
ips = append(ips, ip)
|
ips = append(ips, ip.Unmap())
|
||||||
isDomain = false
|
isDomain = false
|
||||||
} else {
|
} else {
|
||||||
domain = host
|
domain = host
|
||||||
|
|||||||
@@ -46,17 +46,24 @@ func RelayDnsConn(ctx context.Context, conn net.Conn, readTimeout time.Duration)
|
|||||||
ctx, cancel := context.WithTimeout(ctx, DefaultDnsRelayTimeout)
|
ctx, cancel := context.WithTimeout(ctx, DefaultDnsRelayTimeout)
|
||||||
defer cancel()
|
defer cancel()
|
||||||
inData := buff[:n]
|
inData := buff[:n]
|
||||||
msg, err := relayDnsPacket(ctx, inData, buff, 0)
|
outBuff := buff[2:]
|
||||||
|
msg, err := relayDnsPacket(ctx, inData, outBuff, 0)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
err = binary.Write(conn, binary.BigEndian, uint16(len(msg)))
|
if &msg[0] == &outBuff[0] { // msg is still in the buff
|
||||||
if err != nil {
|
binary.BigEndian.PutUint16(buff[:2], uint16(len(msg)))
|
||||||
return err
|
outBuff = buff[:2+len(msg)]
|
||||||
|
} else { // buff not big enough (WTF???)
|
||||||
|
newBuff := pool.Get(len(msg) + 2)
|
||||||
|
defer pool.Put(newBuff)
|
||||||
|
binary.BigEndian.PutUint16(newBuff[:2], uint16(len(msg)))
|
||||||
|
copy(newBuff[2:], msg)
|
||||||
|
outBuff = newBuff
|
||||||
}
|
}
|
||||||
|
|
||||||
_, err = conn.Write(msg)
|
_, err = conn.Write(outBuff)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,7 +5,6 @@ import (
|
|||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"net/netip"
|
"net/netip"
|
||||||
"strings"
|
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/metacubex/mihomo/common/utils"
|
"github.com/metacubex/mihomo/common/utils"
|
||||||
@@ -68,7 +67,8 @@ func LookupIPv4WithResolver(ctx context.Context, host string, r Resolver) ([]net
|
|||||||
|
|
||||||
ip, err := netip.ParseAddr(host)
|
ip, err := netip.ParseAddr(host)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
if ip.Is4() || ip.Is4In6() {
|
ip = ip.Unmap()
|
||||||
|
if ip.Is4() {
|
||||||
return []netip.Addr{ip}, nil
|
return []netip.Addr{ip}, nil
|
||||||
}
|
}
|
||||||
return []netip.Addr{}, ErrIPVersion
|
return []netip.Addr{}, ErrIPVersion
|
||||||
@@ -117,7 +117,8 @@ func LookupIPv6WithResolver(ctx context.Context, host string, r Resolver) ([]net
|
|||||||
}
|
}
|
||||||
|
|
||||||
if ip, err := netip.ParseAddr(host); err == nil {
|
if ip, err := netip.ParseAddr(host); err == nil {
|
||||||
if strings.Contains(host, ":") {
|
ip = ip.Unmap()
|
||||||
|
if ip.Is6() {
|
||||||
return []netip.Addr{ip}, nil
|
return []netip.Addr{ip}, nil
|
||||||
}
|
}
|
||||||
return nil, ErrIPVersion
|
return nil, ErrIPVersion
|
||||||
@@ -166,6 +167,7 @@ func LookupIPWithResolver(ctx context.Context, host string, r Resolver) ([]netip
|
|||||||
}
|
}
|
||||||
|
|
||||||
if ip, err := netip.ParseAddr(host); err == nil {
|
if ip, err := netip.ParseAddr(host); err == nil {
|
||||||
|
ip = ip.Unmap()
|
||||||
return []netip.Addr{ip}, nil
|
return []netip.Addr{ip}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -6,6 +6,8 @@ import (
|
|||||||
"net/netip"
|
"net/netip"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/metacubex/sing/common/metadata"
|
||||||
|
|
||||||
"github.com/metacubex/mihomo/common/lru"
|
"github.com/metacubex/mihomo/common/lru"
|
||||||
N "github.com/metacubex/mihomo/common/net"
|
N "github.com/metacubex/mihomo/common/net"
|
||||||
C "github.com/metacubex/mihomo/constant"
|
C "github.com/metacubex/mihomo/constant"
|
||||||
@@ -72,8 +74,16 @@ func (sd *Dispatcher) UDPSniff(packet C.PacketAdapter, packetSender C.PacketSend
|
|||||||
overrideDest := config.OverrideDest
|
overrideDest := config.OverrideDest
|
||||||
|
|
||||||
if inWhitelist {
|
if inWhitelist {
|
||||||
|
replaceDomain := func(metadata *C.Metadata, host string) {
|
||||||
|
if sd.domainCanReplace(host) {
|
||||||
|
replaceDomain(metadata, host, overrideDest)
|
||||||
|
} else {
|
||||||
|
log.Debugln("[Sniffer] Skip sni[%s]", host)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if wrapable, ok := current.(sniffer.MultiPacketSniffer); ok {
|
if wrapable, ok := current.(sniffer.MultiPacketSniffer); ok {
|
||||||
return wrapable.WrapperSender(packetSender, overrideDest)
|
return wrapable.WrapperSender(packetSender, replaceDomain)
|
||||||
}
|
}
|
||||||
|
|
||||||
host, err := current.SniffData(packet.Data())
|
host, err := current.SniffData(packet.Data())
|
||||||
@@ -81,7 +91,7 @@ func (sd *Dispatcher) UDPSniff(packet C.PacketAdapter, packetSender C.PacketSend
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
replaceDomain(metadata, host, overrideDest)
|
replaceDomain(metadata, host)
|
||||||
return packetSender
|
return packetSender
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -128,11 +138,9 @@ func (sd *Dispatcher) TCPSniff(conn *N.BufferedConn, metadata *C.Metadata) bool
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, matcher := range sd.skipDomain {
|
if !sd.domainCanReplace(host) {
|
||||||
if matcher.MatchDomain(host) {
|
log.Debugln("[Sniffer] Skip sni[%s]", host)
|
||||||
log.Debugln("[Sniffer] Skip sni[%s]", host)
|
return false
|
||||||
return false
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
sd.skipList.Delete(dst)
|
sd.skipList.Delete(dst)
|
||||||
@@ -152,10 +160,23 @@ func replaceDomain(metadata *C.Metadata, host string, overrideDest bool) {
|
|||||||
metadata.RemoteAddress(),
|
metadata.RemoteAddress(),
|
||||||
metadata.Host, host)
|
metadata.Host, host)
|
||||||
metadata.Host = host
|
metadata.Host = host
|
||||||
|
metadata.DstIP = netip.Addr{}
|
||||||
}
|
}
|
||||||
metadata.DNSMode = C.DNSNormal
|
metadata.DNSMode = C.DNSNormal
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (sd *Dispatcher) domainCanReplace(host string) bool {
|
||||||
|
if host == "." || !metadata.IsDomainName(host) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
for _, matcher := range sd.skipDomain {
|
||||||
|
if matcher.MatchDomain(host) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
func (sd *Dispatcher) Enable() bool {
|
func (sd *Dispatcher) Enable() bool {
|
||||||
return sd != nil && sd.enable
|
return sd != nil && sd.enable
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -74,24 +74,25 @@ func (sniffer *QuicSniffer) SniffData(b []byte) (string, error) {
|
|||||||
return "", ErrorUnsupportedSniffer
|
return "", ErrorUnsupportedSniffer
|
||||||
}
|
}
|
||||||
|
|
||||||
func (sniffer *QuicSniffer) WrapperSender(packetSender constant.PacketSender, override bool) constant.PacketSender {
|
func (sniffer *QuicSniffer) WrapperSender(packetSender constant.PacketSender, replaceDomain sniffer.ReplaceDomain) constant.PacketSender {
|
||||||
return &quicPacketSender{
|
return &quicPacketSender{
|
||||||
sender: packetSender,
|
PacketSender: packetSender,
|
||||||
chClose: make(chan struct{}),
|
replaceDomain: replaceDomain,
|
||||||
override: override,
|
chClose: make(chan struct{}),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var _ constant.PacketSender = (*quicPacketSender)(nil)
|
var _ constant.PacketSender = (*quicPacketSender)(nil)
|
||||||
|
|
||||||
type quicPacketSender struct {
|
type quicPacketSender struct {
|
||||||
lock sync.RWMutex
|
lock sync.RWMutex
|
||||||
ranges utils.IntRanges[uint64]
|
ranges utils.IntRanges[uint64]
|
||||||
buffer []byte
|
buffer []byte
|
||||||
result string
|
result *string
|
||||||
override bool
|
|
||||||
|
|
||||||
sender constant.PacketSender
|
replaceDomain sniffer.ReplaceDomain
|
||||||
|
|
||||||
|
constant.PacketSender
|
||||||
|
|
||||||
chClose chan struct{}
|
chClose chan struct{}
|
||||||
closed bool
|
closed bool
|
||||||
@@ -100,7 +101,7 @@ type quicPacketSender struct {
|
|||||||
// Send will send PacketAdapter nonblocking
|
// Send will send PacketAdapter nonblocking
|
||||||
// the implement must call UDPPacket.Drop() inside Send
|
// the implement must call UDPPacket.Drop() inside Send
|
||||||
func (q *quicPacketSender) Send(current constant.PacketAdapter) {
|
func (q *quicPacketSender) Send(current constant.PacketAdapter) {
|
||||||
defer q.sender.Send(current)
|
defer q.PacketSender.Send(current)
|
||||||
|
|
||||||
q.lock.RLock()
|
q.lock.RLock()
|
||||||
if q.closed {
|
if q.closed {
|
||||||
@@ -116,29 +117,27 @@ func (q *quicPacketSender) Send(current constant.PacketAdapter) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Process is a blocking loop to send PacketAdapter to PacketConn and update the WriteBackProxy
|
// DoSniff wait sniffer recv all fragments and update the domain
|
||||||
func (q *quicPacketSender) Process(conn constant.PacketConn, proxy constant.WriteBackProxy) {
|
func (q *quicPacketSender) DoSniff(metadata *constant.Metadata) error {
|
||||||
q.sender.Process(conn, proxy)
|
|
||||||
}
|
|
||||||
|
|
||||||
// ResolveUDP wait sniffer recv all fragments and update the domain
|
|
||||||
func (q *quicPacketSender) ResolveUDP(data *constant.Metadata) error {
|
|
||||||
select {
|
select {
|
||||||
case <-q.chClose:
|
case <-q.chClose:
|
||||||
q.lock.RLock()
|
q.lock.RLock()
|
||||||
replaceDomain(data, q.result, q.override)
|
if q.result != nil {
|
||||||
|
host := *q.result
|
||||||
|
q.replaceDomain(metadata, host)
|
||||||
|
}
|
||||||
q.lock.RUnlock()
|
q.lock.RUnlock()
|
||||||
break
|
break
|
||||||
case <-time.After(quicWaitConn):
|
case <-time.After(quicWaitConn):
|
||||||
q.close()
|
q.close()
|
||||||
}
|
}
|
||||||
|
|
||||||
return q.sender.ResolveUDP(data)
|
return q.PacketSender.DoSniff(metadata)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Close stop the Process loop
|
// Close stop the Process loop
|
||||||
func (q *quicPacketSender) Close() {
|
func (q *quicPacketSender) Close() {
|
||||||
q.sender.Close()
|
q.PacketSender.Close()
|
||||||
q.close()
|
q.close()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -433,7 +432,7 @@ func (q *quicPacketSender) tryAssemble() error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
q.lock.Lock()
|
q.lock.Lock()
|
||||||
q.result = *domain
|
q.result = domain
|
||||||
q.closeLocked()
|
q.closeLocked()
|
||||||
q.lock.Unlock()
|
q.lock.Unlock()
|
||||||
|
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type fakeSender struct {
|
type fakeSender struct {
|
||||||
resultCh chan *constant.Metadata
|
constant.PacketSender
|
||||||
}
|
}
|
||||||
|
|
||||||
var _ constant.PacketSender = (*fakeSender)(nil)
|
var _ constant.PacketSender = (*fakeSender)(nil)
|
||||||
@@ -22,18 +22,7 @@ func (e *fakeSender) Send(packet constant.PacketAdapter) {
|
|||||||
packet.Drop()
|
packet.Drop()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e *fakeSender) Process(constant.PacketConn, constant.WriteBackProxy) {
|
func (e *fakeSender) DoSniff(metadata *constant.Metadata) error { return nil }
|
||||||
panic("not implemented")
|
|
||||||
}
|
|
||||||
|
|
||||||
func (e *fakeSender) ResolveUDP(metadata *constant.Metadata) error {
|
|
||||||
e.resultCh <- metadata
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (e *fakeSender) Close() {
|
|
||||||
panic("not implemented")
|
|
||||||
}
|
|
||||||
|
|
||||||
type fakeUDPPacket struct {
|
type fakeUDPPacket struct {
|
||||||
data []byte
|
data []byte
|
||||||
@@ -78,23 +67,28 @@ func asPacket(data string) constant.PacketAdapter {
|
|||||||
return pktAdp
|
return pktAdp
|
||||||
}
|
}
|
||||||
|
|
||||||
func testQuicSniffer(data []string, async bool) (string, error) {
|
const fakeHost = "fake.host.com"
|
||||||
|
|
||||||
|
func testQuicSniffer(data []string, async bool) (string, string, error) {
|
||||||
q, err := NewQuicSniffer(SnifferConfig{})
|
q, err := NewQuicSniffer(SnifferConfig{})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", "", err
|
||||||
}
|
}
|
||||||
|
|
||||||
resultCh := make(chan *constant.Metadata, 1)
|
resultCh := make(chan *constant.Metadata, 1)
|
||||||
emptySender := &fakeSender{resultCh: resultCh}
|
emptySender := &fakeSender{}
|
||||||
|
|
||||||
sender := q.WrapperSender(emptySender, true)
|
sender := q.WrapperSender(emptySender, func(metadata *constant.Metadata, host string) {
|
||||||
|
replaceDomain(metadata, host, true)
|
||||||
|
})
|
||||||
|
|
||||||
go func() {
|
go func() {
|
||||||
meta := constant.Metadata{}
|
meta := constant.Metadata{Host: fakeHost}
|
||||||
err = sender.ResolveUDP(&meta)
|
err := sender.DoSniff(&meta)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
|
resultCh <- &meta
|
||||||
}()
|
}()
|
||||||
|
|
||||||
for _, d := range data {
|
for _, d := range data {
|
||||||
@@ -106,14 +100,15 @@ func testQuicSniffer(data []string, async bool) (string, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
meta := <-resultCh
|
meta := <-resultCh
|
||||||
return meta.SniffHost, nil
|
return meta.SniffHost, meta.Host, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestQuicHeaders(t *testing.T) {
|
func TestQuicHeaders(t *testing.T) {
|
||||||
|
|
||||||
cases := []struct {
|
cases := []struct {
|
||||||
input []string
|
input []string
|
||||||
domain string
|
domain string
|
||||||
|
invalid bool
|
||||||
}{
|
}{
|
||||||
//Normal domain quic sniff
|
//Normal domain quic sniff
|
||||||
{
|
{
|
||||||
@@ -171,16 +166,31 @@ func TestQuicHeaders(t *testing.T) {
|
|||||||
},
|
},
|
||||||
domain: "www.google.com",
|
domain: "www.google.com",
|
||||||
},
|
},
|
||||||
|
// invalid packet
|
||||||
|
{
|
||||||
|
input: []string{"00000000000000000000"},
|
||||||
|
invalid: true,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, test := range cases {
|
for _, test := range cases {
|
||||||
data, err := testQuicSniffer(test.input, true)
|
data, host, err := testQuicSniffer(test.input, true)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.Equal(t, test.domain, data)
|
assert.Equal(t, test.domain, data)
|
||||||
|
if test.invalid {
|
||||||
|
assert.Equal(t, fakeHost, host)
|
||||||
|
} else {
|
||||||
|
assert.Equal(t, test.domain, host)
|
||||||
|
}
|
||||||
|
|
||||||
data, err = testQuicSniffer(test.input, false)
|
data, host, err = testQuicSniffer(test.input, false)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.Equal(t, test.domain, data)
|
assert.Equal(t, test.domain, data)
|
||||||
|
if test.invalid {
|
||||||
|
assert.Equal(t, fakeHost, host)
|
||||||
|
} else {
|
||||||
|
assert.Equal(t, test.domain, host)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"net"
|
"net"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"runtime/debug"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
N "github.com/metacubex/mihomo/common/net"
|
N "github.com/metacubex/mihomo/common/net"
|
||||||
@@ -63,6 +64,7 @@ func NewListenerForHttps(l net.Listener, httpServer *http.Server, tlsConfig *Con
|
|||||||
}
|
}
|
||||||
return c, nil
|
return c, nil
|
||||||
}, func(a any) {
|
}, func(a any) {
|
||||||
log.Errorln("https server panic: %s", a)
|
stack := debug.Stack()
|
||||||
|
log.Errorln("https server panic: %s\n%s", a, stack)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -270,6 +270,7 @@ type RawTun struct {
|
|||||||
AutoRedirect bool `yaml:"auto-redirect" json:"auto-redirect,omitempty"`
|
AutoRedirect bool `yaml:"auto-redirect" json:"auto-redirect,omitempty"`
|
||||||
AutoRedirectInputMark uint32 `yaml:"auto-redirect-input-mark" json:"auto-redirect-input-mark,omitempty"`
|
AutoRedirectInputMark uint32 `yaml:"auto-redirect-input-mark" json:"auto-redirect-input-mark,omitempty"`
|
||||||
AutoRedirectOutputMark uint32 `yaml:"auto-redirect-output-mark" json:"auto-redirect-output-mark,omitempty"`
|
AutoRedirectOutputMark uint32 `yaml:"auto-redirect-output-mark" json:"auto-redirect-output-mark,omitempty"`
|
||||||
|
LoopbackAddress []netip.Addr `yaml:"loopback-address" json:"loopback-address,omitempty"`
|
||||||
StrictRoute bool `yaml:"strict-route" json:"strict-route,omitempty"`
|
StrictRoute bool `yaml:"strict-route" json:"strict-route,omitempty"`
|
||||||
RouteAddress []netip.Prefix `yaml:"route-address" json:"route-address,omitempty"`
|
RouteAddress []netip.Prefix `yaml:"route-address" json:"route-address,omitempty"`
|
||||||
RouteAddressSet []string `yaml:"route-address-set" json:"route-address-set,omitempty"`
|
RouteAddressSet []string `yaml:"route-address-set" json:"route-address-set,omitempty"`
|
||||||
@@ -1168,10 +1169,19 @@ func parseNameServer(servers []string, respectRules bool, preferH3 bool) ([]dns.
|
|||||||
return nil, fmt.Errorf("DNS NameServer[%d] format error: %s", idx, err.Error())
|
return nil, fmt.Errorf("DNS NameServer[%d] format error: %s", idx, err.Error())
|
||||||
}
|
}
|
||||||
|
|
||||||
proxyName := u.Fragment
|
var proxyName string
|
||||||
|
params := map[string]string{}
|
||||||
|
for _, s := range strings.Split(u.Fragment, "&") {
|
||||||
|
arr := strings.SplitN(s, "=", 2)
|
||||||
|
switch len(arr) {
|
||||||
|
case 1:
|
||||||
|
proxyName = arr[0]
|
||||||
|
case 2:
|
||||||
|
params[arr[0]] = arr[1]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
var addr, dnsNetType string
|
var addr, dnsNetType string
|
||||||
params := map[string]string{}
|
|
||||||
switch u.Scheme {
|
switch u.Scheme {
|
||||||
case "udp":
|
case "udp":
|
||||||
addr, err = hostWithDefaultPort(u.Host, "53")
|
addr, err = hostWithDefaultPort(u.Host, "53")
|
||||||
@@ -1189,23 +1199,8 @@ func parseNameServer(servers []string, respectRules bool, preferH3 bool) ([]dns.
|
|||||||
addr, err = hostWithDefaultPort(u.Host, "80")
|
addr, err = hostWithDefaultPort(u.Host, "80")
|
||||||
}
|
}
|
||||||
if err == nil {
|
if err == nil {
|
||||||
proxyName = ""
|
|
||||||
clearURL := url.URL{Scheme: u.Scheme, Host: addr, Path: u.Path, User: u.User}
|
clearURL := url.URL{Scheme: u.Scheme, Host: addr, Path: u.Path, User: u.User}
|
||||||
addr = clearURL.String()
|
addr = clearURL.String()
|
||||||
if len(u.Fragment) != 0 {
|
|
||||||
for _, s := range strings.Split(u.Fragment, "&") {
|
|
||||||
arr := strings.Split(s, "=")
|
|
||||||
if len(arr) == 0 {
|
|
||||||
continue
|
|
||||||
} else if len(arr) == 1 {
|
|
||||||
proxyName = arr[0]
|
|
||||||
} else if len(arr) == 2 {
|
|
||||||
params[arr[0]] = arr[1]
|
|
||||||
} else {
|
|
||||||
params[arr[0]] = strings.Join(arr[1:], "=")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
case "quic":
|
case "quic":
|
||||||
addr, err = hostWithDefaultPort(u.Host, "853")
|
addr, err = hostWithDefaultPort(u.Host, "853")
|
||||||
@@ -1563,6 +1558,7 @@ func parseTun(rawTun RawTun, general *General) error {
|
|||||||
AutoRedirect: rawTun.AutoRedirect,
|
AutoRedirect: rawTun.AutoRedirect,
|
||||||
AutoRedirectInputMark: rawTun.AutoRedirectInputMark,
|
AutoRedirectInputMark: rawTun.AutoRedirectInputMark,
|
||||||
AutoRedirectOutputMark: rawTun.AutoRedirectOutputMark,
|
AutoRedirectOutputMark: rawTun.AutoRedirectOutputMark,
|
||||||
|
LoopbackAddress: rawTun.LoopbackAddress,
|
||||||
StrictRoute: rawTun.StrictRoute,
|
StrictRoute: rawTun.StrictRoute,
|
||||||
RouteAddress: rawTun.RouteAddress,
|
RouteAddress: rawTun.RouteAddress,
|
||||||
RouteAddressSet: rawTun.RouteAddressSet,
|
RouteAddressSet: rawTun.RouteAddressSet,
|
||||||
|
|||||||
@@ -4,6 +4,8 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"net"
|
"net"
|
||||||
"net/netip"
|
"net/netip"
|
||||||
|
"os"
|
||||||
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/metacubex/mihomo/adapter/outboundgroup"
|
"github.com/metacubex/mihomo/adapter/outboundgroup"
|
||||||
@@ -150,6 +152,9 @@ func proxyGroupsDagSort(groupsConfig []map[string]any) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func verifyIP6() bool {
|
func verifyIP6() bool {
|
||||||
|
if skip, _ := strconv.ParseBool(os.Getenv("SKIP_SYSTEM_IPV6_CHECK")); skip {
|
||||||
|
return true
|
||||||
|
}
|
||||||
if iAddrs, err := net.InterfaceAddrs(); err == nil {
|
if iAddrs, err := net.InterfaceAddrs(); err == nil {
|
||||||
for _, addr := range iAddrs {
|
for _, addr := range iAddrs {
|
||||||
if prefix, err := netip.ParsePrefix(addr.String()); err == nil {
|
if prefix, err := netip.ParsePrefix(addr.String()); err == nil {
|
||||||
|
|||||||
@@ -92,8 +92,7 @@ type Conn interface {
|
|||||||
type PacketConn interface {
|
type PacketConn interface {
|
||||||
N.EnhancePacketConn
|
N.EnhancePacketConn
|
||||||
Connection
|
Connection
|
||||||
// Deprecate WriteWithMetadata because of remote resolve DNS cause TURN failed
|
ResolveUDP(ctx context.Context, metadata *Metadata) error
|
||||||
// WriteWithMetadata(p []byte, metadata *Metadata) (n int, err error)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type Dialer interface {
|
type Dialer interface {
|
||||||
@@ -319,10 +318,15 @@ type PacketSender interface {
|
|||||||
Send(PacketAdapter)
|
Send(PacketAdapter)
|
||||||
// Process is a blocking loop to send PacketAdapter to PacketConn and update the WriteBackProxy
|
// Process is a blocking loop to send PacketAdapter to PacketConn and update the WriteBackProxy
|
||||||
Process(PacketConn, WriteBackProxy)
|
Process(PacketConn, WriteBackProxy)
|
||||||
// ResolveUDP do a local resolve UDP dns blocking if metadata is not resolved
|
|
||||||
ResolveUDP(*Metadata) error
|
|
||||||
// Close stop the Process loop
|
// Close stop the Process loop
|
||||||
Close()
|
Close()
|
||||||
|
// DoSniff will blocking after sniffer work done
|
||||||
|
DoSniff(*Metadata) error
|
||||||
|
// AddMapping add a destination NAT record
|
||||||
|
AddMapping(originMetadata *Metadata, metadata *Metadata)
|
||||||
|
// RestoreReadFrom restore destination NAT for ReadFrom
|
||||||
|
// the implement must ensure returned netip.Add is valid (or just return input addr)
|
||||||
|
RestoreReadFrom(addr netip.Addr) netip.Addr
|
||||||
}
|
}
|
||||||
|
|
||||||
type NatTable interface {
|
type NatTable interface {
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
package constant
|
package constant
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
|
||||||
"errors"
|
"errors"
|
||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
@@ -22,44 +21,6 @@ const (
|
|||||||
|
|
||||||
type DNSMode int
|
type DNSMode int
|
||||||
|
|
||||||
// UnmarshalYAML unserialize EnhancedMode with yaml
|
|
||||||
func (e *DNSMode) UnmarshalYAML(unmarshal func(any) error) error {
|
|
||||||
var tp string
|
|
||||||
if err := unmarshal(&tp); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
mode, exist := DNSModeMapping[strings.ToLower(tp)]
|
|
||||||
if !exist {
|
|
||||||
return errors.New("invalid mode")
|
|
||||||
}
|
|
||||||
*e = mode
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// MarshalYAML serialize EnhancedMode with yaml
|
|
||||||
func (e DNSMode) MarshalYAML() (any, error) {
|
|
||||||
return e.String(), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// UnmarshalJSON unserialize EnhancedMode with json
|
|
||||||
func (e *DNSMode) UnmarshalJSON(data []byte) error {
|
|
||||||
var tp string
|
|
||||||
if err := json.Unmarshal(data, &tp); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
mode, exist := DNSModeMapping[strings.ToLower(tp)]
|
|
||||||
if !exist {
|
|
||||||
return errors.New("invalid mode")
|
|
||||||
}
|
|
||||||
*e = mode
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// MarshalJSON serialize EnhancedMode with json
|
|
||||||
func (e DNSMode) MarshalJSON() ([]byte, error) {
|
|
||||||
return json.Marshal(e.String())
|
|
||||||
}
|
|
||||||
|
|
||||||
// UnmarshalText unserialize EnhancedMode
|
// UnmarshalText unserialize EnhancedMode
|
||||||
func (e *DNSMode) UnmarshalText(data []byte) error {
|
func (e *DNSMode) UnmarshalText(data []byte) error {
|
||||||
mode, exist := DNSModeMapping[strings.ToLower(string(data))]
|
mode, exist := DNSModeMapping[strings.ToLower(string(data))]
|
||||||
@@ -157,40 +118,6 @@ func (e FilterMode) String() string {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e FilterMode) MarshalYAML() (interface{}, error) {
|
|
||||||
return e.String(), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (e *FilterMode) UnmarshalYAML(unmarshal func(interface{}) error) error {
|
|
||||||
var tp string
|
|
||||||
if err := unmarshal(&tp); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
mode, exist := FilterModeMapping[strings.ToLower(tp)]
|
|
||||||
if !exist {
|
|
||||||
return errors.New("invalid mode")
|
|
||||||
}
|
|
||||||
*e = mode
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (e FilterMode) MarshalJSON() ([]byte, error) {
|
|
||||||
return json.Marshal(e.String())
|
|
||||||
}
|
|
||||||
|
|
||||||
func (e *FilterMode) UnmarshalJSON(data []byte) error {
|
|
||||||
var tp string
|
|
||||||
if err := json.Unmarshal(data, &tp); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
mode, exist := FilterModeMapping[strings.ToLower(tp)]
|
|
||||||
if !exist {
|
|
||||||
return errors.New("invalid mode")
|
|
||||||
}
|
|
||||||
*e = mode
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (e FilterMode) MarshalText() ([]byte, error) {
|
func (e FilterMode) MarshalText() ([]byte, error) {
|
||||||
return []byte(e.String()), nil
|
return []byte(e.String()), nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -261,6 +261,11 @@ func (m *Metadata) Pure() *Metadata {
|
|||||||
return m
|
return m
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (m *Metadata) Clone() *Metadata {
|
||||||
|
copyM := *m
|
||||||
|
return ©M
|
||||||
|
}
|
||||||
|
|
||||||
func (m *Metadata) AddrPort() netip.AddrPort {
|
func (m *Metadata) AddrPort() netip.AddrPort {
|
||||||
return netip.AddrPortFrom(m.DstIP.Unmap(), m.DstPort)
|
return netip.AddrPortFrom(m.DstIP.Unmap(), m.DstPort)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -91,9 +91,7 @@ type RuleProvider interface {
|
|||||||
Provider
|
Provider
|
||||||
Behavior() RuleBehavior
|
Behavior() RuleBehavior
|
||||||
Count() int
|
Count() int
|
||||||
Match(*constant.Metadata) bool
|
Match(metadata *constant.Metadata, helper constant.RuleMatchHelper) bool
|
||||||
ShouldResolveIP() bool
|
|
||||||
ShouldFindProcess() bool
|
|
||||||
Strategy() any
|
Strategy() any
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -111,14 +111,17 @@ func (rt RuleType) String() string {
|
|||||||
|
|
||||||
type Rule interface {
|
type Rule interface {
|
||||||
RuleType() RuleType
|
RuleType() RuleType
|
||||||
Match(metadata *Metadata) (bool, string)
|
Match(metadata *Metadata, helper RuleMatchHelper) (bool, string)
|
||||||
Adapter() string
|
Adapter() string
|
||||||
Payload() string
|
Payload() string
|
||||||
ShouldResolveIP() bool
|
|
||||||
ShouldFindProcess() bool
|
|
||||||
ProviderNames() []string
|
ProviderNames() []string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type RuleMatchHelper struct {
|
||||||
|
ResolveIP func()
|
||||||
|
FindProcess func()
|
||||||
|
}
|
||||||
|
|
||||||
type RuleGroup interface {
|
type RuleGroup interface {
|
||||||
Rule
|
Rule
|
||||||
GetRecodeSize() int
|
GetRecodeSize() int
|
||||||
|
|||||||
@@ -10,8 +10,10 @@ type Sniffer interface {
|
|||||||
SupportPort(port uint16) bool
|
SupportPort(port uint16) bool
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type ReplaceDomain func(metadata *constant.Metadata, host string)
|
||||||
|
|
||||||
type MultiPacketSniffer interface {
|
type MultiPacketSniffer interface {
|
||||||
WrapperSender(packetSender constant.PacketSender, override bool) constant.PacketSender
|
WrapperSender(packetSender constant.PacketSender, replaceDomain ReplaceDomain) constant.PacketSender
|
||||||
}
|
}
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
package constant
|
package constant
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
|
||||||
"errors"
|
"errors"
|
||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
@@ -20,42 +19,6 @@ const (
|
|||||||
|
|
||||||
type TUNStack int
|
type TUNStack int
|
||||||
|
|
||||||
// UnmarshalYAML unserialize TUNStack with yaml
|
|
||||||
func (e *TUNStack) UnmarshalYAML(unmarshal func(any) error) error {
|
|
||||||
var tp string
|
|
||||||
if err := unmarshal(&tp); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
mode, exist := StackTypeMapping[strings.ToLower(tp)]
|
|
||||||
if !exist {
|
|
||||||
return errors.New("invalid tun stack")
|
|
||||||
}
|
|
||||||
*e = mode
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// MarshalYAML serialize TUNStack with yaml
|
|
||||||
func (e TUNStack) MarshalYAML() (any, error) {
|
|
||||||
return e.String(), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// UnmarshalJSON unserialize TUNStack with json
|
|
||||||
func (e *TUNStack) UnmarshalJSON(data []byte) error {
|
|
||||||
var tp string
|
|
||||||
json.Unmarshal(data, &tp)
|
|
||||||
mode, exist := StackTypeMapping[strings.ToLower(tp)]
|
|
||||||
if !exist {
|
|
||||||
return errors.New("invalid tun stack")
|
|
||||||
}
|
|
||||||
*e = mode
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// MarshalJSON serialize TUNStack with json
|
|
||||||
func (e TUNStack) MarshalJSON() ([]byte, error) {
|
|
||||||
return json.Marshal(e.String())
|
|
||||||
}
|
|
||||||
|
|
||||||
// UnmarshalText unserialize TUNStack
|
// UnmarshalText unserialize TUNStack
|
||||||
func (e *TUNStack) UnmarshalText(data []byte) error {
|
func (e *TUNStack) UnmarshalText(data []byte) error {
|
||||||
mode, exist := StackTypeMapping[strings.ToLower(string(data))]
|
mode, exist := StackTypeMapping[strings.ToLower(string(data))]
|
||||||
|
|||||||
@@ -6,8 +6,10 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"net"
|
"net"
|
||||||
"strings"
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/metacubex/mihomo/component/ca"
|
"github.com/metacubex/mihomo/component/ca"
|
||||||
|
C "github.com/metacubex/mihomo/constant"
|
||||||
"github.com/metacubex/mihomo/log"
|
"github.com/metacubex/mihomo/log"
|
||||||
|
|
||||||
D "github.com/miekg/dns"
|
D "github.com/miekg/dns"
|
||||||
@@ -105,3 +107,24 @@ func (c *client) ExchangeContext(ctx context.Context, m *D.Msg) (*D.Msg, error)
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (c *client) ResetConnection() {}
|
func (c *client) ResetConnection() {}
|
||||||
|
|
||||||
|
func newClient(addr string, resolver *Resolver, netType string, params map[string]string, proxyAdapter C.ProxyAdapter, proxyName string) *client {
|
||||||
|
host, port, _ := net.SplitHostPort(addr)
|
||||||
|
c := &client{
|
||||||
|
Client: &D.Client{
|
||||||
|
Net: netType,
|
||||||
|
TLSConfig: &tls.Config{
|
||||||
|
ServerName: host,
|
||||||
|
},
|
||||||
|
UDPSize: 4096,
|
||||||
|
Timeout: 5 * time.Second,
|
||||||
|
},
|
||||||
|
port: port,
|
||||||
|
host: host,
|
||||||
|
dialer: newDNSDialer(resolver, proxyAdapter, proxyName),
|
||||||
|
}
|
||||||
|
if params["skip-cert-verify"] == "true" {
|
||||||
|
c.TLSConfig.InsecureSkipVerify = true
|
||||||
|
}
|
||||||
|
return c
|
||||||
|
}
|
||||||
|
|||||||
33
dns/doh.go
33
dns/doh.go
@@ -9,7 +9,6 @@ import (
|
|||||||
"io"
|
"io"
|
||||||
"net"
|
"net"
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/netip"
|
|
||||||
"net/url"
|
"net/url"
|
||||||
"runtime"
|
"runtime"
|
||||||
"strconv"
|
"strconv"
|
||||||
@@ -71,8 +70,6 @@ type dnsOverHTTPS struct {
|
|||||||
dialer *dnsDialer
|
dialer *dnsDialer
|
||||||
addr string
|
addr string
|
||||||
skipCertVerify bool
|
skipCertVerify bool
|
||||||
ecsPrefix netip.Prefix
|
|
||||||
ecsOverride bool
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// type check
|
// type check
|
||||||
@@ -105,28 +102,6 @@ func newDoHClient(urlString string, r *Resolver, preferH3 bool, params map[strin
|
|||||||
doh.skipCertVerify = true
|
doh.skipCertVerify = true
|
||||||
}
|
}
|
||||||
|
|
||||||
if ecs := params["ecs"]; ecs != "" {
|
|
||||||
prefix, err := netip.ParsePrefix(ecs)
|
|
||||||
if err != nil {
|
|
||||||
addr, err := netip.ParseAddr(ecs)
|
|
||||||
if err != nil {
|
|
||||||
log.Warnln("DOH [%s] config with invalid ecs: %s", doh.addr, ecs)
|
|
||||||
} else {
|
|
||||||
doh.ecsPrefix = netip.PrefixFrom(addr, addr.BitLen())
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
doh.ecsPrefix = prefix
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if doh.ecsPrefix.IsValid() {
|
|
||||||
log.Debugln("DOH [%s] config with ecs: %s", doh.addr, doh.ecsPrefix)
|
|
||||||
}
|
|
||||||
|
|
||||||
if params["ecs-override"] == "true" {
|
|
||||||
doh.ecsOverride = true
|
|
||||||
}
|
|
||||||
|
|
||||||
runtime.SetFinalizer(doh, (*dnsOverHTTPS).Close)
|
runtime.SetFinalizer(doh, (*dnsOverHTTPS).Close)
|
||||||
|
|
||||||
return doh
|
return doh
|
||||||
@@ -154,10 +129,6 @@ func (doh *dnsOverHTTPS) ExchangeContext(ctx context.Context, m *D.Msg) (msg *D.
|
|||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
||||||
if doh.ecsPrefix.IsValid() {
|
|
||||||
setEdns0Subnet(m, doh.ecsPrefix, doh.ecsOverride)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check if there was already an active client before sending the request.
|
// Check if there was already an active client before sending the request.
|
||||||
// We'll only attempt to re-connect if there was one.
|
// We'll only attempt to re-connect if there was one.
|
||||||
client, isCached, err := doh.getClient(ctx)
|
client, isCached, err := doh.getClient(ctx)
|
||||||
@@ -552,8 +523,8 @@ func (doh *dnsOverHTTPS) createTransportH3(
|
|||||||
Dial: func(
|
Dial: func(
|
||||||
ctx context.Context,
|
ctx context.Context,
|
||||||
|
|
||||||
// Ignore the address and always connect to the one that we got
|
// Ignore the address and always connect to the one that we got
|
||||||
// from the bootstrapper.
|
// from the bootstrapper.
|
||||||
_ string,
|
_ string,
|
||||||
tlsCfg *tlsC.Config,
|
tlsCfg *tlsC.Config,
|
||||||
cfg *quic.Config,
|
cfg *quic.Config,
|
||||||
|
|||||||
15
dns/doq.go
15
dns/doq.go
@@ -61,15 +61,16 @@ type dnsOverQUIC struct {
|
|||||||
bytesPool *sync.Pool
|
bytesPool *sync.Pool
|
||||||
bytesPoolGuard sync.Mutex
|
bytesPoolGuard sync.Mutex
|
||||||
|
|
||||||
addr string
|
addr string
|
||||||
dialer *dnsDialer
|
dialer *dnsDialer
|
||||||
|
skipCertVerify bool
|
||||||
}
|
}
|
||||||
|
|
||||||
// type check
|
// type check
|
||||||
var _ dnsClient = (*dnsOverQUIC)(nil)
|
var _ dnsClient = (*dnsOverQUIC)(nil)
|
||||||
|
|
||||||
// newDoQ returns the DNS-over-QUIC Upstream.
|
// newDoQ returns the DNS-over-QUIC Upstream.
|
||||||
func newDoQ(resolver *Resolver, addr string, proxyAdapter C.ProxyAdapter, proxyName string) (dnsClient, error) {
|
func newDoQ(addr string, resolver *Resolver, params map[string]string, proxyAdapter C.ProxyAdapter, proxyName string) *dnsOverQUIC {
|
||||||
doq := &dnsOverQUIC{
|
doq := &dnsOverQUIC{
|
||||||
addr: addr,
|
addr: addr,
|
||||||
dialer: newDNSDialer(resolver, proxyAdapter, proxyName),
|
dialer: newDNSDialer(resolver, proxyAdapter, proxyName),
|
||||||
@@ -79,8 +80,12 @@ func newDoQ(resolver *Resolver, addr string, proxyAdapter C.ProxyAdapter, proxyN
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if params["skip-cert-verify"] == "true" {
|
||||||
|
doq.skipCertVerify = true
|
||||||
|
}
|
||||||
|
|
||||||
runtime.SetFinalizer(doq, (*dnsOverQUIC).Close)
|
runtime.SetFinalizer(doq, (*dnsOverQUIC).Close)
|
||||||
return doq, nil
|
return doq
|
||||||
}
|
}
|
||||||
|
|
||||||
// Address implements the Upstream interface for *dnsOverQUIC.
|
// Address implements the Upstream interface for *dnsOverQUIC.
|
||||||
@@ -329,7 +334,7 @@ func (doq *dnsOverQUIC) openConnection(ctx context.Context) (conn quic.Connectio
|
|||||||
tlsConfig := ca.GetGlobalTLSConfig(
|
tlsConfig := ca.GetGlobalTLSConfig(
|
||||||
&tls.Config{
|
&tls.Config{
|
||||||
ServerName: host,
|
ServerName: host,
|
||||||
InsecureSkipVerify: false,
|
InsecureSkipVerify: doq.skipCertVerify,
|
||||||
NextProtos: []string{
|
NextProtos: []string{
|
||||||
NextProtoDQ,
|
NextProtoDQ,
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -348,7 +348,8 @@ func (r *Resolver) ipExchange(ctx context.Context, m *D.Msg) (msg *D.Msg, err er
|
|||||||
func (r *Resolver) lookupIP(ctx context.Context, host string, dnsType uint16) (ips []netip.Addr, err error) {
|
func (r *Resolver) lookupIP(ctx context.Context, host string, dnsType uint16) (ips []netip.Addr, err error) {
|
||||||
ip, err := netip.ParseAddr(host)
|
ip, err := netip.ParseAddr(host)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
isIPv4 := ip.Is4() || ip.Is4In6()
|
ip = ip.Unmap()
|
||||||
|
isIPv4 := ip.Is4()
|
||||||
if dnsType == D.TypeAAAA && !isIPv4 {
|
if dnsType == D.TypeAAAA && !isIPv4 {
|
||||||
return []netip.Addr{ip}, nil
|
return []netip.Addr{ip}, nil
|
||||||
} else if dnsType == D.TypeA && isIPv4 {
|
} else if dnsType == D.TypeA && isIPv4 {
|
||||||
|
|||||||
107
dns/util.go
107
dns/util.go
@@ -2,10 +2,8 @@ package dns
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"crypto/tls"
|
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"net"
|
|
||||||
"net/netip"
|
"net/netip"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
@@ -92,46 +90,95 @@ func isIPRequest(q D.Question) bool {
|
|||||||
func transform(servers []NameServer, resolver *Resolver) []dnsClient {
|
func transform(servers []NameServer, resolver *Resolver) []dnsClient {
|
||||||
ret := make([]dnsClient, 0, len(servers))
|
ret := make([]dnsClient, 0, len(servers))
|
||||||
for _, s := range servers {
|
for _, s := range servers {
|
||||||
|
var c dnsClient
|
||||||
switch s.Net {
|
switch s.Net {
|
||||||
case "https":
|
case "https":
|
||||||
ret = append(ret, newDoHClient(s.Addr, resolver, s.PreferH3, s.Params, s.ProxyAdapter, s.ProxyName))
|
c = newDoHClient(s.Addr, resolver, s.PreferH3, s.Params, s.ProxyAdapter, s.ProxyName)
|
||||||
continue
|
|
||||||
case "dhcp":
|
case "dhcp":
|
||||||
ret = append(ret, newDHCPClient(s.Addr))
|
c = newDHCPClient(s.Addr)
|
||||||
continue
|
|
||||||
case "system":
|
case "system":
|
||||||
ret = append(ret, newSystemClient())
|
c = newSystemClient()
|
||||||
continue
|
|
||||||
case "rcode":
|
case "rcode":
|
||||||
ret = append(ret, newRCodeClient(s.Addr))
|
c = newRCodeClient(s.Addr)
|
||||||
continue
|
|
||||||
case "quic":
|
case "quic":
|
||||||
if doq, err := newDoQ(resolver, s.Addr, s.ProxyAdapter, s.ProxyName); err == nil {
|
c = newDoQ(s.Addr, resolver, s.Params, s.ProxyAdapter, s.ProxyName)
|
||||||
ret = append(ret, doq)
|
default:
|
||||||
} else {
|
c = newClient(s.Addr, resolver, s.Net, s.Params, s.ProxyAdapter, s.ProxyName)
|
||||||
log.Fatalln("DoQ format error: %v", err)
|
|
||||||
}
|
|
||||||
continue
|
|
||||||
}
|
}
|
||||||
|
|
||||||
host, port, _ := net.SplitHostPort(s.Addr)
|
c = warpClientWithEdns0Subnet(c, s.Params)
|
||||||
ret = append(ret, &client{
|
|
||||||
Client: &D.Client{
|
if s.Params["disable-ipv4"] == "true" {
|
||||||
Net: s.Net,
|
c = warpClientWithDisableType(c, D.TypeA)
|
||||||
TLSConfig: &tls.Config{
|
}
|
||||||
ServerName: host,
|
|
||||||
},
|
if s.Params["disable-ipv6"] == "true" {
|
||||||
UDPSize: 4096,
|
c = warpClientWithDisableType(c, D.TypeAAAA)
|
||||||
Timeout: 5 * time.Second,
|
}
|
||||||
},
|
|
||||||
port: port,
|
ret = append(ret, c)
|
||||||
host: host,
|
|
||||||
dialer: newDNSDialer(resolver, s.ProxyAdapter, s.ProxyName),
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
return ret
|
return ret
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type clientWithDisableType struct {
|
||||||
|
dnsClient
|
||||||
|
qType uint16
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c clientWithDisableType) ExchangeContext(ctx context.Context, m *D.Msg) (msg *D.Msg, err error) {
|
||||||
|
if len(m.Question) > 0 {
|
||||||
|
q := m.Question[0]
|
||||||
|
if q.Qtype == c.qType {
|
||||||
|
return handleMsgWithEmptyAnswer(m), nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return c.dnsClient.ExchangeContext(ctx, m)
|
||||||
|
}
|
||||||
|
|
||||||
|
func warpClientWithDisableType(c dnsClient, qType uint16) dnsClient {
|
||||||
|
return clientWithDisableType{c, qType}
|
||||||
|
}
|
||||||
|
|
||||||
|
type clientWithEdns0Subnet struct {
|
||||||
|
dnsClient
|
||||||
|
ecsPrefix netip.Prefix
|
||||||
|
ecsOverride bool
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c clientWithEdns0Subnet) ExchangeContext(ctx context.Context, m *D.Msg) (*D.Msg, error) {
|
||||||
|
m = m.Copy()
|
||||||
|
setEdns0Subnet(m, c.ecsPrefix, c.ecsOverride)
|
||||||
|
return c.dnsClient.ExchangeContext(ctx, m)
|
||||||
|
}
|
||||||
|
|
||||||
|
func warpClientWithEdns0Subnet(c dnsClient, params map[string]string) dnsClient {
|
||||||
|
var ecsPrefix netip.Prefix
|
||||||
|
var ecsOverride bool
|
||||||
|
if ecs := params["ecs"]; ecs != "" {
|
||||||
|
prefix, err := netip.ParsePrefix(ecs)
|
||||||
|
if err != nil {
|
||||||
|
addr, err := netip.ParseAddr(ecs)
|
||||||
|
if err != nil {
|
||||||
|
log.Warnln("DNS [%s] config with invalid ecs: %s", c.Address(), ecs)
|
||||||
|
} else {
|
||||||
|
ecsPrefix = netip.PrefixFrom(addr, addr.BitLen())
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
ecsPrefix = prefix
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ecsPrefix.IsValid() {
|
||||||
|
log.Debugln("DNS [%s] config with ecs: %s", c.Address(), ecsPrefix)
|
||||||
|
if params["ecs-override"] == "true" {
|
||||||
|
ecsOverride = true
|
||||||
|
}
|
||||||
|
return clientWithEdns0Subnet{c, ecsPrefix, ecsOverride}
|
||||||
|
}
|
||||||
|
return c
|
||||||
|
}
|
||||||
|
|
||||||
func handleMsgWithEmptyAnswer(r *D.Msg) *D.Msg {
|
func handleMsgWithEmptyAnswer(r *D.Msg) *D.Msg {
|
||||||
msg := &D.Msg{}
|
msg := &D.Msg{}
|
||||||
msg.Answer = []D.RR{}
|
msg.Answer = []D.RR{}
|
||||||
|
|||||||
@@ -1020,7 +1020,7 @@ proxy-providers:
|
|||||||
type: http # http 的 path 可空置,默认储存路径为 homedir 的 proxies 文件夹,文件名为 url 的 md5
|
type: http # http 的 path 可空置,默认储存路径为 homedir 的 proxies 文件夹,文件名为 url 的 md5
|
||||||
url: "url"
|
url: "url"
|
||||||
interval: 3600
|
interval: 3600
|
||||||
path: ./provider1.yaml # 默认只允许存储在 mihomo 的 Home Dir,如果想存储到任意位置,添加环境变量 SKIP_SAFE_PATH_CHECK=1
|
path: ./provider1.yaml # 默认只允许存储在 mihomo 的 Home Dir,如果想存储到其他位置,请通过设置 SAFE_PATHS 环境变量指定额外的安全路径。该环境变量的语法同本操作系统的PATH环境变量解析规则(即Windows下以分号分割,其他系统下以冒号分割)
|
||||||
proxy: DIRECT
|
proxy: DIRECT
|
||||||
# size-limit: 10240 # 限制下载文件最大为10kb,默认为0即不限制文件大小
|
# size-limit: 10240 # 限制下载文件最大为10kb,默认为0即不限制文件大小
|
||||||
header:
|
header:
|
||||||
@@ -1077,7 +1077,7 @@ rule-providers:
|
|||||||
rule1:
|
rule1:
|
||||||
behavior: classical # domain ipcidr
|
behavior: classical # domain ipcidr
|
||||||
interval: 259200
|
interval: 259200
|
||||||
path: /path/to/save/file.yaml # 默认只允许存储在 mihomo 的 Home Dir,如果想存储到任意位置,添加环境变量 SKIP_SAFE_PATH_CHECK=1
|
path: /path/to/save/file.yaml # 默认只允许存储在 mihomo 的 Home Dir,如果想存储到其他位置,请通过设置 SAFE_PATHS 环境变量指定额外的安全路径。该环境变量的语法同本操作系统的PATH环境变量解析规则(即Windows下以分号分割,其他系统下以冒号分割)
|
||||||
type: http # http 的 path 可空置,默认储存路径为 homedir 的 rules 文件夹,文件名为 url 的 md5
|
type: http # http 的 path 可空置,默认储存路径为 homedir 的 rules 文件夹,文件名为 url 的 md5
|
||||||
url: "url"
|
url: "url"
|
||||||
proxy: DIRECT
|
proxy: DIRECT
|
||||||
@@ -1276,6 +1276,16 @@ listeners:
|
|||||||
# - 0123456789abcdef
|
# - 0123456789abcdef
|
||||||
# server-names:
|
# server-names:
|
||||||
# - test.com
|
# - test.com
|
||||||
|
# #下列两个 limit 为选填,可对未通过验证的回落连接限速,bytesPerSec 默认为 0 即不启用
|
||||||
|
# #回落限速是一种特征,不建议启用,如果您是面板/一键脚本开发者,务必让这些参数随机化
|
||||||
|
# limit-fallback-upload:
|
||||||
|
# after-bytes: 0 # 传输指定字节后开始限速
|
||||||
|
# bytes-per-sec: 0 # 基准速率(字节/秒)
|
||||||
|
# burst-bytes-per-sec: 0 # 突发速率(字节/秒),大于 bytesPerSec 时生效
|
||||||
|
# limit-fallback-download:
|
||||||
|
# after-bytes: 0 # 传输指定字节后开始限速
|
||||||
|
# bytes-per-sec: 0 # 基准速率(字节/秒)
|
||||||
|
# burst-bytes-per-sec: 0 # 突发速率(字节/秒),大于 bytesPerSec 时生效
|
||||||
|
|
||||||
- name: tuic-in-1
|
- name: tuic-in-1
|
||||||
type: tuic
|
type: tuic
|
||||||
@@ -1343,6 +1353,16 @@ listeners:
|
|||||||
- 0123456789abcdef
|
- 0123456789abcdef
|
||||||
server-names:
|
server-names:
|
||||||
- test.com
|
- test.com
|
||||||
|
#下列两个 limit 为选填,可对未通过验证的回落连接限速,bytesPerSec 默认为 0 即不启用
|
||||||
|
#回落限速是一种特征,不建议启用,如果您是面板/一键脚本开发者,务必让这些参数随机化
|
||||||
|
limit-fallback-upload:
|
||||||
|
after-bytes: 0 # 传输指定字节后开始限速
|
||||||
|
bytes-per-sec: 0 # 基准速率(字节/秒)
|
||||||
|
burst-bytes-per-sec: 0 # 突发速率(字节/秒),大于 bytesPerSec 时生效
|
||||||
|
limit-fallback-download:
|
||||||
|
after-bytes: 0 # 传输指定字节后开始限速
|
||||||
|
bytes-per-sec: 0 # 基准速率(字节/秒)
|
||||||
|
burst-bytes-per-sec: 0 # 突发速率(字节/秒),大于 bytesPerSec 时生效
|
||||||
### 注意,对于vless listener, 至少需要填写 “certificate和private-key” 或 “reality-config” 的其中一项 ###
|
### 注意,对于vless listener, 至少需要填写 “certificate和private-key” 或 “reality-config” 的其中一项 ###
|
||||||
|
|
||||||
- name: anytls-in-1
|
- name: anytls-in-1
|
||||||
@@ -1393,6 +1413,16 @@ listeners:
|
|||||||
# - 0123456789abcdef
|
# - 0123456789abcdef
|
||||||
# server-names:
|
# server-names:
|
||||||
# - test.com
|
# - test.com
|
||||||
|
# #下列两个 limit 为选填,可对未通过验证的回落连接限速,bytesPerSec 默认为 0 即不启用
|
||||||
|
# #回落限速是一种特征,不建议启用,如果您是面板/一键脚本开发者,务必让这些参数随机化
|
||||||
|
# limit-fallback-upload:
|
||||||
|
# after-bytes: 0 # 传输指定字节后开始限速
|
||||||
|
# bytes-per-sec: 0 # 基准速率(字节/秒)
|
||||||
|
# burst-bytes-per-sec: 0 # 突发速率(字节/秒),大于 bytesPerSec 时生效
|
||||||
|
# limit-fallback-download:
|
||||||
|
# after-bytes: 0 # 传输指定字节后开始限速
|
||||||
|
# bytes-per-sec: 0 # 基准速率(字节/秒)
|
||||||
|
# burst-bytes-per-sec: 0 # 突发速率(字节/秒),大于 bytesPerSec 时生效
|
||||||
# ss-option: # like trojan-go's `shadowsocks` config
|
# ss-option: # like trojan-go's `shadowsocks` config
|
||||||
# enabled: false
|
# enabled: false
|
||||||
# method: aes-128-gcm # aes-128-gcm/aes-256-gcm/chacha20-ietf-poly1305
|
# method: aes-128-gcm # aes-128-gcm/aes-256-gcm/chacha20-ietf-poly1305
|
||||||
|
|||||||
18
go.mod
18
go.mod
@@ -20,23 +20,23 @@ require (
|
|||||||
github.com/metacubex/amneziawg-go v0.0.0-20240922133038-fdf3a4d5a4ab
|
github.com/metacubex/amneziawg-go v0.0.0-20240922133038-fdf3a4d5a4ab
|
||||||
github.com/metacubex/bart v0.20.5
|
github.com/metacubex/bart v0.20.5
|
||||||
github.com/metacubex/bbolt v0.0.0-20240822011022-aed6d4850399
|
github.com/metacubex/bbolt v0.0.0-20240822011022-aed6d4850399
|
||||||
github.com/metacubex/chacha v0.1.2
|
github.com/metacubex/chacha v0.1.5
|
||||||
github.com/metacubex/fswatch v0.1.1
|
github.com/metacubex/fswatch v0.1.1
|
||||||
github.com/metacubex/gopacket v1.1.20-0.20230608035415-7e2f98a3e759
|
github.com/metacubex/gopacket v1.1.20-0.20230608035415-7e2f98a3e759
|
||||||
github.com/metacubex/quic-go v0.51.1-0.20250511032541-4e34341cf18b
|
github.com/metacubex/quic-go v0.52.1-0.20250522021943-aef454b9e639
|
||||||
github.com/metacubex/randv2 v0.2.0
|
github.com/metacubex/randv2 v0.2.0
|
||||||
github.com/metacubex/sing v0.5.3-0.20250504031621-1f99e54c15b7
|
github.com/metacubex/sing v0.5.4-0.20250605054047-54dc6097da29
|
||||||
github.com/metacubex/sing-mux v0.3.2
|
github.com/metacubex/sing-mux v0.3.2
|
||||||
github.com/metacubex/sing-quic v0.0.0-20250520025433-6e556a6bef7a
|
github.com/metacubex/sing-quic v0.0.0-20250523120938-f1a248e5ec7f
|
||||||
github.com/metacubex/sing-shadowsocks v0.2.9
|
github.com/metacubex/sing-shadowsocks v0.2.11-0.20250621023810-0e9ef9dd0c92
|
||||||
github.com/metacubex/sing-shadowsocks2 v0.2.3
|
github.com/metacubex/sing-shadowsocks2 v0.2.5-0.20250621023950-93d605a2143d
|
||||||
github.com/metacubex/sing-shadowtls v0.0.0-20250503063515-5d9f966d17a2
|
github.com/metacubex/sing-shadowtls v0.0.0-20250503063515-5d9f966d17a2
|
||||||
github.com/metacubex/sing-tun v0.4.6-0.20250503065609-efb9f0beb6f6
|
github.com/metacubex/sing-tun v0.4.7-0.20250611091011-60774779fdd8
|
||||||
github.com/metacubex/sing-vmess v0.2.1
|
github.com/metacubex/sing-vmess v0.2.2
|
||||||
github.com/metacubex/sing-wireguard v0.0.0-20250503063753-2dc62acc626f
|
github.com/metacubex/sing-wireguard v0.0.0-20250503063753-2dc62acc626f
|
||||||
github.com/metacubex/smux v0.0.0-20250503055512-501391591dee
|
github.com/metacubex/smux v0.0.0-20250503055512-501391591dee
|
||||||
github.com/metacubex/tfo-go v0.0.0-20250516165257-e29c16ae41d4
|
github.com/metacubex/tfo-go v0.0.0-20250516165257-e29c16ae41d4
|
||||||
github.com/metacubex/utls v1.7.3
|
github.com/metacubex/utls v1.7.4-0.20250610022031-808d767c8c73
|
||||||
github.com/metacubex/wireguard-go v0.0.0-20240922131502-c182e7471181
|
github.com/metacubex/wireguard-go v0.0.0-20240922131502-c182e7471181
|
||||||
github.com/miekg/dns v1.1.63 // lastest version compatible with golang1.20
|
github.com/miekg/dns v1.1.63 // lastest version compatible with golang1.20
|
||||||
github.com/mroth/weightedrand/v2 v2.1.0
|
github.com/mroth/weightedrand/v2 v2.1.0
|
||||||
|
|||||||
40
go.sum
40
go.sum
@@ -101,8 +101,8 @@ github.com/metacubex/bart v0.20.5 h1:XkgLZ17QxfxkqKdGsojoM2Zu01mmHyyQSFzt2/calTM
|
|||||||
github.com/metacubex/bart v0.20.5/go.mod h1:DCcyfP4MC+Zy7sLK7XeGuMw+P5K9mIRsYOBgiE8icsI=
|
github.com/metacubex/bart v0.20.5/go.mod h1:DCcyfP4MC+Zy7sLK7XeGuMw+P5K9mIRsYOBgiE8icsI=
|
||||||
github.com/metacubex/bbolt v0.0.0-20240822011022-aed6d4850399 h1:oBowHVKZycNtAFbZ6avaCSZJYeme2Nrj+4RpV2cNJig=
|
github.com/metacubex/bbolt v0.0.0-20240822011022-aed6d4850399 h1:oBowHVKZycNtAFbZ6avaCSZJYeme2Nrj+4RpV2cNJig=
|
||||||
github.com/metacubex/bbolt v0.0.0-20240822011022-aed6d4850399/go.mod h1:4xcieuIK+M4bGQmQYZVqEaIYqjS1ahO4kXG7EmDgEro=
|
github.com/metacubex/bbolt v0.0.0-20240822011022-aed6d4850399/go.mod h1:4xcieuIK+M4bGQmQYZVqEaIYqjS1ahO4kXG7EmDgEro=
|
||||||
github.com/metacubex/chacha v0.1.2 h1:QulCq3eVm3TO6+4nVIWJtmSe7BT2GMrgVHuAoqRQnlc=
|
github.com/metacubex/chacha v0.1.5 h1:fKWMb/5c7ZrY8Uoqi79PPFxl+qwR7X/q0OrsAubyX2M=
|
||||||
github.com/metacubex/chacha v0.1.2/go.mod h1:Djn9bPZxLTXbJFSeyo0/qzEzQI+gUSSzttuzZM75GH8=
|
github.com/metacubex/chacha v0.1.5/go.mod h1:Djn9bPZxLTXbJFSeyo0/qzEzQI+gUSSzttuzZM75GH8=
|
||||||
github.com/metacubex/fswatch v0.1.1 h1:jqU7C/v+g0qc2RUFgmAOPoVvfl2BXXUXEumn6oQuxhU=
|
github.com/metacubex/fswatch v0.1.1 h1:jqU7C/v+g0qc2RUFgmAOPoVvfl2BXXUXEumn6oQuxhU=
|
||||||
github.com/metacubex/fswatch v0.1.1/go.mod h1:czrTT7Zlbz7vWft8RQu9Qqh+JoX+Nnb+UabuyN1YsgI=
|
github.com/metacubex/fswatch v0.1.1/go.mod h1:czrTT7Zlbz7vWft8RQu9Qqh+JoX+Nnb+UabuyN1YsgI=
|
||||||
github.com/metacubex/gopacket v1.1.20-0.20230608035415-7e2f98a3e759 h1:cjd4biTvOzK9ubNCCkQ+ldc4YSH/rILn53l/xGBFHHI=
|
github.com/metacubex/gopacket v1.1.20-0.20230608035415-7e2f98a3e759 h1:cjd4biTvOzK9ubNCCkQ+ldc4YSH/rILn53l/xGBFHHI=
|
||||||
@@ -111,35 +111,39 @@ github.com/metacubex/gvisor v0.0.0-20250324165734-5857f47bd43b h1:RUh4OdVPz/jDrM
|
|||||||
github.com/metacubex/gvisor v0.0.0-20250324165734-5857f47bd43b/go.mod h1:8LpS0IJW1VmWzUm3ylb0e2SK5QDm5lO/2qwWLZgRpBU=
|
github.com/metacubex/gvisor v0.0.0-20250324165734-5857f47bd43b/go.mod h1:8LpS0IJW1VmWzUm3ylb0e2SK5QDm5lO/2qwWLZgRpBU=
|
||||||
github.com/metacubex/nftables v0.0.0-20250503052935-30a69ab87793 h1:1Qpuy+sU3DmyX9HwI+CrBT/oLNJngvBorR2RbajJcqo=
|
github.com/metacubex/nftables v0.0.0-20250503052935-30a69ab87793 h1:1Qpuy+sU3DmyX9HwI+CrBT/oLNJngvBorR2RbajJcqo=
|
||||||
github.com/metacubex/nftables v0.0.0-20250503052935-30a69ab87793/go.mod h1:RjRNb4G52yAgfR+Oe/kp9G4PJJ97Fnj89eY1BFO3YyA=
|
github.com/metacubex/nftables v0.0.0-20250503052935-30a69ab87793/go.mod h1:RjRNb4G52yAgfR+Oe/kp9G4PJJ97Fnj89eY1BFO3YyA=
|
||||||
github.com/metacubex/quic-go v0.51.1-0.20250511032541-4e34341cf18b h1:8oDU32eJ+RRhl1FodGgPfxQxtoBAiD9D40XG2XtU/SE=
|
github.com/metacubex/quic-go v0.52.1-0.20250522021943-aef454b9e639 h1:L+1brQNzBhCCxWlicwfK1TlceemCRmrDE4HmcVHc29w=
|
||||||
github.com/metacubex/quic-go v0.51.1-0.20250511032541-4e34341cf18b/go.mod h1:9R1NOzCgTcWsdWvOMlmtMuF0uKzuOpsfvEf7U3I8zM0=
|
github.com/metacubex/quic-go v0.52.1-0.20250522021943-aef454b9e639/go.mod h1:Kc6h++Q/zf3AxcUCevJhJwgrskJumv+pZdR8g/E/10k=
|
||||||
github.com/metacubex/randv2 v0.2.0 h1:uP38uBvV2SxYfLj53kuvAjbND4RUDfFJjwr4UigMiLs=
|
github.com/metacubex/randv2 v0.2.0 h1:uP38uBvV2SxYfLj53kuvAjbND4RUDfFJjwr4UigMiLs=
|
||||||
github.com/metacubex/randv2 v0.2.0/go.mod h1:kFi2SzrQ5WuneuoLLCMkABtiBu6VRrMrWFqSPyj2cxY=
|
github.com/metacubex/randv2 v0.2.0/go.mod h1:kFi2SzrQ5WuneuoLLCMkABtiBu6VRrMrWFqSPyj2cxY=
|
||||||
github.com/metacubex/sing v0.5.2/go.mod h1:ypf0mjwlZm0sKdQSY+yQvmsbWa0hNPtkeqyRMGgoN+w=
|
github.com/metacubex/sing v0.5.2/go.mod h1:ypf0mjwlZm0sKdQSY+yQvmsbWa0hNPtkeqyRMGgoN+w=
|
||||||
github.com/metacubex/sing v0.5.3-0.20250504031621-1f99e54c15b7 h1:m4nSxvw46JEgxMzzmnXams+ebwabcry4Ydep/zNiesQ=
|
github.com/metacubex/sing v0.5.4-0.20250605054047-54dc6097da29 h1:SD9q025FNTaepuFXFOKDhnGLVu6PQYChBvw2ZYPXeLo=
|
||||||
github.com/metacubex/sing v0.5.3-0.20250504031621-1f99e54c15b7/go.mod h1:ypf0mjwlZm0sKdQSY+yQvmsbWa0hNPtkeqyRMGgoN+w=
|
github.com/metacubex/sing v0.5.4-0.20250605054047-54dc6097da29/go.mod h1:ypf0mjwlZm0sKdQSY+yQvmsbWa0hNPtkeqyRMGgoN+w=
|
||||||
github.com/metacubex/sing-mux v0.3.2 h1:nJv52pyRivHcaZJKk2JgxpaVvj1GAXG81scSa9N7ncw=
|
github.com/metacubex/sing-mux v0.3.2 h1:nJv52pyRivHcaZJKk2JgxpaVvj1GAXG81scSa9N7ncw=
|
||||||
github.com/metacubex/sing-mux v0.3.2/go.mod h1:3rt1soewn0O6j89GCLmwAQFsq257u0jf2zQSPhTL3Bw=
|
github.com/metacubex/sing-mux v0.3.2/go.mod h1:3rt1soewn0O6j89GCLmwAQFsq257u0jf2zQSPhTL3Bw=
|
||||||
github.com/metacubex/sing-quic v0.0.0-20250520025433-6e556a6bef7a h1:Ho73vGiB94LmtK5T+tKVwtCNEi/YiHmPjlqpHSAmAVs=
|
github.com/metacubex/sing-quic v0.0.0-20250523120938-f1a248e5ec7f h1:mP3vIm+9hRFI0C0Vl3pE0NESF/L85FDbuB0tGgUii6I=
|
||||||
github.com/metacubex/sing-quic v0.0.0-20250520025433-6e556a6bef7a/go.mod h1:JPTpf7fpnojsSuwRJExhSZSy63pVbp3VM39+zj+sAJM=
|
github.com/metacubex/sing-quic v0.0.0-20250523120938-f1a248e5ec7f/go.mod h1:JPTpf7fpnojsSuwRJExhSZSy63pVbp3VM39+zj+sAJM=
|
||||||
github.com/metacubex/sing-shadowsocks v0.2.9 h1:2e++13WNN7EGjGtvrGLUzW1xrCdQbW2gIFpgw5GEw00=
|
github.com/metacubex/sing-shadowsocks v0.2.11-0.20250621021503-4f85ef9bf4b3 h1:dtiRj7WaCAXp4UhCkmaIiFF6v886qXiuqeIDN4Z//9E=
|
||||||
github.com/metacubex/sing-shadowsocks v0.2.9/go.mod h1:CJSEGO4FWQAWe+ZiLZxCweGdjRR60A61SIoVjdjQeBA=
|
github.com/metacubex/sing-shadowsocks v0.2.11-0.20250621021503-4f85ef9bf4b3/go.mod h1:/squZ38pXrYjqtg8qn+joVvwbpGNYQNp8yxKsMVbCto=
|
||||||
github.com/metacubex/sing-shadowsocks2 v0.2.3 h1:v3rNS/5Ywh0NIZ6VU/NmdERQIN5RePzyxCFeQsU4Cx0=
|
github.com/metacubex/sing-shadowsocks v0.2.11-0.20250621023810-0e9ef9dd0c92 h1:Y9ebcKya6ow7VHoESCN5+l4zZvg5eaL2IhI5LLCQxQA=
|
||||||
github.com/metacubex/sing-shadowsocks2 v0.2.3/go.mod h1:/WNy/Q8ahLCoPRriWuFZFD0Jy+JNp1MEQl28Zw6SaF8=
|
github.com/metacubex/sing-shadowsocks v0.2.11-0.20250621023810-0e9ef9dd0c92/go.mod h1:/squZ38pXrYjqtg8qn+joVvwbpGNYQNp8yxKsMVbCto=
|
||||||
|
github.com/metacubex/sing-shadowsocks2 v0.2.5-0.20250621021638-dcd503063651 h1:vwLj0DDjPYy4AHEZvfRVf8ih52o6wpBnJxXxqa+ztmE=
|
||||||
|
github.com/metacubex/sing-shadowsocks2 v0.2.5-0.20250621021638-dcd503063651/go.mod h1:+ukTd0OPFglT3bnKAYTJWYPbuox6HYNXE235r5tHdUk=
|
||||||
|
github.com/metacubex/sing-shadowsocks2 v0.2.5-0.20250621023950-93d605a2143d h1:Ey3A1tA8lVkRbK1FDmwuWj/57Nr8JMdpoVqe45mFzJg=
|
||||||
|
github.com/metacubex/sing-shadowsocks2 v0.2.5-0.20250621023950-93d605a2143d/go.mod h1:+ukTd0OPFglT3bnKAYTJWYPbuox6HYNXE235r5tHdUk=
|
||||||
github.com/metacubex/sing-shadowtls v0.0.0-20250503063515-5d9f966d17a2 h1:gXU+MYPm7Wme3/OAY2FFzVq9d9GxPHOqu5AQfg/ddhI=
|
github.com/metacubex/sing-shadowtls v0.0.0-20250503063515-5d9f966d17a2 h1:gXU+MYPm7Wme3/OAY2FFzVq9d9GxPHOqu5AQfg/ddhI=
|
||||||
github.com/metacubex/sing-shadowtls v0.0.0-20250503063515-5d9f966d17a2/go.mod h1:mbfboaXauKJNIHJYxQRa+NJs4JU9NZfkA+I33dS2+9E=
|
github.com/metacubex/sing-shadowtls v0.0.0-20250503063515-5d9f966d17a2/go.mod h1:mbfboaXauKJNIHJYxQRa+NJs4JU9NZfkA+I33dS2+9E=
|
||||||
github.com/metacubex/sing-tun v0.4.6-0.20250503065609-efb9f0beb6f6 h1:TAwL91XPa6x1QK55CRm+VTzPvLPUfEr/uFDnOZArqEU=
|
github.com/metacubex/sing-tun v0.4.7-0.20250611091011-60774779fdd8 h1:4zWKqxTx75TbfW2EmlQ3hxM6RTRg2PYOAVMCnU4I61I=
|
||||||
github.com/metacubex/sing-tun v0.4.6-0.20250503065609-efb9f0beb6f6/go.mod h1:HDaHDL6onAX2ZGbAGUXKp++PohRdNb7Nzt6zxzhox+U=
|
github.com/metacubex/sing-tun v0.4.7-0.20250611091011-60774779fdd8/go.mod h1:2YywXPWW8Z97kTH7RffOeykKzU+l0aiKlglWV1PAS64=
|
||||||
github.com/metacubex/sing-vmess v0.2.1 h1:I6gM3VUjtvJ15D805EUbNH+SRBuqzJeFnuIbKYUsWZ0=
|
github.com/metacubex/sing-vmess v0.2.2 h1:nG6GIKF1UOGmlzs+BIetdGHkFZ20YqFVIYp5Htqzp+4=
|
||||||
github.com/metacubex/sing-vmess v0.2.1/go.mod h1:DsODWItJtOMZNna8Qhheg8r3tUivrcO3vWgaTYKnfTo=
|
github.com/metacubex/sing-vmess v0.2.2/go.mod h1:CVDNcdSLVYFgTHQlubr88d8CdqupAUDqLjROos+H9xk=
|
||||||
github.com/metacubex/sing-wireguard v0.0.0-20250503063753-2dc62acc626f h1:Sr/DYKYofKHKc4GF3qkRGNuj6XA6c0eqPgEDN+VAsYU=
|
github.com/metacubex/sing-wireguard v0.0.0-20250503063753-2dc62acc626f h1:Sr/DYKYofKHKc4GF3qkRGNuj6XA6c0eqPgEDN+VAsYU=
|
||||||
github.com/metacubex/sing-wireguard v0.0.0-20250503063753-2dc62acc626f/go.mod h1:jpAkVLPnCpGSfNyVmj6Cq4YbuZsFepm/Dc+9BAOcR80=
|
github.com/metacubex/sing-wireguard v0.0.0-20250503063753-2dc62acc626f/go.mod h1:jpAkVLPnCpGSfNyVmj6Cq4YbuZsFepm/Dc+9BAOcR80=
|
||||||
github.com/metacubex/smux v0.0.0-20250503055512-501391591dee h1:lp6hJ+4wCLZu113awp7P6odM2okB5s60HUyF0FMqKmo=
|
github.com/metacubex/smux v0.0.0-20250503055512-501391591dee h1:lp6hJ+4wCLZu113awp7P6odM2okB5s60HUyF0FMqKmo=
|
||||||
github.com/metacubex/smux v0.0.0-20250503055512-501391591dee/go.mod h1:4bPD8HWx9jPJ9aE4uadgyN7D1/Wz3KmPy+vale8sKLE=
|
github.com/metacubex/smux v0.0.0-20250503055512-501391591dee/go.mod h1:4bPD8HWx9jPJ9aE4uadgyN7D1/Wz3KmPy+vale8sKLE=
|
||||||
github.com/metacubex/tfo-go v0.0.0-20250516165257-e29c16ae41d4 h1:j1VRTiC9JLR4nUbSikx9OGdu/3AgFDqgcLj4GoqyQkc=
|
github.com/metacubex/tfo-go v0.0.0-20250516165257-e29c16ae41d4 h1:j1VRTiC9JLR4nUbSikx9OGdu/3AgFDqgcLj4GoqyQkc=
|
||||||
github.com/metacubex/tfo-go v0.0.0-20250516165257-e29c16ae41d4/go.mod h1:l9oLnLoEXyGZ5RVLsh7QCC5XsouTUyKk4F2nLm2DHLw=
|
github.com/metacubex/tfo-go v0.0.0-20250516165257-e29c16ae41d4/go.mod h1:l9oLnLoEXyGZ5RVLsh7QCC5XsouTUyKk4F2nLm2DHLw=
|
||||||
github.com/metacubex/utls v1.7.3 h1:yDcMEWojFh+t8rU9X0HPcZDPAoFze/rIIyssqivzj8A=
|
github.com/metacubex/utls v1.7.4-0.20250610022031-808d767c8c73 h1:HWKsf92BqLYqugATFIJ3hYiEBZ7JF6AoqyvqF39afuI=
|
||||||
github.com/metacubex/utls v1.7.3/go.mod h1:oknYT0qTOwE4hjPmZOEpzVdefnW7bAdGLvZcqmk4TLU=
|
github.com/metacubex/utls v1.7.4-0.20250610022031-808d767c8c73/go.mod h1:oknYT0qTOwE4hjPmZOEpzVdefnW7bAdGLvZcqmk4TLU=
|
||||||
github.com/metacubex/wireguard-go v0.0.0-20240922131502-c182e7471181 h1:hJLQviGySBuaynlCwf/oYgIxbVbGRUIKZCxdya9YrbQ=
|
github.com/metacubex/wireguard-go v0.0.0-20240922131502-c182e7471181 h1:hJLQviGySBuaynlCwf/oYgIxbVbGRUIKZCxdya9YrbQ=
|
||||||
github.com/metacubex/wireguard-go v0.0.0-20240922131502-c182e7471181/go.mod h1:phewKljNYiTVT31Gcif8RiCKnTUOgVWFJjccqYM8s+Y=
|
github.com/metacubex/wireguard-go v0.0.0-20240922131502-c182e7471181/go.mod h1:phewKljNYiTVT31Gcif8RiCKnTUOgVWFJjccqYM8s+Y=
|
||||||
github.com/miekg/dns v1.1.63 h1:8M5aAw6OMZfFXTT7K5V0Eu5YiiL8l7nUAkyN6C9YwaY=
|
github.com/miekg/dns v1.1.63 h1:8M5aAw6OMZfFXTT7K5V0Eu5YiiL8l7nUAkyN6C9YwaY=
|
||||||
|
|||||||
@@ -75,6 +75,7 @@ type tunSchema struct {
|
|||||||
AutoRedirect *bool `yaml:"auto-redirect" json:"auto-redirect,omitempty"`
|
AutoRedirect *bool `yaml:"auto-redirect" json:"auto-redirect,omitempty"`
|
||||||
AutoRedirectInputMark *uint32 `yaml:"auto-redirect-input-mark" json:"auto-redirect-input-mark,omitempty"`
|
AutoRedirectInputMark *uint32 `yaml:"auto-redirect-input-mark" json:"auto-redirect-input-mark,omitempty"`
|
||||||
AutoRedirectOutputMark *uint32 `yaml:"auto-redirect-output-mark" json:"auto-redirect-output-mark,omitempty"`
|
AutoRedirectOutputMark *uint32 `yaml:"auto-redirect-output-mark" json:"auto-redirect-output-mark,omitempty"`
|
||||||
|
LoopbackAddress *[]netip.Addr `yaml:"loopback-address" json:"loopback-address,omitempty"`
|
||||||
StrictRoute *bool `yaml:"strict-route" json:"strict-route,omitempty"`
|
StrictRoute *bool `yaml:"strict-route" json:"strict-route,omitempty"`
|
||||||
RouteAddress *[]netip.Prefix `yaml:"route-address" json:"route-address,omitempty"`
|
RouteAddress *[]netip.Prefix `yaml:"route-address" json:"route-address,omitempty"`
|
||||||
RouteAddressSet *[]string `yaml:"route-address-set" json:"route-address-set,omitempty"`
|
RouteAddressSet *[]string `yaml:"route-address-set" json:"route-address-set,omitempty"`
|
||||||
@@ -174,6 +175,9 @@ func pointerOrDefaultTun(p *tunSchema, def LC.Tun) LC.Tun {
|
|||||||
if p.AutoRedirectOutputMark != nil {
|
if p.AutoRedirectOutputMark != nil {
|
||||||
def.AutoRedirectOutputMark = *p.AutoRedirectOutputMark
|
def.AutoRedirectOutputMark = *p.AutoRedirectOutputMark
|
||||||
}
|
}
|
||||||
|
if p.LoopbackAddress != nil {
|
||||||
|
def.LoopbackAddress = *p.LoopbackAddress
|
||||||
|
}
|
||||||
if p.StrictRoute != nil {
|
if p.StrictRoute != nil {
|
||||||
def.StrictRoute = *p.StrictRoute
|
def.StrictRoute = *p.StrictRoute
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,18 +9,6 @@ import (
|
|||||||
"golang.org/x/exp/slices"
|
"golang.org/x/exp/slices"
|
||||||
)
|
)
|
||||||
|
|
||||||
func StringSliceToNetipPrefixSlice(ss []string) ([]netip.Prefix, error) {
|
|
||||||
lps := make([]netip.Prefix, 0, len(ss))
|
|
||||||
for _, s := range ss {
|
|
||||||
prefix, err := netip.ParsePrefix(s)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
lps = append(lps, prefix)
|
|
||||||
}
|
|
||||||
return lps, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
type Tun struct {
|
type Tun struct {
|
||||||
Enable bool `yaml:"enable" json:"enable"`
|
Enable bool `yaml:"enable" json:"enable"`
|
||||||
Device string `yaml:"device" json:"device"`
|
Device string `yaml:"device" json:"device"`
|
||||||
@@ -39,6 +27,7 @@ type Tun struct {
|
|||||||
AutoRedirect bool `yaml:"auto-redirect" json:"auto-redirect,omitempty"`
|
AutoRedirect bool `yaml:"auto-redirect" json:"auto-redirect,omitempty"`
|
||||||
AutoRedirectInputMark uint32 `yaml:"auto-redirect-input-mark" json:"auto-redirect-input-mark,omitempty"`
|
AutoRedirectInputMark uint32 `yaml:"auto-redirect-input-mark" json:"auto-redirect-input-mark,omitempty"`
|
||||||
AutoRedirectOutputMark uint32 `yaml:"auto-redirect-output-mark" json:"auto-redirect-output-mark,omitempty"`
|
AutoRedirectOutputMark uint32 `yaml:"auto-redirect-output-mark" json:"auto-redirect-output-mark,omitempty"`
|
||||||
|
LoopbackAddress []netip.Addr `yaml:"loopback-address" json:"loopback-address,omitempty"`
|
||||||
StrictRoute bool `yaml:"strict-route" json:"strict-route,omitempty"`
|
StrictRoute bool `yaml:"strict-route" json:"strict-route,omitempty"`
|
||||||
RouteAddress []netip.Prefix `yaml:"route-address" json:"route-address,omitempty"`
|
RouteAddress []netip.Prefix `yaml:"route-address" json:"route-address,omitempty"`
|
||||||
RouteAddressSet []string `yaml:"route-address-set" json:"route-address-set,omitempty"`
|
RouteAddressSet []string `yaml:"route-address-set" json:"route-address-set,omitempty"`
|
||||||
@@ -142,6 +131,9 @@ func (t *Tun) Equal(other Tun) bool {
|
|||||||
if t.AutoRedirectOutputMark != other.AutoRedirectOutputMark {
|
if t.AutoRedirectOutputMark != other.AutoRedirectOutputMark {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
if !slices.Equal(t.RouteAddress, other.RouteAddress) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
if t.StrictRoute != other.StrictRoute {
|
if t.StrictRoute != other.StrictRoute {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,6 +9,15 @@ type RealityConfig struct {
|
|||||||
ServerNames []string `inbound:"server-names"`
|
ServerNames []string `inbound:"server-names"`
|
||||||
MaxTimeDifference int `inbound:"max-time-difference,omitempty"`
|
MaxTimeDifference int `inbound:"max-time-difference,omitempty"`
|
||||||
Proxy string `inbound:"proxy,omitempty"`
|
Proxy string `inbound:"proxy,omitempty"`
|
||||||
|
|
||||||
|
LimitFallbackUpload RealityLimitFallback `inbound:"limit-fallback-upload,omitempty"`
|
||||||
|
LimitFallbackDownload RealityLimitFallback `inbound:"limit-fallback-download,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type RealityLimitFallback struct {
|
||||||
|
AfterBytes uint64 `inbound:"after-bytes,omitempty"`
|
||||||
|
BytesPerSec uint64 `inbound:"bytes-per-sec,omitempty"`
|
||||||
|
BurstBytesPerSec uint64 `inbound:"burst-bytes-per-sec,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c RealityConfig) Build() reality.Config {
|
func (c RealityConfig) Build() reality.Config {
|
||||||
@@ -19,5 +28,16 @@ func (c RealityConfig) Build() reality.Config {
|
|||||||
ServerNames: c.ServerNames,
|
ServerNames: c.ServerNames,
|
||||||
MaxTimeDifference: c.MaxTimeDifference,
|
MaxTimeDifference: c.MaxTimeDifference,
|
||||||
Proxy: c.Proxy,
|
Proxy: c.Proxy,
|
||||||
|
|
||||||
|
LimitFallbackUpload: reality.LimitFallback{
|
||||||
|
AfterBytes: c.LimitFallbackUpload.AfterBytes,
|
||||||
|
BytesPerSec: c.LimitFallbackUpload.BytesPerSec,
|
||||||
|
BurstBytesPerSec: c.LimitFallbackUpload.BurstBytesPerSec,
|
||||||
|
},
|
||||||
|
LimitFallbackDownload: reality.LimitFallback{
|
||||||
|
AfterBytes: c.LimitFallbackDownload.AfterBytes,
|
||||||
|
BytesPerSec: c.LimitFallbackDownload.BytesPerSec,
|
||||||
|
BurstBytesPerSec: c.LimitFallbackDownload.BurstBytesPerSec,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
package inbound
|
package inbound
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
"encoding"
|
||||||
"strings"
|
"net/netip"
|
||||||
|
|
||||||
C "github.com/metacubex/mihomo/constant"
|
C "github.com/metacubex/mihomo/constant"
|
||||||
LC "github.com/metacubex/mihomo/listener/config"
|
LC "github.com/metacubex/mihomo/listener/config"
|
||||||
@@ -12,50 +12,55 @@ import (
|
|||||||
|
|
||||||
type TunOption struct {
|
type TunOption struct {
|
||||||
BaseOption
|
BaseOption
|
||||||
Device string `inbound:"device,omitempty"`
|
Device string `inbound:"device,omitempty"`
|
||||||
Stack string `inbound:"stack,omitempty"`
|
Stack C.TUNStack `inbound:"stack,omitempty"`
|
||||||
DNSHijack []string `inbound:"dns-hijack,omitempty"`
|
DNSHijack []string `inbound:"dns-hijack,omitempty"`
|
||||||
AutoRoute bool `inbound:"auto-route,omitempty"`
|
AutoRoute bool `inbound:"auto-route,omitempty"`
|
||||||
AutoDetectInterface bool `inbound:"auto-detect-interface,omitempty"`
|
AutoDetectInterface bool `inbound:"auto-detect-interface,omitempty"`
|
||||||
|
|
||||||
MTU uint32 `inbound:"mtu,omitempty"`
|
MTU uint32 `inbound:"mtu,omitempty"`
|
||||||
GSO bool `inbound:"gso,omitempty"`
|
GSO bool `inbound:"gso,omitempty"`
|
||||||
GSOMaxSize uint32 `inbound:"gso-max-size,omitempty"`
|
GSOMaxSize uint32 `inbound:"gso-max-size,omitempty"`
|
||||||
Inet4Address []string `inbound:"inet4-address,omitempty"`
|
Inet4Address []netip.Prefix `inbound:"inet4-address,omitempty"`
|
||||||
Inet6Address []string `inbound:"inet6-address,omitempty"`
|
Inet6Address []netip.Prefix `inbound:"inet6-address,omitempty"`
|
||||||
IPRoute2TableIndex int `inbound:"iproute2-table-index,omitempty"`
|
IPRoute2TableIndex int `inbound:"iproute2-table-index,omitempty"`
|
||||||
IPRoute2RuleIndex int `inbound:"iproute2-rule-index,omitempty"`
|
IPRoute2RuleIndex int `inbound:"iproute2-rule-index,omitempty"`
|
||||||
AutoRedirect bool `inbound:"auto-redirect,omitempty"`
|
AutoRedirect bool `inbound:"auto-redirect,omitempty"`
|
||||||
AutoRedirectInputMark uint32 `inbound:"auto-redirect-input-mark,omitempty"`
|
AutoRedirectInputMark uint32 `inbound:"auto-redirect-input-mark,omitempty"`
|
||||||
AutoRedirectOutputMark uint32 `inbound:"auto-redirect-output-mark,omitempty"`
|
AutoRedirectOutputMark uint32 `inbound:"auto-redirect-output-mark,omitempty"`
|
||||||
StrictRoute bool `inbound:"strict-route,omitempty"`
|
LoopbackAddress []netip.Addr `inbound:"loopback-address,omitempty"`
|
||||||
RouteAddress []string `inbound:"route-address,omitempty"`
|
StrictRoute bool `inbound:"strict-route,omitempty"`
|
||||||
RouteAddressSet []string `inbound:"route-address-set,omitempty"`
|
RouteAddress []netip.Prefix `inbound:"route-address,omitempty"`
|
||||||
RouteExcludeAddress []string `inbound:"route-exclude-address,omitempty"`
|
RouteAddressSet []string `inbound:"route-address-set,omitempty"`
|
||||||
RouteExcludeAddressSet []string `inbound:"route-exclude-address-set,omitempty"`
|
RouteExcludeAddress []netip.Prefix `inbound:"route-exclude-address,omitempty"`
|
||||||
IncludeInterface []string `inbound:"include-interface,omitempty"`
|
RouteExcludeAddressSet []string `inbound:"route-exclude-address-set,omitempty"`
|
||||||
ExcludeInterface []string `inbound:"exclude-interface,omitempty"`
|
IncludeInterface []string `inbound:"include-interface,omitempty"`
|
||||||
IncludeUID []uint32 `inbound:"include-uid,omitempty"`
|
ExcludeInterface []string `inbound:"exclude-interface,omitempty"`
|
||||||
IncludeUIDRange []string `inbound:"include-uid-range,omitempty"`
|
IncludeUID []uint32 `inbound:"include-uid,omitempty"`
|
||||||
ExcludeUID []uint32 `inbound:"exclude-uid,omitempty"`
|
IncludeUIDRange []string `inbound:"include-uid-range,omitempty"`
|
||||||
ExcludeUIDRange []string `inbound:"exclude-uid-range,omitempty"`
|
ExcludeUID []uint32 `inbound:"exclude-uid,omitempty"`
|
||||||
ExcludeSrcPort []uint16 `inbound:"exclude-src-port,omitempty"`
|
ExcludeUIDRange []string `inbound:"exclude-uid-range,omitempty"`
|
||||||
ExcludeSrcPortRange []string `inbound:"exclude-src-port-range,omitempty"`
|
ExcludeSrcPort []uint16 `inbound:"exclude-src-port,omitempty"`
|
||||||
ExcludeDstPort []uint16 `inbound:"exclude-dst-port,omitempty"`
|
ExcludeSrcPortRange []string `inbound:"exclude-src-port-range,omitempty"`
|
||||||
ExcludeDstPortRange []string `inbound:"exclude-dst-port-range,omitempty"`
|
ExcludeDstPort []uint16 `inbound:"exclude-dst-port,omitempty"`
|
||||||
IncludeAndroidUser []int `inbound:"include-android-user,omitempty"`
|
ExcludeDstPortRange []string `inbound:"exclude-dst-port-range,omitempty"`
|
||||||
IncludePackage []string `inbound:"include-package,omitempty"`
|
IncludeAndroidUser []int `inbound:"include-android-user,omitempty"`
|
||||||
ExcludePackage []string `inbound:"exclude-package,omitempty"`
|
IncludePackage []string `inbound:"include-package,omitempty"`
|
||||||
EndpointIndependentNat bool `inbound:"endpoint-independent-nat,omitempty"`
|
ExcludePackage []string `inbound:"exclude-package,omitempty"`
|
||||||
UDPTimeout int64 `inbound:"udp-timeout,omitempty"`
|
EndpointIndependentNat bool `inbound:"endpoint-independent-nat,omitempty"`
|
||||||
FileDescriptor int `inbound:"file-descriptor,omitempty"`
|
UDPTimeout int64 `inbound:"udp-timeout,omitempty"`
|
||||||
|
FileDescriptor int `inbound:"file-descriptor,omitempty"`
|
||||||
|
|
||||||
Inet4RouteAddress []string `inbound:"inet4-route-address,omitempty"`
|
Inet4RouteAddress []netip.Prefix `inbound:"inet4-route-address,omitempty"`
|
||||||
Inet6RouteAddress []string `inbound:"inet6-route-address,omitempty"`
|
Inet6RouteAddress []netip.Prefix `inbound:"inet6-route-address,omitempty"`
|
||||||
Inet4RouteExcludeAddress []string `inbound:"inet4-route-exclude-address,omitempty"`
|
Inet4RouteExcludeAddress []netip.Prefix `inbound:"inet4-route-exclude-address,omitempty"`
|
||||||
Inet6RouteExcludeAddress []string `inbound:"inet6-route-exclude-address,omitempty"`
|
Inet6RouteExcludeAddress []netip.Prefix `inbound:"inet6-route-exclude-address,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var _ encoding.TextUnmarshaler = (*netip.Addr)(nil) // ensure netip.Addr can decode direct by structure package
|
||||||
|
var _ encoding.TextUnmarshaler = (*netip.Prefix)(nil) // ensure netip.Prefix can decode direct by structure package
|
||||||
|
var _ encoding.TextUnmarshaler = (*C.TUNStack)(nil) // ensure C.TUNStack can decode direct by structure package
|
||||||
|
|
||||||
func (o TunOption) Equal(config C.InboundConfig) bool {
|
func (o TunOption) Equal(config C.InboundConfig) bool {
|
||||||
return optionToString(o) == optionToString(config)
|
return optionToString(o) == optionToString(config)
|
||||||
}
|
}
|
||||||
@@ -72,68 +77,31 @@ func NewTun(options *TunOption) (*Tun, error) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
stack, exist := C.StackTypeMapping[strings.ToLower(options.Stack)]
|
|
||||||
if !exist {
|
|
||||||
return nil, errors.New("invalid tun stack")
|
|
||||||
}
|
|
||||||
|
|
||||||
routeAddress, err := LC.StringSliceToNetipPrefixSlice(options.RouteAddress)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
routeExcludeAddress, err := LC.StringSliceToNetipPrefixSlice(options.RouteExcludeAddress)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
inet4Address, err := LC.StringSliceToNetipPrefixSlice(options.Inet4Address)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
inet6Address, err := LC.StringSliceToNetipPrefixSlice(options.Inet6Address)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
inet4RouteAddress, err := LC.StringSliceToNetipPrefixSlice(options.Inet4RouteAddress)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
inet6RouteAddress, err := LC.StringSliceToNetipPrefixSlice(options.Inet6RouteAddress)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
inet4RouteExcludeAddress, err := LC.StringSliceToNetipPrefixSlice(options.Inet4RouteExcludeAddress)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
inet6RouteExcludeAddress, err := LC.StringSliceToNetipPrefixSlice(options.Inet6RouteExcludeAddress)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return &Tun{
|
return &Tun{
|
||||||
Base: base,
|
Base: base,
|
||||||
config: options,
|
config: options,
|
||||||
tun: LC.Tun{
|
tun: LC.Tun{
|
||||||
Enable: true,
|
Enable: true,
|
||||||
Device: options.Device,
|
Device: options.Device,
|
||||||
Stack: stack,
|
Stack: options.Stack,
|
||||||
DNSHijack: options.DNSHijack,
|
DNSHijack: options.DNSHijack,
|
||||||
AutoRoute: options.AutoRoute,
|
AutoRoute: options.AutoRoute,
|
||||||
AutoDetectInterface: options.AutoDetectInterface,
|
AutoDetectInterface: options.AutoDetectInterface,
|
||||||
MTU: options.MTU,
|
MTU: options.MTU,
|
||||||
GSO: options.GSO,
|
GSO: options.GSO,
|
||||||
GSOMaxSize: options.GSOMaxSize,
|
GSOMaxSize: options.GSOMaxSize,
|
||||||
Inet4Address: inet4Address,
|
Inet4Address: options.Inet4Address,
|
||||||
Inet6Address: inet6Address,
|
Inet6Address: options.Inet6Address,
|
||||||
IPRoute2TableIndex: options.IPRoute2TableIndex,
|
IPRoute2TableIndex: options.IPRoute2TableIndex,
|
||||||
IPRoute2RuleIndex: options.IPRoute2RuleIndex,
|
IPRoute2RuleIndex: options.IPRoute2RuleIndex,
|
||||||
AutoRedirect: options.AutoRedirect,
|
AutoRedirect: options.AutoRedirect,
|
||||||
AutoRedirectInputMark: options.AutoRedirectInputMark,
|
AutoRedirectInputMark: options.AutoRedirectInputMark,
|
||||||
AutoRedirectOutputMark: options.AutoRedirectOutputMark,
|
AutoRedirectOutputMark: options.AutoRedirectOutputMark,
|
||||||
|
LoopbackAddress: options.LoopbackAddress,
|
||||||
StrictRoute: options.StrictRoute,
|
StrictRoute: options.StrictRoute,
|
||||||
RouteAddress: routeAddress,
|
RouteAddress: options.RouteAddress,
|
||||||
RouteAddressSet: options.RouteAddressSet,
|
RouteAddressSet: options.RouteAddressSet,
|
||||||
RouteExcludeAddress: routeExcludeAddress,
|
RouteExcludeAddress: options.RouteExcludeAddress,
|
||||||
RouteExcludeAddressSet: options.RouteExcludeAddressSet,
|
RouteExcludeAddressSet: options.RouteExcludeAddressSet,
|
||||||
IncludeInterface: options.IncludeInterface,
|
IncludeInterface: options.IncludeInterface,
|
||||||
ExcludeInterface: options.ExcludeInterface,
|
ExcludeInterface: options.ExcludeInterface,
|
||||||
@@ -152,10 +120,10 @@ func NewTun(options *TunOption) (*Tun, error) {
|
|||||||
UDPTimeout: options.UDPTimeout,
|
UDPTimeout: options.UDPTimeout,
|
||||||
FileDescriptor: options.FileDescriptor,
|
FileDescriptor: options.FileDescriptor,
|
||||||
|
|
||||||
Inet4RouteAddress: inet4RouteAddress,
|
Inet4RouteAddress: options.Inet4RouteAddress,
|
||||||
Inet6RouteAddress: inet6RouteAddress,
|
Inet6RouteAddress: options.Inet6RouteAddress,
|
||||||
Inet4RouteExcludeAddress: inet4RouteExcludeAddress,
|
Inet4RouteExcludeAddress: options.Inet4RouteExcludeAddress,
|
||||||
Inet6RouteExcludeAddress: inet6RouteExcludeAddress,
|
Inet6RouteExcludeAddress: options.Inet6RouteExcludeAddress,
|
||||||
},
|
},
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -64,7 +64,7 @@ func ParseListener(mapping map[string]any) (C.InboundListener, error) {
|
|||||||
listener, err = IN.NewTunnel(tunnelOption)
|
listener, err = IN.NewTunnel(tunnelOption)
|
||||||
case "tun":
|
case "tun":
|
||||||
tunOption := &IN.TunOption{
|
tunOption := &IN.TunOption{
|
||||||
Stack: C.TunGvisor.String(),
|
Stack: C.TunGvisor,
|
||||||
DNSHijack: []string{"0.0.0.0:53"}, // default hijack all dns query
|
DNSHijack: []string{"0.0.0.0:53"}, // default hijack all dns query
|
||||||
}
|
}
|
||||||
err = decoder.Decode(mapping, tunOption)
|
err = decoder.Decode(mapping, tunOption)
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import (
|
|||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"net"
|
"net"
|
||||||
|
"runtime/debug"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
N "github.com/metacubex/mihomo/common/net"
|
N "github.com/metacubex/mihomo/common/net"
|
||||||
@@ -19,6 +20,7 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type Conn = utls.Conn
|
type Conn = utls.Conn
|
||||||
|
type LimitFallback = utls.RealityLimitFallback
|
||||||
|
|
||||||
type Config struct {
|
type Config struct {
|
||||||
Dest string
|
Dest string
|
||||||
@@ -27,6 +29,9 @@ type Config struct {
|
|||||||
ServerNames []string
|
ServerNames []string
|
||||||
MaxTimeDifference int
|
MaxTimeDifference int
|
||||||
Proxy string
|
Proxy string
|
||||||
|
|
||||||
|
LimitFallbackUpload LimitFallback
|
||||||
|
LimitFallbackDownload LimitFallback
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c Config) Build(tunnel C.Tunnel) (*Builder, error) {
|
func (c Config) Build(tunnel C.Tunnel) (*Builder, error) {
|
||||||
@@ -72,6 +77,9 @@ func (c Config) Build(tunnel C.Tunnel) (*Builder, error) {
|
|||||||
return inner.HandleTcp(tunnel, address, c.Proxy)
|
return inner.HandleTcp(tunnel, address, c.Proxy)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
realityConfig.LimitFallbackUpload = c.LimitFallbackUpload
|
||||||
|
realityConfig.LimitFallbackDownload = c.LimitFallbackDownload
|
||||||
|
|
||||||
return &Builder{realityConfig}, nil
|
return &Builder{realityConfig}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -89,7 +97,8 @@ func (b Builder) NewListener(l net.Listener) net.Listener {
|
|||||||
// We fixed it by calling Close() directly.
|
// We fixed it by calling Close() directly.
|
||||||
return realityConnWrapper{c}, nil
|
return realityConnWrapper{c}, nil
|
||||||
}, func(a any) {
|
}, func(a any) {
|
||||||
log.Errorln("reality server panic: %s", a)
|
stack := debug.Stack()
|
||||||
|
log.Errorln("reality server panic: %s\n%s", a, stack)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ package sing
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"golang.org/x/exp/slices"
|
"golang.org/x/exp/slices"
|
||||||
|
"net"
|
||||||
|
|
||||||
"github.com/metacubex/mihomo/adapter/inbound"
|
"github.com/metacubex/mihomo/adapter/inbound"
|
||||||
|
|
||||||
@@ -29,3 +30,18 @@ func getAdditions(ctx context.Context) (additions []inbound.Addition) {
|
|||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var ctxKeyInAddr = contextKey("InAddr")
|
||||||
|
|
||||||
|
func WithInAddr(ctx context.Context, inAddr net.Addr) context.Context {
|
||||||
|
return context.WithValue(ctx, ctxKeyInAddr, inAddr)
|
||||||
|
}
|
||||||
|
|
||||||
|
func getInAddr(ctx context.Context) net.Addr {
|
||||||
|
if v := ctx.Value(ctxKeyInAddr); v != nil {
|
||||||
|
if a, ok := v.(net.Addr); ok {
|
||||||
|
return a
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ import (
|
|||||||
|
|
||||||
mux "github.com/metacubex/sing-mux"
|
mux "github.com/metacubex/sing-mux"
|
||||||
vmess "github.com/metacubex/sing-vmess"
|
vmess "github.com/metacubex/sing-vmess"
|
||||||
|
"github.com/metacubex/sing/common"
|
||||||
"github.com/metacubex/sing/common/buf"
|
"github.com/metacubex/sing/common/buf"
|
||||||
"github.com/metacubex/sing/common/bufio"
|
"github.com/metacubex/sing/common/bufio"
|
||||||
"github.com/metacubex/sing/common/bufio/deadline"
|
"github.com/metacubex/sing/common/bufio/deadline"
|
||||||
@@ -146,11 +147,11 @@ func (h *ListenerHandler) NewConnection(ctx context.Context, conn net.Conn, meta
|
|||||||
func (h *ListenerHandler) NewPacketConnection(ctx context.Context, conn network.PacketConn, metadata M.Metadata) error {
|
func (h *ListenerHandler) NewPacketConnection(ctx context.Context, conn network.PacketConn, metadata M.Metadata) error {
|
||||||
defer func() { _ = conn.Close() }()
|
defer func() { _ = conn.Close() }()
|
||||||
mutex := sync.Mutex{}
|
mutex := sync.Mutex{}
|
||||||
conn2 := bufio.NewNetPacketConn(conn) // a new interface to set nil in defer
|
writer := bufio.NewNetPacketWriter(conn) // a new interface to set nil in defer
|
||||||
defer func() {
|
defer func() {
|
||||||
mutex.Lock() // this goroutine must exit after all conn.WritePacket() is not running
|
mutex.Lock() // this goroutine must exit after all conn.WritePacket() is not running
|
||||||
defer mutex.Unlock()
|
defer mutex.Unlock()
|
||||||
conn2 = nil
|
writer = nil
|
||||||
}()
|
}()
|
||||||
rwOptions := network.ReadWaitOptions{}
|
rwOptions := network.ReadWaitOptions{}
|
||||||
readWaiter, isReadWaiter := bufio.CreatePacketReadWaiter(conn)
|
readWaiter, isReadWaiter := bufio.CreatePacketReadWaiter(conn)
|
||||||
@@ -180,32 +181,59 @@ func (h *ListenerHandler) NewPacketConnection(ctx context.Context, conn network.
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
cPacket := &packet{
|
cPacket := &packet{
|
||||||
conn: &conn2,
|
writer: &writer,
|
||||||
mutex: &mutex,
|
mutex: &mutex,
|
||||||
rAddr: metadata.Source.UDPAddr(),
|
rAddr: metadata.Source.UDPAddr(),
|
||||||
lAddr: conn.LocalAddr(),
|
lAddr: conn.LocalAddr(),
|
||||||
buff: buff,
|
buff: buff,
|
||||||
}
|
}
|
||||||
|
if lAddr := getInAddr(ctx); lAddr != nil {
|
||||||
cMetadata := &C.Metadata{
|
cPacket.lAddr = lAddr
|
||||||
NetWork: C.UDP,
|
|
||||||
Type: h.Type,
|
|
||||||
}
|
}
|
||||||
if metadata.Source.IsIP() && metadata.Source.Fqdn == "" {
|
h.handlePacket(ctx, cPacket, metadata.Source, dest)
|
||||||
cMetadata.RawSrcAddr = metadata.Source.Unwrap().UDPAddr()
|
|
||||||
}
|
|
||||||
if dest.IsIP() && dest.Fqdn == "" {
|
|
||||||
cMetadata.RawDstAddr = dest.Unwrap().UDPAddr()
|
|
||||||
}
|
|
||||||
inbound.ApplyAdditions(cMetadata, inbound.WithDstAddr(dest), inbound.WithSrcAddr(metadata.Source), inbound.WithInAddr(conn.LocalAddr()))
|
|
||||||
inbound.ApplyAdditions(cMetadata, h.Additions...)
|
|
||||||
inbound.ApplyAdditions(cMetadata, getAdditions(ctx)...)
|
|
||||||
|
|
||||||
h.Tunnel.HandleUDPPacket(cPacket, cMetadata)
|
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type localAddr interface {
|
||||||
|
LocalAddr() net.Addr
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *ListenerHandler) NewPacket(ctx context.Context, key netip.AddrPort, buffer *buf.Buffer, metadata M.Metadata, init func(natConn network.PacketConn) network.PacketWriter) {
|
||||||
|
writer := bufio.NewNetPacketWriter(init(nil))
|
||||||
|
mutex := sync.Mutex{}
|
||||||
|
cPacket := &packet{
|
||||||
|
writer: &writer,
|
||||||
|
mutex: &mutex,
|
||||||
|
rAddr: metadata.Source.UDPAddr(),
|
||||||
|
buff: buffer,
|
||||||
|
}
|
||||||
|
if conn, ok := common.Cast[localAddr](writer); ok {
|
||||||
|
cPacket.rAddr = conn.LocalAddr()
|
||||||
|
} else {
|
||||||
|
cPacket.rAddr = metadata.Source.UDPAddr() // tun does not have real inAddr
|
||||||
|
}
|
||||||
|
h.handlePacket(ctx, cPacket, metadata.Source, metadata.Destination)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *ListenerHandler) handlePacket(ctx context.Context, cPacket *packet, source M.Socksaddr, destination M.Socksaddr) {
|
||||||
|
cMetadata := &C.Metadata{
|
||||||
|
NetWork: C.UDP,
|
||||||
|
Type: h.Type,
|
||||||
|
}
|
||||||
|
if source.IsIP() && source.Fqdn == "" {
|
||||||
|
cMetadata.RawSrcAddr = source.Unwrap().UDPAddr()
|
||||||
|
}
|
||||||
|
if destination.IsIP() && destination.Fqdn == "" {
|
||||||
|
cMetadata.RawDstAddr = destination.Unwrap().UDPAddr()
|
||||||
|
}
|
||||||
|
inbound.ApplyAdditions(cMetadata, inbound.WithDstAddr(destination), inbound.WithSrcAddr(source), inbound.WithInAddr(cPacket.InAddr()))
|
||||||
|
inbound.ApplyAdditions(cMetadata, h.Additions...)
|
||||||
|
inbound.ApplyAdditions(cMetadata, getAdditions(ctx)...)
|
||||||
|
|
||||||
|
h.Tunnel.HandleUDPPacket(cPacket, cMetadata)
|
||||||
|
}
|
||||||
|
|
||||||
func (h *ListenerHandler) NewError(ctx context.Context, err error) {
|
func (h *ListenerHandler) NewError(ctx context.Context, err error) {
|
||||||
log.Warnln("%s listener get error: %+v", h.Type.String(), err)
|
log.Warnln("%s listener get error: %+v", h.Type.String(), err)
|
||||||
}
|
}
|
||||||
@@ -225,11 +253,11 @@ func ShouldIgnorePacketError(err error) bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type packet struct {
|
type packet struct {
|
||||||
conn *network.NetPacketConn
|
writer *network.NetPacketWriter
|
||||||
mutex *sync.Mutex
|
mutex *sync.Mutex
|
||||||
rAddr net.Addr
|
rAddr net.Addr
|
||||||
lAddr net.Addr
|
lAddr net.Addr
|
||||||
buff *buf.Buffer
|
buff *buf.Buffer
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *packet) Data() []byte {
|
func (c *packet) Data() []byte {
|
||||||
@@ -245,7 +273,7 @@ func (c *packet) WriteBack(b []byte, addr net.Addr) (n int, err error) {
|
|||||||
|
|
||||||
c.mutex.Lock()
|
c.mutex.Lock()
|
||||||
defer c.mutex.Unlock()
|
defer c.mutex.Unlock()
|
||||||
conn := *c.conn
|
conn := *c.writer
|
||||||
if conn == nil {
|
if conn == nil {
|
||||||
err = errors.New("writeBack to closed connection")
|
err = errors.New("writeBack to closed connection")
|
||||||
return
|
return
|
||||||
|
|||||||
@@ -156,11 +156,7 @@ func New(config LC.ShadowsocksServer, tunnel C.Tunnel, additions ...inbound.Addi
|
|||||||
|
|
||||||
go func() {
|
go func() {
|
||||||
conn := bufio.NewPacketConn(ul)
|
conn := bufio.NewPacketConn(ul)
|
||||||
rwOptions := network.ReadWaitOptions{
|
rwOptions := network.NewReadWaitOptions(conn, sl.service)
|
||||||
FrontHeadroom: network.CalculateFrontHeadroom(sl.service),
|
|
||||||
RearHeadroom: network.CalculateRearHeadroom(sl.service),
|
|
||||||
MTU: network.CalculateMTU(conn, sl.service),
|
|
||||||
}
|
|
||||||
readWaiter, isReadWaiter := bufio.CreatePacketReadWaiter(conn)
|
readWaiter, isReadWaiter := bufio.CreatePacketReadWaiter(conn)
|
||||||
if isReadWaiter {
|
if isReadWaiter {
|
||||||
readWaiter.InitializeReadWaiter(rwOptions)
|
readWaiter.InitializeReadWaiter(rwOptions)
|
||||||
@@ -188,7 +184,9 @@ func New(config LC.ShadowsocksServer, tunnel C.Tunnel, additions ...inbound.Addi
|
|||||||
}
|
}
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
_ = sl.service.NewPacket(context.TODO(), conn, buff, M.Metadata{
|
ctx := context.TODO()
|
||||||
|
ctx = sing.WithInAddr(ctx, ul.LocalAddr())
|
||||||
|
_ = sl.service.NewPacket(ctx, conn, buff, M.Metadata{
|
||||||
Protocol: "shadowsocks",
|
Protocol: "shadowsocks",
|
||||||
Source: dest,
|
Source: dest,
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -43,16 +43,31 @@ func (h *ListenerHandler) NewConnection(ctx context.Context, conn net.Conn, meta
|
|||||||
return h.ListenerHandler.NewConnection(ctx, conn, metadata)
|
return h.ListenerHandler.NewConnection(ctx, conn, metadata)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (h *ListenerHandler) NewPacket(ctx context.Context, key netip.AddrPort, buffer *buf.Buffer, metadata M.Metadata, init func(natConn network.PacketConn) network.PacketWriter) {
|
||||||
|
if h.ShouldHijackDns(metadata.Destination.AddrPort()) {
|
||||||
|
log.Debugln("[DNS] hijack udp:%s from %s", metadata.Destination.String(), metadata.Source.String())
|
||||||
|
writer := init(nil)
|
||||||
|
rwOptions := network.ReadWaitOptions{
|
||||||
|
FrontHeadroom: network.CalculateFrontHeadroom(writer),
|
||||||
|
RearHeadroom: network.CalculateRearHeadroom(writer),
|
||||||
|
MTU: resolver.SafeDnsPacketSize,
|
||||||
|
}
|
||||||
|
go relayDnsPacket(ctx, buffer, rwOptions, metadata.Destination, nil, &writer)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
h.ListenerHandler.NewPacket(ctx, key, buffer, metadata, init)
|
||||||
|
}
|
||||||
|
|
||||||
func (h *ListenerHandler) NewPacketConnection(ctx context.Context, conn network.PacketConn, metadata M.Metadata) error {
|
func (h *ListenerHandler) NewPacketConnection(ctx context.Context, conn network.PacketConn, metadata M.Metadata) error {
|
||||||
if h.ShouldHijackDns(metadata.Destination.AddrPort()) {
|
if h.ShouldHijackDns(metadata.Destination.AddrPort()) {
|
||||||
log.Debugln("[DNS] hijack udp:%s from %s", metadata.Destination.String(), metadata.Source.String())
|
log.Debugln("[DNS] hijack udp:%s from %s", metadata.Destination.String(), metadata.Source.String())
|
||||||
defer func() { _ = conn.Close() }()
|
defer func() { _ = conn.Close() }()
|
||||||
mutex := sync.Mutex{}
|
mutex := sync.Mutex{}
|
||||||
conn2 := conn // a new interface to set nil in defer
|
var writer network.PacketWriter = conn // a new interface to set nil in defer
|
||||||
defer func() {
|
defer func() {
|
||||||
mutex.Lock() // this goroutine must exit after all conn.WritePacket() is not running
|
mutex.Lock() // this goroutine must exit after all conn.WritePacket() is not running
|
||||||
defer mutex.Unlock()
|
defer mutex.Unlock()
|
||||||
conn2 = nil
|
writer = nil
|
||||||
}()
|
}()
|
||||||
rwOptions := network.ReadWaitOptions{
|
rwOptions := network.ReadWaitOptions{
|
||||||
FrontHeadroom: network.CalculateFrontHeadroom(conn),
|
FrontHeadroom: network.CalculateFrontHeadroom(conn),
|
||||||
@@ -89,43 +104,47 @@ func (h *ListenerHandler) NewPacketConnection(ctx context.Context, conn network.
|
|||||||
}
|
}
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
go func() {
|
go relayDnsPacket(ctx, readBuff, rwOptions, dest, &mutex, &writer)
|
||||||
ctx, cancel := context.WithTimeout(ctx, resolver.DefaultDnsRelayTimeout)
|
|
||||||
defer cancel()
|
|
||||||
inData := readBuff.Bytes()
|
|
||||||
writeBuff := readBuff
|
|
||||||
writeBuff.Resize(writeBuff.Start(), 0)
|
|
||||||
if len(writeBuff.FreeBytes()) < resolver.SafeDnsPacketSize { // only create a new buffer when space don't enough
|
|
||||||
writeBuff = rwOptions.NewPacketBuffer()
|
|
||||||
}
|
|
||||||
msg, err := resolver.RelayDnsPacket(ctx, inData, writeBuff.FreeBytes())
|
|
||||||
if writeBuff != readBuff {
|
|
||||||
readBuff.Release()
|
|
||||||
}
|
|
||||||
if err != nil {
|
|
||||||
writeBuff.Release()
|
|
||||||
return
|
|
||||||
}
|
|
||||||
writeBuff.Truncate(len(msg))
|
|
||||||
mutex.Lock()
|
|
||||||
defer mutex.Unlock()
|
|
||||||
conn := conn2
|
|
||||||
if conn == nil {
|
|
||||||
writeBuff.Release()
|
|
||||||
return
|
|
||||||
}
|
|
||||||
err = conn.WritePacket(writeBuff, dest) // WritePacket will release writeBuff
|
|
||||||
if err != nil {
|
|
||||||
writeBuff.Release()
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
return h.ListenerHandler.NewPacketConnection(ctx, conn, metadata)
|
return h.ListenerHandler.NewPacketConnection(ctx, conn, metadata)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func relayDnsPacket(ctx context.Context, readBuff *buf.Buffer, rwOptions network.ReadWaitOptions, dest M.Socksaddr, mutex *sync.Mutex, writer *network.PacketWriter) {
|
||||||
|
ctx, cancel := context.WithTimeout(ctx, resolver.DefaultDnsRelayTimeout)
|
||||||
|
defer cancel()
|
||||||
|
inData := readBuff.Bytes()
|
||||||
|
writeBuff := readBuff
|
||||||
|
writeBuff.Resize(writeBuff.Start(), 0)
|
||||||
|
if len(writeBuff.FreeBytes()) < resolver.SafeDnsPacketSize { // only create a new buffer when space don't enough
|
||||||
|
writeBuff = rwOptions.NewPacketBuffer()
|
||||||
|
}
|
||||||
|
msg, err := resolver.RelayDnsPacket(ctx, inData, writeBuff.FreeBytes())
|
||||||
|
if writeBuff != readBuff {
|
||||||
|
readBuff.Release()
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
writeBuff.Release()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
writeBuff.Truncate(len(msg))
|
||||||
|
if mutex != nil {
|
||||||
|
mutex.Lock()
|
||||||
|
defer mutex.Unlock()
|
||||||
|
}
|
||||||
|
conn := *writer
|
||||||
|
if conn == nil {
|
||||||
|
writeBuff.Release()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
err = conn.WritePacket(writeBuff, dest) // WritePacket will release writeBuff
|
||||||
|
if err != nil {
|
||||||
|
writeBuff.Release()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func (h *ListenerHandler) TypeMutation(typ C.Type) *ListenerHandler {
|
func (h *ListenerHandler) TypeMutation(typ C.Type) *ListenerHandler {
|
||||||
handle := *h
|
handle := *h
|
||||||
handle.ListenerHandler = h.ListenerHandler.TypeMutation(typ)
|
handle.ListenerHandler = h.ListenerHandler.TypeMutation(typ)
|
||||||
|
|||||||
@@ -347,6 +347,8 @@ func New(options LC.Tun, tunnel C.Tunnel, additions ...inbound.Addition) (l *Lis
|
|||||||
IPRoute2RuleIndex: ruleIndex,
|
IPRoute2RuleIndex: ruleIndex,
|
||||||
AutoRedirectInputMark: inputMark,
|
AutoRedirectInputMark: inputMark,
|
||||||
AutoRedirectOutputMark: outputMark,
|
AutoRedirectOutputMark: outputMark,
|
||||||
|
Inet4LoopbackAddress: common.Filter(options.LoopbackAddress, netip.Addr.Is4),
|
||||||
|
Inet6LoopbackAddress: common.Filter(options.LoopbackAddress, netip.Addr.Is6),
|
||||||
StrictRoute: options.StrictRoute,
|
StrictRoute: options.StrictRoute,
|
||||||
Inet4RouteAddress: inet4RouteAddress,
|
Inet4RouteAddress: inet4RouteAddress,
|
||||||
Inet6RouteAddress: inet6RouteAddress,
|
Inet6RouteAddress: inet6RouteAddress,
|
||||||
|
|||||||
35
log/level.go
35
log/level.go
@@ -1,7 +1,6 @@
|
|||||||
package log
|
package log
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
|
||||||
"errors"
|
"errors"
|
||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
@@ -25,30 +24,6 @@ const (
|
|||||||
|
|
||||||
type LogLevel int
|
type LogLevel int
|
||||||
|
|
||||||
// UnmarshalYAML unserialize LogLevel with yaml
|
|
||||||
func (l *LogLevel) UnmarshalYAML(unmarshal func(any) error) error {
|
|
||||||
var tp string
|
|
||||||
unmarshal(&tp)
|
|
||||||
level, exist := LogLevelMapping[strings.ToLower(tp)]
|
|
||||||
if !exist {
|
|
||||||
return errors.New("invalid log-level")
|
|
||||||
}
|
|
||||||
*l = level
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// UnmarshalJSON unserialize LogLevel with json
|
|
||||||
func (l *LogLevel) UnmarshalJSON(data []byte) error {
|
|
||||||
var tp string
|
|
||||||
json.Unmarshal(data, &tp)
|
|
||||||
level, exist := LogLevelMapping[strings.ToLower(tp)]
|
|
||||||
if !exist {
|
|
||||||
return errors.New("invalid log-level")
|
|
||||||
}
|
|
||||||
*l = level
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// UnmarshalText unserialize LogLevel
|
// UnmarshalText unserialize LogLevel
|
||||||
func (l *LogLevel) UnmarshalText(data []byte) error {
|
func (l *LogLevel) UnmarshalText(data []byte) error {
|
||||||
level, exist := LogLevelMapping[strings.ToLower(string(data))]
|
level, exist := LogLevelMapping[strings.ToLower(string(data))]
|
||||||
@@ -59,16 +34,6 @@ func (l *LogLevel) UnmarshalText(data []byte) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// MarshalYAML serialize LogLevel with yaml
|
|
||||||
func (l LogLevel) MarshalYAML() (any, error) {
|
|
||||||
return l.String(), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// MarshalJSON serialize LogLevel with json
|
|
||||||
func (l LogLevel) MarshalJSON() ([]byte, error) {
|
|
||||||
return json.Marshal(l.String())
|
|
||||||
}
|
|
||||||
|
|
||||||
// MarshalText serialize LogLevel
|
// MarshalText serialize LogLevel
|
||||||
func (l LogLevel) MarshalText() ([]byte, error) {
|
func (l LogLevel) MarshalText() ([]byte, error) {
|
||||||
return []byte(l.String()), nil
|
return []byte(l.String()), nil
|
||||||
|
|||||||
@@ -21,14 +21,6 @@ var (
|
|||||||
type Base struct {
|
type Base struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b *Base) ShouldFindProcess() bool {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
func (b *Base) ShouldResolveIP() bool {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
func (b *Base) ProviderNames() []string { return nil }
|
func (b *Base) ProviderNames() []string { return nil }
|
||||||
|
|
||||||
func ParseParams(params []string) (isSrc bool, noResolve bool) {
|
func ParseParams(params []string) (isSrc bool, noResolve bool) {
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ func (d *Domain) RuleType() C.RuleType {
|
|||||||
return C.Domain
|
return C.Domain
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *Domain) Match(metadata *C.Metadata) (bool, string) {
|
func (d *Domain) Match(metadata *C.Metadata, helper C.RuleMatchHelper) (bool, string) {
|
||||||
return metadata.RuleHost() == d.domain, d.adapter
|
return metadata.RuleHost() == d.domain, d.adapter
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ func (dk *DomainKeyword) RuleType() C.RuleType {
|
|||||||
return C.DomainKeyword
|
return C.DomainKeyword
|
||||||
}
|
}
|
||||||
|
|
||||||
func (dk *DomainKeyword) Match(metadata *C.Metadata) (bool, string) {
|
func (dk *DomainKeyword) Match(metadata *C.Metadata, helper C.RuleMatchHelper) (bool, string) {
|
||||||
domain := metadata.RuleHost()
|
domain := metadata.RuleHost()
|
||||||
return strings.Contains(domain, dk.keyword), dk.adapter
|
return strings.Contains(domain, dk.keyword), dk.adapter
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ func (dr *DomainRegex) RuleType() C.RuleType {
|
|||||||
return C.DomainRegex
|
return C.DomainRegex
|
||||||
}
|
}
|
||||||
|
|
||||||
func (dr *DomainRegex) Match(metadata *C.Metadata) (bool, string) {
|
func (dr *DomainRegex) Match(metadata *C.Metadata, helper C.RuleMatchHelper) (bool, string) {
|
||||||
domain := metadata.RuleHost()
|
domain := metadata.RuleHost()
|
||||||
match, _ := dr.regex.MatchString(domain)
|
match, _ := dr.regex.MatchString(domain)
|
||||||
return match, dr.adapter
|
return match, dr.adapter
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ func (ds *DomainSuffix) RuleType() C.RuleType {
|
|||||||
return C.DomainSuffix
|
return C.DomainSuffix
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ds *DomainSuffix) Match(metadata *C.Metadata) (bool, string) {
|
func (ds *DomainSuffix) Match(metadata *C.Metadata, helper C.RuleMatchHelper) (bool, string) {
|
||||||
domain := metadata.RuleHost()
|
domain := metadata.RuleHost()
|
||||||
return strings.HasSuffix(domain, "."+ds.suffix) || domain == ds.suffix, ds.adapter
|
return strings.HasSuffix(domain, "."+ds.suffix) || domain == ds.suffix, ds.adapter
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ func (d *DSCP) RuleType() C.RuleType {
|
|||||||
return C.DSCP
|
return C.DSCP
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *DSCP) Match(metadata *C.Metadata) (bool, string) {
|
func (d *DSCP) Match(metadata *C.Metadata, helper C.RuleMatchHelper) (bool, string) {
|
||||||
return d.ranges.Check(metadata.DSCP), d.adapter
|
return d.ranges.Check(metadata.DSCP), d.adapter
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ func (f *Match) RuleType() C.RuleType {
|
|||||||
return C.MATCH
|
return C.MATCH
|
||||||
}
|
}
|
||||||
|
|
||||||
func (f *Match) Match(metadata *C.Metadata) (bool, string) {
|
func (f *Match) Match(metadata *C.Metadata, helper C.RuleMatchHelper) (bool, string) {
|
||||||
return true, f.adapter
|
return true, f.adapter
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -33,7 +33,11 @@ func (g *GEOIP) RuleType() C.RuleType {
|
|||||||
return C.GEOIP
|
return C.GEOIP
|
||||||
}
|
}
|
||||||
|
|
||||||
func (g *GEOIP) Match(metadata *C.Metadata) (bool, string) {
|
func (g *GEOIP) Match(metadata *C.Metadata, helper C.RuleMatchHelper) (bool, string) {
|
||||||
|
if !g.noResolveIP && !g.isSourceIP && helper.ResolveIP != nil {
|
||||||
|
helper.ResolveIP()
|
||||||
|
}
|
||||||
|
|
||||||
ip := metadata.DstIP
|
ip := metadata.DstIP
|
||||||
if g.isSourceIP {
|
if g.isSourceIP {
|
||||||
ip = metadata.SrcIP
|
ip = metadata.SrcIP
|
||||||
@@ -161,10 +165,6 @@ func (g *GEOIP) Payload() string {
|
|||||||
return g.country
|
return g.country
|
||||||
}
|
}
|
||||||
|
|
||||||
func (g *GEOIP) ShouldResolveIP() bool {
|
|
||||||
return !g.noResolveIP
|
|
||||||
}
|
|
||||||
|
|
||||||
func (g *GEOIP) GetCountry() string {
|
func (g *GEOIP) GetCountry() string {
|
||||||
return g.country
|
return g.country
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ func (gs *GEOSITE) RuleType() C.RuleType {
|
|||||||
return C.GEOSITE
|
return C.GEOSITE
|
||||||
}
|
}
|
||||||
|
|
||||||
func (gs *GEOSITE) Match(metadata *C.Metadata) (bool, string) {
|
func (gs *GEOSITE) Match(metadata *C.Metadata, helper C.RuleMatchHelper) (bool, string) {
|
||||||
return gs.MatchDomain(metadata.RuleHost()), gs.adapter
|
return gs.MatchDomain(metadata.RuleHost()), gs.adapter
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -2,8 +2,9 @@ package common
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
C "github.com/metacubex/mihomo/constant"
|
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
C "github.com/metacubex/mihomo/constant"
|
||||||
)
|
)
|
||||||
|
|
||||||
type InName struct {
|
type InName struct {
|
||||||
@@ -13,7 +14,7 @@ type InName struct {
|
|||||||
payload string
|
payload string
|
||||||
}
|
}
|
||||||
|
|
||||||
func (u *InName) Match(metadata *C.Metadata) (bool, string) {
|
func (u *InName) Match(metadata *C.Metadata, helper C.RuleMatchHelper) (bool, string) {
|
||||||
for _, name := range u.names {
|
for _, name := range u.names {
|
||||||
if metadata.InName == name {
|
if metadata.InName == name {
|
||||||
return true, u.adapter
|
return true, u.adapter
|
||||||
@@ -36,8 +37,12 @@ func (u *InName) Payload() string {
|
|||||||
|
|
||||||
func NewInName(iNames, adapter string) (*InName, error) {
|
func NewInName(iNames, adapter string) (*InName, error) {
|
||||||
names := strings.Split(iNames, "/")
|
names := strings.Split(iNames, "/")
|
||||||
if len(names) == 0 {
|
for i, name := range names {
|
||||||
return nil, fmt.Errorf("in name couldn't be empty")
|
name = strings.TrimSpace(name)
|
||||||
|
if len(name) == 0 {
|
||||||
|
return nil, fmt.Errorf("in name couldn't be empty")
|
||||||
|
}
|
||||||
|
names[i] = name
|
||||||
}
|
}
|
||||||
|
|
||||||
return &InName{
|
return &InName{
|
||||||
|
|||||||
@@ -2,8 +2,9 @@ package common
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
C "github.com/metacubex/mihomo/constant"
|
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
C "github.com/metacubex/mihomo/constant"
|
||||||
)
|
)
|
||||||
|
|
||||||
type InType struct {
|
type InType struct {
|
||||||
@@ -13,7 +14,7 @@ type InType struct {
|
|||||||
payload string
|
payload string
|
||||||
}
|
}
|
||||||
|
|
||||||
func (u *InType) Match(metadata *C.Metadata) (bool, string) {
|
func (u *InType) Match(metadata *C.Metadata, helper C.RuleMatchHelper) (bool, string) {
|
||||||
for _, tp := range u.types {
|
for _, tp := range u.types {
|
||||||
if metadata.Type == tp {
|
if metadata.Type == tp {
|
||||||
return true, u.adapter
|
return true, u.adapter
|
||||||
@@ -36,8 +37,12 @@ func (u *InType) Payload() string {
|
|||||||
|
|
||||||
func NewInType(iTypes, adapter string) (*InType, error) {
|
func NewInType(iTypes, adapter string) (*InType, error) {
|
||||||
types := strings.Split(iTypes, "/")
|
types := strings.Split(iTypes, "/")
|
||||||
if len(types) == 0 {
|
for i, tp := range types {
|
||||||
return nil, fmt.Errorf("in type couldn't be empty")
|
tp = strings.TrimSpace(tp)
|
||||||
|
if len(tp) == 0 {
|
||||||
|
return nil, fmt.Errorf("in type couldn't be empty")
|
||||||
|
}
|
||||||
|
types[i] = tp
|
||||||
}
|
}
|
||||||
|
|
||||||
tps, err := parseInTypes(types)
|
tps, err := parseInTypes(types)
|
||||||
|
|||||||
@@ -2,8 +2,9 @@ package common
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
C "github.com/metacubex/mihomo/constant"
|
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
C "github.com/metacubex/mihomo/constant"
|
||||||
)
|
)
|
||||||
|
|
||||||
type InUser struct {
|
type InUser struct {
|
||||||
@@ -13,7 +14,7 @@ type InUser struct {
|
|||||||
payload string
|
payload string
|
||||||
}
|
}
|
||||||
|
|
||||||
func (u *InUser) Match(metadata *C.Metadata) (bool, string) {
|
func (u *InUser) Match(metadata *C.Metadata, helper C.RuleMatchHelper) (bool, string) {
|
||||||
for _, user := range u.users {
|
for _, user := range u.users {
|
||||||
if metadata.InUser == user {
|
if metadata.InUser == user {
|
||||||
return true, u.adapter
|
return true, u.adapter
|
||||||
@@ -36,8 +37,12 @@ func (u *InUser) Payload() string {
|
|||||||
|
|
||||||
func NewInUser(iUsers, adapter string) (*InUser, error) {
|
func NewInUser(iUsers, adapter string) (*InUser, error) {
|
||||||
users := strings.Split(iUsers, "/")
|
users := strings.Split(iUsers, "/")
|
||||||
if len(users) == 0 {
|
for i, user := range users {
|
||||||
return nil, fmt.Errorf("in user couldn't be empty")
|
user = strings.TrimSpace(user)
|
||||||
|
if len(user) == 0 {
|
||||||
|
return nil, fmt.Errorf("in user couldn't be empty")
|
||||||
|
}
|
||||||
|
users[i] = user
|
||||||
}
|
}
|
||||||
|
|
||||||
return &InUser{
|
return &InUser{
|
||||||
|
|||||||
@@ -15,7 +15,11 @@ type ASN struct {
|
|||||||
isSourceIP bool
|
isSourceIP bool
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *ASN) Match(metadata *C.Metadata) (bool, string) {
|
func (a *ASN) Match(metadata *C.Metadata, helper C.RuleMatchHelper) (bool, string) {
|
||||||
|
if !a.noResolveIP && !a.isSourceIP && helper.ResolveIP != nil {
|
||||||
|
helper.ResolveIP()
|
||||||
|
}
|
||||||
|
|
||||||
ip := metadata.DstIP
|
ip := metadata.DstIP
|
||||||
if a.isSourceIP {
|
if a.isSourceIP {
|
||||||
ip = metadata.SrcIP
|
ip = metadata.SrcIP
|
||||||
@@ -49,10 +53,6 @@ func (a *ASN) Payload() string {
|
|||||||
return a.asn
|
return a.asn
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *ASN) ShouldResolveIP() bool {
|
|
||||||
return !a.noResolveIP
|
|
||||||
}
|
|
||||||
|
|
||||||
func (a *ASN) GetASN() string {
|
func (a *ASN) GetASN() string {
|
||||||
return a.asn
|
return a.asn
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -35,7 +35,11 @@ func (i *IPCIDR) RuleType() C.RuleType {
|
|||||||
return C.IPCIDR
|
return C.IPCIDR
|
||||||
}
|
}
|
||||||
|
|
||||||
func (i *IPCIDR) Match(metadata *C.Metadata) (bool, string) {
|
func (i *IPCIDR) Match(metadata *C.Metadata, helper C.RuleMatchHelper) (bool, string) {
|
||||||
|
if !i.noResolveIP && !i.isSourceIP && helper.ResolveIP != nil {
|
||||||
|
helper.ResolveIP()
|
||||||
|
}
|
||||||
|
|
||||||
ip := metadata.DstIP
|
ip := metadata.DstIP
|
||||||
if i.isSourceIP {
|
if i.isSourceIP {
|
||||||
ip = metadata.SrcIP
|
ip = metadata.SrcIP
|
||||||
@@ -51,10 +55,6 @@ func (i *IPCIDR) Payload() string {
|
|||||||
return i.ipnet.String()
|
return i.ipnet.String()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (i *IPCIDR) ShouldResolveIP() bool {
|
|
||||||
return !i.noResolveIP
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewIPCIDR(s string, adapter string, opts ...IPCIDROption) (*IPCIDR, error) {
|
func NewIPCIDR(s string, adapter string, opts ...IPCIDROption) (*IPCIDR, error) {
|
||||||
ipnet, err := netip.ParsePrefix(s)
|
ipnet, err := netip.ParsePrefix(s)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
@@ -22,7 +22,11 @@ func (is *IPSuffix) RuleType() C.RuleType {
|
|||||||
return C.IPSuffix
|
return C.IPSuffix
|
||||||
}
|
}
|
||||||
|
|
||||||
func (is *IPSuffix) Match(metadata *C.Metadata) (bool, string) {
|
func (is *IPSuffix) Match(metadata *C.Metadata, helper C.RuleMatchHelper) (bool, string) {
|
||||||
|
if !is.noResolveIP && !is.isSourceIP && helper.ResolveIP != nil {
|
||||||
|
helper.ResolveIP()
|
||||||
|
}
|
||||||
|
|
||||||
ip := metadata.DstIP
|
ip := metadata.DstIP
|
||||||
if is.isSourceIP {
|
if is.isSourceIP {
|
||||||
ip = metadata.SrcIP
|
ip = metadata.SrcIP
|
||||||
@@ -57,10 +61,6 @@ func (is *IPSuffix) Payload() string {
|
|||||||
return is.payload
|
return is.payload
|
||||||
}
|
}
|
||||||
|
|
||||||
func (is *IPSuffix) ShouldResolveIP() bool {
|
|
||||||
return !is.noResolveIP
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewIPSuffix(payload, adapter string, isSrc, noResolveIP bool) (*IPSuffix, error) {
|
func NewIPSuffix(payload, adapter string, isSrc, noResolveIP bool) (*IPSuffix, error) {
|
||||||
ipnet, err := netip.ParsePrefix(payload)
|
ipnet, err := netip.ParsePrefix(payload)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user