forked from OSchip/llvm-project
Update DebugServer to support IPv6 over TCP
Summary: This patch adds IPv6 support to debugserver. It follows a similar pattern to the changes proposed for LLDB/Host except that the listen implementation is only with kqueue(2) because debugserver is only supported on Darwin. Reviewers: jingham, jasonmolenda, clayborg Reviewed By: clayborg Subscribers: mgorny, lldb-commits Differential Revision: https://reviews.llvm.org/D31824 llvm-svn: 300580
This commit is contained in:
parent
31e7c5e89f
commit
d01a2fa38d
|
@ -101,6 +101,7 @@
|
|||
AF48558D1D75127500D19C07 /* StdStringExtractor.cpp in Sources */ = {isa = PBXBuildFile; fileRef = AF48558B1D75126800D19C07 /* StdStringExtractor.cpp */; };
|
||||
AFA3FCA11E39984900218D5E /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 49D404611E39260F00570CDC /* Foundation.framework */; };
|
||||
AFEC3364194A8B0B00FF05C6 /* Genealogy.cpp in Sources */ = {isa = PBXBuildFile; fileRef = AFEC3363194A8B0B00FF05C6 /* Genealogy.cpp */; };
|
||||
D6631CA91E848FE9006A7B11 /* SocketAddress.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D6631CA81E848FE9006A7B11 /* SocketAddress.cpp */; };
|
||||
/* End PBXBuildFile section */
|
||||
|
||||
/* Begin PBXFileReference section */
|
||||
|
@ -219,6 +220,7 @@
|
|||
AF67ABFF0D34604D0022D128 /* PseudoTerminal.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = PseudoTerminal.cpp; sourceTree = "<group>"; };
|
||||
AF67AC000D34604D0022D128 /* PseudoTerminal.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PseudoTerminal.h; sourceTree = "<group>"; };
|
||||
AFEC3363194A8B0B00FF05C6 /* Genealogy.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Genealogy.cpp; sourceTree = "<group>"; };
|
||||
D6631CA81E848FE9006A7B11 /* SocketAddress.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = SocketAddress.cpp; path = ../../source/Host/common/SocketAddress.cpp; sourceTree = "<group>"; };
|
||||
ED128B7918E1F163003F6A7B /* libpmenergy.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libpmenergy.dylib; path = usr/lib/libpmenergy.dylib; sourceTree = SDKROOT; };
|
||||
ED128B7A18E1F163003F6A7B /* libpmsample.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libpmsample.dylib; path = usr/lib/libpmsample.dylib; sourceTree = SDKROOT; };
|
||||
EF88788B0D9C7558001831DA /* com.apple.debugserver.applist.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = com.apple.debugserver.applist.plist; sourceTree = "<group>"; };
|
||||
|
@ -251,6 +253,7 @@
|
|||
08FB7794FE84155DC02AAC07 /* dbgnub */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
D6631CA81E848FE9006A7B11 /* SocketAddress.cpp */,
|
||||
26ACA3330D3E94F200A2120B /* Framework */,
|
||||
26C637D50C71334A0024798E /* source */,
|
||||
1AB674ADFE9D54B511CA2CBB /* Products */,
|
||||
|
@ -577,6 +580,7 @@
|
|||
isa = PBXSourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
D6631CA91E848FE9006A7B11 /* SocketAddress.cpp in Sources */,
|
||||
26CE05A7115C360D0022F371 /* DNBError.cpp in Sources */,
|
||||
26CE05A8115C36170022F371 /* DNBThreadResumeActions.cpp in Sources */,
|
||||
26CE05A9115C36250022F371 /* debugserver.cpp in Sources */,
|
||||
|
|
|
@ -62,6 +62,7 @@ set(lldbDebugserverCommonSources
|
|||
StdStringExtractor.cpp
|
||||
# JSON reader depends on the following LLDB-common files
|
||||
${LLDB_SOURCE_DIR}/source/Host/common/StringConvert.cpp
|
||||
${LLDB_SOURCE_DIR}/source/Host/common/SocketAddress.cpp
|
||||
# end JSON reader dependencies
|
||||
libdebugserver.cpp
|
||||
PseudoTerminal.cpp
|
||||
|
|
|
@ -17,10 +17,15 @@
|
|||
#include <arpa/inet.h>
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <map>
|
||||
#include <netdb.h>
|
||||
#include <netinet/in.h>
|
||||
#include <netinet/tcp.h>
|
||||
#include <sys/event.h>
|
||||
#include <termios.h>
|
||||
#include <vector>
|
||||
|
||||
#include "lldb/Host/SocketAddress.h"
|
||||
|
||||
#ifdef WITH_LOCKDOWN
|
||||
#include "lockdown.h"
|
||||
|
@ -66,176 +71,160 @@ rnb_err_t RNBSocket::Listen(const char *listen_host, uint16_t port,
|
|||
// Disconnect without saving errno
|
||||
Disconnect(false);
|
||||
|
||||
// Now figure out the hostname that will be attaching and palce it into
|
||||
struct sockaddr_in listen_addr;
|
||||
::memset(&listen_addr, 0, sizeof listen_addr);
|
||||
listen_addr.sin_len = sizeof listen_addr;
|
||||
listen_addr.sin_family = AF_INET;
|
||||
listen_addr.sin_port = htons(port);
|
||||
listen_addr.sin_addr.s_addr = INADDR_ANY;
|
||||
|
||||
if (!ResolveIPV4HostName(listen_host, listen_addr.sin_addr.s_addr)) {
|
||||
DNBLogThreaded("error: failed to resolve connecting host '%s'",
|
||||
listen_host);
|
||||
return rnb_err;
|
||||
}
|
||||
|
||||
DNBError err;
|
||||
int listen_fd = ::socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
|
||||
if (listen_fd == -1)
|
||||
err.SetError(errno, DNBError::POSIX);
|
||||
|
||||
if (err.Fail() || DNBLogCheckLogBit(LOG_RNB_COMM))
|
||||
err.LogThreaded("::socket ( domain = AF_INET, type = SOCK_STREAM, protocol "
|
||||
"= IPPROTO_TCP ) => socket = %i",
|
||||
listen_fd);
|
||||
|
||||
if (err.Fail())
|
||||
return rnb_err;
|
||||
|
||||
// enable local address reuse
|
||||
SetSocketOption(listen_fd, SOL_SOCKET, SO_REUSEADDR, 1);
|
||||
|
||||
struct sockaddr_in sa;
|
||||
::memset(&sa, 0, sizeof sa);
|
||||
sa.sin_len = sizeof sa;
|
||||
sa.sin_family = AF_INET;
|
||||
sa.sin_port = htons(port);
|
||||
sa.sin_addr.s_addr = INADDR_ANY; // Let incoming connections bind to any host
|
||||
// network interface (this is NOT who can
|
||||
// connect to us)
|
||||
int error = ::bind(listen_fd, (struct sockaddr *)&sa, sizeof(sa));
|
||||
if (error == -1)
|
||||
err.SetError(errno, DNBError::POSIX);
|
||||
|
||||
if (err.Fail() || DNBLogCheckLogBit(LOG_RNB_COMM))
|
||||
err.LogThreaded(
|
||||
"::bind ( socket = %i, (struct sockaddr *) &sa, sizeof(sa)) )",
|
||||
listen_fd);
|
||||
|
||||
if (err.Fail()) {
|
||||
ClosePort(listen_fd, false);
|
||||
int queue_id = kqueue();
|
||||
if (queue_id < 0) {
|
||||
err.SetError(errno, DNBError::MachKernel);
|
||||
err.LogThreaded("error: failed to create kqueue.");
|
||||
return rnb_err;
|
||||
}
|
||||
|
||||
error = ::listen(listen_fd, 5);
|
||||
if (error == -1)
|
||||
err.SetError(errno, DNBError::POSIX);
|
||||
std::map<int, lldb_private::SocketAddress> sockets;
|
||||
auto addresses =
|
||||
lldb_private::SocketAddress::GetAddressInfo(listen_host, NULL);
|
||||
|
||||
if (err.Fail() || DNBLogCheckLogBit(LOG_RNB_COMM))
|
||||
err.LogThreaded("::listen ( socket = %i, backlog = 1 )", listen_fd);
|
||||
for (auto address : addresses) {
|
||||
int sock_fd = ::socket(address.GetFamily(), SOCK_STREAM, IPPROTO_TCP);
|
||||
if (sock_fd == -1)
|
||||
continue;
|
||||
|
||||
if (err.Fail()) {
|
||||
ClosePort(listen_fd, false);
|
||||
return rnb_err;
|
||||
}
|
||||
SetSocketOption(sock_fd, SOL_SOCKET, SO_REUSEADDR, 1);
|
||||
|
||||
if (callback) {
|
||||
// We were asked to listen on port zero which means we
|
||||
// must now read the actual port that was given to us
|
||||
// as port zero is a special code for "find an open port
|
||||
// for me".
|
||||
if (port == 0) {
|
||||
socklen_t sa_len = sizeof(sa);
|
||||
if (getsockname(listen_fd, (struct sockaddr *)&sa, &sa_len) == 0) {
|
||||
port = ntohs(sa.sin_port);
|
||||
callback(callback_baton, port);
|
||||
}
|
||||
} else {
|
||||
callback(callback_baton, port);
|
||||
address.SetPort(port);
|
||||
|
||||
int error = ::bind(sock_fd, &address.sockaddr(), address.GetLength());
|
||||
if (error == -1) {
|
||||
ClosePort(sock_fd, false);
|
||||
continue;
|
||||
}
|
||||
|
||||
error = ::listen(sock_fd, 5);
|
||||
if (error == -1) {
|
||||
ClosePort(sock_fd, false);
|
||||
continue;
|
||||
}
|
||||
|
||||
// We were asked to listen on port zero which means we must now read the
|
||||
// actual port that was given to us as port zero is a special code for "find
|
||||
// an open port for me". This will only execute on the first socket created,
|
||||
// subesquent sockets will reuse this port number.
|
||||
if (port == 0) {
|
||||
socklen_t sa_len = address.GetLength();
|
||||
if (getsockname(sock_fd, &address.sockaddr(), &sa_len) == 0)
|
||||
port = address.GetPort();
|
||||
}
|
||||
|
||||
sockets[sock_fd] = address;
|
||||
}
|
||||
|
||||
struct sockaddr_in accept_addr;
|
||||
::memset(&accept_addr, 0, sizeof accept_addr);
|
||||
accept_addr.sin_len = sizeof accept_addr;
|
||||
if (sockets.size() == 0) {
|
||||
err.SetError(errno, DNBError::POSIX);
|
||||
err.LogThreaded("::listen or ::bind failed");
|
||||
return rnb_err;
|
||||
}
|
||||
|
||||
if (callback)
|
||||
callback(callback_baton, port);
|
||||
|
||||
std::vector<struct kevent> events;
|
||||
events.resize(sockets.size());
|
||||
int i = 0;
|
||||
for (auto socket : sockets) {
|
||||
EV_SET(&events[i++], socket.first, EVFILT_READ, EV_ADD, 0, 0, 0);
|
||||
}
|
||||
|
||||
bool accept_connection = false;
|
||||
|
||||
// Loop until we are happy with our connection
|
||||
while (!accept_connection) {
|
||||
socklen_t accept_addr_len = sizeof accept_addr;
|
||||
m_fd =
|
||||
::accept(listen_fd, (struct sockaddr *)&accept_addr, &accept_addr_len);
|
||||
|
||||
if (m_fd == -1)
|
||||
err.SetError(errno, DNBError::POSIX);
|
||||
struct kevent event_list[4];
|
||||
int num_events =
|
||||
kevent(queue_id, events.data(), events.size(), event_list, 4, NULL);
|
||||
|
||||
if (err.Fail() || DNBLogCheckLogBit(LOG_RNB_COMM))
|
||||
err.LogThreaded(
|
||||
"::accept ( socket = %i, address = %p, address_len = %u )", listen_fd,
|
||||
&accept_addr, accept_addr_len);
|
||||
if (num_events < 0) {
|
||||
err.SetError(errno, DNBError::MachKernel);
|
||||
err.LogThreaded("error: kevent() failed.");
|
||||
}
|
||||
|
||||
if (err.Fail())
|
||||
break;
|
||||
for (int i = 0; i < num_events; ++i) {
|
||||
auto sock_fd = event_list[i].ident;
|
||||
auto socket_pair = sockets.find(sock_fd);
|
||||
if (socket_pair == sockets.end())
|
||||
continue;
|
||||
|
||||
if (listen_addr.sin_addr.s_addr == INADDR_ANY)
|
||||
accept_connection = true;
|
||||
else {
|
||||
if (accept_addr_len == listen_addr.sin_len &&
|
||||
accept_addr.sin_addr.s_addr == listen_addr.sin_addr.s_addr) {
|
||||
lldb_private::SocketAddress &addr_in = socket_pair->second;
|
||||
lldb_private::SocketAddress accept_addr;
|
||||
socklen_t sa_len = accept_addr.GetMaxLength();
|
||||
m_fd = ::accept(sock_fd, &accept_addr.sockaddr(), &sa_len);
|
||||
|
||||
if (m_fd == -1) {
|
||||
err.SetError(errno, DNBError::POSIX);
|
||||
err.LogThreaded("error: Socket accept failed.");
|
||||
}
|
||||
|
||||
if (addr_in.IsAnyAddr())
|
||||
accept_connection = true;
|
||||
} else {
|
||||
::close(m_fd);
|
||||
m_fd = -1;
|
||||
const uint8_t *accept_ip =
|
||||
(const uint8_t *)&accept_addr.sin_addr.s_addr;
|
||||
const uint8_t *listen_ip =
|
||||
(const uint8_t *)&listen_addr.sin_addr.s_addr;
|
||||
::fprintf(stderr, "error: rejecting incoming connection from "
|
||||
"%u.%u.%u.%u (expecting %u.%u.%u.%u)\n",
|
||||
accept_ip[0], accept_ip[1], accept_ip[2], accept_ip[3],
|
||||
listen_ip[0], listen_ip[1], listen_ip[2], listen_ip[3]);
|
||||
DNBLogThreaded("error: rejecting connection from %u.%u.%u.%u "
|
||||
"(expecting %u.%u.%u.%u)",
|
||||
accept_ip[0], accept_ip[1], accept_ip[2], accept_ip[3],
|
||||
listen_ip[0], listen_ip[1], listen_ip[2], listen_ip[3]);
|
||||
else {
|
||||
if (accept_addr == addr_in)
|
||||
accept_connection = true;
|
||||
else {
|
||||
::close(m_fd);
|
||||
m_fd = -1;
|
||||
::fprintf(
|
||||
stderr,
|
||||
"error: rejecting incoming connection from %s (expecting %s)\n",
|
||||
accept_addr.GetIPAddress().c_str(),
|
||||
addr_in.GetIPAddress().c_str());
|
||||
DNBLogThreaded("error: rejecting connection from %s (expecting %s)\n",
|
||||
accept_addr.GetIPAddress().c_str(),
|
||||
addr_in.GetIPAddress().c_str());
|
||||
}
|
||||
}
|
||||
}
|
||||
if (err.Fail())
|
||||
break;
|
||||
}
|
||||
for (auto socket : sockets) {
|
||||
int ListenFd = socket.first;
|
||||
ClosePort(ListenFd, false);
|
||||
}
|
||||
|
||||
ClosePort(listen_fd, false);
|
||||
|
||||
if (err.Fail()) {
|
||||
if (err.Fail())
|
||||
return rnb_err;
|
||||
} else {
|
||||
// Keep our TCP packets coming without any delays.
|
||||
SetSocketOption(m_fd, IPPROTO_TCP, TCP_NODELAY, 1);
|
||||
}
|
||||
|
||||
// Keep our TCP packets coming without any delays.
|
||||
SetSocketOption(m_fd, IPPROTO_TCP, TCP_NODELAY, 1);
|
||||
|
||||
return rnb_success;
|
||||
}
|
||||
|
||||
rnb_err_t RNBSocket::Connect(const char *host, uint16_t port) {
|
||||
auto result = rnb_err;
|
||||
Disconnect(false);
|
||||
|
||||
// Create the socket
|
||||
m_fd = ::socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
|
||||
if (m_fd == -1)
|
||||
return rnb_err;
|
||||
auto addresses = lldb_private::SocketAddress::GetAddressInfo(host, NULL);
|
||||
|
||||
// Enable local address reuse
|
||||
SetSocketOption(m_fd, SOL_SOCKET, SO_REUSEADDR, 1);
|
||||
for (auto address : addresses) {
|
||||
m_fd = ::socket(address.GetFamily(), SOCK_STREAM, IPPROTO_TCP);
|
||||
if (m_fd == -1)
|
||||
continue;
|
||||
|
||||
struct sockaddr_in sa;
|
||||
::memset(&sa, 0, sizeof(sa));
|
||||
sa.sin_family = AF_INET;
|
||||
sa.sin_port = htons(port);
|
||||
// Enable local address reuse
|
||||
SetSocketOption(m_fd, SOL_SOCKET, SO_REUSEADDR, 1);
|
||||
|
||||
if (!ResolveIPV4HostName(host, sa.sin_addr.s_addr)) {
|
||||
DNBLogThreaded("error: failed to resolve host '%s'", host);
|
||||
Disconnect(false);
|
||||
return rnb_err;
|
||||
address.SetPort(port);
|
||||
|
||||
if (-1 == ::connect(m_fd, &address.sockaddr(), address.GetLength())) {
|
||||
Disconnect(false);
|
||||
continue;
|
||||
}
|
||||
SetSocketOption(m_fd, IPPROTO_TCP, TCP_NODELAY, 1);
|
||||
|
||||
result = rnb_success;
|
||||
break;
|
||||
}
|
||||
|
||||
if (-1 == ::connect(m_fd, (const struct sockaddr *)&sa, sizeof(sa))) {
|
||||
Disconnect(false);
|
||||
return rnb_err;
|
||||
}
|
||||
|
||||
// Keep our TCP packets coming without any delays.
|
||||
SetSocketOption(m_fd, IPPROTO_TCP, TCP_NODELAY, 1);
|
||||
return rnb_success;
|
||||
return result;
|
||||
}
|
||||
|
||||
rnb_err_t RNBSocket::useFD(int fd) {
|
||||
|
|
|
@ -1345,10 +1345,18 @@ int main(int argc, char *argv[]) {
|
|||
show_usage_and_exit(1);
|
||||
}
|
||||
// accept 'localhost:' prefix on port number
|
||||
|
||||
int items_scanned = ::sscanf(argv[0], "%[^:]:%i", str, &port);
|
||||
if (items_scanned == 2) {
|
||||
host = str;
|
||||
std::string host_specifier = argv[0];
|
||||
auto colon_location = host_specifier.rfind(':');
|
||||
if (colon_location != std::string::npos) {
|
||||
host = host_specifier.substr(0, colon_location);
|
||||
std::string port_str =
|
||||
host_specifier.substr(colon_location + 1, std::string::npos);
|
||||
char *end_ptr;
|
||||
port = strtoul(port_str.c_str(), &end_ptr, 0);
|
||||
if (end_ptr < port_str.c_str() + port_str.size())
|
||||
show_usage_and_exit(2);
|
||||
if (host.front() == '[' && host.back() == ']')
|
||||
host = host.substr(1, host.size() - 2);
|
||||
DNBLogDebug("host = '%s' port = %i", host.c_str(), port);
|
||||
} else {
|
||||
// No hostname means "localhost"
|
||||
|
|
|
@ -53,9 +53,20 @@ static void ServerCallbackv4(const void *baton, in_port_t port) {
|
|||
}
|
||||
|
||||
void TestSocketListen(const char *addr) {
|
||||
// Skip IPv6 tests if there isn't a valid interafce
|
||||
auto addresses = lldb_private::SocketAddress::GetAddressInfo(addr, NULL);
|
||||
if (addresses.size() == 0)
|
||||
return;
|
||||
|
||||
char addr_wrap[256];
|
||||
if (addresses.front().GetFamily() == AF_INET6)
|
||||
sprintf(addr_wrap, "[%s]", addr);
|
||||
else
|
||||
sprintf(addr_wrap, "%s", addr);
|
||||
|
||||
RNBSocket server_socket;
|
||||
auto result =
|
||||
server_socket.Listen(addr, 0, ServerCallbackv4, (const void *)addr);
|
||||
server_socket.Listen(addr, 0, ServerCallbackv4, (const void *)addr_wrap);
|
||||
ASSERT_TRUE(result == rnb_success);
|
||||
result = server_socket.Write(hello.c_str(), hello.length());
|
||||
ASSERT_TRUE(result == rnb_success);
|
||||
|
@ -71,9 +82,19 @@ void TestSocketListen(const char *addr) {
|
|||
|
||||
TEST(RNBSocket, LoopBackListenIPv4) { TestSocketListen("127.0.0.1"); }
|
||||
|
||||
TEST(RNBSocket, LoopBackListenIPv6) { TestSocketListen("::1"); }
|
||||
|
||||
void TestSocketConnect(const char *addr) {
|
||||
// Skip IPv6 tests if there isn't a valid interafce
|
||||
auto addresses = lldb_private::SocketAddress::GetAddressInfo(addr, NULL);
|
||||
if (addresses.size() == 0)
|
||||
return;
|
||||
|
||||
char addr_wrap[256];
|
||||
sprintf(addr_wrap, "%s:0", addr);
|
||||
if (addresses.front().GetFamily() == AF_INET6)
|
||||
sprintf(addr_wrap, "[%s]:0", addr);
|
||||
else
|
||||
sprintf(addr_wrap, "%s:0", addr);
|
||||
|
||||
Socket *server_socket;
|
||||
Predicate<uint16_t> port_predicate;
|
||||
|
@ -131,3 +152,5 @@ void TestSocketConnect(const char *addr) {
|
|||
}
|
||||
|
||||
TEST(RNBSocket, LoopBackConnectIPv4) { TestSocketConnect("127.0.0.1"); }
|
||||
|
||||
TEST(RNBSocket, LoopBackConnectIPv6) { TestSocketConnect("::1"); }
|
||||
|
|
Loading…
Reference in New Issue