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

Provided by KDAB

Privacy Policy
Learn to use CMake with our Intro Training
Find out more

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