Qt
Internal/Contributor docs for the Qt SDK. <b>Note:</b> These are NOT official API docs; those are found <a href='https://doc.qt.io/'>here</a>.
Loading...
Searching...
No Matches
qlocalserver_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
4#include "qlocalserver.h"
5#include "qlocalserver_p.h"
6#include "qlocalsocket.h"
7#include <QtCore/private/qsystemerror_p.h>
8
9#include <qdebug.h>
10
11#include <aclapi.h>
12#include <accctrl.h>
13#include <sddl.h>
14
15// The buffer size need to be 0 otherwise data could be
16// lost if the socket that has written data closes the connection
17// before it is read. Pipewriter is used for write buffering.
18#define BUFSIZE 0
19
21
22using namespace Qt::StringLiterals;
23
24bool QLocalServerPrivate::addListener()
25{
26 // The object must not change its address once the
27 // contained OVERLAPPED struct is passed to Windows.
28 listeners.push_back(std::make_unique<Listener>());
29 auto &listener = listeners.back();
30
31 SECURITY_ATTRIBUTES sa;
32 sa.nLength = sizeof(SECURITY_ATTRIBUTES);
33 sa.bInheritHandle = FALSE; //non inheritable handle, same as default
34 sa.lpSecurityDescriptor = 0; //default security descriptor
35
36 QScopedPointer<SECURITY_DESCRIPTOR> pSD;
37 PSID worldSID = 0;
38 QByteArray aclBuffer;
39 QByteArray tokenUserBuffer;
40 QByteArray tokenGroupBuffer;
41
42 // create security descriptor if access options were specified
43 if ((socketOptions.value() & QLocalServer::WorldAccessOption)) {
44 pSD.reset(new SECURITY_DESCRIPTOR);
45 if (!InitializeSecurityDescriptor(pSD.data(), SECURITY_DESCRIPTOR_REVISION)) {
46 setError("QLocalServerPrivate::addListener"_L1);
47 return false;
48 }
49 HANDLE hToken = NULL;
50 if (!OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &hToken))
51 return false;
52 DWORD dwBufferSize = 0;
53 GetTokenInformation(hToken, TokenUser, 0, 0, &dwBufferSize);
54 tokenUserBuffer.fill(0, dwBufferSize);
55 auto pTokenUser = reinterpret_cast<PTOKEN_USER>(tokenUserBuffer.data());
56 if (!GetTokenInformation(hToken, TokenUser, pTokenUser, dwBufferSize, &dwBufferSize)) {
57 setError("QLocalServerPrivate::addListener"_L1);
58 CloseHandle(hToken);
59 return false;
60 }
61
62 dwBufferSize = 0;
63 GetTokenInformation(hToken, TokenPrimaryGroup, 0, 0, &dwBufferSize);
64 tokenGroupBuffer.fill(0, dwBufferSize);
65 auto pTokenGroup = reinterpret_cast<PTOKEN_PRIMARY_GROUP>(tokenGroupBuffer.data());
66 if (!GetTokenInformation(hToken, TokenPrimaryGroup, pTokenGroup, dwBufferSize, &dwBufferSize)) {
67 setError("QLocalServerPrivate::addListener"_L1);
68 CloseHandle(hToken);
69 return false;
70 }
71 CloseHandle(hToken);
72
73#ifdef QLOCALSERVER_DEBUG
74 DWORD groupNameSize;
75 DWORD domainNameSize;
76 SID_NAME_USE groupNameUse;
77 LPWSTR groupNameSid;
78 LookupAccountSid(0, pTokenGroup->PrimaryGroup, 0, &groupNameSize, 0, &domainNameSize, &groupNameUse);
79 QScopedPointer<wchar_t, QScopedPointerArrayDeleter<wchar_t>> groupName(new wchar_t[groupNameSize]);
80 QScopedPointer<wchar_t, QScopedPointerArrayDeleter<wchar_t>> domainName(new wchar_t[domainNameSize]);
81 if (LookupAccountSid(0, pTokenGroup->PrimaryGroup, groupName.data(), &groupNameSize, domainName.data(), &domainNameSize, &groupNameUse)) {
82 qDebug() << "primary group" << QString::fromWCharArray(domainName.data()) << "\\" << QString::fromWCharArray(groupName.data()) << "type=" << groupNameUse;
83 }
84 if (ConvertSidToStringSid(pTokenGroup->PrimaryGroup, &groupNameSid)) {
85 qDebug() << "primary group SID" << QString::fromWCharArray(groupNameSid) << "valid" << IsValidSid(pTokenGroup->PrimaryGroup);
86 LocalFree(groupNameSid);
87 }
88#endif
89
90 SID_IDENTIFIER_AUTHORITY WorldAuth = { SECURITY_WORLD_SID_AUTHORITY };
91 if (!AllocateAndInitializeSid(&WorldAuth, 1, SECURITY_WORLD_RID,
92 0, 0, 0, 0, 0, 0, 0,
93 &worldSID)) {
94 setError("QLocalServerPrivate::addListener"_L1);
95 return false;
96 }
97
98 //calculate size of ACL buffer
99 DWORD aclSize = sizeof(ACL) + ((sizeof(ACCESS_ALLOWED_ACE)) * 3);
100 aclSize += GetLengthSid(pTokenUser->User.Sid) - sizeof(DWORD);
101 aclSize += GetLengthSid(pTokenGroup->PrimaryGroup) - sizeof(DWORD);
102 aclSize += GetLengthSid(worldSID) - sizeof(DWORD);
103 aclSize = (aclSize + (sizeof(DWORD) - 1)) & 0xfffffffc;
104
105 aclBuffer.fill(0, aclSize);
106 auto acl = reinterpret_cast<PACL>(aclBuffer.data());
107 InitializeAcl(acl, aclSize, ACL_REVISION_DS);
108
109 if (socketOptions.value() & QLocalServer::UserAccessOption) {
110 if (!AddAccessAllowedAce(acl, ACL_REVISION, FILE_ALL_ACCESS, pTokenUser->User.Sid)) {
111 setError("QLocalServerPrivate::addListener"_L1);
112 FreeSid(worldSID);
113 return false;
114 }
115 }
116 if (socketOptions.value() & QLocalServer::GroupAccessOption) {
117 if (!AddAccessAllowedAce(acl, ACL_REVISION, FILE_ALL_ACCESS, pTokenGroup->PrimaryGroup)) {
118 setError("QLocalServerPrivate::addListener"_L1);
119 FreeSid(worldSID);
120 return false;
121 }
122 }
123 if (socketOptions.value() & QLocalServer::OtherAccessOption) {
124 if (!AddAccessAllowedAce(acl, ACL_REVISION, FILE_ALL_ACCESS, worldSID)) {
125 setError("QLocalServerPrivate::addListener"_L1);
126 FreeSid(worldSID);
127 return false;
128 }
129 }
130 SetSecurityDescriptorOwner(pSD.data(), pTokenUser->User.Sid, FALSE);
131 SetSecurityDescriptorGroup(pSD.data(), pTokenGroup->PrimaryGroup, FALSE);
132 if (!SetSecurityDescriptorDacl(pSD.data(), TRUE, acl, FALSE)) {
133 setError("QLocalServerPrivate::addListener"_L1);
134 FreeSid(worldSID);
135 return false;
136 }
137
138 sa.lpSecurityDescriptor = pSD.data();
139 }
140
141 listener->handle = CreateNamedPipe(
142 reinterpret_cast<const wchar_t *>(fullServerName.utf16()), // pipe name
143 PIPE_ACCESS_DUPLEX | FILE_FLAG_OVERLAPPED, // read/write access
144 PIPE_TYPE_BYTE | // byte type pipe
145 PIPE_READMODE_BYTE | // byte-read mode
146 PIPE_WAIT, // blocking mode
147 PIPE_UNLIMITED_INSTANCES, // max. instances
148 BUFSIZE, // output buffer size
149 BUFSIZE, // input buffer size
150 3000, // client time-out
151 &sa);
152
153 if (listener->handle == INVALID_HANDLE_VALUE) {
154 setError("QLocalServerPrivate::addListener"_L1);
155 listeners.pop_back();
156 return false;
157 }
158
159 if (worldSID)
160 FreeSid(worldSID);
161
162 memset(&listener->overlapped, 0, sizeof(OVERLAPPED));
163 listener->overlapped.hEvent = eventHandle;
164
165 // Beware! ConnectNamedPipe will reset the eventHandle to non-signaled.
166 // Callers of addListener must check all listeners for connections.
167 if (!ConnectNamedPipe(listener->handle, &listener->overlapped)) {
168 switch (GetLastError()) {
169 case ERROR_IO_PENDING:
170 listener->connected = false;
171 break;
172 case ERROR_PIPE_CONNECTED:
173 listener->connected = true;
174 break;
175 default:
176 CloseHandle(listener->handle);
177 setError("QLocalServerPrivate::addListener"_L1);
178 listeners.pop_back();
179 return false;
180 }
181 } else {
182 Q_ASSERT_X(false, "QLocalServerPrivate::addListener", "The impossible happened");
183 SetEvent(eventHandle);
184 }
185 return true;
186}
187
188void QLocalServerPrivate::setError(const QString &function)
189{
190 int windowsError = GetLastError();
191 errorString = QString::fromLatin1("%1: %2").arg(function, qt_error_string(windowsError));
193}
194
196{
197}
198
200{
201 Q_UNUSED(name);
202 return true;
203}
204
206{
207 Q_Q(QLocalServer);
208
209 const auto pipePath = "\\\\.\\pipe\\"_L1;
210 if (name.startsWith(pipePath))
212 else
213 fullServerName = pipePath + name;
214
215 // Use only one event for all listeners of one socket.
216 // The idea is that listener events are rare, so polling all listeners once in a while is
217 // cheap compared to waiting for N additional events in each iteration of the main loop.
218 eventHandle = CreateEvent(NULL, TRUE, FALSE, NULL);
219 connectionEventNotifier = new QWinEventNotifier(eventHandle , q);
220 q->connect(connectionEventNotifier, SIGNAL(activated(HANDLE)), q, SLOT(_q_onNewConnection()));
221
222 for (int i = 0; i < listenBacklog; ++i)
223 if (!addListener())
224 return false;
225
227 return true;
228}
229
231{
232 qWarning("QLocalServer::listen(qintptr) is not supported on Windows QTBUG-24230");
233 return false;
234}
235
237{
238 Q_Q(QLocalServer);
239 DWORD dummy;
240 bool tryAgain;
241 do {
242 tryAgain = false;
243
244 // Reset first, otherwise we could reset an event which was asserted
245 // immediately after we checked the conn status.
246 ResetEvent(eventHandle);
247
248 // Testing shows that there is indeed absolutely no guarantee which listener gets
249 // a client connection first, so there is no way around polling all of them.
250 for (size_t i = 0; i < listeners.size(); ) {
251 HANDLE handle = listeners[i]->handle;
252 if (listeners[i]->connected
253 || GetOverlappedResult(handle, &listeners[i]->overlapped, &dummy, FALSE))
254 {
255 listeners.erase(listeners.begin() + i);
256
257 addListener();
258
260 connectionEventNotifier->setEnabled(false);
261 else
262 tryAgain = true;
263
264 // Make this the last thing so connected slots can wreak the least havoc
265 q->incomingConnection(reinterpret_cast<quintptr>(handle));
266 } else {
267 if (GetLastError() != ERROR_IO_INCOMPLETE) {
268 q->close();
269 setError("QLocalServerPrivate::_q_onNewConnection"_L1);
270 return;
271 }
272
273 ++i;
274 }
275 }
276 } while (tryAgain);
277}
278
280{
281 connectionEventNotifier->setEnabled(false); // Otherwise, closed handle is checked before deleter runs
282 connectionEventNotifier->deleteLater();
283 connectionEventNotifier = 0;
284 CloseHandle(eventHandle);
285 for (size_t i = 0; i < listeners.size(); ++i)
286 CloseHandle(listeners[i]->handle);
287 listeners.clear();
288}
289
290void QLocalServerPrivate::waitForNewConnection(int msecs, bool *timedOut)
291{
292 Q_Q(QLocalServer);
293 if (!pendingConnections.isEmpty() || !q->isListening())
294 return;
295
296 DWORD result = WaitForSingleObject(eventHandle, (msecs == -1) ? INFINITE : msecs);
297 if (result == WAIT_TIMEOUT) {
298 if (timedOut)
299 *timedOut = true;
300 } else {
302 }
303}
304
QList< QtAndroidInput::GenericMotionEventListener * > listeners
bool connected
\inmodule QtCore
Definition qbytearray.h:57
qsizetype size() const noexcept
Definition qlist.h:397
bool isEmpty() const noexcept
Definition qlist.h:401
void waitForNewConnection(int msec, bool *timedOut)
static bool removeServer(const QString &name)
void setError(const QString &function)
QQueue< QLocalSocket * > pendingConnections
QAbstractSocket::SocketError error
bool listen(const QString &name)
The QLocalServer class provides a local socket based server.
\macro QT_RESTRICTED_CAST_FROM_ASCII
Definition qstring.h:129
static QString fromLatin1(QByteArrayView ba)
This is an overloaded member function, provided for convenience. It differs from the above function o...
Definition qstring.cpp:5871
const ushort * utf16() const
Returns the QString as a '\0\'-terminated array of unsigned shorts.
Definition qstring.cpp:6995
static QString fromWCharArray(const wchar_t *string, qsizetype size=-1)
Definition qstring.h:1309
\inmodule QtCore
Combined button and popup list for selecting options.
void * HANDLE
#define BUFSIZE
Q_DECL_COLD_FUNCTION Q_CORE_EXPORT QString qt_error_string(int errorCode=-1)
#define qDebug
[1]
Definition qlogging.h:164
#define qWarning
Definition qlogging.h:166
#define SLOT(a)
Definition qobjectdefs.h:52
#define SIGNAL(a)
Definition qobjectdefs.h:53
GLuint64 GLenum void * handle
GLuint name
GLdouble GLdouble GLdouble GLdouble q
Definition qopenglext.h:259
GLuint64EXT * result
[6]
#define Q_ASSERT_X(cond, x, msg)
Definition qrandom.cpp:48
#define Q_UNUSED(x)
size_t quintptr
Definition qtypes.h:167
ptrdiff_t qintptr
Definition qtypes.h:166