1 //===-- RNBSocket.cpp -------------------------------------------*- C++ -*-===//
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
7 //===----------------------------------------------------------------------===//
9 // Created by Greg Clayton on 12/12/07.
11 //===----------------------------------------------------------------------===//
13 #include "RNBSocket.h"
16 #include <arpa/inet.h>
21 #include <netinet/in.h>
22 #include <netinet/tcp.h>
23 #include <sys/event.h>
27 #include "lldb/Host/SocketAddress.h"
33 rnb_err_t
RNBSocket::Listen(const char *listen_host
, uint16_t port
,
34 PortBoundCallback callback
,
35 const void *callback_baton
) {
36 // DNBLogThreadedIf(LOG_RNB_COMM, "%8u RNBSocket::%s called",
37 // (uint32_t)m_timer.ElapsedMicroSeconds(true), __FUNCTION__);
38 // Disconnect without saving errno
42 int queue_id
= kqueue();
44 err
.SetError(errno
, DNBError::MachKernel
);
45 err
.LogThreaded("error: failed to create kqueue.");
49 bool any_addr
= (strcmp(listen_host
, "*") == 0);
51 // If the user wants to allow connections from any address we should create
52 // sockets on all families that can resolve localhost. This will allow us to
53 // listen for IPv6 and IPv4 connections from all addresses if those interfaces
55 const char *local_addr
= any_addr
? "localhost" : listen_host
;
57 std::map
<int, lldb_private::SocketAddress
> sockets
;
58 auto addresses
= lldb_private::SocketAddress::GetAddressInfo(
59 local_addr
, NULL
, AF_UNSPEC
, SOCK_STREAM
, IPPROTO_TCP
);
61 for (auto address
: addresses
) {
62 int sock_fd
= ::socket(address
.GetFamily(), SOCK_STREAM
, IPPROTO_TCP
);
66 SetSocketOption(sock_fd
, SOL_SOCKET
, SO_REUSEADDR
, 1);
68 lldb_private::SocketAddress bind_address
= address
;
70 if(any_addr
|| !bind_address
.IsLocalhost())
71 bind_address
.SetToAnyAddress(bind_address
.GetFamily(), port
);
73 bind_address
.SetPort(port
);
76 ::bind(sock_fd
, &bind_address
.sockaddr(), bind_address
.GetLength());
78 ClosePort(sock_fd
, false);
82 error
= ::listen(sock_fd
, 5);
84 ClosePort(sock_fd
, false);
88 // We were asked to listen on port zero which means we must now read the
89 // actual port that was given to us as port zero is a special code for "find
90 // an open port for me". This will only execute on the first socket created,
91 // subesquent sockets will reuse this port number.
93 socklen_t sa_len
= address
.GetLength();
94 if (getsockname(sock_fd
, &address
.sockaddr(), &sa_len
) == 0)
95 port
= address
.GetPort();
98 sockets
[sock_fd
] = address
;
101 if (sockets
.size() == 0) {
102 err
.SetError(errno
, DNBError::POSIX
);
103 err
.LogThreaded("::listen or ::bind failed");
108 callback(callback_baton
, port
);
110 std::vector
<struct kevent
> events
;
111 events
.resize(sockets
.size());
113 for (auto socket
: sockets
) {
114 EV_SET(&events
[i
++], socket
.first
, EVFILT_READ
, EV_ADD
, 0, 0, 0);
117 bool accept_connection
= false;
119 // Loop until we are happy with our connection
120 while (!accept_connection
) {
122 struct kevent event_list
[4];
127 kevent(queue_id
, events
.data(), events
.size(), event_list
, 4, NULL
);
128 } while (num_events
== -1 &&
129 (errno
== EAGAIN
|| errno
== EWOULDBLOCK
|| errno
== EINTR
));
131 if (num_events
< 0) {
132 err
.SetError(errno
, DNBError::MachKernel
);
133 err
.LogThreaded("error: kevent() failed.");
136 for (int i
= 0; i
< num_events
; ++i
) {
137 auto sock_fd
= event_list
[i
].ident
;
138 auto socket_pair
= sockets
.find(sock_fd
);
139 if (socket_pair
== sockets
.end())
142 lldb_private::SocketAddress
&addr_in
= socket_pair
->second
;
143 lldb_private::SocketAddress accept_addr
;
144 socklen_t sa_len
= accept_addr
.GetMaxLength();
145 m_fd
= ::accept(sock_fd
, &accept_addr
.sockaddr(), &sa_len
);
148 err
.SetError(errno
, DNBError::POSIX
);
149 err
.LogThreaded("error: Socket accept failed.");
152 if (addr_in
.IsAnyAddr())
153 accept_connection
= true;
155 if (accept_addr
== addr_in
)
156 accept_connection
= true;
162 "error: rejecting incoming connection from %s (expecting %s)\n",
163 accept_addr
.GetIPAddress().c_str(),
164 addr_in
.GetIPAddress().c_str());
165 DNBLogThreaded("error: rejecting connection from %s (expecting %s)\n",
166 accept_addr
.GetIPAddress().c_str(),
167 addr_in
.GetIPAddress().c_str());
175 for (auto socket
: sockets
) {
176 int ListenFd
= socket
.first
;
177 ClosePort(ListenFd
, false);
183 // Keep our TCP packets coming without any delays.
184 SetSocketOption(m_fd
, IPPROTO_TCP
, TCP_NODELAY
, 1);
189 rnb_err_t
RNBSocket::Connect(const char *host
, uint16_t port
) {
190 auto result
= rnb_err
;
193 auto addresses
= lldb_private::SocketAddress::GetAddressInfo(
194 host
, NULL
, AF_UNSPEC
, SOCK_STREAM
, IPPROTO_TCP
);
196 for (auto address
: addresses
) {
197 m_fd
= ::socket(address
.GetFamily(), SOCK_STREAM
, IPPROTO_TCP
);
201 // Enable local address reuse
202 SetSocketOption(m_fd
, SOL_SOCKET
, SO_REUSEADDR
, 1);
204 address
.SetPort(port
);
206 if (-1 == ::connect(m_fd
, &address
.sockaddr(), address
.GetLength())) {
210 SetSocketOption(m_fd
, IPPROTO_TCP
, TCP_NODELAY
, 1);
212 result
= rnb_success
;
218 rnb_err_t
RNBSocket::useFD(int fd
) {
220 DNBLogThreadedIf(LOG_RNB_COMM
, "Bad file descriptor passed in.");
229 rnb_err_t
RNBSocket::ConnectToService() {
230 DNBLog("Connecting to com.apple.%s service...", DEBUGSERVER_PROGRAM_NAME
);
231 // Disconnect from any previous connections
233 if (::secure_lockdown_checkin(&m_ld_conn
, NULL
, NULL
) != kLDESuccess
) {
234 DNBLogThreadedIf(LOG_RNB_COMM
,
235 "::secure_lockdown_checkin(&m_fd, NULL, NULL) failed");
237 return rnb_not_connected
;
239 m_fd
= ::lockdown_get_socket(m_ld_conn
);
241 DNBLogThreadedIf(LOG_RNB_COMM
, "::lockdown_get_socket() failed");
242 return rnb_not_connected
;
244 m_fd_from_lockdown
= true;
249 rnb_err_t
RNBSocket::OpenFile(const char *path
) {
251 m_fd
= open(path
, O_RDWR
);
253 err
.SetError(errno
, DNBError::POSIX
);
254 err
.LogThreaded("can't open file '%s'", path
);
255 return rnb_not_connected
;
257 struct termios stdin_termios
;
259 if (::tcgetattr(m_fd
, &stdin_termios
) == 0) {
260 stdin_termios
.c_lflag
&= ~ECHO
; // Turn off echoing
261 stdin_termios
.c_lflag
&= ~ICANON
; // Get one char at a time
262 ::tcsetattr(m_fd
, TCSANOW
, &stdin_termios
);
268 int RNBSocket::SetSocketOption(int fd
, int level
, int option_name
,
270 return ::setsockopt(fd
, level
, option_name
, &option_value
,
271 sizeof(option_value
));
274 rnb_err_t
RNBSocket::Disconnect(bool save_errno
) {
276 if (m_fd_from_lockdown
) {
277 m_fd_from_lockdown
= false;
279 lockdown_disconnect(m_ld_conn
);
283 return ClosePort(m_fd
, save_errno
);
286 rnb_err_t
RNBSocket::Read(std::string
&p
) {
290 // Note that BUF is on the stack so we must be careful to keep any
291 // writes to BUF from overflowing or we'll have security issues.
296 // DNBLogThreadedIf(LOG_RNB_COMM, "%8u RNBSocket::%s calling read()",
297 // (uint32_t)m_timer.ElapsedMicroSeconds(true), __FUNCTION__);
302 bytesread
= read(m_fd
, buf
, sizeof(buf
));
303 } while (bytesread
== -1 &&
304 (errno
== EAGAIN
|| errno
== EWOULDBLOCK
|| errno
== EINTR
));
306 err
.SetError(errno
, DNBError::POSIX
);
308 p
.append(buf
, bytesread
);
310 if (err
.Fail() || DNBLogCheckLogBit(LOG_RNB_COMM
))
311 err
.LogThreaded("::read ( %i, %p, %llu ) => %i", m_fd
, buf
, sizeof(buf
),
312 (uint64_t)bytesread
);
314 // Our port went away - we have to mark this so IsConnected will return the
316 if (bytesread
== 0) {
318 return rnb_not_connected
;
319 } else if (bytesread
== -1) {
323 // Strip spaces from the end of the buffer
324 while (!p
.empty() && isspace(p
[p
.size() - 1]))
325 p
.erase(p
.size() - 1);
327 // Most data in the debugserver packets valid printable characters...
328 DNBLogThreadedIf(LOG_RNB_COMM
, "read: %s", p
.c_str());
332 rnb_err_t
RNBSocket::Write(const void *buffer
, size_t length
) {
337 ssize_t bytessent
= write(m_fd
, buffer
, length
);
339 err
.SetError(errno
, DNBError::POSIX
);
341 if (err
.Fail() || DNBLogCheckLogBit(LOG_RNB_COMM
))
342 err
.LogThreaded("::write ( socket = %i, buffer = %p, length = %llu) => %i",
343 m_fd
, buffer
, length
, (uint64_t)bytessent
);
348 if ((size_t)bytessent
!= length
)
352 LOG_RNB_PACKETS
, "putpkt: %*s", (int)length
,
354 buffer
); // All data is string based in debugserver, so this is safe
355 DNBLogThreadedIf(LOG_RNB_COMM
, "sent: %*s", (int)length
,
356 (const char *)buffer
);
361 rnb_err_t
RNBSocket::ClosePort(int &fd
, bool save_errno
) {
365 close_err
= close(fd
);
368 return close_err
!= 0 ? rnb_err
: rnb_success
;