123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252 |
- package libbox
- import (
- "bufio"
- "bytes"
- "compress/gzip"
- "encoding/binary"
- E "github.com/sagernet/sing/common/exceptions"
- "github.com/sagernet/sing/common/varbin"
- )
- func EncodeChunkedMessage(data []byte) []byte {
- var buffer bytes.Buffer
- binary.Write(&buffer, binary.BigEndian, uint16(len(data)))
- buffer.Write(data)
- return buffer.Bytes()
- }
- func DecodeLengthChunk(data []byte) int32 {
- return int32(binary.BigEndian.Uint16(data))
- }
- const (
- MessageTypeError = iota
- MessageTypeProfileList
- MessageTypeProfileContentRequest
- MessageTypeProfileContent
- )
- type ErrorMessage struct {
- Message string
- }
- func (e *ErrorMessage) Encode() []byte {
- var buffer bytes.Buffer
- buffer.WriteByte(MessageTypeError)
- varbin.Write(&buffer, binary.BigEndian, e.Message)
- return buffer.Bytes()
- }
- func DecodeErrorMessage(data []byte) (*ErrorMessage, error) {
- reader := bytes.NewReader(data)
- messageType, err := reader.ReadByte()
- if err != nil {
- return nil, err
- }
- if messageType != MessageTypeError {
- return nil, E.New("invalid message")
- }
- var message ErrorMessage
- message.Message, err = varbin.ReadValue[string](reader, binary.BigEndian)
- if err != nil {
- return nil, err
- }
- return &message, nil
- }
- const (
- ProfileTypeLocal int32 = iota
- ProfileTypeiCloud
- ProfileTypeRemote
- )
- type ProfilePreview struct {
- ProfileID int64
- Name string
- Type int32
- }
- type ProfilePreviewIterator interface {
- Next() *ProfilePreview
- HasNext() bool
- }
- type ProfileEncoder struct {
- profiles []ProfilePreview
- }
- func (e *ProfileEncoder) Append(profile *ProfilePreview) {
- e.profiles = append(e.profiles, *profile)
- }
- func (e *ProfileEncoder) Encode() []byte {
- var buffer bytes.Buffer
- buffer.WriteByte(MessageTypeProfileList)
- binary.Write(&buffer, binary.BigEndian, uint16(len(e.profiles)))
- for _, preview := range e.profiles {
- binary.Write(&buffer, binary.BigEndian, preview.ProfileID)
- varbin.Write(&buffer, binary.BigEndian, preview.Name)
- binary.Write(&buffer, binary.BigEndian, preview.Type)
- }
- return buffer.Bytes()
- }
- type ProfileDecoder struct {
- profiles []*ProfilePreview
- }
- func (d *ProfileDecoder) Decode(data []byte) error {
- reader := bytes.NewReader(data)
- messageType, err := reader.ReadByte()
- if err != nil {
- return err
- }
- if messageType != MessageTypeProfileList {
- return E.New("invalid message")
- }
- var profileCount uint16
- err = binary.Read(reader, binary.BigEndian, &profileCount)
- if err != nil {
- return err
- }
- for i := 0; i < int(profileCount); i++ {
- var profile ProfilePreview
- err = binary.Read(reader, binary.BigEndian, &profile.ProfileID)
- if err != nil {
- return err
- }
- profile.Name, err = varbin.ReadValue[string](reader, binary.BigEndian)
- if err != nil {
- return err
- }
- err = binary.Read(reader, binary.BigEndian, &profile.Type)
- if err != nil {
- return err
- }
- d.profiles = append(d.profiles, &profile)
- }
- return nil
- }
- func (d *ProfileDecoder) Iterator() ProfilePreviewIterator {
- return newIterator(d.profiles)
- }
- type ProfileContentRequest struct {
- ProfileID int64
- }
- func (r *ProfileContentRequest) Encode() []byte {
- var buffer bytes.Buffer
- buffer.WriteByte(MessageTypeProfileContentRequest)
- binary.Write(&buffer, binary.BigEndian, r.ProfileID)
- return buffer.Bytes()
- }
- func DecodeProfileContentRequest(data []byte) (*ProfileContentRequest, error) {
- reader := bytes.NewReader(data)
- messageType, err := reader.ReadByte()
- if err != nil {
- return nil, err
- }
- if messageType != MessageTypeProfileContentRequest {
- return nil, E.New("invalid message")
- }
- var request ProfileContentRequest
- err = binary.Read(reader, binary.BigEndian, &request.ProfileID)
- if err != nil {
- return nil, err
- }
- return &request, nil
- }
- type ProfileContent struct {
- Name string
- Type int32
- Config string
- RemotePath string
- AutoUpdate bool
- AutoUpdateInterval int32
- LastUpdated int64
- }
- func (c *ProfileContent) Encode() []byte {
- buffer := new(bytes.Buffer)
- buffer.WriteByte(MessageTypeProfileContent)
- buffer.WriteByte(1)
- gWriter := gzip.NewWriter(buffer)
- writer := bufio.NewWriter(gWriter)
- varbin.Write(writer, binary.BigEndian, c.Name)
- binary.Write(writer, binary.BigEndian, c.Type)
- varbin.Write(writer, binary.BigEndian, c.Config)
- if c.Type != ProfileTypeLocal {
- varbin.Write(writer, binary.BigEndian, c.RemotePath)
- }
- if c.Type == ProfileTypeRemote {
- binary.Write(writer, binary.BigEndian, c.AutoUpdate)
- binary.Write(writer, binary.BigEndian, c.AutoUpdateInterval)
- binary.Write(writer, binary.BigEndian, c.LastUpdated)
- }
- writer.Flush()
- gWriter.Flush()
- gWriter.Close()
- return buffer.Bytes()
- }
- func DecodeProfileContent(data []byte) (*ProfileContent, error) {
- reader := bytes.NewReader(data)
- messageType, err := reader.ReadByte()
- if err != nil {
- return nil, err
- }
- if messageType != MessageTypeProfileContent {
- return nil, E.New("invalid message")
- }
- version, err := reader.ReadByte()
- if err != nil {
- return nil, err
- }
- gReader, err := gzip.NewReader(reader)
- if err != nil {
- return nil, E.Cause(err, "unsupported profile")
- }
- bReader := varbin.StubReader(gReader)
- var content ProfileContent
- content.Name, err = varbin.ReadValue[string](bReader, binary.BigEndian)
- if err != nil {
- return nil, err
- }
- err = binary.Read(bReader, binary.BigEndian, &content.Type)
- if err != nil {
- return nil, err
- }
- content.Config, err = varbin.ReadValue[string](bReader, binary.BigEndian)
- if err != nil {
- return nil, err
- }
- if content.Type != ProfileTypeLocal {
- content.RemotePath, err = varbin.ReadValue[string](bReader, binary.BigEndian)
- if err != nil {
- return nil, err
- }
- }
- if content.Type == ProfileTypeRemote || (version == 0 && content.Type != ProfileTypeLocal) {
- err = binary.Read(bReader, binary.BigEndian, &content.AutoUpdate)
- if err != nil {
- return nil, err
- }
- if version >= 1 {
- err = binary.Read(bReader, binary.BigEndian, &content.AutoUpdateInterval)
- if err != nil {
- return nil, err
- }
- }
- err = binary.Read(bReader, binary.BigEndian, &content.LastUpdated)
- if err != nil {
- return nil, err
- }
- }
- return &content, nil
- }
|