Home | History | Annotate | Download | only in source
      1 //===-- RNBSocket.cpp -------------------------------------------*- C++ -*-===//
      2 //
      3 //                     The LLVM Compiler Infrastructure
      4 //
      5 // This file is distributed under the University of Illinois Open Source
      6 // License. See LICENSE.TXT for details.
      7 //
      8 //===----------------------------------------------------------------------===//
      9 //
     10 //  Created by Greg Clayton on 12/12/07.
     11 //
     12 //===----------------------------------------------------------------------===//
     13 
     14 #include "RNBSocket.h"
     15 #include <arpa/inet.h>
     16 #include <errno.h>
     17 #include <fcntl.h>
     18 #include <netdb.h>
     19 #include <netinet/in.h>
     20 #include <netinet/tcp.h>
     21 #include <termios.h>
     22 #include "DNBLog.h"
     23 #include "DNBError.h"
     24 
     25 #ifdef WITH_LOCKDOWN
     26 #include "lockdown.h"
     27 #endif
     28 
     29 /* Once we have a RNBSocket object with a port # specified,
     30    this function is called to wait for an incoming connection.
     31    This function blocks while waiting for that connection.  */
     32 
     33 bool
     34 ResolveIPV4HostName (const char *hostname, in_addr_t &addr)
     35 {
     36     if (hostname == NULL ||
     37         hostname[0] == '\0' ||
     38         strcmp(hostname, "localhost") == 0 ||
     39         strcmp(hostname, "127.0.0.1") == 0)
     40     {
     41         addr = htonl (INADDR_LOOPBACK);
     42         return true;
     43     }
     44     else if (strcmp(hostname, "*") == 0)
     45     {
     46         addr = htonl (INADDR_ANY);
     47         return true;
     48     }
     49     else
     50     {
     51         // See if an IP address was specified as numbers
     52         int inet_pton_result = ::inet_pton (AF_INET, hostname, &addr);
     53 
     54         if (inet_pton_result == 1)
     55             return true;
     56 
     57         struct hostent *host_entry = gethostbyname (hostname);
     58         if (host_entry)
     59         {
     60             std::string ip_str (::inet_ntoa (*(struct in_addr *)*host_entry->h_addr_list));
     61             inet_pton_result = ::inet_pton (AF_INET, ip_str.c_str(), &addr);
     62             if (inet_pton_result == 1)
     63                 return true;
     64         }
     65     }
     66     return false;
     67 }
     68 
     69 rnb_err_t
     70 RNBSocket::Listen (const char *listen_host, in_port_t port, PortBoundCallback callback, const void *callback_baton)
     71 {
     72     //DNBLogThreadedIf(LOG_RNB_COMM, "%8u RNBSocket::%s called", (uint32_t)m_timer.ElapsedMicroSeconds(true), __FUNCTION__);
     73     // Disconnect without saving errno
     74     Disconnect (false);
     75 
     76     // Now figure out the hostname that will be attaching and palce it into
     77     struct sockaddr_in listen_addr;
     78     ::memset (&listen_addr, 0, sizeof listen_addr);
     79     listen_addr.sin_len = sizeof listen_addr;
     80     listen_addr.sin_family = AF_INET;
     81     listen_addr.sin_port = htons (port);
     82     listen_addr.sin_addr.s_addr = INADDR_ANY;
     83 
     84     if (!ResolveIPV4HostName(listen_host, listen_addr.sin_addr.s_addr))
     85     {
     86         DNBLogThreaded("error: failed to resolve connecting host '%s'", listen_host);
     87         return rnb_err;
     88     }
     89 
     90     DNBError err;
     91     int listen_fd = ::socket (AF_INET, SOCK_STREAM, IPPROTO_TCP);
     92     if (listen_fd == -1)
     93         err.SetError(errno, DNBError::POSIX);
     94 
     95     if (err.Fail() || DNBLogCheckLogBit(LOG_RNB_COMM))
     96         err.LogThreaded("::socket ( domain = AF_INET, type = SOCK_STREAM, protocol = IPPROTO_TCP ) => socket = %i", listen_fd);
     97 
     98     if (err.Fail())
     99         return rnb_err;
    100 
    101     // enable local address reuse
    102     SetSocketOption (listen_fd, SOL_SOCKET, SO_REUSEADDR, 1);
    103 
    104     struct sockaddr_in sa;
    105     ::memset (&sa, 0, sizeof sa);
    106     sa.sin_len = sizeof sa;
    107     sa.sin_family = AF_INET;
    108     sa.sin_port = htons (port);
    109     sa.sin_addr.s_addr = INADDR_ANY; // Let incoming connections bind to any host network interface (this is NOT who can connect to us)
    110     int error = ::bind (listen_fd, (struct sockaddr *) &sa, sizeof(sa));
    111     if (error == -1)
    112         err.SetError(errno, DNBError::POSIX);
    113 
    114     if (err.Fail() || DNBLogCheckLogBit(LOG_RNB_COMM))
    115         err.LogThreaded("::bind ( socket = %i, (struct sockaddr *) &sa, sizeof(sa)) )", listen_fd);
    116 
    117     if (err.Fail())
    118     {
    119         ClosePort (listen_fd, false);
    120         return rnb_err;
    121     }
    122 
    123     if (callback && port == 0)
    124     {
    125         // We were asked to listen on port zero which means we
    126         // must now read the actual port that was given to us
    127         // as port zero is a special code for "find an open port
    128         // for me".
    129         socklen_t sa_len = sizeof (sa);
    130         if (getsockname(listen_fd, (struct sockaddr *)&sa, &sa_len) == 0)
    131         {
    132             port = ntohs (sa.sin_port);
    133             callback (callback_baton, port);
    134         }
    135     }
    136 
    137     error = ::listen (listen_fd, 5);
    138     if (error == -1)
    139         err.SetError(errno, DNBError::POSIX);
    140 
    141     if (err.Fail() || DNBLogCheckLogBit(LOG_RNB_COMM))
    142         err.LogThreaded("::listen ( socket = %i, backlog = 1 )", listen_fd);
    143 
    144     if (err.Fail())
    145     {
    146         ClosePort (listen_fd, false);
    147         return rnb_err;
    148     }
    149 
    150     struct sockaddr_in accept_addr;
    151     ::memset (&accept_addr, 0, sizeof accept_addr);
    152     accept_addr.sin_len = sizeof accept_addr;
    153 
    154     bool accept_connection = false;
    155 
    156     // Loop until we are happy with our connection
    157     while (!accept_connection)
    158     {
    159         socklen_t accept_addr_len = sizeof accept_addr;
    160         m_fd = ::accept (listen_fd, (struct sockaddr *)&accept_addr, &accept_addr_len);
    161 
    162         if (m_fd == -1)
    163             err.SetError(errno, DNBError::POSIX);
    164 
    165         if (err.Fail() || DNBLogCheckLogBit(LOG_RNB_COMM))
    166             err.LogThreaded("::accept ( socket = %i, address = %p, address_len = %u )", listen_fd, &accept_addr, accept_addr_len);
    167 
    168         if (err.Fail())
    169             break;
    170 
    171         if (listen_addr.sin_addr.s_addr == INADDR_ANY)
    172             accept_connection = true;
    173         else
    174         {
    175             if (accept_addr_len == listen_addr.sin_len &&
    176                 accept_addr.sin_addr.s_addr == listen_addr.sin_addr.s_addr)
    177             {
    178                 accept_connection = true;
    179             }
    180             else
    181             {
    182                 ::close (m_fd);
    183                 m_fd = -1;
    184                 const uint8_t *accept_ip = (const uint8_t *)&accept_addr.sin_addr.s_addr;
    185                 const uint8_t *listen_ip = (const uint8_t *)&listen_addr.sin_addr.s_addr;
    186                 ::fprintf (stderr,
    187                            "error: rejecting incoming connection from %u.%u.%u.%u (expecting %u.%u.%u.%u)\n",
    188                            accept_ip[0], accept_ip[1], accept_ip[2], accept_ip[3],
    189                            listen_ip[0], listen_ip[1], listen_ip[2], listen_ip[3]);
    190                 DNBLogThreaded ("error: rejecting connection from %u.%u.%u.%u (expecting %u.%u.%u.%u)",
    191                                 accept_ip[0], accept_ip[1], accept_ip[2], accept_ip[3],
    192                                 listen_ip[0], listen_ip[1], listen_ip[2], listen_ip[3]);
    193             }
    194         }
    195     }
    196 
    197     ClosePort (listen_fd, false);
    198 
    199     if (err.Fail())
    200     {
    201         return rnb_err;
    202     }
    203     else
    204     {
    205         // Keep our TCP packets coming without any delays.
    206         SetSocketOption (m_fd, IPPROTO_TCP, TCP_NODELAY, 1);
    207     }
    208 
    209     return rnb_success;
    210 }
    211 
    212 rnb_err_t
    213 RNBSocket::Connect (const char *host, uint16_t port)
    214 {
    215     Disconnect (false);
    216 
    217     // Create the socket
    218     m_fd = ::socket (AF_INET, SOCK_STREAM, IPPROTO_TCP);
    219     if (m_fd == -1)
    220         return rnb_err;
    221 
    222     // Enable local address reuse
    223     SetSocketOption (m_fd, SOL_SOCKET, SO_REUSEADDR, 1);
    224 
    225     struct sockaddr_in sa;
    226     ::memset (&sa, 0, sizeof (sa));
    227     sa.sin_family = AF_INET;
    228     sa.sin_port = htons (port);
    229 
    230     if (!ResolveIPV4HostName(host, sa.sin_addr.s_addr))
    231     {
    232         DNBLogThreaded("error: failed to resolve host '%s'", host);
    233         Disconnect (false);
    234         return rnb_err;
    235     }
    236 
    237     if (-1 == ::connect (m_fd, (const struct sockaddr *)&sa, sizeof(sa)))
    238     {
    239         Disconnect (false);
    240         return rnb_err;
    241     }
    242 
    243     // Keep our TCP packets coming without any delays.
    244     SetSocketOption (m_fd, IPPROTO_TCP, TCP_NODELAY, 1);
    245     return rnb_success;
    246 }
    247 
    248 rnb_err_t
    249 RNBSocket::useFD(int fd)
    250 {
    251        if (fd < 0) {
    252                DNBLogThreadedIf(LOG_RNB_COMM, "Bad file descriptor passed in.");
    253                return rnb_err;
    254        }
    255 
    256        m_fd = fd;
    257        return rnb_success;
    258 }
    259 
    260 #ifdef WITH_LOCKDOWN
    261 rnb_err_t
    262 RNBSocket::ConnectToService()
    263 {
    264     DNBLog("Connecting to com.apple.%s service...", DEBUGSERVER_PROGRAM_NAME);
    265     // Disconnect from any previous connections
    266     Disconnect(false);
    267     if (::secure_lockdown_checkin (&m_ld_conn, NULL, NULL) != kLDESuccess)
    268     {
    269         DNBLogThreadedIf(LOG_RNB_COMM, "::secure_lockdown_checkin(&m_fd, NULL, NULL) failed");
    270         m_fd = -1;
    271         return rnb_not_connected;
    272     }
    273     m_fd = ::lockdown_get_socket (m_ld_conn);
    274     if (m_fd == -1)
    275     {
    276         DNBLogThreadedIf(LOG_RNB_COMM, "::lockdown_get_socket() failed");
    277         return rnb_not_connected;
    278     }
    279     m_fd_from_lockdown = true;
    280     return rnb_success;
    281 }
    282 #endif
    283 
    284 rnb_err_t
    285 RNBSocket::OpenFile (const char *path)
    286 {
    287     DNBError err;
    288     m_fd = open (path, O_RDWR);
    289     if (m_fd == -1)
    290     {
    291         err.SetError(errno, DNBError::POSIX);
    292         err.LogThreaded ("can't open file '%s'", path);
    293         return rnb_not_connected;
    294     }
    295     else
    296     {
    297         struct termios stdin_termios;
    298 
    299         if (::tcgetattr (m_fd, &stdin_termios) == 0)
    300         {
    301             stdin_termios.c_lflag &= ~ECHO;     // Turn off echoing
    302             stdin_termios.c_lflag &= ~ICANON;   // Get one char at a time
    303             ::tcsetattr (m_fd, TCSANOW, &stdin_termios);
    304         }
    305     }
    306     return rnb_success;
    307 }
    308 
    309 int
    310 RNBSocket::SetSocketOption(int fd, int level, int option_name, int option_value)
    311 {
    312     return ::setsockopt(fd, level, option_name, &option_value, sizeof(option_value));
    313 }
    314 
    315 rnb_err_t
    316 RNBSocket::Disconnect (bool save_errno)
    317 {
    318 #ifdef WITH_LOCKDOWN
    319     if (m_fd_from_lockdown)
    320     {
    321         m_fd_from_lockdown = false;
    322         m_fd = -1;
    323         lockdown_disconnect (m_ld_conn);
    324         return rnb_success;
    325     }
    326 #endif
    327     return ClosePort (m_fd, save_errno);
    328 }
    329 
    330 
    331 rnb_err_t
    332 RNBSocket::Read (std::string &p)
    333 {
    334     char buf[1024];
    335     p.clear();
    336 
    337     // Note that BUF is on the stack so we must be careful to keep any
    338     // writes to BUF from overflowing or we'll have security issues.
    339 
    340     if (m_fd == -1)
    341         return rnb_err;
    342 
    343     //DNBLogThreadedIf(LOG_RNB_COMM, "%8u RNBSocket::%s calling read()", (uint32_t)m_timer.ElapsedMicroSeconds(true), __FUNCTION__);
    344     DNBError err;
    345     int bytesread = read (m_fd, buf, sizeof (buf));
    346     if (bytesread <= 0)
    347         err.SetError(errno, DNBError::POSIX);
    348     else
    349         p.append(buf, bytesread);
    350 
    351     if (err.Fail() || DNBLogCheckLogBit(LOG_RNB_COMM))
    352         err.LogThreaded("::read ( %i, %p, %llu ) => %i", m_fd, buf, sizeof (buf), (uint64_t)bytesread);
    353 
    354     // Our port went away - we have to mark this so IsConnected will return the truth.
    355     if (bytesread == 0)
    356     {
    357         m_fd = -1;
    358         return rnb_not_connected;
    359     }
    360     else if (bytesread == -1)
    361     {
    362         m_fd = -1;
    363         return rnb_err;
    364     }
    365     // Strip spaces from the end of the buffer
    366     while (!p.empty() && isspace (p[p.size() - 1]))
    367         p.erase (p.size () - 1);
    368 
    369     // Most data in the debugserver packets valid printable characters...
    370     DNBLogThreadedIf(LOG_RNB_COMM, "read: %s", p.c_str());
    371     return rnb_success;
    372 }
    373 
    374 rnb_err_t
    375 RNBSocket::Write (const void *buffer, size_t length)
    376 {
    377     if (m_fd == -1)
    378         return rnb_err;
    379 
    380     DNBError err;
    381     int bytessent = write (m_fd, buffer, length);
    382     if (bytessent < 0)
    383         err.SetError(errno, DNBError::POSIX);
    384 
    385     if (err.Fail() || DNBLogCheckLogBit(LOG_RNB_COMM))
    386         err.LogThreaded("::write ( socket = %i, buffer = %p, length = %llu) => %i", m_fd, buffer, length, (uint64_t)bytessent);
    387 
    388     if (bytessent < 0)
    389         return rnb_err;
    390 
    391     if (bytessent != length)
    392         return rnb_err;
    393 
    394     DNBLogThreadedIf(LOG_RNB_PACKETS, "putpkt: %*s", (int)length, (char *)buffer);   // All data is string based in debugserver, so this is safe
    395     DNBLogThreadedIf(LOG_RNB_COMM, "sent: %*s", (int)length, (char *)buffer);
    396 
    397     return rnb_success;
    398 }
    399 
    400 
    401 rnb_err_t
    402 RNBSocket::ClosePort (int& fd, bool save_errno)
    403 {
    404     int close_err = 0;
    405     if (fd > 0)
    406     {
    407         errno = 0;
    408         close_err = close (fd);
    409         fd = -1;
    410     }
    411     return close_err != 0 ? rnb_err : rnb_success;
    412 }
    413 
    414 
    415