130fdc8d8SChris Lattner //===-- RNBSocket.cpp -------------------------------------------*- C++ -*-===// 230fdc8d8SChris Lattner // 330fdc8d8SChris Lattner // The LLVM Compiler Infrastructure 430fdc8d8SChris Lattner // 530fdc8d8SChris Lattner // This file is distributed under the University of Illinois Open Source 630fdc8d8SChris Lattner // License. See LICENSE.TXT for details. 730fdc8d8SChris Lattner // 830fdc8d8SChris Lattner //===----------------------------------------------------------------------===// 930fdc8d8SChris Lattner // 1030fdc8d8SChris Lattner // Created by Greg Clayton on 12/12/07. 1130fdc8d8SChris Lattner // 1230fdc8d8SChris Lattner //===----------------------------------------------------------------------===// 1330fdc8d8SChris Lattner 1430fdc8d8SChris Lattner #include "RNBSocket.h" 15b9c1b51eSKate Stone #include "DNBError.h" 16b9c1b51eSKate Stone #include "DNBLog.h" 1795bf0fd3SGreg Clayton #include <arpa/inet.h> 1830fdc8d8SChris Lattner #include <errno.h> 1930fdc8d8SChris Lattner #include <fcntl.h> 20*d01a2fa3SChris Bieneman #include <map> 2195bf0fd3SGreg Clayton #include <netdb.h> 2230fdc8d8SChris Lattner #include <netinet/in.h> 2330fdc8d8SChris Lattner #include <netinet/tcp.h> 24*d01a2fa3SChris Bieneman #include <sys/event.h> 2530fdc8d8SChris Lattner #include <termios.h> 26*d01a2fa3SChris Bieneman #include <vector> 27*d01a2fa3SChris Bieneman 28*d01a2fa3SChris Bieneman #include "lldb/Host/SocketAddress.h" 2930fdc8d8SChris Lattner 3042999a48SJason Molenda #ifdef WITH_LOCKDOWN 3130fdc8d8SChris Lattner #include "lockdown.h" 3230fdc8d8SChris Lattner #endif 3330fdc8d8SChris Lattner 3430fdc8d8SChris Lattner /* Once we have a RNBSocket object with a port # specified, 3530fdc8d8SChris Lattner this function is called to wait for an incoming connection. 3630fdc8d8SChris Lattner This function blocks while waiting for that connection. */ 3730fdc8d8SChris Lattner 38b9c1b51eSKate Stone bool ResolveIPV4HostName(const char *hostname, in_addr_t &addr) { 39b9c1b51eSKate Stone if (hostname == NULL || hostname[0] == '\0' || 40fd23889eSGreg Clayton strcmp(hostname, "localhost") == 0 || 41b9c1b51eSKate Stone strcmp(hostname, "127.0.0.1") == 0) { 42fd23889eSGreg Clayton addr = htonl(INADDR_LOOPBACK); 43fd23889eSGreg Clayton return true; 44b9c1b51eSKate Stone } else if (strcmp(hostname, "*") == 0) { 45fd23889eSGreg Clayton addr = htonl(INADDR_ANY); 46fd23889eSGreg Clayton return true; 47b9c1b51eSKate Stone } else { 48fd23889eSGreg Clayton // See if an IP address was specified as numbers 49fd23889eSGreg Clayton int inet_pton_result = ::inet_pton(AF_INET, hostname, &addr); 50fd23889eSGreg Clayton 51fd23889eSGreg Clayton if (inet_pton_result == 1) 52fd23889eSGreg Clayton return true; 53fd23889eSGreg Clayton 54fd23889eSGreg Clayton struct hostent *host_entry = gethostbyname(hostname); 55b9c1b51eSKate Stone if (host_entry) { 56b9c1b51eSKate Stone std::string ip_str( 57b9c1b51eSKate Stone ::inet_ntoa(*(struct in_addr *)*host_entry->h_addr_list)); 58fd23889eSGreg Clayton inet_pton_result = ::inet_pton(AF_INET, ip_str.c_str(), &addr); 59fd23889eSGreg Clayton if (inet_pton_result == 1) 60fd23889eSGreg Clayton return true; 61fd23889eSGreg Clayton } 62fd23889eSGreg Clayton } 63fd23889eSGreg Clayton return false; 64fd23889eSGreg Clayton } 65fd23889eSGreg Clayton 66b9c1b51eSKate Stone rnb_err_t RNBSocket::Listen(const char *listen_host, uint16_t port, 67b9c1b51eSKate Stone PortBoundCallback callback, 68b9c1b51eSKate Stone const void *callback_baton) { 69b9c1b51eSKate Stone // DNBLogThreadedIf(LOG_RNB_COMM, "%8u RNBSocket::%s called", 70b9c1b51eSKate Stone // (uint32_t)m_timer.ElapsedMicroSeconds(true), __FUNCTION__); 7130fdc8d8SChris Lattner // Disconnect without saving errno 7230fdc8d8SChris Lattner Disconnect(false); 7330fdc8d8SChris Lattner 7430fdc8d8SChris Lattner DNBError err; 75*d01a2fa3SChris Bieneman int queue_id = kqueue(); 76*d01a2fa3SChris Bieneman if (queue_id < 0) { 77*d01a2fa3SChris Bieneman err.SetError(errno, DNBError::MachKernel); 78*d01a2fa3SChris Bieneman err.LogThreaded("error: failed to create kqueue."); 7930fdc8d8SChris Lattner return rnb_err; 8030fdc8d8SChris Lattner } 8130fdc8d8SChris Lattner 82*d01a2fa3SChris Bieneman std::map<int, lldb_private::SocketAddress> sockets; 83*d01a2fa3SChris Bieneman auto addresses = 84*d01a2fa3SChris Bieneman lldb_private::SocketAddress::GetAddressInfo(listen_host, NULL); 8530fdc8d8SChris Lattner 86*d01a2fa3SChris Bieneman for (auto address : addresses) { 87*d01a2fa3SChris Bieneman int sock_fd = ::socket(address.GetFamily(), SOCK_STREAM, IPPROTO_TCP); 88*d01a2fa3SChris Bieneman if (sock_fd == -1) 89*d01a2fa3SChris Bieneman continue; 9030fdc8d8SChris Lattner 91*d01a2fa3SChris Bieneman SetSocketOption(sock_fd, SOL_SOCKET, SO_REUSEADDR, 1); 92*d01a2fa3SChris Bieneman 93*d01a2fa3SChris Bieneman address.SetPort(port); 94*d01a2fa3SChris Bieneman 95*d01a2fa3SChris Bieneman int error = ::bind(sock_fd, &address.sockaddr(), address.GetLength()); 96*d01a2fa3SChris Bieneman if (error == -1) { 97*d01a2fa3SChris Bieneman ClosePort(sock_fd, false); 98*d01a2fa3SChris Bieneman continue; 9930fdc8d8SChris Lattner } 10030fdc8d8SChris Lattner 101*d01a2fa3SChris Bieneman error = ::listen(sock_fd, 5); 102*d01a2fa3SChris Bieneman if (error == -1) { 103*d01a2fa3SChris Bieneman ClosePort(sock_fd, false); 104*d01a2fa3SChris Bieneman continue; 105*d01a2fa3SChris Bieneman } 106*d01a2fa3SChris Bieneman 107*d01a2fa3SChris Bieneman // We were asked to listen on port zero which means we must now read the 108*d01a2fa3SChris Bieneman // actual port that was given to us as port zero is a special code for "find 109*d01a2fa3SChris Bieneman // an open port for me". This will only execute on the first socket created, 110*d01a2fa3SChris Bieneman // subesquent sockets will reuse this port number. 111b9c1b51eSKate Stone if (port == 0) { 112*d01a2fa3SChris Bieneman socklen_t sa_len = address.GetLength(); 113*d01a2fa3SChris Bieneman if (getsockname(sock_fd, &address.sockaddr(), &sa_len) == 0) 114*d01a2fa3SChris Bieneman port = address.GetPort(); 11591a9b247SGreg Clayton } 11691a9b247SGreg Clayton 117*d01a2fa3SChris Bieneman sockets[sock_fd] = address; 118*d01a2fa3SChris Bieneman } 119*d01a2fa3SChris Bieneman 120*d01a2fa3SChris Bieneman if (sockets.size() == 0) { 121*d01a2fa3SChris Bieneman err.SetError(errno, DNBError::POSIX); 122*d01a2fa3SChris Bieneman err.LogThreaded("::listen or ::bind failed"); 123*d01a2fa3SChris Bieneman return rnb_err; 124*d01a2fa3SChris Bieneman } 125*d01a2fa3SChris Bieneman 126*d01a2fa3SChris Bieneman if (callback) 127*d01a2fa3SChris Bieneman callback(callback_baton, port); 128*d01a2fa3SChris Bieneman 129*d01a2fa3SChris Bieneman std::vector<struct kevent> events; 130*d01a2fa3SChris Bieneman events.resize(sockets.size()); 131*d01a2fa3SChris Bieneman int i = 0; 132*d01a2fa3SChris Bieneman for (auto socket : sockets) { 133*d01a2fa3SChris Bieneman EV_SET(&events[i++], socket.first, EVFILT_READ, EV_ADD, 0, 0, 0); 134*d01a2fa3SChris Bieneman } 135fd23889eSGreg Clayton 136fd23889eSGreg Clayton bool accept_connection = false; 137fd23889eSGreg Clayton 138fd23889eSGreg Clayton // Loop until we are happy with our connection 139b9c1b51eSKate Stone while (!accept_connection) { 140fd23889eSGreg Clayton 141*d01a2fa3SChris Bieneman struct kevent event_list[4]; 142*d01a2fa3SChris Bieneman int num_events = 143*d01a2fa3SChris Bieneman kevent(queue_id, events.data(), events.size(), event_list, 4, NULL); 144*d01a2fa3SChris Bieneman 145*d01a2fa3SChris Bieneman if (num_events < 0) { 146*d01a2fa3SChris Bieneman err.SetError(errno, DNBError::MachKernel); 147*d01a2fa3SChris Bieneman err.LogThreaded("error: kevent() failed."); 148*d01a2fa3SChris Bieneman } 149*d01a2fa3SChris Bieneman 150*d01a2fa3SChris Bieneman for (int i = 0; i < num_events; ++i) { 151*d01a2fa3SChris Bieneman auto sock_fd = event_list[i].ident; 152*d01a2fa3SChris Bieneman auto socket_pair = sockets.find(sock_fd); 153*d01a2fa3SChris Bieneman if (socket_pair == sockets.end()) 154*d01a2fa3SChris Bieneman continue; 155*d01a2fa3SChris Bieneman 156*d01a2fa3SChris Bieneman lldb_private::SocketAddress &addr_in = socket_pair->second; 157*d01a2fa3SChris Bieneman lldb_private::SocketAddress accept_addr; 158*d01a2fa3SChris Bieneman socklen_t sa_len = accept_addr.GetMaxLength(); 159*d01a2fa3SChris Bieneman m_fd = ::accept(sock_fd, &accept_addr.sockaddr(), &sa_len); 160*d01a2fa3SChris Bieneman 161*d01a2fa3SChris Bieneman if (m_fd == -1) { 16230fdc8d8SChris Lattner err.SetError(errno, DNBError::POSIX); 163*d01a2fa3SChris Bieneman err.LogThreaded("error: Socket accept failed."); 164*d01a2fa3SChris Bieneman } 16530fdc8d8SChris Lattner 166*d01a2fa3SChris Bieneman if (addr_in.IsAnyAddr()) 167fd23889eSGreg Clayton accept_connection = true; 168b9c1b51eSKate Stone else { 169*d01a2fa3SChris Bieneman if (accept_addr == addr_in) 170fd23889eSGreg Clayton accept_connection = true; 171*d01a2fa3SChris Bieneman else { 172fd23889eSGreg Clayton ::close(m_fd); 173fd23889eSGreg Clayton m_fd = -1; 174*d01a2fa3SChris Bieneman ::fprintf( 175*d01a2fa3SChris Bieneman stderr, 176*d01a2fa3SChris Bieneman "error: rejecting incoming connection from %s (expecting %s)\n", 177*d01a2fa3SChris Bieneman accept_addr.GetIPAddress().c_str(), 178*d01a2fa3SChris Bieneman addr_in.GetIPAddress().c_str()); 179*d01a2fa3SChris Bieneman DNBLogThreaded("error: rejecting connection from %s (expecting %s)\n", 180*d01a2fa3SChris Bieneman accept_addr.GetIPAddress().c_str(), 181*d01a2fa3SChris Bieneman addr_in.GetIPAddress().c_str()); 182fd23889eSGreg Clayton } 183fd23889eSGreg Clayton } 184fd23889eSGreg Clayton } 185*d01a2fa3SChris Bieneman if (err.Fail()) 186*d01a2fa3SChris Bieneman break; 187*d01a2fa3SChris Bieneman } 188*d01a2fa3SChris Bieneman for (auto socket : sockets) { 189*d01a2fa3SChris Bieneman int ListenFd = socket.first; 190*d01a2fa3SChris Bieneman ClosePort(ListenFd, false); 191*d01a2fa3SChris Bieneman } 1928b82f087SGreg Clayton 193*d01a2fa3SChris Bieneman if (err.Fail()) 19430fdc8d8SChris Lattner return rnb_err; 195*d01a2fa3SChris Bieneman 19630fdc8d8SChris Lattner // Keep our TCP packets coming without any delays. 1978b82f087SGreg Clayton SetSocketOption(m_fd, IPPROTO_TCP, TCP_NODELAY, 1); 19830fdc8d8SChris Lattner 19930fdc8d8SChris Lattner return rnb_success; 20030fdc8d8SChris Lattner } 20130fdc8d8SChris Lattner 202b9c1b51eSKate Stone rnb_err_t RNBSocket::Connect(const char *host, uint16_t port) { 203*d01a2fa3SChris Bieneman auto result = rnb_err; 20495bf0fd3SGreg Clayton Disconnect(false); 20595bf0fd3SGreg Clayton 206*d01a2fa3SChris Bieneman auto addresses = lldb_private::SocketAddress::GetAddressInfo(host, NULL); 207*d01a2fa3SChris Bieneman 208*d01a2fa3SChris Bieneman for (auto address : addresses) { 209*d01a2fa3SChris Bieneman m_fd = ::socket(address.GetFamily(), SOCK_STREAM, IPPROTO_TCP); 2108b82f087SGreg Clayton if (m_fd == -1) 211*d01a2fa3SChris Bieneman continue; 21295bf0fd3SGreg Clayton 21395bf0fd3SGreg Clayton // Enable local address reuse 2148b82f087SGreg Clayton SetSocketOption(m_fd, SOL_SOCKET, SO_REUSEADDR, 1); 21595bf0fd3SGreg Clayton 216*d01a2fa3SChris Bieneman address.SetPort(port); 21795bf0fd3SGreg Clayton 218*d01a2fa3SChris Bieneman if (-1 == ::connect(m_fd, &address.sockaddr(), address.GetLength())) { 21995bf0fd3SGreg Clayton Disconnect(false); 220*d01a2fa3SChris Bieneman continue; 22195bf0fd3SGreg Clayton } 2228b82f087SGreg Clayton SetSocketOption(m_fd, IPPROTO_TCP, TCP_NODELAY, 1); 223*d01a2fa3SChris Bieneman 224*d01a2fa3SChris Bieneman result = rnb_success; 225*d01a2fa3SChris Bieneman break; 226*d01a2fa3SChris Bieneman } 227*d01a2fa3SChris Bieneman return result; 22895bf0fd3SGreg Clayton } 22995bf0fd3SGreg Clayton 230b9c1b51eSKate Stone rnb_err_t RNBSocket::useFD(int fd) { 23142999a48SJason Molenda if (fd < 0) { 23242999a48SJason Molenda DNBLogThreadedIf(LOG_RNB_COMM, "Bad file descriptor passed in."); 23342999a48SJason Molenda return rnb_err; 23442999a48SJason Molenda } 23542999a48SJason Molenda 23642999a48SJason Molenda m_fd = fd; 23742999a48SJason Molenda return rnb_success; 23842999a48SJason Molenda } 23942999a48SJason Molenda 24042999a48SJason Molenda #ifdef WITH_LOCKDOWN 241b9c1b51eSKate Stone rnb_err_t RNBSocket::ConnectToService() { 24230fdc8d8SChris Lattner DNBLog("Connecting to com.apple.%s service...", DEBUGSERVER_PROGRAM_NAME); 24330fdc8d8SChris Lattner // Disconnect from any previous connections 24430fdc8d8SChris Lattner Disconnect(false); 245b9c1b51eSKate Stone if (::secure_lockdown_checkin(&m_ld_conn, NULL, NULL) != kLDESuccess) { 246b9c1b51eSKate Stone DNBLogThreadedIf(LOG_RNB_COMM, 247b9c1b51eSKate Stone "::secure_lockdown_checkin(&m_fd, NULL, NULL) failed"); 24894379541SJason Molenda m_fd = -1; 24930fdc8d8SChris Lattner return rnb_not_connected; 25030fdc8d8SChris Lattner } 2517eaf54d8SJason Molenda m_fd = ::lockdown_get_socket(m_ld_conn); 252b9c1b51eSKate Stone if (m_fd == -1) { 2537eaf54d8SJason Molenda DNBLogThreadedIf(LOG_RNB_COMM, "::lockdown_get_socket() failed"); 2547eaf54d8SJason Molenda return rnb_not_connected; 2557eaf54d8SJason Molenda } 2568b82f087SGreg Clayton m_fd_from_lockdown = true; 25730fdc8d8SChris Lattner return rnb_success; 25830fdc8d8SChris Lattner } 25930fdc8d8SChris Lattner #endif 26030fdc8d8SChris Lattner 261b9c1b51eSKate Stone rnb_err_t RNBSocket::OpenFile(const char *path) { 26230fdc8d8SChris Lattner DNBError err; 2638b82f087SGreg Clayton m_fd = open(path, O_RDWR); 264b9c1b51eSKate Stone if (m_fd == -1) { 26530fdc8d8SChris Lattner err.SetError(errno, DNBError::POSIX); 26630fdc8d8SChris Lattner err.LogThreaded("can't open file '%s'", path); 26730fdc8d8SChris Lattner return rnb_not_connected; 268b9c1b51eSKate Stone } else { 26930fdc8d8SChris Lattner struct termios stdin_termios; 27030fdc8d8SChris Lattner 271b9c1b51eSKate Stone if (::tcgetattr(m_fd, &stdin_termios) == 0) { 27230fdc8d8SChris Lattner stdin_termios.c_lflag &= ~ECHO; // Turn off echoing 27330fdc8d8SChris Lattner stdin_termios.c_lflag &= ~ICANON; // Get one char at a time 2748b82f087SGreg Clayton ::tcsetattr(m_fd, TCSANOW, &stdin_termios); 27530fdc8d8SChris Lattner } 27630fdc8d8SChris Lattner } 27730fdc8d8SChris Lattner return rnb_success; 27830fdc8d8SChris Lattner } 27930fdc8d8SChris Lattner 280b9c1b51eSKate Stone int RNBSocket::SetSocketOption(int fd, int level, int option_name, 281b9c1b51eSKate Stone int option_value) { 282b9c1b51eSKate Stone return ::setsockopt(fd, level, option_name, &option_value, 283b9c1b51eSKate Stone sizeof(option_value)); 28430fdc8d8SChris Lattner } 28530fdc8d8SChris Lattner 286b9c1b51eSKate Stone rnb_err_t RNBSocket::Disconnect(bool save_errno) { 28742999a48SJason Molenda #ifdef WITH_LOCKDOWN 288b9c1b51eSKate Stone if (m_fd_from_lockdown) { 2898b82f087SGreg Clayton m_fd_from_lockdown = false; 2907eaf54d8SJason Molenda m_fd = -1; 2912d512bcaSJim Ingham lockdown_disconnect(m_ld_conn); 2927eaf54d8SJason Molenda return rnb_success; 2937eaf54d8SJason Molenda } 29442999a48SJason Molenda #endif 2958b82f087SGreg Clayton return ClosePort(m_fd, save_errno); 29630fdc8d8SChris Lattner } 29730fdc8d8SChris Lattner 298b9c1b51eSKate Stone rnb_err_t RNBSocket::Read(std::string &p) { 29930fdc8d8SChris Lattner char buf[1024]; 30030fdc8d8SChris Lattner p.clear(); 30130fdc8d8SChris Lattner 30230fdc8d8SChris Lattner // Note that BUF is on the stack so we must be careful to keep any 30330fdc8d8SChris Lattner // writes to BUF from overflowing or we'll have security issues. 30430fdc8d8SChris Lattner 3058b82f087SGreg Clayton if (m_fd == -1) 30630fdc8d8SChris Lattner return rnb_err; 30730fdc8d8SChris Lattner 308b9c1b51eSKate Stone // DNBLogThreadedIf(LOG_RNB_COMM, "%8u RNBSocket::%s calling read()", 309b9c1b51eSKate Stone // (uint32_t)m_timer.ElapsedMicroSeconds(true), __FUNCTION__); 31030fdc8d8SChris Lattner DNBError err; 311ee2ed525SGreg Clayton ssize_t bytesread = read(m_fd, buf, sizeof(buf)); 31230fdc8d8SChris Lattner if (bytesread <= 0) 31330fdc8d8SChris Lattner err.SetError(errno, DNBError::POSIX); 31430fdc8d8SChris Lattner else 31530fdc8d8SChris Lattner p.append(buf, bytesread); 31630fdc8d8SChris Lattner 31730fdc8d8SChris Lattner if (err.Fail() || DNBLogCheckLogBit(LOG_RNB_COMM)) 318b9c1b51eSKate Stone err.LogThreaded("::read ( %i, %p, %llu ) => %i", m_fd, buf, sizeof(buf), 319b9c1b51eSKate Stone (uint64_t)bytesread); 32030fdc8d8SChris Lattner 321b9c1b51eSKate Stone // Our port went away - we have to mark this so IsConnected will return the 322b9c1b51eSKate Stone // truth. 323b9c1b51eSKate Stone if (bytesread == 0) { 3248b82f087SGreg Clayton m_fd = -1; 32530fdc8d8SChris Lattner return rnb_not_connected; 326b9c1b51eSKate Stone } else if (bytesread == -1) { 3278b82f087SGreg Clayton m_fd = -1; 32830fdc8d8SChris Lattner return rnb_err; 32930fdc8d8SChris Lattner } 33030fdc8d8SChris Lattner // Strip spaces from the end of the buffer 33130fdc8d8SChris Lattner while (!p.empty() && isspace(p[p.size() - 1])) 33230fdc8d8SChris Lattner p.erase(p.size() - 1); 33330fdc8d8SChris Lattner 33430fdc8d8SChris Lattner // Most data in the debugserver packets valid printable characters... 33530fdc8d8SChris Lattner DNBLogThreadedIf(LOG_RNB_COMM, "read: %s", p.c_str()); 33630fdc8d8SChris Lattner return rnb_success; 33730fdc8d8SChris Lattner } 33830fdc8d8SChris Lattner 339b9c1b51eSKate Stone rnb_err_t RNBSocket::Write(const void *buffer, size_t length) { 3408b82f087SGreg Clayton if (m_fd == -1) 34130fdc8d8SChris Lattner return rnb_err; 34230fdc8d8SChris Lattner 34330fdc8d8SChris Lattner DNBError err; 344ee2ed525SGreg Clayton ssize_t bytessent = write(m_fd, buffer, length); 34530fdc8d8SChris Lattner if (bytessent < 0) 34630fdc8d8SChris Lattner err.SetError(errno, DNBError::POSIX); 34730fdc8d8SChris Lattner 34830fdc8d8SChris Lattner if (err.Fail() || DNBLogCheckLogBit(LOG_RNB_COMM)) 349b9c1b51eSKate Stone err.LogThreaded("::write ( socket = %i, buffer = %p, length = %llu) => %i", 350b9c1b51eSKate Stone m_fd, buffer, length, (uint64_t)bytessent); 35130fdc8d8SChris Lattner 35230fdc8d8SChris Lattner if (bytessent < 0) 35330fdc8d8SChris Lattner return rnb_err; 35430fdc8d8SChris Lattner 355a026de05SBruce Mitchener if ((size_t)bytessent != length) 35630fdc8d8SChris Lattner return rnb_err; 35730fdc8d8SChris Lattner 358b9c1b51eSKate Stone DNBLogThreadedIf( 359b9c1b51eSKate Stone LOG_RNB_PACKETS, "putpkt: %*s", (int)length, 360b9c1b51eSKate Stone (char *) 361b9c1b51eSKate Stone buffer); // All data is string based in debugserver, so this is safe 362490fbbe2SGreg Clayton DNBLogThreadedIf(LOG_RNB_COMM, "sent: %*s", (int)length, (char *)buffer); 36330fdc8d8SChris Lattner 36430fdc8d8SChris Lattner return rnb_success; 36530fdc8d8SChris Lattner } 36630fdc8d8SChris Lattner 367b9c1b51eSKate Stone rnb_err_t RNBSocket::ClosePort(int &fd, bool save_errno) { 36830fdc8d8SChris Lattner int close_err = 0; 369b9c1b51eSKate Stone if (fd > 0) { 37030fdc8d8SChris Lattner errno = 0; 37130fdc8d8SChris Lattner close_err = close(fd); 37230fdc8d8SChris Lattner fd = -1; 37330fdc8d8SChris Lattner } 37430fdc8d8SChris Lattner return close_err != 0 ? rnb_err : rnb_success; 37530fdc8d8SChris Lattner } 376