brintos

brintos / llvm-project-archived public Read only

0
0
Text · 10.4 KiB · fc55dbf Raw
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