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 <errno.h> 16 #include <fcntl.h> 17 #include <netinet/in.h> 18 #include <netinet/tcp.h> 19 #include <termios.h> 20 #include "DNBLog.h" 21 #include "DNBError.h" 22 23 #if defined (__arm__) 24 #include "lockdown.h" 25 #endif 26 27 /* Once we have a RNBSocket object with a port # specified, 28 this function is called to wait for an incoming connection. 29 This function blocks while waiting for that connection. */ 30 31 rnb_err_t 32 RNBSocket::Listen (in_port_t listen_port_num) 33 { 34 //DNBLogThreadedIf(LOG_RNB_COMM, "%8u RNBSocket::%s called", (uint32_t)m_timer.ElapsedMicroSeconds(true), __FUNCTION__); 35 // Disconnect without saving errno 36 Disconnect (false); 37 38 DNBError err; 39 int listen_port = ::socket (AF_INET, SOCK_STREAM, IPPROTO_TCP); 40 if (listen_port == -1) 41 err.SetError(errno, DNBError::POSIX); 42 43 if (err.Fail() || DNBLogCheckLogBit(LOG_RNB_COMM)) 44 err.LogThreaded("::socket ( domain = AF_INET, type = SOCK_STREAM, protocol = IPPROTO_TCP ) => socket = %i", listen_port); 45 46 if (err.Fail()) 47 return rnb_err; 48 49 // enable local address reuse 50 SetSocketOption (listen_port, SOL_SOCKET, SO_REUSEADDR, 1); 51 52 struct sockaddr_in sa; 53 ::memset (&sa, 0, sizeof sa); 54 sa.sin_len = sizeof sa; 55 sa.sin_family = AF_INET; 56 sa.sin_port = htons (listen_port_num); 57 sa.sin_addr.s_addr = htonl (INADDR_ANY); 58 59 int error = ::bind (listen_port, (struct sockaddr *) &sa, sizeof(sa)); 60 if (error == -1) 61 err.SetError(errno, DNBError::POSIX); 62 63 if (err.Fail() || DNBLogCheckLogBit(LOG_RNB_COMM)) 64 err.LogThreaded("::bind ( socket = %i, (struct sockaddr *) &sa, sizeof(sa)) )", listen_port); 65 66 if (err.Fail()) 67 { 68 ClosePort (listen_port, false); 69 return rnb_err; 70 } 71 72 error = ::listen (listen_port, 1); 73 if (error == -1) 74 err.SetError(errno, DNBError::POSIX); 75 76 if (err.Fail() || DNBLogCheckLogBit(LOG_RNB_COMM)) 77 err.LogThreaded("::listen ( socket = %i, backlog = 1 )", listen_port); 78 79 if (err.Fail()) 80 { 81 ClosePort (listen_port, false); 82 return rnb_err; 83 } 84 85 m_conn_port = ::accept (listen_port, NULL, 0); 86 if (m_conn_port == -1) 87 err.SetError(errno, DNBError::POSIX); 88 89 if (err.Fail() || DNBLogCheckLogBit(LOG_RNB_COMM)) 90 err.LogThreaded("::accept ( socket = %i, address = NULL, address_len = 0 )", listen_port); 91 92 if (err.Fail()) 93 { 94 ClosePort (listen_port, false); 95 return rnb_err; 96 } 97 else 98 { 99 // We are done with the listen port 100 ClosePort (listen_port, false); 101 102 // Keep our TCP packets coming without any delays. 103 SetSocketOption (m_conn_port, IPPROTO_TCP, TCP_NODELAY, 1); 104 } 105 106 return rnb_success; 107 } 108 109 #if defined (__arm__) 110 rnb_err_t 111 RNBSocket::ConnectToService() 112 { 113 DNBLog("Connecting to com.apple.%s service...", DEBUGSERVER_PROGRAM_NAME); 114 // Disconnect from any previous connections 115 Disconnect(false); 116 117 m_conn_port = ::lockdown_checkin (NULL, NULL); 118 if (m_conn_port == -1) 119 { 120 DNBLogThreadedIf(LOG_RNB_COMM, "::lockdown_checkin(NULL, NULL) failed"); 121 return rnb_not_connected; 122 } 123 m_conn_port_from_lockdown = true; 124 return rnb_success; 125 } 126 #endif 127 128 rnb_err_t 129 RNBSocket::OpenFile (const char *path) 130 { 131 DNBError err; 132 m_conn_port = open (path, O_RDWR); 133 if (m_conn_port == -1) 134 { 135 err.SetError(errno, DNBError::POSIX); 136 err.LogThreaded ("can't open file '%s'", path); 137 return rnb_not_connected; 138 } 139 else 140 { 141 struct termios stdin_termios; 142 143 if (::tcgetattr (m_conn_port, &stdin_termios) == 0) 144 { 145 stdin_termios.c_lflag &= ~ECHO; // Turn off echoing 146 stdin_termios.c_lflag &= ~ICANON; // Get one char at a time 147 ::tcsetattr (m_conn_port, TCSANOW, &stdin_termios); 148 } 149 } 150 return rnb_success; 151 } 152 153 int 154 RNBSocket::SetSocketOption(int fd, int level, int option_name, int option_value) 155 { 156 return ::setsockopt(fd, level, option_name, &option_value, sizeof(option_value)); 157 } 158 159 rnb_err_t 160 RNBSocket::Disconnect (bool save_errno) 161 { 162 if (m_conn_port_from_lockdown) 163 m_conn_port_from_lockdown = false; 164 return ClosePort (m_conn_port, save_errno); 165 } 166 167 168 rnb_err_t 169 RNBSocket::Read (std::string &p) 170 { 171 char buf[1024]; 172 p.clear(); 173 174 // Note that BUF is on the stack so we must be careful to keep any 175 // writes to BUF from overflowing or we'll have security issues. 176 177 if (m_conn_port == -1) 178 return rnb_err; 179 180 //DNBLogThreadedIf(LOG_RNB_COMM, "%8u RNBSocket::%s calling read()", (uint32_t)m_timer.ElapsedMicroSeconds(true), __FUNCTION__); 181 DNBError err; 182 int bytesread = read (m_conn_port, buf, sizeof (buf)); 183 if (bytesread <= 0) 184 err.SetError(errno, DNBError::POSIX); 185 else 186 p.append(buf, bytesread); 187 188 if (err.Fail() || DNBLogCheckLogBit(LOG_RNB_COMM)) 189 err.LogThreaded("::read ( %i, %p, %zu ) => %i", m_conn_port, buf, sizeof (buf), bytesread); 190 191 // Our port went away - we have to mark this so IsConnected will return the truth. 192 if (bytesread == 0) 193 { 194 m_conn_port = -1; 195 return rnb_not_connected; 196 } 197 else if (bytesread == -1) 198 { 199 m_conn_port = -1; 200 return rnb_err; 201 } 202 // Strip spaces from the end of the buffer 203 while (!p.empty() && isspace (p[p.size() - 1])) 204 p.erase (p.size () - 1); 205 206 // Most data in the debugserver packets valid printable characters... 207 DNBLogThreadedIf(LOG_RNB_COMM, "read: %s", p.c_str()); 208 return rnb_success; 209 } 210 211 rnb_err_t 212 RNBSocket::Write (const void *buffer, size_t length) 213 { 214 if (m_conn_port == -1) 215 return rnb_err; 216 217 DNBError err; 218 int bytessent = send (m_conn_port, buffer, length, 0); 219 if (bytessent < 0) 220 err.SetError(errno, DNBError::POSIX); 221 222 if (err.Fail() || DNBLogCheckLogBit(LOG_RNB_COMM)) 223 err.LogThreaded("::send ( socket = %i, buffer = %p, length = %zu, flags = 0 ) => %i", m_conn_port, buffer, length, bytessent); 224 225 if (bytessent < 0) 226 return rnb_err; 227 228 if (bytessent != length) 229 return rnb_err; 230 231 DNBLogThreadedIf(LOG_RNB_PACKETS, "putpkt: %*s", length, (char *)buffer); // All data is string based in debugserver, so this is safe 232 DNBLogThreadedIf(LOG_RNB_COMM, "sent: %*s", length, (char *)buffer); 233 234 return rnb_success; 235 } 236 237 238 rnb_err_t 239 RNBSocket::ClosePort (int& fd, bool save_errno) 240 { 241 int close_err = 0; 242 if (fd > 0) 243 { 244 errno = 0; 245 close_err = close (fd); 246 fd = -1; 247 } 248 return close_err != 0 ? rnb_err : rnb_success; 249 } 250 251 252