mirror of
https://github.com/MetaCubeX/mihomo.git
synced 2025-12-20 17:10:08 +08:00
feat: inbound support grpc(lite)
This commit is contained in:
parent
91324b76d2
commit
8d783c65c1
@ -1161,6 +1161,7 @@ listeners:
|
|||||||
uuid: 9d0cb9d0-964f-4ef6-897d-6c6b3ccf9e68
|
uuid: 9d0cb9d0-964f-4ef6-897d-6c6b3ccf9e68
|
||||||
alterId: 1
|
alterId: 1
|
||||||
# ws-path: "/" # 如果不为空则开启 websocket 传输层
|
# ws-path: "/" # 如果不为空则开启 websocket 传输层
|
||||||
|
# grpc-service-name: "GunService" # 如果不为空则开启 grpc 传输层
|
||||||
# 下面两项如果填写则开启 tls(需要同时填写)
|
# 下面两项如果填写则开启 tls(需要同时填写)
|
||||||
# certificate: ./server.crt
|
# certificate: ./server.crt
|
||||||
# private-key: ./server.key
|
# private-key: ./server.key
|
||||||
@ -1213,6 +1214,7 @@ listeners:
|
|||||||
uuid: 9d0cb9d0-964f-4ef6-897d-6c6b3ccf9e68
|
uuid: 9d0cb9d0-964f-4ef6-897d-6c6b3ccf9e68
|
||||||
flow: xtls-rprx-vision
|
flow: xtls-rprx-vision
|
||||||
# ws-path: "/" # 如果不为空则开启 websocket 传输层
|
# ws-path: "/" # 如果不为空则开启 websocket 传输层
|
||||||
|
# grpc-service-name: "GunService" # 如果不为空则开启 grpc 传输层
|
||||||
# 下面两项如果填写则开启 tls(需要同时填写)
|
# 下面两项如果填写则开启 tls(需要同时填写)
|
||||||
# certificate: ./server.crt
|
# certificate: ./server.crt
|
||||||
# private-key: ./server.key
|
# private-key: ./server.key
|
||||||
@ -1248,6 +1250,7 @@ listeners:
|
|||||||
- username: 1
|
- username: 1
|
||||||
password: 9d0cb9d0-964f-4ef6-897d-6c6b3ccf9e68
|
password: 9d0cb9d0-964f-4ef6-897d-6c6b3ccf9e68
|
||||||
# ws-path: "/" # 如果不为空则开启 websocket 传输层
|
# ws-path: "/" # 如果不为空则开启 websocket 传输层
|
||||||
|
# grpc-service-name: "GunService" # 如果不为空则开启 grpc 传输层
|
||||||
# 下面两项如果填写则开启 tls(需要同时填写)
|
# 下面两项如果填写则开启 tls(需要同时填写)
|
||||||
certificate: ./server.crt
|
certificate: ./server.crt
|
||||||
private-key: ./server.key
|
private-key: ./server.key
|
||||||
|
|||||||
@ -13,15 +13,16 @@ type TrojanUser struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type TrojanServer struct {
|
type TrojanServer struct {
|
||||||
Enable bool
|
Enable bool
|
||||||
Listen string
|
Listen string
|
||||||
Users []TrojanUser
|
Users []TrojanUser
|
||||||
WsPath string
|
WsPath string
|
||||||
Certificate string
|
GrpcServiceName string
|
||||||
PrivateKey string
|
Certificate string
|
||||||
RealityConfig reality.Config
|
PrivateKey string
|
||||||
MuxOption sing.MuxOption
|
RealityConfig reality.Config
|
||||||
TrojanSSOption TrojanSSOption
|
MuxOption sing.MuxOption
|
||||||
|
TrojanSSOption TrojanSSOption
|
||||||
}
|
}
|
||||||
|
|
||||||
// TrojanSSOption from https://github.com/p4gefau1t/trojan-go/blob/v0.10.6/tunnel/shadowsocks/config.go#L5
|
// TrojanSSOption from https://github.com/p4gefau1t/trojan-go/blob/v0.10.6/tunnel/shadowsocks/config.go#L5
|
||||||
|
|||||||
@ -14,14 +14,15 @@ type VlessUser struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type VlessServer struct {
|
type VlessServer struct {
|
||||||
Enable bool
|
Enable bool
|
||||||
Listen string
|
Listen string
|
||||||
Users []VlessUser
|
Users []VlessUser
|
||||||
WsPath string
|
WsPath string
|
||||||
Certificate string
|
GrpcServiceName string
|
||||||
PrivateKey string
|
Certificate string
|
||||||
RealityConfig reality.Config
|
PrivateKey string
|
||||||
MuxOption sing.MuxOption `yaml:"mux-option" json:"mux-option,omitempty"`
|
RealityConfig reality.Config
|
||||||
|
MuxOption sing.MuxOption `yaml:"mux-option" json:"mux-option,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t VlessServer) String() string {
|
func (t VlessServer) String() string {
|
||||||
|
|||||||
@ -14,14 +14,15 @@ type VmessUser struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type VmessServer struct {
|
type VmessServer struct {
|
||||||
Enable bool
|
Enable bool
|
||||||
Listen string
|
Listen string
|
||||||
Users []VmessUser
|
Users []VmessUser
|
||||||
WsPath string
|
WsPath string
|
||||||
Certificate string
|
GrpcServiceName string
|
||||||
PrivateKey string
|
Certificate string
|
||||||
RealityConfig reality.Config
|
PrivateKey string
|
||||||
MuxOption sing.MuxOption `yaml:"mux-option" json:"mux-option,omitempty"`
|
RealityConfig reality.Config
|
||||||
|
MuxOption sing.MuxOption `yaml:"mux-option" json:"mux-option,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t VmessServer) String() string {
|
func (t VmessServer) String() string {
|
||||||
|
|||||||
@ -9,13 +9,14 @@ import (
|
|||||||
|
|
||||||
type TrojanOption struct {
|
type TrojanOption struct {
|
||||||
BaseOption
|
BaseOption
|
||||||
Users []TrojanUser `inbound:"users"`
|
Users []TrojanUser `inbound:"users"`
|
||||||
WsPath string `inbound:"ws-path,omitempty"`
|
WsPath string `inbound:"ws-path,omitempty"`
|
||||||
Certificate string `inbound:"certificate,omitempty"`
|
GrpcServiceName string `inbound:"grpc-service-name,omitempty"`
|
||||||
PrivateKey string `inbound:"private-key,omitempty"`
|
Certificate string `inbound:"certificate,omitempty"`
|
||||||
RealityConfig RealityConfig `inbound:"reality-config,omitempty"`
|
PrivateKey string `inbound:"private-key,omitempty"`
|
||||||
MuxOption MuxOption `inbound:"mux-option,omitempty"`
|
RealityConfig RealityConfig `inbound:"reality-config,omitempty"`
|
||||||
SSOption TrojanSSOption `inbound:"ss-option,omitempty"`
|
MuxOption MuxOption `inbound:"mux-option,omitempty"`
|
||||||
|
SSOption TrojanSSOption `inbound:"ss-option,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type TrojanUser struct {
|
type TrojanUser struct {
|
||||||
@ -57,14 +58,15 @@ func NewTrojan(options *TrojanOption) (*Trojan, error) {
|
|||||||
Base: base,
|
Base: base,
|
||||||
config: options,
|
config: options,
|
||||||
vs: LC.TrojanServer{
|
vs: LC.TrojanServer{
|
||||||
Enable: true,
|
Enable: true,
|
||||||
Listen: base.RawAddress(),
|
Listen: base.RawAddress(),
|
||||||
Users: users,
|
Users: users,
|
||||||
WsPath: options.WsPath,
|
WsPath: options.WsPath,
|
||||||
Certificate: options.Certificate,
|
GrpcServiceName: options.GrpcServiceName,
|
||||||
PrivateKey: options.PrivateKey,
|
Certificate: options.Certificate,
|
||||||
RealityConfig: options.RealityConfig.Build(),
|
PrivateKey: options.PrivateKey,
|
||||||
MuxOption: options.MuxOption.Build(),
|
RealityConfig: options.RealityConfig.Build(),
|
||||||
|
MuxOption: options.MuxOption.Build(),
|
||||||
TrojanSSOption: LC.TrojanSSOption{
|
TrojanSSOption: LC.TrojanSSOption{
|
||||||
Enabled: options.SSOption.Enabled,
|
Enabled: options.SSOption.Enabled,
|
||||||
Method: options.SSOption.Method,
|
Method: options.SSOption.Method,
|
||||||
|
|||||||
@ -9,12 +9,13 @@ import (
|
|||||||
|
|
||||||
type VlessOption struct {
|
type VlessOption struct {
|
||||||
BaseOption
|
BaseOption
|
||||||
Users []VlessUser `inbound:"users"`
|
Users []VlessUser `inbound:"users"`
|
||||||
WsPath string `inbound:"ws-path,omitempty"`
|
WsPath string `inbound:"ws-path,omitempty"`
|
||||||
Certificate string `inbound:"certificate,omitempty"`
|
GrpcServiceName string `inbound:"grpc-service-name,omitempty"`
|
||||||
PrivateKey string `inbound:"private-key,omitempty"`
|
Certificate string `inbound:"certificate,omitempty"`
|
||||||
RealityConfig RealityConfig `inbound:"reality-config,omitempty"`
|
PrivateKey string `inbound:"private-key,omitempty"`
|
||||||
MuxOption MuxOption `inbound:"mux-option,omitempty"`
|
RealityConfig RealityConfig `inbound:"reality-config,omitempty"`
|
||||||
|
MuxOption MuxOption `inbound:"mux-option,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type VlessUser struct {
|
type VlessUser struct {
|
||||||
@ -51,14 +52,15 @@ func NewVless(options *VlessOption) (*Vless, error) {
|
|||||||
Base: base,
|
Base: base,
|
||||||
config: options,
|
config: options,
|
||||||
vs: LC.VlessServer{
|
vs: LC.VlessServer{
|
||||||
Enable: true,
|
Enable: true,
|
||||||
Listen: base.RawAddress(),
|
Listen: base.RawAddress(),
|
||||||
Users: users,
|
Users: users,
|
||||||
WsPath: options.WsPath,
|
WsPath: options.WsPath,
|
||||||
Certificate: options.Certificate,
|
GrpcServiceName: options.GrpcServiceName,
|
||||||
PrivateKey: options.PrivateKey,
|
Certificate: options.Certificate,
|
||||||
RealityConfig: options.RealityConfig.Build(),
|
PrivateKey: options.PrivateKey,
|
||||||
MuxOption: options.MuxOption.Build(),
|
RealityConfig: options.RealityConfig.Build(),
|
||||||
|
MuxOption: options.MuxOption.Build(),
|
||||||
},
|
},
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|||||||
@ -9,12 +9,13 @@ import (
|
|||||||
|
|
||||||
type VmessOption struct {
|
type VmessOption struct {
|
||||||
BaseOption
|
BaseOption
|
||||||
Users []VmessUser `inbound:"users"`
|
Users []VmessUser `inbound:"users"`
|
||||||
WsPath string `inbound:"ws-path,omitempty"`
|
WsPath string `inbound:"ws-path,omitempty"`
|
||||||
Certificate string `inbound:"certificate,omitempty"`
|
GrpcServiceName string `inbound:"grpc-service-name,omitempty"`
|
||||||
PrivateKey string `inbound:"private-key,omitempty"`
|
Certificate string `inbound:"certificate,omitempty"`
|
||||||
RealityConfig RealityConfig `inbound:"reality-config,omitempty"`
|
PrivateKey string `inbound:"private-key,omitempty"`
|
||||||
MuxOption MuxOption `inbound:"mux-option,omitempty"`
|
RealityConfig RealityConfig `inbound:"reality-config,omitempty"`
|
||||||
|
MuxOption MuxOption `inbound:"mux-option,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type VmessUser struct {
|
type VmessUser struct {
|
||||||
@ -51,14 +52,15 @@ func NewVmess(options *VmessOption) (*Vmess, error) {
|
|||||||
Base: base,
|
Base: base,
|
||||||
config: options,
|
config: options,
|
||||||
vs: LC.VmessServer{
|
vs: LC.VmessServer{
|
||||||
Enable: true,
|
Enable: true,
|
||||||
Listen: base.RawAddress(),
|
Listen: base.RawAddress(),
|
||||||
Users: users,
|
Users: users,
|
||||||
WsPath: options.WsPath,
|
WsPath: options.WsPath,
|
||||||
Certificate: options.Certificate,
|
GrpcServiceName: options.GrpcServiceName,
|
||||||
PrivateKey: options.PrivateKey,
|
Certificate: options.Certificate,
|
||||||
RealityConfig: options.RealityConfig.Build(),
|
PrivateKey: options.PrivateKey,
|
||||||
MuxOption: options.MuxOption.Build(),
|
RealityConfig: options.RealityConfig.Build(),
|
||||||
|
MuxOption: options.MuxOption.Build(),
|
||||||
},
|
},
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|||||||
@ -18,6 +18,7 @@ import (
|
|||||||
"github.com/metacubex/mihomo/listener/reality"
|
"github.com/metacubex/mihomo/listener/reality"
|
||||||
"github.com/metacubex/mihomo/listener/sing"
|
"github.com/metacubex/mihomo/listener/sing"
|
||||||
"github.com/metacubex/mihomo/log"
|
"github.com/metacubex/mihomo/log"
|
||||||
|
"github.com/metacubex/mihomo/transport/gun"
|
||||||
mihomoVMess "github.com/metacubex/mihomo/transport/vmess"
|
mihomoVMess "github.com/metacubex/mihomo/transport/vmess"
|
||||||
|
|
||||||
"github.com/metacubex/sing-vmess/vless"
|
"github.com/metacubex/sing-vmess/vless"
|
||||||
@ -92,7 +93,7 @@ func New(config LC.VlessServer, tunnel C.Tunnel, additions ...inbound.Addition)
|
|||||||
|
|
||||||
tlsConfig := &tls.Config{}
|
tlsConfig := &tls.Config{}
|
||||||
var realityBuilder *reality.Builder
|
var realityBuilder *reality.Builder
|
||||||
var httpMux *http.ServeMux
|
var httpHandler http.Handler
|
||||||
|
|
||||||
if config.Certificate != "" && config.PrivateKey != "" {
|
if config.Certificate != "" && config.PrivateKey != "" {
|
||||||
cert, err := N.ParseCert(config.Certificate, config.PrivateKey, C.Path)
|
cert, err := N.ParseCert(config.Certificate, config.PrivateKey, C.Path)
|
||||||
@ -111,17 +112,28 @@ func New(config LC.VlessServer, tunnel C.Tunnel, additions ...inbound.Addition)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if config.WsPath != "" {
|
if config.WsPath != "" {
|
||||||
httpMux = http.NewServeMux()
|
httpMux := http.NewServeMux()
|
||||||
httpMux.HandleFunc(config.WsPath, func(w http.ResponseWriter, r *http.Request) {
|
httpMux.HandleFunc(config.WsPath, func(w http.ResponseWriter, r *http.Request) {
|
||||||
conn, err := mihomoVMess.StreamUpgradedWebsocketConn(w, r)
|
conn, err := mihomoVMess.StreamUpgradedWebsocketConn(w, r)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
http.Error(w, err.Error(), 500)
|
http.Error(w, err.Error(), 500)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
sl.HandleConn(conn, tunnel)
|
sl.HandleConn(conn, tunnel, additions...)
|
||||||
})
|
})
|
||||||
|
httpHandler = httpMux
|
||||||
tlsConfig.NextProtos = append(tlsConfig.NextProtos, "http/1.1")
|
tlsConfig.NextProtos = append(tlsConfig.NextProtos, "http/1.1")
|
||||||
}
|
}
|
||||||
|
if config.GrpcServiceName != "" {
|
||||||
|
httpHandler = gun.NewServerHandler(gun.ServerOption{
|
||||||
|
ServiceName: config.GrpcServiceName,
|
||||||
|
ConnHandler: func(conn net.Conn) {
|
||||||
|
sl.HandleConn(conn, tunnel, additions...)
|
||||||
|
},
|
||||||
|
HttpHandler: httpHandler,
|
||||||
|
})
|
||||||
|
tlsConfig.NextProtos = append(tlsConfig.NextProtos, "h2")
|
||||||
|
}
|
||||||
|
|
||||||
for _, addr := range strings.Split(config.Listen, ",") {
|
for _, addr := range strings.Split(config.Listen, ",") {
|
||||||
addr := addr
|
addr := addr
|
||||||
@ -141,8 +153,8 @@ func New(config LC.VlessServer, tunnel C.Tunnel, additions ...inbound.Addition)
|
|||||||
sl.listeners = append(sl.listeners, l)
|
sl.listeners = append(sl.listeners, l)
|
||||||
|
|
||||||
go func() {
|
go func() {
|
||||||
if httpMux != nil {
|
if httpHandler != nil {
|
||||||
_ = http.Serve(l, httpMux)
|
_ = http.Serve(l, httpHandler)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
for {
|
for {
|
||||||
|
|||||||
@ -16,6 +16,7 @@ import (
|
|||||||
"github.com/metacubex/mihomo/listener/reality"
|
"github.com/metacubex/mihomo/listener/reality"
|
||||||
"github.com/metacubex/mihomo/listener/sing"
|
"github.com/metacubex/mihomo/listener/sing"
|
||||||
"github.com/metacubex/mihomo/ntp"
|
"github.com/metacubex/mihomo/ntp"
|
||||||
|
"github.com/metacubex/mihomo/transport/gun"
|
||||||
mihomoVMess "github.com/metacubex/mihomo/transport/vmess"
|
mihomoVMess "github.com/metacubex/mihomo/transport/vmess"
|
||||||
|
|
||||||
vmess "github.com/metacubex/sing-vmess"
|
vmess "github.com/metacubex/sing-vmess"
|
||||||
@ -76,7 +77,7 @@ func New(config LC.VmessServer, tunnel C.Tunnel, additions ...inbound.Addition)
|
|||||||
|
|
||||||
tlsConfig := &tls.Config{}
|
tlsConfig := &tls.Config{}
|
||||||
var realityBuilder *reality.Builder
|
var realityBuilder *reality.Builder
|
||||||
var httpMux *http.ServeMux
|
var httpHandler http.Handler
|
||||||
|
|
||||||
if config.Certificate != "" && config.PrivateKey != "" {
|
if config.Certificate != "" && config.PrivateKey != "" {
|
||||||
cert, err := N.ParseCert(config.Certificate, config.PrivateKey, C.Path)
|
cert, err := N.ParseCert(config.Certificate, config.PrivateKey, C.Path)
|
||||||
@ -95,17 +96,28 @@ func New(config LC.VmessServer, tunnel C.Tunnel, additions ...inbound.Addition)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if config.WsPath != "" {
|
if config.WsPath != "" {
|
||||||
httpMux = http.NewServeMux()
|
httpMux := http.NewServeMux()
|
||||||
httpMux.HandleFunc(config.WsPath, func(w http.ResponseWriter, r *http.Request) {
|
httpMux.HandleFunc(config.WsPath, func(w http.ResponseWriter, r *http.Request) {
|
||||||
conn, err := mihomoVMess.StreamUpgradedWebsocketConn(w, r)
|
conn, err := mihomoVMess.StreamUpgradedWebsocketConn(w, r)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
http.Error(w, err.Error(), 500)
|
http.Error(w, err.Error(), 500)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
sl.HandleConn(conn, tunnel)
|
sl.HandleConn(conn, tunnel, additions...)
|
||||||
})
|
})
|
||||||
|
httpHandler = httpMux
|
||||||
tlsConfig.NextProtos = append(tlsConfig.NextProtos, "http/1.1")
|
tlsConfig.NextProtos = append(tlsConfig.NextProtos, "http/1.1")
|
||||||
}
|
}
|
||||||
|
if config.GrpcServiceName != "" {
|
||||||
|
httpHandler = gun.NewServerHandler(gun.ServerOption{
|
||||||
|
ServiceName: config.GrpcServiceName,
|
||||||
|
ConnHandler: func(conn net.Conn) {
|
||||||
|
sl.HandleConn(conn, tunnel, additions...)
|
||||||
|
},
|
||||||
|
HttpHandler: httpHandler,
|
||||||
|
})
|
||||||
|
tlsConfig.NextProtos = append(tlsConfig.NextProtos, "h2")
|
||||||
|
}
|
||||||
|
|
||||||
for _, addr := range strings.Split(config.Listen, ",") {
|
for _, addr := range strings.Split(config.Listen, ",") {
|
||||||
addr := addr
|
addr := addr
|
||||||
@ -123,8 +135,8 @@ func New(config LC.VmessServer, tunnel C.Tunnel, additions ...inbound.Addition)
|
|||||||
sl.listeners = append(sl.listeners, l)
|
sl.listeners = append(sl.listeners, l)
|
||||||
|
|
||||||
go func() {
|
go func() {
|
||||||
if httpMux != nil {
|
if httpHandler != nil {
|
||||||
_ = http.Serve(l, httpMux)
|
_ = http.Serve(l, httpHandler)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
for {
|
for {
|
||||||
|
|||||||
@ -14,6 +14,7 @@ import (
|
|||||||
LC "github.com/metacubex/mihomo/listener/config"
|
LC "github.com/metacubex/mihomo/listener/config"
|
||||||
"github.com/metacubex/mihomo/listener/reality"
|
"github.com/metacubex/mihomo/listener/reality"
|
||||||
"github.com/metacubex/mihomo/listener/sing"
|
"github.com/metacubex/mihomo/listener/sing"
|
||||||
|
"github.com/metacubex/mihomo/transport/gun"
|
||||||
"github.com/metacubex/mihomo/transport/shadowsocks/core"
|
"github.com/metacubex/mihomo/transport/shadowsocks/core"
|
||||||
"github.com/metacubex/mihomo/transport/socks5"
|
"github.com/metacubex/mihomo/transport/socks5"
|
||||||
"github.com/metacubex/mihomo/transport/trojan"
|
"github.com/metacubex/mihomo/transport/trojan"
|
||||||
@ -70,7 +71,7 @@ func New(config LC.TrojanServer, tunnel C.Tunnel, additions ...inbound.Addition)
|
|||||||
|
|
||||||
tlsConfig := &tls.Config{}
|
tlsConfig := &tls.Config{}
|
||||||
var realityBuilder *reality.Builder
|
var realityBuilder *reality.Builder
|
||||||
var httpMux *http.ServeMux
|
var httpHandler http.Handler
|
||||||
|
|
||||||
if config.Certificate != "" && config.PrivateKey != "" {
|
if config.Certificate != "" && config.PrivateKey != "" {
|
||||||
cert, err := N.ParseCert(config.Certificate, config.PrivateKey, C.Path)
|
cert, err := N.ParseCert(config.Certificate, config.PrivateKey, C.Path)
|
||||||
@ -89,17 +90,28 @@ func New(config LC.TrojanServer, tunnel C.Tunnel, additions ...inbound.Addition)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if config.WsPath != "" {
|
if config.WsPath != "" {
|
||||||
httpMux = http.NewServeMux()
|
httpMux := http.NewServeMux()
|
||||||
httpMux.HandleFunc(config.WsPath, func(w http.ResponseWriter, r *http.Request) {
|
httpMux.HandleFunc(config.WsPath, func(w http.ResponseWriter, r *http.Request) {
|
||||||
conn, err := mihomoVMess.StreamUpgradedWebsocketConn(w, r)
|
conn, err := mihomoVMess.StreamUpgradedWebsocketConn(w, r)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
http.Error(w, err.Error(), 500)
|
http.Error(w, err.Error(), 500)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
sl.HandleConn(conn, tunnel)
|
sl.HandleConn(conn, tunnel, additions...)
|
||||||
})
|
})
|
||||||
|
httpHandler = httpMux
|
||||||
tlsConfig.NextProtos = append(tlsConfig.NextProtos, "http/1.1")
|
tlsConfig.NextProtos = append(tlsConfig.NextProtos, "http/1.1")
|
||||||
}
|
}
|
||||||
|
if config.GrpcServiceName != "" {
|
||||||
|
httpHandler = gun.NewServerHandler(gun.ServerOption{
|
||||||
|
ServiceName: config.GrpcServiceName,
|
||||||
|
ConnHandler: func(conn net.Conn) {
|
||||||
|
sl.HandleConn(conn, tunnel, additions...)
|
||||||
|
},
|
||||||
|
HttpHandler: httpHandler,
|
||||||
|
})
|
||||||
|
tlsConfig.NextProtos = append(tlsConfig.NextProtos, "h2")
|
||||||
|
}
|
||||||
|
|
||||||
for _, addr := range strings.Split(config.Listen, ",") {
|
for _, addr := range strings.Split(config.Listen, ",") {
|
||||||
addr := addr
|
addr := addr
|
||||||
@ -119,8 +131,8 @@ func New(config LC.TrojanServer, tunnel C.Tunnel, additions ...inbound.Addition)
|
|||||||
sl.listeners = append(sl.listeners, l)
|
sl.listeners = append(sl.listeners, l)
|
||||||
|
|
||||||
go func() {
|
go func() {
|
||||||
if httpMux != nil {
|
if httpHandler != nil {
|
||||||
_ = http.Serve(l, httpMux)
|
_ = http.Serve(l, httpHandler)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
for {
|
for {
|
||||||
@ -132,7 +144,7 @@ func New(config LC.TrojanServer, tunnel C.Tunnel, additions ...inbound.Addition)
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
go sl.HandleConn(c, tunnel)
|
go sl.HandleConn(c, tunnel, additions...)
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
}
|
}
|
||||||
|
|||||||
@ -38,15 +38,17 @@ var defaultHeader = http.Header{
|
|||||||
type DialFn = func(network, addr string) (net.Conn, error)
|
type DialFn = func(network, addr string) (net.Conn, error)
|
||||||
|
|
||||||
type Conn struct {
|
type Conn struct {
|
||||||
response *http.Response
|
initFn func() (io.ReadCloser, error)
|
||||||
request *http.Request
|
writer io.Writer
|
||||||
transport *TransportWrap
|
flusher http.Flusher
|
||||||
writer *io.PipeWriter
|
netAddr
|
||||||
once sync.Once
|
|
||||||
close atomic.Bool
|
reader io.ReadCloser
|
||||||
err error
|
once sync.Once
|
||||||
remain int
|
close atomic.Bool
|
||||||
br *bufio.Reader
|
err error
|
||||||
|
remain int
|
||||||
|
br *bufio.Reader
|
||||||
// deadlines
|
// deadlines
|
||||||
deadline *time.Timer
|
deadline *time.Timer
|
||||||
}
|
}
|
||||||
@ -57,26 +59,32 @@ type Config struct {
|
|||||||
ClientFingerprint string
|
ClientFingerprint string
|
||||||
}
|
}
|
||||||
|
|
||||||
func (g *Conn) initRequest() {
|
func (g *Conn) initReader() {
|
||||||
response, err := g.transport.RoundTrip(g.request)
|
reader, err := g.initFn()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
g.err = err
|
g.err = err
|
||||||
g.writer.Close()
|
if closer, ok := g.writer.(io.Closer); ok {
|
||||||
|
closer.Close()
|
||||||
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if !g.close.Load() {
|
if !g.close.Load() {
|
||||||
g.response = response
|
g.reader = reader
|
||||||
g.br = bufio.NewReader(response.Body)
|
g.br = bufio.NewReader(reader)
|
||||||
} else {
|
} else {
|
||||||
response.Body.Close()
|
reader.Close()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (g *Conn) Init() error {
|
||||||
|
g.once.Do(g.initReader)
|
||||||
|
return g.err
|
||||||
|
}
|
||||||
|
|
||||||
func (g *Conn) Read(b []byte) (n int, err error) {
|
func (g *Conn) Read(b []byte) (n int, err error) {
|
||||||
g.once.Do(g.initRequest)
|
if err = g.Init(); err != nil {
|
||||||
if g.err != nil {
|
return
|
||||||
return 0, g.err
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if g.remain > 0 {
|
if g.remain > 0 {
|
||||||
@ -88,7 +96,7 @@ func (g *Conn) Read(b []byte) (n int, err error) {
|
|||||||
n, err = io.ReadFull(g.br, b[:size])
|
n, err = io.ReadFull(g.br, b[:size])
|
||||||
g.remain -= n
|
g.remain -= n
|
||||||
return
|
return
|
||||||
} else if g.response == nil {
|
} else if g.reader == nil {
|
||||||
return 0, net.ErrClosed
|
return 0, net.ErrClosed
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -139,6 +147,10 @@ func (g *Conn) Write(b []byte) (n int, err error) {
|
|||||||
err = g.err
|
err = g.err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if g.flusher != nil {
|
||||||
|
g.flusher.Flush()
|
||||||
|
}
|
||||||
|
|
||||||
return len(b), err
|
return len(b), err
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -158,6 +170,10 @@ func (g *Conn) WriteBuffer(buffer *buf.Buffer) error {
|
|||||||
err = g.err
|
err = g.err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if g.flusher != nil {
|
||||||
|
g.flusher.Flush()
|
||||||
|
}
|
||||||
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -167,15 +183,16 @@ func (g *Conn) FrontHeadroom() int {
|
|||||||
|
|
||||||
func (g *Conn) Close() error {
|
func (g *Conn) Close() error {
|
||||||
g.close.Store(true)
|
g.close.Store(true)
|
||||||
if r := g.response; r != nil {
|
if reader := g.reader; reader != nil {
|
||||||
r.Body.Close()
|
reader.Close()
|
||||||
}
|
}
|
||||||
|
|
||||||
return g.writer.Close()
|
if closer, ok := g.writer.(io.Closer); ok {
|
||||||
|
return closer.Close()
|
||||||
|
}
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (g *Conn) LocalAddr() net.Addr { return g.transport.LocalAddr() }
|
|
||||||
func (g *Conn) RemoteAddr() net.Addr { return g.transport.RemoteAddr() }
|
|
||||||
func (g *Conn) SetReadDeadline(t time.Time) error { return g.SetDeadline(t) }
|
func (g *Conn) SetReadDeadline(t time.Time) error { return g.SetDeadline(t) }
|
||||||
func (g *Conn) SetWriteDeadline(t time.Time) error { return g.SetDeadline(t) }
|
func (g *Conn) SetWriteDeadline(t time.Time) error { return g.SetDeadline(t) }
|
||||||
|
|
||||||
@ -200,6 +217,7 @@ func NewHTTP2Client(dialFn DialFn, tlsConfig *tls.Config, Fingerprint string, re
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
wrap.remoteAddr = pconn.RemoteAddr()
|
wrap.remoteAddr = pconn.RemoteAddr()
|
||||||
|
wrap.localAddr = pconn.LocalAddr()
|
||||||
|
|
||||||
if tlsConfig == nil {
|
if tlsConfig == nil {
|
||||||
return pconn, nil
|
return pconn, nil
|
||||||
@ -286,13 +304,18 @@ func StreamGunWithTransport(transport *TransportWrap, cfg *Config) (net.Conn, er
|
|||||||
}
|
}
|
||||||
|
|
||||||
conn := &Conn{
|
conn := &Conn{
|
||||||
request: request,
|
initFn: func() (io.ReadCloser, error) {
|
||||||
transport: transport,
|
response, err := transport.RoundTrip(request)
|
||||||
writer: writer,
|
if err != nil {
|
||||||
close: atomic.NewBool(false),
|
return nil, err
|
||||||
|
}
|
||||||
|
return response.Body, nil
|
||||||
|
},
|
||||||
|
writer: writer,
|
||||||
|
netAddr: transport.netAddr,
|
||||||
}
|
}
|
||||||
|
|
||||||
go conn.once.Do(conn.initRequest)
|
go conn.Init()
|
||||||
return conn, nil
|
return conn, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
70
transport/gun/server.go
Normal file
70
transport/gun/server.go
Normal file
@ -0,0 +1,70 @@
|
|||||||
|
package gun
|
||||||
|
|
||||||
|
import (
|
||||||
|
"io"
|
||||||
|
"net"
|
||||||
|
"net/http"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
N "github.com/metacubex/mihomo/common/net"
|
||||||
|
C "github.com/metacubex/mihomo/constant"
|
||||||
|
|
||||||
|
"golang.org/x/net/http2"
|
||||||
|
"golang.org/x/net/http2/h2c"
|
||||||
|
)
|
||||||
|
|
||||||
|
const idleTimeout = 30 * time.Second
|
||||||
|
|
||||||
|
type ServerOption struct {
|
||||||
|
ServiceName string
|
||||||
|
ConnHandler func(conn net.Conn)
|
||||||
|
HttpHandler http.Handler
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewServerHandler(options ServerOption) http.Handler {
|
||||||
|
path := "/" + options.ServiceName + "/Tun"
|
||||||
|
connHandler := options.ConnHandler
|
||||||
|
httpHandler := options.HttpHandler
|
||||||
|
if httpHandler == nil {
|
||||||
|
httpHandler = http.NewServeMux()
|
||||||
|
}
|
||||||
|
// using h2c.NewHandler to ensure we can work in plain http2
|
||||||
|
// and some tls conn is not *tls.Conn (like *reality.Conn)
|
||||||
|
return h2c.NewHandler(http.HandlerFunc(func(writer http.ResponseWriter, request *http.Request) {
|
||||||
|
if request.URL.Path == path &&
|
||||||
|
request.Method == http.MethodPost &&
|
||||||
|
strings.HasPrefix(request.Header.Get("Content-Type"), "application/grpc") {
|
||||||
|
|
||||||
|
writer.Header().Set("Content-Type", "application/grpc")
|
||||||
|
writer.Header().Set("TE", "trailers")
|
||||||
|
writer.WriteHeader(http.StatusOK)
|
||||||
|
|
||||||
|
conn := &Conn{
|
||||||
|
initFn: func() (io.ReadCloser, error) {
|
||||||
|
return request.Body, nil
|
||||||
|
},
|
||||||
|
writer: writer,
|
||||||
|
flusher: writer.(http.Flusher),
|
||||||
|
}
|
||||||
|
if request.RemoteAddr != "" {
|
||||||
|
metadata := C.Metadata{}
|
||||||
|
if err := metadata.SetRemoteAddress(request.RemoteAddr); err == nil {
|
||||||
|
conn.remoteAddr = net.TCPAddrFromAddrPort(metadata.AddrPort())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if addr, ok := request.Context().Value(http.LocalAddrContextKey).(net.Addr); ok {
|
||||||
|
conn.localAddr = addr
|
||||||
|
}
|
||||||
|
|
||||||
|
// gun.Conn can't correct handle ReadDeadline
|
||||||
|
// so call N.NewDeadlineConn to add a safe wrapper
|
||||||
|
connHandler(N.NewDeadlineConn(conn))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
httpHandler.ServeHTTP(writer, request)
|
||||||
|
}), &http2.Server{
|
||||||
|
IdleTimeout: idleTimeout,
|
||||||
|
})
|
||||||
|
}
|
||||||
@ -7,8 +7,7 @@ import (
|
|||||||
|
|
||||||
type TransportWrap struct {
|
type TransportWrap struct {
|
||||||
*http2.Transport
|
*http2.Transport
|
||||||
remoteAddr net.Addr
|
netAddr
|
||||||
localAddr net.Addr
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (tw *TransportWrap) RemoteAddr() net.Addr {
|
func (tw *TransportWrap) RemoteAddr() net.Addr {
|
||||||
@ -18,3 +17,16 @@ func (tw *TransportWrap) RemoteAddr() net.Addr {
|
|||||||
func (tw *TransportWrap) LocalAddr() net.Addr {
|
func (tw *TransportWrap) LocalAddr() net.Addr {
|
||||||
return tw.localAddr
|
return tw.localAddr
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type netAddr struct {
|
||||||
|
remoteAddr net.Addr
|
||||||
|
localAddr net.Addr
|
||||||
|
}
|
||||||
|
|
||||||
|
func (addr *netAddr) RemoteAddr() net.Addr {
|
||||||
|
return addr.remoteAddr
|
||||||
|
}
|
||||||
|
|
||||||
|
func (addr *netAddr) LocalAddr() net.Addr {
|
||||||
|
return addr.localAddr
|
||||||
|
}
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user