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_win.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
6#include <qscopedvaluerollback.h>
7#include <qdeadlinetimer.h>
8
9QT_BEGIN_NAMESPACE
10
11using namespace Qt::StringLiterals;
12
13namespace {
14struct QSocketPoller
15{
16 QSocketPoller(const QLocalSocketPrivate &socket);
17
18 qint64 getRemainingTime(const QDeadlineTimer &deadline) const;
19 bool poll(const QDeadlineTimer &deadline);
20
21 enum { maxHandles = 2 };
22 HANDLE handles[maxHandles];
23 DWORD handleCount = 0;
24 bool waitForClose = false;
25 bool writePending = false;
26};
27
28QSocketPoller::QSocketPoller(const QLocalSocketPrivate &socket)
29{
30 if (socket.pipeWriter && socket.pipeWriter->isWriteOperationActive()) {
31 handles[handleCount++] = socket.pipeWriter->syncEvent();
32 writePending = true;
33 }
34 if (socket.pipeReader->isReadOperationActive())
35 handles[handleCount++] = socket.pipeReader->syncEvent();
36 else
37 waitForClose = true;
38}
39
40qint64 QSocketPoller::getRemainingTime(const QDeadlineTimer &deadline) const
41{
42 const qint64 sleepTime = 10;
43 qint64 remainingTime = deadline.remainingTime();
44 if (waitForClose && (remainingTime > sleepTime || remainingTime == -1))
45 return sleepTime;
46
47 return remainingTime;
48}
49
50/*!
51 \internal
52
53 Waits until new data is available for reading or write operation
54 completes. Returns \c true, if we need to check pipe workers;
55 otherwise it returns \c false (if an error occurred or the operation
56 timed out).
57
58 \note If the read operation is inactive, it succeeds after
59 a short wait, allowing the caller to check the state of the socket.
60*/
61bool QSocketPoller::poll(const QDeadlineTimer &deadline)
62{
63 Q_ASSERT(handleCount != 0);
64 QDeadlineTimer timer(getRemainingTime(deadline));
65 DWORD waitRet;
66
67 do {
68 waitRet = WaitForMultipleObjectsEx(handleCount, handles, FALSE,
69 timer.remainingTime(), TRUE);
70 } while (waitRet == WAIT_IO_COMPLETION);
71
72 if (waitRet == WAIT_TIMEOUT)
73 return waitForClose || !deadline.hasExpired();
74
75 return waitRet - WAIT_OBJECT_0 < handleCount;
76}
77} // anonymous namespace
78
80{
81 Q_Q(QLocalSocket);
82 pipeReader = new QWindowsPipeReader(q);
83 QObjectPrivate::connect(pipeReader, &QWindowsPipeReader::readyRead,
84 this, &QLocalSocketPrivate::_q_canRead);
85 q->connect(pipeReader, SIGNAL(pipeClosed()), SLOT(_q_pipeClosed()), Qt::QueuedConnection);
86 q->connect(pipeReader, SIGNAL(winError(ulong,QString)), SLOT(_q_winError(ulong,QString)));
87}
88
89void QLocalSocketPrivate::_q_winError(ulong windowsError, const QString &function)
90{
91 Q_Q(QLocalSocket);
92 QLocalSocket::LocalSocketState currentState = state;
93
94 // If the connectToServer fails due to WaitNamedPipe() time-out, assume ConnectionError
95 if (state == QLocalSocket::ConnectingState && windowsError == ERROR_SEM_TIMEOUT)
96 windowsError = ERROR_NO_DATA;
97
98 switch (windowsError) {
99 case ERROR_PIPE_NOT_CONNECTED:
100 case ERROR_BROKEN_PIPE:
101 case ERROR_NO_DATA:
102 error = QLocalSocket::ConnectionError;
103 errorString = QLocalSocket::tr("%1: Connection error").arg(function);
104 state = QLocalSocket::UnconnectedState;
105 break;
106 case ERROR_FILE_NOT_FOUND:
107 error = QLocalSocket::ServerNotFoundError;
108 errorString = QLocalSocket::tr("%1: Invalid name").arg(function);
109 state = QLocalSocket::UnconnectedState;
110 break;
111 case ERROR_ACCESS_DENIED:
112 error = QLocalSocket::SocketAccessError;
113 errorString = QLocalSocket::tr("%1: Access denied").arg(function);
114 state = QLocalSocket::UnconnectedState;
115 break;
116 default:
117 error = QLocalSocket::UnknownSocketError;
118 errorString = QLocalSocket::tr("%1: Unknown error %2").arg(function).arg(windowsError);
119#if defined QLOCALSOCKET_DEBUG
120 qWarning() << "QLocalSocket error not handled:" << errorString;
121#endif
122 state = QLocalSocket::UnconnectedState;
123 }
124
125 if (currentState != state) {
126 emit q->stateChanged(state);
127 if (state == QLocalSocket::UnconnectedState && currentState != QLocalSocket::ConnectingState)
128 emit q->disconnected();
129 }
130 emit q->errorOccurred(error);
131}
132
133QLocalSocketPrivate::QLocalSocketPrivate() : QIODevicePrivate(),
134 handle(INVALID_HANDLE_VALUE),
135 pipeWriter(0),
136 pipeReader(0),
137 error(QLocalSocket::UnknownSocketError),
138 state(QLocalSocket::UnconnectedState),
139 emittedReadyRead(false),
140 emittedBytesWritten(false)
141{
142}
143
144QLocalSocketPrivate::~QLocalSocketPrivate()
145{
146 Q_ASSERT(state == QLocalSocket::UnconnectedState);
147 Q_ASSERT(handle == INVALID_HANDLE_VALUE);
148 Q_ASSERT(pipeWriter == nullptr);
149}
150
151void QLocalSocket::connectToServer(OpenMode openMode)
152{
153 Q_D(QLocalSocket);
154 if (state() == ConnectedState || state() == ConnectingState) {
155 d->error = OperationError;
156 d->errorString = tr("Trying to connect while connection is in progress");
157 emit errorOccurred(QLocalSocket::OperationError);
158 return;
159 }
160
161 d->error = QLocalSocket::UnknownSocketError;
162 d->errorString = QString();
163 d->state = ConnectingState;
164 emit stateChanged(d->state);
165 if (d->serverName.isEmpty()) {
166 d->error = ServerNotFoundError;
167 d->errorString = tr("%1: Invalid name").arg("QLocalSocket::connectToServer"_L1);
168 d->state = UnconnectedState;
169 emit errorOccurred(d->error);
170 emit stateChanged(d->state);
171 return;
172 }
173
174 const auto pipePath = "\\\\.\\pipe\\"_L1;
175 if (d->serverName.startsWith(pipePath))
176 d->fullServerName = d->serverName;
177 else
178 d->fullServerName = pipePath + d->serverName;
179 // Try to open a named pipe
180 HANDLE localSocket;
181 forever {
182 DWORD permissions = (openMode & QIODevice::ReadOnly) ? GENERIC_READ : 0;
183 permissions |= (openMode & QIODevice::WriteOnly) ? GENERIC_WRITE : 0;
184 localSocket = CreateFile(reinterpret_cast<const wchar_t *>(d->fullServerName.utf16()), // pipe name
185 permissions,
186 0, // no sharing
187 NULL, // default security attributes
188 OPEN_EXISTING, // opens existing pipe
189 FILE_FLAG_OVERLAPPED,
190 NULL); // no template file
191
192 if (localSocket != INVALID_HANDLE_VALUE)
193 break;
194 DWORD error = GetLastError();
195 // It is really an error only if it is not ERROR_PIPE_BUSY
196 if (ERROR_PIPE_BUSY != error) {
197 break;
198 }
199
200 // All pipe instances are busy, so wait until connected or up to 5 seconds.
201 if (!WaitNamedPipe((const wchar_t *)d->fullServerName.utf16(), 5000))
202 break;
203 }
204
205 if (localSocket == INVALID_HANDLE_VALUE) {
206 const DWORD winError = GetLastError();
207 d->_q_winError(winError, "QLocalSocket::connectToServer"_L1);
208 d->fullServerName = QString();
209 return;
210 }
211
212 // we have a valid handle
213 if (setSocketDescriptor(reinterpret_cast<qintptr>(localSocket), ConnectedState, openMode))
214 emit connected();
215}
216
218{
219 // QWindowsPipeReader's reading functions return error codes
220 // that don't match what we need.
221 switch (res) {
222 case 0: // EOF -> transform to error
223 return -1;
224 case -2: // EWOULDBLOCK -> no error, just no bytes
225 return 0;
226 default:
227 return res;
228 }
229}
230
231// This is reading from the buffer
232qint64 QLocalSocket::readData(char *data, qint64 maxSize)
233{
234 Q_D(QLocalSocket);
235
236 if (!maxSize)
237 return 0;
238
239 return transformPipeReaderResult(d->pipeReader->read(data, maxSize));
240}
241
242qint64 QLocalSocket::readLineData(char *data, qint64 maxSize)
243{
244 Q_D(QLocalSocket);
245
246 if (!maxSize)
247 return 0;
248
249 // QIODevice::readLine() reserves space for the trailing '\0' byte,
250 // so we must read 'maxSize + 1' bytes.
251 return transformPipeReaderResult(d->pipeReader->readLine(data, maxSize + 1));
252}
253
254qint64 QLocalSocket::skipData(qint64 maxSize)
255{
256 Q_D(QLocalSocket);
257
258 if (!maxSize)
259 return 0;
260
261 return transformPipeReaderResult(d->pipeReader->skip(maxSize));
262}
263
264qint64 QLocalSocket::writeData(const char *data, qint64 len)
265{
266 Q_D(QLocalSocket);
267 if (!isValid()) {
268 d->error = OperationError;
269 d->errorString = tr("Socket is not connected");
270 return -1;
271 }
272
273 if (len == 0)
274 return 0;
275
276 if (!d->pipeWriter) {
277 d->pipeWriter = new QWindowsPipeWriter(d->handle, this);
278 QObjectPrivate::connect(d->pipeWriter, &QWindowsPipeWriter::bytesWritten,
279 d, &QLocalSocketPrivate::_q_bytesWritten);
280 QObjectPrivate::connect(d->pipeWriter, &QWindowsPipeWriter::writeFailed,
281 d, &QLocalSocketPrivate::_q_writeFailed);
282 }
283
284 if (d->isWriteChunkCached(data, len))
285 d->pipeWriter->write(*(d->currentWriteChunk));
286 else
287 d->pipeWriter->write(data, len);
288
289 return len;
290}
291
292void QLocalSocket::abort()
293{
294 Q_D(QLocalSocket);
295 if (d->pipeWriter) {
296 delete d->pipeWriter;
297 d->pipeWriter = 0;
298 }
299 close();
300}
301
302void QLocalSocketPrivate::_q_canRead()
303{
304 Q_Q(QLocalSocket);
305 if (!emittedReadyRead) {
306 QScopedValueRollback<bool> guard(emittedReadyRead, true);
307 emit q->readyRead();
308 }
309}
310
311void QLocalSocketPrivate::_q_pipeClosed()
312{
313 Q_Q(QLocalSocket);
314 if (state == QLocalSocket::UnconnectedState)
315 return;
316
317 if (state != QLocalSocket::ClosingState) {
318 state = QLocalSocket::ClosingState;
319 emit q->stateChanged(state);
320 if (state != QLocalSocket::ClosingState)
321 return;
322 }
323
324 serverName.clear();
325 fullServerName.clear();
326 pipeReader->stop();
327 delete pipeWriter;
328 pipeWriter = nullptr;
329 if (handle != INVALID_HANDLE_VALUE) {
330 DisconnectNamedPipe(handle);
331 CloseHandle(handle);
332 handle = INVALID_HANDLE_VALUE;
333 }
334
335 state = QLocalSocket::UnconnectedState;
336 emit q->stateChanged(state);
337 emit q->readChannelFinished();
338 emit q->disconnected();
339}
340
341qint64 QLocalSocket::bytesAvailable() const
342{
343 Q_D(const QLocalSocket);
344 qint64 available = QIODevice::bytesAvailable();
345 available += d->pipeReader->bytesAvailable();
346 return available;
347}
348
349qint64 QLocalSocket::bytesToWrite() const
350{
351 Q_D(const QLocalSocket);
352 return d->pipeWriterBytesToWrite();
353}
354
355bool QLocalSocket::canReadLine() const
356{
357 Q_D(const QLocalSocket);
358 return QIODevice::canReadLine() || d->pipeReader->canReadLine();
359}
360
361void QLocalSocket::close()
362{
363 Q_D(QLocalSocket);
364
365 QIODevice::close();
366 d->pipeReader->stopAndClear();
367 d->serverName = QString();
368 d->fullServerName = QString();
369 disconnectFromServer();
370}
371
372bool QLocalSocket::flush()
373{
374 Q_D(QLocalSocket);
375
376 return d->pipeWriter && d->pipeWriter->checkForWrite();
377}
378
379void QLocalSocket::disconnectFromServer()
380{
381 Q_D(QLocalSocket);
382
383 if (bytesToWrite() == 0) {
384 d->_q_pipeClosed();
385 } else if (d->state != QLocalSocket::ClosingState) {
386 d->state = QLocalSocket::ClosingState;
387 emit stateChanged(d->state);
388 }
389}
390
391QLocalSocket::LocalSocketError QLocalSocket::error() const
392{
393 Q_D(const QLocalSocket);
394 return d->error;
395}
396
397bool QLocalSocket::setSocketDescriptor(qintptr socketDescriptor,
398 LocalSocketState socketState, OpenMode openMode)
399{
400 Q_D(QLocalSocket);
401 d->pipeReader->stop();
402 d->handle = reinterpret_cast<HANDLE>(socketDescriptor);
403 d->state = socketState;
404 d->pipeReader->setHandle(d->handle);
405 QIODevice::open(openMode);
406 emit stateChanged(d->state);
407 if (d->state == ConnectedState && openMode.testFlag(QIODevice::ReadOnly))
408 d->pipeReader->startAsyncRead();
409 return true;
410}
411
412qint64 QLocalSocketPrivate::pipeWriterBytesToWrite() const
413{
414 return pipeWriter ? pipeWriter->bytesToWrite() : qint64(0);
415}
416
417void QLocalSocketPrivate::_q_bytesWritten(qint64 bytes)
418{
419 Q_Q(QLocalSocket);
420 if (!emittedBytesWritten) {
421 QScopedValueRollback<bool> guard(emittedBytesWritten, true);
422 emit q->bytesWritten(bytes);
423 }
424 if (state == QLocalSocket::ClosingState)
425 q->disconnectFromServer();
426}
427
428void QLocalSocketPrivate::_q_writeFailed()
429{
430 Q_Q(QLocalSocket);
431 error = QLocalSocket::PeerClosedError;
432 errorString = QLocalSocket::tr("Remote closed");
433 emit q->errorOccurred(error);
434
435 _q_pipeClosed();
436}
437
438qintptr QLocalSocket::socketDescriptor() const
439{
440 Q_D(const QLocalSocket);
441 return reinterpret_cast<qintptr>(d->handle);
442}
443
444qint64 QLocalSocket::readBufferSize() const
445{
446 Q_D(const QLocalSocket);
447 return d->pipeReader->maxReadBufferSize();
448}
449
450void QLocalSocket::setReadBufferSize(qint64 size)
451{
452 Q_D(QLocalSocket);
453 d->pipeReader->setMaxReadBufferSize(size);
454}
455
456bool QLocalSocket::waitForConnected(int msecs)
457{
458 Q_UNUSED(msecs);
459 return (state() == ConnectedState);
460}
461
462bool QLocalSocket::waitForDisconnected(int msecs)
463{
464 Q_D(QLocalSocket);
465 if (state() == UnconnectedState) {
466 qWarning("QLocalSocket::waitForDisconnected() is not allowed in UnconnectedState");
467 return false;
468 }
469
470 QDeadlineTimer deadline(msecs);
471 bool wasChecked = false;
472 while (!d->pipeReader->isPipeClosed()) {
473 if (wasChecked && deadline.hasExpired())
474 return false;
475
476 QSocketPoller poller(*d);
477 // The first parameter of the WaitForMultipleObjectsEx() call cannot
478 // be zero. So we have to call SleepEx() here.
479 if (!poller.writePending && poller.waitForClose) {
480 // Prevent waiting on the first pass, if both the pipe reader
481 // and the pipe writer are inactive.
482 if (wasChecked)
483 SleepEx(poller.getRemainingTime(deadline), TRUE);
484 } else if (!poller.poll(deadline)) {
485 return false;
486 }
487
488 if (d->pipeWriter)
489 d->pipeWriter->checkForWrite();
490
491 // When the read buffer is full, the read sequence is not running,
492 // so we need to peek the pipe to detect disconnection.
493 if (poller.waitForClose && isValid())
494 d->pipeReader->checkPipeState();
495
496 d->pipeReader->checkForReadyRead();
497 wasChecked = true;
498 }
499 d->_q_pipeClosed();
500 return true;
501}
502
503bool QLocalSocket::isValid() const
504{
505 Q_D(const QLocalSocket);
506 return d->handle != INVALID_HANDLE_VALUE;
507}
508
509bool QLocalSocket::waitForReadyRead(int msecs)
510{
511 Q_D(QLocalSocket);
512
513 if (d->state != QLocalSocket::ConnectedState)
514 return false;
515
516 QDeadlineTimer deadline(msecs);
517 while (!d->pipeReader->isPipeClosed()) {
518 QSocketPoller poller(*d);
519 if (poller.waitForClose || !poller.poll(deadline))
520 return false;
521
522 if (d->pipeWriter)
523 d->pipeWriter->checkForWrite();
524
525 if (d->pipeReader->checkForReadyRead())
526 return true;
527 }
528 d->_q_pipeClosed();
529 return false;
530}
531
532bool QLocalSocket::waitForBytesWritten(int msecs)
533{
534 Q_D(QLocalSocket);
535
536 if (d->state == QLocalSocket::UnconnectedState)
537 return false;
538
539 QDeadlineTimer deadline(msecs);
540 bool wasChecked = false;
541 while (!d->pipeReader->isPipeClosed()) {
542 if (wasChecked && deadline.hasExpired())
543 return false;
544
545 QSocketPoller poller(*d);
546 if (!poller.writePending || !poller.poll(deadline))
547 return false;
548
549 Q_ASSERT(d->pipeWriter);
550 if (d->pipeWriter->checkForWrite())
551 return true;
552
553 if (poller.waitForClose && isValid())
554 d->pipeReader->checkPipeState();
555
556 d->pipeReader->checkForReadyRead();
557 wasChecked = true;
558 }
559 d->_q_pipeClosed();
560 return false;
561}
562
563QT_END_NAMESPACE
\inmodule QtCore
static qint64 transformPipeReaderResult(qint64 res)