mirror of
https://github.com/shtorm-7/sing-box-extended.git
synced 2026-10-02 04:16:38 +00:00
Add api command
This commit is contained in:
@@ -0,0 +1,161 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"net/netip"
|
||||
"slices"
|
||||
"strings"
|
||||
|
||||
"github.com/sagernet/sing-box/daemon"
|
||||
"github.com/sagernet/sing-box/dns"
|
||||
"github.com/sagernet/sing/common"
|
||||
E "github.com/sagernet/sing/common/exceptions"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
var commandAPITailscalePeer = &cobra.Command{
|
||||
Use: "peer",
|
||||
Short: "Manage Tailscale peers",
|
||||
}
|
||||
|
||||
func init() {
|
||||
commandAPITailscalePeer.PersistentFlags().StringVar(&commandAPITailscaleFlagEndpoint, "endpoint", "", commandAPITailscaleEndpointUsage)
|
||||
commandAPITailscale.AddCommand(commandAPITailscalePeer)
|
||||
}
|
||||
|
||||
type tailscalePeerEntry struct {
|
||||
peer *daemon.TailscalePeer
|
||||
group *daemon.TailscaleUserGroup
|
||||
self bool
|
||||
}
|
||||
|
||||
func tailscalePeerEntries(endpoint *daemon.TailscaleEndpointStatus) []tailscalePeerEntry {
|
||||
var entries []tailscalePeerEntry
|
||||
if endpoint.GetSelf() != nil {
|
||||
entries = append(entries, tailscalePeerEntry{peer: endpoint.GetSelf(), self: true})
|
||||
}
|
||||
for _, group := range endpoint.GetUserGroups() {
|
||||
for _, peer := range group.GetPeers() {
|
||||
entries = append(entries, tailscalePeerEntry{peer: peer, group: group})
|
||||
}
|
||||
}
|
||||
return entries
|
||||
}
|
||||
|
||||
func resolveTailscalePeer(entries []tailscalePeerEntry, selector string) (tailscalePeerEntry, error) {
|
||||
selectorAddress, addressErr := netip.ParseAddr(selector)
|
||||
matchers := []func(peer *daemon.TailscalePeer) bool{
|
||||
func(peer *daemon.TailscalePeer) bool {
|
||||
return peer.GetStableID() == selector
|
||||
},
|
||||
func(peer *daemon.TailscalePeer) bool {
|
||||
if addressErr != nil {
|
||||
return false
|
||||
}
|
||||
return slices.ContainsFunc(peer.GetTailscaleIPs(), func(it string) bool {
|
||||
address, parseErr := netip.ParseAddr(it)
|
||||
if parseErr != nil {
|
||||
return false
|
||||
}
|
||||
return address.Unmap() == selectorAddress.Unmap()
|
||||
})
|
||||
},
|
||||
func(peer *daemon.TailscalePeer) bool {
|
||||
dnsName := peer.GetDnsName()
|
||||
if dnsName == "" {
|
||||
return false
|
||||
}
|
||||
return strings.EqualFold(dnsName, selector) || strings.EqualFold(dns.FqdnToDomain(dnsName), selector)
|
||||
},
|
||||
func(peer *daemon.TailscalePeer) bool {
|
||||
label, _, _ := strings.Cut(peer.GetDnsName(), ".")
|
||||
if label == "" {
|
||||
return false
|
||||
}
|
||||
return strings.EqualFold(label, selector)
|
||||
},
|
||||
func(peer *daemon.TailscalePeer) bool {
|
||||
hostName := peer.GetHostName()
|
||||
if hostName == "" {
|
||||
return false
|
||||
}
|
||||
return strings.EqualFold(hostName, selector)
|
||||
},
|
||||
}
|
||||
for _, matcher := range matchers {
|
||||
matches := common.Filter(entries, func(it tailscalePeerEntry) bool {
|
||||
return matcher(it.peer)
|
||||
})
|
||||
if len(matches) == 1 {
|
||||
return matches[0], nil
|
||||
}
|
||||
if len(matches) > 1 {
|
||||
return tailscalePeerEntry{}, newTailscaleAmbiguousPeerError(selector, matches)
|
||||
}
|
||||
}
|
||||
return tailscalePeerEntry{}, E.New("peer not found: ", selector)
|
||||
}
|
||||
|
||||
func newTailscaleAmbiguousPeerError(selector string, matches []tailscalePeerEntry) error {
|
||||
sortTailscalePeerEntries(matches)
|
||||
names := common.Map(matches, func(it tailscalePeerEntry) string {
|
||||
return tailscalePeerName(it.peer)
|
||||
})
|
||||
addresses := common.Map(matches, func(it tailscalePeerEntry) string {
|
||||
address := tailscalePeerAddress(it.peer)
|
||||
if address == "" {
|
||||
return "-"
|
||||
}
|
||||
return address
|
||||
})
|
||||
nameWidth := len(common.MaxBy(names, func(it string) int {
|
||||
return len(it)
|
||||
}))
|
||||
addressWidth := len(common.MaxBy(addresses, func(it string) int {
|
||||
return len(it)
|
||||
}))
|
||||
var builder strings.Builder
|
||||
builder.WriteString("ambiguous peer: ")
|
||||
builder.WriteString(selector)
|
||||
for index, entry := range matches {
|
||||
builder.WriteString("\n ")
|
||||
builder.WriteString(names[index])
|
||||
builder.WriteString(strings.Repeat(" ", nameWidth-len(names[index])+3))
|
||||
builder.WriteString(addresses[index])
|
||||
builder.WriteString(strings.Repeat(" ", addressWidth-len(addresses[index])+3))
|
||||
builder.WriteString(entry.peer.GetStableID())
|
||||
}
|
||||
return E.New(builder.String())
|
||||
}
|
||||
|
||||
func sortTailscalePeerEntries(entries []tailscalePeerEntry) {
|
||||
common.SortBy(entries, func(it tailscalePeerEntry) string {
|
||||
return strings.ToLower(tailscalePeerName(it.peer))
|
||||
})
|
||||
}
|
||||
|
||||
func tailscalePeerName(peer *daemon.TailscalePeer) string {
|
||||
name := dns.FqdnToDomain(peer.GetDnsName())
|
||||
if name == "" {
|
||||
name = peer.GetHostName()
|
||||
}
|
||||
if name == "" {
|
||||
name = peer.GetStableID()
|
||||
}
|
||||
return name
|
||||
}
|
||||
|
||||
func tailscalePeerAddress(peer *daemon.TailscalePeer) string {
|
||||
addresses := peer.GetTailscaleIPs()
|
||||
if len(addresses) == 0 {
|
||||
return ""
|
||||
}
|
||||
return addresses[0]
|
||||
}
|
||||
|
||||
func formatYesNo(value bool) string {
|
||||
if value {
|
||||
return "yes"
|
||||
}
|
||||
return "no"
|
||||
}
|
||||
Reference in New Issue
Block a user