github.com/xmplusdev/xmcore@v1.8.11-0.20240412132628-5518b55526af/proxy/shadowsocks_2022/inbound_multi.go (about)

     1  package shadowsocks_2022
     2  
     3  import (
     4  	"context"
     5  	"encoding/base64"
     6  	"strconv"
     7  	"strings"
     8  	"sync"
     9  
    10  	"github.com/sagernet/sing-shadowsocks/shadowaead_2022"
    11  	C "github.com/sagernet/sing/common"
    12  	A "github.com/sagernet/sing/common/auth"
    13  	B "github.com/sagernet/sing/common/buf"
    14  	"github.com/sagernet/sing/common/bufio"
    15  	E "github.com/sagernet/sing/common/exceptions"
    16  	M "github.com/sagernet/sing/common/metadata"
    17  	N "github.com/sagernet/sing/common/network"
    18  	"github.com/xmplusdev/xmcore/common"
    19  	"github.com/xmplusdev/xmcore/common/buf"
    20  	"github.com/xmplusdev/xmcore/common/log"
    21  	"github.com/xmplusdev/xmcore/common/net"
    22  	"github.com/xmplusdev/xmcore/common/protocol"
    23  	"github.com/xmplusdev/xmcore/common/session"
    24  	"github.com/xmplusdev/xmcore/common/singbridge"
    25  	"github.com/xmplusdev/xmcore/common/uuid"
    26  	"github.com/xmplusdev/xmcore/features/routing"
    27  	"github.com/xmplusdev/xmcore/transport/internet/stat"
    28  )
    29  
    30  func init() {
    31  	common.Must(common.RegisterConfig((*MultiUserServerConfig)(nil), func(ctx context.Context, config interface{}) (interface{}, error) {
    32  		return NewMultiServer(ctx, config.(*MultiUserServerConfig))
    33  	}))
    34  }
    35  
    36  type MultiUserInbound struct {
    37  	sync.Mutex
    38  	networks []net.Network
    39  	users    []*User
    40  	service  *shadowaead_2022.MultiService[int]
    41  }
    42  
    43  func NewMultiServer(ctx context.Context, config *MultiUserServerConfig) (*MultiUserInbound, error) {
    44  	networks := config.Network
    45  	if len(networks) == 0 {
    46  		networks = []net.Network{
    47  			net.Network_TCP,
    48  			net.Network_UDP,
    49  		}
    50  	}
    51  	inbound := &MultiUserInbound{
    52  		networks: networks,
    53  		users:    config.Users,
    54  	}
    55  	if config.Key == "" {
    56  		return nil, newError("missing key")
    57  	}
    58  	psk, err := base64.StdEncoding.DecodeString(config.Key)
    59  	if err != nil {
    60  		return nil, newError("parse config").Base(err)
    61  	}
    62  	service, err := shadowaead_2022.NewMultiService[int](config.Method, psk, 500, inbound, nil)
    63  	if err != nil {
    64  		return nil, newError("create service").Base(err)
    65  	}
    66  
    67  	for i, user := range config.Users {
    68  		if user.Email == "" {
    69  			u := uuid.New()
    70  			user.Email = "unnamed-user-" + strconv.Itoa(i) + "-" + u.String()
    71  		}
    72  	}
    73  	err = service.UpdateUsersWithPasswords(
    74  		C.MapIndexed(config.Users, func(index int, it *User) int { return index }),
    75  		C.Map(config.Users, func(it *User) string { return it.Key }),
    76  	)
    77  	if err != nil {
    78  		return nil, newError("create service").Base(err)
    79  	}
    80  
    81  	inbound.service = service
    82  	return inbound, nil
    83  }
    84  
    85  // AddUser implements proxy.UserManager.AddUser().
    86  func (i *MultiUserInbound) AddUser(ctx context.Context, u *protocol.MemoryUser) error {
    87  	i.Lock()
    88  	defer i.Unlock()
    89  
    90  	account := u.Account.(*MemoryAccount)
    91  	if account.Email != "" {
    92  		for idx := range i.users {
    93  			if i.users[idx].Email == account.Email {
    94  				return newError("User ", account.Email, " already exists.")
    95  			}
    96  		}
    97  	}
    98  	i.users = append(i.users, &User{
    99  		Key:   account.Key,
   100  		Email: account.Email,
   101  		Level: account.Level,
   102  	})
   103  
   104  	// sync to multi service
   105  	// Considering implements shadowsocks2022 in xray-core may have better performance.
   106  	i.service.UpdateUsersWithPasswords(
   107  		C.MapIndexed(i.users, func(index int, it *User) int { return index }),
   108  		C.Map(i.users, func(it *User) string { return it.Key }),
   109  	)
   110  
   111  	return nil
   112  }
   113  
   114  // RemoveUser implements proxy.UserManager.RemoveUser().
   115  func (i *MultiUserInbound) RemoveUser(ctx context.Context, email string) error {
   116  	if email == "" {
   117  		return newError("Email must not be empty.")
   118  	}
   119  
   120  	i.Lock()
   121  	defer i.Unlock()
   122  
   123  	idx := -1
   124  	for ii, u := range i.users {
   125  		if strings.EqualFold(u.Email, email) {
   126  			idx = ii
   127  			break
   128  		}
   129  	}
   130  
   131  	if idx == -1 {
   132  		return newError("User ", email, " not found.")
   133  	}
   134  
   135  	ulen := len(i.users)
   136  
   137  	i.users[idx] = i.users[ulen-1]
   138  	i.users[ulen-1] = nil
   139  	i.users = i.users[:ulen-1]
   140  
   141  	// sync to multi service
   142  	// Considering implements shadowsocks2022 in xray-core may have better performance.
   143  	i.service.UpdateUsersWithPasswords(
   144  		C.MapIndexed(i.users, func(index int, it *User) int { return index }),
   145  		C.Map(i.users, func(it *User) string { return it.Key }),
   146  	)
   147  
   148  	return nil
   149  }
   150  
   151  func (i *MultiUserInbound) Network() []net.Network {
   152  	return i.networks
   153  }
   154  
   155  func (i *MultiUserInbound) Process(ctx context.Context, network net.Network, connection stat.Connection, dispatcher routing.Dispatcher) error {
   156  	inbound := session.InboundFromContext(ctx)
   157  	inbound.Name = "shadowsocks-2022-multi"
   158  	inbound.SetCanSpliceCopy(3)
   159  
   160  	var metadata M.Metadata
   161  	if inbound.Source.IsValid() {
   162  		metadata.Source = M.ParseSocksaddr(inbound.Source.NetAddr())
   163  	}
   164  
   165  	ctx = session.ContextWithDispatcher(ctx, dispatcher)
   166  
   167  	if network == net.Network_TCP {
   168  		return singbridge.ReturnError(i.service.NewConnection(ctx, connection, metadata))
   169  	} else {
   170  		reader := buf.NewReader(connection)
   171  		pc := &natPacketConn{connection}
   172  		for {
   173  			mb, err := reader.ReadMultiBuffer()
   174  			if err != nil {
   175  				buf.ReleaseMulti(mb)
   176  				return singbridge.ReturnError(err)
   177  			}
   178  			for _, buffer := range mb {
   179  				packet := B.As(buffer.Bytes()).ToOwned()
   180  				buffer.Release()
   181  				err = i.service.NewPacket(ctx, pc, packet, metadata)
   182  				if err != nil {
   183  					packet.Release()
   184  					buf.ReleaseMulti(mb)
   185  					return err
   186  				}
   187  			}
   188  		}
   189  	}
   190  }
   191  
   192  func (i *MultiUserInbound) NewConnection(ctx context.Context, conn net.Conn, metadata M.Metadata) error {
   193  	inbound := session.InboundFromContext(ctx)
   194  	userInt, _ := A.UserFromContext[int](ctx)
   195  	user := i.users[userInt]
   196  	inbound.User = &protocol.MemoryUser{
   197  		Email: user.Email,
   198  		Level: uint32(user.Level),
   199  	}
   200  	ctx = log.ContextWithAccessMessage(ctx, &log.AccessMessage{
   201  		From:   metadata.Source,
   202  		To:     metadata.Destination,
   203  		Status: log.AccessAccepted,
   204  		Email:  user.Email,
   205  	})
   206  	newError("tunnelling request to tcp:", metadata.Destination).WriteToLog(session.ExportIDToError(ctx))
   207  	dispatcher := session.DispatcherFromContext(ctx)
   208  	destination := singbridge.ToDestination(metadata.Destination, net.Network_TCP)
   209  	if !destination.IsValid() {
   210  		return newError("invalid destination")
   211  	}
   212  
   213  	link, err := dispatcher.Dispatch(ctx, destination)
   214  	if err != nil {
   215  		return err
   216  	}
   217  	return singbridge.CopyConn(ctx, conn, link, conn)
   218  }
   219  
   220  func (i *MultiUserInbound) NewPacketConnection(ctx context.Context, conn N.PacketConn, metadata M.Metadata) error {
   221  	inbound := session.InboundFromContext(ctx)
   222  	userInt, _ := A.UserFromContext[int](ctx)
   223  	user := i.users[userInt]
   224  	inbound.User = &protocol.MemoryUser{
   225  		Email: user.Email,
   226  		Level: uint32(user.Level),
   227  	}
   228  	ctx = log.ContextWithAccessMessage(ctx, &log.AccessMessage{
   229  		From:   metadata.Source,
   230  		To:     metadata.Destination,
   231  		Status: log.AccessAccepted,
   232  		Email:  user.Email,
   233  	})
   234  	newError("tunnelling request to udp:", metadata.Destination).WriteToLog(session.ExportIDToError(ctx))
   235  	dispatcher := session.DispatcherFromContext(ctx)
   236  	destination := singbridge.ToDestination(metadata.Destination, net.Network_UDP)
   237  	link, err := dispatcher.Dispatch(ctx, destination)
   238  	if err != nil {
   239  		return err
   240  	}
   241  	outConn := &singbridge.PacketConnWrapper{
   242  		Reader: link.Reader,
   243  		Writer: link.Writer,
   244  		Dest:   destination,
   245  	}
   246  	return bufio.CopyPacketConn(ctx, conn, outConn)
   247  }
   248  
   249  func (i *MultiUserInbound) NewError(ctx context.Context, err error) {
   250  	if E.IsClosed(err) {
   251  		return
   252  	}
   253  	newError(err).AtWarning().WriteToLog()
   254  }