RNBSocket.cpp revision 7dd846ae1be02fae0fc945cd511c915270d69ccc
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 33rnb_err_t 34RNBSocket::Listen (in_port_t port, PortBoundCallback callback, const void *callback_baton, bool localhost_only) 35{ 36 //DNBLogThreadedIf(LOG_RNB_COMM, "%8u RNBSocket::%s called", (uint32_t)m_timer.ElapsedMicroSeconds(true), __FUNCTION__); 37 // Disconnect without saving errno 38 Disconnect (false); 39 40 DNBError err; 41 int listen_fd = ::socket (AF_INET, SOCK_STREAM, IPPROTO_TCP); 42 if (listen_fd == -1) 43 err.SetError(errno, DNBError::POSIX); 44 45 if (err.Fail() || DNBLogCheckLogBit(LOG_RNB_COMM)) 46 err.LogThreaded("::socket ( domain = AF_INET, type = SOCK_STREAM, protocol = IPPROTO_TCP ) => socket = %i", listen_fd); 47 48 if (err.Fail()) 49 return rnb_err; 50 51 // enable local address reuse 52 SetSocketOption (listen_fd, SOL_SOCKET, SO_REUSEADDR, 1); 53 54 struct sockaddr_in sa; 55 ::memset (&sa, 0, sizeof sa); 56 sa.sin_len = sizeof sa; 57 sa.sin_family = AF_INET; 58 sa.sin_port = htons (port); 59 if (localhost_only) 60 { 61 sa.sin_addr.s_addr = htonl (INADDR_LOOPBACK); 62 } 63 else 64 { 65 sa.sin_addr.s_addr = htonl (INADDR_ANY); 66 } 67 68 int error = ::bind (listen_fd, (struct sockaddr *) &sa, sizeof(sa)); 69 if (error == -1) 70 err.SetError(errno, DNBError::POSIX); 71 72 if (err.Fail() || DNBLogCheckLogBit(LOG_RNB_COMM)) 73 err.LogThreaded("::bind ( socket = %i, (struct sockaddr *) &sa, sizeof(sa)) )", listen_fd); 74 75 if (err.Fail()) 76 { 77 ClosePort (listen_fd, false); 78 return rnb_err; 79 } 80 81 if (callback && port == 0) 82 { 83 // We were asked to listen on port zero which means we 84 // must now read the actual port that was given to us 85 // as port zero is a special code for "find an open port 86 // for me". 87 socklen_t sa_len = sizeof (sa); 88 if (getsockname(listen_fd, (struct sockaddr *)&sa, &sa_len) == 0) 89 { 90 port = ntohs (sa.sin_port); 91 callback (callback_baton, port); 92 } 93 } 94 95 error = ::listen (listen_fd, 1); 96 if (error == -1) 97 err.SetError(errno, DNBError::POSIX); 98 99 if (err.Fail() || DNBLogCheckLogBit(LOG_RNB_COMM)) 100 err.LogThreaded("::listen ( socket = %i, backlog = 1 )", listen_fd); 101 102 if (err.Fail()) 103 { 104 ClosePort (listen_fd, false); 105 return rnb_err; 106 } 107 108 m_fd = ::accept (listen_fd, NULL, 0); 109 if (m_fd == -1) 110 err.SetError(errno, DNBError::POSIX); 111 112 if (err.Fail() || DNBLogCheckLogBit(LOG_RNB_COMM)) 113 err.LogThreaded("::accept ( socket = %i, address = NULL, address_len = 0 )", listen_fd); 114 115 ClosePort (listen_fd, false); 116 117 if (err.Fail()) 118 { 119 return rnb_err; 120 } 121 else 122 { 123 // Keep our TCP packets coming without any delays. 124 SetSocketOption (m_fd, IPPROTO_TCP, TCP_NODELAY, 1); 125 } 126 127 return rnb_success; 128} 129 130rnb_err_t 131RNBSocket::Connect (const char *host, uint16_t port) 132{ 133 Disconnect (false); 134 135 // Create the socket 136 m_fd = ::socket (AF_INET, SOCK_STREAM, IPPROTO_TCP); 137 if (m_fd == -1) 138 return rnb_err; 139 140 // Enable local address reuse 141 SetSocketOption (m_fd, SOL_SOCKET, SO_REUSEADDR, 1); 142 143 struct sockaddr_in sa; 144 ::memset (&sa, 0, sizeof (sa)); 145 sa.sin_family = AF_INET; 146 sa.sin_port = htons (port); 147 148 if (host == NULL) 149 host = "localhost"; 150 151 int inet_pton_result = ::inet_pton (AF_INET, host, &sa.sin_addr); 152 153 if (inet_pton_result <= 0) 154 { 155 struct hostent *host_entry = gethostbyname (host); 156 if (host_entry) 157 { 158 std::string host_str (::inet_ntoa (*(struct in_addr *)*host_entry->h_addr_list)); 159 inet_pton_result = ::inet_pton (AF_INET, host_str.c_str(), &sa.sin_addr); 160 if (inet_pton_result <= 0) 161 { 162 Disconnect (false); 163 return rnb_err; 164 } 165 } 166 } 167 168 if (-1 == ::connect (m_fd, (const struct sockaddr *)&sa, sizeof(sa))) 169 { 170 Disconnect (false); 171 return rnb_err; 172 } 173 174 // Keep our TCP packets coming without any delays. 175 SetSocketOption (m_fd, IPPROTO_TCP, TCP_NODELAY, 1); 176 return rnb_success; 177} 178 179rnb_err_t 180RNBSocket::useFD(int fd) 181{ 182 if (fd < 0) { 183 DNBLogThreadedIf(LOG_RNB_COMM, "Bad file descriptor passed in."); 184 return rnb_err; 185 } 186 187 m_fd = fd; 188 return rnb_success; 189} 190 191#ifdef WITH_LOCKDOWN 192rnb_err_t 193RNBSocket::ConnectToService() 194{ 195 DNBLog("Connecting to com.apple.%s service...", DEBUGSERVER_PROGRAM_NAME); 196 // Disconnect from any previous connections 197 Disconnect(false); 198 if (::secure_lockdown_checkin (&m_ld_conn, NULL, NULL) != kLDESuccess) 199 { 200 DNBLogThreadedIf(LOG_RNB_COMM, "::secure_lockdown_checkin(&m_fd, NULL, NULL) failed"); 201 m_fd = -1; 202 return rnb_not_connected; 203 } 204 m_fd = ::lockdown_get_socket (m_ld_conn); 205 if (m_fd == -1) 206 { 207 DNBLogThreadedIf(LOG_RNB_COMM, "::lockdown_get_socket() failed"); 208 return rnb_not_connected; 209 } 210 m_fd_from_lockdown = true; 211 return rnb_success; 212} 213#endif 214 215rnb_err_t 216RNBSocket::OpenFile (const char *path) 217{ 218 DNBError err; 219 m_fd = open (path, O_RDWR); 220 if (m_fd == -1) 221 { 222 err.SetError(errno, DNBError::POSIX); 223 err.LogThreaded ("can't open file '%s'", path); 224 return rnb_not_connected; 225 } 226 else 227 { 228 struct termios stdin_termios; 229 230 if (::tcgetattr (m_fd, &stdin_termios) == 0) 231 { 232 stdin_termios.c_lflag &= ~ECHO; // Turn off echoing 233 stdin_termios.c_lflag &= ~ICANON; // Get one char at a time 234 ::tcsetattr (m_fd, TCSANOW, &stdin_termios); 235 } 236 } 237 return rnb_success; 238} 239 240int 241RNBSocket::SetSocketOption(int fd, int level, int option_name, int option_value) 242{ 243 return ::setsockopt(fd, level, option_name, &option_value, sizeof(option_value)); 244} 245 246rnb_err_t 247RNBSocket::Disconnect (bool save_errno) 248{ 249#ifdef WITH_LOCKDOWN 250 if (m_fd_from_lockdown) 251 { 252 m_fd_from_lockdown = false; 253 m_fd = -1; 254 if (lockdown_deactivate (m_ld_conn) == 0) 255 return rnb_success; 256 else 257 return rnb_err; 258 } 259#endif 260 return ClosePort (m_fd, save_errno); 261} 262 263 264rnb_err_t 265RNBSocket::Read (std::string &p) 266{ 267 char buf[1024]; 268 p.clear(); 269 270 // Note that BUF is on the stack so we must be careful to keep any 271 // writes to BUF from overflowing or we'll have security issues. 272 273 if (m_fd == -1) 274 return rnb_err; 275 276 //DNBLogThreadedIf(LOG_RNB_COMM, "%8u RNBSocket::%s calling read()", (uint32_t)m_timer.ElapsedMicroSeconds(true), __FUNCTION__); 277 DNBError err; 278 int bytesread = read (m_fd, buf, sizeof (buf)); 279 if (bytesread <= 0) 280 err.SetError(errno, DNBError::POSIX); 281 else 282 p.append(buf, bytesread); 283 284 if (err.Fail() || DNBLogCheckLogBit(LOG_RNB_COMM)) 285 err.LogThreaded("::read ( %i, %p, %llu ) => %i", m_fd, buf, sizeof (buf), (uint64_t)bytesread); 286 287 // Our port went away - we have to mark this so IsConnected will return the truth. 288 if (bytesread == 0) 289 { 290 m_fd = -1; 291 return rnb_not_connected; 292 } 293 else if (bytesread == -1) 294 { 295 m_fd = -1; 296 return rnb_err; 297 } 298 // Strip spaces from the end of the buffer 299 while (!p.empty() && isspace (p[p.size() - 1])) 300 p.erase (p.size () - 1); 301 302 // Most data in the debugserver packets valid printable characters... 303 DNBLogThreadedIf(LOG_RNB_COMM, "read: %s", p.c_str()); 304 return rnb_success; 305} 306 307rnb_err_t 308RNBSocket::Write (const void *buffer, size_t length) 309{ 310 if (m_fd == -1) 311 return rnb_err; 312 313 DNBError err; 314 int bytessent = write (m_fd, buffer, length); 315 if (bytessent < 0) 316 err.SetError(errno, DNBError::POSIX); 317 318 if (err.Fail() || DNBLogCheckLogBit(LOG_RNB_COMM)) 319 err.LogThreaded("::write ( socket = %i, buffer = %p, length = %llu) => %i", m_fd, buffer, length, (uint64_t)bytessent); 320 321 if (bytessent < 0) 322 return rnb_err; 323 324 if (bytessent != length) 325 return rnb_err; 326 327 DNBLogThreadedIf(LOG_RNB_PACKETS, "putpkt: %*s", (int)length, (char *)buffer); // All data is string based in debugserver, so this is safe 328 DNBLogThreadedIf(LOG_RNB_COMM, "sent: %*s", (int)length, (char *)buffer); 329 330 return rnb_success; 331} 332 333 334rnb_err_t 335RNBSocket::ClosePort (int& fd, bool save_errno) 336{ 337 int close_err = 0; 338 if (fd > 0) 339 { 340 errno = 0; 341 close_err = close (fd); 342 fd = -1; 343 } 344 return close_err != 0 ? rnb_err : rnb_success; 345} 346 347 348