mirror of
https://github.com/SagerNet/sing-box.git
synced 2024-12-25 14:55:38 +08:00
956ee361df
Co-authored-by: x_123 <x@a>
253 lines
6.0 KiB
Go
253 lines
6.0 KiB
Go
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
|
|
}
|