fix(gateway): use registered control flows
This commit is contained in:
@@ -46,10 +46,7 @@ func ValidateGatewayClipboard(policy protocol.ClipboardPolicy, value protocol.Ga
|
|||||||
if err := policy.Validate(); err != nil || value.Validate() != nil || !utf8.ValidString(value.Text) {
|
if err := policy.Validate(); err != nil || value.Validate() != nil || !utf8.ValidString(value.Text) {
|
||||||
return ErrProviderMalformed
|
return ErrProviderMalformed
|
||||||
}
|
}
|
||||||
if len(value.Text) > int(policy.MaxTextBytes) || len(value.LoopToken) < 16 || len(value.LoopToken) > 128 {
|
if len(value.Text) > int(policy.MaxTextBytes) {
|
||||||
return ErrProviderMalformed
|
|
||||||
}
|
|
||||||
if _, err := base64.RawURLEncoding.DecodeString(value.LoopToken); err != nil {
|
|
||||||
return ErrProviderMalformed
|
return ErrProviderMalformed
|
||||||
}
|
}
|
||||||
switch value.Direction {
|
switch value.Direction {
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ func TestValidateGatewayClipboardEnforcesServerOwnedPolicy(t *testing.T) {
|
|||||||
if err := ValidateGatewayClipboard(policy, valid); err != nil {
|
if err := ValidateGatewayClipboard(policy, valid); err != nil {
|
||||||
t.Fatalf("ValidateGatewayClipboard() valid text = %v", err)
|
t.Fatalf("ValidateGatewayClipboard() valid text = %v", err)
|
||||||
}
|
}
|
||||||
if err := ValidateGatewayClipboard(policy, protocol.GatewayClipboardText{Direction: "client_to_provider", Text: "hello", Encoding: "utf-8", LoopToken: "not base64url!"}); err == nil {
|
if err := ValidateGatewayClipboard(policy, protocol.GatewayClipboardText{Direction: "client_to_provider", Text: "hello", Encoding: "utf-8", LoopToken: "!!!!!!!!!!!!!!!!"}); err == nil {
|
||||||
t.Fatal("ValidateGatewayClipboard() accepted malformed loop token")
|
t.Fatal("ValidateGatewayClipboard() accepted malformed loop token")
|
||||||
}
|
}
|
||||||
if err := ValidateGatewayClipboard(policy, protocol.GatewayClipboardText{Direction: "client_to_provider", Text: strings.Repeat("x", 6), Encoding: "utf-8", LoopToken: "abcdefghijklmnop"}); err == nil {
|
if err := ValidateGatewayClipboard(policy, protocol.GatewayClipboardText{Direction: "client_to_provider", Text: strings.Repeat("x", 6), Encoding: "utf-8", LoopToken: "abcdefghijklmnop"}); err == nil {
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import (
|
|||||||
"crypto/tls"
|
"crypto/tls"
|
||||||
"crypto/x509"
|
"crypto/x509"
|
||||||
"crypto/x509/pkix"
|
"crypto/x509/pkix"
|
||||||
|
"encoding/base64"
|
||||||
"encoding/hex"
|
"encoding/hex"
|
||||||
"errors"
|
"errors"
|
||||||
"math/big"
|
"math/big"
|
||||||
@@ -320,6 +321,197 @@ func TestAdmissionQUICMTLSRelayAndCleanup(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestRegisteredChannelFramesTraversePublicTransport(t *testing.T) {
|
||||||
|
h := newGatewayTransportHarness(t)
|
||||||
|
|
||||||
|
input, err := EncodeInputEvent(InputEvent{Sequence: 7, Device: "keyboard", Code: 7, Pressed: true})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
feedback, err := EncodeClientFeedback(Feedback{Sequence: 8, Kind: FeedbackIDR})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
clipboard, err := protocol.EncodeGatewayClipboardText(protocol.GatewayClipboardText{Direction: "client_to_provider", Text: "registered clipboard", Encoding: "utf-8", LoopToken: "abcdefghijklmnop"})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
for _, frame := range []protocol.ChannelFrame{
|
||||||
|
testChannelFrame("input.sequenced.v1", 7, input),
|
||||||
|
testChannelFrame("control.ack.v1", 8, feedback),
|
||||||
|
testChannelFrame("clipboard.text.v1", 9, clipboard),
|
||||||
|
} {
|
||||||
|
encoded, encodeErr := protocol.EncodeChannelFrame(frame)
|
||||||
|
if encodeErr != nil {
|
||||||
|
t.Fatal(encodeErr)
|
||||||
|
}
|
||||||
|
if writeErr := h.client.writeControl(encoded); writeErr != nil {
|
||||||
|
t.Fatal(writeErr)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
select {
|
||||||
|
case value := <-h.session.clipboardWrites:
|
||||||
|
if value != "registered clipboard" {
|
||||||
|
t.Fatalf("provider clipboard = %q", value)
|
||||||
|
}
|
||||||
|
case <-time.After(time.Second):
|
||||||
|
t.Fatal("registered clipboard flow was not forwarded")
|
||||||
|
}
|
||||||
|
deadline := time.Now().Add(time.Second)
|
||||||
|
for {
|
||||||
|
h.session.mu.Lock()
|
||||||
|
inputs := append([]InputEvent(nil), h.session.inputs...)
|
||||||
|
feedbacks := append([]Feedback(nil), h.session.feedback...)
|
||||||
|
h.session.mu.Unlock()
|
||||||
|
if len(inputs) == 1 && inputs[0].Sequence == 7 && len(feedbacks) == 1 && feedbacks[0].Sequence == 8 && feedbacks[0].Kind == FeedbackIDR {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
if time.Now().After(deadline) {
|
||||||
|
t.Fatalf("registered flows inputs=%#v feedback=%#v", inputs, feedbacks)
|
||||||
|
}
|
||||||
|
time.Sleep(time.Millisecond)
|
||||||
|
}
|
||||||
|
|
||||||
|
h.session.EmitEvent(ProviderEvent{Kind: ProviderEventRumble, Payload: []byte{1, 2, 3, 4, 5}})
|
||||||
|
eventCtx, cancelEvent := context.WithTimeout(context.Background(), time.Second)
|
||||||
|
event, err := h.client.ReceiveProviderEvent(eventCtx)
|
||||||
|
cancelEvent()
|
||||||
|
if err != nil || event.Kind != ProviderEventRumble || string(event.Payload) != string([]byte{1, 2, 3, 4, 5}) {
|
||||||
|
t.Fatalf("registered provider feedback = %#v, %v", event, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
h.session.mu.Lock()
|
||||||
|
h.session.clipboard = "registered provider clipboard"
|
||||||
|
h.session.mu.Unlock()
|
||||||
|
clipboardCtx, cancelClipboard := context.WithTimeout(context.Background(), time.Second)
|
||||||
|
value, err := h.client.ReceiveClipboard(clipboardCtx)
|
||||||
|
cancelClipboard()
|
||||||
|
if err != nil || value.Text != "registered provider clipboard" {
|
||||||
|
t.Fatalf("registered provider clipboard = %#v, %v", value, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestPrivateChannelAliasesAreRejectedOnPublicTransport(t *testing.T) {
|
||||||
|
for _, test := range []struct {
|
||||||
|
alias string
|
||||||
|
payload func(t *testing.T) []byte
|
||||||
|
}{
|
||||||
|
{"control", func(t *testing.T) []byte {
|
||||||
|
t.Helper()
|
||||||
|
value, err := EncodeClientFeedback(Feedback{Kind: FeedbackIDR})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
return value
|
||||||
|
}},
|
||||||
|
{"input", func(t *testing.T) []byte {
|
||||||
|
t.Helper()
|
||||||
|
value, err := EncodeInputEvent(InputEvent{Device: "keyboard", Code: 7, Pressed: true})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
return value
|
||||||
|
}},
|
||||||
|
{"clipboard", func(t *testing.T) []byte {
|
||||||
|
t.Helper()
|
||||||
|
value, err := protocol.EncodeGatewayClipboardText(protocol.GatewayClipboardText{Direction: "client_to_provider", Text: "alias", Encoding: "utf-8", LoopToken: "abcdefghijklmnop"})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
return value
|
||||||
|
}},
|
||||||
|
} {
|
||||||
|
t.Run(test.alias, func(t *testing.T) {
|
||||||
|
h := newGatewayTransportHarness(t)
|
||||||
|
encoded, err := protocol.EncodeChannelFrame(testChannelFrame(test.alias, 1, test.payload(t)))
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if err := h.client.writeControl(encoded); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
h.waitReleased(t)
|
||||||
|
h.session.mu.Lock()
|
||||||
|
defer h.session.mu.Unlock()
|
||||||
|
if len(h.session.inputs) != 0 || len(h.session.feedback) != 0 {
|
||||||
|
t.Fatalf("alias reached provider: inputs=%#v feedback=%#v", h.session.inputs, h.session.feedback)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestProviderClipboardAuditWaitsForPublicTransportDelivery(t *testing.T) {
|
||||||
|
h := newGatewayTransportHarness(t)
|
||||||
|
h.client.control.CancelRead(0)
|
||||||
|
h.session.mu.Lock()
|
||||||
|
h.session.clipboard = "undeliverable clipboard"
|
||||||
|
h.session.mu.Unlock()
|
||||||
|
|
||||||
|
h.waitReleased(t)
|
||||||
|
for _, audit := range h.reporter.Audits() {
|
||||||
|
if audit.Direction == "provider_to_client" && audit.Outcome == "forwarded" {
|
||||||
|
t.Fatalf("failed delivery was audited as forwarded: %#v", audit)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func testChannelFrame(flowID string, sequence int64, payload []byte) protocol.ChannelFrame {
|
||||||
|
return protocol.ChannelFrame{Version: "1", FlowID: flowID, Sequence: sequence, Flags: 0, FragmentIndex: 0, FragmentCount: 1, TimestampMs: time.Now().UnixMilli(), Payload: base64.StdEncoding.EncodeToString(payload)}
|
||||||
|
}
|
||||||
|
|
||||||
|
type gatewayTransportHarness struct {
|
||||||
|
client *Client
|
||||||
|
session *fakeSession
|
||||||
|
admission *oneTimeAdmission
|
||||||
|
reporter *recordingProviderStateReporter
|
||||||
|
}
|
||||||
|
|
||||||
|
func newGatewayTransportHarness(t *testing.T) gatewayTransportHarness {
|
||||||
|
t.Helper()
|
||||||
|
serverTLS, clientTLS := testTLS(t)
|
||||||
|
fake := NewFakeApollo(FakeApolloConfig{Now: time.Date(2026, time.January, 1, 0, 0, 0, 0, time.UTC)})
|
||||||
|
authority := protocol.SessionAuthority{Version: "1", SessionID: "session-transport", GatewayID: "gateway-1", Audience: "versevdi-gateway", ReconnectSequence: 0, ExpiresAt: time.Now().Add(5 * time.Second).UTC().Format(time.RFC3339Nano), Capabilities: DefaultCapabilities(), ProviderProfile: ProviderProfileApollo, ProviderIdentity: fake.config.Identity.Key()}
|
||||||
|
admission := &oneTimeAdmission{authority: authority, released: make(chan struct{})}
|
||||||
|
reporter := &recordingProviderStateReporter{}
|
||||||
|
server, err := NewServer(ServerConfig{ListenAddress: "127.0.0.1:0", TLSConfig: serverTLS, GatewayID: "gateway-1", Capabilities: DefaultCapabilities(), ProviderCapabilities: DefaultCapabilities(), Admission: admission, ProviderStateReporter: reporter, ClipboardAuditReporter: reporter, Provider: fake})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
ctx, cancel := context.WithCancel(context.Background())
|
||||||
|
serveDone := make(chan error, 1)
|
||||||
|
go func() { serveDone <- server.Serve(ctx) }()
|
||||||
|
request := protocol.TunnelAdmissionRequest{Version: "1", SessionID: authority.SessionID, GatewayID: authority.GatewayID, Audience: authority.Audience, Grant: strings.Repeat("g", 64), ReconnectSequence: 0, ClientNonce: "nonce-0000000001", DeviceSignature: strings.Repeat("s", 86), Capabilities: DefaultCapabilities()}
|
||||||
|
client, err := Dial(context.Background(), server.Addr().String(), clientTLS, request)
|
||||||
|
if err != nil {
|
||||||
|
cancel()
|
||||||
|
_ = server.Close()
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
session, ok := fake.LastSession().(*fakeSession)
|
||||||
|
if !ok {
|
||||||
|
t.Fatal("fake provider session type")
|
||||||
|
}
|
||||||
|
t.Cleanup(func() {
|
||||||
|
_ = client.Close()
|
||||||
|
cancel()
|
||||||
|
_ = server.Close()
|
||||||
|
if err := <-serveDone; err != nil {
|
||||||
|
t.Errorf("serve: %v", err)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
return gatewayTransportHarness{client: client, session: session, admission: admission, reporter: reporter}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h gatewayTransportHarness) waitReleased(t *testing.T) {
|
||||||
|
t.Helper()
|
||||||
|
select {
|
||||||
|
case <-h.admission.released:
|
||||||
|
case <-time.After(2 * time.Second):
|
||||||
|
t.Fatal("gateway did not reject the channel")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func testTLS(t *testing.T) (*tls.Config, *tls.Config) {
|
func testTLS(t *testing.T) (*tls.Config, *tls.Config) {
|
||||||
t.Helper()
|
t.Helper()
|
||||||
caKey, _ := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
|
caKey, _ := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
|
||||||
|
|||||||
+17
-14
@@ -23,6 +23,9 @@ const (
|
|||||||
defaultControlLimit = 128 * 1024
|
defaultControlLimit = 128 * 1024
|
||||||
clientControlBacklog = 64
|
clientControlBacklog = 64
|
||||||
applicationError = quic.ApplicationErrorCode(0x100)
|
applicationError = quic.ApplicationErrorCode(0x100)
|
||||||
|
controlFlowID = "control.ack.v1"
|
||||||
|
inputFlowID = "input.sequenced.v1"
|
||||||
|
clipboardFlowID = "clipboard.text.v1"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
@@ -446,11 +449,11 @@ func (s *gatewaySession) clipboardLoop() {
|
|||||||
}
|
}
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if err := s.reportClipboardAudit(value.Direction, "forwarded", clipboardAuditTextBytes(value.Text), "forwarded"); err != nil {
|
if err := s.sendClipboard(value); err != nil {
|
||||||
s.result <- err
|
s.result <- err
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if err := s.sendClipboard(value); err != nil {
|
if err := s.reportClipboardAudit(value.Direction, "forwarded", clipboardAuditTextBytes(value.Text), "forwarded"); err != nil {
|
||||||
s.result <- err
|
s.result <- err
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -499,7 +502,7 @@ func (s *gatewaySession) controlLoop() {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
switch frame.FlowID {
|
switch frame.FlowID {
|
||||||
case "control":
|
case controlFlowID:
|
||||||
sequence, sequenceErr := channelSequence(frame.Sequence)
|
sequence, sequenceErr := channelSequence(frame.Sequence)
|
||||||
if sequenceErr != nil {
|
if sequenceErr != nil {
|
||||||
s.result <- sequenceErr
|
s.result <- sequenceErr
|
||||||
@@ -509,7 +512,7 @@ func (s *gatewaySession) controlLoop() {
|
|||||||
s.result <- err
|
s.result <- err
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
case "input":
|
case inputFlowID:
|
||||||
sequence, sequenceErr := channelSequence(frame.Sequence)
|
sequence, sequenceErr := channelSequence(frame.Sequence)
|
||||||
if sequenceErr != nil {
|
if sequenceErr != nil {
|
||||||
s.result <- sequenceErr
|
s.result <- sequenceErr
|
||||||
@@ -519,7 +522,7 @@ func (s *gatewaySession) controlLoop() {
|
|||||||
s.result <- err
|
s.result <- err
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
case "clipboard":
|
case clipboardFlowID:
|
||||||
value, decodeErr := protocol.DecodeGatewayClipboardText(payload)
|
value, decodeErr := protocol.DecodeGatewayClipboardText(payload)
|
||||||
if decodeErr != nil {
|
if decodeErr != nil {
|
||||||
s.result <- ErrProviderMalformed
|
s.result <- ErrProviderMalformed
|
||||||
@@ -625,7 +628,7 @@ func (s *gatewaySession) sendControl(sequence uint32, payload []byte) error {
|
|||||||
if len(payload) > 1024 {
|
if len(payload) > 1024 {
|
||||||
return ErrFramePayloadLimit
|
return ErrFramePayloadLimit
|
||||||
}
|
}
|
||||||
frame := protocol.ChannelFrame{Version: "1", FlowID: "control", Sequence: int64(sequence), Flags: 0, FragmentIndex: 0, FragmentCount: 1, TimestampMs: time.Now().UnixMilli(), Payload: base64.StdEncoding.EncodeToString(payload)}
|
frame := protocol.ChannelFrame{Version: "1", FlowID: controlFlowID, Sequence: int64(sequence), Flags: 0, FragmentIndex: 0, FragmentCount: 1, TimestampMs: time.Now().UnixMilli(), Payload: base64.StdEncoding.EncodeToString(payload)}
|
||||||
encoded, err := protocol.EncodeChannelFrame(frame)
|
encoded, err := protocol.EncodeChannelFrame(frame)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@@ -640,7 +643,7 @@ func (s *gatewaySession) sendClipboard(value protocol.GatewayClipboardText) erro
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
frame := protocol.ChannelFrame{Version: "1", FlowID: "clipboard", Sequence: int64(s.sequence.Add(1)), Flags: 0, FragmentIndex: 0, FragmentCount: 1, TimestampMs: time.Now().UnixMilli(), Payload: base64.StdEncoding.EncodeToString(payload)}
|
frame := protocol.ChannelFrame{Version: "1", FlowID: clipboardFlowID, Sequence: int64(s.sequence.Add(1)), Flags: 0, FragmentIndex: 0, FragmentCount: 1, TimestampMs: time.Now().UnixMilli(), Payload: base64.StdEncoding.EncodeToString(payload)}
|
||||||
encoded, err := protocol.EncodeChannelFrame(frame)
|
encoded, err := protocol.EncodeChannelFrame(frame)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@@ -899,7 +902,7 @@ func (c *Client) SendInput(event InputEvent) error {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
frame := protocol.ChannelFrame{Version: "1", FlowID: "input", Sequence: int64(event.Sequence), Flags: 0, FragmentIndex: 0, FragmentCount: 1, TimestampMs: time.Now().UnixMilli(), Payload: base64.StdEncoding.EncodeToString(payload)}
|
frame := protocol.ChannelFrame{Version: "1", FlowID: inputFlowID, Sequence: int64(event.Sequence), Flags: 0, FragmentIndex: 0, FragmentCount: 1, TimestampMs: time.Now().UnixMilli(), Payload: base64.StdEncoding.EncodeToString(payload)}
|
||||||
encoded, err := protocol.EncodeChannelFrame(frame)
|
encoded, err := protocol.EncodeChannelFrame(frame)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@@ -924,7 +927,7 @@ func (c *Client) SendClipboard(value protocol.GatewayClipboardText) error {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
frame := protocol.ChannelFrame{Version: "1", FlowID: "clipboard", Sequence: 0, Flags: 0, FragmentIndex: 0, FragmentCount: 1, TimestampMs: time.Now().UnixMilli(), Payload: base64.StdEncoding.EncodeToString(payload)}
|
frame := protocol.ChannelFrame{Version: "1", FlowID: clipboardFlowID, Sequence: 0, Flags: 0, FragmentIndex: 0, FragmentCount: 1, TimestampMs: time.Now().UnixMilli(), Payload: base64.StdEncoding.EncodeToString(payload)}
|
||||||
encoded, err := protocol.EncodeChannelFrame(frame)
|
encoded, err := protocol.EncodeChannelFrame(frame)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@@ -933,7 +936,7 @@ func (c *Client) SendClipboard(value protocol.GatewayClipboardText) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (c *Client) sendControl(sequence uint32, payload []byte) error {
|
func (c *Client) sendControl(sequence uint32, payload []byte) error {
|
||||||
frame := protocol.ChannelFrame{Version: "1", FlowID: "control", Sequence: int64(sequence), Flags: 0, FragmentIndex: 0, FragmentCount: 1, TimestampMs: time.Now().UnixMilli(), Payload: base64.StdEncoding.EncodeToString(payload)}
|
frame := protocol.ChannelFrame{Version: "1", FlowID: controlFlowID, Sequence: int64(sequence), Flags: 0, FragmentIndex: 0, FragmentCount: 1, TimestampMs: time.Now().UnixMilli(), Payload: base64.StdEncoding.EncodeToString(payload)}
|
||||||
encoded, err := protocol.EncodeChannelFrame(frame)
|
encoded, err := protocol.EncodeChannelFrame(frame)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@@ -956,7 +959,7 @@ func (c *Client) ReceiveFrame(ctx context.Context) (Frame, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (c *Client) ReceiveProviderEvent(ctx context.Context) (ProviderEvent, error) {
|
func (c *Client) ReceiveProviderEvent(ctx context.Context) (ProviderEvent, error) {
|
||||||
payload, err := c.receiveControlPayload(ctx, "control")
|
payload, err := c.receiveControlPayload(ctx, controlFlowID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return ProviderEvent{}, err
|
return ProviderEvent{}, err
|
||||||
}
|
}
|
||||||
@@ -967,7 +970,7 @@ func (c *Client) ReceiveProviderEvent(ctx context.Context) (ProviderEvent, error
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (c *Client) ReceiveClipboard(ctx context.Context) (protocol.GatewayClipboardText, error) {
|
func (c *Client) ReceiveClipboard(ctx context.Context) (protocol.GatewayClipboardText, error) {
|
||||||
payload, err := c.receiveControlPayload(ctx, "clipboard")
|
payload, err := c.receiveControlPayload(ctx, clipboardFlowID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return protocol.GatewayClipboardText{}, err
|
return protocol.GatewayClipboardText{}, err
|
||||||
}
|
}
|
||||||
@@ -975,7 +978,7 @@ func (c *Client) ReceiveClipboard(ctx context.Context) (protocol.GatewayClipboar
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (c *Client) receiveControlPayload(ctx context.Context, flowID string) ([]byte, error) {
|
func (c *Client) receiveControlPayload(ctx context.Context, flowID string) ([]byte, error) {
|
||||||
if c == nil || c.control == nil || (flowID != "control" && flowID != "clipboard") {
|
if c == nil || c.control == nil || (flowID != controlFlowID && flowID != clipboardFlowID) {
|
||||||
return nil, ErrProviderMalformed
|
return nil, ErrProviderMalformed
|
||||||
}
|
}
|
||||||
c.controlReadMu.Lock()
|
c.controlReadMu.Lock()
|
||||||
@@ -1000,7 +1003,7 @@ func (c *Client) receiveControlPayload(ctx context.Context, flowID string) ([]by
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
frame, err := protocol.DecodeChannelFrame(data)
|
frame, err := protocol.DecodeChannelFrame(data)
|
||||||
if err != nil || (frame.FlowID != "control" && frame.FlowID != "clipboard") {
|
if err != nil || (frame.FlowID != controlFlowID && frame.FlowID != clipboardFlowID) {
|
||||||
return nil, ErrProviderMalformed
|
return nil, ErrProviderMalformed
|
||||||
}
|
}
|
||||||
payload, err := base64.StdEncoding.DecodeString(frame.Payload)
|
payload, err := base64.StdEncoding.DecodeString(frame.Payload)
|
||||||
|
|||||||
Reference in New Issue
Block a user