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 | |
11 | #include "llvm/Support/Errno.h" |
12 | #include "llvm/Support/FileSystem.h" |
13 | |
14 | #include <cstddef> |
15 | #include <sys/socket.h> |
16 | #include <sys/un.h> |
17 | |
18 | using namespace lldb; |
19 | using namespace lldb_private; |
20 | |
21 | #ifdef __ANDROID__ |
22 | // Android does not have SUN_LEN |
23 | #ifndef SUN_LEN |
24 | #define SUN_LEN(ptr) \ |
25 | (offsetof(struct sockaddr_un, sun_path) + strlen((ptr)->sun_path)) |
26 | #endif |
27 | #endif // #ifdef __ANDROID__ |
28 | |
29 | static const int kDomain = AF_UNIX; |
30 | static const int kType = SOCK_STREAM; |
31 | |
32 | static bool SetSockAddr(llvm::StringRef name, const size_t name_offset, |
33 | sockaddr_un *saddr_un, socklen_t &saddr_un_len) { |
34 | if (name.size() + name_offset > sizeof(saddr_un->sun_path)) |
35 | return false; |
36 | |
37 | memset(s: saddr_un, c: 0, n: sizeof(*saddr_un)); |
38 | saddr_un->sun_family = kDomain; |
39 | |
40 | memcpy(dest: saddr_un->sun_path + name_offset, src: name.data(), n: name.size()); |
41 | |
42 | // For domain sockets we can use SUN_LEN in order to calculate size of |
43 | // sockaddr_un, but for abstract sockets we have to calculate size manually |
44 | // because of leading null symbol. |
45 | if (name_offset == 0) |
46 | saddr_un_len = SUN_LEN(saddr_un); |
47 | else |
48 | saddr_un_len = |
49 | offsetof(struct sockaddr_un, sun_path) + name_offset + name.size(); |
50 | |
51 | #if defined(__APPLE__) || defined(__FreeBSD__) || defined(__NetBSD__) || \ |
52 | defined(__OpenBSD__) |
53 | saddr_un->sun_len = saddr_un_len; |
54 | #endif |
55 | |
56 | return true; |
57 | } |
58 | |
59 | DomainSocket::DomainSocket(bool should_close, bool child_processes_inherit) |
60 | : Socket(ProtocolUnixDomain, should_close, child_processes_inherit) {} |
61 | |
62 | DomainSocket::DomainSocket(SocketProtocol protocol, |
63 | bool child_processes_inherit) |
64 | : Socket(protocol, true, child_processes_inherit) {} |
65 | |
66 | DomainSocket::DomainSocket(NativeSocket socket, |
67 | const DomainSocket &listen_socket) |
68 | : Socket(ProtocolUnixDomain, listen_socket.m_should_close_fd, |
69 | listen_socket.m_child_processes_inherit) { |
70 | m_socket = socket; |
71 | } |
72 | |
73 | Status DomainSocket::Connect(llvm::StringRef name) { |
74 | sockaddr_un saddr_un; |
75 | socklen_t saddr_un_len; |
76 | if (!SetSockAddr(name, name_offset: GetNameOffset(), saddr_un: &saddr_un, saddr_un_len)) |
77 | return Status("Failed to set socket address" ); |
78 | |
79 | Status error; |
80 | m_socket = CreateSocket(domain: kDomain, type: kType, protocol: 0, child_processes_inherit: m_child_processes_inherit, error); |
81 | if (error.Fail()) |
82 | return error; |
83 | if (llvm::sys::RetryAfterSignal(Fail: -1, F&: ::connect, As: GetNativeSocket(), |
84 | As: (struct sockaddr *)&saddr_un, As: saddr_un_len) < 0) |
85 | SetLastError(error); |
86 | |
87 | return error; |
88 | } |
89 | |
90 | Status DomainSocket::Listen(llvm::StringRef name, int backlog) { |
91 | sockaddr_un saddr_un; |
92 | socklen_t saddr_un_len; |
93 | if (!SetSockAddr(name, name_offset: GetNameOffset(), saddr_un: &saddr_un, saddr_un_len)) |
94 | return Status("Failed to set socket address" ); |
95 | |
96 | DeleteSocketFile(name); |
97 | |
98 | Status error; |
99 | m_socket = CreateSocket(domain: kDomain, type: kType, protocol: 0, child_processes_inherit: m_child_processes_inherit, error); |
100 | if (error.Fail()) |
101 | return error; |
102 | if (::bind(fd: GetNativeSocket(), addr: (struct sockaddr *)&saddr_un, len: saddr_un_len) == |
103 | 0) |
104 | if (::listen(fd: GetNativeSocket(), n: backlog) == 0) |
105 | return error; |
106 | |
107 | SetLastError(error); |
108 | return error; |
109 | } |
110 | |
111 | Status DomainSocket::Accept(Socket *&socket) { |
112 | Status error; |
113 | auto conn_fd = AcceptSocket(sockfd: GetNativeSocket(), addr: nullptr, addrlen: nullptr, |
114 | child_processes_inherit: m_child_processes_inherit, error); |
115 | if (error.Success()) |
116 | socket = new DomainSocket(conn_fd, *this); |
117 | |
118 | return error; |
119 | } |
120 | |
121 | size_t DomainSocket::GetNameOffset() const { return 0; } |
122 | |
123 | void DomainSocket::DeleteSocketFile(llvm::StringRef name) { |
124 | llvm::sys::fs::remove(path: name); |
125 | } |
126 | |
127 | std::string DomainSocket::GetSocketName() const { |
128 | if (m_socket == kInvalidSocketValue) |
129 | return "" ; |
130 | |
131 | struct sockaddr_un saddr_un; |
132 | saddr_un.sun_family = AF_UNIX; |
133 | socklen_t sock_addr_len = sizeof(struct sockaddr_un); |
134 | if (::getpeername(fd: m_socket, addr: (struct sockaddr *)&saddr_un, len: &sock_addr_len) != |
135 | 0) |
136 | return "" ; |
137 | |
138 | if (sock_addr_len <= offsetof(struct sockaddr_un, sun_path)) |
139 | return "" ; // Unnamed domain socket |
140 | |
141 | llvm::StringRef name(saddr_un.sun_path + GetNameOffset(), |
142 | sock_addr_len - offsetof(struct sockaddr_un, sun_path) - |
143 | GetNameOffset()); |
144 | name = name.rtrim(Char: '\0'); |
145 | |
146 | return name.str(); |
147 | } |
148 | |
149 | std::string DomainSocket::GetRemoteConnectionURI() const { |
150 | std::string name = GetSocketName(); |
151 | if (name.empty()) |
152 | return name; |
153 | |
154 | return llvm::formatv( |
155 | Fmt: "{0}://{1}" , |
156 | Vals: GetNameOffset() == 0 ? "unix-connect" : "unix-abstract-connect" , Vals&: name); |
157 | } |
158 | |