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

View file

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

View file

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

View file

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

View file

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

View file

@ -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")

View file

@ -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 {

View file

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

View file

@ -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)

View file

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

View file

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

View file

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

View file

@ -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:

View file

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

View file

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

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 {
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
}