508 lines
14 KiB
Go
508 lines
14 KiB
Go
package pogs
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"net/url"
|
|
"time"
|
|
|
|
"github.com/cloudflare/cloudflared/tunnelrpc"
|
|
capnp "zombiezen.com/go/capnproto2"
|
|
"zombiezen.com/go/capnproto2/pogs"
|
|
"zombiezen.com/go/capnproto2/rpc"
|
|
)
|
|
|
|
///
|
|
/// Structs
|
|
///
|
|
|
|
type ClientConfig struct {
|
|
Version uint64
|
|
AutoUpdateFrequency time.Duration
|
|
MetricsUpdateFrequency time.Duration
|
|
HeartbeatInterval time.Duration
|
|
MaxFailedHeartbeats uint64
|
|
GracePeriod time.Duration
|
|
DoHProxyConfigs []*DoHProxyConfig
|
|
ReverseProxyConfigs []*ReverseProxyConfig
|
|
NumHAConnections uint8
|
|
}
|
|
|
|
type UseConfigurationResult struct {
|
|
Success bool
|
|
ErrorMessage string
|
|
}
|
|
|
|
type DoHProxyConfig struct {
|
|
ListenHost string
|
|
ListenPort uint16
|
|
Upstreams []string
|
|
}
|
|
|
|
type ReverseProxyConfig struct {
|
|
TunnelHostname string
|
|
Origin OriginConfig
|
|
Retries uint64
|
|
ConnectionTimeout time.Duration
|
|
CompressionQuality uint64
|
|
}
|
|
|
|
func NewReverseProxyConfig(
|
|
tunnelHostname string,
|
|
originConfig OriginConfig,
|
|
retries uint64,
|
|
connectionTimeout time.Duration,
|
|
compressionQuality uint64,
|
|
) (*ReverseProxyConfig, error) {
|
|
if originConfig == nil {
|
|
return nil, fmt.Errorf("NewReverseProxyConfig: originConfig was null")
|
|
}
|
|
return &ReverseProxyConfig{
|
|
TunnelHostname: tunnelHostname,
|
|
Origin: originConfig,
|
|
Retries: retries,
|
|
ConnectionTimeout: connectionTimeout,
|
|
CompressionQuality: compressionQuality,
|
|
}, nil
|
|
}
|
|
|
|
//go-sumtype:decl OriginConfig
|
|
type OriginConfig interface {
|
|
isOriginConfig()
|
|
}
|
|
|
|
type HTTPOriginConfig struct {
|
|
URL OriginAddr `capnp:"url"`
|
|
TCPKeepAlive time.Duration `capnp:"tcpKeepAlive"`
|
|
DialDualStack bool
|
|
TLSHandshakeTimeout time.Duration `capnp:"tlsHandshakeTimeout"`
|
|
TLSVerify bool `capnp:"tlsVerify"`
|
|
OriginCAPool string
|
|
OriginServerName string
|
|
MaxIdleConnections uint64
|
|
IdleConnectionTimeout time.Duration
|
|
ProxyConnectTimeout time.Duration
|
|
ExpectContinueTimeout time.Duration
|
|
ChunkedEncoding bool
|
|
}
|
|
|
|
func (_ *HTTPOriginConfig) isOriginConfig() {}
|
|
|
|
type OriginAddr interface {
|
|
Addr() string
|
|
}
|
|
|
|
type HTTPURL struct {
|
|
URL *url.URL
|
|
}
|
|
|
|
func (ha *HTTPURL) Addr() string {
|
|
return ha.URL.String()
|
|
}
|
|
|
|
func (ha *HTTPURL) capnpHTTPURL() *CapnpHTTPURL {
|
|
return &CapnpHTTPURL{
|
|
URL: ha.URL.String(),
|
|
}
|
|
}
|
|
|
|
// URL for a HTTP origin, capnp doesn't have native support for URL, so represent it as string
|
|
type CapnpHTTPURL struct {
|
|
URL string `capnp:"url"`
|
|
}
|
|
|
|
type UnixPath struct {
|
|
Path string
|
|
}
|
|
|
|
func (up *UnixPath) Addr() string {
|
|
return up.Path
|
|
}
|
|
|
|
type WebSocketOriginConfig struct {
|
|
URL string `capnp:"url"`
|
|
TLSVerify bool `capnp:"tlsVerify"`
|
|
OriginCAPool string
|
|
OriginServerName string
|
|
}
|
|
|
|
func (_ *WebSocketOriginConfig) isOriginConfig() {}
|
|
|
|
type HelloWorldOriginConfig struct{}
|
|
|
|
func (_ *HelloWorldOriginConfig) isOriginConfig() {}
|
|
|
|
/*
|
|
* Boilerplate to convert between these structs and the primitive structs
|
|
* generated by capnp-go.
|
|
* Mnemonics for variable names in this section:
|
|
* - `p` is for POGS (plain old Go struct)
|
|
* - `s` (and `ss`) is for "capnp.Struct", which is the fundamental type
|
|
* underlying the capnp-go data structures.
|
|
*/
|
|
|
|
func MarshalClientConfig(s tunnelrpc.ClientConfig, p *ClientConfig) error {
|
|
s.SetVersion(p.Version)
|
|
s.SetAutoUpdateFrequency(p.AutoUpdateFrequency.Nanoseconds())
|
|
s.SetMetricsUpdateFrequency(p.MetricsUpdateFrequency.Nanoseconds())
|
|
s.SetHeartbeatInterval(p.HeartbeatInterval.Nanoseconds())
|
|
s.SetMaxFailedHeartbeats(p.MaxFailedHeartbeats)
|
|
s.SetGracePeriod(p.GracePeriod.Nanoseconds())
|
|
s.SetNumHAConnections(p.NumHAConnections)
|
|
err := marshalDoHProxyConfigs(s, p.DoHProxyConfigs)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
return marshalReverseProxyConfigs(s, p.ReverseProxyConfigs)
|
|
}
|
|
|
|
func marshalDoHProxyConfigs(s tunnelrpc.ClientConfig, dohProxyConfigs []*DoHProxyConfig) error {
|
|
capnpList, err := s.NewDohProxyConfigs(int32(len(dohProxyConfigs)))
|
|
if err != nil {
|
|
return err
|
|
}
|
|
for i, unmarshalledConfig := range dohProxyConfigs {
|
|
err := MarshalDoHProxyConfig(capnpList.At(i), unmarshalledConfig)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func marshalReverseProxyConfigs(s tunnelrpc.ClientConfig, reverseProxyConfigs []*ReverseProxyConfig) error {
|
|
capnpList, err := s.NewReverseProxyConfigs(int32(len(reverseProxyConfigs)))
|
|
if err != nil {
|
|
return err
|
|
}
|
|
for i, unmarshalledConfig := range reverseProxyConfigs {
|
|
err := MarshalReverseProxyConfig(capnpList.At(i), unmarshalledConfig)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func UnmarshalClientConfig(s tunnelrpc.ClientConfig) (*ClientConfig, error) {
|
|
p := new(ClientConfig)
|
|
p.Version = s.Version()
|
|
p.AutoUpdateFrequency = time.Duration(s.AutoUpdateFrequency())
|
|
p.MetricsUpdateFrequency = time.Duration(s.MetricsUpdateFrequency())
|
|
p.HeartbeatInterval = time.Duration(s.HeartbeatInterval())
|
|
p.MaxFailedHeartbeats = s.MaxFailedHeartbeats()
|
|
p.GracePeriod = time.Duration(s.GracePeriod())
|
|
p.NumHAConnections = s.NumHAConnections()
|
|
dohProxyConfigs, err := unmarshalDoHProxyConfigs(s)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
p.DoHProxyConfigs = dohProxyConfigs
|
|
reverseProxyConfigs, err := unmarshalReverseProxyConfigs(s)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
p.ReverseProxyConfigs = reverseProxyConfigs
|
|
return p, err
|
|
}
|
|
|
|
func unmarshalDoHProxyConfigs(s tunnelrpc.ClientConfig) ([]*DoHProxyConfig, error) {
|
|
var result []*DoHProxyConfig
|
|
marshalledDoHProxyConfigs, err := s.DohProxyConfigs()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
for i := 0; i < marshalledDoHProxyConfigs.Len(); i++ {
|
|
ss := marshalledDoHProxyConfigs.At(i)
|
|
dohProxyConfig, err := UnmarshalDoHProxyConfig(ss)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
result = append(result, dohProxyConfig)
|
|
}
|
|
return result, nil
|
|
}
|
|
|
|
func unmarshalReverseProxyConfigs(s tunnelrpc.ClientConfig) ([]*ReverseProxyConfig, error) {
|
|
var result []*ReverseProxyConfig
|
|
marshalledReverseProxyConfigs, err := s.ReverseProxyConfigs()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
for i := 0; i < marshalledReverseProxyConfigs.Len(); i++ {
|
|
ss := marshalledReverseProxyConfigs.At(i)
|
|
reverseProxyConfig, err := UnmarshalReverseProxyConfig(ss)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
result = append(result, reverseProxyConfig)
|
|
}
|
|
return result, nil
|
|
}
|
|
|
|
func MarshalUseConfigurationResult(s tunnelrpc.UseConfigurationResult, p *UseConfigurationResult) error {
|
|
return pogs.Insert(tunnelrpc.UseConfigurationResult_TypeID, s.Struct, p)
|
|
}
|
|
|
|
func UnmarshalUseConfigurationResult(s tunnelrpc.UseConfigurationResult) (*UseConfigurationResult, error) {
|
|
p := new(UseConfigurationResult)
|
|
err := pogs.Extract(p, tunnelrpc.UseConfigurationResult_TypeID, s.Struct)
|
|
return p, err
|
|
}
|
|
|
|
func MarshalDoHProxyConfig(s tunnelrpc.DoHProxyConfig, p *DoHProxyConfig) error {
|
|
return pogs.Insert(tunnelrpc.DoHProxyConfig_TypeID, s.Struct, p)
|
|
}
|
|
|
|
func UnmarshalDoHProxyConfig(s tunnelrpc.DoHProxyConfig) (*DoHProxyConfig, error) {
|
|
p := new(DoHProxyConfig)
|
|
err := pogs.Extract(p, tunnelrpc.DoHProxyConfig_TypeID, s.Struct)
|
|
return p, err
|
|
}
|
|
|
|
func MarshalReverseProxyConfig(s tunnelrpc.ReverseProxyConfig, p *ReverseProxyConfig) error {
|
|
s.SetTunnelHostname(p.TunnelHostname)
|
|
switch config := p.Origin.(type) {
|
|
case *HTTPOriginConfig:
|
|
ss, err := s.Origin().NewHttp()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if err := MarshalHTTPOriginConfig(ss, config); err != nil {
|
|
return err
|
|
}
|
|
case *WebSocketOriginConfig:
|
|
ss, err := s.Origin().NewWebsocket()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if err := MarshalWebSocketOriginConfig(ss, config); err != nil {
|
|
return err
|
|
}
|
|
case *HelloWorldOriginConfig:
|
|
ss, err := s.Origin().NewHelloWorld()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if err := MarshalHelloWorldOriginConfig(ss, config); err != nil {
|
|
return err
|
|
}
|
|
default:
|
|
return fmt.Errorf("Unknown type for config: %T", config)
|
|
}
|
|
s.SetRetries(p.Retries)
|
|
s.SetConnectionTimeout(p.ConnectionTimeout.Nanoseconds())
|
|
s.SetCompressionQuality(p.CompressionQuality)
|
|
return nil
|
|
}
|
|
|
|
func UnmarshalReverseProxyConfig(s tunnelrpc.ReverseProxyConfig) (*ReverseProxyConfig, error) {
|
|
p := new(ReverseProxyConfig)
|
|
tunnelHostname, err := s.TunnelHostname()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
p.TunnelHostname = tunnelHostname
|
|
switch s.Origin().Which() {
|
|
case tunnelrpc.ReverseProxyConfig_origin_Which_http:
|
|
ss, err := s.Origin().Http()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
config, err := UnmarshalHTTPOriginConfig(ss)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
p.Origin = config
|
|
case tunnelrpc.ReverseProxyConfig_origin_Which_websocket:
|
|
ss, err := s.Origin().Websocket()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
config, err := UnmarshalWebSocketOriginConfig(ss)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
p.Origin = config
|
|
case tunnelrpc.ReverseProxyConfig_origin_Which_helloWorld:
|
|
ss, err := s.Origin().HelloWorld()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
config, err := UnmarshalHelloWorldOriginConfig(ss)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
p.Origin = config
|
|
}
|
|
p.Retries = s.Retries()
|
|
p.ConnectionTimeout = time.Duration(s.ConnectionTimeout())
|
|
p.CompressionQuality = s.CompressionQuality()
|
|
return p, nil
|
|
}
|
|
|
|
func MarshalHTTPOriginConfig(s tunnelrpc.HTTPOriginConfig, p *HTTPOriginConfig) error {
|
|
switch originAddr := p.URL.(type) {
|
|
case *HTTPURL:
|
|
ss, err := s.OriginAddr().NewHttp()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if err := MarshalHTTPURL(ss, originAddr); err != nil {
|
|
return err
|
|
}
|
|
case *UnixPath:
|
|
ss, err := s.OriginAddr().NewUnix()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if err := MarshalUnixPath(ss, originAddr); err != nil {
|
|
return err
|
|
}
|
|
default:
|
|
return fmt.Errorf("Unknown type for OriginAddr: %T", originAddr)
|
|
}
|
|
s.SetTcpKeepAlive(p.TCPKeepAlive.Nanoseconds())
|
|
s.SetDialDualStack(p.DialDualStack)
|
|
s.SetTlsHandshakeTimeout(p.TLSHandshakeTimeout.Nanoseconds())
|
|
s.SetTlsVerify(p.TLSVerify)
|
|
s.SetOriginCAPool(p.OriginCAPool)
|
|
s.SetOriginServerName(p.OriginServerName)
|
|
s.SetMaxIdleConnections(p.MaxIdleConnections)
|
|
s.SetIdleConnectionTimeout(p.IdleConnectionTimeout.Nanoseconds())
|
|
s.SetProxyConnectionTimeout(p.ProxyConnectTimeout.Nanoseconds())
|
|
s.SetExpectContinueTimeout(p.ExpectContinueTimeout.Nanoseconds())
|
|
s.SetChunkedEncoding(p.ChunkedEncoding)
|
|
return nil
|
|
}
|
|
|
|
func UnmarshalHTTPOriginConfig(s tunnelrpc.HTTPOriginConfig) (*HTTPOriginConfig, error) {
|
|
p := new(HTTPOriginConfig)
|
|
switch s.OriginAddr().Which() {
|
|
case tunnelrpc.HTTPOriginConfig_originAddr_Which_http:
|
|
ss, err := s.OriginAddr().Http()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
originAddr, err := UnmarshalCapnpHTTPURL(ss)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
p.URL = originAddr
|
|
case tunnelrpc.HTTPOriginConfig_originAddr_Which_unix:
|
|
ss, err := s.OriginAddr().Unix()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
originAddr, err := UnmarshalUnixPath(ss)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
p.URL = originAddr
|
|
default:
|
|
return nil, fmt.Errorf("Unknown type for OriginAddr: %T", s.OriginAddr().Which())
|
|
}
|
|
p.TCPKeepAlive = time.Duration(s.TcpKeepAlive())
|
|
p.DialDualStack = s.DialDualStack()
|
|
p.TLSHandshakeTimeout = time.Duration(s.TlsHandshakeTimeout())
|
|
p.TLSVerify = s.TlsVerify()
|
|
originCAPool, err := s.OriginCAPool()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
p.OriginCAPool = originCAPool
|
|
originServerName, err := s.OriginServerName()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
p.OriginServerName = originServerName
|
|
p.MaxIdleConnections = s.MaxIdleConnections()
|
|
p.IdleConnectionTimeout = time.Duration(s.IdleConnectionTimeout())
|
|
p.ProxyConnectTimeout = time.Duration(s.ProxyConnectionTimeout())
|
|
p.ExpectContinueTimeout = time.Duration(s.ExpectContinueTimeout())
|
|
p.ChunkedEncoding = s.ChunkedEncoding()
|
|
return p, nil
|
|
}
|
|
|
|
func MarshalHTTPURL(s tunnelrpc.CapnpHTTPURL, p *HTTPURL) error {
|
|
return pogs.Insert(tunnelrpc.CapnpHTTPURL_TypeID, s.Struct, p.capnpHTTPURL())
|
|
}
|
|
|
|
func UnmarshalCapnpHTTPURL(s tunnelrpc.CapnpHTTPURL) (*HTTPURL, error) {
|
|
p := new(CapnpHTTPURL)
|
|
err := pogs.Extract(p, tunnelrpc.CapnpHTTPURL_TypeID, s.Struct)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
url, err := url.Parse(p.URL)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return &HTTPURL{
|
|
URL: url,
|
|
}, nil
|
|
}
|
|
|
|
func MarshalUnixPath(s tunnelrpc.UnixPath, p *UnixPath) error {
|
|
err := pogs.Insert(tunnelrpc.UnixPath_TypeID, s.Struct, p)
|
|
return err
|
|
}
|
|
|
|
func UnmarshalUnixPath(s tunnelrpc.UnixPath) (*UnixPath, error) {
|
|
p := new(UnixPath)
|
|
err := pogs.Extract(p, tunnelrpc.UnixPath_TypeID, s.Struct)
|
|
return p, err
|
|
}
|
|
|
|
func MarshalWebSocketOriginConfig(s tunnelrpc.WebSocketOriginConfig, p *WebSocketOriginConfig) error {
|
|
return pogs.Insert(tunnelrpc.WebSocketOriginConfig_TypeID, s.Struct, p)
|
|
}
|
|
|
|
func UnmarshalWebSocketOriginConfig(s tunnelrpc.WebSocketOriginConfig) (*WebSocketOriginConfig, error) {
|
|
p := new(WebSocketOriginConfig)
|
|
err := pogs.Extract(p, tunnelrpc.WebSocketOriginConfig_TypeID, s.Struct)
|
|
return p, err
|
|
}
|
|
|
|
func MarshalHelloWorldOriginConfig(s tunnelrpc.HelloWorldOriginConfig, p *HelloWorldOriginConfig) error {
|
|
return pogs.Insert(tunnelrpc.HelloWorldOriginConfig_TypeID, s.Struct, p)
|
|
}
|
|
|
|
func UnmarshalHelloWorldOriginConfig(s tunnelrpc.HelloWorldOriginConfig) (*HelloWorldOriginConfig, error) {
|
|
p := new(HelloWorldOriginConfig)
|
|
err := pogs.Extract(p, tunnelrpc.HelloWorldOriginConfig_TypeID, s.Struct)
|
|
return p, err
|
|
}
|
|
|
|
type ClientService interface {
|
|
UseConfiguration(ctx context.Context, config *ClientConfig) (*UseConfigurationResult, error)
|
|
}
|
|
|
|
type ClientService_PogsClient struct {
|
|
Client capnp.Client
|
|
Conn *rpc.Conn
|
|
}
|
|
|
|
func (c *ClientService_PogsClient) Close() error {
|
|
return c.Conn.Close()
|
|
}
|
|
|
|
func (c *ClientService_PogsClient) UseConfiguration(
|
|
ctx context.Context,
|
|
config *ClientConfig,
|
|
) (*UseConfigurationResult, error) {
|
|
client := tunnelrpc.ClientService{Client: c.Client}
|
|
promise := client.UseConfiguration(ctx, func(p tunnelrpc.ClientService_useConfiguration_Params) error {
|
|
clientServiceConfig, err := p.NewClientServiceConfig()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
return MarshalClientConfig(clientServiceConfig, config)
|
|
})
|
|
retval, err := promise.Result().Struct()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return UnmarshalUseConfigurationResult(retval)
|
|
}
|