Feature: add DST-PORT and SRC-PORT

This commit is contained in:
Dreamacro 2019-05-09 21:00:29 +08:00
parent cff4841f3e
commit 225c530d13
17 changed files with 137 additions and 59 deletions

View file

@ -23,7 +23,10 @@ func (h *HTTPAdapter) Metadata() *C.Metadata {
// NewHTTP is HTTPAdapter generator // NewHTTP is HTTPAdapter generator
func NewHTTP(request *http.Request, conn net.Conn) *HTTPAdapter { func NewHTTP(request *http.Request, conn net.Conn) *HTTPAdapter {
metadata := parseHTTPAddr(request) 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{ return &HTTPAdapter{
metadata: metadata, metadata: metadata,
R: request, R: request,

View file

@ -8,7 +8,10 @@ import (
// NewHTTPS is HTTPAdapter generator // NewHTTPS is HTTPAdapter generator
func NewHTTPS(request *http.Request, conn net.Conn) *SocketAdapter { func NewHTTPS(request *http.Request, conn net.Conn) *SocketAdapter {
metadata := parseHTTPAddr(request) 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{ return &SocketAdapter{
metadata: metadata, metadata: metadata,
Conn: conn, Conn: conn,

View file

@ -19,11 +19,14 @@ func (s *SocketAdapter) Metadata() *C.Metadata {
} }
// NewSocket is SocketAdapter generator // 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 := parseSocksAddr(target)
metadata.NetWork = netType metadata.NetWork = netType
metadata.Source = source metadata.Type = source
metadata.SourceIP = parseSourceIP(conn) if ip, port, err := parseAddr(conn.RemoteAddr().String()); err == nil {
metadata.SrcIP = ip
metadata.SrcPort = port
}
return &SocketAdapter{ return &SocketAdapter{
Conn: conn, Conn: conn,

View file

@ -17,15 +17,15 @@ func parseSocksAddr(target socks5.Addr) *C.Metadata {
switch target[0] { switch target[0] {
case socks5.AtypDomainName: case socks5.AtypDomainName:
metadata.Host = string(target[2 : 2+target[1]]) 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: case socks5.AtypIPv4:
ip := net.IP(target[1 : 1+net.IPv4len]) ip := net.IP(target[1 : 1+net.IPv4len])
metadata.IP = &ip metadata.DstIP = &ip
metadata.Port = strconv.Itoa((int(target[1+net.IPv4len]) << 8) | int(target[1+net.IPv4len+1])) metadata.DstPort = strconv.Itoa((int(target[1+net.IPv4len]) << 8) | int(target[1+net.IPv4len+1]))
case socks5.AtypIPv6: case socks5.AtypIPv6:
ip := net.IP(target[1 : 1+net.IPv6len]) ip := net.IP(target[1 : 1+net.IPv6len])
metadata.IP = &ip metadata.DstIP = &ip
metadata.Port = strconv.Itoa((int(target[1+net.IPv6len]) << 8) | int(target[1+net.IPv6len+1])) metadata.DstPort = strconv.Itoa((int(target[1+net.IPv6len]) << 8) | int(target[1+net.IPv6len+1]))
} }
return metadata return metadata
@ -40,11 +40,11 @@ func parseHTTPAddr(request *http.Request) *C.Metadata {
metadata := &C.Metadata{ metadata := &C.Metadata{
NetWork: C.TCP, NetWork: C.TCP,
Source: C.HTTP, Type: C.HTTP,
AddrType: C.AtypDomainName, AddrType: C.AtypDomainName,
Host: host, Host: host,
IP: nil, DstIP: nil,
Port: port, DstPort: port,
} }
ip := net.ParseIP(host) ip := net.ParseIP(host)
@ -55,15 +55,18 @@ func parseHTTPAddr(request *http.Request) *C.Metadata {
default: default:
metadata.AddrType = C.AtypIPv4 metadata.AddrType = C.AtypIPv4
} }
metadata.IP = &ip metadata.DstIP = &ip
} }
return metadata return metadata
} }
func parseSourceIP(conn net.Conn) *net.IP { func parseAddr(addr string) (*net.IP, string, error) {
if addr, ok := conn.RemoteAddr().(*net.TCPAddr); ok { host, port, err := net.SplitHostPort(addr)
return &addr.IP if err != nil {
return nil, "", err
} }
return nil
ip := net.ParseIP(host)
return &ip, port, nil
} }

View file

@ -11,9 +11,9 @@ type Direct struct {
} }
func (d *Direct) Dial(metadata *C.Metadata) (net.Conn, error) { func (d *Direct) Dial(metadata *C.Metadata) (net.Conn, error) {
address := net.JoinHostPort(metadata.Host, metadata.Port) address := net.JoinHostPort(metadata.Host, metadata.DstPort)
if metadata.IP != nil { if metadata.DstIP != nil {
address = net.JoinHostPort(metadata.IP.String(), metadata.Port) address = net.JoinHostPort(metadata.DstIP.String(), metadata.DstPort)
} }
c, err := net.DialTimeout("tcp", address, tcpTimeout) 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 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 { if err != nil {
return nil, nil, err return nil, nil, err
} }

View file

@ -58,7 +58,7 @@ func (h *Http) shakeHand(metadata *C.Metadata, rw io.ReadWriter) error {
var buf bytes.Buffer var buf bytes.Buffer
var err error 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("CONNECT " + addr + " HTTP/1.1\r\n")
buf.WriteString("Host: " + metadata.String() + "\r\n") buf.WriteString("Host: " + metadata.String() + "\r\n")
buf.WriteString("Proxy-Connection: Keep-Alive\r\n") buf.WriteString("Proxy-Connection: Keep-Alive\r\n")

View file

@ -34,11 +34,11 @@ func getKey(metadata *C.Metadata) string {
} }
} }
if metadata.IP == nil { if metadata.DstIP == nil {
return "" return ""
} }
return metadata.IP.String() return metadata.DstIP.String()
} }
func jumpHash(key uint64, buckets int32) int32 { func jumpHash(key uint64, buckets int32) int32 {

View file

@ -92,7 +92,7 @@ func (ss *ShadowSocks) DialUDP(metadata *C.Metadata) (net.PacketConn, net.Addr,
return nil, nil, err 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 { if err != nil {
return nil, nil, err return nil, nil, err
} }

View file

@ -44,8 +44,8 @@ func urlToMetadata(rawURL string) (addr C.Metadata, err error) {
addr = C.Metadata{ addr = C.Metadata{
AddrType: C.AtypDomainName, AddrType: C.AtypDomainName,
Host: u.Hostname(), Host: u.Hostname(),
IP: nil, DstIP: nil,
Port: port, DstPort: port,
} }
return return
} }
@ -67,7 +67,7 @@ func getClientSessionCache() tls.ClientSessionCache {
func serializesSocksAddr(metadata *C.Metadata) []byte { func serializesSocksAddr(metadata *C.Metadata) []byte {
var buf [][]byte var buf [][]byte
aType := uint8(metadata.AddrType) aType := uint8(metadata.AddrType)
p, _ := strconv.Atoi(metadata.Port) p, _ := strconv.Atoi(metadata.DstPort)
port := []byte{uint8(p >> 8), uint8(p & 0xff)} port := []byte{uint8(p >> 8), uint8(p & 0xff)}
switch metadata.AddrType { switch metadata.AddrType {
case socks5.AtypDomainName: case socks5.AtypDomainName:
@ -75,10 +75,10 @@ func serializesSocksAddr(metadata *C.Metadata) []byte {
host := []byte(metadata.Host) host := []byte(metadata.Host)
buf = [][]byte{{aType, len}, host, port} buf = [][]byte{{aType, len}, host, port}
case socks5.AtypIPv4: case socks5.AtypIPv4:
host := metadata.IP.To4() host := metadata.DstIP.To4()
buf = [][]byte{{aType}, host, port} buf = [][]byte{{aType}, host, port}
case socks5.AtypIPv6: case socks5.AtypIPv6:
host := metadata.IP.To16() host := metadata.DstIP.To16()
buf = [][]byte{{aType}, host, port} buf = [][]byte{{aType}, host, port}
} }
return bytes.Join(buf, nil) return bytes.Join(buf, nil)

View file

@ -88,11 +88,11 @@ func parseVmessAddr(metadata *C.Metadata) *vmess.DstAddr {
case C.AtypIPv4: case C.AtypIPv4:
addrType = byte(vmess.AtypIPv4) addrType = byte(vmess.AtypIPv4)
addr = make([]byte, net.IPv4len) addr = make([]byte, net.IPv4len)
copy(addr[:], metadata.IP.To4()) copy(addr[:], metadata.DstIP.To4())
case C.AtypIPv6: case C.AtypIPv6:
addrType = byte(vmess.AtypIPv6) addrType = byte(vmess.AtypIPv6)
addr = make([]byte, net.IPv6len) addr = make([]byte, net.IPv6len)
copy(addr[:], metadata.IP.To16()) copy(addr[:], metadata.DstIP.To16())
case C.AtypDomainName: case C.AtypDomainName:
addrType = byte(vmess.AtypDomainName) addrType = byte(vmess.AtypDomainName)
addr = make([]byte, len(metadata.Host)+1) addr = make([]byte, len(metadata.Host)+1)
@ -100,7 +100,7 @@ func parseVmessAddr(metadata *C.Metadata) *vmess.DstAddr {
copy(addr[1:], []byte(metadata.Host)) copy(addr[1:], []byte(metadata.Host))
} }
port, _ := strconv.Atoi(metadata.Port) port, _ := strconv.Atoi(metadata.DstPort)
return &vmess.DstAddr{ return &vmess.DstAddr{
UDP: metadata.NetWork == C.UDP, UDP: metadata.NetWork == C.UDP,
AddrType: addrType, AddrType: addrType,

View file

@ -372,12 +372,24 @@ func parseRules(cfg *rawConfig) ([]C.Rule, error) {
if rule := R.NewIPCIDR(payload, target, false); rule != nil { if rule := R.NewIPCIDR(payload, target, false); rule != nil {
parsed = rule parsed = rule
} }
// deprecated when bump to 1.0
case "SOURCE-IP-CIDR": case "SOURCE-IP-CIDR":
fallthrough
case "SRC-IP-CIDR":
if rule := R.NewIPCIDR(payload, target, true); rule != nil { if rule := R.NewIPCIDR(payload, target, true); rule != nil {
parsed = rule 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": case "MATCH":
fallthrough fallthrough
// deprecated when bump to 1.0
case "FINAL": case "FINAL":
parsed = R.NewMatch(target) parsed = R.NewMatch(target)
} }

View file

@ -13,7 +13,7 @@ const (
TCP NetWork = iota TCP NetWork = iota
UDP UDP
HTTP SourceType = iota HTTP Type = iota
SOCKS SOCKS
REDIR REDIR
) )
@ -27,26 +27,27 @@ func (n *NetWork) String() string {
return "udp" return "udp"
} }
type SourceType int type Type int
// Metadata is used to store connection address // Metadata is used to store connection address
type Metadata struct { type Metadata struct {
NetWork NetWork NetWork NetWork
Source SourceType Type Type
SourceIP *net.IP SrcIP *net.IP
DstIP *net.IP
SrcPort string
DstPort string
AddrType int AddrType int
Host string Host string
IP *net.IP
Port string
} }
func (m *Metadata) String() string { func (m *Metadata) String() string {
if m.Host == "" { if m.Host == "" {
return m.IP.String() return m.DstIP.String()
} }
return m.Host return m.Host
} }
func (m *Metadata) Valid() bool { func (m *Metadata) Valid() bool {
return m.Host != "" || m.IP != nil return m.Host != "" || m.DstIP != nil
} }

View file

@ -7,7 +7,9 @@ const (
DomainKeyword DomainKeyword
GEOIP GEOIP
IPCIDR IPCIDR
SourceIPCIDR SrcIPCIDR
SrcPort
DstPort
MATCH MATCH
) )
@ -25,8 +27,12 @@ func (rt RuleType) String() string {
return "GEOIP" return "GEOIP"
case IPCIDR: case IPCIDR:
return "IPCIDR" return "IPCIDR"
case SourceIPCIDR: case SrcIPCIDR:
return "SourceIPCIDR" return "SrcIPCIDR"
case SrcPort:
return "SrcPort"
case DstPort:
return "DstPort"
case MATCH: case MATCH:
return "MATCH" return "MATCH"
default: default:

View file

@ -24,10 +24,10 @@ func (g *GEOIP) RuleType() C.RuleType {
} }
func (g *GEOIP) IsMatch(metadata *C.Metadata) bool { func (g *GEOIP) IsMatch(metadata *C.Metadata) bool {
if metadata.IP == nil { if metadata.DstIP == nil {
return false return false
} }
record, _ := mmdb.Country(*metadata.IP) record, _ := mmdb.Country(*metadata.DstIP)
return record.Country.IsoCode == g.country return record.Country.IsoCode == g.country
} }

View file

@ -14,15 +14,15 @@ type IPCIDR struct {
func (i *IPCIDR) RuleType() C.RuleType { func (i *IPCIDR) RuleType() C.RuleType {
if i.isSourceIP { if i.isSourceIP {
return C.SourceIPCIDR return C.SrcIPCIDR
} }
return C.IPCIDR return C.IPCIDR
} }
func (i *IPCIDR) IsMatch(metadata *C.Metadata) bool { func (i *IPCIDR) IsMatch(metadata *C.Metadata) bool {
ip := metadata.IP ip := metadata.DstIP
if i.isSourceIP { if i.isSourceIP {
ip = metadata.SourceIP ip = metadata.SrcIP
} }
return ip != nil && i.ipnet.Contains(*ip) return ip != nil && i.ipnet.Contains(*ip)
} }

47
rules/port.go Normal file
View file

@ -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,
}
}

View file

@ -118,7 +118,7 @@ func (t *Tunnel) resolveIP(host string) (net.IP, error) {
} }
func (t *Tunnel) needLookupIP(metadata *C.Metadata) bool { 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) { func (t *Tunnel) handleConn(localConn C.ServerAdapter) {
@ -132,12 +132,12 @@ func (t *Tunnel) handleConn(localConn C.ServerAdapter) {
// preprocess enhanced-mode metadata // preprocess enhanced-mode metadata
if t.needLookupIP(metadata) { if t.needLookupIP(metadata) {
host, exist := t.resolver.IPToHost(*metadata.IP) host, exist := t.resolver.IPToHost(*metadata.DstIP)
if exist { if exist {
metadata.Host = host metadata.Host = host
metadata.AddrType = C.AtypDomainName metadata.AddrType = C.AtypDomainName
if t.resolver.IsFakeIP() { 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) pc, addr, err := proxy.DialUDP(metadata)
defer pc.Close() defer pc.Close()
if err != nil { 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) t.handleUDPOverTCP(localConn, pc, addr)
@ -170,7 +170,7 @@ func (t *Tunnel) handleConn(localConn C.ServerAdapter) {
remoConn, err := proxy.Dial(metadata) remoConn, err := proxy.Dial(metadata)
if err != nil { 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 return
} }
defer remoConn.Close() 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 { 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) { 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()) log.Debugln("[DNS] resolve %s error: %s", metadata.Host, err.Error())
} else { } else {
log.Debugln("[DNS] %s --> %s", metadata.Host, ip.String()) log.Debugln("[DNS] %s --> %s", metadata.Host, ip.String())
metadata.IP = &ip metadata.DstIP = &ip
} }
resolved = true resolved = true
} }
@ -217,11 +217,11 @@ func (t *Tunnel) match(metadata *C.Metadata) (C.Proxy, error) {
continue 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 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 return t.proxies["DIRECT"], nil
} }