Add new admin panel, failover, dns fallback, providers, limiters. Update XHTTP

This commit is contained in:
Sergei Maklagin
2026-05-11 00:59:35 +03:00
parent 652e0baf57
commit 3bd162ed6f
241 changed files with 36409 additions and 4086 deletions

View File

@@ -0,0 +1,109 @@
package client
import (
"context"
"net"
"sync"
"github.com/sagernet/sing-box/adapter"
boxService "github.com/sagernet/sing-box/adapter/service"
"github.com/sagernet/sing-box/common/dialer"
"github.com/sagernet/sing-box/common/tls"
C "github.com/sagernet/sing-box/constant"
"github.com/sagernet/sing-box/log"
"github.com/sagernet/sing-box/option"
pb "github.com/sagernet/sing-box/service/manager_api/grpc/manager"
"github.com/sagernet/sing/common"
M "github.com/sagernet/sing/common/metadata"
N "github.com/sagernet/sing/common/network"
"google.golang.org/grpc"
"google.golang.org/grpc/connectivity"
"google.golang.org/grpc/credentials"
"google.golang.org/grpc/credentials/insecure"
"google.golang.org/grpc/metadata"
)
type Client struct {
boxService.Adapter
ctx context.Context
logger log.ContextLogger
dialer N.Dialer
creds credentials.TransportCredentials
options option.ManagerAPIClientOptions
conn *grpc.ClientConn
mtx sync.Mutex
}
func NewClient(ctx context.Context, logger log.ContextLogger, tag string, options option.ManagerAPIClientOptions) (*Client, error) {
outboundDialer, err := dialer.New(ctx, options.DialerOptions, options.ServerIsDomain())
if err != nil {
return nil, err
}
creds := insecure.NewCredentials()
if options.TLS != nil {
tlsConfig, err := tls.NewClient(ctx, logger, options.Server, common.PtrValueOrDefault(options.TLS))
if err != nil {
return nil, err
}
creds = &tlsCreds{config: tlsConfig}
}
return &Client{
Adapter: boxService.NewAdapter(C.TypeManagerAPI, tag),
ctx: ctx,
logger: logger,
dialer: outboundDialer,
creds: creds,
options: options,
}, nil
}
func (s *Client) Start(stage adapter.StartStage) error { return nil }
func (s *Client) Close() error {
s.mtx.Lock()
defer s.mtx.Unlock()
if s.conn != nil {
return s.conn.Close()
}
return nil
}
func (s *Client) client() (pb.ManagerClient, error) {
conn, err := s.getConn()
if err != nil {
return nil, err
}
return pb.NewManagerClient(conn), nil
}
func (s *Client) getConn() (*grpc.ClientConn, error) {
s.mtx.Lock()
defer s.mtx.Unlock()
if s.conn != nil {
state := s.conn.GetState()
if state != connectivity.Shutdown && state != connectivity.TransientFailure {
return s.conn, nil
}
}
conn, err := grpc.NewClient(
s.options.ServerOptions.Build().String(),
grpc.WithContextDialer(func(ctx context.Context, addr string) (net.Conn, error) {
return s.dialer.DialContext(ctx, N.NetworkTCP, M.ParseSocksaddr(addr))
}),
grpc.WithTransportCredentials(s.creds),
)
if err != nil {
return nil, err
}
s.conn = conn
return conn, nil
}
func (s *Client) callContext() context.Context {
if s.options.APIKey == "" {
return s.ctx
}
return metadata.AppendToOutgoingContext(s.ctx, "authorization", s.options.APIKey)
}

View File

@@ -0,0 +1,146 @@
package client
import (
"time"
CM "github.com/sagernet/sing-box/service/manager/constant"
pb "github.com/sagernet/sing-box/service/manager_api/grpc/manager"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
)
func mapError(err error) error {
if err == nil {
return nil
}
if st, ok := status.FromError(err); ok && st.Code() == codes.NotFound {
return CM.ErrNotFound
}
return err
}
func toIntSlice(values []int32) []int {
out := make([]int, len(values))
for i, v := range values {
out[i] = int(v)
}
return out
}
func toInt32Slice(values []int) []int32 {
out := make([]int32, len(values))
for i, v := range values {
out[i] = int32(v)
}
return out
}
func timeFromNano(ns int64) time.Time { return time.Unix(0, ns) }
func convertSquad(v *pb.Squad) CM.Squad {
return CM.Squad{
ID: int(v.GetId()),
Name: v.GetName(),
CreatedAt: timeFromNano(v.GetCreatedAt()),
UpdatedAt: timeFromNano(v.GetUpdatedAt()),
}
}
func convertNode(v *pb.Node) CM.Node {
return CM.Node{
UUID: v.GetUuid(),
Name: v.GetName(),
SquadIDs: toIntSlice(v.GetSquadIds()),
CreatedAt: timeFromNano(v.GetCreatedAt()),
UpdatedAt: timeFromNano(v.GetUpdatedAt()),
}
}
func convertUser(v *pb.User) CM.User {
return CM.User{
ID: int(v.GetId()),
SquadIDs: toIntSlice(v.GetSquadIds()),
Username: v.GetUsername(),
Inbound: v.GetInbound(),
Type: v.GetType(),
UUID: v.GetUuid(),
Password: v.GetPassword(),
Secret: v.GetSecret(),
Flow: v.GetFlow(),
AlterID: int(v.GetAlterId()),
CreatedAt: timeFromNano(v.GetCreatedAt()),
UpdatedAt: timeFromNano(v.GetUpdatedAt()),
}
}
func convertBandwidthLimiter(v *pb.BandwidthLimiter) CM.BandwidthLimiter {
return CM.BandwidthLimiter{
ID: int(v.GetId()),
SquadIDs: toIntSlice(v.GetSquadIds()),
Username: v.GetUsername(),
Outbound: v.GetOutbound(),
Strategy: v.GetStrategy(),
ConnectionType: v.GetConnectionType(),
Mode: v.GetMode(),
FlowKeys: v.GetFlowKeys(),
Speed: v.GetSpeed(),
RawSpeed: v.GetRawSpeed(),
CreatedAt: timeFromNano(v.GetCreatedAt()),
UpdatedAt: timeFromNano(v.GetUpdatedAt()),
}
}
func convertTrafficLimiter(v *pb.TrafficLimiter) CM.TrafficLimiter {
return CM.TrafficLimiter{
ID: int(v.GetId()),
SquadIDs: toIntSlice(v.GetSquadIds()),
Username: v.GetUsername(),
Outbound: v.GetOutbound(),
Strategy: v.GetStrategy(),
Mode: v.GetMode(),
RawUsed: v.GetRawUsed(),
Quota: v.GetQuota(),
RawQuota: v.GetRawQuota(),
CreatedAt: timeFromNano(v.GetCreatedAt()),
UpdatedAt: timeFromNano(v.GetUpdatedAt()),
}
}
func convertConnectionLimiter(v *pb.ConnectionLimiter) CM.ConnectionLimiter {
return CM.ConnectionLimiter{
ID: int(v.GetId()),
SquadIDs: toIntSlice(v.GetSquadIds()),
Username: v.GetUsername(),
Outbound: v.GetOutbound(),
Strategy: v.GetStrategy(),
ConnectionType: v.GetConnectionType(),
LockType: v.GetLockType(),
Count: v.GetCount(),
CreatedAt: timeFromNano(v.GetCreatedAt()),
UpdatedAt: timeFromNano(v.GetUpdatedAt()),
}
}
func convertRateLimiter(v *pb.RateLimiter) CM.RateLimiter {
return CM.RateLimiter{
ID: int(v.GetId()),
SquadIDs: toIntSlice(v.GetSquadIds()),
Username: v.GetUsername(),
Outbound: v.GetOutbound(),
Strategy: v.GetStrategy(),
ConnectionType: v.GetConnectionType(),
Count: v.GetCount(),
Interval: v.GetInterval(),
CreatedAt: timeFromNano(v.GetCreatedAt()),
UpdatedAt: timeFromNano(v.GetUpdatedAt()),
}
}
func convertFilters(filters map[string][]string) *pb.Filters {
values := make(map[string]*pb.StringList, len(filters))
for k, v := range filters {
values[k] = &pb.StringList{Values: append([]string(nil), v...)}
}
return &pb.Filters{Values: values}
}

View File

@@ -0,0 +1,658 @@
package client
import (
CM "github.com/sagernet/sing-box/service/manager/constant"
pb "github.com/sagernet/sing-box/service/manager_api/grpc/manager"
E "github.com/sagernet/sing/common/exceptions"
)
var _ CM.Manager = (*Client)(nil)
func (s *Client) CreateSquad(in CM.SquadCreate) (CM.Squad, error) {
c, err := s.client()
if err != nil {
return CM.Squad{}, err
}
reply, err := c.CreateSquad(s.callContext(), &pb.SquadCreate{Name: in.Name})
if err != nil {
return CM.Squad{}, mapError(err)
}
return convertSquad(reply), nil
}
func (s *Client) GetSquads(filters map[string][]string) ([]CM.Squad, error) {
c, err := s.client()
if err != nil {
return nil, err
}
reply, err := c.GetSquads(s.callContext(), convertFilters(filters))
if err != nil {
return nil, mapError(err)
}
out := make([]CM.Squad, len(reply.GetValues()))
for i, v := range reply.GetValues() {
out[i] = convertSquad(v)
}
return out, nil
}
func (s *Client) GetSquadsCount(filters map[string][]string) (int, error) {
c, err := s.client()
if err != nil {
return 0, err
}
reply, err := c.GetSquadsCount(s.callContext(), convertFilters(filters))
if err != nil {
return 0, mapError(err)
}
return int(reply.GetCount()), nil
}
func (s *Client) GetSquad(id int) (CM.Squad, error) {
c, err := s.client()
if err != nil {
return CM.Squad{}, err
}
reply, err := c.GetSquad(s.callContext(), &pb.IdRequest{Id: int32(id)})
if err != nil {
return CM.Squad{}, mapError(err)
}
return convertSquad(reply), nil
}
func (s *Client) UpdateSquad(id int, in CM.SquadUpdate) (CM.Squad, error) {
c, err := s.client()
if err != nil {
return CM.Squad{}, err
}
reply, err := c.UpdateSquad(s.callContext(), &pb.SquadUpdateRequest{
Id: int32(id),
Update: &pb.SquadUpdate{Name: in.Name},
})
if err != nil {
return CM.Squad{}, mapError(err)
}
return convertSquad(reply), nil
}
func (s *Client) DeleteSquad(id int) (CM.Squad, error) {
c, err := s.client()
if err != nil {
return CM.Squad{}, err
}
reply, err := c.DeleteSquad(s.callContext(), &pb.IdRequest{Id: int32(id)})
if err != nil {
return CM.Squad{}, mapError(err)
}
return convertSquad(reply), nil
}
func (s *Client) CreateNode(in CM.NodeCreate) (CM.Node, error) {
c, err := s.client()
if err != nil {
return CM.Node{}, err
}
reply, err := c.CreateNode(s.callContext(), &pb.NodeCreate{
Uuid: in.UUID,
Name: in.Name,
SquadIds: toInt32Slice(in.SquadIDs),
})
if err != nil {
return CM.Node{}, mapError(err)
}
return convertNode(reply), nil
}
func (s *Client) GetNodes(filters map[string][]string) ([]CM.Node, error) {
c, err := s.client()
if err != nil {
return nil, err
}
reply, err := c.GetNodes(s.callContext(), convertFilters(filters))
if err != nil {
return nil, mapError(err)
}
out := make([]CM.Node, len(reply.GetValues()))
for i, v := range reply.GetValues() {
out[i] = convertNode(v)
}
return out, nil
}
func (s *Client) GetNodesCount(filters map[string][]string) (int, error) {
c, err := s.client()
if err != nil {
return 0, err
}
reply, err := c.GetNodesCount(s.callContext(), convertFilters(filters))
if err != nil {
return 0, mapError(err)
}
return int(reply.GetCount()), nil
}
func (s *Client) GetNode(uuid string) (CM.Node, error) {
c, err := s.client()
if err != nil {
return CM.Node{}, err
}
reply, err := c.GetNode(s.callContext(), &pb.UuidRequest{Uuid: uuid})
if err != nil {
return CM.Node{}, mapError(err)
}
return convertNode(reply), nil
}
func (s *Client) GetNodeStatus(uuid string) (string, error) {
c, err := s.client()
if err != nil {
return "", err
}
reply, err := c.GetNodeStatus(s.callContext(), &pb.UuidRequest{Uuid: uuid})
if err != nil {
return "", mapError(err)
}
return reply.GetStatus(), nil
}
func (s *Client) UpdateNode(uuid string, in CM.NodeUpdate) (CM.Node, error) {
c, err := s.client()
if err != nil {
return CM.Node{}, err
}
reply, err := c.UpdateNode(s.callContext(), &pb.NodeUpdateRequest{
Uuid: uuid,
Update: &pb.NodeUpdate{Name: in.Name},
})
if err != nil {
return CM.Node{}, mapError(err)
}
return convertNode(reply), nil
}
func (s *Client) DeleteNode(uuid string) (CM.Node, error) {
c, err := s.client()
if err != nil {
return CM.Node{}, err
}
reply, err := c.DeleteNode(s.callContext(), &pb.UuidRequest{Uuid: uuid})
if err != nil {
return CM.Node{}, mapError(err)
}
return convertNode(reply), nil
}
func (s *Client) CreateUser(in CM.UserCreate) (CM.User, error) {
c, err := s.client()
if err != nil {
return CM.User{}, err
}
reply, err := c.CreateUser(s.callContext(), &pb.UserCreate{
SquadIds: toInt32Slice(in.SquadIDs),
Username: in.Username,
Inbound: in.Inbound,
Type: in.Type,
Uuid: in.UUID,
Password: in.Password,
Secret: in.Secret,
Flow: in.Flow,
AlterId: int32(in.AlterID),
})
if err != nil {
return CM.User{}, mapError(err)
}
return convertUser(reply), nil
}
func (s *Client) GetUsers(filters map[string][]string) ([]CM.User, error) {
c, err := s.client()
if err != nil {
return nil, err
}
reply, err := c.GetUsers(s.callContext(), convertFilters(filters))
if err != nil {
return nil, mapError(err)
}
out := make([]CM.User, len(reply.GetValues()))
for i, v := range reply.GetValues() {
out[i] = convertUser(v)
}
return out, nil
}
func (s *Client) GetUsersCount(filters map[string][]string) (int, error) {
c, err := s.client()
if err != nil {
return 0, err
}
reply, err := c.GetUsersCount(s.callContext(), convertFilters(filters))
if err != nil {
return 0, mapError(err)
}
return int(reply.GetCount()), nil
}
func (s *Client) GetUser(id int) (CM.User, error) {
c, err := s.client()
if err != nil {
return CM.User{}, err
}
reply, err := c.GetUser(s.callContext(), &pb.IdRequest{Id: int32(id)})
if err != nil {
return CM.User{}, mapError(err)
}
return convertUser(reply), nil
}
func (s *Client) UpdateUser(id int, in CM.UserUpdate) (CM.User, error) {
c, err := s.client()
if err != nil {
return CM.User{}, err
}
reply, err := c.UpdateUser(s.callContext(), &pb.UserUpdateRequest{
Id: int32(id),
Update: &pb.UserUpdate{
Uuid: in.UUID,
Password: in.Password,
Secret: in.Secret,
Flow: in.Flow,
AlterId: int32(in.AlterID),
},
})
if err != nil {
return CM.User{}, mapError(err)
}
return convertUser(reply), nil
}
func (s *Client) DeleteUser(id int) (CM.User, error) {
c, err := s.client()
if err != nil {
return CM.User{}, err
}
reply, err := c.DeleteUser(s.callContext(), &pb.IdRequest{Id: int32(id)})
if err != nil {
return CM.User{}, mapError(err)
}
return convertUser(reply), nil
}
func (s *Client) CreateBandwidthLimiter(in CM.BandwidthLimiterCreate) (CM.BandwidthLimiter, error) {
c, err := s.client()
if err != nil {
return CM.BandwidthLimiter{}, err
}
reply, err := c.CreateBandwidthLimiter(s.callContext(), &pb.BandwidthLimiterCreate{
SquadIds: toInt32Slice(in.SquadIDs),
Username: in.Username,
Outbound: in.Outbound,
Strategy: in.Strategy,
ConnectionType: in.ConnectionType,
Mode: in.Mode,
FlowKeys: in.FlowKeys,
Speed: in.Speed,
})
if err != nil {
return CM.BandwidthLimiter{}, mapError(err)
}
return convertBandwidthLimiter(reply), nil
}
func (s *Client) GetBandwidthLimiters(filters map[string][]string) ([]CM.BandwidthLimiter, error) {
c, err := s.client()
if err != nil {
return nil, err
}
reply, err := c.GetBandwidthLimiters(s.callContext(), convertFilters(filters))
if err != nil {
return nil, mapError(err)
}
out := make([]CM.BandwidthLimiter, len(reply.GetValues()))
for i, v := range reply.GetValues() {
out[i] = convertBandwidthLimiter(v)
}
return out, nil
}
func (s *Client) GetBandwidthLimitersCount(filters map[string][]string) (int, error) {
c, err := s.client()
if err != nil {
return 0, err
}
reply, err := c.GetBandwidthLimitersCount(s.callContext(), convertFilters(filters))
if err != nil {
return 0, mapError(err)
}
return int(reply.GetCount()), nil
}
func (s *Client) GetBandwidthLimiter(id int) (CM.BandwidthLimiter, error) {
c, err := s.client()
if err != nil {
return CM.BandwidthLimiter{}, err
}
reply, err := c.GetBandwidthLimiter(s.callContext(), &pb.IdRequest{Id: int32(id)})
if err != nil {
return CM.BandwidthLimiter{}, mapError(err)
}
return convertBandwidthLimiter(reply), nil
}
func (s *Client) UpdateBandwidthLimiter(id int, in CM.BandwidthLimiterUpdate) (CM.BandwidthLimiter, error) {
c, err := s.client()
if err != nil {
return CM.BandwidthLimiter{}, err
}
reply, err := c.UpdateBandwidthLimiter(s.callContext(), &pb.BandwidthLimiterUpdateRequest{
Id: int32(id),
Update: &pb.BandwidthLimiterUpdate{
Username: in.Username,
Outbound: in.Outbound,
Strategy: in.Strategy,
ConnectionType: in.ConnectionType,
Mode: in.Mode,
FlowKeys: in.FlowKeys,
Speed: in.Speed,
},
})
if err != nil {
return CM.BandwidthLimiter{}, mapError(err)
}
return convertBandwidthLimiter(reply), nil
}
func (s *Client) DeleteBandwidthLimiter(id int) (CM.BandwidthLimiter, error) {
c, err := s.client()
if err != nil {
return CM.BandwidthLimiter{}, err
}
reply, err := c.DeleteBandwidthLimiter(s.callContext(), &pb.IdRequest{Id: int32(id)})
if err != nil {
return CM.BandwidthLimiter{}, mapError(err)
}
return convertBandwidthLimiter(reply), nil
}
func (s *Client) CreateTrafficLimiter(in CM.TrafficLimiterCreate) (CM.TrafficLimiter, error) {
c, err := s.client()
if err != nil {
return CM.TrafficLimiter{}, err
}
reply, err := c.CreateTrafficLimiter(s.callContext(), &pb.TrafficLimiterCreate{
SquadIds: toInt32Slice(in.SquadIDs),
Username: in.Username,
Outbound: in.Outbound,
Strategy: in.Strategy,
Mode: in.Mode,
Quota: in.Quota,
})
if err != nil {
return CM.TrafficLimiter{}, mapError(err)
}
return convertTrafficLimiter(reply), nil
}
func (s *Client) GetTrafficLimiters(filters map[string][]string) ([]CM.TrafficLimiter, error) {
c, err := s.client()
if err != nil {
return nil, err
}
reply, err := c.GetTrafficLimiters(s.callContext(), convertFilters(filters))
if err != nil {
return nil, mapError(err)
}
out := make([]CM.TrafficLimiter, len(reply.GetValues()))
for i, v := range reply.GetValues() {
out[i] = convertTrafficLimiter(v)
}
return out, nil
}
func (s *Client) GetTrafficLimitersCount(filters map[string][]string) (int, error) {
c, err := s.client()
if err != nil {
return 0, err
}
reply, err := c.GetTrafficLimitersCount(s.callContext(), convertFilters(filters))
if err != nil {
return 0, mapError(err)
}
return int(reply.GetCount()), nil
}
func (s *Client) GetTrafficLimiter(id int) (CM.TrafficLimiter, error) {
c, err := s.client()
if err != nil {
return CM.TrafficLimiter{}, err
}
reply, err := c.GetTrafficLimiter(s.callContext(), &pb.IdRequest{Id: int32(id)})
if err != nil {
return CM.TrafficLimiter{}, mapError(err)
}
return convertTrafficLimiter(reply), nil
}
func (s *Client) UpdateTrafficLimiter(id int, in CM.TrafficLimiterUpdate) (CM.TrafficLimiter, error) {
c, err := s.client()
if err != nil {
return CM.TrafficLimiter{}, err
}
reply, err := c.UpdateTrafficLimiter(s.callContext(), &pb.TrafficLimiterUpdateRequest{
Id: int32(id),
Update: &pb.TrafficLimiterUpdate{
Username: in.Username,
Outbound: in.Outbound,
Strategy: in.Strategy,
Mode: in.Mode,
Quota: in.Quota,
},
})
if err != nil {
return CM.TrafficLimiter{}, mapError(err)
}
return convertTrafficLimiter(reply), nil
}
func (s *Client) UpdateTrafficLimiterUsed(_ int, _ uint64) (CM.TrafficLimiter, error) {
return CM.TrafficLimiter{}, E.New("UpdateTrafficLimiterUsed not implemented over gRPC")
}
func (s *Client) DeleteTrafficLimiter(id int) (CM.TrafficLimiter, error) {
c, err := s.client()
if err != nil {
return CM.TrafficLimiter{}, err
}
reply, err := c.DeleteTrafficLimiter(s.callContext(), &pb.IdRequest{Id: int32(id)})
if err != nil {
return CM.TrafficLimiter{}, mapError(err)
}
return convertTrafficLimiter(reply), nil
}
func (s *Client) CreateConnectionLimiter(in CM.ConnectionLimiterCreate) (CM.ConnectionLimiter, error) {
c, err := s.client()
if err != nil {
return CM.ConnectionLimiter{}, err
}
reply, err := c.CreateConnectionLimiter(s.callContext(), &pb.ConnectionLimiterCreate{
SquadIds: toInt32Slice(in.SquadIDs),
Username: in.Username,
Outbound: in.Outbound,
Strategy: in.Strategy,
ConnectionType: in.ConnectionType,
LockType: in.LockType,
Count: in.Count,
})
if err != nil {
return CM.ConnectionLimiter{}, mapError(err)
}
return convertConnectionLimiter(reply), nil
}
func (s *Client) GetConnectionLimiters(filters map[string][]string) ([]CM.ConnectionLimiter, error) {
c, err := s.client()
if err != nil {
return nil, err
}
reply, err := c.GetConnectionLimiters(s.callContext(), convertFilters(filters))
if err != nil {
return nil, mapError(err)
}
out := make([]CM.ConnectionLimiter, len(reply.GetValues()))
for i, v := range reply.GetValues() {
out[i] = convertConnectionLimiter(v)
}
return out, nil
}
func (s *Client) GetConnectionLimitersCount(filters map[string][]string) (int, error) {
c, err := s.client()
if err != nil {
return 0, err
}
reply, err := c.GetConnectionLimitersCount(s.callContext(), convertFilters(filters))
if err != nil {
return 0, mapError(err)
}
return int(reply.GetCount()), nil
}
func (s *Client) GetConnectionLimiter(id int) (CM.ConnectionLimiter, error) {
c, err := s.client()
if err != nil {
return CM.ConnectionLimiter{}, err
}
reply, err := c.GetConnectionLimiter(s.callContext(), &pb.IdRequest{Id: int32(id)})
if err != nil {
return CM.ConnectionLimiter{}, mapError(err)
}
return convertConnectionLimiter(reply), nil
}
func (s *Client) UpdateConnectionLimiter(id int, in CM.ConnectionLimiterUpdate) (CM.ConnectionLimiter, error) {
c, err := s.client()
if err != nil {
return CM.ConnectionLimiter{}, err
}
reply, err := c.UpdateConnectionLimiter(s.callContext(), &pb.ConnectionLimiterUpdateRequest{
Id: int32(id),
Update: &pb.ConnectionLimiterUpdate{
Username: in.Username,
Outbound: in.Outbound,
Strategy: in.Strategy,
ConnectionType: in.ConnectionType,
LockType: in.LockType,
Count: in.Count,
},
})
if err != nil {
return CM.ConnectionLimiter{}, mapError(err)
}
return convertConnectionLimiter(reply), nil
}
func (s *Client) DeleteConnectionLimiter(id int) (CM.ConnectionLimiter, error) {
c, err := s.client()
if err != nil {
return CM.ConnectionLimiter{}, err
}
reply, err := c.DeleteConnectionLimiter(s.callContext(), &pb.IdRequest{Id: int32(id)})
if err != nil {
return CM.ConnectionLimiter{}, mapError(err)
}
return convertConnectionLimiter(reply), nil
}
func (s *Client) CreateRateLimiter(in CM.RateLimiterCreate) (CM.RateLimiter, error) {
c, err := s.client()
if err != nil {
return CM.RateLimiter{}, err
}
reply, err := c.CreateRateLimiter(s.callContext(), &pb.RateLimiterCreate{
SquadIds: toInt32Slice(in.SquadIDs),
Username: in.Username,
Outbound: in.Outbound,
Strategy: in.Strategy,
ConnectionType: in.ConnectionType,
Count: in.Count,
Interval: in.Interval,
})
if err != nil {
return CM.RateLimiter{}, mapError(err)
}
return convertRateLimiter(reply), nil
}
func (s *Client) GetRateLimiters(filters map[string][]string) ([]CM.RateLimiter, error) {
c, err := s.client()
if err != nil {
return nil, err
}
reply, err := c.GetRateLimiters(s.callContext(), convertFilters(filters))
if err != nil {
return nil, mapError(err)
}
out := make([]CM.RateLimiter, len(reply.GetValues()))
for i, v := range reply.GetValues() {
out[i] = convertRateLimiter(v)
}
return out, nil
}
func (s *Client) GetRateLimitersCount(filters map[string][]string) (int, error) {
c, err := s.client()
if err != nil {
return 0, err
}
reply, err := c.GetRateLimitersCount(s.callContext(), convertFilters(filters))
if err != nil {
return 0, mapError(err)
}
return int(reply.GetCount()), nil
}
func (s *Client) GetRateLimiter(id int) (CM.RateLimiter, error) {
c, err := s.client()
if err != nil {
return CM.RateLimiter{}, err
}
reply, err := c.GetRateLimiter(s.callContext(), &pb.IdRequest{Id: int32(id)})
if err != nil {
return CM.RateLimiter{}, mapError(err)
}
return convertRateLimiter(reply), nil
}
func (s *Client) UpdateRateLimiter(id int, in CM.RateLimiterUpdate) (CM.RateLimiter, error) {
c, err := s.client()
if err != nil {
return CM.RateLimiter{}, err
}
reply, err := c.UpdateRateLimiter(s.callContext(), &pb.RateLimiterUpdateRequest{
Id: int32(id),
Update: &pb.RateLimiterUpdate{
Username: in.Username,
Outbound: in.Outbound,
Strategy: in.Strategy,
ConnectionType: in.ConnectionType,
Count: in.Count,
Interval: in.Interval,
},
})
if err != nil {
return CM.RateLimiter{}, mapError(err)
}
return convertRateLimiter(reply), nil
}
func (s *Client) DeleteRateLimiter(id int) (CM.RateLimiter, error) {
c, err := s.client()
if err != nil {
return CM.RateLimiter{}, err
}
reply, err := c.DeleteRateLimiter(s.callContext(), &pb.IdRequest{Id: int32(id)})
if err != nil {
return CM.RateLimiter{}, mapError(err)
}
return convertRateLimiter(reply), nil
}

View File

@@ -0,0 +1,43 @@
package client
import (
"context"
"net"
"github.com/sagernet/sing-box/common/tls"
E "github.com/sagernet/sing/common/exceptions"
"google.golang.org/grpc/credentials"
)
type tlsCreds struct {
config tls.Config
}
func (c tlsCreds) Info() credentials.ProtocolInfo {
return credentials.ProtocolInfo{
SecurityProtocol: "tls",
SecurityVersion: "1.2",
ServerName: c.config.ServerName(),
}
}
func (c *tlsCreds) ClientHandshake(ctx context.Context, authority string, rawConn net.Conn) (net.Conn, credentials.AuthInfo, error) {
conn, err := tls.ClientHandshake(ctx, rawConn, c.config)
if err != nil {
return nil, nil, err
}
return conn, credentials.TLSInfo{State: conn.ConnectionState()}, err
}
func (c *tlsCreds) ServerHandshake(rawConn net.Conn) (net.Conn, credentials.AuthInfo, error) {
return nil, nil, E.New("not implemented")
}
func (c *tlsCreds) Clone() credentials.TransportCredentials {
return &tlsCreds{config: c.config.Clone()}
}
func (c *tlsCreds) OverrideServerName(serverNameOverride string) error {
c.config.SetServerName(serverNameOverride)
return nil
}