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