mirror of
https://github.com/XTLS/Xray-core.git
synced 2024-11-13 04:03:19 +00:00
!feat(vless): IP restriction
Beta, only works for vless for now and it's not perfect needs a lot of testing.
This commit is contained in:
parent
34b68518fd
commit
3d692eb208
|
@ -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()
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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 (
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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),
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue