package failover import ( "encoding/binary" "io" "github.com/gofrs/uuid/v5" "github.com/sagernet/sing/common" "github.com/sagernet/sing/common/buf" E "github.com/sagernet/sing/common/exceptions" M "github.com/sagernet/sing/common/metadata" ) const ( Version = 0 BufferSize = 10 ) const ( CommandTCP = 1 CommandReconnect = 2 ) const ( StatusOK uint8 = iota + 1 StatusSessionNotFound ) var ( SessionClosed = E.New("session closed") SessionNotFound = E.New("session not found") SessionExpired = E.New("session expired") SessionBroken = E.New("session broken") ) var Destination = M.Socksaddr{ Fqdn: "sp.failover.sing-box.arpa", Port: 444, } var AddressSerializer = M.NewSerializer( M.AddressFamilyByte(0x01, M.AddressFamilyIPv4), M.AddressFamilyByte(0x03, M.AddressFamilyIPv6), M.AddressFamilyByte(0x02, M.AddressFamilyFqdn), M.PortThenAddress(), ) type Request struct { UUID uuid.UUID Command byte Destination M.Socksaddr } func ReadRequest(reader io.Reader) (*Request, error) { var request Request var version uint8 err := binary.Read(reader, binary.BigEndian, &version) if err != nil { return nil, err } if version != Version { return nil, E.New("unknown version: ", version) } _, err = io.ReadFull(reader, request.UUID[:]) if err != nil { return nil, err } err = binary.Read(reader, binary.BigEndian, &request.Command) if err != nil { return nil, err } request.Destination, err = AddressSerializer.ReadAddrPort(reader) if err != nil { return nil, err } return &request, nil } func WriteRequest(writer io.Writer, request *Request) error { var requestLen int requestLen += 1 // version requestLen += 1 // command requestLen += 16 // UUID requestLen += AddressSerializer.AddrPortLen(request.Destination) buffer := buf.NewSize(requestLen) defer buffer.Release() common.Must( buffer.WriteByte(Version), common.Error(buffer.Write(request.UUID[:])), buffer.WriteByte(request.Command), ) err := AddressSerializer.WriteAddrPort(buffer, request.Destination) if err != nil { return err } return common.Error(writer.Write(buffer.Bytes())) }