mirror of
https://github.com/SagerNet/sing-box.git
synced 2026-04-11 17:47:20 +10:00
tools: Tailscale status
This commit is contained in:
39
adapter/tailscale.go
Normal file
39
adapter/tailscale.go
Normal file
@@ -0,0 +1,39 @@
|
||||
package adapter
|
||||
|
||||
import "context"
|
||||
|
||||
type TailscaleStatusProvider interface {
|
||||
SubscribeTailscaleStatus(ctx context.Context, fn func(*TailscaleEndpointStatus)) error
|
||||
}
|
||||
|
||||
type TailscaleEndpointStatus struct {
|
||||
BackendState string
|
||||
AuthURL string
|
||||
NetworkName string
|
||||
MagicDNSSuffix string
|
||||
Self *TailscalePeer
|
||||
Users map[int64]*TailscaleUser
|
||||
Peers []*TailscalePeer
|
||||
}
|
||||
|
||||
type TailscalePeer struct {
|
||||
HostName string
|
||||
DNSName string
|
||||
OS string
|
||||
TailscaleIPs []string
|
||||
Online bool
|
||||
ExitNode bool
|
||||
ExitNodeOption bool
|
||||
Active bool
|
||||
RxBytes int64
|
||||
TxBytes int64
|
||||
UserID int64
|
||||
KeyExpiry int64
|
||||
}
|
||||
|
||||
type TailscaleUser struct {
|
||||
ID int64
|
||||
LoginName string
|
||||
DisplayName string
|
||||
ProfilePicURL string
|
||||
}
|
||||
@@ -6,12 +6,14 @@ import (
|
||||
"runtime"
|
||||
"sync"
|
||||
"time"
|
||||
"unsafe"
|
||||
|
||||
"github.com/sagernet/sing-box/adapter"
|
||||
"github.com/sagernet/sing-box/common/dialer"
|
||||
"github.com/sagernet/sing-box/common/networkquality"
|
||||
"github.com/sagernet/sing-box/common/stun"
|
||||
"github.com/sagernet/sing-box/common/urltest"
|
||||
C "github.com/sagernet/sing-box/constant"
|
||||
"github.com/sagernet/sing-box/experimental/clashapi"
|
||||
"github.com/sagernet/sing-box/experimental/clashapi/trafficontrol"
|
||||
"github.com/sagernet/sing-box/experimental/deprecated"
|
||||
@@ -707,7 +709,7 @@ func (s *StartedService) TriggerDebugCrash(ctx context.Context, request *DebugCr
|
||||
switch request.Type {
|
||||
case DebugCrashRequest_GO:
|
||||
time.AfterFunc(200*time.Millisecond, func() {
|
||||
panic("debug go crash")
|
||||
*(*int)(unsafe.Pointer(uintptr(0))) = 0
|
||||
})
|
||||
case DebugCrashRequest_NATIVE:
|
||||
err := s.handler.TriggerNativeCrash()
|
||||
@@ -1287,6 +1289,142 @@ func (s *StartedService) StartSTUNTest(
|
||||
})
|
||||
}
|
||||
|
||||
func (s *StartedService) SubscribeTailscaleStatus(
|
||||
_ *emptypb.Empty,
|
||||
server grpc.ServerStreamingServer[TailscaleStatusUpdate],
|
||||
) error {
|
||||
err := s.waitForStarted(server.Context())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
s.serviceAccess.RLock()
|
||||
boxService := s.instance
|
||||
s.serviceAccess.RUnlock()
|
||||
|
||||
endpointManager := service.FromContext[adapter.EndpointManager](boxService.ctx)
|
||||
if endpointManager == nil {
|
||||
return status.Error(codes.FailedPrecondition, "endpoint manager not available")
|
||||
}
|
||||
|
||||
type tailscaleEndpoint struct {
|
||||
tag string
|
||||
provider adapter.TailscaleStatusProvider
|
||||
}
|
||||
var endpoints []tailscaleEndpoint
|
||||
for _, endpoint := range endpointManager.Endpoints() {
|
||||
if endpoint.Type() != C.TypeTailscale {
|
||||
continue
|
||||
}
|
||||
provider, loaded := endpoint.(adapter.TailscaleStatusProvider)
|
||||
if !loaded {
|
||||
continue
|
||||
}
|
||||
endpoints = append(endpoints, tailscaleEndpoint{
|
||||
tag: endpoint.Tag(),
|
||||
provider: provider,
|
||||
})
|
||||
}
|
||||
if len(endpoints) == 0 {
|
||||
return status.Error(codes.NotFound, "no Tailscale endpoint found")
|
||||
}
|
||||
|
||||
type taggedStatus struct {
|
||||
tag string
|
||||
status *adapter.TailscaleEndpointStatus
|
||||
}
|
||||
updates := make(chan taggedStatus, len(endpoints))
|
||||
ctx, cancel := context.WithCancel(server.Context())
|
||||
defer cancel()
|
||||
|
||||
var waitGroup sync.WaitGroup
|
||||
for _, endpoint := range endpoints {
|
||||
waitGroup.Add(1)
|
||||
go func(tag string, provider adapter.TailscaleStatusProvider) {
|
||||
defer waitGroup.Done()
|
||||
_ = provider.SubscribeTailscaleStatus(ctx, func(endpointStatus *adapter.TailscaleEndpointStatus) {
|
||||
select {
|
||||
case updates <- taggedStatus{tag: tag, status: endpointStatus}:
|
||||
case <-ctx.Done():
|
||||
}
|
||||
})
|
||||
}(endpoint.tag, endpoint.provider)
|
||||
}
|
||||
|
||||
go func() {
|
||||
waitGroup.Wait()
|
||||
close(updates)
|
||||
}()
|
||||
|
||||
statuses := make(map[string]*adapter.TailscaleEndpointStatus, len(endpoints))
|
||||
for update := range updates {
|
||||
statuses[update.tag] = update.status
|
||||
protoEndpoints := make([]*TailscaleEndpointStatus, 0, len(statuses))
|
||||
for tag, endpointStatus := range statuses {
|
||||
protoEndpoints = append(protoEndpoints, tailscaleEndpointStatusToProto(tag, endpointStatus))
|
||||
}
|
||||
sendErr := server.Send(&TailscaleStatusUpdate{
|
||||
Endpoints: protoEndpoints,
|
||||
})
|
||||
if sendErr != nil {
|
||||
return sendErr
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func tailscaleEndpointStatusToProto(tag string, s *adapter.TailscaleEndpointStatus) *TailscaleEndpointStatus {
|
||||
userGroupMap := make(map[int64]*TailscaleUserGroup)
|
||||
for userID, user := range s.Users {
|
||||
userGroupMap[userID] = &TailscaleUserGroup{
|
||||
UserID: userID,
|
||||
LoginName: user.LoginName,
|
||||
DisplayName: user.DisplayName,
|
||||
ProfilePicURL: user.ProfilePicURL,
|
||||
}
|
||||
}
|
||||
for _, peer := range s.Peers {
|
||||
protoPeer := tailscalePeerToProto(peer)
|
||||
group, loaded := userGroupMap[peer.UserID]
|
||||
if !loaded {
|
||||
group = &TailscaleUserGroup{UserID: peer.UserID}
|
||||
userGroupMap[peer.UserID] = group
|
||||
}
|
||||
group.Peers = append(group.Peers, protoPeer)
|
||||
}
|
||||
userGroups := make([]*TailscaleUserGroup, 0, len(userGroupMap))
|
||||
for _, group := range userGroupMap {
|
||||
userGroups = append(userGroups, group)
|
||||
}
|
||||
result := &TailscaleEndpointStatus{
|
||||
EndpointTag: tag,
|
||||
BackendState: s.BackendState,
|
||||
AuthURL: s.AuthURL,
|
||||
NetworkName: s.NetworkName,
|
||||
MagicDNSSuffix: s.MagicDNSSuffix,
|
||||
UserGroups: userGroups,
|
||||
}
|
||||
if s.Self != nil {
|
||||
result.Self = tailscalePeerToProto(s.Self)
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
func tailscalePeerToProto(peer *adapter.TailscalePeer) *TailscalePeer {
|
||||
return &TailscalePeer{
|
||||
HostName: peer.HostName,
|
||||
DnsName: peer.DNSName,
|
||||
Os: peer.OS,
|
||||
TailscaleIPs: peer.TailscaleIPs,
|
||||
Online: peer.Online,
|
||||
ExitNode: peer.ExitNode,
|
||||
ExitNodeOption: peer.ExitNodeOption,
|
||||
Active: peer.Active,
|
||||
RxBytes: peer.RxBytes,
|
||||
TxBytes: peer.TxBytes,
|
||||
KeyExpiry: peer.KeyExpiry,
|
||||
}
|
||||
}
|
||||
|
||||
func (s *StartedService) mustEmbedUnimplementedStartedServiceServer() {
|
||||
}
|
||||
|
||||
|
||||
@@ -2248,6 +2248,342 @@ func (x *STUNTestProgress) GetNatTypeSupported() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
type TailscaleStatusUpdate struct {
|
||||
state protoimpl.MessageState `protogen:"open.v1"`
|
||||
Endpoints []*TailscaleEndpointStatus `protobuf:"bytes,1,rep,name=endpoints,proto3" json:"endpoints,omitempty"`
|
||||
unknownFields protoimpl.UnknownFields
|
||||
sizeCache protoimpl.SizeCache
|
||||
}
|
||||
|
||||
func (x *TailscaleStatusUpdate) Reset() {
|
||||
*x = TailscaleStatusUpdate{}
|
||||
mi := &file_daemon_started_service_proto_msgTypes[31]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
|
||||
func (x *TailscaleStatusUpdate) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*TailscaleStatusUpdate) ProtoMessage() {}
|
||||
|
||||
func (x *TailscaleStatusUpdate) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_daemon_started_service_proto_msgTypes[31]
|
||||
if x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
return ms
|
||||
}
|
||||
return mi.MessageOf(x)
|
||||
}
|
||||
|
||||
// Deprecated: Use TailscaleStatusUpdate.ProtoReflect.Descriptor instead.
|
||||
func (*TailscaleStatusUpdate) Descriptor() ([]byte, []int) {
|
||||
return file_daemon_started_service_proto_rawDescGZIP(), []int{31}
|
||||
}
|
||||
|
||||
func (x *TailscaleStatusUpdate) GetEndpoints() []*TailscaleEndpointStatus {
|
||||
if x != nil {
|
||||
return x.Endpoints
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type TailscaleEndpointStatus struct {
|
||||
state protoimpl.MessageState `protogen:"open.v1"`
|
||||
EndpointTag string `protobuf:"bytes,1,opt,name=endpointTag,proto3" json:"endpointTag,omitempty"`
|
||||
BackendState string `protobuf:"bytes,2,opt,name=backendState,proto3" json:"backendState,omitempty"`
|
||||
AuthURL string `protobuf:"bytes,3,opt,name=authURL,proto3" json:"authURL,omitempty"`
|
||||
NetworkName string `protobuf:"bytes,4,opt,name=networkName,proto3" json:"networkName,omitempty"`
|
||||
MagicDNSSuffix string `protobuf:"bytes,5,opt,name=magicDNSSuffix,proto3" json:"magicDNSSuffix,omitempty"`
|
||||
Self *TailscalePeer `protobuf:"bytes,6,opt,name=self,proto3" json:"self,omitempty"`
|
||||
UserGroups []*TailscaleUserGroup `protobuf:"bytes,7,rep,name=userGroups,proto3" json:"userGroups,omitempty"`
|
||||
unknownFields protoimpl.UnknownFields
|
||||
sizeCache protoimpl.SizeCache
|
||||
}
|
||||
|
||||
func (x *TailscaleEndpointStatus) Reset() {
|
||||
*x = TailscaleEndpointStatus{}
|
||||
mi := &file_daemon_started_service_proto_msgTypes[32]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
|
||||
func (x *TailscaleEndpointStatus) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*TailscaleEndpointStatus) ProtoMessage() {}
|
||||
|
||||
func (x *TailscaleEndpointStatus) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_daemon_started_service_proto_msgTypes[32]
|
||||
if x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
return ms
|
||||
}
|
||||
return mi.MessageOf(x)
|
||||
}
|
||||
|
||||
// Deprecated: Use TailscaleEndpointStatus.ProtoReflect.Descriptor instead.
|
||||
func (*TailscaleEndpointStatus) Descriptor() ([]byte, []int) {
|
||||
return file_daemon_started_service_proto_rawDescGZIP(), []int{32}
|
||||
}
|
||||
|
||||
func (x *TailscaleEndpointStatus) GetEndpointTag() string {
|
||||
if x != nil {
|
||||
return x.EndpointTag
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *TailscaleEndpointStatus) GetBackendState() string {
|
||||
if x != nil {
|
||||
return x.BackendState
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *TailscaleEndpointStatus) GetAuthURL() string {
|
||||
if x != nil {
|
||||
return x.AuthURL
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *TailscaleEndpointStatus) GetNetworkName() string {
|
||||
if x != nil {
|
||||
return x.NetworkName
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *TailscaleEndpointStatus) GetMagicDNSSuffix() string {
|
||||
if x != nil {
|
||||
return x.MagicDNSSuffix
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *TailscaleEndpointStatus) GetSelf() *TailscalePeer {
|
||||
if x != nil {
|
||||
return x.Self
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (x *TailscaleEndpointStatus) GetUserGroups() []*TailscaleUserGroup {
|
||||
if x != nil {
|
||||
return x.UserGroups
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type TailscaleUserGroup struct {
|
||||
state protoimpl.MessageState `protogen:"open.v1"`
|
||||
UserID int64 `protobuf:"varint,1,opt,name=userID,proto3" json:"userID,omitempty"`
|
||||
LoginName string `protobuf:"bytes,2,opt,name=loginName,proto3" json:"loginName,omitempty"`
|
||||
DisplayName string `protobuf:"bytes,3,opt,name=displayName,proto3" json:"displayName,omitempty"`
|
||||
ProfilePicURL string `protobuf:"bytes,4,opt,name=profilePicURL,proto3" json:"profilePicURL,omitempty"`
|
||||
Peers []*TailscalePeer `protobuf:"bytes,5,rep,name=peers,proto3" json:"peers,omitempty"`
|
||||
unknownFields protoimpl.UnknownFields
|
||||
sizeCache protoimpl.SizeCache
|
||||
}
|
||||
|
||||
func (x *TailscaleUserGroup) Reset() {
|
||||
*x = TailscaleUserGroup{}
|
||||
mi := &file_daemon_started_service_proto_msgTypes[33]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
|
||||
func (x *TailscaleUserGroup) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*TailscaleUserGroup) ProtoMessage() {}
|
||||
|
||||
func (x *TailscaleUserGroup) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_daemon_started_service_proto_msgTypes[33]
|
||||
if x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
return ms
|
||||
}
|
||||
return mi.MessageOf(x)
|
||||
}
|
||||
|
||||
// Deprecated: Use TailscaleUserGroup.ProtoReflect.Descriptor instead.
|
||||
func (*TailscaleUserGroup) Descriptor() ([]byte, []int) {
|
||||
return file_daemon_started_service_proto_rawDescGZIP(), []int{33}
|
||||
}
|
||||
|
||||
func (x *TailscaleUserGroup) GetUserID() int64 {
|
||||
if x != nil {
|
||||
return x.UserID
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func (x *TailscaleUserGroup) GetLoginName() string {
|
||||
if x != nil {
|
||||
return x.LoginName
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *TailscaleUserGroup) GetDisplayName() string {
|
||||
if x != nil {
|
||||
return x.DisplayName
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *TailscaleUserGroup) GetProfilePicURL() string {
|
||||
if x != nil {
|
||||
return x.ProfilePicURL
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *TailscaleUserGroup) GetPeers() []*TailscalePeer {
|
||||
if x != nil {
|
||||
return x.Peers
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type TailscalePeer struct {
|
||||
state protoimpl.MessageState `protogen:"open.v1"`
|
||||
HostName string `protobuf:"bytes,1,opt,name=hostName,proto3" json:"hostName,omitempty"`
|
||||
DnsName string `protobuf:"bytes,2,opt,name=dnsName,proto3" json:"dnsName,omitempty"`
|
||||
Os string `protobuf:"bytes,3,opt,name=os,proto3" json:"os,omitempty"`
|
||||
TailscaleIPs []string `protobuf:"bytes,4,rep,name=tailscaleIPs,proto3" json:"tailscaleIPs,omitempty"`
|
||||
Online bool `protobuf:"varint,5,opt,name=online,proto3" json:"online,omitempty"`
|
||||
ExitNode bool `protobuf:"varint,6,opt,name=exitNode,proto3" json:"exitNode,omitempty"`
|
||||
ExitNodeOption bool `protobuf:"varint,7,opt,name=exitNodeOption,proto3" json:"exitNodeOption,omitempty"`
|
||||
Active bool `protobuf:"varint,8,opt,name=active,proto3" json:"active,omitempty"`
|
||||
RxBytes int64 `protobuf:"varint,9,opt,name=rxBytes,proto3" json:"rxBytes,omitempty"`
|
||||
TxBytes int64 `protobuf:"varint,10,opt,name=txBytes,proto3" json:"txBytes,omitempty"`
|
||||
KeyExpiry int64 `protobuf:"varint,11,opt,name=keyExpiry,proto3" json:"keyExpiry,omitempty"`
|
||||
unknownFields protoimpl.UnknownFields
|
||||
sizeCache protoimpl.SizeCache
|
||||
}
|
||||
|
||||
func (x *TailscalePeer) Reset() {
|
||||
*x = TailscalePeer{}
|
||||
mi := &file_daemon_started_service_proto_msgTypes[34]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
|
||||
func (x *TailscalePeer) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*TailscalePeer) ProtoMessage() {}
|
||||
|
||||
func (x *TailscalePeer) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_daemon_started_service_proto_msgTypes[34]
|
||||
if x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
return ms
|
||||
}
|
||||
return mi.MessageOf(x)
|
||||
}
|
||||
|
||||
// Deprecated: Use TailscalePeer.ProtoReflect.Descriptor instead.
|
||||
func (*TailscalePeer) Descriptor() ([]byte, []int) {
|
||||
return file_daemon_started_service_proto_rawDescGZIP(), []int{34}
|
||||
}
|
||||
|
||||
func (x *TailscalePeer) GetHostName() string {
|
||||
if x != nil {
|
||||
return x.HostName
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *TailscalePeer) GetDnsName() string {
|
||||
if x != nil {
|
||||
return x.DnsName
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *TailscalePeer) GetOs() string {
|
||||
if x != nil {
|
||||
return x.Os
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *TailscalePeer) GetTailscaleIPs() []string {
|
||||
if x != nil {
|
||||
return x.TailscaleIPs
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (x *TailscalePeer) GetOnline() bool {
|
||||
if x != nil {
|
||||
return x.Online
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (x *TailscalePeer) GetExitNode() bool {
|
||||
if x != nil {
|
||||
return x.ExitNode
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (x *TailscalePeer) GetExitNodeOption() bool {
|
||||
if x != nil {
|
||||
return x.ExitNodeOption
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (x *TailscalePeer) GetActive() bool {
|
||||
if x != nil {
|
||||
return x.Active
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (x *TailscalePeer) GetRxBytes() int64 {
|
||||
if x != nil {
|
||||
return x.RxBytes
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func (x *TailscalePeer) GetTxBytes() int64 {
|
||||
if x != nil {
|
||||
return x.TxBytes
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func (x *TailscalePeer) GetKeyExpiry() int64 {
|
||||
if x != nil {
|
||||
return x.KeyExpiry
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
type Log_Message struct {
|
||||
state protoimpl.MessageState `protogen:"open.v1"`
|
||||
Level LogLevel `protobuf:"varint,1,opt,name=level,proto3,enum=daemon.LogLevel" json:"level,omitempty"`
|
||||
@@ -2258,7 +2594,7 @@ type Log_Message struct {
|
||||
|
||||
func (x *Log_Message) Reset() {
|
||||
*x = Log_Message{}
|
||||
mi := &file_daemon_started_service_proto_msgTypes[31]
|
||||
mi := &file_daemon_started_service_proto_msgTypes[35]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
@@ -2270,7 +2606,7 @@ func (x *Log_Message) String() string {
|
||||
func (*Log_Message) ProtoMessage() {}
|
||||
|
||||
func (x *Log_Message) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_daemon_started_service_proto_msgTypes[31]
|
||||
mi := &file_daemon_started_service_proto_msgTypes[35]
|
||||
if x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
@@ -2472,7 +2808,38 @@ const file_daemon_started_service_proto_rawDesc = "" +
|
||||
"\fnatFiltering\x18\x05 \x01(\x05R\fnatFiltering\x12\x18\n" +
|
||||
"\aisFinal\x18\x06 \x01(\bR\aisFinal\x12\x14\n" +
|
||||
"\x05error\x18\a \x01(\tR\x05error\x12*\n" +
|
||||
"\x10natTypeSupported\x18\b \x01(\bR\x10natTypeSupported*U\n" +
|
||||
"\x10natTypeSupported\x18\b \x01(\bR\x10natTypeSupported\"V\n" +
|
||||
"\x15TailscaleStatusUpdate\x12=\n" +
|
||||
"\tendpoints\x18\x01 \x03(\v2\x1f.daemon.TailscaleEndpointStatusR\tendpoints\"\xaa\x02\n" +
|
||||
"\x17TailscaleEndpointStatus\x12 \n" +
|
||||
"\vendpointTag\x18\x01 \x01(\tR\vendpointTag\x12\"\n" +
|
||||
"\fbackendState\x18\x02 \x01(\tR\fbackendState\x12\x18\n" +
|
||||
"\aauthURL\x18\x03 \x01(\tR\aauthURL\x12 \n" +
|
||||
"\vnetworkName\x18\x04 \x01(\tR\vnetworkName\x12&\n" +
|
||||
"\x0emagicDNSSuffix\x18\x05 \x01(\tR\x0emagicDNSSuffix\x12)\n" +
|
||||
"\x04self\x18\x06 \x01(\v2\x15.daemon.TailscalePeerR\x04self\x12:\n" +
|
||||
"\n" +
|
||||
"userGroups\x18\a \x03(\v2\x1a.daemon.TailscaleUserGroupR\n" +
|
||||
"userGroups\"\xbf\x01\n" +
|
||||
"\x12TailscaleUserGroup\x12\x16\n" +
|
||||
"\x06userID\x18\x01 \x01(\x03R\x06userID\x12\x1c\n" +
|
||||
"\tloginName\x18\x02 \x01(\tR\tloginName\x12 \n" +
|
||||
"\vdisplayName\x18\x03 \x01(\tR\vdisplayName\x12$\n" +
|
||||
"\rprofilePicURL\x18\x04 \x01(\tR\rprofilePicURL\x12+\n" +
|
||||
"\x05peers\x18\x05 \x03(\v2\x15.daemon.TailscalePeerR\x05peers\"\xbf\x02\n" +
|
||||
"\rTailscalePeer\x12\x1a\n" +
|
||||
"\bhostName\x18\x01 \x01(\tR\bhostName\x12\x18\n" +
|
||||
"\adnsName\x18\x02 \x01(\tR\adnsName\x12\x0e\n" +
|
||||
"\x02os\x18\x03 \x01(\tR\x02os\x12\"\n" +
|
||||
"\ftailscaleIPs\x18\x04 \x03(\tR\ftailscaleIPs\x12\x16\n" +
|
||||
"\x06online\x18\x05 \x01(\bR\x06online\x12\x1a\n" +
|
||||
"\bexitNode\x18\x06 \x01(\bR\bexitNode\x12&\n" +
|
||||
"\x0eexitNodeOption\x18\a \x01(\bR\x0eexitNodeOption\x12\x16\n" +
|
||||
"\x06active\x18\b \x01(\bR\x06active\x12\x18\n" +
|
||||
"\arxBytes\x18\t \x01(\x03R\arxBytes\x12\x18\n" +
|
||||
"\atxBytes\x18\n" +
|
||||
" \x01(\x03R\atxBytes\x12\x1c\n" +
|
||||
"\tkeyExpiry\x18\v \x01(\x03R\tkeyExpiry*U\n" +
|
||||
"\bLogLevel\x12\t\n" +
|
||||
"\x05PANIC\x10\x00\x12\t\n" +
|
||||
"\x05FATAL\x10\x01\x12\t\n" +
|
||||
@@ -2484,7 +2851,7 @@ const file_daemon_started_service_proto_rawDesc = "" +
|
||||
"\x13ConnectionEventType\x12\x18\n" +
|
||||
"\x14CONNECTION_EVENT_NEW\x10\x00\x12\x1b\n" +
|
||||
"\x17CONNECTION_EVENT_UPDATE\x10\x01\x12\x1b\n" +
|
||||
"\x17CONNECTION_EVENT_CLOSED\x10\x022\xac\x0f\n" +
|
||||
"\x17CONNECTION_EVENT_CLOSED\x10\x022\x83\x10\n" +
|
||||
"\x0eStartedService\x12=\n" +
|
||||
"\vStopService\x12\x16.google.protobuf.Empty\x1a\x16.google.protobuf.Empty\x12?\n" +
|
||||
"\rReloadService\x12\x16.google.protobuf.Empty\x1a\x16.google.protobuf.Empty\x12K\n" +
|
||||
@@ -2512,7 +2879,8 @@ const file_daemon_started_service_proto_rawDesc = "" +
|
||||
"\rListOutbounds\x12\x16.google.protobuf.Empty\x1a\x14.daemon.OutboundList\"\x00\x12F\n" +
|
||||
"\x12SubscribeOutbounds\x12\x16.google.protobuf.Empty\x1a\x14.daemon.OutboundList\"\x000\x01\x12d\n" +
|
||||
"\x17StartNetworkQualityTest\x12!.daemon.NetworkQualityTestRequest\x1a\".daemon.NetworkQualityTestProgress\"\x000\x01\x12F\n" +
|
||||
"\rStartSTUNTest\x12\x17.daemon.STUNTestRequest\x1a\x18.daemon.STUNTestProgress\"\x000\x01B%Z#github.com/sagernet/sing-box/daemonb\x06proto3"
|
||||
"\rStartSTUNTest\x12\x17.daemon.STUNTestRequest\x1a\x18.daemon.STUNTestProgress\"\x000\x01\x12U\n" +
|
||||
"\x18SubscribeTailscaleStatus\x12\x16.google.protobuf.Empty\x1a\x1d.daemon.TailscaleStatusUpdate\"\x000\x01B%Z#github.com/sagernet/sing-box/daemonb\x06proto3"
|
||||
|
||||
var (
|
||||
file_daemon_started_service_proto_rawDescOnce sync.Once
|
||||
@@ -2528,7 +2896,7 @@ func file_daemon_started_service_proto_rawDescGZIP() []byte {
|
||||
|
||||
var (
|
||||
file_daemon_started_service_proto_enumTypes = make([]protoimpl.EnumInfo, 4)
|
||||
file_daemon_started_service_proto_msgTypes = make([]protoimpl.MessageInfo, 32)
|
||||
file_daemon_started_service_proto_msgTypes = make([]protoimpl.MessageInfo, 36)
|
||||
file_daemon_started_service_proto_goTypes = []any{
|
||||
(LogLevel)(0), // 0: daemon.LogLevel
|
||||
(ConnectionEventType)(0), // 1: daemon.ConnectionEventType
|
||||
@@ -2565,14 +2933,18 @@ var (
|
||||
(*NetworkQualityTestProgress)(nil), // 32: daemon.NetworkQualityTestProgress
|
||||
(*STUNTestRequest)(nil), // 33: daemon.STUNTestRequest
|
||||
(*STUNTestProgress)(nil), // 34: daemon.STUNTestProgress
|
||||
(*Log_Message)(nil), // 35: daemon.Log.Message
|
||||
(*emptypb.Empty)(nil), // 36: google.protobuf.Empty
|
||||
(*TailscaleStatusUpdate)(nil), // 35: daemon.TailscaleStatusUpdate
|
||||
(*TailscaleEndpointStatus)(nil), // 36: daemon.TailscaleEndpointStatus
|
||||
(*TailscaleUserGroup)(nil), // 37: daemon.TailscaleUserGroup
|
||||
(*TailscalePeer)(nil), // 38: daemon.TailscalePeer
|
||||
(*Log_Message)(nil), // 39: daemon.Log.Message
|
||||
(*emptypb.Empty)(nil), // 40: google.protobuf.Empty
|
||||
}
|
||||
)
|
||||
|
||||
var file_daemon_started_service_proto_depIdxs = []int32{
|
||||
2, // 0: daemon.ServiceStatus.status:type_name -> daemon.ServiceStatus.Type
|
||||
35, // 1: daemon.Log.messages:type_name -> daemon.Log.Message
|
||||
39, // 1: daemon.Log.messages:type_name -> daemon.Log.Message
|
||||
0, // 2: daemon.DefaultLogLevel.level:type_name -> daemon.LogLevel
|
||||
11, // 3: daemon.Groups.group:type_name -> daemon.Group
|
||||
12, // 4: daemon.Group.items:type_name -> daemon.GroupItem
|
||||
@@ -2583,66 +2955,72 @@ var file_daemon_started_service_proto_depIdxs = []int32{
|
||||
25, // 9: daemon.Connection.processInfo:type_name -> daemon.ProcessInfo
|
||||
28, // 10: daemon.DeprecatedWarnings.warnings:type_name -> daemon.DeprecatedWarning
|
||||
12, // 11: daemon.OutboundList.outbounds:type_name -> daemon.GroupItem
|
||||
0, // 12: daemon.Log.Message.level:type_name -> daemon.LogLevel
|
||||
36, // 13: daemon.StartedService.StopService:input_type -> google.protobuf.Empty
|
||||
36, // 14: daemon.StartedService.ReloadService:input_type -> google.protobuf.Empty
|
||||
36, // 15: daemon.StartedService.SubscribeServiceStatus:input_type -> google.protobuf.Empty
|
||||
36, // 16: daemon.StartedService.SubscribeLog:input_type -> google.protobuf.Empty
|
||||
36, // 17: daemon.StartedService.GetDefaultLogLevel:input_type -> google.protobuf.Empty
|
||||
36, // 18: daemon.StartedService.ClearLogs:input_type -> google.protobuf.Empty
|
||||
6, // 19: daemon.StartedService.SubscribeStatus:input_type -> daemon.SubscribeStatusRequest
|
||||
36, // 20: daemon.StartedService.SubscribeGroups:input_type -> google.protobuf.Empty
|
||||
36, // 21: daemon.StartedService.GetClashModeStatus:input_type -> google.protobuf.Empty
|
||||
36, // 22: daemon.StartedService.SubscribeClashMode:input_type -> google.protobuf.Empty
|
||||
16, // 23: daemon.StartedService.SetClashMode:input_type -> daemon.ClashMode
|
||||
13, // 24: daemon.StartedService.URLTest:input_type -> daemon.URLTestRequest
|
||||
14, // 25: daemon.StartedService.SelectOutbound:input_type -> daemon.SelectOutboundRequest
|
||||
15, // 26: daemon.StartedService.SetGroupExpand:input_type -> daemon.SetGroupExpandRequest
|
||||
36, // 27: daemon.StartedService.GetSystemProxyStatus:input_type -> google.protobuf.Empty
|
||||
19, // 28: daemon.StartedService.SetSystemProxyEnabled:input_type -> daemon.SetSystemProxyEnabledRequest
|
||||
20, // 29: daemon.StartedService.TriggerDebugCrash:input_type -> daemon.DebugCrashRequest
|
||||
36, // 30: daemon.StartedService.TriggerOOMReport:input_type -> google.protobuf.Empty
|
||||
21, // 31: daemon.StartedService.SubscribeConnections:input_type -> daemon.SubscribeConnectionsRequest
|
||||
26, // 32: daemon.StartedService.CloseConnection:input_type -> daemon.CloseConnectionRequest
|
||||
36, // 33: daemon.StartedService.CloseAllConnections:input_type -> google.protobuf.Empty
|
||||
36, // 34: daemon.StartedService.GetDeprecatedWarnings:input_type -> google.protobuf.Empty
|
||||
36, // 35: daemon.StartedService.GetStartedAt:input_type -> google.protobuf.Empty
|
||||
36, // 36: daemon.StartedService.ListOutbounds:input_type -> google.protobuf.Empty
|
||||
36, // 37: daemon.StartedService.SubscribeOutbounds:input_type -> google.protobuf.Empty
|
||||
31, // 38: daemon.StartedService.StartNetworkQualityTest:input_type -> daemon.NetworkQualityTestRequest
|
||||
33, // 39: daemon.StartedService.StartSTUNTest:input_type -> daemon.STUNTestRequest
|
||||
36, // 40: daemon.StartedService.StopService:output_type -> google.protobuf.Empty
|
||||
36, // 41: daemon.StartedService.ReloadService:output_type -> google.protobuf.Empty
|
||||
4, // 42: daemon.StartedService.SubscribeServiceStatus:output_type -> daemon.ServiceStatus
|
||||
7, // 43: daemon.StartedService.SubscribeLog:output_type -> daemon.Log
|
||||
8, // 44: daemon.StartedService.GetDefaultLogLevel:output_type -> daemon.DefaultLogLevel
|
||||
36, // 45: daemon.StartedService.ClearLogs:output_type -> google.protobuf.Empty
|
||||
9, // 46: daemon.StartedService.SubscribeStatus:output_type -> daemon.Status
|
||||
10, // 47: daemon.StartedService.SubscribeGroups:output_type -> daemon.Groups
|
||||
17, // 48: daemon.StartedService.GetClashModeStatus:output_type -> daemon.ClashModeStatus
|
||||
16, // 49: daemon.StartedService.SubscribeClashMode:output_type -> daemon.ClashMode
|
||||
36, // 50: daemon.StartedService.SetClashMode:output_type -> google.protobuf.Empty
|
||||
36, // 51: daemon.StartedService.URLTest:output_type -> google.protobuf.Empty
|
||||
36, // 52: daemon.StartedService.SelectOutbound:output_type -> google.protobuf.Empty
|
||||
36, // 53: daemon.StartedService.SetGroupExpand:output_type -> google.protobuf.Empty
|
||||
18, // 54: daemon.StartedService.GetSystemProxyStatus:output_type -> daemon.SystemProxyStatus
|
||||
36, // 55: daemon.StartedService.SetSystemProxyEnabled:output_type -> google.protobuf.Empty
|
||||
36, // 56: daemon.StartedService.TriggerDebugCrash:output_type -> google.protobuf.Empty
|
||||
36, // 57: daemon.StartedService.TriggerOOMReport:output_type -> google.protobuf.Empty
|
||||
23, // 58: daemon.StartedService.SubscribeConnections:output_type -> daemon.ConnectionEvents
|
||||
36, // 59: daemon.StartedService.CloseConnection:output_type -> google.protobuf.Empty
|
||||
36, // 60: daemon.StartedService.CloseAllConnections:output_type -> google.protobuf.Empty
|
||||
27, // 61: daemon.StartedService.GetDeprecatedWarnings:output_type -> daemon.DeprecatedWarnings
|
||||
29, // 62: daemon.StartedService.GetStartedAt:output_type -> daemon.StartedAt
|
||||
30, // 63: daemon.StartedService.ListOutbounds:output_type -> daemon.OutboundList
|
||||
30, // 64: daemon.StartedService.SubscribeOutbounds:output_type -> daemon.OutboundList
|
||||
32, // 65: daemon.StartedService.StartNetworkQualityTest:output_type -> daemon.NetworkQualityTestProgress
|
||||
34, // 66: daemon.StartedService.StartSTUNTest:output_type -> daemon.STUNTestProgress
|
||||
40, // [40:67] is the sub-list for method output_type
|
||||
13, // [13:40] is the sub-list for method input_type
|
||||
13, // [13:13] is the sub-list for extension type_name
|
||||
13, // [13:13] is the sub-list for extension extendee
|
||||
0, // [0:13] is the sub-list for field type_name
|
||||
36, // 12: daemon.TailscaleStatusUpdate.endpoints:type_name -> daemon.TailscaleEndpointStatus
|
||||
38, // 13: daemon.TailscaleEndpointStatus.self:type_name -> daemon.TailscalePeer
|
||||
37, // 14: daemon.TailscaleEndpointStatus.userGroups:type_name -> daemon.TailscaleUserGroup
|
||||
38, // 15: daemon.TailscaleUserGroup.peers:type_name -> daemon.TailscalePeer
|
||||
0, // 16: daemon.Log.Message.level:type_name -> daemon.LogLevel
|
||||
40, // 17: daemon.StartedService.StopService:input_type -> google.protobuf.Empty
|
||||
40, // 18: daemon.StartedService.ReloadService:input_type -> google.protobuf.Empty
|
||||
40, // 19: daemon.StartedService.SubscribeServiceStatus:input_type -> google.protobuf.Empty
|
||||
40, // 20: daemon.StartedService.SubscribeLog:input_type -> google.protobuf.Empty
|
||||
40, // 21: daemon.StartedService.GetDefaultLogLevel:input_type -> google.protobuf.Empty
|
||||
40, // 22: daemon.StartedService.ClearLogs:input_type -> google.protobuf.Empty
|
||||
6, // 23: daemon.StartedService.SubscribeStatus:input_type -> daemon.SubscribeStatusRequest
|
||||
40, // 24: daemon.StartedService.SubscribeGroups:input_type -> google.protobuf.Empty
|
||||
40, // 25: daemon.StartedService.GetClashModeStatus:input_type -> google.protobuf.Empty
|
||||
40, // 26: daemon.StartedService.SubscribeClashMode:input_type -> google.protobuf.Empty
|
||||
16, // 27: daemon.StartedService.SetClashMode:input_type -> daemon.ClashMode
|
||||
13, // 28: daemon.StartedService.URLTest:input_type -> daemon.URLTestRequest
|
||||
14, // 29: daemon.StartedService.SelectOutbound:input_type -> daemon.SelectOutboundRequest
|
||||
15, // 30: daemon.StartedService.SetGroupExpand:input_type -> daemon.SetGroupExpandRequest
|
||||
40, // 31: daemon.StartedService.GetSystemProxyStatus:input_type -> google.protobuf.Empty
|
||||
19, // 32: daemon.StartedService.SetSystemProxyEnabled:input_type -> daemon.SetSystemProxyEnabledRequest
|
||||
20, // 33: daemon.StartedService.TriggerDebugCrash:input_type -> daemon.DebugCrashRequest
|
||||
40, // 34: daemon.StartedService.TriggerOOMReport:input_type -> google.protobuf.Empty
|
||||
21, // 35: daemon.StartedService.SubscribeConnections:input_type -> daemon.SubscribeConnectionsRequest
|
||||
26, // 36: daemon.StartedService.CloseConnection:input_type -> daemon.CloseConnectionRequest
|
||||
40, // 37: daemon.StartedService.CloseAllConnections:input_type -> google.protobuf.Empty
|
||||
40, // 38: daemon.StartedService.GetDeprecatedWarnings:input_type -> google.protobuf.Empty
|
||||
40, // 39: daemon.StartedService.GetStartedAt:input_type -> google.protobuf.Empty
|
||||
40, // 40: daemon.StartedService.ListOutbounds:input_type -> google.protobuf.Empty
|
||||
40, // 41: daemon.StartedService.SubscribeOutbounds:input_type -> google.protobuf.Empty
|
||||
31, // 42: daemon.StartedService.StartNetworkQualityTest:input_type -> daemon.NetworkQualityTestRequest
|
||||
33, // 43: daemon.StartedService.StartSTUNTest:input_type -> daemon.STUNTestRequest
|
||||
40, // 44: daemon.StartedService.SubscribeTailscaleStatus:input_type -> google.protobuf.Empty
|
||||
40, // 45: daemon.StartedService.StopService:output_type -> google.protobuf.Empty
|
||||
40, // 46: daemon.StartedService.ReloadService:output_type -> google.protobuf.Empty
|
||||
4, // 47: daemon.StartedService.SubscribeServiceStatus:output_type -> daemon.ServiceStatus
|
||||
7, // 48: daemon.StartedService.SubscribeLog:output_type -> daemon.Log
|
||||
8, // 49: daemon.StartedService.GetDefaultLogLevel:output_type -> daemon.DefaultLogLevel
|
||||
40, // 50: daemon.StartedService.ClearLogs:output_type -> google.protobuf.Empty
|
||||
9, // 51: daemon.StartedService.SubscribeStatus:output_type -> daemon.Status
|
||||
10, // 52: daemon.StartedService.SubscribeGroups:output_type -> daemon.Groups
|
||||
17, // 53: daemon.StartedService.GetClashModeStatus:output_type -> daemon.ClashModeStatus
|
||||
16, // 54: daemon.StartedService.SubscribeClashMode:output_type -> daemon.ClashMode
|
||||
40, // 55: daemon.StartedService.SetClashMode:output_type -> google.protobuf.Empty
|
||||
40, // 56: daemon.StartedService.URLTest:output_type -> google.protobuf.Empty
|
||||
40, // 57: daemon.StartedService.SelectOutbound:output_type -> google.protobuf.Empty
|
||||
40, // 58: daemon.StartedService.SetGroupExpand:output_type -> google.protobuf.Empty
|
||||
18, // 59: daemon.StartedService.GetSystemProxyStatus:output_type -> daemon.SystemProxyStatus
|
||||
40, // 60: daemon.StartedService.SetSystemProxyEnabled:output_type -> google.protobuf.Empty
|
||||
40, // 61: daemon.StartedService.TriggerDebugCrash:output_type -> google.protobuf.Empty
|
||||
40, // 62: daemon.StartedService.TriggerOOMReport:output_type -> google.protobuf.Empty
|
||||
23, // 63: daemon.StartedService.SubscribeConnections:output_type -> daemon.ConnectionEvents
|
||||
40, // 64: daemon.StartedService.CloseConnection:output_type -> google.protobuf.Empty
|
||||
40, // 65: daemon.StartedService.CloseAllConnections:output_type -> google.protobuf.Empty
|
||||
27, // 66: daemon.StartedService.GetDeprecatedWarnings:output_type -> daemon.DeprecatedWarnings
|
||||
29, // 67: daemon.StartedService.GetStartedAt:output_type -> daemon.StartedAt
|
||||
30, // 68: daemon.StartedService.ListOutbounds:output_type -> daemon.OutboundList
|
||||
30, // 69: daemon.StartedService.SubscribeOutbounds:output_type -> daemon.OutboundList
|
||||
32, // 70: daemon.StartedService.StartNetworkQualityTest:output_type -> daemon.NetworkQualityTestProgress
|
||||
34, // 71: daemon.StartedService.StartSTUNTest:output_type -> daemon.STUNTestProgress
|
||||
35, // 72: daemon.StartedService.SubscribeTailscaleStatus:output_type -> daemon.TailscaleStatusUpdate
|
||||
45, // [45:73] is the sub-list for method output_type
|
||||
17, // [17:45] is the sub-list for method input_type
|
||||
17, // [17:17] is the sub-list for extension type_name
|
||||
17, // [17:17] is the sub-list for extension extendee
|
||||
0, // [0:17] is the sub-list for field type_name
|
||||
}
|
||||
|
||||
func init() { file_daemon_started_service_proto_init() }
|
||||
@@ -2656,7 +3034,7 @@ func file_daemon_started_service_proto_init() {
|
||||
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
|
||||
RawDescriptor: unsafe.Slice(unsafe.StringData(file_daemon_started_service_proto_rawDesc), len(file_daemon_started_service_proto_rawDesc)),
|
||||
NumEnums: 4,
|
||||
NumMessages: 32,
|
||||
NumMessages: 36,
|
||||
NumExtensions: 0,
|
||||
NumServices: 1,
|
||||
},
|
||||
|
||||
@@ -39,6 +39,7 @@ service StartedService {
|
||||
rpc SubscribeOutbounds(google.protobuf.Empty) returns (stream OutboundList) {}
|
||||
rpc StartNetworkQualityTest(NetworkQualityTestRequest) returns (stream NetworkQualityTestProgress) {}
|
||||
rpc StartSTUNTest(STUNTestRequest) returns (stream STUNTestProgress) {}
|
||||
rpc SubscribeTailscaleStatus(google.protobuf.Empty) returns (stream TailscaleStatusUpdate) {}
|
||||
}
|
||||
|
||||
message ServiceStatus {
|
||||
@@ -278,3 +279,39 @@ message STUNTestProgress {
|
||||
string error = 7;
|
||||
bool natTypeSupported = 8;
|
||||
}
|
||||
|
||||
message TailscaleStatusUpdate {
|
||||
repeated TailscaleEndpointStatus endpoints = 1;
|
||||
}
|
||||
|
||||
message TailscaleEndpointStatus {
|
||||
string endpointTag = 1;
|
||||
string backendState = 2;
|
||||
string authURL = 3;
|
||||
string networkName = 4;
|
||||
string magicDNSSuffix = 5;
|
||||
TailscalePeer self = 6;
|
||||
repeated TailscaleUserGroup userGroups = 7;
|
||||
}
|
||||
|
||||
message TailscaleUserGroup {
|
||||
int64 userID = 1;
|
||||
string loginName = 2;
|
||||
string displayName = 3;
|
||||
string profilePicURL = 4;
|
||||
repeated TailscalePeer peers = 5;
|
||||
}
|
||||
|
||||
message TailscalePeer {
|
||||
string hostName = 1;
|
||||
string dnsName = 2;
|
||||
string os = 3;
|
||||
repeated string tailscaleIPs = 4;
|
||||
bool online = 5;
|
||||
bool exitNode = 6;
|
||||
bool exitNodeOption = 7;
|
||||
bool active = 8;
|
||||
int64 rxBytes = 9;
|
||||
int64 txBytes = 10;
|
||||
int64 keyExpiry = 11;
|
||||
}
|
||||
|
||||
@@ -42,6 +42,7 @@ const (
|
||||
StartedService_SubscribeOutbounds_FullMethodName = "/daemon.StartedService/SubscribeOutbounds"
|
||||
StartedService_StartNetworkQualityTest_FullMethodName = "/daemon.StartedService/StartNetworkQualityTest"
|
||||
StartedService_StartSTUNTest_FullMethodName = "/daemon.StartedService/StartSTUNTest"
|
||||
StartedService_SubscribeTailscaleStatus_FullMethodName = "/daemon.StartedService/SubscribeTailscaleStatus"
|
||||
)
|
||||
|
||||
// StartedServiceClient is the client API for StartedService service.
|
||||
@@ -75,6 +76,7 @@ type StartedServiceClient interface {
|
||||
SubscribeOutbounds(ctx context.Context, in *emptypb.Empty, opts ...grpc.CallOption) (grpc.ServerStreamingClient[OutboundList], error)
|
||||
StartNetworkQualityTest(ctx context.Context, in *NetworkQualityTestRequest, opts ...grpc.CallOption) (grpc.ServerStreamingClient[NetworkQualityTestProgress], error)
|
||||
StartSTUNTest(ctx context.Context, in *STUNTestRequest, opts ...grpc.CallOption) (grpc.ServerStreamingClient[STUNTestProgress], error)
|
||||
SubscribeTailscaleStatus(ctx context.Context, in *emptypb.Empty, opts ...grpc.CallOption) (grpc.ServerStreamingClient[TailscaleStatusUpdate], error)
|
||||
}
|
||||
|
||||
type startedServiceClient struct {
|
||||
@@ -436,6 +438,25 @@ func (c *startedServiceClient) StartSTUNTest(ctx context.Context, in *STUNTestRe
|
||||
// This type alias is provided for backwards compatibility with existing code that references the prior non-generic stream type by name.
|
||||
type StartedService_StartSTUNTestClient = grpc.ServerStreamingClient[STUNTestProgress]
|
||||
|
||||
func (c *startedServiceClient) SubscribeTailscaleStatus(ctx context.Context, in *emptypb.Empty, opts ...grpc.CallOption) (grpc.ServerStreamingClient[TailscaleStatusUpdate], error) {
|
||||
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
|
||||
stream, err := c.cc.NewStream(ctx, &StartedService_ServiceDesc.Streams[9], StartedService_SubscribeTailscaleStatus_FullMethodName, cOpts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
x := &grpc.GenericClientStream[emptypb.Empty, TailscaleStatusUpdate]{ClientStream: stream}
|
||||
if err := x.ClientStream.SendMsg(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := x.ClientStream.CloseSend(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return x, nil
|
||||
}
|
||||
|
||||
// This type alias is provided for backwards compatibility with existing code that references the prior non-generic stream type by name.
|
||||
type StartedService_SubscribeTailscaleStatusClient = grpc.ServerStreamingClient[TailscaleStatusUpdate]
|
||||
|
||||
// StartedServiceServer is the server API for StartedService service.
|
||||
// All implementations must embed UnimplementedStartedServiceServer
|
||||
// for forward compatibility.
|
||||
@@ -467,6 +488,7 @@ type StartedServiceServer interface {
|
||||
SubscribeOutbounds(*emptypb.Empty, grpc.ServerStreamingServer[OutboundList]) error
|
||||
StartNetworkQualityTest(*NetworkQualityTestRequest, grpc.ServerStreamingServer[NetworkQualityTestProgress]) error
|
||||
StartSTUNTest(*STUNTestRequest, grpc.ServerStreamingServer[STUNTestProgress]) error
|
||||
SubscribeTailscaleStatus(*emptypb.Empty, grpc.ServerStreamingServer[TailscaleStatusUpdate]) error
|
||||
mustEmbedUnimplementedStartedServiceServer()
|
||||
}
|
||||
|
||||
@@ -584,6 +606,10 @@ func (UnimplementedStartedServiceServer) StartNetworkQualityTest(*NetworkQuality
|
||||
func (UnimplementedStartedServiceServer) StartSTUNTest(*STUNTestRequest, grpc.ServerStreamingServer[STUNTestProgress]) error {
|
||||
return status.Error(codes.Unimplemented, "method StartSTUNTest not implemented")
|
||||
}
|
||||
|
||||
func (UnimplementedStartedServiceServer) SubscribeTailscaleStatus(*emptypb.Empty, grpc.ServerStreamingServer[TailscaleStatusUpdate]) error {
|
||||
return status.Error(codes.Unimplemented, "method SubscribeTailscaleStatus not implemented")
|
||||
}
|
||||
func (UnimplementedStartedServiceServer) mustEmbedUnimplementedStartedServiceServer() {}
|
||||
func (UnimplementedStartedServiceServer) testEmbeddedByValue() {}
|
||||
|
||||
@@ -1028,6 +1054,17 @@ func _StartedService_StartSTUNTest_Handler(srv interface{}, stream grpc.ServerSt
|
||||
// This type alias is provided for backwards compatibility with existing code that references the prior non-generic stream type by name.
|
||||
type StartedService_StartSTUNTestServer = grpc.ServerStreamingServer[STUNTestProgress]
|
||||
|
||||
func _StartedService_SubscribeTailscaleStatus_Handler(srv interface{}, stream grpc.ServerStream) error {
|
||||
m := new(emptypb.Empty)
|
||||
if err := stream.RecvMsg(m); err != nil {
|
||||
return err
|
||||
}
|
||||
return srv.(StartedServiceServer).SubscribeTailscaleStatus(m, &grpc.GenericServerStream[emptypb.Empty, TailscaleStatusUpdate]{ServerStream: stream})
|
||||
}
|
||||
|
||||
// This type alias is provided for backwards compatibility with existing code that references the prior non-generic stream type by name.
|
||||
type StartedService_SubscribeTailscaleStatusServer = grpc.ServerStreamingServer[TailscaleStatusUpdate]
|
||||
|
||||
// StartedService_ServiceDesc is the grpc.ServiceDesc for StartedService service.
|
||||
// It's only intended for direct use with grpc.RegisterService,
|
||||
// and not to be introspected or modified (even as a copy)
|
||||
@@ -1154,6 +1191,11 @@ var StartedService_ServiceDesc = grpc.ServiceDesc{
|
||||
Handler: _StartedService_StartSTUNTest_Handler,
|
||||
ServerStreams: true,
|
||||
},
|
||||
{
|
||||
StreamName: "SubscribeTailscaleStatus",
|
||||
Handler: _StartedService_SubscribeTailscaleStatus_Handler,
|
||||
ServerStreams: true,
|
||||
},
|
||||
},
|
||||
Metadata: "daemon/started_service.proto",
|
||||
}
|
||||
|
||||
@@ -720,3 +720,25 @@ func (c *CommandClient) StartSTUNTest(server string, outboundTag string, handler
|
||||
handler.OnProgress(stunTestProgressFromGRPC(event))
|
||||
}
|
||||
}
|
||||
|
||||
func (c *CommandClient) SubscribeTailscaleStatus(handler TailscaleStatusHandler) error {
|
||||
client, err := c.getClientForCall()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if c.standalone {
|
||||
defer c.closeConnection()
|
||||
}
|
||||
stream, err := client.SubscribeTailscaleStatus(context.Background(), &emptypb.Empty{})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for {
|
||||
event, recvErr := stream.Recv()
|
||||
if recvErr != nil {
|
||||
handler.OnError(recvErr.Error())
|
||||
return recvErr
|
||||
}
|
||||
handler.OnStatusUpdate(tailscaleStatusUpdateFromGRPC(event))
|
||||
}
|
||||
}
|
||||
|
||||
132
experimental/libbox/command_types_tailscale.go
Normal file
132
experimental/libbox/command_types_tailscale.go
Normal file
@@ -0,0 +1,132 @@
|
||||
package libbox
|
||||
|
||||
import "github.com/sagernet/sing-box/daemon"
|
||||
|
||||
type TailscaleStatusUpdate struct {
|
||||
endpoints []*TailscaleEndpointStatus
|
||||
}
|
||||
|
||||
func (u *TailscaleStatusUpdate) Endpoints() TailscaleEndpointStatusIterator {
|
||||
return newIterator(u.endpoints)
|
||||
}
|
||||
|
||||
type TailscaleEndpointStatusIterator interface {
|
||||
Next() *TailscaleEndpointStatus
|
||||
HasNext() bool
|
||||
}
|
||||
|
||||
type TailscaleEndpointStatus struct {
|
||||
EndpointTag string
|
||||
BackendState string
|
||||
AuthURL string
|
||||
NetworkName string
|
||||
MagicDNSSuffix string
|
||||
Self *TailscalePeer
|
||||
userGroups []*TailscaleUserGroup
|
||||
}
|
||||
|
||||
func (s *TailscaleEndpointStatus) UserGroups() TailscaleUserGroupIterator {
|
||||
return newIterator(s.userGroups)
|
||||
}
|
||||
|
||||
type TailscaleUserGroupIterator interface {
|
||||
Next() *TailscaleUserGroup
|
||||
HasNext() bool
|
||||
}
|
||||
|
||||
type TailscaleUserGroup struct {
|
||||
UserID int64
|
||||
LoginName string
|
||||
DisplayName string
|
||||
ProfilePicURL string
|
||||
peers []*TailscalePeer
|
||||
}
|
||||
|
||||
func (g *TailscaleUserGroup) Peers() TailscalePeerIterator {
|
||||
return newIterator(g.peers)
|
||||
}
|
||||
|
||||
type TailscalePeerIterator interface {
|
||||
Next() *TailscalePeer
|
||||
HasNext() bool
|
||||
}
|
||||
|
||||
type TailscalePeer struct {
|
||||
HostName string
|
||||
DNSName string
|
||||
OS string
|
||||
tailscaleIPs []string
|
||||
Online bool
|
||||
ExitNode bool
|
||||
ExitNodeOption bool
|
||||
Active bool
|
||||
RxBytes int64
|
||||
TxBytes int64
|
||||
KeyExpiry int64
|
||||
}
|
||||
|
||||
func (p *TailscalePeer) TailscaleIPs() StringIterator {
|
||||
return newIterator(p.tailscaleIPs)
|
||||
}
|
||||
|
||||
type TailscaleStatusHandler interface {
|
||||
OnStatusUpdate(status *TailscaleStatusUpdate)
|
||||
OnError(message string)
|
||||
}
|
||||
|
||||
func tailscaleStatusUpdateFromGRPC(update *daemon.TailscaleStatusUpdate) *TailscaleStatusUpdate {
|
||||
endpoints := make([]*TailscaleEndpointStatus, len(update.Endpoints))
|
||||
for i, endpoint := range update.Endpoints {
|
||||
endpoints[i] = tailscaleEndpointStatusFromGRPC(endpoint)
|
||||
}
|
||||
return &TailscaleStatusUpdate{endpoints: endpoints}
|
||||
}
|
||||
|
||||
func tailscaleEndpointStatusFromGRPC(status *daemon.TailscaleEndpointStatus) *TailscaleEndpointStatus {
|
||||
userGroups := make([]*TailscaleUserGroup, len(status.UserGroups))
|
||||
for i, group := range status.UserGroups {
|
||||
userGroups[i] = tailscaleUserGroupFromGRPC(group)
|
||||
}
|
||||
result := &TailscaleEndpointStatus{
|
||||
EndpointTag: status.EndpointTag,
|
||||
BackendState: status.BackendState,
|
||||
AuthURL: status.AuthURL,
|
||||
NetworkName: status.NetworkName,
|
||||
MagicDNSSuffix: status.MagicDNSSuffix,
|
||||
userGroups: userGroups,
|
||||
}
|
||||
if status.Self != nil {
|
||||
result.Self = tailscalePeerFromGRPC(status.Self)
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
func tailscaleUserGroupFromGRPC(group *daemon.TailscaleUserGroup) *TailscaleUserGroup {
|
||||
peers := make([]*TailscalePeer, len(group.Peers))
|
||||
for i, peer := range group.Peers {
|
||||
peers[i] = tailscalePeerFromGRPC(peer)
|
||||
}
|
||||
return &TailscaleUserGroup{
|
||||
UserID: group.UserID,
|
||||
LoginName: group.LoginName,
|
||||
DisplayName: group.DisplayName,
|
||||
ProfilePicURL: group.ProfilePicURL,
|
||||
peers: peers,
|
||||
}
|
||||
}
|
||||
|
||||
func tailscalePeerFromGRPC(peer *daemon.TailscalePeer) *TailscalePeer {
|
||||
return &TailscalePeer{
|
||||
HostName: peer.HostName,
|
||||
DNSName: peer.DnsName,
|
||||
OS: peer.Os,
|
||||
tailscaleIPs: peer.TailscaleIPs,
|
||||
Online: peer.Online,
|
||||
ExitNode: peer.ExitNode,
|
||||
ExitNodeOption: peer.ExitNodeOption,
|
||||
Active: peer.Active,
|
||||
RxBytes: peer.RxBytes,
|
||||
TxBytes: peer.TxBytes,
|
||||
KeyExpiry: peer.KeyExpiry,
|
||||
}
|
||||
}
|
||||
@@ -1,9 +1,12 @@
|
||||
package libbox
|
||||
|
||||
import "time"
|
||||
import (
|
||||
"time"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
func TriggerGoPanic() {
|
||||
time.AfterFunc(200*time.Millisecond, func() {
|
||||
panic("debug go crash")
|
||||
*(*int)(unsafe.Pointer(uintptr(0))) = 0
|
||||
})
|
||||
}
|
||||
|
||||
@@ -12,6 +12,7 @@ import (
|
||||
"github.com/sagernet/sing-box/common/networkquality"
|
||||
"github.com/sagernet/sing-box/common/stun"
|
||||
C "github.com/sagernet/sing-box/constant"
|
||||
"github.com/sagernet/sing-box/dns"
|
||||
"github.com/sagernet/sing-box/experimental/locale"
|
||||
"github.com/sagernet/sing-box/log"
|
||||
"github.com/sagernet/sing-box/service/oomkiller"
|
||||
@@ -181,6 +182,10 @@ func FormatNATFiltering(value int32) string {
|
||||
return stun.NATFiltering(value).String()
|
||||
}
|
||||
|
||||
func FormatFQDN(fqdn string) string {
|
||||
return dns.FqdnToDomain(fqdn)
|
||||
}
|
||||
|
||||
func ProxyDisplayType(proxyType string) string {
|
||||
return C.ProxyDisplayName(proxyType)
|
||||
}
|
||||
|
||||
92
protocol/tailscale/status.go
Normal file
92
protocol/tailscale/status.go
Normal file
@@ -0,0 +1,92 @@
|
||||
//go:build with_gvisor
|
||||
|
||||
package tailscale
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/sagernet/sing-box/adapter"
|
||||
"github.com/sagernet/tailscale/ipn"
|
||||
"github.com/sagernet/tailscale/ipn/ipnstate"
|
||||
"github.com/sagernet/tailscale/tailcfg"
|
||||
)
|
||||
|
||||
var _ adapter.TailscaleStatusProvider = (*Endpoint)(nil)
|
||||
|
||||
func (t *Endpoint) SubscribeTailscaleStatus(ctx context.Context, fn func(*adapter.TailscaleEndpointStatus)) error {
|
||||
localBackend := t.server.ExportLocalBackend()
|
||||
sendStatus := func() {
|
||||
status := localBackend.Status()
|
||||
fn(convertTailscaleStatus(status))
|
||||
}
|
||||
sendStatus()
|
||||
localBackend.WatchNotifications(ctx, ipn.NotifyInitialState|ipn.NotifyInitialNetMap|ipn.NotifyRateLimit, nil, func(roNotify *ipn.Notify) (keepGoing bool) {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return false
|
||||
default:
|
||||
}
|
||||
if roNotify.State != nil || roNotify.NetMap != nil || roNotify.BrowseToURL != nil {
|
||||
sendStatus()
|
||||
}
|
||||
return true
|
||||
})
|
||||
return ctx.Err()
|
||||
}
|
||||
|
||||
func convertTailscaleStatus(status *ipnstate.Status) *adapter.TailscaleEndpointStatus {
|
||||
result := &adapter.TailscaleEndpointStatus{
|
||||
BackendState: status.BackendState,
|
||||
AuthURL: status.AuthURL,
|
||||
}
|
||||
if status.CurrentTailnet != nil {
|
||||
result.NetworkName = status.CurrentTailnet.Name
|
||||
result.MagicDNSSuffix = status.CurrentTailnet.MagicDNSSuffix
|
||||
}
|
||||
if status.Self != nil {
|
||||
result.Self = convertTailscalePeer(status.Self)
|
||||
}
|
||||
result.Users = make(map[int64]*adapter.TailscaleUser, len(status.User))
|
||||
for userID, profile := range status.User {
|
||||
result.Users[int64(userID)] = convertTailscaleUser(userID, profile)
|
||||
}
|
||||
result.Peers = make([]*adapter.TailscalePeer, 0, len(status.Peer))
|
||||
for _, peer := range status.Peer {
|
||||
result.Peers = append(result.Peers, convertTailscalePeer(peer))
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
func convertTailscalePeer(peer *ipnstate.PeerStatus) *adapter.TailscalePeer {
|
||||
ips := make([]string, len(peer.TailscaleIPs))
|
||||
for i, ip := range peer.TailscaleIPs {
|
||||
ips[i] = ip.String()
|
||||
}
|
||||
var keyExpiry int64
|
||||
if peer.KeyExpiry != nil {
|
||||
keyExpiry = peer.KeyExpiry.Unix()
|
||||
}
|
||||
return &adapter.TailscalePeer{
|
||||
HostName: peer.HostName,
|
||||
DNSName: peer.DNSName,
|
||||
OS: peer.OS,
|
||||
TailscaleIPs: ips,
|
||||
Online: peer.Online,
|
||||
ExitNode: peer.ExitNode,
|
||||
ExitNodeOption: peer.ExitNodeOption,
|
||||
Active: peer.Active,
|
||||
RxBytes: peer.RxBytes,
|
||||
TxBytes: peer.TxBytes,
|
||||
UserID: int64(peer.UserID),
|
||||
KeyExpiry: keyExpiry,
|
||||
}
|
||||
}
|
||||
|
||||
func convertTailscaleUser(id tailcfg.UserID, profile tailcfg.UserProfile) *adapter.TailscaleUser {
|
||||
return &adapter.TailscaleUser{
|
||||
ID: int64(id),
|
||||
LoginName: profile.LoginName,
|
||||
DisplayName: profile.DisplayName,
|
||||
ProfilePicURL: profile.ProfilePicURL,
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user