From 3d692eb2088e368c7f60d171a965ce6b57d8dbb7 Mon Sep 17 00:00:00 2001 From: Devman Date: Fri, 30 Jun 2023 13:13:36 +0000 Subject: [PATCH 1/8] !feat(vless): IP restriction Beta, only works for vless for now and it's not perfect needs a lot of testing. --- app/proxyman/inbound/worker.go | 33 ++++++++++++++++++++--- common/protocol/user.go | 2 ++ common/protocol/user.pb.go | 38 +++++++++++++++++---------- common/protocol/user.proto | 3 +++ infra/conf/common.go | 6 +++-- proxy/dokodemo/dokodemo.go | 2 +- proxy/proxy.go | 3 ++- proxy/vless/inbound/inbound.go | 23 +++++++++++++++- transport/internet/stat/connection.go | 6 +++++ 9 files changed, 94 insertions(+), 22 deletions(-) diff --git a/app/proxyman/inbound/worker.go b/app/proxyman/inbound/worker.go index 8ed4090a..8ba87bfa 100644 --- a/app/proxyman/inbound/worker.go +++ b/app/proxyman/inbound/worker.go @@ -42,6 +42,7 @@ type tcpWorker struct { sniffingConfig *proxyman.SniffingConfig uplinkCounter stats.Counter downlinkCounter stats.Counter + ipLimitPool map[session.ID]*stat.UserIpRestriction hub internet.Listener @@ -104,9 +105,18 @@ func (w *tcpWorker) callback(conn stat.Connection) { } ctx = session.ContextWithContent(ctx, content) - if err := w.proxy.Process(ctx, net.Network_TCP, conn, w.dispatcher); err != nil { + // Add this IP address to the pool for futher IP limit check + w.ipLimitPool[sid] = &stat.UserIpRestriction{ + IpAddress: net.IP(conn.RemoteAddr().Network()), + } + + if err := w.proxy.Process(ctx, net.Network_TCP, conn, w.dispatcher, &w.ipLimitPool, w.ipLimitPool[sid]); err != nil { newError("connection ends").Base(err).WriteToLog(session.ExportIDToError(ctx)) } + + // Deletes the IP address from the pool after the connection ends + delete(w.ipLimitPool, sid) + cancel() conn.Close() } @@ -116,6 +126,9 @@ func (w *tcpWorker) Proxy() proxy.Inbound { } func (w *tcpWorker) Start() error { + if len(w.ipLimitPool) == 0 { + w.ipLimitPool = make(map[session.ID]*stat.UserIpRestriction) + } ctx := context.Background() hub, err := internet.ListenTCP(ctx, w.address, w.port, w.stream, func(conn stat.Connection) { go w.callback(conn) @@ -244,6 +257,7 @@ type udpWorker struct { sniffingConfig *proxyman.SniffingConfig uplinkCounter stats.Counter downlinkCounter stats.Counter + ipLimitPool map[session.ID]*stat.UserIpRestriction checker *task.Periodic activeConn map[connID]*udpConn @@ -326,9 +340,19 @@ func (w *udpWorker) callback(b *buf.Buffer, source net.Destination, originalDest content.SniffingRequest.RouteOnly = w.sniffingConfig.RouteOnly } ctx = session.ContextWithContent(ctx, content) - if err := w.proxy.Process(ctx, net.Network_UDP, conn, w.dispatcher); err != nil { + + // Add this IP address to the pool for futher IP limit check + w.ipLimitPool[sid] = &stat.UserIpRestriction{ + IpAddress: net.IP(conn.RemoteAddr().Network()), + } + + if err := w.proxy.Process(ctx, net.Network_UDP, conn, w.dispatcher, &w.ipLimitPool, w.ipLimitPool[sid]); err != nil { newError("connection ends").Base(err).WriteToLog(session.ExportIDToError(ctx)) } + + // Deletes the IP address from the pool after the connection ends + delete(w.ipLimitPool, sid) + conn.Close() // conn not removed by checker TODO may be lock worker here is better if !conn.inactive { @@ -379,6 +403,9 @@ func (w *udpWorker) clean() error { } func (w *udpWorker) Start() error { + if len(w.ipLimitPool) == 0 { + w.ipLimitPool = make(map[session.ID]*stat.UserIpRestriction) + } w.activeConn = make(map[connID]*udpConn, 16) ctx := context.Background() h, err := udp.ListenUDP(ctx, w.address, w.port, w.stream, udp.HubCapacity(256)) @@ -478,7 +505,7 @@ func (w *dsWorker) callback(conn stat.Connection) { } ctx = session.ContextWithContent(ctx, content) - if err := w.proxy.Process(ctx, net.Network_UNIX, conn, w.dispatcher); err != nil { + if err := w.proxy.Process(ctx, net.Network_UNIX, conn, w.dispatcher, nil, nil); err != nil { newError("connection ends").Base(err).WriteToLog(session.ExportIDToError(ctx)) } cancel() diff --git a/common/protocol/user.go b/common/protocol/user.go index 8325f555..9006a4a0 100644 --- a/common/protocol/user.go +++ b/common/protocol/user.go @@ -27,6 +27,7 @@ func (u *User) ToMemoryUser() (*MemoryUser, error) { Account: account, Email: u.Email, Level: u.Level, + IpLimit: u.Ips, }, nil } @@ -36,4 +37,5 @@ type MemoryUser struct { Account Account Email string Level uint32 + IpLimit uint32 } diff --git a/common/protocol/user.pb.go b/common/protocol/user.pb.go index 6f063e73..2e22e41f 100644 --- a/common/protocol/user.pb.go +++ b/common/protocol/user.pb.go @@ -32,6 +32,8 @@ type User struct { // Protocol specific account information. Must be the account proto in one of // the proxies. Account *serial.TypedMessage `protobuf:"bytes,3,opt,name=account,proto3" json:"account,omitempty"` + // Allowed IPs + Ips uint32 `protobuf:"varint,4,opt,name=ips,proto3" json:"ips,omitempty"` } func (x *User) Reset() { @@ -87,6 +89,13 @@ func (x *User) GetAccount() *serial.TypedMessage { return nil } +func (x *User) GetIps() uint32 { + if x != nil { + return x.Ips + } + return 0 +} + var File_common_protocol_user_proto protoreflect.FileDescriptor var file_common_protocol_user_proto_rawDesc = []byte{ @@ -95,20 +104,21 @@ var file_common_protocol_user_proto_rawDesc = []byte{ 0x61, 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x1a, 0x21, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2f, 0x73, 0x65, 0x72, 0x69, 0x61, 0x6c, 0x2f, 0x74, 0x79, 0x70, 0x65, 0x64, 0x5f, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x2e, - 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x6e, 0x0a, 0x04, 0x55, 0x73, 0x65, 0x72, 0x12, 0x14, 0x0a, - 0x05, 0x6c, 0x65, 0x76, 0x65, 0x6c, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x05, 0x6c, 0x65, - 0x76, 0x65, 0x6c, 0x12, 0x14, 0x0a, 0x05, 0x65, 0x6d, 0x61, 0x69, 0x6c, 0x18, 0x02, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x05, 0x65, 0x6d, 0x61, 0x69, 0x6c, 0x12, 0x3a, 0x0a, 0x07, 0x61, 0x63, 0x63, - 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x20, 0x2e, 0x78, 0x72, 0x61, - 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x73, 0x65, 0x72, 0x69, 0x61, 0x6c, 0x2e, - 0x54, 0x79, 0x70, 0x65, 0x64, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x52, 0x07, 0x61, 0x63, - 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x42, 0x5e, 0x0a, 0x18, 0x63, 0x6f, 0x6d, 0x2e, 0x78, 0x72, 0x61, - 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, - 0x6c, 0x50, 0x01, 0x5a, 0x29, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, - 0x78, 0x74, 0x6c, 0x73, 0x2f, 0x78, 0x72, 0x61, 0x79, 0x2d, 0x63, 0x6f, 0x72, 0x65, 0x2f, 0x63, - 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0xaa, 0x02, - 0x14, 0x58, 0x72, 0x61, 0x79, 0x2e, 0x43, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x50, 0x72, 0x6f, - 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x80, 0x01, 0x0a, 0x04, 0x55, 0x73, 0x65, 0x72, 0x12, 0x14, + 0x0a, 0x05, 0x6c, 0x65, 0x76, 0x65, 0x6c, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x05, 0x6c, + 0x65, 0x76, 0x65, 0x6c, 0x12, 0x14, 0x0a, 0x05, 0x65, 0x6d, 0x61, 0x69, 0x6c, 0x18, 0x02, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x05, 0x65, 0x6d, 0x61, 0x69, 0x6c, 0x12, 0x3a, 0x0a, 0x07, 0x61, 0x63, + 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x20, 0x2e, 0x78, 0x72, + 0x61, 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x73, 0x65, 0x72, 0x69, 0x61, 0x6c, + 0x2e, 0x54, 0x79, 0x70, 0x65, 0x64, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x52, 0x07, 0x61, + 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x10, 0x0a, 0x03, 0x69, 0x70, 0x73, 0x18, 0x04, 0x20, + 0x01, 0x28, 0x0d, 0x52, 0x03, 0x69, 0x70, 0x73, 0x42, 0x5e, 0x0a, 0x18, 0x63, 0x6f, 0x6d, 0x2e, + 0x78, 0x72, 0x61, 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x70, 0x72, 0x6f, 0x74, + 0x6f, 0x63, 0x6f, 0x6c, 0x50, 0x01, 0x5a, 0x29, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, + 0x6f, 0x6d, 0x2f, 0x78, 0x74, 0x6c, 0x73, 0x2f, 0x78, 0x72, 0x61, 0x79, 0x2d, 0x63, 0x6f, 0x72, + 0x65, 0x2f, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, + 0x6c, 0xaa, 0x02, 0x14, 0x58, 0x72, 0x61, 0x79, 0x2e, 0x43, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, + 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( diff --git a/common/protocol/user.proto b/common/protocol/user.proto index 44770edf..3632eeaa 100644 --- a/common/protocol/user.proto +++ b/common/protocol/user.proto @@ -16,4 +16,7 @@ message User { // Protocol specific account information. Must be the account proto in one of // the proxies. xray.common.serial.TypedMessage account = 3; + + // Allowed IPs + uint32 ips = 4; } diff --git a/infra/conf/common.go b/infra/conf/common.go index f8f56056..ba5abb9d 100644 --- a/infra/conf/common.go +++ b/infra/conf/common.go @@ -231,13 +231,15 @@ func (list *PortList) UnmarshalJSON(data []byte) error { } type User struct { - EmailString string `json:"email"` - LevelByte byte `json:"level"` + EmailString string `json:"email"` + LevelByte byte `json:"level"` + IpLimitByte byte `json:"ips"` } func (v *User) Build() *protocol.User { return &protocol.User{ Email: v.EmailString, Level: uint32(v.LevelByte), + Ips: uint32(v.IpLimitByte), } } diff --git a/proxy/dokodemo/dokodemo.go b/proxy/dokodemo/dokodemo.go index 42d8256f..99bb2534 100644 --- a/proxy/dokodemo/dokodemo.go +++ b/proxy/dokodemo/dokodemo.go @@ -76,7 +76,7 @@ type hasHandshakeAddress interface { } // Process implements proxy.Inbound. -func (d *DokodemoDoor) Process(ctx context.Context, network net.Network, conn stat.Connection, dispatcher routing.Dispatcher) error { +func (d *DokodemoDoor) Process(ctx context.Context, network net.Network, conn stat.Connection, dispatcher routing.Dispatcher, _ *map[session.ID]*stat.UserIpRestriction, _ *stat.UserIpRestriction) error { newError("processing connection from: ", conn.RemoteAddr()).AtDebug().WriteToLog(session.ExportIDToError(ctx)) dest := net.Destination{ Network: network, diff --git a/proxy/proxy.go b/proxy/proxy.go index fb52605c..3600a670 100644 --- a/proxy/proxy.go +++ b/proxy/proxy.go @@ -10,6 +10,7 @@ import ( "github.com/xtls/xray-core/common/net" "github.com/xtls/xray-core/common/protocol" + "github.com/xtls/xray-core/common/session" "github.com/xtls/xray-core/features/routing" "github.com/xtls/xray-core/transport" "github.com/xtls/xray-core/transport/internet" @@ -22,7 +23,7 @@ type Inbound interface { Network() []net.Network // Process processes a connection of given network. If necessary, the Inbound can dispatch the connection to an Outbound. - Process(context.Context, net.Network, stat.Connection, routing.Dispatcher) error + Process(context.Context, net.Network, stat.Connection, routing.Dispatcher, *map[session.ID]*stat.UserIpRestriction, *stat.UserIpRestriction) error } // An Outbound process outbound connections. diff --git a/proxy/vless/inbound/inbound.go b/proxy/vless/inbound/inbound.go index 8653e1e3..00b553f8 100644 --- a/proxy/vless/inbound/inbound.go +++ b/proxy/vless/inbound/inbound.go @@ -178,7 +178,7 @@ func (*Handler) Network() []net.Network { } // Process implements proxy.Inbound.Process(). -func (h *Handler) Process(ctx context.Context, network net.Network, connection stat.Connection, dispatcher routing.Dispatcher) error { +func (h *Handler) Process(ctx context.Context, network net.Network, connection stat.Connection, dispatcher routing.Dispatcher, usrIpRstrct *map[session.ID]*stat.UserIpRestriction, connIp *stat.UserIpRestriction) error { sid := session.ExportIDToError(ctx) iConn := connection @@ -447,6 +447,27 @@ func (h *Handler) Process(ctx context.Context, network net.Network, connection s // Flow: requestAddons.Flow, } + if (request.User.IpLimit > 0) { + addr := connection.RemoteAddr().(*net.TCPAddr) + user := account.ID.String() + + uniqueIps := make(map[string]bool) + // Iterate through the connections and find unique used IP addresses withing last 30 seconds. + for _, conn := range *usrIpRstrct { + if conn.User == user && !conn.IpAddress.Equal(addr.IP) && ((time.Now().Unix() - conn.Time) < 30) { + uniqueIps[conn.IpAddress.String()] = true + } + } + + if (len(uniqueIps) >= int(request.User.IpLimit)) { + return newError("User ", user, " has exceeded their allowed IPs.").AtWarning() + } + + connIp.IpAddress = addr.IP + connIp.User = user + connIp.Time = time.Now().Unix() + } + var netConn net.Conn var rawConn syscall.RawConn var input *bytes.Reader diff --git a/transport/internet/stat/connection.go b/transport/internet/stat/connection.go index 6921943d..4a3fd681 100644 --- a/transport/internet/stat/connection.go +++ b/transport/internet/stat/connection.go @@ -6,6 +6,12 @@ import ( "github.com/xtls/xray-core/features/stats" ) +type UserIpRestriction struct { + User string + IpAddress net.IP + Time int64 +} + type Connection interface { net.Conn } From 97fa8827e24101d35e116101e83c3df04d76adf2 Mon Sep 17 00:00:00 2001 From: Devman Date: Fri, 30 Jun 2023 21:28:24 +0000 Subject: [PATCH 2/8] feat(vmess): ip restriction for vmess --- proxy/vmess/inbound/inbound.go | 22 +++++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/proxy/vmess/inbound/inbound.go b/proxy/vmess/inbound/inbound.go index f48a26e1..e39d1a89 100644 --- a/proxy/vmess/inbound/inbound.go +++ b/proxy/vmess/inbound/inbound.go @@ -208,7 +208,7 @@ func transferResponse(timer signal.ActivityUpdater, session *encoding.ServerSess } // Process implements proxy.Inbound.Process(). -func (h *Handler) Process(ctx context.Context, network net.Network, connection stat.Connection, dispatcher routing.Dispatcher) error { +func (h *Handler) Process(ctx context.Context, network net.Network, connection stat.Connection, dispatcher routing.Dispatcher, usrIpRstrct *map[session.ID]*stat.UserIpRestriction, connIp *stat.UserIpRestriction) error { sessionPolicy := h.policyManager.ForLevel(0) if err := connection.SetReadDeadline(time.Now().Add(sessionPolicy.Timeouts.Handshake)); err != nil { return newError("unable to set read deadline").Base(err).AtWarning() @@ -264,6 +264,26 @@ func (h *Handler) Process(ctx context.Context, network net.Network, connection s sessionPolicy = h.policyManager.ForLevel(request.User.Level) + if (request.User.IpLimit > 0) { + addr := connection.RemoteAddr().(*net.TCPAddr) + + uniqueIps := make(map[string]bool) + // Iterate through the connections and find unique used IP addresses withing last 30 seconds. + for _, conn := range *usrIpRstrct { + if conn.User == request.User.Email && !conn.IpAddress.Equal(addr.IP) && ((time.Now().Unix() - conn.Time) < 30) { + uniqueIps[conn.IpAddress.String()] = true + } + } + + if (len(uniqueIps) >= int(request.User.IpLimit)) { + return newError("User ", request.User.Email, " has exceeded their allowed IPs.").AtWarning() + } + + connIp.IpAddress = addr.IP + connIp.User = request.User.Email + connIp.Time = time.Now().Unix() + } + ctx, cancel := context.WithCancel(ctx) timer := signal.CancelAfterInactivity(ctx, cancel, sessionPolicy.Timeouts.ConnectionIdle) From 343adca4c03698a4d8506aac1d00132632d0a434 Mon Sep 17 00:00:00 2001 From: Devman Date: Fri, 30 Jun 2023 21:28:36 +0000 Subject: [PATCH 3/8] feat(trojan): ip restriction for trojan --- proxy/trojan/server.go | 22 +++++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/proxy/trojan/server.go b/proxy/trojan/server.go index 368374ff..f952cdf9 100644 --- a/proxy/trojan/server.go +++ b/proxy/trojan/server.go @@ -131,7 +131,7 @@ func (s *Server) Network() []net.Network { } // Process implements proxy.Inbound.Process(). -func (s *Server) Process(ctx context.Context, network net.Network, conn stat.Connection, dispatcher routing.Dispatcher) error { +func (s *Server) Process(ctx context.Context, network net.Network, conn stat.Connection, dispatcher routing.Dispatcher, usrIpRstrct *map[session.ID]*stat.UserIpRestriction, connIp *stat.UserIpRestriction) error { sid := session.ExportIDToError(ctx) iConn := conn @@ -221,6 +221,26 @@ func (s *Server) Process(ctx context.Context, network net.Network, conn stat.Con inbound.User = user sessionPolicy = s.policyManager.ForLevel(user.Level) + if (user.IpLimit > 0) { + addr := conn.RemoteAddr().(*net.TCPAddr) + + uniqueIps := make(map[string]bool) + // Iterate through the connections and find unique used IP addresses withing last 30 seconds. + for _, conn := range *usrIpRstrct { + if conn.User == user.Email && !conn.IpAddress.Equal(addr.IP) && ((time.Now().Unix() - conn.Time) < 30) { + uniqueIps[conn.IpAddress.String()] = true + } + } + + if (len(uniqueIps) >= int(user.IpLimit)) { + return newError("User ", user, " has exceeded their allowed IPs.").AtWarning() + } + + connIp.IpAddress = addr.IP + connIp.User = user.Email + connIp.Time = time.Now().Unix() + } + if destination.Network == net.Network_UDP { // handle udp request return s.handleUDPPayload(ctx, &PacketReader{Reader: clientReader}, &PacketWriter{Writer: conn}, dispatcher) } From 6a0ff0efcec6c5adf74e7dd572ea5f5975a3b881 Mon Sep 17 00:00:00 2001 From: Devman Date: Fri, 30 Jun 2023 21:29:07 +0000 Subject: [PATCH 4/8] chore(vless): use email instead of id for ip restriction --- proxy/vless/inbound/inbound.go | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/proxy/vless/inbound/inbound.go b/proxy/vless/inbound/inbound.go index 00b553f8..9f4bed30 100644 --- a/proxy/vless/inbound/inbound.go +++ b/proxy/vless/inbound/inbound.go @@ -449,22 +449,21 @@ func (h *Handler) Process(ctx context.Context, network net.Network, connection s if (request.User.IpLimit > 0) { addr := connection.RemoteAddr().(*net.TCPAddr) - user := account.ID.String() uniqueIps := make(map[string]bool) // Iterate through the connections and find unique used IP addresses withing last 30 seconds. for _, conn := range *usrIpRstrct { - if conn.User == user && !conn.IpAddress.Equal(addr.IP) && ((time.Now().Unix() - conn.Time) < 30) { + if conn.User == request.User.Email && !conn.IpAddress.Equal(addr.IP) && ((time.Now().Unix() - conn.Time) < 30) { uniqueIps[conn.IpAddress.String()] = true } } if (len(uniqueIps) >= int(request.User.IpLimit)) { - return newError("User ", user, " has exceeded their allowed IPs.").AtWarning() + return newError("User ", request.User.Email, " has exceeded their allowed IPs.").AtWarning() } connIp.IpAddress = addr.IP - connIp.User = user + connIp.User = request.User.Email connIp.Time = time.Now().Unix() } From e1843be1c89af4026d40d793b971caab4538fd23 Mon Sep 17 00:00:00 2001 From: amir-devman Date: Wed, 19 Jul 2023 23:16:29 +0000 Subject: [PATCH 5/8] fix(ip-restriction): protect usrIpRstrct from concurrent access --- proxy/trojan/server.go | 6 ++++++ proxy/vless/inbound/inbound.go | 6 ++++++ proxy/vmess/inbound/inbound.go | 4 ++++ 3 files changed, 16 insertions(+) diff --git a/proxy/trojan/server.go b/proxy/trojan/server.go index f952cdf9..da800fbb 100644 --- a/proxy/trojan/server.go +++ b/proxy/trojan/server.go @@ -5,6 +5,7 @@ import ( "io" "strconv" "strings" + "sync" "time" "github.com/xtls/xray-core/common" @@ -35,6 +36,8 @@ func init() { // Server is an inbound connection handler that handles messages in trojan protocol. type Server struct { + sync.Mutex + policyManager policy.Manager validator *Validator fallbacks map[string]map[string]map[string]*Fallback // or nil @@ -225,12 +228,15 @@ func (s *Server) Process(ctx context.Context, network net.Network, conn stat.Con addr := conn.RemoteAddr().(*net.TCPAddr) uniqueIps := make(map[string]bool) + + s.Lock() // Iterate through the connections and find unique used IP addresses withing last 30 seconds. for _, conn := range *usrIpRstrct { if conn.User == user.Email && !conn.IpAddress.Equal(addr.IP) && ((time.Now().Unix() - conn.Time) < 30) { uniqueIps[conn.IpAddress.String()] = true } } + s.Unlock() if (len(uniqueIps) >= int(user.IpLimit)) { return newError("User ", user, " has exceeded their allowed IPs.").AtWarning() diff --git a/proxy/vless/inbound/inbound.go b/proxy/vless/inbound/inbound.go index 9f4bed30..708176ca 100644 --- a/proxy/vless/inbound/inbound.go +++ b/proxy/vless/inbound/inbound.go @@ -10,6 +10,7 @@ import ( "reflect" "strconv" "strings" + "sync" "syscall" "time" "unsafe" @@ -53,6 +54,8 @@ func init() { // Handler is an inbound connection handler that handles messages in VLess protocol. type Handler struct { + sync.Mutex + inboundHandlerManager feature_inbound.Manager policyManager policy.Manager validator *vless.Validator @@ -451,12 +454,15 @@ func (h *Handler) Process(ctx context.Context, network net.Network, connection s addr := connection.RemoteAddr().(*net.TCPAddr) uniqueIps := make(map[string]bool) + + h.Lock() // Iterate through the connections and find unique used IP addresses withing last 30 seconds. for _, conn := range *usrIpRstrct { if conn.User == request.User.Email && !conn.IpAddress.Equal(addr.IP) && ((time.Now().Unix() - conn.Time) < 30) { uniqueIps[conn.IpAddress.String()] = true } } + h.Unlock() if (len(uniqueIps) >= int(request.User.IpLimit)) { return newError("User ", request.User.Email, " has exceeded their allowed IPs.").AtWarning() diff --git a/proxy/vmess/inbound/inbound.go b/proxy/vmess/inbound/inbound.go index e39d1a89..b014be88 100644 --- a/proxy/vmess/inbound/inbound.go +++ b/proxy/vmess/inbound/inbound.go @@ -97,6 +97,8 @@ func (v *userByEmail) Remove(email string) bool { // Handler is an inbound connection handler that handles messages in VMess protocol. type Handler struct { + sync.Mutex + policyManager policy.Manager inboundHandlerManager feature_inbound.Manager clients *vmess.TimedUserValidator @@ -268,12 +270,14 @@ func (h *Handler) Process(ctx context.Context, network net.Network, connection s addr := connection.RemoteAddr().(*net.TCPAddr) uniqueIps := make(map[string]bool) + h.Lock() // Iterate through the connections and find unique used IP addresses withing last 30 seconds. for _, conn := range *usrIpRstrct { if conn.User == request.User.Email && !conn.IpAddress.Equal(addr.IP) && ((time.Now().Unix() - conn.Time) < 30) { uniqueIps[conn.IpAddress.String()] = true } } + h.Unlock() if (len(uniqueIps) >= int(request.User.IpLimit)) { return newError("User ", request.User.Email, " has exceeded their allowed IPs.").AtWarning() From 38d5d4d1cfce71cc763080b0985501d83c92d524 Mon Sep 17 00:00:00 2001 From: root Date: Sat, 18 Nov 2023 15:25:58 +0000 Subject: [PATCH 6/8] Merge branch 'main' of https://github.com/XTLS/Xray-core into main --- .github/build/friendly-filenames.json | 1 - .github/workflows/release.yml | 6 +- README.md | 2 + app/commander/config.pb.go | 2 +- app/dispatcher/config.pb.go | 2 +- app/dns/config.pb.go | 2 +- app/dns/fakedns/fakedns.pb.go | 2 +- app/log/command/config.pb.go | 2 +- app/log/config.pb.go | 2 +- app/metrics/config.pb.go | 2 +- app/observatory/command/command.pb.go | 2 +- app/observatory/config.pb.go | 2 +- app/policy/config.pb.go | 2 +- app/proxyman/command/command.pb.go | 2 +- app/proxyman/config.pb.go | 2 +- app/reverse/config.pb.go | 2 +- app/router/command/command.pb.go | 2 +- app/router/config.pb.go | 2 +- app/stats/command/command.pb.go | 2 +- app/stats/config.pb.go | 2 +- common/buf/readv_reader.go | 2 +- common/log/log.pb.go | 2 +- common/net/address.pb.go | 2 +- common/net/destination.pb.go | 2 +- common/net/network.pb.go | 2 +- common/net/port.pb.go | 2 +- common/platform/others.go | 6 +- common/platform/platform.go | 27 +- common/platform/windows.go | 6 +- common/protocol/headers.pb.go | 2 +- common/protocol/server_spec.pb.go | 2 +- common/protocol/user.pb.go | 2 +- common/serial/typed_message.pb.go | 2 +- common/xudp/errors.generated.go | 9 + common/xudp/xudp.go | 15 +- core/config.pb.go | 2 +- core/core.go | 2 +- core/xray.go | 5 +- features/policy/policy.go | 6 +- go.mod | 60 +-- go.sum | 126 ++--- infra/conf/common.go | 6 +- infra/conf/freedom.go | 25 +- infra/conf/transport_internet.go | 24 +- infra/conf/wireguard.go | 96 ++-- infra/conf/wireguard_test.go | 21 +- infra/conf/xray.go | 45 +- infra/conf/xray_test.go | 8 +- main/commands/all/x25519.go | 15 +- proxy/blackhole/config.pb.go | 2 +- proxy/dns/config.pb.go | 2 +- proxy/dokodemo/config.pb.go | 2 +- proxy/freedom/config.go | 43 +- proxy/freedom/config.pb.go | 79 ++-- proxy/freedom/config.proto | 7 + proxy/freedom/freedom.go | 38 +- proxy/http/config.pb.go | 2 +- proxy/loopback/config.pb.go | 2 +- proxy/proxy.go | 2 +- proxy/shadowsocks/config.pb.go | 2 +- proxy/shadowsocks_2022/config.pb.go | 2 +- proxy/socks/config.pb.go | 2 +- proxy/trojan/config.pb.go | 2 +- proxy/vless/account.pb.go | 2 +- proxy/vless/encoding/addons.pb.go | 2 +- proxy/vless/inbound/config.pb.go | 2 +- proxy/vless/outbound/config.pb.go | 2 +- proxy/vmess/account.pb.go | 2 +- proxy/vmess/inbound/config.pb.go | 2 +- proxy/vmess/outbound/config.pb.go | 2 +- proxy/vmess/outbound/outbound.go | 2 +- proxy/wireguard/bind.go | 158 +++---- proxy/wireguard/client.go | 255 ++++++++++ proxy/wireguard/config.go | 32 ++ proxy/wireguard/config.pb.go | 155 ++++-- proxy/wireguard/config.proto | 32 +- proxy/wireguard/gvisortun/tun.go | 230 +++++++++ proxy/wireguard/server.go | 185 ++++++++ proxy/wireguard/tun.go | 440 +++++++----------- proxy/wireguard/tun_default.go | 16 + proxy/wireguard/tun_linux.go | 237 ++++++++++ proxy/wireguard/wireguard.go | 302 +++--------- transport/global/config.pb.go | 2 +- transport/internet/config.go | 43 ++ transport/internet/config.pb.go | 77 ++- transport/internet/config.proto | 7 + transport/internet/dialer.go | 36 +- transport/internet/domainsocket/config.pb.go | 2 +- transport/internet/grpc/config.pb.go | 2 +- transport/internet/grpc/encoding/stream.pb.go | 2 +- transport/internet/headers/dns/config.pb.go | 2 +- transport/internet/headers/http/config.pb.go | 2 +- transport/internet/headers/noop/config.pb.go | 2 +- transport/internet/headers/srtp/config.pb.go | 2 +- transport/internet/headers/tls/config.pb.go | 2 +- transport/internet/headers/utp/config.pb.go | 2 +- .../internet/headers/wechat/config.pb.go | 2 +- .../internet/headers/wireguard/config.pb.go | 2 +- transport/internet/http/config.pb.go | 2 +- transport/internet/kcp/config.pb.go | 2 +- transport/internet/quic/config.pb.go | 2 +- transport/internet/quic/dialer.go | 15 +- transport/internet/reality/config.pb.go | 2 +- transport/internet/reality/reality.go | 17 +- transport/internet/sockopt_darwin.go | 32 +- transport/internet/sockopt_other.go | 4 +- transport/internet/tcp/config.pb.go | 2 +- transport/internet/tls/config.pb.go | 2 +- transport/internet/udp/config.pb.go | 2 +- transport/internet/websocket/config.pb.go | 2 +- transport/internet/websocket/dialer.go | 8 +- 111 files changed, 2078 insertions(+), 1013 deletions(-) create mode 100644 common/xudp/errors.generated.go create mode 100644 proxy/wireguard/client.go create mode 100644 proxy/wireguard/config.go create mode 100644 proxy/wireguard/gvisortun/tun.go create mode 100644 proxy/wireguard/server.go create mode 100644 proxy/wireguard/tun_default.go create mode 100644 proxy/wireguard/tun_linux.go diff --git a/.github/build/friendly-filenames.json b/.github/build/friendly-filenames.json index f7a3e526..d4001f70 100644 --- a/.github/build/friendly-filenames.json +++ b/.github/build/friendly-filenames.json @@ -2,7 +2,6 @@ "android-arm64": { "friendlyName": "android-arm64-v8a" }, "darwin-amd64": { "friendlyName": "macos-64" }, "darwin-arm64": { "friendlyName": "macos-arm64-v8a" }, - "dragonfly-amd64": { "friendlyName": "dragonfly-64" }, "freebsd-386": { "friendlyName": "freebsd-32" }, "freebsd-amd64": { "friendlyName": "freebsd-64" }, "freebsd-arm64": { "friendlyName": "freebsd-arm64-v8a" }, diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 30b96142..57e14693 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -70,12 +70,10 @@ jobs: strategy: matrix: # Include amd64 on all platforms. - goos: [windows, freebsd, openbsd, linux, dragonfly, darwin] + goos: [windows, freebsd, openbsd, linux, darwin] goarch: [amd64, 386] exclude: - # Exclude i386 on darwin and dragonfly. - - goarch: 386 - goos: dragonfly + # Exclude i386 on darwin - goarch: 386 goos: darwin include: diff --git a/README.md b/README.md index a91c4373..7d84f044 100644 --- a/README.md +++ b/README.md @@ -91,6 +91,8 @@ - iOS & macOS arm64 - [Shadowrocket](https://apps.apple.com/app/shadowrocket/id932747118) +- Xray Tools + - [xray-knife](https://github.com/lilendian0x00/xray-knife) - Xray Wrapper - [XTLS/libXray](https://github.com/XTLS/libXray) - [xtlsapi](https://github.com/hiddify/xtlsapi) diff --git a/app/commander/config.pb.go b/app/commander/config.pb.go index 73037653..32dd608a 100644 --- a/app/commander/config.pb.go +++ b/app/commander/config.pb.go @@ -1,6 +1,6 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.30.0 +// protoc-gen-go v1.31.0 // protoc v4.23.1 // source: app/commander/config.proto diff --git a/app/dispatcher/config.pb.go b/app/dispatcher/config.pb.go index e0a55ab2..1512f186 100644 --- a/app/dispatcher/config.pb.go +++ b/app/dispatcher/config.pb.go @@ -1,6 +1,6 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.30.0 +// protoc-gen-go v1.31.0 // protoc v4.23.1 // source: app/dispatcher/config.proto diff --git a/app/dns/config.pb.go b/app/dns/config.pb.go index 8a066a9e..2d84fe72 100644 --- a/app/dns/config.pb.go +++ b/app/dns/config.pb.go @@ -1,6 +1,6 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.30.0 +// protoc-gen-go v1.31.0 // protoc v4.23.1 // source: app/dns/config.proto diff --git a/app/dns/fakedns/fakedns.pb.go b/app/dns/fakedns/fakedns.pb.go index dc9970f9..dea59f99 100644 --- a/app/dns/fakedns/fakedns.pb.go +++ b/app/dns/fakedns/fakedns.pb.go @@ -1,6 +1,6 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.30.0 +// protoc-gen-go v1.31.0 // protoc v4.23.1 // source: app/dns/fakedns/fakedns.proto diff --git a/app/log/command/config.pb.go b/app/log/command/config.pb.go index 6a3d6602..21637a8b 100644 --- a/app/log/command/config.pb.go +++ b/app/log/command/config.pb.go @@ -1,6 +1,6 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.30.0 +// protoc-gen-go v1.31.0 // protoc v4.23.1 // source: app/log/command/config.proto diff --git a/app/log/config.pb.go b/app/log/config.pb.go index 0bc09d40..7829713c 100644 --- a/app/log/config.pb.go +++ b/app/log/config.pb.go @@ -1,6 +1,6 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.30.0 +// protoc-gen-go v1.31.0 // protoc v4.23.1 // source: app/log/config.proto diff --git a/app/metrics/config.pb.go b/app/metrics/config.pb.go index 2cf67618..a93bbb64 100644 --- a/app/metrics/config.pb.go +++ b/app/metrics/config.pb.go @@ -1,6 +1,6 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.30.0 +// protoc-gen-go v1.31.0 // protoc v4.23.1 // source: app/metrics/config.proto diff --git a/app/observatory/command/command.pb.go b/app/observatory/command/command.pb.go index 9eab1533..df531537 100644 --- a/app/observatory/command/command.pb.go +++ b/app/observatory/command/command.pb.go @@ -1,6 +1,6 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.30.0 +// protoc-gen-go v1.31.0 // protoc v4.23.1 // source: app/observatory/command/command.proto diff --git a/app/observatory/config.pb.go b/app/observatory/config.pb.go index 741da506..43eb7196 100644 --- a/app/observatory/config.pb.go +++ b/app/observatory/config.pb.go @@ -1,6 +1,6 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.30.0 +// protoc-gen-go v1.31.0 // protoc v4.23.1 // source: app/observatory/config.proto diff --git a/app/policy/config.pb.go b/app/policy/config.pb.go index 9841fff0..c8b4311c 100644 --- a/app/policy/config.pb.go +++ b/app/policy/config.pb.go @@ -1,6 +1,6 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.30.0 +// protoc-gen-go v1.31.0 // protoc v4.23.1 // source: app/policy/config.proto diff --git a/app/proxyman/command/command.pb.go b/app/proxyman/command/command.pb.go index 9add8afb..458179e5 100644 --- a/app/proxyman/command/command.pb.go +++ b/app/proxyman/command/command.pb.go @@ -1,6 +1,6 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.30.0 +// protoc-gen-go v1.31.0 // protoc v4.23.1 // source: app/proxyman/command/command.proto diff --git a/app/proxyman/config.pb.go b/app/proxyman/config.pb.go index cabc09dd..db41ad75 100644 --- a/app/proxyman/config.pb.go +++ b/app/proxyman/config.pb.go @@ -1,6 +1,6 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.30.0 +// protoc-gen-go v1.31.0 // protoc v4.23.1 // source: app/proxyman/config.proto diff --git a/app/reverse/config.pb.go b/app/reverse/config.pb.go index 0e1bc941..83c0709b 100644 --- a/app/reverse/config.pb.go +++ b/app/reverse/config.pb.go @@ -1,6 +1,6 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.30.0 +// protoc-gen-go v1.31.0 // protoc v4.23.1 // source: app/reverse/config.proto diff --git a/app/router/command/command.pb.go b/app/router/command/command.pb.go index eb7c8530..18296ff9 100644 --- a/app/router/command/command.pb.go +++ b/app/router/command/command.pb.go @@ -1,6 +1,6 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.30.0 +// protoc-gen-go v1.31.0 // protoc v4.23.1 // source: app/router/command/command.proto diff --git a/app/router/config.pb.go b/app/router/config.pb.go index 3fd01cc7..cfe5a8fc 100644 --- a/app/router/config.pb.go +++ b/app/router/config.pb.go @@ -1,6 +1,6 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.30.0 +// protoc-gen-go v1.31.0 // protoc v4.23.1 // source: app/router/config.proto diff --git a/app/stats/command/command.pb.go b/app/stats/command/command.pb.go index 6d4d0d38..089dd183 100644 --- a/app/stats/command/command.pb.go +++ b/app/stats/command/command.pb.go @@ -1,6 +1,6 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.30.0 +// protoc-gen-go v1.31.0 // protoc v4.23.1 // source: app/stats/command/command.proto diff --git a/app/stats/config.pb.go b/app/stats/config.pb.go index 216da044..7c944e9b 100644 --- a/app/stats/config.pb.go +++ b/app/stats/config.pb.go @@ -1,6 +1,6 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.30.0 +// protoc-gen-go v1.31.0 // protoc v4.23.1 // source: app/stats/config.proto diff --git a/common/buf/readv_reader.go b/common/buf/readv_reader.go index f897ccca..bcd0f0ed 100644 --- a/common/buf/readv_reader.go +++ b/common/buf/readv_reader.go @@ -147,7 +147,7 @@ var useReadv bool func init() { const defaultFlagValue = "NOT_DEFINED_AT_ALL" - value := platform.NewEnvFlag("xray.buf.readv").GetValue(func() string { return defaultFlagValue }) + value := platform.NewEnvFlag(platform.UseReadV).GetValue(func() string { return defaultFlagValue }) switch value { case defaultFlagValue, "auto", "enable": useReadv = true diff --git a/common/log/log.pb.go b/common/log/log.pb.go index 92ea0811..871d019b 100644 --- a/common/log/log.pb.go +++ b/common/log/log.pb.go @@ -1,6 +1,6 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.30.0 +// protoc-gen-go v1.31.0 // protoc v4.23.1 // source: common/log/log.proto diff --git a/common/net/address.pb.go b/common/net/address.pb.go index 82240db3..c0ebb72a 100644 --- a/common/net/address.pb.go +++ b/common/net/address.pb.go @@ -1,6 +1,6 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.30.0 +// protoc-gen-go v1.31.0 // protoc v4.23.1 // source: common/net/address.proto diff --git a/common/net/destination.pb.go b/common/net/destination.pb.go index ea6339cb..ee775c7b 100644 --- a/common/net/destination.pb.go +++ b/common/net/destination.pb.go @@ -1,6 +1,6 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.30.0 +// protoc-gen-go v1.31.0 // protoc v4.23.1 // source: common/net/destination.proto diff --git a/common/net/network.pb.go b/common/net/network.pb.go index 18e0df4b..39a1dcd3 100644 --- a/common/net/network.pb.go +++ b/common/net/network.pb.go @@ -1,6 +1,6 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.30.0 +// protoc-gen-go v1.31.0 // protoc v4.23.1 // source: common/net/network.proto diff --git a/common/net/port.pb.go b/common/net/port.pb.go index 255bd940..791e4e7e 100644 --- a/common/net/port.pb.go +++ b/common/net/port.pb.go @@ -1,6 +1,6 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.30.0 +// protoc-gen-go v1.31.0 // protoc v4.23.1 // source: common/net/port.proto diff --git a/common/platform/others.go b/common/platform/others.go index ff45a054..7401a526 100644 --- a/common/platform/others.go +++ b/common/platform/others.go @@ -17,15 +17,13 @@ func LineSeparator() string { } func GetToolLocation(file string) string { - const name = "xray.location.tool" - toolPath := EnvFlag{Name: name, AltName: NormalizeEnvName(name)}.GetValue(getExecutableDir) + toolPath := NewEnvFlag(ToolLocation).GetValue(getExecutableDir) return filepath.Join(toolPath, file) } // GetAssetLocation searches for `file` in certain locations func GetAssetLocation(file string) string { - const name = "xray.location.asset" - assetPath := NewEnvFlag(name).GetValue(getExecutableDir) + assetPath := NewEnvFlag(AssetLocation).GetValue(getExecutableDir) defPath := filepath.Join(assetPath, file) for _, p := range []string{ defPath, diff --git a/common/platform/platform.go b/common/platform/platform.go index d5149db4..51e25447 100644 --- a/common/platform/platform.go +++ b/common/platform/platform.go @@ -7,6 +7,24 @@ import ( "strings" ) +const ( + PluginLocation = "xray.location.plugin" + ConfigLocation = "xray.location.config" + ConfdirLocation = "xray.location.confdir" + ToolLocation = "xray.location.tool" + AssetLocation = "xray.location.asset" + + UseReadV = "xray.buf.readv" + UseFreedomSplice = "xray.buf.splice" + UseVmessPadding = "xray.vmess.padding" + UseCone = "xray.cone.disabled" + + BufferSize = "xray.ray.buffer.size" + BrowserDialerAddress = "xray.browser.dialer" + XUDPLog = "xray.xudp.show" + XUDPBaseKey = "xray.xudp.basekey" +) + type EnvFlag struct { Name string AltName string @@ -67,20 +85,17 @@ func getExecutableSubDir(dir string) func() string { } func GetPluginDirectory() string { - const name = "xray.location.plugin" - pluginDir := NewEnvFlag(name).GetValue(getExecutableSubDir("plugins")) + pluginDir := NewEnvFlag(PluginLocation).GetValue(getExecutableSubDir("plugins")) return pluginDir } func GetConfigurationPath() string { - const name = "xray.location.config" - configPath := NewEnvFlag(name).GetValue(getExecutableDir) + configPath := NewEnvFlag(ConfigLocation).GetValue(getExecutableDir) return filepath.Join(configPath, "config.json") } // GetConfDirPath reads "xray.location.confdir" func GetConfDirPath() string { - const name = "xray.location.confdir" - configPath := NewEnvFlag(name).GetValue(func() string { return "" }) + configPath := NewEnvFlag(ConfdirLocation).GetValue(func() string { return "" }) return configPath } diff --git a/common/platform/windows.go b/common/platform/windows.go index a568d5ae..2aeca80c 100644 --- a/common/platform/windows.go +++ b/common/platform/windows.go @@ -15,14 +15,12 @@ func LineSeparator() string { } func GetToolLocation(file string) string { - const name = "xray.location.tool" - toolPath := EnvFlag{Name: name, AltName: NormalizeEnvName(name)}.GetValue(getExecutableDir) + toolPath := NewEnvFlag(ToolLocation).GetValue(getExecutableDir) return filepath.Join(toolPath, file+".exe") } // GetAssetLocation searches for `file` in the excutable dir func GetAssetLocation(file string) string { - const name = "xray.location.asset" - assetPath := NewEnvFlag(name).GetValue(getExecutableDir) + assetPath := NewEnvFlag(AssetLocation).GetValue(getExecutableDir) return filepath.Join(assetPath, file) } diff --git a/common/protocol/headers.pb.go b/common/protocol/headers.pb.go index 96c427d4..f733c419 100644 --- a/common/protocol/headers.pb.go +++ b/common/protocol/headers.pb.go @@ -1,6 +1,6 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.30.0 +// protoc-gen-go v1.31.0 // protoc v4.23.1 // source: common/protocol/headers.proto diff --git a/common/protocol/server_spec.pb.go b/common/protocol/server_spec.pb.go index da698518..5c8ed7de 100644 --- a/common/protocol/server_spec.pb.go +++ b/common/protocol/server_spec.pb.go @@ -1,6 +1,6 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.30.0 +// protoc-gen-go v1.31.0 // protoc v4.23.1 // source: common/protocol/server_spec.proto diff --git a/common/protocol/user.pb.go b/common/protocol/user.pb.go index 2e22e41f..dac02941 100644 --- a/common/protocol/user.pb.go +++ b/common/protocol/user.pb.go @@ -1,6 +1,6 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.30.0 +// protoc-gen-go v1.31.0 // protoc v4.23.1 // source: common/protocol/user.proto diff --git a/common/serial/typed_message.pb.go b/common/serial/typed_message.pb.go index b03f2aaa..09c71b0d 100644 --- a/common/serial/typed_message.pb.go +++ b/common/serial/typed_message.pb.go @@ -1,6 +1,6 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.30.0 +// protoc-gen-go v1.31.0 // protoc v4.23.1 // source: common/serial/typed_message.proto diff --git a/common/xudp/errors.generated.go b/common/xudp/errors.generated.go new file mode 100644 index 00000000..e14625a8 --- /dev/null +++ b/common/xudp/errors.generated.go @@ -0,0 +1,9 @@ +package xudp + +import "github.com/xtls/xray-core/common/errors" + +type errPathObjHolder struct{} + +func newError(values ...interface{}) *errors.Error { + return errors.New(values...).WithPathObj(errPathObjHolder{}) +} diff --git a/common/xudp/xudp.go b/common/xudp/xudp.go index 513247c3..af18119f 100644 --- a/common/xudp/xudp.go +++ b/common/xudp/xudp.go @@ -6,11 +6,11 @@ import ( "encoding/base64" "fmt" "io" - "os" "strings" "github.com/xtls/xray-core/common/buf" "github.com/xtls/xray-core/common/net" + "github.com/xtls/xray-core/common/platform" "github.com/xtls/xray-core/common/protocol" "github.com/xtls/xray-core/common/session" "lukechampine.com/blake3" @@ -28,20 +28,15 @@ var ( BaseKey []byte ) -const ( - EnvShow = "XRAY_XUDP_SHOW" - EnvBaseKey = "XRAY_XUDP_BASEKEY" -) - func init() { - if strings.ToLower(os.Getenv(EnvShow)) == "true" { + if strings.ToLower(platform.NewEnvFlag(platform.XUDPLog).GetValue(func() string { return "" })) == "true" { Show = true } - if raw, found := os.LookupEnv(EnvBaseKey); found { + if raw := platform.NewEnvFlag(platform.XUDPBaseKey).GetValue(func() string { return "" }); raw != "" { if BaseKey, _ = base64.RawURLEncoding.DecodeString(raw); len(BaseKey) == 32 { return } - panic(EnvBaseKey + ": invalid value: " + raw) + panic(platform.XUDPBaseKey + ": invalid value: " + raw) } rand.Read(BaseKey) } @@ -56,7 +51,7 @@ func GetGlobalID(ctx context.Context) (globalID [8]byte) { h.Write([]byte(inbound.Source.String())) copy(globalID[:], h.Sum(nil)) if Show { - fmt.Printf("XUDP inbound.Source.String(): %v\tglobalID: %v\n", inbound.Source.String(), globalID) + newError(fmt.Sprintf("XUDP inbound.Source.String(): %v\tglobalID: %v\n", inbound.Source.String(), globalID)).WriteToLog(session.ExportIDToError(ctx)) } } return diff --git a/core/config.pb.go b/core/config.pb.go index c36e7fd0..72ba2379 100644 --- a/core/config.pb.go +++ b/core/config.pb.go @@ -1,6 +1,6 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.30.0 +// protoc-gen-go v1.31.0 // protoc v4.23.1 // source: core/config.proto diff --git a/core/core.go b/core/core.go index e1128775..d11fa273 100644 --- a/core/core.go +++ b/core/core.go @@ -21,7 +21,7 @@ import ( var ( Version_x byte = 1 Version_y byte = 8 - Version_z byte = 4 + Version_z byte = 6 ) var ( diff --git a/core/xray.go b/core/xray.go index 5c7518f9..20484c60 100644 --- a/core/xray.go +++ b/core/xray.go @@ -2,11 +2,11 @@ package core import ( "context" - "os" "reflect" "sync" "github.com/xtls/xray-core/common" + "github.com/xtls/xray-core/common/platform" "github.com/xtls/xray-core/common/serial" "github.com/xtls/xray-core/features" "github.com/xtls/xray-core/features/dns" @@ -181,7 +181,8 @@ func NewWithContext(ctx context.Context, config *Config) (*Instance, error) { } func initInstanceWithConfig(config *Config, server *Instance) (bool, error) { - server.ctx = context.WithValue(server.ctx, "cone", os.Getenv("XRAY_CONE_DISABLED") != "true") + server.ctx = context.WithValue(server.ctx, "cone", + platform.NewEnvFlag(platform.UseCone).GetValue(func() string { return "" }) != "true") if config.Transport != nil { features.PrintDeprecatedFeatureWarning("global transport settings") diff --git a/features/policy/policy.go b/features/policy/policy.go index c3d48e6a..4d3f7ecf 100644 --- a/features/policy/policy.go +++ b/features/policy/policy.go @@ -83,12 +83,8 @@ func ManagerType() interface{} { var defaultBufferSize int32 func init() { - const key = "xray.ray.buffer.size" const defaultValue = -17 - size := platform.EnvFlag{ - Name: key, - AltName: platform.NormalizeEnvName(key), - }.GetValueAsInt(defaultValue) + size := platform.NewEnvFlag(platform.BufferSize).GetValueAsInt(defaultValue) switch size { case 0: diff --git a/go.mod b/go.mod index a9dae3b6..c0e7bfbe 100644 --- a/go.mod +++ b/go.mod @@ -1,39 +1,40 @@ module github.com/xtls/xray-core -go 1.21 +go 1.21.4 require ( github.com/ghodss/yaml v1.0.1-0.20220118164431-d8423dcdf344 github.com/golang/mock v1.6.0 - github.com/google/go-cmp v0.5.9 - github.com/gorilla/websocket v1.5.0 - github.com/miekg/dns v1.1.56 + github.com/google/go-cmp v0.6.0 + github.com/gorilla/websocket v1.5.1 + github.com/miekg/dns v1.1.57 github.com/pelletier/go-toml v1.9.5 github.com/pires/go-proxyproto v0.7.0 - github.com/quic-go/quic-go v0.39.0 - github.com/refraction-networking/utls v1.5.3 - github.com/sagernet/sing v0.2.12 + github.com/quic-go/quic-go v0.40.0 + github.com/refraction-networking/utls v1.5.4 + github.com/sagernet/sing v0.2.17 github.com/sagernet/sing-shadowsocks v0.2.5 - github.com/sagernet/wireguard-go v0.0.0-20221116151939-c99467f53f2c github.com/seiflotfy/cuckoofilter v0.0.0-20220411075957-e3b120b3f5fb github.com/stretchr/testify v1.8.4 github.com/v2fly/ss-bloomring v0.0.0-20210312155135-28617310f63e - github.com/xtls/reality v0.0.0-20230828171259-e426190d57f6 + github.com/vishvananda/netlink v1.2.1-beta.2.0.20230316163032-ced5aaba43e3 + github.com/xtls/reality v0.0.0-20231112171332-de1173cf2b19 go4.org/netipx v0.0.0-20230824141953-6213f710f925 - golang.org/x/crypto v0.13.0 - golang.org/x/net v0.15.0 - golang.org/x/sync v0.3.0 - golang.org/x/sys v0.12.0 - google.golang.org/grpc v1.58.2 + golang.org/x/crypto v0.15.0 + golang.org/x/net v0.18.0 + golang.org/x/sync v0.5.0 + golang.org/x/sys v0.14.0 + golang.zx2c4.com/wireguard v0.0.0-20231022001213-2e0774f246fb + google.golang.org/grpc v1.59.0 google.golang.org/protobuf v1.31.0 - gvisor.dev/gvisor v0.0.0-20230822212503-5bf4e5f98744 + gvisor.dev/gvisor v0.0.0-20231104011432-48a6d7d5bd0b h12.io/socks v1.0.3 lukechampine.com/blake3 v1.2.1 ) require ( - github.com/andybalholm/brotli v1.0.5 // indirect - github.com/cloudflare/circl v1.3.3 // indirect + github.com/andybalholm/brotli v1.0.6 // indirect + github.com/cloudflare/circl v1.3.6 // indirect github.com/davecgh/go-spew v1.1.1 // indirect github.com/dgryski/go-metro v0.0.0-20211217172704-adc40b04c140 // indirect github.com/francoispqt/gojay v1.2.13 // indirect @@ -41,21 +42,22 @@ require ( github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 // indirect github.com/golang/protobuf v1.5.3 // indirect github.com/google/btree v1.1.2 // indirect - github.com/google/pprof v0.0.0-20230926050212-f7f687d19a98 // indirect - github.com/klauspost/compress v1.17.0 // indirect - github.com/klauspost/cpuid/v2 v2.2.5 // indirect - github.com/onsi/ginkgo/v2 v2.12.1 // indirect + github.com/google/pprof v0.0.0-20231101202521-4ca4178f5c7a // indirect + github.com/klauspost/compress v1.17.2 // indirect + github.com/klauspost/cpuid/v2 v2.2.6 // indirect + github.com/onsi/ginkgo/v2 v2.13.1 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect - github.com/quic-go/qtls-go1-20 v0.3.4 // indirect + github.com/quic-go/qtls-go1-20 v0.4.1 // indirect github.com/riobard/go-bloom v0.0.0-20200614022211-cdc8013cb5b3 // indirect - go.uber.org/atomic v1.11.0 // indirect + github.com/vishvananda/netns v0.0.0-20200728191858-db3c7e526aae // indirect go.uber.org/mock v0.3.0 // indirect - golang.org/x/exp v0.0.0-20230905200255-921286631fa9 // indirect - golang.org/x/mod v0.12.0 // indirect - golang.org/x/text v0.13.0 // indirect - golang.org/x/time v0.3.0 // indirect - golang.org/x/tools v0.13.0 // indirect - google.golang.org/genproto/googleapis/rpc v0.0.0-20230920204549-e6e6cdab5c13 // indirect + golang.org/x/exp v0.0.0-20231110203233-9a3e6036ecaa // indirect + golang.org/x/mod v0.14.0 // indirect + golang.org/x/text v0.14.0 // indirect + golang.org/x/time v0.4.0 // indirect + golang.org/x/tools v0.15.0 // indirect + golang.zx2c4.com/wintun v0.0.0-20230126152724-0fa3db229ce2 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20231106174013-bbf56f31fb17 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/go.sum b/go.sum index faa51913..1239d8f0 100644 --- a/go.sum +++ b/go.sum @@ -8,15 +8,15 @@ dmitri.shuralyov.com/service/change v0.0.0-20181023043359-a85b471d5412/go.mod h1 dmitri.shuralyov.com/state v0.0.0-20180228185332-28bcc343414c/go.mod h1:0PRwlb0D6DFvNNtx+9ybjezNCa8XF0xaYcETyp6rHWU= git.apache.org/thrift.git v0.0.0-20180902110319-2566ecd5d999/go.mod h1:fPE2ZNJGynbRyZ4dJvy6G277gSllfV2HJqblrnkyeyg= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= -github.com/andybalholm/brotli v1.0.5 h1:8uQZIdzKmjc/iuPu7O2ioW48L81FgatrcpfFmiq/cCs= -github.com/andybalholm/brotli v1.0.5/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig= +github.com/andybalholm/brotli v1.0.6 h1:Yf9fFpf49Zrxb9NlQaluyE92/+X7UVHlhMNJN2sxfOI= +github.com/andybalholm/brotli v1.0.6/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig= github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239/go.mod h1:2FmKhYUyUczH0OGQWaF5ceTx0UBShxjsH6f8oGKYe2c= github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= github.com/bradfitz/go-smtpd v0.0.0-20170404230938-deb6d6237625/go.mod h1:HYsPBTaaSFSlLx/70C2HPIMNZpVV8+vt/A+FMnYP11g= github.com/buger/jsonparser v0.0.0-20181115193947-bf1c66bbce23/go.mod h1:bbYlZJ7hK1yFx9hf58LP0zeX7UjIGs20ufpu3evjr+s= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= -github.com/cloudflare/circl v1.3.3 h1:fE/Qz0QdIGqeWfnwq0RE0R7MI51s0M2E4Ga9kq5AEMs= -github.com/cloudflare/circl v1.3.3/go.mod h1:5XYMA4rFBvNIrhs50XuiBJ15vF2pZn4nnUKZrLbUZFA= +github.com/cloudflare/circl v1.3.6 h1:/xbKIqSHbZXHwkhbrhrt2YOHIwYJlXH94E3tI/gDlUg= +github.com/cloudflare/circl v1.3.6/go.mod h1:5XYMA4rFBvNIrhs50XuiBJ15vF2pZn4nnUKZrLbUZFA= github.com/coreos/go-systemd v0.0.0-20181012123002-c6f51f82210d/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= @@ -36,8 +36,8 @@ github.com/ghodss/yaml v1.0.1-0.20220118164431-d8423dcdf344 h1:Arcl6UOIS/kgO2nW3 github.com/ghodss/yaml v1.0.1-0.20220118164431-d8423dcdf344/go.mod h1:GIjDIg/heH5DOkXY3YJ/wNhfHsQHoXGjl8G8amsYQ1I= github.com/gliderlabs/ssh v0.1.1/go.mod h1:U7qILu1NlMHj9FlMhZLlkCdDnU1DBEAqr0aevW3Awn0= github.com/go-errors/errors v1.0.1/go.mod h1:f4zRHt4oKfwPJE5k8C9vpYG+aDHdBFUsgrm6/TyX73Q= -github.com/go-logr/logr v1.2.4 h1:g01GSCwiDw2xSZfjJ2/T9M+S6pFdcNtFYsp+Y43HYDQ= -github.com/go-logr/logr v1.2.4/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= +github.com/go-logr/logr v1.3.0 h1:2y3SDp0ZXuc6/cjLSZ+Q3ir+QB9T/iG5yYRXqsagWSY= +github.com/go-logr/logr v1.3.0/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 h1:tfuBGBXKqDEevZMzYi5KSi8KkcZtzBcTgAUUtapy0OI= github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572/go.mod h1:9Pwr4B2jHnOSGXyyzV8ROjYa2ojvAY6HCGYYfMoC3Ls= github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= @@ -57,19 +57,19 @@ github.com/google/btree v1.1.2 h1:xf4v41cLI2Z6FxbKm+8Bu+m8ifhj15JuZ9sa0jZCMUU= github.com/google/btree v1.1.2/go.mod h1:qOPhT0dTNdNzV6Z/lhRX0YXUafgPLFUh+gZMl761Gm4= github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= -github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= +github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/go-github v17.0.0+incompatible/go.mod h1:zLgOLi98H3fifZn+44m+umXrS52loVEgC2AApnigrVQ= github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck= github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= -github.com/google/pprof v0.0.0-20230926050212-f7f687d19a98 h1:pUa4ghanp6q4IJHwE9RwLgmVFfReJN+KbQ8ExNEUUoQ= -github.com/google/pprof v0.0.0-20230926050212-f7f687d19a98/go.mod h1:czg5+yv1E0ZGTi6S6vVK1mke0fV+FaUhNGcd6VRS9Ik= +github.com/google/pprof v0.0.0-20231101202521-4ca4178f5c7a h1:fEBsGL/sjAuJrgah5XqmmYsTLzJp/TO9Lhy39gkverk= +github.com/google/pprof v0.0.0-20231101202521-4ca4178f5c7a/go.mod h1:czg5+yv1E0ZGTi6S6vVK1mke0fV+FaUhNGcd6VRS9Ik= github.com/googleapis/gax-go v2.0.0+incompatible/go.mod h1:SFVmujtThgffbyetf+mdk2eWhX2bMyUtNHzFKcPA9HY= github.com/googleapis/gax-go/v2 v2.0.3/go.mod h1:LLvjysVCY1JZeum8Z6l8qUty8fiNwE08qbEPm1M08qg= github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= -github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc= -github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= +github.com/gorilla/websocket v1.5.1 h1:gmztn0JnHVt9JZquRuzLw3g4wouNVzKL15iLr/zn/QY= +github.com/gorilla/websocket v1.5.1/go.mod h1:x3kM2JMyaluk02fnUJpQuwD2dCS5NDG2ZHL0uE0tcaY= github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA= github.com/grpc-ecosystem/grpc-gateway v1.5.0/go.mod h1:RSKVYQBd5MCa4OVpNdGskqpgL2+G+NZTnrVHpWWfpdw= github.com/h12w/go-socks5 v0.0.0-20200522160539-76189e178364 h1:5XxdakFhqd9dnXoAZy1Mb2R/DZ6D1e+0bGC/JhucGYI= @@ -78,10 +78,10 @@ github.com/jellevandenhooff/dkim v0.0.0-20150330215556-f50fe3d243e1/go.mod h1:E0 github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= -github.com/klauspost/compress v1.17.0 h1:Rnbp4K9EjcDuVuHtd0dgA4qNuv9yKDYKK1ulpJwgrqM= -github.com/klauspost/compress v1.17.0/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE= -github.com/klauspost/cpuid/v2 v2.2.5 h1:0E5MSMDEoAulmXNFquVs//DdoomxaoTY1kUhbc/qbZg= -github.com/klauspost/cpuid/v2 v2.2.5/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws= +github.com/klauspost/compress v1.17.2 h1:RlWWUY/Dr4fL8qk9YG7DTZ7PDgME2V4csBXA8L/ixi4= +github.com/klauspost/compress v1.17.2/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE= +github.com/klauspost/cpuid/v2 v2.2.6 h1:ndNyv040zDGIDh8thGkXYjnFtiN02M1PVVF+JE/48xc= +github.com/klauspost/cpuid/v2 v2.2.6/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws= github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= @@ -92,16 +92,16 @@ github.com/lunixbochs/vtclean v1.0.0/go.mod h1:pHhQNgMf3btfWnGBVipUOjRYhoOsdGqdm github.com/mailru/easyjson v0.0.0-20190312143242-1de009706dbe/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= github.com/microcosm-cc/bluemonday v1.0.1/go.mod h1:hsXNsILzKxV+sX77C5b8FSuKF00vh2OMYv+xgHpAMF4= -github.com/miekg/dns v1.1.56 h1:5imZaSeoRNvpM9SzWNhEcP9QliKiz20/dA2QabIGVnE= -github.com/miekg/dns v1.1.56/go.mod h1:cRm6Oo2C8TY9ZS/TqsSrseAcncm74lfK5G+ikN2SWWY= +github.com/miekg/dns v1.1.57 h1:Jzi7ApEIzwEPLHWRcafCN9LZSBbqQpxjt/wpgvg7wcM= +github.com/miekg/dns v1.1.57/go.mod h1:uqRjCRUuEAA6qsOiJvDd+CFo/vW+y5WR6SNmHE55hZk= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= github.com/neelance/astrewrite v0.0.0-20160511093645-99348263ae86/go.mod h1:kHJEU3ofeGjhHklVoIGuVj85JJwZ6kWPaJwCIxgnFmo= github.com/neelance/sourcemap v0.0.0-20151028013722-8c68805598ab/go.mod h1:Qr6/a/Q4r9LP1IltGz7tA7iOK1WonHEYhu1HRBA7ZiM= -github.com/onsi/ginkgo/v2 v2.12.1 h1:uHNEO1RP2SpuZApSkel9nEh1/Mu+hmQe7Q+Pepg5OYA= -github.com/onsi/ginkgo/v2 v2.12.1/go.mod h1:TE309ZR8s5FsKKpuB1YAQYBzCaAfUgatB/xlT/ETL/o= -github.com/onsi/gomega v1.27.10 h1:naR28SdDFlqrG6kScpT8VWpu1xWY5nJRCF3XaYyBjhI= -github.com/onsi/gomega v1.27.10/go.mod h1:RsS8tutOdbdgzbPtzzATp12yT7kM5I5aElG3evPbQ0M= +github.com/onsi/ginkgo/v2 v2.13.1 h1:LNGfMbR2OVGBfXjvRZIZ2YCTQdGKtPLvuI1rMCCj3OU= +github.com/onsi/ginkgo/v2 v2.13.1/go.mod h1:XStQ8QcGwLyF4HdfcZB8SFOS/MWCgDuXMSBe6zrvLgM= +github.com/onsi/gomega v1.29.0 h1:KIA/t2t5UBzoirT4H9tsML45GEbo3ouUnBHsCfD2tVg= +github.com/onsi/gomega v1.29.0/go.mod h1:9sxs+SwGrKI0+PWe4Fxa9tFQQBG5xSsSbMXOI8PPpoQ= github.com/openzipkin/zipkin-go v0.1.1/go.mod h1:NtoC/o8u3JlF1lSlyPNswIbeQH9bJTmOf0Erfk+hxe8= github.com/pelletier/go-toml v1.9.5 h1:4yBQzkHv+7BHq2PQUZF3Mx0IYxG7LsP222s7Agd3ve8= github.com/pelletier/go-toml v1.9.5/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c= @@ -116,21 +116,19 @@ github.com/prometheus/client_golang v0.8.0/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXP github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= github.com/prometheus/common v0.0.0-20180801064454-c7de2306084e/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro= github.com/prometheus/procfs v0.0.0-20180725123919-05ee40e3a273/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= -github.com/quic-go/qtls-go1-20 v0.3.4 h1:MfFAPULvst4yoMgY9QmtpYmfij/em7O8UUi+bNVm7Cg= -github.com/quic-go/qtls-go1-20 v0.3.4/go.mod h1:X9Nh97ZL80Z+bX/gUXMbipO6OxdiDi58b/fMC9mAL+k= -github.com/quic-go/quic-go v0.39.0 h1:AgP40iThFMY0bj8jGxROhw3S0FMGa8ryqsmi9tBH3So= -github.com/quic-go/quic-go v0.39.0/go.mod h1:T09QsDQWjLiQ74ZmacDfqZmhY/NLnw5BC40MANNNZ1Q= -github.com/refraction-networking/utls v1.5.3 h1:Ds5Ocg1+MC1ahNx5iBEcHe0jHeLaA/fLey61EENm7ro= -github.com/refraction-networking/utls v1.5.3/go.mod h1:SPuDbBmgLGp8s+HLNc83FuavwZCFoMmExj+ltUHiHUw= +github.com/quic-go/qtls-go1-20 v0.4.1 h1:D33340mCNDAIKBqXuAvexTNMUByrYmFYVfKfDN5nfFs= +github.com/quic-go/qtls-go1-20 v0.4.1/go.mod h1:X9Nh97ZL80Z+bX/gUXMbipO6OxdiDi58b/fMC9mAL+k= +github.com/quic-go/quic-go v0.40.0 h1:GYd1iznlKm7dpHD7pOVpUvItgMPo/jrMgDWZhMCecqw= +github.com/quic-go/quic-go v0.40.0/go.mod h1:PeN7kuVJ4xZbxSv/4OX6S1USOX8MJvydwpTx31vx60c= +github.com/refraction-networking/utls v1.5.4 h1:9k6EO2b8TaOGsQ7Pl7p9w6PUhx18/ZCeT0WNTZ7Uw4o= +github.com/refraction-networking/utls v1.5.4/go.mod h1:SPuDbBmgLGp8s+HLNc83FuavwZCFoMmExj+ltUHiHUw= github.com/riobard/go-bloom v0.0.0-20200614022211-cdc8013cb5b3 h1:f/FNXud6gA3MNr8meMVVGxhp+QBTqY91tM8HjEuMjGg= github.com/riobard/go-bloom v0.0.0-20200614022211-cdc8013cb5b3/go.mod h1:HgjTstvQsPGkxUsCd2KWxErBblirPizecHcpD3ffK+s= github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g= -github.com/sagernet/sing v0.2.12 h1:wwdLm3c4qvU4hW8tNtadh60V5z2FGlDZSYYGRzHhD74= -github.com/sagernet/sing v0.2.12/go.mod h1:GQ673iPfUnkbK/dIPkfd1Xh1MjOGo36gkl/mkiHY7Jg= +github.com/sagernet/sing v0.2.17 h1:vMPKb3MV0Aa5ws4dCJkRI8XEjrsUcDn810czd0FwmzI= +github.com/sagernet/sing v0.2.17/go.mod h1:OL6k2F0vHmEzXz2KW19qQzu172FDgSbUSODylighuVo= github.com/sagernet/sing-shadowsocks v0.2.5 h1:qxIttos4xu6ii7MTVJYA8EFQR7Q3KG6xMqmLJIFtBaY= github.com/sagernet/sing-shadowsocks v0.2.5/go.mod h1:MGWGkcU2xW2G2mfArT9/QqpVLOGU+dBaahZCtPHdt7A= -github.com/sagernet/wireguard-go v0.0.0-20221116151939-c99467f53f2c h1:vK2wyt9aWYHHvNLWniwijBu/n4pySypiKRhN32u/JGo= -github.com/sagernet/wireguard-go v0.0.0-20221116151939-c99467f53f2c/go.mod h1:euOmN6O5kk9dQmgSS8Df4psAl3TCjxOz0NW60EWkSaI= github.com/seiflotfy/cuckoofilter v0.0.0-20220411075957-e3b120b3f5fb h1:XfLJSPIOUX+osiMraVgIrMR27uMXnRJWGm1+GL8/63U= github.com/seiflotfy/cuckoofilter v0.0.0-20220411075957-e3b120b3f5fb/go.mod h1:bR6DqgcAl1zTcOX8/pE2Qkj9XO00eCNqmKb7lXP8EAg= github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo= @@ -168,12 +166,14 @@ github.com/v2fly/ss-bloomring v0.0.0-20210312155135-28617310f63e h1:5QefA066A1tF github.com/v2fly/ss-bloomring v0.0.0-20210312155135-28617310f63e/go.mod h1:5t19P9LBIrNamL6AcMQOncg/r10y3Pc01AbHeMhwlpU= github.com/viant/assertly v0.4.8/go.mod h1:aGifi++jvCrUaklKEKT0BU95igDNaqkvz+49uaYMPRU= github.com/viant/toolbox v0.24.0/go.mod h1:OxMCG57V0PXuIP2HNQrtJf2CjqdmbrOx5EkMILuUhzM= -github.com/xtls/reality v0.0.0-20230828171259-e426190d57f6 h1:T+YCYGfFdzyaKTDCdZn/hEiKvsw6yUfd+e4hze0rCUw= -github.com/xtls/reality v0.0.0-20230828171259-e426190d57f6/go.mod h1:rkuAY1S9F8eI8gDiPDYvACE8e2uwkyg8qoOTuwWov7Y= +github.com/vishvananda/netlink v1.2.1-beta.2.0.20230316163032-ced5aaba43e3 h1:tkMT5pTye+1NlKIXETU78NXw0fyjnaNHmJyyLyzw8+U= +github.com/vishvananda/netlink v1.2.1-beta.2.0.20230316163032-ced5aaba43e3/go.mod h1:cAAsePK2e15YDAMJNyOpGYEWNe4sIghTY7gpz4cX/Ik= +github.com/vishvananda/netns v0.0.0-20200728191858-db3c7e526aae h1:4hwBBUfQCFe3Cym0ZtKyq7L16eZUtYKs+BaHDN6mAns= +github.com/vishvananda/netns v0.0.0-20200728191858-db3c7e526aae/go.mod h1:DD4vA1DwXk04H54A1oHXtwZmA0grkVMdPxx/VGLCah0= +github.com/xtls/reality v0.0.0-20231112171332-de1173cf2b19 h1:capMfFYRgH9BCLd6A3Er/cH3A9Nz3CU2KwxwOQZIePI= +github.com/xtls/reality v0.0.0-20231112171332-de1173cf2b19/go.mod h1:dm4y/1QwzjGaK17ofi0Vs6NpKAHegZky8qk6J2JJZAE= github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= go.opencensus.io v0.18.0/go.mod h1:vKdFvxhtzZ9onBp9VKHK8z/sRpBMnKAsufL7wlDrCOA= -go.uber.org/atomic v1.11.0 h1:ZvwS0R+56ePWxUNi+Atn9dWONBPp/AUETXlHW0DxSjE= -go.uber.org/atomic v1.11.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0= go.uber.org/mock v0.3.0 h1:3mUxI1No2/60yUYax92Pt8eNOEecx2D3lcXZh2NEZJo= go.uber.org/mock v0.3.0/go.mod h1:a6FSlNadKUHUa9IP5Vyt1zh4fC7uAwxMutEAscFbkZc= go4.org v0.0.0-20180809161055-417644f6feb5/go.mod h1:MkTOUMDaeVYJUOUsaDXIhWPZYa1yOyC1qaOBpL57BhE= @@ -184,17 +184,17 @@ golang.org/x/crypto v0.0.0-20181030102418-4d3f4d9ffa16/go.mod h1:6SG95UA2DQfeDnf golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190313024323-a1f597ede03a/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.13.0 h1:mvySKfSWJ+UKUii46M40LOvyWfN0s2U+46/jDd0e6Ck= -golang.org/x/crypto v0.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliYc= +golang.org/x/crypto v0.15.0 h1:frVn1TEaCEaZcn3Tmd7Y2b5KKPaZ+I32Q2OA3kYp5TA= +golang.org/x/crypto v0.15.0/go.mod h1:4ChreQoLWfG3xLDer1WdlH5NdlQ3+mwnQq1YTKY+72g= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= -golang.org/x/exp v0.0.0-20230905200255-921286631fa9 h1:GoHiUyI/Tp2nVkLI2mCxVkOjsbSXD66ic0XW0js0R9g= -golang.org/x/exp v0.0.0-20230905200255-921286631fa9/go.mod h1:S2oDrQGGwySpoQPVqRShND87VCbxmc6bL1Yd2oYrm6k= +golang.org/x/exp v0.0.0-20231110203233-9a3e6036ecaa h1:FRnLl4eNAQl8hwxVVC17teOw8kdjVDVAiFMtgUdTSRQ= +golang.org/x/exp v0.0.0-20231110203233-9a3e6036ecaa/go.mod h1:zk2irFbV9DP96SEBUUAy67IdHUaZuSnrz1n472HUCLE= golang.org/x/lint v0.0.0-20180702182130-06c8688daad7/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/mod v0.12.0 h1:rmsUpXtvNzj340zd98LZ4KntptpfRHwpFOHG188oHXc= -golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= +golang.org/x/mod v0.14.0 h1:dGoOF9QVLYng8IHTm7BAyWqCqSheQ5pYWGhzW00YJr0= +golang.org/x/mod v0.14.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -206,8 +206,8 @@ golang.org/x/net v0.0.0-20190313220215-9f648a60d977/go.mod h1:t9HGtf8HONx5eT2rtn golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= -golang.org/x/net v0.15.0 h1:ugBLEUaxABaB5AJqW9enI0ACdci2RUd4eP51NTBvuJ8= -golang.org/x/net v0.15.0/go.mod h1:idbUs1IY1+zTqbi8yxTbhexhEEk5ur9LInksu6HrEpk= +golang.org/x/net v0.18.0 h1:mIYleuAkSbHh0tCv7RvjL3F6ZVbLjq4+R7zbOn3Kokg= +golang.org/x/net v0.18.0/go.mod h1:/czyP5RqHAH4odGYxBJ1qz0+CE5WZ+2j1YgoEo8F2jQ= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20181017192945-9dcd33a902f4/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20181203162652-d668ce993890/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= @@ -219,30 +219,32 @@ golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.3.0 h1:ftCYgMx6zT/asHUrPw8BLLscYtGznsLAnjq5RH9P66E= -golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y= +golang.org/x/sync v0.5.0 h1:60k92dhOjHxJkrqnwsfl8KuaHbn/5dl0lUPUklKo3qE= +golang.org/x/sync v0.5.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181029174526-d69651ed3497/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190316082340-a2f829d7f35f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200217220822-9197077df867/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220804214406-8e32c043e418/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.12.0 h1:CM0HF96J0hcLAwsHPJZjfdNzs0gftsLfgKt57wWHJ0o= -golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.14.0 h1:Vz7Qs629MkJkGyHxUlRHizWJRG2j8fbQKjELVSNhy7Q= +golang.org/x/sys v0.14.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.13.0 h1:ablQoSUd0tRdKxZewP80B+BaqeKJuVhuRxj/dkrun3k= -golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= +golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= +golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.3.0 h1:rg5rLMjNzMS1RkNLzCG38eapWhnYLFYXDXj2gOlr8j4= -golang.org/x/time v0.3.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.4.0 h1:Z81tqI5ddIoXDPvVQ7/7CC9TnLM7ubaFG2qXYd5BbYY= +golang.org/x/time v0.4.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= golang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20181030000716-a0a13e073c7b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= @@ -250,12 +252,16 @@ golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGm golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= -golang.org/x/tools v0.13.0 h1:Iey4qkscZuv0VvIt8E0neZjtPVQFSc870HQ448QgEmQ= -golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58= +golang.org/x/tools v0.15.0 h1:zdAyfUGbYmuVokhzVmghFl2ZJh5QhcfebBgmVPFYA+8= +golang.org/x/tools v0.15.0/go.mod h1:hpksKq4dtpQWS1uQ61JkdqWM3LscIS6Slf+VVkm+wQk= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.zx2c4.com/wintun v0.0.0-20230126152724-0fa3db229ce2 h1:B82qJJgjvYKsXS9jeunTOisW56dUokqW/FOteYJJ/yg= +golang.zx2c4.com/wintun v0.0.0-20230126152724-0fa3db229ce2/go.mod h1:deeaetjYA+DHMHg+sMSMI58GrEteJUUzzw7en6TJQcI= +golang.zx2c4.com/wireguard v0.0.0-20231022001213-2e0774f246fb h1:c5tyN8sSp8jSDxdCCDXVOpJwYXXhmTkNMt+g0zTSOic= +golang.zx2c4.com/wireguard v0.0.0-20231022001213-2e0774f246fb/go.mod h1:tkCQ4FQXmpAgYVh++1cq16/dH4QJtmvpRv19DWGAHSA= google.golang.org/api v0.0.0-20180910000450-7ca32eb868bf/go.mod h1:4mhQ8q/RsB7i+udVvVy5NUi08OU8ZlA0gRVgrF7VFY0= google.golang.org/api v0.0.0-20181030000543-1d582fd0359e/go.mod h1:4mhQ8q/RsB7i+udVvVy5NUi08OU8ZlA0gRVgrF7VFY0= google.golang.org/api v0.1.0/go.mod h1:UGEZY7KEX120AnNLIHFMKIo4obdJhkp2tPbaPlQx13Y= @@ -268,14 +274,14 @@ google.golang.org/genproto v0.0.0-20180831171423-11092d34479b/go.mod h1:JiN7NxoA google.golang.org/genproto v0.0.0-20181029155118-b69ba1387ce2/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= google.golang.org/genproto v0.0.0-20181202183823-bd91e49a0898/go.mod h1:7Ep/1NZk928CDR8SjdVbjWNpdIf6nzjE3BTgJDr2Atg= google.golang.org/genproto v0.0.0-20190306203927-b5d61aea6440/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= -google.golang.org/genproto/googleapis/rpc v0.0.0-20230920204549-e6e6cdab5c13 h1:N3bU/SQDCDyD6R528GJ/PwW9KjYcJA3dgyH+MovAkIM= -google.golang.org/genproto/googleapis/rpc v0.0.0-20230920204549-e6e6cdab5c13/go.mod h1:KSqppvjFjtoCI+KGd4PELB0qLNxdJHRGqRI09mB6pQA= +google.golang.org/genproto/googleapis/rpc v0.0.0-20231106174013-bbf56f31fb17 h1:Jyp0Hsi0bmHXG6k9eATXoYtjd6e2UzZ1SCn/wIupY14= +google.golang.org/genproto/googleapis/rpc v0.0.0-20231106174013-bbf56f31fb17/go.mod h1:oQ5rr10WTTMvP4A36n8JpR1OrO1BEiV4f78CneXZxkA= google.golang.org/grpc v1.14.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw= google.golang.org/grpc v1.16.0/go.mod h1:0JHn/cJsOMiMfNA9+DeHDlAU7KAAB5GDlYFpa9MZMio= google.golang.org/grpc v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= -google.golang.org/grpc v1.58.2 h1:SXUpjxeVF3FKrTYQI4f4KvbGD5u2xccdYdurwowix5I= -google.golang.org/grpc v1.58.2/go.mod h1:tgX3ZQDlNJGU96V6yHh1T/JeoBQ2TXdr43YbYSsCJk0= +google.golang.org/grpc v1.59.0 h1:Z5Iec2pjwb+LEOqzpB2MR12/eKFhDPhuqW91O+4bwUk= +google.golang.org/grpc v1.59.0/go.mod h1:aUPDwccQo6OTjy7Hct4AfBPD1GptF4fyUjIkQ9YtF98= google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= google.golang.org/protobuf v1.31.0 h1:g0LDEJHgrBl9N9r17Ru3sqWhkIx2NB67okBHPwC7hs8= @@ -293,8 +299,8 @@ gopkg.in/yaml.v3 v3.0.0-20200605160147-a5ece683394c/go.mod h1:K4uyk7z7BCEPqu6E+C gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= grpc.go4.org v0.0.0-20170609214715-11d0a25b4919/go.mod h1:77eQGdRu53HpSqPFJFmuJdjuHRquDANNeA4x7B8WQ9o= -gvisor.dev/gvisor v0.0.0-20230822212503-5bf4e5f98744 h1:tE44CyJgxEGzoPtHs9GI7ddKdgEGCREQBP54AmaVM+I= -gvisor.dev/gvisor v0.0.0-20230822212503-5bf4e5f98744/go.mod h1:lYEMhXbxgudVhALYsMQrBaUAjM3NMinh8mKL1CJv7rc= +gvisor.dev/gvisor v0.0.0-20231104011432-48a6d7d5bd0b h1:yqkg3pTifuKukuWanp8spDsL4irJkHF5WI0J47hU87o= +gvisor.dev/gvisor v0.0.0-20231104011432-48a6d7d5bd0b/go.mod h1:10sU+Uh5KKNv1+2x2A0Gvzt8FjD3ASIhorV3YsauXhk= h12.io/socks v1.0.3 h1:Ka3qaQewws4j4/eDQnOdpr4wXsC//dXtWvftlIcCQUo= h12.io/socks v1.0.3/go.mod h1:AIhxy1jOId/XCz9BO+EIgNL2rQiPTBNnOfnVnQ+3Eck= honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= diff --git a/infra/conf/common.go b/infra/conf/common.go index ba5abb9d..62876441 100644 --- a/infra/conf/common.go +++ b/infra/conf/common.go @@ -2,10 +2,10 @@ package conf import ( "encoding/json" - "os" "strings" "github.com/xtls/xray-core/common/net" + "github.com/xtls/xray-core/common/platform" "github.com/xtls/xray-core/common/protocol" ) @@ -46,7 +46,7 @@ func (v *Address) UnmarshalJSON(data []byte) error { return newError("invalid address: ", string(data)).Base(err) } if strings.HasPrefix(rawStr, "env:") { - rawStr = os.Getenv(rawStr[4:]) + rawStr = platform.NewEnvFlag(rawStr[4:]).GetValue(func() string { return "" }) } v.Address = net.ParseAddress(rawStr) @@ -118,7 +118,7 @@ func parseIntPort(data []byte) (net.Port, error) { func parseStringPort(s string) (net.Port, net.Port, error) { if strings.HasPrefix(s, "env:") { - s = os.Getenv(s[4:]) + s = platform.NewEnvFlag(s[4:]).GetValue(func() string { return "" }) } pair := strings.SplitN(s, "-", 2) diff --git a/infra/conf/freedom.go b/infra/conf/freedom.go index 21f0616a..dd812db6 100644 --- a/infra/conf/freedom.go +++ b/infra/conf/freedom.go @@ -28,14 +28,31 @@ type Fragment struct { // Build implements Buildable func (c *FreedomConfig) Build() (proto.Message, error) { config := new(freedom.Config) - config.DomainStrategy = freedom.Config_AS_IS switch strings.ToLower(c.DomainStrategy) { - case "useip", "use_ip", "use-ip": + case "asis", "": + config.DomainStrategy = freedom.Config_AS_IS + case "useip": config.DomainStrategy = freedom.Config_USE_IP - case "useip4", "useipv4", "use_ip4", "use_ipv4", "use_ip_v4", "use-ip4", "use-ipv4", "use-ip-v4": + case "useipv4": config.DomainStrategy = freedom.Config_USE_IP4 - case "useip6", "useipv6", "use_ip6", "use_ipv6", "use_ip_v6", "use-ip6", "use-ipv6", "use-ip-v6": + case "useipv6": config.DomainStrategy = freedom.Config_USE_IP6 + case "useipv4v6": + config.DomainStrategy = freedom.Config_USE_IP46 + case "useipv6v4": + config.DomainStrategy = freedom.Config_USE_IP64 + case "forceip": + config.DomainStrategy = freedom.Config_FORCE_IP + case "forceipv4": + config.DomainStrategy = freedom.Config_FORCE_IP4 + case "forceipv6": + config.DomainStrategy = freedom.Config_FORCE_IP6 + case "forceipv4v6": + config.DomainStrategy = freedom.Config_FORCE_IP46 + case "forceipv6v4": + config.DomainStrategy = freedom.Config_FORCE_IP64 + default: + return nil, newError("unsupported domain strategy: ", c.DomainStrategy) } if c.Fragment != nil { diff --git a/infra/conf/transport_internet.go b/infra/conf/transport_internet.go index 9e10feed..e1471bde 100644 --- a/infra/conf/transport_internet.go +++ b/infra/conf/transport_internet.go @@ -654,12 +654,30 @@ func (c *SocketConfig) Build() (*internet.SocketConfig, error) { dStrategy := internet.DomainStrategy_AS_IS switch strings.ToLower(c.DomainStrategy) { - case "useip", "use_ip": + case "asis", "": + dStrategy = internet.DomainStrategy_AS_IS + case "useip": dStrategy = internet.DomainStrategy_USE_IP - case "useip4", "useipv4", "use_ipv4", "use_ip_v4", "use_ip4": + case "useipv4": dStrategy = internet.DomainStrategy_USE_IP4 - case "useip6", "useipv6", "use_ipv6", "use_ip_v6", "use_ip6": + case "useipv6": dStrategy = internet.DomainStrategy_USE_IP6 + case "useipv4v6": + dStrategy = internet.DomainStrategy_USE_IP46 + case "useipv6v4": + dStrategy = internet.DomainStrategy_USE_IP64 + case "forceip": + dStrategy = internet.DomainStrategy_FORCE_IP + case "forceipv4": + dStrategy = internet.DomainStrategy_FORCE_IP4 + case "forceipv6": + dStrategy = internet.DomainStrategy_FORCE_IP6 + case "forceipv4v6": + dStrategy = internet.DomainStrategy_FORCE_IP46 + case "forceipv6v4": + dStrategy = internet.DomainStrategy_FORCE_IP64 + default: + return nil, newError("unsupported domain strategy: ", c.DomainStrategy) } return &internet.SocketConfig{ diff --git a/infra/conf/wireguard.go b/infra/conf/wireguard.go index 0c79297b..a4f0eda6 100644 --- a/infra/conf/wireguard.go +++ b/infra/conf/wireguard.go @@ -3,6 +3,7 @@ package conf import ( "encoding/base64" "encoding/hex" + "strings" "github.com/xtls/xray-core/proxy/wireguard" "google.golang.org/protobuf/proto" @@ -12,7 +13,7 @@ type WireGuardPeerConfig struct { PublicKey string `json:"publicKey"` PreSharedKey string `json:"preSharedKey"` Endpoint string `json:"endpoint"` - KeepAlive int `json:"keepAlive"` + KeepAlive uint32 `json:"keepAlive"` AllowedIPs []string `json:"allowedIPs,omitempty"` } @@ -20,9 +21,11 @@ func (c *WireGuardPeerConfig) Build() (proto.Message, error) { var err error config := new(wireguard.PeerConfig) - config.PublicKey, err = parseWireGuardKey(c.PublicKey) - if err != nil { - return nil, err + if c.PublicKey != "" { + config.PublicKey, err = parseWireGuardKey(c.PublicKey) + if err != nil { + return nil, err + } } if c.PreSharedKey != "" { @@ -30,13 +33,11 @@ func (c *WireGuardPeerConfig) Build() (proto.Message, error) { if err != nil { return nil, err } - } else { - config.PreSharedKey = "0000000000000000000000000000000000000000000000000000000000000000" } config.Endpoint = c.Endpoint // default 0 - config.KeepAlive = int32(c.KeepAlive) + config.KeepAlive = c.KeepAlive if c.AllowedIPs == nil { config.AllowedIps = []string{"0.0.0.0/0", "::0/0"} } else { @@ -47,12 +48,16 @@ func (c *WireGuardPeerConfig) Build() (proto.Message, error) { } type WireGuardConfig struct { - SecretKey string `json:"secretKey"` - Address []string `json:"address"` - Peers []*WireGuardPeerConfig `json:"peers"` - MTU int `json:"mtu"` - NumWorkers int `json:"workers"` - Reserved []byte `json:"reserved"` + IsClient bool `json:""` + + KernelMode *bool `json:"kernelMode"` + SecretKey string `json:"secretKey"` + Address []string `json:"address"` + Peers []*WireGuardPeerConfig `json:"peers"` + MTU int32 `json:"mtu"` + NumWorkers int32 `json:"workers"` + Reserved []byte `json:"reserved"` + DomainStrategy string `json:"domainStrategy"` } func (c *WireGuardConfig) Build() (proto.Message, error) { @@ -85,33 +90,68 @@ func (c *WireGuardConfig) Build() (proto.Message, error) { if c.MTU == 0 { config.Mtu = 1420 } else { - config.Mtu = int32(c.MTU) + config.Mtu = c.MTU } - // these a fallback code exists in github.com/nanoda0523/wireguard-go code, + // these a fallback code exists in wireguard-go code, // we don't need to process fallback manually - config.NumWorkers = int32(c.NumWorkers) + config.NumWorkers = c.NumWorkers if len(c.Reserved) != 0 && len(c.Reserved) != 3 { return nil, newError(`"reserved" should be empty or 3 bytes`) } config.Reserved = c.Reserved + switch strings.ToLower(c.DomainStrategy) { + case "forceip", "": + config.DomainStrategy = wireguard.DeviceConfig_FORCE_IP + case "forceipv4": + config.DomainStrategy = wireguard.DeviceConfig_FORCE_IP4 + case "forceipv6": + config.DomainStrategy = wireguard.DeviceConfig_FORCE_IP6 + case "forceipv4v6": + config.DomainStrategy = wireguard.DeviceConfig_FORCE_IP46 + case "forceipv6v4": + config.DomainStrategy = wireguard.DeviceConfig_FORCE_IP64 + default: + return nil, newError("unsupported domain strategy: ", c.DomainStrategy) + } + + config.IsClient = c.IsClient + if c.KernelMode != nil { + config.KernelMode = *c.KernelMode + if config.KernelMode && !wireguard.KernelTunSupported() { + newError("kernel mode is not supported on your OS or permission is insufficient").AtWarning().WriteToLog() + } + } else { + config.KernelMode = wireguard.KernelTunSupported() + if config.KernelMode { + newError("kernel mode is enabled as it's supported and permission is sufficient").AtDebug().WriteToLog() + } + } + return config, nil } func parseWireGuardKey(str string) (string, error) { - if len(str) != 64 { - // may in base64 form - dat, err := base64.StdEncoding.DecodeString(str) - if err != nil { - return "", err + var err error + + if len(str)%2 == 0 { + _, err = hex.DecodeString(str) + if err == nil { + return str, nil } - if len(dat) != 32 { - return "", newError("key should be 32 bytes: " + str) - } - return hex.EncodeToString(dat), err - } else { - // already hex form - return str, nil } + + var dat []byte + str = strings.TrimSuffix(str, "=") + if strings.ContainsRune(str, '+') || strings.ContainsRune(str, '/') { + dat, err = base64.RawStdEncoding.DecodeString(str) + } else { + dat, err = base64.RawURLEncoding.DecodeString(str) + } + if err == nil { + return hex.EncodeToString(dat), nil + } + + return "", newError("failed to deserialize key").Base(err) } diff --git a/infra/conf/wireguard_test.go b/infra/conf/wireguard_test.go index f0136bf0..57951105 100644 --- a/infra/conf/wireguard_test.go +++ b/infra/conf/wireguard_test.go @@ -7,7 +7,7 @@ import ( "github.com/xtls/xray-core/proxy/wireguard" ) -func TestWireGuardOutbound(t *testing.T) { +func TestWireGuardConfig(t *testing.T) { creator := func() Buildable { return new(WireGuardConfig) } @@ -24,7 +24,9 @@ func TestWireGuardOutbound(t *testing.T) { } ], "mtu": 1300, - "workers": 2 + "workers": 2, + "domainStrategy": "ForceIPv6v4", + "kernelMode": false }`, Parser: loadJSON(creator), Output: &wireguard.DeviceConfig{ @@ -34,15 +36,16 @@ func TestWireGuardOutbound(t *testing.T) { Peers: []*wireguard.PeerConfig{ { // also can read from hex form directly - PublicKey: "6e65ce0be17517110c17d77288ad87e7fd5252dcc7d09b95a39d61db03df832a", - PreSharedKey: "0000000000000000000000000000000000000000000000000000000000000000", - Endpoint: "127.0.0.1:1234", - KeepAlive: 0, - AllowedIps: []string{"0.0.0.0/0", "::0/0"}, + PublicKey: "6e65ce0be17517110c17d77288ad87e7fd5252dcc7d09b95a39d61db03df832a", + Endpoint: "127.0.0.1:1234", + KeepAlive: 0, + AllowedIps: []string{"0.0.0.0/0", "::0/0"}, }, }, - Mtu: 1300, - NumWorkers: 2, + Mtu: 1300, + NumWorkers: 2, + DomainStrategy: wireguard.DeviceConfig_FORCE_IP64, + KernelMode: false, }, }, }) diff --git a/infra/conf/xray.go b/infra/conf/xray.go index 7bfc53e3..0935b1b0 100644 --- a/infra/conf/xray.go +++ b/infra/conf/xray.go @@ -24,6 +24,7 @@ var ( "vless": func() interface{} { return new(VLessInboundConfig) }, "vmess": func() interface{} { return new(VMessInboundConfig) }, "trojan": func() interface{} { return new(TrojanServerConfig) }, + "wireguard": func() interface{} { return &WireGuardConfig{IsClient: false} }, }, "protocol", "settings") outboundConfigLoader = NewJSONConfigLoader(ConfigCreatorCache{ @@ -37,7 +38,7 @@ var ( "vmess": func() interface{} { return new(VMessOutboundConfig) }, "trojan": func() interface{} { return new(TrojanClientConfig) }, "dns": func() interface{} { return new(DNSOutboundConfig) }, - "wireguard": func() interface{} { return new(WireGuardConfig) }, + "wireguard": func() interface{} { return &WireGuardConfig{IsClient: true} }, }, "protocol", "settings") ctllog = log.New(os.Stderr, "xctl> ", 0) @@ -487,38 +488,40 @@ func (c *Config) Override(o *Config, fn string) { } // deprecated attrs - // update the Inbound in slice if the only one in overide config has same tag + // update the Inbound in slice if the only one in override config has same tag if len(o.InboundConfigs) > 0 { - if len(c.InboundConfigs) > 0 && len(o.InboundConfigs) == 1 { - if idx := c.findInboundTag(o.InboundConfigs[0].Tag); idx > -1 { - c.InboundConfigs[idx] = o.InboundConfigs[0] - ctllog.Println("[", fn, "] updated inbound with tag: ", o.InboundConfigs[0].Tag) + for i := range o.InboundConfigs { + if idx := c.findInboundTag(o.InboundConfigs[i].Tag); idx > -1 { + c.InboundConfigs[idx] = o.InboundConfigs[i] + newError("[", fn, "] updated inbound with tag: ", o.InboundConfigs[i].Tag).AtInfo().WriteToLog() + } else { - c.InboundConfigs = append(c.InboundConfigs, o.InboundConfigs[0]) - ctllog.Println("[", fn, "] appended inbound with tag: ", o.InboundConfigs[0].Tag) + c.InboundConfigs = append(c.InboundConfigs, o.InboundConfigs[i]) + newError("[", fn, "] appended inbound with tag: ", o.InboundConfigs[i].Tag).AtInfo().WriteToLog() } - } else { - c.InboundConfigs = o.InboundConfigs + } } - // update the Outbound in slice if the only one in overide config has same tag + // update the Outbound in slice if the only one in override config has same tag if len(o.OutboundConfigs) > 0 { - if len(c.OutboundConfigs) > 0 && len(o.OutboundConfigs) == 1 { - if idx := c.findOutboundTag(o.OutboundConfigs[0].Tag); idx > -1 { - c.OutboundConfigs[idx] = o.OutboundConfigs[0] - ctllog.Println("[", fn, "] updated outbound with tag: ", o.OutboundConfigs[0].Tag) + outboundPrepends := []OutboundDetourConfig{} + for i := range o.OutboundConfigs { + if idx := c.findOutboundTag(o.OutboundConfigs[i].Tag); idx > -1 { + c.OutboundConfigs[idx] = o.OutboundConfigs[i] + newError("[", fn, "] updated outbound with tag: ", o.OutboundConfigs[i].Tag).AtInfo().WriteToLog() } else { if strings.Contains(strings.ToLower(fn), "tail") { - c.OutboundConfigs = append(c.OutboundConfigs, o.OutboundConfigs[0]) - ctllog.Println("[", fn, "] appended outbound with tag: ", o.OutboundConfigs[0].Tag) + c.OutboundConfigs = append(c.OutboundConfigs, o.OutboundConfigs[i]) + newError("[", fn, "] appended outbound with tag: ", o.OutboundConfigs[i].Tag).AtInfo().WriteToLog() } else { - c.OutboundConfigs = append(o.OutboundConfigs, c.OutboundConfigs...) - ctllog.Println("[", fn, "] prepended outbound with tag: ", o.OutboundConfigs[0].Tag) + outboundPrepends = append(outboundPrepends, o.OutboundConfigs[i]) + newError("[", fn, "] prepend outbound with tag: ", o.OutboundConfigs[i].Tag).AtInfo().WriteToLog() } } - } else { - c.OutboundConfigs = o.OutboundConfigs + } + if !strings.Contains(strings.ToLower(fn), "tail") && len(outboundPrepends) > 0 { + c.OutboundConfigs = append(outboundPrepends, c.OutboundConfigs...) } } } diff --git a/infra/conf/xray_test.go b/infra/conf/xray_test.go index c7e20ed4..a53cd216 100644 --- a/infra/conf/xray_test.go +++ b/infra/conf/xray_test.go @@ -427,7 +427,7 @@ func TestConfig_Override(t *testing.T) { &Config{InboundConfigs: []InboundDetourConfig{{Tag: "pos0"}, {Protocol: "vmess", Tag: "pos1"}}}, &Config{InboundConfigs: []InboundDetourConfig{{Tag: "pos1", Protocol: "kcp"}, {Tag: "pos2", Protocol: "kcp"}}}, "", - &Config{InboundConfigs: []InboundDetourConfig{{Tag: "pos1", Protocol: "kcp"}, {Tag: "pos2", Protocol: "kcp"}}}, + &Config{InboundConfigs: []InboundDetourConfig{{Tag: "pos0"}, {Tag: "pos1", Protocol: "kcp"}, {Tag: "pos2", Protocol: "kcp"}}}, }, { "replace/notag-append", @@ -445,10 +445,10 @@ func TestConfig_Override(t *testing.T) { }, { "replace/outbounds-prepend", - &Config{OutboundConfigs: []OutboundDetourConfig{{Tag: "pos0"}, {Protocol: "vmess", Tag: "pos1"}}}, - &Config{OutboundConfigs: []OutboundDetourConfig{{Tag: "pos1", Protocol: "kcp"}, {Tag: "pos2", Protocol: "kcp"}}}, + &Config{OutboundConfigs: []OutboundDetourConfig{{Tag: "pos0"}, {Protocol: "vmess", Tag: "pos1"}, {Tag: "pos3"}}}, + &Config{OutboundConfigs: []OutboundDetourConfig{{Tag: "pos1", Protocol: "kcp"}, {Tag: "pos2", Protocol: "kcp"}, {Tag: "pos4", Protocol: "kcp"}}}, "config.json", - &Config{OutboundConfigs: []OutboundDetourConfig{{Tag: "pos1", Protocol: "kcp"}, {Tag: "pos2", Protocol: "kcp"}}}, + &Config{OutboundConfigs: []OutboundDetourConfig{{Tag: "pos2", Protocol: "kcp"}, {Tag: "pos4", Protocol: "kcp"}, {Tag: "pos0"}, {Tag: "pos1", Protocol: "kcp"}, {Tag: "pos3"}}}, }, { "replace/outbounds-append", diff --git a/main/commands/all/x25519.go b/main/commands/all/x25519.go index e7909d9b..814cca72 100644 --- a/main/commands/all/x25519.go +++ b/main/commands/all/x25519.go @@ -10,7 +10,7 @@ import ( ) var cmdX25519 = &base.Command{ - UsageLine: `{{.Exec}} x25519 [-i "private key (base64.RawURLEncoding)"]`, + UsageLine: `{{.Exec}} x25519 [-i "private key (base64.RawURLEncoding)"] [--std-encoding]`, Short: `Generate key pair for x25519 key exchange`, Long: ` Generate key pair for x25519 key exchange. @@ -18,6 +18,7 @@ Generate key pair for x25519 key exchange. Random: {{.Exec}} x25519 From private key: {{.Exec}} x25519 -i "private key (base64.RawURLEncoding)" +For Std Encoding: {{.Exec}} x25519 --std-encoding `, } @@ -26,12 +27,14 @@ func init() { } var input_base64 = cmdX25519.Flag.String("i", "", "") +var input_stdEncoding = cmdX25519.Flag.Bool("std-encoding", false, "") func executeX25519(cmd *base.Command, args []string) { var output string var err error var privateKey []byte var publicKey []byte + var encoding *base64.Encoding if len(*input_base64) > 0 { privateKey, err = base64.RawURLEncoding.DecodeString(*input_base64) if err != nil { @@ -63,9 +66,15 @@ func executeX25519(cmd *base.Command, args []string) { goto out } + if *input_stdEncoding { + encoding = base64.StdEncoding + } else { + encoding = base64.RawURLEncoding + } + output = fmt.Sprintf("Private key: %v\nPublic key: %v", - base64.RawURLEncoding.EncodeToString(privateKey), - base64.RawURLEncoding.EncodeToString(publicKey)) + encoding.EncodeToString(privateKey), + encoding.EncodeToString(publicKey)) out: fmt.Println(output) } diff --git a/proxy/blackhole/config.pb.go b/proxy/blackhole/config.pb.go index 2bc838c6..0cdb33a7 100644 --- a/proxy/blackhole/config.pb.go +++ b/proxy/blackhole/config.pb.go @@ -1,6 +1,6 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.30.0 +// protoc-gen-go v1.31.0 // protoc v4.23.1 // source: proxy/blackhole/config.proto diff --git a/proxy/dns/config.pb.go b/proxy/dns/config.pb.go index 24016460..767ad2ca 100644 --- a/proxy/dns/config.pb.go +++ b/proxy/dns/config.pb.go @@ -1,6 +1,6 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.30.0 +// protoc-gen-go v1.31.0 // protoc v4.23.1 // source: proxy/dns/config.proto diff --git a/proxy/dokodemo/config.pb.go b/proxy/dokodemo/config.pb.go index 6e43def1..c56e74b9 100644 --- a/proxy/dokodemo/config.pb.go +++ b/proxy/dokodemo/config.pb.go @@ -1,6 +1,6 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.30.0 +// protoc-gen-go v1.31.0 // protoc v4.23.1 // source: proxy/dokodemo/config.proto diff --git a/proxy/freedom/config.go b/proxy/freedom/config.go index 61cc6ad5..2e67244b 100644 --- a/proxy/freedom/config.go +++ b/proxy/freedom/config.go @@ -1,5 +1,44 @@ package freedom -func (c *Config) useIP() bool { - return c.DomainStrategy == Config_USE_IP || c.DomainStrategy == Config_USE_IP4 || c.DomainStrategy == Config_USE_IP6 +var strategy = [][]byte{ + // name strategy, prefer, fallback + {0, 0, 0}, // AsIs none, /, / + {1, 0, 0}, // UseIP use, both, none + {1, 4, 0}, // UseIPv4 use, 4, none + {1, 6, 0}, // UseIPv6 use, 6, none + {1, 4, 6}, // UseIPv4v6 use, 4, 6 + {1, 6, 4}, // UseIPv6v4 use, 6, 4 + {2, 0, 0}, // ForceIP force, both, none + {2, 4, 0}, // ForceIPv4 force, 4, none + {2, 6, 0}, // ForceIPv6 force, 6, none + {2, 4, 6}, // ForceIPv4v6 force, 4, 6 + {2, 6, 4}, // ForceIPv6v4 force, 6, 4 +} + +func (c *Config) hasStrategy() bool { + return strategy[c.DomainStrategy][0] != 0 +} + +func (c *Config) forceIP() bool { + return strategy[c.DomainStrategy][0] == 2 +} + +func (c *Config) preferIP4() bool { + return strategy[c.DomainStrategy][1] == 4 || strategy[c.DomainStrategy][1] == 0 +} + +func (c *Config) preferIP6() bool { + return strategy[c.DomainStrategy][1] == 6 || strategy[c.DomainStrategy][1] == 0 +} + +func (c *Config) hasFallback() bool { + return strategy[c.DomainStrategy][2] != 0 +} + +func (c *Config) fallbackIP4() bool { + return strategy[c.DomainStrategy][2] == 4 +} + +func (c *Config) fallbackIP6() bool { + return strategy[c.DomainStrategy][2] == 6 } diff --git a/proxy/freedom/config.pb.go b/proxy/freedom/config.pb.go index 7561f7fd..229630d4 100644 --- a/proxy/freedom/config.pb.go +++ b/proxy/freedom/config.pb.go @@ -1,6 +1,6 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.30.0 +// protoc-gen-go v1.31.0 // protoc v4.23.1 // source: proxy/freedom/config.proto @@ -24,25 +24,46 @@ const ( type Config_DomainStrategy int32 const ( - Config_AS_IS Config_DomainStrategy = 0 - Config_USE_IP Config_DomainStrategy = 1 - Config_USE_IP4 Config_DomainStrategy = 2 - Config_USE_IP6 Config_DomainStrategy = 3 + Config_AS_IS Config_DomainStrategy = 0 + Config_USE_IP Config_DomainStrategy = 1 + Config_USE_IP4 Config_DomainStrategy = 2 + Config_USE_IP6 Config_DomainStrategy = 3 + Config_USE_IP46 Config_DomainStrategy = 4 + Config_USE_IP64 Config_DomainStrategy = 5 + Config_FORCE_IP Config_DomainStrategy = 6 + Config_FORCE_IP4 Config_DomainStrategy = 7 + Config_FORCE_IP6 Config_DomainStrategy = 8 + Config_FORCE_IP46 Config_DomainStrategy = 9 + Config_FORCE_IP64 Config_DomainStrategy = 10 ) // Enum value maps for Config_DomainStrategy. var ( Config_DomainStrategy_name = map[int32]string{ - 0: "AS_IS", - 1: "USE_IP", - 2: "USE_IP4", - 3: "USE_IP6", + 0: "AS_IS", + 1: "USE_IP", + 2: "USE_IP4", + 3: "USE_IP6", + 4: "USE_IP46", + 5: "USE_IP64", + 6: "FORCE_IP", + 7: "FORCE_IP4", + 8: "FORCE_IP6", + 9: "FORCE_IP46", + 10: "FORCE_IP64", } Config_DomainStrategy_value = map[string]int32{ - "AS_IS": 0, - "USE_IP": 1, - "USE_IP4": 2, - "USE_IP6": 3, + "AS_IS": 0, + "USE_IP": 1, + "USE_IP4": 2, + "USE_IP6": 3, + "USE_IP46": 4, + "USE_IP64": 5, + "FORCE_IP": 6, + "FORCE_IP4": 7, + "FORCE_IP6": 8, + "FORCE_IP46": 9, + "FORCE_IP64": 10, } ) @@ -314,7 +335,7 @@ var file_proxy_freedom_config_proto_rawDesc = []byte{ 0x6c, 0x5f, 0x6d, 0x69, 0x6e, 0x18, 0x05, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0b, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x4d, 0x69, 0x6e, 0x12, 0x21, 0x0a, 0x0c, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x5f, 0x6d, 0x61, 0x78, 0x18, 0x06, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0b, - 0x69, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x4d, 0x61, 0x78, 0x22, 0xf2, 0x02, 0x0a, 0x06, + 0x69, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x4d, 0x61, 0x78, 0x22, 0xdb, 0x03, 0x0a, 0x06, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x52, 0x0a, 0x0f, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x5f, 0x73, 0x74, 0x72, 0x61, 0x74, 0x65, 0x67, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x29, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x2e, 0x66, 0x72, 0x65, @@ -333,18 +354,24 @@ var file_proxy_freedom_config_proto_rawDesc = []byte{ 0x76, 0x65, 0x6c, 0x12, 0x38, 0x0a, 0x08, 0x66, 0x72, 0x61, 0x67, 0x6d, 0x65, 0x6e, 0x74, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x2e, 0x66, 0x72, 0x65, 0x65, 0x64, 0x6f, 0x6d, 0x2e, 0x46, 0x72, 0x61, 0x67, 0x6d, - 0x65, 0x6e, 0x74, 0x52, 0x08, 0x66, 0x72, 0x61, 0x67, 0x6d, 0x65, 0x6e, 0x74, 0x22, 0x41, 0x0a, - 0x0e, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x53, 0x74, 0x72, 0x61, 0x74, 0x65, 0x67, 0x79, 0x12, - 0x09, 0x0a, 0x05, 0x41, 0x53, 0x5f, 0x49, 0x53, 0x10, 0x00, 0x12, 0x0a, 0x0a, 0x06, 0x55, 0x53, - 0x45, 0x5f, 0x49, 0x50, 0x10, 0x01, 0x12, 0x0b, 0x0a, 0x07, 0x55, 0x53, 0x45, 0x5f, 0x49, 0x50, - 0x34, 0x10, 0x02, 0x12, 0x0b, 0x0a, 0x07, 0x55, 0x53, 0x45, 0x5f, 0x49, 0x50, 0x36, 0x10, 0x03, - 0x42, 0x58, 0x0a, 0x16, 0x63, 0x6f, 0x6d, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x70, 0x72, 0x6f, - 0x78, 0x79, 0x2e, 0x66, 0x72, 0x65, 0x65, 0x64, 0x6f, 0x6d, 0x50, 0x01, 0x5a, 0x27, 0x67, 0x69, - 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x78, 0x74, 0x6c, 0x73, 0x2f, 0x78, 0x72, - 0x61, 0x79, 0x2d, 0x63, 0x6f, 0x72, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x2f, 0x66, 0x72, - 0x65, 0x65, 0x64, 0x6f, 0x6d, 0xaa, 0x02, 0x12, 0x58, 0x72, 0x61, 0x79, 0x2e, 0x50, 0x72, 0x6f, - 0x78, 0x79, 0x2e, 0x46, 0x72, 0x65, 0x65, 0x64, 0x6f, 0x6d, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, - 0x6f, 0x33, + 0x65, 0x6e, 0x74, 0x52, 0x08, 0x66, 0x72, 0x61, 0x67, 0x6d, 0x65, 0x6e, 0x74, 0x22, 0xa9, 0x01, + 0x0a, 0x0e, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x53, 0x74, 0x72, 0x61, 0x74, 0x65, 0x67, 0x79, + 0x12, 0x09, 0x0a, 0x05, 0x41, 0x53, 0x5f, 0x49, 0x53, 0x10, 0x00, 0x12, 0x0a, 0x0a, 0x06, 0x55, + 0x53, 0x45, 0x5f, 0x49, 0x50, 0x10, 0x01, 0x12, 0x0b, 0x0a, 0x07, 0x55, 0x53, 0x45, 0x5f, 0x49, + 0x50, 0x34, 0x10, 0x02, 0x12, 0x0b, 0x0a, 0x07, 0x55, 0x53, 0x45, 0x5f, 0x49, 0x50, 0x36, 0x10, + 0x03, 0x12, 0x0c, 0x0a, 0x08, 0x55, 0x53, 0x45, 0x5f, 0x49, 0x50, 0x34, 0x36, 0x10, 0x04, 0x12, + 0x0c, 0x0a, 0x08, 0x55, 0x53, 0x45, 0x5f, 0x49, 0x50, 0x36, 0x34, 0x10, 0x05, 0x12, 0x0c, 0x0a, + 0x08, 0x46, 0x4f, 0x52, 0x43, 0x45, 0x5f, 0x49, 0x50, 0x10, 0x06, 0x12, 0x0d, 0x0a, 0x09, 0x46, + 0x4f, 0x52, 0x43, 0x45, 0x5f, 0x49, 0x50, 0x34, 0x10, 0x07, 0x12, 0x0d, 0x0a, 0x09, 0x46, 0x4f, + 0x52, 0x43, 0x45, 0x5f, 0x49, 0x50, 0x36, 0x10, 0x08, 0x12, 0x0e, 0x0a, 0x0a, 0x46, 0x4f, 0x52, + 0x43, 0x45, 0x5f, 0x49, 0x50, 0x34, 0x36, 0x10, 0x09, 0x12, 0x0e, 0x0a, 0x0a, 0x46, 0x4f, 0x52, + 0x43, 0x45, 0x5f, 0x49, 0x50, 0x36, 0x34, 0x10, 0x0a, 0x42, 0x58, 0x0a, 0x16, 0x63, 0x6f, 0x6d, + 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x2e, 0x66, 0x72, 0x65, 0x65, + 0x64, 0x6f, 0x6d, 0x50, 0x01, 0x5a, 0x27, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, + 0x6d, 0x2f, 0x78, 0x74, 0x6c, 0x73, 0x2f, 0x78, 0x72, 0x61, 0x79, 0x2d, 0x63, 0x6f, 0x72, 0x65, + 0x2f, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x2f, 0x66, 0x72, 0x65, 0x65, 0x64, 0x6f, 0x6d, 0xaa, 0x02, + 0x12, 0x58, 0x72, 0x61, 0x79, 0x2e, 0x50, 0x72, 0x6f, 0x78, 0x79, 0x2e, 0x46, 0x72, 0x65, 0x65, + 0x64, 0x6f, 0x6d, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( diff --git a/proxy/freedom/config.proto b/proxy/freedom/config.proto index 53524e19..0f328022 100644 --- a/proxy/freedom/config.proto +++ b/proxy/freedom/config.proto @@ -27,6 +27,13 @@ message Config { USE_IP = 1; USE_IP4 = 2; USE_IP6 = 3; + USE_IP46 = 4; + USE_IP64 = 5; + FORCE_IP = 6; + FORCE_IP4 = 7; + FORCE_IP6 = 8; + FORCE_IP46 = 9; + FORCE_IP64 = 10; } DomainStrategy domain_strategy = 1; uint32 timeout = 2 [deprecated = true]; diff --git a/proxy/freedom/freedom.go b/proxy/freedom/freedom.go index 808f837f..809d4df8 100644 --- a/proxy/freedom/freedom.go +++ b/proxy/freedom/freedom.go @@ -41,9 +41,9 @@ func init() { return h, nil })) const defaultFlagValue = "NOT_DEFINED_AT_ALL" - value := platform.NewEnvFlag("xray.buf.splice").GetValue(func() string { return defaultFlagValue }) + value := platform.NewEnvFlag(platform.UseFreedomSplice).GetValue(func() string { return defaultFlagValue }) switch value { - case "auto", "enable": + case defaultFlagValue, "auto", "enable": useSplice = true } } @@ -73,26 +73,18 @@ func (h *Handler) policy() policy.Session { } func (h *Handler) resolveIP(ctx context.Context, domain string, localAddr net.Address) net.Address { - var option dns.IPOption = dns.IPOption{ - IPv4Enable: true, - IPv6Enable: true, - FakeEnable: false, - } - if h.config.DomainStrategy == Config_USE_IP4 || (localAddr != nil && localAddr.Family().IsIPv4()) { - option = dns.IPOption{ - IPv4Enable: true, - IPv6Enable: false, - FakeEnable: false, - } - } else if h.config.DomainStrategy == Config_USE_IP6 || (localAddr != nil && localAddr.Family().IsIPv6()) { - option = dns.IPOption{ - IPv4Enable: false, - IPv6Enable: true, - FakeEnable: false, + ips, err := h.dns.LookupIP(domain, dns.IPOption{ + IPv4Enable: (localAddr == nil || localAddr.Family().IsIPv4()) && h.config.preferIP4(), + IPv6Enable: (localAddr == nil || localAddr.Family().IsIPv6()) && h.config.preferIP6(), + }) + { // Resolve fallback + if (len(ips) == 0 || err != nil) && h.config.hasFallback() && localAddr == nil { + ips, err = h.dns.LookupIP(domain, dns.IPOption{ + IPv4Enable: h.config.fallbackIP4(), + IPv6Enable: h.config.fallbackIP6(), + }) } } - - ips, err := h.dns.LookupIP(domain, option) if err != nil { newError("failed to get IP address for domain ", domain).Base(err).WriteToLog(session.ExportIDToError(ctx)) } @@ -142,7 +134,7 @@ func (h *Handler) Process(ctx context.Context, link *transport.Link, dialer inte var conn stat.Connection err := retry.ExponentialBackoff(5, 100).On(func() error { dialDest := destination - if h.config.useIP() && dialDest.Address.Family().IsDomain() { + if h.config.hasStrategy() && dialDest.Address.Family().IsDomain() { ip := h.resolveIP(ctx, dialDest.Address.Domain(), dialer.Address()) if ip != nil { dialDest = net.Destination{ @@ -151,6 +143,8 @@ func (h *Handler) Process(ctx context.Context, link *transport.Link, dialer inte Port: dialDest.Port, } newError("dialing to ", dialDest).WriteToLog(session.ExportIDToError(ctx)) + } else if h.config.forceIP() { + return dns.ErrEmptyResponse } } @@ -325,7 +319,7 @@ func (w *PacketWriter) WriteMultiBuffer(mb buf.MultiBuffer) error { if w.UDPOverride.Port != 0 { b.UDP.Port = w.UDPOverride.Port } - if w.Handler.config.useIP() && b.UDP.Address.Family().IsDomain() { + if w.Handler.config.hasStrategy() && b.UDP.Address.Family().IsDomain() { ip := w.Handler.resolveIP(w.Context, b.UDP.Address.Domain(), nil) if ip != nil { b.UDP.Address = ip diff --git a/proxy/http/config.pb.go b/proxy/http/config.pb.go index 986a038a..6ae78777 100644 --- a/proxy/http/config.pb.go +++ b/proxy/http/config.pb.go @@ -1,6 +1,6 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.30.0 +// protoc-gen-go v1.31.0 // protoc v4.23.1 // source: proxy/http/config.proto diff --git a/proxy/loopback/config.pb.go b/proxy/loopback/config.pb.go index e25f8425..3902e532 100644 --- a/proxy/loopback/config.pb.go +++ b/proxy/loopback/config.pb.go @@ -1,6 +1,6 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.30.0 +// protoc-gen-go v1.31.0 // protoc v4.23.1 // source: proxy/loopback/config.proto diff --git a/proxy/proxy.go b/proxy/proxy.go index 6df1e2bb..3cc86d7e 100644 --- a/proxy/proxy.go +++ b/proxy/proxy.go @@ -257,7 +257,7 @@ func ReshapeMultiBuffer(ctx context.Context, buffer buf.MultiBuffer) buf.MultiBu for i, buffer1 := range buffer { if buffer1.Len() >= buf.Size-21 { index := int32(bytes.LastIndex(buffer1.Bytes(), TlsApplicationDataStart)) - if index <= 0 || index > buf.Size-21 { + if index < 21 || index > buf.Size-21 { index = buf.Size / 2 } buffer2 := buf.New() diff --git a/proxy/shadowsocks/config.pb.go b/proxy/shadowsocks/config.pb.go index 5fc221da..86351df8 100644 --- a/proxy/shadowsocks/config.pb.go +++ b/proxy/shadowsocks/config.pb.go @@ -1,6 +1,6 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.30.0 +// protoc-gen-go v1.31.0 // protoc v4.23.1 // source: proxy/shadowsocks/config.proto diff --git a/proxy/shadowsocks_2022/config.pb.go b/proxy/shadowsocks_2022/config.pb.go index 8ccb848a..633f3cda 100644 --- a/proxy/shadowsocks_2022/config.pb.go +++ b/proxy/shadowsocks_2022/config.pb.go @@ -1,6 +1,6 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.30.0 +// protoc-gen-go v1.31.0 // protoc v4.23.1 // source: proxy/shadowsocks_2022/config.proto diff --git a/proxy/socks/config.pb.go b/proxy/socks/config.pb.go index a1daa276..c22af748 100644 --- a/proxy/socks/config.pb.go +++ b/proxy/socks/config.pb.go @@ -1,6 +1,6 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.30.0 +// protoc-gen-go v1.31.0 // protoc v4.23.1 // source: proxy/socks/config.proto diff --git a/proxy/trojan/config.pb.go b/proxy/trojan/config.pb.go index 49374001..d90271d9 100644 --- a/proxy/trojan/config.pb.go +++ b/proxy/trojan/config.pb.go @@ -1,6 +1,6 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.30.0 +// protoc-gen-go v1.31.0 // protoc v4.23.1 // source: proxy/trojan/config.proto diff --git a/proxy/vless/account.pb.go b/proxy/vless/account.pb.go index 1d055185..405d8811 100644 --- a/proxy/vless/account.pb.go +++ b/proxy/vless/account.pb.go @@ -1,6 +1,6 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.30.0 +// protoc-gen-go v1.31.0 // protoc v4.23.1 // source: proxy/vless/account.proto diff --git a/proxy/vless/encoding/addons.pb.go b/proxy/vless/encoding/addons.pb.go index b78c878c..8c409cbc 100644 --- a/proxy/vless/encoding/addons.pb.go +++ b/proxy/vless/encoding/addons.pb.go @@ -1,6 +1,6 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.30.0 +// protoc-gen-go v1.31.0 // protoc v4.23.1 // source: proxy/vless/encoding/addons.proto diff --git a/proxy/vless/inbound/config.pb.go b/proxy/vless/inbound/config.pb.go index f15f9114..c2ddd5e5 100644 --- a/proxy/vless/inbound/config.pb.go +++ b/proxy/vless/inbound/config.pb.go @@ -1,6 +1,6 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.30.0 +// protoc-gen-go v1.31.0 // protoc v4.23.1 // source: proxy/vless/inbound/config.proto diff --git a/proxy/vless/outbound/config.pb.go b/proxy/vless/outbound/config.pb.go index 5bd8912a..3fb9ed75 100644 --- a/proxy/vless/outbound/config.pb.go +++ b/proxy/vless/outbound/config.pb.go @@ -1,6 +1,6 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.30.0 +// protoc-gen-go v1.31.0 // protoc v4.23.1 // source: proxy/vless/outbound/config.proto diff --git a/proxy/vmess/account.pb.go b/proxy/vmess/account.pb.go index 9938cfb1..575ec583 100644 --- a/proxy/vmess/account.pb.go +++ b/proxy/vmess/account.pb.go @@ -1,6 +1,6 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.30.0 +// protoc-gen-go v1.31.0 // protoc v4.23.1 // source: proxy/vmess/account.proto diff --git a/proxy/vmess/inbound/config.pb.go b/proxy/vmess/inbound/config.pb.go index 663256aa..67fa6461 100644 --- a/proxy/vmess/inbound/config.pb.go +++ b/proxy/vmess/inbound/config.pb.go @@ -1,6 +1,6 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.30.0 +// protoc-gen-go v1.31.0 // protoc v4.23.1 // source: proxy/vmess/inbound/config.proto diff --git a/proxy/vmess/outbound/config.pb.go b/proxy/vmess/outbound/config.pb.go index b68cbffa..80492e05 100644 --- a/proxy/vmess/outbound/config.pb.go +++ b/proxy/vmess/outbound/config.pb.go @@ -1,6 +1,6 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.30.0 +// protoc-gen-go v1.31.0 // protoc v4.23.1 // source: proxy/vmess/outbound/config.proto diff --git a/proxy/vmess/outbound/outbound.go b/proxy/vmess/outbound/outbound.go index 5e228d68..c3c55d95 100644 --- a/proxy/vmess/outbound/outbound.go +++ b/proxy/vmess/outbound/outbound.go @@ -245,7 +245,7 @@ func init() { const defaultFlagValue = "NOT_DEFINED_AT_ALL" - paddingValue := platform.NewEnvFlag("xray.vmess.padding").GetValue(func() string { return defaultFlagValue }) + paddingValue := platform.NewEnvFlag(platform.UseVmessPadding).GetValue(func() string { return defaultFlagValue }) if paddingValue != defaultFlagValue { enablePadding = true } diff --git a/proxy/wireguard/bind.go b/proxy/wireguard/bind.go index 527f0e74..1fbcbc98 100644 --- a/proxy/wireguard/bind.go +++ b/proxy/wireguard/bind.go @@ -9,7 +9,8 @@ import ( "strconv" "sync" - "github.com/sagernet/wireguard-go/conn" + "golang.zx2c4.com/wireguard/conn" + xnet "github.com/xtls/xray-core/common/net" "github.com/xtls/xray-core/features/dns" "github.com/xtls/xray-core/transport/internet" @@ -26,48 +27,45 @@ type netReadInfo struct { err error } -type netBindClient struct { - workers int - dialer internet.Dialer +// reduce duplicated code +type netBind struct { dns dns.Client dnsOption dns.IPOption - reserved []byte + workers int readQueue chan *netReadInfo } -func (n *netBindClient) ParseEndpoint(s string) (conn.Endpoint, error) { - ipStr, port, _, err := splitAddrPort(s) +// SetMark implements conn.Bind +func (bind *netBind) SetMark(mark uint32) error { + return nil +} + +// ParseEndpoint implements conn.Bind +func (n *netBind) ParseEndpoint(s string) (conn.Endpoint, error) { + ipStr, port, err := net.SplitHostPort(s) + if err != nil { + return nil, err + } + portNum, err := strconv.Atoi(port) if err != nil { return nil, err } - var addr net.IP - if IsDomainName(ipStr) { - ips, err := n.dns.LookupIP(ipStr, n.dnsOption) + addr := xnet.ParseAddress(ipStr) + if addr.Family() == xnet.AddressFamilyDomain { + ips, err := n.dns.LookupIP(addr.Domain(), n.dnsOption) if err != nil { return nil, err } else if len(ips) == 0 { return nil, dns.ErrEmptyResponse } - addr = ips[0] - } else { - addr = net.ParseIP(ipStr) - } - if addr == nil { - return nil, errors.New("failed to parse ip: " + ipStr) - } - - var ip xnet.Address - if p4 := addr.To4(); len(p4) == net.IPv4len { - ip = xnet.IPAddress(p4[:]) - } else { - ip = xnet.IPAddress(addr[:]) + addr = xnet.IPAddress(ips[0]) } dst := xnet.Destination{ - Address: ip, - Port: xnet.Port(port), + Address: addr, + Port: xnet.Port(portNum), Network: xnet.Network_UDP, } @@ -76,25 +74,31 @@ func (n *netBindClient) ParseEndpoint(s string) (conn.Endpoint, error) { }, nil } -func (bind *netBindClient) Open(uport uint16) ([]conn.ReceiveFunc, uint16, error) { +// BatchSize implements conn.Bind +func (bind *netBind) BatchSize() int { + return 1 +} + +// Open implements conn.Bind +func (bind *netBind) Open(uport uint16) ([]conn.ReceiveFunc, uint16, error) { bind.readQueue = make(chan *netReadInfo) - fun := func(buff []byte) (cap int, ep conn.Endpoint, err error) { + fun := func(bufs [][]byte, sizes []int, eps []conn.Endpoint) (n int, err error) { defer func() { if r := recover(); r != nil { - cap = 0 - ep = nil + n = 0 err = errors.New("channel closed") } }() r := &netReadInfo{ - buff: buff, + buff: bufs[0], } r.waiter.Add(1) bind.readQueue <- r r.waiter.Wait() // wait read goroutine done, or we will miss the result - return r.bytes, r.endpoint, r.err + sizes[0], eps[0] = r.bytes, r.endpoint + return 1, r.err } workers := bind.workers if workers <= 0 { @@ -108,13 +112,21 @@ func (bind *netBindClient) Open(uport uint16) ([]conn.ReceiveFunc, uint16, error return arr, uint16(uport), nil } -func (bind *netBindClient) Close() error { +// Close implements conn.Bind +func (bind *netBind) Close() error { if bind.readQueue != nil { close(bind.readQueue) } return nil } +type netBindClient struct { + netBind + + dialer internet.Dialer + reserved []byte +} + func (bind *netBindClient) connectTo(endpoint *netEndpoint) error { c, err := bind.dialer.Dial(context.Background(), endpoint.dst) if err != nil { @@ -150,7 +162,7 @@ func (bind *netBindClient) connectTo(endpoint *netEndpoint) error { return nil } -func (bind *netBindClient) Send(buff []byte, endpoint conn.Endpoint) error { +func (bind *netBindClient) Send(buff [][]byte, endpoint conn.Endpoint) error { var err error nend, ok := endpoint.(*netEndpoint) @@ -165,17 +177,40 @@ func (bind *netBindClient) Send(buff []byte, endpoint conn.Endpoint) error { } } - if len(buff) > 3 && len(bind.reserved) == 3 { - copy(buff[1:], bind.reserved) + for _, buff := range buff { + if len(buff) > 3 && len(bind.reserved) == 3 { + copy(buff[1:], bind.reserved) + } + if _, err = nend.conn.Write(buff); err != nil { + return err + } } - - _, err = nend.conn.Write(buff) - - return err + return nil } -func (bind *netBindClient) SetMark(mark uint32) error { - return nil +type netBindServer struct { + netBind +} + +func (bind *netBindServer) Send(buff [][]byte, endpoint conn.Endpoint) error { + var err error + + nend, ok := endpoint.(*netEndpoint) + if !ok { + return conn.ErrWrongEndpointType + } + + if nend.conn == nil { + return newError("connection not open yet") + } + + for _, buff := range buff { + if _, err = nend.conn.Write(buff); err != nil { + return err + } + } + + return err } type netEndpoint struct { @@ -186,7 +221,7 @@ type netEndpoint struct { func (netEndpoint) ClearSrc() {} func (e netEndpoint) DstIP() netip.Addr { - return toNetIpAddr(e.dst.Address) + return netip.Addr{} } func (e netEndpoint) SrcIP() netip.Addr { @@ -225,42 +260,3 @@ func toNetIpAddr(addr xnet.Address) netip.Addr { return netip.AddrFrom16(arr) } } - -func stringsLastIndexByte(s string, b byte) int { - for i := len(s) - 1; i >= 0; i-- { - if s[i] == b { - return i - } - } - return -1 -} - -func splitAddrPort(s string) (ip string, port uint16, v6 bool, err error) { - i := stringsLastIndexByte(s, ':') - if i == -1 { - return "", 0, false, errors.New("not an ip:port") - } - - ip = s[:i] - portStr := s[i+1:] - if len(ip) == 0 { - return "", 0, false, errors.New("no IP") - } - if len(portStr) == 0 { - return "", 0, false, errors.New("no port") - } - port64, err := strconv.ParseUint(portStr, 10, 16) - if err != nil { - return "", 0, false, errors.New("invalid port " + strconv.Quote(portStr) + " parsing " + strconv.Quote(s)) - } - port = uint16(port64) - if ip[0] == '[' { - if len(ip) < 2 || ip[len(ip)-1] != ']' { - return "", 0, false, errors.New("missing ]") - } - ip = ip[1 : len(ip)-1] - v6 = true - } - - return ip, port, v6, nil -} diff --git a/proxy/wireguard/client.go b/proxy/wireguard/client.go new file mode 100644 index 00000000..def07878 --- /dev/null +++ b/proxy/wireguard/client.go @@ -0,0 +1,255 @@ +/* + +Some of codes are copied from https://github.com/octeep/wireproxy, license below. + +Copyright (c) 2022 Wind T.F. Wong + +Permission to use, copy, modify, and distribute this software for any +purpose with or without fee is hereby granted, provided that the above +copyright notice and this permission notice appear in all copies. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF +OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +*/ + +package wireguard + +import ( + "context" + "net/netip" + "sync" + + "github.com/xtls/xray-core/common" + "github.com/xtls/xray-core/common/buf" + "github.com/xtls/xray-core/common/dice" + "github.com/xtls/xray-core/common/log" + "github.com/xtls/xray-core/common/net" + "github.com/xtls/xray-core/common/protocol" + "github.com/xtls/xray-core/common/session" + "github.com/xtls/xray-core/common/signal" + "github.com/xtls/xray-core/common/task" + "github.com/xtls/xray-core/core" + "github.com/xtls/xray-core/features/dns" + "github.com/xtls/xray-core/features/policy" + "github.com/xtls/xray-core/transport" + "github.com/xtls/xray-core/transport/internet" +) + +// Handler is an outbound connection that silently swallow the entire payload. +type Handler struct { + conf *DeviceConfig + net Tunnel + bind *netBindClient + policyManager policy.Manager + dns dns.Client + // cached configuration + ipc string + endpoints []netip.Addr + hasIPv4, hasIPv6 bool + wgLock sync.Mutex +} + +// New creates a new wireguard handler. +func New(ctx context.Context, conf *DeviceConfig) (*Handler, error) { + v := core.MustFromContext(ctx) + + endpoints, hasIPv4, hasIPv6, err := parseEndpoints(conf) + if err != nil { + return nil, err + } + + d := v.GetFeature(dns.ClientType()).(dns.Client) + return &Handler{ + conf: conf, + policyManager: v.GetFeature(policy.ManagerType()).(policy.Manager), + dns: d, + ipc: createIPCRequest(conf), + endpoints: endpoints, + hasIPv4: hasIPv4, + hasIPv6: hasIPv6, + }, nil +} + +func (h *Handler) processWireGuard(dialer internet.Dialer) (err error) { + h.wgLock.Lock() + defer h.wgLock.Unlock() + + if h.bind != nil && h.bind.dialer == dialer && h.net != nil { + return nil + } + + log.Record(&log.GeneralMessage{ + Severity: log.Severity_Info, + Content: "switching dialer", + }) + + if h.net != nil { + _ = h.net.Close() + h.net = nil + } + if h.bind != nil { + _ = h.bind.Close() + h.bind = nil + } + + // bind := conn.NewStdNetBind() // TODO: conn.Bind wrapper for dialer + bind := &netBindClient{ + netBind: netBind{ + dns: h.dns, + dnsOption: dns.IPOption{ + IPv4Enable: h.hasIPv4, + IPv6Enable: h.hasIPv6, + }, + workers: int(h.conf.NumWorkers), + }, + dialer: dialer, + reserved: h.conf.Reserved, + } + defer func() { + if err != nil { + _ = bind.Close() + } + }() + + h.net, err = h.makeVirtualTun(bind) + if err != nil { + return newError("failed to create virtual tun interface").Base(err) + } + h.bind = bind + return nil +} + +// Process implements OutboundHandler.Dispatch(). +func (h *Handler) Process(ctx context.Context, link *transport.Link, dialer internet.Dialer) error { + outbound := session.OutboundFromContext(ctx) + if outbound == nil || !outbound.Target.IsValid() { + return newError("target not specified") + } + outbound.Name = "wireguard" + inbound := session.InboundFromContext(ctx) + if inbound != nil { + inbound.SetCanSpliceCopy(3) + } + + if err := h.processWireGuard(dialer); err != nil { + return err + } + + // Destination of the inner request. + destination := outbound.Target + command := protocol.RequestCommandTCP + if destination.Network == net.Network_UDP { + command = protocol.RequestCommandUDP + } + + // resolve dns + addr := destination.Address + if addr.Family().IsDomain() { + ips, err := h.dns.LookupIP(addr.Domain(), dns.IPOption{ + IPv4Enable: h.hasIPv4 && h.conf.preferIP4(), + IPv6Enable: h.hasIPv6 && h.conf.preferIP6(), + }) + { // Resolve fallback + if (len(ips) == 0 || err != nil) && h.conf.hasFallback() { + ips, err = h.dns.LookupIP(addr.Domain(), dns.IPOption{ + IPv4Enable: h.hasIPv4 && h.conf.fallbackIP4(), + IPv6Enable: h.hasIPv6 && h.conf.fallbackIP6(), + }) + } + } + if err != nil { + return newError("failed to lookup DNS").Base(err) + } else if len(ips) == 0 { + return dns.ErrEmptyResponse + } + addr = net.IPAddress(ips[dice.Roll(len(ips))]) + } + + var newCtx context.Context + var newCancel context.CancelFunc + if session.TimeoutOnlyFromContext(ctx) { + newCtx, newCancel = context.WithCancel(context.Background()) + } + + p := h.policyManager.ForLevel(0) + + ctx, cancel := context.WithCancel(ctx) + timer := signal.CancelAfterInactivity(ctx, func() { + cancel() + if newCancel != nil { + newCancel() + } + }, p.Timeouts.ConnectionIdle) + addrPort := netip.AddrPortFrom(toNetIpAddr(addr), destination.Port.Value()) + + var requestFunc func() error + var responseFunc func() error + + if command == protocol.RequestCommandTCP { + conn, err := h.net.DialContextTCPAddrPort(ctx, addrPort) + if err != nil { + return newError("failed to create TCP connection").Base(err) + } + defer conn.Close() + + requestFunc = func() error { + defer timer.SetTimeout(p.Timeouts.DownlinkOnly) + return buf.Copy(link.Reader, buf.NewWriter(conn), buf.UpdateActivity(timer)) + } + responseFunc = func() error { + defer timer.SetTimeout(p.Timeouts.UplinkOnly) + return buf.Copy(buf.NewReader(conn), link.Writer, buf.UpdateActivity(timer)) + } + } else if command == protocol.RequestCommandUDP { + conn, err := h.net.DialUDPAddrPort(netip.AddrPort{}, addrPort) + if err != nil { + return newError("failed to create UDP connection").Base(err) + } + defer conn.Close() + + requestFunc = func() error { + defer timer.SetTimeout(p.Timeouts.DownlinkOnly) + return buf.Copy(link.Reader, buf.NewWriter(conn), buf.UpdateActivity(timer)) + } + responseFunc = func() error { + defer timer.SetTimeout(p.Timeouts.UplinkOnly) + return buf.Copy(buf.NewReader(conn), link.Writer, buf.UpdateActivity(timer)) + } + } + + if newCtx != nil { + ctx = newCtx + } + + responseDonePost := task.OnSuccess(responseFunc, task.Close(link.Writer)) + if err := task.Run(ctx, requestFunc, responseDonePost); err != nil { + common.Interrupt(link.Reader) + common.Interrupt(link.Writer) + return newError("connection ends").Base(err) + } + + return nil +} + +// creates a tun interface on netstack given a configuration +func (h *Handler) makeVirtualTun(bind *netBindClient) (Tunnel, error) { + t, err := h.conf.createTun()(h.endpoints, int(h.conf.Mtu), nil) + if err != nil { + return nil, err + } + + bind.dnsOption.IPv4Enable = h.hasIPv4 + bind.dnsOption.IPv6Enable = h.hasIPv6 + + if err = t.BuildDevice(h.ipc, bind); err != nil { + _ = t.Close() + return nil, err + } + return t, nil +} diff --git a/proxy/wireguard/config.go b/proxy/wireguard/config.go new file mode 100644 index 00000000..2a316cdd --- /dev/null +++ b/proxy/wireguard/config.go @@ -0,0 +1,32 @@ +package wireguard + +func (c *DeviceConfig) preferIP4() bool { + return c.DomainStrategy == DeviceConfig_FORCE_IP || + c.DomainStrategy == DeviceConfig_FORCE_IP4 || + c.DomainStrategy == DeviceConfig_FORCE_IP46 +} + +func (c *DeviceConfig) preferIP6() bool { + return c.DomainStrategy == DeviceConfig_FORCE_IP || + c.DomainStrategy == DeviceConfig_FORCE_IP6 || + c.DomainStrategy == DeviceConfig_FORCE_IP64 +} + +func (c *DeviceConfig) hasFallback() bool { + return c.DomainStrategy == DeviceConfig_FORCE_IP46 || c.DomainStrategy == DeviceConfig_FORCE_IP64 +} + +func (c *DeviceConfig) fallbackIP4() bool { + return c.DomainStrategy == DeviceConfig_FORCE_IP64 +} + +func (c *DeviceConfig) fallbackIP6() bool { + return c.DomainStrategy == DeviceConfig_FORCE_IP46 +} + +func (c *DeviceConfig) createTun() tunCreator { + if c.KernelMode { + return createKernelTun + } + return createGVisorTun +} diff --git a/proxy/wireguard/config.pb.go b/proxy/wireguard/config.pb.go index 442d78f0..ed8b135e 100644 --- a/proxy/wireguard/config.pb.go +++ b/proxy/wireguard/config.pb.go @@ -1,7 +1,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.30.0 -// protoc v4.23.1 +// protoc-gen-go v1.28.1 +// protoc v4.25.0 // source: proxy/wireguard/config.proto package wireguard @@ -20,6 +20,61 @@ const ( _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) ) +type DeviceConfig_DomainStrategy int32 + +const ( + DeviceConfig_FORCE_IP DeviceConfig_DomainStrategy = 0 + DeviceConfig_FORCE_IP4 DeviceConfig_DomainStrategy = 1 + DeviceConfig_FORCE_IP6 DeviceConfig_DomainStrategy = 2 + DeviceConfig_FORCE_IP46 DeviceConfig_DomainStrategy = 3 + DeviceConfig_FORCE_IP64 DeviceConfig_DomainStrategy = 4 +) + +// Enum value maps for DeviceConfig_DomainStrategy. +var ( + DeviceConfig_DomainStrategy_name = map[int32]string{ + 0: "FORCE_IP", + 1: "FORCE_IP4", + 2: "FORCE_IP6", + 3: "FORCE_IP46", + 4: "FORCE_IP64", + } + DeviceConfig_DomainStrategy_value = map[string]int32{ + "FORCE_IP": 0, + "FORCE_IP4": 1, + "FORCE_IP6": 2, + "FORCE_IP46": 3, + "FORCE_IP64": 4, + } +) + +func (x DeviceConfig_DomainStrategy) Enum() *DeviceConfig_DomainStrategy { + p := new(DeviceConfig_DomainStrategy) + *p = x + return p +} + +func (x DeviceConfig_DomainStrategy) String() string { + return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) +} + +func (DeviceConfig_DomainStrategy) Descriptor() protoreflect.EnumDescriptor { + return file_proxy_wireguard_config_proto_enumTypes[0].Descriptor() +} + +func (DeviceConfig_DomainStrategy) Type() protoreflect.EnumType { + return &file_proxy_wireguard_config_proto_enumTypes[0] +} + +func (x DeviceConfig_DomainStrategy) Number() protoreflect.EnumNumber { + return protoreflect.EnumNumber(x) +} + +// Deprecated: Use DeviceConfig_DomainStrategy.Descriptor instead. +func (DeviceConfig_DomainStrategy) EnumDescriptor() ([]byte, []int) { + return file_proxy_wireguard_config_proto_rawDescGZIP(), []int{1, 0} +} + type PeerConfig struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -28,7 +83,7 @@ type PeerConfig struct { PublicKey string `protobuf:"bytes,1,opt,name=public_key,json=publicKey,proto3" json:"public_key,omitempty"` PreSharedKey string `protobuf:"bytes,2,opt,name=pre_shared_key,json=preSharedKey,proto3" json:"pre_shared_key,omitempty"` Endpoint string `protobuf:"bytes,3,opt,name=endpoint,proto3" json:"endpoint,omitempty"` - KeepAlive int32 `protobuf:"varint,4,opt,name=keep_alive,json=keepAlive,proto3" json:"keep_alive,omitempty"` + KeepAlive uint32 `protobuf:"varint,4,opt,name=keep_alive,json=keepAlive,proto3" json:"keep_alive,omitempty"` AllowedIps []string `protobuf:"bytes,5,rep,name=allowed_ips,json=allowedIps,proto3" json:"allowed_ips,omitempty"` } @@ -85,7 +140,7 @@ func (x *PeerConfig) GetEndpoint() string { return "" } -func (x *PeerConfig) GetKeepAlive() int32 { +func (x *PeerConfig) GetKeepAlive() uint32 { if x != nil { return x.KeepAlive } @@ -104,12 +159,15 @@ type DeviceConfig struct { sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - SecretKey string `protobuf:"bytes,1,opt,name=secret_key,json=secretKey,proto3" json:"secret_key,omitempty"` - Endpoint []string `protobuf:"bytes,2,rep,name=endpoint,proto3" json:"endpoint,omitempty"` - Peers []*PeerConfig `protobuf:"bytes,3,rep,name=peers,proto3" json:"peers,omitempty"` - Mtu int32 `protobuf:"varint,4,opt,name=mtu,proto3" json:"mtu,omitempty"` - NumWorkers int32 `protobuf:"varint,5,opt,name=num_workers,json=numWorkers,proto3" json:"num_workers,omitempty"` - Reserved []byte `protobuf:"bytes,6,opt,name=reserved,proto3" json:"reserved,omitempty"` + SecretKey string `protobuf:"bytes,1,opt,name=secret_key,json=secretKey,proto3" json:"secret_key,omitempty"` + Endpoint []string `protobuf:"bytes,2,rep,name=endpoint,proto3" json:"endpoint,omitempty"` + Peers []*PeerConfig `protobuf:"bytes,3,rep,name=peers,proto3" json:"peers,omitempty"` + Mtu int32 `protobuf:"varint,4,opt,name=mtu,proto3" json:"mtu,omitempty"` + NumWorkers int32 `protobuf:"varint,5,opt,name=num_workers,json=numWorkers,proto3" json:"num_workers,omitempty"` + Reserved []byte `protobuf:"bytes,6,opt,name=reserved,proto3" json:"reserved,omitempty"` + DomainStrategy DeviceConfig_DomainStrategy `protobuf:"varint,7,opt,name=domain_strategy,json=domainStrategy,proto3,enum=xray.proxy.wireguard.DeviceConfig_DomainStrategy" json:"domain_strategy,omitempty"` + IsClient bool `protobuf:"varint,8,opt,name=is_client,json=isClient,proto3" json:"is_client,omitempty"` + KernelMode bool `protobuf:"varint,9,opt,name=kernel_mode,json=kernelMode,proto3" json:"kernel_mode,omitempty"` } func (x *DeviceConfig) Reset() { @@ -186,6 +244,27 @@ func (x *DeviceConfig) GetReserved() []byte { return nil } +func (x *DeviceConfig) GetDomainStrategy() DeviceConfig_DomainStrategy { + if x != nil { + return x.DomainStrategy + } + return DeviceConfig_FORCE_IP +} + +func (x *DeviceConfig) GetIsClient() bool { + if x != nil { + return x.IsClient + } + return false +} + +func (x *DeviceConfig) GetKernelMode() bool { + if x != nil { + return x.KernelMode + } + return false +} + var File_proxy_wireguard_config_proto protoreflect.FileDescriptor var file_proxy_wireguard_config_proto_rawDesc = []byte{ @@ -200,10 +279,10 @@ var file_proxy_wireguard_config_proto_rawDesc = []byte{ 0x68, 0x61, 0x72, 0x65, 0x64, 0x4b, 0x65, 0x79, 0x12, 0x1a, 0x0a, 0x08, 0x65, 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x65, 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x12, 0x1d, 0x0a, 0x0a, 0x6b, 0x65, 0x65, 0x70, 0x5f, 0x61, 0x6c, 0x69, - 0x76, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x05, 0x52, 0x09, 0x6b, 0x65, 0x65, 0x70, 0x41, 0x6c, + 0x76, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x09, 0x6b, 0x65, 0x65, 0x70, 0x41, 0x6c, 0x69, 0x76, 0x65, 0x12, 0x1f, 0x0a, 0x0b, 0x61, 0x6c, 0x6c, 0x6f, 0x77, 0x65, 0x64, 0x5f, 0x69, 0x70, 0x73, 0x18, 0x05, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0a, 0x61, 0x6c, 0x6c, 0x6f, 0x77, 0x65, - 0x64, 0x49, 0x70, 0x73, 0x22, 0xd0, 0x01, 0x0a, 0x0c, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x43, + 0x64, 0x49, 0x70, 0x73, 0x22, 0xc8, 0x03, 0x0a, 0x0c, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x1d, 0x0a, 0x0a, 0x73, 0x65, 0x63, 0x72, 0x65, 0x74, 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x73, 0x65, 0x63, 0x72, 0x65, 0x74, 0x4b, 0x65, 0x79, 0x12, 0x1a, 0x0a, 0x08, 0x65, 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74, @@ -216,13 +295,29 @@ var file_proxy_wireguard_config_proto_rawDesc = []byte{ 0x6d, 0x5f, 0x77, 0x6f, 0x72, 0x6b, 0x65, 0x72, 0x73, 0x18, 0x05, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0a, 0x6e, 0x75, 0x6d, 0x57, 0x6f, 0x72, 0x6b, 0x65, 0x72, 0x73, 0x12, 0x1a, 0x0a, 0x08, 0x72, 0x65, 0x73, 0x65, 0x72, 0x76, 0x65, 0x64, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x08, 0x72, - 0x65, 0x73, 0x65, 0x72, 0x76, 0x65, 0x64, 0x42, 0x5e, 0x0a, 0x18, 0x63, 0x6f, 0x6d, 0x2e, 0x78, - 0x72, 0x61, 0x79, 0x2e, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x2e, 0x77, 0x69, 0x72, 0x65, 0x67, 0x75, - 0x61, 0x72, 0x64, 0x50, 0x01, 0x5a, 0x29, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, - 0x6d, 0x2f, 0x78, 0x74, 0x6c, 0x73, 0x2f, 0x78, 0x72, 0x61, 0x79, 0x2d, 0x63, 0x6f, 0x72, 0x65, - 0x2f, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x2f, 0x77, 0x69, 0x72, 0x65, 0x67, 0x75, 0x61, 0x72, 0x64, - 0xaa, 0x02, 0x14, 0x58, 0x72, 0x61, 0x79, 0x2e, 0x50, 0x72, 0x6f, 0x78, 0x79, 0x2e, 0x57, 0x69, - 0x72, 0x65, 0x47, 0x75, 0x61, 0x72, 0x64, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x65, 0x73, 0x65, 0x72, 0x76, 0x65, 0x64, 0x12, 0x5a, 0x0a, 0x0f, 0x64, 0x6f, 0x6d, 0x61, 0x69, + 0x6e, 0x5f, 0x73, 0x74, 0x72, 0x61, 0x74, 0x65, 0x67, 0x79, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0e, + 0x32, 0x31, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x2e, 0x77, 0x69, + 0x72, 0x65, 0x67, 0x75, 0x61, 0x72, 0x64, 0x2e, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x43, 0x6f, + 0x6e, 0x66, 0x69, 0x67, 0x2e, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x53, 0x74, 0x72, 0x61, 0x74, + 0x65, 0x67, 0x79, 0x52, 0x0e, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x53, 0x74, 0x72, 0x61, 0x74, + 0x65, 0x67, 0x79, 0x12, 0x1b, 0x0a, 0x09, 0x69, 0x73, 0x5f, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, + 0x18, 0x08, 0x20, 0x01, 0x28, 0x08, 0x52, 0x08, 0x69, 0x73, 0x43, 0x6c, 0x69, 0x65, 0x6e, 0x74, + 0x12, 0x1f, 0x0a, 0x0b, 0x6b, 0x65, 0x72, 0x6e, 0x65, 0x6c, 0x5f, 0x6d, 0x6f, 0x64, 0x65, 0x18, + 0x09, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0a, 0x6b, 0x65, 0x72, 0x6e, 0x65, 0x6c, 0x4d, 0x6f, 0x64, + 0x65, 0x22, 0x5c, 0x0a, 0x0e, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x53, 0x74, 0x72, 0x61, 0x74, + 0x65, 0x67, 0x79, 0x12, 0x0c, 0x0a, 0x08, 0x46, 0x4f, 0x52, 0x43, 0x45, 0x5f, 0x49, 0x50, 0x10, + 0x00, 0x12, 0x0d, 0x0a, 0x09, 0x46, 0x4f, 0x52, 0x43, 0x45, 0x5f, 0x49, 0x50, 0x34, 0x10, 0x01, + 0x12, 0x0d, 0x0a, 0x09, 0x46, 0x4f, 0x52, 0x43, 0x45, 0x5f, 0x49, 0x50, 0x36, 0x10, 0x02, 0x12, + 0x0e, 0x0a, 0x0a, 0x46, 0x4f, 0x52, 0x43, 0x45, 0x5f, 0x49, 0x50, 0x34, 0x36, 0x10, 0x03, 0x12, + 0x0e, 0x0a, 0x0a, 0x46, 0x4f, 0x52, 0x43, 0x45, 0x5f, 0x49, 0x50, 0x36, 0x34, 0x10, 0x04, 0x42, + 0x5e, 0x0a, 0x18, 0x63, 0x6f, 0x6d, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x70, 0x72, 0x6f, 0x78, + 0x79, 0x2e, 0x77, 0x69, 0x72, 0x65, 0x67, 0x75, 0x61, 0x72, 0x64, 0x50, 0x01, 0x5a, 0x29, 0x67, + 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x78, 0x74, 0x6c, 0x73, 0x2f, 0x78, + 0x72, 0x61, 0x79, 0x2d, 0x63, 0x6f, 0x72, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x2f, 0x77, + 0x69, 0x72, 0x65, 0x67, 0x75, 0x61, 0x72, 0x64, 0xaa, 0x02, 0x14, 0x58, 0x72, 0x61, 0x79, 0x2e, + 0x50, 0x72, 0x6f, 0x78, 0x79, 0x2e, 0x57, 0x69, 0x72, 0x65, 0x47, 0x75, 0x61, 0x72, 0x64, 0x62, + 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( @@ -237,18 +332,21 @@ func file_proxy_wireguard_config_proto_rawDescGZIP() []byte { return file_proxy_wireguard_config_proto_rawDescData } +var file_proxy_wireguard_config_proto_enumTypes = make([]protoimpl.EnumInfo, 1) var file_proxy_wireguard_config_proto_msgTypes = make([]protoimpl.MessageInfo, 2) var file_proxy_wireguard_config_proto_goTypes = []interface{}{ - (*PeerConfig)(nil), // 0: xray.proxy.wireguard.PeerConfig - (*DeviceConfig)(nil), // 1: xray.proxy.wireguard.DeviceConfig + (DeviceConfig_DomainStrategy)(0), // 0: xray.proxy.wireguard.DeviceConfig.DomainStrategy + (*PeerConfig)(nil), // 1: xray.proxy.wireguard.PeerConfig + (*DeviceConfig)(nil), // 2: xray.proxy.wireguard.DeviceConfig } var file_proxy_wireguard_config_proto_depIdxs = []int32{ - 0, // 0: xray.proxy.wireguard.DeviceConfig.peers:type_name -> xray.proxy.wireguard.PeerConfig - 1, // [1:1] is the sub-list for method output_type - 1, // [1:1] is the sub-list for method input_type - 1, // [1:1] is the sub-list for extension type_name - 1, // [1:1] is the sub-list for extension extendee - 0, // [0:1] is the sub-list for field type_name + 1, // 0: xray.proxy.wireguard.DeviceConfig.peers:type_name -> xray.proxy.wireguard.PeerConfig + 0, // 1: xray.proxy.wireguard.DeviceConfig.domain_strategy:type_name -> xray.proxy.wireguard.DeviceConfig.DomainStrategy + 2, // [2:2] is the sub-list for method output_type + 2, // [2:2] is the sub-list for method input_type + 2, // [2:2] is the sub-list for extension type_name + 2, // [2:2] is the sub-list for extension extendee + 0, // [0:2] is the sub-list for field type_name } func init() { file_proxy_wireguard_config_proto_init() } @@ -287,13 +385,14 @@ func file_proxy_wireguard_config_proto_init() { File: protoimpl.DescBuilder{ GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: file_proxy_wireguard_config_proto_rawDesc, - NumEnums: 0, + NumEnums: 1, NumMessages: 2, NumExtensions: 0, NumServices: 0, }, GoTypes: file_proxy_wireguard_config_proto_goTypes, DependencyIndexes: file_proxy_wireguard_config_proto_depIdxs, + EnumInfos: file_proxy_wireguard_config_proto_enumTypes, MessageInfos: file_proxy_wireguard_config_proto_msgTypes, }.Build() File_proxy_wireguard_config_proto = out.File diff --git a/proxy/wireguard/config.proto b/proxy/wireguard/config.proto index 810a1126..e7fd66f4 100644 --- a/proxy/wireguard/config.proto +++ b/proxy/wireguard/config.proto @@ -7,18 +7,28 @@ option java_package = "com.xray.proxy.wireguard"; option java_multiple_files = true; message PeerConfig { - string public_key = 1; - string pre_shared_key = 2; - string endpoint = 3; - int32 keep_alive = 4; - repeated string allowed_ips = 5; + string public_key = 1; + string pre_shared_key = 2; + string endpoint = 3; + uint32 keep_alive = 4; + repeated string allowed_ips = 5; } message DeviceConfig { - string secret_key = 1; - repeated string endpoint = 2; - repeated PeerConfig peers = 3; - int32 mtu = 4; - int32 num_workers = 5; - bytes reserved = 6; + enum DomainStrategy { + FORCE_IP = 0; + FORCE_IP4 = 1; + FORCE_IP6 = 2; + FORCE_IP46 = 3; + FORCE_IP64 = 4; + } + string secret_key = 1; + repeated string endpoint = 2; + repeated PeerConfig peers = 3; + int32 mtu = 4; + int32 num_workers = 5; + bytes reserved = 6; + DomainStrategy domain_strategy = 7; + bool is_client = 8; + bool kernel_mode = 9; } \ No newline at end of file diff --git a/proxy/wireguard/gvisortun/tun.go b/proxy/wireguard/gvisortun/tun.go new file mode 100644 index 00000000..9e9a0b2b --- /dev/null +++ b/proxy/wireguard/gvisortun/tun.go @@ -0,0 +1,230 @@ +/* SPDX-License-Identifier: MIT + * + * Copyright (C) 2017-2022 WireGuard LLC. All Rights Reserved. + */ + +package gvisortun + +import ( + "context" + "fmt" + "net/netip" + "os" + "syscall" + + "golang.zx2c4.com/wireguard/tun" + "gvisor.dev/gvisor/pkg/buffer" + "gvisor.dev/gvisor/pkg/tcpip" + "gvisor.dev/gvisor/pkg/tcpip/adapters/gonet" + "gvisor.dev/gvisor/pkg/tcpip/header" + "gvisor.dev/gvisor/pkg/tcpip/link/channel" + "gvisor.dev/gvisor/pkg/tcpip/network/ipv4" + "gvisor.dev/gvisor/pkg/tcpip/network/ipv6" + "gvisor.dev/gvisor/pkg/tcpip/stack" + "gvisor.dev/gvisor/pkg/tcpip/transport/icmp" + "gvisor.dev/gvisor/pkg/tcpip/transport/tcp" + "gvisor.dev/gvisor/pkg/tcpip/transport/udp" +) + +type netTun struct { + ep *channel.Endpoint + stack *stack.Stack + events chan tun.Event + incomingPacket chan *buffer.View + mtu int + hasV4, hasV6 bool +} + +type Net netTun + +func CreateNetTUN(localAddresses []netip.Addr, mtu int, promiscuousMode bool) (tun.Device, *Net, *stack.Stack, error) { + opts := stack.Options{ + NetworkProtocols: []stack.NetworkProtocolFactory{ipv4.NewProtocol, ipv6.NewProtocol}, + TransportProtocols: []stack.TransportProtocolFactory{tcp.NewProtocol, udp.NewProtocol, icmp.NewProtocol6, icmp.NewProtocol4}, + HandleLocal: !promiscuousMode, + } + dev := &netTun{ + ep: channel.New(1024, uint32(mtu), ""), + stack: stack.New(opts), + events: make(chan tun.Event, 1), + incomingPacket: make(chan *buffer.View), + mtu: mtu, + } + dev.ep.AddNotify(dev) + tcpipErr := dev.stack.CreateNIC(1, dev.ep) + if tcpipErr != nil { + return nil, nil, dev.stack, fmt.Errorf("CreateNIC: %v", tcpipErr) + } + for _, ip := range localAddresses { + var protoNumber tcpip.NetworkProtocolNumber + if ip.Is4() { + protoNumber = ipv4.ProtocolNumber + } else if ip.Is6() { + protoNumber = ipv6.ProtocolNumber + } + protoAddr := tcpip.ProtocolAddress{ + Protocol: protoNumber, + AddressWithPrefix: tcpip.AddrFromSlice(ip.AsSlice()).WithPrefix(), + } + tcpipErr := dev.stack.AddProtocolAddress(1, protoAddr, stack.AddressProperties{}) + if tcpipErr != nil { + return nil, nil, dev.stack, fmt.Errorf("AddProtocolAddress(%v): %v", ip, tcpipErr) + } + if ip.Is4() { + dev.hasV4 = true + } else if ip.Is6() { + dev.hasV6 = true + } + } + if dev.hasV4 { + dev.stack.AddRoute(tcpip.Route{Destination: header.IPv4EmptySubnet, NIC: 1}) + } + if dev.hasV6 { + dev.stack.AddRoute(tcpip.Route{Destination: header.IPv6EmptySubnet, NIC: 1}) + } + if promiscuousMode { + // enable promiscuous mode to handle all packets processed by netstack + dev.stack.SetPromiscuousMode(1, true) + dev.stack.SetSpoofing(1, true) + } + + opt := tcpip.CongestionControlOption("cubic") + if err := dev.stack.SetTransportProtocolOption(tcp.ProtocolNumber, &opt); err != nil { + return nil, nil, dev.stack, fmt.Errorf("SetTransportProtocolOption(%d, &%T(%s)): %s", tcp.ProtocolNumber, opt, opt, err) + } + + dev.events <- tun.EventUp + return dev, (*Net)(dev), dev.stack, nil +} + +// BatchSize implements tun.Device +func (tun *netTun) BatchSize() int { + return 1 +} + +// Name implements tun.Device +func (tun *netTun) Name() (string, error) { + return "go", nil +} + +// File implements tun.Device +func (tun *netTun) File() *os.File { + return nil +} + +// Events implements tun.Device +func (tun *netTun) Events() <-chan tun.Event { + return tun.events +} + +// Read implements tun.Device + +func (tun *netTun) Read(buf [][]byte, sizes []int, offset int) (int, error) { + view, ok := <-tun.incomingPacket + if !ok { + return 0, os.ErrClosed + } + + n, err := view.Read(buf[0][offset:]) + if err != nil { + return 0, err + } + sizes[0] = n + return 1, nil +} + +// Write implements tun.Device +func (tun *netTun) Write(buf [][]byte, offset int) (int, error) { + for _, buf := range buf { + packet := buf[offset:] + if len(packet) == 0 { + continue + } + + pkb := stack.NewPacketBuffer(stack.PacketBufferOptions{Payload: buffer.MakeWithData(packet)}) + switch packet[0] >> 4 { + case 4: + tun.ep.InjectInbound(header.IPv4ProtocolNumber, pkb) + case 6: + tun.ep.InjectInbound(header.IPv6ProtocolNumber, pkb) + default: + return 0, syscall.EAFNOSUPPORT + } + } + return len(buf), nil +} + +// WriteNotify implements channel.Notification +func (tun *netTun) WriteNotify() { + pkt := tun.ep.Read() + if pkt.IsNil() { + return + } + + view := pkt.ToView() + pkt.DecRef() + + tun.incomingPacket <- view +} + +// Flush implements tun.Device +func (tun *netTun) Flush() error { + return nil +} + +// Close implements tun.Device +func (tun *netTun) Close() error { + tun.stack.RemoveNIC(1) + + if tun.events != nil { + close(tun.events) + } + + tun.ep.Close() + + if tun.incomingPacket != nil { + close(tun.incomingPacket) + } + + return nil +} + +// MTU implements tun.Device +func (tun *netTun) MTU() (int, error) { + return tun.mtu, nil +} + +func convertToFullAddr(endpoint netip.AddrPort) (tcpip.FullAddress, tcpip.NetworkProtocolNumber) { + var protoNumber tcpip.NetworkProtocolNumber + if endpoint.Addr().Is4() { + protoNumber = ipv4.ProtocolNumber + } else { + protoNumber = ipv6.ProtocolNumber + } + return tcpip.FullAddress{ + NIC: 1, + Addr: tcpip.AddrFromSlice(endpoint.Addr().AsSlice()), + Port: endpoint.Port(), + }, protoNumber +} + +func (net *Net) DialContextTCPAddrPort(ctx context.Context, addr netip.AddrPort) (*gonet.TCPConn, error) { + fa, pn := convertToFullAddr(addr) + return gonet.DialContextTCP(ctx, net.stack, fa, pn) +} + +func (net *Net) DialUDPAddrPort(laddr, raddr netip.AddrPort) (*gonet.UDPConn, error) { + var lfa, rfa *tcpip.FullAddress + var pn tcpip.NetworkProtocolNumber + if laddr.IsValid() || laddr.Port() > 0 { + var addr tcpip.FullAddress + addr, pn = convertToFullAddr(laddr) + lfa = &addr + } + if raddr.IsValid() || raddr.Port() > 0 { + var addr tcpip.FullAddress + addr, pn = convertToFullAddr(raddr) + rfa = &addr + } + return gonet.DialUDP(net.stack, lfa, rfa, pn) +} diff --git a/proxy/wireguard/server.go b/proxy/wireguard/server.go new file mode 100644 index 00000000..bdb4e801 --- /dev/null +++ b/proxy/wireguard/server.go @@ -0,0 +1,185 @@ +package wireguard + +import ( + "context" + "errors" + "io" + + "github.com/xtls/xray-core/common" + "github.com/xtls/xray-core/common/buf" + "github.com/xtls/xray-core/common/log" + "github.com/xtls/xray-core/common/net" + "github.com/xtls/xray-core/common/session" + "github.com/xtls/xray-core/common/signal" + "github.com/xtls/xray-core/common/task" + "github.com/xtls/xray-core/core" + "github.com/xtls/xray-core/features/dns" + "github.com/xtls/xray-core/features/policy" + "github.com/xtls/xray-core/features/routing" + "github.com/xtls/xray-core/transport/internet/stat" +) + +var nullDestination = net.TCPDestination(net.AnyIP, 0) + +type Server struct { + bindServer *netBindServer + + info routingInfo + policyManager policy.Manager +} + +type routingInfo struct { + ctx context.Context + dispatcher routing.Dispatcher + inboundTag *session.Inbound + outboundTag *session.Outbound + contentTag *session.Content +} + +func NewServer(ctx context.Context, conf *DeviceConfig) (*Server, error) { + v := core.MustFromContext(ctx) + + endpoints, hasIPv4, hasIPv6, err := parseEndpoints(conf) + if err != nil { + return nil, err + } + + server := &Server{ + bindServer: &netBindServer{ + netBind: netBind{ + dns: v.GetFeature(dns.ClientType()).(dns.Client), + dnsOption: dns.IPOption{ + IPv4Enable: hasIPv4, + IPv6Enable: hasIPv6, + }, + }, + }, + policyManager: v.GetFeature(policy.ManagerType()).(policy.Manager), + } + + tun, err := conf.createTun()(endpoints, int(conf.Mtu), server.forwardConnection) + if err != nil { + return nil, err + } + + if err = tun.BuildDevice(createIPCRequest(conf), server.bindServer); err != nil { + _ = tun.Close() + return nil, err + } + + return server, nil +} + +// Network implements proxy.Inbound. +func (*Server) Network() []net.Network { + return []net.Network{net.Network_UDP} +} + +// Process implements proxy.Inbound. +func (s *Server) Process(ctx context.Context, network net.Network, conn stat.Connection, dispatcher routing.Dispatcher) error { + inbound := session.InboundFromContext(ctx) + inbound.Name = "wireguard" + inbound.SetCanSpliceCopy(3) + + s.info = routingInfo{ + ctx: core.ToBackgroundDetachedContext(ctx), + dispatcher: dispatcher, + inboundTag: session.InboundFromContext(ctx), + outboundTag: session.OutboundFromContext(ctx), + contentTag: session.ContentFromContext(ctx), + } + + ep, err := s.bindServer.ParseEndpoint(conn.RemoteAddr().String()) + if err != nil { + return err + } + + nep := ep.(*netEndpoint) + nep.conn = conn + + reader := buf.NewPacketReader(conn) + for { + mpayload, err := reader.ReadMultiBuffer() + if err != nil { + return err + } + + for _, payload := range mpayload { + v, ok := <-s.bindServer.readQueue + if !ok { + return nil + } + i, err := payload.Read(v.buff) + + v.bytes = i + v.endpoint = nep + v.err = err + v.waiter.Done() + if err != nil && errors.Is(err, io.EOF) { + nep.conn = nil + return nil + } + } + } +} + +func (s *Server) forwardConnection(dest net.Destination, conn net.Conn) { + if s.info.dispatcher == nil { + newError("unexpected: dispatcher == nil").AtError().WriteToLog() + return + } + defer conn.Close() + + ctx, cancel := context.WithCancel(core.ToBackgroundDetachedContext(s.info.ctx)) + plcy := s.policyManager.ForLevel(0) + timer := signal.CancelAfterInactivity(ctx, cancel, plcy.Timeouts.ConnectionIdle) + + ctx = log.ContextWithAccessMessage(ctx, &log.AccessMessage{ + From: nullDestination, + To: dest, + Status: log.AccessAccepted, + Reason: "", + }) + + if s.info.inboundTag != nil { + ctx = session.ContextWithInbound(ctx, s.info.inboundTag) + } + if s.info.outboundTag != nil { + ctx = session.ContextWithOutbound(ctx, s.info.outboundTag) + } + if s.info.contentTag != nil { + ctx = session.ContextWithContent(ctx, s.info.contentTag) + } + + link, err := s.info.dispatcher.Dispatch(ctx, dest) + if err != nil { + newError("dispatch connection").Base(err).AtError().WriteToLog() + } + defer cancel() + + requestDone := func() error { + defer timer.SetTimeout(plcy.Timeouts.DownlinkOnly) + if err := buf.Copy(buf.NewReader(conn), link.Writer, buf.UpdateActivity(timer)); err != nil { + return newError("failed to transport all TCP request").Base(err) + } + + return nil + } + + responseDone := func() error { + defer timer.SetTimeout(plcy.Timeouts.UplinkOnly) + if err := buf.Copy(link.Reader, buf.NewWriter(conn), buf.UpdateActivity(timer)); err != nil { + return newError("failed to transport all TCP response").Base(err) + } + + return nil + } + + requestDonePost := task.OnSuccess(requestDone, task.Close(link.Writer)) + if err := task.Run(ctx, requestDonePost, responseDone); err != nil { + common.Interrupt(link.Reader) + common.Interrupt(link.Writer) + newError("connection ends").Base(err).AtDebug().WriteToLog() + return + } +} diff --git a/proxy/wireguard/tun.go b/proxy/wireguard/tun.go index ed6e434f..c2d30323 100644 --- a/proxy/wireguard/tun.go +++ b/proxy/wireguard/tun.go @@ -1,303 +1,205 @@ -/* SPDX-License-Identifier: MIT - * - * Copyright (C) 2017-2022 WireGuard LLC. All Rights Reserved. - */ - package wireguard import ( "context" + "errors" "fmt" "net" "net/netip" - "os" + "runtime" + "strconv" + "strings" + "sync" + "time" - "github.com/sagernet/wireguard-go/tun" - "github.com/xtls/xray-core/features/dns" - "gvisor.dev/gvisor/pkg/buffer" + "github.com/xtls/xray-core/common/log" + xnet "github.com/xtls/xray-core/common/net" + "github.com/xtls/xray-core/proxy/wireguard/gvisortun" "gvisor.dev/gvisor/pkg/tcpip" "gvisor.dev/gvisor/pkg/tcpip/adapters/gonet" - "gvisor.dev/gvisor/pkg/tcpip/header" - "gvisor.dev/gvisor/pkg/tcpip/link/channel" - "gvisor.dev/gvisor/pkg/tcpip/network/ipv4" - "gvisor.dev/gvisor/pkg/tcpip/network/ipv6" - "gvisor.dev/gvisor/pkg/tcpip/stack" "gvisor.dev/gvisor/pkg/tcpip/transport/tcp" "gvisor.dev/gvisor/pkg/tcpip/transport/udp" + "gvisor.dev/gvisor/pkg/waiter" + + "golang.zx2c4.com/wireguard/conn" + "golang.zx2c4.com/wireguard/device" + "golang.zx2c4.com/wireguard/tun" ) -type netTun struct { - ep *channel.Endpoint - stack *stack.Stack - events chan tun.Event - incomingPacket chan *buffer.View - mtu int - dnsClient dns.Client - hasV4, hasV6 bool +type tunCreator func(localAddresses []netip.Addr, mtu int, handler promiscuousModeHandler) (Tunnel, error) + +type promiscuousModeHandler func(dest xnet.Destination, conn net.Conn) + +type Tunnel interface { + BuildDevice(ipc string, bind conn.Bind) error + DialContextTCPAddrPort(ctx context.Context, addr netip.AddrPort) (net.Conn, error) + DialUDPAddrPort(laddr, raddr netip.AddrPort) (net.Conn, error) + Close() error } -type Net netTun - -func CreateNetTUN(localAddresses []netip.Addr, dnsClient dns.Client, mtu int) (tun.Device, *Net, error) { - opts := stack.Options{ - NetworkProtocols: []stack.NetworkProtocolFactory{ipv4.NewProtocol, ipv6.NewProtocol}, - TransportProtocols: []stack.TransportProtocolFactory{tcp.NewProtocol, udp.NewProtocol}, - HandleLocal: true, - } - dev := &netTun{ - ep: channel.New(1024, uint32(mtu), ""), - stack: stack.New(opts), - events: make(chan tun.Event, 10), - incomingPacket: make(chan *buffer.View), - dnsClient: dnsClient, - mtu: mtu, - } - dev.ep.AddNotify(dev) - tcpipErr := dev.stack.CreateNIC(1, dev.ep) - if tcpipErr != nil { - return nil, nil, fmt.Errorf("CreateNIC: %v", tcpipErr) - } - for _, ip := range localAddresses { - var protoNumber tcpip.NetworkProtocolNumber - if ip.Is4() { - protoNumber = ipv4.ProtocolNumber - } else if ip.Is6() { - protoNumber = ipv6.ProtocolNumber - } - protoAddr := tcpip.ProtocolAddress{ - Protocol: protoNumber, - AddressWithPrefix: tcpip.AddrFromSlice(ip.AsSlice()).WithPrefix(), - } - tcpipErr := dev.stack.AddProtocolAddress(1, protoAddr, stack.AddressProperties{}) - if tcpipErr != nil { - return nil, nil, fmt.Errorf("AddProtocolAddress(%v): %v", ip, tcpipErr) - } - if ip.Is4() { - dev.hasV4 = true - } else if ip.Is6() { - dev.hasV6 = true - } - } - if dev.hasV4 { - dev.stack.AddRoute(tcpip.Route{Destination: header.IPv4EmptySubnet, NIC: 1}) - } - if dev.hasV6 { - dev.stack.AddRoute(tcpip.Route{Destination: header.IPv6EmptySubnet, NIC: 1}) - } - - dev.events <- tun.EventUp - return dev, (*Net)(dev), nil +type tunnel struct { + tun tun.Device + device *device.Device + rw sync.Mutex } -func (tun *netTun) Name() (string, error) { - return "go", nil -} +func (t *tunnel) BuildDevice(ipc string, bind conn.Bind) (err error) { + t.rw.Lock() + defer t.rw.Unlock() -func (tun *netTun) File() *os.File { + if t.device != nil { + return errors.New("device is already initialized") + } + + logger := &device.Logger{ + Verbosef: func(format string, args ...any) { + log.Record(&log.GeneralMessage{ + Severity: log.Severity_Debug, + Content: fmt.Sprintf(format, args...), + }) + }, + Errorf: func(format string, args ...any) { + log.Record(&log.GeneralMessage{ + Severity: log.Severity_Error, + Content: fmt.Sprintf(format, args...), + }) + }, + } + + t.device = device.NewDevice(t.tun, bind, logger) + if err = t.device.IpcSet(ipc); err != nil { + return err + } + if err = t.device.Up(); err != nil { + return err + } return nil } -func (tun *netTun) Events() chan tun.Event { - return tun.events -} +func (t *tunnel) Close() (err error) { + t.rw.Lock() + defer t.rw.Unlock() -func (tun *netTun) Read(buf []byte, offset int) (int, error) { - view, ok := <-tun.incomingPacket - if !ok { - return 0, os.ErrClosed + if t.device == nil { + return nil } - return view.Read(buf[offset:]) + t.device.Close() + t.device = nil + err = t.tun.Close() + t.tun = nil + return nil } -func (tun *netTun) Write(buf []byte, offset int) (int, error) { - packet := buf[offset:] - if len(packet) == 0 { - return 0, nil +func CalculateInterfaceName(name string) (tunName string) { + if runtime.GOOS == "darwin" { + tunName = "utun" + } else if name != "" { + tunName = name + } else { + tunName = "tun" } - - pkb := stack.NewPacketBuffer(stack.PacketBufferOptions{Payload: buffer.MakeWithData(packet)}) - switch packet[0] >> 4 { - case 4: - tun.ep.InjectInbound(header.IPv4ProtocolNumber, pkb) - case 6: - tun.ep.InjectInbound(header.IPv6ProtocolNumber, pkb) - } - - return len(buf), nil -} - -func (tun *netTun) WriteNotify() { - pkt := tun.ep.Read() - if pkt == nil { + interfaces, err := net.Interfaces() + if err != nil { return } - - view := pkt.ToView() - pkt.DecRef() - - tun.incomingPacket <- view -} - -func (tun *netTun) Flush() error { - return nil -} - -func (tun *netTun) Close() error { - tun.stack.RemoveNIC(1) - - if tun.events != nil { - close(tun.events) - } - - tun.ep.Close() - - if tun.incomingPacket != nil { - close(tun.incomingPacket) - } - - return nil -} - -func (tun *netTun) MTU() (int, error) { - return tun.mtu, nil -} - -func convertToFullAddr(endpoint netip.AddrPort) (tcpip.FullAddress, tcpip.NetworkProtocolNumber) { - var protoNumber tcpip.NetworkProtocolNumber - if endpoint.Addr().Is4() { - protoNumber = ipv4.ProtocolNumber - } else { - protoNumber = ipv6.ProtocolNumber - } - return tcpip.FullAddress{ - NIC: 1, - Addr: tcpip.AddrFromSlice(endpoint.Addr().AsSlice()), - Port: endpoint.Port(), - }, protoNumber -} - -func (net *Net) DialContextTCPAddrPort(ctx context.Context, addr netip.AddrPort) (*gonet.TCPConn, error) { - fa, pn := convertToFullAddr(addr) - return gonet.DialContextTCP(ctx, net.stack, fa, pn) -} - -func (net *Net) DialContextTCP(ctx context.Context, addr *net.TCPAddr) (*gonet.TCPConn, error) { - if addr == nil { - return net.DialContextTCPAddrPort(ctx, netip.AddrPort{}) - } - ip, _ := netip.AddrFromSlice(addr.IP) - return net.DialContextTCPAddrPort(ctx, netip.AddrPortFrom(ip, uint16(addr.Port))) -} - -func (net *Net) DialTCPAddrPort(addr netip.AddrPort) (*gonet.TCPConn, error) { - fa, pn := convertToFullAddr(addr) - return gonet.DialTCP(net.stack, fa, pn) -} - -func (net *Net) DialTCP(addr *net.TCPAddr) (*gonet.TCPConn, error) { - if addr == nil { - return net.DialTCPAddrPort(netip.AddrPort{}) - } - ip, _ := netip.AddrFromSlice(addr.IP) - return net.DialTCPAddrPort(netip.AddrPortFrom(ip, uint16(addr.Port))) -} - -func (net *Net) ListenTCPAddrPort(addr netip.AddrPort) (*gonet.TCPListener, error) { - fa, pn := convertToFullAddr(addr) - return gonet.ListenTCP(net.stack, fa, pn) -} - -func (net *Net) ListenTCP(addr *net.TCPAddr) (*gonet.TCPListener, error) { - if addr == nil { - return net.ListenTCPAddrPort(netip.AddrPort{}) - } - ip, _ := netip.AddrFromSlice(addr.IP) - return net.ListenTCPAddrPort(netip.AddrPortFrom(ip, uint16(addr.Port))) -} - -func (net *Net) DialUDPAddrPort(laddr, raddr netip.AddrPort) (*gonet.UDPConn, error) { - var lfa, rfa *tcpip.FullAddress - var pn tcpip.NetworkProtocolNumber - if laddr.IsValid() || laddr.Port() > 0 { - var addr tcpip.FullAddress - addr, pn = convertToFullAddr(laddr) - lfa = &addr - } - if raddr.IsValid() || raddr.Port() > 0 { - var addr tcpip.FullAddress - addr, pn = convertToFullAddr(raddr) - rfa = &addr - } - return gonet.DialUDP(net.stack, lfa, rfa, pn) -} - -func (net *Net) ListenUDPAddrPort(laddr netip.AddrPort) (*gonet.UDPConn, error) { - return net.DialUDPAddrPort(laddr, netip.AddrPort{}) -} - -func (net *Net) DialUDP(laddr, raddr *net.UDPAddr) (*gonet.UDPConn, error) { - var la, ra netip.AddrPort - if laddr != nil { - ip, _ := netip.AddrFromSlice(laddr.IP) - la = netip.AddrPortFrom(ip, uint16(laddr.Port)) - } - if raddr != nil { - ip, _ := netip.AddrFromSlice(raddr.IP) - ra = netip.AddrPortFrom(ip, uint16(raddr.Port)) - } - return net.DialUDPAddrPort(la, ra) -} - -func (net *Net) ListenUDP(laddr *net.UDPAddr) (*gonet.UDPConn, error) { - return net.DialUDP(laddr, nil) -} - -func (n *Net) HasV4() bool { - return n.hasV4 -} - -func (n *Net) HasV6() bool { - return n.hasV6 -} - -func IsDomainName(s string) bool { - l := len(s) - if l == 0 || l > 254 || l == 254 && s[l-1] != '.' { - return false - } - last := byte('.') - nonNumeric := false - partlen := 0 - for i := 0; i < len(s); i++ { - c := s[i] - switch { - default: - return false - case 'a' <= c && c <= 'z' || 'A' <= c && c <= 'Z' || c == '_': - nonNumeric = true - partlen++ - case '0' <= c && c <= '9': - partlen++ - case c == '-': - if last == '.' { - return false + var tunIndex int + for _, netInterface := range interfaces { + if strings.HasPrefix(netInterface.Name, tunName) { + index, parseErr := strconv.ParseInt(netInterface.Name[len(tunName):], 10, 16) + if parseErr == nil { + tunIndex = int(index) + 1 } - partlen++ - nonNumeric = true - case c == '.': - if last == '.' || last == '-' { - return false - } - if partlen > 63 || partlen == 0 { - return false - } - partlen = 0 } - last = c } - if last == '-' || partlen > 63 { - return false - } - return nonNumeric + tunName = fmt.Sprintf("%s%d", tunName, tunIndex) + return +} + +var _ Tunnel = (*gvisorNet)(nil) + +type gvisorNet struct { + tunnel + net *gvisortun.Net +} + +func (g *gvisorNet) Close() error { + return g.tunnel.Close() +} + +func (g *gvisorNet) DialContextTCPAddrPort(ctx context.Context, addr netip.AddrPort) ( + net.Conn, error, +) { + return g.net.DialContextTCPAddrPort(ctx, addr) +} + +func (g *gvisorNet) DialUDPAddrPort(laddr, raddr netip.AddrPort) (net.Conn, error) { + return g.net.DialUDPAddrPort(laddr, raddr) +} + +func createGVisorTun(localAddresses []netip.Addr, mtu int, handler promiscuousModeHandler) (Tunnel, error) { + out := &gvisorNet{} + tun, n, stack, err := gvisortun.CreateNetTUN(localAddresses, mtu, handler != nil) + if err != nil { + return nil, err + } + + if handler != nil { + // handler is only used for promiscuous mode + // capture all packets and send to handler + + tcpForwarder := tcp.NewForwarder(stack, 0, 65535, func(r *tcp.ForwarderRequest) { + go func(r *tcp.ForwarderRequest) { + var ( + wq waiter.Queue + id = r.ID() + ) + + // Perform a TCP three-way handshake. + ep, err := r.CreateEndpoint(&wq) + if err != nil { + newError(err.String()).AtError().WriteToLog() + r.Complete(true) + return + } + r.Complete(false) + defer ep.Close() + + // enable tcp keep-alive to prevent hanging connections + ep.SocketOptions().SetKeepAlive(true) + + // local address is actually destination + handler(xnet.TCPDestination(xnet.IPAddress(id.LocalAddress.AsSlice()), xnet.Port(id.LocalPort)), gonet.NewTCPConn(&wq, ep)) + }(r) + }) + stack.SetTransportProtocolHandler(tcp.ProtocolNumber, tcpForwarder.HandlePacket) + + udpForwarder := udp.NewForwarder(stack, func(r *udp.ForwarderRequest) { + go func(r *udp.ForwarderRequest) { + var ( + wq waiter.Queue + id = r.ID() + ) + + ep, err := r.CreateEndpoint(&wq) + if err != nil { + newError(err.String()).AtError().WriteToLog() + return + } + defer ep.Close() + + // prevents hanging connections and ensure timely release + ep.SocketOptions().SetLinger(tcpip.LingerOption{ + Enabled: true, + Timeout: 15 * time.Second, + }) + + handler(xnet.UDPDestination(xnet.IPAddress(id.LocalAddress.AsSlice()), xnet.Port(id.LocalPort)), gonet.NewUDPConn(stack, &wq, ep)) + }(r) + }) + stack.SetTransportProtocolHandler(udp.ProtocolNumber, udpForwarder.HandlePacket) + } + + out.tun, out.net = tun, n + return out, nil } diff --git a/proxy/wireguard/tun_default.go b/proxy/wireguard/tun_default.go new file mode 100644 index 00000000..4d0567af --- /dev/null +++ b/proxy/wireguard/tun_default.go @@ -0,0 +1,16 @@ +//go:build !linux || android + +package wireguard + +import ( + "errors" + "net/netip" +) + +func createKernelTun(localAddresses []netip.Addr, mtu int, handler promiscuousModeHandler) (t Tunnel, err error) { + return nil, errors.New("not implemented") +} + +func KernelTunSupported() bool { + return false +} diff --git a/proxy/wireguard/tun_linux.go b/proxy/wireguard/tun_linux.go new file mode 100644 index 00000000..b85a9d09 --- /dev/null +++ b/proxy/wireguard/tun_linux.go @@ -0,0 +1,237 @@ +//go:build linux && !android + +package wireguard + +import ( + "context" + "errors" + "fmt" + "net" + "net/netip" + "os" + + "golang.org/x/sys/unix" + + "github.com/sagernet/sing/common/control" + "github.com/vishvananda/netlink" + wgtun "golang.zx2c4.com/wireguard/tun" +) + +type deviceNet struct { + tunnel + dialer net.Dialer + + handle *netlink.Handle + linkAddrs []netlink.Addr + routes []*netlink.Route + rules []*netlink.Rule +} + +func newDeviceNet(interfaceName string) *deviceNet { + var dialer net.Dialer + bindControl := control.BindToInterface(control.DefaultInterfaceFinder(), interfaceName, -1) + dialer.Control = control.Append(dialer.Control, bindControl) + return &deviceNet{dialer: dialer} +} + +func (d *deviceNet) DialContextTCPAddrPort(ctx context.Context, addr netip.AddrPort) ( + net.Conn, error, +) { + return d.dialer.DialContext(ctx, "tcp", addr.String()) +} + +func (d *deviceNet) DialUDPAddrPort(laddr, raddr netip.AddrPort) (net.Conn, error) { + dialer := d.dialer + dialer.LocalAddr = &net.UDPAddr{IP: laddr.Addr().AsSlice(), Port: int(laddr.Port())} + return dialer.DialContext(context.Background(), "udp", raddr.String()) +} + +func (d *deviceNet) Close() (err error) { + var errs []error + for _, rule := range d.rules { + if err = d.handle.RuleDel(rule); err != nil { + errs = append(errs, fmt.Errorf("failed to delete rule: %w", err)) + } + } + for _, route := range d.routes { + if err = d.handle.RouteDel(route); err != nil { + errs = append(errs, fmt.Errorf("failed to delete route: %w", err)) + } + } + if err = d.tunnel.Close(); err != nil { + errs = append(errs, fmt.Errorf("failed to close tunnel: %w", err)) + } + if d.handle != nil { + d.handle.Close() + d.handle = nil + } + if len(errs) == 0 { + return nil + } + return errors.Join(errs...) +} + +func createKernelTun(localAddresses []netip.Addr, mtu int, handler promiscuousModeHandler) (t Tunnel, err error) { + if handler != nil { + return nil, newError("TODO: support promiscuous mode") + } + + var v4, v6 *netip.Addr + for _, prefixes := range localAddresses { + if v4 == nil && prefixes.Is4() { + x := prefixes + v4 = &x + } + if v6 == nil && prefixes.Is6() { + x := prefixes + v6 = &x + } + } + + writeSysctlZero := func(path string) error { + _, err := os.Stat(path) + if os.IsNotExist(err) { + return nil + } + if err != nil { + return err + } + return os.WriteFile(path, []byte("0"), 0o644) + } + + // system configs. + if v4 != nil { + if err = writeSysctlZero("/proc/sys/net/ipv4/conf/all/rp_filter"); err != nil { + return nil, fmt.Errorf("failed to disable ipv4 rp_filter for all: %w", err) + } + } + if v6 != nil { + if err = writeSysctlZero("/proc/sys/net/ipv6/conf/all/disable_ipv6"); err != nil { + return nil, fmt.Errorf("failed to enable ipv6: %w", err) + } + if err = writeSysctlZero("/proc/sys/net/ipv6/conf/all/rp_filter"); err != nil { + return nil, fmt.Errorf("failed to disable ipv6 rp_filter for all: %w", err) + } + } + + n := CalculateInterfaceName("wg") + wgt, err := wgtun.CreateTUN(n, mtu) + if err != nil { + return nil, err + } + defer func() { + if err != nil { + _ = wgt.Close() + } + }() + + // disable linux rp_filter for tunnel device to avoid packet drop. + // the operation require root privilege on container require '--privileged' flag. + if v4 != nil { + if err = writeSysctlZero("/proc/sys/net/ipv4/conf/" + n + "/rp_filter"); err != nil { + return nil, fmt.Errorf("failed to disable ipv4 rp_filter for tunnel: %w", err) + } + } + if v6 != nil { + if err = writeSysctlZero("/proc/sys/net/ipv6/conf/" + n + "/rp_filter"); err != nil { + return nil, fmt.Errorf("failed to disable ipv6 rp_filter for tunnel: %w", err) + } + } + + ipv6TableIndex := 1023 + if v6 != nil { + r := &netlink.Route{Table: ipv6TableIndex} + for { + routeList, fErr := netlink.RouteListFiltered(netlink.FAMILY_V6, r, netlink.RT_FILTER_TABLE) + if len(routeList) == 0 || fErr != nil { + break + } + ipv6TableIndex-- + if ipv6TableIndex < 0 { + return nil, fmt.Errorf("failed to find available ipv6 table index") + } + } + } + + out := newDeviceNet(n) + out.handle, err = netlink.NewHandle() + if err != nil { + return nil, err + } + defer func() { + if err != nil { + _ = out.Close() + } + }() + + l, err := netlink.LinkByName(n) + if err != nil { + return nil, err + } + + if v4 != nil { + addr := netlink.Addr{ + IPNet: &net.IPNet{ + IP: v4.AsSlice(), + Mask: net.CIDRMask(v4.BitLen(), v4.BitLen()), + }, + } + out.linkAddrs = append(out.linkAddrs, addr) + } + if v6 != nil { + addr := netlink.Addr{ + IPNet: &net.IPNet{ + IP: v6.AsSlice(), + Mask: net.CIDRMask(v6.BitLen(), v6.BitLen()), + }, + } + out.linkAddrs = append(out.linkAddrs, addr) + + rt := &netlink.Route{ + LinkIndex: l.Attrs().Index, + Dst: &net.IPNet{ + IP: net.IPv6zero, + Mask: net.CIDRMask(0, 128), + }, + Table: ipv6TableIndex, + } + out.routes = append(out.routes, rt) + + r := netlink.NewRule() + r.Table, r.Family, r.Src = ipv6TableIndex, unix.AF_INET6, addr.IPNet + out.rules = append(out.rules, r) + } + + for _, addr := range out.linkAddrs { + if err = out.handle.AddrAdd(l, &addr); err != nil { + return nil, fmt.Errorf("failed to add address %s to %s: %w", addr, n, err) + } + } + if err = out.handle.LinkSetMTU(l, mtu); err != nil { + return nil, err + } + if err = out.handle.LinkSetUp(l); err != nil { + return nil, err + } + + for _, route := range out.routes { + if err = out.handle.RouteAdd(route); err != nil { + return nil, fmt.Errorf("failed to add route %s: %w", route, err) + } + } + for _, rule := range out.rules { + if err = out.handle.RuleAdd(rule); err != nil { + return nil, fmt.Errorf("failed to add rule %s: %w", rule, err) + } + } + out.tun = wgt + return out, nil +} + +func KernelTunSupported() bool { + // run a superuser permission check to check + // if the current user has the sufficient permission + // to create a tun device. + + return unix.Geteuid() == 0 // 0 means root +} diff --git a/proxy/wireguard/wireguard.go b/proxy/wireguard/wireguard.go index 899dcac5..2b3c3007 100644 --- a/proxy/wireguard/wireguard.go +++ b/proxy/wireguard/wireguard.go @@ -1,289 +1,111 @@ -/* - -Some of codes are copied from https://github.com/octeep/wireproxy, license below. - -Copyright (c) 2022 Wind T.F. Wong - -Permission to use, copy, modify, and distribute this software for any -purpose with or without fee is hereby granted, provided that the above -copyright notice and this permission notice appear in all copies. - -THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES -WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR -ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF -OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - -*/ - package wireguard import ( - "bytes" "context" "fmt" "net/netip" "strings" - "github.com/sagernet/wireguard-go/device" "github.com/xtls/xray-core/common" - "github.com/xtls/xray-core/common/buf" "github.com/xtls/xray-core/common/log" - "github.com/xtls/xray-core/common/net" - "github.com/xtls/xray-core/common/protocol" - "github.com/xtls/xray-core/common/session" - "github.com/xtls/xray-core/common/signal" - "github.com/xtls/xray-core/common/task" - "github.com/xtls/xray-core/core" - "github.com/xtls/xray-core/features/dns" - "github.com/xtls/xray-core/features/policy" - "github.com/xtls/xray-core/transport" - "github.com/xtls/xray-core/transport/internet" + "golang.zx2c4.com/wireguard/device" ) -// Handler is an outbound connection that silently swallow the entire payload. -type Handler struct { - conf *DeviceConfig - net *Net - bind *netBindClient - policyManager policy.Manager - dns dns.Client - // cached configuration - ipc string - endpoints []netip.Addr -} +//go:generate go run github.com/xtls/xray-core/common/errors/errorgen -// New creates a new wireguard handler. -func New(ctx context.Context, conf *DeviceConfig) (*Handler, error) { - v := core.MustFromContext(ctx) - - endpoints, err := parseEndpoints(conf) - if err != nil { - return nil, err - } - - return &Handler{ - conf: conf, - policyManager: v.GetFeature(policy.ManagerType()).(policy.Manager), - dns: v.GetFeature(dns.ClientType()).(dns.Client), - ipc: createIPCRequest(conf), - endpoints: endpoints, - }, nil -} - -// Process implements OutboundHandler.Dispatch(). -func (h *Handler) Process(ctx context.Context, link *transport.Link, dialer internet.Dialer) error { - outbound := session.OutboundFromContext(ctx) - if outbound == nil || !outbound.Target.IsValid() { - return newError("target not specified") - } - outbound.Name = "wireguard" - inbound := session.InboundFromContext(ctx) - if inbound != nil { - inbound.SetCanSpliceCopy(3) - } - - if h.bind == nil || h.bind.dialer != dialer || h.net == nil { +var wgLogger = &device.Logger{ + Verbosef: func(format string, args ...any) { log.Record(&log.GeneralMessage{ - Severity: log.Severity_Info, - Content: "switching dialer", + Severity: log.Severity_Debug, + Content: fmt.Sprintf(format, args...), }) - // bind := conn.NewStdNetBind() // TODO: conn.Bind wrapper for dialer - bind := &netBindClient{ - dialer: dialer, - workers: int(h.conf.NumWorkers), - dns: h.dns, - reserved: h.conf.Reserved, - } - - net, err := h.makeVirtualTun(bind) - if err != nil { - bind.Close() - return newError("failed to create virtual tun interface").Base(err) - } - - h.net = net - if h.bind != nil { - h.bind.Close() - } - h.bind = bind - } - - // Destination of the inner request. - destination := outbound.Target - command := protocol.RequestCommandTCP - if destination.Network == net.Network_UDP { - command = protocol.RequestCommandUDP - } - - // resolve dns - addr := destination.Address - if addr.Family().IsDomain() { - ips, err := h.dns.LookupIP(addr.Domain(), dns.IPOption{ - IPv4Enable: h.net.HasV4(), - IPv6Enable: h.net.HasV6(), + }, + Errorf: func(format string, args ...any) { + log.Record(&log.GeneralMessage{ + Severity: log.Severity_Error, + Content: fmt.Sprintf(format, args...), }) - if err != nil { - return newError("failed to lookup DNS").Base(err) - } else if len(ips) == 0 { - return dns.ErrEmptyResponse - } - addr = net.IPAddress(ips[0]) - } - - var newCtx context.Context - var newCancel context.CancelFunc - if session.TimeoutOnlyFromContext(ctx) { - newCtx, newCancel = context.WithCancel(context.Background()) - } - - p := h.policyManager.ForLevel(0) - - ctx, cancel := context.WithCancel(ctx) - timer := signal.CancelAfterInactivity(ctx, func() { - cancel() - if newCancel != nil { - newCancel() - } - }, p.Timeouts.ConnectionIdle) - addrPort := netip.AddrPortFrom(toNetIpAddr(addr), destination.Port.Value()) - - var requestFunc func() error - var responseFunc func() error - - if command == protocol.RequestCommandTCP { - conn, err := h.net.DialContextTCPAddrPort(ctx, addrPort) - if err != nil { - return newError("failed to create TCP connection").Base(err) - } - defer conn.Close() - - requestFunc = func() error { - defer timer.SetTimeout(p.Timeouts.DownlinkOnly) - return buf.Copy(link.Reader, buf.NewWriter(conn), buf.UpdateActivity(timer)) - } - responseFunc = func() error { - defer timer.SetTimeout(p.Timeouts.UplinkOnly) - return buf.Copy(buf.NewReader(conn), link.Writer, buf.UpdateActivity(timer)) - } - } else if command == protocol.RequestCommandUDP { - conn, err := h.net.DialUDPAddrPort(netip.AddrPort{}, addrPort) - if err != nil { - return newError("failed to create UDP connection").Base(err) - } - defer conn.Close() - - requestFunc = func() error { - defer timer.SetTimeout(p.Timeouts.DownlinkOnly) - return buf.Copy(link.Reader, buf.NewWriter(conn), buf.UpdateActivity(timer)) - } - responseFunc = func() error { - defer timer.SetTimeout(p.Timeouts.UplinkOnly) - return buf.Copy(buf.NewReader(conn), link.Writer, buf.UpdateActivity(timer)) - } - } - - if newCtx != nil { - ctx = newCtx - } - - responseDonePost := task.OnSuccess(responseFunc, task.Close(link.Writer)) - if err := task.Run(ctx, requestFunc, responseDonePost); err != nil { - common.Interrupt(link.Reader) - common.Interrupt(link.Writer) - return newError("connection ends").Base(err) - } - - return nil + }, } -// serialize the config into an IPC request -func createIPCRequest(conf *DeviceConfig) string { - var request bytes.Buffer - - request.WriteString(fmt.Sprintf("private_key=%s\n", conf.SecretKey)) - - for _, peer := range conf.Peers { - request.WriteString(fmt.Sprintf("public_key=%s\nendpoint=%s\npersistent_keepalive_interval=%d\npreshared_key=%s\n", - peer.PublicKey, peer.Endpoint, peer.KeepAlive, peer.PreSharedKey)) - - for _, ip := range peer.AllowedIps { - request.WriteString(fmt.Sprintf("allowed_ip=%s\n", ip)) +func init() { + common.Must(common.RegisterConfig((*DeviceConfig)(nil), func(ctx context.Context, config interface{}) (interface{}, error) { + deviceConfig := config.(*DeviceConfig) + if deviceConfig.IsClient { + return New(ctx, deviceConfig) + } else { + return NewServer(ctx, deviceConfig) } - } - - return request.String()[:request.Len()] + })) } // convert endpoint string to netip.Addr -func parseEndpoints(conf *DeviceConfig) ([]netip.Addr, error) { +func parseEndpoints(conf *DeviceConfig) ([]netip.Addr, bool, bool, error) { + var hasIPv4, hasIPv6 bool + endpoints := make([]netip.Addr, len(conf.Endpoint)) for i, str := range conf.Endpoint { var addr netip.Addr if strings.Contains(str, "/") { prefix, err := netip.ParsePrefix(str) if err != nil { - return nil, err + return nil, false, false, err } addr = prefix.Addr() if prefix.Bits() != addr.BitLen() { - return nil, newError("interface address subnet should be /32 for IPv4 and /128 for IPv6") + return nil, false, false, newError("interface address subnet should be /32 for IPv4 and /128 for IPv6") } } else { var err error addr, err = netip.ParseAddr(str) if err != nil { - return nil, err + return nil, false, false, err } } endpoints[i] = addr + + if addr.Is4() { + hasIPv4 = true + } else if addr.Is6() { + hasIPv6 = true + } } - return endpoints, nil + return endpoints, hasIPv4, hasIPv6, nil } -// creates a tun interface on netstack given a configuration -func (h *Handler) makeVirtualTun(bind *netBindClient) (*Net, error) { - tun, tnet, err := CreateNetTUN(h.endpoints, h.dns, int(h.conf.Mtu)) - if err != nil { - return nil, err +// serialize the config into an IPC request +func createIPCRequest(conf *DeviceConfig) string { + var request strings.Builder + + request.WriteString(fmt.Sprintf("private_key=%s\n", conf.SecretKey)) + + if !conf.IsClient { + // placeholder, we'll handle actual port listening on Xray + request.WriteString("listen_port=1337\n") } - bind.dnsOption.IPv4Enable = tnet.HasV4() - bind.dnsOption.IPv6Enable = tnet.HasV6() + for _, peer := range conf.Peers { + if peer.PublicKey != "" { + request.WriteString(fmt.Sprintf("public_key=%s\n", peer.PublicKey)) + } - // dev := device.NewDevice(tun, conn.NewDefaultBind(), nil /* device.NewLogger(device.LogLevelVerbose, "") */) - dev := device.NewDevice(tun, bind, &device.Logger{ - Verbosef: func(format string, args ...any) { - log.Record(&log.GeneralMessage{ - Severity: log.Severity_Debug, - Content: fmt.Sprintf(format, args...), - }) - }, - Errorf: func(format string, args ...any) { - log.Record(&log.GeneralMessage{ - Severity: log.Severity_Error, - Content: fmt.Sprintf(format, args...), - }) - }, - }, int(h.conf.NumWorkers)) - err = dev.IpcSet(h.ipc) - if err != nil { - return nil, err + if peer.PreSharedKey != "" { + request.WriteString(fmt.Sprintf("preshared_key=%s\n", peer.PreSharedKey)) + } + + if peer.Endpoint != "" { + request.WriteString(fmt.Sprintf("endpoint=%s\n", peer.Endpoint)) + } + + for _, ip := range peer.AllowedIps { + request.WriteString(fmt.Sprintf("allowed_ip=%s\n", ip)) + } + + if peer.KeepAlive != 0 { + request.WriteString(fmt.Sprintf("persistent_keepalive_interval=%d\n", peer.KeepAlive)) + } } - err = dev.Up() - if err != nil { - return nil, err - } - - return tnet, nil -} - -func init() { - common.Must(common.RegisterConfig((*DeviceConfig)(nil), func(ctx context.Context, config interface{}) (interface{}, error) { - return New(ctx, config.(*DeviceConfig)) - })) + return request.String()[:request.Len()] } diff --git a/transport/global/config.pb.go b/transport/global/config.pb.go index 1d763122..4bd8c84f 100644 --- a/transport/global/config.pb.go +++ b/transport/global/config.pb.go @@ -1,6 +1,6 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.30.0 +// protoc-gen-go v1.31.0 // protoc v4.23.1 // source: transport/global/config.proto diff --git a/transport/internet/config.go b/transport/internet/config.go index a7ff96c6..6725a995 100644 --- a/transport/internet/config.go +++ b/transport/internet/config.go @@ -12,6 +12,21 @@ var ( globalTransportSettings []*TransportConfig ) +var strategy = [][]byte{ + // name strategy, prefer, fallback + {0, 0, 0}, // AsIs none, /, / + {1, 0, 0}, // UseIP use, both, none + {1, 4, 0}, // UseIPv4 use, 4, none + {1, 6, 0}, // UseIPv6 use, 6, none + {1, 4, 6}, // UseIPv4v6 use, 4, 6 + {1, 6, 4}, // UseIPv6v4 use, 6, 4 + {2, 0, 0}, // ForceIP force, both, none + {2, 4, 0}, // ForceIPv4 force, 4, none + {2, 6, 0}, // ForceIPv6 force, 6, none + {2, 4, 6}, // ForceIPv4v6 force, 4, 6 + {2, 6, 4}, // ForceIPv6v4 force, 6, 4 +} + const unknownProtocol = "unknown" func transportProtocolToString(protocol TransportProtocol) string { @@ -122,3 +137,31 @@ func (c *ProxyConfig) HasTag() bool { func (m SocketConfig_TProxyMode) IsEnabled() bool { return m != SocketConfig_Off } + +func (s DomainStrategy) hasStrategy() bool { + return strategy[s][0] != 0 +} + +func (s DomainStrategy) forceIP() bool { + return strategy[s][0] == 2 +} + +func (s DomainStrategy) preferIP4() bool { + return strategy[s][1] == 4 || strategy[s][1] == 0 +} + +func (s DomainStrategy) preferIP6() bool { + return strategy[s][1] == 6 || strategy[s][1] == 0 +} + +func (s DomainStrategy) hasFallback() bool { + return strategy[s][2] != 0 +} + +func (s DomainStrategy) fallbackIP4() bool { + return strategy[s][2] == 4 +} + +func (s DomainStrategy) fallbackIP6() bool { + return strategy[s][2] == 6 +} diff --git a/transport/internet/config.pb.go b/transport/internet/config.pb.go index 33519b63..9636bfb4 100644 --- a/transport/internet/config.pb.go +++ b/transport/internet/config.pb.go @@ -1,7 +1,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: // protoc-gen-go v1.31.0 -// protoc v4.24.1 +// protoc v4.23.1 // source: transport/internet/config.proto package internet @@ -82,25 +82,46 @@ func (TransportProtocol) EnumDescriptor() ([]byte, []int) { type DomainStrategy int32 const ( - DomainStrategy_AS_IS DomainStrategy = 0 - DomainStrategy_USE_IP DomainStrategy = 1 - DomainStrategy_USE_IP4 DomainStrategy = 2 - DomainStrategy_USE_IP6 DomainStrategy = 3 + DomainStrategy_AS_IS DomainStrategy = 0 + DomainStrategy_USE_IP DomainStrategy = 1 + DomainStrategy_USE_IP4 DomainStrategy = 2 + DomainStrategy_USE_IP6 DomainStrategy = 3 + DomainStrategy_USE_IP46 DomainStrategy = 4 + DomainStrategy_USE_IP64 DomainStrategy = 5 + DomainStrategy_FORCE_IP DomainStrategy = 6 + DomainStrategy_FORCE_IP4 DomainStrategy = 7 + DomainStrategy_FORCE_IP6 DomainStrategy = 8 + DomainStrategy_FORCE_IP46 DomainStrategy = 9 + DomainStrategy_FORCE_IP64 DomainStrategy = 10 ) // Enum value maps for DomainStrategy. var ( DomainStrategy_name = map[int32]string{ - 0: "AS_IS", - 1: "USE_IP", - 2: "USE_IP4", - 3: "USE_IP6", + 0: "AS_IS", + 1: "USE_IP", + 2: "USE_IP4", + 3: "USE_IP6", + 4: "USE_IP46", + 5: "USE_IP64", + 6: "FORCE_IP", + 7: "FORCE_IP4", + 8: "FORCE_IP6", + 9: "FORCE_IP46", + 10: "FORCE_IP64", } DomainStrategy_value = map[string]int32{ - "AS_IS": 0, - "USE_IP": 1, - "USE_IP4": 2, - "USE_IP6": 3, + "AS_IS": 0, + "USE_IP": 1, + "USE_IP4": 2, + "USE_IP6": 3, + "USE_IP46": 4, + "USE_IP64": 5, + "FORCE_IP": 6, + "FORCE_IP4": 7, + "FORCE_IP6": 8, + "FORCE_IP46": 9, + "FORCE_IP64": 10, } ) @@ -710,18 +731,24 @@ var file_transport_internet_config_proto_rawDesc = []byte{ 0x04, 0x4d, 0x4b, 0x43, 0x50, 0x10, 0x02, 0x12, 0x0d, 0x0a, 0x09, 0x57, 0x65, 0x62, 0x53, 0x6f, 0x63, 0x6b, 0x65, 0x74, 0x10, 0x03, 0x12, 0x08, 0x0a, 0x04, 0x48, 0x54, 0x54, 0x50, 0x10, 0x04, 0x12, 0x10, 0x0a, 0x0c, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x53, 0x6f, 0x63, 0x6b, 0x65, 0x74, - 0x10, 0x05, 0x2a, 0x41, 0x0a, 0x0e, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x53, 0x74, 0x72, 0x61, - 0x74, 0x65, 0x67, 0x79, 0x12, 0x09, 0x0a, 0x05, 0x41, 0x53, 0x5f, 0x49, 0x53, 0x10, 0x00, 0x12, - 0x0a, 0x0a, 0x06, 0x55, 0x53, 0x45, 0x5f, 0x49, 0x50, 0x10, 0x01, 0x12, 0x0b, 0x0a, 0x07, 0x55, - 0x53, 0x45, 0x5f, 0x49, 0x50, 0x34, 0x10, 0x02, 0x12, 0x0b, 0x0a, 0x07, 0x55, 0x53, 0x45, 0x5f, - 0x49, 0x50, 0x36, 0x10, 0x03, 0x42, 0x67, 0x0a, 0x1b, 0x63, 0x6f, 0x6d, 0x2e, 0x78, 0x72, 0x61, - 0x79, 0x2e, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x69, 0x6e, 0x74, 0x65, - 0x72, 0x6e, 0x65, 0x74, 0x50, 0x01, 0x5a, 0x2c, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, - 0x6f, 0x6d, 0x2f, 0x78, 0x74, 0x6c, 0x73, 0x2f, 0x78, 0x72, 0x61, 0x79, 0x2d, 0x63, 0x6f, 0x72, - 0x65, 0x2f, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x2f, 0x69, 0x6e, 0x74, 0x65, - 0x72, 0x6e, 0x65, 0x74, 0xaa, 0x02, 0x17, 0x58, 0x72, 0x61, 0x79, 0x2e, 0x54, 0x72, 0x61, 0x6e, - 0x73, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x62, 0x06, - 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x10, 0x05, 0x2a, 0xa9, 0x01, 0x0a, 0x0e, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x53, 0x74, 0x72, + 0x61, 0x74, 0x65, 0x67, 0x79, 0x12, 0x09, 0x0a, 0x05, 0x41, 0x53, 0x5f, 0x49, 0x53, 0x10, 0x00, + 0x12, 0x0a, 0x0a, 0x06, 0x55, 0x53, 0x45, 0x5f, 0x49, 0x50, 0x10, 0x01, 0x12, 0x0b, 0x0a, 0x07, + 0x55, 0x53, 0x45, 0x5f, 0x49, 0x50, 0x34, 0x10, 0x02, 0x12, 0x0b, 0x0a, 0x07, 0x55, 0x53, 0x45, + 0x5f, 0x49, 0x50, 0x36, 0x10, 0x03, 0x12, 0x0c, 0x0a, 0x08, 0x55, 0x53, 0x45, 0x5f, 0x49, 0x50, + 0x34, 0x36, 0x10, 0x04, 0x12, 0x0c, 0x0a, 0x08, 0x55, 0x53, 0x45, 0x5f, 0x49, 0x50, 0x36, 0x34, + 0x10, 0x05, 0x12, 0x0c, 0x0a, 0x08, 0x46, 0x4f, 0x52, 0x43, 0x45, 0x5f, 0x49, 0x50, 0x10, 0x06, + 0x12, 0x0d, 0x0a, 0x09, 0x46, 0x4f, 0x52, 0x43, 0x45, 0x5f, 0x49, 0x50, 0x34, 0x10, 0x07, 0x12, + 0x0d, 0x0a, 0x09, 0x46, 0x4f, 0x52, 0x43, 0x45, 0x5f, 0x49, 0x50, 0x36, 0x10, 0x08, 0x12, 0x0e, + 0x0a, 0x0a, 0x46, 0x4f, 0x52, 0x43, 0x45, 0x5f, 0x49, 0x50, 0x34, 0x36, 0x10, 0x09, 0x12, 0x0e, + 0x0a, 0x0a, 0x46, 0x4f, 0x52, 0x43, 0x45, 0x5f, 0x49, 0x50, 0x36, 0x34, 0x10, 0x0a, 0x42, 0x67, + 0x0a, 0x1b, 0x63, 0x6f, 0x6d, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x74, 0x72, 0x61, 0x6e, 0x73, + 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x50, 0x01, 0x5a, + 0x2c, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x78, 0x74, 0x6c, 0x73, + 0x2f, 0x78, 0x72, 0x61, 0x79, 0x2d, 0x63, 0x6f, 0x72, 0x65, 0x2f, 0x74, 0x72, 0x61, 0x6e, 0x73, + 0x70, 0x6f, 0x72, 0x74, 0x2f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x65, 0x74, 0xaa, 0x02, 0x17, + 0x58, 0x72, 0x61, 0x79, 0x2e, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x49, + 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( diff --git a/transport/internet/config.proto b/transport/internet/config.proto index 550d00ec..f596d19f 100644 --- a/transport/internet/config.proto +++ b/transport/internet/config.proto @@ -22,6 +22,13 @@ enum DomainStrategy { USE_IP = 1; USE_IP4 = 2; USE_IP6 = 3; + USE_IP46 = 4; + USE_IP64 = 5; + FORCE_IP = 6; + FORCE_IP4 = 7; + FORCE_IP6 = 8; + FORCE_IP46 = 9; + FORCE_IP64 = 10; } message TransportConfig { diff --git a/transport/internet/dialer.go b/transport/internet/dialer.go index d178bdcd..deae4df0 100644 --- a/transport/internet/dialer.go +++ b/transport/internet/dialer.go @@ -78,37 +78,27 @@ func lookupIP(domain string, strategy DomainStrategy, localAddr net.Address) ([] return nil, nil } - option := dns.IPOption{ - IPv4Enable: true, - IPv6Enable: true, - FakeEnable: false, + ips, err := dnsClient.LookupIP(domain, dns.IPOption{ + IPv4Enable: (localAddr == nil || localAddr.Family().IsIPv4()) && strategy.preferIP4(), + IPv6Enable: (localAddr == nil || localAddr.Family().IsIPv6()) && strategy.preferIP6(), + }) + { // Resolve fallback + if (len(ips) == 0 || err != nil) && strategy.hasFallback() && localAddr == nil { + ips, err = dnsClient.LookupIP(domain, dns.IPOption{ + IPv4Enable: strategy.fallbackIP4(), + IPv6Enable: strategy.fallbackIP6(), + }) + } } - switch { - case strategy == DomainStrategy_USE_IP4 || (localAddr != nil && localAddr.Family().IsIPv4()): - option = dns.IPOption{ - IPv4Enable: true, - IPv6Enable: false, - FakeEnable: false, - } - case strategy == DomainStrategy_USE_IP6 || (localAddr != nil && localAddr.Family().IsIPv6()): - option = dns.IPOption{ - IPv4Enable: false, - IPv6Enable: true, - FakeEnable: false, - } - case strategy == DomainStrategy_AS_IS: - return nil, nil - } - - return dnsClient.LookupIP(domain, option) + return ips, err } func canLookupIP(ctx context.Context, dst net.Destination, sockopt *SocketConfig) bool { if dst.Address.Family().IsIP() || dnsClient == nil { return false } - return sockopt.DomainStrategy != DomainStrategy_AS_IS + return sockopt.DomainStrategy.hasStrategy() } func redirect(ctx context.Context, dst net.Destination, obt string) net.Conn { diff --git a/transport/internet/domainsocket/config.pb.go b/transport/internet/domainsocket/config.pb.go index 6089cf34..9d3eb25c 100644 --- a/transport/internet/domainsocket/config.pb.go +++ b/transport/internet/domainsocket/config.pb.go @@ -1,6 +1,6 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.30.0 +// protoc-gen-go v1.31.0 // protoc v4.23.1 // source: transport/internet/domainsocket/config.proto diff --git a/transport/internet/grpc/config.pb.go b/transport/internet/grpc/config.pb.go index 91289606..4a794bb1 100644 --- a/transport/internet/grpc/config.pb.go +++ b/transport/internet/grpc/config.pb.go @@ -1,6 +1,6 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.30.0 +// protoc-gen-go v1.31.0 // protoc v4.23.1 // source: transport/internet/grpc/config.proto diff --git a/transport/internet/grpc/encoding/stream.pb.go b/transport/internet/grpc/encoding/stream.pb.go index 96cf41bd..f8e7c7fb 100644 --- a/transport/internet/grpc/encoding/stream.pb.go +++ b/transport/internet/grpc/encoding/stream.pb.go @@ -1,6 +1,6 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.30.0 +// protoc-gen-go v1.31.0 // protoc v4.23.1 // source: transport/internet/grpc/encoding/stream.proto diff --git a/transport/internet/headers/dns/config.pb.go b/transport/internet/headers/dns/config.pb.go index aeadae6a..34d37c33 100644 --- a/transport/internet/headers/dns/config.pb.go +++ b/transport/internet/headers/dns/config.pb.go @@ -1,6 +1,6 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.30.0 +// protoc-gen-go v1.31.0 // protoc v4.23.1 // source: transport/internet/headers/dns/config.proto diff --git a/transport/internet/headers/http/config.pb.go b/transport/internet/headers/http/config.pb.go index 786bd928..69fefd8c 100644 --- a/transport/internet/headers/http/config.pb.go +++ b/transport/internet/headers/http/config.pb.go @@ -1,6 +1,6 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.30.0 +// protoc-gen-go v1.31.0 // protoc v4.23.1 // source: transport/internet/headers/http/config.proto diff --git a/transport/internet/headers/noop/config.pb.go b/transport/internet/headers/noop/config.pb.go index cd8880a8..9777e46f 100644 --- a/transport/internet/headers/noop/config.pb.go +++ b/transport/internet/headers/noop/config.pb.go @@ -1,6 +1,6 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.30.0 +// protoc-gen-go v1.31.0 // protoc v4.23.1 // source: transport/internet/headers/noop/config.proto diff --git a/transport/internet/headers/srtp/config.pb.go b/transport/internet/headers/srtp/config.pb.go index 553349e8..2d9c5354 100644 --- a/transport/internet/headers/srtp/config.pb.go +++ b/transport/internet/headers/srtp/config.pb.go @@ -1,6 +1,6 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.30.0 +// protoc-gen-go v1.31.0 // protoc v4.23.1 // source: transport/internet/headers/srtp/config.proto diff --git a/transport/internet/headers/tls/config.pb.go b/transport/internet/headers/tls/config.pb.go index 8d940553..c0b77de4 100644 --- a/transport/internet/headers/tls/config.pb.go +++ b/transport/internet/headers/tls/config.pb.go @@ -1,6 +1,6 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.30.0 +// protoc-gen-go v1.31.0 // protoc v4.23.1 // source: transport/internet/headers/tls/config.proto diff --git a/transport/internet/headers/utp/config.pb.go b/transport/internet/headers/utp/config.pb.go index 9dff1aed..b3bd9749 100644 --- a/transport/internet/headers/utp/config.pb.go +++ b/transport/internet/headers/utp/config.pb.go @@ -1,6 +1,6 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.30.0 +// protoc-gen-go v1.31.0 // protoc v4.23.1 // source: transport/internet/headers/utp/config.proto diff --git a/transport/internet/headers/wechat/config.pb.go b/transport/internet/headers/wechat/config.pb.go index 02c4f50d..8bec6fe4 100644 --- a/transport/internet/headers/wechat/config.pb.go +++ b/transport/internet/headers/wechat/config.pb.go @@ -1,6 +1,6 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.30.0 +// protoc-gen-go v1.31.0 // protoc v4.23.1 // source: transport/internet/headers/wechat/config.proto diff --git a/transport/internet/headers/wireguard/config.pb.go b/transport/internet/headers/wireguard/config.pb.go index 68c72d96..4274198c 100644 --- a/transport/internet/headers/wireguard/config.pb.go +++ b/transport/internet/headers/wireguard/config.pb.go @@ -1,6 +1,6 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.30.0 +// protoc-gen-go v1.31.0 // protoc v4.23.1 // source: transport/internet/headers/wireguard/config.proto diff --git a/transport/internet/http/config.pb.go b/transport/internet/http/config.pb.go index 6abb97dd..baaa5630 100644 --- a/transport/internet/http/config.pb.go +++ b/transport/internet/http/config.pb.go @@ -1,6 +1,6 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.30.0 +// protoc-gen-go v1.31.0 // protoc v4.23.1 // source: transport/internet/http/config.proto diff --git a/transport/internet/kcp/config.pb.go b/transport/internet/kcp/config.pb.go index 8b6dbd40..74537db7 100644 --- a/transport/internet/kcp/config.pb.go +++ b/transport/internet/kcp/config.pb.go @@ -1,6 +1,6 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.30.0 +// protoc-gen-go v1.31.0 // protoc v4.23.1 // source: transport/internet/kcp/config.proto diff --git a/transport/internet/quic/config.pb.go b/transport/internet/quic/config.pb.go index 0b850900..f10998eb 100644 --- a/transport/internet/quic/config.pb.go +++ b/transport/internet/quic/config.pb.go @@ -1,6 +1,6 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.30.0 +// protoc-gen-go v1.31.0 // protoc v4.23.1 // source: transport/internet/quic/config.proto diff --git a/transport/internet/quic/dialer.go b/transport/internet/quic/dialer.go index 1358dac7..c6bc08aa 100644 --- a/transport/internet/quic/dialer.go +++ b/transport/internet/quic/dialer.go @@ -146,10 +146,19 @@ func (s *clientConnections) openConnection(ctx context.Context, destAddr net.Add return qlog.NewConnectionTracer(&QlogWriter{connID: ci}, p, ci) }, } - udpConn, _ := rawConn.(*net.UDPConn) - if udpConn == nil { - udpConn = rawConn.(*internet.PacketConnWrapper).Conn.(*net.UDPConn) + + var udpConn *net.UDPConn + switch conn := rawConn.(type) { + case *net.UDPConn: + udpConn = conn + case *internet.PacketConnWrapper: + udpConn = conn.Conn.(*net.UDPConn) + default: + // TODO: Support sockopt for QUIC + rawConn.Close() + return nil, newError("QUIC with sockopt is unsupported").AtWarning() } + sysConn, err := wrapSysConn(udpConn, config) if err != nil { rawConn.Close() diff --git a/transport/internet/reality/config.pb.go b/transport/internet/reality/config.pb.go index 799e30d4..2b44d9b7 100644 --- a/transport/internet/reality/config.pb.go +++ b/transport/internet/reality/config.pb.go @@ -1,6 +1,6 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.30.0 +// protoc-gen-go v1.31.0 // protoc v4.23.1 // source: transport/internet/reality/config.proto diff --git a/transport/internet/reality/reality.go b/transport/internet/reality/reality.go index 30d4e2ae..de8a6ac6 100644 --- a/transport/internet/reality/reality.go +++ b/transport/internet/reality/reality.go @@ -29,6 +29,7 @@ import ( "github.com/xtls/reality" "github.com/xtls/xray-core/common/errors" "github.com/xtls/xray-core/common/net" + "github.com/xtls/xray-core/common/session" "github.com/xtls/xray-core/core" "github.com/xtls/xray-core/transport/internet/tls" "golang.org/x/crypto/chacha20poly1305" @@ -133,7 +134,7 @@ func UClient(c net.Conn, config *Config, ctx context.Context, dest net.Destinati binary.BigEndian.PutUint32(hello.SessionId[4:], uint32(time.Now().Unix())) copy(hello.SessionId[8:], config.ShortId) if config.Show { - fmt.Printf("REALITY localAddr: %v\thello.SessionId[:16]: %v\n", localAddr, hello.SessionId[:16]) + newError(fmt.Sprintf("REALITY localAddr: %v\thello.SessionId[:16]: %v\n", localAddr, hello.SessionId[:16])).WriteToLog(session.ExportIDToError(ctx)) } publicKey, _ := ecdh.X25519().NewPublicKey(config.PublicKey) uConn.AuthKey, _ = uConn.HandshakeState.State13.EcdheKey.ECDH(publicKey) @@ -151,7 +152,7 @@ func UClient(c net.Conn, config *Config, ctx context.Context, dest net.Destinati aead, _ = chacha20poly1305.New(uConn.AuthKey) } if config.Show { - fmt.Printf("REALITY localAddr: %v\tuConn.AuthKey[:16]: %v\tAEAD: %T\n", localAddr, uConn.AuthKey[:16], aead) + newError(fmt.Sprintf("REALITY localAddr: %v\tuConn.AuthKey[:16]: %v\tAEAD: %T\n", localAddr, uConn.AuthKey[:16], aead)).WriteToLog(session.ExportIDToError(ctx)) } aead.Seal(hello.SessionId[:0], hello.Random[20:], hello.SessionId[:16], hello.Raw) copy(hello.Raw[39:], hello.SessionId) @@ -160,14 +161,14 @@ func UClient(c net.Conn, config *Config, ctx context.Context, dest net.Destinati return nil, err } if config.Show { - fmt.Printf("REALITY localAddr: %v\tuConn.Verified: %v\n", localAddr, uConn.Verified) + newError(fmt.Sprintf("REALITY localAddr: %v\tuConn.Verified: %v\n", localAddr, uConn.Verified)).WriteToLog(session.ExportIDToError(ctx)) } if !uConn.Verified { go func() { client := &http.Client{ Transport: &http2.Transport{ DialTLSContext: func(ctx context.Context, network, addr string, cfg *gotls.Config) (net.Conn, error) { - fmt.Printf("REALITY localAddr: %v\tDialTLSContext\n", localAddr) + newError(fmt.Sprintf("REALITY localAddr: %v\tDialTLSContext\n", localAddr)).WriteToLog(session.ExportIDToError(ctx)) return uConn, nil }, }, @@ -201,7 +202,7 @@ func UClient(c net.Conn, config *Config, ctx context.Context, dest net.Destinati } req.Header.Set("User-Agent", fingerprint.Client) // TODO: User-Agent map if first && config.Show { - fmt.Printf("REALITY localAddr: %v\treq.UserAgent(): %v\n", localAddr, req.UserAgent()) + newError(fmt.Sprintf("REALITY localAddr: %v\treq.UserAgent(): %v\n", localAddr, req.UserAgent())).WriteToLog(session.ExportIDToError(ctx)) } times := 1 if !first { @@ -228,9 +229,9 @@ func UClient(c net.Conn, config *Config, ctx context.Context, dest net.Destinati } req.URL.Path = getPathLocked(paths) if config.Show { - fmt.Printf("REALITY localAddr: %v\treq.Referer(): %v\n", localAddr, req.Referer()) - fmt.Printf("REALITY localAddr: %v\tlen(body): %v\n", localAddr, len(body)) - fmt.Printf("REALITY localAddr: %v\tlen(paths): %v\n", localAddr, len(paths)) + newError(fmt.Sprintf("REALITY localAddr: %v\treq.Referer(): %v\n", localAddr, req.Referer())).WriteToLog(session.ExportIDToError(ctx)) + newError(fmt.Sprintf("REALITY localAddr: %v\tlen(body): %v\n", localAddr, len(body))).WriteToLog(session.ExportIDToError(ctx)) + newError(fmt.Sprintf("REALITY localAddr: %v\tlen(paths): %v\n", localAddr, len(paths))).WriteToLog(session.ExportIDToError(ctx)) } maps.Unlock() if !first { diff --git a/transport/internet/sockopt_darwin.go b/transport/internet/sockopt_darwin.go index d8724c77..de405538 100644 --- a/transport/internet/sockopt_darwin.go +++ b/transport/internet/sockopt_darwin.go @@ -165,7 +165,7 @@ func applyInboundSocketOptions(network string, fd uintptr, config *SocketConfig) } } } - + if config.TcpKeepAliveIdle > 0 || config.TcpKeepAliveInterval > 0 { if config.TcpKeepAliveIdle > 0 { if err := unix.SetsockoptInt(int(fd), unix.IPPROTO_TCP, unix.TCP_KEEPALIVE, int(config.TcpKeepAliveInterval)); err != nil { @@ -191,14 +191,42 @@ func applyInboundSocketOptions(network string, fd uintptr, config *SocketConfig) } func bindAddr(fd uintptr, address []byte, port uint32) error { - return nil + setReuseAddr(fd) + setReusePort(fd) + + var sockaddr unix.Sockaddr + + switch len(address) { + case net.IPv4len: + a4 := &unix.SockaddrInet4{ + Port: int(port), + } + copy(a4.Addr[:], address) + sockaddr = a4 + case net.IPv6len: + a6 := &unix.SockaddrInet6{ + Port: int(port), + } + copy(a6.Addr[:], address) + sockaddr = a6 + default: + return newError("unexpected length of ip") + } + + return unix.Bind(int(fd), sockaddr) } func setReuseAddr(fd uintptr) error { + if err := unix.SetsockoptInt(int(fd), unix.SOL_SOCKET, unix.SO_REUSEADDR, 1); err != nil { + return newError("failed to set SO_REUSEADDR").Base(err).AtWarning() + } return nil } func setReusePort(fd uintptr) error { + if err := unix.SetsockoptInt(int(fd), unix.SOL_SOCKET, unix.SO_REUSEPORT, 1); err != nil { + return newError("failed to set SO_REUSEPORT").Base(err).AtWarning() + } return nil } func getInterfaceIndexByName(name string) int { diff --git a/transport/internet/sockopt_other.go b/transport/internet/sockopt_other.go index ebcf4e26..7e91110e 100644 --- a/transport/internet/sockopt_other.go +++ b/transport/internet/sockopt_other.go @@ -1,5 +1,5 @@ -//go:build js || dragonfly || netbsd || openbsd || solaris -// +build js dragonfly netbsd openbsd solaris +//go:build js || netbsd || openbsd || solaris +// +build js netbsd openbsd solaris package internet diff --git a/transport/internet/tcp/config.pb.go b/transport/internet/tcp/config.pb.go index 1ff79f64..e7ecd49b 100644 --- a/transport/internet/tcp/config.pb.go +++ b/transport/internet/tcp/config.pb.go @@ -1,6 +1,6 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.30.0 +// protoc-gen-go v1.31.0 // protoc v4.23.1 // source: transport/internet/tcp/config.proto diff --git a/transport/internet/tls/config.pb.go b/transport/internet/tls/config.pb.go index 65c18e6b..9bd5a84d 100644 --- a/transport/internet/tls/config.pb.go +++ b/transport/internet/tls/config.pb.go @@ -1,6 +1,6 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.30.0 +// protoc-gen-go v1.31.0 // protoc v4.23.1 // source: transport/internet/tls/config.proto diff --git a/transport/internet/udp/config.pb.go b/transport/internet/udp/config.pb.go index b3921e46..b56c5fa4 100644 --- a/transport/internet/udp/config.pb.go +++ b/transport/internet/udp/config.pb.go @@ -1,6 +1,6 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.30.0 +// protoc-gen-go v1.31.0 // protoc v4.23.1 // source: transport/internet/udp/config.proto diff --git a/transport/internet/websocket/config.pb.go b/transport/internet/websocket/config.pb.go index 7880a1a5..ab0aa373 100644 --- a/transport/internet/websocket/config.pb.go +++ b/transport/internet/websocket/config.pb.go @@ -1,6 +1,6 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.30.0 +// protoc-gen-go v1.31.0 // protoc v4.23.1 // source: transport/internet/websocket/config.proto diff --git a/transport/internet/websocket/dialer.go b/transport/internet/websocket/dialer.go index 5017cb50..02b73a66 100644 --- a/transport/internet/websocket/dialer.go +++ b/transport/internet/websocket/dialer.go @@ -4,16 +4,15 @@ import ( "context" _ "embed" "encoding/base64" - "fmt" "io" gonet "net" "net/http" - "os" "time" "github.com/gorilla/websocket" "github.com/xtls/xray-core/common" "github.com/xtls/xray-core/common/net" + "github.com/xtls/xray-core/common/platform" "github.com/xtls/xray-core/common/session" "github.com/xtls/xray-core/transport/internet" "github.com/xtls/xray-core/transport/internet/stat" @@ -26,14 +25,15 @@ var webpage []byte var conns chan *websocket.Conn func init() { - if addr := os.Getenv("XRAY_BROWSER_DIALER"); addr != "" { + addr := platform.NewEnvFlag(platform.BrowserDialerAddress).GetValue(func() string { return "" }) + if addr != "" { conns = make(chan *websocket.Conn, 256) go http.ListenAndServe(addr, http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { if r.URL.Path == "/websocket" { if conn, err := upgrader.Upgrade(w, r, nil); err == nil { conns <- conn } else { - fmt.Println("unexpected error") + newError("Browser dialer http upgrade unexpected error").AtError().WriteToLog() } } else { w.Write(webpage) From 973f3da54fe3582a09afc4849b9a50bfb769519f Mon Sep 17 00:00:00 2001 From: amir-devman Date: Mon, 5 Feb 2024 20:05:20 +0000 Subject: [PATCH 7/8] chore(restriction): move config to policies Fixed a panic on worker.go --- app/policy/config.go | 11 ++ app/policy/config.pb.go | 238 +++++++++++++++++--------- app/policy/config.proto | 5 + app/proxyman/inbound/worker.go | 27 ++- common/protocol/user.go | 2 - common/protocol/user.proto | 3 - features/policy/policy.go | 22 ++- infra/conf/common.go | 2 - infra/conf/policy.go | 7 + proxy/dokodemo/dokodemo.go | 3 +- proxy/proxy.go | 3 +- proxy/trojan/server.go | 7 +- proxy/vless/inbound/inbound.go | 31 +--- proxy/vmess/inbound/inbound.go | 7 +- transport/internet/restriction/ip.go | 11 ++ transport/internet/stat/connection.go | 6 - 16 files changed, 245 insertions(+), 140 deletions(-) create mode 100644 transport/internet/restriction/ip.go diff --git a/app/policy/config.go b/app/policy/config.go index 267307b7..836b2324 100644 --- a/app/policy/config.go +++ b/app/policy/config.go @@ -27,6 +27,9 @@ func defaultPolicy() *Policy { Buffer: &Policy_Buffer{ Connection: p.Buffer.PerConnection, }, + Restriction: &Policy_Restriction{ + MaxIPs: p.Restriction.MaxIPs, + }, } } @@ -58,6 +61,11 @@ func (p *Policy) overrideWith(another *Policy) { Connection: another.Buffer.Connection, } } + if another.Restriction != nil { + p.Restriction = &Policy_Restriction{ + MaxIPs: another.Restriction.MaxIPs, + } + } } // ToCorePolicy converts this Policy to policy.Session. @@ -77,6 +85,9 @@ func (p *Policy) ToCorePolicy() policy.Session { if p.Buffer != nil { cp.Buffer.PerConnection = p.Buffer.Connection } + if p.Restriction != nil { + cp.Restriction.MaxIPs = p.Restriction.MaxIPs + } return cp } diff --git a/app/policy/config.pb.go b/app/policy/config.pb.go index c8b4311c..96c3fa0c 100644 --- a/app/policy/config.pb.go +++ b/app/policy/config.pb.go @@ -72,9 +72,10 @@ type Policy struct { sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - Timeout *Policy_Timeout `protobuf:"bytes,1,opt,name=timeout,proto3" json:"timeout,omitempty"` - Stats *Policy_Stats `protobuf:"bytes,2,opt,name=stats,proto3" json:"stats,omitempty"` - Buffer *Policy_Buffer `protobuf:"bytes,3,opt,name=buffer,proto3" json:"buffer,omitempty"` + Timeout *Policy_Timeout `protobuf:"bytes,1,opt,name=timeout,proto3" json:"timeout,omitempty"` + Stats *Policy_Stats `protobuf:"bytes,2,opt,name=stats,proto3" json:"stats,omitempty"` + Buffer *Policy_Buffer `protobuf:"bytes,3,opt,name=buffer,proto3" json:"buffer,omitempty"` + Restriction *Policy_Restriction `protobuf:"bytes,4,opt,name=restriction,proto3" json:"restriction,omitempty"` } func (x *Policy) Reset() { @@ -130,6 +131,13 @@ func (x *Policy) GetBuffer() *Policy_Buffer { return nil } +func (x *Policy) GetRestriction() *Policy_Restriction { + if x != nil { + return x.Restriction + } + return nil +} + type SystemPolicy struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -407,6 +415,53 @@ func (x *Policy_Buffer) GetConnection() int32 { return 0 } +type Policy_Restriction struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + MaxIPs int32 `protobuf:"varint,1,opt,name=maxIPs,proto3" json:"maxIPs,omitempty"` +} + +func (x *Policy_Restriction) Reset() { + *x = Policy_Restriction{} + if protoimpl.UnsafeEnabled { + mi := &file_app_policy_config_proto_msgTypes[7] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *Policy_Restriction) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Policy_Restriction) ProtoMessage() {} + +func (x *Policy_Restriction) ProtoReflect() protoreflect.Message { + mi := &file_app_policy_config_proto_msgTypes[7] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Policy_Restriction.ProtoReflect.Descriptor instead. +func (*Policy_Restriction) Descriptor() ([]byte, []int) { + return file_app_policy_config_proto_rawDescGZIP(), []int{1, 3} +} + +func (x *Policy_Restriction) GetMaxIPs() int32 { + if x != nil { + return x.MaxIPs + } + return 0 +} + type SystemPolicy_Stats struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -421,7 +476,7 @@ type SystemPolicy_Stats struct { func (x *SystemPolicy_Stats) Reset() { *x = SystemPolicy_Stats{} if protoimpl.UnsafeEnabled { - mi := &file_app_policy_config_proto_msgTypes[7] + mi := &file_app_policy_config_proto_msgTypes[8] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -434,7 +489,7 @@ func (x *SystemPolicy_Stats) String() string { func (*SystemPolicy_Stats) ProtoMessage() {} func (x *SystemPolicy_Stats) ProtoReflect() protoreflect.Message { - mi := &file_app_policy_config_proto_msgTypes[7] + mi := &file_app_policy_config_proto_msgTypes[8] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -485,7 +540,7 @@ var file_app_policy_config_proto_rawDesc = []byte{ 0x66, 0x69, 0x67, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x0f, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x22, 0x1e, 0x0a, 0x06, 0x53, 0x65, 0x63, 0x6f, 0x6e, 0x64, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x01, 0x20, - 0x01, 0x28, 0x0d, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x22, 0xa6, 0x04, 0x0a, 0x06, 0x50, + 0x01, 0x28, 0x0d, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x22, 0x94, 0x05, 0x0a, 0x06, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x12, 0x39, 0x0a, 0x07, 0x74, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1f, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, @@ -496,65 +551,72 @@ var file_app_policy_config_proto_rawDesc = []byte{ 0x73, 0x74, 0x61, 0x74, 0x73, 0x12, 0x36, 0x0a, 0x06, 0x62, 0x75, 0x66, 0x66, 0x65, 0x72, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1e, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x42, - 0x75, 0x66, 0x66, 0x65, 0x72, 0x52, 0x06, 0x62, 0x75, 0x66, 0x66, 0x65, 0x72, 0x1a, 0xfa, 0x01, - 0x0a, 0x07, 0x54, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x12, 0x35, 0x0a, 0x09, 0x68, 0x61, 0x6e, - 0x64, 0x73, 0x68, 0x61, 0x6b, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x78, - 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x53, - 0x65, 0x63, 0x6f, 0x6e, 0x64, 0x52, 0x09, 0x68, 0x61, 0x6e, 0x64, 0x73, 0x68, 0x61, 0x6b, 0x65, - 0x12, 0x40, 0x0a, 0x0f, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x69, - 0x64, 0x6c, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x78, 0x72, 0x61, 0x79, - 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x53, 0x65, 0x63, 0x6f, - 0x6e, 0x64, 0x52, 0x0e, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x64, - 0x6c, 0x65, 0x12, 0x38, 0x0a, 0x0b, 0x75, 0x70, 0x6c, 0x69, 0x6e, 0x6b, 0x5f, 0x6f, 0x6e, 0x6c, - 0x79, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, - 0x70, 0x70, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x53, 0x65, 0x63, 0x6f, 0x6e, 0x64, - 0x52, 0x0a, 0x75, 0x70, 0x6c, 0x69, 0x6e, 0x6b, 0x4f, 0x6e, 0x6c, 0x79, 0x12, 0x3c, 0x0a, 0x0d, - 0x64, 0x6f, 0x77, 0x6e, 0x6c, 0x69, 0x6e, 0x6b, 0x5f, 0x6f, 0x6e, 0x6c, 0x79, 0x18, 0x04, 0x20, + 0x75, 0x66, 0x66, 0x65, 0x72, 0x52, 0x06, 0x62, 0x75, 0x66, 0x66, 0x65, 0x72, 0x12, 0x45, 0x0a, + 0x0b, 0x72, 0x65, 0x73, 0x74, 0x72, 0x69, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x04, 0x20, 0x01, + 0x28, 0x0b, 0x32, 0x23, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x70, 0x6f, + 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x52, 0x65, 0x73, 0x74, + 0x72, 0x69, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x0b, 0x72, 0x65, 0x73, 0x74, 0x72, 0x69, 0x63, + 0x74, 0x69, 0x6f, 0x6e, 0x1a, 0xfa, 0x01, 0x0a, 0x07, 0x54, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, + 0x12, 0x35, 0x0a, 0x09, 0x68, 0x61, 0x6e, 0x64, 0x73, 0x68, 0x61, 0x6b, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x70, - 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x53, 0x65, 0x63, 0x6f, 0x6e, 0x64, 0x52, 0x0c, 0x64, 0x6f, - 0x77, 0x6e, 0x6c, 0x69, 0x6e, 0x6b, 0x4f, 0x6e, 0x6c, 0x79, 0x1a, 0x4d, 0x0a, 0x05, 0x53, 0x74, - 0x61, 0x74, 0x73, 0x12, 0x1f, 0x0a, 0x0b, 0x75, 0x73, 0x65, 0x72, 0x5f, 0x75, 0x70, 0x6c, 0x69, - 0x6e, 0x6b, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0a, 0x75, 0x73, 0x65, 0x72, 0x55, 0x70, - 0x6c, 0x69, 0x6e, 0x6b, 0x12, 0x23, 0x0a, 0x0d, 0x75, 0x73, 0x65, 0x72, 0x5f, 0x64, 0x6f, 0x77, - 0x6e, 0x6c, 0x69, 0x6e, 0x6b, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0c, 0x75, 0x73, 0x65, - 0x72, 0x44, 0x6f, 0x77, 0x6e, 0x6c, 0x69, 0x6e, 0x6b, 0x1a, 0x28, 0x0a, 0x06, 0x42, 0x75, 0x66, - 0x66, 0x65, 0x72, 0x12, 0x1e, 0x0a, 0x0a, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x69, 0x6f, - 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0a, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, - 0x69, 0x6f, 0x6e, 0x22, 0xfb, 0x01, 0x0a, 0x0c, 0x53, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x50, 0x6f, - 0x6c, 0x69, 0x63, 0x79, 0x12, 0x39, 0x0a, 0x05, 0x73, 0x74, 0x61, 0x74, 0x73, 0x18, 0x01, 0x20, - 0x01, 0x28, 0x0b, 0x32, 0x23, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x70, - 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x53, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x50, 0x6f, 0x6c, 0x69, - 0x63, 0x79, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x73, 0x52, 0x05, 0x73, 0x74, 0x61, 0x74, 0x73, 0x1a, - 0xaf, 0x01, 0x0a, 0x05, 0x53, 0x74, 0x61, 0x74, 0x73, 0x12, 0x25, 0x0a, 0x0e, 0x69, 0x6e, 0x62, - 0x6f, 0x75, 0x6e, 0x64, 0x5f, 0x75, 0x70, 0x6c, 0x69, 0x6e, 0x6b, 0x18, 0x01, 0x20, 0x01, 0x28, - 0x08, 0x52, 0x0d, 0x69, 0x6e, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x55, 0x70, 0x6c, 0x69, 0x6e, 0x6b, - 0x12, 0x29, 0x0a, 0x10, 0x69, 0x6e, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x5f, 0x64, 0x6f, 0x77, 0x6e, - 0x6c, 0x69, 0x6e, 0x6b, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0f, 0x69, 0x6e, 0x62, 0x6f, - 0x75, 0x6e, 0x64, 0x44, 0x6f, 0x77, 0x6e, 0x6c, 0x69, 0x6e, 0x6b, 0x12, 0x27, 0x0a, 0x0f, 0x6f, - 0x75, 0x74, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x5f, 0x75, 0x70, 0x6c, 0x69, 0x6e, 0x6b, 0x18, 0x03, - 0x20, 0x01, 0x28, 0x08, 0x52, 0x0e, 0x6f, 0x75, 0x74, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x55, 0x70, - 0x6c, 0x69, 0x6e, 0x6b, 0x12, 0x2b, 0x0a, 0x11, 0x6f, 0x75, 0x74, 0x62, 0x6f, 0x75, 0x6e, 0x64, - 0x5f, 0x64, 0x6f, 0x77, 0x6e, 0x6c, 0x69, 0x6e, 0x6b, 0x18, 0x04, 0x20, 0x01, 0x28, 0x08, 0x52, - 0x10, 0x6f, 0x75, 0x74, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x44, 0x6f, 0x77, 0x6e, 0x6c, 0x69, 0x6e, - 0x6b, 0x22, 0xcc, 0x01, 0x0a, 0x06, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x38, 0x0a, 0x05, - 0x6c, 0x65, 0x76, 0x65, 0x6c, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x22, 0x2e, 0x78, 0x72, - 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x43, 0x6f, - 0x6e, 0x66, 0x69, 0x67, 0x2e, 0x4c, 0x65, 0x76, 0x65, 0x6c, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, - 0x05, 0x6c, 0x65, 0x76, 0x65, 0x6c, 0x12, 0x35, 0x0a, 0x06, 0x73, 0x79, 0x73, 0x74, 0x65, 0x6d, - 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, - 0x70, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x53, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x50, - 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x52, 0x06, 0x73, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x1a, 0x51, 0x0a, - 0x0a, 0x4c, 0x65, 0x76, 0x65, 0x6c, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, - 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x2d, 0x0a, - 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x78, - 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x50, - 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, - 0x42, 0x4f, 0x0a, 0x13, 0x63, 0x6f, 0x6d, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, - 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x50, 0x01, 0x5a, 0x24, 0x67, 0x69, 0x74, 0x68, 0x75, - 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x78, 0x74, 0x6c, 0x73, 0x2f, 0x78, 0x72, 0x61, 0x79, 0x2d, - 0x63, 0x6f, 0x72, 0x65, 0x2f, 0x61, 0x70, 0x70, 0x2f, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0xaa, - 0x02, 0x0f, 0x58, 0x72, 0x61, 0x79, 0x2e, 0x41, 0x70, 0x70, 0x2e, 0x50, 0x6f, 0x6c, 0x69, 0x63, - 0x79, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x53, 0x65, 0x63, 0x6f, 0x6e, 0x64, 0x52, 0x09, 0x68, 0x61, + 0x6e, 0x64, 0x73, 0x68, 0x61, 0x6b, 0x65, 0x12, 0x40, 0x0a, 0x0f, 0x63, 0x6f, 0x6e, 0x6e, 0x65, + 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x69, 0x64, 0x6c, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, + 0x32, 0x17, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x70, 0x6f, 0x6c, 0x69, + 0x63, 0x79, 0x2e, 0x53, 0x65, 0x63, 0x6f, 0x6e, 0x64, 0x52, 0x0e, 0x63, 0x6f, 0x6e, 0x6e, 0x65, + 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x64, 0x6c, 0x65, 0x12, 0x38, 0x0a, 0x0b, 0x75, 0x70, 0x6c, + 0x69, 0x6e, 0x6b, 0x5f, 0x6f, 0x6e, 0x6c, 0x79, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, + 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, + 0x2e, 0x53, 0x65, 0x63, 0x6f, 0x6e, 0x64, 0x52, 0x0a, 0x75, 0x70, 0x6c, 0x69, 0x6e, 0x6b, 0x4f, + 0x6e, 0x6c, 0x79, 0x12, 0x3c, 0x0a, 0x0d, 0x64, 0x6f, 0x77, 0x6e, 0x6c, 0x69, 0x6e, 0x6b, 0x5f, + 0x6f, 0x6e, 0x6c, 0x79, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x78, 0x72, 0x61, + 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x53, 0x65, 0x63, + 0x6f, 0x6e, 0x64, 0x52, 0x0c, 0x64, 0x6f, 0x77, 0x6e, 0x6c, 0x69, 0x6e, 0x6b, 0x4f, 0x6e, 0x6c, + 0x79, 0x1a, 0x4d, 0x0a, 0x05, 0x53, 0x74, 0x61, 0x74, 0x73, 0x12, 0x1f, 0x0a, 0x0b, 0x75, 0x73, + 0x65, 0x72, 0x5f, 0x75, 0x70, 0x6c, 0x69, 0x6e, 0x6b, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, + 0x0a, 0x75, 0x73, 0x65, 0x72, 0x55, 0x70, 0x6c, 0x69, 0x6e, 0x6b, 0x12, 0x23, 0x0a, 0x0d, 0x75, + 0x73, 0x65, 0x72, 0x5f, 0x64, 0x6f, 0x77, 0x6e, 0x6c, 0x69, 0x6e, 0x6b, 0x18, 0x02, 0x20, 0x01, + 0x28, 0x08, 0x52, 0x0c, 0x75, 0x73, 0x65, 0x72, 0x44, 0x6f, 0x77, 0x6e, 0x6c, 0x69, 0x6e, 0x6b, + 0x1a, 0x28, 0x0a, 0x06, 0x42, 0x75, 0x66, 0x66, 0x65, 0x72, 0x12, 0x1e, 0x0a, 0x0a, 0x63, 0x6f, + 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0a, + 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x1a, 0x25, 0x0a, 0x0b, 0x52, 0x65, + 0x73, 0x74, 0x72, 0x69, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x16, 0x0a, 0x06, 0x6d, 0x61, 0x78, + 0x49, 0x50, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x05, 0x52, 0x06, 0x6d, 0x61, 0x78, 0x49, 0x50, + 0x73, 0x22, 0xfb, 0x01, 0x0a, 0x0c, 0x53, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x50, 0x6f, 0x6c, 0x69, + 0x63, 0x79, 0x12, 0x39, 0x0a, 0x05, 0x73, 0x74, 0x61, 0x74, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x0b, 0x32, 0x23, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x70, 0x6f, 0x6c, + 0x69, 0x63, 0x79, 0x2e, 0x53, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, + 0x2e, 0x53, 0x74, 0x61, 0x74, 0x73, 0x52, 0x05, 0x73, 0x74, 0x61, 0x74, 0x73, 0x1a, 0xaf, 0x01, + 0x0a, 0x05, 0x53, 0x74, 0x61, 0x74, 0x73, 0x12, 0x25, 0x0a, 0x0e, 0x69, 0x6e, 0x62, 0x6f, 0x75, + 0x6e, 0x64, 0x5f, 0x75, 0x70, 0x6c, 0x69, 0x6e, 0x6b, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, + 0x0d, 0x69, 0x6e, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x55, 0x70, 0x6c, 0x69, 0x6e, 0x6b, 0x12, 0x29, + 0x0a, 0x10, 0x69, 0x6e, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x5f, 0x64, 0x6f, 0x77, 0x6e, 0x6c, 0x69, + 0x6e, 0x6b, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0f, 0x69, 0x6e, 0x62, 0x6f, 0x75, 0x6e, + 0x64, 0x44, 0x6f, 0x77, 0x6e, 0x6c, 0x69, 0x6e, 0x6b, 0x12, 0x27, 0x0a, 0x0f, 0x6f, 0x75, 0x74, + 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x5f, 0x75, 0x70, 0x6c, 0x69, 0x6e, 0x6b, 0x18, 0x03, 0x20, 0x01, + 0x28, 0x08, 0x52, 0x0e, 0x6f, 0x75, 0x74, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x55, 0x70, 0x6c, 0x69, + 0x6e, 0x6b, 0x12, 0x2b, 0x0a, 0x11, 0x6f, 0x75, 0x74, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x5f, 0x64, + 0x6f, 0x77, 0x6e, 0x6c, 0x69, 0x6e, 0x6b, 0x18, 0x04, 0x20, 0x01, 0x28, 0x08, 0x52, 0x10, 0x6f, + 0x75, 0x74, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x44, 0x6f, 0x77, 0x6e, 0x6c, 0x69, 0x6e, 0x6b, 0x22, + 0xcc, 0x01, 0x0a, 0x06, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x38, 0x0a, 0x05, 0x6c, 0x65, + 0x76, 0x65, 0x6c, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x22, 0x2e, 0x78, 0x72, 0x61, 0x79, + 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x43, 0x6f, 0x6e, 0x66, + 0x69, 0x67, 0x2e, 0x4c, 0x65, 0x76, 0x65, 0x6c, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x05, 0x6c, + 0x65, 0x76, 0x65, 0x6c, 0x12, 0x35, 0x0a, 0x06, 0x73, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x18, 0x02, + 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, + 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x53, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x50, 0x6f, 0x6c, + 0x69, 0x63, 0x79, 0x52, 0x06, 0x73, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x1a, 0x51, 0x0a, 0x0a, 0x4c, + 0x65, 0x76, 0x65, 0x6c, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x2d, 0x0a, 0x05, 0x76, + 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x78, 0x72, 0x61, + 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x50, 0x6f, 0x6c, + 0x69, 0x63, 0x79, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x42, 0x4f, + 0x0a, 0x13, 0x63, 0x6f, 0x6d, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x70, + 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x50, 0x01, 0x5a, 0x24, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, + 0x63, 0x6f, 0x6d, 0x2f, 0x78, 0x74, 0x6c, 0x73, 0x2f, 0x78, 0x72, 0x61, 0x79, 0x2d, 0x63, 0x6f, + 0x72, 0x65, 0x2f, 0x61, 0x70, 0x70, 0x2f, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0xaa, 0x02, 0x0f, + 0x58, 0x72, 0x61, 0x79, 0x2e, 0x41, 0x70, 0x70, 0x2e, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x62, + 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( @@ -569,7 +631,7 @@ func file_app_policy_config_proto_rawDescGZIP() []byte { return file_app_policy_config_proto_rawDescData } -var file_app_policy_config_proto_msgTypes = make([]protoimpl.MessageInfo, 9) +var file_app_policy_config_proto_msgTypes = make([]protoimpl.MessageInfo, 10) var file_app_policy_config_proto_goTypes = []interface{}{ (*Second)(nil), // 0: xray.app.policy.Second (*Policy)(nil), // 1: xray.app.policy.Policy @@ -578,26 +640,28 @@ var file_app_policy_config_proto_goTypes = []interface{}{ (*Policy_Timeout)(nil), // 4: xray.app.policy.Policy.Timeout (*Policy_Stats)(nil), // 5: xray.app.policy.Policy.Stats (*Policy_Buffer)(nil), // 6: xray.app.policy.Policy.Buffer - (*SystemPolicy_Stats)(nil), // 7: xray.app.policy.SystemPolicy.Stats - nil, // 8: xray.app.policy.Config.LevelEntry + (*Policy_Restriction)(nil), // 7: xray.app.policy.Policy.Restriction + (*SystemPolicy_Stats)(nil), // 8: xray.app.policy.SystemPolicy.Stats + nil, // 9: xray.app.policy.Config.LevelEntry } var file_app_policy_config_proto_depIdxs = []int32{ 4, // 0: xray.app.policy.Policy.timeout:type_name -> xray.app.policy.Policy.Timeout 5, // 1: xray.app.policy.Policy.stats:type_name -> xray.app.policy.Policy.Stats 6, // 2: xray.app.policy.Policy.buffer:type_name -> xray.app.policy.Policy.Buffer - 7, // 3: xray.app.policy.SystemPolicy.stats:type_name -> xray.app.policy.SystemPolicy.Stats - 8, // 4: xray.app.policy.Config.level:type_name -> xray.app.policy.Config.LevelEntry - 2, // 5: xray.app.policy.Config.system:type_name -> xray.app.policy.SystemPolicy - 0, // 6: xray.app.policy.Policy.Timeout.handshake:type_name -> xray.app.policy.Second - 0, // 7: xray.app.policy.Policy.Timeout.connection_idle:type_name -> xray.app.policy.Second - 0, // 8: xray.app.policy.Policy.Timeout.uplink_only:type_name -> xray.app.policy.Second - 0, // 9: xray.app.policy.Policy.Timeout.downlink_only:type_name -> xray.app.policy.Second - 1, // 10: xray.app.policy.Config.LevelEntry.value:type_name -> xray.app.policy.Policy - 11, // [11:11] is the sub-list for method output_type - 11, // [11:11] is the sub-list for method input_type - 11, // [11:11] is the sub-list for extension type_name - 11, // [11:11] is the sub-list for extension extendee - 0, // [0:11] is the sub-list for field type_name + 7, // 3: xray.app.policy.Policy.restriction:type_name -> xray.app.policy.Policy.Restriction + 8, // 4: xray.app.policy.SystemPolicy.stats:type_name -> xray.app.policy.SystemPolicy.Stats + 9, // 5: xray.app.policy.Config.level:type_name -> xray.app.policy.Config.LevelEntry + 2, // 6: xray.app.policy.Config.system:type_name -> xray.app.policy.SystemPolicy + 0, // 7: xray.app.policy.Policy.Timeout.handshake:type_name -> xray.app.policy.Second + 0, // 8: xray.app.policy.Policy.Timeout.connection_idle:type_name -> xray.app.policy.Second + 0, // 9: xray.app.policy.Policy.Timeout.uplink_only:type_name -> xray.app.policy.Second + 0, // 10: xray.app.policy.Policy.Timeout.downlink_only:type_name -> xray.app.policy.Second + 1, // 11: xray.app.policy.Config.LevelEntry.value:type_name -> xray.app.policy.Policy + 12, // [12:12] is the sub-list for method output_type + 12, // [12:12] is the sub-list for method input_type + 12, // [12:12] is the sub-list for extension type_name + 12, // [12:12] is the sub-list for extension extendee + 0, // [0:12] is the sub-list for field type_name } func init() { file_app_policy_config_proto_init() } @@ -691,6 +755,18 @@ func file_app_policy_config_proto_init() { } } file_app_policy_config_proto_msgTypes[7].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Policy_Restriction); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_app_policy_config_proto_msgTypes[8].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*SystemPolicy_Stats); i { case 0: return &v.state @@ -709,7 +785,7 @@ func file_app_policy_config_proto_init() { GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: file_app_policy_config_proto_rawDesc, NumEnums: 0, - NumMessages: 9, + NumMessages: 10, NumExtensions: 0, NumServices: 0, }, diff --git a/app/policy/config.proto b/app/policy/config.proto index e5f29547..a0f37f96 100644 --- a/app/policy/config.proto +++ b/app/policy/config.proto @@ -29,9 +29,14 @@ message Policy { int32 connection = 1; } + message Restriction { + int32 maxIPs = 1; + } + Timeout timeout = 1; Stats stats = 2; Buffer buffer = 3; + Restriction restriction = 4; } message SystemPolicy { diff --git a/app/proxyman/inbound/worker.go b/app/proxyman/inbound/worker.go index 18e38d65..199fd4c5 100644 --- a/app/proxyman/inbound/worker.go +++ b/app/proxyman/inbound/worker.go @@ -18,6 +18,7 @@ import ( "github.com/xtls/xray-core/features/stats" "github.com/xtls/xray-core/proxy" "github.com/xtls/xray-core/transport/internet" + "github.com/xtls/xray-core/transport/internet/restriction" "github.com/xtls/xray-core/transport/internet/stat" "github.com/xtls/xray-core/transport/internet/tcp" "github.com/xtls/xray-core/transport/internet/udp" @@ -32,6 +33,8 @@ type worker interface { } type tcpWorker struct { + sync.Mutex + address net.Address port net.Port proxy proxy.Inbound @@ -42,7 +45,7 @@ type tcpWorker struct { sniffingConfig *proxyman.SniffingConfig uplinkCounter stats.Counter downlinkCounter stats.Counter - ipLimitPool map[session.ID]*stat.UserIpRestriction + ipLimitPool map[session.ID]*restriction.UserMaxIp hub internet.Listener @@ -106,16 +109,20 @@ func (w *tcpWorker) callback(conn stat.Connection) { ctx = session.ContextWithContent(ctx, content) // Add this IP address to the pool for futher IP limit check - w.ipLimitPool[sid] = &stat.UserIpRestriction{ + w.Lock() + w.ipLimitPool[sid] = &restriction.UserMaxIp{ IpAddress: net.IP(conn.RemoteAddr().Network()), } + w.Unlock() if err := w.proxy.Process(ctx, net.Network_TCP, conn, w.dispatcher, &w.ipLimitPool, w.ipLimitPool[sid]); err != nil { newError("connection ends").Base(err).WriteToLog(session.ExportIDToError(ctx)) } - + // Deletes the IP address from the pool after the connection ends + w.Lock() delete(w.ipLimitPool, sid) + w.Unlock() cancel() conn.Close() @@ -127,7 +134,7 @@ func (w *tcpWorker) Proxy() proxy.Inbound { func (w *tcpWorker) Start() error { if len(w.ipLimitPool) == 0 { - w.ipLimitPool = make(map[session.ID]*stat.UserIpRestriction) + w.ipLimitPool = make(map[session.ID]*restriction.UserMaxIp) } ctx := context.Background() hub, err := internet.ListenTCP(ctx, w.address, w.port, w.stream, func(conn stat.Connection) { @@ -257,7 +264,7 @@ type udpWorker struct { sniffingConfig *proxyman.SniffingConfig uplinkCounter stats.Counter downlinkCounter stats.Counter - ipLimitPool map[session.ID]*stat.UserIpRestriction + ipLimitPool map[session.ID]*restriction.UserMaxIp checker *task.Periodic activeConn map[connID]*udpConn @@ -342,16 +349,20 @@ func (w *udpWorker) callback(b *buf.Buffer, source net.Destination, originalDest ctx = session.ContextWithContent(ctx, content) // Add this IP address to the pool for futher IP limit check - w.ipLimitPool[sid] = &stat.UserIpRestriction{ + w.Lock() + w.ipLimitPool[sid] = &restriction.UserMaxIp{ IpAddress: net.IP(conn.RemoteAddr().Network()), } - + w.Unlock() + if err := w.proxy.Process(ctx, net.Network_UDP, conn, w.dispatcher, &w.ipLimitPool, w.ipLimitPool[sid]); err != nil { newError("connection ends").Base(err).WriteToLog(session.ExportIDToError(ctx)) } // Deletes the IP address from the pool after the connection ends + w.Lock() delete(w.ipLimitPool, sid) + w.Unlock() conn.Close() // conn not removed by checker TODO may be lock worker here is better @@ -404,7 +415,7 @@ func (w *udpWorker) clean() error { func (w *udpWorker) Start() error { if len(w.ipLimitPool) == 0 { - w.ipLimitPool = make(map[session.ID]*stat.UserIpRestriction) + w.ipLimitPool = make(map[session.ID]*restriction.UserMaxIp) } w.activeConn = make(map[connID]*udpConn, 16) ctx := context.Background() diff --git a/common/protocol/user.go b/common/protocol/user.go index 9006a4a0..8325f555 100644 --- a/common/protocol/user.go +++ b/common/protocol/user.go @@ -27,7 +27,6 @@ func (u *User) ToMemoryUser() (*MemoryUser, error) { Account: account, Email: u.Email, Level: u.Level, - IpLimit: u.Ips, }, nil } @@ -37,5 +36,4 @@ type MemoryUser struct { Account Account Email string Level uint32 - IpLimit uint32 } diff --git a/common/protocol/user.proto b/common/protocol/user.proto index 3632eeaa..44770edf 100644 --- a/common/protocol/user.proto +++ b/common/protocol/user.proto @@ -16,7 +16,4 @@ message User { // Protocol specific account information. Must be the account proto in one of // the proxies. xray.common.serial.TypedMessage account = 3; - - // Allowed IPs - uint32 ips = 4; } diff --git a/features/policy/policy.go b/features/policy/policy.go index 4d3f7ecf..b71e76c3 100644 --- a/features/policy/policy.go +++ b/features/policy/policy.go @@ -35,6 +35,12 @@ type Buffer struct { PerConnection int32 } +// Buffer contains settings for restriction such as ip restriction. +type Restriction struct { + // Maximum allowed ips, -1 for unlimited + MaxIPs int32 +} + // SystemStats contains stat policy settings on system level. type SystemStats struct { // Whether or not to enable stat counter for uplink traffic in inbound handlers. @@ -55,9 +61,10 @@ type System struct { // Session is session based settings for controlling Xray requests. It contains various settings (or limits) that may differ for different users in the context. type Session struct { - Timeouts Timeout // Timeout settings - Stats Stats - Buffer Buffer + Timeouts Timeout // Timeout settings + Stats Stats + Buffer Buffer + Restriction Restriction } // Manager is a feature that provides Policy for the given user by its id or level. @@ -109,6 +116,12 @@ func defaultBufferPolicy() Buffer { } } +func defaultRestrictionPolicy() Restriction { + return Restriction{ + MaxIPs: -1, + } +} + // SessionDefault returns the Policy when user is not specified. func SessionDefault() Session { return Session{ @@ -124,7 +137,8 @@ func SessionDefault() Session { UserUplink: false, UserDownlink: false, }, - Buffer: defaultBufferPolicy(), + Buffer: defaultBufferPolicy(), + Restriction: defaultRestrictionPolicy(), } } diff --git a/infra/conf/common.go b/infra/conf/common.go index 62876441..e755b70f 100644 --- a/infra/conf/common.go +++ b/infra/conf/common.go @@ -233,13 +233,11 @@ func (list *PortList) UnmarshalJSON(data []byte) error { type User struct { EmailString string `json:"email"` LevelByte byte `json:"level"` - IpLimitByte byte `json:"ips"` } func (v *User) Build() *protocol.User { return &protocol.User{ Email: v.EmailString, Level: uint32(v.LevelByte), - Ips: uint32(v.IpLimitByte), } } diff --git a/infra/conf/policy.go b/infra/conf/policy.go index 5fbf01e6..74809401 100644 --- a/infra/conf/policy.go +++ b/infra/conf/policy.go @@ -12,6 +12,7 @@ type Policy struct { StatsUserUplink bool `json:"statsUserUplink"` StatsUserDownlink bool `json:"statsUserDownlink"` BufferSize *int32 `json:"bufferSize"` + MaxIPs *int32 `json:"maxIPs"` } func (t *Policy) Build() (*policy.Policy, error) { @@ -47,6 +48,12 @@ func (t *Policy) Build() (*policy.Policy, error) { } } + if t.MaxIPs != nil { + p.Restriction = &policy.Policy_Restriction{ + MaxIPs: (*t.MaxIPs), + } + } + return p, nil } diff --git a/proxy/dokodemo/dokodemo.go b/proxy/dokodemo/dokodemo.go index 144748b5..8b882367 100644 --- a/proxy/dokodemo/dokodemo.go +++ b/proxy/dokodemo/dokodemo.go @@ -18,6 +18,7 @@ import ( "github.com/xtls/xray-core/core" "github.com/xtls/xray-core/features/policy" "github.com/xtls/xray-core/features/routing" + "github.com/xtls/xray-core/transport/internet/restriction" "github.com/xtls/xray-core/transport/internet/stat" ) @@ -76,7 +77,7 @@ type hasHandshakeAddress interface { } // Process implements proxy.Inbound. -func (d *DokodemoDoor) Process(ctx context.Context, network net.Network, conn stat.Connection, dispatcher routing.Dispatcher, _ *map[session.ID]*stat.UserIpRestriction, _ *stat.UserIpRestriction) error { +func (d *DokodemoDoor) Process(ctx context.Context, network net.Network, conn stat.Connection, dispatcher routing.Dispatcher, _ *map[session.ID]*restriction.UserMaxIp, _ *restriction.UserMaxIp) error { newError("processing connection from: ", conn.RemoteAddr()).AtDebug().WriteToLog(session.ExportIDToError(ctx)) dest := net.Destination{ Network: network, diff --git a/proxy/proxy.go b/proxy/proxy.go index 3cc86d7e..4f493588 100644 --- a/proxy/proxy.go +++ b/proxy/proxy.go @@ -26,6 +26,7 @@ import ( "github.com/xtls/xray-core/transport" "github.com/xtls/xray-core/transport/internet" "github.com/xtls/xray-core/transport/internet/reality" + "github.com/xtls/xray-core/transport/internet/restriction" "github.com/xtls/xray-core/transport/internet/stat" "github.com/xtls/xray-core/transport/internet/tls" ) @@ -60,7 +61,7 @@ type Inbound interface { Network() []net.Network // Process processes a connection of given network. If necessary, the Inbound can dispatch the connection to an Outbound. - Process(context.Context, net.Network, stat.Connection, routing.Dispatcher, *map[session.ID]*stat.UserIpRestriction, *stat.UserIpRestriction) error + Process(context.Context, net.Network, stat.Connection, routing.Dispatcher, *map[session.ID]*restriction.UserMaxIp, *restriction.UserMaxIp) error } // An Outbound process outbound connections. diff --git a/proxy/trojan/server.go b/proxy/trojan/server.go index 9244774f..244153cb 100644 --- a/proxy/trojan/server.go +++ b/proxy/trojan/server.go @@ -23,6 +23,7 @@ import ( "github.com/xtls/xray-core/features/policy" "github.com/xtls/xray-core/features/routing" "github.com/xtls/xray-core/transport/internet/reality" + "github.com/xtls/xray-core/transport/internet/restriction" "github.com/xtls/xray-core/transport/internet/stat" "github.com/xtls/xray-core/transport/internet/tls" "github.com/xtls/xray-core/transport/internet/udp" @@ -134,7 +135,7 @@ func (s *Server) Network() []net.Network { } // Process implements proxy.Inbound.Process(). -func (s *Server) Process(ctx context.Context, network net.Network, conn stat.Connection, dispatcher routing.Dispatcher, usrIpRstrct *map[session.ID]*stat.UserIpRestriction, connIp *stat.UserIpRestriction) error { +func (s *Server) Process(ctx context.Context, network net.Network, conn stat.Connection, dispatcher routing.Dispatcher, usrIpRstrct *map[session.ID]*restriction.UserMaxIp, connIp *restriction.UserMaxIp) error { sid := session.ExportIDToError(ctx) iConn := conn @@ -222,7 +223,7 @@ func (s *Server) Process(ctx context.Context, network net.Network, conn stat.Con inbound.User = user sessionPolicy = s.policyManager.ForLevel(user.Level) - if (user.IpLimit > 0) { + if sessionPolicy.Restriction.MaxIPs > 0 { addr := conn.RemoteAddr().(*net.TCPAddr) uniqueIps := make(map[string]bool) @@ -236,7 +237,7 @@ func (s *Server) Process(ctx context.Context, network net.Network, conn stat.Con } s.Unlock() - if (len(uniqueIps) >= int(user.IpLimit)) { + if len(uniqueIps) >= int(sessionPolicy.Restriction.MaxIPs) { return newError("User ", user, " has exceeded their allowed IPs.").AtWarning() } diff --git a/proxy/vless/inbound/inbound.go b/proxy/vless/inbound/inbound.go index 809276f4..597ecaed 100644 --- a/proxy/vless/inbound/inbound.go +++ b/proxy/vless/inbound/inbound.go @@ -33,6 +33,7 @@ import ( "github.com/xtls/xray-core/proxy/vless" "github.com/xtls/xray-core/proxy/vless/encoding" "github.com/xtls/xray-core/transport/internet/reality" + "github.com/xtls/xray-core/transport/internet/restriction" "github.com/xtls/xray-core/transport/internet/stat" "github.com/xtls/xray-core/transport/internet/tls" ) @@ -179,7 +180,7 @@ func (*Handler) Network() []net.Network { } // Process implements proxy.Inbound.Process(). -func (h *Handler) Process(ctx context.Context, network net.Network, connection stat.Connection, dispatcher routing.Dispatcher, usrIpRstrct *map[session.ID]*stat.UserIpRestriction, connIp *stat.UserIpRestriction) error { +func (h *Handler) Process(ctx context.Context, network net.Network, connection stat.Connection, dispatcher routing.Dispatcher, usrIpRstrct *map[session.ID]*restriction.UserMaxIp, connIp *restriction.UserMaxIp) error { sid := session.ExportIDToError(ctx) iConn := connection @@ -187,6 +188,7 @@ func (h *Handler) Process(ctx context.Context, network net.Network, connection s iConn = statConn.Connection } + sessionPolicy := h.policyManager.ForLevel(0) if err := connection.SetReadDeadline(time.Now().Add(sessionPolicy.Timeouts.Handshake)); err != nil { return newError("unable to set read deadline").Base(err).AtWarning() @@ -447,7 +449,7 @@ func (h *Handler) Process(ctx context.Context, network net.Network, connection s // Flow: requestAddons.Flow, } - if (request.User.IpLimit > 0) { + if sessionPolicy.Restriction.MaxIPs > 0 { addr := connection.RemoteAddr().(*net.TCPAddr) uniqueIps := make(map[string]bool) @@ -461,30 +463,7 @@ func (h *Handler) Process(ctx context.Context, network net.Network, connection s } h.Unlock() - if (len(uniqueIps) >= int(request.User.IpLimit)) { - return newError("User ", request.User.Email, " has exceeded their allowed IPs.").AtWarning() - } - - connIp.IpAddress = addr.IP - connIp.User = request.User.Email - connIp.Time = time.Now().Unix() - } - - if (request.User.IpLimit > 0) { - addr := connection.RemoteAddr().(*net.TCPAddr) - - uniqueIps := make(map[string]bool) - - h.Lock() - // Iterate through the connections and find unique used IP addresses withing last 30 seconds. - for _, conn := range *usrIpRstrct { - if conn.User == request.User.Email && !conn.IpAddress.Equal(addr.IP) && ((time.Now().Unix() - conn.Time) < 30) { - uniqueIps[conn.IpAddress.String()] = true - } - } - h.Unlock() - - if (len(uniqueIps) >= int(request.User.IpLimit)) { + if len(uniqueIps) >= int(sessionPolicy.Restriction.MaxIPs) { return newError("User ", request.User.Email, " has exceeded their allowed IPs.").AtWarning() } diff --git a/proxy/vmess/inbound/inbound.go b/proxy/vmess/inbound/inbound.go index a13c299b..f6c3bfdd 100644 --- a/proxy/vmess/inbound/inbound.go +++ b/proxy/vmess/inbound/inbound.go @@ -25,6 +25,7 @@ import ( "github.com/xtls/xray-core/features/routing" "github.com/xtls/xray-core/proxy/vmess" "github.com/xtls/xray-core/proxy/vmess/encoding" + "github.com/xtls/xray-core/transport/internet/restriction" "github.com/xtls/xray-core/transport/internet/stat" ) @@ -210,7 +211,7 @@ func transferResponse(timer signal.ActivityUpdater, session *encoding.ServerSess } // Process implements proxy.Inbound.Process(). -func (h *Handler) Process(ctx context.Context, network net.Network, connection stat.Connection, dispatcher routing.Dispatcher, usrIpRstrct *map[session.ID]*stat.UserIpRestriction, connIp *stat.UserIpRestriction) error { +func (h *Handler) Process(ctx context.Context, network net.Network, connection stat.Connection, dispatcher routing.Dispatcher, usrIpRstrct *map[session.ID]*restriction.UserMaxIp, connIp *restriction.UserMaxIp) error { sessionPolicy := h.policyManager.ForLevel(0) if err := connection.SetReadDeadline(time.Now().Add(sessionPolicy.Timeouts.Handshake)); err != nil { return newError("unable to set read deadline").Base(err).AtWarning() @@ -264,7 +265,7 @@ func (h *Handler) Process(ctx context.Context, network net.Network, connection s sessionPolicy = h.policyManager.ForLevel(request.User.Level) - if (request.User.IpLimit > 0) { + if sessionPolicy.Restriction.MaxIPs > 0 { addr := connection.RemoteAddr().(*net.TCPAddr) uniqueIps := make(map[string]bool) @@ -277,7 +278,7 @@ func (h *Handler) Process(ctx context.Context, network net.Network, connection s } h.Unlock() - if (len(uniqueIps) >= int(request.User.IpLimit)) { + if len(uniqueIps) >= int(sessionPolicy.Restriction.MaxIPs) { return newError("User ", request.User.Email, " has exceeded their allowed IPs.").AtWarning() } diff --git a/transport/internet/restriction/ip.go b/transport/internet/restriction/ip.go new file mode 100644 index 00000000..05ff2fa3 --- /dev/null +++ b/transport/internet/restriction/ip.go @@ -0,0 +1,11 @@ +package restriction + +import ( + "net" +) + +type UserMaxIp struct { + User string + IpAddress net.IP + Time int64 +} diff --git a/transport/internet/stat/connection.go b/transport/internet/stat/connection.go index 4a3fd681..6921943d 100644 --- a/transport/internet/stat/connection.go +++ b/transport/internet/stat/connection.go @@ -6,12 +6,6 @@ import ( "github.com/xtls/xray-core/features/stats" ) -type UserIpRestriction struct { - User string - IpAddress net.IP - Time int64 -} - type Connection interface { net.Conn } From 4f5dbf9b72c94d3d0a3c924ed5c55401df02c070 Mon Sep 17 00:00:00 2001 From: amir-devman Date: Mon, 5 Feb 2024 20:27:52 +0000 Subject: [PATCH 8/8] chore(restriction): missing from last commit --- common/protocol/user.pb.go | 38 ++++++++++++++------------------------ 1 file changed, 14 insertions(+), 24 deletions(-) diff --git a/common/protocol/user.pb.go b/common/protocol/user.pb.go index dac02941..77c26e22 100644 --- a/common/protocol/user.pb.go +++ b/common/protocol/user.pb.go @@ -32,8 +32,6 @@ type User struct { // Protocol specific account information. Must be the account proto in one of // the proxies. Account *serial.TypedMessage `protobuf:"bytes,3,opt,name=account,proto3" json:"account,omitempty"` - // Allowed IPs - Ips uint32 `protobuf:"varint,4,opt,name=ips,proto3" json:"ips,omitempty"` } func (x *User) Reset() { @@ -89,13 +87,6 @@ func (x *User) GetAccount() *serial.TypedMessage { return nil } -func (x *User) GetIps() uint32 { - if x != nil { - return x.Ips - } - return 0 -} - var File_common_protocol_user_proto protoreflect.FileDescriptor var file_common_protocol_user_proto_rawDesc = []byte{ @@ -104,21 +95,20 @@ var file_common_protocol_user_proto_rawDesc = []byte{ 0x61, 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x1a, 0x21, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2f, 0x73, 0x65, 0x72, 0x69, 0x61, 0x6c, 0x2f, 0x74, 0x79, 0x70, 0x65, 0x64, 0x5f, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x2e, - 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x80, 0x01, 0x0a, 0x04, 0x55, 0x73, 0x65, 0x72, 0x12, 0x14, - 0x0a, 0x05, 0x6c, 0x65, 0x76, 0x65, 0x6c, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x05, 0x6c, - 0x65, 0x76, 0x65, 0x6c, 0x12, 0x14, 0x0a, 0x05, 0x65, 0x6d, 0x61, 0x69, 0x6c, 0x18, 0x02, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x05, 0x65, 0x6d, 0x61, 0x69, 0x6c, 0x12, 0x3a, 0x0a, 0x07, 0x61, 0x63, - 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x20, 0x2e, 0x78, 0x72, - 0x61, 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x73, 0x65, 0x72, 0x69, 0x61, 0x6c, - 0x2e, 0x54, 0x79, 0x70, 0x65, 0x64, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x52, 0x07, 0x61, - 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x10, 0x0a, 0x03, 0x69, 0x70, 0x73, 0x18, 0x04, 0x20, - 0x01, 0x28, 0x0d, 0x52, 0x03, 0x69, 0x70, 0x73, 0x42, 0x5e, 0x0a, 0x18, 0x63, 0x6f, 0x6d, 0x2e, - 0x78, 0x72, 0x61, 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x70, 0x72, 0x6f, 0x74, - 0x6f, 0x63, 0x6f, 0x6c, 0x50, 0x01, 0x5a, 0x29, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, - 0x6f, 0x6d, 0x2f, 0x78, 0x74, 0x6c, 0x73, 0x2f, 0x78, 0x72, 0x61, 0x79, 0x2d, 0x63, 0x6f, 0x72, - 0x65, 0x2f, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, - 0x6c, 0xaa, 0x02, 0x14, 0x58, 0x72, 0x61, 0x79, 0x2e, 0x43, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, - 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x6e, 0x0a, 0x04, 0x55, 0x73, 0x65, 0x72, 0x12, 0x14, 0x0a, + 0x05, 0x6c, 0x65, 0x76, 0x65, 0x6c, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x05, 0x6c, 0x65, + 0x76, 0x65, 0x6c, 0x12, 0x14, 0x0a, 0x05, 0x65, 0x6d, 0x61, 0x69, 0x6c, 0x18, 0x02, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x05, 0x65, 0x6d, 0x61, 0x69, 0x6c, 0x12, 0x3a, 0x0a, 0x07, 0x61, 0x63, 0x63, + 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x20, 0x2e, 0x78, 0x72, 0x61, + 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x73, 0x65, 0x72, 0x69, 0x61, 0x6c, 0x2e, + 0x54, 0x79, 0x70, 0x65, 0x64, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x52, 0x07, 0x61, 0x63, + 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x42, 0x5e, 0x0a, 0x18, 0x63, 0x6f, 0x6d, 0x2e, 0x78, 0x72, 0x61, + 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, + 0x6c, 0x50, 0x01, 0x5a, 0x29, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, + 0x78, 0x74, 0x6c, 0x73, 0x2f, 0x78, 0x72, 0x61, 0x79, 0x2d, 0x63, 0x6f, 0x72, 0x65, 0x2f, 0x63, + 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0xaa, 0x02, + 0x14, 0x58, 0x72, 0x61, 0x79, 0x2e, 0x43, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x50, 0x72, 0x6f, + 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var (