github.com/sagernet/sing-box@v1.9.0-rc.20/experimental/libbox/command_clash_mode.go (about)

     1  package libbox
     2  
     3  import (
     4  	"encoding/binary"
     5  	"io"
     6  	"net"
     7  	"time"
     8  
     9  	"github.com/sagernet/sing-box/adapter"
    10  	"github.com/sagernet/sing-box/experimental/clashapi"
    11  	E "github.com/sagernet/sing/common/exceptions"
    12  	"github.com/sagernet/sing/common/rw"
    13  )
    14  
    15  func (c *CommandClient) SetClashMode(newMode string) error {
    16  	conn, err := c.directConnect()
    17  	if err != nil {
    18  		return err
    19  	}
    20  	defer conn.Close()
    21  	err = binary.Write(conn, binary.BigEndian, uint8(CommandSetClashMode))
    22  	if err != nil {
    23  		return err
    24  	}
    25  	err = rw.WriteVString(conn, newMode)
    26  	if err != nil {
    27  		return err
    28  	}
    29  	return readError(conn)
    30  }
    31  
    32  func (s *CommandServer) handleSetClashMode(conn net.Conn) error {
    33  	newMode, err := rw.ReadVString(conn)
    34  	if err != nil {
    35  		return err
    36  	}
    37  	service := s.service
    38  	if service == nil {
    39  		return writeError(conn, E.New("service not ready"))
    40  	}
    41  	clashServer := service.instance.Router().ClashServer()
    42  	if clashServer == nil {
    43  		return writeError(conn, E.New("Clash API disabled"))
    44  	}
    45  	clashServer.(*clashapi.Server).SetMode(newMode)
    46  	return writeError(conn, nil)
    47  }
    48  
    49  func (c *CommandClient) handleModeConn(conn net.Conn) {
    50  	defer conn.Close()
    51  
    52  	for {
    53  		newMode, err := rw.ReadVString(conn)
    54  		if err != nil {
    55  			c.handler.Disconnected(err.Error())
    56  			return
    57  		}
    58  		c.handler.UpdateClashMode(newMode)
    59  	}
    60  }
    61  
    62  func (s *CommandServer) handleModeConn(conn net.Conn) error {
    63  	ctx := connKeepAlive(conn)
    64  	for s.service == nil {
    65  		select {
    66  		case <-time.After(time.Second):
    67  			continue
    68  		case <-ctx.Done():
    69  			return ctx.Err()
    70  		}
    71  	}
    72  	clashServer := s.service.instance.Router().ClashServer()
    73  	if clashServer == nil {
    74  		return binary.Write(conn, binary.BigEndian, uint16(0))
    75  	}
    76  	err := writeClashModeList(conn, clashServer)
    77  	if err != nil {
    78  		return err
    79  	}
    80  	for {
    81  		select {
    82  		case <-s.modeUpdate:
    83  			err = rw.WriteVString(conn, clashServer.Mode())
    84  			if err != nil {
    85  				return err
    86  			}
    87  		case <-ctx.Done():
    88  			return ctx.Err()
    89  		}
    90  	}
    91  }
    92  
    93  func readClashModeList(reader io.Reader) (modeList []string, currentMode string, err error) {
    94  	var modeListLength uint16
    95  	err = binary.Read(reader, binary.BigEndian, &modeListLength)
    96  	if err != nil {
    97  		return
    98  	}
    99  	if modeListLength == 0 {
   100  		return
   101  	}
   102  	modeList = make([]string, modeListLength)
   103  	for i := 0; i < int(modeListLength); i++ {
   104  		modeList[i], err = rw.ReadVString(reader)
   105  		if err != nil {
   106  			return
   107  		}
   108  	}
   109  	currentMode, err = rw.ReadVString(reader)
   110  	return
   111  }
   112  
   113  func writeClashModeList(writer io.Writer, clashServer adapter.ClashServer) error {
   114  	modeList := clashServer.ModeList()
   115  	err := binary.Write(writer, binary.BigEndian, uint16(len(modeList)))
   116  	if err != nil {
   117  		return err
   118  	}
   119  	if len(modeList) > 0 {
   120  		for _, mode := range modeList {
   121  			err = rw.WriteVString(writer, mode)
   122  			if err != nil {
   123  				return err
   124  			}
   125  		}
   126  		err = rw.WriteVString(writer, clashServer.Mode())
   127  		if err != nil {
   128  			return err
   129  		}
   130  	}
   131  	return nil
   132  }