From 225c530d13833690a00a0241aea296c676312024 Mon Sep 17 00:00:00 2001 From: Dreamacro <305009791@qq.com> Date: Thu, 9 May 2019 21:00:29 +0800 Subject: [PATCH] Feature: add DST-PORT and SRC-PORT --- adapters/inbound/http.go | 5 +++- adapters/inbound/https.go | 5 +++- adapters/inbound/socket.go | 9 ++++-- adapters/inbound/util.go | 29 +++++++++++--------- adapters/outbound/direct.go | 8 +++--- adapters/outbound/http.go | 2 +- adapters/outbound/loadbalance.go | 4 +-- adapters/outbound/shadowsocks.go | 2 +- adapters/outbound/util.go | 10 +++---- adapters/outbound/vmess.go | 6 ++-- config/config.go | 12 ++++++++ constant/metadata.go | 17 ++++++------ constant/rule.go | 12 ++++++-- rules/geoip.go | 4 +-- rules/ipcidr.go | 6 ++-- rules/port.go | 47 ++++++++++++++++++++++++++++++++ tunnel/tunnel.go | 18 ++++++------ 17 files changed, 137 insertions(+), 59 deletions(-) create mode 100644 rules/port.go diff --git a/adapters/inbound/http.go b/adapters/inbound/http.go index e97bcc3a..f60544f8 100644 --- a/adapters/inbound/http.go +++ b/adapters/inbound/http.go @@ -23,7 +23,10 @@ func (h *HTTPAdapter) Metadata() *C.Metadata { // NewHTTP is HTTPAdapter generator func NewHTTP(request *http.Request, conn net.Conn) *HTTPAdapter { metadata := parseHTTPAddr(request) - metadata.SourceIP = parseSourceIP(conn) + if ip, port, err := parseAddr(conn.RemoteAddr().String()); err == nil { + metadata.SrcIP = ip + metadata.SrcPort = port + } return &HTTPAdapter{ metadata: metadata, R: request, diff --git a/adapters/inbound/https.go b/adapters/inbound/https.go index 5207a5da..a880af74 100644 --- a/adapters/inbound/https.go +++ b/adapters/inbound/https.go @@ -8,7 +8,10 @@ import ( // NewHTTPS is HTTPAdapter generator func NewHTTPS(request *http.Request, conn net.Conn) *SocketAdapter { metadata := parseHTTPAddr(request) - metadata.SourceIP = parseSourceIP(conn) + if ip, port, err := parseAddr(conn.RemoteAddr().String()); err == nil { + metadata.SrcIP = ip + metadata.SrcPort = port + } return &SocketAdapter{ metadata: metadata, Conn: conn, diff --git a/adapters/inbound/socket.go b/adapters/inbound/socket.go index fae80e01..017f7424 100644 --- a/adapters/inbound/socket.go +++ b/adapters/inbound/socket.go @@ -19,11 +19,14 @@ func (s *SocketAdapter) Metadata() *C.Metadata { } // NewSocket is SocketAdapter generator -func NewSocket(target socks5.Addr, conn net.Conn, source C.SourceType, netType C.NetWork) *SocketAdapter { +func NewSocket(target socks5.Addr, conn net.Conn, source C.Type, netType C.NetWork) *SocketAdapter { metadata := parseSocksAddr(target) metadata.NetWork = netType - metadata.Source = source - metadata.SourceIP = parseSourceIP(conn) + metadata.Type = source + if ip, port, err := parseAddr(conn.RemoteAddr().String()); err == nil { + metadata.SrcIP = ip + metadata.SrcPort = port + } return &SocketAdapter{ Conn: conn, diff --git a/adapters/inbound/util.go b/adapters/inbound/util.go index d5bca74d..9d977cf8 100644 --- a/adapters/inbound/util.go +++ b/adapters/inbound/util.go @@ -17,15 +17,15 @@ func parseSocksAddr(target socks5.Addr) *C.Metadata { switch target[0] { case socks5.AtypDomainName: metadata.Host = string(target[2 : 2+target[1]]) - metadata.Port = strconv.Itoa((int(target[2+target[1]]) << 8) | int(target[2+target[1]+1])) + metadata.DstPort = strconv.Itoa((int(target[2+target[1]]) << 8) | int(target[2+target[1]+1])) case socks5.AtypIPv4: ip := net.IP(target[1 : 1+net.IPv4len]) - metadata.IP = &ip - metadata.Port = strconv.Itoa((int(target[1+net.IPv4len]) << 8) | int(target[1+net.IPv4len+1])) + metadata.DstIP = &ip + metadata.DstPort = strconv.Itoa((int(target[1+net.IPv4len]) << 8) | int(target[1+net.IPv4len+1])) case socks5.AtypIPv6: ip := net.IP(target[1 : 1+net.IPv6len]) - metadata.IP = &ip - metadata.Port = strconv.Itoa((int(target[1+net.IPv6len]) << 8) | int(target[1+net.IPv6len+1])) + metadata.DstIP = &ip + metadata.DstPort = strconv.Itoa((int(target[1+net.IPv6len]) << 8) | int(target[1+net.IPv6len+1])) } return metadata @@ -40,11 +40,11 @@ func parseHTTPAddr(request *http.Request) *C.Metadata { metadata := &C.Metadata{ NetWork: C.TCP, - Source: C.HTTP, + Type: C.HTTP, AddrType: C.AtypDomainName, Host: host, - IP: nil, - Port: port, + DstIP: nil, + DstPort: port, } ip := net.ParseIP(host) @@ -55,15 +55,18 @@ func parseHTTPAddr(request *http.Request) *C.Metadata { default: metadata.AddrType = C.AtypIPv4 } - metadata.IP = &ip + metadata.DstIP = &ip } return metadata } -func parseSourceIP(conn net.Conn) *net.IP { - if addr, ok := conn.RemoteAddr().(*net.TCPAddr); ok { - return &addr.IP +func parseAddr(addr string) (*net.IP, string, error) { + host, port, err := net.SplitHostPort(addr) + if err != nil { + return nil, "", err } - return nil + + ip := net.ParseIP(host) + return &ip, port, nil } diff --git a/adapters/outbound/direct.go b/adapters/outbound/direct.go index 6cf18c09..563f7074 100644 --- a/adapters/outbound/direct.go +++ b/adapters/outbound/direct.go @@ -11,9 +11,9 @@ type Direct struct { } func (d *Direct) Dial(metadata *C.Metadata) (net.Conn, error) { - address := net.JoinHostPort(metadata.Host, metadata.Port) - if metadata.IP != nil { - address = net.JoinHostPort(metadata.IP.String(), metadata.Port) + address := net.JoinHostPort(metadata.Host, metadata.DstPort) + if metadata.DstIP != nil { + address = net.JoinHostPort(metadata.DstIP.String(), metadata.DstPort) } c, err := net.DialTimeout("tcp", address, tcpTimeout) @@ -30,7 +30,7 @@ func (d *Direct) DialUDP(metadata *C.Metadata) (net.PacketConn, net.Addr, error) return nil, nil, err } - addr, err := net.ResolveUDPAddr("udp", net.JoinHostPort(metadata.String(), metadata.Port)) + addr, err := net.ResolveUDPAddr("udp", net.JoinHostPort(metadata.String(), metadata.DstPort)) if err != nil { return nil, nil, err } diff --git a/adapters/outbound/http.go b/adapters/outbound/http.go index 79d8b31e..58f335dc 100644 --- a/adapters/outbound/http.go +++ b/adapters/outbound/http.go @@ -58,7 +58,7 @@ func (h *Http) shakeHand(metadata *C.Metadata, rw io.ReadWriter) error { var buf bytes.Buffer var err error - addr := net.JoinHostPort(metadata.String(), metadata.Port) + addr := net.JoinHostPort(metadata.String(), metadata.DstPort) buf.WriteString("CONNECT " + addr + " HTTP/1.1\r\n") buf.WriteString("Host: " + metadata.String() + "\r\n") buf.WriteString("Proxy-Connection: Keep-Alive\r\n") diff --git a/adapters/outbound/loadbalance.go b/adapters/outbound/loadbalance.go index 761a184f..e8702713 100644 --- a/adapters/outbound/loadbalance.go +++ b/adapters/outbound/loadbalance.go @@ -34,11 +34,11 @@ func getKey(metadata *C.Metadata) string { } } - if metadata.IP == nil { + if metadata.DstIP == nil { return "" } - return metadata.IP.String() + return metadata.DstIP.String() } func jumpHash(key uint64, buckets int32) int32 { diff --git a/adapters/outbound/shadowsocks.go b/adapters/outbound/shadowsocks.go index 8c8e92bf..e9e157f9 100644 --- a/adapters/outbound/shadowsocks.go +++ b/adapters/outbound/shadowsocks.go @@ -92,7 +92,7 @@ func (ss *ShadowSocks) DialUDP(metadata *C.Metadata) (net.PacketConn, net.Addr, return nil, nil, err } - remoteAddr, err := net.ResolveUDPAddr("udp", net.JoinHostPort(metadata.String(), metadata.Port)) + remoteAddr, err := net.ResolveUDPAddr("udp", net.JoinHostPort(metadata.String(), metadata.DstPort)) if err != nil { return nil, nil, err } diff --git a/adapters/outbound/util.go b/adapters/outbound/util.go index 94d19d4b..d7c134b3 100644 --- a/adapters/outbound/util.go +++ b/adapters/outbound/util.go @@ -44,8 +44,8 @@ func urlToMetadata(rawURL string) (addr C.Metadata, err error) { addr = C.Metadata{ AddrType: C.AtypDomainName, Host: u.Hostname(), - IP: nil, - Port: port, + DstIP: nil, + DstPort: port, } return } @@ -67,7 +67,7 @@ func getClientSessionCache() tls.ClientSessionCache { func serializesSocksAddr(metadata *C.Metadata) []byte { var buf [][]byte aType := uint8(metadata.AddrType) - p, _ := strconv.Atoi(metadata.Port) + p, _ := strconv.Atoi(metadata.DstPort) port := []byte{uint8(p >> 8), uint8(p & 0xff)} switch metadata.AddrType { case socks5.AtypDomainName: @@ -75,10 +75,10 @@ func serializesSocksAddr(metadata *C.Metadata) []byte { host := []byte(metadata.Host) buf = [][]byte{{aType, len}, host, port} case socks5.AtypIPv4: - host := metadata.IP.To4() + host := metadata.DstIP.To4() buf = [][]byte{{aType}, host, port} case socks5.AtypIPv6: - host := metadata.IP.To16() + host := metadata.DstIP.To16() buf = [][]byte{{aType}, host, port} } return bytes.Join(buf, nil) diff --git a/adapters/outbound/vmess.go b/adapters/outbound/vmess.go index 823469f7..e1113bd1 100644 --- a/adapters/outbound/vmess.go +++ b/adapters/outbound/vmess.go @@ -88,11 +88,11 @@ func parseVmessAddr(metadata *C.Metadata) *vmess.DstAddr { case C.AtypIPv4: addrType = byte(vmess.AtypIPv4) addr = make([]byte, net.IPv4len) - copy(addr[:], metadata.IP.To4()) + copy(addr[:], metadata.DstIP.To4()) case C.AtypIPv6: addrType = byte(vmess.AtypIPv6) addr = make([]byte, net.IPv6len) - copy(addr[:], metadata.IP.To16()) + copy(addr[:], metadata.DstIP.To16()) case C.AtypDomainName: addrType = byte(vmess.AtypDomainName) addr = make([]byte, len(metadata.Host)+1) @@ -100,7 +100,7 @@ func parseVmessAddr(metadata *C.Metadata) *vmess.DstAddr { copy(addr[1:], []byte(metadata.Host)) } - port, _ := strconv.Atoi(metadata.Port) + port, _ := strconv.Atoi(metadata.DstPort) return &vmess.DstAddr{ UDP: metadata.NetWork == C.UDP, AddrType: addrType, diff --git a/config/config.go b/config/config.go index 570ec9fc..ddc0548a 100644 --- a/config/config.go +++ b/config/config.go @@ -372,12 +372,24 @@ func parseRules(cfg *rawConfig) ([]C.Rule, error) { if rule := R.NewIPCIDR(payload, target, false); rule != nil { parsed = rule } + // deprecated when bump to 1.0 case "SOURCE-IP-CIDR": + fallthrough + case "SRC-IP-CIDR": if rule := R.NewIPCIDR(payload, target, true); rule != nil { parsed = rule } + case "SRC-PORT": + if rule := R.NewPort(payload, target, true); rule != nil { + parsed = rule + } + case "DST-PORT": + if rule := R.NewPort(payload, target, false); rule != nil { + parsed = rule + } case "MATCH": fallthrough + // deprecated when bump to 1.0 case "FINAL": parsed = R.NewMatch(target) } diff --git a/constant/metadata.go b/constant/metadata.go index 4bb02885..f95bea58 100644 --- a/constant/metadata.go +++ b/constant/metadata.go @@ -13,7 +13,7 @@ const ( TCP NetWork = iota UDP - HTTP SourceType = iota + HTTP Type = iota SOCKS REDIR ) @@ -27,26 +27,27 @@ func (n *NetWork) String() string { return "udp" } -type SourceType int +type Type int // Metadata is used to store connection address type Metadata struct { NetWork NetWork - Source SourceType - SourceIP *net.IP + Type Type + SrcIP *net.IP + DstIP *net.IP + SrcPort string + DstPort string AddrType int Host string - IP *net.IP - Port string } func (m *Metadata) String() string { if m.Host == "" { - return m.IP.String() + return m.DstIP.String() } return m.Host } func (m *Metadata) Valid() bool { - return m.Host != "" || m.IP != nil + return m.Host != "" || m.DstIP != nil } diff --git a/constant/rule.go b/constant/rule.go index e142efc0..fa8a12c7 100644 --- a/constant/rule.go +++ b/constant/rule.go @@ -7,7 +7,9 @@ const ( DomainKeyword GEOIP IPCIDR - SourceIPCIDR + SrcIPCIDR + SrcPort + DstPort MATCH ) @@ -25,8 +27,12 @@ func (rt RuleType) String() string { return "GEOIP" case IPCIDR: return "IPCIDR" - case SourceIPCIDR: - return "SourceIPCIDR" + case SrcIPCIDR: + return "SrcIPCIDR" + case SrcPort: + return "SrcPort" + case DstPort: + return "DstPort" case MATCH: return "MATCH" default: diff --git a/rules/geoip.go b/rules/geoip.go index 5fb9a1f7..07cc2eff 100644 --- a/rules/geoip.go +++ b/rules/geoip.go @@ -24,10 +24,10 @@ func (g *GEOIP) RuleType() C.RuleType { } func (g *GEOIP) IsMatch(metadata *C.Metadata) bool { - if metadata.IP == nil { + if metadata.DstIP == nil { return false } - record, _ := mmdb.Country(*metadata.IP) + record, _ := mmdb.Country(*metadata.DstIP) return record.Country.IsoCode == g.country } diff --git a/rules/ipcidr.go b/rules/ipcidr.go index 87214554..e8160300 100644 --- a/rules/ipcidr.go +++ b/rules/ipcidr.go @@ -14,15 +14,15 @@ type IPCIDR struct { func (i *IPCIDR) RuleType() C.RuleType { if i.isSourceIP { - return C.SourceIPCIDR + return C.SrcIPCIDR } return C.IPCIDR } func (i *IPCIDR) IsMatch(metadata *C.Metadata) bool { - ip := metadata.IP + ip := metadata.DstIP if i.isSourceIP { - ip = metadata.SourceIP + ip = metadata.SrcIP } return ip != nil && i.ipnet.Contains(*ip) } diff --git a/rules/port.go b/rules/port.go new file mode 100644 index 00000000..ba9bad57 --- /dev/null +++ b/rules/port.go @@ -0,0 +1,47 @@ +package rules + +import ( + "strconv" + + C "github.com/Dreamacro/clash/constant" +) + +type Port struct { + adapter string + port string + isSource bool +} + +func (p *Port) RuleType() C.RuleType { + if p.isSource { + return C.SrcPort + } + return C.DstPort +} + +func (p *Port) IsMatch(metadata *C.Metadata) bool { + if p.isSource { + return metadata.SrcPort == p.port + } + return metadata.DstPort == p.port +} + +func (p *Port) Adapter() string { + return p.adapter +} + +func (p *Port) Payload() string { + return p.port +} + +func NewPort(port string, adapter string, isSource bool) *Port { + _, err := strconv.Atoi(port) + if err != nil { + return nil + } + return &Port{ + adapter: adapter, + port: port, + isSource: isSource, + } +} diff --git a/tunnel/tunnel.go b/tunnel/tunnel.go index c5c0a3a2..1a95eefa 100644 --- a/tunnel/tunnel.go +++ b/tunnel/tunnel.go @@ -118,7 +118,7 @@ func (t *Tunnel) resolveIP(host string) (net.IP, error) { } func (t *Tunnel) needLookupIP(metadata *C.Metadata) bool { - return t.hasResolver() && (t.resolver.IsMapping() || t.resolver.IsFakeIP()) && metadata.Host == "" && metadata.IP != nil + return t.hasResolver() && (t.resolver.IsMapping() || t.resolver.IsFakeIP()) && metadata.Host == "" && metadata.DstIP != nil } func (t *Tunnel) handleConn(localConn C.ServerAdapter) { @@ -132,12 +132,12 @@ func (t *Tunnel) handleConn(localConn C.ServerAdapter) { // preprocess enhanced-mode metadata if t.needLookupIP(metadata) { - host, exist := t.resolver.IPToHost(*metadata.IP) + host, exist := t.resolver.IPToHost(*metadata.DstIP) if exist { metadata.Host = host metadata.AddrType = C.AtypDomainName if t.resolver.IsFakeIP() { - metadata.IP = nil + metadata.DstIP = nil } } } @@ -161,7 +161,7 @@ func (t *Tunnel) handleConn(localConn C.ServerAdapter) { pc, addr, err := proxy.DialUDP(metadata) defer pc.Close() if err != nil { - log.Warnln("Proxy[%s] connect [%s --> %s] error: %s", proxy.Name(), metadata.SourceIP.String(), metadata.String(), err.Error()) + log.Warnln("Proxy[%s] connect [%s --> %s] error: %s", proxy.Name(), metadata.SrcIP.String(), metadata.String(), err.Error()) } t.handleUDPOverTCP(localConn, pc, addr) @@ -170,7 +170,7 @@ func (t *Tunnel) handleConn(localConn C.ServerAdapter) { remoConn, err := proxy.Dial(metadata) if err != nil { - log.Warnln("Proxy[%s] connect [%s --> %s] error: %s", proxy.Name(), metadata.SourceIP.String(), metadata.String(), err.Error()) + log.Warnln("Proxy[%s] connect [%s --> %s] error: %s", proxy.Name(), metadata.SrcIP.String(), metadata.String(), err.Error()) return } defer remoConn.Close() @@ -184,7 +184,7 @@ func (t *Tunnel) handleConn(localConn C.ServerAdapter) { } func (t *Tunnel) shouldResolveIP(rule C.Rule, metadata *C.Metadata) bool { - return (rule.RuleType() == C.GEOIP || rule.RuleType() == C.IPCIDR) && metadata.Host != "" && metadata.IP == nil + return (rule.RuleType() == C.GEOIP || rule.RuleType() == C.IPCIDR) && metadata.Host != "" && metadata.DstIP == nil } func (t *Tunnel) match(metadata *C.Metadata) (C.Proxy, error) { @@ -202,7 +202,7 @@ func (t *Tunnel) match(metadata *C.Metadata) (C.Proxy, error) { log.Debugln("[DNS] resolve %s error: %s", metadata.Host, err.Error()) } else { log.Debugln("[DNS] %s --> %s", metadata.Host, ip.String()) - metadata.IP = &ip + metadata.DstIP = &ip } resolved = true } @@ -217,11 +217,11 @@ func (t *Tunnel) match(metadata *C.Metadata) (C.Proxy, error) { continue } - log.Infoln("%s --> %v match %s using %s", metadata.SourceIP.String(), metadata.String(), rule.RuleType().String(), rule.Adapter()) + log.Infoln("%s --> %v match %s using %s", metadata.SrcIP.String(), metadata.String(), rule.RuleType().String(), rule.Adapter()) return adapter, nil } } - log.Infoln("%s --> %v doesn't match any rule using DIRECT", metadata.SourceIP.String(), metadata.String()) + log.Infoln("%s --> %v doesn't match any rule using DIRECT", metadata.SrcIP.String(), metadata.String()) return t.proxies["DIRECT"], nil }