From f267fbc56accde7b9b31cda796edcc7aa50021d3 Mon Sep 17 00:00:00 2001 From: Pavel Karpy Date: Fri, 21 May 2021 15:16:07 +0300 Subject: [PATCH] [#549] network/Address: Add TLS There is no TLS protocol support in `go-multiaddr` library, but there is public function that can register any protocol that can be implemented outside the library. Also `TLSEnabled` function for parsing TLS protocol from `network.Address` was added. Signed-off-by: Pavel Karpy --- pkg/network/address.go | 16 +++++++++ pkg/network/address_test.go | 60 +++++++++++++++++++++++++++++++++ pkg/network/tls.go | 67 +++++++++++++++++++++++++++++++++++++ pkg/network/tls_test.go | 48 ++++++++++++++++++++++++++ 4 files changed, 191 insertions(+) create mode 100644 pkg/network/tls.go create mode 100644 pkg/network/tls_test.go diff --git a/pkg/network/address.go b/pkg/network/address.go index 861340b01..d37e08b44 100644 --- a/pkg/network/address.go +++ b/pkg/network/address.go @@ -31,6 +31,22 @@ type LocalAddressSource interface { LocalAddress() *Address } +// Encapsulate wraps this Address around another. For example: +// +// /ip4/1.2.3.4 encapsulate /tcp/80 = /ip4/1.2.3.4/tcp/80 +// +func (a *Address) Encapsulate(addr *Address) { + a.ma = a.ma.Encapsulate(addr.ma) +} + +// Decapsulate removes an Address wrapping. For example: +// +// /ip4/1.2.3.4/tcp/80 decapsulate /ip4/1.2.3.4 = /tcp/80 +// +func (a *Address) Decapsulate(addr *Address) { + a.ma = a.ma.Decapsulate(addr.ma) +} + // String returns multiaddr string func (a Address) String() string { return a.ma.String() diff --git a/pkg/network/address_test.go b/pkg/network/address_test.go index 51b06d74c..aa6f155ec 100644 --- a/pkg/network/address_test.go +++ b/pkg/network/address_test.go @@ -82,6 +82,66 @@ func TestAddress_HostAddrString(t *testing.T) { }) } +func TestAddress_Encapsulate(t *testing.T) { + ma1, ma2 := "/dns4/neofs.bigcorp.com/tcp/8080", "/tls" + + testcases := []struct { + ma1 multiaddr.Multiaddr + ma2 multiaddr.Multiaddr + want string + }{ + { + buildMultiaddr(ma1, t), + buildMultiaddr(ma2, t), + ma1 + ma2, + }, + { + buildMultiaddr(ma2, t), + buildMultiaddr(ma1, t), + ma2 + ma1, + }, + } + + for _, testcase := range testcases { + addr1 := &Address{testcase.ma1} + addr2 := &Address{testcase.ma2} + + addr1.Encapsulate(addr2) + + require.Equal(t, addr1.String(), testcase.want) + } +} + +func TestAddress_Decapsulate(t *testing.T) { + ma1, ma2 := "/dns4/neofs.bigcorp.com/tcp/8080", "/tls" + + testcases := []struct { + ma1 multiaddr.Multiaddr + ma2 multiaddr.Multiaddr + want string + }{ + { + buildMultiaddr(ma1+ma2, t), + buildMultiaddr(ma2, t), + ma1, + }, + { + buildMultiaddr(ma2+ma1, t), + buildMultiaddr(ma1, t), + ma2, + }, + } + + for _, testcase := range testcases { + addr1 := &Address{testcase.ma1} + addr2 := &Address{testcase.ma2} + + addr1.Decapsulate(addr2) + + require.Equal(t, addr1.String(), testcase.want) + } +} + func buildMultiaddr(s string, t *testing.T) multiaddr.Multiaddr { ma, err := multiaddr.NewMultiaddr(s) require.NoError(t, err) diff --git a/pkg/network/tls.go b/pkg/network/tls.go new file mode 100644 index 000000000..a552480eb --- /dev/null +++ b/pkg/network/tls.go @@ -0,0 +1,67 @@ +package network + +import ( + "fmt" + + "github.com/multiformats/go-multiaddr" +) + +// There is implementation of TLS protocol for +// github.com/multiformats/go-multiaddr library in this file. +// +// After addition TLS protocol via `multiaddr.AddProtocol` function +// the library is ready to parse "tls" protocol with empty body, e.g.: +// +// "/dns4/localhost/tcp/8080/tls" + +const ( + tlsProtocolName = "tls" + + // tlsProtocolCode is chosen according to its draft version's code in + // original multiaddr repository: https://github.com/multiformats/multicodec. + tlsProtocolCode = 0x01c0 +) + +// tls var is used for (un)wrapping other multiaddrs around TLS multiaddr. +var tls multiaddr.Multiaddr + +func init() { + tlsProtocol := multiaddr.Protocol{ + Name: tlsProtocolName, + Code: tlsProtocolCode, + Size: 0, + VCode: multiaddr.CodeToVarint(tlsProtocolCode), + } + + err := multiaddr.AddProtocol(tlsProtocol) + if err != nil { + panic(fmt.Errorf("could not add 'TLS' protocol to multiadd library: %w", err)) + } + + tls, err = multiaddr.NewMultiaddr("/" + tlsProtocolName) + if err != nil { + panic(fmt.Errorf("could not init 'TLS' protocol with multiadd library: %w", err)) + } +} + +// TLSEnabled searches for wrapped TLS protocol in multiaddr. +func (a Address) TLSEnabled() bool { + for _, protoc := range a.ma.Protocols() { + if protoc.Code == tlsProtocolCode { + return true + } + } + + return false +} + +// AddTLS encapsulates a Address if there is no TLS yet. +func (a *Address) AddTLS() { + // not need to add TLS if it is + // already included + if a.TLSEnabled() { + return + } + + a.ma = a.ma.Encapsulate(tls) +} diff --git a/pkg/network/tls_test.go b/pkg/network/tls_test.go new file mode 100644 index 000000000..1d7f106dd --- /dev/null +++ b/pkg/network/tls_test.go @@ -0,0 +1,48 @@ +package network + +import ( + "testing" + + "github.com/stretchr/testify/require" +) + +func TestAddress_TLSEnabled(t *testing.T) { + testCases := [...]struct { + input string + wantTLS bool + }{ + {"/dns4/localhost/tcp/8080", false}, + {"/dns4/localhost/tcp/8080/tls", true}, + {"/tls/dns4/localhost/tcp/8080", true}, + } + + for _, test := range testCases { + addr := Address{ + ma: buildMultiaddr(test.input, t), + } + + require.Equal(t, test.wantTLS, addr.TLSEnabled(), test.input) + } +} + +func TestAddress_AddTLS(t *testing.T) { + input, tls := "/dns4/localhost/tcp/8080", tls.String() + + testCases := [...]struct { + input string + want string + }{ + {input, input + tls}, + {input + tls, input + tls}, + } + + for _, test := range testCases { + addr := Address{ + ma: buildMultiaddr(test.input, t), + } + + addr.AddTLS() + + require.Equal(t, test.want, addr.String(), test.input) + } +}