xref: /freebsd/contrib/llvm-project/lldb/source/Host/posix/DomainSocket.cpp (revision 700637cbb5e582861067a11aaca4d053546871d2)
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 #ifdef __linux__
12 #include <lldb/Host/linux/AbstractSocket.h>
13 #endif
14 
15 #include "llvm/Support/Errno.h"
16 #include "llvm/Support/Error.h"
17 #include "llvm/Support/FileSystem.h"
18 
19 #include <cstddef>
20 #include <fcntl.h>
21 #include <memory>
22 #include <sys/socket.h>
23 #include <sys/un.h>
24 
25 using namespace lldb;
26 using namespace lldb_private;
27 
28 static const int kDomain = AF_UNIX;
29 static const int kType = SOCK_STREAM;
30 
SetSockAddr(llvm::StringRef name,const size_t name_offset,sockaddr_un * saddr_un,socklen_t & saddr_un_len)31 static bool SetSockAddr(llvm::StringRef name, const size_t name_offset,
32                         sockaddr_un *saddr_un, socklen_t &saddr_un_len) {
33   if (name.size() + name_offset > sizeof(saddr_un->sun_path))
34     return false;
35 
36   memset(saddr_un, 0, sizeof(*saddr_un));
37   saddr_un->sun_family = kDomain;
38 
39   memcpy(saddr_un->sun_path + name_offset, name.data(), name.size());
40 
41   // For domain sockets we can use SUN_LEN in order to calculate size of
42   // sockaddr_un, but for abstract sockets we have to calculate size manually
43   // because of leading null symbol.
44   if (name_offset == 0)
45     saddr_un_len = SUN_LEN(saddr_un);
46   else
47     saddr_un_len =
48         offsetof(struct sockaddr_un, sun_path) + name_offset + name.size();
49 
50 #if defined(__APPLE__) || defined(__FreeBSD__) || defined(__NetBSD__) ||       \
51     defined(__OpenBSD__)
52   saddr_un->sun_len = saddr_un_len;
53 #endif
54 
55   return true;
56 }
57 
DomainSocket(bool should_close)58 DomainSocket::DomainSocket(bool should_close)
59     : DomainSocket(kInvalidSocketValue, should_close) {}
60 
DomainSocket(NativeSocket socket,bool should_close)61 DomainSocket::DomainSocket(NativeSocket socket, bool should_close)
62     : Socket(ProtocolUnixDomain, should_close) {
63   m_socket = socket;
64 }
65 
DomainSocket(SocketProtocol protocol)66 DomainSocket::DomainSocket(SocketProtocol protocol)
67     : Socket(protocol, /*should_close=*/true) {}
68 
DomainSocket(NativeSocket socket,const DomainSocket & listen_socket)69 DomainSocket::DomainSocket(NativeSocket socket,
70                            const DomainSocket &listen_socket)
71     : Socket(ProtocolUnixDomain, listen_socket.m_should_close_fd) {
72   m_socket = socket;
73 }
74 
DomainSocket(SocketProtocol protocol,NativeSocket socket,bool should_close)75 DomainSocket::DomainSocket(SocketProtocol protocol, NativeSocket socket,
76                            bool should_close)
77     : Socket(protocol, should_close) {
78   m_socket = socket;
79 }
80 
CreatePair()81 llvm::Expected<DomainSocket::Pair> DomainSocket::CreatePair() {
82   int sockets[2];
83   int type = SOCK_STREAM;
84 #ifdef SOCK_CLOEXEC
85   type |= SOCK_CLOEXEC;
86 #endif
87   if (socketpair(AF_UNIX, type, 0, sockets) == -1)
88     return llvm::errorCodeToError(llvm::errnoAsErrorCode());
89 
90 #ifndef SOCK_CLOEXEC
91   for (int s : sockets) {
92     int r = fcntl(s, F_SETFD, FD_CLOEXEC | fcntl(s, F_GETFD));
93     assert(r == 0);
94     (void)r;
95   }
96 #endif
97 
98   return Pair(std::unique_ptr<DomainSocket>(
99                   new DomainSocket(ProtocolUnixDomain, sockets[0],
100                                    /*should_close=*/true)),
101               std::unique_ptr<DomainSocket>(
102                   new DomainSocket(ProtocolUnixDomain, sockets[1],
103                                    /*should_close=*/true)));
104 }
105 
Connect(llvm::StringRef name)106 Status DomainSocket::Connect(llvm::StringRef name) {
107   sockaddr_un saddr_un;
108   socklen_t saddr_un_len;
109   if (!SetSockAddr(name, GetNameOffset(), &saddr_un, saddr_un_len))
110     return Status::FromErrorString("Failed to set socket address");
111 
112   Status error;
113   m_socket = CreateSocket(kDomain, kType, 0, error);
114   if (error.Fail())
115     return error;
116   if (llvm::sys::RetryAfterSignal(-1, ::connect, GetNativeSocket(),
117                                   (struct sockaddr *)&saddr_un,
118                                   saddr_un_len) < 0)
119     SetLastError(error);
120 
121   return error;
122 }
123 
Listen(llvm::StringRef name,int backlog)124 Status DomainSocket::Listen(llvm::StringRef name, int backlog) {
125   sockaddr_un saddr_un;
126   socklen_t saddr_un_len;
127   if (!SetSockAddr(name, GetNameOffset(), &saddr_un, saddr_un_len))
128     return Status::FromErrorString("Failed to set socket address");
129 
130   DeleteSocketFile(name);
131 
132   Status error;
133   m_socket = CreateSocket(kDomain, kType, 0, error);
134   if (error.Fail())
135     return error;
136   if (::bind(GetNativeSocket(), (struct sockaddr *)&saddr_un, saddr_un_len) ==
137       0)
138     if (::listen(GetNativeSocket(), backlog) == 0)
139       return error;
140 
141   SetLastError(error);
142   return error;
143 }
144 
Accept(MainLoopBase & loop,std::function<void (std::unique_ptr<Socket> socket)> sock_cb)145 llvm::Expected<std::vector<MainLoopBase::ReadHandleUP>> DomainSocket::Accept(
146     MainLoopBase &loop,
147     std::function<void(std::unique_ptr<Socket> socket)> sock_cb) {
148   // TODO: Refactor MainLoop to avoid the shared_ptr requirement.
149   auto io_sp = std::make_shared<DomainSocket>(GetNativeSocket(), false);
150   auto cb = [this, sock_cb](MainLoopBase &loop) {
151     Log *log = GetLog(LLDBLog::Host);
152     Status error;
153     auto conn_fd = AcceptSocket(GetNativeSocket(), nullptr, nullptr, error);
154     if (error.Fail()) {
155       LLDB_LOG(log, "AcceptSocket({0}): {1}", GetNativeSocket(), error);
156       return;
157     }
158     std::unique_ptr<DomainSocket> sock_up(new DomainSocket(conn_fd, *this));
159     sock_cb(std::move(sock_up));
160   };
161 
162   Status error;
163   std::vector<MainLoopBase::ReadHandleUP> handles;
164   handles.emplace_back(loop.RegisterReadObject(io_sp, cb, error));
165   if (error.Fail())
166     return error.ToError();
167   return handles;
168 }
169 
GetNameOffset() const170 size_t DomainSocket::GetNameOffset() const { return 0; }
171 
DeleteSocketFile(llvm::StringRef name)172 void DomainSocket::DeleteSocketFile(llvm::StringRef name) {
173   llvm::sys::fs::remove(name);
174 }
175 
GetSocketName() const176 std::string DomainSocket::GetSocketName() const {
177   if (m_socket == kInvalidSocketValue)
178     return "";
179 
180   struct sockaddr_un saddr_un;
181   saddr_un.sun_family = AF_UNIX;
182   socklen_t sock_addr_len = sizeof(struct sockaddr_un);
183   if (::getpeername(m_socket, (struct sockaddr *)&saddr_un, &sock_addr_len) !=
184       0)
185     return "";
186 
187   if (sock_addr_len <= offsetof(struct sockaddr_un, sun_path))
188     return ""; // Unnamed domain socket
189 
190   llvm::StringRef name(saddr_un.sun_path + GetNameOffset(),
191                        sock_addr_len - offsetof(struct sockaddr_un, sun_path) -
192                            GetNameOffset());
193   name = name.rtrim('\0');
194 
195   return name.str();
196 }
197 
GetRemoteConnectionURI() const198 std::string DomainSocket::GetRemoteConnectionURI() const {
199   std::string name = GetSocketName();
200   if (name.empty())
201     return name;
202 
203   return llvm::formatv(
204       "{0}://{1}",
205       GetNameOffset() == 0 ? "unix-connect" : "unix-abstract-connect", name);
206 }
207 
GetListeningConnectionURI() const208 std::vector<std::string> DomainSocket::GetListeningConnectionURI() const {
209   if (m_socket == kInvalidSocketValue)
210     return {};
211 
212   struct sockaddr_un addr;
213   memset(&addr, 0, sizeof(struct sockaddr_un));
214   addr.sun_family = AF_UNIX;
215   socklen_t addr_len = sizeof(struct sockaddr_un);
216   if (::getsockname(m_socket, (struct sockaddr *)&addr, &addr_len) != 0)
217     return {};
218 
219   return {llvm::formatv("unix-connect://{0}", addr.sun_path)};
220 }
221 
222 llvm::Expected<std::unique_ptr<DomainSocket>>
FromBoundNativeSocket(NativeSocket sockfd,bool should_close)223 DomainSocket::FromBoundNativeSocket(NativeSocket sockfd, bool should_close) {
224   // Check if fd represents domain socket or abstract socket.
225   struct sockaddr_un addr;
226   socklen_t addr_len = sizeof(addr);
227   if (getsockname(sockfd, (struct sockaddr *)&addr, &addr_len) == -1)
228     return llvm::createStringError("not a socket or error occurred");
229   if (addr.sun_family != AF_UNIX)
230     return llvm::createStringError("Bad socket type");
231 #ifdef __linux__
232   if (addr_len > offsetof(struct sockaddr_un, sun_path) &&
233       addr.sun_path[0] == '\0')
234     return std::make_unique<AbstractSocket>(sockfd, should_close);
235 #endif
236   return std::make_unique<DomainSocket>(sockfd, should_close);
237 }
238