//===-- TcpSocket.cpp -------------------------------------------*- C++ -*-===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// #include "lldb/Host/common/TCPSocket.h" #include "lldb/Core/Log.h" #include "lldb/Host/Config.h" #ifndef LLDB_DISABLE_POSIX #include #include #include #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 child_processes_inherit, Error &error) : TCPSocket(CreateSocket(kDomain, kType, IPPROTO_TCP, child_processes_inherit, error), true) { } // 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 (); } return 0; } std::string TCPSocket::GetLocalIPAddress() const { // We bound to port zero, so we need to figure out which port we actually bound to 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.GetIPAddress (); } return ""; } uint16_t TCPSocket::GetRemotePortNumber() const { if (m_socket != kInvalidSocketValue) { 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 TCPSocket::GetRemoteIPAddress () const { // We bound to port zero, so we need to figure out which port we actually bound to if (m_socket != kInvalidSocketValue) { 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 ""; } Error TCPSocket::Connect(llvm::StringRef name) { if (m_socket == kInvalidSocketValue) return Error("Invalid socket"); Log *log(lldb_private::GetLogIfAnyCategoriesSet (LIBLLDB_LOG_COMMUNICATION)); if (log) log->Printf ("TCPSocket::%s (host/port = %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; // Enable local address reuse SetOptionReuseAddress(); 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()); return error; } } if (-1 == ::connect (GetNativeSocket(), (const struct sockaddr *)&sa, sizeof(sa))) { SetLastError (error); return error; } // Keep our TCP packets coming without any delays. SetOptionNoDelay(); error.Clear(); 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()); 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; // 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); if (!bind_addr_success) { error.SetErrorString("Failed to bind port"); return error; } int err = ::bind (GetNativeSocket(), bind_addr, bind_addr.GetLength()); if (err != -1) err = ::listen (GetNativeSocket(), backlog); if (err == -1) SetLastError (error); return error; } Error TCPSocket::Accept(llvm::StringRef name, bool child_processes_inherit, 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)) 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 accepted_socket; // 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; #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 (error.Fail()) break; bool is_same_addr = true; #if !(defined(__linux__) || (defined(_WIN32))) is_same_addr = (accept_addr_len == listen_addr.sockaddr_in().sin_len); #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(); } } if (!accepted_socket) return error; // Keep our TCP packets coming without any delays. accepted_socket->SetOptionNoDelay(); error.Clear(); conn_socket = accepted_socket.release(); return error; } int TCPSocket::SetOptionNoDelay() { return SetOption (IPPROTO_TCP, TCP_NODELAY, 1); } int TCPSocket::SetOptionReuseAddress() { return SetOption(SOL_SOCKET, SO_REUSEADDR, 1); }