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

source code of qtbase/src/network/socket/qlocalsocket_unix.cpp