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
25using namespace lldb;
26using namespace lldb_private;
27
28static const int kDomain = AF_UNIX;
29static const int kType = SOCK_STREAM;
30
31static 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(s: saddr_un, c: 0, n: sizeof(*saddr_un));
37 saddr_un->sun_family = kDomain;
38
39 memcpy(dest: saddr_un->sun_path + name_offset, src: name.data(), n: 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
58DomainSocket::DomainSocket(bool should_close)
59 : DomainSocket(kInvalidSocketValue, should_close) {}
60
61DomainSocket::DomainSocket(NativeSocket socket, bool should_close)
62 : Socket(ProtocolUnixDomain, should_close) {
63 m_socket = socket;
64}
65
66DomainSocket::DomainSocket(SocketProtocol protocol)
67 : Socket(protocol, /*should_close=*/true) {}
68
69DomainSocket::DomainSocket(NativeSocket socket,
70 const DomainSocket &listen_socket)
71 : Socket(ProtocolUnixDomain, listen_socket.m_should_close_fd) {
72 m_socket = socket;
73}
74
75DomainSocket::DomainSocket(SocketProtocol protocol, NativeSocket socket,
76 bool should_close)
77 : Socket(protocol, should_close) {
78 m_socket = socket;
79}
80
81llvm::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: type, protocol: 0, fds: sockets) == -1)
88 return llvm::errorCodeToError(EC: 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
106Status DomainSocket::Connect(llvm::StringRef name) {
107 sockaddr_un saddr_un;
108 socklen_t saddr_un_len;
109 if (!SetSockAddr(name, name_offset: GetNameOffset(), saddr_un: &saddr_un, saddr_un_len))
110 return Status::FromErrorString(str: "Failed to set socket address");
111
112 Status error;
113 m_socket = CreateSocket(domain: kDomain, type: kType, protocol: 0, error);
114 if (error.Fail())
115 return error;
116 if (llvm::sys::RetryAfterSignal(Fail: -1, F&: ::connect, As: GetNativeSocket(),
117 As: (struct sockaddr *)&saddr_un,
118 As: saddr_un_len) < 0)
119 SetLastError(error);
120
121 return error;
122}
123
124Status DomainSocket::Listen(llvm::StringRef name, int backlog) {
125 sockaddr_un saddr_un;
126 socklen_t saddr_un_len;
127 if (!SetSockAddr(name, name_offset: GetNameOffset(), saddr_un: &saddr_un, saddr_un_len))
128 return Status::FromErrorString(str: "Failed to set socket address");
129
130 DeleteSocketFile(name);
131
132 Status error;
133 m_socket = CreateSocket(domain: kDomain, type: kType, protocol: 0, error);
134 if (error.Fail())
135 return error;
136 if (::bind(fd: GetNativeSocket(), addr: (struct sockaddr *)&saddr_un, len: saddr_un_len) ==
137 0)
138 if (::listen(fd: GetNativeSocket(), n: backlog) == 0)
139 return error;
140
141 SetLastError(error);
142 return error;
143}
144
145llvm::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>(args: GetNativeSocket(), args: false);
150 auto cb = [this, sock_cb](MainLoopBase &loop) {
151 Log *log = GetLog(mask: LLDBLog::Host);
152 Status error;
153 auto conn_fd = AcceptSocket(sockfd: GetNativeSocket(), addr: nullptr, addrlen: 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(args: loop.RegisterReadObject(object_sp: io_sp, callback: cb, error));
165 if (error.Fail())
166 return error.ToError();
167 return handles;
168}
169
170size_t DomainSocket::GetNameOffset() const { return 0; }
171
172void DomainSocket::DeleteSocketFile(llvm::StringRef name) {
173 llvm::sys::fs::remove(path: name);
174}
175
176std::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(fd: m_socket, addr: (struct sockaddr *)&saddr_un, len: &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(Char: '\0');
194
195 return name.str();
196}
197
198std::string DomainSocket::GetRemoteConnectionURI() const {
199 std::string name = GetSocketName();
200 if (name.empty())
201 return name;
202
203 return llvm::formatv(
204 Fmt: "{0}://{1}",
205 Vals: GetNameOffset() == 0 ? "unix-connect" : "unix-abstract-connect", Vals&: name);
206}
207
208std::vector<std::string> DomainSocket::GetListeningConnectionURI() const {
209 if (m_socket == kInvalidSocketValue)
210 return {};
211
212 struct sockaddr_un addr;
213 memset(s: &addr, c: 0, n: sizeof(struct sockaddr_un));
214 addr.sun_family = AF_UNIX;
215 socklen_t addr_len = sizeof(struct sockaddr_un);
216 if (::getsockname(fd: m_socket, addr: (struct sockaddr *)&addr, len: &addr_len) != 0)
217 return {};
218
219 return {llvm::formatv(Fmt: "unix-connect://{0}", Vals&: addr.sun_path)};
220}
221
222llvm::Expected<std::unique_ptr<DomainSocket>>
223DomainSocket::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(fd: sockfd, addr: (struct sockaddr *)&addr, len: &addr_len) == -1)
228 return llvm::createStringError(Fmt: "not a socket or error occurred");
229 if (addr.sun_family != AF_UNIX)
230 return llvm::createStringError(Fmt: "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>(args&: sockfd, args&: should_close);
235#endif
236 return std::make_unique<DomainSocket>(args&: sockfd, args&: should_close);
237}
238

source code of lldb/source/Host/posix/DomainSocket.cpp