github.com/sagernet/sing-box@v1.9.0-rc.20/experimental/libbox/profile_import.go (about) 1 package libbox 2 3 import ( 4 "bytes" 5 "compress/gzip" 6 "encoding/binary" 7 "io" 8 9 E "github.com/sagernet/sing/common/exceptions" 10 "github.com/sagernet/sing/common/rw" 11 ) 12 13 func EncodeChunkedMessage(data []byte) []byte { 14 var buffer bytes.Buffer 15 binary.Write(&buffer, binary.BigEndian, uint16(len(data))) 16 buffer.Write(data) 17 return buffer.Bytes() 18 } 19 20 func DecodeLengthChunk(data []byte) int32 { 21 return int32(binary.BigEndian.Uint16(data)) 22 } 23 24 const ( 25 MessageTypeError = iota 26 MessageTypeProfileList 27 MessageTypeProfileContentRequest 28 MessageTypeProfileContent 29 ) 30 31 type ErrorMessage struct { 32 Message string 33 } 34 35 func (e *ErrorMessage) Encode() []byte { 36 var buffer bytes.Buffer 37 buffer.WriteByte(MessageTypeError) 38 rw.WriteVString(&buffer, e.Message) 39 return buffer.Bytes() 40 } 41 42 func DecodeErrorMessage(data []byte) (*ErrorMessage, error) { 43 reader := bytes.NewReader(data) 44 messageType, err := rw.ReadByte(reader) 45 if err != nil { 46 return nil, err 47 } 48 if messageType != MessageTypeError { 49 return nil, E.New("invalid message") 50 } 51 var message ErrorMessage 52 message.Message, err = rw.ReadVString(reader) 53 if err != nil { 54 return nil, err 55 } 56 return &message, nil 57 } 58 59 const ( 60 ProfileTypeLocal int32 = iota 61 ProfileTypeiCloud 62 ProfileTypeRemote 63 ) 64 65 type ProfilePreview struct { 66 ProfileID int64 67 Name string 68 Type int32 69 } 70 71 type ProfilePreviewIterator interface { 72 Next() *ProfilePreview 73 HasNext() bool 74 } 75 76 type ProfileEncoder struct { 77 profiles []ProfilePreview 78 } 79 80 func (e *ProfileEncoder) Append(profile *ProfilePreview) { 81 e.profiles = append(e.profiles, *profile) 82 } 83 84 func (e *ProfileEncoder) Encode() []byte { 85 var buffer bytes.Buffer 86 buffer.WriteByte(MessageTypeProfileList) 87 binary.Write(&buffer, binary.BigEndian, uint16(len(e.profiles))) 88 for _, preview := range e.profiles { 89 binary.Write(&buffer, binary.BigEndian, preview.ProfileID) 90 rw.WriteVString(&buffer, preview.Name) 91 binary.Write(&buffer, binary.BigEndian, preview.Type) 92 } 93 return buffer.Bytes() 94 } 95 96 type ProfileDecoder struct { 97 profiles []*ProfilePreview 98 } 99 100 func (d *ProfileDecoder) Decode(data []byte) error { 101 reader := bytes.NewReader(data) 102 messageType, err := reader.ReadByte() 103 if err != nil { 104 return err 105 } 106 if messageType != MessageTypeProfileList { 107 return E.New("invalid message") 108 } 109 var profileCount uint16 110 err = binary.Read(reader, binary.BigEndian, &profileCount) 111 if err != nil { 112 return err 113 } 114 for i := 0; i < int(profileCount); i++ { 115 var profile ProfilePreview 116 err = binary.Read(reader, binary.BigEndian, &profile.ProfileID) 117 if err != nil { 118 return err 119 } 120 profile.Name, err = rw.ReadVString(reader) 121 if err != nil { 122 return err 123 } 124 err = binary.Read(reader, binary.BigEndian, &profile.Type) 125 if err != nil { 126 return err 127 } 128 d.profiles = append(d.profiles, &profile) 129 } 130 return nil 131 } 132 133 func (d *ProfileDecoder) Iterator() ProfilePreviewIterator { 134 return newIterator(d.profiles) 135 } 136 137 type ProfileContentRequest struct { 138 ProfileID int64 139 } 140 141 func (r *ProfileContentRequest) Encode() []byte { 142 var buffer bytes.Buffer 143 buffer.WriteByte(MessageTypeProfileContentRequest) 144 binary.Write(&buffer, binary.BigEndian, r.ProfileID) 145 return buffer.Bytes() 146 } 147 148 func DecodeProfileContentRequest(data []byte) (*ProfileContentRequest, error) { 149 reader := bytes.NewReader(data) 150 messageType, err := rw.ReadByte(reader) 151 if err != nil { 152 return nil, err 153 } 154 if messageType != MessageTypeProfileContentRequest { 155 return nil, E.New("invalid message") 156 } 157 var request ProfileContentRequest 158 err = binary.Read(reader, binary.BigEndian, &request.ProfileID) 159 if err != nil { 160 return nil, err 161 } 162 return &request, nil 163 } 164 165 type ProfileContent struct { 166 Name string 167 Type int32 168 Config string 169 RemotePath string 170 AutoUpdate bool 171 AutoUpdateInterval int32 172 LastUpdated int64 173 } 174 175 func (c *ProfileContent) Encode() []byte { 176 buffer := new(bytes.Buffer) 177 buffer.WriteByte(MessageTypeProfileContent) 178 buffer.WriteByte(1) 179 writer := gzip.NewWriter(buffer) 180 rw.WriteVString(writer, c.Name) 181 binary.Write(writer, binary.BigEndian, c.Type) 182 rw.WriteVString(writer, c.Config) 183 if c.Type != ProfileTypeLocal { 184 rw.WriteVString(writer, c.RemotePath) 185 } 186 if c.Type == ProfileTypeRemote { 187 binary.Write(writer, binary.BigEndian, c.AutoUpdate) 188 binary.Write(writer, binary.BigEndian, c.AutoUpdateInterval) 189 binary.Write(writer, binary.BigEndian, c.LastUpdated) 190 } 191 writer.Flush() 192 writer.Close() 193 return buffer.Bytes() 194 } 195 196 func DecodeProfileContent(data []byte) (*ProfileContent, error) { 197 var reader io.Reader = bytes.NewReader(data) 198 messageType, err := rw.ReadByte(reader) 199 if err != nil { 200 return nil, err 201 } 202 if messageType != MessageTypeProfileContent { 203 return nil, E.New("invalid message") 204 } 205 version, err := rw.ReadByte(reader) 206 if err != nil { 207 return nil, err 208 } 209 reader, err = gzip.NewReader(reader) 210 if err != nil { 211 return nil, E.Cause(err, "unsupported profile") 212 } 213 var content ProfileContent 214 content.Name, err = rw.ReadVString(reader) 215 if err != nil { 216 return nil, err 217 } 218 err = binary.Read(reader, binary.BigEndian, &content.Type) 219 if err != nil { 220 return nil, err 221 } 222 content.Config, err = rw.ReadVString(reader) 223 if err != nil { 224 return nil, err 225 } 226 if content.Type != ProfileTypeLocal { 227 content.RemotePath, err = rw.ReadVString(reader) 228 if err != nil { 229 return nil, err 230 } 231 } 232 if content.Type == ProfileTypeRemote || (version == 0 && content.Type != ProfileTypeLocal) { 233 err = binary.Read(reader, binary.BigEndian, &content.AutoUpdate) 234 if err != nil { 235 return nil, err 236 } 237 if version >= 1 { 238 err = binary.Read(reader, binary.BigEndian, &content.AutoUpdateInterval) 239 if err != nil { 240 return nil, err 241 } 242 } 243 err = binary.Read(reader, binary.BigEndian, &content.LastUpdated) 244 if err != nil { 245 return nil, err 246 } 247 } 248 return &content, nil 249 }