1 //===-- Acceptor.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 #include "Acceptor.h"
11 
12 #include "llvm/ADT/StringRef.h"
13 
14 #include "lldb/Core/StreamString.h"
15 #include "lldb/Host/ConnectionFileDescriptor.h"
16 #include "lldb/Host/common/TCPSocket.h"
17 
18 #include "Utility/UriParser.h"
19 
20 using namespace lldb;
21 using namespace lldb_private;
22 using namespace lldb_private::lldb_server;
23 using namespace llvm;
24 
25 namespace {
26 
27 struct SocketScheme
28 {
29     const char* m_scheme;
30     const Socket::SocketProtocol m_protocol;
31 };
32 
33 SocketScheme socket_schemes[] = {
34     {"tcp", Socket::ProtocolTcp},
35     {"udp", Socket::ProtocolUdp},
36     {"unix", Socket::ProtocolUnixDomain},
37     {"unix-abstract", Socket::ProtocolUnixAbstract},
38 };
39 
40 bool
41 FindProtocolByScheme(const char* scheme, Socket::SocketProtocol& protocol)
42 {
43     for (auto s: socket_schemes)
44     {
45         if (!strcmp(s.m_scheme, scheme))
46         {
47             protocol = s.m_protocol;
48             return true;
49         }
50     }
51     return false;
52 }
53 
54 const char*
55 FindSchemeByProtocol(const Socket::SocketProtocol protocol)
56 {
57     for (auto s: socket_schemes)
58     {
59         if (s.m_protocol == protocol)
60             return s.m_scheme;
61     }
62     return nullptr;
63 }
64 
65 }
66 
67 Error
68 Acceptor::Listen(int backlog)
69 {
70     return m_listener_socket_up->Listen(StringRef(m_name.c_str()),
71                                         backlog);
72 }
73 
74 Error
75 Acceptor::Accept(const bool child_processes_inherit, Connection *&conn)
76 {
77     Socket* conn_socket = nullptr;
78     auto error = m_listener_socket_up->Accept(StringRef(m_name.c_str()),
79                                               child_processes_inherit,
80                                               conn_socket);
81     if (error.Success())
82         conn = new ConnectionFileDescriptor(conn_socket);
83 
84     return error;
85 }
86 
87 Socket::SocketProtocol
88 Acceptor::GetSocketProtocol() const
89 {
90     return m_listener_socket_up->GetSocketProtocol();
91 }
92 
93 const char*
94 Acceptor::GetSocketScheme() const
95 {
96     return FindSchemeByProtocol(GetSocketProtocol());
97 }
98 
99 std::string
100 Acceptor::GetLocalSocketId() const
101 {
102     return m_local_socket_id();
103 }
104 
105 std::unique_ptr<Acceptor>
106 Acceptor::Create(StringRef name, const bool child_processes_inherit, Error &error)
107 {
108     error.Clear();
109 
110     Socket::SocketProtocol socket_protocol = Socket::ProtocolUnixDomain;
111     int port;
112     std::string scheme, host, path;
113     // Try to match socket name as URL - e.g., tcp://localhost:5555
114     if (UriParser::Parse(name.str(), scheme, host, port, path))
115     {
116         if (!FindProtocolByScheme(scheme.c_str(), socket_protocol))
117             error.SetErrorStringWithFormat("Unknown protocol scheme \"%s\"", scheme.c_str());
118         else
119             name = name.drop_front(scheme.size() + strlen("://"));
120     }
121     else
122     {
123         std::string host_str;
124         std::string port_str;
125         int32_t port = INT32_MIN;
126         // Try to match socket name as $host:port - e.g., localhost:5555
127         if (Socket::DecodeHostAndPort (name, host_str, port_str, port, nullptr))
128             socket_protocol = Socket::ProtocolTcp;
129     }
130 
131     if (error.Fail())
132         return std::unique_ptr<Acceptor>();
133 
134     std::unique_ptr<Socket> listener_socket_up = Socket::Create(
135         socket_protocol, child_processes_inherit, error);
136 
137     LocalSocketIdFunc local_socket_id;
138     if (error.Success())
139     {
140         if (listener_socket_up->GetSocketProtocol() == Socket::ProtocolTcp)
141         {
142             TCPSocket* tcp_socket = static_cast<TCPSocket*>(listener_socket_up.get());
143             local_socket_id = [tcp_socket]() {
144                 auto local_port = tcp_socket->GetLocalPortNumber();
145                 return (local_port != 0) ? std::to_string(local_port) : "";
146             };
147         }
148         else
149         {
150             const std::string socket_name = name;
151             local_socket_id = [socket_name](){
152                 return socket_name;
153             };
154         }
155 
156         return std::unique_ptr<Acceptor>(
157             new Acceptor(std::move(listener_socket_up), name, local_socket_id));
158     }
159 
160     return std::unique_ptr<Acceptor>();
161 }
162 
163 Acceptor::Acceptor(std::unique_ptr<Socket> &&listener_socket,
164                    StringRef name,
165                    const LocalSocketIdFunc &local_socket_id)
166     : m_listener_socket_up(std::move(listener_socket)),
167       m_name(name.str()),
168       m_local_socket_id(local_socket_id)
169 {
170 }
171