LCOV - code coverage report
Current view: directory - src/trusted/debug_stub - transport_common.cc (source / functions) Found Hit Coverage
Test: coverage.lcov Lines: 188 152 80.9 %
Date: 2014-06-18 Functions: 0 0 -

       1                 : /*
       2                 :  * Copyright (c) 2012 The Native Client Authors. All rights reserved.
       3                 :  * Use of this source code is governed by a BSD-style license that can be
       4                 :  * found in the LICENSE file.
       5                 :  */
       6                 : 
       7                 : #include <errno.h>
       8                 : #include <stdlib.h>
       9                 : #include <string.h>
      10                 : 
      11                 : #include <algorithm>
      12                 : #include <string>
      13                 : 
      14                 : #include "native_client/src/include/nacl_scoped_ptr.h"
      15                 : #include "native_client/src/include/portability_sockets.h"
      16                 : #include "native_client/src/shared/platform/nacl_log.h"
      17                 : #include "native_client/src/trusted/debug_stub/platform.h"
      18                 : #include "native_client/src/trusted/debug_stub/transport.h"
      19                 : #include "native_client/src/trusted/debug_stub/util.h"
      20                 : #include "native_client/src/trusted/service_runtime/sel_ldr.h"
      21                 : 
      22                 : using gdb_rsp::stringvec;
      23                 : using gdb_rsp::StringSplit;
      24                 : 
      25                 : #if NACL_WINDOWS
      26                 : typedef int socklen_t;
      27                 : #endif
      28                 : 
      29                 : namespace port {
      30                 : 
      31                 : class Transport : public ITransport {
      32                 :  public:
      33                 :   Transport()
      34                 :     : buf_(new char[kBufSize]),
      35                 :       pos_(0),
      36                 :       size_(0) {
      37                 :     handle_ = ::socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
      38                 : #if NACL_WINDOWS
      39                 :     CreateSocketEvent();
      40                 : #endif
      41                 :   }
      42                 : 
      43              58 :   explicit Transport(NaClSocketHandle s)
      44                 :     : buf_(new char[kBufSize]),
      45                 :       pos_(0),
      46                 :       size_(0),
      47              87 :       handle_(s) {
      48                 : #if NACL_WINDOWS
      49                 :     CreateSocketEvent();
      50                 : #endif
      51              58 :   }
      52                 : 
      53              56 :   ~Transport() {
      54              42 :     if (handle_ != NACL_INVALID_SOCKET) NaClCloseSocket(handle_);
      55                 : #if NACL_WINDOWS
      56                 :     if (!WSACloseEvent(socket_event_)) {
      57                 :       NaClLog(LOG_FATAL,
      58                 :               "Transport::~Transport: Failed to close socket event\n");
      59                 :     }
      60                 : #endif
      61              56 :   }
      62                 : 
      63                 : #if NACL_WINDOWS
      64                 :   void CreateSocketEvent() {
      65                 :     socket_event_ = WSACreateEvent();
      66                 :     if (socket_event_ == WSA_INVALID_EVENT) {
      67                 :       NaClLog(LOG_FATAL,
      68                 :               "Transport::CreateSocketEvent: Failed to create socket event\n");
      69                 :     }
      70                 :     // Listen for close events in order to handle them correctly.
      71                 :     // Additionally listen for read readiness as WSAEventSelect sets the socket
      72                 :     // to non-blocking mode.
      73                 :     // http://msdn.microsoft.com/en-us/library/windows/desktop/ms738547(v=vs.85).aspx
      74                 :     if (WSAEventSelect(
      75                 :           handle_, socket_event_, FD_CLOSE | FD_READ) == SOCKET_ERROR) {
      76                 :       NaClLog(LOG_FATAL,
      77                 :               "Transport::CreateSocketEvent: Failed to bind event to socket\n");
      78                 :     }
      79                 :   }
      80                 : #endif
      81                 : 
      82                 :   // Read from this transport, return true on success.
      83                 :   virtual bool Read(void *ptr, int32_t len);
      84                 : 
      85                 :   // Write to this transport, return true on success.
      86                 :   virtual bool Write(const void *ptr, int32_t len);
      87                 : 
      88                 :   // Return true if there is data to read.
      89                 :   virtual bool IsDataAvailable() {
      90               1 :     if (pos_ < size_) {
      91               1 :       return true;
      92                 :     }
      93               0 :     fd_set fds;
      94                 : 
      95               0 :     FD_ZERO(&fds);
      96               0 :     FD_SET(handle_, &fds);
      97                 : 
      98                 :     // We want a "non-blocking" check
      99               0 :     struct timeval timeout;
     100               0 :     timeout.tv_sec = 0;
     101               0 :     timeout.tv_usec = 0;
     102                 : 
     103                 :     // Check if this file handle can select on read
     104               0 :     int cnt = select(static_cast<int>(handle_) + 1, &fds, 0, 0, &timeout);
     105                 : 
     106                 :     // If we are ready, or if there is an error.  We return true
     107                 :     // on error, to let the next IO request fail.
     108               0 :     if (cnt != 0) return true;
     109                 : 
     110               0 :     return false;
     111               1 :   }
     112                 : 
     113                 :   virtual void WaitForDebugStubEvent(struct NaClApp *nap,
     114                 :                                      bool ignore_input_from_gdb);
     115                 : 
     116                 : // On windows, the header that defines this has other definition
     117                 : // colitions, so we define it outselves just in case
     118                 : #ifndef SD_BOTH
     119                 : #define SD_BOTH 2
     120                 : #endif
     121                 : 
     122                 :   virtual void Disconnect() {
     123                 :     // Shutdown the conneciton in both diections.  This should
     124                 :     // always succeed, and nothing we can do if this fails.
     125              30 :     (void) ::shutdown(handle_, SD_BOTH);
     126              30 :   }
     127                 : 
     128                 :  protected:
     129                 :   // Copy buffered data to *dst up to len bytes and update dst and len.
     130                 :   void CopyFromBuffer(char **dst, int32_t *len);
     131                 : 
     132                 :   // Read available data from the socket. Return false on EOF or error.
     133                 :   bool ReadSomeData();
     134                 : 
     135                 :   static const int kBufSize = 4096;
     136                 :   nacl::scoped_array<char> buf_;
     137                 :   int32_t pos_;
     138                 :   int32_t size_;
     139                 :   NaClSocketHandle handle_;
     140                 : #if NACL_WINDOWS
     141                 :   HANDLE socket_event_;
     142                 : #endif
     143                 : };
     144                 : 
     145           23738 : void Transport::CopyFromBuffer(char **dst, int32_t *len) {
     146           23738 :   int32_t copy_bytes = std::min(*len, size_ - pos_);
     147           23738 :   memcpy(*dst, buf_.get() + pos_, copy_bytes);
     148           23738 :   pos_ += copy_bytes;
     149           23738 :   *len -= copy_bytes;
     150           23738 :   *dst += copy_bytes;
     151           23738 : }
     152                 : 
     153                 : bool Transport::ReadSomeData() {
     154            1793 :   while (true) {
     155            1793 :     int result = ::recv(handle_, buf_.get() + size_, kBufSize - size_, 0);
     156            1793 :     if (result > 0) {
     157            1764 :       size_ += result;
     158            1764 :       return true;
     159                 :     }
     160              29 :     if (result == 0)
     161              24 :       return false;
     162                 : #if NACL_WINDOWS
     163                 :     // WSAEventSelect sets socket to non-blocking mode. This is essential
     164                 :     // for socket event notification to work, there is no workaround.
     165                 :     // See remarks section at the page
     166                 :     // http://msdn.microsoft.com/en-us/library/windows/desktop/ms741576(v=vs.85).aspx
     167                 :     if (NaClSocketGetLastError() == WSAEWOULDBLOCK) {
     168                 :       if (WaitForSingleObject(socket_event_, INFINITE) == WAIT_FAILED) {
     169                 :         NaClLog(LOG_FATAL,
     170                 :                 "Transport::ReadSomeData: Failed to wait on socket event\n");
     171                 :       }
     172                 :       if (!ResetEvent(socket_event_)) {
     173                 :         NaClLog(LOG_FATAL,
     174                 :                 "Transport::ReadSomeData: Failed to reset socket event\n");
     175                 :       }
     176                 :       continue;
     177                 :     }
     178                 : #endif
     179               5 :     if (NaClSocketGetLastError() != EINTR)
     180               5 :       return false;
     181               0 :   }
     182            1793 : }
     183                 : 
     184           23767 : bool Transport::Read(void *ptr, int32_t len) {
     185           23767 :   char *dst = static_cast<char *>(ptr);
     186           23767 :   if (pos_ < size_) {
     187           21986 :     CopyFromBuffer(&dst, &len);
     188           21986 :   }
     189           49286 :   while (len > 0) {
     190            1781 :     pos_ = 0;
     191            1781 :     size_ = 0;
     192            1781 :     if (!ReadSomeData()) {
     193              29 :       return false;
     194                 :     }
     195            1752 :     CopyFromBuffer(&dst, &len);
     196            1752 :   }
     197           23738 :   return true;
     198           23767 : }
     199                 : 
     200            2349 : bool Transport::Write(const void *ptr, int32_t len) {
     201            2349 :   const char *src = static_cast<const char *>(ptr);
     202            7047 :   while (len > 0) {
     203            2349 :     int result = ::send(handle_, src, len, 0);
     204            2349 :     if (result > 0) {
     205            2349 :       src += result;
     206            2349 :       len -= result;
     207            2349 :       continue;
     208                 :     }
     209               0 :     if (result == 0) {
     210               0 :       return false;
     211                 :     }
     212               0 :     if (NaClSocketGetLastError() != EINTR) {
     213               0 :       return false;
     214                 :     }
     215               0 :   }
     216            2349 :   return true;
     217            2349 : }
     218                 : 
     219             148 : void Transport::WaitForDebugStubEvent(struct NaClApp *nap,
     220             148 :                                       bool ignore_input_from_gdb) {
     221             148 :   bool wait = true;
     222                 :   // If we are told to ignore messages from gdb, we will exit from this
     223                 :   // function only if new data is sent by gdb.
     224             296 :   if ((pos_ < size_ && !ignore_input_from_gdb) ||
     225                 :       nap->faulted_thread_count > 0) {
     226                 :     // Clear faulted thread events to save debug stub loop iterations.
     227              84 :     wait = false;
     228              84 :   }
     229                 : #if NACL_WINDOWS
     230                 :   HANDLE handles[2];
     231                 :   handles[0] = nap->faulted_thread_event;
     232                 :   handles[1] = socket_event_;
     233                 :   int count = size_ < kBufSize ? 2 : 1;
     234                 :   int result = WaitForMultipleObjects(count, handles, FALSE,
     235                 :                                       wait ? INFINITE : 0);
     236                 :   if (result == WAIT_OBJECT_0 + 1) {
     237                 :     if (!ResetEvent(socket_event_)) {
     238                 :       NaClLog(LOG_FATAL,
     239                 :               "Transport::WaitForDebugStubEvent: "
     240                 :               "Failed to reset socket event\n");
     241                 :     }
     242                 :     return;
     243                 :   }
     244                 :   if (result == WAIT_TIMEOUT || result == WAIT_OBJECT_0)
     245                 :     return;
     246                 :   NaClLog(LOG_FATAL,
     247                 :           "Transport::WaitForDebugStubEvent: Wait for events failed\n");
     248                 : #else
     249             148 :   fd_set fds;
     250                 : 
     251             148 :   FD_ZERO(&fds);
     252             296 :   FD_SET(nap->faulted_thread_fd_read, &fds);
     253             148 :   int max_fd = nap->faulted_thread_fd_read;
     254             148 :   if (size_ < kBufSize) {
     255             296 :     FD_SET(handle_, &fds);
     256             148 :     max_fd = std::max(max_fd, handle_);
     257             148 :   }
     258                 : 
     259             148 :   int ret;
     260                 :   // We don't need sleep-polling on Linux now, so we set either zero or infinite
     261                 :   // timeout.
     262             148 :   if (wait) {
     263              64 :     ret = select(max_fd + 1, &fds, NULL, NULL, NULL);
     264              64 :   } else {
     265              84 :     struct timeval timeout;
     266              84 :     timeout.tv_sec = 0;
     267              84 :     timeout.tv_usec = 0;
     268              84 :     ret = select(max_fd + 1, &fds, NULL, NULL, &timeout);
     269                 :   }
     270             148 :   if (ret < 0) {
     271               0 :     NaClLog(LOG_FATAL,
     272                 :             "Transport::WaitForDebugStubEvent: Failed to wait for "
     273                 :             "debug stub event\n");
     274               0 :   }
     275                 : 
     276             148 :   if (ret > 0) {
     277              84 :     if (FD_ISSET(nap->faulted_thread_fd_read, &fds)) {
     278              83 :       char buf[16];
     279              83 :       if (read(nap->faulted_thread_fd_read, &buf, sizeof(buf)) < 0) {
     280               0 :         NaClLog(LOG_FATAL,
     281                 :                 "Transport::WaitForDebugStubEvent: Failed to read from "
     282                 :                 "debug stub event pipe fd\n");
     283               0 :       }
     284              83 :     }
     285              84 :     if (FD_ISSET(handle_, &fds))
     286              12 :       ReadSomeData();
     287              84 :   }
     288                 : #endif
     289             148 : }
     290                 : 
     291                 : // Convert string in the form of [addr][:port] where addr is a
     292                 : // IPv4 address or host name, and port is a 16b tcp/udp port.
     293                 : // Both portions are optional, and only the portion of the address
     294                 : // provided is updated.  Values are provided in network order.
     295              17 : static bool StringToIPv4(const std::string &instr, uint32_t *addr,
     296              17 :                          uint16_t *port) {
     297                 :   // Make a copy so the are unchanged unless we succeed
     298              17 :   uint32_t outaddr = *addr;
     299              17 :   uint16_t outport = *port;
     300                 : 
     301                 :   // Substrings of the full ADDR:PORT
     302              17 :   std::string addrstr;
     303              17 :   std::string portstr;
     304                 : 
     305                 :   // We should either have one or two tokens in the form of:
     306                 :   //  IP - IP, NUL
     307                 :   //  IP: -  IP, NUL
     308                 :   //  :PORT - NUL, PORT
     309                 :   //  IP:PORT - IP, PORT
     310                 : 
     311                 :   // Search for the port marker
     312              34 :   size_t portoff = instr.find(':');
     313                 : 
     314                 :   // If we found a ":" before the end, get both substrings
     315              51 :   if ((portoff != std::string::npos) && (portoff + 1 < instr.size())) {
     316              51 :     addrstr = instr.substr(0, portoff);
     317              51 :     portstr = instr.substr(portoff + 1, std::string::npos);
     318              17 :   } else {
     319                 :     // otherwise the entire string is the addr portion.
     320               0 :     addrstr = instr;
     321               0 :     portstr = "";
     322                 :   }
     323                 : 
     324                 :   // If the address portion was provided, update it
     325              34 :   if (addrstr.size()) {
     326                 :     // Special case 0.0.0.0 which means any IPv4 interface
     327              34 :     if (addrstr == "0.0.0.0") {
     328               0 :       outaddr = 0;
     329               0 :     } else {
     330              51 :       struct hostent *host = gethostbyname(addrstr.data());
     331                 : 
     332                 :       // Check that we found an IPv4 host
     333              34 :       if ((NULL == host) || (AF_INET != host->h_addrtype)) return false;
     334                 : 
     335                 :       // Make sure the IP list isn't empty.
     336              17 :       if (0 == host->h_addr_list[0]) return false;
     337                 : 
     338                 :       // Use the first address in the array of address pointers.
     339              17 :       uint32_t **addrarray = reinterpret_cast<uint32_t**>(host->h_addr_list);
     340              17 :       outaddr = *addrarray[0];
     341                 :     }
     342              17 :   }
     343                 : 
     344                 :   // if the port portion was provided, then update it
     345              34 :   if (portstr.size()) {
     346              51 :     int val = atoi(portstr.data());
     347              34 :     if ((val < 0) || (val > 65535)) return false;
     348              34 :     outport = ntohs(static_cast<uint16_t>(val));
     349              17 :   }
     350                 : 
     351                 :   // We haven't failed, so set the values
     352              17 :   *addr = outaddr;
     353              17 :   *port = outport;
     354              51 :   return true;
     355              17 : }
     356                 : 
     357              17 : static bool BuildSockAddr(const char *addr, struct sockaddr_in *sockaddr) {
     358              34 :   std::string addrstr = addr;
     359              17 :   uint32_t *pip = reinterpret_cast<uint32_t*>(&sockaddr->sin_addr.s_addr);
     360              17 :   uint16_t *pport = reinterpret_cast<uint16_t*>(&sockaddr->sin_port);
     361                 : 
     362              17 :   sockaddr->sin_family = AF_INET;
     363              17 :   return StringToIPv4(addrstr, pip, pport);
     364              17 : }
     365                 : 
     366              34 : SocketBinding::SocketBinding(NaClSocketHandle socket_handle)
     367              17 :     : socket_handle_(socket_handle) {
     368              34 : }
     369                 : 
     370              17 : SocketBinding *SocketBinding::Bind(const char *addr) {
     371              17 :   NaClSocketHandle socket_handle = ::socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
     372              17 :   if (socket_handle == NACL_INVALID_SOCKET) {
     373               0 :     NaClLog(LOG_ERROR, "Failed to create socket.\n");
     374               0 :     return NULL;
     375                 :   }
     376              17 :   struct sockaddr_in saddr;
     377                 :   // Clearing sockaddr_in first appears to be necessary on Mac OS X.
     378              17 :   memset(&saddr, 0, sizeof(saddr));
     379              17 :   socklen_t addrlen = static_cast<socklen_t>(sizeof(saddr));
     380              17 :   saddr.sin_family = AF_INET;
     381              17 :   saddr.sin_addr.s_addr = htonl(0x7F000001);
     382              17 :   saddr.sin_port = htons(4014);
     383                 : 
     384                 :   // Override portions address that are provided
     385              34 :   if (addr) BuildSockAddr(addr, &saddr);
     386                 : 
     387                 : #if NACL_WINDOWS
     388                 :   // On Windows, SO_REUSEADDR has a different meaning than on POSIX systems.
     389                 :   // SO_REUSEADDR allows hijacking of an open socket by another process.
     390                 :   // The SO_EXCLUSIVEADDRUSE flag prevents this behavior.
     391                 :   // See: http://msdn.microsoft.com/en-us/library/windows/desktop/ms740621(v=vs.85).aspx
     392                 :   //
     393                 :   // Additionally, unlike POSIX, TCP server sockets can be bound to
     394                 :   // ports in the TIME_WAIT state, without setting SO_REUSEADDR.
     395                 :   int exclusive_address = 1;
     396                 :   if (setsockopt(socket_handle, SOL_SOCKET, SO_EXCLUSIVEADDRUSE,
     397                 :                  reinterpret_cast<char *>(&exclusive_address),
     398                 :                  sizeof(exclusive_address))) {
     399                 :     NaClLog(LOG_WARNING, "Failed to set SO_EXCLUSIVEADDRUSE option.\n");
     400                 :   }
     401                 : #else
     402                 :   // On POSIX, this is necessary to ensure that the TCP port is released
     403                 :   // promptly when sel_ldr exits.  Without this, the TCP port might
     404                 :   // only be released after a timeout, and later processes can fail
     405                 :   // to bind it.
     406              17 :   int reuse_address = 1;
     407              17 :   if (setsockopt(socket_handle, SOL_SOCKET, SO_REUSEADDR,
     408                 :                  reinterpret_cast<char *>(&reuse_address),
     409                 :                  sizeof(reuse_address))) {
     410               0 :     NaClLog(LOG_WARNING, "Failed to set SO_REUSEADDR option.\n");
     411               0 :   }
     412                 : #endif
     413                 : 
     414              17 :   struct sockaddr *psaddr = reinterpret_cast<struct sockaddr *>(&saddr);
     415              17 :   if (bind(socket_handle, psaddr, addrlen)) {
     416               0 :     NaClLog(LOG_ERROR, "Failed to bind server.\n");
     417               0 :     return NULL;
     418                 :   }
     419                 : 
     420              17 :   if (listen(socket_handle, 1)) {
     421               0 :     NaClLog(LOG_ERROR, "Failed to listen.\n");
     422               0 :     return NULL;
     423                 :   }
     424              34 :   return new SocketBinding(socket_handle);
     425              17 : }
     426                 : 
     427                 : ITransport *SocketBinding::AcceptConnection() {
     428              29 :   NaClSocketHandle socket = ::accept(socket_handle_, NULL, 0);
     429              29 :   if (socket != NACL_INVALID_SOCKET) {
     430                 :     // Do not delay sending small packets.  This significantly speeds up
     431                 :     // remote debugging.  Debug stub uses buffering to send outgoing packets so
     432                 :     // they are not split into more TCP packets than necessary.
     433              29 :     int nodelay = 1;
     434              29 :     if (setsockopt(socket, IPPROTO_TCP, TCP_NODELAY,
     435                 :                    reinterpret_cast<char *>(&nodelay),
     436                 :                    sizeof(nodelay))) {
     437               0 :       NaClLog(LOG_WARNING, "Failed to set TCP_NODELAY option.\n");
     438               0 :     }
     439              58 :     return new Transport(socket);
     440                 :   }
     441               0 :   return NULL;
     442              29 : }
     443                 : 
     444                 : uint16_t SocketBinding::GetBoundPort() {
     445              17 :   struct sockaddr_in saddr;
     446              17 :   struct sockaddr *psaddr = reinterpret_cast<struct sockaddr *>(&saddr);
     447                 :   // Clearing sockaddr_in first appears to be necessary on Mac OS X.
     448              17 :   memset(&saddr, 0, sizeof(saddr));
     449              17 :   socklen_t addrlen = static_cast<socklen_t>(sizeof(saddr));
     450              17 :   if (::getsockname(socket_handle_, psaddr, &addrlen)) {
     451               0 :     NaClLog(LOG_ERROR, "Failed to retrieve bound address.\n");
     452               0 :     return 0;
     453                 :   }
     454              17 :   return ntohs(saddr.sin_port);
     455              17 : }
     456                 : 
     457                 : }  // namespace port

Generated by: LCOV version 1.7