add support for uot on hysteria2

Co-authored-by: chise0713 <serika@milkdog.onmicrosoft.com>
This commit is contained in:
unknown 2023-12-01 20:29:30 +03:30
parent cca9c06704
commit 25f44254d5
5 changed files with 58 additions and 9 deletions

View File

@ -15,6 +15,7 @@
}, },
"password": "goofy_ahh_password", "password": "goofy_ahh_password",
"network": "tcp", "network": "tcp",
"udp_over_stream": false,
"tls": {}, "tls": {},
"brutal_debug": false, "brutal_debug": false,
@ -72,6 +73,15 @@ One of `tcp` `udp`.
Both is enabled by default. Both is enabled by default.
#### udp_over_stream
This is the Hysteria2 port of the [UDP over TCP protocol](/configuration/shared/udp-over-tcp), designed to provide a QUIC
stream based UDP relay mode that Hysteria2 does not provide. Since it is an add-on protocol, you will need to use sing-box or
another program compatible with the protocol as a server.
This mode can improve reliability in proxying UDP traffic in lossy networks, as it supports retransmitting lost packets using
QUIC's loss detection mechanisms.
#### tls #### tls
==Required== ==Required==

View File

@ -15,6 +15,7 @@
}, },
"password": "goofy_ahh_password", "password": "goofy_ahh_password",
"network": "tcp", "network": "tcp",
"udp_over_stream": false,
"tls": {}, "tls": {},
"brutal_debug": false, "brutal_debug": false,
@ -70,6 +71,15 @@ QUIC 流量混淆器密码.
默认所有。 默认所有。
#### udp_over_stream
这是 Hysteria2 版本的 [UDP over TCP protocol](/configuration/shared/udp-over-tcp), 设计为提供一个Hysteria2没有的
基于QUIC stream的UDP中继模式。由于这是一个附加的协议你需要使用
sing-box 或者与这个协议兼容的其他软件作为服务器。
这个协议可以增加在恶劣环境代理UDP的可靠性因为它支持使用
QUIC 的丢失检测机制重传丢失的数据包。
#### tls #### tls
==必填== ==必填==

View File

@ -4,6 +4,7 @@ package inbound
import ( import (
"context" "context"
"github.com/sagernet/sing-box/common/uot"
"net" "net"
"net/http" "net/http"
"net/http/httputil" "net/http/httputil"
@ -81,7 +82,7 @@ func NewHysteria2(ctx context.Context, router adapter.Router, logger log.Context
protocol: C.TypeHysteria2, protocol: C.TypeHysteria2,
network: []string{N.NetworkUDP}, network: []string{N.NetworkUDP},
ctx: ctx, ctx: ctx,
router: router, router: uot.NewRouter(router, logger),
logger: logger, logger: logger,
tag: tag, tag: tag,
listenOptions: options.ListenOptions, listenOptions: options.ListenOptions,

View File

@ -31,5 +31,6 @@ type Hysteria2OutboundOptions struct {
Password string `json:"password,omitempty"` Password string `json:"password,omitempty"`
Network NetworkList `json:"network,omitempty"` Network NetworkList `json:"network,omitempty"`
OutboundTLSOptionsContainer OutboundTLSOptionsContainer
UDPOverStream bool `json:"udp_over_stream,omitempty"`
BrutalDebug bool `json:"brutal_debug,omitempty"` BrutalDebug bool `json:"brutal_debug,omitempty"`
} }

View File

@ -4,6 +4,7 @@ package outbound
import ( import (
"context" "context"
"github.com/sagernet/sing/common/uot"
"net" "net"
"os" "os"
@ -29,7 +30,8 @@ var (
type Hysteria2 struct { type Hysteria2 struct {
myOutboundAdapter myOutboundAdapter
client *hysteria2.Client client *hysteria2.Client
udpStream bool
} }
func NewHysteria2(ctx context.Context, router adapter.Router, logger log.ContextLogger, tag string, options option.Hysteria2OutboundOptions) (*Hysteria2, error) { func NewHysteria2(ctx context.Context, router adapter.Router, logger log.ContextLogger, tag string, options option.Hysteria2OutboundOptions) (*Hysteria2, error) {
@ -83,7 +85,8 @@ func NewHysteria2(ctx context.Context, router adapter.Router, logger log.Context
tag: tag, tag: tag,
dependencies: withDialerDependency(options.DialerOptions), dependencies: withDialerDependency(options.DialerOptions),
}, },
client: client, client: client,
udpStream: options.UDPOverStream,
}, nil }, nil
} }
@ -93,19 +96,43 @@ func (h *Hysteria2) DialContext(ctx context.Context, network string, destination
h.logger.InfoContext(ctx, "outbound connection to ", destination) h.logger.InfoContext(ctx, "outbound connection to ", destination)
return h.client.DialConn(ctx, destination) return h.client.DialConn(ctx, destination)
case N.NetworkUDP: case N.NetworkUDP:
conn, err := h.ListenPacket(ctx, destination) if h.udpStream {
if err != nil { h.logger.InfoContext(ctx, "outbound stream packet connection to ", destination)
return nil, err streamConn, err := h.client.DialConn(ctx, uot.RequestDestination(uot.Version))
if err != nil {
return nil, err
}
return uot.NewLazyConn(streamConn, uot.Request{
IsConnect: true,
Destination: destination,
}), nil
} else {
conn, err := h.ListenPacket(ctx, destination)
if err != nil {
return nil, err
}
return bufio.NewBindPacketConn(conn, destination), nil
} }
return bufio.NewBindPacketConn(conn, destination), nil
default: default:
return nil, E.New("unsupported network: ", network) return nil, E.New("unsupported network: ", network)
} }
} }
func (h *Hysteria2) ListenPacket(ctx context.Context, destination M.Socksaddr) (net.PacketConn, error) { func (h *Hysteria2) ListenPacket(ctx context.Context, destination M.Socksaddr) (net.PacketConn, error) {
h.logger.InfoContext(ctx, "outbound packet connection to ", destination) if h.udpStream {
return h.client.ListenPacket(ctx) h.logger.InfoContext(ctx, "outbound stream packet connection to ", destination)
streamConn, err := h.client.DialConn(ctx, uot.RequestDestination(uot.Version))
if err != nil {
return nil, err
}
return uot.NewLazyConn(streamConn, uot.Request{
IsConnect: false,
Destination: destination,
}), nil
} else {
h.logger.InfoContext(ctx, "outbound packet connection to ", destination)
return h.client.ListenPacket(ctx)
}
} }
func (h *Hysteria2) NewConnection(ctx context.Context, conn net.Conn, metadata adapter.InboundContext) error { func (h *Hysteria2) NewConnection(ctx context.Context, conn net.Conn, metadata adapter.InboundContext) error {