Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
qlocalserver_unix.cpp
Go to the documentation of this file.
1// Copyright (C) 2016 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3// Qt-Security score:significant reason:default
4
5#include "qlocalserver.h"
7#include "qlocalsocket.h"
9#include "qnet_unix_p.h"
10#include "qtemporarydir.h"
11
12#include <stddef.h>
13#include <sys/socket.h>
14#include <sys/un.h>
15
16#include <qdebug.h>
17#include <qdir.h>
18#include <qdatetime.h>
19
20#include <optional>
21
22#ifdef Q_OS_VXWORKS
23# include <selectLib.h>
24#endif
25
27
28using namespace Qt::StringLiterals;
29
30namespace {
31QLocalServer::SocketOptions optionsForPlatform(QLocalServer::SocketOptions srcOptions)
32{
33 // For OS that does not support abstract namespace the AbstractNamespaceOption
34 // means that we go for WorldAccessOption - as it is the closest option in
35 // regards of access rights. In Linux/Android case we clean-up the access rights.
36
37 if (srcOptions.testFlag(QLocalServer::AbstractNamespaceOption)) {
38 if (PlatformSupportsAbstractNamespace)
39 return QLocalServer::AbstractNamespaceOption;
40 else
41 return QLocalServer::WorldAccessOption;
42 }
43 return srcOptions;
44}
45}
46
47void QLocalServerPrivate::init()
48{
49}
50
51bool QLocalServerPrivate::removeServer(const QString &name)
52{
53 QString fileName;
54 if (name.startsWith(u'/')) {
55 fileName = name;
56 } else {
57 fileName = QDir::cleanPath(QDir::tempPath());
58 fileName += u'/' + name;
59 }
60 if (QFile::exists(fileName))
61 return QFile::remove(fileName);
62 else
63 return true;
64}
65
66bool QLocalServerPrivate::listen(const QString &requestedServerName)
67{
68 Q_Q(QLocalServer);
69
70 // socket options adjusted for current platform
71 auto options = optionsForPlatform(socketOptions.value());
72
73 // determine the full server path
74 if (options.testFlag(QLocalServer::AbstractNamespaceOption)
75 || requestedServerName.startsWith(u'/')) {
76 fullServerName = requestedServerName;
77 } else {
78 fullServerName = QDir::cleanPath(QDir::tempPath());
79 fullServerName += u'/' + requestedServerName;
80 }
81 serverName = requestedServerName;
82
83 QByteArray encodedTempPath;
84 const QByteArray encodedFullServerName = QFile::encodeName(fullServerName);
85 std::optional<QTemporaryDir> tempDir;
86
87 if (options & QLocalServer::WorldAccessOption) {
88 QFileInfo serverNameFileInfo(fullServerName);
89 tempDir.emplace(serverNameFileInfo.absolutePath() + u'/');
90 if (!tempDir->isValid()) {
91 setError("QLocalServer::listen"_L1);
92 return false;
93 }
94 encodedTempPath = QFile::encodeName(tempDir->path() + "/s"_L1);
95 }
96
97 // create the unix socket
98 listenSocket = qt_safe_socket(PF_UNIX, SOCK_STREAM, 0);
99 if (-1 == listenSocket) {
100 setError("QLocalServer::listen"_L1);
101 closeServer();
102 return false;
103 }
104
105 // Construct the unix address
106 struct ::sockaddr_un addr;
107
108 addr.sun_family = PF_UNIX;
109 ::memset(addr.sun_path, 0, sizeof(addr.sun_path));
110
111 // for abstract namespace add 2 to length, to take into account trailing AND leading null
112 constexpr unsigned int extraCharacters = PlatformSupportsAbstractNamespace ? 2 : 1;
113
114 if (sizeof(addr.sun_path) < static_cast<size_t>(encodedFullServerName.size() + extraCharacters)) {
115 setError("QLocalServer::listen"_L1);
116 closeServer();
117 return false;
118 }
119
120 QT_SOCKLEN_T addrSize = sizeof(::sockaddr_un);
121 if (options.testFlag(QLocalServer::AbstractNamespaceOption)) {
122 // Abstract socket address is distinguished by the fact
123 // that sun_path[0] is a null byte ('\0')
124 ::memcpy(addr.sun_path + 1, encodedFullServerName.constData(),
125 encodedFullServerName.size() + 1);
126 addrSize = offsetof(::sockaddr_un, sun_path) + encodedFullServerName.size() + 1;
127 } else if (options & QLocalServer::WorldAccessOption) {
128 if (sizeof(addr.sun_path) < static_cast<size_t>(encodedTempPath.size() + 1)) {
129 setError("QLocalServer::listen"_L1);
130 closeServer();
131 return false;
132 }
133 ::memcpy(addr.sun_path, encodedTempPath.constData(),
134 encodedTempPath.size() + 1);
135 } else {
136 ::memcpy(addr.sun_path, encodedFullServerName.constData(),
137 encodedFullServerName.size() + 1);
138 }
139
140 // bind
141 if (-1 == QT_SOCKET_BIND(listenSocket, (sockaddr *)&addr, addrSize)) {
142 setError("QLocalServer::listen"_L1);
143 // if address is in use already, just close the socket, but do not delete the file
144 if (errno == EADDRINUSE)
145 QT_CLOSE(listenSocket);
146 // otherwise, close the socket and delete the file
147 else
148 closeServer();
149 listenSocket = -1;
150 return false;
151 }
152
153 // listen for connections
154 if (-1 == qt_safe_listen(listenSocket, listenBacklog)) {
155 setError("QLocalServer::listen"_L1);
156 closeServer();
157 return false;
158 }
159
160 if (options & QLocalServer::WorldAccessOption) {
161 mode_t mode = 000;
162
163 if (options & QLocalServer::UserAccessOption)
164 mode |= S_IRWXU;
165
166 if (options & QLocalServer::GroupAccessOption)
167 mode |= S_IRWXG;
168
169 if (options & QLocalServer::OtherAccessOption)
170 mode |= S_IRWXO;
171
172 if (::chmod(encodedTempPath.constData(), mode) == -1) {
173 setError("QLocalServer::listen"_L1);
174 closeServer();
175 return false;
176 }
177
178 if (::rename(encodedTempPath.constData(), encodedFullServerName.constData()) == -1) {
179 setError("QLocalServer::listen"_L1);
180 closeServer();
181 return false;
182 }
183 }
184
185 Q_ASSERT(!socketNotifier);
186 socketNotifier = new QSocketNotifier(listenSocket,
187 QSocketNotifier::Read, q);
188 q->connect(socketNotifier, SIGNAL(activated(QSocketDescriptor)),
189 q, SLOT(_q_onNewConnection()));
190 socketNotifier->setEnabled(maxPendingConnections > 0);
191 return true;
192}
193
194bool QLocalServerPrivate::listen(qintptr socketDescriptor)
195{
196 Q_Q(QLocalServer);
197
198 // Attach to the localsocket
199 listenSocket = socketDescriptor;
200
201 ::fcntl(listenSocket, F_SETFD, FD_CLOEXEC);
202 ::fcntl(listenSocket, F_SETFL, ::fcntl(listenSocket, F_GETFL) | O_NONBLOCK);
203
204 bool abstractAddress = false;
205 struct ::sockaddr_un addr;
206 QT_SOCKLEN_T len = sizeof(addr);
207 memset(&addr, 0, sizeof(addr));
208 if (::getsockname(socketDescriptor, (sockaddr *)&addr, &len) == 0) {
209#if defined(Q_OS_QNX)
210 if (addr.sun_path[0] == 0 && addr.sun_path[1] == 0)
211 len = SUN_LEN(&addr);
212#endif
213 if (QLocalSocketPrivate::parseSockaddr(addr, len, fullServerName, serverName,
214 abstractAddress)) {
215 QLocalServer::SocketOptions options = socketOptions.value();
216 socketOptions = options.setFlag(QLocalServer::AbstractNamespaceOption, abstractAddress);
217 }
218 }
219
220 Q_ASSERT(!socketNotifier);
221 socketNotifier = new QSocketNotifier(listenSocket,
222 QSocketNotifier::Read, q);
223 q->connect(socketNotifier, SIGNAL(activated(QSocketDescriptor)),
224 q, SLOT(_q_onNewConnection()));
225 socketNotifier->setEnabled(maxPendingConnections > 0);
226 return true;
227}
228
229/*!
230 \internal
231
232 \sa QLocalServer::closeServer()
233 */
234void QLocalServerPrivate::closeServer()
235{
236 if (socketNotifier) {
237 socketNotifier->setEnabled(false); // Otherwise, closed socket is checked before deleter runs
238 socketNotifier->deleteLater();
239 socketNotifier = nullptr;
240 }
241
242 if (-1 != listenSocket)
243 QT_CLOSE(listenSocket);
244 listenSocket = -1;
245
246 if (!fullServerName.isEmpty()
247 && !optionsForPlatform(socketOptions).testFlag(QLocalServer::AbstractNamespaceOption)) {
248 QFile::remove(fullServerName);
249 }
250
251 serverName.clear();
252 fullServerName.clear();
253}
254
255/*!
256 \internal
257
258 We have received a notification that we can read on the listen socket.
259 Accept the new socket.
260 */
261void QLocalServerPrivate::_q_onNewConnection()
262{
263 Q_Q(QLocalServer);
264 if (-1 == listenSocket)
265 return;
266
267 ::sockaddr_un addr;
268 QT_SOCKLEN_T length = sizeof(sockaddr_un);
269 int connectedSocket = qt_safe_accept(listenSocket, (sockaddr *)&addr, &length);
270 if (-1 == connectedSocket) {
271 setError("QLocalSocket::activated"_L1);
272 closeServer();
273 } else {
274 socketNotifier->setEnabled(pendingConnections.size()
275 <= maxPendingConnections);
276 q->incomingConnection(connectedSocket);
277 }
278}
279
280void QLocalServerPrivate::waitForNewConnection(int msec, bool *timedOut)
281{
282 pollfd pfd = qt_make_pollfd(listenSocket, POLLIN);
283 switch (qt_safe_poll(&pfd, 1, QDeadlineTimer(msec))) {
284 case 0:
285 if (timedOut)
286 *timedOut = true;
287
288 return;
289 break;
290 default:
291 if ((pfd.revents & POLLNVAL) == 0) {
292 _q_onNewConnection();
293 return;
294 }
295
296 errno = EBADF;
297 Q_FALLTHROUGH();
298 case -1:
299 setError("QLocalServer::waitForNewConnection"_L1);
300 closeServer();
301 break;
302 }
303}
304
305void QLocalServerPrivate::setError(const QString &function)
306{
307 if (EAGAIN == errno)
308 return;
309
310 switch (errno) {
311 case EACCES:
312 errorString = QLocalServer::tr("%1: Permission denied").arg(function);
313 error = QAbstractSocket::SocketAccessError;
314 break;
315 case ELOOP:
316 case ENOENT:
317 case ENAMETOOLONG:
318 case EROFS:
319 case ENOTDIR:
320 errorString = QLocalServer::tr("%1: Name error").arg(function);
321 error = QAbstractSocket::HostNotFoundError;
322 break;
323 case EADDRINUSE:
324 errorString = QLocalServer::tr("%1: Address in use").arg(function);
325 error = QAbstractSocket::AddressInUseError;
326 break;
327
328 default:
329 errorString = QLocalServer::tr("%1: Unknown error %2")
330 .arg(function).arg(errno);
331 error = QAbstractSocket::UnknownSocketError;
332#if defined QLOCALSERVER_DEBUG
333 qWarning() << errorString << "fullServerName:" << fullServerName;
334#endif
335 }
336}
337
338QT_END_NAMESPACE