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  }