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 }