Feature: add DST-PORT and SRC-PORT
This commit is contained in:
parent
2885f9a8de
commit
7062bb4bba
17 changed files with 137 additions and 59 deletions
|
@ -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,
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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")
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
||||
|
|
|
@ -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
47
rules/port.go
Normal 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,
|
||||
}
|
||||
}
|
|
@ -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
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue