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 | |
23 | using namespace lldb; |
24 | using namespace lldb_private; |
25 | |
26 | static const int kDomain = AF_UNIX; |
27 | static const int kType = SOCK_STREAM; |
28 | |
29 | static 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 | |
56 | DomainSocket::DomainSocket(bool should_close) |
57 | : DomainSocket(kInvalidSocketValue, should_close) {} |
58 | |
59 | DomainSocket::DomainSocket(NativeSocket socket, bool should_close) |
60 | : Socket(ProtocolUnixDomain, should_close) { |
61 | m_socket = socket; |
62 | } |
63 | |
64 | DomainSocket::DomainSocket(SocketProtocol protocol) |
65 | : Socket(protocol, /*should_close=*/true) {} |
66 | |
67 | DomainSocket::DomainSocket(NativeSocket socket, |
68 | const DomainSocket &listen_socket) |
69 | : Socket(ProtocolUnixDomain, listen_socket.m_should_close_fd) { |
70 | m_socket = socket; |
71 | } |
72 | |
73 | DomainSocket::DomainSocket(SocketProtocol protocol, NativeSocket socket, |
74 | bool should_close) |
75 | : Socket(protocol, should_close) { |
76 | m_socket = socket; |
77 | } |
78 | |
79 | Status 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 | |
97 | Status 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 | |
118 | llvm::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 | |
143 | size_t DomainSocket::GetNameOffset() const { return 0; } |
144 | |
145 | void DomainSocket::DeleteSocketFile(llvm::StringRef name) { |
146 | llvm::sys::fs::remove(path: name); |
147 | } |
148 | |
149 | std::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 | |
171 | std::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 | |
181 | std::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 | |
195 | llvm::Expected<std::unique_ptr<DomainSocket>> |
196 | DomainSocket::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 | |