sing-box/experimental/libbox/command_group.go

199 lines
4.6 KiB
Go
Raw Normal View History

2023-07-02 08:45:30 +00:00
package libbox
import (
2024-06-24 01:49:15 +00:00
"bufio"
2023-07-02 08:45:30 +00:00
"encoding/binary"
"io"
"net"
2023-08-04 09:13:46 +00:00
"time"
2023-07-02 08:45:30 +00:00
"github.com/sagernet/sing-box/adapter"
"github.com/sagernet/sing-box/common/urltest"
2024-11-01 16:39:02 +00:00
"github.com/sagernet/sing-box/protocol/group"
2023-08-12 11:33:43 +00:00
E "github.com/sagernet/sing/common/exceptions"
2024-06-24 01:49:15 +00:00
"github.com/sagernet/sing/common/varbin"
2023-07-02 08:45:30 +00:00
"github.com/sagernet/sing/service"
)
func (c *CommandClient) handleGroupConn(conn net.Conn) {
defer conn.Close()
for {
groups, err := readGroups(conn)
if err != nil {
c.handler.Disconnected(err.Error())
return
}
c.handler.WriteGroups(groups)
}
}
func (s *CommandServer) handleGroupConn(conn net.Conn) error {
var interval int64
err := binary.Read(conn, binary.BigEndian, &interval)
if err != nil {
return E.Cause(err, "read interval")
}
ticker := time.NewTicker(time.Duration(interval))
defer ticker.Stop()
2023-07-02 08:45:30 +00:00
ctx := connKeepAlive(conn)
2024-06-24 01:49:15 +00:00
writer := bufio.NewWriter(conn)
2023-07-02 08:45:30 +00:00
for {
service := s.service
if service != nil {
2024-06-24 01:49:15 +00:00
err = writeGroups(writer, service)
2023-07-02 08:45:30 +00:00
if err != nil {
return err
}
} else {
2024-06-24 01:49:15 +00:00
err = binary.Write(writer, binary.BigEndian, uint16(0))
2023-07-02 08:45:30 +00:00
if err != nil {
return err
}
}
2024-06-24 01:49:15 +00:00
err = writer.Flush()
if err != nil {
return err
}
2023-07-02 08:45:30 +00:00
select {
2023-08-04 09:13:46 +00:00
case <-ctx.Done():
return ctx.Err()
case <-ticker.C:
2023-08-04 09:13:46 +00:00
}
select {
2023-07-02 08:45:30 +00:00
case <-ctx.Done():
return ctx.Err()
case <-s.urlTestUpdate:
}
}
}
2024-06-11 13:16:33 +00:00
type OutboundGroup struct {
Tag string
Type string
Selectable bool
Selected string
IsExpand bool
2024-06-24 01:49:15 +00:00
ItemList []*OutboundGroupItem
2024-06-11 13:16:33 +00:00
}
func (g *OutboundGroup) GetItems() OutboundGroupItemIterator {
2024-06-24 01:49:15 +00:00
return newIterator(g.ItemList)
2024-06-11 13:16:33 +00:00
}
type OutboundGroupIterator interface {
Next() *OutboundGroup
HasNext() bool
}
type OutboundGroupItem struct {
Tag string
Type string
URLTestTime int64
URLTestDelay int32
}
type OutboundGroupItemIterator interface {
Next() *OutboundGroupItem
HasNext() bool
}
2023-07-02 08:45:30 +00:00
func readGroups(reader io.Reader) (OutboundGroupIterator, error) {
2024-06-24 01:49:15 +00:00
groups, err := varbin.ReadValue[[]*OutboundGroup](reader, binary.BigEndian)
2023-07-02 08:45:30 +00:00
if err != nil {
return nil, err
}
return newIterator(groups), nil
}
func writeGroups(writer io.Writer, boxService *BoxService) error {
historyStorage := service.PtrFromContext[urltest.HistoryStorage](boxService.ctx)
2023-11-28 04:00:28 +00:00
cacheFile := service.FromContext[adapter.CacheFile](boxService.ctx)
2023-07-02 08:45:30 +00:00
outbounds := boxService.instance.Router().Outbounds()
var iGroups []adapter.OutboundGroup
for _, it := range outbounds {
if group, isGroup := it.(adapter.OutboundGroup); isGroup {
iGroups = append(iGroups, group)
}
}
var groups []OutboundGroup
for _, iGroup := range iGroups {
2024-11-01 16:39:02 +00:00
var outboundGroup OutboundGroup
outboundGroup.Tag = iGroup.Tag()
outboundGroup.Type = iGroup.Type()
_, outboundGroup.Selectable = iGroup.(*group.Selector)
outboundGroup.Selected = iGroup.Now()
2023-08-12 11:33:43 +00:00
if cacheFile != nil {
2024-11-01 16:39:02 +00:00
if isExpand, loaded := cacheFile.LoadGroupExpand(outboundGroup.Tag); loaded {
outboundGroup.IsExpand = isExpand
2023-08-12 11:33:43 +00:00
}
}
2023-07-02 08:45:30 +00:00
for _, itemTag := range iGroup.All() {
itemOutbound, isLoaded := boxService.instance.Router().Outbound(itemTag)
if !isLoaded {
continue
}
var item OutboundGroupItem
item.Tag = itemTag
item.Type = itemOutbound.Type()
if history := historyStorage.LoadURLTestHistory(adapter.OutboundTag(itemOutbound)); history != nil {
item.URLTestTime = history.Time.Unix()
item.URLTestDelay = int32(history.Delay)
}
2024-11-01 16:39:02 +00:00
outboundGroup.ItemList = append(outboundGroup.ItemList, &item)
2023-07-02 08:45:30 +00:00
}
2024-11-01 16:39:02 +00:00
if len(outboundGroup.ItemList) < 2 {
continue
}
2024-11-01 16:39:02 +00:00
groups = append(groups, outboundGroup)
2023-07-02 08:45:30 +00:00
}
2024-06-24 01:49:15 +00:00
return varbin.Write(writer, binary.BigEndian, groups)
2023-07-02 08:45:30 +00:00
}
2023-08-12 11:33:43 +00:00
func (c *CommandClient) SetGroupExpand(groupTag string, isExpand bool) error {
conn, err := c.directConnect()
if err != nil {
return err
}
defer conn.Close()
err = binary.Write(conn, binary.BigEndian, uint8(CommandGroupExpand))
if err != nil {
return err
}
2024-06-24 01:49:15 +00:00
err = varbin.Write(conn, binary.BigEndian, groupTag)
2023-08-12 11:33:43 +00:00
if err != nil {
return err
}
err = binary.Write(conn, binary.BigEndian, isExpand)
if err != nil {
return err
}
return readError(conn)
}
func (s *CommandServer) handleSetGroupExpand(conn net.Conn) error {
2024-06-24 01:49:15 +00:00
groupTag, err := varbin.ReadValue[string](conn, binary.BigEndian)
2023-08-12 11:33:43 +00:00
if err != nil {
return err
}
var isExpand bool
err = binary.Read(conn, binary.BigEndian, &isExpand)
if err != nil {
return err
}
2023-11-28 04:00:28 +00:00
serviceNow := s.service
if serviceNow == nil {
2023-08-12 11:33:43 +00:00
return writeError(conn, E.New("service not ready"))
}
2023-11-28 04:00:28 +00:00
cacheFile := service.FromContext[adapter.CacheFile](serviceNow.ctx)
if cacheFile != nil {
err = cacheFile.StoreGroupExpand(groupTag, isExpand)
if err != nil {
return writeError(conn, err)
2023-08-12 11:33:43 +00:00
}
}
return writeError(conn, nil)
}