@@ -5,19 +5,23 @@ import (
55 "errors"
66 "fmt"
77 "net"
8+ "net/netip"
89 "strconv"
910 "time"
1011
1112 N "github.com/metacubex/mihomo/common/net"
1213 "github.com/metacubex/mihomo/common/utils"
1314 "github.com/metacubex/mihomo/component/ca"
15+ "github.com/metacubex/mihomo/component/resolver"
1416 C "github.com/metacubex/mihomo/constant"
1517 "github.com/metacubex/mihomo/log"
1618 "github.com/metacubex/mihomo/transport/tuic/common"
1719
20+ "github.com/metacubex/http"
1821 "github.com/metacubex/quic-go"
1922 qtls "github.com/metacubex/sing-quic"
2023 "github.com/metacubex/sing-quic/hysteria2"
24+ "github.com/metacubex/sing-quic/hysteria2/realm"
2125 M "github.com/metacubex/sing/common/metadata"
2226 "github.com/metacubex/tls"
2327)
@@ -55,13 +59,31 @@ type Hysteria2Option struct {
5559 BBRProfile string `proxy:"bbr-profile,omitempty"`
5660 UdpMTU int `proxy:"udp-mtu,omitempty"`
5761
62+ RealmOpts Hysteria2RealmOption `proxy:"realm-opts,omitempty"`
63+
5864 // quic-go special config
5965 InitialStreamReceiveWindow uint64 `proxy:"initial-stream-receive-window,omitempty"`
6066 MaxStreamReceiveWindow uint64 `proxy:"max-stream-receive-window,omitempty"`
6167 InitialConnectionReceiveWindow uint64 `proxy:"initial-connection-receive-window,omitempty"`
6268 MaxConnectionReceiveWindow uint64 `proxy:"max-connection-receive-window,omitempty"`
6369}
6470
71+ type Hysteria2RealmOption struct {
72+ Enable bool `proxy:"enable,omitempty"`
73+ ServerURL string `proxy:"server-url,omitempty"`
74+ Token string `proxy:"token,omitempty"`
75+ RealmID string `proxy:"realm-id,omitempty"`
76+ STUNServers []string `proxy:"stun-servers,omitempty"`
77+
78+ // for ServerURL
79+ SNI string `proxy:"sni,omitempty"`
80+ SkipCertVerify bool `proxy:"skip-cert-verify,omitempty"`
81+ Fingerprint string `proxy:"fingerprint,omitempty"`
82+ Certificate string `proxy:"certificate,omitempty"`
83+ PrivateKey string `proxy:"private-key,omitempty"`
84+ ALPN []string `proxy:"alpn,omitempty"`
85+ }
86+
6587func (h * Hysteria2 ) DialContext (ctx context.Context , metadata * C.Metadata ) (_ C.Conn , err error ) {
6688 c , err := h .client .DialConn (ctx , M .ParseSocksaddrHostPort (metadata .String (), metadata .DstPort ))
6789 if err != nil {
@@ -229,6 +251,48 @@ func NewHysteria2(option Hysteria2Option) (*Hysteria2, error) {
229251 return nil , errors .New ("invalid port" )
230252 }
231253
254+ if option .RealmOpts .Enable {
255+ httpTLSClientConfig , err := ca .GetTLSConfig (ca.Option {
256+ TLSConfig : & tls.Config {
257+ ServerName : option .RealmOpts .SNI ,
258+ InsecureSkipVerify : option .RealmOpts .SkipCertVerify ,
259+ },
260+ Fingerprint : option .RealmOpts .Fingerprint ,
261+ Certificate : option .RealmOpts .Certificate ,
262+ PrivateKey : option .RealmOpts .PrivateKey ,
263+ })
264+ if err != nil {
265+ return nil , err
266+ }
267+ clientOptions .RealmOptions = & realm.Options {
268+ ServerURL : option .RealmOpts .ServerURL ,
269+ Token : option .RealmOpts .Token ,
270+ RealmID : option .RealmOpts .RealmID ,
271+ STUNServers : option .RealmOpts .STUNServers ,
272+ HTTPClient : & http.Client {
273+ Transport : & http.Transport {
274+ DialContext : outbound .dialer .DialContext ,
275+ TLSClientConfig : httpTLSClientConfig ,
276+ // from http.DefaultTransport
277+ ForceAttemptHTTP2 : true ,
278+ MaxIdleConns : 100 ,
279+ IdleConnTimeout : 90 * time .Second ,
280+ TLSHandshakeTimeout : 10 * time .Second ,
281+ ExpectContinueTimeout : 1 * time .Second ,
282+ },
283+ },
284+ Resolver : func (ctx context.Context , host string , ipv4 , ipv6 bool ) ([]netip.Addr , error ) {
285+ if ipv4 && ! ipv6 {
286+ return resolver .LookupIPv4WithResolver (ctx , host , resolver .ProxyServerHostResolver )
287+ } else if ipv6 && ! ipv4 {
288+ return resolver .LookupIPv4WithResolver (ctx , host , resolver .ProxyServerHostResolver )
289+ }
290+ return resolver .LookupIPWithResolver (ctx , host , resolver .ProxyServerHostResolver )
291+ },
292+ Logger : log .SingLogger ,
293+ }
294+ }
295+
232296 client , err := hysteria2 .NewClient (clientOptions )
233297 if err != nil {
234298 return nil , err
0 commit comments