beanz created this revision. Herald added a subscriber: emaste. This patch adds IPv6 support to LLDB/Host's TCP socket implementation. Supporting IPv6 involved a few significant changes to the implementation of the socket layers, and I have performed some significant code cleanup along the way.
This patch changes the Socket constructors for all types of sockets to not create sockets until first use. This is required for IPv6 support because the socket type will vary based on the address you are connecting to. This also has the benefit of removing code that could have errors from the Socket subclass constructors (which seems like a win to me). The patch also slightly changes the API and behaviors of the Listen/Accept pattern. Previously both Listen and Accept calls took an address specified as a string. Now only listen does. This change was made because the Listen call can result in opening more than one socket. In order to support listening for both IPv4 and IPv6 connections we need to open one AF_INET socket and one AF_INET6 socket. During the listen call we construct a map of file descriptors to addrin structures which represent the allowable incoming connection address. This map removes the need for taking an address into the Accept call. This does have a change in functionality. Previously you could Listen for connections based on one address, and Accept connections from a different address. This is no longer supported. I could not find anywhere in LLDB where we actually used the APIs in that way. The new API does still support AnyAddr for allowing incoming connections from any address. The Listen implementation is implemented using kqueue on FreeBSD and Darwin, WSAPoll on Windows and poll(2) everywhere else. https://reviews.llvm.org/D31823 Files: include/lldb/Host/Socket.h include/lldb/Host/common/TCPSocket.h include/lldb/Host/common/UDPSocket.h include/lldb/Host/freebsd/Config.h include/lldb/Host/linux/AbstractSocket.h include/lldb/Host/macosx/Config.h include/lldb/Host/posix/DomainSocket.h source/Host/common/Socket.cpp source/Host/common/TCPSocket.cpp source/Host/common/UDPSocket.cpp source/Host/linux/AbstractSocket.cpp source/Host/posix/ConnectionFileDescriptorPosix.cpp source/Host/posix/DomainSocket.cpp source/Plugins/Platform/Android/PlatformAndroidRemoteGDBServer.cpp tools/lldb-server/Acceptor.cpp unittests/Host/SocketTest.cpp unittests/Process/gdb-remote/GDBRemoteTestUtils.cpp
Index: unittests/Process/gdb-remote/GDBRemoteTestUtils.cpp =================================================================== --- unittests/Process/gdb-remote/GDBRemoteTestUtils.cpp +++ unittests/Process/gdb-remote/GDBRemoteTestUtils.cpp @@ -33,15 +33,14 @@ void Connect(GDBRemoteCommunication &client, GDBRemoteCommunication &server) { bool child_processes_inherit = false; Error error; - TCPSocket listen_socket(child_processes_inherit, error); + TCPSocket listen_socket(true, child_processes_inherit); ASSERT_FALSE(error.Fail()); error = listen_socket.Listen("127.0.0.1:0", 5); ASSERT_FALSE(error.Fail()); Socket *accept_socket; std::future<Error> accept_error = std::async(std::launch::async, [&] { - return listen_socket.Accept("127.0.0.1:0", child_processes_inherit, - accept_socket); + return listen_socket.Accept(accept_socket); }); char connect_remote_address[64]; Index: unittests/Host/SocketTest.cpp =================================================================== --- unittests/Host/SocketTest.cpp +++ unittests/Host/SocketTest.cpp @@ -44,8 +44,7 @@ const char *listen_remote_address, bool child_processes_inherit, Socket **accept_socket, Error *error) { - *error = listen_socket->Accept(listen_remote_address, - child_processes_inherit, *accept_socket); + *error = listen_socket->Accept(*accept_socket); } template <typename SocketType> @@ -56,7 +55,7 @@ bool child_processes_inherit = false; Error error; std::unique_ptr<SocketType> listen_socket_up( - new SocketType(child_processes_inherit, error)); + new SocketType(true, child_processes_inherit)); EXPECT_FALSE(error.Fail()); error = listen_socket_up->Listen(listen_remote_address, 5); EXPECT_FALSE(error.Fail()); @@ -70,7 +69,7 @@ std::string connect_remote_address = get_connect_addr(*listen_socket_up); std::unique_ptr<SocketType> connect_socket_up( - new SocketType(child_processes_inherit, error)); + new SocketType(true, child_processes_inherit)); EXPECT_FALSE(error.Fail()); error = connect_socket_up->Connect(connect_remote_address); EXPECT_FALSE(error.Fail()); @@ -141,6 +140,20 @@ EXPECT_STREQ("65535", port_str.c_str()); EXPECT_EQ(65535, port); EXPECT_TRUE(error.Success()); + + EXPECT_TRUE( + Socket::DecodeHostAndPort("[::1]:12345", host_str, port_str, port, &error)); + EXPECT_STREQ("::1", host_str.c_str()); + EXPECT_STREQ("12345", port_str.c_str()); + EXPECT_EQ(12345, port); + EXPECT_TRUE(error.Success()); + + EXPECT_TRUE( + Socket::DecodeHostAndPort("[abcd:12fg:AF58::1]:12345", host_str, port_str, port, &error)); + EXPECT_STREQ("abcd:12fg:AF58::1", host_str.c_str()); + EXPECT_STREQ("12345", port_str.c_str()); + EXPECT_EQ(12345, port); + EXPECT_TRUE(error.Success()); } #ifndef LLDB_DISABLE_POSIX Index: tools/lldb-server/Acceptor.cpp =================================================================== --- tools/lldb-server/Acceptor.cpp +++ tools/lldb-server/Acceptor.cpp @@ -62,8 +62,7 @@ Error Acceptor::Accept(const bool child_processes_inherit, Connection *&conn) { Socket *conn_socket = nullptr; - auto error = m_listener_socket_up->Accept( - StringRef(m_name), child_processes_inherit, conn_socket); + auto error = m_listener_socket_up->Accept(conn_socket); if (error.Success()) conn = new ConnectionFileDescriptor(conn_socket); Index: source/Plugins/Platform/Android/PlatformAndroidRemoteGDBServer.cpp =================================================================== --- source/Plugins/Platform/Android/PlatformAndroidRemoteGDBServer.cpp +++ source/Plugins/Platform/Android/PlatformAndroidRemoteGDBServer.cpp @@ -67,7 +67,7 @@ static Error FindUnusedPort(uint16_t &port) { Error error; - std::unique_ptr<TCPSocket> tcp_socket(new TCPSocket(false, error)); + std::unique_ptr<TCPSocket> tcp_socket(new TCPSocket(true, false)); if (error.Fail()) return error; Index: source/Host/posix/DomainSocket.cpp =================================================================== --- source/Host/posix/DomainSocket.cpp +++ source/Host/posix/DomainSocket.cpp @@ -56,27 +56,32 @@ return true; } -} - -DomainSocket::DomainSocket(NativeSocket socket) - : Socket(socket, ProtocolUnixDomain, true) {} +} // namespace -DomainSocket::DomainSocket(bool child_processes_inherit, Error &error) - : DomainSocket( - CreateSocket(kDomain, kType, 0, child_processes_inherit, error)) {} +DomainSocket::DomainSocket(bool should_close, bool child_processes_inherit) + : Socket(ProtocolUnixDomain, should_close, child_processes_inherit) {} DomainSocket::DomainSocket(SocketProtocol protocol, - bool child_processes_inherit, Error &error) - : Socket(CreateSocket(kDomain, kType, 0, child_processes_inherit, error), - protocol, true) {} + bool child_processes_inherit) + : Socket(protocol, true, child_processes_inherit) {} + +DomainSocket::DomainSocket(NativeSocket socket, + const DomainSocket &listen_socket) + : Socket(ProtocolUnixDomain, listen_socket.m_should_close_fd, + listen_socket.m_child_processes_inherit) { + m_socket = socket; +} Error DomainSocket::Connect(llvm::StringRef name) { sockaddr_un saddr_un; socklen_t saddr_un_len; if (!SetSockAddr(name, GetNameOffset(), &saddr_un, saddr_un_len)) return Error("Failed to set socket address"); Error error; + m_socket = CreateSocket(kDomain, kType, 0, m_child_processes_inherit, error); + if (error.Fail()) + return error; if (::connect(GetNativeSocket(), (struct sockaddr *)&saddr_un, saddr_un_len) < 0) SetLastError(error); @@ -93,6 +98,9 @@ DeleteSocketFile(name); Error error; + m_socket = CreateSocket(kDomain, kType, 0, m_child_processes_inherit, error); + if (error.Fail()) + return error; if (::bind(GetNativeSocket(), (struct sockaddr *)&saddr_un, saddr_un_len) == 0) if (::listen(GetNativeSocket(), backlog) == 0) @@ -102,13 +110,12 @@ return error; } -Error DomainSocket::Accept(llvm::StringRef name, bool child_processes_inherit, - Socket *&socket) { +Error DomainSocket::Accept(Socket *&socket) { Error error; auto conn_fd = AcceptSocket(GetNativeSocket(), nullptr, nullptr, - child_processes_inherit, error); + m_child_processes_inherit, error); if (error.Success()) - socket = new DomainSocket(conn_fd); + socket = new DomainSocket(conn_fd, *this); return error; } Index: source/Host/posix/ConnectionFileDescriptorPosix.cpp =================================================================== --- source/Host/posix/ConnectionFileDescriptorPosix.cpp +++ source/Host/posix/ConnectionFileDescriptorPosix.cpp @@ -218,7 +218,7 @@ // assume we don't own it. std::unique_ptr<TCPSocket> tcp_socket; - tcp_socket.reset(new TCPSocket(fd, false)); + tcp_socket.reset(new TCPSocket(fd, false, false)); // Try and get a socket option from this file descriptor to // see if this is a socket and set m_is_socket accordingly. int resuse; @@ -720,7 +720,7 @@ listening_socket_up.reset(socket); socket = nullptr; - error = listening_socket_up->Accept(s, m_child_processes_inherit, socket); + error = listening_socket_up->Accept(socket); listening_socket_up.reset(); if (error_ptr) *error_ptr = error; Index: source/Host/linux/AbstractSocket.cpp =================================================================== --- source/Host/linux/AbstractSocket.cpp +++ source/Host/linux/AbstractSocket.cpp @@ -14,8 +14,8 @@ using namespace lldb; using namespace lldb_private; -AbstractSocket::AbstractSocket(bool child_processes_inherit, Error &error) - : DomainSocket(ProtocolUnixAbstract, child_processes_inherit, error) {} +AbstractSocket::AbstractSocket(bool child_processes_inherit) + : DomainSocket(ProtocolUnixAbstract, child_processes_inherit) {} size_t AbstractSocket::GetNameOffset() const { return 1; } Index: source/Host/common/UDPSocket.cpp =================================================================== --- source/Host/common/UDPSocket.cpp +++ source/Host/common/UDPSocket.cpp @@ -28,41 +28,31 @@ const int kType = SOCK_DGRAM; static const char *g_not_supported_error = "Not supported"; -} +} // namespace -UDPSocket::UDPSocket(NativeSocket socket) : Socket(socket, ProtocolUdp, true) {} +UDPSocket::UDPSocket(bool should_close, bool child_processes_inherit) + : Socket(ProtocolUdp, should_close, child_processes_inherit) {} -UDPSocket::UDPSocket(bool child_processes_inherit, Error &error) - : UDPSocket( - CreateSocket(kDomain, kType, 0, child_processes_inherit, error)) {} +UDPSocket::UDPSocket(NativeSocket socket, const UDPSocket &listen_socket) + : Socket(ProtocolUdp, listen_socket.m_should_close_fd, + listen_socket.m_child_processes_inherit) { + m_socket = socket; +} size_t UDPSocket::Send(const void *buf, const size_t num_bytes) { return ::sendto(m_socket, static_cast<const char *>(buf), num_bytes, 0, m_sockaddr, m_sockaddr.GetLength()); } Error UDPSocket::Connect(llvm::StringRef name) { - return Error("%s", g_not_supported_error); -} - -Error UDPSocket::Listen(llvm::StringRef name, int backlog) { - return Error("%s", g_not_supported_error); -} - -Error UDPSocket::Accept(llvm::StringRef name, bool child_processes_inherit, - Socket *&socket) { - return Error("%s", g_not_supported_error); -} - -Error UDPSocket::Connect(llvm::StringRef name, bool child_processes_inherit, - Socket *&socket) { - std::unique_ptr<UDPSocket> final_socket; - Log *log(lldb_private::GetLogIfAnyCategoriesSet(LIBLLDB_LOG_CONNECTION)); if (log) log->Printf("UDPSocket::%s (host/port = %s)", __FUNCTION__, name.data()); Error error; + if (error.Fail()) + return error; + std::string host_str; std::string port_str; int32_t port = INT32_MIN; @@ -94,44 +84,68 @@ for (struct addrinfo *service_info_ptr = service_info_list; service_info_ptr != nullptr; service_info_ptr = service_info_ptr->ai_next) { - auto send_fd = CreateSocket( + m_socket = Socket::CreateSocket( service_info_ptr->ai_family, service_info_ptr->ai_socktype, - service_info_ptr->ai_protocol, child_processes_inherit, error); + service_info_ptr->ai_protocol, m_child_processes_inherit, error); if (error.Success()) { - final_socket.reset(new UDPSocket(send_fd)); - final_socket->m_sockaddr = service_info_ptr; + m_sockaddr = service_info_ptr; break; } else continue; } ::freeaddrinfo(service_info_list); - if (!final_socket) + if (IsValid()) return error; SocketAddress bind_addr; // Only bind to the loopback address if we are expecting a connection from // localhost to avoid any firewall issues. - const bool bind_addr_success = (host_str == "127.0.0.1" || host_str == "localhost") - ? bind_addr.SetToLocalhost(kDomain, port) - : bind_addr.SetToAnyAddress(kDomain, port); + const bool bind_addr_success = + (host_str == "127.0.0.1" || host_str == "localhost") + ? bind_addr.SetToLocalhost(kDomain, port) + : bind_addr.SetToAnyAddress(kDomain, port); if (!bind_addr_success) { error.SetErrorString("Failed to get hostspec to bind for"); return error; } bind_addr.SetPort(0); // Let the source port # be determined dynamically - err = ::bind(final_socket->GetNativeSocket(), bind_addr, bind_addr.GetLength()); - - struct sockaddr_in source_info; - socklen_t address_len = sizeof (struct sockaddr_in); - err = ::getsockname(final_socket->GetNativeSocket(), (struct sockaddr *) &source_info, &address_len); + err = ::bind(m_socket, bind_addr, bind_addr.GetLength()); - socket = final_socket.release(); error.Clear(); return error; } + +Error UDPSocket::Listen(llvm::StringRef name, int backlog) { + return Error("%s", g_not_supported_error); +} + +Error UDPSocket::Accept(Socket *&socket) { + return Error("%s", g_not_supported_error); +} + +Error UDPSocket::CreateSocket() { + Error error; + if (IsValid()) + error = Close(); + if (error.Fail()) + return error; + m_socket = + Socket::CreateSocket(kDomain, kType, 0, m_child_processes_inherit, error); + return error; +} + +Error UDPSocket::Connect(llvm::StringRef name, bool child_processes_inherit, + Socket *&socket) { + std::unique_ptr<UDPSocket> final_socket( + new UDPSocket(true, child_processes_inherit)); + Error error = final_socket->Connect(name); + if (!error.Fail()) + socket = final_socket.release(); + return error; +} Index: source/Host/common/TCPSocket.cpp =================================================================== --- source/Host/common/TCPSocket.cpp +++ source/Host/common/TCPSocket.cpp @@ -22,30 +22,57 @@ #include <sys/socket.h> #endif +// If kqueue(2) is available we prefer that over select(2). I believe that we +// only have kqueue(2) on Darwin and FreeBSD. +#if HAVE_SYS_EVENT_H +#include <sys/event.h> +#elif defined(_WIN32) +#include <winsock2.h> +#else +#include <poll.h> +#endif + using namespace lldb; using namespace lldb_private; namespace { - -const int kDomain = AF_INET; const int kType = SOCK_STREAM; } -TCPSocket::TCPSocket(NativeSocket socket, bool should_close) - : Socket(socket, ProtocolTcp, should_close) {} +TCPSocket::TCPSocket(bool should_close, bool child_processes_inherit) + : Socket(ProtocolTcp, should_close, child_processes_inherit) {} + +TCPSocket::TCPSocket(NativeSocket socket, const TCPSocket &listen_socket) + : Socket(ProtocolTcp, listen_socket.m_should_close_fd, + listen_socket.m_child_processes_inherit) { + m_socket = socket; +} + +TCPSocket::TCPSocket(NativeSocket socket, bool should_close, + bool child_processes_inherit) + : Socket(ProtocolTcp, should_close, child_processes_inherit) { + m_socket = socket; +} + +TCPSocket::~TCPSocket() { CloseListenSockets(); } -TCPSocket::TCPSocket(bool child_processes_inherit, Error &error) - : TCPSocket(CreateSocket(kDomain, kType, IPPROTO_TCP, - child_processes_inherit, error), - true) {} +bool TCPSocket::IsValid() const { + return m_socket != kInvalidSocketValue || m_listen_sockets.size() != 0; +} // Return the port number that is being used by the socket. uint16_t TCPSocket::GetLocalPortNumber() const { if (m_socket != kInvalidSocketValue) { SocketAddress sock_addr; socklen_t sock_addr_len = sock_addr.GetMaxLength(); if (::getsockname(m_socket, sock_addr, &sock_addr_len) == 0) return sock_addr.GetPort(); + } else if (!m_listen_sockets.empty()) { + SocketAddress sock_addr; + socklen_t sock_addr_len = sock_addr.GetMaxLength(); + if (::getsockname(m_listen_sockets.begin()->first, sock_addr, + &sock_addr_len) == 0) + return sock_addr.GetPort(); } return 0; } @@ -84,9 +111,18 @@ return ""; } +Error TCPSocket::CreateSocket(int domain) { + Error error; + if (IsValid()) + error = Close(); + if (error.Fail()) + return error; + m_socket = Socket::CreateSocket(domain, kType, IPPROTO_TCP, + m_child_processes_inherit, error); + return error; +} + Error TCPSocket::Connect(llvm::StringRef name) { - if (m_socket == kInvalidSocketValue) - return Error("Invalid socket"); Log *log(lldb_private::GetLogIfAnyCategoriesSet(LIBLLDB_LOG_COMMUNICATION)); if (log) @@ -99,146 +135,199 @@ if (!DecodeHostAndPort(name, host_str, port_str, port, &error)) return error; - struct sockaddr_in sa; - ::memset(&sa, 0, sizeof(sa)); - sa.sin_family = kDomain; - sa.sin_port = htons(port); - - int inet_pton_result = ::inet_pton(kDomain, host_str.c_str(), &sa.sin_addr); - - if (inet_pton_result <= 0) { - struct hostent *host_entry = gethostbyname(host_str.c_str()); - if (host_entry) - host_str = ::inet_ntoa(*(struct in_addr *)*host_entry->h_addr_list); - inet_pton_result = ::inet_pton(kDomain, host_str.c_str(), &sa.sin_addr); - if (inet_pton_result <= 0) { - if (inet_pton_result == -1) - SetLastError(error); - else - error.SetErrorStringWithFormat("invalid host string: '%s'", - host_str.c_str()); + auto addresses = + lldb_private::SocketAddress::GetAddressInfo(host_str.c_str(), NULL); + for (auto address : addresses) { + error = CreateSocket(address.GetFamily()); + if (error.Fail()) + continue; + + address.SetPort(port); - return error; + if (-1 == ::connect(GetNativeSocket(), &address.sockaddr(), + address.GetLength())) { + continue; } - } - if (-1 == - ::connect(GetNativeSocket(), (const struct sockaddr *)&sa, sizeof(sa))) { - SetLastError(error); + SetOptionNoDelay(); + + error.Clear(); return error; } - // Keep our TCP packets coming without any delays. - SetOptionNoDelay(); - error.Clear(); + error.SetErrorString("Failed to connect port"); return error; } Error TCPSocket::Listen(llvm::StringRef name, int backlog) { - Error error; - - // enable local address reuse - SetOptionReuseAddress(); - Log *log(lldb_private::GetLogIfAnyCategoriesSet(LIBLLDB_LOG_CONNECTION)); if (log) log->Printf("TCPSocket::%s (%s)", __FUNCTION__, name.data()); + Error error; std::string host_str; std::string port_str; int32_t port = INT32_MIN; if (!DecodeHostAndPort(name, host_str, port_str, port, &error)) return error; - SocketAddress bind_addr; + auto addresses = + lldb_private::SocketAddress::GetAddressInfo(host_str.c_str(), NULL); + for (auto address : addresses) { + int fd = Socket::CreateSocket(address.GetFamily(), kType, IPPROTO_TCP, + m_child_processes_inherit, error); + if (error.Fail()) { + error.Clear(); + continue; + } - // Only bind to the loopback address if we are expecting a connection from - // localhost to avoid any firewall issues. - const bool bind_addr_success = (host_str == "127.0.0.1") - ? bind_addr.SetToLocalhost(kDomain, port) - : bind_addr.SetToAnyAddress(kDomain, port); + // enable local address reuse + SetOptionReuseAddress(); - if (!bind_addr_success) { - error.SetErrorString("Failed to bind port"); - return error; - } + address.SetPort(port); + + int err = ::bind(fd, &address.sockaddr(), address.GetLength()); + if (-1 != err) + err = ::listen(fd, backlog); - int err = ::bind(GetNativeSocket(), bind_addr, bind_addr.GetLength()); - if (err != -1) - err = ::listen(GetNativeSocket(), backlog); + if (-1 == err) { +#if defined(_WIN32) + closesocket(fd); +#else + ::close(fd); +#endif + continue; + } - if (err == -1) - SetLastError(error); + if (port == 0) { + socklen_t sa_len = address.GetLength(); + if (getsockname(fd, &address.sockaddr(), &sa_len) == 0) + port = address.GetPort(); + } + m_listen_sockets[fd] = address; + } + if (m_listen_sockets.size() == 0) + error.SetErrorString("Failed to connect port"); return error; } -Error TCPSocket::Accept(llvm::StringRef name, bool child_processes_inherit, - Socket *&conn_socket) { +void TCPSocket::CloseListenSockets() { + for (auto socket : m_listen_sockets) +#if defined(_WIN32) + closesocket(socket.first); +#else + ::close(socket.first); +#endif + m_listen_sockets.clear(); +} + +Error TCPSocket::Accept(Socket *&conn_socket) { Error error; - std::string host_str; - std::string port_str; - int32_t port; - if (!DecodeHostAndPort(name, host_str, port_str, port, &error)) + if (m_listen_sockets.size() == 0) { + error.SetErrorString("No open listening sockets!"); return error; - - const sa_family_t family = kDomain; - const int socktype = kType; - const int protocol = IPPROTO_TCP; - SocketAddress listen_addr; - if (host_str.empty()) - listen_addr.SetToLocalhost(family, port); - else if (host_str.compare("*") == 0) - listen_addr.SetToAnyAddress(family, port); - else { - if (!listen_addr.getaddrinfo(host_str.c_str(), port_str.c_str(), family, - socktype, protocol)) { - error.SetErrorStringWithFormat("unable to resolve hostname '%s'", - host_str.c_str()); - return error; - } } bool accept_connection = false; std::unique_ptr<TCPSocket> accepted_socket; +#if HAVE_SYS_EVENT_H + + int queue_id = kqueue(); + if (queue_id < 0) { + error.SetErrorString("error: failed to create kqueue."); + return error; + } + + std::vector<struct kevent> events; + events.resize(m_listen_sockets.size()); + int i = 0; + for (auto socket: m_listen_sockets) { + EV_SET(&events[i++], socket.first, EVFILT_READ, EV_ADD, 0, 0, 0); + } +#else + std::vector<struct pollfd> sock_set; + + int nfds = 0; + for (auto socket : m_listen_sockets) { + struct pollfd sock_fd; + sock_fd.fd = socket.first; + sock_fd.events = POLLIN | POLLERR; + sock_fd.revents = 0; + sock_set.push_back(sock_fd); + } +#endif + // Loop until we are happy with our connection while (!accept_connection) { - struct sockaddr_in accept_addr; - ::memset(&accept_addr, 0, sizeof accept_addr); -#if !(defined(__linux__) || defined(_WIN32)) - accept_addr.sin_len = sizeof accept_addr; + int sock_fd = -1; +#if HAVE_SYS_EVENT_H + struct kevent event_list[4]; + int num_events = + kevent(queue_id, events.data(), events.size(), event_list, 4, NULL); + + if (num_events < 0) { + error.SetErrorString("error: kevent() failed."); + return error; + } + + for (int i = 0; i < num_events; ++i) { + auto socket_pair = m_listen_sockets.find(event_list[i].ident); + if (socket_pair == m_listen_sockets.end()) + continue; + sock_fd = event_list[i].ident; + } +#else + +#if defined(_WIN32) + int retval = WSAPoll(sock_set.data(), sock_set.size(), 1000); +#else + int retval = poll(sock_set.data(), sock_set.size(), 1000); +#endif + if (retval == -1) + break; + if (retval == 0) + continue; + + for (auto socket : sock_set) { + if ((socket.revents & POLLERR) != 0) { + error.SetErrorString("Socket error encountered"); + return error; + } + + if ((socket.revents & POLLIN) == 0) + continue; + sock_fd = socket.fd; + break; + } #endif - socklen_t accept_addr_len = sizeof accept_addr; - int sock = AcceptSocket(GetNativeSocket(), (struct sockaddr *)&accept_addr, - &accept_addr_len, child_processes_inherit, error); + if(-1 == sock_fd) + continue; + lldb_private::SocketAddress AccptAddr; + socklen_t sa_len = AccptAddr.GetMaxLength(); + int sock = AcceptSocket(sock_fd, &AccptAddr.sockaddr(), &sa_len, + m_child_processes_inherit, error); if (error.Fail()) - break; + return error; - bool is_same_addr = true; -#if !(defined(__linux__) || (defined(_WIN32))) - is_same_addr = (accept_addr_len == listen_addr.sockaddr_in().sin_len); + lldb_private::SocketAddress &AddrIn = m_listen_sockets[sock_fd]; + if (!AddrIn.IsAnyAddr() && AccptAddr != AddrIn) { +#if defined(_WIN32) + closesocket(sock); +#else + ::close(sock); #endif - if (is_same_addr) - is_same_addr = (accept_addr.sin_addr.s_addr == - listen_addr.sockaddr_in().sin_addr.s_addr); - - if (is_same_addr || - (listen_addr.sockaddr_in().sin_addr.s_addr == INADDR_ANY)) { - accept_connection = true; - accepted_socket.reset(new TCPSocket(sock, true)); - } else { - const uint8_t *accept_ip = (const uint8_t *)&accept_addr.sin_addr.s_addr; - const uint8_t *listen_ip = - (const uint8_t *)&listen_addr.sockaddr_in().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]); - accepted_socket.reset(); + ::fprintf( + stderr, + "error: rejecting incoming connection from %s (expecting %s)\n", + AccptAddr.GetIPAddress().c_str(), AddrIn.GetIPAddress().c_str()); + continue; } + accept_connection = true; + accepted_socket.reset(new TCPSocket(sock, *this)); } if (!accepted_socket) @@ -248,6 +337,7 @@ accepted_socket->SetOptionNoDelay(); error.Clear(); conn_socket = accepted_socket.release(); + CloseListenSockets(); return error; } Index: source/Host/common/Socket.cpp =================================================================== --- source/Host/common/Socket.cpp +++ source/Host/common/Socket.cpp @@ -67,9 +67,11 @@ } } -Socket::Socket(NativeSocket socket, SocketProtocol protocol, bool should_close) +Socket::Socket(SocketProtocol protocol, bool should_close, + bool child_processes_inherit) : IOObject(eFDTypeSocket, should_close), m_protocol(protocol), - m_socket(socket) {} + m_socket(kInvalidSocketValue), + m_child_processes_inherit(child_processes_inherit) {} Socket::~Socket() { Close(); } @@ -81,22 +83,22 @@ std::unique_ptr<Socket> socket_up; switch (protocol) { case ProtocolTcp: - socket_up.reset(new TCPSocket(child_processes_inherit, error)); + socket_up.reset(new TCPSocket(true, child_processes_inherit)); break; case ProtocolUdp: - socket_up.reset(new UDPSocket(child_processes_inherit, error)); + socket_up.reset(new UDPSocket(true, child_processes_inherit)); break; case ProtocolUnixDomain: #ifndef LLDB_DISABLE_POSIX - socket_up.reset(new DomainSocket(child_processes_inherit, error)); + socket_up.reset(new DomainSocket(true, child_processes_inherit)); #else error.SetErrorString( "Unix domain sockets are not supported on this platform."); #endif break; case ProtocolUnixAbstract: #ifdef __linux__ - socket_up.reset(new AbstractSocket(child_processes_inherit, error)); + socket_up.reset(new AbstractSocket(true, child_processes_inherit)); #else error.SetErrorString( "Abstract domain sockets are not supported on this platform."); @@ -145,7 +147,7 @@ return error; std::unique_ptr<TCPSocket> listen_socket( - new TCPSocket(child_processes_inherit, error)); + new TCPSocket(child_processes_inherit, true)); if (error.Fail()) return error; @@ -208,7 +210,7 @@ if (error.Fail()) return error; - error = listen_socket->Accept(name, child_processes_inherit, socket); + error = listen_socket->Accept(socket); return error; } @@ -240,18 +242,22 @@ if (error.Fail()) return error; - error = listen_socket->Accept(name, child_processes_inherit, socket); + error = listen_socket->Accept(socket); return error; } bool Socket::DecodeHostAndPort(llvm::StringRef host_and_port, std::string &host_str, std::string &port_str, int32_t &port, Error *error_ptr) { - static RegularExpression g_regex(llvm::StringRef("([^:]+):([0-9]+)")); + static RegularExpression g_regex( + llvm::StringRef("([^:]+|\\[[0-9a-fA-F:]+.*\\]):([0-9]+)")); RegularExpression::Match regex_match(2); if (g_regex.Execute(host_and_port, ®ex_match)) { if (regex_match.GetMatchAtIndex(host_and_port.data(), 1, host_str) && regex_match.GetMatchAtIndex(host_and_port.data(), 2, port_str)) { + // IPv6 addresses are wrapped in [] when specified with ports + if (host_str.front() == '[' && host_str.back() == ']') + host_str = host_str.substr(1, host_str.size() - 2); bool ok = false; port = StringConvert::ToUInt32(port_str.c_str(), UINT32_MAX, 10, &ok); if (ok && port <= UINT16_MAX) { @@ -404,12 +410,12 @@ const int protocol, bool child_processes_inherit, Error &error) { error.Clear(); - auto socketType = type; + auto socket_type = type; #ifdef SOCK_CLOEXEC if (!child_processes_inherit) - socketType |= SOCK_CLOEXEC; + socket_type |= SOCK_CLOEXEC; #endif - auto sock = ::socket(domain, socketType, protocol); + auto sock = ::socket(domain, socket_type, protocol); if (sock == kInvalidSocketValue) SetLastError(error); Index: include/lldb/Host/posix/DomainSocket.h =================================================================== --- include/lldb/Host/posix/DomainSocket.h +++ include/lldb/Host/posix/DomainSocket.h @@ -15,22 +15,20 @@ namespace lldb_private { class DomainSocket : public Socket { public: - DomainSocket(bool child_processes_inherit, Error &error); + DomainSocket(bool should_close, bool child_processes_inherit); Error Connect(llvm::StringRef name) override; Error Listen(llvm::StringRef name, int backlog) override; - Error Accept(llvm::StringRef name, bool child_processes_inherit, - Socket *&socket) override; + Error Accept(Socket *&socket) override; protected: - DomainSocket(SocketProtocol protocol, bool child_processes_inherit, - Error &error); + DomainSocket(SocketProtocol protocol, bool child_processes_inherit); virtual size_t GetNameOffset() const; virtual void DeleteSocketFile(llvm::StringRef name); private: - DomainSocket(NativeSocket socket); + DomainSocket(NativeSocket socket, const DomainSocket &listen_socket); }; } Index: include/lldb/Host/macosx/Config.h =================================================================== --- include/lldb/Host/macosx/Config.h +++ include/lldb/Host/macosx/Config.h @@ -14,8 +14,8 @@ // platform functionality availability. //---------------------------------------------------------------------- -#ifndef liblldb_Platform_Config_h_ -#define liblldb_Platform_Config_h_ +#ifndef LLDB_HOST_MACOSX_CONFIG_H +#define LLDB_HOST_MACOSX_CONFIG_H #define LLDB_CONFIG_TERMIOS_SUPPORTED 1 @@ -25,4 +25,7 @@ #define LLDB_CONFIG_FCNTL_GETPATH_SUPPORTED 1 -#endif // #ifndef liblldb_Platform_Config_h_ +// FIXME: This should be set by a configure-time check. +#define HAVE_SYS_EVENT_H 1 + +#endif // LLDB_HOST_MACOSX_CONFIG_H Index: include/lldb/Host/linux/AbstractSocket.h =================================================================== --- include/lldb/Host/linux/AbstractSocket.h +++ include/lldb/Host/linux/AbstractSocket.h @@ -15,7 +15,7 @@ namespace lldb_private { class AbstractSocket : public DomainSocket { public: - AbstractSocket(bool child_processes_inherit, Error &error); + AbstractSocket(bool child_processes_inherit); protected: size_t GetNameOffset() const override; Index: include/lldb/Host/freebsd/Config.h =================================================================== --- include/lldb/Host/freebsd/Config.h +++ include/lldb/Host/freebsd/Config.h @@ -25,4 +25,7 @@ //#define LLDB_CONFIG_FCNTL_GETPATH_SUPPORTED 1 +// FIXME: This should be set by a configure-time check. +#define HAVE_SYS_EVENT_H 1 + #endif // #ifndef liblldb_Platform_Config_h_ Index: include/lldb/Host/common/UDPSocket.h =================================================================== --- include/lldb/Host/common/UDPSocket.h +++ include/lldb/Host/common/UDPSocket.h @@ -15,19 +15,20 @@ namespace lldb_private { class UDPSocket : public Socket { public: - UDPSocket(bool child_processes_inherit, Error &error); + UDPSocket(bool should_close, bool child_processes_inherit); static Error Connect(llvm::StringRef name, bool child_processes_inherit, Socket *&socket); private: - UDPSocket(NativeSocket socket); + UDPSocket(NativeSocket socket, const UDPSocket &listen_socket); size_t Send(const void *buf, const size_t num_bytes) override; Error Connect(llvm::StringRef name) override; Error Listen(llvm::StringRef name, int backlog) override; - Error Accept(llvm::StringRef name, bool child_processes_inherit, - Socket *&socket) override; + Error Accept(Socket *&socket) override; + + Error CreateSocket(); SocketAddress m_sockaddr; }; Index: include/lldb/Host/common/TCPSocket.h =================================================================== --- include/lldb/Host/common/TCPSocket.h +++ include/lldb/Host/common/TCPSocket.h @@ -11,12 +11,16 @@ #define liblldb_TCPSocket_h_ #include "lldb/Host/Socket.h" +#include "lldb/Host/SocketAddress.h" +#include <map> namespace lldb_private { class TCPSocket : public Socket { public: - TCPSocket(NativeSocket socket, bool should_close); - TCPSocket(bool child_processes_inherit, Error &error); + TCPSocket(bool should_close, bool child_processes_inherit); + TCPSocket(NativeSocket socket, bool should_close, + bool child_processes_inherit); + ~TCPSocket(); // returns port number or 0 if error uint16_t GetLocalPortNumber() const; @@ -37,8 +41,18 @@ Error Connect(llvm::StringRef name) override; Error Listen(llvm::StringRef name, int backlog) override; - Error Accept(llvm::StringRef name, bool child_processes_inherit, - Socket *&conn_socket) override; + Error Accept(Socket *&conn_socket) override; + + Error CreateSocket(int domain); + + bool IsValid() const override; + +private: + TCPSocket(NativeSocket socket, const TCPSocket &listen_socket); + + void CloseListenSockets(); + + std::map<int, SocketAddress> m_listen_sockets; }; } Index: include/lldb/Host/Socket.h =================================================================== --- include/lldb/Host/Socket.h +++ include/lldb/Host/Socket.h @@ -57,8 +57,7 @@ virtual Error Connect(llvm::StringRef name) = 0; virtual Error Listen(llvm::StringRef name, int backlog) = 0; - virtual Error Accept(llvm::StringRef name, bool child_processes_inherit, - Socket *&socket) = 0; + virtual Error Accept(Socket *&socket) = 0; // Initialize a Tcp Socket object in listening mode. listen and accept are // implemented @@ -103,7 +102,8 @@ int32_t &port, Error *error_ptr); protected: - Socket(NativeSocket socket, SocketProtocol protocol, bool should_close); + Socket(SocketProtocol protocol, bool should_close, + bool m_child_process_inherit); virtual size_t Send(const void *buf, const size_t num_bytes); @@ -117,6 +117,7 @@ SocketProtocol m_protocol; NativeSocket m_socket; + bool m_child_processes_inherit; }; } // namespace lldb_private
_______________________________________________ lldb-commits mailing list lldb-commits@lists.llvm.org http://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits