xref: /llvm-project/lldb/source/Host/posix/DomainSocket.cpp (revision e289cb545adabd8f7b72c0c4a023dcf640823767)
1 //===-- DomainSocket.cpp --------------------------------------------------===//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===----------------------------------------------------------------------===//
8 
9 #include "lldb/Host/posix/DomainSocket.h"
10 #include "lldb/Utility/LLDBLog.h"
11 
12 #include "llvm/Support/Errno.h"
13 #include "llvm/Support/FileSystem.h"
14 
15 #include <cstddef>
16 #include <memory>
17 #include <sys/socket.h>
18 #include <sys/un.h>
19 
20 using namespace lldb;
21 using namespace lldb_private;
22 
23 static const int kDomain = AF_UNIX;
24 static const int kType = SOCK_STREAM;
25 
26 static bool SetSockAddr(llvm::StringRef name, const size_t name_offset,
27                         sockaddr_un *saddr_un, socklen_t &saddr_un_len) {
28   if (name.size() + name_offset > sizeof(saddr_un->sun_path))
29     return false;
30 
31   memset(saddr_un, 0, sizeof(*saddr_un));
32   saddr_un->sun_family = kDomain;
33 
34   memcpy(saddr_un->sun_path + name_offset, name.data(), name.size());
35 
36   // For domain sockets we can use SUN_LEN in order to calculate size of
37   // sockaddr_un, but for abstract sockets we have to calculate size manually
38   // because of leading null symbol.
39   if (name_offset == 0)
40     saddr_un_len = SUN_LEN(saddr_un);
41   else
42     saddr_un_len =
43         offsetof(struct sockaddr_un, sun_path) + name_offset + name.size();
44 
45 #if defined(__APPLE__) || defined(__FreeBSD__) || defined(__NetBSD__) ||       \
46     defined(__OpenBSD__)
47   saddr_un->sun_len = saddr_un_len;
48 #endif
49 
50   return true;
51 }
52 
53 DomainSocket::DomainSocket(bool should_close)
54     : DomainSocket(kInvalidSocketValue, should_close) {}
55 
56 DomainSocket::DomainSocket(NativeSocket socket, bool should_close)
57     : Socket(ProtocolUnixDomain, should_close) {
58   m_socket = socket;
59 }
60 
61 DomainSocket::DomainSocket(SocketProtocol protocol)
62     : Socket(protocol, /*should_close=*/true) {}
63 
64 DomainSocket::DomainSocket(NativeSocket socket,
65                            const DomainSocket &listen_socket)
66     : Socket(ProtocolUnixDomain, listen_socket.m_should_close_fd) {
67   m_socket = socket;
68 }
69 
70 Status DomainSocket::Connect(llvm::StringRef name) {
71   sockaddr_un saddr_un;
72   socklen_t saddr_un_len;
73   if (!SetSockAddr(name, GetNameOffset(), &saddr_un, saddr_un_len))
74     return Status::FromErrorString("Failed to set socket address");
75 
76   Status error;
77   m_socket = CreateSocket(kDomain, kType, 0, error);
78   if (error.Fail())
79     return error;
80   if (llvm::sys::RetryAfterSignal(-1, ::connect, GetNativeSocket(),
81                                   (struct sockaddr *)&saddr_un,
82                                   saddr_un_len) < 0)
83     SetLastError(error);
84 
85   return error;
86 }
87 
88 Status DomainSocket::Listen(llvm::StringRef name, int backlog) {
89   sockaddr_un saddr_un;
90   socklen_t saddr_un_len;
91   if (!SetSockAddr(name, GetNameOffset(), &saddr_un, saddr_un_len))
92     return Status::FromErrorString("Failed to set socket address");
93 
94   DeleteSocketFile(name);
95 
96   Status error;
97   m_socket = CreateSocket(kDomain, kType, 0, error);
98   if (error.Fail())
99     return error;
100   if (::bind(GetNativeSocket(), (struct sockaddr *)&saddr_un, saddr_un_len) ==
101       0)
102     if (::listen(GetNativeSocket(), backlog) == 0)
103       return error;
104 
105   SetLastError(error);
106   return error;
107 }
108 
109 llvm::Expected<std::vector<MainLoopBase::ReadHandleUP>> DomainSocket::Accept(
110     MainLoopBase &loop,
111     std::function<void(std::unique_ptr<Socket> socket)> sock_cb) {
112   // TODO: Refactor MainLoop to avoid the shared_ptr requirement.
113   auto io_sp = std::make_shared<DomainSocket>(GetNativeSocket(), false);
114   auto cb = [this, sock_cb](MainLoopBase &loop) {
115     Log *log = GetLog(LLDBLog::Host);
116     Status error;
117     auto conn_fd = AcceptSocket(GetNativeSocket(), nullptr, nullptr, error);
118     if (error.Fail()) {
119       LLDB_LOG(log, "AcceptSocket({0}): {1}", GetNativeSocket(), error);
120       return;
121     }
122     std::unique_ptr<DomainSocket> sock_up(new DomainSocket(conn_fd, *this));
123     sock_cb(std::move(sock_up));
124   };
125 
126   Status error;
127   std::vector<MainLoopBase::ReadHandleUP> handles;
128   handles.emplace_back(loop.RegisterReadObject(io_sp, cb, error));
129   if (error.Fail())
130     return error.ToError();
131   return handles;
132 }
133 
134 size_t DomainSocket::GetNameOffset() const { return 0; }
135 
136 void DomainSocket::DeleteSocketFile(llvm::StringRef name) {
137   llvm::sys::fs::remove(name);
138 }
139 
140 std::string DomainSocket::GetSocketName() const {
141   if (m_socket == kInvalidSocketValue)
142     return "";
143 
144   struct sockaddr_un saddr_un;
145   saddr_un.sun_family = AF_UNIX;
146   socklen_t sock_addr_len = sizeof(struct sockaddr_un);
147   if (::getpeername(m_socket, (struct sockaddr *)&saddr_un, &sock_addr_len) !=
148       0)
149     return "";
150 
151   if (sock_addr_len <= offsetof(struct sockaddr_un, sun_path))
152     return ""; // Unnamed domain socket
153 
154   llvm::StringRef name(saddr_un.sun_path + GetNameOffset(),
155                        sock_addr_len - offsetof(struct sockaddr_un, sun_path) -
156                            GetNameOffset());
157   name = name.rtrim('\0');
158 
159   return name.str();
160 }
161 
162 std::string DomainSocket::GetRemoteConnectionURI() const {
163   std::string name = GetSocketName();
164   if (name.empty())
165     return name;
166 
167   return llvm::formatv(
168       "{0}://{1}",
169       GetNameOffset() == 0 ? "unix-connect" : "unix-abstract-connect", name);
170 }
171 
172 std::vector<std::string> DomainSocket::GetListeningConnectionURI() const {
173   if (m_socket == kInvalidSocketValue)
174     return {};
175 
176   struct sockaddr_un addr;
177   memset(&addr, 0, sizeof(struct sockaddr_un));
178   addr.sun_family = AF_UNIX;
179   socklen_t addr_len = sizeof(struct sockaddr_un);
180   if (::getsockname(m_socket, (struct sockaddr *)&addr, &addr_len) != 0)
181     return {};
182 
183   return {llvm::formatv("unix-connect://{0}", addr.sun_path)};
184 }
185