github.com/inazumav/sing-box@v0.0.0-20230926072359-ab51429a14f1/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/inazumav/sing-box/adapter" 10 "github.com/inazumav/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 defer conn.Close() 34 newMode, err := rw.ReadVString(conn) 35 if err != nil { 36 return err 37 } 38 service := s.service 39 if service == nil { 40 return writeError(conn, E.New("service not ready")) 41 } 42 clashServer := service.instance.Router().ClashServer() 43 if clashServer == nil { 44 return writeError(conn, E.New("Clash API disabled")) 45 } 46 clashServer.(*clashapi.Server).SetMode(newMode) 47 return writeError(conn, nil) 48 } 49 50 func (c *CommandClient) handleModeConn(conn net.Conn) { 51 defer conn.Close() 52 53 for { 54 newMode, err := rw.ReadVString(conn) 55 if err != nil { 56 c.handler.Disconnected(err.Error()) 57 return 58 } 59 c.handler.UpdateClashMode(newMode) 60 } 61 } 62 63 func (s *CommandServer) handleModeConn(conn net.Conn) error { 64 defer conn.Close() 65 ctx := connKeepAlive(conn) 66 for s.service == nil { 67 select { 68 case <-time.After(time.Second): 69 continue 70 case <-ctx.Done(): 71 return ctx.Err() 72 } 73 } 74 clashServer := s.service.instance.Router().ClashServer() 75 if clashServer == nil { 76 defer conn.Close() 77 return binary.Write(conn, binary.BigEndian, uint16(0)) 78 } 79 err := writeClashModeList(conn, clashServer) 80 if err != nil { 81 return err 82 } 83 for { 84 select { 85 case <-s.modeUpdate: 86 err = rw.WriteVString(conn, clashServer.Mode()) 87 if err != nil { 88 return err 89 } 90 case <-ctx.Done(): 91 return ctx.Err() 92 } 93 } 94 } 95 96 func readClashModeList(reader io.Reader) (modeList []string, currentMode string, err error) { 97 var modeListLength uint16 98 err = binary.Read(reader, binary.BigEndian, &modeListLength) 99 if err != nil { 100 return 101 } 102 if modeListLength == 0 { 103 return 104 } 105 modeList = make([]string, modeListLength) 106 for i := 0; i < int(modeListLength); i++ { 107 modeList[i], err = rw.ReadVString(reader) 108 if err != nil { 109 return 110 } 111 } 112 currentMode, err = rw.ReadVString(reader) 113 return 114 } 115 116 func writeClashModeList(writer io.Writer, clashServer adapter.ClashServer) error { 117 modeList := clashServer.ModeList() 118 err := binary.Write(writer, binary.BigEndian, uint16(len(modeList))) 119 if err != nil { 120 return err 121 } 122 if len(modeList) > 0 { 123 for _, mode := range modeList { 124 err = rw.WriteVString(writer, mode) 125 if err != nil { 126 return err 127 } 128 } 129 err = rw.WriteVString(writer, clashServer.Mode()) 130 if err != nil { 131 return err 132 } 133 } 134 return nil 135 }