The code below shows how to create a SASL client.
#include <QCoreApplication>
#include <QTimer>
#include <QTcpSocket>
#include <QTcpServer>
#include <stdio.h>
#include <QtCrypto>
#ifdef QT_STATICPLUGIN
#include "import_plugins.h"
#endif
static QString prompt(const QString &s)
{
printf("* %s ", qPrintable(s));
fflush(stdout);
char line[256];
fgets(line, 255, stdin);
QString result = line;
if(result[result.length()-1] == '\n')
result.truncate(result.length()-1);
return result;
}
static QString socketErrorToString(QAbstractSocket::SocketError x)
{
QString s;
switch(x)
{
case QAbstractSocket::ConnectionRefusedError:
s = "connection refused or timed out"; break;
case QAbstractSocket::RemoteHostClosedError:
s = "remote host closed the connection"; break;
case QAbstractSocket::HostNotFoundError:
s = "host not found"; break;
case QAbstractSocket::SocketAccessError:
s = "access error"; break;
case QAbstractSocket::SocketResourceError:
s = "too many sockets"; break;
case QAbstractSocket::SocketTimeoutError:
s = "operation timed out"; break;
case QAbstractSocket::DatagramTooLargeError:
s = "datagram was larger than system limit"; break;
case QAbstractSocket::NetworkError:
s = "network error"; break;
case QAbstractSocket::AddressInUseError:
s = "address is already in use"; break;
case QAbstractSocket::SocketAddressNotAvailableError:
s = "address does not belong to the host"; break;
case QAbstractSocket::UnsupportedSocketOperationError:
s = "operation is not supported by the local operating system"; break;
default:
s = "unknown socket error"; break;
}
return s;
}
{
QString s;
switch(x)
{
s = "no appropriate mechanism could be negotiated"; break;
s = "bad SASL protocol"; break;
s = "server failed mutual authentication"; break;
default:
s = "generic authentication failure"; break;
};
return s;
}
{
Q_OBJECT
private:
QString host, proto, authzid, realm, user, pass;
int port;
bool no_authzid, no_realm;
int mode;
QTcpSocket *sock;
QByteArray inbuf;
bool sock_done;
int waitCycles;
public:
ClientTest(const QString &_host, int _port, const QString &_proto, const QString &_authzid, const QString &_realm, const QString &_user, const QString &_pass, bool _no_authzid, bool _no_realm) :
host(_host),
proto(_proto),
authzid(_authzid),
realm(_realm),
user(_user),
pass(_pass),
port(_port),
no_authzid(_no_authzid),
no_realm(_no_realm),
sock_done(false),
waitCycles(0)
{
sock = new QTcpSocket(this);
connect(sock, SIGNAL(connected()), SLOT(sock_connected()));
connect(sock, SIGNAL(readyRead()), SLOT(sock_readyRead()));
connect(sock, SIGNAL(error(QAbstractSocket::SocketError)), SLOT(sock_error(QAbstractSocket::SocketError)));
connect(sasl, SIGNAL(clientStarted(bool, const QByteArray &)), SLOT(sasl_clientFirstStep(bool, const QByteArray &)));
connect(sasl, SIGNAL(nextStep(const QByteArray &)), SLOT(sasl_nextStep(const QByteArray &)));
connect(sasl, SIGNAL(authenticated()), SLOT(sasl_authenticated()));
connect(sasl, SIGNAL(readyRead()), SLOT(sasl_readyRead()));
connect(sasl, SIGNAL(readyReadOutgoing()), SLOT(sasl_readyReadOutgoing()));
connect(sasl, SIGNAL(error()), SLOT(sasl_error()));
}
public slots:
void start()
{
mode = 0;
int flags = 0;
flags |= QCA::SASL::AllowPlain;
flags |= QCA::SASL::AllowAnonymous;
if(!user.isEmpty())
if(!authzid.isEmpty())
if(!pass.isEmpty())
if(!realm.isEmpty())
printf("Connecting to %s:%d, for protocol %s\n", qPrintable(host), port, qPrintable(proto));
sock->connectToHost(host, port);
}
signals:
void quit();
private slots:
void sock_connected()
{
printf("Connected to server. Awaiting mechanism list...\n");
}
void sock_error(QAbstractSocket::SocketError x)
{
if(x == QAbstractSocket::RemoteHostClosedError)
{
if(mode == 2)
{
sock_done = true;
tryFinished();
return;
}
else
{
printf("Error: server closed connection unexpectedly.\n");
emit quit();
return;
}
}
printf("Error: socket: %s\n", qPrintable(socketErrorToString(x)));
emit quit();
}
void sock_readyRead()
{
if(mode == 2)
{
QByteArray a = sock->readAll();
printf("Read %d bytes\n", a.size());
if(waitCycles == 0)
{
waitCycles = 3;
QMetaObject::invokeMethod(this, "waitWriteIncoming", Qt::QueuedConnection);
}
}
else
{
if(sock->canReadLine())
{
QString line = sock->readLine();
line.truncate(line.length() - 1);
handleLine(line);
}
}
}
void sasl_clientFirstStep(bool clientInit, const QByteArray &clientInitData)
{
printf(
"Choosing mech: %s\n", qPrintable(sasl->
mechanism()));
if(clientInit)
{
line += ' ';
line += arrayToString(clientInitData);
}
sendLine(line);
}
void sasl_nextStep(const QByteArray &stepData)
{
QString line = "C";
if(!stepData.isEmpty())
{
line += ',';
line += arrayToString(stepData);
}
sendLine(line);
}
{
{
user = prompt("Username:");
}
{
authzid = prompt("Authorize As (enter to skip):");
if(!authzid.isEmpty())
}
{
}
{
printf("Available realms:\n");
if(realms.isEmpty())
printf(" (none specified)\n");
foreach(const QString &s, realms)
printf(" %s\n", qPrintable(s));
realm = prompt("Realm (enter to skip):");
if(!realm.isEmpty())
}
}
void sasl_authenticated()
{
printf("SASL success!\n");
printf(
"SSF: %d\n", sasl->
ssf());
}
void sasl_readyRead()
{
QByteArray a = sasl->
read();
inbuf += a;
processInbuf();
}
void sasl_readyReadOutgoing()
{
sock->write(a);
}
void sasl_error()
{
printf("Error: sasl: initialization failed.\n");
printf(
"Error: sasl: %s.\n", qPrintable(saslAuthConditionToString(sasl->
authCondition())));
printf("Error: sasl: broken security layer.\n");
else
printf("Error: sasl: unknown error.\n");
emit quit();
}
void waitWriteIncoming()
{
--waitCycles;
if(waitCycles > 0)
{
QMetaObject::invokeMethod(this, "waitWriteIncoming", Qt::QueuedConnection);
return;
}
tryFinished();
}
private:
void tryFinished()
{
if(sock_done && waitCycles == 0)
{
printf("Finished, server closed connection.\n");
sasl->disconnect(this);
emit quit();
}
}
QString arrayToString(const QByteArray &ba)
{
}
QByteArray stringToArray(const QString &s)
{
}
void sendLine(const QString &line)
{
printf("Writing: {%s}\n", qPrintable(line));
QString s = line + '\n';
QByteArray a = s.toUtf8();
if(mode == 2)
else
sock->write(a);
}
void processInbuf()
{
QStringList list;
int at;
while((at = inbuf.indexOf('\n')) != -1)
{
list += QString::fromUtf8(inbuf.mid(0, at));
inbuf = inbuf.mid(at + 1);
}
foreach(const QString &line, list)
handleLine(line);
}
void handleLine(const QString &line)
{
printf("Reading: [%s]\n", qPrintable(line));
if(mode == 0)
{
QStringList mechlist = line.split(' ');
mode = 1;
}
else if(mode == 1)
{
QString type, rest;
int n = line.indexOf(',');
if(n != -1)
{
type = line.mid(0, n);
rest = line.mid(n + 1);
}
else
type = line;
if(type == "C")
{
sasl->
putStep(stringToArray(rest));
}
else if(type == "E")
{
if(!rest.isEmpty())
printf("Error: server says: %s.\n", qPrintable(rest));
else
printf("Error: server error, unspecified.\n");
emit quit();
return;
}
else if(type == "A")
{
printf("Authentication success.\n");
mode = 2;
sock_readyRead();
return;
}
else
{
printf("Error: Bad format from peer, closing.\n");
emit quit();
return;
}
}
}
};
void usage()
{
printf("usage: saslclient (options) host(:port) (user) (pass)\n");
printf("options: --proto=x, --authzid=x, --realm=x\n");
}
int main(int argc, char **argv)
{
QCoreApplication qapp(argc, argv);
QStringList args = qapp.arguments();
args.removeFirst();
QString proto = "qcatest";
QString authzid, realm;
bool no_authzid = false;
bool no_realm = false;
for(int n = 0; n < args.count(); ++n)
{
if(!args[n].startsWith("--"))
continue;
QString opt = args[n].mid(2);
QString var, val;
int at = opt.indexOf('=');
if(at != -1)
{
var = opt.mid(0, at);
val = opt.mid(at + 1);
}
else
var = opt;
if(var == "proto")
{
proto = val;
}
else if(var == "authzid")
{
if(val.isEmpty())
no_authzid = true;
else
authzid = val;
}
else if(var == "realm")
{
if(val.isEmpty())
no_realm = true;
else
realm = val;
}
args.removeAt(n);
--n;
}
if(args.count() < 1)
{
usage();
return 0;
}
QString host, user, pass;
int port = 8001;
QString hostinput = args[0];
if(args.count() >= 2)
user = args[1];
if(args.count() >= 3)
pass = args[2];
int at = hostinput.indexOf(':');
if(at != -1)
{
host = hostinput.mid(0, at);
port = hostinput.mid(at + 1).toInt();
}
else
host = hostinput;
{
printf("Error: SASL support not found.\n");
return 1;
}
ClientTest client(host, port, proto, authzid, realm, user, pass, no_authzid, no_realm);
QObject::connect(&client, SIGNAL(quit()), &qapp, SLOT(quit()));
QTimer::singleShot(0, &client, SLOT(start()));
qapp.exec();
return 0;
}
#include "saslclient.moc"