/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* Licensed 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 <folly/SocketAddress.h>
#include <iostream>
#include <sstream>
#include <system_error>
#include <folly/String.h>
#include <folly/container/Array.h>
#include <folly/portability/GTest.h>
#include <folly/portability/Sockets.h>
#include <folly/test/SocketAddressTestHelper.h>
#include <folly/testing/TestUtil.h>
using folly::NetworkSocket;
using folly::SocketAddress;
using folly::SocketAddressTestHelper;
using folly::test::TemporaryDirectory;
using std::string;
namespace netops = folly::netops;
TEST(SocketAddress, Size) {
SocketAddress addr;
EXPECT_EQ(sizeof(addr), 32);
}
TEST(SocketAddress, ConstructFromIpv4) {
SocketAddress addr("1.2.3.4", 4321);
EXPECT_EQ(addr.getFamily(), AF_INET);
EXPECT_EQ(addr.getAddressStr(), "1.2.3.4");
EXPECT_EQ(addr.getPort(), 4321);
sockaddr_storage addrStorage;
addr.getAddress(&addrStorage);
const sockaddr_in* inaddr = reinterpret_cast<sockaddr_in*>(&addrStorage);
EXPECT_EQ(inaddr->sin_addr.s_addr, htonl(0x01020304));
EXPECT_EQ(inaddr->sin_port, htons(4321));
}
TEST(SocketAddress, StringConversion) {
SocketAddress addr("1.2.3.4", 4321);
EXPECT_EQ(addr.getFamily(), AF_INET);
EXPECT_EQ(addr.getAddressStr(), "1.2.3.4");
char buf[30];
addr.getAddressStr(buf, 2);
EXPECT_STREQ(buf, "1");
}
TEST(SocketAddress, IPv4ToStringConversion) {
// testing addresses *.5.5.5, 5.*.5.5, 5.5.*.5, 5.5.5.*
SocketAddress addr;
for (int pos = 0; pos < 4; ++pos) {
for (int i = 0; i < 256; ++i) {
auto fragments = folly::make_array(5, 5, 5, 5);
fragments[pos] = i;
auto ipString = folly::join(".", fragments);
addr.setFromIpPort(ipString, 1234);
EXPECT_EQ(addr.getAddressStr(), ipString);
}
}
}
TEST(SocketAddress, SetFromIpAddressPort) {
SocketAddress addr;
folly::IPAddress ipAddr("123.234.0.23");
addr.setFromIpAddrPort(ipAddr, 8888);
EXPECT_EQ(addr.getFamily(), AF_INET);
EXPECT_EQ(addr.getAddressStr(), "123.234.0.23");
EXPECT_EQ(addr.getIPAddress(), ipAddr);
EXPECT_EQ(addr.getPort(), 8888);
folly::IPAddress ip6Addr("2620:0:1cfe:face:b00c::3");
SocketAddress addr6(ip6Addr, 8888);
EXPECT_EQ(addr6.getFamily(), AF_INET6);
EXPECT_EQ(addr6.getAddressStr(), "2620:0:1cfe:face:b00c::3");
EXPECT_EQ(addr6.getIPAddress(), ip6Addr);
EXPECT_EQ(addr6.getPort(), 8888);
}
TEST(SocketAddress, SetFromIpv4) {
SocketAddress addr;
addr.setFromIpPort("255.254.253.252", 8888);
EXPECT_EQ(addr.getFamily(), AF_INET);
EXPECT_EQ(addr.getAddressStr(), "255.254.253.252");
EXPECT_EQ(addr.getPort(), 8888);
sockaddr_storage addrStorage;
addr.getAddress(&addrStorage);
const sockaddr_in* inaddr = reinterpret_cast<sockaddr_in*>(&addrStorage);
EXPECT_EQ(inaddr->sin_addr.s_addr, htonl(0xfffefdfc));
EXPECT_EQ(inaddr->sin_port, htons(8888));
}
TEST(SocketAddress, ConstructFromInvalidIpv4) {
EXPECT_THROW(SocketAddress("1.2.3.256", 1234), std::runtime_error);
}
TEST(SocketAddress, SetFromInvalidIpv4) {
SocketAddress addr("12.34.56.78", 80);
// Try setting to an invalid value
// Since setFromIpPort() shouldn't allow hostname lookups, setting to
// "localhost" should fail, even if localhost is resolvable
EXPECT_THROW(addr.setFromIpPort("localhost", 1234), std::runtime_error);
// Make sure the address still has the old contents
EXPECT_EQ(addr.getFamily(), AF_INET);
EXPECT_EQ(addr.getAddressStr(), "12.34.56.78");
EXPECT_EQ(addr.getPort(), 80);
sockaddr_storage addrStorage;
addr.getAddress(&addrStorage);
const sockaddr_in* inaddr = reinterpret_cast<sockaddr_in*>(&addrStorage);
EXPECT_EQ(inaddr->sin_addr.s_addr, htonl(0x0c22384e));
}
TEST(SocketAddress, SetFromHostname) {
// hopefully "localhost" is resolvable on any system that will run the tests
EXPECT_THROW(SocketAddress("localhost", 80), std::runtime_error);
SocketAddress addr("localhost", 80, true);
SocketAddress addr2;
EXPECT_THROW(addr2.setFromIpPort("localhost", 0), std::runtime_error);
addr2.setFromHostPort("localhost", 0);
}
TEST(SocketAddress, SetFromStrings) {
SocketAddress addr;
// Set from a numeric port string
addr.setFromLocalPort("1234");
EXPECT_EQ(addr.getPort(), 1234);
// setFromLocalPort() should not accept service names
EXPECT_THROW(addr.setFromLocalPort("http"), std::runtime_error);
// Call setFromLocalIpPort() with just a port, no IP
addr.setFromLocalIpPort("80");
EXPECT_EQ(addr.getPort(), 80);
// Call setFromLocalIpPort() with an IP and port.
if (SocketAddressTestHelper::isIPv4Enabled()) {
addr.setFromLocalIpPort("127.0.0.1:4321");
EXPECT_EQ(addr.getAddressStr(), "127.0.0.1");
EXPECT_EQ(addr.getPort(), 4321);
}
if (SocketAddressTestHelper::isIPv6Enabled()) {
addr.setFromLocalIpPort("::1:4321");
EXPECT_EQ(addr.getAddressStr(), "::1");
EXPECT_EQ(addr.getPort(), 4321);
}
// setFromIpPort() without an address should fail
EXPECT_THROW(addr.setFromIpPort("4321"), std::invalid_argument);
// Call setFromIpPort() with an IPv6 address and port
addr.setFromIpPort("2620:0:1cfe:face:b00c::3:65535");
EXPECT_EQ(addr.getFamily(), AF_INET6);
EXPECT_EQ(addr.getAddressStr(), "2620:0:1cfe:face:b00c::3");
EXPECT_EQ(addr.getPort(), 65535);
// Call setFromIpPort() with an IPv4 address and port
addr.setFromIpPort("1.2.3.4:9999");
EXPECT_EQ(addr.getFamily(), AF_INET);
EXPECT_EQ(addr.getAddressStr(), "1.2.3.4");
EXPECT_EQ(addr.getPort(), 9999);
// Call setFromIpPort() with a bracketed IPv6
addr.setFromIpPort("[::]:1234");
EXPECT_EQ(addr.getFamily(), AF_INET6);
EXPECT_EQ(addr.getAddressStr(), "::");
EXPECT_EQ(addr.getPort(), 1234);
// Call setFromIpPort() with a bracketed IPv6
addr.setFromIpPort("[9:8::2]:1234");
EXPECT_EQ(addr.getFamily(), AF_INET6);
EXPECT_EQ(addr.getAddressStr(), "9:8::2");
EXPECT_EQ(addr.getPort(), 1234);
// Call setFromIpPort() with a bracketed IPv6 and no port
EXPECT_THROW(addr.setFromIpPort("[::]"), std::system_error);
}
TEST(SocketAddress, EqualityAndHash) {
SocketAddress empty1;
SocketAddress empty2;
EXPECT_EQ(empty1, empty2);
EXPECT_EQ(empty1.hash(), empty2.hash());
// IPv4
SocketAddress local1("127.0.0.1", 1234);
EXPECT_EQ(local1, local1);
EXPECT_EQ(local1.hash(), local1.hash());
SocketAddress local2("127.0.0.1", 1234);
EXPECT_EQ(local1, local2);
EXPECT_EQ(local1.hash(), local2.hash());
SocketAddress local3("127.0.0.1", 4321);
EXPECT_NE(local1, local3);
EXPECT_NE(local1.hash(), local3.hash());
SocketAddress other1("1.2.3.4", 1234);
EXPECT_EQ(other1, other1);
EXPECT_EQ(other1.hash(), other1.hash());
EXPECT_NE(local1, other1);
EXPECT_NE(local1.hash(), other1.hash());
SocketAddress other2("4.3.2.1", 1234);
EXPECT_NE(other1.hash(), other2.hash());
EXPECT_NE(other1.hash(), other2.hash());
other2.setFromIpPort("1.2.3.4", 0);
EXPECT_NE(other1.hash(), other2.hash());
EXPECT_NE(other1.hash(), other2.hash());
other2.setPort(1234);
EXPECT_EQ(other1.hash(), other2.hash());
EXPECT_EQ(other1.hash(), other2.hash());
// IPv6
SocketAddress v6_1("2620:0:1c00:face:b00c:0:0:abcd", 1234);
SocketAddress v6_2("2620:0:1c00:face:b00c::abcd", 1234);
SocketAddress v6_3("2620:0:1c00:face:b00c::bcda", 1234);
EXPECT_EQ(v6_1, v6_2);
EXPECT_EQ(v6_1.hash(), v6_2.hash());
EXPECT_NE(v6_1, v6_3);
EXPECT_NE(v6_1.hash(), v6_3.hash());
// IPv4 versus IPv6 comparison
SocketAddress localIPv6("::1", 1234);
// Even though these both refer to localhost,
// IPv4 and IPv6 addresses are never treated as the same address
EXPECT_NE(local1, localIPv6);
EXPECT_NE(local1.hash(), localIPv6.hash());
// IPv4-mapped IPv6 addresses are not treated as equal
// to the equivalent IPv4 address
SocketAddress v4("10.0.0.3", 99);
SocketAddress v6_mapped1("::ffff:10.0.0.3", 99);
SocketAddress v6_mapped2("::ffff:0a00:0003", 99);
EXPECT_NE(v4, v6_mapped1);
EXPECT_NE(v4, v6_mapped2);
EXPECT_EQ(v6_mapped1, v6_mapped2);
// However, after calling convertToIPv4(), the mapped address should now be
// equal to the v4 version.
EXPECT_TRUE(v6_mapped1.isIPv4Mapped());
v6_mapped1.convertToIPv4();
EXPECT_EQ(v6_mapped1, v4);
EXPECT_NE(v6_mapped1, v6_mapped2);
// Unix
SocketAddress unix1;
unix1.setFromPath("/foo");
SocketAddress unix2;
unix2.setFromPath("/foo");
SocketAddress unix3;
unix3.setFromPath("/bar");
SocketAddress unixAnon;
unixAnon.setFromPath("");
auto unix5 = SocketAddress::makeFromPath("/foo");
auto unixAnon2 = SocketAddress::makeFromPath("");
EXPECT_EQ(unix1, unix2);
EXPECT_EQ(unix1, unix5);
EXPECT_EQ(unix1.hash(), unix2.hash());
EXPECT_EQ(unix1.hash(), unix5.hash());
EXPECT_NE(unix1, unix3);
EXPECT_NE(unix1, unixAnon);
EXPECT_NE(unix1, unixAnon2);
EXPECT_NE(unix2, unix3);
EXPECT_NE(unix5, unix3);
EXPECT_NE(unix2, unixAnon);
EXPECT_NE(unix2, unixAnon2);
EXPECT_NE(unix5, unixAnon);
EXPECT_NE(unix5, unixAnon2);
// anonymous addresses aren't equal to any other address,
// including themselves
EXPECT_NE(unixAnon, unixAnon);
EXPECT_NE(unixAnon2, unixAnon2);
// It isn't strictly required that hashes for different addresses be
// different, but we should have very few collisions. It generally indicates
// a problem if these collide
EXPECT_NE(unix1.hash(), unix3.hash());
EXPECT_NE(unix1.hash(), unixAnon.hash());
EXPECT_NE(unix3.hash(), unixAnon.hash());
EXPECT_NE(unix1.hash(), unixAnon2.hash());
EXPECT_NE(unix3.hash(), unixAnon2.hash());
}
TEST(SocketAddress, IsPrivate) {
// IPv4
SocketAddress addr("9.255.255.255", 0);
EXPECT_TRUE(!addr.isPrivateAddress());
addr.setFromIpPort("10.0.0.0", 0);
EXPECT_TRUE(addr.isPrivateAddress());
addr.setFromIpPort("10.255.255.255", 0);
EXPECT_TRUE(addr.isPrivateAddress());
addr.setFromIpPort("11.0.0.0", 0);
EXPECT_TRUE(!addr.isPrivateAddress());
addr.setFromIpPort("172.15.255.255", 0);
EXPECT_TRUE(!addr.isPrivateAddress());
addr.setFromIpPort("172.16.0.0", 0);
EXPECT_TRUE(addr.isPrivateAddress());
addr.setFromIpPort("172.31.255.255", 0);
EXPECT_TRUE(addr.isPrivateAddress());
addr.setFromIpPort("172.32.0.0", 0);
EXPECT_TRUE(!addr.isPrivateAddress());
addr.setFromIpPort("192.167.255.255", 0);
EXPECT_TRUE(!addr.isPrivateAddress());
addr.setFromIpPort("192.168.0.0", 0);
EXPECT_TRUE(addr.isPrivateAddress());
addr.setFromIpPort("192.168.255.255", 0);
EXPECT_TRUE(addr.isPrivateAddress());
addr.setFromIpPort("192.169.0.0", 0);
EXPECT_TRUE(!addr.isPrivateAddress());
addr.setFromIpPort("126.255.255.255", 0);
EXPECT_TRUE(!addr.isPrivateAddress());
addr.setFromIpPort("127.0.0.0", 0);
EXPECT_TRUE(addr.isPrivateAddress());
addr.setFromIpPort("127.0.0.1", 0);
EXPECT_TRUE(addr.isPrivateAddress());
addr.setFromIpPort("127.255.255.255", 0);
EXPECT_TRUE(addr.isPrivateAddress());
addr.setFromIpPort("128.0.0.0", 0);
EXPECT_TRUE(!addr.isPrivateAddress());
addr.setFromIpPort("1.2.3.4", 0);
EXPECT_TRUE(!addr.isPrivateAddress());
addr.setFromIpPort("69.171.239.10", 0);
EXPECT_TRUE(!addr.isPrivateAddress());
// IPv6
addr.setFromIpPort("fbff:ffff:ffff:ffff:ffff:ffff:ffff:ffff", 0);
EXPECT_TRUE(!addr.isPrivateAddress());
addr.setFromIpPort("fc00::", 0);
EXPECT_TRUE(addr.isPrivateAddress());
addr.setFromIpPort("fdff:ffff:ffff:ffff:ffff:ffff:ffff:ffff", 0);
EXPECT_TRUE(addr.isPrivateAddress());
addr.setFromIpPort("fe00::", 0);
EXPECT_TRUE(!addr.isPrivateAddress());
addr.setFromIpPort("fe7f:ffff:ffff:ffff:ffff:ffff:ffff:ffff", 0);
EXPECT_TRUE(!addr.isPrivateAddress());
addr.setFromIpPort("fe80::", 0);
EXPECT_TRUE(addr.isPrivateAddress());
addr.setFromIpPort("fe80::ffff:ffff:ffff:ffff", 0);
EXPECT_TRUE(addr.isPrivateAddress());
addr.setFromIpPort("febf:ffff:ffff:ffff:ffff:ffff:ffff:ffff", 0);
EXPECT_TRUE(addr.isPrivateAddress());
addr.setFromIpPort("fec0::", 0);
EXPECT_TRUE(!addr.isPrivateAddress());
addr.setFromIpPort("::0", 0);
EXPECT_TRUE(!addr.isPrivateAddress());
addr.setFromIpPort("2620:0:1c00:face:b00c:0:0:abcd", 0);
EXPECT_TRUE(!addr.isPrivateAddress());
// IPv4-mapped IPv6
addr.setFromIpPort("::ffff:127.0.0.1", 0);
EXPECT_TRUE(addr.isPrivateAddress());
addr.setFromIpPort("::ffff:10.1.2.3", 0);
EXPECT_TRUE(addr.isPrivateAddress());
addr.setFromIpPort("::ffff:172.24.0.115", 0);
EXPECT_TRUE(addr.isPrivateAddress());
addr.setFromIpPort("::ffff:192.168.0.1", 0);
EXPECT_TRUE(addr.isPrivateAddress());
addr.setFromIpPort("::ffff:69.171.239.10", 0);
EXPECT_TRUE(!addr.isPrivateAddress());
// Unix sockets are considered private addresses
addr.setFromPath("/tmp/mysock");
EXPECT_TRUE(addr.isPrivateAddress());
}
TEST(SocketAddress, IsLoopback) {
// IPv4
SocketAddress addr("127.0.0.1", 0);
EXPECT_TRUE(addr.isLoopbackAddress());
addr.setFromIpPort("127.0.0.0", 0xffff);
EXPECT_TRUE(addr.isLoopbackAddress());
addr.setFromIpPort("127.1.1.1", 0xffff);
EXPECT_TRUE(addr.isLoopbackAddress());
addr.setFromIpPort("127.255.255.255", 80);
EXPECT_TRUE(addr.isLoopbackAddress());
addr.setFromIpPort("128.0.0.0", 0);
EXPECT_TRUE(!addr.isLoopbackAddress());
addr.setFromIpPort("126.255.255.255", 0);
EXPECT_TRUE(!addr.isLoopbackAddress());
addr.setFromIpPort("10.1.2.3", 0);
EXPECT_TRUE(!addr.isLoopbackAddress());
// IPv6
addr.setFromIpPort("::1", 0);
EXPECT_TRUE(addr.isLoopbackAddress());
addr.setFromIpPort("::0", 0);
EXPECT_TRUE(!addr.isLoopbackAddress());
addr.setFromIpPort("::2", 0);
EXPECT_TRUE(!addr.isLoopbackAddress());
// IPv4-mapped IPv6
addr.setFromIpPort("::ffff:127.0.0.1", 0);
EXPECT_TRUE(addr.isLoopbackAddress());
addr.setFromIpPort("::ffff:7f0a:141e", 0);
EXPECT_TRUE(addr.isLoopbackAddress());
addr.setFromIpPort("::ffff:169.254.0.13", 0);
EXPECT_TRUE(!addr.isLoopbackAddress());
// Unix sockets are considered loopback addresses
addr.setFromPath("/tmp/mysock");
EXPECT_TRUE(addr.isLoopbackAddress());
}
void CheckPrefixMatch(
const SocketAddress& first,
const SocketAddress& second,
unsigned matchingPrefixLen) {
unsigned i;
for (i = 0; i <= matchingPrefixLen; i++) {
EXPECT_TRUE(first.prefixMatch(second, i));
}
unsigned addrLen = (first.getFamily() == AF_INET6) ? 128 : 32;
for (; i <= addrLen; i++) {
EXPECT_TRUE(!first.prefixMatch(second, i));
}
}
TEST(SocketAddress, PrefixMatch) {
// IPv4
SocketAddress addr1("127.0.0.1", 0);
SocketAddress addr2("127.0.0.1", 0);
CheckPrefixMatch(addr1, addr2, 32);
addr2.setFromIpPort("127.0.1.1", 0);
CheckPrefixMatch(addr1, addr2, 23);
addr2.setFromIpPort("1.1.0.127", 0);
CheckPrefixMatch(addr1, addr2, 1);
// Address family mismatch
addr2.setFromIpPort("::ffff:127.0.0.1", 0);
EXPECT_TRUE(!addr1.prefixMatch(addr2, 1));
// IPv6
addr1.setFromIpPort("2a03:2880:10:8f02:face:b00c:0:25", 0);
CheckPrefixMatch(addr1, addr2, 2);
addr2.setFromIpPort("2a03:2880:10:8f02:face:b00c:0:25", 0);
CheckPrefixMatch(addr1, addr2, 128);
addr2.setFromIpPort("2a03:2880:30:8f02:face:b00c:0:25", 0);
CheckPrefixMatch(addr1, addr2, 42);
}
void CheckFirstLessThanSecond(SocketAddress first, SocketAddress second) {
EXPECT_TRUE(!(first < first));
EXPECT_TRUE(!(second < second));
EXPECT_TRUE(first < second);
EXPECT_TRUE(!(first == second));
EXPECT_TRUE(!(second < first));
}
TEST(SocketAddress, CheckComparatorBehavior) {
SocketAddress first, second;
// The following comparison are strict (so if first and second were
// inverted that is ok.
// IP V4
// port comparisions
first.setFromIpPort("128.0.0.0", 0);
second.setFromIpPort("128.0.0.0", 0xFFFF);
CheckFirstLessThanSecond(first, second);
first.setFromIpPort("128.0.0.100", 0);
second.setFromIpPort("128.0.0.0", 0xFFFF);
CheckFirstLessThanSecond(first, second);
// Address comparisons
first.setFromIpPort("128.0.0.0", 10);
second.setFromIpPort("128.0.0.100", 10);
CheckFirstLessThanSecond(first, second);
// Comaprision between IPV4 and IPV6
first.setFromIpPort("128.0.0.0", 0);
second.setFromIpPort("::ffff:127.0.0.1", 0);
CheckFirstLessThanSecond(first, second);
first.setFromIpPort("128.0.0.0", 100);
second.setFromIpPort("::ffff:127.0.0.1", 0);
CheckFirstLessThanSecond(first, second);
// IPV6 comparisons
// port comparisions
first.setFromIpPort("::0", 0);
second.setFromIpPort("::0", 0xFFFF);
CheckFirstLessThanSecond(first, second);
first.setFromIpPort("::0", 0);
second.setFromIpPort("::1", 0xFFFF);
CheckFirstLessThanSecond(first, second);
// Address comparisons
first.setFromIpPort("::0", 10);
second.setFromIpPort("::1", 10);
CheckFirstLessThanSecond(first, second);
// Unix
first.setFromPath("/foo");
second.setFromPath("/1234");
// The exact comparison order doesn't really matter, as long as
// (a < b), (b < a), and (a == b) are consistent.
// This checks our current comparison rules, which checks the path length
// before the path contents.
CheckFirstLessThanSecond(first, second);
first.setFromPath("/1234");
second.setFromPath("/5678");
CheckFirstLessThanSecond(first, second);
// IPv4 vs Unix.
// We currently compare the address family values, and AF_UNIX < AF_INET
first.setFromPath("/foo");
second.setFromIpPort("127.0.0.1", 80);
CheckFirstLessThanSecond(first, second);
}
TEST(SocketAddress, Unix) {
SocketAddress addr;
// Test a small path
addr.setFromPath("foo");
EXPECT_EQ(addr.getFamily(), AF_UNIX);
EXPECT_EQ(addr.describe(), "foo");
EXPECT_THROW(addr.getAddressStr(), std::invalid_argument);
EXPECT_THROW(addr.getPort(), std::invalid_argument);
EXPECT_TRUE(addr.isPrivateAddress());
EXPECT_TRUE(addr.isLoopbackAddress());
// Test a path that is too large
const char longPath[] =
"abcdefghijklmnopqrstuvwxyz0123456789"
"abcdefghijklmnopqrstuvwxyz0123456789"
"abcdefghijklmnopqrstuvwxyz0123456789"
"abcdefghijklmnopqrstuvwxyz0123456789";
EXPECT_THROW(addr.setFromPath(longPath), std::invalid_argument);
// The original address should still be the same
EXPECT_EQ(addr.getFamily(), AF_UNIX);
EXPECT_EQ(addr.describe(), "foo");
// Test a path that exactly fits in sockaddr_un
// (not including the NUL terminator)
const char exactLengthPath[] =
"abcdefghijklmnopqrstuvwxyz0123456789"
"abcdefghijklmnopqrstuvwxyz0123456789"
"abcdefghijklmnopqrstuvwxyz0123456789";
addr.setFromPath(exactLengthPath);
EXPECT_EQ(addr.describe(), exactLengthPath);
// Test converting a unix socket address to an IPv4 one, then back
addr.setFromHostPort("127.0.0.1", 1234);
EXPECT_EQ(addr.getFamily(), AF_INET);
EXPECT_EQ(addr.describe(), "127.0.0.1:1234");
addr.setFromPath("/i/am/a/unix/address");
EXPECT_EQ(addr.getFamily(), AF_UNIX);
EXPECT_EQ(addr.describe(), "/i/am/a/unix/address");
// Test copy constructor and assignment operator
{
SocketAddress copy(addr);
EXPECT_EQ(copy, addr);
copy.setFromPath("/abc");
EXPECT_NE(copy, addr);
copy = addr;
EXPECT_EQ(copy, addr);
copy.setFromIpPort("127.0.0.1", 80);
EXPECT_NE(copy, addr);
copy = addr;
EXPECT_EQ(copy, addr);
}
{
SocketAddress copy(addr);
EXPECT_EQ(copy, addr);
EXPECT_EQ(copy.describe(), "/i/am/a/unix/address");
EXPECT_EQ(copy.getPath(), "/i/am/a/unix/address");
SocketAddress other("127.0.0.1", 80);
EXPECT_NE(other, addr);
other = copy;
EXPECT_EQ(other, copy);
EXPECT_EQ(other, addr);
EXPECT_EQ(copy, addr);
}
{
SocketAddress copy;
{
// move a unix address into a non-unix address
SocketAddress tmpCopy(addr);
copy = std::move(tmpCopy);
}
EXPECT_EQ(copy, addr);
copy.setFromPath("/another/path");
{
// move a unix address into a unix address
SocketAddress tmpCopy(addr);
copy = std::move(tmpCopy);
}
EXPECT_EQ(copy, addr);
{
// move a non-unix address into a unix address
SocketAddress tmp("127.0.0.1", 80);
copy = std::move(tmp);
}
EXPECT_EQ(copy.getAddressStr(), "127.0.0.1");
EXPECT_EQ(copy.getPort(), 80);
copy = addr;
// move construct a unix address
SocketAddress other(std::move(copy));
EXPECT_EQ(other, addr);
EXPECT_EQ(other.getPath(), addr.getPath());
}
}
TEST(SocketAddress, AnonymousUnix) {
// Create a unix socket pair, and get the addresses.
NetworkSocket fds[2];
int rc = netops::socketpair(AF_UNIX, SOCK_STREAM, 0, fds);
EXPECT_EQ(rc, 0);
SocketAddress addr0;
SocketAddress peer0;
SocketAddress addr1;
SocketAddress peer1;
addr0.setFromLocalAddress(fds[0]);
peer0.setFromPeerAddress(fds[0]);
addr1.setFromLocalAddress(fds[1]);
peer1.setFromPeerAddress(fds[1]);
netops::close(fds[0]);
netops::close(fds[1]);
EXPECT_EQ(addr0.describe(), "<anonymous unix address>");
EXPECT_EQ(addr1.describe(), "<anonymous unix address>");
EXPECT_EQ(peer0.describe(), "<anonymous unix address>");
EXPECT_EQ(peer1.describe(), "<anonymous unix address>");
// Anonymous addresses should never compare equal
EXPECT_NE(addr0, addr1);
EXPECT_NE(peer0, peer1);
// Note that logically addr0 and peer1 are the same,
// but since they are both anonymous we have no way to determine this
EXPECT_NE(addr0, peer1);
// We can't even tell if an anonymous address is equal to itself
EXPECT_NE(addr0, addr0);
}
#define REQUIRE_ERRNO(cond, msg) \
if (!(cond)) { \
ADD_FAILURE() << (msg) << ": " << ::folly::errnoStr(errno); \
}
void testSetFromSocket(
const SocketAddress* serverBindAddr,
const SocketAddress* clientBindAddr,
SocketAddress* listenAddrRet,
SocketAddress* acceptAddrRet,
SocketAddress* serverAddrRet,
SocketAddress* serverPeerAddrRet,
SocketAddress* clientAddrRet,
SocketAddress* clientPeerAddrRet) {
auto listenSock = netops::socket(serverBindAddr->getFamily(), SOCK_STREAM, 0);
REQUIRE_ERRNO(
listenSock != NetworkSocket(), "failed to create listen socket");
sockaddr_storage laddr;
serverBindAddr->getAddress(&laddr);
socklen_t laddrLen = serverBindAddr->getActualSize();
int rc =
netops::bind(listenSock, reinterpret_cast<sockaddr*>(&laddr), laddrLen);
REQUIRE_ERRNO(rc == 0, "failed to bind to server socket");
rc = netops::listen(listenSock, 10);
REQUIRE_ERRNO(rc == 0, "failed to listen");
listenAddrRet->setFromLocalAddress(listenSock);
SocketAddress listenPeerAddr;
EXPECT_THROW(
listenPeerAddr.setFromPeerAddress(listenSock), std::runtime_error);
// Note that we use the family from serverBindAddr here, since we allow
// clientBindAddr to be nullptr.
auto clientSock = netops::socket(serverBindAddr->getFamily(), SOCK_STREAM, 0);
REQUIRE_ERRNO(
clientSock != NetworkSocket(), "failed to create client socket");
if (clientBindAddr != nullptr) {
sockaddr_storage clientAddr;
clientBindAddr->getAddress(&clientAddr);
rc = netops::bind(
clientSock,
reinterpret_cast<sockaddr*>(&clientAddr),
clientBindAddr->getActualSize());
REQUIRE_ERRNO(rc == 0, "failed to bind to client socket");
}
sockaddr_storage listenAddr;
listenAddrRet->getAddress(&listenAddr);
rc = netops::connect(
clientSock,
reinterpret_cast<sockaddr*>(&listenAddr),
listenAddrRet->getActualSize());
REQUIRE_ERRNO(rc == 0, "failed to connect");
sockaddr_storage acceptAddr;
socklen_t acceptAddrLen = sizeof(acceptAddr);
auto serverSock = netops::accept(
listenSock, reinterpret_cast<sockaddr*>(&acceptAddr), &acceptAddrLen);
REQUIRE_ERRNO(serverSock != NetworkSocket(), "failed to accept");
acceptAddrRet->setFromSockaddr(
reinterpret_cast<sockaddr*>(&acceptAddr), acceptAddrLen);
serverAddrRet->setFromLocalAddress(serverSock);
serverPeerAddrRet->setFromPeerAddress(serverSock);
clientAddrRet->setFromLocalAddress(clientSock);
clientPeerAddrRet->setFromPeerAddress(clientSock);
netops::close(clientSock);
netops::close(serverSock);
netops::close(listenSock);
}
TEST(SocketAddress, SetFromSocketIPv4) {
SocketAddress serverBindAddr("0.0.0.0", 0);
SocketAddress clientBindAddr("0.0.0.0", 0);
SocketAddress listenAddr;
SocketAddress acceptAddr;
SocketAddress serverAddr;
SocketAddress serverPeerAddr;
SocketAddress clientAddr;
SocketAddress clientPeerAddr;
testSetFromSocket(
&serverBindAddr,
&clientBindAddr,
&listenAddr,
&acceptAddr,
&serverAddr,
&serverPeerAddr,
&clientAddr,
&clientPeerAddr);
// The server socket's local address should have the same port as the listen
// address. The IP will be different, since the listening socket is
// listening on INADDR_ANY, but the server socket will have a concrete IP
// address assigned to it.
EXPECT_EQ(serverAddr.getPort(), listenAddr.getPort());
// The client's peer address should always be the same as the server
// socket's address.
EXPECT_EQ(clientPeerAddr, serverAddr);
// The address returned by getpeername() on the server socket should
// be the same as the address returned by accept()
EXPECT_EQ(serverPeerAddr, acceptAddr);
EXPECT_EQ(serverPeerAddr, clientAddr);
EXPECT_EQ(acceptAddr, clientAddr);
}
/*
* Note this test exercises Linux-specific Unix socket behavior
*/
TEST(SocketAddress, SetFromSocketUnixAbstract) {
// Explicitly binding to an empty path results in an abstract socket
// name being picked for us automatically.
SocketAddress serverBindAddr;
string path(1, 0);
path.append("test address");
serverBindAddr.setFromPath(path);
SocketAddress clientBindAddr;
clientBindAddr.setFromPath("");
SocketAddress listenAddr;
SocketAddress acceptAddr;
SocketAddress serverAddr;
SocketAddress serverPeerAddr;
SocketAddress clientAddr;
SocketAddress clientPeerAddr;
testSetFromSocket(
&serverBindAddr,
&clientBindAddr,
&listenAddr,
&acceptAddr,
&serverAddr,
&serverPeerAddr,
&clientAddr,
&clientPeerAddr);
// The server socket's local address should be the same as the listen
// address.
EXPECT_EQ(serverAddr, listenAddr);
// The client's peer address should always be the same as the server
// socket's address.
EXPECT_EQ(clientPeerAddr, serverAddr);
EXPECT_EQ(serverPeerAddr, clientAddr);
// Oddly, the address returned by accept() does not seem to match the address
// returned by getpeername() on the server socket or getsockname() on the
// client socket.
// EXPECT_EQ(serverPeerAddr, acceptAddr);
// EXPECT_EQ(acceptAddr, clientAddr);
}
TEST(SocketAddress, SetFromSocketUnixExplicit) {
// Pick two temporary path names.
TemporaryDirectory tempDirectory("SocketAddressTest");
std::string serverPath = (tempDirectory.path() / "server").string();
std::string clientPath = (tempDirectory.path() / "client").string();
SocketAddress serverBindAddr;
SocketAddress clientBindAddr;
SocketAddress listenAddr;
SocketAddress acceptAddr;
SocketAddress serverAddr;
SocketAddress serverPeerAddr;
SocketAddress clientAddr;
SocketAddress clientPeerAddr;
try {
serverBindAddr.setFromPath(serverPath.c_str());
clientBindAddr.setFromPath(clientPath.c_str());
testSetFromSocket(
&serverBindAddr,
&clientBindAddr,
&listenAddr,
&acceptAddr,
&serverAddr,
&serverPeerAddr,
&clientAddr,
&clientPeerAddr);
} catch (...) {
// Remove the socket files after we are done
unlink(serverPath.c_str());
unlink(clientPath.c_str());
throw;
}
unlink(serverPath.c_str());
unlink(clientPath.c_str());
// The server socket's local address should be the same as the listen
// address.
EXPECT_EQ(serverAddr, listenAddr);
// The client's peer address should always be the same as the server
// socket's address.
EXPECT_EQ(clientPeerAddr, serverAddr);
EXPECT_EQ(serverPeerAddr, clientAddr);
EXPECT_EQ(serverPeerAddr, acceptAddr);
EXPECT_EQ(acceptAddr, clientAddr);
}
TEST(SocketAddress, SetFromSocketUnixAnonymous) {
// Test an anonymous client talking to a fixed-path unix socket.
TemporaryDirectory tempDirectory("SocketAddressTest");
std::string serverPath = (tempDirectory.path() / "server").string();
SocketAddress serverBindAddr;
SocketAddress listenAddr;
SocketAddress acceptAddr;
SocketAddress serverAddr;
SocketAddress serverPeerAddr;
SocketAddress clientAddr;
SocketAddress clientPeerAddr;
try {
serverBindAddr.setFromPath(serverPath.c_str());
testSetFromSocket(
&serverBindAddr,
nullptr,
&listenAddr,
&acceptAddr,
&serverAddr,
&serverPeerAddr,
&clientAddr,
&clientPeerAddr);
} catch (...) {
// Remove the socket file after we are done
unlink(serverPath.c_str());
throw;
}
unlink(serverPath.c_str());
// The server socket's local address should be the same as the listen
// address.
EXPECT_EQ(serverAddr, listenAddr);
// The client's peer address should always be the same as the server
// socket's address.
EXPECT_EQ(clientPeerAddr, serverAddr);
// Since the client is using an anonymous address, it won't compare equal to
// any other anonymous addresses. Make sure the addresses are anonymous.
EXPECT_EQ(serverPeerAddr.getPath(), "");
EXPECT_EQ(clientAddr.getPath(), "");
EXPECT_EQ(acceptAddr.getPath(), "");
}
TEST(SocketAddress, ResetUnixAddress) {
SocketAddress addy;
addy.setFromPath("/foo");
addy.reset();
EXPECT_EQ(addy.getFamily(), AF_UNSPEC);
}
TEST(SocketAddress, ResetIPAddress) {
SocketAddress addr;
addr.setFromIpPort("127.0.0.1", 80);
addr.reset();
EXPECT_EQ(addr.getFamily(), AF_UNSPEC);
EXPECT_FALSE(addr.isInitialized());
EXPECT_TRUE(addr.empty());
addr.setFromIpPort("2620:0:1cfe:face:b00c::3:65535");
addr.reset();
EXPECT_EQ(addr.getFamily(), AF_UNSPEC);
EXPECT_FALSE(addr.isInitialized());
EXPECT_TRUE(addr.empty());
}
TEST(SocketAddress, ValidFamilyInet) {
SocketAddress addr;
EXPECT_FALSE(addr.isFamilyInet());
folly::IPAddress ipAddr("123.234.0.23");
addr.setFromIpAddrPort(ipAddr, 8888);
EXPECT_TRUE(addr.isFamilyInet());
folly::IPAddress ip6Addr("2620:0:1cfe:face:b00c::3");
SocketAddress addr6(ip6Addr, 8888);
EXPECT_TRUE(addr6.isFamilyInet());
}