github.com/inazumav/sing-box@v0.0.0-20230926072359-ab51429a14f1/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 LastUpdated int64 172 } 173 174 func (c *ProfileContent) Encode() []byte { 175 buffer := new(bytes.Buffer) 176 buffer.WriteByte(MessageTypeProfileContent) 177 buffer.WriteByte(0) 178 writer := gzip.NewWriter(buffer) 179 rw.WriteVString(writer, c.Name) 180 binary.Write(writer, binary.BigEndian, c.Type) 181 rw.WriteVString(writer, c.Config) 182 if c.Type != ProfileTypeLocal { 183 rw.WriteVString(writer, c.RemotePath) 184 binary.Write(writer, binary.BigEndian, c.AutoUpdate) 185 binary.Write(writer, binary.BigEndian, c.LastUpdated) 186 } 187 writer.Flush() 188 writer.Close() 189 return buffer.Bytes() 190 } 191 192 func DecodeProfileContent(data []byte) (*ProfileContent, error) { 193 var reader io.Reader = bytes.NewReader(data) 194 messageType, err := rw.ReadByte(reader) 195 if err != nil { 196 return nil, err 197 } 198 if messageType != MessageTypeProfileContent { 199 return nil, E.New("invalid message") 200 } 201 version, err := rw.ReadByte(reader) 202 if err != nil { 203 return nil, err 204 } 205 if version == 0 { 206 reader, err = gzip.NewReader(reader) 207 if err != nil { 208 return nil, E.Cause(err, "unsupported profile") 209 } 210 } else { 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 err = binary.Read(reader, binary.BigEndian, &content.AutoUpdate) 232 if err != nil { 233 return nil, err 234 } 235 err = binary.Read(reader, binary.BigEndian, &content.LastUpdated) 236 if err != nil { 237 return nil, err 238 } 239 } 240 return &content, nil 241 }