[Lldb-commits] [lldb] r300580 - Update DebugServer to support IPv6 over TCP

Chris Bieneman via lldb-commits lldb-commits at lists.llvm.org
Tue Apr 18 13:01:59 PDT 2017


Author: cbieneman
Date: Tue Apr 18 15:01:59 2017
New Revision: 300580

URL: http://llvm.org/viewvc/llvm-project?rev=300580&view=rev
Log:
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

Modified:
    lldb/trunk/tools/debugserver/debugserver.xcodeproj/project.pbxproj
    lldb/trunk/tools/debugserver/source/CMakeLists.txt
    lldb/trunk/tools/debugserver/source/RNBSocket.cpp
    lldb/trunk/tools/debugserver/source/debugserver.cpp
    lldb/trunk/unittests/debugserver/RNBSocketTest.cpp

Modified: lldb/trunk/tools/debugserver/debugserver.xcodeproj/project.pbxproj
URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/tools/debugserver/debugserver.xcodeproj/project.pbxproj?rev=300580&r1=300579&r2=300580&view=diff
==============================================================================
--- lldb/trunk/tools/debugserver/debugserver.xcodeproj/project.pbxproj (original)
+++ lldb/trunk/tools/debugserver/debugserver.xcodeproj/project.pbxproj Tue Apr 18 15:01:59 2017
@@ -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 */,

Modified: lldb/trunk/tools/debugserver/source/CMakeLists.txt
URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/tools/debugserver/source/CMakeLists.txt?rev=300580&r1=300579&r2=300580&view=diff
==============================================================================
--- lldb/trunk/tools/debugserver/source/CMakeLists.txt (original)
+++ lldb/trunk/tools/debugserver/source/CMakeLists.txt Tue Apr 18 15:01:59 2017
@@ -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

Modified: lldb/trunk/tools/debugserver/source/RNBSocket.cpp
URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/tools/debugserver/source/RNBSocket.cpp?rev=300580&r1=300579&r2=300580&view=diff
==============================================================================
--- lldb/trunk/tools/debugserver/source/RNBSocket.cpp (original)
+++ lldb/trunk/tools/debugserver/source/RNBSocket.cpp Tue Apr 18 15:01:59 2017
@@ -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 *
   // 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);
+  DNBError err;
+  int queue_id = kqueue();
+  if (queue_id < 0) {
+    err.SetError(errno, DNBError::MachKernel);
+    err.LogThreaded("error: failed to create kqueue.");
     return rnb_err;
   }
 
-  DNBError err;
-  int listen_fd = ::socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
-  if (listen_fd == -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("::socket ( domain = AF_INET, type = SOCK_STREAM, protocol "
-                    "= IPPROTO_TCP ) => socket = %i",
-                    listen_fd);
+  for (auto address : addresses) {
+    int sock_fd = ::socket(address.GetFamily(), SOCK_STREAM, IPPROTO_TCP);
+    if (sock_fd == -1)
+      continue;
 
-  if (err.Fail())
-    return rnb_err;
+    SetSocketOption(sock_fd, SOL_SOCKET, SO_REUSEADDR, 1);
 
-  // enable local address reuse
-  SetSocketOption(listen_fd, SOL_SOCKET, SO_REUSEADDR, 1);
+    address.SetPort(port);
 
-  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);
+    int error = ::bind(sock_fd, &address.sockaddr(), address.GetLength());
+    if (error == -1) {
+      ClosePort(sock_fd, false);
+      continue;
+    }
 
-  if (err.Fail() || DNBLogCheckLogBit(LOG_RNB_COMM))
-    err.LogThreaded(
-        "::bind ( socket = %i, (struct sockaddr *) &sa, sizeof(sa)) )",
-        listen_fd);
+    error = ::listen(sock_fd, 5);
+    if (error == -1) {
+      ClosePort(sock_fd, false);
+      continue;
+    }
 
-  if (err.Fail()) {
-    ClosePort(listen_fd, false);
-    return rnb_err;
+    // 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;
   }
 
-  error = ::listen(listen_fd, 5);
-  if (error == -1)
+  if (sockets.size() == 0) {
     err.SetError(errno, DNBError::POSIX);
-
-  if (err.Fail() || DNBLogCheckLogBit(LOG_RNB_COMM))
-    err.LogThreaded("::listen ( socket = %i, backlog = 1 )", listen_fd);
-
-  if (err.Fail()) {
-    ClosePort(listen_fd, false);
+    err.LogThreaded("::listen or ::bind failed");
     return rnb_err;
   }
 
-  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);
-    }
-  }
+  if (callback)
+    callback(callback_baton, port);
 
-  struct sockaddr_in accept_addr;
-  ::memset(&accept_addr, 0, sizeof accept_addr);
-  accept_addr.sin_len = sizeof accept_addr;
+  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);
 
-    if (err.Fail() || DNBLogCheckLogBit(LOG_RNB_COMM))
-      err.LogThreaded(
-          "::accept ( socket = %i, address = %p, address_len = %u )", listen_fd,
-          &accept_addr, accept_addr_len);
+    struct kevent event_list[4];
+    int num_events =
+        kevent(queue_id, events.data(), events.size(), event_list, 4, NULL);
+
+    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;
+
+      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 (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) {
+      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);
-
-  if (!ResolveIPV4HostName(host, sa.sin_addr.s_addr)) {
-    DNBLogThreaded("error: failed to resolve host '%s'", host);
-    Disconnect(false);
-    return rnb_err;
-  }
+    // Enable local address reuse
+    SetSocketOption(m_fd, SOL_SOCKET, SO_REUSEADDR, 1);
 
-  if (-1 == ::connect(m_fd, (const struct sockaddr *)&sa, sizeof(sa))) {
-    Disconnect(false);
-    return rnb_err;
-  }
+    address.SetPort(port);
 
-  // Keep our TCP packets coming without any delays.
-  SetSocketOption(m_fd, IPPROTO_TCP, TCP_NODELAY, 1);
-  return rnb_success;
+    if (-1 == ::connect(m_fd, &address.sockaddr(), address.GetLength())) {
+      Disconnect(false);
+      continue;
+    }
+    SetSocketOption(m_fd, IPPROTO_TCP, TCP_NODELAY, 1);
+
+    result = rnb_success;
+    break;
+  }
+  return result;
 }
 
 rnb_err_t RNBSocket::useFD(int fd) {

Modified: lldb/trunk/tools/debugserver/source/debugserver.cpp
URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/tools/debugserver/source/debugserver.cpp?rev=300580&r1=300579&r2=300580&view=diff
==============================================================================
--- lldb/trunk/tools/debugserver/source/debugserver.cpp (original)
+++ lldb/trunk/tools/debugserver/source/debugserver.cpp Tue Apr 18 15:01:59 2017
@@ -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"

Modified: lldb/trunk/unittests/debugserver/RNBSocketTest.cpp
URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/unittests/debugserver/RNBSocketTest.cpp?rev=300580&r1=300579&r2=300580&view=diff
==============================================================================
--- lldb/trunk/unittests/debugserver/RNBSocketTest.cpp (original)
+++ lldb/trunk/unittests/debugserver/RNBSocketTest.cpp Tue Apr 18 15:01:59 2017
@@ -53,9 +53,20 @@ static void ServerCallbackv4(const void
 }
 
 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"); }




More information about the lldb-commits mailing list