blob: 89094bb850086a0dc875f1b5c578315cb9c413da [file] [log] [blame]
// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements. See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership. The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License. You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the License for the
// specific language governing permissions and limitations
// under the License.
#include "kudu/util/net/net_util.h"
#include <sys/socket.h>
#include <algorithm>
#include <cstdint>
#include <ostream>
#include <string>
#include <vector>
#include <gflags/gflags_declare.h>
#include <glog/logging.h>
#include <gtest/gtest.h>
#include "kudu/gutil/strings/join.h"
#include "kudu/gutil/strings/substitute.h"
#include "kudu/gutil/strings/util.h"
#include "kudu/util/net/sockaddr.h"
#include "kudu/util/net/socket.h"
#include "kudu/util/status.h"
#include "kudu/util/test_macros.h"
#include "kudu/util/test_util.h"
DECLARE_bool(fail_dns_resolution);
DECLARE_string(fail_dns_resolution_hostports);
using std::string;
using std::vector;
using strings::Substitute;
namespace kudu {
class NetUtilTest : public KuduTest {
protected:
static Status DoParseBindAddresses(const string& input, string* result) {
vector<Sockaddr> addrs;
RETURN_NOT_OK(ParseAddressList(input, kDefaultPort, &addrs));
std::sort(addrs.begin(), addrs.end(), Sockaddr::BytewiseLess);
vector<string> addr_strs;
for (const Sockaddr& addr : addrs) {
addr_strs.push_back(addr.ToString());
}
*result = JoinStrings(addr_strs, ",");
return Status::OK();
}
static const uint16_t kDefaultPort = 7150;
};
TEST(SockaddrTest, Test) {
Sockaddr addr;
ASSERT_OK(addr.ParseString("1.1.1.1:12345", 12345));
ASSERT_EQ(12345, addr.port());
ASSERT_EQ("1.1.1.1", addr.host());
}
TEST_F(NetUtilTest, TestParseAddresses) {
string ret;
ASSERT_OK(DoParseBindAddresses("0.0.0.0:12345", &ret));
ASSERT_EQ("0.0.0.0:12345", ret);
ASSERT_OK(DoParseBindAddresses("0.0.0.0", &ret));
ASSERT_EQ("0.0.0.0:7150", ret);
ASSERT_OK(DoParseBindAddresses("0.0.0.0:12345, 0.0.0.0:12346", &ret));
ASSERT_EQ("0.0.0.0:12345,0.0.0.0:12346", ret);
// Test some invalid addresses.
Status s = DoParseBindAddresses("0.0.0.0:xyz", &ret);
ASSERT_STR_CONTAINS(s.ToString(), "invalid port");
s = DoParseBindAddresses("0.0.0.0:100000", &ret);
ASSERT_STR_CONTAINS(s.ToString(), "invalid port");
s = DoParseBindAddresses("0.0.0.0:", &ret);
ASSERT_STR_CONTAINS(s.ToString(), "invalid port");
}
TEST_F(NetUtilTest, TestParseAddressesWithScheme) {
vector<HostPort> hostports;
const uint16_t kDefaultPort = 12345;
EXPECT_OK(HostPort::ParseStringsWithScheme("", kDefaultPort, &hostports));
EXPECT_TRUE(hostports.empty());
EXPECT_OK(HostPort::ParseStringsWithScheme(",,,", kDefaultPort, &hostports));
EXPECT_TRUE(hostports.empty());
EXPECT_OK(HostPort::ParseStringsWithScheme("http://foo-bar-baz:1234", kDefaultPort, &hostports));
EXPECT_EQ(vector<HostPort>({ HostPort("foo-bar-baz", 1234) }), hostports);
EXPECT_OK(HostPort::ParseStringsWithScheme("http://foo-bar-baz:1234/path",
kDefaultPort, &hostports));
EXPECT_EQ(vector<HostPort>({ HostPort("foo-bar-baz", 1234) }), hostports);
EXPECT_OK(HostPort::ParseStringsWithScheme("http://abc:1234,xyz", kDefaultPort, &hostports));
EXPECT_EQ(vector<HostPort>({ HostPort("abc", 1234), HostPort("xyz", kDefaultPort) }),
hostports);
// Test some invalid addresses.
Status s = HostPort::ParseStringsWithScheme("abc:1234/path", kDefaultPort, &hostports);
EXPECT_TRUE(s.IsInvalidArgument());
ASSERT_STR_CONTAINS(s.ToString(), "invalid port");
s = HostPort::ParseStringsWithScheme("://scheme:12", kDefaultPort, &hostports);
EXPECT_TRUE(s.IsInvalidArgument());
ASSERT_STR_CONTAINS(s.ToString(), "invalid scheme format");
s = HostPort::ParseStringsWithScheme("http:///path", kDefaultPort, &hostports);
EXPECT_TRUE(s.IsInvalidArgument());
ASSERT_STR_CONTAINS(s.ToString(), "invalid address format");
s = HostPort::ParseStringsWithScheme("http://abc:1234,://scheme,xyz",
kDefaultPort, &hostports);
EXPECT_TRUE(s.IsInvalidArgument());
ASSERT_STR_CONTAINS(s.ToString(), "invalid scheme format");
}
TEST_F(NetUtilTest, TestInjectFailureToResolveAddresses) {
HostPort hp1("localhost", 12345);
HostPort hp2("localhost", 12346);
FLAGS_fail_dns_resolution_hostports = hp1.ToString();
FLAGS_fail_dns_resolution = true;
// With a list of bad hostports specified, check that resolution fails as
// expected.
vector<Sockaddr> addrs;
Status s = hp1.ResolveAddresses(&addrs);
ASSERT_TRUE(s.IsNetworkError()) << s.ToString();
// Addresses not in the list should resolve fine.
ASSERT_TRUE(addrs.empty());
ASSERT_OK(hp2.ResolveAddresses(&addrs));
ASSERT_FALSE(addrs.empty());
// With both in the list, resolution should fail.
FLAGS_fail_dns_resolution_hostports = Substitute("$0,$1", hp1.ToString(), hp2.ToString());
addrs.clear();
s = hp1.ResolveAddresses(&addrs);
ASSERT_TRUE(s.IsNetworkError()) << s.ToString();
s = hp2.ResolveAddresses(&addrs);
ASSERT_TRUE(s.IsNetworkError()) << s.ToString();
// When a list isn't specified, all resolution fails.
FLAGS_fail_dns_resolution_hostports = "";
s = hp1.ResolveAddresses(&addrs);
ASSERT_TRUE(s.IsNetworkError()) << s.ToString();
s = hp2.ResolveAddresses(&addrs);
ASSERT_TRUE(s.IsNetworkError()) << s.ToString();
}
TEST_F(NetUtilTest, TestResolveAddresses) {
HostPort hp("localhost", 12345);
vector<Sockaddr> addrs;
ASSERT_OK(hp.ResolveAddresses(&addrs));
ASSERT_TRUE(!addrs.empty());
for (const Sockaddr& addr : addrs) {
LOG(INFO) << "Address: " << addr.ToString();
EXPECT_TRUE(HasPrefixString(addr.ToString(), "127."));
EXPECT_TRUE(HasSuffixString(addr.ToString(), ":12345"));
EXPECT_TRUE(addr.IsAnyLocalAddress());
}
ASSERT_OK(hp.ResolveAddresses(nullptr));
}
TEST_F(NetUtilTest, TestWithinNetwork) {
Sockaddr addr;
Network network;
ASSERT_OK(addr.ParseString("10.0.23.0:12345", 0));
ASSERT_OK(network.ParseCIDRString("10.0.0.0/8"));
EXPECT_TRUE(network.WithinNetwork(addr));
ASSERT_OK(addr.ParseString("172.28.3.4:0", 0));
ASSERT_OK(network.ParseCIDRString("172.16.0.0/12"));
EXPECT_TRUE(network.WithinNetwork(addr));
ASSERT_OK(addr.ParseString("192.168.0.23", 0));
ASSERT_OK(network.ParseCIDRString("192.168.1.14/16"));
EXPECT_TRUE(network.WithinNetwork(addr));
ASSERT_OK(addr.ParseString("8.8.8.8:0", 0));
ASSERT_OK(network.ParseCIDRString("0.0.0.0/0"));
EXPECT_TRUE(network.WithinNetwork(addr));
ASSERT_OK(addr.ParseString("192.169.0.23", 0));
ASSERT_OK(network.ParseCIDRString("192.168.0.0/16"));
EXPECT_FALSE(network.WithinNetwork(addr));
}
// Ensure that we are able to do a reverse DNS lookup on various IP addresses.
// The reverse lookups should never fail, but may return numeric strings.
TEST_F(NetUtilTest, TestReverseLookup) {
string host;
Sockaddr addr;
vector<HostPort> hps;
ASSERT_OK(addr.ParseString("0.0.0.0:12345", 0));
EXPECT_EQ(12345, addr.port());
ASSERT_OK(HostPortsFromAddrs({ addr }, &hps));
EXPECT_NE("0.0.0.0", hps[0].host());
EXPECT_NE("", hps[0].host());
EXPECT_EQ(12345, hps[0].port());
hps.clear();
ASSERT_OK(addr.ParseString("127.0.0.1:12345", 0));
ASSERT_OK(HostPortsFromAddrs({ addr }, &hps));
EXPECT_EQ("127.0.0.1", hps[0].host());
EXPECT_EQ(12345, hps[0].port());
}
TEST_F(NetUtilTest, TestLsof) {
Sockaddr addr = Sockaddr::Wildcard();
Socket s;
ASSERT_OK(s.Init(addr.family(), 0));
ASSERT_OK(s.BindAndListen(addr, 1));
ASSERT_OK(s.GetSocketAddress(&addr));
ASSERT_NE(addr.port(), 0);
vector<string> lsof_lines;
TryRunLsof(addr, &lsof_lines);
SCOPED_TRACE(JoinStrings(lsof_lines, "\n"));
ASSERT_GE(lsof_lines.size(), 3);
ASSERT_STR_CONTAINS(lsof_lines[2], "net_util-test");
}
TEST_F(NetUtilTest, TestGetFQDN) {
string fqdn;
ASSERT_OK(GetFQDN(&fqdn));
LOG(INFO) << "fqdn is " << fqdn;
}
TEST_F(NetUtilTest, TestGetRandomPort) {
uint16_t port;
ASSERT_OK(GetRandomPort("127.0.0.1", &port));
LOG(INFO) << "Random port is " << port;
}
TEST_F(NetUtilTest, TestSockaddr) {
auto addr1 = Sockaddr::Wildcard();
addr1.set_port(1000);
auto addr2 = Sockaddr::Wildcard();
addr2.set_port(2000);
ASSERT_EQ(1000, addr1.port());
ASSERT_EQ(2000, addr2.port());
ASSERT_EQ(string("0.0.0.0:1000"), addr1.ToString());
ASSERT_EQ(string("0.0.0.0:2000"), addr2.ToString());
Sockaddr addr3(addr1);
ASSERT_EQ(string("0.0.0.0:1000"), addr3.ToString());
}
TEST_F(NetUtilTest, TestSockaddrEquality) {
Sockaddr uninitialized_1;
Sockaddr uninitialized_2;
ASSERT_TRUE(uninitialized_1 == uninitialized_2);
Sockaddr wildcard = Sockaddr::Wildcard();
ASSERT_FALSE(wildcard == uninitialized_1);
ASSERT_FALSE(uninitialized_1 == wildcard);
Sockaddr wildcard_2 = Sockaddr::Wildcard();
ASSERT_TRUE(wildcard == wildcard_2);
ASSERT_TRUE(wildcard_2 == wildcard);
Sockaddr ip_port;
ASSERT_OK(ip_port.ParseString("127.0.0.1:12345", 0));
ASSERT_FALSE(ip_port == uninitialized_1);
ASSERT_FALSE(ip_port == wildcard);
ASSERT_TRUE(ip_port == ip_port);
Sockaddr copy = ip_port;
ASSERT_TRUE(ip_port == copy);
}
TEST_F(NetUtilTest, TestUnixSockaddr) {
Sockaddr addr;
ASSERT_OK(addr.ParseUnixDomainPath("/foo/bar"));
ASSERT_EQ(addr.family(), AF_UNIX);
ASSERT_EQ(addr.UnixDomainPath(), "/foo/bar");
ASSERT_EQ(addr.ToString(), "unix:/foo/bar");
ASSERT_EQ(Sockaddr::UnixAddressType::kPath, addr.unix_address_type());
Sockaddr addr2;
ASSERT_OK(addr2.ParseUnixDomainPath("@my-abstract"));
ASSERT_EQ(addr2.family(), AF_UNIX);
ASSERT_EQ(addr2.UnixDomainPath(), "@my-abstract");
ASSERT_EQ(addr2.ToString(), "unix:@my-abstract");
ASSERT_EQ(Sockaddr::UnixAddressType::kAbstractNamespace, addr2.unix_address_type());
ASSERT_TRUE(addr == addr);
ASSERT_TRUE(addr2 == addr2);
ASSERT_FALSE(addr == addr2);
ASSERT_FALSE(addr2 == addr);
ASSERT_FALSE(addr == Sockaddr::Wildcard());
ASSERT_FALSE(Sockaddr::Wildcard() == addr);
ASSERT_FALSE(addr == Sockaddr());
ASSERT_FALSE(Sockaddr() == addr);
}
} // namespace kudu