You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1619 lines
53 KiB
1619 lines
53 KiB
diff --git a/data/sddm.conf.in b/data/sddm.conf.in
|
|
index 9522ffd..204dc52 100644
|
|
--- a/data/sddm.conf.in
|
|
+++ b/data/sddm.conf.in
|
|
@@ -89,3 +89,6 @@ MinimumVT=7
|
|
# Valid values: on|off|none
|
|
# If property is set to none, numlock won't be changed
|
|
Numlock=none
|
|
+
|
|
+# Enables the XDMCP Server
|
|
+XDMCPServer=false
|
|
\ No newline at end of file
|
|
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
|
|
index a82ae45..1d6d117 100644
|
|
--- a/src/CMakeLists.txt
|
|
+++ b/src/CMakeLists.txt
|
|
@@ -20,6 +20,9 @@ set(DAEMON_SOURCES
|
|
daemon/Session.cpp
|
|
daemon/SignalHandler.cpp
|
|
daemon/SocketServer.cpp
|
|
+ daemon/xdmcp/Packet.cpp
|
|
+ daemon/xdmcp/Server.cpp
|
|
+ daemon/xdmcp/Utils.cpp
|
|
)
|
|
|
|
if(USE_QT5)
|
|
diff --git a/src/common/Configuration.cpp b/src/common/Configuration.cpp
|
|
index 2095b05..4b780b3 100644
|
|
--- a/src/common/Configuration.cpp
|
|
+++ b/src/common/Configuration.cpp
|
|
@@ -63,6 +63,7 @@ namespace SDDM {
|
|
bool autoRelogin { false };
|
|
|
|
Configuration::NumState numlock { Configuration::NUM_NONE };
|
|
+ bool xdmcpServerEnabled { false };
|
|
};
|
|
|
|
Configuration::Configuration(const QString &configPath, QObject *parent) : QObject(parent), d(new ConfigurationPrivate()) {
|
|
@@ -122,6 +123,7 @@ namespace SDDM {
|
|
} else {
|
|
d->numlock = Configuration::NUM_NONE;
|
|
}
|
|
+ d->xdmcpServerEnabled = settings.value("XDMCPServer", d->xdmcpServerEnabled).toBool();
|
|
}
|
|
|
|
void Configuration::save() {
|
|
@@ -158,6 +160,8 @@ namespace SDDM {
|
|
settings.setValue("Numlock", "on");
|
|
else if (d->numlock == NUM_SET_OFF)
|
|
settings.setValue("Numlock", "off");
|
|
+
|
|
+ settings.setValue("XDMCPServer", d->xdmcpServerEnabled);
|
|
}
|
|
|
|
Configuration *Configuration::instance() {
|
|
@@ -261,4 +265,9 @@ namespace SDDM {
|
|
const Configuration::NumState Configuration::numlock() const {
|
|
return d->numlock;
|
|
}
|
|
+
|
|
+ bool Configuration::xdmcpServerEnabled() const {
|
|
+ return d->xdmcpServerEnabled;
|
|
+ }
|
|
+
|
|
}
|
|
diff --git a/src/common/Configuration.h b/src/common/Configuration.h
|
|
index cbef261..4b610d8 100644
|
|
--- a/src/common/Configuration.h
|
|
+++ b/src/common/Configuration.h
|
|
@@ -79,6 +79,8 @@ namespace SDDM {
|
|
enum NumState { NUM_NONE, NUM_SET_ON, NUM_SET_OFF };
|
|
const NumState numlock() const;
|
|
|
|
+ bool xdmcpServerEnabled() const;
|
|
+
|
|
bool first { true };
|
|
|
|
bool testing { false };
|
|
diff --git a/src/daemon/DaemonApp.cpp b/src/daemon/DaemonApp.cpp
|
|
index 9ad226b..9feb734 100644
|
|
--- a/src/daemon/DaemonApp.cpp
|
|
+++ b/src/daemon/DaemonApp.cpp
|
|
@@ -25,6 +25,7 @@
|
|
#include "PowerManager.h"
|
|
#include "SeatManager.h"
|
|
#include "SignalHandler.h"
|
|
+#include "xdmcp/Server.h"
|
|
|
|
#ifdef USE_QT5
|
|
#include "MessageHandler.h"
|
|
@@ -65,6 +66,12 @@ namespace SDDM {
|
|
// create seat manager
|
|
m_seatManager = new SeatManager(this);
|
|
|
|
+ // start the XDMCP server
|
|
+ if (configuration()->xdmcpServerEnabled()) {
|
|
+ m_xdmcpServer = XDMCP::Server::instance(this);
|
|
+ m_xdmcpServer->start();
|
|
+ }
|
|
+
|
|
// connect with display manager
|
|
connect(m_seatManager, SIGNAL(seatCreated(QString)), m_displayManager, SLOT(AddSeat(QString)));
|
|
connect(m_seatManager, SIGNAL(seatRemoved(QString)), m_displayManager, SLOT(RemoveSeat(QString)));
|
|
diff --git a/src/daemon/DaemonApp.h b/src/daemon/DaemonApp.h
|
|
index 81f955c..2088010 100644
|
|
--- a/src/daemon/DaemonApp.h
|
|
+++ b/src/daemon/DaemonApp.h
|
|
@@ -29,6 +29,9 @@ namespace SDDM {
|
|
class DisplayManager;
|
|
class PowerManager;
|
|
class SeatManager;
|
|
+ namespace XDMCP {
|
|
+ class Server;
|
|
+ }
|
|
|
|
class DaemonApp : public QCoreApplication {
|
|
Q_OBJECT
|
|
@@ -57,6 +60,7 @@ namespace SDDM {
|
|
DisplayManager *m_displayManager { nullptr };
|
|
PowerManager *m_powerManager { nullptr };
|
|
SeatManager *m_seatManager { nullptr };
|
|
+ XDMCP::Server *m_xdmcpServer { nullptr };
|
|
};
|
|
}
|
|
|
|
diff --git a/src/daemon/Display.cpp b/src/daemon/Display.cpp
|
|
index f1a54b4..48137e6 100644
|
|
--- a/src/daemon/Display.cpp
|
|
+++ b/src/daemon/Display.cpp
|
|
@@ -53,6 +53,17 @@ namespace SDDM {
|
|
return name;
|
|
}
|
|
|
|
+ Display::Display(const QString& hostname, const int displayId, QObject* parent) : QObject(parent),
|
|
+ m_displayId(displayId),
|
|
+ m_authenticator(new Authenticator(this)),
|
|
+ m_displayServer(nullptr),
|
|
+ m_socketServer(new SocketServer(this)),
|
|
+ m_greeter(new Greeter(this)) {
|
|
+ m_display = QString("%1:%2").arg(hostname).arg(displayId);
|
|
+
|
|
+ init();
|
|
+ }
|
|
+
|
|
Display::Display(const int displayId, const int terminalId, Seat *parent) : QObject(parent),
|
|
m_displayId(displayId), m_terminalId(terminalId),
|
|
m_authenticator(new Authenticator(this)),
|
|
@@ -63,12 +74,17 @@ namespace SDDM {
|
|
|
|
m_display = QString(":%1").arg(m_displayId);
|
|
|
|
- // restart display after user session ended
|
|
- connect(m_authenticator, SIGNAL(stopped()), this, SLOT(stop()));
|
|
-
|
|
// restart display after display server ended
|
|
connect(m_displayServer, SIGNAL(stopped()), this, SLOT(stop()));
|
|
|
|
+ init();
|
|
+ }
|
|
+
|
|
+ void Display::init()
|
|
+ {
|
|
+ // restart display after user session ended
|
|
+ connect(m_authenticator, SIGNAL(stopped()), this, SLOT(stop()));
|
|
+
|
|
// connect login signal
|
|
connect(m_socketServer, SIGNAL(login(QLocalSocket*,QString,QString,QString)), this, SLOT(login(QLocalSocket*,QString,QString,QString)));
|
|
|
|
@@ -91,6 +107,22 @@ namespace SDDM {
|
|
|
|
// set socket name
|
|
m_socket = QString("sddm-%1-%2").arg(m_display).arg(generateName(6));
|
|
+
|
|
+ // generate cookie
|
|
+ std::random_device rd;
|
|
+ std::mt19937 gen(rd());
|
|
+ std::uniform_int_distribution<> dis(0, 15);
|
|
+
|
|
+ // resever 32 bytes
|
|
+ m_cookie.reserve(32);
|
|
+
|
|
+ // create a random hexadecimal number
|
|
+ const char *digits = "0123456789abcdef";
|
|
+ for (int i = 0; i < 32; ++i)
|
|
+ m_cookie[i] = digits[dis(gen)];
|
|
+
|
|
+ // generate auth file
|
|
+ addCookie(m_authPath);
|
|
}
|
|
|
|
Display::~Display() {
|
|
@@ -113,6 +145,16 @@ namespace SDDM {
|
|
return m_cookie;
|
|
}
|
|
|
|
+ const QByteArray Display::rawCookie() const {
|
|
+ QByteArray cookie;
|
|
+ for (int i = 0; i < m_cookie.length() / 2; i++) {
|
|
+ // horrible, just horrible
|
|
+ quint8 byte = QString("%1%2").arg(m_cookie[i*2]).arg(m_cookie[i*2+1]).toUInt(nullptr, 16);
|
|
+ cookie.append(byte);
|
|
+ }
|
|
+ return cookie;
|
|
+ }
|
|
+
|
|
Seat *Display::seat() const {
|
|
return m_seat;
|
|
}
|
|
@@ -144,28 +186,14 @@ namespace SDDM {
|
|
if (m_started)
|
|
return;
|
|
|
|
- // generate cookie
|
|
- std::random_device rd;
|
|
- std::mt19937 gen(rd());
|
|
- std::uniform_int_distribution<> dis(0, 15);
|
|
+ if (m_displayServer != nullptr) {
|
|
+ // set display server params
|
|
+ m_displayServer->setDisplay(m_display);
|
|
+ m_displayServer->setAuthPath(m_authPath);
|
|
|
|
- // resever 32 bytes
|
|
- m_cookie.reserve(32);
|
|
-
|
|
- // create a random hexadecimal number
|
|
- const char *digits = "0123456789abcdef";
|
|
- for (int i = 0; i < 32; ++i)
|
|
- m_cookie[i] = digits[dis(gen)];
|
|
-
|
|
- // generate auth file
|
|
- addCookie(m_authPath);
|
|
-
|
|
- // set display server params
|
|
- m_displayServer->setDisplay(m_display);
|
|
- m_displayServer->setAuthPath(m_authPath);
|
|
-
|
|
- // start display server
|
|
- m_displayServer->start();
|
|
+ // start display server
|
|
+ m_displayServer->start();
|
|
+ }
|
|
|
|
if ((daemonApp->configuration()->first || daemonApp->configuration()->autoRelogin()) &&
|
|
!daemonApp->configuration()->autoUser().isEmpty() && !daemonApp->configuration()->lastSession().isEmpty()) {
|
|
@@ -221,9 +249,11 @@ namespace SDDM {
|
|
m_socketServer->stop();
|
|
|
|
// stop display server
|
|
- m_displayServer->blockSignals(true);
|
|
- m_displayServer->stop();
|
|
- m_displayServer->blockSignals(false);
|
|
+ if (m_displayServer != nullptr) {
|
|
+ m_displayServer->blockSignals(true);
|
|
+ m_displayServer->stop();
|
|
+ m_displayServer->blockSignals(false);
|
|
+ }
|
|
|
|
// remove authority file
|
|
QFile::remove(m_authPath);
|
|
diff --git a/src/daemon/Display.h b/src/daemon/Display.h
|
|
index 46d320b..9556209 100644
|
|
--- a/src/daemon/Display.h
|
|
+++ b/src/daemon/Display.h
|
|
@@ -35,6 +35,7 @@ namespace SDDM {
|
|
Q_OBJECT
|
|
Q_DISABLE_COPY(Display)
|
|
public:
|
|
+ explicit Display(const QString& hostname, const int displayId, QObject *parent = 0);
|
|
explicit Display(const int displayId, const int terminalId, Seat *parent);
|
|
~Display();
|
|
|
|
@@ -44,6 +45,7 @@ namespace SDDM {
|
|
const QString &name() const;
|
|
|
|
const QString &cookie() const;
|
|
+ const QByteArray rawCookie() const;
|
|
void addCookie(const QString &file);
|
|
|
|
Seat *seat() const;
|
|
@@ -61,6 +63,8 @@ namespace SDDM {
|
|
void loginSucceeded(QLocalSocket *socket);
|
|
|
|
private:
|
|
+ void init();
|
|
+
|
|
bool m_relogin { true };
|
|
bool m_started { false };
|
|
|
|
diff --git a/src/daemon/xdmcp/Packet.cpp b/src/daemon/xdmcp/Packet.cpp
|
|
new file mode 100644
|
|
index 0000000..3a0c3d9
|
|
--- /dev/null
|
|
+++ b/src/daemon/xdmcp/Packet.cpp
|
|
@@ -0,0 +1,397 @@
|
|
+/*
|
|
+ * Packet type handling for X Display Control Protocol
|
|
+ * Copyright (C) 2013 Martin Bříza <mbriza@redhat.com>
|
|
+ *
|
|
+ * This program is free software; you can redistribute it and/or modify
|
|
+ * it under the terms of the GNU General Public License as published by
|
|
+ * the Free Software Foundation; either version 2 of the License, or
|
|
+ * (at your option) any later version.
|
|
+ *
|
|
+ * This program is distributed in the hope that it will be useful,
|
|
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
+ * GNU General Public License for more details.
|
|
+ *
|
|
+ * You should have received a copy of the GNU General Public License along
|
|
+ * with this program; if not, write to the Free Software Foundation, Inc.,
|
|
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
|
+ *
|
|
+ */
|
|
+
|
|
+#include "Packet.h"
|
|
+#include "Server.h"
|
|
+#include "../Display.h"
|
|
+
|
|
+namespace SDDM {
|
|
+namespace XDMCP {
|
|
+
|
|
+/*******************************************************************************
|
|
+* PLUMBING
|
|
+ ******************************************************************************/
|
|
+
|
|
+ Packet::Packet(const QHostAddress &host, quint16 port) : m_host(host),
|
|
+ m_port(port),
|
|
+ m_valid(true) {
|
|
+
|
|
+ }
|
|
+
|
|
+ Packet::Packet(const QHostAddress &host, quint16 port, Reader &r) : m_host(host),
|
|
+ m_port(port),
|
|
+ m_valid(false) {
|
|
+
|
|
+ }
|
|
+
|
|
+ Packet::~Packet() {
|
|
+
|
|
+ }
|
|
+
|
|
+ bool Packet::isValid() const {
|
|
+ return m_valid;
|
|
+ }
|
|
+
|
|
+ // static
|
|
+ Packet *Packet::decode(const QByteArray &data, const QHostAddress &host, quint16 port) {
|
|
+ Reader reader(data);
|
|
+ uint16_t version, opcode, length;
|
|
+
|
|
+ reader >> version >> opcode >> length;
|
|
+
|
|
+ if (version != 1)
|
|
+ return nullptr;
|
|
+ if (length != data.size() - 6)
|
|
+ return nullptr;
|
|
+
|
|
+ switch (opcode) {
|
|
+ case _Query:
|
|
+ return new Query(host, port, reader);
|
|
+ case _BroadcastQuery:
|
|
+ return new BroadcastQuery(host, port, reader);
|
|
+ case _IndirectQuery:
|
|
+ return new IndirectQuery(host, port, reader);
|
|
+ case _ForwardQuery:
|
|
+ return new ForwardQuery(host, port, reader);
|
|
+ case _Willing:
|
|
+ return new Willing(host, port, reader);
|
|
+ case _Unwilling:
|
|
+ return new Unwilling(host, port, reader);
|
|
+ case _Request:
|
|
+ return new Request(host, port, reader);
|
|
+ case _Accept:
|
|
+ return new Accept(host, port, reader);
|
|
+ case _Decline:
|
|
+ return new Decline(host, port, reader);
|
|
+ case _Manage:
|
|
+ return new Manage(host, port, reader);
|
|
+ case _Refuse:
|
|
+ return new Refuse(host, port, reader);
|
|
+ case _Failed:
|
|
+ return new Failed(host, port, reader);
|
|
+ case _KeepAlive:
|
|
+ return new KeepAlive(host, port, reader);
|
|
+ case _Alive:
|
|
+ return new Alive(host, port, reader);
|
|
+ default:
|
|
+ qDebug() << " XDMCP: Got packet of an unknown type" << opcode;
|
|
+ return nullptr;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ void Packet::setHost(const QHostAddress &host) {
|
|
+ m_host = QHostAddress(host);
|
|
+ }
|
|
+
|
|
+ const QHostAddress& Packet::host() const {
|
|
+ return m_host;
|
|
+ }
|
|
+
|
|
+ void Packet::setPort(quint16 port) {
|
|
+ m_port = port;
|
|
+ }
|
|
+
|
|
+ quint16 Packet::port() const {
|
|
+ return m_port;
|
|
+ }
|
|
+
|
|
+ QByteArray Packet::encode() const {
|
|
+ return QByteArray();
|
|
+ }
|
|
+
|
|
+ Packet *Packet::onClientReceived() const {
|
|
+ qDebug() << " XDMCP: Client received a wrong packet type (no action assigned)";
|
|
+ return nullptr;
|
|
+ }
|
|
+
|
|
+ Packet *Packet::onServerReceived() const {
|
|
+ qDebug() << " XDMCP: Server received a wrong packet type (no action assigned)";
|
|
+ return nullptr;
|
|
+ }
|
|
+
|
|
+ Packet::Query::Query(const QHostAddress &host, quint16 port, Reader &r) : Packet(host, port, r) {
|
|
+ r >> m_authenticationNames;
|
|
+ if (r.isFinished())
|
|
+ m_valid = true;
|
|
+ }
|
|
+
|
|
+ QByteArray Packet::Query::encode() const {
|
|
+ Writer w;
|
|
+ w << m_authenticationNames;
|
|
+ return w.finalize(Packet::_Query);
|
|
+ }
|
|
+
|
|
+ Packet::BroadcastQuery::BroadcastQuery(const QHostAddress &host, quint16 port, Reader &r) : Packet(host, port, r) {
|
|
+ r >> m_authenticationNames;
|
|
+ if (r.isFinished())
|
|
+ m_valid = true;
|
|
+ }
|
|
+
|
|
+ QByteArray Packet::BroadcastQuery::encode() const {
|
|
+ Writer w;
|
|
+ w << m_authenticationNames;
|
|
+ return w.finalize(Packet::_BroadcastQuery);
|
|
+ }
|
|
+
|
|
+ Packet::IndirectQuery::IndirectQuery(const QHostAddress &host, quint16 port, Reader &r) : Packet(host, port, r) {
|
|
+ r >> m_authenticationNames;
|
|
+ if (r.isFinished())
|
|
+ m_valid = true;
|
|
+ }
|
|
+
|
|
+ QByteArray Packet::IndirectQuery::encode() const {
|
|
+ Writer w;
|
|
+ w << m_authenticationNames;
|
|
+ return w.finalize(Packet::_IndirectQuery);
|
|
+ }
|
|
+
|
|
+ Packet::ForwardQuery::ForwardQuery(const QHostAddress &host, quint16 port, Reader &r) : Packet(host, port, r) {
|
|
+ r >> m_clientAddress >> m_clientPort >> m_authenticationNames;
|
|
+ if (r.isFinished())
|
|
+ m_valid = true;
|
|
+ }
|
|
+
|
|
+ QByteArray Packet::ForwardQuery::encode() const {
|
|
+ Writer w;
|
|
+ w << m_clientAddress << m_clientPort << m_authenticationNames;
|
|
+ return w.finalize(Packet::_ForwardQuery);
|
|
+ }
|
|
+
|
|
+ Packet::Willing::Willing(const QHostAddress &host, quint16 port, const QString &authenticationName, const QString &hostname, const QString &status) : Packet(host, port),
|
|
+ m_authenticationName(authenticationName.toLatin1()),
|
|
+ m_hostname(hostname.toLatin1()),
|
|
+ m_status(status.toLatin1()) {
|
|
+ qDebug() << " XDMCP: Prepared Willing reply for" << host << port << "with contents" << authenticationName << hostname << status;
|
|
+ }
|
|
+
|
|
+ Packet::Willing::Willing(const QHostAddress &host, quint16 port, Reader &r) : Packet(host, port, r) {
|
|
+ r >> m_authenticationName >> m_hostname >> m_status;
|
|
+ if (r.isFinished())
|
|
+ m_valid = true;
|
|
+ }
|
|
+
|
|
+ QByteArray Packet::Willing::encode() const {
|
|
+ Writer w;
|
|
+ w << m_authenticationName << m_hostname << m_status;
|
|
+ return w.finalize(Packet::_Willing);
|
|
+ }
|
|
+
|
|
+ Packet::Unwilling::Unwilling(const QHostAddress &host, quint16 port, const QString &hostname, const QString &status) : Packet(host, port),
|
|
+ m_hostname(hostname.toLatin1()),
|
|
+ m_status(status.toLatin1()) {
|
|
+ qDebug() << " XDMCP: Prepared Unwilling reply for" << host << port << "with contents" << hostname << status;
|
|
+ }
|
|
+
|
|
+ Packet::Unwilling::Unwilling(const QHostAddress &host, quint16 port, Reader &r) : Packet(host, port, r) {
|
|
+ r >> m_hostname >> m_status;
|
|
+ if (r.isFinished())
|
|
+ m_valid = true;
|
|
+ }
|
|
+
|
|
+ QByteArray Packet::Unwilling::encode() const {
|
|
+ Writer w;
|
|
+ w << m_hostname << m_status;
|
|
+ return w.finalize(Packet::_Unwilling);
|
|
+ }
|
|
+
|
|
+ Packet::Request::Request(const QHostAddress &host, quint16 port, Reader &r) : Packet(host, port, r) {
|
|
+ r >> m_displayNumber >> m_connectionTypes >> m_connectionAddresses >> m_authenticationName >> m_authenticationData >> m_authorizationNames >> m_manufacturerDisplayID;
|
|
+ if (r.isFinished())
|
|
+ m_valid = true;
|
|
+ }
|
|
+
|
|
+ QByteArray Packet::Request::encode() const {
|
|
+ Writer w;
|
|
+ w << m_displayNumber << m_connectionTypes << m_connectionAddresses << m_authenticationName << m_authenticationData << m_authorizationNames << m_manufacturerDisplayID;
|
|
+ return w.finalize(Packet::_Request);
|
|
+ }
|
|
+
|
|
+ Packet::Accept::Accept(const QHostAddress &host, quint16 port, uint32_t sessionId, const QString authenticationName, const QByteArray authenticationData, const QString authorizationName, const QByteArray authorizationData) : Packet(host, port),
|
|
+ m_sessionID(sessionId),
|
|
+ m_authenticationName(authenticationName.toLatin1()),
|
|
+ m_authenticationData(authenticationData),
|
|
+ m_authorizationName(authorizationName.toLatin1()),
|
|
+ m_authorizationData(authorizationData) {
|
|
+ qDebug() << " XDMCP: Prepared Accept reply for" << host << port << "with contents" << sessionId << authenticationName << authenticationData << authorizationName << authorizationData;
|
|
+ }
|
|
+
|
|
+ Packet::Accept::Accept(const QHostAddress &host, quint16 port, Reader &r) : Packet(host, port, r) {
|
|
+ r >> m_sessionID >> m_authenticationName >> m_authenticationData >> m_authorizationName >> m_authorizationData;
|
|
+ if (r.isFinished())
|
|
+ m_valid = true;
|
|
+ }
|
|
+
|
|
+ QByteArray Packet::Accept::encode() const {
|
|
+ Writer w;
|
|
+ w << m_sessionID << m_authenticationName << m_authenticationData << m_authorizationName << m_authorizationData;
|
|
+ return w.finalize(Packet::_Accept);
|
|
+ }
|
|
+
|
|
+ Packet::Decline::Decline(const QHostAddress &host, quint16 port, const QString status, const QString authenticationName, const QByteArray authenticationData) : Packet(host, port),
|
|
+ m_status(status.toLatin1()),
|
|
+ m_authenticationName(authenticationName.toLatin1()),
|
|
+ m_authenticationData(authenticationData) {
|
|
+ qDebug() << " XDMCP: Prepared Decline reply for" << host << port << "with contents" << status << authenticationName << authenticationData;
|
|
+ }
|
|
+
|
|
+ Packet::Decline::Decline(const QHostAddress &host, quint16 port, Reader &r) : Packet(host, port, r) {
|
|
+ r >> m_status >> m_authenticationName >> m_authenticationData;
|
|
+ if (r.isFinished())
|
|
+ m_valid = true;
|
|
+ }
|
|
+
|
|
+ QByteArray Packet::Decline::encode() const {
|
|
+ Writer w;
|
|
+ w << m_status << m_authenticationName << m_authenticationData;
|
|
+ return w.finalize(Packet::_Decline);
|
|
+ }
|
|
+
|
|
+ Packet::Manage::Manage(const QHostAddress &host, quint16 port, Reader &r) : Packet(host, port, r) {
|
|
+ r >> m_sessionID >> m_displayNumber >> m_displayClass;
|
|
+ if (r.isFinished())
|
|
+ m_valid = true;
|
|
+ }
|
|
+
|
|
+ QByteArray Packet::Manage::encode() const {
|
|
+ Writer w;
|
|
+ w << m_sessionID << m_displayNumber << m_displayClass;
|
|
+ return w.finalize(Packet::_Manage);
|
|
+ }
|
|
+
|
|
+ Packet::Refuse::Refuse(const QHostAddress &host, quint16 port, uint32_t sessionID) : Packet(host, port),
|
|
+ m_sessionID(sessionID) {
|
|
+ qDebug() << " XDMCP: Prepared Refuse reply for" << host << port << "with contents" << sessionID;
|
|
+ }
|
|
+
|
|
+ Packet::Refuse::Refuse(const QHostAddress &host, quint16 port, Reader &r) : Packet(host, port, r) {
|
|
+ r >> m_sessionID;
|
|
+ if (r.isFinished())
|
|
+ m_valid = true;
|
|
+ }
|
|
+
|
|
+ QByteArray Packet::Refuse::encode() const {
|
|
+ Writer w;
|
|
+ w << m_sessionID;
|
|
+ return w.finalize(Packet::_Refuse);
|
|
+ }
|
|
+
|
|
+ Packet::Failed::Failed(const QHostAddress &host, quint16 port, uint32_t sessionID, const QString &status) : Packet(host, port),
|
|
+ m_sessionID(sessionID),
|
|
+ m_status(status.toLatin1()) {
|
|
+ qDebug() << " XDMCP: Prepared Failed reply for" << host << port << "with contents" << sessionID << status;
|
|
+ }
|
|
+
|
|
+ Packet::Failed::Failed(const QHostAddress &host, quint16 port, Reader &r) : Packet(host, port, r) {
|
|
+ r >> m_sessionID >> m_status;
|
|
+ if (r.isFinished())
|
|
+ m_valid = true;
|
|
+ }
|
|
+
|
|
+ QByteArray Packet::Failed::encode() const {
|
|
+ Writer w;
|
|
+ w << m_sessionID << m_status;
|
|
+ return w.finalize(Packet::_Failed);
|
|
+ }
|
|
+
|
|
+ Packet::KeepAlive::KeepAlive(const QHostAddress &host, quint16 port, Reader &r) : Packet(host, port, r) {
|
|
+ r >> m_displayNumber >> m_sessionID;
|
|
+ if (r.isFinished())
|
|
+ m_valid = true;
|
|
+ }
|
|
+
|
|
+ QByteArray Packet::KeepAlive::encode() const {
|
|
+ Writer w;
|
|
+ w << m_displayNumber << m_sessionID;
|
|
+ return w.finalize(Packet::_KeepAlive);
|
|
+ }
|
|
+
|
|
+ Packet::Alive::Alive(const QHostAddress &host, quint16 port, uint8_t sessionRunning, uint32_t sessionID) : Packet(host, port),
|
|
+ m_sessionRunning(sessionRunning),
|
|
+ m_sessionID(sessionID) {
|
|
+ qDebug() << " XDMCP: Prepared Alive reply for" << host << port << "with contents" << sessionRunning << sessionID;
|
|
+ }
|
|
+
|
|
+ Packet::Alive::Alive(const QHostAddress &host, quint16 port, Reader &r) : Packet(host, port, r) {
|
|
+ r >> m_sessionRunning >> m_sessionID;
|
|
+ if (r.isFinished())
|
|
+ m_valid = true;
|
|
+ }
|
|
+
|
|
+ QByteArray Packet::Alive::encode() const {
|
|
+ Writer w;
|
|
+ w << m_sessionRunning << m_sessionID;
|
|
+ return w.finalize(Packet::_Alive);
|
|
+ }
|
|
+
|
|
+/*******************************************************************************
|
|
+ * SERVER IMPLEMENTATIONS
|
|
+ ******************************************************************************/
|
|
+
|
|
+ Packet *Packet::Query::onServerReceived() const {
|
|
+ if (m_authenticationNames.isEmpty())
|
|
+ return new Willing(m_host, m_port, "", Server::instance()->hostname(), Server::instance()->status());
|
|
+
|
|
+ return new Unwilling(m_host, m_port, Server::instance()->hostname(), "Server does not support authentication");
|
|
+ }
|
|
+
|
|
+ Packet* Packet::Request::onServerReceived() const {
|
|
+ qDebug() << " XDMCP: Server: Received Request" << m_displayNumber << m_connectionTypes << m_connectionAddresses << m_authenticationName << m_authenticationData << m_authorizationNames << m_manufacturerDisplayID;
|
|
+
|
|
+ if (m_authorizationNames.contains("MIT-MAGIC-COOKIE-1")) {
|
|
+ uint32_t sessionId = Server::instance()->newSessionId();
|
|
+ // FIXME for obvious reasons
|
|
+ QHostAddress addr(QString("%1.%2.%3.%4").arg((uint) m_connectionAddresses.first()[0]).arg((uint) m_connectionAddresses.first()[1]).arg((uint) m_connectionAddresses.first()[2]).arg((uint) m_connectionAddresses.first()[3]));
|
|
+ Display *display = Server::instance()->newDisplay(sessionId, addr.toString(), m_displayNumber);
|
|
+
|
|
+ return new Accept(m_host, m_port, sessionId, m_authenticationName, m_authenticationData, "MIT-MAGIC-COOKIE-1", display->rawCookie());
|
|
+ }
|
|
+
|
|
+ return new Decline(m_host, m_port, Server::instance()->status(), m_authenticationName, m_authenticationData);
|
|
+ }
|
|
+
|
|
+ Packet* Packet::Manage::onServerReceived() const {
|
|
+ Display *display = Server::instance()->getDisplay(m_sessionID);
|
|
+
|
|
+ if (display != nullptr) {
|
|
+ display->start();
|
|
+ return nullptr; // this packet doesn't have any response on success
|
|
+ }
|
|
+
|
|
+ return new Refuse(m_host, m_port, m_sessionID);
|
|
+ }
|
|
+
|
|
+ Packet* Packet::KeepAlive::onServerReceived() const {
|
|
+ Display *display = Server::instance()->getDisplay(m_sessionID);
|
|
+
|
|
+ if (display == nullptr)
|
|
+ return new Alive(m_host, m_port, 0, m_sessionID);
|
|
+
|
|
+ if (display->displayId() != m_displayNumber)
|
|
+ return new Alive(m_host, m_port, 0, m_sessionID);
|
|
+
|
|
+ return new Alive(m_host, m_port, 1, m_sessionID);
|
|
+ }
|
|
+
|
|
+/*******************************************************************************
|
|
+ * CLIENT IMPLEMENTATIONS
|
|
+ ******************************************************************************/
|
|
+
|
|
+};
|
|
+};
|
|
diff --git a/src/daemon/xdmcp/Packet.h b/src/daemon/xdmcp/Packet.h
|
|
new file mode 100644
|
|
index 0000000..9246541
|
|
--- /dev/null
|
|
+++ b/src/daemon/xdmcp/Packet.h
|
|
@@ -0,0 +1,394 @@
|
|
+/*
|
|
+ * Packet type handling for X Display Control Protocol
|
|
+ * Copyright (C) 2013 Martin Bříza <mbriza@redhat.com>
|
|
+ *
|
|
+ * This program is free software; you can redistribute it and/or modify
|
|
+ * it under the terms of the GNU General Public License as published by
|
|
+ * the Free Software Foundation; either version 2 of the License, or
|
|
+ * (at your option) any later version.
|
|
+ *
|
|
+ * This program is distributed in the hope that it will be useful,
|
|
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
+ * GNU General Public License for more details.
|
|
+ *
|
|
+ * You should have received a copy of the GNU General Public License along
|
|
+ * with this program; if not, write to the Free Software Foundation, Inc.,
|
|
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
|
+ *
|
|
+ */
|
|
+
|
|
+#ifndef SDDM_XDMCP_PACKET_H
|
|
+#define SDDM_XDMCP_PACKET_H
|
|
+
|
|
+#include <QtCore/QByteArray>
|
|
+#include <QtNetwork/QHostAddress>
|
|
+
|
|
+#include "Utils.h"
|
|
+
|
|
+namespace SDDM {
|
|
+namespace XDMCP {
|
|
+ class Reader;
|
|
+ /**
|
|
+ * XDMCP Packet main class
|
|
+ *
|
|
+ * Defines interface and static methods to work with the protocol data
|
|
+ */
|
|
+ class Packet {
|
|
+ public:
|
|
+ virtual ~Packet();
|
|
+
|
|
+ /**
|
|
+ * Get the packet's validity (especially for responses)
|
|
+ *
|
|
+ * \return validity
|
|
+ */
|
|
+ bool isValid() const;
|
|
+
|
|
+ /**
|
|
+ * Defines server behavior on receiving this packet
|
|
+ */
|
|
+ virtual Packet *onServerReceived() const;
|
|
+
|
|
+ /**
|
|
+ * Defines client behavior on receiving this packet
|
|
+ */
|
|
+ virtual Packet *onClientReceived() const;
|
|
+
|
|
+ /**
|
|
+ * Encode the packet to raw data according to the protocol
|
|
+ *
|
|
+ * \return Data byte array
|
|
+ */
|
|
+ virtual QByteArray encode() const;
|
|
+
|
|
+ /**
|
|
+ * Decode raw packet data and create a packet for further processing
|
|
+ *
|
|
+ * \param data Raw data from the socket
|
|
+ * \param host Source host of the packet
|
|
+ * \param port Source port of the packet
|
|
+ * \return Parsed packet
|
|
+ */
|
|
+ static Packet *decode(const QByteArray &data, const QHostAddress &host = QHostAddress(), quint16 port = 0);
|
|
+
|
|
+ /**
|
|
+ * Set the packet's source/destination host
|
|
+ *
|
|
+ * \param host The host
|
|
+ */
|
|
+ void setHost(const QHostAddress &host);
|
|
+
|
|
+ /**
|
|
+ * Get the packet's source/destination host
|
|
+ *
|
|
+ * \return The host
|
|
+ */
|
|
+ const QHostAddress& host() const;
|
|
+
|
|
+ /**
|
|
+ * Set the packet's source/destination host
|
|
+ *
|
|
+ * \param port The port
|
|
+ */
|
|
+ void setPort(quint16 port);
|
|
+
|
|
+ /**
|
|
+ * Get the packet's source/destination host
|
|
+ *
|
|
+ * \return The port
|
|
+ */
|
|
+ quint16 port() const;
|
|
+
|
|
+ /**
|
|
+ * Redundancy for everyone!
|
|
+ */
|
|
+ enum Opcode {
|
|
+ _None = 0,
|
|
+ _BroadcastQuery = 1,
|
|
+ _Query,
|
|
+ _IndirectQuery,
|
|
+ _ForwardQuery,
|
|
+ _Willing,
|
|
+ _Unwilling,
|
|
+ _Request,
|
|
+ _Accept,
|
|
+ _Decline,
|
|
+ _Manage,
|
|
+ _Refuse,
|
|
+ _Failed,
|
|
+ _KeepAlive,
|
|
+ _Alive,
|
|
+ };
|
|
+
|
|
+ class BroadcastQuery;
|
|
+ class Query;
|
|
+ class IndirectQuery;
|
|
+ class ForwardQuery;
|
|
+ class Willing;
|
|
+ class Unwilling;
|
|
+ class Request;
|
|
+ class Accept;
|
|
+ class Decline;
|
|
+ class Manage;
|
|
+ class Refuse;
|
|
+ class Failed;
|
|
+ class KeepAlive;
|
|
+ class Alive;
|
|
+
|
|
+ protected:
|
|
+ /**
|
|
+ * C'tor targetted for creating response packets
|
|
+ *
|
|
+ * Automatically sets the packet to be valid
|
|
+ * \param host Destination host for the response
|
|
+ * \param port Destination port for the response
|
|
+ */
|
|
+ Packet(const QHostAddress &host, quint16 port);
|
|
+ /**
|
|
+ * C'tor targetted for parsing raw data
|
|
+ *
|
|
+ * \param host Destination host for the response
|
|
+ * \param port Destination port for the response
|
|
+ * \param r Reader containing the packet's raw data
|
|
+ */
|
|
+ Packet(const QHostAddress &host, quint16 port, Reader &r);
|
|
+
|
|
+ QHostAddress m_host;
|
|
+ quint16 m_port { 0 };
|
|
+ bool m_valid { false };
|
|
+ };
|
|
+
|
|
+ class Packet::BroadcastQuery : public Packet {
|
|
+ public:
|
|
+ BroadcastQuery(const QHostAddress &host, quint16 port, Reader &r);
|
|
+ virtual QByteArray encode() const;
|
|
+ private:
|
|
+ QVector<QByteArray> m_authenticationNames;
|
|
+ };
|
|
+
|
|
+ class Packet::Query : public Packet {
|
|
+ public:
|
|
+ Query(const QHostAddress &host, quint16 port, Reader &r);
|
|
+ virtual QByteArray encode() const;
|
|
+ /**
|
|
+ * Server side handling of Query packet
|
|
+ *
|
|
+ * Client sends a list of authorization names
|
|
+ *
|
|
+ * If the list is empty (and we are willing to continue without
|
|
+ * authorization), we reply with \ref Willing with empty
|
|
+ * authenticationName
|
|
+ *
|
|
+ * Otherwise, we choose the one name that complies to the supported ones
|
|
+ * in the server and use it as authenticationName for the \ref Willing
|
|
+ * packet
|
|
+ *
|
|
+ * If none of the names complies and/or we don't want to continue
|
|
+ * without authorization, the reply is \ref Unwilling
|
|
+ *
|
|
+ * \return Response
|
|
+ */
|
|
+ virtual Packet *onServerReceived() const;
|
|
+ private:
|
|
+ QVector<QByteArray> m_authenticationNames;
|
|
+ };
|
|
+
|
|
+ class Packet::IndirectQuery : public Packet {
|
|
+ public:
|
|
+ IndirectQuery(const QHostAddress &host, quint16 port, Reader &r);
|
|
+ virtual QByteArray encode() const;
|
|
+ private:
|
|
+ QVector<QByteArray> m_authenticationNames;
|
|
+ };
|
|
+
|
|
+ class Packet::ForwardQuery : public Packet {
|
|
+ public:
|
|
+ ForwardQuery(const QHostAddress &host, quint16 port, Reader &r);
|
|
+ virtual QByteArray encode() const;
|
|
+ private:
|
|
+ QByteArray m_clientAddress;
|
|
+ QByteArray m_clientPort;
|
|
+ QVector<QByteArray> m_authenticationNames;
|
|
+ };
|
|
+
|
|
+ class Packet::Willing : public Packet {
|
|
+ public:
|
|
+ Willing(const QHostAddress &host, quint16 port,
|
|
+ const QString &authenticationName, const QString &hostname,
|
|
+ const QString &status);
|
|
+ Willing(const QHostAddress &host, quint16 port, Reader &r);
|
|
+ virtual QByteArray encode() const;
|
|
+ /**
|
|
+ * Client side handling of Willing packet
|
|
+ *
|
|
+ * Description TBD
|
|
+ *
|
|
+ * Reply on success is \ref Request
|
|
+ *
|
|
+ * \return Response
|
|
+ */
|
|
+// virtual Packet *onClientReceived() const;
|
|
+ private:
|
|
+ QByteArray m_authenticationName;
|
|
+ QByteArray m_hostname;
|
|
+ QByteArray m_status;
|
|
+ };
|
|
+
|
|
+ class Packet::Unwilling : public Packet {
|
|
+ public:
|
|
+ Unwilling(const QHostAddress &host, quint16 port,
|
|
+ const QString &hostname, const QString &status);
|
|
+ Unwilling(const QHostAddress &host, quint16 port, Reader &r);
|
|
+ virtual QByteArray encode() const;
|
|
+ private:
|
|
+ QByteArray m_hostname;
|
|
+ QByteArray m_status;
|
|
+ };
|
|
+
|
|
+ class Packet::Request : public Packet {
|
|
+ public:
|
|
+ Request(const QHostAddress &host, quint16 port, Reader &r);
|
|
+ virtual QByteArray encode() const;
|
|
+ /**
|
|
+ * Server side handling of Request packet
|
|
+ *
|
|
+ * Client informs there will be displey displayNumber running on
|
|
+ * connectionAddresses accessible via connectionTypes.
|
|
+ * It also authorizes with the authenticationName and authenticationData.
|
|
+ * It sends a list of propsed authorizationNames for the server to choose
|
|
+ * one of them and reply using the \ref Accept packet where the chosen
|
|
+ * authorizationName will be stored along with the authorizationData for
|
|
+ * the client and the new sessionID.
|
|
+ *
|
|
+ * If the display cannot be used, the server replies using the \ref Decline
|
|
+ * packet with its status.
|
|
+ *
|
|
+ * \return Response
|
|
+ */
|
|
+ virtual Packet *onServerReceived() const;
|
|
+ private:
|
|
+ uint16_t m_displayNumber;
|
|
+ QVector<uint16_t> m_connectionTypes;
|
|
+ QVector<QByteArray> m_connectionAddresses;
|
|
+ QByteArray m_authenticationName;
|
|
+ QByteArray m_authenticationData;
|
|
+ QVector<QByteArray> m_authorizationNames;
|
|
+ QByteArray m_manufacturerDisplayID;
|
|
+ };
|
|
+
|
|
+ class Packet::Accept : public Packet {
|
|
+ public:
|
|
+ Accept(const QHostAddress &host, quint16 port, uint32_t sessionId,
|
|
+ const QString authenticationName, const QByteArray authenticationData,
|
|
+ const QString authorizationName, const QByteArray authorizationData);
|
|
+ Accept(const QHostAddress &host, quint16 port, Reader &r);
|
|
+ virtual QByteArray encode() const;
|
|
+ /**
|
|
+ * Client side handling of Accept packet
|
|
+ *
|
|
+ * Description TBD
|
|
+ *
|
|
+ * Reply on succes is \ref Manage
|
|
+ *
|
|
+ * \return Response
|
|
+ */
|
|
+// virtual Packet *onClientReceived() const;
|
|
+ private:
|
|
+ uint32_t m_sessionID;
|
|
+ QByteArray m_authenticationName;
|
|
+ QByteArray m_authenticationData;
|
|
+ QByteArray m_authorizationName;
|
|
+ QByteArray m_authorizationData;
|
|
+ };
|
|
+
|
|
+ class Packet::Decline : public Packet {
|
|
+ public:
|
|
+ Decline(const QHostAddress &host, quint16 port, const QString status,
|
|
+ const QString authenticationName, const QByteArray authenticationData);
|
|
+ Decline(const QHostAddress &host, quint16 port, Reader &r);
|
|
+ virtual QByteArray encode() const;
|
|
+ private:
|
|
+ QByteArray m_status;
|
|
+ QByteArray m_authenticationName;
|
|
+ QByteArray m_authenticationData;
|
|
+ };
|
|
+
|
|
+ class Packet::Manage : public Packet {
|
|
+ public:
|
|
+ Manage(const QHostAddress &host, quint16 port, Reader &r);
|
|
+ virtual QByteArray encode() const;
|
|
+ /**
|
|
+ * Server side handling of Manage packet
|
|
+ *
|
|
+ * Client asks the server to open a connection to its opened display
|
|
+ * specified in the previous Request packet.
|
|
+ *
|
|
+ * There is no answer on success, just opening a connection.
|
|
+ *
|
|
+ * If the connection is specified wrong (erroneous sessionID, etc.), then
|
|
+ * the server replies with a \ref Refuse packet.
|
|
+ *
|
|
+ * If the connection cannot be opened due to an internal error,
|
|
+ * \ref Failed packet is sent to the client.
|
|
+ *
|
|
+ * \return Response
|
|
+ */
|
|
+ virtual Packet *onServerReceived() const;
|
|
+ private:
|
|
+ uint32_t m_sessionID;
|
|
+ uint16_t m_displayNumber;
|
|
+ QByteArray m_displayClass;
|
|
+ };
|
|
+
|
|
+ class Packet::Refuse : public Packet {
|
|
+ public:
|
|
+ Refuse(const QHostAddress &host, quint16 port, uint32_t sessionID);
|
|
+ Refuse(const QHostAddress &host, quint16 port, Reader &r);
|
|
+ virtual QByteArray encode() const;
|
|
+ private:
|
|
+ uint32_t m_sessionID;
|
|
+ };
|
|
+
|
|
+ class Packet::Failed : public Packet {
|
|
+ public:
|
|
+ Failed(const QHostAddress &host, quint16 port, uint32_t sessionID, const QString &status);
|
|
+ Failed(const QHostAddress &host, quint16 port, Reader &r);
|
|
+ virtual QByteArray encode() const;
|
|
+ private:
|
|
+ uint32_t m_sessionID;
|
|
+ QByteArray m_status;
|
|
+ };
|
|
+
|
|
+ class Packet::KeepAlive : public Packet {
|
|
+ public:
|
|
+ KeepAlive(const QHostAddress &host, quint16 port, Reader &r);
|
|
+ virtual QByteArray encode() const;
|
|
+ /**
|
|
+ * Server side handling of KeepAlive packet
|
|
+ *
|
|
+ * Clients asks the server if the session is still alive.
|
|
+ *
|
|
+ * Server replies with \ref Alive packet with either sessionRunning == 0
|
|
+ * for a dead session or sessionRunning != 0 for a live one
|
|
+ */
|
|
+ virtual Packet *onServerReceived() const;
|
|
+ private:
|
|
+ uint16_t m_displayNumber;
|
|
+ uint32_t m_sessionID;
|
|
+ };
|
|
+
|
|
+ class Packet::Alive : public Packet {
|
|
+ public:
|
|
+ Alive(const QHostAddress &host, quint16 port, uint8_t sessionRunning, uint32_t sessionID);
|
|
+ Alive(const QHostAddress &host, quint16 port, Reader &r);
|
|
+ virtual QByteArray encode() const;
|
|
+ private:
|
|
+ uint8_t m_sessionRunning;
|
|
+ uint32_t m_sessionID;
|
|
+ };
|
|
+
|
|
+} // namespace XDMCP
|
|
+} // namespace SDDM
|
|
+
|
|
+#endif // SDDM_XDMCP_PACKET_H
|
|
diff --git a/src/daemon/xdmcp/Server.cpp b/src/daemon/xdmcp/Server.cpp
|
|
new file mode 100644
|
|
index 0000000..b4dc6e1
|
|
--- /dev/null
|
|
+++ b/src/daemon/xdmcp/Server.cpp
|
|
@@ -0,0 +1,148 @@
|
|
+/*
|
|
+ * Server implementation for X Display Control Protocol
|
|
+ * Copyright (C) 2013 Martin Bříza <mbriza@redhat.com>
|
|
+ *
|
|
+ * This program is free software; you can redistribute it and/or modify
|
|
+ * it under the terms of the GNU General Public License as published by
|
|
+ * the Free Software Foundation; either version 2 of the License, or
|
|
+ * (at your option) any later version.
|
|
+ *
|
|
+ * This program is distributed in the hope that it will be useful,
|
|
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
+ * GNU General Public License for more details.
|
|
+ *
|
|
+ * You should have received a copy of the GNU General Public License along
|
|
+ * with this program; if not, write to the Free Software Foundation, Inc.,
|
|
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
|
+ *
|
|
+ */
|
|
+
|
|
+#include "Server.h"
|
|
+#include "Packet.h"
|
|
+#include "../DaemonApp.h"
|
|
+#include "../Display.h"
|
|
+
|
|
+#include <QtNetwork/QHostInfo>
|
|
+
|
|
+namespace SDDM {
|
|
+namespace XDMCP {
|
|
+
|
|
+ Server *Server::self = nullptr;
|
|
+
|
|
+ Server* Server::instance(DaemonApp* parent) {
|
|
+ if (self == nullptr) {
|
|
+ self = new Server(parent);
|
|
+ }
|
|
+ return self;
|
|
+ }
|
|
+
|
|
+ Server::Server(DaemonApp* parent) : QUdpSocket(parent),
|
|
+ m_hostname(QHostInfo::localHostName()) {
|
|
+
|
|
+ }
|
|
+
|
|
+ Server::~Server() {
|
|
+
|
|
+ }
|
|
+
|
|
+ bool Server::start() {
|
|
+ qDebug() << " XDMCP: Server: Starting...";
|
|
+ connect(this, SIGNAL(readyRead()), this, SLOT(newData()));
|
|
+ bool result = bind(m_address, m_port);
|
|
+ if (!result) {
|
|
+ qDebug() << " XDMCP: Server: Cannot bind" << m_address << m_port << errorString();
|
|
+ }
|
|
+ else {
|
|
+ m_started = true;
|
|
+ m_status = "online";
|
|
+ qDebug() << " XDMCP: Server: Started and listening on" << m_address << ":" << m_port;
|
|
+ }
|
|
+ return result;
|
|
+ }
|
|
+
|
|
+ void Server::socketError(QAbstractSocket::SocketError socketError) {
|
|
+ qDebug() << " XDMCP: Error:" << errorString();
|
|
+ // TODO: error recovery
|
|
+ m_started = false;
|
|
+ m_status = "error";
|
|
+ }
|
|
+
|
|
+ QString Server::hostname() const {
|
|
+ return m_hostname;
|
|
+ }
|
|
+
|
|
+ QString Server::status() const {
|
|
+ return m_status;
|
|
+ }
|
|
+
|
|
+ bool Server::isStarted() const {
|
|
+ return m_started;
|
|
+ }
|
|
+
|
|
+ uint32_t Server::newSessionId() {
|
|
+ // realistically, can this serve more than 4 billion clients to actually cause trouble in removeDisplay?
|
|
+ while (m_displays.keys().contains(m_lastSession))
|
|
+ m_lastSession++;
|
|
+ return m_lastSession++;
|
|
+ }
|
|
+
|
|
+ Display* Server::newDisplay(uint32_t sessionId, const QString &hostName, uint32_t displayNumber) {
|
|
+ if (m_displays.contains(sessionId))
|
|
+ return nullptr;
|
|
+ Display *display = new Display(hostName, displayNumber, this);
|
|
+ connect(display, SIGNAL(destroyed(QObject*)), SLOT(removeDisplay(QObject*)));
|
|
+ m_displays[sessionId] = display;
|
|
+ return display;
|
|
+ }
|
|
+
|
|
+ Display* Server::getDisplay(uint32_t id) {
|
|
+ if (m_displays.contains(id))
|
|
+ return m_displays[id];
|
|
+ return nullptr;
|
|
+ }
|
|
+
|
|
+ void Server::removeDisplay(QObject* obj) {
|
|
+ int key = m_displays.key(qobject_cast<Display*>(obj), -1);
|
|
+
|
|
+ if (key == -1)
|
|
+ return;
|
|
+
|
|
+ m_displays.remove(key);
|
|
+ }
|
|
+
|
|
+ void Server::setAddress(QHostAddress address) {
|
|
+ m_address = address;
|
|
+ }
|
|
+
|
|
+ void Server::setPort(int port) {
|
|
+ m_port = port;
|
|
+ }
|
|
+
|
|
+ void Server::newData() {
|
|
+ while (hasPendingDatagrams()) {
|
|
+ QByteArray data;
|
|
+ QHostAddress sender;
|
|
+ quint16 port;
|
|
+ data.resize(pendingDatagramSize());
|
|
+
|
|
+ readDatagram(data.data(), data.size(), &sender, &port);
|
|
+
|
|
+ Packet *toProcess = Packet::decode(data, sender, port);
|
|
+ if (toProcess && toProcess->isValid()) {
|
|
+ Packet *response = toProcess->onServerReceived();
|
|
+ if (response && response->isValid()) {
|
|
+ writeDatagram(response->encode(), response->host(), response->port());
|
|
+ }
|
|
+ delete response;
|
|
+ } else {
|
|
+ qDebug() << " XDMCP: Server: Received packet wasn't decoded as valid";
|
|
+ }
|
|
+ delete toProcess;
|
|
+ }
|
|
+ }
|
|
+
|
|
+} // namespace XDMCP
|
|
+} // namespace SDDM
|
|
+
|
|
+#include "Server.moc"
|
|
diff --git a/src/daemon/xdmcp/Server.h b/src/daemon/xdmcp/Server.h
|
|
new file mode 100644
|
|
index 0000000..6f7bdae
|
|
--- /dev/null
|
|
+++ b/src/daemon/xdmcp/Server.h
|
|
@@ -0,0 +1,118 @@
|
|
+/*
|
|
+ * Server implementation for X Display Control Protocol
|
|
+ * Copyright (C) 2013 Martin Bříza <mbriza@redhat.com>
|
|
+ *
|
|
+ * This program is free software; you can redistribute it and/or modify
|
|
+ * it under the terms of the GNU General Public License as published by
|
|
+ * the Free Software Foundation; either version 2 of the License, or
|
|
+ * (at your option) any later version.
|
|
+ *
|
|
+ * This program is distributed in the hope that it will be useful,
|
|
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
+ * GNU General Public License for more details.
|
|
+ *
|
|
+ * You should have received a copy of the GNU General Public License along
|
|
+ * with this program; if not, write to the Free Software Foundation, Inc.,
|
|
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
|
+ *
|
|
+ */
|
|
+
|
|
+#ifndef SDDM_XDMCP_SERVER_H
|
|
+#define SDDM_XDMCP_SERVER_H
|
|
+
|
|
+#include <QtCore/QObject>
|
|
+#include <QtCore/QMap>
|
|
+#include <QtCore/QTimer>
|
|
+#include <QtNetwork/QUdpSocket>
|
|
+
|
|
+// the same as in <X11/Xdmcp.h>
|
|
+#define XDM_UDP_PORT 177
|
|
+
|
|
+namespace SDDM {
|
|
+
|
|
+class Display;
|
|
+ class DaemonApp;
|
|
+namespace XDMCP {
|
|
+
|
|
+ class Server : protected QUdpSocket
|
|
+ {
|
|
+ Q_OBJECT
|
|
+ public:
|
|
+ /**
|
|
+ * Get an instance of the XDMCP server. If there isn't any, construct a
|
|
+ * new one
|
|
+ *
|
|
+ * \param parent Parent for the eventual construction
|
|
+ * \return Singleton XDMCP Server instance
|
|
+ */
|
|
+ static Server *instance(DaemonApp *parent = nullptr);
|
|
+ /**
|
|
+ * D'tor
|
|
+ */
|
|
+ virtual ~Server();
|
|
+
|
|
+ /**
|
|
+ * Set port to listen on
|
|
+ *
|
|
+ * \param port The port
|
|
+ */
|
|
+ void setPort(int port);
|
|
+
|
|
+ /**
|
|
+ * Set address to listen on
|
|
+ *
|
|
+ * \param address The address
|
|
+ */
|
|
+ void setAddress(QHostAddress address);
|
|
+
|
|
+ /**
|
|
+ * Start the server
|
|
+ *
|
|
+ * \return True if successful
|
|
+ */
|
|
+ bool start();
|
|
+
|
|
+ /**
|
|
+ * Get server online status
|
|
+ *
|
|
+ * \return True if running
|
|
+ */
|
|
+ bool isStarted() const;
|
|
+
|
|
+
|
|
+ /**
|
|
+ * Returns a new session ID for incoming requests
|
|
+ */
|
|
+ uint32_t newSessionId();
|
|
+
|
|
+ /**
|
|
+ * Create a new display
|
|
+ */
|
|
+ Display *newDisplay(uint32_t sessionId, const QString &hostName, uint32_t displayNumber);
|
|
+ Display *getDisplay(uint32_t id);
|
|
+ QString status() const;
|
|
+ QString hostname() const;
|
|
+
|
|
+ private slots:
|
|
+ void newData();
|
|
+ void socketError(QAbstractSocket::SocketError socketError);
|
|
+ void removeDisplay(QObject *obj);
|
|
+
|
|
+ private:
|
|
+ static Server *self;
|
|
+ explicit Server(DaemonApp *parent = nullptr);
|
|
+
|
|
+ QString m_status { "offline" };
|
|
+ QString m_hostname { "localhost" };
|
|
+ QHostAddress m_address { QHostAddress::Any };
|
|
+ quint16 m_port { XDM_UDP_PORT };
|
|
+ bool m_started { false };
|
|
+ uint32_t m_lastSession { 0 };
|
|
+ QMap<int, Display*> m_displays;
|
|
+ QMap<int, QTimer*> m_timers;
|
|
+ };
|
|
+}
|
|
+}
|
|
+
|
|
+#endif // SDDM_XDMCP_SERVER_H
|
|
diff --git a/src/daemon/xdmcp/Utils.cpp b/src/daemon/xdmcp/Utils.cpp
|
|
new file mode 100644
|
|
index 0000000..53ac7e3
|
|
--- /dev/null
|
|
+++ b/src/daemon/xdmcp/Utils.cpp
|
|
@@ -0,0 +1,143 @@
|
|
+/*
|
|
+ * Utilities for X Display Control Protocol
|
|
+ * Copyright (C) 2013 Martin Bříza <mbriza@redhat.com>
|
|
+ *
|
|
+ * This program is free software; you can redistribute it and/or modify
|
|
+ * it under the terms of the GNU General Public License as published by
|
|
+ * the Free Software Foundation; either version 2 of the License, or
|
|
+ * (at your option) any later version.
|
|
+ *
|
|
+ * This program is distributed in the hope that it will be useful,
|
|
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
+ * GNU General Public License for more details.
|
|
+ *
|
|
+ * You should have received a copy of the GNU General Public License along
|
|
+ * with this program; if not, write to the Free Software Foundation, Inc.,
|
|
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
|
+ *
|
|
+ */
|
|
+
|
|
+#include "Utils.h"
|
|
+
|
|
+#include <QtCore/QVector>
|
|
+
|
|
+namespace SDDM {
|
|
+namespace XDMCP {
|
|
+
|
|
+ Reader::Reader(const QByteArray &data) : m_data(data),
|
|
+ m_stream(&m_data, QIODevice::ReadOnly | QIODevice::Unbuffered) {
|
|
+ m_stream.setByteOrder(QDataStream::BigEndian);
|
|
+ }
|
|
+
|
|
+ Reader& Reader::operator>>(uint8_t &byte) {
|
|
+ m_stream >> byte;
|
|
+ return *this;
|
|
+ }
|
|
+
|
|
+ Reader& Reader::operator>>(uint16_t &word) {
|
|
+ m_stream >> word;
|
|
+ return *this;
|
|
+ }
|
|
+
|
|
+ Reader& Reader::operator>>(uint32_t &doubleword) {
|
|
+ m_stream >> doubleword;
|
|
+ return *this;
|
|
+ }
|
|
+
|
|
+ Reader& Reader::operator>>(QByteArray &array) {
|
|
+ uint16_t arrayLen;
|
|
+ *this >> arrayLen;
|
|
+ while (arrayLen--) {
|
|
+ uint8_t byte;
|
|
+ *this >> byte;
|
|
+ array.append(byte);
|
|
+ }
|
|
+ return *this;
|
|
+ }
|
|
+
|
|
+ Reader& Reader::operator>>(QVector< uint16_t > &wordArray) {
|
|
+ uint8_t arrayLen;
|
|
+ *this >> arrayLen;
|
|
+ while (arrayLen--) {
|
|
+ uint16_t word;
|
|
+ *this >> word;
|
|
+ wordArray.append(word);
|
|
+ }
|
|
+ return *this;
|
|
+ }
|
|
+
|
|
+ Reader& Reader::operator>>(QVector< QByteArray > &arrayOfArrays) {
|
|
+ uint8_t arrayCount;
|
|
+ *this >> arrayCount;
|
|
+ while (arrayCount--) {
|
|
+ QByteArray array;
|
|
+ *this >> array;
|
|
+ arrayOfArrays.append(array);
|
|
+ }
|
|
+ return *this;
|
|
+ }
|
|
+
|
|
+ bool Reader::isFinished() const {
|
|
+ if ((m_stream.status() == QDataStream::Ok) && m_stream.atEnd())
|
|
+ return true;
|
|
+ else
|
|
+ return false;
|
|
+ }
|
|
+
|
|
+ Writer::Writer() : m_data(),
|
|
+ m_stream(&m_data, QIODevice::WriteOnly | QIODevice::Unbuffered) {
|
|
+ m_stream.setByteOrder(QDataStream::BigEndian);
|
|
+ }
|
|
+
|
|
+ Writer& Writer::operator<<(const uint8_t byte) {
|
|
+ qDebug() << "Appending:" << byte << QChar(byte);
|
|
+ m_stream << byte;
|
|
+ return *this;
|
|
+ }
|
|
+
|
|
+ Writer& Writer::operator<<(const uint16_t word) {
|
|
+ m_stream << word;
|
|
+ return *this;
|
|
+ }
|
|
+
|
|
+ Writer& Writer::operator<<(const uint32_t doubleword) {
|
|
+ m_stream << doubleword;
|
|
+ return *this;
|
|
+ }
|
|
+
|
|
+ Writer& Writer::operator<<(const QByteArray &array) {
|
|
+ *this << (uint16_t) array.count();
|
|
+ for (uint8_t c : array)
|
|
+ m_stream << c;
|
|
+ return *this;
|
|
+ }
|
|
+
|
|
+ Writer& Writer::operator<<(const QVector< uint16_t > &wordArray) {
|
|
+ *this << (uint8_t) wordArray.count();
|
|
+ for (const uint16_t &i : wordArray)
|
|
+ *this << i;
|
|
+ return *this;
|
|
+ }
|
|
+
|
|
+ Writer& Writer::operator<<(const QVector< QByteArray > &arrayOfArrays) {
|
|
+ *this << (uint16_t) arrayOfArrays.count();
|
|
+ for (const QByteArray &i : arrayOfArrays)
|
|
+ *this << i;
|
|
+ return *this;
|
|
+ }
|
|
+
|
|
+ QByteArray Writer::finalize(uint16_t opcode) {
|
|
+ QByteArray result;
|
|
+ QDataStream finalStream(&result, QIODevice::WriteOnly | QIODevice::Unbuffered);
|
|
+ finalStream.setByteOrder(QDataStream::BigEndian);
|
|
+ finalStream << (uint16_t) 1;
|
|
+ finalStream << (uint16_t) opcode;
|
|
+ finalStream << (uint16_t) m_data.size();
|
|
+ for (uint8_t c : m_data)
|
|
+ finalStream << c;
|
|
+ return result;
|
|
+ }
|
|
+
|
|
+} // namespace XDMCP
|
|
+} // namespace SDDM
|
|
\ No newline at end of file
|
|
diff --git a/src/daemon/xdmcp/Utils.h b/src/daemon/xdmcp/Utils.h
|
|
new file mode 100644
|
|
index 0000000..bd96708
|
|
--- /dev/null
|
|
+++ b/src/daemon/xdmcp/Utils.h
|
|
@@ -0,0 +1,93 @@
|
|
+/*
|
|
+ * Utilities for X Display Control Protocol
|
|
+ * Copyright (C) 2013 Martin Bříza <mbriza@redhat.com>
|
|
+ *
|
|
+ * This program is free software; you can redistribute it and/or modify
|
|
+ * it under the terms of the GNU General Public License as published by
|
|
+ * the Free Software Foundation; either version 2 of the License, or
|
|
+ * (at your option) any later version.
|
|
+ *
|
|
+ * This program is distributed in the hope that it will be useful,
|
|
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
+ * GNU General Public License for more details.
|
|
+ *
|
|
+ * You should have received a copy of the GNU General Public License along
|
|
+ * with this program; if not, write to the Free Software Foundation, Inc.,
|
|
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
|
+ *
|
|
+ */
|
|
+
|
|
+#ifndef SDDM_XDMCP_UTILS_H
|
|
+#define SDDM_XDMCP_UTILS_H
|
|
+
|
|
+#include <QtCore/QByteArray>
|
|
+#include <QtCore/QDataStream>
|
|
+
|
|
+#include "Packet.h"
|
|
+
|
|
+namespace SDDM {
|
|
+namespace XDMCP {
|
|
+
|
|
+ /**
|
|
+ * Class for reading information from raw packets and setting the right byte order
|
|
+ *
|
|
+ * Workflow is as follows:
|
|
+ * * Construct Reader from the data received
|
|
+ * * Using the stream operator extract all required variables
|
|
+ * * Check if the stream is at its end by isFinished()
|
|
+ */
|
|
+ class Reader {
|
|
+ public:
|
|
+ Reader(const QByteArray &data);
|
|
+ ~Reader() {}
|
|
+ Reader& operator>>(uint8_t &byte);
|
|
+ Reader& operator>>(uint16_t &word);
|
|
+ Reader& operator>>(uint32_t &doubleword);
|
|
+ Reader& operator>>(QByteArray &array);
|
|
+ Reader& operator>>(QVector<uint16_t> &wordArray);
|
|
+ Reader& operator>>(QVector<QByteArray> &arrayOfArrays);
|
|
+ /**
|
|
+ * Returns true if the stream is at its end and no errors occured
|
|
+ *
|
|
+ * \return Finished status
|
|
+ */
|
|
+ bool isFinished() const;
|
|
+ private:
|
|
+ QByteArray m_data;
|
|
+ QDataStream m_stream;
|
|
+ };
|
|
+
|
|
+ /**
|
|
+ * Class for writing information to raw packets and setting the right byte order
|
|
+ *
|
|
+ * Workflow is as follows:
|
|
+ * * Construct empty writer
|
|
+ * * Using the stream operator insert all contained variables
|
|
+ * * Get a complete packet by the finalize(opcode) method
|
|
+ */
|
|
+ class Writer {
|
|
+ public:
|
|
+ Writer();
|
|
+ Writer& operator<<(const uint8_t byte);
|
|
+ Writer& operator<<(const uint16_t word);
|
|
+ Writer& operator<<(const uint32_t doubleword);
|
|
+ Writer& operator<<(const QByteArray &array);
|
|
+ Writer& operator<<(const QVector<uint16_t> &wordArray);
|
|
+ Writer& operator<<(const QVector<QByteArray> &arrayOfArrays);
|
|
+ /**
|
|
+ * Finalizes building of the packet
|
|
+ *
|
|
+ * \param opcode XDMCP protocol code of the packet type
|
|
+ * \return Raw packet data
|
|
+ */
|
|
+ QByteArray finalize(uint16_t opcode);
|
|
+ private:
|
|
+ QByteArray m_data;
|
|
+ QDataStream m_stream;
|
|
+ };
|
|
+
|
|
+} // namespace XDMCP
|
|
+} // namespace SDDM
|
|
+
|
|
+#endif // SDDM_XDMCP_UTILS_H
|