implement tcp and tcp+tls transports
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"crypto/tls"
|
||||
"fmt"
|
||||
"net"
|
||||
|
||||
@@ -11,6 +12,7 @@ import (
|
||||
"github.com/problame/go-netssh"
|
||||
"github.com/problame/go-streamrpc"
|
||||
"time"
|
||||
"github.com/zrepl/zrepl/cmd/tlsconf"
|
||||
)
|
||||
|
||||
type SSHStdinserverConnecter struct {
|
||||
@@ -74,3 +76,79 @@ func (c *SSHStdinserverConnecter) Connect(dialCtx context.Context) (net.Conn, er
|
||||
}
|
||||
return netsshConnToConn{nconn}, nil
|
||||
}
|
||||
|
||||
type TCPConnecter struct {
|
||||
Host string
|
||||
Port uint16
|
||||
dialer net.Dialer
|
||||
tlsConfig *tls.Config
|
||||
}
|
||||
|
||||
func parseTCPConnecter(i map[string]interface{}) (*TCPConnecter, error) {
|
||||
var in struct {
|
||||
Host string
|
||||
Port uint16
|
||||
DialTimeout string `mapstructure:"dial_timeout"`
|
||||
TLS map[string]interface{}
|
||||
}
|
||||
if err := mapstructure.Decode(i, &in); err != nil {
|
||||
return nil, errors.Wrap(err, "mapstructure error")
|
||||
}
|
||||
|
||||
if in.Host == "" || in.Port == 0 {
|
||||
return nil, errors.New("fields 'host' and 'port' must not be empty")
|
||||
}
|
||||
dialTimeout, err := parsePostitiveDuration(in.DialTimeout)
|
||||
if err != nil {
|
||||
if in.DialTimeout != "" {
|
||||
return nil, errors.Wrap(err, "cannot parse field 'dial_timeout'")
|
||||
}
|
||||
dialTimeout = 10 * time.Second
|
||||
}
|
||||
dialer := net.Dialer{
|
||||
Timeout: dialTimeout,
|
||||
}
|
||||
|
||||
var tlsConfig *tls.Config
|
||||
if in.TLS != nil {
|
||||
tlsConfig, err = func(i map[string]interface{}) (config *tls.Config, err error) {
|
||||
var in struct {
|
||||
CA string
|
||||
Cert string
|
||||
Key string
|
||||
ServerCN string `mapstructure:"server_cn"`
|
||||
}
|
||||
if err := mapstructure.Decode(i, &in); err != nil {
|
||||
return nil, errors.Wrap(err, "mapstructure error")
|
||||
}
|
||||
if in.CA == "" || in.Cert == "" || in.Key == "" || in.ServerCN == "" {
|
||||
return nil, errors.New("fields 'ca', 'cert', 'key' and 'server_cn' must be specified")
|
||||
}
|
||||
|
||||
ca, err := tlsconf.ParseCAFile(in.CA)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "cannot parse ca file")
|
||||
}
|
||||
|
||||
cert, err := tls.LoadX509KeyPair(in.Cert, in.Key)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "cannot parse cert/key pair")
|
||||
}
|
||||
|
||||
return tlsconf.ClientAuthClient(in.ServerCN, ca, cert)
|
||||
}(in.TLS)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "cannot parse TLS config in field 'tls'")
|
||||
}
|
||||
}
|
||||
|
||||
return &TCPConnecter{in.Host, in.Port, dialer, tlsConfig}, nil
|
||||
}
|
||||
|
||||
func (c *TCPConnecter) Connect(dialCtx context.Context) (conn net.Conn, err error) {
|
||||
addr := fmt.Sprintf("%s:%d", c.Host, c.Port)
|
||||
if c.tlsConfig != nil {
|
||||
return tls.DialWithDialer(&c.dialer, "tcp", addr, c.tlsConfig)
|
||||
}
|
||||
return c.dialer.DialContext(dialCtx, "tcp", addr)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user