370 lines · cpp
1//===-- RNBSocket.cpp -------------------------------------------*- C++ -*-===//2//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-exception6//7//===----------------------------------------------------------------------===//8//9// Created by Greg Clayton on 12/12/07.10//11//===----------------------------------------------------------------------===//12 13#include "RNBSocket.h"14#include "DNBError.h"15#include "DNBLog.h"16#include <arpa/inet.h>17#include <cerrno>18#include <fcntl.h>19#include <map>20#include <netdb.h>21#include <netinet/in.h>22#include <netinet/tcp.h>23#include <sys/event.h>24#include <termios.h>25#include <vector>26 27#include "lldb/Host/SocketAddress.h"28 29#ifdef WITH_LOCKDOWN30#include "lockdown.h"31#endif32 33rnb_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 errno39 Disconnect(false);40 41 DNBError err;42 int queue_id = kqueue();43 if (queue_id < 0) {44 err.SetError(errno, DNBError::MachKernel);45 err.LogThreaded("error: failed to create kqueue.");46 return rnb_err;47 }48 49 bool any_addr = (strcmp(listen_host, "*") == 0);50 51 // If the user wants to allow connections from any address we should create52 // sockets on all families that can resolve localhost. This will allow us to53 // listen for IPv6 and IPv4 connections from all addresses if those interfaces54 // are available.55 const char *local_addr = any_addr ? "localhost" : listen_host;56 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);60 61 for (auto address : addresses) {62 int sock_fd = ::socket(address.GetFamily(), SOCK_STREAM, IPPROTO_TCP);63 if (sock_fd == -1)64 continue;65 66 SetSocketOption(sock_fd, SOL_SOCKET, SO_REUSEADDR, 1);67 68 lldb_private::SocketAddress bind_address = address;69 70 if(any_addr || !bind_address.IsLocalhost())71 bind_address.SetToAnyAddress(bind_address.GetFamily(), port);72 else73 bind_address.SetPort(port);74 75 int error =76 ::bind(sock_fd, &bind_address.sockaddr(), bind_address.GetLength());77 if (error == -1) {78 ClosePort(sock_fd, false);79 continue;80 }81 82 error = ::listen(sock_fd, 5);83 if (error == -1) {84 ClosePort(sock_fd, false);85 continue;86 }87 88 // We were asked to listen on port zero which means we must now read the89 // actual port that was given to us as port zero is a special code for "find90 // an open port for me". This will only execute on the first socket created,91 // subesquent sockets will reuse this port number.92 if (port == 0) {93 socklen_t sa_len = address.GetLength();94 if (getsockname(sock_fd, &address.sockaddr(), &sa_len) == 0)95 port = address.GetPort();96 }97 98 sockets[sock_fd] = address;99 }100 101 if (sockets.size() == 0) {102 err.SetError(errno, DNBError::POSIX);103 err.LogThreaded("::listen or ::bind failed");104 return rnb_err;105 }106 107 if (callback)108 callback(callback_baton, port);109 110 std::vector<struct kevent> events;111 events.resize(sockets.size());112 int i = 0;113 for (auto socket : sockets) {114 EV_SET(&events[i++], socket.first, EVFILT_READ, EV_ADD, 0, 0, 0);115 }116 117 bool accept_connection = false;118 119 // Loop until we are happy with our connection120 while (!accept_connection) {121 122 struct kevent event_list[4];123 int num_events;124 do {125 errno = 0;126 num_events =127 kevent(queue_id, events.data(), events.size(), event_list, 4, NULL);128 } while (num_events == -1 &&129 (errno == EAGAIN || errno == EWOULDBLOCK || errno == EINTR));130 131 if (num_events < 0) {132 err.SetError(errno, DNBError::MachKernel);133 err.LogThreaded("error: kevent() failed.");134 }135 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())140 continue;141 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);146 147 if (m_fd == -1) {148 err.SetError(errno, DNBError::POSIX);149 err.LogThreaded("error: Socket accept failed.");150 }151 152 if (addr_in.IsAnyAddr())153 accept_connection = true;154 else {155 if (accept_addr == addr_in)156 accept_connection = true;157 else {158 ::close(m_fd);159 m_fd = -1;160 ::fprintf(161 stderr,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());168 err.Clear();169 }170 }171 }172 if (err.Fail())173 break;174 }175 for (auto socket : sockets) {176 int ListenFd = socket.first;177 ClosePort(ListenFd, false);178 }179 180 if (err.Fail())181 return rnb_err;182 183 // Keep our TCP packets coming without any delays.184 SetSocketOption(m_fd, IPPROTO_TCP, TCP_NODELAY, 1);185 186 return rnb_success;187}188 189rnb_err_t RNBSocket::Connect(const char *host, uint16_t port) {190 auto result = rnb_err;191 Disconnect(false);192 193 auto addresses = lldb_private::SocketAddress::GetAddressInfo(194 host, NULL, AF_UNSPEC, SOCK_STREAM, IPPROTO_TCP);195 196 for (auto address : addresses) {197 m_fd = ::socket(address.GetFamily(), SOCK_STREAM, IPPROTO_TCP);198 if (m_fd == -1)199 continue;200 201 // Enable local address reuse202 SetSocketOption(m_fd, SOL_SOCKET, SO_REUSEADDR, 1);203 204 address.SetPort(port);205 206 if (-1 == ::connect(m_fd, &address.sockaddr(), address.GetLength())) {207 Disconnect(false);208 continue;209 }210 SetSocketOption(m_fd, IPPROTO_TCP, TCP_NODELAY, 1);211 212 result = rnb_success;213 break;214 }215 return result;216}217 218rnb_err_t RNBSocket::useFD(int fd) {219 if (fd < 0) {220 DNBLogThreadedIf(LOG_RNB_COMM, "Bad file descriptor passed in.");221 return rnb_err;222 }223 224 m_fd = fd;225 return rnb_success;226}227 228#ifdef WITH_LOCKDOWN229rnb_err_t RNBSocket::ConnectToService() {230 DNBLog("Connecting to com.apple.%s service...", DEBUGSERVER_PROGRAM_NAME);231 // Disconnect from any previous connections232 Disconnect(false);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");236 m_fd = -1;237 return rnb_not_connected;238 }239 m_fd = ::lockdown_get_socket(m_ld_conn);240 if (m_fd == -1) {241 DNBLogThreadedIf(LOG_RNB_COMM, "::lockdown_get_socket() failed");242 return rnb_not_connected;243 }244 m_fd_from_lockdown = true;245 return rnb_success;246}247#endif248 249rnb_err_t RNBSocket::OpenFile(const char *path) {250 DNBError err;251 m_fd = open(path, O_RDWR);252 if (m_fd == -1) {253 err.SetError(errno, DNBError::POSIX);254 err.LogThreaded("can't open file '%s'", path);255 return rnb_not_connected;256 } else {257 struct termios stdin_termios;258 259 if (::tcgetattr(m_fd, &stdin_termios) == 0) {260 stdin_termios.c_lflag &= ~ECHO; // Turn off echoing261 stdin_termios.c_lflag &= ~ICANON; // Get one char at a time262 ::tcsetattr(m_fd, TCSANOW, &stdin_termios);263 }264 }265 return rnb_success;266}267 268int RNBSocket::SetSocketOption(int fd, int level, int option_name,269 int option_value) {270 return ::setsockopt(fd, level, option_name, &option_value,271 sizeof(option_value));272}273 274rnb_err_t RNBSocket::Disconnect(bool save_errno) {275#ifdef WITH_LOCKDOWN276 if (m_fd_from_lockdown) {277 m_fd_from_lockdown = false;278 m_fd = -1;279 lockdown_disconnect(m_ld_conn);280 return rnb_success;281 }282#endif283 return ClosePort(m_fd, save_errno);284}285 286rnb_err_t RNBSocket::Read(std::string &p) {287 char buf[1024];288 p.clear();289 290 // Note that BUF is on the stack so we must be careful to keep any291 // writes to BUF from overflowing or we'll have security issues.292 293 if (m_fd == -1)294 return rnb_err;295 296 // DNBLogThreadedIf(LOG_RNB_COMM, "%8u RNBSocket::%s calling read()",297 // (uint32_t)m_timer.ElapsedMicroSeconds(true), __FUNCTION__);298 DNBError err;299 ssize_t bytesread;300 do {301 errno = 0;302 bytesread = read(m_fd, buf, sizeof(buf));303 } while (bytesread == -1 &&304 (errno == EAGAIN || errno == EWOULDBLOCK || errno == EINTR));305 if (bytesread <= 0)306 err.SetError(errno, DNBError::POSIX);307 else308 p.append(buf, bytesread);309 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);313 314 // Our port went away - we have to mark this so IsConnected will return the315 // truth.316 if (bytesread == 0) {317 m_fd = -1;318 return rnb_not_connected;319 } else if (bytesread == -1) {320 m_fd = -1;321 return rnb_err;322 }323 // Strip spaces from the end of the buffer324 while (!p.empty() && isspace(p[p.size() - 1]))325 p.erase(p.size() - 1);326 327 // Most data in the debugserver packets valid printable characters...328 DNBLogThreadedIf(LOG_RNB_COMM, "read: %s", p.c_str());329 return rnb_success;330}331 332rnb_err_t RNBSocket::Write(const void *buffer, size_t length) {333 if (m_fd == -1)334 return rnb_err;335 336 DNBError err;337 ssize_t bytessent = write(m_fd, buffer, length);338 if (bytessent < 0)339 err.SetError(errno, DNBError::POSIX);340 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);344 345 if (bytessent < 0)346 return rnb_err;347 348 if ((size_t)bytessent != length)349 return rnb_err;350 351 DNBLogThreadedIf(352 LOG_RNB_PACKETS, "putpkt: %*s", (int)length,353 (const char *)354 buffer); // All data is string based in debugserver, so this is safe355 DNBLogThreadedIf(LOG_RNB_COMM, "sent: %*s", (int)length,356 (const char *)buffer);357 358 return rnb_success;359}360 361rnb_err_t RNBSocket::ClosePort(int &fd, bool save_errno) {362 int close_err = 0;363 if (fd > 0) {364 errno = 0;365 close_err = close(fd);366 fd = -1;367 }368 return close_err != 0 ? rnb_err : rnb_success;369}370