mirror of
https://github.com/MetaCubeX/mihomo.git
synced 2025-12-19 16:30:07 +08:00
Some checks failed
Test / test (1.20, macos-13) (push) Waiting to run
Test / test (1.20, macos-latest) (push) Waiting to run
Test / test (1.20, ubuntu-24.04-arm) (push) Waiting to run
Test / test (1.20, windows-latest) (push) Waiting to run
Test / test (1.21, macos-13) (push) Waiting to run
Test / test (1.21, macos-latest) (push) Waiting to run
Test / test (1.21, ubuntu-24.04-arm) (push) Waiting to run
Test / test (1.21, windows-latest) (push) Waiting to run
Test / test (1.22, macos-13) (push) Waiting to run
Test / test (1.22, macos-latest) (push) Waiting to run
Test / test (1.22, ubuntu-24.04-arm) (push) Waiting to run
Test / test (1.22, windows-latest) (push) Waiting to run
Test / test (1.23, macos-13) (push) Waiting to run
Test / test (1.23, macos-latest) (push) Waiting to run
Test / test (1.23, ubuntu-24.04-arm) (push) Waiting to run
Test / test (1.23, windows-latest) (push) Waiting to run
Test / test (1.24, macos-13) (push) Waiting to run
Test / test (1.24, macos-latest) (push) Waiting to run
Test / test (1.24, ubuntu-24.04-arm) (push) Waiting to run
Test / test (1.24, windows-latest) (push) Waiting to run
Test / test (1.20, ubuntu-latest) (push) Failing after 1s
Test / test (1.21, ubuntu-latest) (push) Failing after 1s
Test / test (1.22, ubuntu-latest) (push) Failing after 1s
Test / test (1.23, ubuntu-latest) (push) Failing after 1s
Test / test (1.24, ubuntu-latest) (push) Failing after 1s
Trigger CMFA Update / trigger-CMFA-update (push) Failing after 1s
The DNS resolution of the overall UDP part has been delayed to the connection initiation stage. During the rule matching process, it will only be triggered when the IP rule without no-resolve is matched. For direct and wireguard outbound, the same logic as the TCP part will be followed, that is, when direct-nameserver (or DNS configured by wireguard) exists, the result of the matching process will be discarded and the domain name will be re-resolved. This re-resolution logic is only effective for fakeip. For reject and DNS outbound, no resolution is required. For other outbound, resolution will still be performed when the connection is initiated, and the domain name will not be sent directly to the remote server at present.
351 lines
7.3 KiB
Go
351 lines
7.3 KiB
Go
package constant
|
|
|
|
import (
|
|
"encoding/json"
|
|
"fmt"
|
|
"net"
|
|
"net/netip"
|
|
"strconv"
|
|
)
|
|
|
|
// SOCKS address types as defined in RFC 1928 section 5.
|
|
const (
|
|
AtypIPv4 AddrType = 1
|
|
AtypDomainName AddrType = 3
|
|
AtypIPv6 AddrType = 4
|
|
)
|
|
|
|
const (
|
|
TCP NetWork = iota
|
|
UDP
|
|
ALLNet
|
|
InvalidNet = 0xff
|
|
)
|
|
|
|
const (
|
|
HTTP Type = iota
|
|
HTTPS
|
|
SOCKS4
|
|
SOCKS5
|
|
SHADOWSOCKS
|
|
VMESS
|
|
VLESS
|
|
REDIR
|
|
TPROXY
|
|
TROJAN
|
|
TUNNEL
|
|
TUN
|
|
TUIC
|
|
HYSTERIA2
|
|
ANYTLS
|
|
INNER
|
|
)
|
|
|
|
type AddrType byte
|
|
|
|
func (a AddrType) String() string {
|
|
switch a {
|
|
case AtypIPv4:
|
|
return "IPv4"
|
|
case AtypDomainName:
|
|
return "DomainName"
|
|
case AtypIPv6:
|
|
return "IPv6"
|
|
default:
|
|
return "Unknown"
|
|
}
|
|
}
|
|
|
|
type NetWork int
|
|
|
|
func (n NetWork) String() string {
|
|
switch n {
|
|
case TCP:
|
|
return "tcp"
|
|
case UDP:
|
|
return "udp"
|
|
case ALLNet:
|
|
return "all"
|
|
default:
|
|
return "invalid"
|
|
}
|
|
}
|
|
|
|
func (n NetWork) MarshalJSON() ([]byte, error) {
|
|
return json.Marshal(n.String())
|
|
}
|
|
|
|
type Type int
|
|
|
|
func (t Type) String() string {
|
|
switch t {
|
|
case HTTP:
|
|
return "HTTP"
|
|
case HTTPS:
|
|
return "HTTPS"
|
|
case SOCKS4:
|
|
return "Socks4"
|
|
case SOCKS5:
|
|
return "Socks5"
|
|
case SHADOWSOCKS:
|
|
return "ShadowSocks"
|
|
case VMESS:
|
|
return "Vmess"
|
|
case VLESS:
|
|
return "Vless"
|
|
case REDIR:
|
|
return "Redir"
|
|
case TPROXY:
|
|
return "TProxy"
|
|
case TROJAN:
|
|
return "Trojan"
|
|
case TUNNEL:
|
|
return "Tunnel"
|
|
case TUN:
|
|
return "Tun"
|
|
case TUIC:
|
|
return "Tuic"
|
|
case HYSTERIA2:
|
|
return "Hysteria2"
|
|
case ANYTLS:
|
|
return "AnyTLS"
|
|
case INNER:
|
|
return "Inner"
|
|
default:
|
|
return "Unknown"
|
|
}
|
|
}
|
|
|
|
func ParseType(t string) (*Type, error) {
|
|
var res Type
|
|
switch t {
|
|
case "HTTP":
|
|
res = HTTP
|
|
case "HTTPS":
|
|
res = HTTPS
|
|
case "SOCKS4":
|
|
res = SOCKS4
|
|
case "SOCKS5":
|
|
res = SOCKS5
|
|
case "SHADOWSOCKS":
|
|
res = SHADOWSOCKS
|
|
case "VMESS":
|
|
res = VMESS
|
|
case "VLESS":
|
|
res = VLESS
|
|
case "REDIR":
|
|
res = REDIR
|
|
case "TPROXY":
|
|
res = TPROXY
|
|
case "TROJAN":
|
|
res = TROJAN
|
|
case "TUNNEL":
|
|
res = TUNNEL
|
|
case "TUN":
|
|
res = TUN
|
|
case "TUIC":
|
|
res = TUIC
|
|
case "HYSTERIA2":
|
|
res = HYSTERIA2
|
|
case "ANYTLS":
|
|
res = ANYTLS
|
|
case "INNER":
|
|
res = INNER
|
|
default:
|
|
return nil, fmt.Errorf("unknown type: %s", t)
|
|
}
|
|
return &res, nil
|
|
}
|
|
|
|
func (t Type) MarshalJSON() ([]byte, error) {
|
|
return json.Marshal(t.String())
|
|
}
|
|
|
|
// Metadata is used to store connection address
|
|
type Metadata struct {
|
|
NetWork NetWork `json:"network"`
|
|
Type Type `json:"type"`
|
|
SrcIP netip.Addr `json:"sourceIP"`
|
|
DstIP netip.Addr `json:"destinationIP"`
|
|
SrcGeoIP []string `json:"sourceGeoIP"` // can be nil if never queried, empty slice if got no result
|
|
DstGeoIP []string `json:"destinationGeoIP"` // can be nil if never queried, empty slice if got no result
|
|
SrcIPASN string `json:"sourceIPASN"`
|
|
DstIPASN string `json:"destinationIPASN"`
|
|
SrcPort uint16 `json:"sourcePort,string"` // `,string` is used to compatible with old version json output
|
|
DstPort uint16 `json:"destinationPort,string"` // `,string` is used to compatible with old version json output
|
|
InIP netip.Addr `json:"inboundIP"`
|
|
InPort uint16 `json:"inboundPort,string"` // `,string` is used to compatible with old version json output
|
|
InName string `json:"inboundName"`
|
|
InUser string `json:"inboundUser"`
|
|
Host string `json:"host"`
|
|
DNSMode DNSMode `json:"dnsMode"`
|
|
Uid uint32 `json:"uid"`
|
|
Process string `json:"process"`
|
|
ProcessPath string `json:"processPath"`
|
|
SpecialProxy string `json:"specialProxy"`
|
|
SpecialRules string `json:"specialRules"`
|
|
RemoteDst string `json:"remoteDestination"`
|
|
DSCP uint8 `json:"dscp"`
|
|
|
|
RawSrcAddr net.Addr `json:"-"`
|
|
RawDstAddr net.Addr `json:"-"`
|
|
// Only domain rule
|
|
SniffHost string `json:"sniffHost"`
|
|
}
|
|
|
|
func (m *Metadata) RemoteAddress() string {
|
|
return net.JoinHostPort(m.String(), strconv.FormatUint(uint64(m.DstPort), 10))
|
|
}
|
|
|
|
func (m *Metadata) SourceAddress() string {
|
|
return net.JoinHostPort(m.SrcIP.String(), strconv.FormatUint(uint64(m.SrcPort), 10))
|
|
}
|
|
|
|
func (m *Metadata) SourceAddrPort() netip.AddrPort {
|
|
return netip.AddrPortFrom(m.SrcIP.Unmap(), m.SrcPort)
|
|
}
|
|
|
|
func (m *Metadata) SourceDetail() string {
|
|
if m.Type == INNER {
|
|
return fmt.Sprintf("%s", MihomoName)
|
|
}
|
|
|
|
switch {
|
|
case m.Process != "" && m.Uid != 0:
|
|
return fmt.Sprintf("%s(%s, uid=%d)", m.SourceAddress(), m.Process, m.Uid)
|
|
case m.Uid != 0:
|
|
return fmt.Sprintf("%s(uid=%d)", m.SourceAddress(), m.Uid)
|
|
case m.Process != "":
|
|
return fmt.Sprintf("%s(%s)", m.SourceAddress(), m.Process)
|
|
default:
|
|
return fmt.Sprintf("%s", m.SourceAddress())
|
|
}
|
|
}
|
|
|
|
func (m *Metadata) SourceValid() bool {
|
|
return m.SrcPort != 0 && m.SrcIP.IsValid()
|
|
}
|
|
|
|
func (m *Metadata) AddrType() AddrType {
|
|
switch true {
|
|
case m.Host != "" || !m.DstIP.IsValid():
|
|
return AtypDomainName
|
|
case m.DstIP.Is4():
|
|
return AtypIPv4
|
|
default:
|
|
return AtypIPv6
|
|
}
|
|
}
|
|
|
|
func (m *Metadata) Resolved() bool {
|
|
return m.DstIP.IsValid()
|
|
}
|
|
|
|
func (m *Metadata) RuleHost() string {
|
|
if len(m.SniffHost) == 0 {
|
|
return m.Host
|
|
} else {
|
|
return m.SniffHost
|
|
}
|
|
}
|
|
|
|
// Pure is used to solve unexpected behavior
|
|
// when dialing proxy connection in DNSMapping mode.
|
|
func (m *Metadata) Pure() *Metadata {
|
|
if (m.DNSMode == DNSMapping || m.DNSMode == DNSHosts) && m.DstIP.IsValid() {
|
|
copyM := *m
|
|
copyM.Host = ""
|
|
return ©M
|
|
}
|
|
|
|
return m
|
|
}
|
|
|
|
func (m *Metadata) Clone() *Metadata {
|
|
copyM := *m
|
|
return ©M
|
|
}
|
|
|
|
func (m *Metadata) AddrPort() netip.AddrPort {
|
|
return netip.AddrPortFrom(m.DstIP.Unmap(), m.DstPort)
|
|
}
|
|
|
|
func (m *Metadata) UDPAddr() *net.UDPAddr {
|
|
if m.NetWork != UDP || !m.DstIP.IsValid() {
|
|
return nil
|
|
}
|
|
return net.UDPAddrFromAddrPort(m.AddrPort())
|
|
}
|
|
|
|
func (m *Metadata) String() string {
|
|
if m.Host != "" {
|
|
return m.Host
|
|
} else if m.DstIP.IsValid() {
|
|
return m.DstIP.String()
|
|
} else {
|
|
return "<nil>"
|
|
}
|
|
}
|
|
|
|
func (m *Metadata) Valid() bool {
|
|
return m.Host != "" || m.DstIP.IsValid()
|
|
}
|
|
|
|
func (m *Metadata) SetRemoteAddr(addr net.Addr) error {
|
|
if addr == nil {
|
|
return nil
|
|
}
|
|
if rawAddr, ok := addr.(interface{ RawAddr() net.Addr }); ok {
|
|
if rawAddr := rawAddr.RawAddr(); rawAddr != nil {
|
|
if err := m.SetRemoteAddr(rawAddr); err == nil {
|
|
return nil
|
|
}
|
|
}
|
|
}
|
|
if addr, ok := addr.(interface{ AddrPort() netip.AddrPort }); ok { // *net.TCPAddr, *net.UDPAddr, M.Socksaddr
|
|
if addrPort := addr.AddrPort(); addrPort.Port() != 0 {
|
|
m.DstPort = addrPort.Port()
|
|
if addrPort.IsValid() { // sing's M.Socksaddr maybe return an invalid AddrPort if it's a DomainName
|
|
m.DstIP = addrPort.Addr().Unmap()
|
|
return nil
|
|
} else {
|
|
if addr, ok := addr.(interface{ AddrString() string }); ok { // must be sing's M.Socksaddr
|
|
m.Host = addr.AddrString() // actually is M.Socksaddr.Fqdn
|
|
return nil
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return m.SetRemoteAddress(addr.String())
|
|
}
|
|
|
|
func (m *Metadata) SetRemoteAddress(rawAddress string) error {
|
|
host, port, err := net.SplitHostPort(rawAddress)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
var uint16Port uint16
|
|
if port, err := strconv.ParseUint(port, 10, 16); err == nil {
|
|
uint16Port = uint16(port)
|
|
}
|
|
|
|
if ip, err := netip.ParseAddr(host); err != nil {
|
|
m.Host = host
|
|
m.DstIP = netip.Addr{}
|
|
} else {
|
|
m.Host = ""
|
|
m.DstIP = ip.Unmap()
|
|
}
|
|
m.DstPort = uint16Port
|
|
|
|
return nil
|
|
}
|
|
|
|
func (m *Metadata) SwapSrcDst() {
|
|
m.SrcIP, m.DstIP = m.DstIP, m.SrcIP
|
|
m.SrcPort, m.DstPort = m.DstPort, m.SrcPort
|
|
m.SrcIPASN, m.DstIPASN = m.DstIPASN, m.SrcIPASN
|
|
m.SrcGeoIP, m.DstGeoIP = m.DstGeoIP, m.SrcGeoIP
|
|
}
|