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