Skip to content

Commit

Permalink
Feature: add inbounds for flexible binding inbound (#2818)
Browse files Browse the repository at this point in the history
  • Loading branch information
fuyuntt authored Aug 3, 2023
1 parent 10f4d53 commit 9e78137
Show file tree
Hide file tree
Showing 25 changed files with 544 additions and 353 deletions.
2 changes: 2 additions & 0 deletions adapter/inbound/http.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package inbound
import (
"net"
"net/netip"
"strconv"

C "github.com/Dreamacro/clash/constant"
"github.com/Dreamacro/clash/context"
Expand All @@ -21,6 +22,7 @@ func NewHTTP(target socks5.Addr, source net.Addr, originTarget net.Addr, conn ne
if originTarget != nil {
if addrPort, err := netip.ParseAddrPort(originTarget.String()); err == nil {
metadata.OriginDst = addrPort
metadata.InboundPort = strconv.Itoa(int(addrPort.Port()))
}
}
return context.NewConnContext(conn, metadata)
Expand Down
2 changes: 2 additions & 0 deletions adapter/inbound/https.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"net"
"net/http"
"net/netip"
"strconv"

C "github.com/Dreamacro/clash/constant"
"github.com/Dreamacro/clash/context"
Expand All @@ -19,6 +20,7 @@ func NewHTTPS(request *http.Request, conn net.Conn) *context.ConnContext {
}
if addrPort, err := netip.ParseAddrPort(conn.LocalAddr().String()); err == nil {
metadata.OriginDst = addrPort
metadata.InboundPort = strconv.Itoa(int(addrPort.Port()))
}
return context.NewConnContext(conn, metadata)
}
2 changes: 2 additions & 0 deletions adapter/inbound/socket.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package inbound
import (
"net"
"net/netip"
"strconv"

C "github.com/Dreamacro/clash/constant"
"github.com/Dreamacro/clash/context"
Expand All @@ -20,6 +21,7 @@ func NewSocket(target socks5.Addr, conn net.Conn, source C.Type) *context.ConnCo
}
if addrPort, err := netip.ParseAddrPort(conn.LocalAddr().String()); err == nil {
metadata.OriginDst = addrPort
metadata.InboundPort = strconv.Itoa(int(addrPort.Port()))
}
return context.NewConnContext(conn, metadata)
}
4 changes: 2 additions & 2 deletions component/process/process_windows.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,9 @@ import (
"net/netip"
"unsafe"

"golang.org/x/sys/windows"

"github.com/Dreamacro/clash/common/pool"

"golang.org/x/sys/windows"
)

var (
Expand Down
41 changes: 22 additions & 19 deletions config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,25 +28,14 @@ import (

// General config
type General struct {
Inbound
LagecyInbound
Controller
Mode T.TunnelMode `json:"mode"`
LogLevel log.LogLevel `json:"log-level"`
IPv6 bool `json:"ipv6"`
Interface string `json:"-"`
RoutingMark int `json:"-"`
}

// Inbound
type Inbound struct {
Port int `json:"port"`
SocksPort int `json:"socks-port"`
RedirPort int `json:"redir-port"`
TProxyPort int `json:"tproxy-port"`
MixedPort int `json:"mixed-port"`
Authentication []string `json:"authentication"`
AllowLan bool `json:"allow-lan"`
BindAddress string `json:"bind-address"`
Authentication []string `json:"authentication"`
Mode T.TunnelMode `json:"mode"`
LogLevel log.LogLevel `json:"log-level"`
IPv6 bool `json:"ipv6"`
Interface string `json:"-"`
RoutingMark int `json:"-"`
}

// Controller
Expand All @@ -56,6 +45,16 @@ type Controller struct {
Secret string `json:"-"`
}

type LagecyInbound struct {
Port int `json:"port"`
SocksPort int `json:"socks-port"`
RedirPort int `json:"redir-port"`
TProxyPort int `json:"tproxy-port"`
MixedPort int `json:"mixed-port"`
AllowLan bool `json:"allow-lan"`
BindAddress string `json:"bind-address"`
}

// DNS config
type DNS struct {
Enable bool `yaml:"enable"`
Expand Down Expand Up @@ -98,6 +97,7 @@ type Config struct {
Experimental *Experimental
Hosts *trie.DomainTrie
Profile *Profile
Inbounds []C.Inbound
Rules []C.Rule
Users []auth.AuthUser
Proxies map[string]C.Proxy
Expand Down Expand Up @@ -207,6 +207,7 @@ type RawConfig struct {

ProxyProvider map[string]map[string]any `yaml:"proxy-providers"`
Hosts map[string]string `yaml:"hosts"`
Inbounds []C.Inbound `yaml:"inbounds"`
DNS RawDNS `yaml:"dns"`
Experimental Experimental `yaml:"experimental"`
Profile Profile `yaml:"profile"`
Expand Down Expand Up @@ -275,6 +276,8 @@ func ParseRawConfig(rawCfg *RawConfig) (*Config, error) {
}
config.General = general

config.Inbounds = rawCfg.Inbounds

proxies, providers, err := parseProxies(rawCfg)
if err != nil {
return nil, err
Expand Down Expand Up @@ -326,7 +329,7 @@ func parseGeneral(cfg *RawConfig) (*General, error) {
}

return &General{
Inbound: Inbound{
LagecyInbound: LagecyInbound{
Port: cfg.Port,
SocksPort: cfg.SocksPort,
RedirPort: cfg.RedirPort,
Expand Down
85 changes: 85 additions & 0 deletions constant/listener.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,92 @@
package constant

import (
"fmt"
"net"
"net/url"
"strconv"
)

type Listener interface {
RawAddress() string
Address() string
Close() error
}

type InboundType string

const (
InboundTypeSocks InboundType = "socks"
InboundTypeRedir InboundType = "redir"
InboundTypeTproxy InboundType = "tproxy"
InboundTypeHTTP InboundType = "http"
InboundTypeMixed InboundType = "mixed"
)

var supportInboundTypes = map[InboundType]bool{
InboundTypeSocks: true,
InboundTypeRedir: true,
InboundTypeTproxy: true,
InboundTypeHTTP: true,
InboundTypeMixed: true,
}

type inbound struct {
Type InboundType `json:"type" yaml:"type"`
BindAddress string `json:"bind-address" yaml:"bind-address"`
IsFromPortCfg bool `json:"-" yaml:"-"`
}

// Inbound
type Inbound inbound

// UnmarshalYAML implements yaml.Unmarshaler
func (i *Inbound) UnmarshalYAML(unmarshal func(any) error) error {
var tp string
if err := unmarshal(&tp); err != nil {
var inner inbound
if err := unmarshal(&inner); err != nil {
return err
}

*i = Inbound(inner)
return nil
}

inner, err := parseInbound(tp)
if err != nil {
return err
}
*i = Inbound(*inner)
if !supportInboundTypes[i.Type] {
return fmt.Errorf("not support inbound type: %s", i.Type)
}
_, portStr, err := net.SplitHostPort(i.BindAddress)
if err != nil {
return fmt.Errorf("bind address parse error. addr:%s, err:%v", i.BindAddress, err)
}
port, err := strconv.Atoi(portStr)
if err != nil {
return fmt.Errorf("port not a number. addr:%s", i.BindAddress)
}
if port == 0 {
return fmt.Errorf("invalid bind port. addr:%s", i.BindAddress)
}
return nil
}

func parseInbound(alias string) (*inbound, error) {
u, err := url.Parse(alias)
if err != nil {
return nil, err
}
listenerType := InboundType(u.Scheme)
return &inbound{
Type: listenerType,
BindAddress: u.Host,
}, nil
}

func (i *Inbound) ToAlias() string {
return string(i.Type) + "://" + i.BindAddress
}
1 change: 1 addition & 0 deletions constant/metadata.go
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@ type Metadata struct {
DstIP net.IP `json:"destinationIP"`
SrcPort string `json:"sourcePort"`
DstPort string `json:"destinationPort"`
InboundPort string `json:"inboundPort"`
Host string `json:"host"`
DNSMode DNSMode `json:"dnsMode"`
ProcessPath string `json:"processPath"`
Expand Down
3 changes: 3 additions & 0 deletions constant/rule.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ const (
SrcIPCIDR
SrcPort
DstPort
InboundPort
Process
ProcessPath
IPSet
Expand All @@ -36,6 +37,8 @@ func (rt RuleType) String() string {
return "SrcPort"
case DstPort:
return "DstPort"
case InboundPort:
return "InboundPort"
case Process:
return "Process"
case ProcessPath:
Expand Down
51 changes: 31 additions & 20 deletions hub/executor/executor.go
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@ func ApplyConfig(cfg *config.Config, force bool) {
updateHosts(cfg.Hosts)
updateProfile(cfg)
updateGeneral(cfg.General, force)
updateInbounds(cfg.Inbounds, force)
updateDNS(cfg.DNS)
updateExperimental(cfg)
updateTunnels(cfg.Tunnels)
Expand All @@ -86,19 +87,19 @@ func GetGeneral() *config.General {
}

general := &config.General{
Inbound: config.Inbound{
Port: ports.Port,
SocksPort: ports.SocksPort,
RedirPort: ports.RedirPort,
TProxyPort: ports.TProxyPort,
MixedPort: ports.MixedPort,
Authentication: authenticator,
AllowLan: listener.AllowLan(),
BindAddress: listener.BindAddress(),
LagecyInbound: config.LagecyInbound{
Port: ports.Port,
SocksPort: ports.SocksPort,
RedirPort: ports.RedirPort,
TProxyPort: ports.TProxyPort,
MixedPort: ports.MixedPort,
AllowLan: listener.AllowLan(),
BindAddress: listener.BindAddress(),
},
Mode: tunnel.Mode(),
LogLevel: log.Level(),
IPv6: !resolver.DisableIPv6,
Authentication: authenticator,
Mode: tunnel.Mode(),
LogLevel: log.Level(),
IPv6: !resolver.DisableIPv6,
}

return general
Expand Down Expand Up @@ -164,6 +165,16 @@ func updateTunnels(tunnels []config.Tunnel) {
listener.PatchTunnel(tunnels, tunnel.TCPIn(), tunnel.UDPIn())
}

func updateInbounds(inbounds []C.Inbound, force bool) {
if !force {
return
}
tcpIn := tunnel.TCPIn()
udpIn := tunnel.UDPIn()

listener.ReCreateListeners(inbounds, tcpIn, udpIn)
}

func updateGeneral(general *config.General, force bool) {
log.SetLevel(general.LogLevel)
tunnel.SetMode(general.Mode)
Expand All @@ -184,14 +195,14 @@ func updateGeneral(general *config.General, force bool) {
bindAddress := general.BindAddress
listener.SetBindAddress(bindAddress)

tcpIn := tunnel.TCPIn()
udpIn := tunnel.UDPIn()

listener.ReCreateHTTP(general.Port, tcpIn)
listener.ReCreateSocks(general.SocksPort, tcpIn, udpIn)
listener.ReCreateRedir(general.RedirPort, tcpIn, udpIn)
listener.ReCreateTProxy(general.TProxyPort, tcpIn, udpIn)
listener.ReCreateMixed(general.MixedPort, tcpIn, udpIn)
ports := listener.Ports{
Port: general.Port,
SocksPort: general.SocksPort,
RedirPort: general.RedirPort,
TProxyPort: general.TProxyPort,
MixedPort: general.MixedPort,
}
listener.ReCreatePortsListeners(ports, tunnel.TCPIn(), tunnel.UDPIn())
}

func updateUsers(users []auth.AuthUser) {
Expand Down
Loading

0 comments on commit 9e78137

Please sign in to comment.