1
#include <qapplication.h>
8
#define ROOTCERT_PATH "/usr/local/share/psi/certs/rootcert.xml"
10
QCA::Cert readCertXml(const QDomElement &e)
13
// there should be one child data tag
14
QDomElement data = e.elementsByTagName("data").item(0).toElement();
16
cert.fromDER(Base64::stringToArray(data.text()));
20
QPtrList<QCA::Cert> getRootCerts(const QString &store)
22
QPtrList<QCA::Cert> list;
24
// open the Psi rootcerts file
26
if(!f.open(IO_ReadOnly)) {
27
printf("unable to open %s\n", f.name().latin1());
34
QDomElement base = doc.documentElement();
35
if(base.tagName() != "store") {
36
printf("wrong format of %s\n", f.name().latin1());
39
QDomNodeList cl = base.elementsByTagName("certificate");
41
printf("no certs found in %s\n", f.name().latin1());
46
for(int n = 0; n < (int)cl.count(); ++n) {
47
QCA::Cert *cert = new QCA::Cert(readCertXml(cl.item(n).toElement()));
49
printf("error reading cert\n");
57
printf("imported %d root certs\n", num);
62
static QString prompt(const QString &s)
64
printf("* %s ", s.latin1());
67
fgets(line, 255, stdin);
68
QString result = line;
69
if(result[result.length()-1] == '\n')
70
result.truncate(result.length()-1);
74
static void showCertInfo(const QCA::Cert &cert)
76
fprintf(stderr, "-- Cert --\n");
77
fprintf(stderr, " CN: %s\n", cert.subject()["CN"].latin1());
78
fprintf(stderr, " Valid from: %s, until %s\n",
79
cert.notBefore().toString().latin1(),
80
cert.notAfter().toString().latin1());
81
fprintf(stderr, " PEM:\n%s\n", cert.toPEM().latin1());
84
static QString resultToString(int result)
88
case QCA::TLS::NoCert:
89
s = QObject::tr("No certificate presented.");
93
case QCA::TLS::HostMismatch:
94
s = QObject::tr("Hostname mismatch.");
96
case QCA::TLS::Rejected:
97
s = QObject::tr("Root CA rejects the specified purpose.");
99
case QCA::TLS::Untrusted:
100
s = QObject::tr("Not trusted for the specified purpose.");
102
case QCA::TLS::SignatureFailed:
103
s = QObject::tr("Invalid signature.");
105
case QCA::TLS::InvalidCA:
106
s = QObject::tr("Invalid CA certificate.");
108
case QCA::TLS::InvalidPurpose:
109
s = QObject::tr("Invalid certificate purpose.");
111
case QCA::TLS::SelfSigned:
112
s = QObject::tr("Certificate is self-signed.");
114
case QCA::TLS::Revoked:
115
s = QObject::tr("Certificate has been revoked.");
117
case QCA::TLS::PathLengthExceeded:
118
s = QObject::tr("Maximum cert chain length exceeded.");
120
case QCA::TLS::Expired:
121
s = QObject::tr("Certificate has expired.");
123
case QCA::TLS::Unknown:
125
s = QObject::tr("General validation error.");
131
class App : public QObject
135
XMPP::AdvancedConnector *conn;
137
XMPP::QCATLSHandler *tlsHandler;
138
XMPP::ClientStream *stream;
141
QPtrList<QCA::Cert> rootCerts;
143
App(const XMPP::Jid &_jid, const XMPP::AdvancedConnector::Proxy &proxy, const QString &host, int port, bool opt_ssl, bool opt_probe)
150
conn = new XMPP::AdvancedConnector;
151
conn->setProxy(proxy);
153
conn->setOptHostPort(host, port);
154
conn->setOptProbe(opt_probe);
155
conn->setOptSSL(opt_ssl);
160
rootCerts.setAutoDelete(true);
161
if(QCA::isSupported(QCA::CAP_TLS)) {
162
rootCerts = getRootCerts(ROOTCERT_PATH);
164
tls->setCertificateStore(rootCerts);
165
tlsHandler = new XMPP::QCATLSHandler(tls);
166
connect(tlsHandler, SIGNAL(tlsHandshaken()), SLOT(tls_handshaken()));
170
stream = new XMPP::ClientStream(conn, tlsHandler);
171
connect(stream, SIGNAL(connected()), SLOT(cs_connected()));
172
connect(stream, SIGNAL(securityLayerActivated(int)), SLOT(cs_securityLayerActivated()));
173
connect(stream, SIGNAL(needAuthParams(bool, bool, bool)), SLOT(cs_needAuthParams(bool, bool, bool)));
174
connect(stream, SIGNAL(authenticated()), SLOT(cs_authenticated()));
175
connect(stream, SIGNAL(connectionClosed()), SLOT(cs_connectionClosed()));
176
connect(stream, SIGNAL(readyRead()), SLOT(cs_readyRead()));
177
connect(stream, SIGNAL(stanzaWritten()), SLOT(cs_stanzaWritten()));
178
connect(stream, SIGNAL(warning(int)), SLOT(cs_warning(int)));
179
connect(stream, SIGNAL(error(int)), SLOT(cs_error(int)));
181
fprintf(stderr, "conntest: Connecting ...\n");
182
stream->setSSFRange(0, 256);
183
stream->connectToServer(jid);
189
delete tls; // this destroys the TLSHandler also
198
void tls_handshaken()
200
QCA::Cert cert = tls->peerCertificate();
201
int vr = tls->certificateValidityResult();
203
fprintf(stderr, "conntest: Successful TLS handshake.\n");
206
if(vr == QCA::TLS::Valid)
207
fprintf(stderr, "conntest: Valid certificate.\n");
209
fprintf(stderr, "conntest: Invalid certificate: %s\n", resultToString(vr).latin1());
211
tlsHandler->continueAfterHandshake();
216
fprintf(stderr, "conntest: Connected\n");
219
void cs_securityLayerActivated()
221
fprintf(stderr, "conntest: Security layer activated (%s)\n", tls->isHandshaken() ? "TLS": "SASL");
224
void cs_needAuthParams(bool user, bool pass, bool realm)
226
fprintf(stderr, "conntest: need auth params -");
228
fprintf(stderr, " (user)");
230
fprintf(stderr, " (pass)");
232
fprintf(stderr, " (realm)");
233
fprintf(stderr, "\n");
236
stream->setUsername(jid.node());
238
stream->setPassword(prompt("Password (not hidden!) :"));
239
stream->continueAfterParams();
242
void cs_authenticated()
244
fprintf(stderr, "conntest: <<< Authenticated >>>\n");
248
connect(c, SIGNAL(connectionClosed()), SLOT(con_connectionClosed()));
249
connect(c, SIGNAL(readyRead()), SLOT(con_readyRead()));
252
void cs_connectionClosed()
254
fprintf(stderr, "conntest: Disconnected by peer\n");
260
for(XMPP::Stanza s; !(s = stream->read()).isNull();) {
261
QString str = s.toString();
262
printf("%s\n", str.local8Bit().data());
266
void cs_stanzaWritten()
268
fprintf(stderr, "conntest: Stanza written\n");
271
void cs_warning(int warn)
273
if(warn == XMPP::ClientStream::WarnOldVersion) {
274
fprintf(stderr, "conntest: Warning: pre-1.0 protocol server\n");
276
else if(warn == XMPP::ClientStream::WarnNoTLS) {
277
fprintf(stderr, "conntest: Warning: TLS not available!\n");
279
stream->continueAfterWarning();
282
void cs_error(int err)
284
if(err == XMPP::ClientStream::ErrParse) {
285
fprintf(stderr, "conntest: XML parsing error\n");
287
else if(err == XMPP::ClientStream::ErrProtocol) {
288
fprintf(stderr, "conntest: XMPP protocol error\n");
290
else if(err == XMPP::ClientStream::ErrStream) {
291
int x = stream->errorCondition();
293
if(x == XMPP::Stream::GenericStreamError)
294
s = "generic stream error";
295
else if(x == XMPP::ClientStream::Conflict)
296
s = "conflict (remote login replacing this one)";
297
else if(x == XMPP::ClientStream::ConnectionTimeout)
298
s = "timed out from inactivity";
299
else if(x == XMPP::ClientStream::InternalServerError)
300
s = "internal server error";
301
else if(x == XMPP::ClientStream::InvalidXml)
303
else if(x == XMPP::ClientStream::PolicyViolation)
304
s = "policy violation. go to jail!";
305
else if(x == XMPP::ClientStream::ResourceConstraint)
306
s = "server out of resources";
307
else if(x == XMPP::ClientStream::SystemShutdown)
308
s = "system is shutting down NOW";
309
fprintf(stderr, "conntest: XMPP stream error: %s\n", s.latin1());
311
else if(err == XMPP::ClientStream::ErrConnection) {
312
int x = conn->errorCode();
314
if(x == XMPP::AdvancedConnector::ErrConnectionRefused)
315
s = "unable to connect to server";
316
else if(x == XMPP::AdvancedConnector::ErrHostNotFound)
317
s = "host not found";
318
else if(x == XMPP::AdvancedConnector::ErrProxyConnect)
320
else if(x == XMPP::AdvancedConnector::ErrProxyNeg)
321
s = "proxy negotiating";
322
else if(x == XMPP::AdvancedConnector::ErrProxyAuth)
323
s = "proxy authorization";
324
else if(x == XMPP::AdvancedConnector::ErrStream)
326
fprintf(stderr, "conntest: connection error: %s\n", s.latin1());
328
else if(err == XMPP::ClientStream::ErrNeg) {
329
int x = stream->errorCondition();
331
if(x == XMPP::ClientStream::HostGone)
332
s = "host no longer hosted";
333
else if(x == XMPP::ClientStream::HostUnknown)
335
else if(x == XMPP::ClientStream::RemoteConnectionFailed)
336
s = "a required remote connection failed";
337
else if(x == XMPP::ClientStream::SeeOtherHost)
338
s = QString("see other host: [%1]").arg(stream->errorText());
339
else if(x == XMPP::ClientStream::UnsupportedVersion)
340
s = "server does not support proper xmpp version";
341
fprintf(stderr, "conntest: stream negotiation error: %s\n", s.latin1());
343
else if(err == XMPP::ClientStream::ErrTLS) {
344
int x = stream->errorCondition();
346
if(x == XMPP::ClientStream::TLSStart)
347
s = "server rejected STARTTLS";
348
else if(x == XMPP::ClientStream::TLSFail) {
349
int t = tlsHandler->tlsError();
350
if(t == QCA::TLS::ErrHandshake)
351
s = "TLS handshake error";
353
s = "broken security layer (TLS)";
355
fprintf(stderr, "conntest: %s\n", s.latin1());
357
else if(err == XMPP::ClientStream::ErrAuth) {
358
int x = stream->errorCondition();
360
if(x == XMPP::ClientStream::GenericAuthError)
361
s = "unable to login";
362
else if(x == XMPP::ClientStream::NoMech)
363
s = "no appropriate auth mechanism available for given security settings";
364
else if(x == XMPP::ClientStream::BadProto)
365
s = "bad server response";
366
else if(x == XMPP::ClientStream::BadServ)
367
s = "server failed mutual authentication";
368
else if(x == XMPP::ClientStream::EncryptionRequired)
369
s = "encryption required for chosen SASL mechanism";
370
else if(x == XMPP::ClientStream::InvalidAuthzid)
371
s = "invalid authzid";
372
else if(x == XMPP::ClientStream::InvalidMech)
373
s = "invalid SASL mechanism";
374
else if(x == XMPP::ClientStream::InvalidRealm)
376
else if(x == XMPP::ClientStream::MechTooWeak)
377
s = "SASL mechanism too weak for authzid";
378
else if(x == XMPP::ClientStream::NotAuthorized)
379
s = "not authorized";
380
else if(x == XMPP::ClientStream::TemporaryAuthFailure)
381
s = "temporary auth failure";
382
fprintf(stderr, "conntest: auth error: %s\n", s.latin1());
384
else if(err == XMPP::ClientStream::ErrSecurityLayer)
385
fprintf(stderr, "conntest: broken security layer (SASL)\n");
389
void con_connectionClosed()
391
fprintf(stderr, "conntest: Closing.\n");
398
QByteArray a = c->read();
400
cs.resize(a.size()+1);
401
memcpy(cs.data(), a.data(), a.size());
402
QString s = QString::fromLocal8Bit(cs);
403
stream->writeDirect(s);
407
#include "conntest.moc"
409
int main(int argc, char **argv)
411
QApplication app(argc, argv, false);
414
printf("usage: conntest [options] [jid]\n");
415
printf(" Options:\n");
416
printf(" --host=host:port\n");
417
printf(" --sslhost=host:port\n");
418
printf(" --probe\n");
419
printf(" --proxy=[https|poll|socks],host:port,url\n");
420
printf(" --proxy-auth=user,pass\n");
425
bool have_tls = QCA::isSupported(QCA::CAP_TLS);
428
XMPP::AdvancedConnector::Proxy proxy;
431
bool opt_ssl = false;
432
bool opt_probe = false;
434
for(int at = 1; at < argc; ++at) {
435
QString s = argv[at];
438
if(s.left(2) == "--") {
441
int n = s.find('=', 2);
443
name = s.mid(2, n-2);
445
args = QStringList::split(',', s.mid(n), true);
454
for(int x = at; x < argc; ++x)
456
--at; // don't advance
459
if(name == "proxy") {
462
QString type = args[0];
467
printf("Invalid host:port for proxy\n");
472
proxy_host = s.mid(0, n);
474
proxy_port = s.mid(n).toInt();
477
if(type == "https") {
478
proxy.setHttpConnect(proxy_host, proxy_port);
480
else if(type == "poll") {
481
if(args.count() < 3) {
482
printf("poll needs more args\n");
485
QString proxy_url = args[2];
486
proxy.setHttpPoll(proxy_host, proxy_port, proxy_url);
488
else if(type == "socks") {
489
proxy.setSocks(proxy_host, proxy_port);
492
printf("No such proxy type '%s'\n", type.latin1());
496
else if(name == "proxy-auth") {
497
proxy.setUserPass(args[0], args[1]);
499
else if(name == "host") {
503
printf("Invalid host:port for host\n");
508
port = s.mid(n).toInt();
510
else if(name == "sslhost") {
514
printf("Invalid host:port for host\n");
519
port = s.mid(n).toInt();
522
else if(name == "probe") {
526
printf("Unknown option '%s'\n", name.latin1());
533
printf("No host specified!\n");
538
if((opt_ssl || opt_probe) && !have_tls) {
539
printf("TLS not supported, so the sslhost and probe options are not allowed.\n");
543
printf("JID: %s\n", jid.full().latin1());
544
if(proxy.type() != XMPP::AdvancedConnector::Proxy::None) {
546
if(proxy.type() == XMPP::AdvancedConnector::Proxy::HttpConnect)
547
printf("HttpConnect (%s:%d)", proxy.host().latin1(), proxy.port());
548
else if(proxy.type() == XMPP::AdvancedConnector::Proxy::HttpPoll) {
549
printf("HttpPoll {%s}", proxy.url().latin1());
550
if(!proxy.host().isEmpty()) {
551
printf(" (%s:%d)", proxy.host().latin1(), proxy.port());
554
else if(proxy.type() == XMPP::AdvancedConnector::Proxy::Socks)
555
printf("Socks (%s:%d)", proxy.host().latin1(), proxy.port());
558
if(proxy.type() != XMPP::AdvancedConnector::Proxy::HttpPoll) {
559
if(!host.isEmpty()) {
560
printf("Host: %s:%d", host.latin1(), port);
567
printf("Probe active\n");
570
printf("----------\n");
572
App *a = new App(jid, proxy, host, port, opt_ssl, opt_probe);
573
QObject::connect(a, SIGNAL(quit()), &app, SLOT(quit()));