The code below shows how to create a SASL server.
#include <QCoreApplication>
#include <QTcpServer>
#include <QTcpSocket>
#include <QTimer>
#include <cstdio>
#include <QtCrypto>
#ifdef QT_STATICPLUGIN
#include "import_plugins.h"
#endif
static QString socketErrorToString(QAbstractSocket::SocketError x)
{
QString s;
switch(x)
{
case QAbstractSocket::ConnectionRefusedError:
s = QStringLiteral("connection refused or timed out"); break;
case QAbstractSocket::RemoteHostClosedError:
s = QStringLiteral("remote host closed the connection"); break;
case QAbstractSocket::HostNotFoundError:
s = QStringLiteral("host not found"); break;
case QAbstractSocket::SocketAccessError:
s = QStringLiteral("access error"); break;
case QAbstractSocket::SocketResourceError:
s = QStringLiteral("too many sockets"); break;
case QAbstractSocket::SocketTimeoutError:
s = QStringLiteral("operation timed out"); break;
case QAbstractSocket::DatagramTooLargeError:
s = QStringLiteral("datagram was larger than system limit"); break;
case QAbstractSocket::NetworkError:
s = QStringLiteral("network error"); break;
case QAbstractSocket::AddressInUseError:
s = QStringLiteral("address is already in use"); break;
case QAbstractSocket::SocketAddressNotAvailableError:
s = QStringLiteral("address does not belong to the host"); break;
case QAbstractSocket::UnsupportedSocketOperationError:
s = QStringLiteral("operation is not supported by the local operating system"); break;
default:
s = QStringLiteral("unknown socket error"); break;
}
return s;
}
{
QString s;
switch(x)
{
s = QStringLiteral("no appropriate mechanism could be negotiated"); break;
s = QStringLiteral("bad SASL protocol"); break;
s = QStringLiteral("authentication failed"); break;
s = QStringLiteral("authorization failed"); break;
s = QStringLiteral("mechanism too weak for this user"); break;
s = QStringLiteral("encryption is needed to use this mechanism"); break;
s = QStringLiteral("passphrase expired"); break;
s = QStringLiteral("account is disabled"); break;
s = QStringLiteral("user not found"); break;
s = QStringLiteral("needed remote service is unavailable"); break;
default:
s = QStringLiteral("generic authentication failure"); break;
};
return s;
}
{
Q_OBJECT
private:
QString host, proto, realm, str;
int port;
QTcpServer *tcpServer;
public:
ServerTest(const QString &_host, int _port, const QString &_proto, const QString &_realm, const QString &_str);
int reserveId();
void releaseId(int id);
public Q_SLOTS:
void start();
Q_SIGNALS:
void quit();
private Q_SLOTS:
void server_newConnection();
};
class ServerTestHandler :
public QObject
{
Q_OBJECT
private:
ServerTest *serverTest;
QTcpSocket *sock;
int id;
QString host, proto, realm, str;
int mode;
int toWrite;
public:
ServerTestHandler(ServerTest *_serverTest, QTcpSocket *_sock, const QString &_host, const QString &_proto, const QString &_realm, const QString &_str) :
serverTest(_serverTest),
sock(_sock),
host(_host),
proto(_proto),
realm(_realm),
str(_str)
{
id = serverTest->reserveId();
sock->setParent(this);
connect(sock, &QTcpSocket::disconnected, this, &ServerTestHandler::sock_disconnected);
connect(sock, &QTcpSocket::readyRead, this, &ServerTestHandler::sock_readyRead);
connect(sock, QOverload<QAbstractSocket::SocketError>::of(&QTcpSocket::error), this, &ServerTestHandler::sock_error);
connect(sock, &QTcpSocket::bytesWritten, this, &ServerTestHandler::sock_bytesWritten);
mode = 0;
toWrite = 0;
int flags = 0;
flags |= QCA::SASL::AllowPlain;
flags |= QCA::SASL::AllowAnonymous;
printf("%d: Connection received! Starting SASL handshake...\n", id);
}
~ServerTestHandler() override
{
serverTest->releaseId(id);
}
private Q_SLOTS:
void sasl_serverStarted()
{
}
void sock_disconnected()
{
printf("%d: Connection closed.\n", id);
discard();
}
void sock_error(QAbstractSocket::SocketError x)
{
if(x == QAbstractSocket::RemoteHostClosedError)
{
printf("%d: Error: client closed connection unexpectedly.\n", id);
discard();
return;
}
printf("%d: Error: socket: %s\n", id, qPrintable(socketErrorToString(x)));
discard();
}
void sock_readyRead()
{
if(sock->canReadLine())
{
QString line = QString::fromLatin1(sock->readLine());
line.truncate(line.length() - 1);
handleLine(line);
}
}
void sock_bytesWritten(qint64 x)
{
if(mode == 2)
{
if(toWrite == 0)
{
printf("%d: Sent, closing.\n", id);
sock->close();
}
}
}
void sasl_nextStep(const QByteArray &stepData)
{
QString line = QStringLiteral("C");
if(!stepData.isEmpty())
{
line += QLatin1Char(',');
line += arrayToString(stepData);
}
sendLine(line);
}
void sasl_authCheck(const QString &user, const QString &authzid)
{
printf("%d: AuthCheck: User: [%s], Authzid: [%s]\n", id, qPrintable(user), qPrintable(authzid));
}
void sasl_authenticated()
{
sendLine(QStringLiteral("A"));
printf("%d: Authentication success.\n", id);
mode = 2;
printf(
"%d: SSF: %d\n",
id, sasl->
ssf());
sendLine(str);
}
void sasl_readyRead()
{
QByteArray a = sasl->
read();
printf("%d: Warning, client sent %d bytes unexpectedly.\n", id, a.size());
}
void sasl_readyReadOutgoing()
{
}
void sasl_error()
{
{
printf("%d: Error: sasl: initialization failed.\n", id);
}
{
QString errstr = saslAuthConditionToString(sasl->
authCondition());
sendLine(QStringLiteral("E,") + errstr);
printf("%d: Error: sasl: %s.\n", id, qPrintable(errstr));
}
{
printf("%d: Error: sasl: broken security layer.\n", id);
}
else
{
printf("%d: Error: sasl: unknown error.\n", id);
}
sock->close();
}
private:
void discard()
{
deleteLater();
}
void handleLine(const QString &line)
{
printf("%d: Reading: [%s]\n", id, qPrintable(line));
if(mode == 0)
{
int n = line.indexOf(QLatin1Char(' '));
if(n != -1)
{
QString mech = line.mid(0, n);
QString rest = QString::fromLatin1(line.mid(n + 1).toUtf8());
}
else
++mode;
}
else if(mode == 1)
{
QString type, rest;
int n = line.indexOf(QLatin1Char(','));
if(n != -1)
{
type = line.mid(0, n);
rest = line.mid(n + 1);
}
else
{
type = line;
rest = QLatin1String("");
}
if(type == QLatin1String("C"))
{
sasl->
putStep(stringToArray(rest));
}
else
{
printf("%d: Bad format from peer, closing.\n", id);
sock->close();
return;
}
}
}
QString arrayToString(const QByteArray &ba)
{
}
QByteArray stringToArray(const QString &s)
{
return decoder.stringToArray(s).toByteArray();
}
void sendLine(const QString &line)
{
printf("%d: Writing: {%s}\n", id, qPrintable(line));
QString s = line + QLatin1Char('\n');
QByteArray a = s.toUtf8();
if(mode == 2)
{
toWrite += a.size();
}
else
sock->write(a);
}
};
ServerTest::ServerTest(const QString &_host, int _port, const QString &_proto, const QString &_realm, const QString &_str) :
host(_host),
proto(_proto),
realm(_realm),
str(_str),
port(_port)
{
tcpServer = new QTcpServer(this);
connect(tcpServer, &QTcpServer::newConnection, this, &ServerTest::server_newConnection);
}
int ServerTest::reserveId()
{
int n = 0;
while(ids.contains(n))
++n;
ids += n;
return n;
}
void ServerTest::releaseId(int id)
{
ids.removeAll(id);
}
void ServerTest::start()
{
if(!tcpServer->listen(QHostAddress::Any, port))
{
printf("Error: unable to bind to port %d.\n", port);
emit quit();
return;
}
printf("Serving on %s:%d, for protocol %s ...\n", qPrintable(host), port, qPrintable(proto));
}
void ServerTest::server_newConnection()
{
QTcpSocket *sock = tcpServer->nextPendingConnection();
new ServerTestHandler(this, sock, host, proto, realm, str);
}
void usage()
{
printf("usage: saslserver host (message)\n");
printf("options: --proto=x, --realm=x\n");
}
int main(int argc, char **argv)
{
QCoreApplication qapp(argc, argv);
QStringList args = qapp.arguments();
args.removeFirst();
QString proto = QStringLiteral("qcatest");
QString realm;
for(int n = 0; n < args.count(); ++n)
{
if(!args[n].startsWith(QLatin1String("--")))
continue;
QString opt = args[n].mid(2);
QString var, val;
int at = opt.indexOf(QLatin1Char('='));
if(at != -1)
{
var = opt.mid(0, at);
val = opt.mid(at + 1);
}
else
var = opt;
if(var == QLatin1String("proto"))
proto = val;
else if(var == QLatin1String("realm"))
realm = val;
args.removeAt(n);
--n;
}
if(args.count() < 1)
{
usage();
return 0;
}
QString host;
int port = 8001;
QString hostinput = args[0];
QString str = QStringLiteral("Hello, World");
if(args.count() >= 2)
str = args[1];
int at = hostinput.indexOf(QLatin1Char(':'));
if(at != -1)
{
host = hostinput.mid(0, at);
port = hostinput.midRef(at + 1).toInt();
}
else
host = hostinput;
{
printf("Error: SASL support not found.\n");
return 1;
}
ServerTest server(host, port, proto, realm, str);
QObject::connect(&server, &ServerTest::quit, &qapp, &QCoreApplication::quit);
QTimer::singleShot(0, &server, &ServerTest::start);
qapp.exec();
return 0;
}
#include "saslserver.moc"