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