#include "lldb/Core/ConnectionFileDescriptor.h"
#include "lldb/Host/Config.h"
+#include "lldb/Host/IOObject.h"
#include "lldb/Host/SocketAddress.h"
+#include "lldb/Host/Socket.h"
// C Includes
#include <errno.h>
#include <string.h>
#include <stdlib.h>
#include <sys/types.h>
+
#ifndef LLDB_DISABLE_POSIX
-#include <arpa/inet.h>
-#include <netdb.h>
-#include <netinet/in.h>
-#include <netinet/tcp.h>
-#include <sys/socket.h>
-#include <sys/un.h>
#include <termios.h>
-#include <unistd.h>
-#endif
-#ifdef _WIN32
-#include "lldb/Host/windows/windows.h"
-#include <winsock2.h>
-#include <ws2tcpip.h>
#endif
// C++ Includes
#endif
// Project includes
#include "lldb/lldb-private-log.h"
-#include "lldb/Interpreter/Args.h"
#include "lldb/Core/Communication.h"
#include "lldb/Core/Log.h"
-#include "lldb/Core/RegularExpression.h"
#include "lldb/Core/Timer.h"
#include "lldb/Host/Host.h"
+#include "lldb/Host/Socket.h"
+#include "lldb/Interpreter/Args.h"
using namespace lldb;
using namespace lldb_private;
-static bool
-DecodeHostAndPort (const char *host_and_port,
- std::string &host_str,
- std::string &port_str,
- int32_t& port,
- Error *error_ptr)
-{
- static RegularExpression g_regex ("([^:]+):([0-9]+)");
- RegularExpression::Match regex_match(2);
- if (g_regex.Execute (host_and_port, ®ex_match))
- {
- if (regex_match.GetMatchAtIndex (host_and_port, 1, host_str) &&
- regex_match.GetMatchAtIndex (host_and_port, 2, port_str))
- {
- port = Args::StringToSInt32 (port_str.c_str(), INT32_MIN);
- if (port != INT32_MIN)
- {
- if (error_ptr)
- error_ptr->Clear();
- return true;
- }
- }
- }
- host_str.clear();
- port_str.clear();
- port = INT32_MIN;
- if (error_ptr)
- error_ptr->SetErrorStringWithFormat("invalid host:port specification: '%s'", host_and_port);
- return false;
-}
-
ConnectionFileDescriptor::ConnectionFileDescriptor () :
Connection(),
- m_fd_send (-1),
- m_fd_recv (-1),
- m_fd_send_type (eFDTypeFile),
- m_fd_recv_type (eFDTypeFile),
- m_udp_send_sockaddr (new SocketAddress()),
- m_socket_timeout_usec (0),
m_pipe (),
m_mutex (Mutex::eMutexTypeRecursive),
- m_should_close_fd (false),
- m_shutting_down (false)
+ m_shutting_down (false),
+ m_waiting_for_accept (false)
{
Log *log(lldb_private::GetLogIfAnyCategoriesSet (LIBLLDB_LOG_CONNECTION | LIBLLDB_LOG_OBJECT));
if (log)
ConnectionFileDescriptor::ConnectionFileDescriptor (int fd, bool owns_fd) :
Connection(),
- m_fd_send (fd),
- m_fd_recv (fd),
- m_fd_send_type (eFDTypeFile),
- m_fd_recv_type (eFDTypeFile),
- m_udp_send_sockaddr (new SocketAddress()),
- m_socket_timeout_usec(0),
m_pipe (),
m_mutex (Mutex::eMutexTypeRecursive),
- m_should_close_fd (owns_fd),
- m_shutting_down (false)
+ m_shutting_down (false),
+ m_waiting_for_accept (false)
{
+ m_write_sp.reset(new File(fd, owns_fd));
+ m_read_sp.reset(new File(fd, false));
+
Log *log(lldb_private::GetLogIfAnyCategoriesSet (LIBLLDB_LOG_CONNECTION | LIBLLDB_LOG_OBJECT));
if (log)
log->Printf ("%p ConnectionFileDescriptor::ConnectionFileDescriptor (fd = %i, owns_fd = %i)",
bool
ConnectionFileDescriptor::IsConnected () const
{
- return m_fd_send >= 0 || m_fd_recv >= 0;
+ return (m_read_sp && m_read_sp->IsValid()) || (m_write_sp && m_write_sp->IsValid());
}
ConnectionStatus
{
return ConnectUDP (s + strlen("udp://"), error_ptr);
}
+#ifndef LLDB_DISABLE_POSIX
else if (strstr(s, "fd://") == s)
{
+ if (error_ptr)
+ error_ptr->SetErrorStringWithFormat ("Protocol is not supported on non-posix hosts '%s'", s);
+ return eConnectionStatusError;
// Just passing a native file descriptor within this current process
// that is already opened (possibly from a service or other source).
s += strlen ("fd://");
bool success = false;
- m_fd_send = m_fd_recv = Args::StringToSInt32 (s, -1, 0, &success);
+ int fd = Args::StringToSInt32 (s, -1, 0, &success);
if (success)
{
// get the flags from the file descriptor and making sure it
// isn't a bad fd.
errno = 0;
-#ifndef LLDB_DISABLE_POSIX
- int flags = ::fcntl (m_fd_send, F_GETFL, 0);
-#else
- int flags = -1;
-#endif
+ int flags = ::fcntl (fd, F_GETFL, 0);
if (flags == -1 || errno == EBADF)
{
if (error_ptr)
error_ptr->SetErrorStringWithFormat ("stale file descriptor: %s", s);
- m_fd_send = m_fd_recv = -1;
+ m_read_sp.reset();
+ m_write_sp.reset();
return eConnectionStatusError;
}
else
{
- // 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;
- bool is_socket = GetSocketOption (m_fd_send, SOL_SOCKET, SO_REUSEADDR, resuse) == 0;
- if (is_socket)
- m_fd_send_type = m_fd_recv_type = eFDTypeSocket;
// Don't take ownership of a file descriptor that gets passed
// to us since someone else opened the file descriptor and
// handed it to us.
// option be "owns=1" or "owns=0" or something like this to
// allow us to specify this. For now, we assume we must
// assume we don't own it.
- m_should_close_fd = false;
+
+ std::unique_ptr<Socket> tcp_socket;
+ tcp_socket.reset(new Socket(fd, Socket::ProtocolTcp, 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;
+ bool is_socket = !!tcp_socket->GetOption(SOL_SOCKET, SO_REUSEADDR, resuse);
+ if (is_socket)
+ {
+ m_read_sp = std::move(tcp_socket);
+ m_write_sp = m_read_sp;
+ }
+ else
+ {
+ m_read_sp.reset(new File(fd, false));
+ m_write_sp.reset(new File(fd, false));
+ }
return eConnectionStatusSuccess;
}
}
if (error_ptr)
error_ptr->SetErrorStringWithFormat ("invalid file descriptor: \"fd://%s\"", s);
- m_fd_send = m_fd_recv = -1;
+ m_read_sp.reset();
+ m_write_sp.reset();
return eConnectionStatusError;
}
else if (strstr(s, "file://") == s)
{
// file:///PATH
const char *path = s + strlen("file://");
-#ifndef LLDB_DISABLE_POSIX
+ int fd = -1;
do
{
- m_fd_send = m_fd_recv = ::open (path, O_RDWR);
- } while (m_fd_send == -1 && errno == EINTR);
- if (m_fd_send == -1)
+ fd = ::open (path, O_RDWR);
+ } while (fd == -1 && errno == EINTR);
+
+ if (fd == -1)
{
if (error_ptr)
error_ptr->SetErrorToErrno();
return eConnectionStatusError;
}
- if (::isatty(m_fd_send))
+ if (::isatty(fd))
{
// Set up serial terminal emulation
struct termios options;
- ::tcgetattr (m_fd_send, &options);
+ ::tcgetattr (fd, &options);
// Set port speed to maximum
::cfsetospeed (&options, B115200);
options.c_cc[VMIN] = 1;
options.c_cc[VTIME] = 0;
- ::tcsetattr (m_fd_send, TCSANOW, &options);
+ ::tcsetattr (fd, TCSANOW, &options);
}
- int flags = ::fcntl (m_fd_send, F_GETFL, 0);
+ int flags = ::fcntl (fd, F_GETFL, 0);
if (flags >= 0)
{
if ((flags & O_NONBLOCK) == 0)
{
flags |= O_NONBLOCK;
- ::fcntl (m_fd_send, F_SETFL, flags);
+ ::fcntl (fd, F_SETFL, flags);
}
}
- m_should_close_fd = true;
+ m_read_sp.reset(new File(fd, true));
+ m_write_sp.reset(new File(fd, false));
return eConnectionStatusSuccess;
-#else
- return eConnectionStatusError;
-#endif
}
+#endif
if (error_ptr)
error_ptr->SetErrorStringWithFormat ("unsupported connection URL: '%s'", s);
return eConnectionStatusError;
log->Printf ("%p ConnectionFileDescriptor::Disconnect ()",
static_cast<void*>(this));
- // Reset the port predicate when disconnecting and don't broadcast
- m_port_predicate.SetValue(0, eBroadcastNever);
-
ConnectionStatus status = eConnectionStatusSuccess;
- if (m_fd_send < 0 && m_fd_recv < 0)
+ if (!IsConnected())
{
if (log)
log->Printf ("%p ConnectionFileDescriptor::Disconnect(): Nothing to disconnect",
return eConnectionStatusSuccess;
}
+ if (m_read_sp && m_read_sp->IsValid() && m_read_sp->GetFdType() == IOObject::eFDTypeSocket)
+ static_cast<Socket&>(*m_read_sp).PreDisconnect();
+
// Try to get the ConnectionFileDescriptor's mutex. If we fail, that is quite likely
// because somebody is doing a blocking read on our file descriptor. If that's the case,
// then send the "q" char to the command file channel so the read will wake up and the connection
static_cast<void*>(this), m_pipe.GetWriteFileDescriptor(), result);
}
else if (log)
+ {
log->Printf ("%p ConnectionFileDescriptor::Disconnect(): Couldn't get the lock, but no command pipe is available.",
static_cast<void*>(this));
- locker.Lock (m_mutex);
- }
-
- if (m_should_close_fd == true)
- {
- if (m_fd_send == m_fd_recv)
- {
- status = Close (m_fd_send, m_fd_send_type, error_ptr);
- }
- else
- {
- // File descriptors are the different, close both if needed
- if (m_fd_send >= 0)
- status = Close (m_fd_send, m_fd_send_type, error_ptr);
- if (m_fd_recv >= 0)
- {
- ConnectionStatus recv_status = Close (m_fd_recv, m_fd_recv_type, error_ptr);
- if (status == eConnectionStatusSuccess)
- status = recv_status;
- }
}
+ locker.Lock (m_mutex);
}
- // Now set all our descriptors to invalid values.
-
- m_fd_send = m_fd_recv = -1;
-
- if (status != eConnectionStatusSuccess)
- {
- return status;
- }
+ Error error = m_read_sp->Close();
+ Error error2 = m_write_sp->Close();
+ if (error.Fail() || error2.Fail())
+ status = eConnectionStatusError;
+ if (error_ptr)
+ *error_ptr = error.Fail() ? error : error2;
m_shutting_down = false;
- return eConnectionStatusSuccess;
+ return status;
}
size_t
Error *error_ptr)
{
Log *log(lldb_private::GetLogIfAnyCategoriesSet (LIBLLDB_LOG_CONNECTION));
- if (log)
- log->Printf ("%p ConnectionFileDescriptor::Read () ::read (fd = %i, dst = %p, dst_len = %" PRIu64 ")...",
- static_cast<void*>(this), m_fd_recv,
- static_cast<void*>(dst), static_cast<uint64_t>(dst_len));
Mutex::Locker locker;
bool got_lock = locker.TryLock (m_mutex);
else if (m_shutting_down)
return eConnectionStatusError;
- ssize_t bytes_read = 0;
-
status = BytesAvailable (timeout_usec, error_ptr);
- if (status == eConnectionStatusSuccess)
- {
- do
- {
-#ifndef LLDB_DISABLE_POSIX
- bytes_read = ::read (m_fd_recv, dst, dst_len);
-#else
- switch (m_fd_send_type) {
- case eFDTypeSocket:
- case eFDTypeSocketUDP:
- bytes_read = ::recv (m_fd_recv, (char*)dst, dst_len, 0);
- break;
- default:
- bytes_read = -1;
- break;
-
- }
-
-#endif
- } while (bytes_read < 0 && errno == EINTR);
- }
-
if (status != eConnectionStatusSuccess)
return 0;
Error error;
+ size_t bytes_read = dst_len;
+ error = m_read_sp->Read(dst, bytes_read);
+
+ if (log)
+ {
+ log->Printf("%p ConnectionFileDescriptor::Read() fd = %" PRIu64 ", dst = %p, dst_len = %" PRIu64 ") => %" PRIu64 ", error = %s",
+ static_cast<void*>(this),
+ static_cast<uint64_t>(m_read_sp->GetWaitableHandle()),
+ static_cast<void*>(dst),
+ static_cast<uint64_t>(dst_len),
+ static_cast<uint64_t>(bytes_read),
+ error.AsCString());
+ }
+
if (bytes_read == 0)
{
error.Clear(); // End-of-file. Do not automatically close; pass along for the end-of-file handlers.
status = eConnectionStatusEndOfFile;
}
- else if (bytes_read < 0)
- {
- error.SetErrorToErrno();
- }
- else
- {
- error.Clear();
- }
-
- if (log)
- log->Printf ("%p ConnectionFileDescriptor::Read () ::read (fd = %i, dst = %p, dst_len = %" PRIu64 ") => %" PRIi64 ", error = %s",
- static_cast<void*>(this), m_fd_recv,
- static_cast<void*>(dst), static_cast<uint64_t>(dst_len),
- static_cast<int64_t>(bytes_read), error.AsCString());
if (error_ptr)
*error_ptr = error;
switch (error_value)
{
case EAGAIN: // The file was marked for non-blocking I/O, and no data were ready to be read.
- if (m_fd_recv_type == eFDTypeSocket || m_fd_recv_type == eFDTypeSocketUDP)
+ if (m_read_sp->GetFdType() == IOObject::eFDTypeSocket)
status = eConnectionStatusTimedOut;
else
status = eConnectionStatusSuccess;
Error error;
- ssize_t bytes_sent = 0;
-
- switch (m_fd_send_type)
- {
- case eFDTypeFile: // Other FD requiring read/write
-#ifndef LLDB_DISABLE_POSIX
- do
- {
- bytes_sent = ::write (m_fd_send, src, src_len);
- } while (bytes_sent < 0 && errno == EINTR);
-#endif
- break;
- case eFDTypeSocket: // Socket requiring send/recv
- do
- {
- bytes_sent = ::send (m_fd_send, (char*)src, src_len, 0);
- } while (bytes_sent < 0 && errno == EINTR);
- break;
-
- case eFDTypeSocketUDP: // Unconnected UDP socket requiring sendto/recvfrom
- assert (m_udp_send_sockaddr->GetFamily() != 0);
- do
- {
- bytes_sent = ::sendto (m_fd_send,
- (char*)src,
- src_len,
- 0,
- *m_udp_send_sockaddr,
- m_udp_send_sockaddr->GetLength());
- } while (bytes_sent < 0 && errno == EINTR);
- break;
- }
-
- if (bytes_sent < 0)
- error.SetErrorToErrno ();
- else
- error.Clear ();
+ size_t bytes_sent = src_len;
+ error = m_write_sp->Write(src, bytes_sent);
if (log)
{
- switch (m_fd_send_type)
- {
- case eFDTypeFile: // Other FD requiring read/write
- log->Printf ("%p ConnectionFileDescriptor::Write() ::write (fd = %i, src = %p, src_len = %" PRIu64 ") => %" PRIi64 " (error = %s)",
- static_cast<void*>(this), m_fd_send,
- static_cast<const void*>(src),
- static_cast<uint64_t>(src_len),
- static_cast<int64_t>(bytes_sent),
- error.AsCString());
- break;
-
- case eFDTypeSocket: // Socket requiring send/recv
- log->Printf ("%p ConnectionFileDescriptor::Write() ::send (socket = %i, src = %p, src_len = %" PRIu64 ", flags = 0) => %" PRIi64 " (error = %s)",
- static_cast<void*>(this), m_fd_send,
- static_cast<const void*>(src),
- static_cast<uint64_t>(src_len),
- static_cast<int64_t>(bytes_sent),
- error.AsCString());
- break;
-
- case eFDTypeSocketUDP: // Unconnected UDP socket requiring sendto/recvfrom
- log->Printf ("%p ConnectionFileDescriptor::Write() ::sendto (socket = %i, src = %p, src_len = %" PRIu64 ", flags = 0) => %" PRIi64 " (error = %s)",
- static_cast<void*>(this), m_fd_send,
- static_cast<const void*>(src),
- static_cast<uint64_t>(src_len),
- static_cast<int64_t>(bytes_sent),
- error.AsCString());
- break;
- }
+ log->Printf ("%p ConnectionFileDescriptor::Write(fd = %" PRIu64 ", src = %p, src_len = %" PRIu64 ") => %" PRIu64 " (error = %s)",
+ static_cast<void*>(this),
+ static_cast<uint64_t>(m_write_sp->GetWaitableHandle()),
+ static_cast<const void*>(src),
+ static_cast<uint64_t>(src_len),
+ static_cast<uint64_t>(bytes_sent),
+ error.AsCString());
}
if (error_ptr)
// Don't need to take the mutex here separately since we are only called from Read. If we
// ever get used more generally we will need to lock here as well.
- Log *log(lldb_private::GetLogIfAnyCategoriesSet (LIBLLDB_LOG_CONNECTION));
+ Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_CONNECTION));
if (log)
log->Printf("%p ConnectionFileDescriptor::BytesAvailable (timeout_usec = %u)",
static_cast<void*>(this), timeout_usec);
+
struct timeval *tv_ptr;
struct timeval tv;
if (timeout_usec == UINT32_MAX)
{
- // Infinite wait...
- tv_ptr = NULL;
+ // Inifinite wait...
+ tv_ptr = nullptr;
}
else
{
// Make a copy of the file descriptors to make sure we don't
// have another thread change these values out from under us
// and cause problems in the loop below where like in FS_SET()
- const int data_fd = m_fd_recv;
+ const IOObject::WaitableHandle handle = m_read_sp->GetWaitableHandle();
const int pipe_fd = m_pipe.GetReadFileDescriptor();
- if (data_fd >= 0)
+ if (handle != IOObject::kInvalidHandleValue)
{
const bool have_pipe_fd = pipe_fd >= 0;
#if !defined(__APPLE__) && !defined(_MSC_VER)
- assert (data_fd < FD_SETSIZE);
+ assert (handle < FD_SETSIZE);
if (have_pipe_fd)
assert (pipe_fd < FD_SETSIZE);
#endif
-
- while (data_fd == m_fd_recv)
+ while (handle == m_read_sp->GetWaitableHandle())
{
- const int nfds = std::max<int>(data_fd, pipe_fd) + 1;
+ const int nfds = std::max<int>(handle, pipe_fd) + 1;
#if defined(__APPLE__)
llvm::SmallVector<fd_set, 1> read_fds;
read_fds.resize((nfds/FD_SETSIZE) + 1);
fd_set read_fds;
FD_ZERO (&read_fds);
#endif
- FD_SET (data_fd, FD_SET_DATA(read_fds));
+ FD_SET (handle, FD_SET_DATA(read_fds));
if (have_pipe_fd)
FD_SET (pipe_fd, FD_SET_DATA(read_fds));
{
if (have_pipe_fd)
log->Printf("%p ConnectionFileDescriptor::BytesAvailable() ::select (nfds=%i, fds={%i, %i}, NULL, NULL, timeout=%p)...",
- static_cast<void*>(this), nfds, data_fd, pipe_fd,
+ static_cast<void*>(this), nfds, handle, pipe_fd,
static_cast<void*>(tv_ptr));
else
log->Printf("%p ConnectionFileDescriptor::BytesAvailable() ::select (nfds=%i, fds={%i}, NULL, NULL, timeout=%p)...",
- static_cast<void*>(this), nfds, data_fd,
+ static_cast<void*>(this), nfds, handle,
static_cast<void*>(tv_ptr));
}
{
if (have_pipe_fd)
log->Printf("%p ConnectionFileDescriptor::BytesAvailable() ::select (nfds=%i, fds={%i, %i}, NULL, NULL, timeout=%p) => %d, error = %s",
- static_cast<void*>(this), nfds, data_fd,
+ static_cast<void*>(this), nfds, handle,
pipe_fd, static_cast<void*>(tv_ptr), num_set_fds,
error.AsCString());
else
log->Printf("%p ConnectionFileDescriptor::BytesAvailable() ::select (nfds=%i, fds={%i}, NULL, NULL, timeout=%p) => %d, error = %s",
- static_cast<void*>(this), nfds, data_fd,
+ static_cast<void*>(this), nfds, handle,
static_cast<void*>(tv_ptr), num_set_fds,
error.AsCString());
}
}
else if (num_set_fds > 0)
{
- if (FD_ISSET(data_fd, FD_SET_DATA(read_fds)))
+ if (FD_ISSET(handle, FD_SET_DATA(read_fds)))
return eConnectionStatusSuccess;
if (have_pipe_fd && FD_ISSET(pipe_fd, FD_SET_DATA(read_fds)))
{
error_ptr->SetErrorString("not connected");
return eConnectionStatusLostConnection;
}
-#if 0
-#include <poll.h>
-
-// This ConnectionFileDescriptor::BytesAvailable() uses poll(). poll() should NOT
-// be used on MacOSX as it has all sorts of restrictions on the types of file descriptors
-// that it doesn't support.
-//
-// There may be some systems that properly support poll() that could use this
-// implementation. I will let each system opt into this on their own.
-//
-// PROS:
-// - no restrictions on the fd value that is used
-// CONS:
-// - varies wildly from platform to platform in its implementation restrictions
ConnectionStatus
-ConnectionFileDescriptor::BytesAvailable (uint32_t timeout_usec, Error *error_ptr)
+ConnectionFileDescriptor::NamedSocketAccept (const char *socket_name, Error *error_ptr)
{
- // Don't need to take the mutex here separately since we are only called from Read. If we
- // ever get used more generally we will need to lock here as well.
-
- Log *log(lldb_private::GetLogIfAnyCategoriesSet (LIBLLDB_LOG_CONNECTION));
- if (log)
- log->Printf("%p ConnectionFileDescriptor::BytesAvailable (timeout_usec = %u)", this, timeout_usec);
- int timeout_msec = 0;
- if (timeout_usec == UINT32_MAX)
- {
- // Infinite wait...
- timeout_msec = -1;
- }
- else if (timeout_usec == 0)
- {
- // Return immediately, don't wait
- timeout_msec = 0;
- }
- else
- {
- // Convert usec to msec
- timeout_msec = (timeout_usec + 999) / 1000;
- }
-
- // Make a copy of the file descriptors to make sure we don't
- // have another thread change these values out from under us
- // and cause problems in the loop below where like in FS_SET()
- const int data_fd = m_fd_recv;
- const int pipe_fd = m_pipe.GetReadFileDescriptor();
-
- // Make sure the file descriptor can be used with select as it
- // must be in range
- if (data_fd >= 0)
- {
- const bool have_pipe_fd = pipe_fd >= 0;
- struct pollfd fds[2] =
- {
- { data_fd, POLLIN, 0 },
- { pipe_fd, POLLIN, 0 }
- };
- const int nfds = have_pipe_fd ? 2 : 1;
- Error error;
- while (data_fd == m_fd_recv)
- {
- const int num_set_fds = ::poll (fds, nfds, timeout_msec);
-
- if (num_set_fds < 0)
- error.SetErrorToErrno();
- else
- error.Clear();
-
- if (error_ptr)
- *error_ptr = error;
-
- if (log)
- {
- if (have_pipe_fd)
- log->Printf("%p ConnectionFileDescriptor::BytesAvailable() ::poll (fds={{%i,POLLIN},{%i,POLLIN}}, nfds=%i, timeout_ms=%i) => %d, error = %s\n",
- this,
- data_fd,
- pipe_fd,
- nfds,
- timeout_msec,
- num_set_fds,
- error.AsCString());
- else
- log->Printf("%p ConnectionFileDescriptor::BytesAvailable() ::poll (fds={{%i,POLLIN}}, nfds=%i, timeout_ms=%i) => %d, error = %s\n",
- this,
- data_fd,
- nfds,
- timeout_msec,
- num_set_fds,
- error.AsCString());
- }
-
- if (error.Fail())
- {
- switch (error.GetError())
- {
- case EBADF: // One of the descriptor sets specified an invalid descriptor.
- return eConnectionStatusLostConnection;
-
- case EINVAL: // The specified time limit is invalid. One of its components is negative or too large.
- default: // Other unknown error
- return eConnectionStatusError;
-
- case EAGAIN: // The kernel was (perhaps temporarily) unable to
- // allocate the requested number of file descriptors,
- // or we have non-blocking IO
- case EINTR: // A signal was delivered before the time limit
- // expired and before any of the selected events
- // occurred.
- break; // Lets keep reading to until we timeout
- }
- }
- else if (num_set_fds == 0)
- {
- return eConnectionStatusTimedOut;
- }
- else if (num_set_fds > 0)
- {
- if (fds[0].revents & POLLIN)
- return eConnectionStatusSuccess;
- if (fds[1].revents & POLLIN)
- {
- // We got a command to exit. Read the data from that pipe:
- char buffer[16];
- ssize_t bytes_read;
-
- do
- {
- bytes_read = ::read (pipe_fd, buffer, sizeof(buffer));
- } while (bytes_read < 0 && errno == EINTR);
-
- if (log)
- log->Printf("%p ConnectionFileDescriptor::BytesAvailable() got data: %*s from the command channel.",
- static_cast<void*>(this),
- static_cast<int>(bytes_read), buffer);
-
- return eConnectionStatusEndOfFile;
- }
- }
- }
- }
+ Socket* socket = nullptr;
+ Error error = Socket::UnixDomainAccept(socket_name, socket);
if (error_ptr)
- error_ptr->SetErrorString("not connected");
- return eConnectionStatusLostConnection;
+ *error_ptr = error;
+ m_write_sp.reset(socket);
+ m_read_sp = m_write_sp;
+ return (error.Success()) ? eConnectionStatusSuccess : eConnectionStatusError;
}
-#endif
-
ConnectionStatus
-ConnectionFileDescriptor::Close (int& fd, FDType type, Error *error_ptr)
+ConnectionFileDescriptor::NamedSocketConnect (const char *socket_name, Error *error_ptr)
{
+ Socket* socket = nullptr;
+ Error error = Socket::UnixDomainConnect(socket_name, socket);
if (error_ptr)
- error_ptr->Clear();
- bool success = true;
- // Avoid taking a lock if we can
- if (fd >= 0)
- {
- Mutex::Locker locker (m_mutex);
- // Check the FD after the lock is taken to ensure only one thread
- // can get into the close scope below
- if (fd >= 0)
- {
- Log *log(lldb_private::GetLogIfAnyCategoriesSet (LIBLLDB_LOG_CONNECTION));
- if (log)
- log->Printf ("%p ConnectionFileDescriptor::Close (fd = %i)",
- static_cast<void*>(this), fd);
-#if _WIN32
- if (type != eFDTypeFile)
- success = closesocket(fd) == 0;
- else
-#endif
- success = ::close (fd) == 0;
- // A reference to a FD was passed in, set it to an invalid value
- fd = -1;
- if (!success && error_ptr)
- {
- // Only set the error if we have been asked to since something else
- // might have caused us to try and shut down the connection and may
- // have already set the error.
- error_ptr->SetErrorToErrno();
- }
- }
- }
- if (success)
- return eConnectionStatusSuccess;
- else
- return eConnectionStatusError;
-}
-
-ConnectionStatus
-ConnectionFileDescriptor::NamedSocketAccept (const char *socket_name, Error *error_ptr)
-{
-#ifndef LLDB_DISABLE_POSIX
- ConnectionStatus result = eConnectionStatusError;
- struct sockaddr_un saddr_un;
-
- m_fd_send_type = m_fd_recv_type = eFDTypeSocket;
-
- int listen_socket = ::socket (AF_UNIX, SOCK_STREAM, 0);
- if (listen_socket == -1)
- {
- if (error_ptr)
- error_ptr->SetErrorToErrno();
- return eConnectionStatusError;
- }
-
- saddr_un.sun_family = AF_UNIX;
- ::strncpy(saddr_un.sun_path, socket_name, sizeof(saddr_un.sun_path) - 1);
- saddr_un.sun_path[sizeof(saddr_un.sun_path) - 1] = '\0';
-#if defined(__APPLE__) || defined(__FreeBSD__) || defined(__NetBSD__)
- saddr_un.sun_len = SUN_LEN (&saddr_un);
-#endif
-
- Host::Unlink (socket_name);
- if (::bind (listen_socket, (struct sockaddr *)&saddr_un, SUN_LEN (&saddr_un)) == 0)
- {
- if (::listen (listen_socket, 5) == 0)
- {
- m_fd_send = m_fd_recv = ::accept (listen_socket, NULL, 0);
- if (m_fd_send > 0)
- {
- m_should_close_fd = true;
-
- if (error_ptr)
- error_ptr->Clear();
- result = eConnectionStatusSuccess;
- }
- }
- }
-
- if (result != eConnectionStatusSuccess)
- {
- if (error_ptr)
- error_ptr->SetErrorToErrno();
- }
- // We are done with the listen port
- Close (listen_socket, eFDTypeSocket, NULL);
- return result;
-#else
- return eConnectionStatusError;
-#endif
+ *error_ptr = error;
+ m_write_sp.reset(socket);
+ m_read_sp = m_write_sp;
+ return (error.Success()) ? eConnectionStatusSuccess : eConnectionStatusError;
}
ConnectionStatus
-ConnectionFileDescriptor::NamedSocketConnect (const char *socket_name, Error *error_ptr)
+ConnectionFileDescriptor::SocketListen(const char *s, Error *error_ptr)
{
-#ifndef LLDB_DISABLE_POSIX
- Disconnect (NULL);
- m_fd_send_type = m_fd_recv_type = eFDTypeSocket;
+ m_port_predicate.SetValue(0, eBroadcastNever);
- // Open the socket that was passed in as an option
- struct sockaddr_un saddr_un;
- m_fd_send = m_fd_recv = ::socket (AF_UNIX, SOCK_STREAM, 0);
- if (m_fd_send == -1)
- {
- if (error_ptr)
- error_ptr->SetErrorToErrno();
+ Socket* socket = nullptr;
+ m_waiting_for_accept = true;
+ Error error = Socket::TcpListen(s, socket, &m_port_predicate);
+ if (error_ptr)
+ *error_ptr = error;
+ if (error.Fail())
return eConnectionStatusError;
- }
- saddr_un.sun_family = AF_UNIX;
- ::strncpy(saddr_un.sun_path, socket_name, sizeof(saddr_un.sun_path) - 1);
- saddr_un.sun_path[sizeof(saddr_un.sun_path) - 1] = '\0';
-#if defined(__APPLE__) || defined(__FreeBSD__) || defined(__NetBSD__)
- saddr_un.sun_len = SUN_LEN (&saddr_un);
-#endif
+ std::unique_ptr<Socket> listening_socket_up;
- if (::connect (m_fd_send, (struct sockaddr *)&saddr_un, SUN_LEN (&saddr_un)) < 0)
- {
- if (error_ptr)
- error_ptr->SetErrorToErrno();
- Disconnect (NULL);
- return eConnectionStatusError;
- }
+ listening_socket_up.reset(socket);
+ socket = nullptr;
+ error = listening_socket_up->BlockingAccept(s, socket);
+ listening_socket_up.reset();
if (error_ptr)
- error_ptr->Clear();
- return eConnectionStatusSuccess;
-#else
- return eConnectionStatusError;
-#endif
-}
-
-ConnectionStatus
-ConnectionFileDescriptor::SocketListen (const char *host_and_port, Error *error_ptr)
-{
- Log *log(lldb_private::GetLogIfAnyCategoriesSet (LIBLLDB_LOG_CONNECTION));
- if (log)
- log->Printf ("%p ConnectionFileDescriptor::SocketListen (%s)",
- static_cast<void*>(this), host_and_port);
-
- Disconnect (NULL);
- m_fd_send_type = m_fd_recv_type = eFDTypeSocket;
- std::string host_str;
- std::string port_str;
- int32_t port = INT32_MIN;
- if (!DecodeHostAndPort (host_and_port, host_str, port_str, port, error_ptr))
- {
- // Might be just a port number
- port = Args::StringToSInt32(host_and_port, -1);
- if (port == -1)
- return eConnectionStatusError;
- else
- host_str.clear();
- }
- const sa_family_t family = AF_INET;
- const int socktype = SOCK_STREAM;
- const int protocol = IPPROTO_TCP;
- int listen_fd = ::socket (family, socktype, protocol);
- if (listen_fd == -1)
- {
- if (error_ptr)
- error_ptr->SetErrorToErrno();
+ *error_ptr = error;
+ if (error.Fail())
return eConnectionStatusError;
- }
-
- // enable local address reuse
- SetSocketOption (listen_fd, SOL_SOCKET, SO_REUSEADDR, 1);
-
- 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))
- {
- if (error_ptr)
- error_ptr->SetErrorStringWithFormat("unable to resolve hostname '%s'", host_str.c_str());
- Close (listen_fd, eFDTypeSocket, NULL);
- return eConnectionStatusError;
- }
- }
- SocketAddress anyaddr;
- if (anyaddr.SetToAnyAddress (family, port))
- {
- int err = ::bind (listen_fd, anyaddr, anyaddr.GetLength());
- if (err == -1)
- {
- if (error_ptr)
- error_ptr->SetErrorToErrno();
- Close (listen_fd, eFDTypeSocket, NULL);
- return eConnectionStatusError;
- }
-
- err = ::listen (listen_fd, 1);
- if (err == -1)
- {
- if (error_ptr)
- error_ptr->SetErrorToErrno();
- Close (listen_fd, eFDTypeSocket, NULL);
- return eConnectionStatusError;
- }
-
- // 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)
- port = GetSocketPort(listen_fd);
-
- // Set the port predicate since when doing a listen://<host>:<port>
- // it often needs to accept the incoming connection which is a blocking
- // system call. Allowing access to the bound port using a predicate allows
- // us to wait for the port predicate to be set to a non-zero value from
- // another thread in an efficient manor.
- m_port_predicate.SetValue(port, eBroadcastAlways);
-
-
- bool accept_connection = false;
-
- // 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 fd = ::accept (listen_fd, (struct sockaddr *)&accept_addr, &accept_addr_len);
-
- if (fd == -1)
- {
- if (error_ptr)
- error_ptr->SetErrorToErrno();
- break;
- }
-
- if (listen_addr.sockaddr_in().sin_addr.s_addr == INADDR_ANY)
- {
- accept_connection = true;
- m_fd_send = m_fd_recv = fd;
- }
- else
- {
- if (
-#if !(defined(__linux__) || (defined(_WIN32)))
- accept_addr_len == listen_addr.sockaddr_in().sin_len &&
-#endif
- accept_addr.sin_addr.s_addr == listen_addr.sockaddr_in().sin_addr.s_addr)
- {
- accept_connection = true;
- m_fd_send = m_fd_recv = fd;
- }
- else
- {
- ::close (fd);
- m_fd_send = m_fd_recv = -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.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]);
- }
- }
- }
-
- if (m_fd_send == -1)
- {
- Close (listen_fd, eFDTypeSocket, NULL);
- return eConnectionStatusError;
- }
- }
-
- // We are done with the listen port
- Close (listen_fd, eFDTypeSocket, NULL);
-
- m_should_close_fd = true;
-
- // Keep our TCP packets coming without any delays.
- SetSocketOption (m_fd_send, IPPROTO_TCP, TCP_NODELAY, 1);
- if (error_ptr)
- error_ptr->Clear();
- return eConnectionStatusSuccess;
+ m_write_sp.reset(socket);
+ m_read_sp = m_write_sp;
+ return (error.Success()) ? eConnectionStatusSuccess : eConnectionStatusError;
}
ConnectionStatus
-ConnectionFileDescriptor::ConnectTCP (const char *host_and_port, Error *error_ptr)
+ConnectionFileDescriptor::ConnectTCP(const char *s, Error *error_ptr)
{
- Log *log(lldb_private::GetLogIfAnyCategoriesSet (LIBLLDB_LOG_CONNECTION));
- if (log)
- log->Printf ("%p ConnectionFileDescriptor::ConnectTCP (host/port = %s)",
- static_cast<void*>(this), host_and_port);
- Disconnect (NULL);
-
- m_fd_send_type = m_fd_recv_type = eFDTypeSocket;
- std::string host_str;
- std::string port_str;
- int32_t port = INT32_MIN;
- if (!DecodeHostAndPort (host_and_port, host_str, port_str, port, error_ptr))
- return eConnectionStatusError;
-
- // Create the socket
- m_fd_send = m_fd_recv = ::socket (AF_INET, SOCK_STREAM, IPPROTO_TCP);
- if (m_fd_send == -1)
- {
- if (error_ptr)
- error_ptr->SetErrorToErrno();
- return eConnectionStatusError;
- }
-
- m_should_close_fd = true;
-
- // Enable local address reuse
- SetSocketOption (m_fd_send, SOL_SOCKET, SO_REUSEADDR, 1);
-
- struct sockaddr_in sa;
- ::memset (&sa, 0, sizeof (sa));
- sa.sin_family = AF_INET;
- sa.sin_port = htons (port);
-
- int inet_pton_result = ::inet_pton (AF_INET, 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 (AF_INET, host_str.c_str(), &sa.sin_addr);
- if (inet_pton_result <= 0)
- {
-
- if (error_ptr)
- {
- if (inet_pton_result == -1)
- error_ptr->SetErrorToErrno();
- else
- error_ptr->SetErrorStringWithFormat("invalid host string: '%s'", host_str.c_str());
- }
- Disconnect (NULL);
-
- return eConnectionStatusError;
- }
- }
-
- if (-1 == ::connect (m_fd_send, (const struct sockaddr *)&sa, sizeof(sa)))
- {
- if (error_ptr)
- error_ptr->SetErrorToErrno();
- Disconnect (NULL);
-
- return eConnectionStatusError;
- }
-
- // Keep our TCP packets coming without any delays.
- SetSocketOption (m_fd_send, IPPROTO_TCP, TCP_NODELAY, 1);
+ Socket* socket = nullptr;
+ Error error = Socket::TcpConnect(s, socket);
if (error_ptr)
- error_ptr->Clear();
- return eConnectionStatusSuccess;
+ *error_ptr = error;
+ m_write_sp.reset(socket);
+ m_read_sp = m_write_sp;
+ return (error.Success()) ? eConnectionStatusSuccess : eConnectionStatusError;
}
ConnectionStatus
-ConnectionFileDescriptor::ConnectUDP (const char *host_and_port, Error *error_ptr)
+ConnectionFileDescriptor::ConnectUDP(const char *s, Error *error_ptr)
{
- Log *log(lldb_private::GetLogIfAnyCategoriesSet (LIBLLDB_LOG_CONNECTION));
- if (log)
- log->Printf ("%p ConnectionFileDescriptor::ConnectUDP (host/port = %s)",
- static_cast<void*>(this), host_and_port);
- Disconnect (NULL);
-
- m_fd_send_type = m_fd_recv_type = eFDTypeSocketUDP;
-
- std::string host_str;
- std::string port_str;
- int32_t port = INT32_MIN;
- if (!DecodeHostAndPort (host_and_port, host_str, port_str, port, error_ptr))
- return eConnectionStatusError;
-
- // Setup the receiving end of the UDP connection on this localhost
- // on port zero. After we bind to port zero we can read the port.
- m_fd_recv = ::socket (AF_INET, SOCK_DGRAM, 0);
- if (m_fd_recv == -1)
- {
- // Socket creation failed...
- if (error_ptr)
- error_ptr->SetErrorToErrno();
- }
- else
- {
- // Socket was created, now lets bind to the requested port
- SocketAddress addr;
- addr.SetToAnyAddress (AF_INET, 0);
-
- if (::bind (m_fd_recv, addr, addr.GetLength()) == -1)
- {
- // Bind failed...
- if (error_ptr)
- error_ptr->SetErrorToErrno();
- Disconnect (NULL);
- }
- }
-
- if (m_fd_recv == -1)
- return eConnectionStatusError;
-
- // At this point we have setup the receive port, now we need to
- // setup the UDP send socket
-
- struct addrinfo hints;
- struct addrinfo *service_info_list = NULL;
-
- ::memset (&hints, 0, sizeof(hints));
- hints.ai_family = AF_INET;
- hints.ai_socktype = SOCK_DGRAM;
- int err = ::getaddrinfo (host_str.c_str(), port_str.c_str(), &hints, &service_info_list);
- if (err != 0)
- {
- if (error_ptr)
- error_ptr->SetErrorStringWithFormat("getaddrinfo(%s, %s, &hints, &info) returned error %i (%s)",
- host_str.c_str(),
- port_str.c_str(),
- err,
- gai_strerror(err));
- Disconnect (NULL);
- return eConnectionStatusError;
- }
-
- for (struct addrinfo *service_info_ptr = service_info_list;
- service_info_ptr != NULL;
- service_info_ptr = service_info_ptr->ai_next)
- {
- m_fd_send = ::socket (service_info_ptr->ai_family,
- service_info_ptr->ai_socktype,
- service_info_ptr->ai_protocol);
-
- if (m_fd_send != -1)
- {
- *m_udp_send_sockaddr = service_info_ptr;
- break;
- }
- else
- continue;
- }
-
- :: freeaddrinfo (service_info_list);
-
- if (m_fd_send == -1)
- {
- Disconnect (NULL);
- return eConnectionStatusError;
- }
-
+ Socket* send_socket = nullptr;
+ Socket* recv_socket = nullptr;
+ Error error = Socket::UdpConnect(s, send_socket, recv_socket);
if (error_ptr)
- error_ptr->Clear();
-
- m_should_close_fd = true;
- return eConnectionStatusSuccess;
-}
-
-#if defined(_WIN32)
-typedef const char * set_socket_option_arg_type;
-typedef char * get_socket_option_arg_type;
-#else // #if defined(_WIN32)
-typedef const void * set_socket_option_arg_type;
-typedef void * get_socket_option_arg_type;
-#endif // #if defined(_WIN32)
-
-int
-ConnectionFileDescriptor::GetSocketOption(int fd, int level, int option_name, int &option_value)
-{
- get_socket_option_arg_type option_value_p = reinterpret_cast<get_socket_option_arg_type>(&option_value);
- socklen_t option_value_size = sizeof(int);
- return ::getsockopt(fd, level, option_name, option_value_p, &option_value_size);
-}
-
-int
-ConnectionFileDescriptor::SetSocketOption(int fd, int level, int option_name, int option_value)
-{
- set_socket_option_arg_type option_value_p = reinterpret_cast<get_socket_option_arg_type>(&option_value);
- return ::setsockopt(fd, level, option_name, option_value_p, sizeof(option_value));
-}
-
-bool
-ConnectionFileDescriptor::SetSocketReceiveTimeout (uint32_t timeout_usec)
-{
- switch (m_fd_recv_type)
- {
- case eFDTypeFile: // Other FD requiring read/write
- break;
-
- case eFDTypeSocket: // Socket requiring send/recv
- case eFDTypeSocketUDP: // Unconnected UDP socket requiring sendto/recvfrom
- {
- // Check in case timeout for m_fd has already been set to this value
- if (timeout_usec == m_socket_timeout_usec)
- return true;
- //printf ("ConnectionFileDescriptor::SetSocketReceiveTimeout (timeout_usec = %u)\n", timeout_usec);
-
- struct timeval timeout;
- if (timeout_usec == UINT32_MAX)
- {
- timeout.tv_sec = 0;
- timeout.tv_usec = 0;
- }
- else if (timeout_usec == 0)
- {
- // Sending in zero does an infinite timeout, so set this as low
- // as we can go to get an effective zero timeout...
- timeout.tv_sec = 0;
- timeout.tv_usec = 1;
- }
- else
- {
- timeout.tv_sec = timeout_usec / TimeValue::MicroSecPerSec;
- timeout.tv_usec = timeout_usec % TimeValue::MicroSecPerSec;
- }
- if (::setsockopt (m_fd_recv, SOL_SOCKET, SO_RCVTIMEO, reinterpret_cast<get_socket_option_arg_type>(&timeout), sizeof(timeout)) == 0)
- {
- m_socket_timeout_usec = timeout_usec;
- return true;
- }
- }
- }
- return false;
-}
-
-uint16_t
-ConnectionFileDescriptor::GetSocketPort (int fd)
-{
- // We bound to port zero, so we need to figure out which port we actually bound to
- if (fd >= 0)
- {
- SocketAddress sock_addr;
- socklen_t sock_addr_len = sock_addr.GetMaxLength ();
- if (::getsockname (fd, sock_addr, &sock_addr_len) == 0)
- return sock_addr.GetPort ();
- }
- return 0;
-}
-
-// If the read file descriptor is a socket, then return
-// the port number that is being used by the socket.
-uint16_t
-ConnectionFileDescriptor::GetReadPort () const
-{
- return ConnectionFileDescriptor::GetSocketPort (m_fd_recv);
+ *error_ptr = error;
+ m_write_sp.reset(send_socket);
+ m_read_sp.reset(recv_socket);
+ return (error.Success()) ? eConnectionStatusSuccess : eConnectionStatusError;
}
-// If the write file descriptor is a socket, then return
-// the port number that is being used by the socket.
-uint16_t
-ConnectionFileDescriptor::GetWritePort () const
-{
- return ConnectionFileDescriptor::GetSocketPort (m_fd_send);
-}
-uint16_t
-ConnectionFileDescriptor::GetBoundPort (uint32_t timeout_sec)
+uint16_t ConnectionFileDescriptor::GetListeningPort(uint32_t timeout_sec)
{
uint16_t bound_port = 0;
if (timeout_sec == UINT32_MAX)
--- /dev/null
+//===-- Socket.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/Socket.h"
+
+#include "lldb/Core/Log.h"
+#include "lldb/Core/RegularExpression.h"
+#include "lldb/Host/Config.h"
+#include "lldb/Host/Host.h"
+#include "lldb/Host/SocketAddress.h"
+#include "lldb/Host/TimeValue.h"
+#include "lldb/Interpreter/Args.h"
+
+#ifndef LLDB_DISABLE_POSIX
+#include <arpa/inet.h>
+#include <netdb.h>
+#include <netinet/in.h>
+#include <netinet/tcp.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#endif
+
+using namespace lldb;
+using namespace lldb_private;
+
+#if defined(_WIN32)
+typedef const char * set_socket_option_arg_type;
+typedef char * get_socket_option_arg_type;
+const NativeSocket Socket::kInvalidSocketValue = INVALID_SOCKET;
+#else // #if defined(_WIN32)
+typedef const void * set_socket_option_arg_type;
+typedef void * get_socket_option_arg_type;
+const NativeSocket Socket::kInvalidSocketValue = -1;
+#endif // #if defined(_WIN32)
+
+Socket::Socket(NativeSocket socket, SocketProtocol protocol, bool should_close)
+ : IOObject(eFDTypeSocket, should_close)
+ , m_protocol(protocol)
+ , m_socket(socket)
+{
+
+}
+
+Socket::~Socket()
+{
+ Close();
+}
+
+Error Socket::TcpConnect(llvm::StringRef host_and_port, Socket *&socket)
+{
+ // Store the result in a unique_ptr in case we error out, the memory will get correctly freed.
+ std::unique_ptr<Socket> final_socket;
+ NativeSocket sock = kInvalidSocketValue;
+ Error error;
+
+ Log *log(lldb_private::GetLogIfAnyCategoriesSet (LIBLLDB_LOG_HOST));
+ if (log)
+ log->Printf ("Socket::TcpConnect (host/port = %s)", host_and_port.data());
+
+ std::string host_str;
+ std::string port_str;
+ int32_t port = INT32_MIN;
+ if (!DecodeHostAndPort (host_and_port, host_str, port_str, port, &error))
+ return error;
+
+ // Create the socket
+ sock = ::socket (AF_INET, SOCK_STREAM, IPPROTO_TCP);
+ if (sock == kInvalidSocketValue)
+ {
+ // TODO: On Windows, use WSAGetLastError().
+ error.SetErrorToErrno();
+ return error;
+ }
+
+ // Since they both refer to the same socket descriptor, arbitrarily choose the send socket to
+ // be the owner.
+ final_socket.reset(new Socket(sock, ProtocolTcp, true));
+
+ // Enable local address reuse
+ final_socket->SetOption(SOL_SOCKET, SO_REUSEADDR, 1);
+
+ struct sockaddr_in sa;
+ ::memset (&sa, 0, sizeof (sa));
+ sa.sin_family = AF_INET;
+ sa.sin_port = htons (port);
+
+ int inet_pton_result = ::inet_pton (AF_INET, 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 (AF_INET, host_str.c_str(), &sa.sin_addr);
+ if (inet_pton_result <= 0)
+ {
+ // TODO: On Windows, use WSAGetLastError()
+ if (inet_pton_result == -1)
+ error.SetErrorToErrno();
+ else
+ error.SetErrorStringWithFormat("invalid host string: '%s'", host_str.c_str());
+
+ return error;
+ }
+ }
+
+ if (-1 == ::connect (sock, (const struct sockaddr *)&sa, sizeof(sa)))
+ {
+ // TODO: On Windows, use WSAGetLastError()
+ error.SetErrorToErrno();
+ return error;
+ }
+
+ // Keep our TCP packets coming without any delays.
+ final_socket->SetOption(IPPROTO_TCP, TCP_NODELAY, 1);
+ error.Clear();
+ socket = final_socket.release();
+ return error;
+}
+
+Error Socket::TcpListen(llvm::StringRef host_and_port, Socket *&socket, Predicate<uint16_t>* predicate)
+{
+ std::unique_ptr<Socket> listen_socket;
+ NativeSocket listen_sock = kInvalidSocketValue;
+ Error error;
+
+ const sa_family_t family = AF_INET;
+ const int socktype = SOCK_STREAM;
+ const int protocol = IPPROTO_TCP;
+ listen_sock = ::socket (family, socktype, protocol);
+ if (listen_sock == kInvalidSocketValue)
+ {
+ error.SetErrorToErrno();
+ return error;
+ }
+
+ listen_socket.reset(new Socket(listen_sock, ProtocolTcp, true));
+
+ // enable local address reuse
+ listen_socket->SetOption(SOL_SOCKET, SO_REUSEADDR, 1);
+
+ Log *log(lldb_private::GetLogIfAnyCategoriesSet (LIBLLDB_LOG_CONNECTION));
+ if (log)
+ log->Printf ("ConnectionFileDescriptor::SocketListen (%s)", host_and_port.data());
+
+ std::string host_str;
+ std::string port_str;
+ int32_t port = INT32_MIN;
+ if (!DecodeHostAndPort (host_and_port, host_str, port_str, port, &error))
+ return error;
+
+ SocketAddress anyaddr;
+ if (anyaddr.SetToAnyAddress (family, port))
+ {
+ int err = ::bind (listen_sock, anyaddr, anyaddr.GetLength());
+ if (err == -1)
+ {
+ // TODO: On Windows, use WSAGetLastError()
+ error.SetErrorToErrno();
+ return error;
+ }
+
+ err = ::listen (listen_sock, 1);
+ if (err == -1)
+ {
+ // TODO: On Windows, use WSAGetLastError()
+ error.SetErrorToErrno();
+ return error;
+ }
+
+ // 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)
+ port = listen_socket->GetPortNumber();
+
+ // Set the port predicate since when doing a listen://<host>:<port>
+ // it often needs to accept the incoming connection which is a blocking
+ // system call. Allowing access to the bound port using a predicate allows
+ // 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);
+
+ socket = listen_socket.release();
+ }
+
+ return error;
+}
+
+Error Socket::BlockingAccept(llvm::StringRef host_and_port, Socket *&socket)
+{
+ Error error;
+ std::string host_str;
+ std::string port_str;
+ int32_t port;
+ if (!DecodeHostAndPort(host_and_port, host_str, port_str, port, &error))
+ return error;
+
+ const sa_family_t family = AF_INET;
+ const int socktype = SOCK_STREAM;
+ 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<Socket> 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 = ::accept (this->GetNativeSocket(), (struct sockaddr *)&accept_addr, &accept_addr_len);
+
+ if (sock == kInvalidSocketValue)
+ {
+ // TODO: On Windows, use WSAGetLastError()
+ error.SetErrorToErrno();
+ 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;
+ // Since both sockets have the same descriptor, arbitrarily choose the send
+ // socket to be the owner.
+ accepted_socket.reset(new Socket(sock, ProtocolTcp, 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->SetOption (IPPROTO_TCP, TCP_NODELAY, 1);
+ error.Clear();
+ socket = accepted_socket.release();
+ return error;
+
+}
+
+Error Socket::UdpConnect(llvm::StringRef host_and_port, Socket *&send_socket, Socket *&recv_socket)
+{
+ std::unique_ptr<Socket> final_send_socket;
+ std::unique_ptr<Socket> final_recv_socket;
+ NativeSocket final_send_fd = kInvalidSocketValue;
+ NativeSocket final_recv_fd = kInvalidSocketValue;
+
+ Log *log(lldb_private::GetLogIfAnyCategoriesSet (LIBLLDB_LOG_CONNECTION));
+ if (log)
+ log->Printf ("Socket::UdpConnect (host/port = %s)", host_and_port.data());
+
+ Error error;
+ std::string host_str;
+ std::string port_str;
+ int32_t port = INT32_MIN;
+ if (!DecodeHostAndPort (host_and_port, host_str, port_str, port, &error))
+ return error;
+
+ // Setup the receiving end of the UDP connection on this localhost
+ // on port zero. After we bind to port zero we can read the port.
+ final_recv_fd = ::socket (AF_INET, SOCK_DGRAM, 0);
+ if (final_recv_fd == kInvalidSocketValue)
+ {
+ // Socket creation failed...
+ // TODO: On Windows, use WSAGetLastError().
+ error.SetErrorToErrno();
+ }
+ else
+ {
+ final_recv_socket.reset(new Socket(final_recv_fd, ProtocolUdp, true));
+
+ // Socket was created, now lets bind to the requested port
+ SocketAddress addr;
+ addr.SetToAnyAddress (AF_INET, 0);
+
+ if (::bind (final_recv_fd, addr, addr.GetLength()) == -1)
+ {
+ // Bind failed...
+ // TODO: On Windows use WSAGetLastError()
+ error.SetErrorToErrno();
+ }
+ }
+
+ assert(error.Fail() == !(final_recv_socket && final_recv_socket->IsValid()));
+ if (error.Fail())
+ return error;
+
+ // At this point we have setup the receive port, now we need to
+ // setup the UDP send socket
+
+ struct addrinfo hints;
+ struct addrinfo *service_info_list = NULL;
+
+ ::memset (&hints, 0, sizeof(hints));
+ hints.ai_family = AF_INET;
+ hints.ai_socktype = SOCK_DGRAM;
+ int err = ::getaddrinfo (host_str.c_str(), port_str.c_str(), &hints, &service_info_list);
+ if (err != 0)
+ {
+ error.SetErrorStringWithFormat("getaddrinfo(%s, %s, &hints, &info) returned error %i (%s)",
+ host_str.c_str(),
+ port_str.c_str(),
+ err,
+ gai_strerror(err));
+ return error;
+ }
+
+ for (struct addrinfo *service_info_ptr = service_info_list;
+ service_info_ptr != NULL;
+ service_info_ptr = service_info_ptr->ai_next)
+ {
+ final_send_fd = ::socket (service_info_ptr->ai_family,
+ service_info_ptr->ai_socktype,
+ service_info_ptr->ai_protocol);
+
+ if (final_send_fd != kInvalidSocketValue)
+ {
+ final_send_socket.reset(new Socket(final_send_fd, ProtocolUdp, true));
+ final_send_socket->m_udp_send_sockaddr = service_info_ptr;
+ break;
+ }
+ else
+ continue;
+ }
+
+ :: freeaddrinfo (service_info_list);
+
+ if (final_send_fd == kInvalidSocketValue)
+ {
+ // TODO: On Windows, use WSAGetLastError().
+ error.SetErrorToErrno();
+ return error;
+ }
+
+ send_socket = final_send_socket.release();
+ recv_socket = final_recv_socket.release();
+ error.Clear();
+ return error;
+}
+
+Error Socket::UnixDomainConnect(llvm::StringRef name, Socket *&socket)
+{
+ Error error;
+#ifndef LLDB_DISABLE_POSIX
+ std::unique_ptr<Socket> final_socket;
+
+ // Open the socket that was passed in as an option
+ struct sockaddr_un saddr_un;
+ int fd = ::socket (AF_UNIX, SOCK_STREAM, 0);
+ if (fd == kInvalidSocketValue)
+ {
+ error.SetErrorToErrno();
+ return error;
+ }
+
+ final_socket.reset(new Socket(fd, ProtocolUnixDomain, true));
+
+ saddr_un.sun_family = AF_UNIX;
+ ::strncpy(saddr_un.sun_path, name.data(), sizeof(saddr_un.sun_path) - 1);
+ saddr_un.sun_path[sizeof(saddr_un.sun_path) - 1] = '\0';
+#if defined(__APPLE__) || defined(__FreeBSD__) || defined(__NetBSD__)
+ saddr_un.sun_len = SUN_LEN (&saddr_un);
+#endif
+
+ if (::connect (fd, (struct sockaddr *)&saddr_un, SUN_LEN (&saddr_un)) < 0)
+ {
+ error.SetErrorToErrno();
+ return error;
+ }
+
+ socket = final_socket.release();
+#else
+ error.SetErrorString("Unix domain sockets are not supported on this platform.");
+#endif
+ return error;
+}
+
+Error Socket::UnixDomainAccept(llvm::StringRef name, Socket *&socket)
+{
+ Error error;
+#ifndef LLDB_DISABLE_POSIX
+ struct sockaddr_un saddr_un;
+ std::unique_ptr<Socket> listen_socket;
+ std::unique_ptr<Socket> final_socket;
+ NativeSocket listen_fd = kInvalidSocketValue;
+ NativeSocket socket_fd = kInvalidSocketValue;
+
+ listen_fd = ::socket (AF_UNIX, SOCK_STREAM, 0);
+ if (listen_fd == kInvalidSocketValue)
+ {
+ error.SetErrorToErrno();
+ return error;
+ }
+
+ listen_socket.reset(new Socket(listen_fd, ProtocolUnixDomain, true));
+
+ saddr_un.sun_family = AF_UNIX;
+ ::strncpy(saddr_un.sun_path, name.data(), sizeof(saddr_un.sun_path) - 1);
+ saddr_un.sun_path[sizeof(saddr_un.sun_path) - 1] = '\0';
+#if defined(__APPLE__) || defined(__FreeBSD__) || defined(__NetBSD__)
+ saddr_un.sun_len = SUN_LEN (&saddr_un);
+#endif
+
+ Host::Unlink (name.data());
+ bool success = false;
+ if (::bind (listen_fd, (struct sockaddr *)&saddr_un, SUN_LEN (&saddr_un)) == 0)
+ {
+ if (::listen (listen_fd, 5) == 0)
+ {
+ socket_fd = ::accept (listen_fd, NULL, 0);
+ if (socket_fd > 0)
+ {
+ final_socket.reset(new Socket(socket_fd, ProtocolUnixDomain, true));
+ success = true;
+ }
+ }
+ }
+
+ if (!success)
+ {
+ error.SetErrorToErrno();
+ return error;
+ }
+ // We are done with the listen port
+ listen_socket.reset();
+
+ socket = final_socket.release();
+#else
+ error.SetErrorString("Unix domain sockets are not supported on this platform.");
+#endif
+ 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 ("([^:]+):([0-9]+)");
+ RegularExpression::Match regex_match(2);
+ if (g_regex.Execute (host_and_port.data(), ®ex_match))
+ {
+ if (regex_match.GetMatchAtIndex (host_and_port.data(), 1, host_str) &&
+ regex_match.GetMatchAtIndex (host_and_port.data(), 2, port_str))
+ {
+ port = Args::StringToSInt32 (port_str.c_str(), INT32_MIN);
+ if (port != INT32_MIN)
+ {
+ if (error_ptr)
+ error_ptr->Clear();
+ return true;
+ }
+ }
+ }
+
+ // If this was unsuccessful, then check if it's simply a signed 32-bit integer, representing
+ // a port with an empty host.
+ host_str.clear();
+ port_str.clear();
+ port = Args::StringToSInt32(host_and_port.data(), INT32_MIN);
+ if (port != INT32_MIN)
+ {
+ port_str = host_and_port;
+ return true;
+ }
+
+ if (error_ptr)
+ error_ptr->SetErrorStringWithFormat("invalid host:port specification: '%s'", host_and_port.data());
+ return false;
+}
+
+IOObject::WaitableHandle Socket::GetWaitableHandle()
+{
+ // TODO: On Windows, use WSAEventSelect
+ return m_socket;
+}
+
+Error Socket::Read (void *buf, size_t &num_bytes)
+{
+ Error error;
+ int bytes_received = 0;
+ do
+ {
+ bytes_received = ::recv (m_socket, static_cast<char *>(buf), num_bytes, 0);
+ // TODO: Use WSAGetLastError on windows.
+ } while (bytes_received < 0 && errno == EINTR);
+
+ if (bytes_received < 0)
+ {
+ error.SetErrorToErrno();
+ num_bytes = 0;
+ }
+ else
+ num_bytes = bytes_received;
+
+ Log *log(lldb_private::GetLogIfAnyCategoriesSet (LIBLLDB_LOG_HOST | LIBLLDB_LOG_COMMUNICATION));
+ if (log)
+ {
+ log->Printf ("%p Socket::Read() (socket = %" PRIu64 ", src = %p, src_len = %" PRIu64 ", flags = 0) => %" PRIi64 " (error = %s)",
+ static_cast<void*>(this),
+ static_cast<uint64_t>(m_socket),
+ buf,
+ static_cast<uint64_t>(num_bytes),
+ static_cast<int64_t>(bytes_received),
+ error.AsCString());
+ }
+
+ return error;
+}
+
+Error Socket::Write (const void *buf, size_t &num_bytes)
+{
+ Error error;
+ int bytes_sent = 0;
+ do
+ {
+ if (m_protocol == ProtocolUdp)
+ {
+ bytes_sent = ::sendto (m_socket,
+ static_cast<const char*>(buf),
+ num_bytes,
+ 0,
+ m_udp_send_sockaddr,
+ m_udp_send_sockaddr.GetLength());
+ }
+ else
+ bytes_sent = ::send (m_socket, static_cast<const char *>(buf), num_bytes, 0);
+ // TODO: Use WSAGetLastError on windows.
+ } while (bytes_sent < 0 && errno == EINTR);
+
+ if (bytes_sent < 0)
+ {
+ // TODO: On Windows, use WSAGEtLastError.
+ error.SetErrorToErrno();
+ num_bytes = 0;
+ }
+ else
+ num_bytes = bytes_sent;
+
+ Log *log(lldb_private::GetLogIfAnyCategoriesSet (LIBLLDB_LOG_HOST));
+ if (log)
+ {
+ log->Printf ("%p Socket::Write() (socket = %" PRIu64 ", src = %p, src_len = %" PRIu64 ", flags = 0) => %" PRIi64 " (error = %s)",
+ static_cast<void*>(this),
+ static_cast<uint64_t>(m_socket),
+ buf,
+ static_cast<uint64_t>(num_bytes),
+ static_cast<int64_t>(bytes_sent),
+ error.AsCString());
+ }
+
+ return error;
+}
+
+Error Socket::PreDisconnect()
+{
+ Error error;
+ return error;
+}
+
+Error Socket::Close()
+{
+ Error error;
+ if (!IsValid() || !m_should_close_fd)
+ return error;
+
+ Log *log(lldb_private::GetLogIfAnyCategoriesSet (LIBLLDB_LOG_CONNECTION));
+ if (log)
+ log->Printf ("%p Socket::Close (fd = %i)", static_cast<void*>(this), m_socket);
+
+#if defined(_WIN32)
+ bool success = !!closesocket(m_socket);
+#else
+ bool success = !!::close (m_socket);
+#endif
+ // A reference to a FD was passed in, set it to an invalid value
+ m_socket = kInvalidSocketValue;
+ if (!success)
+ {
+ // TODO: On Windows, use WSAGetLastError().
+ error.SetErrorToErrno();
+ }
+
+ return error;
+}
+
+
+int Socket::GetOption(int level, int option_name, int &option_value)
+{
+ get_socket_option_arg_type option_value_p = reinterpret_cast<get_socket_option_arg_type>(&option_value);
+ socklen_t option_value_size = sizeof(int);
+ return ::getsockopt(m_socket, level, option_name, option_value_p, &option_value_size);
+}
+
+int Socket::SetOption(int level, int option_name, int option_value)
+{
+ set_socket_option_arg_type option_value_p = reinterpret_cast<get_socket_option_arg_type>(&option_value);
+ return ::setsockopt(m_socket, level, option_name, option_value_p, sizeof(option_value));
+}
+
+uint16_t Socket::GetPortNumber(const NativeSocket& socket)
+{
+ // We bound to port zero, so we need to figure out which port we actually bound to
+ if (socket >= 0)
+ {
+ SocketAddress sock_addr;
+ socklen_t sock_addr_len = sock_addr.GetMaxLength ();
+ if (::getsockname (socket, sock_addr, &sock_addr_len) == 0)
+ return sock_addr.GetPort ();
+ }
+ return 0;
+}
+
+// Return the port number that is being used by the socket.
+uint16_t Socket::GetPortNumber() const
+{
+ return GetPortNumber(m_socket);
+}