From fc2333c93416ff446a3c25393e98f6cdf8f104f0 Mon Sep 17 00:00:00 2001 From: cthuang Date: Fri, 12 Nov 2021 09:37:28 +0000 Subject: [PATCH] TUN-5300: Define RPC to register UDP sessions --- connection/quic.go | 60 ++- connection/quic_test.go | 70 ++-- connection/udp_session.go | 43 +++ origin/tunnel.go | 1 + quic/quic_protocol.go | 244 +++++++++---- quic/quic_protocol_test.go | 107 +++++- tunnelrpc/pogs/sessionrpc.go | 118 ++++++ tunnelrpc/tunnelrpc.capnp | 8 + tunnelrpc/tunnelrpc.capnp.go | 685 ++++++++++++++++++++++++++--------- 9 files changed, 1035 insertions(+), 301 deletions(-) create mode 100644 connection/udp_session.go create mode 100644 tunnelrpc/pogs/sessionrpc.go diff --git a/connection/quic.go b/connection/quic.go index eb7513e8..1a050843 100644 --- a/connection/quic.go +++ b/connection/quic.go @@ -10,6 +10,7 @@ import ( "strconv" "strings" + "github.com/google/uuid" "github.com/lucas-clemente/quic-go" "github.com/pkg/errors" "github.com/rs/zerolog" @@ -34,6 +35,7 @@ type QUICConnection struct { httpProxy OriginProxy gracefulShutdownC <-chan struct{} stoppedGracefully bool + udpSessions *udpSessions } // NewQUICConnection returns a new instance of QUICConnection. @@ -64,9 +66,10 @@ func NewQUICConnection( } return &QUICConnection{ - session: session, - httpProxy: httpProxy, - logger: observer.log, + session: session, + httpProxy: httpProxy, + logger: observer.log, + udpSessions: newUDPSessions(), }, nil } @@ -99,7 +102,30 @@ func (q *QUICConnection) Close() { } func (q *QUICConnection) handleStream(stream quic.Stream) error { - connectRequest, err := quicpogs.ReadConnectRequestData(stream) + signature, err := quicpogs.DetermineProtocol(stream) + if err != nil { + return err + } + switch signature { + case quicpogs.DataStreamProtocolSignature: + reqServerStream, err := quicpogs.NewRequestServerStream(stream, signature) + if err != nil { + return nil + } + return q.handleDataStream(reqServerStream) + case quicpogs.RPCStreamProtocolSignature: + rpcStream, err := quicpogs.NewRPCServerStream(stream, signature) + if err != nil { + return err + } + return q.handleRPCStream(rpcStream) + default: + return fmt.Errorf("Unknown protocol %v", signature) + } +} + +func (q *QUICConnection) handleDataStream(stream *quicpogs.RequestServerStream) error { + connectRequest, err := stream.ReadConnectRequestData() if err != nil { return err } @@ -114,32 +140,38 @@ func (q *QUICConnection) handleStream(stream quic.Stream) error { w := newHTTPResponseAdapter(stream) return q.httpProxy.ProxyHTTP(w, req, connectRequest.Type == quicpogs.ConnectionTypeWebsocket) case quicpogs.ConnectionTypeTCP: - rwa := &streamReadWriteAcker{ - ReadWriter: stream, - } + rwa := &streamReadWriteAcker{stream} return q.httpProxy.ProxyTCP(context.Background(), rwa, &TCPRequest{Dest: connectRequest.Dest}) } return nil } +func (q *QUICConnection) handleRPCStream(rpcStream *quicpogs.RPCServerStream) error { + return rpcStream.Serve(q, q.logger) +} + +func (q *QUICConnection) RegisterUdpSession(ctx context.Context, sessionID uuid.UUID, dstIP net.IP, dstPort uint16) error { + return q.udpSessions.register(sessionID, dstIP, dstPort) +} + // streamReadWriteAcker is a light wrapper over QUIC streams with a callback to send response back to // the client. type streamReadWriteAcker struct { - io.ReadWriter + *quicpogs.RequestServerStream } // AckConnection acks response back to the proxy. func (s *streamReadWriteAcker) AckConnection() error { - return quicpogs.WriteConnectResponseData(s, nil) + return s.WriteConnectResponseData(nil) } // httpResponseAdapter translates responses written by the HTTP Proxy into ones that can be used in QUIC. type httpResponseAdapter struct { - io.Writer + *quicpogs.RequestServerStream } -func newHTTPResponseAdapter(w io.Writer) httpResponseAdapter { - return httpResponseAdapter{w} +func newHTTPResponseAdapter(s *quicpogs.RequestServerStream) httpResponseAdapter { + return httpResponseAdapter{s} } func (hrw httpResponseAdapter) WriteRespHeaders(status int, header http.Header) error { @@ -151,11 +183,11 @@ func (hrw httpResponseAdapter) WriteRespHeaders(status int, header http.Header) metadata = append(metadata, quicpogs.Metadata{Key: httpHeaderKey, Val: v}) } } - return quicpogs.WriteConnectResponseData(hrw, nil, metadata...) + return hrw.WriteConnectResponseData(nil, metadata...) } func (hrw httpResponseAdapter) WriteErrorResponse(err error) { - quicpogs.WriteConnectResponseData(hrw, err, quicpogs.Metadata{Key: "HttpStatus", Val: strconv.Itoa(http.StatusBadGateway)}) + hrw.WriteConnectResponseData(err, quicpogs.Metadata{Key: "HttpStatus", Val: strconv.Itoa(http.StatusBadGateway)}) } func buildHTTPRequest(connectRequest *quicpogs.ConnectRequest, body io.ReadCloser) (*http.Request, error) { diff --git a/connection/quic_test.go b/connection/quic_test.go index fc551a4b..1bc1a809 100644 --- a/connection/quic_test.go +++ b/connection/quic_test.go @@ -26,7 +26,6 @@ import ( "github.com/stretchr/testify/require" quicpogs "github.com/cloudflare/cloudflared/quic" - "github.com/cloudflare/cloudflared/tunnelrpc/pogs" tunnelpogs "github.com/cloudflare/cloudflared/tunnelrpc/pogs" ) @@ -76,15 +75,15 @@ func TestQUICServer(t *testing.T) { dest: "/ok", connectionType: quicpogs.ConnectionTypeHTTP, metadata: []quicpogs.Metadata{ - quicpogs.Metadata{ + { Key: "HttpHeader:Cf-Ray", Val: "123123123", }, - quicpogs.Metadata{ + { Key: "HttpHost", Val: "cf.host", }, - quicpogs.Metadata{ + { Key: "HttpMethod", Val: "GET", }, @@ -96,19 +95,19 @@ func TestQUICServer(t *testing.T) { dest: "/echo_body", connectionType: quicpogs.ConnectionTypeHTTP, metadata: []quicpogs.Metadata{ - quicpogs.Metadata{ + { Key: "HttpHeader:Cf-Ray", Val: "123123123", }, - quicpogs.Metadata{ + { Key: "HttpHost", Val: "cf.host", }, - quicpogs.Metadata{ + { Key: "HttpMethod", Val: "POST", }, - quicpogs.Metadata{ + { Key: "HttpHeader:Content-Length", Val: "24", }, @@ -121,19 +120,19 @@ func TestQUICServer(t *testing.T) { dest: "/ok", connectionType: quicpogs.ConnectionTypeWebsocket, metadata: []quicpogs.Metadata{ - quicpogs.Metadata{ + { Key: "HttpHeader:Cf-Cloudflared-Proxy-Connection-Upgrade", Val: "Websocket", }, - quicpogs.Metadata{ + { Key: "HttpHeader:Another-Header", Val: "Misc", }, - quicpogs.Metadata{ + { Key: "HttpHost", Val: "cf.host", }, - quicpogs.Metadata{ + { Key: "HttpMethod", Val: "get", }, @@ -171,7 +170,7 @@ func TestQUICServer(t *testing.T) { udpListener.LocalAddr(), tlsClientConfig, originProxy, - &pogs.ConnectionOptions{}, + &tunnelpogs.ConnectionOptions{}, controlStream, NewObserver(&log, &log, false), ) @@ -218,10 +217,11 @@ func quicServer( stream, err := session.OpenStreamSync(context.Background()) require.NoError(t, err) - err = quicpogs.WriteConnectRequestData(stream, dest, connectionType, metadata...) + reqClientStream := quicpogs.RequestClientStream{ReadWriteCloser: stream} + err = reqClientStream.WriteConnectRequestData(dest, connectionType, metadata...) require.NoError(t, err) - _, err = quicpogs.ReadConnectResponseData(stream) + _, err = reqClientStream.ReadConnectResponseData() require.NoError(t, err) if message != nil { @@ -309,23 +309,23 @@ func TestBuildHTTPRequest(t *testing.T) { connectRequest: &quicpogs.ConnectRequest{ Dest: "http://test.com", Metadata: []quicpogs.Metadata{ - quicpogs.Metadata{ + { Key: "HttpHeader:Cf-Cloudflared-Proxy-Connection-Upgrade", Val: "Websocket", }, - quicpogs.Metadata{ + { Key: "HttpHeader:Content-Length", Val: "514", }, - quicpogs.Metadata{ + { Key: "HttpHeader:Another-Header", Val: "Misc", }, - quicpogs.Metadata{ + { Key: "HttpHost", Val: "cf.host", }, - quicpogs.Metadata{ + { Key: "HttpMethod", Val: "get", }, @@ -355,19 +355,19 @@ func TestBuildHTTPRequest(t *testing.T) { connectRequest: &quicpogs.ConnectRequest{ Dest: "http://test.com", Metadata: []quicpogs.Metadata{ - quicpogs.Metadata{ + { Key: "HttpHeader:Cf-Cloudflared-Proxy-Connection-Upgrade", Val: "Websocket", }, - quicpogs.Metadata{ + { Key: "HttpHeader:Another-Header", Val: "Misc", }, - quicpogs.Metadata{ + { Key: "HttpHost", Val: "cf.host", }, - quicpogs.Metadata{ + { Key: "HttpMethod", Val: "get", }, @@ -396,19 +396,19 @@ func TestBuildHTTPRequest(t *testing.T) { connectRequest: &quicpogs.ConnectRequest{ Dest: "http://test.com", Metadata: []quicpogs.Metadata{ - quicpogs.Metadata{ + { Key: "HttpHeader:Another-Header", Val: "Misc", }, - quicpogs.Metadata{ + { Key: "HttpHeader:Transfer-Encoding", Val: "chunked", }, - quicpogs.Metadata{ + { Key: "HttpHost", Val: "cf.host", }, - quicpogs.Metadata{ + { Key: "HttpMethod", Val: "get", }, @@ -438,19 +438,19 @@ func TestBuildHTTPRequest(t *testing.T) { connectRequest: &quicpogs.ConnectRequest{ Dest: "http://test.com", Metadata: []quicpogs.Metadata{ - quicpogs.Metadata{ + { Key: "HttpHeader:Another-Header", Val: "Misc", }, - quicpogs.Metadata{ + { Key: "HttpHeader:Transfer-Encoding", Val: "gzip,chunked", }, - quicpogs.Metadata{ + { Key: "HttpHost", Val: "cf.host", }, - quicpogs.Metadata{ + { Key: "HttpMethod", Val: "get", }, @@ -481,15 +481,15 @@ func TestBuildHTTPRequest(t *testing.T) { Type: quicpogs.ConnectionTypeWebsocket, Dest: "http://test.com", Metadata: []quicpogs.Metadata{ - quicpogs.Metadata{ + { Key: "HttpHeader:Another-Header", Val: "Misc", }, - quicpogs.Metadata{ + { Key: "HttpHost", Val: "cf.host", }, - quicpogs.Metadata{ + { Key: "HttpMethod", Val: "get", }, diff --git a/connection/udp_session.go b/connection/udp_session.go new file mode 100644 index 00000000..a038ac96 --- /dev/null +++ b/connection/udp_session.go @@ -0,0 +1,43 @@ +package connection + +import ( + "net" + "sync" + + "github.com/google/uuid" +) + +// TODO: TUN-5422 Unregister session +type udpSessions struct { + lock sync.Mutex + sessions map[uuid.UUID]*net.UDPConn +} + +func newUDPSessions() *udpSessions { + return &udpSessions{ + sessions: make(map[uuid.UUID]*net.UDPConn), + } +} + +func (us *udpSessions) register(id uuid.UUID, dstIP net.IP, dstPort uint16) error { + us.lock.Lock() + defer us.lock.Unlock() + dstAddr := &net.UDPAddr{ + IP: dstIP, + Port: int(dstPort), + } + conn, err := net.DialUDP("udp", us.localAddr(), dstAddr) + if err != nil { + return err + } + us.sessions[id] = conn + return nil +} + +func (ud *udpSessions) localAddr() *net.UDPAddr { + // TODO: Determine the IP to bind to + return &net.UDPAddr{ + IP: net.IPv4zero, + Port: 0, + } +} diff --git a/origin/tunnel.go b/origin/tunnel.go index 247f0f88..183060b9 100644 --- a/origin/tunnel.go +++ b/origin/tunnel.go @@ -532,6 +532,7 @@ func ServeQUIC( MaxIncomingStreams: connection.MaxConcurrentStreams, MaxIncomingUniStreams: connection.MaxConcurrentStreams, KeepAlive: true, + EnableDatagrams: true, Tracer: quicpogs.NewClientTracer(connLogger.Logger(), connIndex), } for { diff --git a/quic/quic_protocol.go b/quic/quic_protocol.go index 258833b1..b40cc6dc 100644 --- a/quic/quic_protocol.go +++ b/quic/quic_protocol.go @@ -1,16 +1,32 @@ package quic import ( - "bytes" + "context" "fmt" "io" + "net" capnp "zombiezen.com/go/capnproto2" + "zombiezen.com/go/capnproto2/rpc" + + "github.com/google/uuid" + "github.com/rs/zerolog" + + "github.com/cloudflare/cloudflared/tunnelrpc" + tunnelpogs "github.com/cloudflare/cloudflared/tunnelrpc/pogs" ) -// protocolSignature is a custom protocol signature to ensure that whoever performs a handshake does not write data -// before writing the metadata. -var protocolSignature = []byte{0x0A, 0x36, 0xCD, 0x12, 0xA1, 0x3E} +// The first 6 bytes of the stream is used to distinguish the type of stream. It ensures whoever performs a handshake does +// not write data before writing the metadata. +type ProtocolSignature [6]byte + +var ( + // DataStreamProtocolSignature is a custom protocol signature for data stream + DataStreamProtocolSignature = ProtocolSignature{0x0A, 0x36, 0xCD, 0x12, 0xA1, 0x3E} + + // RPCStreamProtocolSignature is a custom protocol signature for RPC stream + RPCStreamProtocolSignature = ProtocolSignature{0x52, 0xBB, 0x82, 0x5C, 0xDB, 0x65} +) const protocolVersionLength = 2 @@ -20,18 +36,26 @@ const ( protocolV1 protocolVersion = "01" ) +// RequestServerStream is a stream to serve requests +type RequestServerStream struct { + io.ReadWriteCloser +} + +func NewRequestServerStream(stream io.ReadWriteCloser, signature ProtocolSignature) (*RequestServerStream, error) { + if signature != DataStreamProtocolSignature { + return nil, fmt.Errorf("RequestClientStream can only be created from data stream") + } + return &RequestServerStream{stream}, nil +} + // ReadConnectRequestData reads the handshake data from a QUIC stream. -func ReadConnectRequestData(stream io.Reader) (*ConnectRequest, error) { - if err := verifySignature(stream); err != nil { - return nil, err - } - +func (rss *RequestServerStream) ReadConnectRequestData() (*ConnectRequest, error) { // This is a NO-OP for now. We could cause a branching if we wanted to use multiple versions. - if _, err := readVersion(stream); err != nil { + if _, err := readVersion(rss); err != nil { return nil, err } - msg, err := capnp.NewDecoder(stream).Decode() + msg, err := capnp.NewDecoder(rss).Decode() if err != nil { return nil, err } @@ -43,50 +67,8 @@ func ReadConnectRequestData(stream io.Reader) (*ConnectRequest, error) { return r, nil } -// WriteConnectRequestData writes requestMeta to a stream. -func WriteConnectRequestData(stream io.Writer, dest string, connectionType ConnectionType, metadata ...Metadata) error { - connectRequest := &ConnectRequest{ - Dest: dest, - Type: connectionType, - Metadata: metadata, - } - - msg, err := connectRequest.toPogs() - if err != nil { - return err - } - - if err := writePreamble(stream); err != nil { - return err - } - return capnp.NewEncoder(stream).Encode(msg) -} - -// ReadConnectResponseData reads the response to a RequestMeta in a stream. -func ReadConnectResponseData(stream io.Reader) (*ConnectResponse, error) { - if err := verifySignature(stream); err != nil { - return nil, err - } - - // This is a NO-OP for now. We could cause a branching if we wanted to use multiple versions. - if _, err := readVersion(stream); err != nil { - return nil, err - } - - msg, err := capnp.NewDecoder(stream).Decode() - if err != nil { - return nil, err - } - - r := &ConnectResponse{} - if err := r.fromPogs(msg); err != nil { - return nil, err - } - return r, nil -} - // WriteConnectResponseData writes response to a QUIC stream. -func WriteConnectResponseData(stream io.Writer, respErr error, metadata ...Metadata) error { +func (rss *RequestServerStream) WriteConnectResponseData(respErr error, metadata ...Metadata) error { var connectResponse *ConnectResponse if respErr != nil { connectResponse = &ConnectResponse{ @@ -103,14 +85,105 @@ func WriteConnectResponseData(stream io.Writer, respErr error, metadata ...Metad return err } - if err := writePreamble(stream); err != nil { + if err := writeDataStreamPreamble(rss); err != nil { return err } - return capnp.NewEncoder(stream).Encode(msg) + return capnp.NewEncoder(rss).Encode(msg) } -func writePreamble(stream io.Writer) error { - if err := writeSignature(stream); err != nil { +type RequestClientStream struct { + io.ReadWriteCloser +} + +// WriteConnectRequestData writes requestMeta to a stream. +func (rcs *RequestClientStream) WriteConnectRequestData(dest string, connectionType ConnectionType, metadata ...Metadata) error { + connectRequest := &ConnectRequest{ + Dest: dest, + Type: connectionType, + Metadata: metadata, + } + + msg, err := connectRequest.toPogs() + if err != nil { + return err + } + + if err := writeDataStreamPreamble(rcs); err != nil { + return err + } + return capnp.NewEncoder(rcs).Encode(msg) +} + +// ReadConnectResponseData reads the response to a RequestMeta in a stream. +func (rcs *RequestClientStream) ReadConnectResponseData() (*ConnectResponse, error) { + signature, err := DetermineProtocol(rcs) + if err != nil { + return nil, err + } + if signature != DataStreamProtocolSignature { + return nil, fmt.Errorf("Wrong protocol signature %v", signature) + } + + // This is a NO-OP for now. We could cause a branching if we wanted to use multiple versions. + if _, err := readVersion(rcs); err != nil { + return nil, err + } + + msg, err := capnp.NewDecoder(rcs).Decode() + if err != nil { + return nil, err + } + + r := &ConnectResponse{} + if err := r.fromPogs(msg); err != nil { + return nil, err + } + return r, nil +} + +// RPCServerStream is a stream to serve RPCs. It is closed when the RPC client is done +type RPCServerStream struct { + io.ReadWriteCloser +} + +func NewRPCServerStream(stream io.ReadWriteCloser, protocol ProtocolSignature) (*RPCServerStream, error) { + if protocol != RPCStreamProtocolSignature { + return nil, fmt.Errorf("RPCStream can only be created from rpc stream") + } + return &RPCServerStream{stream}, nil +} + +func (s *RPCServerStream) Serve(sessionManager tunnelpogs.SessionManager, logger *zerolog.Logger) error { + rpcTransport := tunnelrpc.NewTransportLogger(logger, rpc.StreamTransport(s)) + defer rpcTransport.Close() + + main := tunnelpogs.SessionManager_ServerToClient(sessionManager) + rpcConn := rpc.NewConn( + rpcTransport, + rpc.MainInterface(main.Client), + ) + defer rpcConn.Close() + + return rpcConn.Wait() +} + +func DetermineProtocol(stream io.Reader) (ProtocolSignature, error) { + signature, err := readSignature(stream) + if err != nil { + return ProtocolSignature{}, err + } + switch signature { + case DataStreamProtocolSignature: + return DataStreamProtocolSignature, nil + case RPCStreamProtocolSignature: + return RPCStreamProtocolSignature, nil + default: + return ProtocolSignature{}, fmt.Errorf("Unknown signature %v", signature) + } +} + +func writeDataStreamPreamble(stream io.Writer) error { + if err := writeSignature(stream, DataStreamProtocolSignature); err != nil { return err } @@ -128,20 +201,53 @@ func readVersion(stream io.Reader) (string, error) { return string(version), err } -func writeSignature(stream io.Writer) error { - _, err := stream.Write(protocolSignature) +func readSignature(stream io.Reader) (ProtocolSignature, error) { + var signature ProtocolSignature + if _, err := io.ReadFull(stream, signature[:]); err != nil { + return ProtocolSignature{}, err + } + return signature, nil +} + +func writeSignature(stream io.Writer, signature ProtocolSignature) error { + _, err := stream.Write(signature[:]) return err } -func verifySignature(stream io.Reader) error { - signature := make([]byte, len(protocolSignature)) - if _, err := io.ReadFull(stream, signature); err != nil { +// RPCClientStream is a stream to call methods of SessionManager +type RPCClientStream struct { + client tunnelpogs.SessionManager_PogsClient + transport rpc.Transport +} + +func NewRPCClientStream(ctx context.Context, stream io.ReadWriteCloser, logger *zerolog.Logger) (*RPCClientStream, error) { + n, err := stream.Write(RPCStreamProtocolSignature[:]) + if err != nil { + return nil, err + } + if n != len(RPCStreamProtocolSignature) { + return nil, fmt.Errorf("expect to write %d bytes for RPC stream protocol signature, wrote %d", len(RPCStreamProtocolSignature), n) + } + transport := tunnelrpc.NewTransportLogger(logger, rpc.StreamTransport(stream)) + conn := rpc.NewConn( + transport, + tunnelrpc.ConnLog(logger), + ) + return &RPCClientStream{ + client: tunnelpogs.SessionManager_PogsClient{Client: conn.Bootstrap(ctx), Conn: conn}, + transport: transport, + }, nil +} + +func (rcs *RPCClientStream) RegisterUdpSession(ctx context.Context, sessionID uuid.UUID, dstIP net.IP, dstPort uint16) error { + resp, err := rcs.client.RegisterUdpSession(ctx, sessionID, dstIP, dstPort) + if err != nil { return err } - - if !bytes.Equal(signature[0:], protocolSignature) { - return fmt.Errorf("Wrong signature: %v", signature) - } - - return nil + return resp.Err +} + +func (rcs *RPCClientStream) Close() { + _ = rcs.client.Close() + _ = rcs.transport.Close() } diff --git a/quic/quic_protocol_test.go b/quic/quic_protocol_test.go index 0856d07b..89a19a77 100644 --- a/quic/quic_protocol_test.go +++ b/quic/quic_protocol_test.go @@ -2,9 +2,15 @@ package quic import ( "bytes" + "context" "errors" + "fmt" + "io" + "net" "testing" + "github.com/google/uuid" + "github.com/rs/zerolog" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) @@ -21,7 +27,7 @@ func TestConnectRequestData(t *testing.T) { hostname: "tunnel.com", connectionType: ConnectionTypeHTTP, metadata: []Metadata{ - Metadata{ + { Key: "key", Val: "1234", }, @@ -31,9 +37,15 @@ func TestConnectRequestData(t *testing.T) { for _, test := range tests { t.Run(test.name, func(t *testing.T) { b := &bytes.Buffer{} - err := WriteConnectRequestData(b, test.hostname, test.connectionType, test.metadata...) + reqClientStream := RequestClientStream{noopCloser{b}} + err := reqClientStream.WriteConnectRequestData(test.hostname, test.connectionType, test.metadata...) require.NoError(t, err) - reqMeta, err := ReadConnectRequestData(b) + protocol, err := DetermineProtocol(b) + require.NoError(t, err) + reqServerStream, err := NewRequestServerStream(noopCloser{b}, protocol) + require.NoError(t, err) + + reqMeta, err := reqServerStream.ReadConnectRequestData() require.NoError(t, err) assert.Equal(t, test.metadata, reqMeta.Metadata) @@ -52,7 +64,7 @@ func TestConnectResponseMeta(t *testing.T) { { name: "Signature verified and response metadata is unmarshaled and read correctly", metadata: []Metadata{ - Metadata{ + { Key: "key", Val: "1234", }, @@ -62,7 +74,7 @@ func TestConnectResponseMeta(t *testing.T) { name: "If error is not empty, other fields should be blank", err: errors.New("something happened"), metadata: []Metadata{ - Metadata{ + { Key: "key", Val: "1234", }, @@ -73,9 +85,12 @@ func TestConnectResponseMeta(t *testing.T) { for _, test := range tests { t.Run(test.name, func(t *testing.T) { b := &bytes.Buffer{} - err := WriteConnectResponseData(b, test.err, test.metadata...) + reqServerStream := RequestServerStream{noopCloser{b}} + err := reqServerStream.WriteConnectResponseData(test.err, test.metadata...) require.NoError(t, err) - respMeta, err := ReadConnectResponseData(b) + + reqClientStream := RequestClientStream{noopCloser{b}} + respMeta, err := reqClientStream.ReadConnectResponseData() require.NoError(t, err) if respMeta.Error == "" { @@ -86,3 +101,81 @@ func TestConnectResponseMeta(t *testing.T) { }) } } + +func TestRegisterUdpSession(t *testing.T) { + clientReader, serverWriter := io.Pipe() + serverReader, clientWriter := io.Pipe() + + clientStream := mockRPCStream{clientReader, clientWriter} + serverStream := mockRPCStream{serverReader, serverWriter} + + rpcServer := mockRPCServer{ + sessionID: uuid.New(), + dstIP: net.IP{172, 16, 0, 1}, + dstPort: 8000, + } + logger := zerolog.Nop() + sessionRegisteredChan := make(chan struct{}) + go func() { + protocol, err := DetermineProtocol(serverStream) + assert.NoError(t, err) + rpcServerStream, err := NewRPCServerStream(serverStream, protocol) + assert.NoError(t, err) + err = rpcServerStream.Serve(rpcServer, &logger) + assert.NoError(t, err) + + serverStream.Close() + close(sessionRegisteredChan) + }() + + rpcClientStream, err := NewRPCClientStream(context.Background(), clientStream, &logger) + assert.NoError(t, err) + + err = rpcClientStream.RegisterUdpSession(context.Background(), rpcServer.sessionID, rpcServer.dstIP, rpcServer.dstPort) + assert.NoError(t, err) + + // Different sessionID, the RPC server should reject the registraion + err = rpcClientStream.RegisterUdpSession(context.Background(), uuid.New(), rpcServer.dstIP, rpcServer.dstPort) + assert.Error(t, err) + + rpcClientStream.Close() + <-sessionRegisteredChan +} + +type mockRPCServer struct { + sessionID uuid.UUID + dstIP net.IP + dstPort uint16 +} + +func (s mockRPCServer) RegisterUdpSession(ctx context.Context, sessionID uuid.UUID, dstIP net.IP, dstPort uint16) error { + if s.sessionID != sessionID { + return fmt.Errorf("expect session ID %s, got %s", s.sessionID, sessionID) + } + if !s.dstIP.Equal(dstIP) { + return fmt.Errorf("expect destination IP %s, got %s", s.dstIP, dstIP) + } + if s.dstPort != dstPort { + return fmt.Errorf("expect session ID %d, got %d", s.dstPort, dstPort) + } + return nil +} + +type mockRPCStream struct { + io.ReadCloser + io.WriteCloser +} + +func (s mockRPCStream) Close() error { + _ = s.ReadCloser.Close() + _ = s.WriteCloser.Close() + return nil +} + +type noopCloser struct { + io.ReadWriter +} + +func (noopCloser) Close() error { + return nil +} diff --git a/tunnelrpc/pogs/sessionrpc.go b/tunnelrpc/pogs/sessionrpc.go new file mode 100644 index 00000000..4a04c671 --- /dev/null +++ b/tunnelrpc/pogs/sessionrpc.go @@ -0,0 +1,118 @@ +package pogs + +import ( + "context" + "fmt" + "net" + + "github.com/cloudflare/cloudflared/tunnelrpc" + "github.com/google/uuid" + capnp "zombiezen.com/go/capnproto2" + "zombiezen.com/go/capnproto2/rpc" + "zombiezen.com/go/capnproto2/server" +) + +type SessionManager interface { + RegisterUdpSession(ctx context.Context, sessionID uuid.UUID, dstIP net.IP, dstPort uint16) error +} + +type SessionManager_PogsImpl struct { + impl SessionManager +} + +func SessionManager_ServerToClient(s SessionManager) tunnelrpc.SessionManager { + return tunnelrpc.SessionManager_ServerToClient(SessionManager_PogsImpl{s}) +} + +func (i SessionManager_PogsImpl) RegisterUdpSession(p tunnelrpc.SessionManager_registerUdpSession) error { + server.Ack(p.Options) + + sessionIDRaw, err := p.Params.SessionId() + if err != nil { + return err + } + sessionID, err := uuid.FromBytes(sessionIDRaw) + if err != nil { + return err + } + + dstIPRaw, err := p.Params.DstIp() + if err != nil { + return err + } + dstIP := net.IP(dstIPRaw) + if dstIP == nil { + return fmt.Errorf("%v is not valid IP", dstIPRaw) + } + dstPort := p.Params.DstPort() + + resp := RegisterUdpSessionResponse{} + registrationErr := i.impl.RegisterUdpSession(p.Ctx, sessionID, dstIP, dstPort) + if registrationErr != nil { + resp.Err = registrationErr + } + + result, err := p.Results.NewResult() + if err != nil { + return err + } + + return resp.Marshal(result) +} + +type RegisterUdpSessionResponse struct { + Err error +} + +func (p *RegisterUdpSessionResponse) Marshal(s tunnelrpc.RegisterUdpSessionResponse) error { + if p.Err != nil { + return s.SetErr(p.Err.Error()) + } + return nil +} + +func (p *RegisterUdpSessionResponse) Unmarshal(s tunnelrpc.RegisterUdpSessionResponse) error { + respErr, err := s.Err() + if err != nil { + return err + } + if respErr != "" { + p.Err = fmt.Errorf(respErr) + } + return nil +} + +type SessionManager_PogsClient struct { + Client capnp.Client + Conn *rpc.Conn +} + +func (c SessionManager_PogsClient) Close() error { + c.Client.Close() + return c.Conn.Close() +} + +func (c SessionManager_PogsClient) RegisterUdpSession(ctx context.Context, sessionID uuid.UUID, dstIP net.IP, dstPort uint16) (*RegisterUdpSessionResponse, error) { + client := tunnelrpc.SessionManager{Client: c.Client} + promise := client.RegisterUdpSession(ctx, func(p tunnelrpc.SessionManager_registerUdpSession_Params) error { + if err := p.SetSessionId(sessionID[:]); err != nil { + return err + } + if err := p.SetDstIp(dstIP); err != nil { + return err + } + p.SetDstPort(dstPort) + return nil + }) + result, err := promise.Result().Struct() + if err != nil { + return nil, wrapRPCError(err) + } + response := new(RegisterUdpSessionResponse) + + err = response.Unmarshal(result) + if err != nil { + return nil, err + } + return response, nil +} diff --git a/tunnelrpc/tunnelrpc.capnp b/tunnelrpc/tunnelrpc.capnp index 20fe4950..2f6c67d7 100644 --- a/tunnelrpc/tunnelrpc.capnp +++ b/tunnelrpc/tunnelrpc.capnp @@ -142,3 +142,11 @@ interface TunnelServer extends (RegistrationServer) { authenticate @4 (originCert :Data, hostname :Text, options :RegistrationOptions) -> (result :AuthenticateResponse); reconnectTunnel @5 (jwt :Data, eventDigest :Data, connDigest :Data, hostname :Text, options :RegistrationOptions) -> (result :TunnelRegistration); } + +struct RegisterUdpSessionResponse { + err @0 :Text; +} + +interface SessionManager { + registerUdpSession @0 (sessionId :Data, dstIp :Data, dstPort: UInt16) -> (result :RegisterUdpSessionResponse); +} \ No newline at end of file diff --git a/tunnelrpc/tunnelrpc.capnp.go b/tunnelrpc/tunnelrpc.capnp.go index 14ea1f3d..e0f573e7 100644 --- a/tunnelrpc/tunnelrpc.capnp.go +++ b/tunnelrpc/tunnelrpc.capnp.go @@ -3371,195 +3371,528 @@ func (p TunnelServer_reconnectTunnel_Results_Promise) Result() TunnelRegistratio return TunnelRegistration_Promise{Pipeline: p.Pipeline.GetPipeline(0)} } -const schema_db8274f9144abc7e = "x\xda\xccY{l\x1c\xd5\xd5?g\xee\xaeg\xed\xd8" + - "\xac\x87\xd9\xc4\x89\x05\x9f\xbf/\x0a\xe2\xc3`\xc0q\xa9" + - "\xd2\x94\xd6v\xb0Sl\xf2\xf0x\x93\x96G\x82\x18\xef" + - "\xde\xd8\xe3\xee\xce,3\xb3\xc6N\x13\x92\x98\xa4\x10\xc4" + - "+!\xe1\x91\x926\x04\xd1\xaa\x14ZR@m*\xaa" + - "B_\x80x\xa3P%\x10\xd4\x96\x90>\xa2PJ\xa0" + - "BT\x94\xa9\xce\xcc\xce\xc3kc;\xd0?\xfa\xdf\xea" + - "\xcc\xb9\xf7\x9es~\xe7\x9e\xf3\xbbg\xcf\xff\x9a\xd8&" + - "4\xc7\xc5\x19\x00\xca\xc6x\x85\xc3\x1b_Y\xbb\xe7\x8c" + - "_\x8d\x82R\x8f\xe8\\\xfbxw\xeaC{\xf4u\x88" + - "3\x11\xa0E\x8b\xafEy}\\\x04\x90G\xe2\x7f\x06" + - "t.\x91\xce\xbb\"\xf5\xd2\x0b\xd7\x81T\x1fU\x8e\x91" + - "\xf2\xea\x8aF\x94\xf3\x15\xa4\xacU\x90\xf2\x85\xf9\x17\xf7" + - "~~\xe7\xb3\x9bA\xaa\x17Be\xc0\x96\x95\xe2Z\x94" + - "5\x914\xb9\xb8\x1c\xd0yo\xc7\xec\x1f\xdc\xfb\xc2\xd3" + - "[@:\x13\xa1t\xf6z\xf15\x04\x94\xb7\x89?\x02" + - "tZ\x17?\xbf\xbf\xbe\xe5\x8e\x1de\xe7\x0a\xa4\xd8\x9c" + - "hD\xb9=A\xbb})q\x0d\xa03w\xe8\x8c\xab" + - "~\xf9\x9bG\xee\x04\xa5\x09\xd19\xdcw\xf6\xabl\xf7" + - "\x03\xaf\xc3J\x14Q\x00h\xb97\xb1\x976\xde\xe7\xea" + - "\xbex\xce\xe3?\xbb\xed\x91\xeb\xbf\x05\xca\x99\x88\x00\xae" + - "#3+\xffI\x0agU\xb6\x02:;\x0e\xfe|Y" + - "~\xdb\xae\xbd\x9ei\xeew\xa5R\x10 \xe6l\xee\xfa" + - " \xbf\xf2\xbe\xf4}%\xa3)J-\x9d\x95'\x10\xb0" + - "\xe5\xb2\xca\x06\x04t.x\xed\xe8\xf2\xa5?^\xf3\xbd" + - "\xc8\xdab\xd5ZZ\xfb\xd0\xff\\R9|t\xf1\xc3" + - " 5\xf9_\xae\xae\xea\xa5/\xb1\xd5\xecc\xf5\xae_" + - "P2\x14\xdd\\\x94\\T\xd6K\x84J\x00j" + - "Y\x94\\\xcd\xdd\xd2 \xca\xfb\xdc\xed\x1er\xb5\x85\xa3" + - "\xea\x9c\x8d\xbf\xfb\xf2\xe1\x08\x0e\xfb\xa47\x11b\xce\xb2" + - "\xaf^1X\xb9\xfe\xc8\x91\xe8A\xf7KnD\xf6\xbb" + - "K\xff\xf6\xddc\xb7\x1e\xcfg\xff\xe4\xe6\x92\x1f\xb3C" + - "\xd2B\x01P~W\xa2,\xafk\xa8\xe9\x9c{\xb0\xe7" + - "\x98\x07\xa5\xb7\xc5\xf3\xa7.\"\x85\xa3\xa7\xd2\x16\x17\\" + - "\xd5\xceW-\xb8\xf4\x18H\xf5l\xcc5\x88\xcb\x0bQ" + - "\x9e)\xd3\x02I\xbe\x1e\xe5\xb3Ru\x00\xce-\xd7v" + - ",\xff\xc2\xdc'ODM\x9a\x93\xa2\xb4\x92\x9bR\xb4" + - "\xdf\x9a\x05\xc7\xbfr\xc6-\xbf=Q\x16HWqi" + - "\xaa\x11\xe5\xd5)r\xfd2R~g\xf1\xb7\x0f\xd4'" + - "\xeb\xdf/\x0bS\x85\x9bA\xa9A\x94\xb7\x91n\xcb\xcd" + - "\xa9\xa7(\x99\xbe\xf9\xfa\x95\xc3\xaf\\\xf7\xde?\xca\x11" + - "u\xb7\xde4\xab\x17\xe5\x9d\xb3h\xebm\xb3\x08\xff;" + - "W\xfce\xc3\xf1\x9d\xb3>\x18\xe7Ws\xdd \xca\x9d" + - "u\xa4\xd9^w\xbd|/\xfdr^\x12\xefk\xee\xd8" + - "\xf0\xec\x87\x91\x8c\xdfZ\xd7M\x19\x7f\x87x\xcf\x91\x8d" + - "\xbf\xbf\xf2\xa3\xa8\xc3[\xea\xde$\x87\xef\xae#\x87\xd7" + - "\xbds\xf7\xc5\xb7\xaez\xf0\xe3\x08|\xfb\xebFi\xa9" + - "]\xd4u\x9e3\x0b\xb1\xccy\xfe\xcf\xcc\xb9\x19\xb5\xa0" + - "\x17\x16\xb6\x17\xed\x01\xae\xdbZF\xb5y/o\xb5\x0a" + - "\x86n\xf1\x1eD\xa5\x96\xc5\x00b\x08 \xa9\x83\x00\xca" + - "U\x0c\x95\x9c\x80\x12b\x8a\x00\x964\x12\x0e0Tl" + - "\x01%AHQ\xc9\x90\xae\x9e\x0b\xa0\xe4\x18*\xc3\x02" + - "\"K!\x03\x90\x8a\xdb\x01\x94a\x86\xcaf\x01\x9d\x02" + - "7\xf3\xaa\xceuH\xda\x9d\xa6\x89\xd5 `5\xa0c" + - "r\xdb\x1cQ\xfbr\x90\xe4\x11\xb18x\x8d\x8d5 " + - "`\x0d\xa03`\x14Mk\xa5n\xa3\x96\xeb\xe5kL" + - "n\xe1\x00V\x80\x80\x15\x80\x81{l\xbc{\x17\xe54" + - "\xae\xdb\xc9.}\x8dQ\xe6T\xf7DNu\x97\x9c\xda" + - "\x1cqj\xd3\"\x00e\x1dC\xe5\x06\x01%V\xf2j" + - "K#\xf5\x05\x86\xcaM\x02:\x19\xf7\x90\xae,\x00\x04" + - "\xf6\xae\xe1\xaa]4\xb9E\xb2S\x00{\x18\xban\x9d" + - "\x02\xb8a\x88\x9b\x96f\xe8\xbe\x9bI\xd5\xcc\x0c\x04\xa1" + - "\x98\x04\xaa\xcea\xcd\xb25\xbd\x7f\x85+o\xed1r" + - "Zf\x84\xbc\xaav\xed<}!\x00\xa24\xf3r\x00" + - "\x14$i\x11@\xab\xd6\xaf\x1b&w\xb2\x9a\x951t" + - "\x9d\x03\xcb\xd8\x1b\xfa\xd4\x9c\xaagxpP\xc5\xf8\x83" + - "\xbc\x03\xd2\xdc\x1c\xe2\xe6\xb9j$A\xe6\xf5\xa8\xa6\xca" + - "\xf2\x96R\x1d\xc4\xb1\xf3r\x00\xa5\x83\xa1\xd2\x13\x89\xe3" + - "R\x8a\xe3\x12\x86\xca\xa5\x918\xae\xa48\xf60TV" + - "\x09\xe8\x18\xa6\xd6\xaf\xe9\x17q`f\x14c\xcb\xd6\xd5" + - "<\xa7\x98\x95\xe2\xb1\xc1(\xd8\x9a\xa1[X\x1b\xd6Q" + - "@\xac\x9d\x1cu\xcf\x81$\xe56\xc5'\x11X{\x16" + - "Y\xfb\xff\x0c\x95\xcfE\xacm\xa6T>\x9f\xa1r\xa1" + - "\x80\x8e\x9a\xc9\x18E\xdd^\x01L\xed/\x03%\xcd!" + - "\x991yh\xaf\x7fl|\x82\xbc\xa3xg\xc8\xf4^" + - "\xee\xdd\xa9sMn\x89\xc5\x9cM\xd6T;\x8eg\xce" + - "|\x00e\x1eC\xe5|\x01k\xf0c\xc7\xb3\xa7i{" + - "hO\x037M\xc3\xc4\xda\xb0\xe6\x94\xbc\xcf\x94\x0e@" + - "C\xef\xe0\xb6\xaa\xe5\x90b\x14\xb4\xb9\xb2\x18M\x05r" + - "Q7y\xbff\xd9\xdc\xf4\xc4\xf3Z\x09\xe9\xbc\xa5\xc4" + - "\x82\xd0\xd5\xec\x02Pj\x19*\xa7\x09\xe8\xf4\x9bj\x86" + - "\xf7p\x135#\xbbL\xd5\x8d4\xe3\x19\x8c\x83\x80\xf1" + - "\xc8\xa1\xa7\x9c\xec\xa1\xbd\xdc*\xe6l\x0b\x82U\x93\xaf" + - "7y)\x08\xa5\xe5=\x0d\x9e\xcd\xa9\xc0\xe6\xf5s\xc3" + - "\xda\x13\xc0\xbd\xa9/\xbc\xbaArn\xa5\xc4\xb8\x81\xa1" + - "\xb2#r\xc9\xb7Q\x1a\xdf\xc6P\xb9G@)\x16K" + - "a\x0c@\xba\x9b\xd2x\x07Ce\x8f0\xb6B\xf1!" + - "\xae\xdb\x1dZ?\x88\xdc\x0a\xa5db\x87\xd6\xcf\x81Y" + - "\x9f5\xd1\x13S\xc4\xc3\xe8\xb3\x8c\x1c\xb7y\x07\xcf\xe4" + - "TS\xb5\xb5!\xee}/%\xa3\x0f\xead\x1b\xf6\xba" + - "\x88\xd0bC\x1f\x07S\x98\xd4%\xa8\xd0\x9a\xac^\x85" + - "\xea\xcb\x0b\xb6&\x1a\xbaE\x971\x82\xce\xc2\x89\xd01" + - "Ct\xd0\x07g4\x0a\x0e\x96\xc0\xd9\x15\xe2 \xc5\x04" + - "\x0f\x9c\xdd{\x01\x94=\x0c\x95\x07\x05l\xf5\xca2\xd6" + - "\x86l\xbc\x14P\xaf\xf8,1\xa0!\xa3\xe6\xba\x0a\x01" + - ",&/\xe4\xd4\x0c\xef\xc4R\xa1\x05D\x10\x10]\x14" + - "\xf3\x05\x93[\x16j\x86\xae\x14\xd5\x9c\xc6\xec\x91\xa0\xfd" + - "\xe8\xc5|\x8f\xc9\x8744\x8aV\xbbm\xf3\xbcX\xb0" + - "\xadq\xcdi\xd2\x00\xd1\x15\x16\xb5\x9cUV\xad\x1a\xc3" + - "\xf2\x10\x04\xa8\x89\xaa\xd59\x0c\x95\x05\x02&\x8bE-" + - "\x1b\x98\x9f32.n\x90\\\xa6\xe6\xf9\xb8\x8eR1" + - "\xe5u\x1as\x19{\xd4\xa4{\x9b\xfe\x9bJ\xfd\xe4\xfc" + - "\x85\\\x07\xb7\x1f\x86&S\x01hc\xa8,\x89\x98\xdc" + - "5?\xe2\x87o\xf2\xd2\xbe\xd0\x0f\xf1\xeb|\xc4\xb7\xaa" + - "\x81\xe7\xa9\xb8\xfa\xc1,9\xd3\x0e\xe2%\xa1\xced\xf6" + - "E/\xd4\xf2B\x83\xeb!\xd9\xb8\xc0\xb7Q\x1e\xc1n" + - "\x80\xf402Lo\xc6\xd0Ly\x13.\x02H\xaf#" + - "\xf9\x0d\x18Z*o\xc1z\x80\xf4F\x92\xdf\x84\x01\xcf" + - "\x92\xb7\xe2\x03\x00\xe9\x9bH|\x17\xa9\xc7\x98{%\xe4" + - "\x9d\xee\xf6;H\xbe\x87\xe4\xf1X\x0a\xe3\x00\xf2nl" + - "\x04H\xdfE\xf2GI^!\xa4\xb0\x02@\xde\x87\x83" + - "\x00\xe9\x87I\xfe8\xc9\xc5x\x8a\xa8\xa6\xbc\x1fM\x80" + - "\xf4OI\xfek\x92'f\xa70\x01 ?\xe9\xca\x9f" + - " \xf9s$\xaf\x9c\x93\xc2J\x00\xf9\x19\x1c\x05H?" + - "M\xf2\x03$\xaf\xc2\x14V\x01\xc8/\xe3.\x80\xf4\x01" + - "\x92\xff\x81\xe43*R8\x03@~\xc3\xb5\xe7 \xc9" + - "\xdf\"yu,\x85\xd5\x00\xf2\x1fq/@\xfa-\x92" + - "\xff\x9d\xe45b\x0ak\x00\xe4\xb7]\xbf\x8e\x93\x80t-\xc9O" + - "\x8b\xd2\x879x9@z6\xc9\xe7\xa1\x80\"\x8f\xcc" + - "y\x8afH\xb0rF\xff\x12M\x9f\xb0'\xf9\xc3#" + - "\xb4\x17\xabZ\xaehr\x08[b\xa9HtD\xba\xb4" + - "7Uj_C\xe9\x97\xa6\xe4\xc9\xa2\x85\"\x08(\x9e" + - "\xdccmZ\x1d\xa3\xd34\x0d4\xcb\xc8\xe7\xfc\x90|" + - "\x06\xdc\x938\xf4\xc5\x0c\x95\x15\x04E\x9b\x07\x85\xd2\x17" + - "\xd2\xe5\x86\x8cZ\xb4\xf88\x1f\x80q3x`[\x03" + - "F1\x97\xed\xe5 \xda\xe6HY\x08\xa6$\xa1i\x9e" + - "\xf4+N\xc2\xad8\xfe\xe8\x16\xfd\x09\xad\xd4\xbc\x0b\x04" + - "\xa9\x89*\x8e?\x8dD\x7f\x10/\xfd\xdf\x03 H\xa7" + - "\x87\x05\x00\xfd\x180C\x1f{\xe5\xbd\x0fn\x8e\xb6a" + - "\x0f\xe2\x7f\xe2\xd9\xe9\xb5\x9f\x93\xb8\xe9c\xa6WT\xc6" + - "\xc5\xe9\xf4\xcd\xe0\xcf\x9e\xb2\x9b^\xf9Y_\xe0~#" + - "\xf9w\x00\x00\x00\xff\xffH\xa22\xa3" +type RegisterUdpSessionResponse struct{ capnp.Struct } + +// RegisterUdpSessionResponse_TypeID is the unique identifier for the type RegisterUdpSessionResponse. +const RegisterUdpSessionResponse_TypeID = 0xab6d5210c1f26687 + +func NewRegisterUdpSessionResponse(s *capnp.Segment) (RegisterUdpSessionResponse, error) { + st, err := capnp.NewStruct(s, capnp.ObjectSize{DataSize: 0, PointerCount: 1}) + return RegisterUdpSessionResponse{st}, err +} + +func NewRootRegisterUdpSessionResponse(s *capnp.Segment) (RegisterUdpSessionResponse, error) { + st, err := capnp.NewRootStruct(s, capnp.ObjectSize{DataSize: 0, PointerCount: 1}) + return RegisterUdpSessionResponse{st}, err +} + +func ReadRootRegisterUdpSessionResponse(msg *capnp.Message) (RegisterUdpSessionResponse, error) { + root, err := msg.RootPtr() + return RegisterUdpSessionResponse{root.Struct()}, err +} + +func (s RegisterUdpSessionResponse) String() string { + str, _ := text.Marshal(0xab6d5210c1f26687, s.Struct) + return str +} + +func (s RegisterUdpSessionResponse) Err() (string, error) { + p, err := s.Struct.Ptr(0) + return p.Text(), err +} + +func (s RegisterUdpSessionResponse) HasErr() bool { + p, err := s.Struct.Ptr(0) + return p.IsValid() || err != nil +} + +func (s RegisterUdpSessionResponse) ErrBytes() ([]byte, error) { + p, err := s.Struct.Ptr(0) + return p.TextBytes(), err +} + +func (s RegisterUdpSessionResponse) SetErr(v string) error { + return s.Struct.SetText(0, v) +} + +// RegisterUdpSessionResponse_List is a list of RegisterUdpSessionResponse. +type RegisterUdpSessionResponse_List struct{ capnp.List } + +// NewRegisterUdpSessionResponse creates a new list of RegisterUdpSessionResponse. +func NewRegisterUdpSessionResponse_List(s *capnp.Segment, sz int32) (RegisterUdpSessionResponse_List, error) { + l, err := capnp.NewCompositeList(s, capnp.ObjectSize{DataSize: 0, PointerCount: 1}, sz) + return RegisterUdpSessionResponse_List{l}, err +} + +func (s RegisterUdpSessionResponse_List) At(i int) RegisterUdpSessionResponse { + return RegisterUdpSessionResponse{s.List.Struct(i)} +} + +func (s RegisterUdpSessionResponse_List) Set(i int, v RegisterUdpSessionResponse) error { + return s.List.SetStruct(i, v.Struct) +} + +func (s RegisterUdpSessionResponse_List) String() string { + str, _ := text.MarshalList(0xab6d5210c1f26687, s.List) + return str +} + +// RegisterUdpSessionResponse_Promise is a wrapper for a RegisterUdpSessionResponse promised by a client call. +type RegisterUdpSessionResponse_Promise struct{ *capnp.Pipeline } + +func (p RegisterUdpSessionResponse_Promise) Struct() (RegisterUdpSessionResponse, error) { + s, err := p.Pipeline.Struct() + return RegisterUdpSessionResponse{s}, err +} + +type SessionManager struct{ Client capnp.Client } + +// SessionManager_TypeID is the unique identifier for the type SessionManager. +const SessionManager_TypeID = 0x839445a59fb01686 + +func (c SessionManager) RegisterUdpSession(ctx context.Context, params func(SessionManager_registerUdpSession_Params) error, opts ...capnp.CallOption) SessionManager_registerUdpSession_Results_Promise { + if c.Client == nil { + return SessionManager_registerUdpSession_Results_Promise{Pipeline: capnp.NewPipeline(capnp.ErrorAnswer(capnp.ErrNullClient))} + } + call := &capnp.Call{ + Ctx: ctx, + Method: capnp.Method{ + InterfaceID: 0x839445a59fb01686, + MethodID: 0, + InterfaceName: "tunnelrpc/tunnelrpc.capnp:SessionManager", + MethodName: "registerUdpSession", + }, + Options: capnp.NewCallOptions(opts), + } + if params != nil { + call.ParamsSize = capnp.ObjectSize{DataSize: 8, PointerCount: 2} + call.ParamsFunc = func(s capnp.Struct) error { return params(SessionManager_registerUdpSession_Params{Struct: s}) } + } + return SessionManager_registerUdpSession_Results_Promise{Pipeline: capnp.NewPipeline(c.Client.Call(call))} +} + +type SessionManager_Server interface { + RegisterUdpSession(SessionManager_registerUdpSession) error +} + +func SessionManager_ServerToClient(s SessionManager_Server) SessionManager { + c, _ := s.(server.Closer) + return SessionManager{Client: server.New(SessionManager_Methods(nil, s), c)} +} + +func SessionManager_Methods(methods []server.Method, s SessionManager_Server) []server.Method { + if cap(methods) == 0 { + methods = make([]server.Method, 0, 1) + } + + methods = append(methods, server.Method{ + Method: capnp.Method{ + InterfaceID: 0x839445a59fb01686, + MethodID: 0, + InterfaceName: "tunnelrpc/tunnelrpc.capnp:SessionManager", + MethodName: "registerUdpSession", + }, + Impl: func(c context.Context, opts capnp.CallOptions, p, r capnp.Struct) error { + call := SessionManager_registerUdpSession{c, opts, SessionManager_registerUdpSession_Params{Struct: p}, SessionManager_registerUdpSession_Results{Struct: r}} + return s.RegisterUdpSession(call) + }, + ResultsSize: capnp.ObjectSize{DataSize: 0, PointerCount: 1}, + }) + + return methods +} + +// SessionManager_registerUdpSession holds the arguments for a server call to SessionManager.registerUdpSession. +type SessionManager_registerUdpSession struct { + Ctx context.Context + Options capnp.CallOptions + Params SessionManager_registerUdpSession_Params + Results SessionManager_registerUdpSession_Results +} + +type SessionManager_registerUdpSession_Params struct{ capnp.Struct } + +// SessionManager_registerUdpSession_Params_TypeID is the unique identifier for the type SessionManager_registerUdpSession_Params. +const SessionManager_registerUdpSession_Params_TypeID = 0x904e297b87fbecea + +func NewSessionManager_registerUdpSession_Params(s *capnp.Segment) (SessionManager_registerUdpSession_Params, error) { + st, err := capnp.NewStruct(s, capnp.ObjectSize{DataSize: 8, PointerCount: 2}) + return SessionManager_registerUdpSession_Params{st}, err +} + +func NewRootSessionManager_registerUdpSession_Params(s *capnp.Segment) (SessionManager_registerUdpSession_Params, error) { + st, err := capnp.NewRootStruct(s, capnp.ObjectSize{DataSize: 8, PointerCount: 2}) + return SessionManager_registerUdpSession_Params{st}, err +} + +func ReadRootSessionManager_registerUdpSession_Params(msg *capnp.Message) (SessionManager_registerUdpSession_Params, error) { + root, err := msg.RootPtr() + return SessionManager_registerUdpSession_Params{root.Struct()}, err +} + +func (s SessionManager_registerUdpSession_Params) String() string { + str, _ := text.Marshal(0x904e297b87fbecea, s.Struct) + return str +} + +func (s SessionManager_registerUdpSession_Params) SessionId() ([]byte, error) { + p, err := s.Struct.Ptr(0) + return []byte(p.Data()), err +} + +func (s SessionManager_registerUdpSession_Params) HasSessionId() bool { + p, err := s.Struct.Ptr(0) + return p.IsValid() || err != nil +} + +func (s SessionManager_registerUdpSession_Params) SetSessionId(v []byte) error { + return s.Struct.SetData(0, v) +} + +func (s SessionManager_registerUdpSession_Params) DstIp() ([]byte, error) { + p, err := s.Struct.Ptr(1) + return []byte(p.Data()), err +} + +func (s SessionManager_registerUdpSession_Params) HasDstIp() bool { + p, err := s.Struct.Ptr(1) + return p.IsValid() || err != nil +} + +func (s SessionManager_registerUdpSession_Params) SetDstIp(v []byte) error { + return s.Struct.SetData(1, v) +} + +func (s SessionManager_registerUdpSession_Params) DstPort() uint16 { + return s.Struct.Uint16(0) +} + +func (s SessionManager_registerUdpSession_Params) SetDstPort(v uint16) { + s.Struct.SetUint16(0, v) +} + +// SessionManager_registerUdpSession_Params_List is a list of SessionManager_registerUdpSession_Params. +type SessionManager_registerUdpSession_Params_List struct{ capnp.List } + +// NewSessionManager_registerUdpSession_Params creates a new list of SessionManager_registerUdpSession_Params. +func NewSessionManager_registerUdpSession_Params_List(s *capnp.Segment, sz int32) (SessionManager_registerUdpSession_Params_List, error) { + l, err := capnp.NewCompositeList(s, capnp.ObjectSize{DataSize: 8, PointerCount: 2}, sz) + return SessionManager_registerUdpSession_Params_List{l}, err +} + +func (s SessionManager_registerUdpSession_Params_List) At(i int) SessionManager_registerUdpSession_Params { + return SessionManager_registerUdpSession_Params{s.List.Struct(i)} +} + +func (s SessionManager_registerUdpSession_Params_List) Set(i int, v SessionManager_registerUdpSession_Params) error { + return s.List.SetStruct(i, v.Struct) +} + +func (s SessionManager_registerUdpSession_Params_List) String() string { + str, _ := text.MarshalList(0x904e297b87fbecea, s.List) + return str +} + +// SessionManager_registerUdpSession_Params_Promise is a wrapper for a SessionManager_registerUdpSession_Params promised by a client call. +type SessionManager_registerUdpSession_Params_Promise struct{ *capnp.Pipeline } + +func (p SessionManager_registerUdpSession_Params_Promise) Struct() (SessionManager_registerUdpSession_Params, error) { + s, err := p.Pipeline.Struct() + return SessionManager_registerUdpSession_Params{s}, err +} + +type SessionManager_registerUdpSession_Results struct{ capnp.Struct } + +// SessionManager_registerUdpSession_Results_TypeID is the unique identifier for the type SessionManager_registerUdpSession_Results. +const SessionManager_registerUdpSession_Results_TypeID = 0x8635c6b4f45bf5cd + +func NewSessionManager_registerUdpSession_Results(s *capnp.Segment) (SessionManager_registerUdpSession_Results, error) { + st, err := capnp.NewStruct(s, capnp.ObjectSize{DataSize: 0, PointerCount: 1}) + return SessionManager_registerUdpSession_Results{st}, err +} + +func NewRootSessionManager_registerUdpSession_Results(s *capnp.Segment) (SessionManager_registerUdpSession_Results, error) { + st, err := capnp.NewRootStruct(s, capnp.ObjectSize{DataSize: 0, PointerCount: 1}) + return SessionManager_registerUdpSession_Results{st}, err +} + +func ReadRootSessionManager_registerUdpSession_Results(msg *capnp.Message) (SessionManager_registerUdpSession_Results, error) { + root, err := msg.RootPtr() + return SessionManager_registerUdpSession_Results{root.Struct()}, err +} + +func (s SessionManager_registerUdpSession_Results) String() string { + str, _ := text.Marshal(0x8635c6b4f45bf5cd, s.Struct) + return str +} + +func (s SessionManager_registerUdpSession_Results) Result() (RegisterUdpSessionResponse, error) { + p, err := s.Struct.Ptr(0) + return RegisterUdpSessionResponse{Struct: p.Struct()}, err +} + +func (s SessionManager_registerUdpSession_Results) HasResult() bool { + p, err := s.Struct.Ptr(0) + return p.IsValid() || err != nil +} + +func (s SessionManager_registerUdpSession_Results) SetResult(v RegisterUdpSessionResponse) error { + return s.Struct.SetPtr(0, v.Struct.ToPtr()) +} + +// NewResult sets the result field to a newly +// allocated RegisterUdpSessionResponse struct, preferring placement in s's segment. +func (s SessionManager_registerUdpSession_Results) NewResult() (RegisterUdpSessionResponse, error) { + ss, err := NewRegisterUdpSessionResponse(s.Struct.Segment()) + if err != nil { + return RegisterUdpSessionResponse{}, err + } + err = s.Struct.SetPtr(0, ss.Struct.ToPtr()) + return ss, err +} + +// SessionManager_registerUdpSession_Results_List is a list of SessionManager_registerUdpSession_Results. +type SessionManager_registerUdpSession_Results_List struct{ capnp.List } + +// NewSessionManager_registerUdpSession_Results creates a new list of SessionManager_registerUdpSession_Results. +func NewSessionManager_registerUdpSession_Results_List(s *capnp.Segment, sz int32) (SessionManager_registerUdpSession_Results_List, error) { + l, err := capnp.NewCompositeList(s, capnp.ObjectSize{DataSize: 0, PointerCount: 1}, sz) + return SessionManager_registerUdpSession_Results_List{l}, err +} + +func (s SessionManager_registerUdpSession_Results_List) At(i int) SessionManager_registerUdpSession_Results { + return SessionManager_registerUdpSession_Results{s.List.Struct(i)} +} + +func (s SessionManager_registerUdpSession_Results_List) Set(i int, v SessionManager_registerUdpSession_Results) error { + return s.List.SetStruct(i, v.Struct) +} + +func (s SessionManager_registerUdpSession_Results_List) String() string { + str, _ := text.MarshalList(0x8635c6b4f45bf5cd, s.List) + return str +} + +// SessionManager_registerUdpSession_Results_Promise is a wrapper for a SessionManager_registerUdpSession_Results promised by a client call. +type SessionManager_registerUdpSession_Results_Promise struct{ *capnp.Pipeline } + +func (p SessionManager_registerUdpSession_Results_Promise) Struct() (SessionManager_registerUdpSession_Results, error) { + s, err := p.Pipeline.Struct() + return SessionManager_registerUdpSession_Results{s}, err +} + +func (p SessionManager_registerUdpSession_Results_Promise) Result() RegisterUdpSessionResponse_Promise { + return RegisterUdpSessionResponse_Promise{Pipeline: p.Pipeline.GetPipeline(0)} +} + +const schema_db8274f9144abc7e = "x\xda\xccY{pT\xe5\x15?\xe7\xde\xdd\xdcM\xd8" + + "es{\x97\x98d\xa4)\x0c\x8c\x05\x0d\x0a)\x1d\xa4" + + "\xda$\x98\xa0\x89\xa8" + + "\"\xdd\xb6\x1f\xfe\xf5\x82\xdc];\xf7\x14\xads~\x1f" + + "7\x86\xe3 do\xec\xf8$\xd7\xfdP\xea\xa1\x92\xdd" + + "a\xc7\xac1'\x11\xb0i\xfc\x98\x06\x04\xb4g\xbeu" + + "l\xe1\xfc\x9f-\xfbq\xe0\xec\xc5\xd1Utv\xf3\xb2" + + "\x93\x07\xaa\x93\xb9G\xcb\xecq\\33\xba\x17\xa5\xf9" + + "Q\xb2\xa7#J*<\xfe\xd5++W\x1e\x9b\xbb\x0f" + + "\xc4FW\x8c\x1aM\x92\x98\xd0\xd5\xfc\x17\xca}\xbfy" + + "\xb2\x1c-\x8e_X\xb4\x07\xa5~\x92\xd3T\x88:\xfa" + + "\xdcz`\xe7\xf9\x91\x07?\xfa\xf9P^\xdc\x15\xebA" + + "i\x7f\x8cn}\xd7\xc4\xcf\xe1" + + "\x01\xed\xfa}\xdf\xfa\xc9\x9c\xcc\x9b/\x0e\xa1\xa9\xf4\xf9" + + "WNJ\x95\x12}\x85%R\xf4h\xe3\xfe\x1b\xde\xbf" + + "\xfd\xe0\xa1\x92\xa2\x8e\x0f\x15\xc9\x09aA\"\xffy\x08" + + "(\xf3\x92\xc3\xb9C\xeaC\xe9\x11G\xdc\xc3\x0e7w" + + "L\xa9[\xf7\xc7o\x1f\x09\x04\xed\x11\xe9]\x84\x90\xbd" + + "\xe0;K\xfa*\xd7\x1c=\x1a\xbch\x97\xe4xd\xbf" + + "s\xf4\x1f?:~\xe7\x89\\\xe6\xaf\x0e\xf0\\\x9f\x1d" + + "\x94f\x13\x8e\xdf\x97(\xab\xcei\x88\xb5O<\xdcu" + + "\xbc\x18\xca\xa2\x88g\x13s\x88\xe1\x9d\x04\x89\x98ym" + + "+[:\xeb\xaa\xe3\x832\xfa\xf3\xc4l\x94b\xe3\x1c" + + "\x90\x8d\xdb\x8c\xd2\x84\x9as\x00\xec;nl[x\xf1" + + "\xc4\x03'\x83*\x895\x84Air\x0d\xc9[6\xeb" + + "\xc4\xe5\x93\xefx\xee\xe4P@k\xaf\x99\x8aRw\x0d" + + "\x99.\x13\xf3\x87s\x7fp\xa8>^\x7f\xaa\xccM\x15" + + "\x0e\x82j\xfaP\xdaB\xbcM\x9bj\x9e'0\xdd\xfc" + + "\xf65+_\xbf\xe9\xa3\x8f\xcb#\xea\x88\xee\xafM\xa2" + + "t{-\x89\xdeRK\xf1\xbfw\xd1\xdf\xd7\x9e\xd8Q" + + "\xf3\xc9 \xbb\xa6\xd4\xf5\xa1ti\x1dq^\\\xb7Y" + + "\xba\x9f\xbe\xec\xd7\x84\x87\xa6\xb7\xad}\xf1\xd3\x00\xe2\xd7" + + "\xd7u\x12\xe2\xef\x11\x1e8\xba\xeeO\xd7|\x164x" + + "M\xdd\xbbd\xf0]ud\xf0\xea\x0f\xef\xbf\xe2\xce\xa5" + + "\x8f}\x11\x08\xdf\xfe\xba\x0dt\xd4*h\x1a\xcb\x1a\xf9" + + "P\xfaB\xf73=-\xad\xe4\xb5\xfc\xec\xd6\x82\xb5\x9c" + + "i\x96\x9aV,\x96d\xcdf^\xd7L\xd6\x85(W" + + "\xf3!\x80\x10\x02\x88J\x1f\x80|-\x8fr\x96C\x11" + + "1A\x01\x16U\".\xe7Q\xb68\x149.A\xf5" + + "E\xbcn\"\x80\x9c\xe5Q^\xc9!\xf2\x09\xe4\x01\xc4" + + "\xc26\x00y%\x8f\xf2F\x0e\xed<3r\x8a\xc64" + + "\x88[\xed\x86\x81Q\xe00\x0ah\x1b\xcc2\xfa\x95\x9e" + + ",\xc4Y\x80,\xf4]oa\x0c8\x8c\x01\xda\xcb\xf5" + + "\x82avk\x16\xaa\xd9$[f0\x13\x97c\x05p" + + "X\x018\x9cy)f\x9a\xaa\xae\xcdW4\xa5\x97\x19" + + "\x00dY\x88\x0f\x03x\x95\x17\xdd\x1a-\x8a;\x81\x13" + + "c\x82m\xb0^\xd5\xb4\x98\x81\xdd\x99\xbcs\x9e\xd7\xb5" + + "\x16\xecB\xff\"~\xf0E\x97eU\xa6Y\xf1\x0em" + + "\x99^\xe6\xbd\xce\xa1\xbc\xd7Y\xf2\xde\xc6\x80\xf7\xd6\xcf" + + "\x01\x90W\xf3(\xdf\xc2\xa1\xc8\x97\xdc\xb7i*\x80\xbc" + + "\x8eG\xf96\x0e\xed\xb4sIG\x06\x00<\xc7,c" + + "\x8aU0\x98I\xb4\xb1\x80]<:\xfe\x1b\x0b\xb8v" + + "\x053\xc8x\xd7\x9fq\xc5H/\xf7|>\x8c\xd3\xda" + + "W\xaa\xa6\xa5j\xbd\x8b\x1czs\x97\x9eU\xd3\xfdd" + + "U\xd4\xd1s\xfcl\x00Dq\xdcb\x00\xe4Dq\x0e" + + "@\xb3\xda\xab\xe9\x06\xb33\xaa\x99\xd65\x8d\x01\x9f\xb6" + + "\xd6\xf6(YEK3\xef\xa2\x8a\xc1\x17\x15/H1" + + "c\x053\xa6)\x01$N\xeaR\x0c\x85\xcf\x99r\xd4" + + "\xf3c\xfbb\x00\xb9\x8dG\xb9+\xe0\xc7\xf9\xe4\xc7y" + + "<\xcaW\x05\xfc\xd8M~\xec\xe2Q^\xca\xa1\xad\x1b" + + "j\xaf\xaa]\xc6\x807\x82`2-M\xc91\xf2Y" + + "\xc9\x1fk\xf5\xbc\xa5\xea\x9a\x89\xd5~\xc1\x06\xc4\xea\x80" + + "\xa7\x84\x91\xe05\xcdE\x8e\x0b\x1c]\x9b\x94dfA" + + "\xc8Z\xa6\x1c\xf2,\x89\xcd\x06\x90#<\xca\x09\x0e\x9b" + + "\x0df\x16\xb2\x16V\xfb}\xf1\x7fq\xeb\x10\xeeK\x0e" + + "\xe5\xbe\x19\x00\xf2\x15<\xca\x8b8\xc4\x92\xf7\xe49\xbe" + + "Km\xb3(\xaf\x030\xe3:\xaf!cZ\x1dy\xf7" + + "\xaf\xb5\x19\xd3\xea\xd2\x0d\x0b\x05\xe0P\x80aS\xa4\x18" + + "\xed8U\x1c\x02S\xc4\xd3m\x0a\x85\xf6\xeb<\xca\xdf" + + "\x08\xe86\x9d\x0a\xccE<\xca\x97ph+\xe9\xb4^" + + "\xd0\xacE\xc0+\xbde\x08N1\x88\xa7\x0d\xe6\x07\xd7" + + "\xbd6\xcaB;RF\x1447\xb4E\xf2" + + "\xa4f\x8akn\x00\x96v\x02\xc8\xd5<\xca\xe7rh" + + "\xf7\x1aJ\x9au1\x03U=\xb3@\xd1\xf4\x14\xcf\xd2" + + "\x18\x06\x0e\xc3\x81K\xc7\x9e\xe9\xa5I\x07\xa1&x\xa7" + + "\x86?o\xb0\x92\x13J\xc7\xbb\x1a\x8a:'<\x9d\xd7" + + "L\xf4;\x82\x17\xee\xf5=~\x9d\xf32y\x0b\x01\xe3" + + "\x16\x1e\xe5\xed\x81\x8ax\x17\xe5\xfcV\x1e\xe5\x078\x14" + + "C\xa1\x04\x86\x00\xc4\xfb\x09\xb5\xdby\x94ws\x03\xfb" + + "\x06[\xc14\xabM\xed\x05\x81\x99>\x95TlS{" + + "\x19\xf0\xe6\xd9V\x85\xc8\x08\xfe\xd0{L=\xcb,\xd6" + + "\xc6\xd2Y\xc5P,u\x05+\xfe^\x02\xa3\x1b\xd4\xe1" + + "p\x9b\x1c\x94\xe1\x84\xdf\xb8\xdb\xaa\x03p\x98\xe8\x97\x16" + + "\x81\x05:\xec0\xda\x16\x85\x93f\xba6\x08\x03~\xc6" + + "\x94p\x80\xe6p\x9d\xc3g_\x98\xb7TA\xd7L\xd2" + + "/\x10\xfa\xd9C\x85\xde\xf0C\xefV\xa1-\x1b\x82\x91" + + "\xc7R\xe4w\xfaA\x16C\\1\xf2\xbb\xf6\x00\xc8\xbb" + + "y\x94\x1f\xe3\xb0\xb9\xd8 \xb1\xda\x7f\xf0\x95\xa2Ul" + + "\x03\xf3thH+Y\xbf|\xd9\x06\xcbg\x954k" + + "\xc7R\xcb\x03D\xe0\x10\x1d\x88\xe4\xf2\x063MTu" + + "M.(Y\x95\xb7\xfa\xbd\x89C+\xe4\xba\x0c\xb6B" + + "E\xbd`\xb6Z\x16\xcb\x09y\xcb\x1c\xcd<\xe2;\x88" + + "\xea\x83\xa0f\xcd\xb2R8\xd5\xaf=\x9e\x83\x1a\xa9\x14" + + "^\xc0\xa3<\x8b\xc3x\xa1\xa0z\xb5\xd8\xce\xeai'" + + "n\x10_\xa0\xe4\xd8\xa0hW\x8c\x98\xab\x032\xbdK" + + "\x89;\xa9\xfa\xff\xd4t\x87\x1fY\xc9tg\xa6\x0b\xa8" + + "L)\xd0\xc2\xa3\xa2?I" + + "\xf4\x0a.\x81\x15\x00\xd2~\xec\x03H\xed#\xfa\xd3D" + + "\x17\xc2\x09z]HO\xa1\x01\x90\xfa%\xd1\x7fO\xf4" + + "Hm\x02#\x00\xd2\x01\x87\xfe\x0c\xd1_\"ze]" + + "\x02+\x01\xa4?\xe0\x06\x80\xd4\x0bD?D\xf4*L" + + "`\x15\xbd\x83q'@\xea\x10\xd1\xffL\xf41\x15\x09" + + "\x1c\x03 \xbd\xe3\xe8s\x98\xe8\xef\x11=\x1aJ`\x14" + + "@\xfa\x0b\xee\x01H\xbdG\xf4\x7f\x12=&$0\x06" + + " }\xe0\xd8u\x82\xe8\x11\xael\x1cv\x11U6\xf3" + + "\xf2\xba\xe9\x85\x8c\x95r\x1c\x8bp\xef\xd2\xe34\xd7b" + + "\xdc_\xf8\x00b\x1c\xd0\xce\xebzv\xc1@\xa4\xc6-" + + "\xa5\xd7t\xe7\xebj\xff\x91\x0eHD\xaf\xefC\\\xd7" + + ":2^!(\xaf:\xae&\xaa\xd9Z\xb0\xf4B\x1e" + + "\x1a2\x8a\xc52^\xcd1\x0a\xda\\C\xcf-Bf" + + "\xe4TM\xc9\x8eP\x8d*\x81\xc3J(\x95\x04W\xf6" + + "\xf0\xa5\xe9\xf4\xaf\x05\x0f\xd1\\9\xa2\x1b\xf2\xb3\x17)" + + "\xbd\xa3\xa9S3\xfc9.\xae\x05\x0aR\xc3\x0a%[" + + "\xf82\xe5i\xe0(\x91l.\x8e\"#\xcd\xd2\xeek" + + "~\xe4R2p \x1c\xd8P1\xb0N\xa3{\xb8\x92" + + "\xfcQ\xab\xdf\xcb\xac\xe2\x17=\x0ai$\x17\x82m\xfe" + + "\xccN'\x99\x19\x1f\x8d\xe9\xfe\xd6c\xe4g\xc4\x10\x8d" + + "\x7f\x88\xb6\xef\xce\x9c\x81\x17-\xc5~)\x8f\xf2\xf2@" + + "\xec\x195\x85\x0c\x8fr\xdeo\xe2\xb9\xa4\xbf\x0e\x10y" + + "\xae\xb4\x0f\xa0F\x91\xe7Q^\xcda\x9c\xde|X\xed" + + "\xef8\x07(=\xf0\x9dKP\xe8\xd02\x0cp\xa5\x8b" + + "\xe6@\xfb\xf0\xd6\x81#Og\xa33\xdb\x9dzGt" + + "\xb8\xb7b+\xbb\xf9\xb4\x0f\xa0\xe6\xe2\xa5\x84\xb3Zg" + + "\x13\xe1\xae\x1b\xd1]i\x89\xfbW\x01'>\"\xa0\xbf" + + "\x92Cw\x03'\xee2\x80\x13w\x08\xc8y\x0b\\t" + + "\x17\xb5\xe2\x96[\x81\x137\x09\xc8{\xfbWt\x97B" + + "\xd3\xfb\xab\x108q\x8d\x80!om\x8d\xeeJI\xbc" + + "\xae\x0f8Q\x150\xec\xadv\xd1\xdd-\x8aWo\x00" + + "N\xec\xf6\xd7\"\xd0\\\xb4\xa3\x05m\x17\xa3\xd0\xe0\xa0" + + "\xb4\x05mwrDw\xa8\x00hA\xdb\x9d\x81\xf9\xd3" + + "\x0d\xc1\x0e\x97\xbb\x00\x80xZ\xb1X\x0b\x0dg\xc5\xfc" + + "\xc7R\x01\x80\x16\x94C\x18\xd8\xa8\x01|\xd9Q'\xc9" + + "\x1a\xce\xa6\x94\xf0C=\xca\xe9\x1eo\x01\x14\x90K\xd3" + + "[\x94G\xb9\x96\x1bq`\x0b\x9d\xce\x0a\x17\xb4q:" + + "L\xf2\xbf\xe6\xc9?H\x03\xcfK<\xca\x87\x03\xe9\xf8" + + "\x06\x11_\xe3Q>\x12\x18x\xde\xa4\x1c=\xcc\xa3|" + + "\xca_\xcf\xfd\xebV\x00\xf9\x14\x8f\xc9\xc0\x00!~N" + + "\x8c\x9fQ\x9bu\xc6\x07,\x8e\x0fa\xdc\x06\x90\x8aP" + + "\xfbM8\xe3C\xa88>\x88\xd8\x03\x90\xaa&\xfa\xb9" + + "\xc1\xf1\xa1\x0e\x17\x03\xa4j\x89>\x09\x07\xbeG\x84\x82" + + "\xe1\x0fXY\xbdw\x9e\xaa\x0d\xd9\x93\xdc}!Zs" + + "\x155[0\x18\xf8-\xb1T$\xda\x02]\xba\xb8H" + + "l]F\xf0K\x11x2hz\x9b\x893x\x09\x8e" + + "\xaac\xb4\x1b\x86\x8eF\xd9\xf09\xc3\x1f>\xbd\xd9s" + + "\xb1\xbfd\x11\xb9\x96\xd2\x96\xa5\xc7\x1f\x97\x1b\xd2J\xc1" + + "d\x83l\x00\x9e\x19\xde\xeb\xdd\\\xae\x17\xb2\x99$\x03" + + "\xc12\xfa\xcb\\0\xe2\x10\x9abq\xb7\xe2D\x9c\x8a" + + "\xe3n\xeb\xd1]\xca\x8b\xd3w\x02'6R\xc5q\x17" + + "\xd0\xe8\xfe\xefE\x9c\xb0\x178q|`/\xea\xfa\xc0" + + "\xd9\x8b\x06S\xbe\xf8\x83\x83\xd1\x81\x0b\xd3\xb3xv\x16" + + "\xdb\xcf\x19d\xfa\x80=\xe2\xa8\xd7o\xde\x7f\x1f\xcb2" + + "\xbd\xf2l\x9f\xf7n#\xf9o\x00\x00\x00\xff\xff2s" + + "\xeb\xfe" func init() { schemas.Register(schema_db8274f9144abc7e, 0x82c325a07ad22a65, + 0x839445a59fb01686, 0x83ced0145b2f114b, 0x84cb9536a2cf6d3c, 0x85c8cea1ab1894f3, + 0x8635c6b4f45bf5cd, + 0x904e297b87fbecea, 0x9496331ab9cd463f, 0x97b3c5c260257622, 0x9b87b390babc2ccf, 0xa29a916d4ebdd894, 0xa353a3556df74984, 0xa766b24d4fe5da35, + 0xab6d5210c1f26687, 0xb046e578094b1ead, 0xb4bf9861fe035d04, 0xb5f39f082b9ac18a,