mirror of
https://github.com/cloudflare/cloudflared.git
synced 2025-07-27 17:19:58 +00:00
TUN-8415: Refactor capnp rpc into a single module
Combines the tunnelrpc and quic/schema capnp files into the same module. To help reduce future issues with capnp id generation, capnpids are provided in the capnp files from the existing capnp struct ids generated in the go files. Reduces the overall interface of the Capnp methods to the rest of the code by providing an interface that will handle the quic protocol selection. Introduces a new `rpc-timeout` config that will allow all of the SessionManager and ConfigurationManager RPC requests to have a timeout. The timeout for these values is set to 5 seconds as non of these operations for the managers should take a long time to complete. Removed the RPC-specific logger as it never provided good debugging value as the RPC method names were not visible in the logs.
This commit is contained in:
12
quic/constants.go
Normal file
12
quic/constants.go
Normal file
@@ -0,0 +1,12 @@
|
||||
package quic
|
||||
|
||||
import "time"
|
||||
|
||||
const (
|
||||
HandshakeIdleTimeout = 5 * time.Second
|
||||
MaxIdleTimeout = 5 * time.Second
|
||||
MaxIdlePingPeriod = 1 * time.Second
|
||||
|
||||
// MaxIncomingStreams is 2^60, which is the maximum supported value by Quic-Go
|
||||
MaxIncomingStreams = 1 << 60
|
||||
)
|
111
quic/pogs.go
111
quic/pogs.go
@@ -1,111 +0,0 @@
|
||||
package quic
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
capnp "zombiezen.com/go/capnproto2"
|
||||
"zombiezen.com/go/capnproto2/pogs"
|
||||
|
||||
"github.com/cloudflare/cloudflared/quic/schema"
|
||||
)
|
||||
|
||||
// ConnectionType indicates the type of underlying connection proxied within the QUIC stream.
|
||||
type ConnectionType uint16
|
||||
|
||||
const (
|
||||
ConnectionTypeHTTP ConnectionType = iota
|
||||
ConnectionTypeWebsocket
|
||||
ConnectionTypeTCP
|
||||
)
|
||||
|
||||
func (c ConnectionType) String() string {
|
||||
switch c {
|
||||
case ConnectionTypeHTTP:
|
||||
return "http"
|
||||
case ConnectionTypeWebsocket:
|
||||
return "ws"
|
||||
case ConnectionTypeTCP:
|
||||
return "tcp"
|
||||
}
|
||||
panic(fmt.Sprintf("invalid ConnectionType: %d", c))
|
||||
}
|
||||
|
||||
// ConnectRequest is the representation of metadata sent at the start of a QUIC application handshake.
|
||||
type ConnectRequest struct {
|
||||
Dest string `capnp:"dest"`
|
||||
Type ConnectionType `capnp:"type"`
|
||||
Metadata []Metadata `capnp:"metadata"`
|
||||
}
|
||||
|
||||
// Metadata is a representation of key value based data sent via RequestMeta.
|
||||
type Metadata struct {
|
||||
Key string `capnp:"key"`
|
||||
Val string `capnp:"val"`
|
||||
}
|
||||
|
||||
// MetadataMap returns a map format of []Metadata.
|
||||
func (r *ConnectRequest) MetadataMap() map[string]string {
|
||||
metadataMap := make(map[string]string)
|
||||
for _, metadata := range r.Metadata {
|
||||
metadataMap[metadata.Key] = metadata.Val
|
||||
}
|
||||
return metadataMap
|
||||
}
|
||||
|
||||
func (r *ConnectRequest) fromPogs(msg *capnp.Message) error {
|
||||
metadata, err := schema.ReadRootConnectRequest(msg)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return pogs.Extract(r, schema.ConnectRequest_TypeID, metadata.Struct)
|
||||
}
|
||||
|
||||
func (r *ConnectRequest) toPogs() (*capnp.Message, error) {
|
||||
msg, seg, err := capnp.NewMessage(capnp.SingleSegment(nil))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
root, err := schema.NewRootConnectRequest(seg)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err := pogs.Insert(schema.ConnectRequest_TypeID, root.Struct, r); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return msg, nil
|
||||
}
|
||||
|
||||
// ConnectResponse is a representation of metadata sent as a response to a QUIC application handshake.
|
||||
type ConnectResponse struct {
|
||||
Error string `capnp:"error"`
|
||||
Metadata []Metadata `capnp:"metadata"`
|
||||
}
|
||||
|
||||
func (r *ConnectResponse) fromPogs(msg *capnp.Message) error {
|
||||
metadata, err := schema.ReadRootConnectResponse(msg)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return pogs.Extract(r, schema.ConnectResponse_TypeID, metadata.Struct)
|
||||
}
|
||||
|
||||
func (r *ConnectResponse) toPogs() (*capnp.Message, error) {
|
||||
msg, seg, err := capnp.NewMessage(capnp.SingleSegment(nil))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
root, err := schema.NewRootConnectResponse(seg)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err := pogs.Insert(schema.ConnectResponse_TypeID, root.Struct, r); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return msg, nil
|
||||
}
|
@@ -1,274 +0,0 @@
|
||||
package quic
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"io"
|
||||
"net"
|
||||
"time"
|
||||
|
||||
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 defines the first 6 bytes of the stream, which 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}
|
||||
)
|
||||
|
||||
type protocolVersion string
|
||||
|
||||
const (
|
||||
protocolV1 protocolVersion = "01"
|
||||
|
||||
protocolVersionLength = 2
|
||||
|
||||
HandshakeIdleTimeout = 5 * time.Second
|
||||
MaxIdleTimeout = 5 * time.Second
|
||||
MaxIdlePingPeriod = 1 * time.Second
|
||||
|
||||
// MaxIncomingStreams is 2^60, which is the maximum supported value by Quic-Go
|
||||
MaxIncomingStreams = 1 << 60
|
||||
)
|
||||
|
||||
// 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 (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(rss); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
msg, err := capnp.NewDecoder(rss).Decode()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
r := &ConnectRequest{}
|
||||
if err := r.fromPogs(msg); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return r, nil
|
||||
}
|
||||
|
||||
// WriteConnectResponseData writes response to a QUIC stream.
|
||||
func (rss *RequestServerStream) WriteConnectResponseData(respErr error, metadata ...Metadata) error {
|
||||
var connectResponse *ConnectResponse
|
||||
if respErr != nil {
|
||||
connectResponse = &ConnectResponse{
|
||||
Error: respErr.Error(),
|
||||
}
|
||||
} else {
|
||||
connectResponse = &ConnectResponse{
|
||||
Metadata: metadata,
|
||||
}
|
||||
}
|
||||
|
||||
msg, err := connectResponse.toPogs()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := writeDataStreamPreamble(rss); err != nil {
|
||||
return err
|
||||
}
|
||||
return capnp.NewEncoder(rss).Encode(msg)
|
||||
}
|
||||
|
||||
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, configManager tunnelpogs.ConfigurationManager, logger *zerolog.Logger) error {
|
||||
// RPC logs are very robust, create a new logger that only logs error to reduce noise
|
||||
rpcLogger := logger.Level(zerolog.ErrorLevel)
|
||||
rpcTransport := tunnelrpc.NewTransportLogger(&rpcLogger, rpc.StreamTransport(s))
|
||||
defer rpcTransport.Close()
|
||||
|
||||
main := tunnelpogs.CloudflaredServer_ServerToClient(sessionManager, configManager)
|
||||
rpcConn := rpc.NewConn(
|
||||
rpcTransport,
|
||||
rpc.MainInterface(main.Client),
|
||||
tunnelrpc.ConnLog(&rpcLogger),
|
||||
)
|
||||
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
|
||||
}
|
||||
|
||||
return writeVersion(stream)
|
||||
}
|
||||
|
||||
func writeVersion(stream io.Writer) error {
|
||||
_, err := stream.Write([]byte(protocolV1)[:protocolVersionLength])
|
||||
return err
|
||||
}
|
||||
|
||||
func readVersion(stream io.Reader) (string, error) {
|
||||
version := make([]byte, protocolVersionLength)
|
||||
_, err := stream.Read(version)
|
||||
return string(version), err
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
// RPCClientStream is a stream to call methods of SessionManager
|
||||
type RPCClientStream struct {
|
||||
client tunnelpogs.CloudflaredServer_PogsClient
|
||||
transport rpc.Transport
|
||||
|
||||
// Time we wait for the server to respond to a request before we close the connection.
|
||||
rpcUnregisterUDPSessionDeadline time.Duration
|
||||
}
|
||||
|
||||
func NewRPCClientStream(ctx context.Context, stream io.ReadWriteCloser, rpcUnregisterUDPSessionDeadline time.Duration, 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.NewCloudflaredServer_PogsClient(conn.Bootstrap(ctx), conn),
|
||||
transport: transport,
|
||||
rpcUnregisterUDPSessionDeadline: rpcUnregisterUDPSessionDeadline,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (rcs *RPCClientStream) RegisterUdpSession(ctx context.Context, sessionID uuid.UUID, dstIP net.IP, dstPort uint16, closeIdleAfterHint time.Duration, traceContext string) (*tunnelpogs.RegisterUdpSessionResponse, error) {
|
||||
return rcs.client.RegisterUdpSession(ctx, sessionID, dstIP, dstPort, closeIdleAfterHint, traceContext)
|
||||
}
|
||||
|
||||
func (rcs *RPCClientStream) UnregisterUdpSession(ctx context.Context, sessionID uuid.UUID, message string) error {
|
||||
ctx, cancel := context.WithTimeout(ctx, rcs.rpcUnregisterUDPSessionDeadline)
|
||||
defer cancel()
|
||||
return rcs.client.UnregisterUdpSession(ctx, sessionID, message)
|
||||
}
|
||||
|
||||
func (rcs *RPCClientStream) UpdateConfiguration(ctx context.Context, version int32, config []byte) (*tunnelpogs.UpdateConfigurationResponse, error) {
|
||||
return rcs.client.UpdateConfiguration(ctx, version, config)
|
||||
}
|
||||
|
||||
func (rcs *RPCClientStream) Close() {
|
||||
_ = rcs.client.Close()
|
||||
_ = rcs.transport.Close()
|
||||
}
|
@@ -1,307 +0,0 @@
|
||||
package quic
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"net"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/google/uuid"
|
||||
"github.com/rs/zerolog"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
tunnelpogs "github.com/cloudflare/cloudflared/tunnelrpc/pogs"
|
||||
)
|
||||
|
||||
const (
|
||||
testCloseIdleAfterHint = time.Minute * 2
|
||||
)
|
||||
|
||||
func TestConnectRequestData(t *testing.T) {
|
||||
var tests = []struct {
|
||||
name string
|
||||
hostname string
|
||||
connectionType ConnectionType
|
||||
metadata []Metadata
|
||||
}{
|
||||
{
|
||||
name: "Signature verified and request metadata is unmarshaled and read correctly",
|
||||
hostname: "tunnel.com",
|
||||
connectionType: ConnectionTypeHTTP,
|
||||
metadata: []Metadata{
|
||||
{
|
||||
Key: "key",
|
||||
Val: "1234",
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, test := range tests {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
b := &bytes.Buffer{}
|
||||
reqClientStream := RequestClientStream{noopCloser{b}}
|
||||
err := reqClientStream.WriteConnectRequestData(test.hostname, test.connectionType, test.metadata...)
|
||||
require.NoError(t, err)
|
||||
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)
|
||||
assert.Equal(t, test.hostname, reqMeta.Dest)
|
||||
assert.Equal(t, test.connectionType, reqMeta.Type)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestConnectResponseMeta(t *testing.T) {
|
||||
var tests = []struct {
|
||||
name string
|
||||
err error
|
||||
metadata []Metadata
|
||||
}{
|
||||
{
|
||||
name: "Signature verified and response metadata is unmarshaled and read correctly",
|
||||
metadata: []Metadata{
|
||||
{
|
||||
Key: "key",
|
||||
Val: "1234",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "If error is not empty, other fields should be blank",
|
||||
err: errors.New("something happened"),
|
||||
metadata: []Metadata{
|
||||
{
|
||||
Key: "key",
|
||||
Val: "1234",
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
b := &bytes.Buffer{}
|
||||
reqServerStream := RequestServerStream{noopCloser{b}}
|
||||
err := reqServerStream.WriteConnectResponseData(test.err, test.metadata...)
|
||||
require.NoError(t, err)
|
||||
|
||||
reqClientStream := RequestClientStream{noopCloser{b}}
|
||||
respMeta, err := reqClientStream.ReadConnectResponseData()
|
||||
require.NoError(t, err)
|
||||
|
||||
if respMeta.Error == "" {
|
||||
assert.Equal(t, test.metadata, respMeta.Metadata)
|
||||
} else {
|
||||
assert.Equal(t, 0, len(respMeta.Metadata))
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestRegisterUdpSession(t *testing.T) {
|
||||
unregisterMessage := "closed by eyeball"
|
||||
|
||||
var tests = []struct {
|
||||
name string
|
||||
sessionRPCServer mockSessionRPCServer
|
||||
}{
|
||||
{
|
||||
name: "RegisterUdpSession (no trace context)",
|
||||
sessionRPCServer: mockSessionRPCServer{
|
||||
sessionID: uuid.New(),
|
||||
dstIP: net.IP{172, 16, 0, 1},
|
||||
dstPort: 8000,
|
||||
closeIdleAfter: testCloseIdleAfterHint,
|
||||
unregisterMessage: unregisterMessage,
|
||||
traceContext: "",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "RegisterUdpSession (with trace context)",
|
||||
sessionRPCServer: mockSessionRPCServer{
|
||||
sessionID: uuid.New(),
|
||||
dstIP: net.IP{172, 16, 0, 1},
|
||||
dstPort: 8000,
|
||||
closeIdleAfter: testCloseIdleAfterHint,
|
||||
unregisterMessage: unregisterMessage,
|
||||
traceContext: "1241ce3ecdefc68854e8514e69ba42ca:b38f1bf5eae406f3:0:1",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
logger := zerolog.Nop()
|
||||
clientStream, serverStream := newMockRPCStreams()
|
||||
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(test.sessionRPCServer, nil, &logger)
|
||||
assert.NoError(t, err)
|
||||
|
||||
serverStream.Close()
|
||||
close(sessionRegisteredChan)
|
||||
}()
|
||||
|
||||
rpcClientStream, err := NewRPCClientStream(context.Background(), clientStream, 5*time.Second, &logger)
|
||||
assert.NoError(t, err)
|
||||
|
||||
reg, err := rpcClientStream.RegisterUdpSession(context.Background(), test.sessionRPCServer.sessionID, test.sessionRPCServer.dstIP, test.sessionRPCServer.dstPort, testCloseIdleAfterHint, test.sessionRPCServer.traceContext)
|
||||
assert.NoError(t, err)
|
||||
assert.NoError(t, reg.Err)
|
||||
|
||||
// Different sessionID, the RPC server should reject the registraion
|
||||
reg, err = rpcClientStream.RegisterUdpSession(context.Background(), uuid.New(), test.sessionRPCServer.dstIP, test.sessionRPCServer.dstPort, testCloseIdleAfterHint, test.sessionRPCServer.traceContext)
|
||||
assert.NoError(t, err)
|
||||
assert.Error(t, reg.Err)
|
||||
|
||||
assert.NoError(t, rpcClientStream.UnregisterUdpSession(context.Background(), test.sessionRPCServer.sessionID, unregisterMessage))
|
||||
|
||||
// Different sessionID, the RPC server should reject the unregistraion
|
||||
assert.Error(t, rpcClientStream.UnregisterUdpSession(context.Background(), uuid.New(), unregisterMessage))
|
||||
|
||||
rpcClientStream.Close()
|
||||
<-sessionRegisteredChan
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestManageConfiguration(t *testing.T) {
|
||||
var (
|
||||
version int32 = 168
|
||||
config = []byte(t.Name())
|
||||
)
|
||||
clientStream, serverStream := newMockRPCStreams()
|
||||
|
||||
configRPCServer := mockConfigRPCServer{
|
||||
version: version,
|
||||
config: config,
|
||||
}
|
||||
|
||||
logger := zerolog.Nop()
|
||||
updatedChan := 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(nil, configRPCServer, &logger)
|
||||
assert.NoError(t, err)
|
||||
|
||||
serverStream.Close()
|
||||
close(updatedChan)
|
||||
}()
|
||||
|
||||
ctx, cancel := context.WithTimeout(context.Background(), time.Second)
|
||||
defer cancel()
|
||||
rpcClientStream, err := NewRPCClientStream(ctx, clientStream, 5*time.Second, &logger)
|
||||
assert.NoError(t, err)
|
||||
|
||||
result, err := rpcClientStream.UpdateConfiguration(ctx, version, config)
|
||||
assert.NoError(t, err)
|
||||
|
||||
require.Equal(t, version, result.LastAppliedVersion)
|
||||
require.NoError(t, result.Err)
|
||||
|
||||
rpcClientStream.Close()
|
||||
<-updatedChan
|
||||
}
|
||||
|
||||
type mockSessionRPCServer struct {
|
||||
sessionID uuid.UUID
|
||||
dstIP net.IP
|
||||
dstPort uint16
|
||||
closeIdleAfter time.Duration
|
||||
unregisterMessage string
|
||||
traceContext string
|
||||
}
|
||||
|
||||
func (s mockSessionRPCServer) RegisterUdpSession(_ context.Context, sessionID uuid.UUID, dstIP net.IP, dstPort uint16, closeIdleAfter time.Duration, traceContext string) (*tunnelpogs.RegisterUdpSessionResponse, error) {
|
||||
if s.sessionID != sessionID {
|
||||
return nil, fmt.Errorf("expect session ID %s, got %s", s.sessionID, sessionID)
|
||||
}
|
||||
if !s.dstIP.Equal(dstIP) {
|
||||
return nil, fmt.Errorf("expect destination IP %s, got %s", s.dstIP, dstIP)
|
||||
}
|
||||
if s.dstPort != dstPort {
|
||||
return nil, fmt.Errorf("expect destination port %d, got %d", s.dstPort, dstPort)
|
||||
}
|
||||
if s.closeIdleAfter != closeIdleAfter {
|
||||
return nil, fmt.Errorf("expect closeIdleAfter %d, got %d", s.closeIdleAfter, closeIdleAfter)
|
||||
}
|
||||
if s.traceContext != traceContext {
|
||||
return nil, fmt.Errorf("expect traceContext %s, got %s", s.traceContext, traceContext)
|
||||
}
|
||||
return &tunnelpogs.RegisterUdpSessionResponse{}, nil
|
||||
}
|
||||
|
||||
func (s mockSessionRPCServer) UnregisterUdpSession(_ context.Context, sessionID uuid.UUID, message string) error {
|
||||
if s.sessionID != sessionID {
|
||||
return fmt.Errorf("expect session ID %s, got %s", s.sessionID, sessionID)
|
||||
}
|
||||
if s.unregisterMessage != message {
|
||||
return fmt.Errorf("expect unregister message %s, got %s", s.unregisterMessage, message)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type mockConfigRPCServer struct {
|
||||
version int32
|
||||
config []byte
|
||||
}
|
||||
|
||||
func (s mockConfigRPCServer) UpdateConfiguration(_ context.Context, version int32, config []byte) *tunnelpogs.UpdateConfigurationResponse {
|
||||
if s.version != version {
|
||||
return &tunnelpogs.UpdateConfigurationResponse{
|
||||
Err: fmt.Errorf("expect version %d, got %d", s.version, version),
|
||||
}
|
||||
}
|
||||
if !bytes.Equal(s.config, config) {
|
||||
return &tunnelpogs.UpdateConfigurationResponse{
|
||||
Err: fmt.Errorf("expect config %v, got %v", s.config, config),
|
||||
}
|
||||
}
|
||||
return &tunnelpogs.UpdateConfigurationResponse{LastAppliedVersion: version}
|
||||
}
|
||||
|
||||
type mockRPCStream struct {
|
||||
io.ReadCloser
|
||||
io.WriteCloser
|
||||
}
|
||||
|
||||
func newMockRPCStreams() (client io.ReadWriteCloser, server io.ReadWriteCloser) {
|
||||
clientReader, serverWriter := io.Pipe()
|
||||
serverReader, clientWriter := io.Pipe()
|
||||
|
||||
client = mockRPCStream{clientReader, clientWriter}
|
||||
server = mockRPCStream{serverReader, serverWriter}
|
||||
return
|
||||
}
|
||||
|
||||
func (s mockRPCStream) Close() error {
|
||||
_ = s.ReadCloser.Close()
|
||||
_ = s.WriteCloser.Close()
|
||||
return nil
|
||||
}
|
||||
|
||||
type noopCloser struct {
|
||||
io.ReadWriter
|
||||
}
|
||||
|
||||
func (noopCloser) Close() error {
|
||||
return nil
|
||||
}
|
@@ -2,8 +2,13 @@ package quic
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/rand"
|
||||
"crypto/rsa"
|
||||
"crypto/tls"
|
||||
"crypto/x509"
|
||||
"encoding/pem"
|
||||
"io"
|
||||
"math/big"
|
||||
"net"
|
||||
"sync"
|
||||
"testing"
|
||||
@@ -147,3 +152,27 @@ func serverRoundTrip(t *testing.T, stream io.ReadWriteCloser, mustWork bool) {
|
||||
}
|
||||
require.NoError(t, err)
|
||||
}
|
||||
|
||||
// GenerateTLSConfig sets up a bare-bones TLS config for a QUIC server
|
||||
func GenerateTLSConfig() *tls.Config {
|
||||
key, err := rsa.GenerateKey(rand.Reader, 1024)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
template := x509.Certificate{SerialNumber: big.NewInt(1)}
|
||||
certDER, err := x509.CreateCertificate(rand.Reader, &template, &template, &key.PublicKey, key)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
keyPEM := pem.EncodeToMemory(&pem.Block{Type: "RSA PRIVATE KEY", Bytes: x509.MarshalPKCS1PrivateKey(key)})
|
||||
certPEM := pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE", Bytes: certDER})
|
||||
|
||||
tlsCert, err := tls.X509KeyPair(certPEM, keyPEM)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return &tls.Config{
|
||||
Certificates: []tls.Certificate{tlsCert},
|
||||
NextProtos: []string{"argotunnel"},
|
||||
}
|
||||
}
|
||||
|
@@ -1,28 +0,0 @@
|
||||
using Go = import "/go.capnp";
|
||||
@0xb29021ef7421cc32;
|
||||
|
||||
$Go.package("schema");
|
||||
$Go.import("schema");
|
||||
|
||||
|
||||
struct ConnectRequest{
|
||||
dest @0 :Text;
|
||||
type @1 :ConnectionType;
|
||||
metadata @2 :List(Metadata);
|
||||
}
|
||||
|
||||
enum ConnectionType{
|
||||
http @0;
|
||||
websocket @1;
|
||||
tcp @2;
|
||||
}
|
||||
|
||||
struct Metadata {
|
||||
key @0 :Text;
|
||||
val @1 :Text;
|
||||
}
|
||||
|
||||
struct ConnectResponse{
|
||||
error @0 :Text;
|
||||
metadata @1 :List(Metadata);
|
||||
}
|
@@ -1,395 +0,0 @@
|
||||
// Code generated by capnpc-go. DO NOT EDIT.
|
||||
|
||||
package schema
|
||||
|
||||
import (
|
||||
capnp "zombiezen.com/go/capnproto2"
|
||||
text "zombiezen.com/go/capnproto2/encoding/text"
|
||||
schemas "zombiezen.com/go/capnproto2/schemas"
|
||||
)
|
||||
|
||||
type ConnectRequest struct{ capnp.Struct }
|
||||
|
||||
// ConnectRequest_TypeID is the unique identifier for the type ConnectRequest.
|
||||
const ConnectRequest_TypeID = 0xc47116a1045e4061
|
||||
|
||||
func NewConnectRequest(s *capnp.Segment) (ConnectRequest, error) {
|
||||
st, err := capnp.NewStruct(s, capnp.ObjectSize{DataSize: 8, PointerCount: 2})
|
||||
return ConnectRequest{st}, err
|
||||
}
|
||||
|
||||
func NewRootConnectRequest(s *capnp.Segment) (ConnectRequest, error) {
|
||||
st, err := capnp.NewRootStruct(s, capnp.ObjectSize{DataSize: 8, PointerCount: 2})
|
||||
return ConnectRequest{st}, err
|
||||
}
|
||||
|
||||
func ReadRootConnectRequest(msg *capnp.Message) (ConnectRequest, error) {
|
||||
root, err := msg.RootPtr()
|
||||
return ConnectRequest{root.Struct()}, err
|
||||
}
|
||||
|
||||
func (s ConnectRequest) String() string {
|
||||
str, _ := text.Marshal(0xc47116a1045e4061, s.Struct)
|
||||
return str
|
||||
}
|
||||
|
||||
func (s ConnectRequest) Dest() (string, error) {
|
||||
p, err := s.Struct.Ptr(0)
|
||||
return p.Text(), err
|
||||
}
|
||||
|
||||
func (s ConnectRequest) HasDest() bool {
|
||||
p, err := s.Struct.Ptr(0)
|
||||
return p.IsValid() || err != nil
|
||||
}
|
||||
|
||||
func (s ConnectRequest) DestBytes() ([]byte, error) {
|
||||
p, err := s.Struct.Ptr(0)
|
||||
return p.TextBytes(), err
|
||||
}
|
||||
|
||||
func (s ConnectRequest) SetDest(v string) error {
|
||||
return s.Struct.SetText(0, v)
|
||||
}
|
||||
|
||||
func (s ConnectRequest) Type() ConnectionType {
|
||||
return ConnectionType(s.Struct.Uint16(0))
|
||||
}
|
||||
|
||||
func (s ConnectRequest) SetType(v ConnectionType) {
|
||||
s.Struct.SetUint16(0, uint16(v))
|
||||
}
|
||||
|
||||
func (s ConnectRequest) Metadata() (Metadata_List, error) {
|
||||
p, err := s.Struct.Ptr(1)
|
||||
return Metadata_List{List: p.List()}, err
|
||||
}
|
||||
|
||||
func (s ConnectRequest) HasMetadata() bool {
|
||||
p, err := s.Struct.Ptr(1)
|
||||
return p.IsValid() || err != nil
|
||||
}
|
||||
|
||||
func (s ConnectRequest) SetMetadata(v Metadata_List) error {
|
||||
return s.Struct.SetPtr(1, v.List.ToPtr())
|
||||
}
|
||||
|
||||
// NewMetadata sets the metadata field to a newly
|
||||
// allocated Metadata_List, preferring placement in s's segment.
|
||||
func (s ConnectRequest) NewMetadata(n int32) (Metadata_List, error) {
|
||||
l, err := NewMetadata_List(s.Struct.Segment(), n)
|
||||
if err != nil {
|
||||
return Metadata_List{}, err
|
||||
}
|
||||
err = s.Struct.SetPtr(1, l.List.ToPtr())
|
||||
return l, err
|
||||
}
|
||||
|
||||
// ConnectRequest_List is a list of ConnectRequest.
|
||||
type ConnectRequest_List struct{ capnp.List }
|
||||
|
||||
// NewConnectRequest creates a new list of ConnectRequest.
|
||||
func NewConnectRequest_List(s *capnp.Segment, sz int32) (ConnectRequest_List, error) {
|
||||
l, err := capnp.NewCompositeList(s, capnp.ObjectSize{DataSize: 8, PointerCount: 2}, sz)
|
||||
return ConnectRequest_List{l}, err
|
||||
}
|
||||
|
||||
func (s ConnectRequest_List) At(i int) ConnectRequest { return ConnectRequest{s.List.Struct(i)} }
|
||||
|
||||
func (s ConnectRequest_List) Set(i int, v ConnectRequest) error { return s.List.SetStruct(i, v.Struct) }
|
||||
|
||||
func (s ConnectRequest_List) String() string {
|
||||
str, _ := text.MarshalList(0xc47116a1045e4061, s.List)
|
||||
return str
|
||||
}
|
||||
|
||||
// ConnectRequest_Promise is a wrapper for a ConnectRequest promised by a client call.
|
||||
type ConnectRequest_Promise struct{ *capnp.Pipeline }
|
||||
|
||||
func (p ConnectRequest_Promise) Struct() (ConnectRequest, error) {
|
||||
s, err := p.Pipeline.Struct()
|
||||
return ConnectRequest{s}, err
|
||||
}
|
||||
|
||||
type ConnectionType uint16
|
||||
|
||||
// ConnectionType_TypeID is the unique identifier for the type ConnectionType.
|
||||
const ConnectionType_TypeID = 0xc52e1bac26d379c8
|
||||
|
||||
// Values of ConnectionType.
|
||||
const (
|
||||
ConnectionType_http ConnectionType = 0
|
||||
ConnectionType_websocket ConnectionType = 1
|
||||
ConnectionType_tcp ConnectionType = 2
|
||||
)
|
||||
|
||||
// String returns the enum's constant name.
|
||||
func (c ConnectionType) String() string {
|
||||
switch c {
|
||||
case ConnectionType_http:
|
||||
return "http"
|
||||
case ConnectionType_websocket:
|
||||
return "websocket"
|
||||
case ConnectionType_tcp:
|
||||
return "tcp"
|
||||
|
||||
default:
|
||||
return ""
|
||||
}
|
||||
}
|
||||
|
||||
// ConnectionTypeFromString returns the enum value with a name,
|
||||
// or the zero value if there's no such value.
|
||||
func ConnectionTypeFromString(c string) ConnectionType {
|
||||
switch c {
|
||||
case "http":
|
||||
return ConnectionType_http
|
||||
case "websocket":
|
||||
return ConnectionType_websocket
|
||||
case "tcp":
|
||||
return ConnectionType_tcp
|
||||
|
||||
default:
|
||||
return 0
|
||||
}
|
||||
}
|
||||
|
||||
type ConnectionType_List struct{ capnp.List }
|
||||
|
||||
func NewConnectionType_List(s *capnp.Segment, sz int32) (ConnectionType_List, error) {
|
||||
l, err := capnp.NewUInt16List(s, sz)
|
||||
return ConnectionType_List{l.List}, err
|
||||
}
|
||||
|
||||
func (l ConnectionType_List) At(i int) ConnectionType {
|
||||
ul := capnp.UInt16List{List: l.List}
|
||||
return ConnectionType(ul.At(i))
|
||||
}
|
||||
|
||||
func (l ConnectionType_List) Set(i int, v ConnectionType) {
|
||||
ul := capnp.UInt16List{List: l.List}
|
||||
ul.Set(i, uint16(v))
|
||||
}
|
||||
|
||||
type Metadata struct{ capnp.Struct }
|
||||
|
||||
// Metadata_TypeID is the unique identifier for the type Metadata.
|
||||
const Metadata_TypeID = 0xe1446b97bfd1cd37
|
||||
|
||||
func NewMetadata(s *capnp.Segment) (Metadata, error) {
|
||||
st, err := capnp.NewStruct(s, capnp.ObjectSize{DataSize: 0, PointerCount: 2})
|
||||
return Metadata{st}, err
|
||||
}
|
||||
|
||||
func NewRootMetadata(s *capnp.Segment) (Metadata, error) {
|
||||
st, err := capnp.NewRootStruct(s, capnp.ObjectSize{DataSize: 0, PointerCount: 2})
|
||||
return Metadata{st}, err
|
||||
}
|
||||
|
||||
func ReadRootMetadata(msg *capnp.Message) (Metadata, error) {
|
||||
root, err := msg.RootPtr()
|
||||
return Metadata{root.Struct()}, err
|
||||
}
|
||||
|
||||
func (s Metadata) String() string {
|
||||
str, _ := text.Marshal(0xe1446b97bfd1cd37, s.Struct)
|
||||
return str
|
||||
}
|
||||
|
||||
func (s Metadata) Key() (string, error) {
|
||||
p, err := s.Struct.Ptr(0)
|
||||
return p.Text(), err
|
||||
}
|
||||
|
||||
func (s Metadata) HasKey() bool {
|
||||
p, err := s.Struct.Ptr(0)
|
||||
return p.IsValid() || err != nil
|
||||
}
|
||||
|
||||
func (s Metadata) KeyBytes() ([]byte, error) {
|
||||
p, err := s.Struct.Ptr(0)
|
||||
return p.TextBytes(), err
|
||||
}
|
||||
|
||||
func (s Metadata) SetKey(v string) error {
|
||||
return s.Struct.SetText(0, v)
|
||||
}
|
||||
|
||||
func (s Metadata) Val() (string, error) {
|
||||
p, err := s.Struct.Ptr(1)
|
||||
return p.Text(), err
|
||||
}
|
||||
|
||||
func (s Metadata) HasVal() bool {
|
||||
p, err := s.Struct.Ptr(1)
|
||||
return p.IsValid() || err != nil
|
||||
}
|
||||
|
||||
func (s Metadata) ValBytes() ([]byte, error) {
|
||||
p, err := s.Struct.Ptr(1)
|
||||
return p.TextBytes(), err
|
||||
}
|
||||
|
||||
func (s Metadata) SetVal(v string) error {
|
||||
return s.Struct.SetText(1, v)
|
||||
}
|
||||
|
||||
// Metadata_List is a list of Metadata.
|
||||
type Metadata_List struct{ capnp.List }
|
||||
|
||||
// NewMetadata creates a new list of Metadata.
|
||||
func NewMetadata_List(s *capnp.Segment, sz int32) (Metadata_List, error) {
|
||||
l, err := capnp.NewCompositeList(s, capnp.ObjectSize{DataSize: 0, PointerCount: 2}, sz)
|
||||
return Metadata_List{l}, err
|
||||
}
|
||||
|
||||
func (s Metadata_List) At(i int) Metadata { return Metadata{s.List.Struct(i)} }
|
||||
|
||||
func (s Metadata_List) Set(i int, v Metadata) error { return s.List.SetStruct(i, v.Struct) }
|
||||
|
||||
func (s Metadata_List) String() string {
|
||||
str, _ := text.MarshalList(0xe1446b97bfd1cd37, s.List)
|
||||
return str
|
||||
}
|
||||
|
||||
// Metadata_Promise is a wrapper for a Metadata promised by a client call.
|
||||
type Metadata_Promise struct{ *capnp.Pipeline }
|
||||
|
||||
func (p Metadata_Promise) Struct() (Metadata, error) {
|
||||
s, err := p.Pipeline.Struct()
|
||||
return Metadata{s}, err
|
||||
}
|
||||
|
||||
type ConnectResponse struct{ capnp.Struct }
|
||||
|
||||
// ConnectResponse_TypeID is the unique identifier for the type ConnectResponse.
|
||||
const ConnectResponse_TypeID = 0xb1032ec91cef8727
|
||||
|
||||
func NewConnectResponse(s *capnp.Segment) (ConnectResponse, error) {
|
||||
st, err := capnp.NewStruct(s, capnp.ObjectSize{DataSize: 0, PointerCount: 2})
|
||||
return ConnectResponse{st}, err
|
||||
}
|
||||
|
||||
func NewRootConnectResponse(s *capnp.Segment) (ConnectResponse, error) {
|
||||
st, err := capnp.NewRootStruct(s, capnp.ObjectSize{DataSize: 0, PointerCount: 2})
|
||||
return ConnectResponse{st}, err
|
||||
}
|
||||
|
||||
func ReadRootConnectResponse(msg *capnp.Message) (ConnectResponse, error) {
|
||||
root, err := msg.RootPtr()
|
||||
return ConnectResponse{root.Struct()}, err
|
||||
}
|
||||
|
||||
func (s ConnectResponse) String() string {
|
||||
str, _ := text.Marshal(0xb1032ec91cef8727, s.Struct)
|
||||
return str
|
||||
}
|
||||
|
||||
func (s ConnectResponse) Error() (string, error) {
|
||||
p, err := s.Struct.Ptr(0)
|
||||
return p.Text(), err
|
||||
}
|
||||
|
||||
func (s ConnectResponse) HasError() bool {
|
||||
p, err := s.Struct.Ptr(0)
|
||||
return p.IsValid() || err != nil
|
||||
}
|
||||
|
||||
func (s ConnectResponse) ErrorBytes() ([]byte, error) {
|
||||
p, err := s.Struct.Ptr(0)
|
||||
return p.TextBytes(), err
|
||||
}
|
||||
|
||||
func (s ConnectResponse) SetError(v string) error {
|
||||
return s.Struct.SetText(0, v)
|
||||
}
|
||||
|
||||
func (s ConnectResponse) Metadata() (Metadata_List, error) {
|
||||
p, err := s.Struct.Ptr(1)
|
||||
return Metadata_List{List: p.List()}, err
|
||||
}
|
||||
|
||||
func (s ConnectResponse) HasMetadata() bool {
|
||||
p, err := s.Struct.Ptr(1)
|
||||
return p.IsValid() || err != nil
|
||||
}
|
||||
|
||||
func (s ConnectResponse) SetMetadata(v Metadata_List) error {
|
||||
return s.Struct.SetPtr(1, v.List.ToPtr())
|
||||
}
|
||||
|
||||
// NewMetadata sets the metadata field to a newly
|
||||
// allocated Metadata_List, preferring placement in s's segment.
|
||||
func (s ConnectResponse) NewMetadata(n int32) (Metadata_List, error) {
|
||||
l, err := NewMetadata_List(s.Struct.Segment(), n)
|
||||
if err != nil {
|
||||
return Metadata_List{}, err
|
||||
}
|
||||
err = s.Struct.SetPtr(1, l.List.ToPtr())
|
||||
return l, err
|
||||
}
|
||||
|
||||
// ConnectResponse_List is a list of ConnectResponse.
|
||||
type ConnectResponse_List struct{ capnp.List }
|
||||
|
||||
// NewConnectResponse creates a new list of ConnectResponse.
|
||||
func NewConnectResponse_List(s *capnp.Segment, sz int32) (ConnectResponse_List, error) {
|
||||
l, err := capnp.NewCompositeList(s, capnp.ObjectSize{DataSize: 0, PointerCount: 2}, sz)
|
||||
return ConnectResponse_List{l}, err
|
||||
}
|
||||
|
||||
func (s ConnectResponse_List) At(i int) ConnectResponse { return ConnectResponse{s.List.Struct(i)} }
|
||||
|
||||
func (s ConnectResponse_List) Set(i int, v ConnectResponse) error {
|
||||
return s.List.SetStruct(i, v.Struct)
|
||||
}
|
||||
|
||||
func (s ConnectResponse_List) String() string {
|
||||
str, _ := text.MarshalList(0xb1032ec91cef8727, s.List)
|
||||
return str
|
||||
}
|
||||
|
||||
// ConnectResponse_Promise is a wrapper for a ConnectResponse promised by a client call.
|
||||
type ConnectResponse_Promise struct{ *capnp.Pipeline }
|
||||
|
||||
func (p ConnectResponse_Promise) Struct() (ConnectResponse, error) {
|
||||
s, err := p.Pipeline.Struct()
|
||||
return ConnectResponse{s}, err
|
||||
}
|
||||
|
||||
const schema_b29021ef7421cc32 = "x\xda\xb4\x91\xcfk\x13A\x1c\xc5\xdf\x9bI\xba\x1e\xa2" +
|
||||
"\x9b!\xd5\x8b\x8a\xa4\xf8+ES\xdb(\xa2\xa7\x80\x15" +
|
||||
"TZ\xcc\x14\xcf\x96u;\x98\x92vw\x92\x9dZ\xf2" +
|
||||
"\x17x\x15/\xe2\xd1\xbb \x15<\x0b\xa2\xa0\xa2\x07\x11" +
|
||||
"\xff\x80\xfe\x05=y\xf0\xb42)\xdb@)\x08Bo" +
|
||||
"\xdfy<\xe6}\xbe\xdfW\xfd\xd5\x16\xb3\xe5\xc7\x04t" +
|
||||
"\xb5<\x91_x\xbas\xeaKSnA5\x98\xcf}" +
|
||||
"\xab\xbb\x9d\xfa\xb3\xb7(\x8b\x00\x98}\xf9\x95\xea]\x00" +
|
||||
"\xa8\xadM0\x8f\xda\x0fK\xafN\xf4?B7\xb8\xdf" +
|
||||
"\xda\xaa\xf3\x03k7\x18\x00\xb5k|\x03\xe6\x9f\x87?" +
|
||||
"\xcf\xbf>\xd9\xfc\x04\xd5\x10c3\xd8\xda\xf6\xce?#" +
|
||||
"\xe7o\xde\x07\xf3\xeb\xdf\x7f\xbc\x7f\xd1\x9b\xdf>\x80\xa0" +
|
||||
"uT<g\xed\x9c\x1fku\xe1!\xfa\x1b\xab\xf1L" +
|
||||
"\x16w'\xccz4\xe3\x1f\xcb\xeb\xc6E+\x91\x8b\x96" +
|
||||
"\xed ui\x9c\xae5\xe3\xc8&\xf6\xe6\xad4IL" +
|
||||
"\xec\x96Lf\xd3$3@\x87\xd4Gd\x09(\x11P" +
|
||||
"\x8d9@\x9f\x95\xd4W\x04\x159I/^\xbe\x07\xe8" +
|
||||
"K\x92\xfa\x8e\xe0\x193\x18\xa4\x03V X\x01\xf3\"" +
|
||||
"\x06\x00\x8f\x81\x1dIV\xc7\xe8\xa0\x17\xff\x87\xae\xbfa" +
|
||||
"27b\xab\xec\xb1\xdd\x9e\x06t[R/\x08\x16h" +
|
||||
"w\xbd6/\xa9;\x82Jp\x92\x02P\x8b\x9ewA" +
|
||||
"Rw\x05\xc3\x15\x93\xb9\x027tCk\x18\x8e[\x00" +
|
||||
"\x19\x1e\xda\x16\xabi\xf2`h\xcd\xee\x16#\xb0\xd3\xd3" +
|
||||
"\xfe3u|\x09\xa0Pj\x0a\x08\xbb\xce\xd9|\xd3<" +
|
||||
"\xca\xd2\xb8g@\x17\xb8\xd8\xeeE\x95\xff\x19\xb5\xb8\xab" +
|
||||
"3\xdaW\xe3\xd4A5z\xf1\xa2\xa4\xbe*\x18\xf4\xcc" +
|
||||
"\xb0\xb8J\xf0$Z+\xe6\xbf\x01\x00\x00\xff\xff\xf5\xed" +
|
||||
"\xc9\xfe"
|
||||
|
||||
func init() {
|
||||
schemas.Register(schema_b29021ef7421cc32,
|
||||
0xb1032ec91cef8727,
|
||||
0xc47116a1045e4061,
|
||||
0xc52e1bac26d379c8,
|
||||
0xe1446b97bfd1cd37)
|
||||
}
|
@@ -1,34 +0,0 @@
|
||||
package quic
|
||||
|
||||
import (
|
||||
"crypto/rand"
|
||||
"crypto/rsa"
|
||||
"crypto/tls"
|
||||
"crypto/x509"
|
||||
"encoding/pem"
|
||||
"math/big"
|
||||
)
|
||||
|
||||
// GenerateTLSConfig sets up a bare-bones TLS config for a QUIC server
|
||||
func GenerateTLSConfig() *tls.Config {
|
||||
key, err := rsa.GenerateKey(rand.Reader, 1024)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
template := x509.Certificate{SerialNumber: big.NewInt(1)}
|
||||
certDER, err := x509.CreateCertificate(rand.Reader, &template, &template, &key.PublicKey, key)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
keyPEM := pem.EncodeToMemory(&pem.Block{Type: "RSA PRIVATE KEY", Bytes: x509.MarshalPKCS1PrivateKey(key)})
|
||||
certPEM := pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE", Bytes: certDER})
|
||||
|
||||
tlsCert, err := tls.X509KeyPair(certPEM, keyPEM)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return &tls.Config{
|
||||
Certificates: []tls.Certificate{tlsCert},
|
||||
NextProtos: []string{"argotunnel"},
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user