Add Socket::Get[Remote/Local]IpAddress and unit tests

Differential Revision: http://reviews.llvm.org/D6917

llvm-svn: 226234
This commit is contained in:
Vince Harron 2015-01-16 00:47:08 +00:00
parent 0a3a7ccb3a
commit 014bb7da79
10 changed files with 478 additions and 19 deletions

View File

@ -12,6 +12,10 @@
236ED33619D490B0008CA7D7 /* Makefile.rules */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = Makefile.rules; sourceTree = "<group>"; };
33064C981A5C7A1A0033D415 /* UriParserTest.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = UriParserTest.cpp; path = Utility/UriParserTest.cpp; sourceTree = "<group>"; };
33064C9D1A5C7AC90033D415 /* do-gtest.py */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.script.python; path = "do-gtest.py"; sourceTree = "<group>"; };
330E475C1A609CF600FD2884 /* Makefile */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.make; name = Makefile; path = Host/Makefile; sourceTree = "<group>"; };
330E475D1A609CF600FD2884 /* SocketTest.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = SocketTest.cpp; path = Host/SocketTest.cpp; sourceTree = "<group>"; };
330E475E1A60B31F00FD2884 /* SocketTestMock.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = SocketTestMock.cpp; path = Host/SocketTestMock.cpp; sourceTree = "<group>"; };
330E47621A62451800FD2884 /* SocketAddressTest.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = SocketAddressTest.cpp; path = Host/SocketAddressTest.cpp; sourceTree = "<group>"; };
338C47F41A1E67B900B46077 /* Makefile */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.make; name = Makefile; path = Utility/Makefile; sourceTree = "<group>"; };
338C47F51A1E67B900B46077 /* StringExtractorTest.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = StringExtractorTest.cpp; path = Utility/StringExtractorTest.cpp; sourceTree = "<group>"; };
/* End PBXFileReference section */
@ -20,6 +24,7 @@
236ED32F19D4901D008CA7D7 /* unittest */ = {
isa = PBXGroup;
children = (
330E475B1A609CDF00FD2884 /* Host */,
338C47F31A1E677900B46077 /* Utility */,
236ED33019D4903E008CA7D7 /* Plugins */,
);
@ -68,6 +73,17 @@
);
sourceTree = "<group>";
};
330E475B1A609CDF00FD2884 /* Host */ = {
isa = PBXGroup;
children = (
330E475E1A60B31F00FD2884 /* SocketTestMock.cpp */,
330E475C1A609CF600FD2884 /* Makefile */,
330E475D1A609CF600FD2884 /* SocketTest.cpp */,
330E47621A62451800FD2884 /* SocketAddressTest.cpp */,
);
name = Host;
sourceTree = "<group>";
};
338C47F31A1E677900B46077 /* Utility */ = {
isa = PBXGroup;
children = (

View File

@ -0,0 +1,32 @@
THIS_FILE_DIR := $(shell dirname $(realpath $(lastword $(MAKEFILE_LIST))))/
LEVEL := $(realpath $(THIS_FILE_DIR)../../make)
CFLAGS_EXTRAS := -D__STDC_LIMIT_MACROS -D__STDC_FORMAT_MACROS -D__STDC_CONSTANT_MACROS
ENABLE_THREADS := YES
# the fact that we need all of these source files to compile Socket.cpp
# is a good indication that we need some refactoring
CXX_SOURCES := $(wildcard *.cpp) \
$(realpath $(LEVEL)/../../source/Core/Error.cpp) \
$(realpath $(LEVEL)/../../source/Core/RegularExpression.cpp) \
$(realpath $(LEVEL)/../../source/Core/Stream.cpp) \
$(realpath $(LEVEL)/../../source/Core/StreamString.cpp) \
$(realpath $(LEVEL)/../../source/Host/common/Socket.cpp) \
$(realpath $(LEVEL)/../../source/Host/common/SocketAddress.cpp) \
$(realpath $(LEVEL)/../../source/Host/common/StringConvert.cpp) \
$(realpath $(LEVEL)/../../source/Host/common/TimeValue.cpp)
OS := $(shell uname -s)
ifeq ($(OS),Windows)
CXX_SOURCES := $(CXX_SOURCES) \
$(LEVEL)/../../source/Host/windows/Condition.cpp \
$(LEVEL)/../../source/Host/windows/Mutex.cpp
else
CXX_SOURCES := $(CXX_SOURCES) \
$(LEVEL)/../../source/Host/common/Condition.cpp \
$(LEVEL)/../../source/Host/common/Mutex.cpp
endif
MAKE_DSYM := NO
include $(LEVEL)/Makefile.rules

View File

@ -0,0 +1,77 @@
//===-- SocketAddressTest.cpp -----------------------------------*- C++ -*-===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
#include "gtest/gtest.h"
#include "lldb/Host/SocketAddress.h"
namespace
{
class SocketAddressTest: public ::testing::Test
{
};
}
using namespace lldb_private;
TEST_F (SocketAddressTest, Set)
{
SocketAddress sa;
ASSERT_TRUE (sa.SetToLocalhost (AF_INET, 1138));
ASSERT_STREQ ("127.0.0.1", sa.GetIPAddress ().c_str ());
ASSERT_EQ (1138, sa.GetPort ());
ASSERT_TRUE (sa.SetToAnyAddress (AF_INET, 0));
ASSERT_STREQ ("0.0.0.0", sa.GetIPAddress ().c_str ());
ASSERT_EQ (0, sa.GetPort ());
ASSERT_TRUE (sa.SetToLocalhost (AF_INET6, 1139));
#ifdef _WIN32
ASSERT_STREQ ("0:0:0:0:0:0:0:1", sa.GetIPAddress ().c_str ());
#else
ASSERT_STREQ ("::1", sa.GetIPAddress ().c_str ());
#endif
ASSERT_EQ (1139, sa.GetPort ());
}
#ifdef _WIN32
// we need to test our inet_ntop implementation for Windows XP
const char* inet_ntop (int af, const void * src,
char * dst, socklen_t size);
TEST_F (SocketAddressTest, inet_ntop)
{
const uint8_t address4[4] = {255, 0, 1, 100};
const uint8_t address6[16] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 255, 0};
char buffer[INET6_ADDRSTRLEN];
memset (buffer, 'x', sizeof (buffer));
EXPECT_STREQ ("1:203:405:607:809:a0b:c0d:ff00", inet_ntop (AF_INET6, address6, buffer, sizeof (buffer)));
memset (buffer, 'x', sizeof (buffer));
EXPECT_STREQ ("1:203:405:607:809:a0b:c0d:ff00", inet_ntop (AF_INET6, address6, buffer, 31));
memset (buffer, 'x', sizeof (buffer));
EXPECT_STREQ (nullptr, inet_ntop (AF_INET6, address6, buffer, 0));
memset (buffer, 'x', sizeof (buffer));
EXPECT_STREQ (nullptr, inet_ntop (AF_INET6, address6, buffer, 30));
memset (buffer, 'x', sizeof (buffer));
EXPECT_STREQ ("255.0.1.100", inet_ntop (AF_INET, address4, buffer, sizeof (buffer)));
memset (buffer, 'x', sizeof (buffer));
EXPECT_STREQ ("255.0.1.100", inet_ntop (AF_INET, address4, buffer, 12));
memset (buffer, 'x', sizeof (buffer));
EXPECT_STREQ (nullptr, inet_ntop (AF_INET, address4, buffer, 0));
memset (buffer, 'x', sizeof (buffer));
EXPECT_STREQ (nullptr, inet_ntop (AF_INET, address4, buffer, 11));
}
#endif

View File

@ -0,0 +1,132 @@
//===-- SocketTest.cpp ------------------------------------------*- C++ -*-===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
#include <thread>
#include "gtest/gtest.h"
#include "lldb/Host/Socket.h"
class SocketTest: public ::testing::Test
{
};
using namespace lldb_private;
void AcceptThread (Socket* listen_socket,
const char* listen_remote_address,
bool child_processes_inherit,
Socket** accept_socket,
Error* error)
{
*error = listen_socket->BlockingAccept (listen_remote_address, child_processes_inherit, *accept_socket);
}
void CreateConnectedSockets (std::unique_ptr<Socket>* a_up, std::unique_ptr<Socket>* b_up)
{
Predicate<uint16_t> port_predicate;
// Used when binding to port zero to wait for the thread
// that creates the socket, binds and listens to resolve
// the port number.
port_predicate.SetValue (0, eBroadcastNever);
bool child_processes_inherit = false;
Socket *socket = nullptr;
const char* listen_remote_address = "localhost:0";
Error error = Socket::TcpListen (listen_remote_address, child_processes_inherit, socket, &port_predicate);
std::unique_ptr<Socket> listen_socket_up (socket);
socket = nullptr;
EXPECT_FALSE (error.Fail ());
EXPECT_NE (nullptr, listen_socket_up.get ());
EXPECT_TRUE (listen_socket_up->IsValid ());
Error accept_error;
Socket* accept_socket;
std::thread accept_thread (AcceptThread,
listen_socket_up.get (),
listen_remote_address,
child_processes_inherit,
&accept_socket,
&accept_error);
char connect_remote_address[64];
snprintf (connect_remote_address, sizeof (connect_remote_address), "localhost:%u", port_predicate.GetValue ());
error = Socket::TcpConnect (connect_remote_address, child_processes_inherit, socket);
a_up->reset (socket);
socket = nullptr;
EXPECT_TRUE (error.Success ());
EXPECT_NE (nullptr, a_up->get ());
EXPECT_TRUE ((*a_up)->IsValid ());
accept_thread.join ();
b_up->reset (accept_socket);
EXPECT_TRUE (accept_error.Success ());
EXPECT_NE (nullptr, b_up->get ());
EXPECT_TRUE ((*b_up)->IsValid ());
listen_socket_up.reset ();
}
TEST_F (SocketTest, DecodeHostAndPort)
{
std::string host_str;
std::string port_str;
int32_t port;
Error error;
EXPECT_TRUE (Socket::DecodeHostAndPort ("localhost:1138", host_str, port_str, port, &error));
EXPECT_STREQ ("localhost", host_str.c_str ());
EXPECT_STREQ ("1138", port_str.c_str ());
EXPECT_EQ (1138, port);
EXPECT_TRUE (error.Success ());
EXPECT_FALSE (Socket::DecodeHostAndPort ("google.com:65536", host_str, port_str, port, &error));
EXPECT_TRUE (error.Fail ());
EXPECT_STREQ ("invalid host:port specification: 'google.com:65536'", error.AsCString ());
EXPECT_FALSE (Socket::DecodeHostAndPort ("google.com:-1138", host_str, port_str, port, &error));
EXPECT_TRUE (error.Fail ());
EXPECT_STREQ ("invalid host:port specification: 'google.com:-1138'", error.AsCString ());
EXPECT_TRUE (Socket::DecodeHostAndPort ("12345", host_str, port_str, port, &error));
EXPECT_STREQ ("", host_str.c_str ());
EXPECT_STREQ ("12345", port_str.c_str ());
EXPECT_EQ (12345, port);
EXPECT_TRUE (error.Success ());
EXPECT_TRUE (Socket::DecodeHostAndPort ("*:0", host_str, port_str, port, &error));
EXPECT_STREQ ("*", host_str.c_str ());
EXPECT_STREQ ("0", port_str.c_str ());
EXPECT_EQ (0, port);
EXPECT_TRUE (error.Success ());
}
TEST_F (SocketTest, Listen0ConnectAccept)
{
std::unique_ptr<Socket> socket_a_up;
std::unique_ptr<Socket> socket_b_up;
CreateConnectedSockets (&socket_a_up, &socket_b_up);
}
TEST_F (SocketTest, GetAddress)
{
std::unique_ptr<Socket> socket_a_up;
std::unique_ptr<Socket> socket_b_up;
CreateConnectedSockets (&socket_a_up, &socket_b_up);
EXPECT_EQ (socket_a_up->GetLocalPortNumber (), socket_b_up->GetRemotePortNumber ());
EXPECT_EQ (socket_b_up->GetLocalPortNumber (), socket_a_up->GetRemotePortNumber ());
EXPECT_NE (socket_a_up->GetLocalPortNumber (), socket_b_up->GetLocalPortNumber ());
EXPECT_STREQ ("127.0.0.1", socket_a_up->GetRemoteIPAddress ().c_str ());
EXPECT_STREQ ("127.0.0.1", socket_b_up->GetRemoteIPAddress ().c_str ());
}

View File

@ -0,0 +1,64 @@
//===-- SocketTestMock.cpp --------------------------------------*- C++ -*-===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
// This file provides a few necessary functions to link SocketTest.cpp
// Bringing in the real implementations results in a cascade of dependencies
// that pull in all of lldb.
#include "lldb/Core/Log.h"
#ifdef _WIN32
#include <windows.h>
#endif
using namespace lldb_private;
void
lldb_private::Log::Error (char const*, ...)
{
}
void
lldb_private::Log::Printf (char const*, ...)
{
}
Log*
lldb_private::GetLogIfAnyCategoriesSet (unsigned int)
{
return nullptr;
}
#include "lldb/Host/FileSystem.h"
#ifdef _WIN32
Error
FileSystem::Unlink(const char *path)
{
Error error;
BOOL result = ::DeleteFile(path);
if (!result)
error.SetError(::GetLastError(), lldb::eErrorTypeWin32);
return error;
}
#else
Error
FileSystem::Unlink (const char *path)
{
Error error;
if (::unlink (path) == -1)
error.SetErrorToErrno ();
return error;
}
#endif

View File

@ -70,22 +70,35 @@ public:
int GetOption (int level, int option_name, int &option_value);
int SetOption (int level, int option_name, int option_value);
static uint16_t GetPortNumber(const NativeSocket& socket);
uint16_t GetPortNumber () const;
// returns port number or 0 if error
static uint16_t GetLocalPortNumber (const NativeSocket& socket);
// returns port number or 0 if error
uint16_t GetLocalPortNumber () const;
// returns ip address string or empty string if error
std::string GetLocalIPAddress () const;
// must be connected
// returns port number or 0 if error
uint16_t GetRemotePortNumber () const;
// must be connected
// returns ip address string or empty string if error
std::string GetRemoteIPAddress () const;
NativeSocket GetNativeSocket () const { return m_socket; }
SocketProtocol GetSocketProtocol() const { return m_protocol; }
SocketProtocol GetSocketProtocol () const { return m_protocol; }
virtual Error Read (void *buf, size_t &num_bytes);
virtual Error Write (const void *buf, size_t &num_bytes);
virtual Error PreDisconnect();
virtual Error Close();
virtual Error PreDisconnect ();
virtual Error Close ();
virtual bool IsValid() const { return m_socket != kInvalidSocketValue; }
virtual WaitableHandle GetWaitableHandle();
virtual bool IsValid () const { return m_socket != kInvalidSocketValue; }
virtual WaitableHandle GetWaitableHandle ();
protected:
static bool
DecodeHostAndPort (llvm::StringRef host_and_port,
std::string &host_str,
@ -93,7 +106,7 @@ protected:
int32_t& port,
Error *error_ptr);
protected:
SocketProtocol m_protocol;
NativeSocket m_socket;
SocketAddress m_udp_send_sockaddr; // Send address used for UDP connections.

View File

@ -31,6 +31,7 @@ typedef ADDRESS_FAMILY sa_family_t;
// C++ Includes
// Other libraries and framework includes
// Project includes
#include <string>
namespace lldb_private {
@ -99,6 +100,12 @@ public:
void
SetFamily (sa_family_t family);
//------------------------------------------------------------------
// Get the address
//------------------------------------------------------------------
std::string
GetIPAddress () const;
//------------------------------------------------------------------
// Get the port if the socket address for the family has a port
//------------------------------------------------------------------

View File

@ -222,7 +222,7 @@ Error Socket::TcpListen(llvm::StringRef host_and_port, bool child_processes_inhe
// as port zero is a special code for "find an open port
// for me".
if (port == 0)
port = listen_socket->GetPortNumber();
port = listen_socket->GetLocalPortNumber();
// Set the port predicate since when doing a listen://<host>:<port>
// it often needs to accept the incoming connection which is a blocking
@ -230,7 +230,7 @@ Error Socket::TcpListen(llvm::StringRef host_and_port, bool child_processes_inhe
// us to wait for the port predicate to be set to a non-zero value from
// another thread in an efficient manor.
if (predicate)
predicate->SetValue(port, eBroadcastAlways);
predicate->SetValue (port, eBroadcastAlways);
socket = listen_socket.release();
}
@ -533,13 +533,18 @@ Socket::DecodeHostAndPort(llvm::StringRef host_and_port,
if (regex_match.GetMatchAtIndex (host_and_port.data(), 1, host_str) &&
regex_match.GetMatchAtIndex (host_and_port.data(), 2, port_str))
{
port = StringConvert::ToSInt32 (port_str.c_str(), INT32_MIN);
if (port != INT32_MIN)
bool ok = false;
port = StringConvert::ToUInt32 (port_str.c_str(), UINT32_MAX, 10, &ok);
if (ok && port < UINT16_MAX)
{
if (error_ptr)
error_ptr->Clear();
return true;
}
// port is too large
if (error_ptr)
error_ptr->SetErrorStringWithFormat("invalid host:port specification: '%s'", host_and_port.data());
return false;
}
}
@ -547,10 +552,13 @@ Socket::DecodeHostAndPort(llvm::StringRef host_and_port,
// a port with an empty host.
host_str.clear();
port_str.clear();
port = StringConvert::ToSInt32(host_and_port.data(), INT32_MIN);
if (port != INT32_MIN)
bool ok = false;
port = StringConvert::ToUInt32 (host_and_port.data(), UINT32_MAX, 10, &ok);
if (ok && port < UINT16_MAX)
{
port_str = host_and_port;
if (error_ptr)
error_ptr->Clear();
return true;
}
@ -688,7 +696,7 @@ int Socket::SetOption(int level, int option_name, int option_value)
return ::setsockopt(m_socket, level, option_name, option_value_p, sizeof(option_value));
}
uint16_t Socket::GetPortNumber(const NativeSocket& socket)
uint16_t Socket::GetLocalPortNumber(const NativeSocket& socket)
{
// We bound to port zero, so we need to figure out which port we actually bound to
if (socket >= 0)
@ -702,7 +710,47 @@ uint16_t Socket::GetPortNumber(const NativeSocket& socket)
}
// Return the port number that is being used by the socket.
uint16_t Socket::GetPortNumber() const
uint16_t Socket::GetLocalPortNumber() const
{
return GetPortNumber(m_socket);
return GetLocalPortNumber (m_socket);
}
std::string Socket::GetLocalIPAddress () const
{
// We bound to port zero, so we need to figure out which port we actually bound to
if (m_socket >= 0)
{
SocketAddress sock_addr;
socklen_t sock_addr_len = sock_addr.GetMaxLength ();
if (::getsockname (m_socket, sock_addr, &sock_addr_len) == 0)
return sock_addr.GetIPAddress ();
}
return "";
}
uint16_t Socket::GetRemotePortNumber () const
{
if (m_socket >= 0)
{
SocketAddress sock_addr;
socklen_t sock_addr_len = sock_addr.GetMaxLength ();
if (::getpeername (m_socket, sock_addr, &sock_addr_len) == 0)
return sock_addr.GetPort ();
}
return 0;
}
std::string Socket::GetRemoteIPAddress () const
{
// We bound to port zero, so we need to figure out which port we actually bound to
if (m_socket >= 0)
{
SocketAddress sock_addr;
socklen_t sock_addr_len = sock_addr.GetMaxLength ();
if (::getpeername (m_socket, sock_addr, &sock_addr_len) == 0)
return sock_addr.GetIPAddress ();
}
return "";
}

View File

@ -21,6 +21,56 @@
// Other libraries and framework includes
// Project includes
// WindowsXP needs an inet_ntop implementation
#ifdef _WIN32
#ifndef INET6_ADDRSTRLEN // might not be defined in older Windows SDKs
#define INET6_ADDRSTRLEN 46
#endif
// TODO: implement shortened form "::" for runs of zeros
const char* inet_ntop(int af, const void * src,
char * dst, socklen_t size)
{
if (size==0)
{
return nullptr;
}
switch (af)
{
case AF_INET:
{
const char* formatted = inet_ntoa(*static_cast<const in_addr*>(src));
if (formatted && strlen(formatted) < size)
{
strncpy(dst, formatted, size);
return dst;
}
return nullptr;
case AF_INET6:
{
char tmp[INET6_ADDRSTRLEN] = {0};
const uint16_t* src16 = static_cast<const uint16_t*>(src);
int full_size = _snprintf(tmp, sizeof(tmp),
"%x:%x:%x:%x:%x:%x:%x:%x",
ntohs(src16[0]), ntohs(src16[1]), ntohs(src16[2]), ntohs(src16[3]),
ntohs(src16[4]), ntohs(src16[5]), ntohs(src16[6]), ntohs(src16[7])
);
if (full_size < size)
{
strncpy(dst,tmp,size);
return dst;
}
return nullptr;
}
}
return nullptr;
}
#endif
using namespace lldb_private;
//----------------------------------------------------------------------
@ -124,6 +174,26 @@ SocketAddress::SetFamily (sa_family_t family)
#endif
}
std::string
SocketAddress::GetIPAddress () const
{
char str[INET6_ADDRSTRLEN] = {0};
switch (GetFamily())
{
case AF_INET:
if (inet_ntop(GetFamily(), &m_socket_addr.sa_ipv4.sin_addr, str, sizeof(str)))
{
return str;
}
case AF_INET6:
if (inet_ntop(GetFamily(), &m_socket_addr.sa_ipv6.sin6_addr, str, sizeof(str)))
{
return str;
}
}
return "";
}
uint16_t
SocketAddress::GetPort () const
{

View File

@ -274,7 +274,7 @@ ProcessKDP::DoConnectRemote (Stream *strm, const char *remote_url)
if (conn_ap->IsConnected())
{
const Socket& socket = static_cast<const Socket&>(*conn_ap->GetReadObject());
const uint16_t reply_port = socket.GetPortNumber();
const uint16_t reply_port = socket.GetLocalPortNumber();
if (reply_port != 0)
{