LLDB mainline
DomainSocket.cpp
Go to the documentation of this file.
1//===----------------------------------------------------------------------===//
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
11#ifdef __linux__
13#endif
14
15#include "llvm/ADT/StringExtras.h"
16#include "llvm/Support/Errno.h"
17#include "llvm/Support/Error.h"
18#include "llvm/Support/FileSystem.h"
19
20#include <algorithm>
21#include <cstddef>
22#include <memory>
23
24#ifdef _WIN32
25#include <afunix.h>
26#else
27#include <sys/socket.h>
28#include <sys/un.h>
29#endif
30
31using namespace lldb;
32using namespace lldb_private;
33
34static const int kDomain = AF_UNIX;
35static const int kType = SOCK_STREAM;
36
37std::string DomainSocket::NativePathToURIPath(llvm::StringRef path) {
38 // A drive-letter path (e.g. "C:\\dir\\sock") is not a valid URI authority, so
39 // it is carried in the RFC 8089 URI form "/C:/dir/sock".
40 if (path.size() >= 2 && llvm::isAlpha(path[0]) && path[1] == ':') {
41 std::string uri_path = "/" + path.str();
42 std::replace(uri_path.begin(), uri_path.end(), '\\', '/');
43 return uri_path;
44 }
45 // A UNC path (e.g. "\\\\server\\share") is already anchored by its leading
46 // slashes.
47 if (path.starts_with("\\\\")) {
48 std::string uri_path = path.str();
49 std::replace(uri_path.begin(), uri_path.end(), '\\', '/');
50 return uri_path;
51 }
52 return path.str();
53}
54
55std::string DomainSocket::URIPathToNativePath(llvm::StringRef path) {
56 // Reverse of the drive-letter mapping: "/C:/dir/sock" -> "C:\\dir\\sock".
57 if (path.size() >= 3 && path[0] == '/' && llvm::isAlpha(path[1]) &&
58 path[2] == ':') {
59 std::string native = path.drop_front().str();
60 std::replace(native.begin(), native.end(), '/', '\\');
61 return native;
62 }
63 // Reverse of the UNC mapping: "//server/share" -> "\\\\server\\share".
64 if (path.starts_with("//")) {
65 std::string native = path.str();
66 std::replace(native.begin(), native.end(), '/', '\\');
67 return native;
68 }
69 return path.str();
70}
71
72static bool SetSockAddr(llvm::StringRef name, const size_t name_offset,
73 sockaddr_un *saddr_un, socklen_t &saddr_un_len) {
74 if (name.size() + name_offset > sizeof(saddr_un->sun_path))
75 return false;
76
77 memset(saddr_un, 0, sizeof(*saddr_un));
78 saddr_un->sun_family = kDomain;
79
80 memcpy(saddr_un->sun_path + name_offset, name.data(), name.size());
81
82 // Compute the address length explicitly rather than via SUN_LEN: that macro
83 // is not available on Windows.
84 saddr_un_len =
85 offsetof(struct sockaddr_un, sun_path) + name_offset + name.size();
86
87#if defined(__APPLE__) || defined(__FreeBSD__) || defined(__NetBSD__) || \
88 defined(__OpenBSD__)
89 saddr_un->sun_len = saddr_un_len;
90#endif
91
92 return true;
93}
94
96 : DomainSocket(kInvalidSocketValue, should_close) {}
97
98DomainSocket::DomainSocket(NativeSocket socket, bool should_close)
99 : Socket(ProtocolUnixDomain, should_close) {
100 m_socket = socket;
101}
102
104 : Socket(protocol, /*should_close=*/true) {}
105
107 const DomainSocket &listen_socket)
109 m_socket = socket;
110}
111
113 bool should_close)
114 : Socket(protocol, should_close) {
115 m_socket = socket;
116}
117
118Status DomainSocket::Connect(llvm::StringRef name) {
119 std::string native_name = URIPathToNativePath(name);
120 sockaddr_un saddr_un;
121 socklen_t saddr_un_len;
122 if (!SetSockAddr(native_name, GetNameOffset(), &saddr_un, saddr_un_len))
123 return Status::FromErrorString("Failed to set socket address");
124
127 if (error.Fail())
128 return error;
129 if (llvm::sys::RetryAfterSignal(-1, ::connect, GetNativeSocket(),
130 (struct sockaddr *)&saddr_un,
131 saddr_un_len) < 0)
133
134 return error;
135}
136
137Status DomainSocket::Listen(llvm::StringRef name, int backlog) {
138 std::string native_name = URIPathToNativePath(name);
139 sockaddr_un saddr_un;
140 socklen_t saddr_un_len;
141 if (!SetSockAddr(native_name, GetNameOffset(), &saddr_un, saddr_un_len))
142 return Status::FromErrorString("Failed to set socket address");
143
144 DeleteSocketFile(native_name);
145
148 if (error.Fail())
149 return error;
150 if (::bind(GetNativeSocket(), (struct sockaddr *)&saddr_un, saddr_un_len) ==
151 0)
152 if (::listen(GetNativeSocket(), backlog) == 0)
153 return error;
154
156 return error;
157}
158
159llvm::Expected<std::vector<MainLoopBase::ReadHandleUP>> DomainSocket::Accept(
160 MainLoopBase &loop,
161 std::function<void(std::unique_ptr<Socket> socket)> sock_cb) {
162 // TODO: Refactor MainLoop to avoid the shared_ptr requirement.
163 auto io_sp = std::make_shared<DomainSocket>(GetNativeSocket(), false);
164 auto cb = [this, sock_cb](MainLoopBase &loop) {
165 Log *log = GetLog(LLDBLog::Host);
167 auto conn_fd = AcceptSocket(GetNativeSocket(), nullptr, nullptr, error);
168 if (error.Fail()) {
169 LLDB_LOG(log, "AcceptSocket({0}): {1}", GetNativeSocket(), error);
170 return;
171 }
172 std::unique_ptr<DomainSocket> sock_up(new DomainSocket(conn_fd, *this));
173 sock_cb(std::move(sock_up));
174 };
175
177 std::vector<MainLoopBase::ReadHandleUP> handles;
178 handles.emplace_back(loop.RegisterReadObject(io_sp, cb, error));
179 if (error.Fail())
180 return error.ToError();
181 return handles;
182}
183
184size_t DomainSocket::GetNameOffset() const { return 0; }
185
186void DomainSocket::DeleteSocketFile(llvm::StringRef name) {
187 llvm::sys::fs::remove(name);
188}
189
190std::string DomainSocket::GetSocketName() const {
192 return "";
193
194 struct sockaddr_un saddr_un;
195 saddr_un.sun_family = AF_UNIX;
196 socklen_t sock_addr_len = sizeof(struct sockaddr_un);
197 if (::getpeername(m_socket, (struct sockaddr *)&saddr_un, &sock_addr_len) !=
198 0)
199 return "";
200
201 if (sock_addr_len <= offsetof(struct sockaddr_un, sun_path))
202 return ""; // Unnamed domain socket
203
204 llvm::StringRef name(saddr_un.sun_path + GetNameOffset(),
205 sock_addr_len - offsetof(struct sockaddr_un, sun_path) -
206 GetNameOffset());
207 name = name.rtrim('\0');
208
209 return name.str();
210}
211
213 std::string name = GetSocketName();
214 if (name.empty())
215 return name;
216
217 if (GetNameOffset() == 0)
218 return llvm::formatv("unix-connect://{0}", NativePathToURIPath(name));
219 return llvm::formatv("unix-abstract-connect://{0}", name);
220}
221
222std::vector<std::string> DomainSocket::GetListeningConnectionURI() const {
224 return {};
225
226 struct sockaddr_un addr;
227 memset(&addr, 0, sizeof(struct sockaddr_un));
228 addr.sun_family = AF_UNIX;
229 socklen_t addr_len = sizeof(struct sockaddr_un);
230 if (::getsockname(m_socket, (struct sockaddr *)&addr, &addr_len) != 0)
231 return {};
232
233 return {
234 llvm::formatv("unix-connect://{0}", NativePathToURIPath(addr.sun_path))};
235}
236
237llvm::Expected<std::unique_ptr<DomainSocket>>
239 // Check if fd represents domain socket or abstract socket.
240 struct sockaddr_un addr;
241 socklen_t addr_len = sizeof(addr);
242 if (getsockname(sockfd, (struct sockaddr *)&addr, &addr_len) == -1)
243 return llvm::createStringError("not a socket or error occurred");
244 if (addr.sun_family != AF_UNIX)
245 return llvm::createStringError("bad socket type");
246#ifdef __linux__
247 if (addr_len > offsetof(struct sockaddr_un, sun_path) &&
248 addr.sun_path[0] == '\0')
249 return std::make_unique<AbstractSocket>(sockfd, should_close);
250#endif
251 return std::make_unique<DomainSocket>(sockfd, should_close);
252}
static llvm::raw_ostream & error(Stream &strm)
static const int kDomain
static bool SetSockAddr(llvm::StringRef name, const size_t name_offset, sockaddr_un *saddr_un, socklen_t &saddr_un_len)
static const int kType
#define LLDB_LOG(log,...)
The LLDB_LOG* macros defined below are the way to emit log messages.
Definition Log.h:375
DomainSocket(NativeSocket socket, bool should_close)
static std::string NativePathToURIPath(llvm::StringRef path)
Convert between a native filesystem path and the path component of a domain-socket URI.
Status Listen(llvm::StringRef name, int backlog) override
llvm::Expected< std::vector< MainLoopBase::ReadHandleUP > > Accept(MainLoopBase &loop, std::function< void(std::unique_ptr< Socket > socket)> sock_cb) override
virtual size_t GetNameOffset() const
virtual void DeleteSocketFile(llvm::StringRef name)
Status Connect(llvm::StringRef name) override
static llvm::Expected< std::unique_ptr< DomainSocket > > FromBoundNativeSocket(NativeSocket sockfd, bool should_close)
std::string GetSocketName() const
std::string GetRemoteConnectionURI() const override
static std::string URIPathToNativePath(llvm::StringRef path)
std::vector< std::string > GetListeningConnectionURI() const override
virtual ReadHandleUP RegisterReadObject(const lldb::IOObjectSP &object_sp, const Callback &callback, Status &error)=0
NativeSocket GetNativeSocket() const
Definition Socket.h:151
static const NativeSocket kInvalidSocketValue
Definition Socket.h:95
static NativeSocket CreateSocket(const int domain, const int type, const int protocol, Status &error)
Definition Socket.cpp:447
static NativeSocket AcceptSocket(NativeSocket sockfd, struct sockaddr *addr, socklen_t *addrlen, Status &error)
Definition Socket.cpp:489
static void SetLastError(Status &error)
Definition Socket.cpp:421
NativeSocket m_socket
Definition Socket.h:187
Socket(SocketProtocol protocol, bool should_close)
Definition Socket.cpp:170
An error handling class.
Definition Status.h:118
static Status FromErrorString(const char *str)
Definition Status.h:141
A class that represents a running process on the host machine.
Log * GetLog(Cat mask)
Retrieve the Log object for the channel associated with the given log enum.
Definition Log.h:338
int NativeSocket
Definition Socket.h:41