xref: /openbsd-src/gnu/llvm/lldb/tools/debugserver/source/RNBSocket.cpp (revision be691f3bb6417f04a68938fadbcaee2d5795e764)
1061da546Spatrick //===-- RNBSocket.cpp -------------------------------------------*- C++ -*-===//
2061da546Spatrick //
3061da546Spatrick // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4061da546Spatrick // See https://llvm.org/LICENSE.txt for license information.
5061da546Spatrick // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6061da546Spatrick //
7061da546Spatrick //===----------------------------------------------------------------------===//
8061da546Spatrick //
9061da546Spatrick //  Created by Greg Clayton on 12/12/07.
10061da546Spatrick //
11061da546Spatrick //===----------------------------------------------------------------------===//
12061da546Spatrick 
13061da546Spatrick #include "RNBSocket.h"
14061da546Spatrick #include "DNBError.h"
15061da546Spatrick #include "DNBLog.h"
16061da546Spatrick #include <arpa/inet.h>
17*be691f3bSpatrick #include <cerrno>
18061da546Spatrick #include <fcntl.h>
19061da546Spatrick #include <map>
20061da546Spatrick #include <netdb.h>
21061da546Spatrick #include <netinet/in.h>
22061da546Spatrick #include <netinet/tcp.h>
23061da546Spatrick #include <sys/event.h>
24061da546Spatrick #include <termios.h>
25061da546Spatrick #include <vector>
26061da546Spatrick 
27061da546Spatrick #include "lldb/Host/SocketAddress.h"
28061da546Spatrick 
29061da546Spatrick #ifdef WITH_LOCKDOWN
30061da546Spatrick #include "lockdown.h"
31061da546Spatrick #endif
32061da546Spatrick 
Listen(const char * listen_host,uint16_t port,PortBoundCallback callback,const void * callback_baton)33061da546Spatrick rnb_err_t RNBSocket::Listen(const char *listen_host, uint16_t port,
34061da546Spatrick                             PortBoundCallback callback,
35061da546Spatrick                             const void *callback_baton) {
36061da546Spatrick   // DNBLogThreadedIf(LOG_RNB_COMM, "%8u RNBSocket::%s called",
37061da546Spatrick   // (uint32_t)m_timer.ElapsedMicroSeconds(true), __FUNCTION__);
38061da546Spatrick   // Disconnect without saving errno
39061da546Spatrick   Disconnect(false);
40061da546Spatrick 
41061da546Spatrick   DNBError err;
42061da546Spatrick   int queue_id = kqueue();
43061da546Spatrick   if (queue_id < 0) {
44061da546Spatrick     err.SetError(errno, DNBError::MachKernel);
45061da546Spatrick     err.LogThreaded("error: failed to create kqueue.");
46061da546Spatrick     return rnb_err;
47061da546Spatrick   }
48061da546Spatrick 
49061da546Spatrick   bool any_addr = (strcmp(listen_host, "*") == 0);
50061da546Spatrick 
51061da546Spatrick   // If the user wants to allow connections from any address we should create
52061da546Spatrick   // sockets on all families that can resolve localhost. This will allow us to
53061da546Spatrick   // listen for IPv6 and IPv4 connections from all addresses if those interfaces
54061da546Spatrick   // are available.
55061da546Spatrick   const char *local_addr = any_addr ? "localhost" : listen_host;
56061da546Spatrick 
57061da546Spatrick   std::map<int, lldb_private::SocketAddress> sockets;
58061da546Spatrick   auto addresses = lldb_private::SocketAddress::GetAddressInfo(
59061da546Spatrick       local_addr, NULL, AF_UNSPEC, SOCK_STREAM, IPPROTO_TCP);
60061da546Spatrick 
61061da546Spatrick   for (auto address : addresses) {
62061da546Spatrick     int sock_fd = ::socket(address.GetFamily(), SOCK_STREAM, IPPROTO_TCP);
63061da546Spatrick     if (sock_fd == -1)
64061da546Spatrick       continue;
65061da546Spatrick 
66061da546Spatrick     SetSocketOption(sock_fd, SOL_SOCKET, SO_REUSEADDR, 1);
67061da546Spatrick 
68061da546Spatrick     lldb_private::SocketAddress bind_address = address;
69061da546Spatrick 
70061da546Spatrick     if(any_addr || !bind_address.IsLocalhost())
71061da546Spatrick       bind_address.SetToAnyAddress(bind_address.GetFamily(), port);
72061da546Spatrick     else
73061da546Spatrick       bind_address.SetPort(port);
74061da546Spatrick 
75061da546Spatrick     int error =
76061da546Spatrick         ::bind(sock_fd, &bind_address.sockaddr(), bind_address.GetLength());
77061da546Spatrick     if (error == -1) {
78061da546Spatrick       ClosePort(sock_fd, false);
79061da546Spatrick       continue;
80061da546Spatrick     }
81061da546Spatrick 
82061da546Spatrick     error = ::listen(sock_fd, 5);
83061da546Spatrick     if (error == -1) {
84061da546Spatrick       ClosePort(sock_fd, false);
85061da546Spatrick       continue;
86061da546Spatrick     }
87061da546Spatrick 
88061da546Spatrick     // We were asked to listen on port zero which means we must now read the
89061da546Spatrick     // actual port that was given to us as port zero is a special code for "find
90061da546Spatrick     // an open port for me". This will only execute on the first socket created,
91061da546Spatrick     // subesquent sockets will reuse this port number.
92061da546Spatrick     if (port == 0) {
93061da546Spatrick       socklen_t sa_len = address.GetLength();
94061da546Spatrick       if (getsockname(sock_fd, &address.sockaddr(), &sa_len) == 0)
95061da546Spatrick         port = address.GetPort();
96061da546Spatrick     }
97061da546Spatrick 
98061da546Spatrick     sockets[sock_fd] = address;
99061da546Spatrick   }
100061da546Spatrick 
101061da546Spatrick   if (sockets.size() == 0) {
102061da546Spatrick     err.SetError(errno, DNBError::POSIX);
103061da546Spatrick     err.LogThreaded("::listen or ::bind failed");
104061da546Spatrick     return rnb_err;
105061da546Spatrick   }
106061da546Spatrick 
107061da546Spatrick   if (callback)
108061da546Spatrick     callback(callback_baton, port);
109061da546Spatrick 
110061da546Spatrick   std::vector<struct kevent> events;
111061da546Spatrick   events.resize(sockets.size());
112061da546Spatrick   int i = 0;
113061da546Spatrick   for (auto socket : sockets) {
114061da546Spatrick     EV_SET(&events[i++], socket.first, EVFILT_READ, EV_ADD, 0, 0, 0);
115061da546Spatrick   }
116061da546Spatrick 
117061da546Spatrick   bool accept_connection = false;
118061da546Spatrick 
119061da546Spatrick   // Loop until we are happy with our connection
120061da546Spatrick   while (!accept_connection) {
121061da546Spatrick 
122061da546Spatrick     struct kevent event_list[4];
123061da546Spatrick     int num_events =
124061da546Spatrick         kevent(queue_id, events.data(), events.size(), event_list, 4, NULL);
125061da546Spatrick 
126061da546Spatrick     if (num_events < 0) {
127061da546Spatrick       err.SetError(errno, DNBError::MachKernel);
128061da546Spatrick       err.LogThreaded("error: kevent() failed.");
129061da546Spatrick     }
130061da546Spatrick 
131061da546Spatrick     for (int i = 0; i < num_events; ++i) {
132061da546Spatrick       auto sock_fd = event_list[i].ident;
133061da546Spatrick       auto socket_pair = sockets.find(sock_fd);
134061da546Spatrick       if (socket_pair == sockets.end())
135061da546Spatrick         continue;
136061da546Spatrick 
137061da546Spatrick       lldb_private::SocketAddress &addr_in = socket_pair->second;
138061da546Spatrick       lldb_private::SocketAddress accept_addr;
139061da546Spatrick       socklen_t sa_len = accept_addr.GetMaxLength();
140061da546Spatrick       m_fd = ::accept(sock_fd, &accept_addr.sockaddr(), &sa_len);
141061da546Spatrick 
142061da546Spatrick       if (m_fd == -1) {
143061da546Spatrick         err.SetError(errno, DNBError::POSIX);
144061da546Spatrick         err.LogThreaded("error: Socket accept failed.");
145061da546Spatrick       }
146061da546Spatrick 
147061da546Spatrick       if (addr_in.IsAnyAddr())
148061da546Spatrick         accept_connection = true;
149061da546Spatrick       else {
150061da546Spatrick         if (accept_addr == addr_in)
151061da546Spatrick           accept_connection = true;
152061da546Spatrick         else {
153061da546Spatrick           ::close(m_fd);
154061da546Spatrick           m_fd = -1;
155061da546Spatrick           ::fprintf(
156061da546Spatrick               stderr,
157061da546Spatrick               "error: rejecting incoming connection from %s (expecting %s)\n",
158061da546Spatrick               accept_addr.GetIPAddress().c_str(),
159061da546Spatrick               addr_in.GetIPAddress().c_str());
160061da546Spatrick           DNBLogThreaded("error: rejecting connection from %s (expecting %s)\n",
161061da546Spatrick                          accept_addr.GetIPAddress().c_str(),
162061da546Spatrick                          addr_in.GetIPAddress().c_str());
163061da546Spatrick           err.Clear();
164061da546Spatrick         }
165061da546Spatrick       }
166061da546Spatrick     }
167061da546Spatrick     if (err.Fail())
168061da546Spatrick       break;
169061da546Spatrick   }
170061da546Spatrick   for (auto socket : sockets) {
171061da546Spatrick     int ListenFd = socket.first;
172061da546Spatrick     ClosePort(ListenFd, false);
173061da546Spatrick   }
174061da546Spatrick 
175061da546Spatrick   if (err.Fail())
176061da546Spatrick     return rnb_err;
177061da546Spatrick 
178061da546Spatrick   // Keep our TCP packets coming without any delays.
179061da546Spatrick   SetSocketOption(m_fd, IPPROTO_TCP, TCP_NODELAY, 1);
180061da546Spatrick 
181061da546Spatrick   return rnb_success;
182061da546Spatrick }
183061da546Spatrick 
Connect(const char * host,uint16_t port)184061da546Spatrick rnb_err_t RNBSocket::Connect(const char *host, uint16_t port) {
185061da546Spatrick   auto result = rnb_err;
186061da546Spatrick   Disconnect(false);
187061da546Spatrick 
188061da546Spatrick   auto addresses = lldb_private::SocketAddress::GetAddressInfo(
189061da546Spatrick       host, NULL, AF_UNSPEC, SOCK_STREAM, IPPROTO_TCP);
190061da546Spatrick 
191061da546Spatrick   for (auto address : addresses) {
192061da546Spatrick     m_fd = ::socket(address.GetFamily(), SOCK_STREAM, IPPROTO_TCP);
193061da546Spatrick     if (m_fd == -1)
194061da546Spatrick       continue;
195061da546Spatrick 
196061da546Spatrick     // Enable local address reuse
197061da546Spatrick     SetSocketOption(m_fd, SOL_SOCKET, SO_REUSEADDR, 1);
198061da546Spatrick 
199061da546Spatrick     address.SetPort(port);
200061da546Spatrick 
201061da546Spatrick     if (-1 == ::connect(m_fd, &address.sockaddr(), address.GetLength())) {
202061da546Spatrick       Disconnect(false);
203061da546Spatrick       continue;
204061da546Spatrick     }
205061da546Spatrick     SetSocketOption(m_fd, IPPROTO_TCP, TCP_NODELAY, 1);
206061da546Spatrick 
207061da546Spatrick     result = rnb_success;
208061da546Spatrick     break;
209061da546Spatrick   }
210061da546Spatrick   return result;
211061da546Spatrick }
212061da546Spatrick 
useFD(int fd)213061da546Spatrick rnb_err_t RNBSocket::useFD(int fd) {
214061da546Spatrick   if (fd < 0) {
215061da546Spatrick     DNBLogThreadedIf(LOG_RNB_COMM, "Bad file descriptor passed in.");
216061da546Spatrick     return rnb_err;
217061da546Spatrick   }
218061da546Spatrick 
219061da546Spatrick   m_fd = fd;
220061da546Spatrick   return rnb_success;
221061da546Spatrick }
222061da546Spatrick 
223061da546Spatrick #ifdef WITH_LOCKDOWN
ConnectToService()224061da546Spatrick rnb_err_t RNBSocket::ConnectToService() {
225061da546Spatrick   DNBLog("Connecting to com.apple.%s service...", DEBUGSERVER_PROGRAM_NAME);
226061da546Spatrick   // Disconnect from any previous connections
227061da546Spatrick   Disconnect(false);
228061da546Spatrick   if (::secure_lockdown_checkin(&m_ld_conn, NULL, NULL) != kLDESuccess) {
229061da546Spatrick     DNBLogThreadedIf(LOG_RNB_COMM,
230061da546Spatrick                      "::secure_lockdown_checkin(&m_fd, NULL, NULL) failed");
231061da546Spatrick     m_fd = -1;
232061da546Spatrick     return rnb_not_connected;
233061da546Spatrick   }
234061da546Spatrick   m_fd = ::lockdown_get_socket(m_ld_conn);
235061da546Spatrick   if (m_fd == -1) {
236061da546Spatrick     DNBLogThreadedIf(LOG_RNB_COMM, "::lockdown_get_socket() failed");
237061da546Spatrick     return rnb_not_connected;
238061da546Spatrick   }
239061da546Spatrick   m_fd_from_lockdown = true;
240061da546Spatrick   return rnb_success;
241061da546Spatrick }
242061da546Spatrick #endif
243061da546Spatrick 
OpenFile(const char * path)244061da546Spatrick rnb_err_t RNBSocket::OpenFile(const char *path) {
245061da546Spatrick   DNBError err;
246061da546Spatrick   m_fd = open(path, O_RDWR);
247061da546Spatrick   if (m_fd == -1) {
248061da546Spatrick     err.SetError(errno, DNBError::POSIX);
249061da546Spatrick     err.LogThreaded("can't open file '%s'", path);
250061da546Spatrick     return rnb_not_connected;
251061da546Spatrick   } else {
252061da546Spatrick     struct termios stdin_termios;
253061da546Spatrick 
254061da546Spatrick     if (::tcgetattr(m_fd, &stdin_termios) == 0) {
255061da546Spatrick       stdin_termios.c_lflag &= ~ECHO;   // Turn off echoing
256061da546Spatrick       stdin_termios.c_lflag &= ~ICANON; // Get one char at a time
257061da546Spatrick       ::tcsetattr(m_fd, TCSANOW, &stdin_termios);
258061da546Spatrick     }
259061da546Spatrick   }
260061da546Spatrick   return rnb_success;
261061da546Spatrick }
262061da546Spatrick 
SetSocketOption(int fd,int level,int option_name,int option_value)263061da546Spatrick int RNBSocket::SetSocketOption(int fd, int level, int option_name,
264061da546Spatrick                                int option_value) {
265061da546Spatrick   return ::setsockopt(fd, level, option_name, &option_value,
266061da546Spatrick                       sizeof(option_value));
267061da546Spatrick }
268061da546Spatrick 
Disconnect(bool save_errno)269061da546Spatrick rnb_err_t RNBSocket::Disconnect(bool save_errno) {
270061da546Spatrick #ifdef WITH_LOCKDOWN
271061da546Spatrick   if (m_fd_from_lockdown) {
272061da546Spatrick     m_fd_from_lockdown = false;
273061da546Spatrick     m_fd = -1;
274061da546Spatrick     lockdown_disconnect(m_ld_conn);
275061da546Spatrick     return rnb_success;
276061da546Spatrick   }
277061da546Spatrick #endif
278061da546Spatrick   return ClosePort(m_fd, save_errno);
279061da546Spatrick }
280061da546Spatrick 
Read(std::string & p)281061da546Spatrick rnb_err_t RNBSocket::Read(std::string &p) {
282061da546Spatrick   char buf[1024];
283061da546Spatrick   p.clear();
284061da546Spatrick 
285061da546Spatrick   // Note that BUF is on the stack so we must be careful to keep any
286061da546Spatrick   // writes to BUF from overflowing or we'll have security issues.
287061da546Spatrick 
288061da546Spatrick   if (m_fd == -1)
289061da546Spatrick     return rnb_err;
290061da546Spatrick 
291061da546Spatrick   // DNBLogThreadedIf(LOG_RNB_COMM, "%8u RNBSocket::%s calling read()",
292061da546Spatrick   // (uint32_t)m_timer.ElapsedMicroSeconds(true), __FUNCTION__);
293061da546Spatrick   DNBError err;
294061da546Spatrick   ssize_t bytesread = read(m_fd, buf, sizeof(buf));
295061da546Spatrick   if (bytesread <= 0)
296061da546Spatrick     err.SetError(errno, DNBError::POSIX);
297061da546Spatrick   else
298061da546Spatrick     p.append(buf, bytesread);
299061da546Spatrick 
300061da546Spatrick   if (err.Fail() || DNBLogCheckLogBit(LOG_RNB_COMM))
301061da546Spatrick     err.LogThreaded("::read ( %i, %p, %llu ) => %i", m_fd, buf, sizeof(buf),
302061da546Spatrick                     (uint64_t)bytesread);
303061da546Spatrick 
304061da546Spatrick   // Our port went away - we have to mark this so IsConnected will return the
305061da546Spatrick   // truth.
306061da546Spatrick   if (bytesread == 0) {
307061da546Spatrick     m_fd = -1;
308061da546Spatrick     return rnb_not_connected;
309061da546Spatrick   } else if (bytesread == -1) {
310061da546Spatrick     m_fd = -1;
311061da546Spatrick     return rnb_err;
312061da546Spatrick   }
313061da546Spatrick   // Strip spaces from the end of the buffer
314061da546Spatrick   while (!p.empty() && isspace(p[p.size() - 1]))
315061da546Spatrick     p.erase(p.size() - 1);
316061da546Spatrick 
317061da546Spatrick   // Most data in the debugserver packets valid printable characters...
318061da546Spatrick   DNBLogThreadedIf(LOG_RNB_COMM, "read: %s", p.c_str());
319061da546Spatrick   return rnb_success;
320061da546Spatrick }
321061da546Spatrick 
Write(const void * buffer,size_t length)322061da546Spatrick rnb_err_t RNBSocket::Write(const void *buffer, size_t length) {
323061da546Spatrick   if (m_fd == -1)
324061da546Spatrick     return rnb_err;
325061da546Spatrick 
326061da546Spatrick   DNBError err;
327061da546Spatrick   ssize_t bytessent = write(m_fd, buffer, length);
328061da546Spatrick   if (bytessent < 0)
329061da546Spatrick     err.SetError(errno, DNBError::POSIX);
330061da546Spatrick 
331061da546Spatrick   if (err.Fail() || DNBLogCheckLogBit(LOG_RNB_COMM))
332061da546Spatrick     err.LogThreaded("::write ( socket = %i, buffer = %p, length = %llu) => %i",
333061da546Spatrick                     m_fd, buffer, length, (uint64_t)bytessent);
334061da546Spatrick 
335061da546Spatrick   if (bytessent < 0)
336061da546Spatrick     return rnb_err;
337061da546Spatrick 
338061da546Spatrick   if ((size_t)bytessent != length)
339061da546Spatrick     return rnb_err;
340061da546Spatrick 
341061da546Spatrick   DNBLogThreadedIf(
342061da546Spatrick       LOG_RNB_PACKETS, "putpkt: %*s", (int)length,
343061da546Spatrick       (const char *)
344061da546Spatrick           buffer); // All data is string based in debugserver, so this is safe
345061da546Spatrick   DNBLogThreadedIf(LOG_RNB_COMM, "sent: %*s", (int)length,
346061da546Spatrick                    (const char *)buffer);
347061da546Spatrick 
348061da546Spatrick   return rnb_success;
349061da546Spatrick }
350061da546Spatrick 
ClosePort(int & fd,bool save_errno)351061da546Spatrick rnb_err_t RNBSocket::ClosePort(int &fd, bool save_errno) {
352061da546Spatrick   int close_err = 0;
353061da546Spatrick   if (fd > 0) {
354061da546Spatrick     errno = 0;
355061da546Spatrick     close_err = close(fd);
356061da546Spatrick     fd = -1;
357061da546Spatrick   }
358061da546Spatrick   return close_err != 0 ? rnb_err : rnb_success;
359061da546Spatrick }
360