diff --git a/pkg/network/validation.go b/pkg/network/validation.go new file mode 100644 index 000000000..66dd5982a --- /dev/null +++ b/pkg/network/validation.go @@ -0,0 +1,87 @@ +package network + +import ( + "errors" + "fmt" + + "github.com/nspcc-dev/neofs-api-go/pkg/netmap" +) + +const ( + // maxProtocolsAmount is maximal amount of protocols + // in multiaddress after parsing with network.AddressFromString + maxProtocolsAmount = 3 + + // minProtocolsAmount is minimal amount of protocols + // in multiaddress after parsing with network.AddressFromString: + // host(ip) and port. + minProtocolsAmount = 2 + + // network protocols + dns, ip4, ip6 = "dns4", "ip4", "ip6" + + // transport protocols + tcp = "tcp" +) + +var ( + errIncorrectProtocolAmount = errors.New("numbers of protocols in multiaddress incorrect") + errUnsupportedNetworkProtocol = errors.New("unsupported network protocol in multiaddress") + errUnsupportedTransportProtocol = errors.New("unsupported transport protocol in multiaddress") + errUnsupportedPresentationProtocol = errors.New("unsupported presentation protocol in multiaddress") +) + +// VerifyMultiAddress validates multiaddress of n. +// +// If n's address contains more than 3 protocols +// or less than 2 protocols an error returns. +// +// If n's address's protocol order is incorrect +// an error returns. +// +// Correct composition(and order from low to high level) +// of protocols: +// +// 1. dns4/ip4/ip6 +// 2. tcp +// 3. tls(optional, may be absent) +// +func VerifyMultiAddress(ni *netmap.NodeInfo) error { + // check if it can be parsed to network.Address + var netAddr Address + + err := netAddr.FromString(ni.Address()) + if err != nil { + return fmt.Errorf("could not parse multiaddr from NodeInfo: %w", err) + } + + // check amount of protocols and its order + return checkProtocols(netAddr) +} + +func checkProtocols(a Address) error { + pp := a.ma.Protocols() + parsedProtocolsAmount := len(pp) + + if parsedProtocolsAmount > maxProtocolsAmount || parsedProtocolsAmount < minProtocolsAmount { + return errIncorrectProtocolAmount + } + + switch pp[0].Name { + case dns, ip4, ip6: + default: + return errUnsupportedNetworkProtocol + } + + if pp[1].Name != tcp { + return errUnsupportedTransportProtocol + } + + if parsedProtocolsAmount != minProtocolsAmount { + if pp[2].Name != tlsProtocolName { + return errUnsupportedPresentationProtocol + } + } + + return nil +} diff --git a/pkg/network/validation_test.go b/pkg/network/validation_test.go new file mode 100644 index 000000000..8409b2915 --- /dev/null +++ b/pkg/network/validation_test.go @@ -0,0 +1,64 @@ +package network + +import ( + "testing" + + "github.com/nspcc-dev/neofs-api-go/pkg/netmap" + "github.com/stretchr/testify/require" +) + +type testCase struct { + input string + err error +} + +func TestVerifyMultiAddress_Order(t *testing.T) { + testCases := []testCase{ + { + input: "/ip4/1.2.3.4/tcp/80", + err: nil, + }, + { + input: "/ip6/1.2.3.4/tcp/80", + err: nil, + }, + { + input: "/dns4/1.2.3.4/tcp/80", + err: nil, + }, + { + input: "/dns4/1.2.3.4/tcp/80/tls", + err: nil, + }, + { + input: "/tls/dns4/1.2.3.4/tcp/80", + err: errUnsupportedNetworkProtocol, + }, + { + input: "/dns4/1.2.3.4/tls/tcp/80", + err: errUnsupportedTransportProtocol, + }, + { + input: "/dns4/1.2.3.4/tcp/80/wss", + err: errUnsupportedPresentationProtocol, + }, + } + + for _, test := range testCases { + ni := constructNodeInfo(test.input) + + if test.err != nil { + require.EqualError(t, test.err, VerifyMultiAddress(ni).Error()) + } else { + require.NoError(t, VerifyMultiAddress(ni)) + } + } +} + +func constructNodeInfo(address string) *netmap.NodeInfo { + ni := new(netmap.NodeInfo) + + ni.SetAddress(address) + + return ni +}