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
qlocalsocket_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 "qlocalsocket.h"
7#include "qnet_unix_p.h"
8
9#include <sys/types.h>
10#include <sys/socket.h>
11#include <sys/un.h>
12#include <unistd.h>
13#include <fcntl.h>
14#include <errno.h>
15
16#include <qdir.h>
17#include <qdeadlinetimer.h>
18#include <qdebug.h>
19#include <qstringconverter.h>
20
21#ifdef Q_OS_VXWORKS
22# include <selectLib.h>
23#endif
24
25using namespace std::chrono_literals;
26
27#define QT_CONNECT_TIMEOUT 30000
28
29QT_BEGIN_NAMESPACE
30
31using namespace Qt::StringLiterals;
32
33namespace {
34// determine the full server path
35static QString pathNameForConnection(const QString &connectingName,
36 QLocalSocket::SocketOptions options)
37{
38 if (options.testFlag(QLocalSocket::AbstractNamespaceOption)
39 || connectingName.startsWith(u'/')) {
40 return connectingName;
41 }
42
43 return QDir::tempPath() + u'/' + connectingName;
44}
45
46static QLocalSocket::SocketOptions optionsForPlatform(QLocalSocket::SocketOptions srcOptions)
47{
48 // For OS that does not support abstract namespace the AbstractNamespaceOption
49 // option is cleared.
50 if (!PlatformSupportsAbstractNamespace)
51 return QLocalSocket::NoOptions;
52 return srcOptions;
53}
54}
55
56QLocalSocketPrivate::QLocalSocketPrivate() : QIODevicePrivate(),
57 delayConnect(nullptr),
58 connectTimer(nullptr),
60 state(QLocalSocket::UnconnectedState),
61 socketOptions(QLocalSocket::NoOptions)
62{
63}
64
66{
67 Q_Q(QLocalSocket);
68 // QIODevice signals
69 q->connect(&unixSocket, SIGNAL(bytesWritten(qint64)),
70 q, SIGNAL(bytesWritten(qint64)));
71 q->connect(&unixSocket, SIGNAL(readyRead()), q, SIGNAL(readyRead()));
72 // QAbstractSocket signals
73 q->connect(&unixSocket, SIGNAL(connected()), q, SIGNAL(connected()));
74 q->connect(&unixSocket, SIGNAL(disconnected()), q, SIGNAL(disconnected()));
75 q->connect(&unixSocket, SIGNAL(stateChanged(QAbstractSocket::SocketState)),
76 q, SLOT(_q_stateChanged(QAbstractSocket::SocketState)));
77 q->connect(&unixSocket, SIGNAL(errorOccurred(QAbstractSocket::SocketError)),
78 q, SLOT(_q_errorOccurred(QAbstractSocket::SocketError)));
79 q->connect(&unixSocket, SIGNAL(readChannelFinished()), q, SIGNAL(readChannelFinished()));
80 unixSocket.setParent(q);
81}
82
83void QLocalSocketPrivate::_q_errorOccurred(QAbstractSocket::SocketError socketError)
84{
85 Q_Q(QLocalSocket);
86 QString function = "QLocalSocket"_L1;
87 QLocalSocket::LocalSocketError error = (QLocalSocket::LocalSocketError)socketError;
88 QString errorString = generateErrorString(error, function);
89 q->setErrorString(errorString);
90 emit q->errorOccurred(error);
91}
92
93void QLocalSocketPrivate::_q_stateChanged(QAbstractSocket::SocketState newState)
94{
95 Q_Q(QLocalSocket);
96 QLocalSocket::LocalSocketState currentState = state;
97 switch(newState) {
98 case QAbstractSocket::UnconnectedState:
99 state = QLocalSocket::UnconnectedState;
100 serverName.clear();
101 fullServerName.clear();
102 break;
103 case QAbstractSocket::ConnectingState:
104 state = QLocalSocket::ConnectingState;
105 break;
106 case QAbstractSocket::ConnectedState:
107 state = QLocalSocket::ConnectedState;
108 break;
109 case QAbstractSocket::ClosingState:
110 state = QLocalSocket::ClosingState;
111 break;
112 default:
113#if defined QLOCALSOCKET_DEBUG
114 qWarning() << "QLocalSocket::Unhandled socket state change:" << newState;
115#endif
116 return;
117 }
118 if (currentState != state)
119 emit q->stateChanged(state);
120}
121
122QString QLocalSocketPrivate::generateErrorString(QLocalSocket::LocalSocketError error, const QString &function) const
123{
124 QString errorString;
125 switch (error) {
126 case QLocalSocket::ConnectionRefusedError:
127 errorString = QLocalSocket::tr("%1: Connection refused").arg(function);
128 break;
129 case QLocalSocket::PeerClosedError:
130 errorString = QLocalSocket::tr("%1: Remote closed").arg(function);
131 break;
132 case QLocalSocket::ServerNotFoundError:
133 errorString = QLocalSocket::tr("%1: Invalid name").arg(function);
134 break;
135 case QLocalSocket::SocketAccessError:
136 errorString = QLocalSocket::tr("%1: Socket access error").arg(function);
137 break;
138 case QLocalSocket::SocketResourceError:
139 errorString = QLocalSocket::tr("%1: Socket resource error").arg(function);
140 break;
141 case QLocalSocket::SocketTimeoutError:
142 errorString = QLocalSocket::tr("%1: Socket operation timed out").arg(function);
143 break;
144 case QLocalSocket::DatagramTooLargeError:
145 errorString = QLocalSocket::tr("%1: Datagram too large").arg(function);
146 break;
147 case QLocalSocket::ConnectionError:
148 errorString = QLocalSocket::tr("%1: Connection error").arg(function);
149 break;
150 case QLocalSocket::UnsupportedSocketOperationError:
151 errorString = QLocalSocket::tr("%1: The socket operation is not supported").arg(function);
152 break;
153 case QLocalSocket::OperationError:
154 errorString = QLocalSocket::tr("%1: Operation not permitted when socket is in this state").arg(function);
155 break;
156 case QLocalSocket::UnknownSocketError:
157 default:
158 errorString = QLocalSocket::tr("%1: Unknown error %2").arg(function).arg(errno);
159 }
160 return errorString;
161}
162
163void QLocalSocketPrivate::setErrorAndEmit(QLocalSocket::LocalSocketError error, const QString &function)
164{
165 Q_Q(QLocalSocket);
166 switch (error) {
167 case QLocalSocket::ConnectionRefusedError:
168 unixSocket.setSocketError(QAbstractSocket::ConnectionRefusedError);
169 break;
170 case QLocalSocket::PeerClosedError:
171 unixSocket.setSocketError(QAbstractSocket::RemoteHostClosedError);
172 break;
173 case QLocalSocket::ServerNotFoundError:
174 unixSocket.setSocketError(QAbstractSocket::HostNotFoundError);
175 break;
176 case QLocalSocket::SocketAccessError:
177 unixSocket.setSocketError(QAbstractSocket::SocketAccessError);
178 break;
179 case QLocalSocket::SocketResourceError:
180 unixSocket.setSocketError(QAbstractSocket::SocketResourceError);
181 break;
182 case QLocalSocket::SocketTimeoutError:
183 unixSocket.setSocketError(QAbstractSocket::SocketTimeoutError);
184 break;
185 case QLocalSocket::DatagramTooLargeError:
186 unixSocket.setSocketError(QAbstractSocket::DatagramTooLargeError);
187 break;
188 case QLocalSocket::ConnectionError:
189 unixSocket.setSocketError(QAbstractSocket::NetworkError);
190 break;
191 case QLocalSocket::UnsupportedSocketOperationError:
192 unixSocket.setSocketError(QAbstractSocket::UnsupportedSocketOperationError);
193 break;
194 case QLocalSocket::UnknownSocketError:
195 default:
196 unixSocket.setSocketError(QAbstractSocket::UnknownSocketError);
197 }
198
199 QString errorString = generateErrorString(error, function);
200 q->setErrorString(errorString);
201 emit q->errorOccurred(error);
202
203 // errors cause a disconnect
204 unixSocket.setSocketState(QAbstractSocket::UnconnectedState);
205 bool stateChanged = (state != QLocalSocket::UnconnectedState);
206 state = QLocalSocket::UnconnectedState;
207 q->close();
208 if (stateChanged)
209 q->emit stateChanged(state);
210}
211
212void QLocalSocket::connectToServer(OpenMode openMode)
213{
214 Q_D(QLocalSocket);
215 if (state() == ConnectedState || state() == ConnectingState) {
216 QString errorString = d->generateErrorString(QLocalSocket::OperationError, "QLocalSocket::connectToserver"_L1);
217 setErrorString(errorString);
218 emit errorOccurred(QLocalSocket::OperationError);
219 return;
220 }
221
222 d->errorString.clear();
223 d->unixSocket.setSocketState(QAbstractSocket::ConnectingState);
224 d->state = ConnectingState;
225 emit stateChanged(d->state);
226
227 if (d->serverName.isEmpty()) {
228 d->setErrorAndEmit(ServerNotFoundError, "QLocalSocket::connectToServer"_L1);
229 return;
230 }
231
232 // create the socket
233 if (-1 == (d->connectingSocket = qt_safe_socket(PF_UNIX, SOCK_STREAM, 0, O_NONBLOCK))) {
234 d->setErrorAndEmit(UnsupportedSocketOperationError, "QLocalSocket::connectToServer"_L1);
235 return;
236 }
237
238 // _q_connectToSocket does the actual connecting
239 d->connectingName = d->serverName;
240 d->connectingOpenMode = openMode;
241 d->_q_connectToSocket();
242 return;
243}
244
245/*!
246 \internal
247
248 Tries to connect connectingName and connectingOpenMode
249
250 \sa connectToServer(), waitForConnected()
251 */
253{
254 Q_Q(QLocalSocket);
255
256 QLocalSocket::SocketOptions options = optionsForPlatform(socketOptions);
257 const QString connectingPathName = pathNameForConnection(connectingName, options);
258 const QByteArray encodedConnectingPathName = QFile::encodeName(connectingPathName);
259 struct ::sockaddr_un addr;
260 addr.sun_family = PF_UNIX;
261 memset(addr.sun_path, 0, sizeof(addr.sun_path));
262
263 // for abstract socket add 2 to length, to take into account trailing AND leading null
264 constexpr unsigned int extraCharacters = PlatformSupportsAbstractNamespace ? 2 : 1;
265
266 if (sizeof(addr.sun_path) < static_cast<size_t>(encodedConnectingPathName.size() + extraCharacters)) {
267 QString function = "QLocalSocket::connectToServer"_L1;
268 setErrorAndEmit(QLocalSocket::ServerNotFoundError, function);
269 return;
270 }
271
272 QT_SOCKLEN_T addrSize = sizeof(::sockaddr_un);
273 if (options.testFlag(QLocalSocket::AbstractNamespaceOption)) {
274 ::memcpy(addr.sun_path + 1, encodedConnectingPathName.constData(),
275 encodedConnectingPathName.size() + 1);
276 addrSize = offsetof(::sockaddr_un, sun_path) + encodedConnectingPathName.size() + 1;
277 } else {
278 ::memcpy(addr.sun_path, encodedConnectingPathName.constData(),
279 encodedConnectingPathName.size() + 1);
280 }
281 if (-1 == qt_safe_connect(connectingSocket, (struct sockaddr *)&addr, addrSize)) {
282 QString function = "QLocalSocket::connectToServer"_L1;
283 switch (errno)
284 {
285 case EINVAL:
286 case ECONNREFUSED:
287 setErrorAndEmit(QLocalSocket::ConnectionRefusedError, function);
288 break;
289 case ENOENT:
290 setErrorAndEmit(QLocalSocket::ServerNotFoundError, function);
291 break;
292 case EACCES:
293 case EPERM:
294 setErrorAndEmit(QLocalSocket::SocketAccessError, function);
295 break;
296 case ETIMEDOUT:
297 setErrorAndEmit(QLocalSocket::SocketTimeoutError, function);
298 break;
299 case EAGAIN:
300 // Try again later, all of the sockets listening are full
301 if (!delayConnect) {
302 delayConnect = new QSocketNotifier(connectingSocket, QSocketNotifier::Write, q);
303 q->connect(delayConnect, SIGNAL(activated(QSocketDescriptor)), q, SLOT(_q_connectToSocket()));
304 }
305 if (!connectTimer) {
306 connectTimer = new QTimer(q);
307 q->connect(connectTimer, SIGNAL(timeout()),
308 q, SLOT(_q_abortConnectionAttempt()),
309 Qt::DirectConnection);
310 connectTimer->start(QT_CONNECT_TIMEOUT);
311 }
312 delayConnect->setEnabled(true);
313 break;
314 default:
315 setErrorAndEmit(QLocalSocket::UnknownSocketError, function);
316 }
317 return;
318 }
319
320 // connected!
322
323 serverName = connectingName;
324 fullServerName = connectingPathName;
325 if (unixSocket.setSocketDescriptor(connectingSocket,
326 QAbstractSocket::ConnectedState, connectingOpenMode)) {
327 q->QIODevice::open(connectingOpenMode);
328 q->emit connected();
329 } else {
330 QString function = "QLocalSocket::connectToServer"_L1;
331 setErrorAndEmit(QLocalSocket::UnknownSocketError, function);
332 }
333 connectingSocket = -1;
334 connectingName.clear();
335 connectingOpenMode = { };
336}
337
338bool QLocalSocket::setSocketDescriptor(qintptr socketDescriptor,
339 LocalSocketState socketState, OpenMode openMode)
340{
341 Q_D(QLocalSocket);
342 QAbstractSocket::SocketState newSocketState = QAbstractSocket::UnconnectedState;
343 switch (socketState) {
344 case ConnectingState:
345 newSocketState = QAbstractSocket::ConnectingState;
346 break;
347 case ConnectedState:
348 newSocketState = QAbstractSocket::ConnectedState;
349 break;
350 case ClosingState:
351 newSocketState = QAbstractSocket::ClosingState;
352 break;
353 case UnconnectedState:
354 newSocketState = QAbstractSocket::UnconnectedState;
355 break;
356 }
357 QIODevice::open(openMode);
358 d->state = socketState;
359 d->describeSocket(socketDescriptor);
360 const bool result = d->unixSocket.setSocketDescriptor(socketDescriptor,
361 newSocketState, openMode);
362 // Since we directly assigned d->state above, any emission from unixSocket for
363 // state change emission is ignored because the state hasn't changed. So, we
364 // emit it directly here ourselves.
365 if (result)
366 emit stateChanged(d->state);
367 return result;
368}
369
370void QLocalSocketPrivate::describeSocket(qintptr socketDescriptor)
371{
372 bool abstractAddress = false;
373
374 struct ::sockaddr_un addr;
375 QT_SOCKLEN_T len = sizeof(addr);
376 memset(&addr, 0, sizeof(addr));
377 const int getpeernameStatus = ::getpeername(socketDescriptor, (sockaddr *)&addr, &len);
378 if (getpeernameStatus != 0 || len == offsetof(sockaddr_un, sun_path)) {
379 // this is the case when we call it from QLocalServer, then there is no peername
380 len = sizeof(addr);
381 if (::getsockname(socketDescriptor, (sockaddr *)&addr, &len) != 0)
382 return;
383 }
384 if (parseSockaddr(addr, static_cast<uint>(len), fullServerName, serverName, abstractAddress)) {
385 QLocalSocket::SocketOptions options = socketOptions.value();
386 socketOptions = options.setFlag(QLocalSocket::AbstractNamespaceOption, abstractAddress);
387 }
388}
389
390bool QLocalSocketPrivate::parseSockaddr(const struct ::sockaddr_un &addr,
391 uint len,
392 QString &fullServerName,
393 QString &serverName,
394 bool &abstractNamespace)
395{
396 if (len <= offsetof(::sockaddr_un, sun_path))
397 return false;
398 len -= offsetof(::sockaddr_un, sun_path);
399 // check for abstract socket address
400 abstractNamespace = PlatformSupportsAbstractNamespace
401 && (addr.sun_family == PF_UNIX && addr.sun_path[0] == 0);
402 QStringDecoder toUtf16(QStringDecoder::System, QStringDecoder::Flag::Stateless);
403 // An abstract socket address can be arbitrary binary. To properly handle such a case,
404 // we'd have to add new access functions for this very specific case. Instead, we just
405 // attempt to decode it according to OS text encoding. If it fails we ignore the result.
406 QByteArrayView textData(addr.sun_path + (abstractNamespace ? 1 : 0),
407 len - (abstractNamespace ? 1 : 0));
408 QString name = toUtf16(textData);
409 if (!name.isEmpty() && !toUtf16.hasError()) {
410 //conversion encodes the trailing zeros. So, in case of non-abstract namespace we
411 //chop them off as \0 character is not allowed in filenames
412 if (!abstractNamespace && (name.at(name.size() - 1) == QChar::fromLatin1('\0'))) {
413 int truncPos = name.size() - 1;
414 while (truncPos > 0 && name.at(truncPos - 1) == QChar::fromLatin1('\0'))
415 truncPos--;
416 name.truncate(truncPos);
417 }
418 fullServerName = name;
419 serverName = abstractNamespace
420 ? name
421 : fullServerName.mid(fullServerName.lastIndexOf(u'/') + 1);
422 if (serverName.isEmpty())
423 serverName = fullServerName;
424 }
425 return true;
426}
427
429{
430 Q_Q(QLocalSocket);
431 q->close();
432}
433
435{
436 if (delayConnect) {
437 delayConnect->setEnabled(false);
438 delete delayConnect;
439 delayConnect = nullptr;
440 connectTimer->stop();
441 delete connectTimer;
442 connectTimer = nullptr;
443 }
444}
445
446qintptr QLocalSocket::socketDescriptor() const
447{
448 Q_D(const QLocalSocket);
449 return d->unixSocket.socketDescriptor();
450}
451
452qint64 QLocalSocket::readData(char *data, qint64 c)
453{
454 Q_D(QLocalSocket);
455 return d->unixSocket.read(data, c);
456}
457
458qint64 QLocalSocket::readLineData(char *data, qint64 maxSize)
459{
460 if (!maxSize)
461 return 0;
462
463 // QIODevice::readLine() reserves space for the trailing '\0' byte,
464 // so we must read 'maxSize + 1' bytes.
465 return d_func()->unixSocket.readLine(data, maxSize + 1);
466}
467
468qint64 QLocalSocket::skipData(qint64 maxSize)
469{
470 return d_func()->unixSocket.skip(maxSize);
471}
472
473qint64 QLocalSocket::writeData(const char *data, qint64 c)
474{
475 Q_D(QLocalSocket);
476 return d->unixSocket.writeData(data, c);
477}
478
479void QLocalSocket::abort()
480{
481 Q_D(QLocalSocket);
482 d->unixSocket.abort();
483 close();
484}
485
486qint64 QLocalSocket::bytesAvailable() const
487{
488 Q_D(const QLocalSocket);
489 return QIODevice::bytesAvailable() + d->unixSocket.bytesAvailable();
490}
491
492qint64 QLocalSocket::bytesToWrite() const
493{
494 Q_D(const QLocalSocket);
495 return d->unixSocket.bytesToWrite();
496}
497
498bool QLocalSocket::canReadLine() const
499{
500 Q_D(const QLocalSocket);
501 return QIODevice::canReadLine() || d->unixSocket.canReadLine();
502}
503
504void QLocalSocket::close()
505{
506 Q_D(QLocalSocket);
507
508 QIODevice::close();
509 d->unixSocket.close();
510 d->cancelDelayedConnect();
511 if (d->connectingSocket != -1)
512 ::close(d->connectingSocket);
513 d->connectingSocket = -1;
514 d->connectingName.clear();
515 d->connectingOpenMode = { };
516 d->serverName.clear();
517 d->fullServerName.clear();
518}
519
520bool QLocalSocket::waitForBytesWritten(int msecs)
521{
522 Q_D(QLocalSocket);
523 return d->unixSocket.waitForBytesWritten(msecs);
524}
525
526bool QLocalSocket::flush()
527{
528 Q_D(QLocalSocket);
529 return d->unixSocket.flush();
530}
531
532void QLocalSocket::disconnectFromServer()
533{
534 Q_D(QLocalSocket);
535 d->unixSocket.disconnectFromHost();
536}
537
538QLocalSocket::LocalSocketError QLocalSocket::error() const
539{
540 Q_D(const QLocalSocket);
541 switch (d->unixSocket.error()) {
542 case QAbstractSocket::ConnectionRefusedError:
543 return QLocalSocket::ConnectionRefusedError;
544 case QAbstractSocket::RemoteHostClosedError:
545 return QLocalSocket::PeerClosedError;
546 case QAbstractSocket::HostNotFoundError:
547 return QLocalSocket::ServerNotFoundError;
548 case QAbstractSocket::SocketAccessError:
549 return QLocalSocket::SocketAccessError;
550 case QAbstractSocket::SocketResourceError:
551 return QLocalSocket::SocketResourceError;
552 case QAbstractSocket::SocketTimeoutError:
553 return QLocalSocket::SocketTimeoutError;
554 case QAbstractSocket::DatagramTooLargeError:
555 return QLocalSocket::DatagramTooLargeError;
556 case QAbstractSocket::NetworkError:
557 return QLocalSocket::ConnectionError;
558 case QAbstractSocket::UnsupportedSocketOperationError:
559 return QLocalSocket::UnsupportedSocketOperationError;
560 case QAbstractSocket::UnknownSocketError:
561 return QLocalSocket::UnknownSocketError;
562 default:
563#if defined QLOCALSOCKET_DEBUG
564 qWarning() << "QLocalSocket error not handled:" << d->unixSocket.error();
565#endif
566 break;
567 }
568 return UnknownSocketError;
569}
570
571bool QLocalSocket::isValid() const
572{
573 Q_D(const QLocalSocket);
574 return d->unixSocket.isValid();
575}
576
577qint64 QLocalSocket::readBufferSize() const
578{
579 Q_D(const QLocalSocket);
580 return d->unixSocket.readBufferSize();
581}
582
583void QLocalSocket::setReadBufferSize(qint64 size)
584{
585 Q_D(QLocalSocket);
586 d->unixSocket.setReadBufferSize(size);
587}
588
589bool QLocalSocket::waitForConnected(int msec)
590{
591 Q_D(QLocalSocket);
592
593 if (state() != ConnectingState)
594 return (state() == ConnectedState);
595
596 pollfd pfd = qt_make_pollfd(d->connectingSocket, POLLIN);
597
598 QDeadlineTimer deadline{msec};
599 auto remainingTime = deadline.remainingTimeAsDuration();
600
601 do {
602 const int result = qt_safe_poll(&pfd, 1, deadline);
603 if (result == -1)
604 d->setErrorAndEmit(QLocalSocket::UnknownSocketError,
605 "QLocalSocket::waitForConnected"_L1);
606 else if (result > 0)
607 d->_q_connectToSocket();
608 } while (state() == ConnectingState
609 && (remainingTime = deadline.remainingTimeAsDuration()) > 0ns);
610
611 return (state() == ConnectedState);
612}
613
614bool QLocalSocket::waitForDisconnected(int msecs)
615{
616 Q_D(QLocalSocket);
617 if (state() == UnconnectedState) {
618 qWarning("QLocalSocket::waitForDisconnected() is not allowed in UnconnectedState");
619 return false;
620 }
621 return (d->unixSocket.waitForDisconnected(msecs));
622}
623
624bool QLocalSocket::waitForReadyRead(int msecs)
625{
626 Q_D(QLocalSocket);
627 if (state() == QLocalSocket::UnconnectedState)
628 return false;
629 return (d->unixSocket.waitForReadyRead(msecs));
630}
631
632QT_END_NAMESPACE
void _q_stateChanged(QAbstractSocket::SocketState newState)
void describeSocket(qintptr socketDescriptor)
void _q_errorOccurred(QAbstractSocket::SocketError newError)
void setErrorAndEmit(QLocalSocket::LocalSocketError, const QString &function)
QString generateErrorString(QLocalSocket::LocalSocketError, const QString &function) const
\inmodule QtCore
#define QT_CONNECT_TIMEOUT