package main import ( "net/netip" "testing" "github.com/sagernet/sing-box/common/tls" C "github.com/sagernet/sing-box/constant" "github.com/sagernet/sing-box/option" "github.com/sagernet/sing/common" "github.com/gofrs/uuid/v5" ) func TestECH(t *testing.T) { _, certPem, keyPem := createSelfSignedCertificate(t, "example.org") echConfig, echKey := common.Must2(tls.ECHKeygenDefault("not.example.org", false)) startInstance(t, option.Options{ Inbounds: []option.LegacyInbound{ { Type: C.TypeMixed, Tag: "mixed-in", MixedOptions: option.HTTPMixedInboundOptions{ ListenOptions: option.ListenOptions{ Listen: option.NewListenAddress(netip.IPv4Unspecified()), ListenPort: clientPort, }, }, }, { Type: C.TypeTrojan, TrojanOptions: option.TrojanInboundOptions{ ListenOptions: option.ListenOptions{ Listen: option.NewListenAddress(netip.IPv4Unspecified()), ListenPort: serverPort, }, Users: []option.TrojanUser{ { Name: "sekai", Password: "password", }, }, InboundTLSOptionsContainer: option.InboundTLSOptionsContainer{ TLS: &option.InboundTLSOptions{ Enabled: true, ServerName: "example.org", CertificatePath: certPem, KeyPath: keyPem, ECH: &option.InboundECHOptions{ Enabled: true, Key: []string{echKey}, }, }, }, }, }, }, LegacyOutbounds: []option.LegacyOutbound{ { Type: C.TypeDirect, }, { Type: C.TypeTrojan, Tag: "trojan-out", TrojanOptions: option.TrojanOutboundOptions{ ServerOptions: option.ServerOptions{ Server: "127.0.0.1", ServerPort: serverPort, }, Password: "password", OutboundTLSOptionsContainer: option.OutboundTLSOptionsContainer{ TLS: &option.OutboundTLSOptions{ Enabled: true, ServerName: "example.org", CertificatePath: certPem, ECH: &option.OutboundECHOptions{ Enabled: true, Config: []string{echConfig}, }, }, }, }, }, }, Route: &option.RouteOptions{ Rules: []option.Rule{ { Type: C.RuleTypeDefault, DefaultOptions: option.DefaultRule{ RawDefaultRule: option.RawDefaultRule{ Inbound: []string{"mixed-in"}, }, RuleAction: option.RuleAction{ Action: C.RuleActionTypeRoute, RouteOptions: option.RouteActionOptions{ Outbound: "trojan-out", }, }, }, }, }, }, }) testSuit(t, clientPort, testPort) } func TestECHQUIC(t *testing.T) { _, certPem, keyPem := createSelfSignedCertificate(t, "example.org") echConfig, echKey := common.Must2(tls.ECHKeygenDefault("not.example.org", false)) startInstance(t, option.Options{ Inbounds: []option.LegacyInbound{ { Type: C.TypeMixed, Tag: "mixed-in", MixedOptions: option.HTTPMixedInboundOptions{ ListenOptions: option.ListenOptions{ Listen: option.NewListenAddress(netip.IPv4Unspecified()), ListenPort: clientPort, }, }, }, { Type: C.TypeTUIC, TUICOptions: option.TUICInboundOptions{ ListenOptions: option.ListenOptions{ Listen: option.NewListenAddress(netip.IPv4Unspecified()), ListenPort: serverPort, }, Users: []option.TUICUser{{ UUID: uuid.Nil.String(), }}, InboundTLSOptionsContainer: option.InboundTLSOptionsContainer{ TLS: &option.InboundTLSOptions{ Enabled: true, ServerName: "example.org", CertificatePath: certPem, KeyPath: keyPem, ECH: &option.InboundECHOptions{ Enabled: true, Key: []string{echKey}, }, }, }, }, }, }, LegacyOutbounds: []option.LegacyOutbound{ { Type: C.TypeDirect, }, { Type: C.TypeTUIC, Tag: "tuic-out", TUICOptions: option.TUICOutboundOptions{ ServerOptions: option.ServerOptions{ Server: "127.0.0.1", ServerPort: serverPort, }, UUID: uuid.Nil.String(), OutboundTLSOptionsContainer: option.OutboundTLSOptionsContainer{ TLS: &option.OutboundTLSOptions{ Enabled: true, ServerName: "example.org", CertificatePath: certPem, ECH: &option.OutboundECHOptions{ Enabled: true, Config: []string{echConfig}, }, }, }, }, }, }, Route: &option.RouteOptions{ Rules: []option.Rule{ { Type: C.RuleTypeDefault, DefaultOptions: option.DefaultRule{ RawDefaultRule: option.RawDefaultRule{ Inbound: []string{"mixed-in"}, }, RuleAction: option.RuleAction{ Action: C.RuleActionTypeRoute, RouteOptions: option.RouteActionOptions{ Outbound: "tuic-out", }, }, }, }, }, }, }) testSuitLargeUDP(t, clientPort, testPort) } func TestECHHysteria2(t *testing.T) { _, certPem, keyPem := createSelfSignedCertificate(t, "example.org") echConfig, echKey := common.Must2(tls.ECHKeygenDefault("not.example.org", false)) startInstance(t, option.Options{ Inbounds: []option.LegacyInbound{ { Type: C.TypeMixed, Tag: "mixed-in", MixedOptions: option.HTTPMixedInboundOptions{ ListenOptions: option.ListenOptions{ Listen: option.NewListenAddress(netip.IPv4Unspecified()), ListenPort: clientPort, }, }, }, { Type: C.TypeHysteria2, Hysteria2Options: option.Hysteria2InboundOptions{ ListenOptions: option.ListenOptions{ Listen: option.NewListenAddress(netip.IPv4Unspecified()), ListenPort: serverPort, }, Users: []option.Hysteria2User{{ Password: "password", }}, InboundTLSOptionsContainer: option.InboundTLSOptionsContainer{ TLS: &option.InboundTLSOptions{ Enabled: true, ServerName: "example.org", CertificatePath: certPem, KeyPath: keyPem, ECH: &option.InboundECHOptions{ Enabled: true, Key: []string{echKey}, }, }, }, }, }, }, LegacyOutbounds: []option.LegacyOutbound{ { Type: C.TypeDirect, }, { Type: C.TypeHysteria2, Tag: "hy2-out", Hysteria2Options: option.Hysteria2OutboundOptions{ ServerOptions: option.ServerOptions{ Server: "127.0.0.1", ServerPort: serverPort, }, Password: "password", OutboundTLSOptionsContainer: option.OutboundTLSOptionsContainer{ TLS: &option.OutboundTLSOptions{ Enabled: true, ServerName: "example.org", CertificatePath: certPem, ECH: &option.OutboundECHOptions{ Enabled: true, Config: []string{echConfig}, }, }, }, }, }, }, Route: &option.RouteOptions{ Rules: []option.Rule{ { Type: C.RuleTypeDefault, DefaultOptions: option.DefaultRule{ RawDefaultRule: option.RawDefaultRule{ Inbound: []string{"mixed-in"}, }, RuleAction: option.RuleAction{ Action: C.RuleActionTypeRoute, RouteOptions: option.RouteActionOptions{ Outbound: "hy2-out", }, }, }, }, }, }, }) testSuitLargeUDP(t, clientPort, testPort) }