Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions adapter/inbound/addition.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package inbound

import (
"net"
"net/netip"

C "github.com/metacubex/mihomo/constant"
)
Expand Down Expand Up @@ -70,4 +71,15 @@ func WithDSCP(dscp uint8) Addition {
}
}

func WithSrcAddrPort(ap netip.AddrPort) Addition {
return func(metadata *C.Metadata) {
if ap.IsValid() {
metadata.SrcIP = ap.Addr()
if p := ap.Port(); p != 0 {
metadata.SrcPort = p
}
}
}
}

func Placeholder(metadata *C.Metadata) {}
27 changes: 27 additions & 0 deletions common/httputils/addr.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,9 @@ package httputils
import (
"context"
"net"
"net/netip"
"strconv"
"strings"

C "github.com/metacubex/mihomo/constant"

Expand Down Expand Up @@ -43,3 +46,27 @@ func NewAddrContext(addr *NetAddr, ctx context.Context) context.Context {
},
})
}

func ClientAddrPortFromHeader(r *http.Request, header string) netip.AddrPort {
if header != "" {
if v := r.Header.Get(header); v != "" {
if i := strings.Index(v, ","); i >= 0 {
v = v[:i]
}

var port uint16
v = strings.TrimSpace(v)
if h, p, err := net.SplitHostPort(v); err == nil {
v = h
if p, err := strconv.ParseUint(p, 10, 16); err == nil {
port = uint16(p)
}
}

if addr, err := netip.ParseAddr(v); err == nil {
return netip.AddrPortFrom(addr.Unmap(), port)
}
}
}
return netip.AddrPort{}
}
2 changes: 2 additions & 0 deletions docs/config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -2460,6 +2460,7 @@ listeners:
# # rate-limit: 0 # fallback 双向转发限速,单位 bit/s;0 表示不限速
### 注意,对于vless listener, 如果 "allow-insecure" 不为 true, 至少需要填写 “certificate和private-key” 或 “shadow-tls” 或 “res-tls” 或 “jls-config” 或 “reality-config” 或 “decryption” 的其中一项 ###
# allow-insecure: false # 是否允许不开启tls加密(注意:仅用于有 nginx, caddy 前置的情况)
# trusted-proxy-header: "" # header to read real client IP from (e.g. X-Forwarded-For)

- name: anytls-in-1
type: anytls
Expand Down Expand Up @@ -2629,6 +2630,7 @@ listeners:
# password: "example"
### 注意,对于trojan listener, 如果 "allow-insecure" 不为 true, 至少需要填写 “certificate和private-key” 或 “shadow-tls” 或 “res-tls” 或 “jls-config” 或 “reality-config” 或 “ss-option” 的其中一项 ###
# allow-insecure: false # 是否允许不开启tls加密(注意:仅用于有 nginx, caddy 前置的情况)
# trusted-proxy-header: "" # header to read real client IP from (e.g. X-Forwarded-For)

- name: hysteria2-in-1
type: hysteria2
Expand Down
35 changes: 18 additions & 17 deletions listener/config/trojan.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,23 +13,24 @@ type TrojanUser struct {
}

type TrojanServer struct {
Enable bool
Listen string
Users []TrojanUser
WsPath string
GrpcServiceName string
Certificate string
PrivateKey string
ClientAuthType string
ClientAuthCert string
EchKey string
AllowInsecure bool
ShadowTLS ShadowTLS `yaml:"shadow-tls" json:"shadow-tls,omitempty"`
ResTLS ResTLS `yaml:"res-tls" json:"res-tls,omitempty"`
JLSConfig JLSConfig `yaml:"jls-config" json:"jls-config,omitempty"`
RealityConfig reality.Config `yaml:"reality-config" json:"reality-config,omitempty"`
MuxOption sing.MuxOption
TrojanSSOption TrojanSSOption
Enable bool
Listen string
Users []TrojanUser
WsPath string
GrpcServiceName string
Certificate string
PrivateKey string
ClientAuthType string
ClientAuthCert string
EchKey string
AllowInsecure bool
TrustedProxyHeader string
ShadowTLS ShadowTLS `yaml:"shadow-tls" json:"shadow-tls,omitempty"`
ResTLS ResTLS `yaml:"res-tls" json:"res-tls,omitempty"`
JLSConfig JLSConfig `yaml:"jls-config" json:"jls-config,omitempty"`
RealityConfig reality.Config `yaml:"reality-config" json:"reality-config,omitempty"`
MuxOption sing.MuxOption
TrojanSSOption TrojanSSOption
}

// TrojanSSOption from https://github.com/p4gefau1t/trojan-go/blob/v0.10.6/tunnel/shadowsocks/config.go#L5
Expand Down
37 changes: 19 additions & 18 deletions listener/config/vless.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,24 +14,25 @@ type VlessUser struct {
}

type VlessServer struct {
Enable bool
Listen string
Users []VlessUser
Decryption string
WsPath string
XHTTPConfig XHTTPConfig
GrpcServiceName string
Certificate string
PrivateKey string
ClientAuthType string
ClientAuthCert string
EchKey string
AllowInsecure bool
ShadowTLS ShadowTLS `yaml:"shadow-tls" json:"shadow-tls,omitempty"`
ResTLS ResTLS `yaml:"res-tls" json:"res-tls,omitempty"`
JLSConfig JLSConfig `yaml:"jls-config" json:"jls-config,omitempty"`
RealityConfig reality.Config `yaml:"reality-config" json:"reality-config,omitempty"`
MuxOption sing.MuxOption `yaml:"mux-option" json:"mux-option,omitempty"`
Enable bool
Listen string
Users []VlessUser
Decryption string
WsPath string
XHTTPConfig XHTTPConfig
GrpcServiceName string
Certificate string
PrivateKey string
ClientAuthType string
ClientAuthCert string
EchKey string
AllowInsecure bool
TrustedProxyHeader string
ShadowTLS ShadowTLS `yaml:"shadow-tls" json:"shadow-tls,omitempty"`
ResTLS ResTLS `yaml:"res-tls" json:"res-tls,omitempty"`
JLSConfig JLSConfig `yaml:"jls-config" json:"jls-config,omitempty"`
RealityConfig reality.Config `yaml:"reality-config" json:"reality-config,omitempty"`
MuxOption sing.MuxOption `yaml:"mux-option" json:"mux-option,omitempty"`
}

type XHTTPConfig struct {
Expand Down
17 changes: 4 additions & 13 deletions listener/hysteria2_realm/validate.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import (
"strings"

"github.com/metacubex/http"
"github.com/metacubex/mihomo/common/httputils"
)

const (
Expand Down Expand Up @@ -42,20 +43,10 @@ func writeJSON(w http.ResponseWriter, status int, v any) {
}

func clientIP(r *http.Request, header string) string {
if header != "" {
if v := r.Header.Get(header); v != "" {
if i := strings.Index(v, ","); i >= 0 {
v = v[:i]
}
v = strings.TrimSpace(v)
if h, _, err := net.SplitHostPort(v); err == nil {
v = h
}
if addr, err := netip.ParseAddr(v); err == nil {
return addr.Unmap().String()
}
}
if ap := httputils.ClientAddrPortFromHeader(r, header); ap.IsValid() {
return ap.Addr().String()
}

host := r.RemoteAddr
if h, _, err := net.SplitHostPort(host); err == nil {
host = h
Expand Down
64 changes: 33 additions & 31 deletions listener/inbound/trojan.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,21 +11,22 @@ import (

type TrojanOption struct {
BaseOption
Users []TrojanUser `inbound:"users"`
WsPath string `inbound:"ws-path,omitempty"`
GrpcServiceName string `inbound:"grpc-service-name,omitempty"`
Certificate string `inbound:"certificate,omitempty"`
PrivateKey string `inbound:"private-key,omitempty"`
ClientAuthType string `inbound:"client-auth-type,omitempty"`
ClientAuthCert string `inbound:"client-auth-cert,omitempty"`
EchKey string `inbound:"ech-key,omitempty"`
AllowInsecure bool `inbound:"allow-insecure,omitempty"`
ShadowTLS ShadowTLS `inbound:"shadow-tls,omitempty"`
ResTLS ResTLS `inbound:"res-tls,omitempty"`
JLSConfig JLSConfig `inbound:"jls-config,omitempty"`
RealityConfig RealityConfig `inbound:"reality-config,omitempty"`
MuxOption MuxOption `inbound:"mux-option,omitempty"`
SSOption TrojanSSOption `inbound:"ss-option,omitempty"`
Users []TrojanUser `inbound:"users"`
WsPath string `inbound:"ws-path,omitempty"`
GrpcServiceName string `inbound:"grpc-service-name,omitempty"`
Certificate string `inbound:"certificate,omitempty"`
PrivateKey string `inbound:"private-key,omitempty"`
ClientAuthType string `inbound:"client-auth-type,omitempty"`
ClientAuthCert string `inbound:"client-auth-cert,omitempty"`
EchKey string `inbound:"ech-key,omitempty"`
AllowInsecure bool `inbound:"allow-insecure,omitempty"`
TrustedProxyHeader string `inbound:"trusted-proxy-header,omitempty"`
ShadowTLS ShadowTLS `inbound:"shadow-tls,omitempty"`
ResTLS ResTLS `inbound:"res-tls,omitempty"`
JLSConfig JLSConfig `inbound:"jls-config,omitempty"`
RealityConfig RealityConfig `inbound:"reality-config,omitempty"`
MuxOption MuxOption `inbound:"mux-option,omitempty"`
SSOption TrojanSSOption `inbound:"ss-option,omitempty"`
}

type TrojanUser struct {
Expand Down Expand Up @@ -67,22 +68,23 @@ func NewTrojan(options *TrojanOption) (*Trojan, error) {
Base: base,
config: options,
vs: LC.TrojanServer{
Enable: true,
Listen: base.RawAddress(),
Users: users,
WsPath: options.WsPath,
GrpcServiceName: options.GrpcServiceName,
Certificate: options.Certificate,
PrivateKey: options.PrivateKey,
ClientAuthType: options.ClientAuthType,
ClientAuthCert: options.ClientAuthCert,
EchKey: options.EchKey,
AllowInsecure: options.AllowInsecure,
ShadowTLS: options.ShadowTLS.Build(),
ResTLS: options.ResTLS.Build(),
JLSConfig: options.JLSConfig.Build(),
RealityConfig: options.RealityConfig.Build(),
MuxOption: options.MuxOption.Build(),
Enable: true,
Listen: base.RawAddress(),
Users: users,
WsPath: options.WsPath,
GrpcServiceName: options.GrpcServiceName,
Certificate: options.Certificate,
PrivateKey: options.PrivateKey,
ClientAuthType: options.ClientAuthType,
ClientAuthCert: options.ClientAuthCert,
EchKey: options.EchKey,
AllowInsecure: options.AllowInsecure,
TrustedProxyHeader: options.TrustedProxyHeader,
ShadowTLS: options.ShadowTLS.Build(),
ResTLS: options.ResTLS.Build(),
JLSConfig: options.JLSConfig.Build(),
RealityConfig: options.RealityConfig.Build(),
MuxOption: options.MuxOption.Build(),
TrojanSSOption: LC.TrojanSSOption{
Enabled: options.SSOption.Enabled,
Method: options.SSOption.Method,
Expand Down
70 changes: 36 additions & 34 deletions listener/inbound/vless.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,22 +11,23 @@ import (

type VlessOption struct {
BaseOption
Users []VlessUser `inbound:"users"`
Decryption string `inbound:"decryption,omitempty"`
WsPath string `inbound:"ws-path,omitempty"`
XHTTPConfig XHTTPConfig `inbound:"xhttp-config,omitempty"`
GrpcServiceName string `inbound:"grpc-service-name,omitempty"`
Certificate string `inbound:"certificate,omitempty"`
PrivateKey string `inbound:"private-key,omitempty"`
ClientAuthType string `inbound:"client-auth-type,omitempty"`
ClientAuthCert string `inbound:"client-auth-cert,omitempty"`
EchKey string `inbound:"ech-key,omitempty"`
AllowInsecure bool `inbound:"allow-insecure,omitempty"`
ShadowTLS ShadowTLS `inbound:"shadow-tls,omitempty"`
ResTLS ResTLS `inbound:"res-tls,omitempty"`
JLSConfig JLSConfig `inbound:"jls-config,omitempty"`
RealityConfig RealityConfig `inbound:"reality-config,omitempty"`
MuxOption MuxOption `inbound:"mux-option,omitempty"`
Users []VlessUser `inbound:"users"`
Decryption string `inbound:"decryption,omitempty"`
WsPath string `inbound:"ws-path,omitempty"`
XHTTPConfig XHTTPConfig `inbound:"xhttp-config,omitempty"`
GrpcServiceName string `inbound:"grpc-service-name,omitempty"`
Certificate string `inbound:"certificate,omitempty"`
PrivateKey string `inbound:"private-key,omitempty"`
ClientAuthType string `inbound:"client-auth-type,omitempty"`
ClientAuthCert string `inbound:"client-auth-cert,omitempty"`
EchKey string `inbound:"ech-key,omitempty"`
AllowInsecure bool `inbound:"allow-insecure,omitempty"`
TrustedProxyHeader string `inbound:"trusted-proxy-header,omitempty"`
ShadowTLS ShadowTLS `inbound:"shadow-tls,omitempty"`
ResTLS ResTLS `inbound:"res-tls,omitempty"`
JLSConfig JLSConfig `inbound:"jls-config,omitempty"`
RealityConfig RealityConfig `inbound:"reality-config,omitempty"`
MuxOption MuxOption `inbound:"mux-option,omitempty"`
}

type VlessUser struct {
Expand Down Expand Up @@ -113,24 +114,25 @@ func NewVless(options *VlessOption) (*Vless, error) {
Base: base,
config: options,
vs: LC.VlessServer{
Enable: true,
Listen: base.RawAddress(),
Users: users,
Decryption: options.Decryption,
WsPath: options.WsPath,
XHTTPConfig: options.XHTTPConfig.Build(),
GrpcServiceName: options.GrpcServiceName,
Certificate: options.Certificate,
PrivateKey: options.PrivateKey,
ClientAuthType: options.ClientAuthType,
ClientAuthCert: options.ClientAuthCert,
EchKey: options.EchKey,
AllowInsecure: options.AllowInsecure,
ShadowTLS: options.ShadowTLS.Build(),
ResTLS: options.ResTLS.Build(),
JLSConfig: options.JLSConfig.Build(),
RealityConfig: options.RealityConfig.Build(),
MuxOption: options.MuxOption.Build(),
Enable: true,
Listen: base.RawAddress(),
Users: users,
Decryption: options.Decryption,
WsPath: options.WsPath,
XHTTPConfig: options.XHTTPConfig.Build(),
GrpcServiceName: options.GrpcServiceName,
Certificate: options.Certificate,
PrivateKey: options.PrivateKey,
ClientAuthType: options.ClientAuthType,
ClientAuthCert: options.ClientAuthCert,
EchKey: options.EchKey,
AllowInsecure: options.AllowInsecure,
TrustedProxyHeader: options.TrustedProxyHeader,
ShadowTLS: options.ShadowTLS.Build(),
ResTLS: options.ResTLS.Build(),
JLSConfig: options.JLSConfig.Build(),
RealityConfig: options.RealityConfig.Build(),
MuxOption: options.MuxOption.Build(),
},
}, nil
}
Expand Down
14 changes: 9 additions & 5 deletions listener/sing_vless/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import (
"time"

"github.com/metacubex/mihomo/adapter/inbound"
"github.com/metacubex/mihomo/common/httputils"
"github.com/metacubex/mihomo/component/ca"
"github.com/metacubex/mihomo/component/ech"
C "github.com/metacubex/mihomo/constant"
Expand Down Expand Up @@ -172,7 +173,8 @@ func New(config LC.VlessServer, lc C.InboundListenConfig, tunnel C.Tunnel, addit
http.Error(w, err.Error(), 500)
return
}
sl.HandleConn(conn, tunnel, additions...)
ap := httputils.ClientAddrPortFromHeader(r, config.TrustedProxyHeader)
sl.HandleConn(conn, tunnel, append(slices.Clip(additions), inbound.WithSrcAddrPort(ap))...)
})
httpServer.Handler = httpMux
httpServer.Protocols.SetHTTP1(true)
Expand All @@ -181,8 +183,9 @@ func New(config LC.VlessServer, lc C.InboundListenConfig, tunnel C.Tunnel, addit
if config.GrpcServiceName != "" {
httpServer.Handler = gun.NewServerHandler(gun.ServerOption{
ServiceName: config.GrpcServiceName,
ConnHandler: func(conn net.Conn) {
sl.HandleConn(conn, tunnel, additions...)
ConnHandler: func(conn net.Conn, r *http.Request) {
ap := httputils.ClientAddrPortFromHeader(r, config.TrustedProxyHeader)
sl.HandleConn(conn, tunnel, append(slices.Clip(additions), inbound.WithSrcAddrPort(ap))...)
},
HttpHandler: httpServer.Handler,
})
Expand Down Expand Up @@ -229,8 +232,9 @@ func New(config LC.VlessServer, lc C.InboundListenConfig, tunnel C.Tunnel, addit
ScMaxBufferedPosts: config.XHTTPConfig.ScMaxBufferedPosts,
ScMaxEachPostBytes: config.XHTTPConfig.ScMaxEachPostBytes,
},
ConnHandler: func(conn net.Conn) {
sl.HandleConn(conn, tunnel, additions...)
ConnHandler: func(conn net.Conn, r *http.Request) {
ap := httputils.ClientAddrPortFromHeader(r, config.TrustedProxyHeader)
sl.HandleConn(conn, tunnel, append(slices.Clip(additions), inbound.WithSrcAddrPort(ap))...)
},
HttpHandler: httpServer.Handler,
})
Expand Down
Loading