TUN-5300: Define RPC to register UDP sessions

This commit is contained in:
cthuang 2021-11-12 09:37:28 +00:00 committed by Arég Harutyunyan
parent 571380b3f5
commit fc2333c934
9 changed files with 1035 additions and 301 deletions

View File

@ -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.
@ -67,6 +69,7 @@ func NewQUICConnection(
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) {

View File

@ -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",
},

43
connection/udp_session.go Normal file
View File

@ -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,
}
}

View File

@ -532,6 +532,7 @@ func ServeQUIC(
MaxIncomingStreams: connection.MaxConcurrentStreams,
MaxIncomingUniStreams: connection.MaxConcurrentStreams,
KeepAlive: true,
EnableDatagrams: true,
Tracer: quicpogs.NewClientTracer(connLogger.Logger(), connIndex),
}
for {

View File

@ -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()
}

View File

@ -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
}

View File

@ -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
}

View File

@ -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);
}

View File

@ -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_" +
"<Z\x8e\x87\xebj\xbe\xaa\x0f\xe5MUnx\xaa\xdc" +
"#n|r\xd7\xd9\x89\xef\xbc\xf7\xd8D\x81\xb9\x7fF" +
"\x1f\xca\xfbgP`\x1e\x9bA\xce\xce\xec\xc2\xc3O4" +
"\xc7~\x12\x0d\xf3\xcc\xeac\xae\xb3\xd5\x14\xe6\xd3\xdf^" +
"T\xa3\xbf3\xfaD\xd9n\xae\xe23\xd5\xdd(\xbfQ" +
"M\xbb\x1dr\x95\xbb\xaf\xb8}{\xfc\xe8\xedO\x91\xa5" +
"\x11|\xe3\x09\xd7\xcf\x1a\x13\xe5\xad5\xf4sKM\x1d" +
"\x03t\xea\x1f\xfe\xe2\x0f\x17e\x0f=;\x81\xa5r\\" +
":!K\x12\xfd\xaa\x91\xc8\xd0#M\xfb\xbe\xf1\xd7\x9b" +
"_>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<!" +
"\x94\x910?\xa3\xca\x98\x163\xac\x002^\xba\xe3\xe8" +
"\xa5{\x8f\x91$6\x85\xc9\xf05\x0e\x88I@\xa7`" +
"\x18\xb9ec35i\xab\xfd\x96\xcf\xeaj\xc3\x17\x1b" +
" \x09\x83\xd6\x0cIC\xef\xca\x06\x85\xa0\xbc\xea\xf8\x96" +
"hV{\xd16\x8a\x05h\xc8\xaa6\xcf\x065\xc7," +
"\xea\x8bM#\xbf\x02\xb9\x99\xd7t57E5\xaa\x04" +
"\x01+\xa1T\x12\xfc\xbd'/M\x9f\xccQ\x83\x8c\x16" +
"\xca3\xba\xa1\xb0p\x85\xda?\x9d:5?\xa4ZI" +
"=R\x90\x1a\x86\xd4\\\xf1\xd3\x94\xa7\xb1\xdd\xbe\xb7\xd5" +
"c\x0bQ\x8aB\x0d%\xc1PI\x09\xd8jz\x1d\xaa" +
"\xd6\x7f\xdaM]J\xc6r\xb6\xa4\xff\x10r\xf7\xc7\xc8" +
"\xb8\x84\xce\x11J\xfbO\xdb\xfc~n{\xbf\xe8)B" +
"<Z\x8cv\xe2\x93[\xdd\xcb\xad\xe4t\\\x0f\x9f\xc0" +
"e\xce\x8b\xd3j\xfc\x13\xb4}\x9f\x16F\xdeQ\x84\xfd" +
"*\x86\xca@\x04{NM!\xcbP)\x84M<\xdf" +
"\x1b\xbe\x0d%&\x94\x1e\x87\xd4(\x0a\x0c\x95u\x02&" +
"\xe9\xa5\x81\xb5\xe1\x0ck\x8c\xd1c_W\x94\x0a]z" +
"\x96\x03\x0e\xfb\xd9\x1ci\x1f\xc1lhj\x025=\xb7" +
"}b:e\xc0\x83y\xcbt\xdf(\xad\xde\xa1\x94g" +
"\xb3Y\x1c \x98=\xa1?\xdf\x90\xf6\xad\x05A\xfa\xbe" +
"\x88\xe1|\x06\xfdq\x8c\xb4\xdb\x04A\xda)\xa2\x10\x0c" +
"\xe8\xd0\x1f\xc4I[o\x04A\xda\"\"\x0b\xe6k\xe8" +
"O\x08\x9aG\xaa\x10\x04i\xbd\x88\xb1`\xa6\x88\xfe|" +
"A\xbaz\x10\x04I\x131\x1e\x8c\xee\xd0\x1f4I\xab" +
"GA\x90V\x8a\x8e\x1f$h\xf5\xfchC\xc7\xcfQ" +
"hp\xb3\xb4\x0d\x1d\x9f9\xa2O*\x00\xda\xd0\xf1i" +
"*\xfb$\x9e\xeaj\xf9\xcfNH\xd2\xc3\xb3\x8d\xc8\x99" +
"w\xff\xb1T\x00\xa0\x0d\x95\x18F\xc6+\x00\x9f\x96\xea" +
"\xf4\xf2\x86\xcfRJ&\x00\xd7;'\x18;D\xf6%" +
"\xf6V\xcdP\x99-LI\xd8b\x9f\xe4\x85\x9f\xb4I" +
"ZL\xfb\xffo\xb0\xff\xcbDx\x9ec\xa8\x1c\x8c\\" +
"\xc7WI\xf8\x12C\xe5p\x84\xf0\x1c\xa2;z\x90\xa1" +
"\xf2~8\xaby\xf7F\x00\xe5}\x86\xbd\x11\x02!\xfd" +
"\x8b\x14?\xa26\xeb\xd2\x07\xf4\xe8C\x1c\xb7\x03\xa4\x13" +
"\xd4~S.}\x88y\xf4A\xc2>\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<rwI\xc7\x02:\xde\xec~\x84" +
"\x9b\xee\xde\xbb\xde{\x17\x09\x82<\x04\x11\xc7\x07\"\xf8" +
"\xa0R)\x8e\xed\x88\xdaB\xb5\xd3\xda\xb1Si\xeb\xa3" +
"\xe3\x13\x07;\xa88mE\xda\xca`m\x11\xc7\xb1U" +
"o\xe7\xdc\xdd\xfb\xc8&$A\xfaG\xff\xbb9{\xbe" +
"\xf3\x9d\xc7\xef<\xbe\x93\x8b0\xd2\xc2M\x0f\xf7F\x01" +
"\xe4\xad\xe1\x0a\x9bM}}\xd5\xee\xc9\xbf\xdb\x00r=" +
"\xa2}\xe3\xd3\x9d\x89O\xad\x0doC\x98\x17\x00\x9a\xea" +
"*V\xa1\xd4X!\x00HS*\xfe\x06h\xdf\\\xb3" +
"\xef\xc1\x87\xdb\xb7\xdf\x04b=\xef3\x036\xc5\x84N" +
"\x94&\x08\xc49^\xb8\\\x9aO_\xf6\x95\xe2\x85K" +
"\x12\xaf\xbdB\xdcA\xd1!\x12=S\x98\x8aR\xbbs" +
"\xa0U \xd1\x97\xe4^\xdd\xf3\xcd\x1d/n\x04\xb1\x9e" +
"\x1b \xba1\xb2\x0a\xa5\xd6\x08q^\x1aY\x08h\x7f" +
"\xb4\xbd\xf6\xd1\x1f\xbe\xf2\xc2&\x10\xcfC(i\xda\x1d" +
"y\x0b\x01%5\xf2S@\xfb\xe5\x8f\x97\x9cz\xf2\xb9" +
"\x997\x838\x85\x18\x90\x18*+'r\x80\xd2\x84\xca" +
"f@\xfb\xf8\x89\xffl\xbea\xca\x82\xad OA\xe2" +
"\xe0\x88\xa3\xb5\xb2\x9e8\xbe[I\"\x9a\xe7\xbe\xfcT" +
"}\xd3=\xdb\xcbTw\x18?\xae\x9c\x8aR\xb8\x8a\x14" +
"\xc2\xaa\xeb\x01\xed\x89+&_\xfb\xdbg\x9f\xb8\x17\xe4" +
"FD\xfbH\xcf\xf9o\xf0\xbb\xf6\xbe\x0d\xdd( \x07" +
"\xd0\xc4\xaa\xf6\x90n\x05\x87\xf7\xd5\x0b\x9e\xfe\xd5\xd6'" +
"6\x7f\x1f\xe4\xf3\xe8j\xc7\x17oV\xfd\x9b\x18>\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}<F\x9e\x19\xd7\x81G\x9e\x99\x1e\xfa" +
"E0\xac\xb1\xb1\xc7\xc93\x13\xc6RL\xc6\x7f0'" +
"\xa6}\xb8\xe1\x992i\x0e\xe3\x81\xb1\x9d(\xbd1\x96" +
"\xa4\x1dt\x98;\x97\xdc\xbd-|\xec\xee\xe7I\xd3\x00" +
"\x9e\xc2\x84\xa3\xa6\\\xdc@i}\x9c>\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<D\x92\x128\xd3\x14\xe7$+V\xbai\x06" +
"3\x85B\xd6\"m\xa2\xb6]T\x87\xbc2\x89G\xf9" +
"\"\x0ec\xf8\x85]\xd4\xa7q\x9b\xafO\x033\x0c\xdd" +
"\xc0j\xbf\x13\x94\x82\x96.]\x80\xba\xd6\xc6,E\xcd" +
"\"\x01\xca\x1b>\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</\xa0r\xc7\x8c\x80\x1d\xae\xca\xf3" +
"{|;\x84\xef\xb1~W\xab\x06\x96\xa3\xca\xed:\xb3" +
"dL+\x08W\xfa<\xc3\xe9\x17L\xa8\x85\xf9\x06\xc7" +
"B\xd2q\x96\xab\xa3\xd4\x8f\x9d\x00\xa9\x95\xc8cj#" +
"\xfajJ\xebq\x0e@j5\xd1oA_Si\x13" +
"\xd6\x03\xa4\xd6\x11\xfd6\xf4Fki\x0b\xee\x05H\xdd" +
"F\xe4\xfb\x88=\xc4;)!\xedp\xc4o'\xfan" +
"\xa2\x87C\x09\x0c\x03H\xbbp*@\xea>\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,