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