mirror of
https://github.com/XTLS/Xray-core.git
synced 2026-10-13 19:14:02 +00:00
Compare commits
14
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
dd3ced5894 | ||
|
|
e77250a40e | ||
|
|
a659344f67 | ||
|
|
36b75894c0 | ||
|
|
dc70f480ae | ||
|
|
a2f6a4b154 | ||
|
|
622207bb96 | ||
|
|
6bc4d4a3e8 | ||
|
|
e33aad40d0 | ||
|
|
105d4547f4 | ||
|
|
1b28c19333 | ||
|
|
d39965f20d | ||
|
|
81bcfcae77 | ||
|
|
e645ed69aa |
@@ -162,7 +162,7 @@ func (d *DefaultDispatcher) getLink(ctx context.Context) (*transport.Link, *tran
|
|||||||
p := d.policy.ForLevel(user.Level)
|
p := d.policy.ForLevel(user.Level)
|
||||||
if p.Stats.UserUplink {
|
if p.Stats.UserUplink {
|
||||||
name := "user>>>" + user.Email + ">>>traffic>>>uplink"
|
name := "user>>>" + user.Email + ">>>traffic>>>uplink"
|
||||||
if c, _ := d.stats.GetOrRegisterCounter(name); c != nil {
|
if c, _ := stats.GetOrRegisterCounter(d.stats, name); c != nil {
|
||||||
inboundLink.Writer = &SizeStatWriter{
|
inboundLink.Writer = &SizeStatWriter{
|
||||||
Counter: c,
|
Counter: c,
|
||||||
Writer: inboundLink.Writer,
|
Writer: inboundLink.Writer,
|
||||||
@@ -171,7 +171,7 @@ func (d *DefaultDispatcher) getLink(ctx context.Context) (*transport.Link, *tran
|
|||||||
}
|
}
|
||||||
if p.Stats.UserDownlink {
|
if p.Stats.UserDownlink {
|
||||||
name := "user>>>" + user.Email + ">>>traffic>>>downlink"
|
name := "user>>>" + user.Email + ">>>traffic>>>downlink"
|
||||||
if c, _ := d.stats.GetOrRegisterCounter(name); c != nil {
|
if c, _ := stats.GetOrRegisterCounter(d.stats, name); c != nil {
|
||||||
outboundLink.Writer = &SizeStatWriter{
|
outboundLink.Writer = &SizeStatWriter{
|
||||||
Counter: c,
|
Counter: c,
|
||||||
Writer: outboundLink.Writer,
|
Writer: outboundLink.Writer,
|
||||||
@@ -200,13 +200,13 @@ func WrapLink(ctx context.Context, policyManager policy.Manager, statsManager st
|
|||||||
p := policyManager.ForLevel(user.Level)
|
p := policyManager.ForLevel(user.Level)
|
||||||
if p.Stats.UserUplink {
|
if p.Stats.UserUplink {
|
||||||
name := "user>>>" + user.Email + ">>>traffic>>>uplink"
|
name := "user>>>" + user.Email + ">>>traffic>>>uplink"
|
||||||
if c, _ := statsManager.GetOrRegisterCounter(name); c != nil {
|
if c, _ := stats.GetOrRegisterCounter(statsManager, name); c != nil {
|
||||||
link.Reader.(*buf.TimeoutWrapperReader).Counter = c
|
link.Reader.(*buf.TimeoutWrapperReader).Counter = c
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if p.Stats.UserDownlink {
|
if p.Stats.UserDownlink {
|
||||||
name := "user>>>" + user.Email + ">>>traffic>>>downlink"
|
name := "user>>>" + user.Email + ">>>traffic>>>downlink"
|
||||||
if c, _ := statsManager.GetOrRegisterCounter(name); c != nil {
|
if c, _ := stats.GetOrRegisterCounter(statsManager, name); c != nil {
|
||||||
link.Writer = &SizeStatWriter{
|
link.Writer = &SizeStatWriter{
|
||||||
Counter: c,
|
Counter: c,
|
||||||
Writer: link.Writer,
|
Writer: link.Writer,
|
||||||
@@ -223,7 +223,7 @@ func WrapLink(ctx context.Context, policyManager policy.Manager, statsManager st
|
|||||||
|
|
||||||
func trackOnlineIP(ctx context.Context, sm stats.Manager, email, ip string) {
|
func trackOnlineIP(ctx context.Context, sm stats.Manager, email, ip string) {
|
||||||
name := "user>>>" + email + ">>>online"
|
name := "user>>>" + email + ">>>online"
|
||||||
if om, _ := sm.GetOrRegisterOnlineMap(name); om != nil {
|
if om, _ := stats.GetOrRegisterOnlineMap(sm, name); om != nil {
|
||||||
om.AddIP(ip)
|
om.AddIP(ip)
|
||||||
context.AfterFunc(ctx, func() { om.RemoveIP(ip) })
|
context.AfterFunc(ctx, func() { om.RemoveIP(ip) })
|
||||||
}
|
}
|
||||||
|
|||||||
+11
-22
@@ -330,7 +330,6 @@ type SenderConfig struct {
|
|||||||
// Send traffic through the given IP. Only IP is allowed.
|
// Send traffic through the given IP. Only IP is allowed.
|
||||||
Via *net.IPOrDomain `protobuf:"bytes,1,opt,name=via,proto3" json:"via,omitempty"`
|
Via *net.IPOrDomain `protobuf:"bytes,1,opt,name=via,proto3" json:"via,omitempty"`
|
||||||
StreamSettings *internet.StreamConfig `protobuf:"bytes,2,opt,name=stream_settings,json=streamSettings,proto3" json:"stream_settings,omitempty"`
|
StreamSettings *internet.StreamConfig `protobuf:"bytes,2,opt,name=stream_settings,json=streamSettings,proto3" json:"stream_settings,omitempty"`
|
||||||
ProxySettings *internet.ProxyConfig `protobuf:"bytes,3,opt,name=proxy_settings,json=proxySettings,proto3" json:"proxy_settings,omitempty"`
|
|
||||||
MultiplexSettings *MultiplexingConfig `protobuf:"bytes,4,opt,name=multiplex_settings,json=multiplexSettings,proto3" json:"multiplex_settings,omitempty"`
|
MultiplexSettings *MultiplexingConfig `protobuf:"bytes,4,opt,name=multiplex_settings,json=multiplexSettings,proto3" json:"multiplex_settings,omitempty"`
|
||||||
ViaCidr string `protobuf:"bytes,5,opt,name=via_cidr,json=viaCidr,proto3" json:"via_cidr,omitempty"`
|
ViaCidr string `protobuf:"bytes,5,opt,name=via_cidr,json=viaCidr,proto3" json:"via_cidr,omitempty"`
|
||||||
TargetStrategy internet.DomainStrategy `protobuf:"varint,6,opt,name=target_strategy,json=targetStrategy,proto3,enum=xray.transport.internet.DomainStrategy" json:"target_strategy,omitempty"`
|
TargetStrategy internet.DomainStrategy `protobuf:"varint,6,opt,name=target_strategy,json=targetStrategy,proto3,enum=xray.transport.internet.DomainStrategy" json:"target_strategy,omitempty"`
|
||||||
@@ -382,13 +381,6 @@ func (x *SenderConfig) GetStreamSettings() *internet.StreamConfig {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (x *SenderConfig) GetProxySettings() *internet.ProxyConfig {
|
|
||||||
if x != nil {
|
|
||||||
return x.ProxySettings
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (x *SenderConfig) GetMultiplexSettings() *MultiplexingConfig {
|
func (x *SenderConfig) GetMultiplexSettings() *MultiplexingConfig {
|
||||||
if x != nil {
|
if x != nil {
|
||||||
return x.MultiplexSettings
|
return x.MultiplexSettings
|
||||||
@@ -506,14 +498,13 @@ const file_app_proxyman_config_proto_rawDesc = "" +
|
|||||||
"\x03tag\x18\x01 \x01(\tR\x03tag\x12M\n" +
|
"\x03tag\x18\x01 \x01(\tR\x03tag\x12M\n" +
|
||||||
"\x11receiver_settings\x18\x02 \x01(\v2 .xray.common.serial.TypedMessageR\x10receiverSettings\x12G\n" +
|
"\x11receiver_settings\x18\x02 \x01(\v2 .xray.common.serial.TypedMessageR\x10receiverSettings\x12G\n" +
|
||||||
"\x0eproxy_settings\x18\x03 \x01(\v2 .xray.common.serial.TypedMessageR\rproxySettings\"\x10\n" +
|
"\x0eproxy_settings\x18\x03 \x01(\v2 .xray.common.serial.TypedMessageR\rproxySettings\"\x10\n" +
|
||||||
"\x0eOutboundConfig\"\x9d\x03\n" +
|
"\x0eOutboundConfig\"\xd6\x02\n" +
|
||||||
"\fSenderConfig\x12-\n" +
|
"\fSenderConfig\x12-\n" +
|
||||||
"\x03via\x18\x01 \x01(\v2\x1b.xray.common.net.IPOrDomainR\x03via\x12N\n" +
|
"\x03via\x18\x01 \x01(\v2\x1b.xray.common.net.IPOrDomainR\x03via\x12N\n" +
|
||||||
"\x0fstream_settings\x18\x02 \x01(\v2%.xray.transport.internet.StreamConfigR\x0estreamSettings\x12K\n" +
|
"\x0fstream_settings\x18\x02 \x01(\v2%.xray.transport.internet.StreamConfigR\x0estreamSettings\x12T\n" +
|
||||||
"\x0eproxy_settings\x18\x03 \x01(\v2$.xray.transport.internet.ProxyConfigR\rproxySettings\x12T\n" +
|
|
||||||
"\x12multiplex_settings\x18\x04 \x01(\v2%.xray.app.proxyman.MultiplexingConfigR\x11multiplexSettings\x12\x19\n" +
|
"\x12multiplex_settings\x18\x04 \x01(\v2%.xray.app.proxyman.MultiplexingConfigR\x11multiplexSettings\x12\x19\n" +
|
||||||
"\bvia_cidr\x18\x05 \x01(\tR\aviaCidr\x12P\n" +
|
"\bvia_cidr\x18\x05 \x01(\tR\aviaCidr\x12P\n" +
|
||||||
"\x0ftarget_strategy\x18\x06 \x01(\x0e2'.xray.transport.internet.DomainStrategyR\x0etargetStrategy\"\xa4\x01\n" +
|
"\x0ftarget_strategy\x18\x06 \x01(\x0e2'.xray.transport.internet.DomainStrategyR\x0etargetStrategyJ\x04\b\x03\x10\x04\"\xa4\x01\n" +
|
||||||
"\x12MultiplexingConfig\x12\x18\n" +
|
"\x12MultiplexingConfig\x12\x18\n" +
|
||||||
"\aenabled\x18\x01 \x01(\bR\aenabled\x12 \n" +
|
"\aenabled\x18\x01 \x01(\bR\aenabled\x12 \n" +
|
||||||
"\vconcurrency\x18\x02 \x01(\x05R\vconcurrency\x12(\n" +
|
"\vconcurrency\x18\x02 \x01(\x05R\vconcurrency\x12(\n" +
|
||||||
@@ -548,8 +539,7 @@ var file_app_proxyman_config_proto_goTypes = []any{
|
|||||||
(*net.IPOrDomain)(nil), // 10: xray.common.net.IPOrDomain
|
(*net.IPOrDomain)(nil), // 10: xray.common.net.IPOrDomain
|
||||||
(*internet.StreamConfig)(nil), // 11: xray.transport.internet.StreamConfig
|
(*internet.StreamConfig)(nil), // 11: xray.transport.internet.StreamConfig
|
||||||
(*serial.TypedMessage)(nil), // 12: xray.common.serial.TypedMessage
|
(*serial.TypedMessage)(nil), // 12: xray.common.serial.TypedMessage
|
||||||
(*internet.ProxyConfig)(nil), // 13: xray.transport.internet.ProxyConfig
|
(internet.DomainStrategy)(0), // 13: xray.transport.internet.DomainStrategy
|
||||||
(internet.DomainStrategy)(0), // 14: xray.transport.internet.DomainStrategy
|
|
||||||
}
|
}
|
||||||
var file_app_proxyman_config_proto_depIdxs = []int32{
|
var file_app_proxyman_config_proto_depIdxs = []int32{
|
||||||
7, // 0: xray.app.proxyman.SniffingConfig.domains_excluded:type_name -> xray.common.geodata.DomainRule
|
7, // 0: xray.app.proxyman.SniffingConfig.domains_excluded:type_name -> xray.common.geodata.DomainRule
|
||||||
@@ -562,14 +552,13 @@ var file_app_proxyman_config_proto_depIdxs = []int32{
|
|||||||
12, // 7: xray.app.proxyman.InboundHandlerConfig.proxy_settings:type_name -> xray.common.serial.TypedMessage
|
12, // 7: xray.app.proxyman.InboundHandlerConfig.proxy_settings:type_name -> xray.common.serial.TypedMessage
|
||||||
10, // 8: xray.app.proxyman.SenderConfig.via:type_name -> xray.common.net.IPOrDomain
|
10, // 8: xray.app.proxyman.SenderConfig.via:type_name -> xray.common.net.IPOrDomain
|
||||||
11, // 9: xray.app.proxyman.SenderConfig.stream_settings:type_name -> xray.transport.internet.StreamConfig
|
11, // 9: xray.app.proxyman.SenderConfig.stream_settings:type_name -> xray.transport.internet.StreamConfig
|
||||||
13, // 10: xray.app.proxyman.SenderConfig.proxy_settings:type_name -> xray.transport.internet.ProxyConfig
|
6, // 10: xray.app.proxyman.SenderConfig.multiplex_settings:type_name -> xray.app.proxyman.MultiplexingConfig
|
||||||
6, // 11: xray.app.proxyman.SenderConfig.multiplex_settings:type_name -> xray.app.proxyman.MultiplexingConfig
|
13, // 11: xray.app.proxyman.SenderConfig.target_strategy:type_name -> xray.transport.internet.DomainStrategy
|
||||||
14, // 12: xray.app.proxyman.SenderConfig.target_strategy:type_name -> xray.transport.internet.DomainStrategy
|
12, // [12:12] is the sub-list for method output_type
|
||||||
13, // [13:13] is the sub-list for method output_type
|
12, // [12:12] is the sub-list for method input_type
|
||||||
13, // [13:13] is the sub-list for method input_type
|
12, // [12:12] is the sub-list for extension type_name
|
||||||
13, // [13:13] is the sub-list for extension type_name
|
12, // [12:12] is the sub-list for extension extendee
|
||||||
13, // [13:13] is the sub-list for extension extendee
|
0, // [0:12] is the sub-list for field type_name
|
||||||
0, // [0:13] is the sub-list for field type_name
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func init() { file_app_proxyman_config_proto_init() }
|
func init() { file_app_proxyman_config_proto_init() }
|
||||||
|
|||||||
@@ -57,7 +57,7 @@ message SenderConfig {
|
|||||||
// Send traffic through the given IP. Only IP is allowed.
|
// Send traffic through the given IP. Only IP is allowed.
|
||||||
xray.common.net.IPOrDomain via = 1;
|
xray.common.net.IPOrDomain via = 1;
|
||||||
xray.transport.internet.StreamConfig stream_settings = 2;
|
xray.transport.internet.StreamConfig stream_settings = 2;
|
||||||
xray.transport.internet.ProxyConfig proxy_settings = 3;
|
reserved 3;
|
||||||
MultiplexingConfig multiplex_settings = 4;
|
MultiplexingConfig multiplex_settings = 4;
|
||||||
string via_cidr = 5;
|
string via_cidr = 5;
|
||||||
xray.transport.internet.DomainStrategy target_strategy = 6;
|
xray.transport.internet.DomainStrategy target_strategy = 6;
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ func getStatCounter(v *core.Instance, tag string) (stats.Counter, stats.Counter)
|
|||||||
if len(tag) > 0 && policy.ForSystem().Stats.InboundUplink {
|
if len(tag) > 0 && policy.ForSystem().Stats.InboundUplink {
|
||||||
statsManager := v.GetFeature(stats.ManagerType()).(stats.Manager)
|
statsManager := v.GetFeature(stats.ManagerType()).(stats.Manager)
|
||||||
name := "inbound>>>" + tag + ">>>traffic>>>uplink"
|
name := "inbound>>>" + tag + ">>>traffic>>>uplink"
|
||||||
c, _ := statsManager.GetOrRegisterCounter(name)
|
c, _ := stats.GetOrRegisterCounter(statsManager, name)
|
||||||
if c != nil {
|
if c != nil {
|
||||||
uplinkCounter = c
|
uplinkCounter = c
|
||||||
}
|
}
|
||||||
@@ -34,7 +34,7 @@ func getStatCounter(v *core.Instance, tag string) (stats.Counter, stats.Counter)
|
|||||||
if len(tag) > 0 && policy.ForSystem().Stats.InboundDownlink {
|
if len(tag) > 0 && policy.ForSystem().Stats.InboundDownlink {
|
||||||
statsManager := v.GetFeature(stats.ManagerType()).(stats.Manager)
|
statsManager := v.GetFeature(stats.ManagerType()).(stats.Manager)
|
||||||
name := "inbound>>>" + tag + ">>>traffic>>>downlink"
|
name := "inbound>>>" + tag + ">>>traffic>>>downlink"
|
||||||
c, _ := statsManager.GetOrRegisterCounter(name)
|
c, _ := stats.GetOrRegisterCounter(statsManager, name)
|
||||||
if c != nil {
|
if c != nil {
|
||||||
downlinkCounter = c
|
downlinkCounter = c
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,7 +15,6 @@ import (
|
|||||||
"github.com/xtls/xray-core/common/errors"
|
"github.com/xtls/xray-core/common/errors"
|
||||||
"github.com/xtls/xray-core/common/mux"
|
"github.com/xtls/xray-core/common/mux"
|
||||||
"github.com/xtls/xray-core/common/net"
|
"github.com/xtls/xray-core/common/net"
|
||||||
"github.com/xtls/xray-core/common/net/cnc"
|
|
||||||
"github.com/xtls/xray-core/common/serial"
|
"github.com/xtls/xray-core/common/serial"
|
||||||
"github.com/xtls/xray-core/common/session"
|
"github.com/xtls/xray-core/common/session"
|
||||||
"github.com/xtls/xray-core/core"
|
"github.com/xtls/xray-core/core"
|
||||||
@@ -26,8 +25,6 @@ import (
|
|||||||
"github.com/xtls/xray-core/transport"
|
"github.com/xtls/xray-core/transport"
|
||||||
"github.com/xtls/xray-core/transport/internet"
|
"github.com/xtls/xray-core/transport/internet"
|
||||||
"github.com/xtls/xray-core/transport/internet/stat"
|
"github.com/xtls/xray-core/transport/internet/stat"
|
||||||
"github.com/xtls/xray-core/transport/internet/tls"
|
|
||||||
"github.com/xtls/xray-core/transport/pipe"
|
|
||||||
"google.golang.org/protobuf/proto"
|
"google.golang.org/protobuf/proto"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -39,7 +36,7 @@ func getStatCounter(v *core.Instance, tag string) (stats.Counter, stats.Counter)
|
|||||||
if len(tag) > 0 && policy.ForSystem().Stats.OutboundUplink {
|
if len(tag) > 0 && policy.ForSystem().Stats.OutboundUplink {
|
||||||
statsManager := v.GetFeature(stats.ManagerType()).(stats.Manager)
|
statsManager := v.GetFeature(stats.ManagerType()).(stats.Manager)
|
||||||
name := "outbound>>>" + tag + ">>>traffic>>>uplink"
|
name := "outbound>>>" + tag + ">>>traffic>>>uplink"
|
||||||
c, _ := statsManager.GetOrRegisterCounter(name)
|
c, _ := stats.GetOrRegisterCounter(statsManager, name)
|
||||||
if c != nil {
|
if c != nil {
|
||||||
uplinkCounter = c
|
uplinkCounter = c
|
||||||
}
|
}
|
||||||
@@ -47,7 +44,7 @@ func getStatCounter(v *core.Instance, tag string) (stats.Counter, stats.Counter)
|
|||||||
if len(tag) > 0 && policy.ForSystem().Stats.OutboundDownlink {
|
if len(tag) > 0 && policy.ForSystem().Stats.OutboundDownlink {
|
||||||
statsManager := v.GetFeature(stats.ManagerType()).(stats.Manager)
|
statsManager := v.GetFeature(stats.ManagerType()).(stats.Manager)
|
||||||
name := "outbound>>>" + tag + ">>>traffic>>>downlink"
|
name := "outbound>>>" + tag + ">>>traffic>>>downlink"
|
||||||
c, _ := statsManager.GetOrRegisterCounter(name)
|
c, _ := stats.GetOrRegisterCounter(statsManager, name)
|
||||||
if c != nil {
|
if c != nil {
|
||||||
downlinkCounter = c
|
downlinkCounter = c
|
||||||
}
|
}
|
||||||
@@ -63,7 +60,6 @@ type Handler struct {
|
|||||||
streamSettings *internet.MemoryStreamConfig
|
streamSettings *internet.MemoryStreamConfig
|
||||||
proxyConfig proto.Message
|
proxyConfig proto.Message
|
||||||
proxy proxy.Outbound
|
proxy proxy.Outbound
|
||||||
outboundManager outbound.Manager
|
|
||||||
mux *mux.ClientManager
|
mux *mux.ClientManager
|
||||||
xudp *mux.ClientManager
|
xudp *mux.ClientManager
|
||||||
udp443 string
|
udp443 string
|
||||||
@@ -77,7 +73,6 @@ func NewHandler(ctx context.Context, config *core.OutboundHandlerConfig) (outbou
|
|||||||
uplinkCounter, downlinkCounter := getStatCounter(v, config.Tag)
|
uplinkCounter, downlinkCounter := getStatCounter(v, config.Tag)
|
||||||
h := &Handler{
|
h := &Handler{
|
||||||
tag: config.Tag,
|
tag: config.Tag,
|
||||||
outboundManager: v.GetFeature(outbound.ManagerType()).(outbound.Manager),
|
|
||||||
uplinkCounter: uplinkCounter,
|
uplinkCounter: uplinkCounter,
|
||||||
downlinkCounter: downlinkCounter,
|
downlinkCounter: downlinkCounter,
|
||||||
}
|
}
|
||||||
@@ -108,7 +103,10 @@ func NewHandler(ctx context.Context, config *core.OutboundHandlerConfig) (outbou
|
|||||||
|
|
||||||
ctx = session.ContextWithFullHandler(ctx, h)
|
ctx = session.ContextWithFullHandler(ctx, h)
|
||||||
|
|
||||||
newCtx := session.ContextWithStreamSettings(ctx, h.streamSettings)
|
newCtx := ctx
|
||||||
|
if h.streamSettings != nil {
|
||||||
|
newCtx = session.ContextWithStreamSettings(newCtx, h.streamSettings)
|
||||||
|
}
|
||||||
|
|
||||||
rawProxyHandler, err := common.CreateObject(newCtx, proxyConfig)
|
rawProxyHandler, err := common.CreateObject(newCtx, proxyConfig)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -270,43 +268,10 @@ func (h *Handler) DestIpAddress() net.IP {
|
|||||||
|
|
||||||
// Dial implements internet.Dialer.
|
// Dial implements internet.Dialer.
|
||||||
func (h *Handler) Dial(ctx context.Context, dest net.Destination) (stat.Connection, error) {
|
func (h *Handler) Dial(ctx context.Context, dest net.Destination) (stat.Connection, error) {
|
||||||
if h.senderSettings != nil {
|
if h.senderSettings != nil && h.senderSettings.Via != nil {
|
||||||
|
outbounds := session.OutboundsFromContext(ctx)
|
||||||
if h.senderSettings.ProxySettings.HasTag() {
|
ob := outbounds[len(outbounds)-1]
|
||||||
|
h.SetOutboundGateway(ctx, ob)
|
||||||
tag := h.senderSettings.ProxySettings.Tag
|
|
||||||
handler := h.outboundManager.GetHandler(tag)
|
|
||||||
if handler != nil {
|
|
||||||
errors.LogDebug(ctx, "proxying to ", tag, " for dest ", dest)
|
|
||||||
outbounds := session.OutboundsFromContext(ctx)
|
|
||||||
ctx = session.ContextWithOutbounds(ctx, append(outbounds, &session.Outbound{
|
|
||||||
Target: dest,
|
|
||||||
Tag: tag,
|
|
||||||
})) // add another outbound in session ctx
|
|
||||||
opts := pipe.OptionsFromContext(ctx)
|
|
||||||
uplinkReader, uplinkWriter := pipe.New(opts...)
|
|
||||||
downlinkReader, downlinkWriter := pipe.New(opts...)
|
|
||||||
|
|
||||||
go handler.Dispatch(ctx, &transport.Link{Reader: uplinkReader, Writer: downlinkWriter})
|
|
||||||
conn := cnc.NewConnection(cnc.ConnectionInputMulti(uplinkWriter), cnc.ConnectionOutputMulti(downlinkReader))
|
|
||||||
|
|
||||||
if config := tls.ConfigFromStreamSettings(h.streamSettings); config != nil {
|
|
||||||
tlsConfig := config.GetTLSConfig(tls.WithDestination(dest))
|
|
||||||
conn = tls.Client(conn, tlsConfig)
|
|
||||||
}
|
|
||||||
|
|
||||||
return h.getStatCouterConnection(conn), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
errors.LogError(ctx, "failed to get outbound handler with tag: ", tag)
|
|
||||||
return nil, errors.New("failed to get outbound handler with tag: " + tag)
|
|
||||||
}
|
|
||||||
|
|
||||||
if h.senderSettings.Via != nil {
|
|
||||||
outbounds := session.OutboundsFromContext(ctx)
|
|
||||||
ob := outbounds[len(outbounds)-1]
|
|
||||||
h.SetOutboundGateway(ctx, ob)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
conn, err := internet.Dial(ctx, dest, h.streamSettings)
|
conn, err := internet.Dial(ctx, dest, h.streamSettings)
|
||||||
@@ -322,14 +287,14 @@ func (h *Handler) Dial(ctx context.Context, dest net.Destination) (stat.Connecti
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (h *Handler) SetOutboundGateway(ctx context.Context, ob *session.Outbound) {
|
func (h *Handler) SetOutboundGateway(ctx context.Context, ob *session.Outbound) {
|
||||||
if ob.Gateway == nil && h.senderSettings != nil && h.senderSettings.Via != nil && !h.senderSettings.ProxySettings.HasTag() && (h.streamSettings.SocketSettings == nil || len(h.streamSettings.SocketSettings.DialerProxy) == 0) {
|
if ob.Gateway == nil && h.senderSettings != nil && h.senderSettings.Via != nil &&
|
||||||
|
(h.streamSettings.SocketSettings == nil || len(h.streamSettings.SocketSettings.DialerProxy) == 0) {
|
||||||
var domain string
|
var domain string
|
||||||
addr := h.senderSettings.Via.AsAddress()
|
addr := h.senderSettings.Via.AsAddress()
|
||||||
domain = h.senderSettings.Via.GetDomain()
|
domain = h.senderSettings.Via.GetDomain()
|
||||||
switch {
|
switch {
|
||||||
case h.senderSettings.ViaCidr != "":
|
case h.senderSettings.ViaCidr != "":
|
||||||
ob.Gateway = ParseRandomIP(addr, h.senderSettings.ViaCidr)
|
ob.Gateway = ParseRandomIP(addr, h.senderSettings.ViaCidr)
|
||||||
|
|
||||||
case domain == "origin":
|
case domain == "origin":
|
||||||
if inbound := session.InboundFromContext(ctx); inbound != nil {
|
if inbound := session.InboundFromContext(ctx); inbound != nil {
|
||||||
if inbound.Local.IsValid() && inbound.Local.Address.Family().IsIP() {
|
if inbound.Local.IsValid() && inbound.Local.Address.Family().IsIP() {
|
||||||
@@ -344,12 +309,9 @@ func (h *Handler) SetOutboundGateway(ctx context.Context, ob *session.Outbound)
|
|||||||
errors.LogDebug(ctx, "use inbound source ip as sendthrough: ", inbound.Source.Address.String())
|
errors.LogDebug(ctx, "use inbound source ip as sendthrough: ", inbound.Source.Address.String())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// case addr.Family().IsDomain():
|
default: // case addr.Family().IsDomain():
|
||||||
default:
|
|
||||||
ob.Gateway = addr
|
ob.Gateway = addr
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -48,20 +48,6 @@ func (m *Manager) RegisterCounter(name string) (stats.Counter, error) {
|
|||||||
return c, nil
|
return c, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetOrRegisterCounter implements stats.Manager.
|
|
||||||
func (m *Manager) GetOrRegisterCounter(name string) (stats.Counter, error) {
|
|
||||||
m.access.Lock()
|
|
||||||
defer m.access.Unlock()
|
|
||||||
|
|
||||||
if c, found := m.counters[name]; found {
|
|
||||||
return c, nil
|
|
||||||
}
|
|
||||||
errors.LogDebug(context.Background(), "create new counter ", name)
|
|
||||||
c := new(Counter)
|
|
||||||
m.counters[name] = c
|
|
||||||
return c, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// UnregisterCounter implements stats.Manager.
|
// UnregisterCounter implements stats.Manager.
|
||||||
func (m *Manager) UnregisterCounter(name string) error {
|
func (m *Manager) UnregisterCounter(name string) error {
|
||||||
m.access.Lock()
|
m.access.Lock()
|
||||||
@@ -111,20 +97,6 @@ func (m *Manager) RegisterOnlineMap(name string) (stats.OnlineMap, error) {
|
|||||||
return om, nil
|
return om, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetOrRegisterOnlineMap implements stats.Manager.
|
|
||||||
func (m *Manager) GetOrRegisterOnlineMap(name string) (stats.OnlineMap, error) {
|
|
||||||
m.access.Lock()
|
|
||||||
defer m.access.Unlock()
|
|
||||||
|
|
||||||
if om, found := m.onlineMaps[name]; found {
|
|
||||||
return om, nil
|
|
||||||
}
|
|
||||||
errors.LogDebug(context.Background(), "create new OnlineMap ", name)
|
|
||||||
om := NewOnlineMap()
|
|
||||||
m.onlineMaps[name] = om
|
|
||||||
return om, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// UnregisterOnlineMap implements stats.Manager.
|
// UnregisterOnlineMap implements stats.Manager.
|
||||||
func (m *Manager) UnregisterOnlineMap(name string) error {
|
func (m *Manager) UnregisterOnlineMap(name string) error {
|
||||||
m.access.Lock()
|
m.access.Lock()
|
||||||
@@ -177,26 +149,6 @@ func (m *Manager) RegisterChannel(name string) (stats.Channel, error) {
|
|||||||
return c, nil
|
return c, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetOrRegisterChannel implements stats.Manager.
|
|
||||||
func (m *Manager) GetOrRegisterChannel(name string) (stats.Channel, error) {
|
|
||||||
m.access.Lock()
|
|
||||||
defer m.access.Unlock()
|
|
||||||
|
|
||||||
if c, found := m.channels[name]; found {
|
|
||||||
return c, nil
|
|
||||||
}
|
|
||||||
errors.LogDebug(context.Background(), "create new channel ", name)
|
|
||||||
c := NewChannel(&ChannelConfig{BufferSize: 64, Blocking: false})
|
|
||||||
if m.running {
|
|
||||||
// Start before publishing so no goroutine can observe an unstarted channel.
|
|
||||||
if err := c.Start(); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
m.channels[name] = c
|
|
||||||
return c, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// UnregisterChannel implements stats.Manager.
|
// UnregisterChannel implements stats.Manager.
|
||||||
func (m *Manager) UnregisterChannel(name string) error {
|
func (m *Manager) UnregisterChannel(name string) error {
|
||||||
m.access.Lock()
|
m.access.Lock()
|
||||||
|
|||||||
@@ -1,348 +0,0 @@
|
|||||||
//go:build darwin
|
|
||||||
|
|
||||||
package net
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"net"
|
|
||||||
"net/netip"
|
|
||||||
"path/filepath"
|
|
||||||
"strings"
|
|
||||||
"syscall"
|
|
||||||
"unsafe"
|
|
||||||
|
|
||||||
"golang.org/x/sys/unix"
|
|
||||||
|
|
||||||
"github.com/xtls/xray-core/common/errors"
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
|
||||||
darwinProcPIDListFDs = 1
|
|
||||||
darwinProcPIDFDSocketInfo = 3
|
|
||||||
darwinProcFDTypeSocket = 2
|
|
||||||
darwinProcFDInfoSize = 8
|
|
||||||
darwinSocketFDInfoSize = 792
|
|
||||||
darwinSocketFDInfoPSIOff = 24
|
|
||||||
darwinSocketInfoProtoOff = darwinSocketFDInfoPSIOff + 156
|
|
||||||
darwinSocketInfoFamilyOff = darwinSocketFDInfoPSIOff + 160
|
|
||||||
darwinSocketInfoKindOff = darwinSocketFDInfoPSIOff + 232
|
|
||||||
darwinSocketInfoInSockOff = darwinSocketFDInfoPSIOff + 240
|
|
||||||
darwinInSockInfoFPortOff = darwinSocketInfoInSockOff
|
|
||||||
darwinInSockInfoLPortOff = darwinSocketInfoInSockOff + 4
|
|
||||||
darwinInSockInfoVFlagOff = darwinSocketInfoInSockOff + 24
|
|
||||||
darwinInSockInfoFAddrOff = darwinSocketInfoInSockOff + 32
|
|
||||||
darwinInSockInfoLAddrOff = darwinSocketInfoInSockOff + 48
|
|
||||||
darwinInSockInfoSize = 80
|
|
||||||
darwinInSockInfoIPv4 = 0x1
|
|
||||||
darwinInSockInfoIPv6 = 0x2
|
|
||||||
darwinSockInfoIN = 1
|
|
||||||
darwinSockInfoTCP = 2
|
|
||||||
)
|
|
||||||
|
|
||||||
type darwinSocketMatchLevel int
|
|
||||||
|
|
||||||
const (
|
|
||||||
darwinSocketNoMatch darwinSocketMatchLevel = iota
|
|
||||||
darwinSocketPortMatch
|
|
||||||
darwinSocketRemoteMatch
|
|
||||||
darwinSocketLocalMatch
|
|
||||||
darwinSocketExactMatch
|
|
||||||
)
|
|
||||||
|
|
||||||
func FindProcess(network, srcIP string, srcPort uint16, destIP string, destPort uint16) (PID int, Name string, AbsolutePath string, err error) {
|
|
||||||
isLocal, err := IsLocal(net.ParseIP(srcIP))
|
|
||||||
if err != nil {
|
|
||||||
return 0, "", "", errors.New("failed to determine if address is local: ", err)
|
|
||||||
}
|
|
||||||
if !isLocal {
|
|
||||||
return 0, "", "", ErrNotLocal
|
|
||||||
}
|
|
||||||
if network != "tcp" && network != "udp" {
|
|
||||||
panic("Unsupported network type for process lookup.")
|
|
||||||
}
|
|
||||||
|
|
||||||
srcAddr, err := netip.ParseAddr(srcIP)
|
|
||||||
if err != nil {
|
|
||||||
return 0, "", "", errors.New("invalid source IP address: ", srcIP)
|
|
||||||
}
|
|
||||||
srcAddr = srcAddr.Unmap()
|
|
||||||
|
|
||||||
var dstAddr netip.Addr
|
|
||||||
hasDstAddr := false
|
|
||||||
if destIP != "" && destPort != 0 {
|
|
||||||
dstAddr, err = netip.ParseAddr(destIP)
|
|
||||||
if err != nil {
|
|
||||||
return 0, "", "", errors.New("invalid destination IP address: ", destIP)
|
|
||||||
}
|
|
||||||
dstAddr = dstAddr.Unmap()
|
|
||||||
hasDstAddr = true
|
|
||||||
}
|
|
||||||
|
|
||||||
processes, err := unix.SysctlKinfoProcSlice("kern.proc.all")
|
|
||||||
if err != nil {
|
|
||||||
return 0, "", "", errors.New("failed to list processes").Base(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
var bestPID int32
|
|
||||||
bestLevel := darwinSocketNoMatch
|
|
||||||
ambiguousBest := false
|
|
||||||
|
|
||||||
for _, process := range processes {
|
|
||||||
pid := process.Proc.P_pid
|
|
||||||
if pid <= 0 {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
matchLevel, err := darwinProcessSocketMatchLevel(pid, network, srcAddr, srcPort, dstAddr, destPort, hasDstAddr)
|
|
||||||
if err != nil || matchLevel == darwinSocketNoMatch {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if matchLevel == darwinSocketExactMatch {
|
|
||||||
bestPID = pid
|
|
||||||
bestLevel = matchLevel
|
|
||||||
ambiguousBest = false
|
|
||||||
break
|
|
||||||
}
|
|
||||||
if matchLevel > bestLevel {
|
|
||||||
bestPID = pid
|
|
||||||
bestLevel = matchLevel
|
|
||||||
ambiguousBest = false
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if matchLevel == bestLevel {
|
|
||||||
ambiguousBest = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if bestLevel == darwinSocketNoMatch {
|
|
||||||
return 0, "", "", errors.New("process not found for ", network, " connection from ", srcIP, ":", srcPort, " to ", destIP, ":", destPort)
|
|
||||||
}
|
|
||||||
if ambiguousBest {
|
|
||||||
return 0, "", "", errors.New("ambiguous process match for ", network, " connection from ", srcIP, ":", srcPort, " to ", destIP, ":", destPort)
|
|
||||||
}
|
|
||||||
|
|
||||||
absPath, err := darwinProcessPath(bestPID)
|
|
||||||
if err != nil {
|
|
||||||
return 0, "", "", errors.New("could not get process path for PID ", bestPID, ": ", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
absPath = filepath.ToSlash(absPath)
|
|
||||||
return int(bestPID), filepath.Base(absPath), absPath, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func darwinProcessSocketMatchLevel(pid int32, network string, srcAddr netip.Addr, srcPort uint16, dstAddr netip.Addr, dstPort uint16, hasDstAddr bool) (darwinSocketMatchLevel, error) {
|
|
||||||
fds, err := darwinProcessFDs(pid)
|
|
||||||
if err != nil {
|
|
||||||
return darwinSocketNoMatch, err
|
|
||||||
}
|
|
||||||
|
|
||||||
bestLevel := darwinSocketNoMatch
|
|
||||||
info := make([]byte, darwinSocketFDInfoSize)
|
|
||||||
for fd := 0; fd+darwinProcFDInfoSize <= len(fds); fd += darwinProcFDInfoSize {
|
|
||||||
fdNumber := int32(darwinReadNativeUint32(fds[fd : fd+4]))
|
|
||||||
fdType := darwinReadNativeUint32(fds[fd+4 : fd+8])
|
|
||||||
if fdType != darwinProcFDTypeSocket {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
n, err := darwinProcPIDFDInfo(pid, fdNumber, darwinProcPIDFDSocketInfo, info)
|
|
||||||
if err != nil || n < darwinSocketInfoInSockOff+darwinInSockInfoSize {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
level := darwinSocketInfoMatchLevel(info[:n], network, srcAddr, srcPort, dstAddr, dstPort, hasDstAddr)
|
|
||||||
if level == darwinSocketExactMatch {
|
|
||||||
return level, nil
|
|
||||||
}
|
|
||||||
if level > bestLevel {
|
|
||||||
bestLevel = level
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return bestLevel, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func darwinProcessFDs(pid int32) ([]byte, error) {
|
|
||||||
n, err := darwinProcPIDInfo(pid, darwinProcPIDListFDs, 0, nil)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
if n <= 0 {
|
|
||||||
return nil, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
buf := make([]byte, n)
|
|
||||||
n, err = darwinProcPIDInfo(pid, darwinProcPIDListFDs, 0, buf)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return buf[:n], nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func darwinSocketInfoMatchLevel(info []byte, network string, srcAddr netip.Addr, srcPort uint16, dstAddr netip.Addr, dstPort uint16, hasDstAddr bool) darwinSocketMatchLevel {
|
|
||||||
protocol := int(darwinReadNativeUint32(info[darwinSocketInfoProtoOff : darwinSocketInfoProtoOff+4]))
|
|
||||||
family := int(darwinReadNativeUint32(info[darwinSocketInfoFamilyOff : darwinSocketInfoFamilyOff+4]))
|
|
||||||
kind := int(darwinReadNativeUint32(info[darwinSocketInfoKindOff : darwinSocketInfoKindOff+4]))
|
|
||||||
|
|
||||||
switch network {
|
|
||||||
case "tcp":
|
|
||||||
if protocol != unix.IPPROTO_TCP || kind != darwinSockInfoTCP {
|
|
||||||
return darwinSocketNoMatch
|
|
||||||
}
|
|
||||||
case "udp":
|
|
||||||
if protocol != unix.IPPROTO_UDP || kind != darwinSockInfoIN {
|
|
||||||
return darwinSocketNoMatch
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
return darwinSocketNoMatch
|
|
||||||
}
|
|
||||||
|
|
||||||
vflag := info[darwinInSockInfoVFlagOff]
|
|
||||||
if srcAddr.Is4() {
|
|
||||||
if family != unix.AF_INET || vflag&darwinInSockInfoIPv4 == 0 {
|
|
||||||
return darwinSocketNoMatch
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if family != unix.AF_INET6 || vflag&darwinInSockInfoIPv6 == 0 {
|
|
||||||
return darwinSocketNoMatch
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
localPort := int32(darwinReadNativeUint32(info[darwinInSockInfoLPortOff : darwinInSockInfoLPortOff+4]))
|
|
||||||
if !darwinPortMatches(localPort, srcPort) {
|
|
||||||
return darwinSocketNoMatch
|
|
||||||
}
|
|
||||||
localAddrMatches := darwinAddrMatchesOrUnspecified(info[darwinInSockInfoLAddrOff:darwinInSockInfoLAddrOff+16], srcAddr)
|
|
||||||
|
|
||||||
foreignAddrRaw := info[darwinInSockInfoFAddrOff : darwinInSockInfoFAddrOff+16]
|
|
||||||
foreignPort := int32(darwinReadNativeUint32(info[darwinInSockInfoFPortOff : darwinInSockInfoFPortOff+4]))
|
|
||||||
|
|
||||||
if !hasDstAddr {
|
|
||||||
if localAddrMatches {
|
|
||||||
return darwinSocketExactMatch
|
|
||||||
}
|
|
||||||
return darwinSocketNoMatch
|
|
||||||
}
|
|
||||||
|
|
||||||
remoteMatches := darwinPortMatches(foreignPort, dstPort) && darwinAddrMatches(foreignAddrRaw, dstAddr)
|
|
||||||
if network == "udp" && darwinEndpointIsZero(foreignAddrRaw, foreignPort) && localAddrMatches {
|
|
||||||
return darwinSocketExactMatch
|
|
||||||
}
|
|
||||||
switch {
|
|
||||||
case localAddrMatches && remoteMatches:
|
|
||||||
return darwinSocketExactMatch
|
|
||||||
case localAddrMatches:
|
|
||||||
return darwinSocketLocalMatch
|
|
||||||
case remoteMatches:
|
|
||||||
return darwinSocketRemoteMatch
|
|
||||||
default:
|
|
||||||
return darwinSocketPortMatch
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func darwinPortMatches(value int32, port uint16) bool {
|
|
||||||
raw := uint16(value)
|
|
||||||
return raw == port || darwinNtohs(raw) == port
|
|
||||||
}
|
|
||||||
|
|
||||||
func darwinNtohs(value uint16) uint16 {
|
|
||||||
return value<<8 | value>>8
|
|
||||||
}
|
|
||||||
|
|
||||||
func darwinAddrMatches(raw []byte, addr netip.Addr) bool {
|
|
||||||
if addr.Is4() {
|
|
||||||
ip := addr.As4()
|
|
||||||
return bytes.Equal(raw[12:16], ip[:])
|
|
||||||
}
|
|
||||||
ip := addr.As16()
|
|
||||||
return bytes.Equal(raw, ip[:])
|
|
||||||
}
|
|
||||||
|
|
||||||
func darwinAddrMatchesOrUnspecified(raw []byte, addr netip.Addr) bool {
|
|
||||||
if darwinAddrMatches(raw, addr) {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
if addr.Is4() {
|
|
||||||
return darwinBytesAreZero(raw[12:16])
|
|
||||||
}
|
|
||||||
return darwinBytesAreZero(raw)
|
|
||||||
}
|
|
||||||
|
|
||||||
func darwinEndpointIsZero(rawAddr []byte, port int32) bool {
|
|
||||||
return uint32(port) == 0 && darwinBytesAreZero(rawAddr)
|
|
||||||
}
|
|
||||||
|
|
||||||
func darwinBytesAreZero(raw []byte) bool {
|
|
||||||
for _, value := range raw {
|
|
||||||
if value != 0 {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
func darwinReadNativeUint32(b []byte) uint32 {
|
|
||||||
return *(*uint32)(unsafe.Pointer(&b[0]))
|
|
||||||
}
|
|
||||||
|
|
||||||
func darwinProcessPath(pid int32) (string, error) {
|
|
||||||
buf := make([]byte, unix.PathMax)
|
|
||||||
n, err := darwinProcPIDPath(pid, buf)
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
if n <= 0 {
|
|
||||||
return "", errors.New("empty process path")
|
|
||||||
}
|
|
||||||
return strings.TrimRight(string(buf[:n]), "\x00"), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func darwinProcPIDInfo(pid int32, flavor int, arg uint64, buf []byte) (int, error) {
|
|
||||||
var ptr unsafe.Pointer
|
|
||||||
if len(buf) > 0 {
|
|
||||||
ptr = unsafe.Pointer(&buf[0])
|
|
||||||
}
|
|
||||||
|
|
||||||
r0, _, errno := syscall_syscall6(libc_proc_pidinfo_trampoline_addr, uintptr(pid), uintptr(flavor), uintptr(arg), uintptr(ptr), uintptr(len(buf)), 0)
|
|
||||||
if errno != 0 {
|
|
||||||
return 0, errno
|
|
||||||
}
|
|
||||||
return int(r0), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func darwinProcPIDFDInfo(pid int32, fd int32, flavor int, buf []byte) (int, error) {
|
|
||||||
var ptr unsafe.Pointer
|
|
||||||
if len(buf) > 0 {
|
|
||||||
ptr = unsafe.Pointer(&buf[0])
|
|
||||||
}
|
|
||||||
|
|
||||||
r0, _, errno := syscall_syscall6(libc_proc_pidfdinfo_trampoline_addr, uintptr(pid), uintptr(fd), uintptr(flavor), uintptr(ptr), uintptr(len(buf)), 0)
|
|
||||||
if errno != 0 {
|
|
||||||
return 0, errno
|
|
||||||
}
|
|
||||||
return int(r0), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func darwinProcPIDPath(pid int32, buf []byte) (int, error) {
|
|
||||||
r0, _, errno := syscall_syscall6(libc_proc_pidpath_trampoline_addr, uintptr(pid), uintptr(unsafe.Pointer(&buf[0])), uintptr(len(buf)), 0, 0, 0)
|
|
||||||
if errno != 0 {
|
|
||||||
return 0, errno
|
|
||||||
}
|
|
||||||
return int(r0), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
var libc_proc_pidinfo_trampoline_addr uintptr
|
|
||||||
|
|
||||||
//go:cgo_import_dynamic libc_proc_pidinfo proc_pidinfo "/usr/lib/libproc.dylib"
|
|
||||||
|
|
||||||
var libc_proc_pidfdinfo_trampoline_addr uintptr
|
|
||||||
|
|
||||||
//go:cgo_import_dynamic libc_proc_pidfdinfo proc_pidfdinfo "/usr/lib/libproc.dylib"
|
|
||||||
|
|
||||||
var libc_proc_pidpath_trampoline_addr uintptr
|
|
||||||
|
|
||||||
//go:cgo_import_dynamic libc_proc_pidpath proc_pidpath "/usr/lib/libproc.dylib"
|
|
||||||
|
|
||||||
// Implemented in the runtime package (runtime/sys_darwin.go).
|
|
||||||
func syscall_syscall6(fn, a1, a2, a3, a4, a5, a6 uintptr) (r1, r2 uintptr, err syscall.Errno)
|
|
||||||
|
|
||||||
//go:linkname syscall_syscall6 syscall.syscall6
|
|
||||||
@@ -1,18 +0,0 @@
|
|||||||
//go:build darwin
|
|
||||||
|
|
||||||
#include "textflag.h"
|
|
||||||
|
|
||||||
TEXT libc_proc_pidinfo_trampoline<>(SB),NOSPLIT,$0-0
|
|
||||||
JMP libc_proc_pidinfo(SB)
|
|
||||||
GLOBL ·libc_proc_pidinfo_trampoline_addr(SB), RODATA, $8
|
|
||||||
DATA ·libc_proc_pidinfo_trampoline_addr(SB)/8, $libc_proc_pidinfo_trampoline<>(SB)
|
|
||||||
|
|
||||||
TEXT libc_proc_pidfdinfo_trampoline<>(SB),NOSPLIT,$0-0
|
|
||||||
JMP libc_proc_pidfdinfo(SB)
|
|
||||||
GLOBL ·libc_proc_pidfdinfo_trampoline_addr(SB), RODATA, $8
|
|
||||||
DATA ·libc_proc_pidfdinfo_trampoline_addr(SB)/8, $libc_proc_pidfdinfo_trampoline<>(SB)
|
|
||||||
|
|
||||||
TEXT libc_proc_pidpath_trampoline<>(SB),NOSPLIT,$0-0
|
|
||||||
JMP libc_proc_pidpath(SB)
|
|
||||||
GLOBL ·libc_proc_pidpath_trampoline_addr(SB), RODATA, $8
|
|
||||||
DATA ·libc_proc_pidpath_trampoline_addr(SB)/8, $libc_proc_pidpath_trampoline<>(SB)
|
|
||||||
@@ -1,293 +0,0 @@
|
|||||||
//go:build darwin
|
|
||||||
|
|
||||||
package net
|
|
||||||
|
|
||||||
import (
|
|
||||||
stdnet "net"
|
|
||||||
"net/netip"
|
|
||||||
"os"
|
|
||||||
"testing"
|
|
||||||
"time"
|
|
||||||
"unsafe"
|
|
||||||
|
|
||||||
"golang.org/x/sys/unix"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestFindProcessDarwinTCP(t *testing.T) {
|
|
||||||
listener, err := stdnet.Listen("tcp", "127.0.0.1:0")
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
defer listener.Close()
|
|
||||||
|
|
||||||
accepted := make(chan stdnet.Conn, 1)
|
|
||||||
go func() {
|
|
||||||
conn, err := listener.Accept()
|
|
||||||
if err == nil {
|
|
||||||
accepted <- conn
|
|
||||||
return
|
|
||||||
}
|
|
||||||
close(accepted)
|
|
||||||
}()
|
|
||||||
|
|
||||||
conn, err := stdnet.Dial("tcp", listener.Addr().String())
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
defer conn.Close()
|
|
||||||
|
|
||||||
serverConn := <-accepted
|
|
||||||
if serverConn == nil {
|
|
||||||
t.Fatal("server did not accept tcp connection")
|
|
||||||
}
|
|
||||||
defer serverConn.Close()
|
|
||||||
|
|
||||||
local := conn.LocalAddr().(*stdnet.TCPAddr)
|
|
||||||
remote := conn.RemoteAddr().(*stdnet.TCPAddr)
|
|
||||||
|
|
||||||
pid, name, path, err := FindProcess("tcp", local.IP.String(), uint16(local.Port), remote.IP.String(), uint16(remote.Port))
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
assertCurrentProcess(t, pid, name, path)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestFindProcessDarwinUDP(t *testing.T) {
|
|
||||||
conn, err := stdnet.ListenUDP("udp", &stdnet.UDPAddr{IP: stdnet.ParseIP("127.0.0.1")})
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
defer conn.Close()
|
|
||||||
|
|
||||||
local := conn.LocalAddr().(*stdnet.UDPAddr)
|
|
||||||
|
|
||||||
pid, name, path, err := FindProcess("udp", local.IP.String(), uint16(local.Port), "", 0)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
assertCurrentProcess(t, pid, name, path)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestFindProcessDarwinNonLocal(t *testing.T) {
|
|
||||||
_, _, _, err := FindProcess("tcp", "203.0.113.1", 80, "", 0)
|
|
||||||
if err != ErrNotLocal {
|
|
||||||
t.Fatalf("expected ErrNotLocal, got %v", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestFindProcessDarwinUnsupportedNetwork(t *testing.T) {
|
|
||||||
defer func() {
|
|
||||||
if recover() == nil {
|
|
||||||
t.Fatal("expected panic")
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
|
|
||||||
_, _, _, _ = FindProcess("icmp", "127.0.0.1", 0, "", 0)
|
|
||||||
}
|
|
||||||
|
|
||||||
func assertCurrentProcess(t *testing.T, pid int, name string, path string) {
|
|
||||||
t.Helper()
|
|
||||||
|
|
||||||
if pid != os.Getpid() {
|
|
||||||
t.Fatalf("expected pid %d, got %d (%s, %s)", os.Getpid(), pid, name, path)
|
|
||||||
}
|
|
||||||
|
|
||||||
executable, err := os.Executable()
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
if path == "" || name == "" {
|
|
||||||
t.Fatalf("expected process path and name, got name=%q path=%q", name, path)
|
|
||||||
}
|
|
||||||
if sameFile(executable, path) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
t.Fatalf("expected executable %q, got %q", executable, path)
|
|
||||||
}
|
|
||||||
|
|
||||||
func sameFile(left string, right string) bool {
|
|
||||||
leftInfo, leftErr := os.Stat(left)
|
|
||||||
rightInfo, rightErr := os.Stat(right)
|
|
||||||
if leftErr != nil || rightErr != nil {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
return os.SameFile(leftInfo, rightInfo)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestFindProcessDarwinTCPWithoutDestination(t *testing.T) {
|
|
||||||
listener, err := stdnet.Listen("tcp", "127.0.0.1:0")
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
defer listener.Close()
|
|
||||||
|
|
||||||
accepted := make(chan stdnet.Conn, 1)
|
|
||||||
go func() {
|
|
||||||
conn, err := listener.Accept()
|
|
||||||
if err == nil {
|
|
||||||
accepted <- conn
|
|
||||||
return
|
|
||||||
}
|
|
||||||
close(accepted)
|
|
||||||
}()
|
|
||||||
|
|
||||||
conn, err := stdnet.DialTimeout("tcp", listener.Addr().String(), time.Second)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
defer conn.Close()
|
|
||||||
|
|
||||||
serverConn := <-accepted
|
|
||||||
if serverConn == nil {
|
|
||||||
t.Fatal("server did not accept tcp connection")
|
|
||||||
}
|
|
||||||
defer serverConn.Close()
|
|
||||||
|
|
||||||
local := conn.LocalAddr().(*stdnet.TCPAddr)
|
|
||||||
pid, name, path, err := FindProcess("tcp", local.IP.String(), uint16(local.Port), "", 0)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
assertCurrentProcess(t, pid, name, path)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestFindProcessDarwinTCPWithDifferentDestination(t *testing.T) {
|
|
||||||
listener, err := stdnet.Listen("tcp", "127.0.0.1:0")
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
defer listener.Close()
|
|
||||||
|
|
||||||
accepted := make(chan stdnet.Conn, 1)
|
|
||||||
go func() {
|
|
||||||
conn, err := listener.Accept()
|
|
||||||
if err == nil {
|
|
||||||
accepted <- conn
|
|
||||||
return
|
|
||||||
}
|
|
||||||
close(accepted)
|
|
||||||
}()
|
|
||||||
|
|
||||||
conn, err := stdnet.DialTimeout("tcp", listener.Addr().String(), time.Second)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
defer conn.Close()
|
|
||||||
|
|
||||||
serverConn := <-accepted
|
|
||||||
if serverConn == nil {
|
|
||||||
t.Fatal("server did not accept tcp connection")
|
|
||||||
}
|
|
||||||
defer serverConn.Close()
|
|
||||||
|
|
||||||
local := conn.LocalAddr().(*stdnet.TCPAddr)
|
|
||||||
pid, name, path, err := FindProcess("tcp", local.IP.String(), uint16(local.Port), "203.0.113.10", 443)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
assertCurrentProcess(t, pid, name, path)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestDarwinSocketInfoMatchLevelFallbacks(t *testing.T) {
|
|
||||||
src := netip.MustParseAddr("198.18.0.2")
|
|
||||||
dst := netip.MustParseAddr("203.0.113.10")
|
|
||||||
otherLocal := netip.MustParseAddr("192.168.1.10")
|
|
||||||
otherRemote := netip.MustParseAddr("198.51.100.10")
|
|
||||||
unspecifiedLocal := netip.MustParseAddr("0.0.0.0")
|
|
||||||
|
|
||||||
tests := []struct {
|
|
||||||
name string
|
|
||||||
local netip.Addr
|
|
||||||
remote netip.Addr
|
|
||||||
hasDst bool
|
|
||||||
wantLevel darwinSocketMatchLevel
|
|
||||||
}{
|
|
||||||
{
|
|
||||||
name: "exact",
|
|
||||||
local: src,
|
|
||||||
remote: dst,
|
|
||||||
hasDst: true,
|
|
||||||
wantLevel: darwinSocketExactMatch,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "unspecified local with matching remote",
|
|
||||||
local: unspecifiedLocal,
|
|
||||||
remote: dst,
|
|
||||||
hasDst: true,
|
|
||||||
wantLevel: darwinSocketExactMatch,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "unspecified local without destination",
|
|
||||||
local: unspecifiedLocal,
|
|
||||||
remote: otherRemote,
|
|
||||||
hasDst: false,
|
|
||||||
wantLevel: darwinSocketExactMatch,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "local match with different remote",
|
|
||||||
local: src,
|
|
||||||
remote: otherRemote,
|
|
||||||
hasDst: true,
|
|
||||||
wantLevel: darwinSocketLocalMatch,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "remote match with different local",
|
|
||||||
local: otherLocal,
|
|
||||||
remote: dst,
|
|
||||||
hasDst: true,
|
|
||||||
wantLevel: darwinSocketRemoteMatch,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "port only with destination",
|
|
||||||
local: otherLocal,
|
|
||||||
remote: otherRemote,
|
|
||||||
hasDst: true,
|
|
||||||
wantLevel: darwinSocketPortMatch,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "different local without destination",
|
|
||||||
local: otherLocal,
|
|
||||||
remote: otherRemote,
|
|
||||||
hasDst: false,
|
|
||||||
wantLevel: darwinSocketNoMatch,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, test := range tests {
|
|
||||||
t.Run(test.name, func(t *testing.T) {
|
|
||||||
info := newDarwinSocketInfo("tcp", test.local, 12345, test.remote, 443)
|
|
||||||
level := darwinSocketInfoMatchLevel(info, "tcp", src, 12345, dst, 443, test.hasDst)
|
|
||||||
if level != test.wantLevel {
|
|
||||||
t.Fatalf("unexpected match level: got %d, want %d", level, test.wantLevel)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func newDarwinSocketInfo(network string, local netip.Addr, localPort uint16, remote netip.Addr, remotePort uint16) []byte {
|
|
||||||
info := make([]byte, darwinSocketFDInfoSize)
|
|
||||||
switch network {
|
|
||||||
case "tcp":
|
|
||||||
writeDarwinNativeUint32(info, darwinSocketInfoProtoOff, uint32(unix.IPPROTO_TCP))
|
|
||||||
writeDarwinNativeUint32(info, darwinSocketInfoKindOff, uint32(darwinSockInfoTCP))
|
|
||||||
case "udp":
|
|
||||||
writeDarwinNativeUint32(info, darwinSocketInfoProtoOff, uint32(unix.IPPROTO_UDP))
|
|
||||||
writeDarwinNativeUint32(info, darwinSocketInfoKindOff, uint32(darwinSockInfoIN))
|
|
||||||
}
|
|
||||||
writeDarwinNativeUint32(info, darwinSocketInfoFamilyOff, uint32(unix.AF_INET))
|
|
||||||
info[darwinInSockInfoVFlagOff] = darwinInSockInfoIPv4
|
|
||||||
writeDarwinNativeUint32(info, darwinInSockInfoLPortOff, uint32(localPort))
|
|
||||||
writeDarwinNativeUint32(info, darwinInSockInfoFPortOff, uint32(remotePort))
|
|
||||||
copyDarwinIPv4(info[darwinInSockInfoLAddrOff:darwinInSockInfoLAddrOff+16], local)
|
|
||||||
copyDarwinIPv4(info[darwinInSockInfoFAddrOff:darwinInSockInfoFAddrOff+16], remote)
|
|
||||||
return info
|
|
||||||
}
|
|
||||||
|
|
||||||
func writeDarwinNativeUint32(b []byte, offset int, value uint32) {
|
|
||||||
*(*uint32)(unsafe.Pointer(&b[offset])) = value
|
|
||||||
}
|
|
||||||
|
|
||||||
func copyDarwinIPv4(dst []byte, addr netip.Addr) {
|
|
||||||
ip := addr.As4()
|
|
||||||
copy(dst[12:16], ip[:])
|
|
||||||
}
|
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
//go:build !windows && !linux && !android && !darwin
|
//go:build !windows && !linux && !android
|
||||||
|
|
||||||
package net
|
package net
|
||||||
|
|
||||||
|
|||||||
@@ -70,7 +70,7 @@ type Outbound struct {
|
|||||||
Tag string
|
Tag string
|
||||||
// Name of the outbound proxy that handles the connection.
|
// Name of the outbound proxy that handles the connection.
|
||||||
Name string
|
Name string
|
||||||
// Unused. Conn is actually internet.Connection. May be nil. It is currently nil for outbound with proxySettings
|
// Unused. Conn is actually internet.Connection. May be nil.
|
||||||
Conn net.Conn
|
Conn net.Conn
|
||||||
// CanSpliceCopy is a property for this connection
|
// CanSpliceCopy is a property for this connection
|
||||||
// 1 = can, 2 = after processing protocol info should be able to, 3 = cannot
|
// 1 = can, 2 = after processing protocol info should be able to, 3 = cannot
|
||||||
|
|||||||
+30
-21
@@ -81,8 +81,6 @@ type Manager interface {
|
|||||||
|
|
||||||
// RegisterCounter registers a new counter to the manager. The identifier string must not be empty, and unique among other counters.
|
// RegisterCounter registers a new counter to the manager. The identifier string must not be empty, and unique among other counters.
|
||||||
RegisterCounter(string) (Counter, error)
|
RegisterCounter(string) (Counter, error)
|
||||||
// GetOrRegisterCounter returns the counter by its identifier, atomically creating and registering it if absent.
|
|
||||||
GetOrRegisterCounter(string) (Counter, error)
|
|
||||||
// UnregisterCounter unregisters a counter from the manager by its identifier.
|
// UnregisterCounter unregisters a counter from the manager by its identifier.
|
||||||
UnregisterCounter(string) error
|
UnregisterCounter(string) error
|
||||||
// GetCounter returns a counter by its identifier.
|
// GetCounter returns a counter by its identifier.
|
||||||
@@ -93,8 +91,6 @@ type Manager interface {
|
|||||||
|
|
||||||
// RegisterOnlineMap registers a new OnlineMap to the manager. The identifier string must not be empty, and unique among other OnlineMaps.
|
// RegisterOnlineMap registers a new OnlineMap to the manager. The identifier string must not be empty, and unique among other OnlineMaps.
|
||||||
RegisterOnlineMap(string) (OnlineMap, error)
|
RegisterOnlineMap(string) (OnlineMap, error)
|
||||||
// GetOrRegisterOnlineMap returns the OnlineMap by its identifier, atomically creating and registering it if absent.
|
|
||||||
GetOrRegisterOnlineMap(string) (OnlineMap, error)
|
|
||||||
// UnregisterOnlineMap unregisters an OnlineMap from the manager by its identifier.
|
// UnregisterOnlineMap unregisters an OnlineMap from the manager by its identifier.
|
||||||
UnregisterOnlineMap(string) error
|
UnregisterOnlineMap(string) error
|
||||||
// GetOnlineMap returns an OnlineMap by its identifier.
|
// GetOnlineMap returns an OnlineMap by its identifier.
|
||||||
@@ -105,8 +101,6 @@ type Manager interface {
|
|||||||
|
|
||||||
// RegisterChannel registers a new channel to the manager. The identifier string must not be empty, and unique among other channels.
|
// RegisterChannel registers a new channel to the manager. The identifier string must not be empty, and unique among other channels.
|
||||||
RegisterChannel(string) (Channel, error)
|
RegisterChannel(string) (Channel, error)
|
||||||
// GetOrRegisterChannel returns the channel by its identifier, atomically creating and registering it if absent.
|
|
||||||
GetOrRegisterChannel(string) (Channel, error)
|
|
||||||
// UnregisterChannel unregisters a channel from the manager by its identifier.
|
// UnregisterChannel unregisters a channel from the manager by its identifier.
|
||||||
UnregisterChannel(string) error
|
UnregisterChannel(string) error
|
||||||
// GetChannel returns a channel by its identifier.
|
// GetChannel returns a channel by its identifier.
|
||||||
@@ -116,6 +110,36 @@ type Manager interface {
|
|||||||
GetAllOnlineUsers() []string
|
GetAllOnlineUsers() []string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetOrRegisterCounter tries to get the StatCounter first. If not exist, it then tries to create a new counter.
|
||||||
|
func GetOrRegisterCounter(m Manager, name string) (Counter, error) {
|
||||||
|
counter := m.GetCounter(name)
|
||||||
|
if counter != nil {
|
||||||
|
return counter, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return m.RegisterCounter(name)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetOrRegisterOnlineMap tries to get the OnlineMap first. If not exist, it then tries to create a new OnlineMap.
|
||||||
|
func GetOrRegisterOnlineMap(m Manager, name string) (OnlineMap, error) {
|
||||||
|
onlineMap := m.GetOnlineMap(name)
|
||||||
|
if onlineMap != nil {
|
||||||
|
return onlineMap, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return m.RegisterOnlineMap(name)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetOrRegisterChannel tries to get the StatChannel first. If not exist, it then tries to create a new channel.
|
||||||
|
func GetOrRegisterChannel(m Manager, name string) (Channel, error) {
|
||||||
|
channel := m.GetChannel(name)
|
||||||
|
if channel != nil {
|
||||||
|
return channel, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return m.RegisterChannel(name)
|
||||||
|
}
|
||||||
|
|
||||||
// ManagerType returns the type of Manager interface. Can be used to implement common.HasType.
|
// ManagerType returns the type of Manager interface. Can be used to implement common.HasType.
|
||||||
//
|
//
|
||||||
// xray:api:stable
|
// xray:api:stable
|
||||||
@@ -136,11 +160,6 @@ func (NoopManager) RegisterCounter(string) (Counter, error) {
|
|||||||
return nil, errors.New("not implemented")
|
return nil, errors.New("not implemented")
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetOrRegisterCounter implements Manager.
|
|
||||||
func (NoopManager) GetOrRegisterCounter(string) (Counter, error) {
|
|
||||||
return nil, errors.New("not implemented")
|
|
||||||
}
|
|
||||||
|
|
||||||
// UnregisterCounter implements Manager.
|
// UnregisterCounter implements Manager.
|
||||||
func (NoopManager) UnregisterCounter(string) error {
|
func (NoopManager) UnregisterCounter(string) error {
|
||||||
return nil
|
return nil
|
||||||
@@ -159,11 +178,6 @@ func (NoopManager) RegisterOnlineMap(string) (OnlineMap, error) {
|
|||||||
return nil, errors.New("not implemented")
|
return nil, errors.New("not implemented")
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetOrRegisterOnlineMap implements Manager.
|
|
||||||
func (NoopManager) GetOrRegisterOnlineMap(string) (OnlineMap, error) {
|
|
||||||
return nil, errors.New("not implemented")
|
|
||||||
}
|
|
||||||
|
|
||||||
// UnregisterOnlineMap implements Manager.
|
// UnregisterOnlineMap implements Manager.
|
||||||
func (NoopManager) UnregisterOnlineMap(string) error {
|
func (NoopManager) UnregisterOnlineMap(string) error {
|
||||||
return nil
|
return nil
|
||||||
@@ -182,11 +196,6 @@ func (NoopManager) RegisterChannel(string) (Channel, error) {
|
|||||||
return nil, errors.New("not implemented")
|
return nil, errors.New("not implemented")
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetOrRegisterChannel implements Manager.
|
|
||||||
func (NoopManager) GetOrRegisterChannel(string) (Channel, error) {
|
|
||||||
return nil, errors.New("not implemented")
|
|
||||||
}
|
|
||||||
|
|
||||||
// UnregisterChannel implements Manager.
|
// UnregisterChannel implements Manager.
|
||||||
func (NoopManager) UnregisterChannel(string) error {
|
func (NoopManager) UnregisterChannel(string) error {
|
||||||
return nil
|
return nil
|
||||||
|
|||||||
@@ -308,21 +308,3 @@ func (c *StreamConfig) Build() (*internet.StreamConfig, error) {
|
|||||||
|
|
||||||
return config, nil
|
return config, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
type ProxyConfig struct {
|
|
||||||
Tag string `json:"tag"`
|
|
||||||
|
|
||||||
// TransportLayerProxy: For compatibility.
|
|
||||||
TransportLayerProxy bool `json:"transportLayer"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// Build implements Buildable.
|
|
||||||
func (v *ProxyConfig) Build() (*internet.ProxyConfig, error) {
|
|
||||||
if v.Tag == "" {
|
|
||||||
return nil, errors.New("Proxy tag is not set.")
|
|
||||||
}
|
|
||||||
return &internet.ProxyConfig{
|
|
||||||
Tag: v.Tag,
|
|
||||||
TransportLayerProxy: v.TransportLayerProxy,
|
|
||||||
}, nil
|
|
||||||
}
|
|
||||||
|
|||||||
+31
-34
@@ -14,6 +14,7 @@ import (
|
|||||||
"github.com/xtls/xray-core/common/net"
|
"github.com/xtls/xray-core/common/net"
|
||||||
"github.com/xtls/xray-core/common/serial"
|
"github.com/xtls/xray-core/common/serial"
|
||||||
core "github.com/xtls/xray-core/core"
|
core "github.com/xtls/xray-core/core"
|
||||||
|
"github.com/xtls/xray-core/proxy/freedom"
|
||||||
"github.com/xtls/xray-core/transport/internet"
|
"github.com/xtls/xray-core/transport/internet"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -215,21 +216,11 @@ type OutboundDetourConfig struct {
|
|||||||
Tag string `json:"tag"`
|
Tag string `json:"tag"`
|
||||||
Settings *json.RawMessage `json:"settings"`
|
Settings *json.RawMessage `json:"settings"`
|
||||||
StreamSetting *StreamConfig `json:"streamSettings"`
|
StreamSetting *StreamConfig `json:"streamSettings"`
|
||||||
ProxySettings *ProxyConfig `json:"proxySettings"`
|
ProxySettings *json.RawMessage `json:"proxySettings"`
|
||||||
MuxSettings *MuxConfig `json:"mux"`
|
MuxSettings *MuxConfig `json:"mux"`
|
||||||
TargetStrategy string `json:"targetStrategy"`
|
TargetStrategy string `json:"targetStrategy"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *OutboundDetourConfig) checkChainProxyConfig() error {
|
|
||||||
if c.StreamSetting == nil || c.ProxySettings == nil || c.StreamSetting.SocketSettings == nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
if len(c.ProxySettings.Tag) > 0 && len(c.StreamSetting.SocketSettings.DialerProxy) > 0 {
|
|
||||||
return errors.New("proxySettings.tag is conflicted with sockopt.dialerProxy").AtWarning()
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func requiresTransportSecurity(address *Address) bool {
|
func requiresTransportSecurity(address *Address) bool {
|
||||||
if address == nil || address.Address == nil {
|
if address == nil || address.Address == nil {
|
||||||
return false
|
return false
|
||||||
@@ -266,6 +257,10 @@ func validateOutboundTransportSecurity(rawConfig interface{}, senderSettings *pr
|
|||||||
|
|
||||||
// Build implements Buildable.
|
// Build implements Buildable.
|
||||||
func (c *OutboundDetourConfig) Build() (*core.OutboundHandlerConfig, error) {
|
func (c *OutboundDetourConfig) Build() (*core.OutboundHandlerConfig, error) {
|
||||||
|
if c.ProxySettings != nil {
|
||||||
|
return nil, errors.PrintRemovedFeatureError(`outbound "proxySettings"`, `"streamSettings.sockopt.dialerProxy"`)
|
||||||
|
}
|
||||||
|
|
||||||
senderSettings := &proxyman.SenderConfig{}
|
senderSettings := &proxyman.SenderConfig{}
|
||||||
switch strings.ToLower(c.TargetStrategy) {
|
switch strings.ToLower(c.TargetStrategy) {
|
||||||
case "asis", "":
|
case "asis", "":
|
||||||
@@ -293,9 +288,6 @@ func (c *OutboundDetourConfig) Build() (*core.OutboundHandlerConfig, error) {
|
|||||||
default:
|
default:
|
||||||
return nil, errors.New("unsupported target domain strategy: ", c.TargetStrategy)
|
return nil, errors.New("unsupported target domain strategy: ", c.TargetStrategy)
|
||||||
}
|
}
|
||||||
if err := c.checkChainProxyConfig(); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
if c.SendThrough != nil {
|
if c.SendThrough != nil {
|
||||||
address := ParseSendThough(c.SendThrough)
|
address := ParseSendThough(c.SendThrough)
|
||||||
@@ -321,26 +313,6 @@ func (c *OutboundDetourConfig) Build() (*core.OutboundHandlerConfig, error) {
|
|||||||
senderSettings.StreamSettings = ss
|
senderSettings.StreamSettings = ss
|
||||||
}
|
}
|
||||||
|
|
||||||
if c.ProxySettings != nil {
|
|
||||||
ps, err := c.ProxySettings.Build()
|
|
||||||
if err != nil {
|
|
||||||
return nil, errors.New("invalid outbound detour proxy settings").Base(err)
|
|
||||||
}
|
|
||||||
if ps.TransportLayerProxy {
|
|
||||||
if senderSettings.StreamSettings != nil {
|
|
||||||
if senderSettings.StreamSettings.SocketSettings != nil {
|
|
||||||
senderSettings.StreamSettings.SocketSettings.DialerProxy = ps.Tag
|
|
||||||
} else {
|
|
||||||
senderSettings.StreamSettings.SocketSettings = &internet.SocketConfig{DialerProxy: ps.Tag}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
senderSettings.StreamSettings = &internet.StreamConfig{SocketSettings: &internet.SocketConfig{DialerProxy: ps.Tag}}
|
|
||||||
}
|
|
||||||
ps = nil
|
|
||||||
}
|
|
||||||
senderSettings.ProxySettings = ps
|
|
||||||
}
|
|
||||||
|
|
||||||
if c.MuxSettings != nil {
|
if c.MuxSettings != nil {
|
||||||
ms, err := c.MuxSettings.Build()
|
ms, err := c.MuxSettings.Build()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -365,6 +337,31 @@ func (c *OutboundDetourConfig) Build() (*core.OutboundHandlerConfig, error) {
|
|||||||
return nil, errors.New("failed to build outbound handler for protocol ", c.Protocol).Base(err)
|
return nil, errors.New("failed to build outbound handler for protocol ", c.Protocol).Base(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if fc, ok := ts.(*freedom.Config); ok {
|
||||||
|
if senderSettings.StreamSettings != nil &&
|
||||||
|
senderSettings.StreamSettings.SocketSettings != nil &&
|
||||||
|
senderSettings.StreamSettings.SocketSettings.AddressPortStrategy != internet.AddressPortStrategy_None {
|
||||||
|
return nil, errors.New(`freedom outbound does not support "sockopt.addressPortStrategy"`)
|
||||||
|
}
|
||||||
|
|
||||||
|
var strategy internet.DomainStrategy
|
||||||
|
if strategy = senderSettings.TargetStrategy; strategy != internet.DomainStrategy_AS_IS {
|
||||||
|
errors.LogWarning(context.Background(), `The "outbound.targetStrategy" setting is not supported directly by freedom and has been automatically migrated to "sockopt.domainStrategy" with no behavior change.`)
|
||||||
|
senderSettings.TargetStrategy = internet.DomainStrategy_AS_IS
|
||||||
|
} else if strategy = fc.DomainStrategy; strategy != internet.DomainStrategy_AS_IS {
|
||||||
|
errors.LogWarning(context.Background(), `The "freedom.domainStrategy" setting is deprecated and will be removed. For compatibility, its value has been automatically migrated to "sockopt.domainStrategy". Please update your config before removal.`)
|
||||||
|
}
|
||||||
|
if strategy != internet.DomainStrategy_AS_IS {
|
||||||
|
if senderSettings.StreamSettings == nil {
|
||||||
|
senderSettings.StreamSettings = &internet.StreamConfig{}
|
||||||
|
}
|
||||||
|
if senderSettings.StreamSettings.SocketSettings == nil {
|
||||||
|
senderSettings.StreamSettings.SocketSettings = &internet.SocketConfig{}
|
||||||
|
}
|
||||||
|
senderSettings.StreamSettings.SocketSettings.DomainStrategy = strategy
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return &core.OutboundHandlerConfig{
|
return &core.OutboundHandlerConfig{
|
||||||
SenderSettings: serial.ToTypedMessage(senderSettings),
|
SenderSettings: serial.ToTypedMessage(senderSettings),
|
||||||
Tag: c.Tag,
|
Tag: c.Tag,
|
||||||
|
|||||||
+90
-101
@@ -40,6 +40,10 @@ var (
|
|||||||
func init() {
|
func init() {
|
||||||
common.Must(common.RegisterConfig((*Config)(nil), func(ctx context.Context, config interface{}) (interface{}, error) {
|
common.Must(common.RegisterConfig((*Config)(nil), func(ctx context.Context, config interface{}) (interface{}, error) {
|
||||||
h := new(Handler)
|
h := new(Handler)
|
||||||
|
if streamSettings, ok := session.StreamSettingsFromContext(ctx).(*internet.MemoryStreamConfig); ok && streamSettings.SocketSettings != nil {
|
||||||
|
h.resolveStrategy = streamSettings.SocketSettings.DomainStrategy
|
||||||
|
h.usesDialerProxy = len(streamSettings.SocketSettings.DialerProxy) > 0
|
||||||
|
}
|
||||||
if err := core.RequireFeatures(ctx, func(pm policy.Manager) error {
|
if err := core.RequireFeatures(ctx, func(pm policy.Manager) error {
|
||||||
return h.Init(config.(*Config), pm)
|
return h.Init(config.(*Config), pm)
|
||||||
}); err != nil {
|
}); err != nil {
|
||||||
@@ -81,9 +85,11 @@ type FinalRule struct {
|
|||||||
|
|
||||||
// Handler handles Freedom connections.
|
// Handler handles Freedom connections.
|
||||||
type Handler struct {
|
type Handler struct {
|
||||||
policyManager policy.Manager
|
policyManager policy.Manager
|
||||||
config *Config
|
config *Config
|
||||||
finalRules []*FinalRule
|
finalRules []*FinalRule
|
||||||
|
resolveStrategy internet.DomainStrategy
|
||||||
|
usesDialerProxy bool
|
||||||
}
|
}
|
||||||
|
|
||||||
func buildFinalRule(config *FinalRuleConfig) (*FinalRule, error) {
|
func buildFinalRule(config *FinalRuleConfig) (*FinalRule, error) {
|
||||||
@@ -160,22 +166,6 @@ func getDefaultFinalRule(inbound *session.Inbound) *FinalRule {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *Handler) shouldResolveDomainBeforeFinalRules(dialDest net.Destination, defaultRule *FinalRule) bool {
|
|
||||||
if !dialDest.Address.Family().IsDomain() {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
if len(h.finalRules) > 0 {
|
|
||||||
rule := h.finalRules[0]
|
|
||||||
if rule.action == RuleAction_Allow && rule.network[dialDest.Network] && len(rule.port) == 0 && rule.ip == nil {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if defaultRule != nil || len(h.finalRules) > 0 {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
func (h *Handler) matchFinalRule(network net.Network, address net.Address, port net.Port, defaultRule *FinalRule) *FinalRule {
|
func (h *Handler) matchFinalRule(network net.Network, address net.Address, port net.Port, defaultRule *FinalRule) *FinalRule {
|
||||||
for _, rule := range h.finalRules {
|
for _, rule := range h.finalRules {
|
||||||
if rule.Apply(network, address, port) {
|
if rule.Apply(network, address, port) {
|
||||||
@@ -188,13 +178,6 @@ func (h *Handler) matchFinalRule(network net.Network, address net.Address, port
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *Handler) applyFinalRules(network net.Network, address net.Address, port net.Port, defaultRule *FinalRule) RuleAction {
|
|
||||||
if rule := h.matchFinalRule(network, address, port, defaultRule); rule != nil {
|
|
||||||
return rule.action
|
|
||||||
}
|
|
||||||
return RuleAction_Allow
|
|
||||||
}
|
|
||||||
|
|
||||||
// Init initializes the Handler with necessary parameters.
|
// Init initializes the Handler with necessary parameters.
|
||||||
func (h *Handler) Init(config *Config, pm policy.Manager) error {
|
func (h *Handler) Init(config *Config, pm policy.Manager) error {
|
||||||
h.config = config
|
h.config = config
|
||||||
@@ -229,6 +212,20 @@ func (h *Handler) blockDelay(rule *FinalRule) time.Duration {
|
|||||||
return time.Duration(min+uint64(dice.Roll(int(span+1)))) * time.Second
|
return time.Duration(min+uint64(dice.Roll(int(span+1)))) * time.Second
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (h *Handler) blackhole(ctx context.Context, input buf.Reader, output buf.Writer, rule *FinalRule, dest *net.Destination) error {
|
||||||
|
delay := h.blockDelay(rule)
|
||||||
|
errors.LogInfo(ctx, "blocked target: ", *dest, ", blackholing connection for ", delay)
|
||||||
|
timer := time.AfterFunc(delay, func() {
|
||||||
|
common.Interrupt(input)
|
||||||
|
common.Interrupt(output)
|
||||||
|
errors.LogInfo(ctx, "closed blackholed connection to blocked target: ", *dest)
|
||||||
|
})
|
||||||
|
defer timer.Stop()
|
||||||
|
defer common.Close(output)
|
||||||
|
_ = buf.Copy(input, buf.Discard)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func isValidAddress(addr *net.IPOrDomain) bool {
|
func isValidAddress(addr *net.IPOrDomain) bool {
|
||||||
if addr == nil {
|
if addr == nil {
|
||||||
return false
|
return false
|
||||||
@@ -276,61 +273,53 @@ func (h *Handler) Process(ctx context.Context, link *transport.Link, dialer inte
|
|||||||
var conn stat.Connection
|
var conn stat.Connection
|
||||||
var blockedDest *net.Destination
|
var blockedDest *net.Destination
|
||||||
var blockedRule *FinalRule
|
var blockedRule *FinalRule
|
||||||
firstResolve := true
|
|
||||||
err := retry.ExponentialBackoff(5, 100).On(func() error {
|
err := retry.ExponentialBackoff(5, 100).On(func() error {
|
||||||
dialDest := destination
|
if destination.Address.Family().IsDomain() {
|
||||||
if h.config.DomainStrategy.HasStrategy() && dialDest.Address.Family().IsDomain() {
|
if defaultRule != nil || len(h.finalRules) > 0 {
|
||||||
strategy := h.config.DomainStrategy
|
if strategy := h.resolveStrategy; strategy.HasStrategy() {
|
||||||
if destination.Network == net.Network_UDP && origTargetAddr != nil && outGateway == nil {
|
ips, err := internet.LookupForIP(destination.Address.Domain(), strategy, outGateway)
|
||||||
strategy = strategy.GetDynamicStrategy(origTargetAddr.Family())
|
if err != nil { // non-force may still dial with system DNS
|
||||||
}
|
errors.LogInfoInner(ctx, err, "failed to get IP address for domain ", destination.Address.Domain())
|
||||||
ips, err := internet.LookupForIP(dialDest.Address.Domain(), strategy, outGateway)
|
if strategy.ForceIP() {
|
||||||
if err != nil {
|
return err // retry
|
||||||
errors.LogInfoInner(ctx, err, "failed to get IP address for domain ", dialDest.Address.Domain())
|
}
|
||||||
if h.config.DomainStrategy.ForceIP() || h.shouldResolveDomainBeforeFinalRules(dialDest, defaultRule) {
|
}
|
||||||
return err
|
for _, ip := range ips {
|
||||||
|
if addr := net.IPAddress(ip); addr != nil {
|
||||||
|
if rule := h.matchFinalRule(destination.Network, addr, destination.Port, defaultRule); rule != nil && rule.action == RuleAction_Block {
|
||||||
|
blockedDest = &destination
|
||||||
|
blockedDest.Address = addr
|
||||||
|
blockedRule = rule
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
addrs, err := net.DefaultResolver.LookupIPAddr(ctx, destination.Address.Domain())
|
||||||
|
if err != nil { // dialer may retry DNS
|
||||||
|
errors.LogInfoInner(ctx, err, "failed to get IP address for domain ", destination.Address.Domain())
|
||||||
|
}
|
||||||
|
for _, addr := range addrs {
|
||||||
|
if ipAddr := net.IPAddress(addr.IP); ipAddr != nil {
|
||||||
|
if rule := h.matchFinalRule(destination.Network, ipAddr, destination.Port, defaultRule); rule != nil && rule.action == RuleAction_Block {
|
||||||
|
blockedDest = &destination
|
||||||
|
blockedDest.Address = ipAddr
|
||||||
|
blockedRule = rule
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
dialDest = net.Destination{
|
|
||||||
Network: dialDest.Network,
|
|
||||||
Address: net.IPAddress(ips[dice.Roll(len(ips))]),
|
|
||||||
Port: dialDest.Port,
|
|
||||||
}
|
|
||||||
errors.LogInfo(ctx, "dialing to ", dialDest)
|
|
||||||
}
|
}
|
||||||
} else if h.shouldResolveDomainBeforeFinalRules(dialDest, defaultRule) { // asis + domain + hasrules
|
} else {
|
||||||
domain := dialDest.Address.Domain()
|
if rule := h.matchFinalRule(destination.Network, destination.Address, destination.Port, defaultRule); rule != nil && rule.action == RuleAction_Block {
|
||||||
var ips []net.IP
|
blockedDest = &destination
|
||||||
if firstResolve {
|
blockedRule = rule
|
||||||
firstResolve = false
|
return nil
|
||||||
supportIPv4, supportIPv6 := utils.CheckRoutes()
|
|
||||||
if supportIPv4 {
|
|
||||||
ips, _ = net.DefaultResolver.LookupIP(ctx, "ip4", domain)
|
|
||||||
}
|
|
||||||
if len(ips) == 0 && supportIPv6 {
|
|
||||||
ips, _ = net.DefaultResolver.LookupIP(ctx, "ip6", domain)
|
|
||||||
}
|
|
||||||
if len(ips) == 0 {
|
|
||||||
return errors.New("failed to get IP address for domain ", domain)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
ips, _ = net.DefaultResolver.LookupIP(ctx, "ip", domain)
|
|
||||||
}
|
}
|
||||||
if len(ips) == 0 { // SRV/TXT, lookup failed
|
|
||||||
return errors.New("failed to get IP address for domain ", domain)
|
|
||||||
}
|
|
||||||
if addr := net.IPAddress(ips[dice.Roll(len(ips))]); addr != nil {
|
|
||||||
dialDest.Address = addr
|
|
||||||
errors.LogInfo(ctx, "dialing to ", dialDest)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if rule := h.matchFinalRule(dialDest.Network, dialDest.Address, dialDest.Port, defaultRule); rule != nil && rule.action == RuleAction_Block {
|
|
||||||
blockedDest = &dialDest
|
|
||||||
blockedRule = rule
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
rawConn, err := dialer.Dial(ctx, dialDest)
|
rawConn, err := dialer.Dial(ctx, destination)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -342,20 +331,21 @@ func (h *Handler) Process(ctx context.Context, link *transport.Link, dialer inte
|
|||||||
return errors.New("failed to open connection to ", destination).Base(err)
|
return errors.New("failed to open connection to ", destination).Base(err)
|
||||||
}
|
}
|
||||||
if blockedDest != nil {
|
if blockedDest != nil {
|
||||||
delay := h.blockDelay(blockedRule)
|
return h.blackhole(ctx, input, output, blockedRule, blockedDest)
|
||||||
errors.LogInfo(ctx, "blocked target: ", *blockedDest, ", blackholing connection for ", delay)
|
|
||||||
timer := time.AfterFunc(delay, func() {
|
|
||||||
common.Interrupt(input)
|
|
||||||
common.Interrupt(output)
|
|
||||||
errors.LogInfo(ctx, "closed blackholed connection to blocked target: ", *blockedDest)
|
|
||||||
})
|
|
||||||
defer timer.Stop()
|
|
||||||
defer common.Close(output)
|
|
||||||
if err := buf.Copy(input, buf.Discard); err != nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
if destination.Address.Family().IsDomain() && (defaultRule != nil || len(h.finalRules) > 0) {
|
||||||
|
if h.usesDialerProxy {
|
||||||
|
errors.LogInfo(ctx, "skipping final rule check for proxied remote endpoint, original target: ", destination)
|
||||||
|
} else {
|
||||||
|
// pre-check may fail or dialer may select another IP
|
||||||
|
remoteDest := net.DestinationFromAddr(conn.RemoteAddr())
|
||||||
|
if rule := h.matchFinalRule(remoteDest.Network, remoteDest.Address, remoteDest.Port, defaultRule); rule != nil && rule.action == RuleAction_Block {
|
||||||
|
conn.Close()
|
||||||
|
return h.blackhole(ctx, input, output, rule, &remoteDest)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if h.config.ProxyProtocol > 0 && h.config.ProxyProtocol <= 2 {
|
if h.config.ProxyProtocol > 0 && h.config.ProxyProtocol <= 2 {
|
||||||
version := byte(h.config.ProxyProtocol)
|
version := byte(h.config.ProxyProtocol)
|
||||||
srcAddr := inbound.Source.RawNetAddr()
|
srcAddr := inbound.Source.RawNetAddr()
|
||||||
@@ -400,7 +390,7 @@ func (h *Handler) Process(ctx context.Context, link *transport.Link, dialer inte
|
|||||||
writer = buf.NewWriter(conn)
|
writer = buf.NewWriter(conn)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
writer = NewPacketWriter(conn, h, defaultRule, UDPOverride, destination)
|
writer = NewPacketWriter(conn, h, defaultRule, UDPOverride, destination, outGateway)
|
||||||
if h.config.Noises != nil {
|
if h.config.Noises != nil {
|
||||||
errors.LogDebug(ctx, "NOISE", h.config.Noises)
|
errors.LogDebug(ctx, "NOISE", h.config.Noises)
|
||||||
writer = &NoisePacketWriter{
|
writer = &NoisePacketWriter{
|
||||||
@@ -504,7 +494,7 @@ func (r *PacketReader) ReadMultiBuffer() (buf.MultiBuffer, error) {
|
|||||||
}
|
}
|
||||||
udpAddr := d.(*net.UDPAddr)
|
udpAddr := d.(*net.UDPAddr)
|
||||||
sourceAddr := net.IPAddress(udpAddr.IP)
|
sourceAddr := net.IPAddress(udpAddr.IP)
|
||||||
if r.Handler.applyFinalRules(net.Network_UDP, sourceAddr, net.Port(udpAddr.Port), r.DefaultRule) == RuleAction_Block {
|
if rule := r.Handler.matchFinalRule(net.Network_UDP, sourceAddr, net.Port(udpAddr.Port), r.DefaultRule); rule != nil && rule.action == RuleAction_Block {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
b.Resize(0, int32(n))
|
b.Resize(0, int32(n))
|
||||||
@@ -529,7 +519,7 @@ func (r *PacketReader) ReadMultiBuffer() (buf.MultiBuffer, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// DialDest means the dial target used in the dialer when creating conn
|
// DialDest means the dial target used in the dialer when creating conn
|
||||||
func NewPacketWriter(conn net.Conn, h *Handler, defaultRule *FinalRule, UDPOverride net.Destination, DialDest net.Destination) buf.Writer {
|
func NewPacketWriter(conn net.Conn, h *Handler, defaultRule *FinalRule, UDPOverride net.Destination, DialDest net.Destination, outGateway net.Address) buf.Writer {
|
||||||
iConn := conn
|
iConn := conn
|
||||||
statConn, ok := iConn.(*stat.CounterConnection)
|
statConn, ok := iConn.(*stat.CounterConnection)
|
||||||
if ok {
|
if ok {
|
||||||
@@ -553,9 +543,8 @@ func NewPacketWriter(conn net.Conn, h *Handler, defaultRule *FinalRule, UDPOverr
|
|||||||
DefaultRule: defaultRule,
|
DefaultRule: defaultRule,
|
||||||
UDPOverride: UDPOverride,
|
UDPOverride: UDPOverride,
|
||||||
ResolvedUDPAddr: resolvedUDPAddr,
|
ResolvedUDPAddr: resolvedUDPAddr,
|
||||||
LocalAddr: net.DestinationFromAddr(conn.LocalAddr()).Address,
|
OutGateway: outGateway,
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
return &buf.SequentialWriter{Writer: conn}
|
return &buf.SequentialWriter{Writer: conn}
|
||||||
}
|
}
|
||||||
@@ -572,7 +561,7 @@ type PacketWriter struct {
|
|||||||
// Resulting in these packets being sent to many different IPs randomly
|
// Resulting in these packets being sent to many different IPs randomly
|
||||||
// So, cache and keep the resolve result
|
// So, cache and keep the resolve result
|
||||||
ResolvedUDPAddr *utils.TypedSyncMap[string, net.Address]
|
ResolvedUDPAddr *utils.TypedSyncMap[string, net.Address]
|
||||||
LocalAddr net.Address
|
OutGateway net.Address
|
||||||
}
|
}
|
||||||
|
|
||||||
func (w *PacketWriter) WriteMultiBuffer(mb buf.MultiBuffer) error {
|
func (w *PacketWriter) WriteMultiBuffer(mb buf.MultiBuffer) error {
|
||||||
@@ -595,21 +584,21 @@ func (w *PacketWriter) WriteMultiBuffer(mb buf.MultiBuffer) error {
|
|||||||
if ip, ok := w.ResolvedUDPAddr.Load(b.UDP.Address.Domain()); ok {
|
if ip, ok := w.ResolvedUDPAddr.Load(b.UDP.Address.Domain()); ok {
|
||||||
b.UDP.Address = ip
|
b.UDP.Address = ip
|
||||||
} else {
|
} else {
|
||||||
ShouldUseSystemResolver := true
|
shouldUseSystemResolver := true
|
||||||
if w.Handler.config.DomainStrategy.HasStrategy() {
|
if strategy := w.Handler.resolveStrategy; strategy.HasStrategy() {
|
||||||
ips, err := internet.LookupForIP(b.UDP.Address.Domain(), w.Handler.config.DomainStrategy, w.LocalAddr)
|
ips, err := internet.LookupForIP(b.UDP.Address.Domain(), strategy, w.OutGateway)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
// drop packet if resolve failed when forceIP
|
// drop packet if resolve failed when forceIP
|
||||||
if w.Handler.config.DomainStrategy.ForceIP() {
|
if strategy.ForceIP() {
|
||||||
b.Release()
|
b.Release()
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
ip = net.IPAddress(ips[dice.Roll(len(ips))])
|
ip = net.IPAddress(ips[dice.Roll(len(ips))])
|
||||||
ShouldUseSystemResolver = false
|
shouldUseSystemResolver = false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if ShouldUseSystemResolver {
|
if shouldUseSystemResolver {
|
||||||
udpAddr, err := net.ResolveUDPAddr("udp", b.UDP.NetAddr())
|
udpAddr, err := net.ResolveUDPAddr("udp", b.UDP.NetAddr())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
b.Release()
|
b.Release()
|
||||||
@@ -623,7 +612,7 @@ func (w *PacketWriter) WriteMultiBuffer(mb buf.MultiBuffer) error {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if w.applyFinalRules(net.Network_UDP, b.UDP.Address, b.UDP.Port, w.DefaultRule) == RuleAction_Block {
|
if rule := w.matchFinalRule(net.Network_UDP, b.UDP.Address, b.UDP.Port, w.DefaultRule); rule != nil && rule.action == RuleAction_Block {
|
||||||
b.Release()
|
b.Release()
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -65,7 +65,7 @@ func (t *Handler) Init(ctx context.Context, pm policy.Manager, dispatcher routin
|
|||||||
if len(t.tag) > 0 && pm.ForSystem().Stats.InboundUplink {
|
if len(t.tag) > 0 && pm.ForSystem().Stats.InboundUplink {
|
||||||
statsManager := core.MustFromContext(ctx).GetFeature(stats.ManagerType()).(stats.Manager)
|
statsManager := core.MustFromContext(ctx).GetFeature(stats.ManagerType()).(stats.Manager)
|
||||||
name := "inbound>>>" + t.tag + ">>>traffic>>>uplink"
|
name := "inbound>>>" + t.tag + ">>>traffic>>>uplink"
|
||||||
c, _ := statsManager.GetOrRegisterCounter(name)
|
c, _ := stats.GetOrRegisterCounter(statsManager, name)
|
||||||
if c != nil {
|
if c != nil {
|
||||||
t.uplinkCounter = c
|
t.uplinkCounter = c
|
||||||
}
|
}
|
||||||
@@ -73,7 +73,7 @@ func (t *Handler) Init(ctx context.Context, pm policy.Manager, dispatcher routin
|
|||||||
if len(t.tag) > 0 && pm.ForSystem().Stats.InboundDownlink {
|
if len(t.tag) > 0 && pm.ForSystem().Stats.InboundDownlink {
|
||||||
statsManager := core.MustFromContext(ctx).GetFeature(stats.ManagerType()).(stats.Manager)
|
statsManager := core.MustFromContext(ctx).GetFeature(stats.ManagerType()).(stats.Manager)
|
||||||
name := "inbound>>>" + t.tag + ">>>traffic>>>downlink"
|
name := "inbound>>>" + t.tag + ">>>traffic>>>downlink"
|
||||||
c, _ := statsManager.GetOrRegisterCounter(name)
|
c, _ := stats.GetOrRegisterCounter(statsManager, name)
|
||||||
if c != nil {
|
if c != nil {
|
||||||
t.downlinkCounter = c
|
t.downlinkCounter = c
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -57,7 +57,7 @@ func NewClient(ctx context.Context, conf *DeviceConfig) (*Handler, error) {
|
|||||||
if len(tag) > 0 && p.ForSystem().Stats.OutboundUplink {
|
if len(tag) > 0 && p.ForSystem().Stats.OutboundUplink {
|
||||||
statsManager := v.GetFeature(stats.ManagerType()).(stats.Manager)
|
statsManager := v.GetFeature(stats.ManagerType()).(stats.Manager)
|
||||||
name := "outbound>>>" + tag + ">>>traffic>>>uplink"
|
name := "outbound>>>" + tag + ">>>traffic>>>uplink"
|
||||||
c, _ := statsManager.GetOrRegisterCounter(name)
|
c, _ := stats.GetOrRegisterCounter(statsManager, name)
|
||||||
if c != nil {
|
if c != nil {
|
||||||
uplinkCounter = c
|
uplinkCounter = c
|
||||||
}
|
}
|
||||||
@@ -65,7 +65,7 @@ func NewClient(ctx context.Context, conf *DeviceConfig) (*Handler, error) {
|
|||||||
if len(tag) > 0 && p.ForSystem().Stats.OutboundDownlink {
|
if len(tag) > 0 && p.ForSystem().Stats.OutboundDownlink {
|
||||||
statsManager := v.GetFeature(stats.ManagerType()).(stats.Manager)
|
statsManager := v.GetFeature(stats.ManagerType()).(stats.Manager)
|
||||||
name := "outbound>>>" + tag + ">>>traffic>>>downlink"
|
name := "outbound>>>" + tag + ">>>traffic>>>downlink"
|
||||||
c, _ := statsManager.GetOrRegisterCounter(name)
|
c, _ := stats.GetOrRegisterCounter(statsManager, name)
|
||||||
if c != nil {
|
if c != nil {
|
||||||
downlinkCounter = c
|
downlinkCounter = c
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -66,7 +66,7 @@ func NewServer(ctx context.Context, conf *DeviceConfig) (*Server, error) {
|
|||||||
if len(tag) > 0 && p.ForSystem().Stats.InboundUplink {
|
if len(tag) > 0 && p.ForSystem().Stats.InboundUplink {
|
||||||
statsManager := v.GetFeature(stats.ManagerType()).(stats.Manager)
|
statsManager := v.GetFeature(stats.ManagerType()).(stats.Manager)
|
||||||
name := "inbound>>>" + tag + ">>>traffic>>>uplink"
|
name := "inbound>>>" + tag + ">>>traffic>>>uplink"
|
||||||
c, _ := statsManager.GetOrRegisterCounter(name)
|
c, _ := stats.GetOrRegisterCounter(statsManager, name)
|
||||||
if c != nil {
|
if c != nil {
|
||||||
uplinkCounter = c
|
uplinkCounter = c
|
||||||
}
|
}
|
||||||
@@ -74,7 +74,7 @@ func NewServer(ctx context.Context, conf *DeviceConfig) (*Server, error) {
|
|||||||
if len(tag) > 0 && p.ForSystem().Stats.InboundDownlink {
|
if len(tag) > 0 && p.ForSystem().Stats.InboundDownlink {
|
||||||
statsManager := v.GetFeature(stats.ManagerType()).(stats.Manager)
|
statsManager := v.GetFeature(stats.ManagerType()).(stats.Manager)
|
||||||
name := "inbound>>>" + tag + ">>>traffic>>>downlink"
|
name := "inbound>>>" + tag + ">>>traffic>>>downlink"
|
||||||
c, _ := statsManager.GetOrRegisterCounter(name)
|
c, _ := stats.GetOrRegisterCounter(statsManager, name)
|
||||||
if c != nil {
|
if c != nil {
|
||||||
downlinkCounter = c
|
downlinkCounter = c
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -82,9 +82,14 @@ func TestResolveIP(t *testing.T) {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
Tag: "direct",
|
Tag: "direct",
|
||||||
ProxySettings: serial.ToTypedMessage(&freedom.Config{
|
SenderSettings: serial.ToTypedMessage(&proxyman.SenderConfig{
|
||||||
DomainStrategy: internet.DomainStrategy_USE_IP,
|
StreamSettings: &internet.StreamConfig{
|
||||||
|
SocketSettings: &internet.SocketConfig{
|
||||||
|
DomainStrategy: internet.DomainStrategy_USE_IP,
|
||||||
|
},
|
||||||
|
},
|
||||||
}),
|
}),
|
||||||
|
ProxySettings: serial.ToTypedMessage(&freedom.Config{}),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -92,7 +92,7 @@ func TestPassiveConnection(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestProxy(t *testing.T) {
|
func TestDialerProxy(t *testing.T) {
|
||||||
tcpServer := tcp.Server{
|
tcpServer := tcp.Server{
|
||||||
MsgProcessor: xor,
|
MsgProcessor: xor,
|
||||||
}
|
}
|
||||||
@@ -187,8 +187,10 @@ func TestProxy(t *testing.T) {
|
|||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
SenderSettings: serial.ToTypedMessage(&proxyman.SenderConfig{
|
SenderSettings: serial.ToTypedMessage(&proxyman.SenderConfig{
|
||||||
ProxySettings: &internet.ProxyConfig{
|
StreamSettings: &internet.StreamConfig{
|
||||||
Tag: "proxy",
|
SocketSettings: &internet.SocketConfig{
|
||||||
|
DialerProxy: "proxy",
|
||||||
|
},
|
||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
},
|
},
|
||||||
@@ -218,7 +220,7 @@ func TestProxy(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestProxyOverKCP(t *testing.T) {
|
func TestDialerProxyOverKCP(t *testing.T) {
|
||||||
tcpServer := tcp.Server{
|
tcpServer := tcp.Server{
|
||||||
MsgProcessor: xor,
|
MsgProcessor: xor,
|
||||||
}
|
}
|
||||||
@@ -321,11 +323,11 @@ func TestProxyOverKCP(t *testing.T) {
|
|||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
SenderSettings: serial.ToTypedMessage(&proxyman.SenderConfig{
|
SenderSettings: serial.ToTypedMessage(&proxyman.SenderConfig{
|
||||||
ProxySettings: &internet.ProxyConfig{
|
|
||||||
Tag: "proxy",
|
|
||||||
},
|
|
||||||
StreamSettings: &internet.StreamConfig{
|
StreamSettings: &internet.StreamConfig{
|
||||||
ProtocolName: "mkcp",
|
ProtocolName: "mkcp",
|
||||||
|
SocketSettings: &internet.SocketConfig{
|
||||||
|
DialerProxy: "proxy",
|
||||||
|
},
|
||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ type ConfigCreator func() interface{}
|
|||||||
|
|
||||||
var globalTransportConfigCreatorCache = make(map[string]ConfigCreator)
|
var globalTransportConfigCreatorCache = make(map[string]ConfigCreator)
|
||||||
|
|
||||||
var strategy = [][]byte{
|
var strategy = [11][3]byte{
|
||||||
// name strategy, prefer, fallback
|
// name strategy, prefer, fallback
|
||||||
{0, 0, 0}, // AsIs none, /, /
|
{0, 0, 0}, // AsIs none, /, /
|
||||||
{1, 0, 0}, // UseIP use, both, none
|
{1, 0, 0}, // UseIP use, both, none
|
||||||
@@ -25,8 +25,6 @@ var strategy = [][]byte{
|
|||||||
{2, 6, 4}, // ForceIPv6v4 force, 6, 4
|
{2, 6, 4}, // ForceIPv6v4 force, 6, 4
|
||||||
}
|
}
|
||||||
|
|
||||||
const unknownProtocol = "unknown"
|
|
||||||
|
|
||||||
func RegisterProtocolConfigCreator(name string, creator ConfigCreator) error {
|
func RegisterProtocolConfigCreator(name string, creator ConfigCreator) error {
|
||||||
if _, found := globalTransportConfigCreatorCache[name]; found {
|
if _, found := globalTransportConfigCreatorCache[name]; found {
|
||||||
return errors.New("protocol ", name, " is already registered").AtError()
|
return errors.New("protocol ", name, " is already registered").AtError()
|
||||||
@@ -91,10 +89,6 @@ func (c *StreamConfig) HasSecuritySettings() bool {
|
|||||||
return len(c.SecuritySettings) > 0
|
return len(c.SecuritySettings) > 0
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *ProxyConfig) HasTag() bool {
|
|
||||||
return c != nil && len(c.Tag) > 0
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m SocketConfig_TProxyMode) IsEnabled() bool {
|
func (m SocketConfig_TProxyMode) IsEnabled() bool {
|
||||||
return m != SocketConfig_Off
|
return m != SocketConfig_Off
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -206,7 +206,7 @@ func (x SocketConfig_TProxyMode) Number() protoreflect.EnumNumber {
|
|||||||
|
|
||||||
// Deprecated: Use SocketConfig_TProxyMode.Descriptor instead.
|
// Deprecated: Use SocketConfig_TProxyMode.Descriptor instead.
|
||||||
func (SocketConfig_TProxyMode) EnumDescriptor() ([]byte, []int) {
|
func (SocketConfig_TProxyMode) EnumDescriptor() ([]byte, []int) {
|
||||||
return file_transport_internet_config_proto_rawDescGZIP(), []int{6, 0}
|
return file_transport_internet_config_proto_rawDescGZIP(), []int{5, 0}
|
||||||
}
|
}
|
||||||
|
|
||||||
type TransportConfig struct {
|
type TransportConfig struct {
|
||||||
@@ -582,58 +582,6 @@ func (x *QuicParams) GetMaxIncomingStreams() int64 {
|
|||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
type ProxyConfig struct {
|
|
||||||
state protoimpl.MessageState `protogen:"open.v1"`
|
|
||||||
Tag string `protobuf:"bytes,1,opt,name=tag,proto3" json:"tag,omitempty"`
|
|
||||||
TransportLayerProxy bool `protobuf:"varint,2,opt,name=transportLayerProxy,proto3" json:"transportLayerProxy,omitempty"`
|
|
||||||
unknownFields protoimpl.UnknownFields
|
|
||||||
sizeCache protoimpl.SizeCache
|
|
||||||
}
|
|
||||||
|
|
||||||
func (x *ProxyConfig) Reset() {
|
|
||||||
*x = ProxyConfig{}
|
|
||||||
mi := &file_transport_internet_config_proto_msgTypes[4]
|
|
||||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
|
||||||
ms.StoreMessageInfo(mi)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (x *ProxyConfig) String() string {
|
|
||||||
return protoimpl.X.MessageStringOf(x)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (*ProxyConfig) ProtoMessage() {}
|
|
||||||
|
|
||||||
func (x *ProxyConfig) ProtoReflect() protoreflect.Message {
|
|
||||||
mi := &file_transport_internet_config_proto_msgTypes[4]
|
|
||||||
if x != nil {
|
|
||||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
|
||||||
if ms.LoadMessageInfo() == nil {
|
|
||||||
ms.StoreMessageInfo(mi)
|
|
||||||
}
|
|
||||||
return ms
|
|
||||||
}
|
|
||||||
return mi.MessageOf(x)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Deprecated: Use ProxyConfig.ProtoReflect.Descriptor instead.
|
|
||||||
func (*ProxyConfig) Descriptor() ([]byte, []int) {
|
|
||||||
return file_transport_internet_config_proto_rawDescGZIP(), []int{4}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (x *ProxyConfig) GetTag() string {
|
|
||||||
if x != nil {
|
|
||||||
return x.Tag
|
|
||||||
}
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
|
|
||||||
func (x *ProxyConfig) GetTransportLayerProxy() bool {
|
|
||||||
if x != nil {
|
|
||||||
return x.TransportLayerProxy
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
type CustomSockopt struct {
|
type CustomSockopt struct {
|
||||||
state protoimpl.MessageState `protogen:"open.v1"`
|
state protoimpl.MessageState `protogen:"open.v1"`
|
||||||
System string `protobuf:"bytes,1,opt,name=system,proto3" json:"system,omitempty"`
|
System string `protobuf:"bytes,1,opt,name=system,proto3" json:"system,omitempty"`
|
||||||
@@ -648,7 +596,7 @@ type CustomSockopt struct {
|
|||||||
|
|
||||||
func (x *CustomSockopt) Reset() {
|
func (x *CustomSockopt) Reset() {
|
||||||
*x = CustomSockopt{}
|
*x = CustomSockopt{}
|
||||||
mi := &file_transport_internet_config_proto_msgTypes[5]
|
mi := &file_transport_internet_config_proto_msgTypes[4]
|
||||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
ms.StoreMessageInfo(mi)
|
ms.StoreMessageInfo(mi)
|
||||||
}
|
}
|
||||||
@@ -660,7 +608,7 @@ func (x *CustomSockopt) String() string {
|
|||||||
func (*CustomSockopt) ProtoMessage() {}
|
func (*CustomSockopt) ProtoMessage() {}
|
||||||
|
|
||||||
func (x *CustomSockopt) ProtoReflect() protoreflect.Message {
|
func (x *CustomSockopt) ProtoReflect() protoreflect.Message {
|
||||||
mi := &file_transport_internet_config_proto_msgTypes[5]
|
mi := &file_transport_internet_config_proto_msgTypes[4]
|
||||||
if x != nil {
|
if x != nil {
|
||||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
if ms.LoadMessageInfo() == nil {
|
if ms.LoadMessageInfo() == nil {
|
||||||
@@ -673,7 +621,7 @@ func (x *CustomSockopt) ProtoReflect() protoreflect.Message {
|
|||||||
|
|
||||||
// Deprecated: Use CustomSockopt.ProtoReflect.Descriptor instead.
|
// Deprecated: Use CustomSockopt.ProtoReflect.Descriptor instead.
|
||||||
func (*CustomSockopt) Descriptor() ([]byte, []int) {
|
func (*CustomSockopt) Descriptor() ([]byte, []int) {
|
||||||
return file_transport_internet_config_proto_rawDescGZIP(), []int{5}
|
return file_transport_internet_config_proto_rawDescGZIP(), []int{4}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (x *CustomSockopt) GetSystem() string {
|
func (x *CustomSockopt) GetSystem() string {
|
||||||
@@ -753,7 +701,7 @@ type SocketConfig struct {
|
|||||||
|
|
||||||
func (x *SocketConfig) Reset() {
|
func (x *SocketConfig) Reset() {
|
||||||
*x = SocketConfig{}
|
*x = SocketConfig{}
|
||||||
mi := &file_transport_internet_config_proto_msgTypes[6]
|
mi := &file_transport_internet_config_proto_msgTypes[5]
|
||||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
ms.StoreMessageInfo(mi)
|
ms.StoreMessageInfo(mi)
|
||||||
}
|
}
|
||||||
@@ -765,7 +713,7 @@ func (x *SocketConfig) String() string {
|
|||||||
func (*SocketConfig) ProtoMessage() {}
|
func (*SocketConfig) ProtoMessage() {}
|
||||||
|
|
||||||
func (x *SocketConfig) ProtoReflect() protoreflect.Message {
|
func (x *SocketConfig) ProtoReflect() protoreflect.Message {
|
||||||
mi := &file_transport_internet_config_proto_msgTypes[6]
|
mi := &file_transport_internet_config_proto_msgTypes[5]
|
||||||
if x != nil {
|
if x != nil {
|
||||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
if ms.LoadMessageInfo() == nil {
|
if ms.LoadMessageInfo() == nil {
|
||||||
@@ -778,7 +726,7 @@ func (x *SocketConfig) ProtoReflect() protoreflect.Message {
|
|||||||
|
|
||||||
// Deprecated: Use SocketConfig.ProtoReflect.Descriptor instead.
|
// Deprecated: Use SocketConfig.ProtoReflect.Descriptor instead.
|
||||||
func (*SocketConfig) Descriptor() ([]byte, []int) {
|
func (*SocketConfig) Descriptor() ([]byte, []int) {
|
||||||
return file_transport_internet_config_proto_rawDescGZIP(), []int{6}
|
return file_transport_internet_config_proto_rawDescGZIP(), []int{5}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (x *SocketConfig) GetMark() int32 {
|
func (x *SocketConfig) GetMark() int32 {
|
||||||
@@ -940,7 +888,7 @@ type HappyEyeballsConfig struct {
|
|||||||
|
|
||||||
func (x *HappyEyeballsConfig) Reset() {
|
func (x *HappyEyeballsConfig) Reset() {
|
||||||
*x = HappyEyeballsConfig{}
|
*x = HappyEyeballsConfig{}
|
||||||
mi := &file_transport_internet_config_proto_msgTypes[7]
|
mi := &file_transport_internet_config_proto_msgTypes[6]
|
||||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
ms.StoreMessageInfo(mi)
|
ms.StoreMessageInfo(mi)
|
||||||
}
|
}
|
||||||
@@ -952,7 +900,7 @@ func (x *HappyEyeballsConfig) String() string {
|
|||||||
func (*HappyEyeballsConfig) ProtoMessage() {}
|
func (*HappyEyeballsConfig) ProtoMessage() {}
|
||||||
|
|
||||||
func (x *HappyEyeballsConfig) ProtoReflect() protoreflect.Message {
|
func (x *HappyEyeballsConfig) ProtoReflect() protoreflect.Message {
|
||||||
mi := &file_transport_internet_config_proto_msgTypes[7]
|
mi := &file_transport_internet_config_proto_msgTypes[6]
|
||||||
if x != nil {
|
if x != nil {
|
||||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
if ms.LoadMessageInfo() == nil {
|
if ms.LoadMessageInfo() == nil {
|
||||||
@@ -965,7 +913,7 @@ func (x *HappyEyeballsConfig) ProtoReflect() protoreflect.Message {
|
|||||||
|
|
||||||
// Deprecated: Use HappyEyeballsConfig.ProtoReflect.Descriptor instead.
|
// Deprecated: Use HappyEyeballsConfig.ProtoReflect.Descriptor instead.
|
||||||
func (*HappyEyeballsConfig) Descriptor() ([]byte, []int) {
|
func (*HappyEyeballsConfig) Descriptor() ([]byte, []int) {
|
||||||
return file_transport_internet_config_proto_rawDescGZIP(), []int{7}
|
return file_transport_internet_config_proto_rawDescGZIP(), []int{6}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (x *HappyEyeballsConfig) GetPrioritizeIpv6() bool {
|
func (x *HappyEyeballsConfig) GetPrioritizeIpv6() bool {
|
||||||
@@ -1040,10 +988,7 @@ const file_transport_internet_config_proto_rawDesc = "" +
|
|||||||
" \x01(\x03R\x0emaxIdleTimeout\x12*\n" +
|
" \x01(\x03R\x0emaxIdleTimeout\x12*\n" +
|
||||||
"\x11keep_alive_period\x18\v \x01(\x03R\x0fkeepAlivePeriod\x12;\n" +
|
"\x11keep_alive_period\x18\v \x01(\x03R\x0fkeepAlivePeriod\x12;\n" +
|
||||||
"\x1adisable_path_mtu_discovery\x18\f \x01(\bR\x17disablePathMtuDiscovery\x120\n" +
|
"\x1adisable_path_mtu_discovery\x18\f \x01(\bR\x17disablePathMtuDiscovery\x120\n" +
|
||||||
"\x14max_incoming_streams\x18\r \x01(\x03R\x12maxIncomingStreams\"Q\n" +
|
"\x14max_incoming_streams\x18\r \x01(\x03R\x12maxIncomingStreams\"\x93\x01\n" +
|
||||||
"\vProxyConfig\x12\x10\n" +
|
|
||||||
"\x03tag\x18\x01 \x01(\tR\x03tag\x120\n" +
|
|
||||||
"\x13transportLayerProxy\x18\x02 \x01(\bR\x13transportLayerProxy\"\x93\x01\n" +
|
|
||||||
"\rCustomSockopt\x12\x16\n" +
|
"\rCustomSockopt\x12\x16\n" +
|
||||||
"\x06system\x18\x01 \x01(\tR\x06system\x12\x18\n" +
|
"\x06system\x18\x01 \x01(\tR\x06system\x12\x18\n" +
|
||||||
"\anetwork\x18\x02 \x01(\tR\anetwork\x12\x14\n" +
|
"\anetwork\x18\x02 \x01(\tR\anetwork\x12\x14\n" +
|
||||||
@@ -1127,7 +1072,7 @@ func file_transport_internet_config_proto_rawDescGZIP() []byte {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var file_transport_internet_config_proto_enumTypes = make([]protoimpl.EnumInfo, 3)
|
var file_transport_internet_config_proto_enumTypes = make([]protoimpl.EnumInfo, 3)
|
||||||
var file_transport_internet_config_proto_msgTypes = make([]protoimpl.MessageInfo, 8)
|
var file_transport_internet_config_proto_msgTypes = make([]protoimpl.MessageInfo, 7)
|
||||||
var file_transport_internet_config_proto_goTypes = []any{
|
var file_transport_internet_config_proto_goTypes = []any{
|
||||||
(DomainStrategy)(0), // 0: xray.transport.internet.DomainStrategy
|
(DomainStrategy)(0), // 0: xray.transport.internet.DomainStrategy
|
||||||
(AddressPortStrategy)(0), // 1: xray.transport.internet.AddressPortStrategy
|
(AddressPortStrategy)(0), // 1: xray.transport.internet.AddressPortStrategy
|
||||||
@@ -1136,28 +1081,27 @@ var file_transport_internet_config_proto_goTypes = []any{
|
|||||||
(*StreamConfig)(nil), // 4: xray.transport.internet.StreamConfig
|
(*StreamConfig)(nil), // 4: xray.transport.internet.StreamConfig
|
||||||
(*UdpHop)(nil), // 5: xray.transport.internet.UdpHop
|
(*UdpHop)(nil), // 5: xray.transport.internet.UdpHop
|
||||||
(*QuicParams)(nil), // 6: xray.transport.internet.QuicParams
|
(*QuicParams)(nil), // 6: xray.transport.internet.QuicParams
|
||||||
(*ProxyConfig)(nil), // 7: xray.transport.internet.ProxyConfig
|
(*CustomSockopt)(nil), // 7: xray.transport.internet.CustomSockopt
|
||||||
(*CustomSockopt)(nil), // 8: xray.transport.internet.CustomSockopt
|
(*SocketConfig)(nil), // 8: xray.transport.internet.SocketConfig
|
||||||
(*SocketConfig)(nil), // 9: xray.transport.internet.SocketConfig
|
(*HappyEyeballsConfig)(nil), // 9: xray.transport.internet.HappyEyeballsConfig
|
||||||
(*HappyEyeballsConfig)(nil), // 10: xray.transport.internet.HappyEyeballsConfig
|
(*serial.TypedMessage)(nil), // 10: xray.common.serial.TypedMessage
|
||||||
(*serial.TypedMessage)(nil), // 11: xray.common.serial.TypedMessage
|
(*net.IPOrDomain)(nil), // 11: xray.common.net.IPOrDomain
|
||||||
(*net.IPOrDomain)(nil), // 12: xray.common.net.IPOrDomain
|
|
||||||
}
|
}
|
||||||
var file_transport_internet_config_proto_depIdxs = []int32{
|
var file_transport_internet_config_proto_depIdxs = []int32{
|
||||||
11, // 0: xray.transport.internet.TransportConfig.settings:type_name -> xray.common.serial.TypedMessage
|
10, // 0: xray.transport.internet.TransportConfig.settings:type_name -> xray.common.serial.TypedMessage
|
||||||
12, // 1: xray.transport.internet.StreamConfig.address:type_name -> xray.common.net.IPOrDomain
|
11, // 1: xray.transport.internet.StreamConfig.address:type_name -> xray.common.net.IPOrDomain
|
||||||
3, // 2: xray.transport.internet.StreamConfig.transport_settings:type_name -> xray.transport.internet.TransportConfig
|
3, // 2: xray.transport.internet.StreamConfig.transport_settings:type_name -> xray.transport.internet.TransportConfig
|
||||||
11, // 3: xray.transport.internet.StreamConfig.security_settings:type_name -> xray.common.serial.TypedMessage
|
10, // 3: xray.transport.internet.StreamConfig.security_settings:type_name -> xray.common.serial.TypedMessage
|
||||||
11, // 4: xray.transport.internet.StreamConfig.udpmasks:type_name -> xray.common.serial.TypedMessage
|
10, // 4: xray.transport.internet.StreamConfig.udpmasks:type_name -> xray.common.serial.TypedMessage
|
||||||
11, // 5: xray.transport.internet.StreamConfig.tcpmasks:type_name -> xray.common.serial.TypedMessage
|
10, // 5: xray.transport.internet.StreamConfig.tcpmasks:type_name -> xray.common.serial.TypedMessage
|
||||||
6, // 6: xray.transport.internet.StreamConfig.quic_params:type_name -> xray.transport.internet.QuicParams
|
6, // 6: xray.transport.internet.StreamConfig.quic_params:type_name -> xray.transport.internet.QuicParams
|
||||||
9, // 7: xray.transport.internet.StreamConfig.socket_settings:type_name -> xray.transport.internet.SocketConfig
|
8, // 7: xray.transport.internet.StreamConfig.socket_settings:type_name -> xray.transport.internet.SocketConfig
|
||||||
5, // 8: xray.transport.internet.QuicParams.udp_hop:type_name -> xray.transport.internet.UdpHop
|
5, // 8: xray.transport.internet.QuicParams.udp_hop:type_name -> xray.transport.internet.UdpHop
|
||||||
2, // 9: xray.transport.internet.SocketConfig.tproxy:type_name -> xray.transport.internet.SocketConfig.TProxyMode
|
2, // 9: xray.transport.internet.SocketConfig.tproxy:type_name -> xray.transport.internet.SocketConfig.TProxyMode
|
||||||
0, // 10: xray.transport.internet.SocketConfig.domain_strategy:type_name -> xray.transport.internet.DomainStrategy
|
0, // 10: xray.transport.internet.SocketConfig.domain_strategy:type_name -> xray.transport.internet.DomainStrategy
|
||||||
8, // 11: xray.transport.internet.SocketConfig.customSockopt:type_name -> xray.transport.internet.CustomSockopt
|
7, // 11: xray.transport.internet.SocketConfig.customSockopt:type_name -> xray.transport.internet.CustomSockopt
|
||||||
1, // 12: xray.transport.internet.SocketConfig.address_port_strategy:type_name -> xray.transport.internet.AddressPortStrategy
|
1, // 12: xray.transport.internet.SocketConfig.address_port_strategy:type_name -> xray.transport.internet.AddressPortStrategy
|
||||||
10, // 13: xray.transport.internet.SocketConfig.happy_eyeballs:type_name -> xray.transport.internet.HappyEyeballsConfig
|
9, // 13: xray.transport.internet.SocketConfig.happy_eyeballs:type_name -> xray.transport.internet.HappyEyeballsConfig
|
||||||
14, // [14:14] is the sub-list for method output_type
|
14, // [14:14] is the sub-list for method output_type
|
||||||
14, // [14:14] is the sub-list for method input_type
|
14, // [14:14] is the sub-list for method input_type
|
||||||
14, // [14:14] is the sub-list for extension type_name
|
14, // [14:14] is the sub-list for extension type_name
|
||||||
@@ -1176,7 +1120,7 @@ func file_transport_internet_config_proto_init() {
|
|||||||
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
|
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
|
||||||
RawDescriptor: unsafe.Slice(unsafe.StringData(file_transport_internet_config_proto_rawDesc), len(file_transport_internet_config_proto_rawDesc)),
|
RawDescriptor: unsafe.Slice(unsafe.StringData(file_transport_internet_config_proto_rawDesc), len(file_transport_internet_config_proto_rawDesc)),
|
||||||
NumEnums: 3,
|
NumEnums: 3,
|
||||||
NumMessages: 8,
|
NumMessages: 7,
|
||||||
NumExtensions: 0,
|
NumExtensions: 0,
|
||||||
NumServices: 0,
|
NumServices: 0,
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -86,11 +86,6 @@ message QuicParams {
|
|||||||
int64 max_incoming_streams = 13;
|
int64 max_incoming_streams = 13;
|
||||||
}
|
}
|
||||||
|
|
||||||
message ProxyConfig {
|
|
||||||
string tag = 1;
|
|
||||||
bool transportLayerProxy = 2;
|
|
||||||
}
|
|
||||||
|
|
||||||
message CustomSockopt {
|
message CustomSockopt {
|
||||||
string system = 1;
|
string system = 1;
|
||||||
string network = 2;
|
string network = 2;
|
||||||
|
|||||||
@@ -24,11 +24,8 @@ func (c *Config) GetNormalizedPath() string {
|
|||||||
path = "/" + path
|
path = "/" + path
|
||||||
}
|
}
|
||||||
|
|
||||||
if c.GetNormalizedSessionPlacement() == PlacementPath ||
|
if path[len(path)-1] != '/' {
|
||||||
c.GetNormalizedSeqPlacement() == PlacementPath {
|
path = path + "/"
|
||||||
if path[len(path)-1] != '/' {
|
|
||||||
path = path + "/"
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return path
|
return path
|
||||||
|
|||||||
@@ -3,77 +3,16 @@ package splithttp_test
|
|||||||
import (
|
import (
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
|
||||||
. "github.com/xtls/xray-core/transport/internet/splithttp"
|
. "github.com/xtls/xray-core/transport/internet/splithttp"
|
||||||
)
|
)
|
||||||
|
|
||||||
func Test_GetNormalizedPath(t *testing.T) {
|
func Test_GetNormalizedPath(t *testing.T) {
|
||||||
tests := []struct {
|
c := Config{
|
||||||
TestName string
|
Path: "/?world",
|
||||||
Path string
|
|
||||||
SessionIDPlacement string
|
|
||||||
SeqPlacement string
|
|
||||||
Expected string
|
|
||||||
}{
|
|
||||||
{
|
|
||||||
TestName: "default placement keeps trailing slash",
|
|
||||||
Path: "/sh",
|
|
||||||
Expected: "/sh/",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
TestName: "query string is stripped",
|
|
||||||
Path: "/?world",
|
|
||||||
Expected: "/",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
TestName: "both off path drops trailing slash",
|
|
||||||
Path: "/stream",
|
|
||||||
SessionIDPlacement: "query",
|
|
||||||
SeqPlacement: "query",
|
|
||||||
Expected: "/stream",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
TestName: "both off path keeps file-like path",
|
|
||||||
Path: "/stream/filename.extension",
|
|
||||||
SessionIDPlacement: "query",
|
|
||||||
SeqPlacement: "header",
|
|
||||||
Expected: "/stream/filename.extension",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
TestName: "seq in path keeps trailing slash",
|
|
||||||
Path: "/stream",
|
|
||||||
SessionIDPlacement: "query",
|
|
||||||
Expected: "/stream/",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
TestName: "session in path keeps trailing slash",
|
|
||||||
Path: "/stream",
|
|
||||||
SeqPlacement: "cookie",
|
|
||||||
Expected: "/stream/",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
TestName: "existing trailing slash preserved",
|
|
||||||
Path: "/stream/",
|
|
||||||
SessionIDPlacement: "query",
|
|
||||||
SeqPlacement: "query",
|
|
||||||
Expected: "/stream/",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
TestName: "root unchanged",
|
|
||||||
Path: "/",
|
|
||||||
SessionIDPlacement: "query",
|
|
||||||
SeqPlacement: "query",
|
|
||||||
Expected: "/",
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
for _, test := range tests {
|
|
||||||
t.Run(test.TestName, func(t *testing.T) {
|
path := c.GetNormalizedPath()
|
||||||
c := Config{
|
if path != "/" {
|
||||||
Path: test.Path,
|
t.Error("Unexpected: ", path)
|
||||||
SessionIDPlacement: test.SessionIDPlacement,
|
|
||||||
SeqPlacement: test.SeqPlacement,
|
|
||||||
}
|
|
||||||
assert.Equal(t, test.Expected, c.GetNormalizedPath())
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -450,7 +450,7 @@ func (c *Config) GetTLSConfig(opts ...Option) *tls.Config {
|
|||||||
for _, s := range tls.CipherSuites() {
|
for _, s := range tls.CipherSuites() {
|
||||||
id[s.Name] = s.ID
|
id[s.Name] = s.ID
|
||||||
}
|
}
|
||||||
for n := range strings.SplitSeq(c.CipherSuites, ":") {
|
for _, n := range strings.Split(c.CipherSuites, ":") {
|
||||||
if id[n] != 0 {
|
if id[n] != 0 {
|
||||||
config.CipherSuites = append(config.CipherSuites, id[n])
|
config.CipherSuites = append(config.CipherSuites, id[n])
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -55,16 +55,19 @@ func ApplyECH(c *Config, config *tls.Config) error {
|
|||||||
}
|
}
|
||||||
config.EncryptedClientHelloConfigList = ECHConfig
|
config.EncryptedClientHelloConfigList = ECHConfig
|
||||||
}()
|
}()
|
||||||
// query config from dns
|
// direct base64 config
|
||||||
if strings.Contains(c.EchConfigList, "://") {
|
if strings.Contains(c.EchConfigList, "://") {
|
||||||
// parse ECH DNS server in format of "example.com+https://1.1.1.1/dns-query"
|
// query config from dns
|
||||||
parts := strings.SplitN(c.EchConfigList, "+", 2)
|
parts := strings.Split(c.EchConfigList, "+")
|
||||||
if len(parts) == 2 {
|
if len(parts) == 2 {
|
||||||
|
// parse ECH DNS server in format of "example.com+https://1.1.1.1/dns-query"
|
||||||
nameToQuery = parts[0]
|
nameToQuery = parts[0]
|
||||||
DNSServer = parts[1]
|
DNSServer = parts[1]
|
||||||
} else {
|
} else if len(parts) == 1 {
|
||||||
// normal format
|
// normal format
|
||||||
DNSServer = parts[0]
|
DNSServer = parts[0]
|
||||||
|
} else {
|
||||||
|
return errors.New("Invalid ECH DNS server format: ", c.EchConfigList)
|
||||||
}
|
}
|
||||||
if nameToQuery == "" {
|
if nameToQuery == "" {
|
||||||
return errors.New("Using DNS for ECH Config needs serverName or use Server format example.com+https://1.1.1.1/dns-query")
|
return errors.New("Using DNS for ECH Config needs serverName or use Server format example.com+https://1.1.1.1/dns-query")
|
||||||
@@ -74,7 +77,6 @@ func ApplyECH(c *Config, config *tls.Config) error {
|
|||||||
return errors.New("Failed to query ECH DNS record for domain: ", nameToQuery, " at server: ", DNSServer).Base(err)
|
return errors.New("Failed to query ECH DNS record for domain: ", nameToQuery, " at server: ", DNSServer).Base(err)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// direct base64 config
|
|
||||||
ECHConfig, err = base64.StdEncoding.DecodeString(c.EchConfigList)
|
ECHConfig, err = base64.StdEncoding.DecodeString(c.EchConfigList)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.New("Failed to unmarshal ECHConfigList: ", err)
|
return errors.New("Failed to unmarshal ECHConfigList: ", err)
|
||||||
@@ -155,7 +157,7 @@ func QueryRecord(domain string, server string, sockopt *internet.SocketConfig) (
|
|||||||
// If expire is zero value, it means we are in initial state, wait for the query to finish
|
// If expire is zero value, it means we are in initial state, wait for the query to finish
|
||||||
// otherwise return old value immediately and update in a goroutine
|
// otherwise return old value immediately and update in a goroutine
|
||||||
// but if the cache is too old, wait for update
|
// but if the cache is too old, wait for update
|
||||||
if configRecord.expire.IsZero() || configRecord.expire.Add(time.Hour*4).Before(time.Now()) {
|
if configRecord.expire == (time.Time{}) || configRecord.expire.Add(time.Hour*4).Before(time.Now()) {
|
||||||
return echConfigCache.Update(domain, server, false, sockopt)
|
return echConfigCache.Update(domain, server, false, sockopt)
|
||||||
} else {
|
} else {
|
||||||
// If someone already acquired the lock, it means it is updating, do not start another update goroutine
|
// If someone already acquired the lock, it means it is updating, do not start another update goroutine
|
||||||
@@ -254,17 +256,14 @@ func dnsQuery(server string, domain string, sockopt *internet.SocketConfig) ([]b
|
|||||||
}
|
}
|
||||||
dnsResolve = respBody
|
dnsResolve = respBody
|
||||||
} else if strings.HasPrefix(server, "udp://") { // for classic udp dns server
|
} else if strings.HasPrefix(server, "udp://") { // for classic udp dns server
|
||||||
udpServerURL, err := url.Parse(server)
|
udpServerAddr := server[len("udp://"):]
|
||||||
if err != nil {
|
|
||||||
return nil, 0, errors.New("failed to parse udp dns server ", server, " for ECH: ", err)
|
|
||||||
}
|
|
||||||
// default port 53 if not specified
|
// default port 53 if not specified
|
||||||
if udpServerURL.Port() == "" {
|
if !strings.Contains(udpServerAddr, ":") {
|
||||||
udpServerURL.Host = udpServerURL.Host + ":53"
|
udpServerAddr = udpServerAddr + ":53"
|
||||||
}
|
}
|
||||||
dest, err := net.ParseDestination("udp" + ":" + udpServerURL.Host)
|
dest, err := net.ParseDestination("udp" + ":" + udpServerAddr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, 0, errors.New("failed to parse udp dns server ", udpServerURL.Host, " for ECH: ", err)
|
return nil, 0, errors.New("failed to parse udp dns server ", udpServerAddr, " for ECH: ", err)
|
||||||
}
|
}
|
||||||
dnsTimeoutCtx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
|
dnsTimeoutCtx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
|
||||||
defer cancel()
|
defer cancel()
|
||||||
|
|||||||
@@ -19,7 +19,8 @@ func TestECHDial(t *testing.T) {
|
|||||||
// test concurrent Dial(to test cache problem)
|
// test concurrent Dial(to test cache problem)
|
||||||
wg := sync.WaitGroup{}
|
wg := sync.WaitGroup{}
|
||||||
for range 10 {
|
for range 10 {
|
||||||
wg.Go(func() {
|
wg.Add(1)
|
||||||
|
go func() {
|
||||||
TLSConfig := config.GetTLSConfig()
|
TLSConfig := config.GetTLSConfig()
|
||||||
TLSConfig.NextProtos = []string{"http/1.1"}
|
TLSConfig.NextProtos = []string{"http/1.1"}
|
||||||
client := &http.Client{
|
client := &http.Client{
|
||||||
@@ -35,7 +36,8 @@ func TestECHDial(t *testing.T) {
|
|||||||
if !strings.Contains(string(body), "sni=encrypted") {
|
if !strings.Contains(string(body), "sni=encrypted") {
|
||||||
t.Error("ECH Dial success but SNI is not encrypted")
|
t.Error("ECH Dial success but SNI is not encrypted")
|
||||||
}
|
}
|
||||||
})
|
wg.Done()
|
||||||
|
}()
|
||||||
}
|
}
|
||||||
wg.Wait()
|
wg.Wait()
|
||||||
// check cache
|
// check cache
|
||||||
|
|||||||
Reference in New Issue
Block a user