source: psi/trunk/iris/example/conntest/conntest.cpp

Last change on this file was 2, checked in by dmik, 19 years ago

Imported original Psi 0.10 sources from Affinix

File size: 15.1 KB
Line 
1#include<qapplication.h>
2#include"bconsole.h"
3#include"qca.h"
4#include"xmpp.h"
5#include"base64.h"
6
7#include<stdio.h>
8
9#define ROOTCERT_PATH "/usr/local/share/psi/certs/rootcert.xml"
10
11QCA::Cert readCertXml(const QDomElement &e)
12{
13 QCA::Cert cert;
14 // there should be one child data tag
15 QDomElement data = e.elementsByTagName("data").item(0).toElement();
16 if(!data.isNull())
17 cert.fromDER(Base64::stringToArray(data.text()));
18 return cert;
19}
20
21QPtrList<QCA::Cert> getRootCerts(const QString &store)
22{
23 QPtrList<QCA::Cert> list;
24
25 // open the Psi rootcerts file
26 QFile f(store);
27 if(!f.open(IO_ReadOnly)) {
28 printf("unable to open %s\n", f.name().latin1());
29 return list;
30 }
31 QDomDocument doc;
32 doc.setContent(&f);
33 f.close();
34
35 QDomElement base = doc.documentElement();
36 if(base.tagName() != "store") {
37 printf("wrong format of %s\n", f.name().latin1());
38 return list;
39 }
40 QDomNodeList cl = base.elementsByTagName("certificate");
41 if(cl.count() == 0) {
42 printf("no certs found in %s\n", f.name().latin1());
43 return list;
44 }
45
46 int num = 0;
47 for(int n = 0; n < (int)cl.count(); ++n) {
48 QCA::Cert *cert = new QCA::Cert(readCertXml(cl.item(n).toElement()));
49 if(cert->isNull()) {
50 printf("error reading cert\n");
51 delete cert;
52 continue;
53 }
54
55 ++num;
56 list.append(cert);
57 }
58 printf("imported %d root certs\n", num);
59
60 return list;
61}
62
63static QString prompt(const QString &s)
64{
65 printf("* %s ", s.latin1());
66 fflush(stdout);
67 char line[256];
68 fgets(line, 255, stdin);
69 QString result = line;
70 if(result[result.length()-1] == '\n')
71 result.truncate(result.length()-1);
72 return result;
73}
74
75static void showCertInfo(const QCA::Cert &cert)
76{
77 fprintf(stderr, "-- Cert --\n");
78 fprintf(stderr, " CN: %s\n", cert.subject()["CN"].latin1());
79 fprintf(stderr, " Valid from: %s, until %s\n",
80 cert.notBefore().toString().latin1(),
81 cert.notAfter().toString().latin1());
82 fprintf(stderr, " PEM:\n%s\n", cert.toPEM().latin1());
83}
84
85static QString resultToString(int result)
86{
87 QString s;
88 switch(result) {
89 case QCA::TLS::NoCert:
90 s = QObject::tr("No certificate presented.");
91 break;
92 case QCA::TLS::Valid:
93 break;
94 case QCA::TLS::HostMismatch:
95 s = QObject::tr("Hostname mismatch.");
96 break;
97 case QCA::TLS::Rejected:
98 s = QObject::tr("Root CA rejects the specified purpose.");
99 break;
100 case QCA::TLS::Untrusted:
101 s = QObject::tr("Not trusted for the specified purpose.");
102 break;
103 case QCA::TLS::SignatureFailed:
104 s = QObject::tr("Invalid signature.");
105 break;
106 case QCA::TLS::InvalidCA:
107 s = QObject::tr("Invalid CA certificate.");
108 break;
109 case QCA::TLS::InvalidPurpose:
110 s = QObject::tr("Invalid certificate purpose.");
111 break;
112 case QCA::TLS::SelfSigned:
113 s = QObject::tr("Certificate is self-signed.");
114 break;
115 case QCA::TLS::Revoked:
116 s = QObject::tr("Certificate has been revoked.");
117 break;
118 case QCA::TLS::PathLengthExceeded:
119 s = QObject::tr("Maximum cert chain length exceeded.");
120 break;
121 case QCA::TLS::Expired:
122 s = QObject::tr("Certificate has expired.");
123 break;
124 case QCA::TLS::Unknown:
125 default:
126 s = QObject::tr("General validation error.");
127 break;
128 }
129 return s;
130}
131
132class App : public QObject
133{
134 Q_OBJECT
135public:
136 XMPP::AdvancedConnector *conn;
137 QCA::TLS *tls;
138 XMPP::QCATLSHandler *tlsHandler;
139 XMPP::ClientStream *stream;
140 BConsole *c;
141 XMPP::Jid jid;
142 QPtrList<QCA::Cert> rootCerts;
143
144 App(const XMPP::Jid &_jid, const XMPP::AdvancedConnector::Proxy &proxy, const QString &host, int port, bool opt_ssl, bool opt_probe)
145 :QObject(0)
146 {
147 c = 0;
148 jid = _jid;
149
150 // Connector
151 conn = new XMPP::AdvancedConnector;
152 conn->setProxy(proxy);
153 if(!host.isEmpty())
154 conn->setOptHostPort(host, port);
155 conn->setOptProbe(opt_probe);
156 conn->setOptSSL(opt_ssl);
157
158 // TLSHandler
159 tls = 0;
160 tlsHandler = 0;
161 rootCerts.setAutoDelete(true);
162 if(QCA::isSupported(QCA::CAP_TLS)) {
163 rootCerts = getRootCerts(ROOTCERT_PATH);
164 tls = new QCA::TLS;
165 tls->setCertificateStore(rootCerts);
166 tlsHandler = new XMPP::QCATLSHandler(tls);
167 connect(tlsHandler, SIGNAL(tlsHandshaken()), SLOT(tls_handshaken()));
168 }
169
170 // Stream
171 stream = new XMPP::ClientStream(conn, tlsHandler);
172 connect(stream, SIGNAL(connected()), SLOT(cs_connected()));
173 connect(stream, SIGNAL(securityLayerActivated(int)), SLOT(cs_securityLayerActivated()));
174 connect(stream, SIGNAL(needAuthParams(bool, bool, bool)), SLOT(cs_needAuthParams(bool, bool, bool)));
175 connect(stream, SIGNAL(authenticated()), SLOT(cs_authenticated()));
176 connect(stream, SIGNAL(connectionClosed()), SLOT(cs_connectionClosed()));
177 connect(stream, SIGNAL(readyRead()), SLOT(cs_readyRead()));
178 connect(stream, SIGNAL(stanzaWritten()), SLOT(cs_stanzaWritten()));
179 connect(stream, SIGNAL(warning(int)), SLOT(cs_warning(int)));
180 connect(stream, SIGNAL(error(int)), SLOT(cs_error(int)));
181
182 fprintf(stderr, "conntest: Connecting ...\n");
183 stream->setSSFRange(0, 256);
184 stream->connectToServer(jid);
185 }
186
187 ~App()
188 {
189 delete stream;
190 delete tls; // this destroys the TLSHandler also
191 delete conn;
192 delete c;
193 }
194
195signals:
196 void quit();
197
198private slots:
199 void tls_handshaken()
200 {
201 QCA::Cert cert = tls->peerCertificate();
202 int vr = tls->certificateValidityResult();
203
204 fprintf(stderr, "conntest: Successful TLS handshake.\n");
205 if(!cert.isNull())
206 showCertInfo(cert);
207 if(vr == QCA::TLS::Valid)
208 fprintf(stderr, "conntest: Valid certificate.\n");
209 else
210 fprintf(stderr, "conntest: Invalid certificate: %s\n", resultToString(vr).latin1());
211
212 tlsHandler->continueAfterHandshake();
213 }
214
215 void cs_connected()
216 {
217 fprintf(stderr, "conntest: Connected\n");
218 }
219
220 void cs_securityLayerActivated()
221 {
222 fprintf(stderr, "conntest: Security layer activated (%s)\n", tls->isHandshaken() ? "TLS": "SASL");
223 }
224
225 void cs_needAuthParams(bool user, bool pass, bool realm)
226 {
227 fprintf(stderr, "conntest: need auth params -");
228 if(user)
229 fprintf(stderr, " (user)");
230 if(pass)
231 fprintf(stderr, " (pass)");
232 if(realm)
233 fprintf(stderr, " (realm)");
234 fprintf(stderr, "\n");
235
236 if(user)
237 stream->setUsername(jid.node());
238 if(pass)
239 stream->setPassword(prompt("Password (not hidden!) :"));
240 stream->continueAfterParams();
241 }
242
243 void cs_authenticated()
244 {
245 fprintf(stderr, "conntest: <<< Authenticated >>>\n");
246
247 // console
248 c = new BConsole;
249 connect(c, SIGNAL(connectionClosed()), SLOT(con_connectionClosed()));
250 connect(c, SIGNAL(readyRead()), SLOT(con_readyRead()));
251 }
252
253 void cs_connectionClosed()
254 {
255 fprintf(stderr, "conntest: Disconnected by peer\n");
256 quit();
257 }
258
259 void cs_readyRead()
260 {
261 for(XMPP::Stanza s; !(s = stream->read()).isNull();) {
262 QString str = s.toString();
263 printf("%s\n", str.local8Bit().data());
264 }
265 }
266
267 void cs_stanzaWritten()
268 {
269 fprintf(stderr, "conntest: Stanza written\n");
270 }
271
272 void cs_warning(int warn)
273 {
274 if(warn == XMPP::ClientStream::WarnOldVersion) {
275 fprintf(stderr, "conntest: Warning: pre-1.0 protocol server\n");
276 }
277 else if(warn == XMPP::ClientStream::WarnNoTLS) {
278 fprintf(stderr, "conntest: Warning: TLS not available!\n");
279 }
280 stream->continueAfterWarning();
281 }
282
283 void cs_error(int err)
284 {
285 if(err == XMPP::ClientStream::ErrParse) {
286 fprintf(stderr, "conntest: XML parsing error\n");
287 }
288 else if(err == XMPP::ClientStream::ErrProtocol) {
289 fprintf(stderr, "conntest: XMPP protocol error\n");
290 }
291 else if(err == XMPP::ClientStream::ErrStream) {
292 int x = stream->errorCondition();
293 QString s;
294 if(x == XMPP::Stream::GenericStreamError)
295 s = "generic stream error";
296 else if(x == XMPP::ClientStream::Conflict)
297 s = "conflict (remote login replacing this one)";
298 else if(x == XMPP::ClientStream::ConnectionTimeout)
299 s = "timed out from inactivity";
300 else if(x == XMPP::ClientStream::InternalServerError)
301 s = "internal server error";
302 else if(x == XMPP::ClientStream::InvalidXml)
303 s = "invalid XML";
304 else if(x == XMPP::ClientStream::PolicyViolation)
305 s = "policy violation. go to jail!";
306 else if(x == XMPP::ClientStream::ResourceConstraint)
307 s = "server out of resources";
308 else if(x == XMPP::ClientStream::SystemShutdown)
309 s = "system is shutting down NOW";
310 fprintf(stderr, "conntest: XMPP stream error: %s\n", s.latin1());
311 }
312 else if(err == XMPP::ClientStream::ErrConnection) {
313 int x = conn->errorCode();
314 QString s;
315 if(x == XMPP::AdvancedConnector::ErrConnectionRefused)
316 s = "unable to connect to server";
317 else if(x == XMPP::AdvancedConnector::ErrHostNotFound)
318 s = "host not found";
319 else if(x == XMPP::AdvancedConnector::ErrProxyConnect)
320 s = "proxy connect";
321 else if(x == XMPP::AdvancedConnector::ErrProxyNeg)
322 s = "proxy negotiating";
323 else if(x == XMPP::AdvancedConnector::ErrProxyAuth)
324 s = "proxy authorization";
325 else if(x == XMPP::AdvancedConnector::ErrStream)
326 s = "stream error";
327 fprintf(stderr, "conntest: connection error: %s\n", s.latin1());
328 }
329 else if(err == XMPP::ClientStream::ErrNeg) {
330 int x = stream->errorCondition();
331 QString s;
332 if(x == XMPP::ClientStream::HostGone)
333 s = "host no longer hosted";
334 else if(x == XMPP::ClientStream::HostUnknown)
335 s = "host unknown";
336 else if(x == XMPP::ClientStream::RemoteConnectionFailed)
337 s = "a required remote connection failed";
338 else if(x == XMPP::ClientStream::SeeOtherHost)
339 s = QString("see other host: [%1]").arg(stream->errorText());
340 else if(x == XMPP::ClientStream::UnsupportedVersion)
341 s = "server does not support proper xmpp version";
342 fprintf(stderr, "conntest: stream negotiation error: %s\n", s.latin1());
343 }
344 else if(err == XMPP::ClientStream::ErrTLS) {
345 int x = stream->errorCondition();
346 QString s;
347 if(x == XMPP::ClientStream::TLSStart)
348 s = "server rejected STARTTLS";
349 else if(x == XMPP::ClientStream::TLSFail) {
350 int t = tlsHandler->tlsError();
351 if(t == QCA::TLS::ErrHandshake)
352 s = "TLS handshake error";
353 else
354 s = "broken security layer (TLS)";
355 }
356 fprintf(stderr, "conntest: %s\n", s.latin1());
357 }
358 else if(err == XMPP::ClientStream::ErrAuth) {
359 int x = stream->errorCondition();
360 QString s;
361 if(x == XMPP::ClientStream::GenericAuthError)
362 s = "unable to login";
363 else if(x == XMPP::ClientStream::NoMech)
364 s = "no appropriate auth mechanism available for given security settings";
365 else if(x == XMPP::ClientStream::BadProto)
366 s = "bad server response";
367 else if(x == XMPP::ClientStream::BadServ)
368 s = "server failed mutual authentication";
369 else if(x == XMPP::ClientStream::EncryptionRequired)
370 s = "encryption required for chosen SASL mechanism";
371 else if(x == XMPP::ClientStream::InvalidAuthzid)
372 s = "invalid authzid";
373 else if(x == XMPP::ClientStream::InvalidMech)
374 s = "invalid SASL mechanism";
375 else if(x == XMPP::ClientStream::InvalidRealm)
376 s = "invalid realm";
377 else if(x == XMPP::ClientStream::MechTooWeak)
378 s = "SASL mechanism too weak for authzid";
379 else if(x == XMPP::ClientStream::NotAuthorized)
380 s = "not authorized";
381 else if(x == XMPP::ClientStream::TemporaryAuthFailure)
382 s = "temporary auth failure";
383 fprintf(stderr, "conntest: auth error: %s\n", s.latin1());
384 }
385 else if(err == XMPP::ClientStream::ErrSecurityLayer)
386 fprintf(stderr, "conntest: broken security layer (SASL)\n");
387 quit();
388 }
389
390 void con_connectionClosed()
391 {
392 fprintf(stderr, "conntest: Closing.\n");
393 stream->close();
394 quit();
395 }
396
397 void con_readyRead()
398 {
399 QByteArray a = c->read();
400 QCString cs;
401 cs.resize(a.size()+1);
402 memcpy(cs.data(), a.data(), a.size());
403 QString s = QString::fromLocal8Bit(cs);
404 stream->writeDirect(s);
405 }
406};
407
408#include"conntest.moc"
409
410int main(int argc, char **argv)
411{
412 QApplication app(argc, argv, false);
413
414 if(argc < 2) {
415 printf("usage: conntest [options] [jid]\n");
416 printf(" Options:\n");
417 printf(" --host=host:port\n");
418 printf(" --sslhost=host:port\n");
419 printf(" --probe\n");
420 printf(" --proxy=[https|poll|socks],host:port,url\n");
421 printf(" --proxy-auth=user,pass\n");
422 printf("\n");
423 return 0;
424 }
425
426 bool have_tls = QCA::isSupported(QCA::CAP_TLS);
427
428 XMPP::Jid jid;
429 XMPP::AdvancedConnector::Proxy proxy;
430 QString host;
431 int port = 0;
432 bool opt_ssl = false;
433 bool opt_probe = false;
434
435 for(int at = 1; at < argc; ++at) {
436 QString s = argv[at];
437
438 // is it an option?
439 if(s.left(2) == "--") {
440 QString name;
441 QStringList args;
442 int n = s.find('=', 2);
443 if(n != -1) {
444 name = s.mid(2, n-2);
445 ++n;
446 args = QStringList::split(',', s.mid(n), true);
447 }
448 else {
449 name = s.mid(2);
450 args.clear();
451 }
452
453 // eat the arg
454 --argc;
455 for(int x = at; x < argc; ++x)
456 argv[x] = argv[x+1];
457 --at; // don't advance
458
459 // process option
460 if(name == "proxy") {
461 QString proxy_host;
462 int proxy_port = 0;
463 QString type = args[0];
464 QString s = args[1];
465 int n = s.find(':');
466 if(n == -1) {
467 if(type != "poll") {
468 printf("Invalid host:port for proxy\n");
469 return 0;
470 }
471 }
472 else {
473 proxy_host = s.mid(0, n);
474 ++n;
475 proxy_port = s.mid(n).toInt();
476 }
477
478 if(type == "https") {
479 proxy.setHttpConnect(proxy_host, proxy_port);
480 }
481 else if(type == "poll") {
482 if(args.count() < 3) {
483 printf("poll needs more args\n");
484 return 0;
485 }
486 QString proxy_url = args[2];
487 proxy.setHttpPoll(proxy_host, proxy_port, proxy_url);
488 }
489 else if(type == "socks") {
490 proxy.setSocks(proxy_host, proxy_port);
491 }
492 else {
493 printf("No such proxy type '%s'\n", type.latin1());
494 return 0;
495 }
496 }
497 else if(name == "proxy-auth") {
498 proxy.setUserPass(args[0], args[1]);
499 }
500 else if(name == "host") {
501 QString s = args[0];
502 int n = s.find(':');
503 if(n == -1) {
504 printf("Invalid host:port for host\n");
505 return 0;
506 }
507 host = s.mid(0, n);
508 ++n;
509 port = s.mid(n).toInt();
510 }
511 else if(name == "sslhost") {
512 QString s = args[0];
513 int n = s.find(':');
514 if(n == -1) {
515 printf("Invalid host:port for host\n");
516 return 0;
517 }
518 host = s.mid(0, n);
519 ++n;
520 port = s.mid(n).toInt();
521 opt_ssl = true;
522 }
523 else if(name == "probe") {
524 opt_probe = true;
525 }
526 else {
527 printf("Unknown option '%s'\n", name.latin1());
528 return 0;
529 }
530 }
531 }
532
533 if(argc < 2) {
534 printf("No host specified!\n");
535 return 0;
536 }
537 jid = argv[1];
538
539 if((opt_ssl || opt_probe) && !have_tls) {
540 printf("TLS not supported, so the sslhost and probe options are not allowed.\n");
541 return 0;
542 }
543
544 printf("JID: %s\n", jid.full().latin1());
545 if(proxy.type() != XMPP::AdvancedConnector::Proxy::None) {
546 printf("Proxy: ");
547 if(proxy.type() == XMPP::AdvancedConnector::Proxy::HttpConnect)
548 printf("HttpConnect (%s:%d)", proxy.host().latin1(), proxy.port());
549 else if(proxy.type() == XMPP::AdvancedConnector::Proxy::HttpPoll) {
550 printf("HttpPoll {%s}", proxy.url().latin1());
551 if(!proxy.host().isEmpty()) {
552 printf(" (%s:%d)", proxy.host().latin1(), proxy.port());
553 }
554 }
555 else if(proxy.type() == XMPP::AdvancedConnector::Proxy::Socks)
556 printf("Socks (%s:%d)", proxy.host().latin1(), proxy.port());
557 printf("\n");
558 }
559 if(proxy.type() != XMPP::AdvancedConnector::Proxy::HttpPoll) {
560 if(!host.isEmpty()) {
561 printf("Host: %s:%d", host.latin1(), port);
562 if(opt_ssl)
563 printf(" (ssl)");
564 printf("\n");
565 }
566 else {
567 if(opt_probe)
568 printf("Probe active\n");
569 }
570 }
571 printf("----------\n");
572
573 App *a = new App(jid, proxy, host, port, opt_ssl, opt_probe);
574 QObject::connect(a, SIGNAL(quit()), &app, SLOT(quit()));
575 app.exec();
576 delete a;
577
578 return 0;
579}
Note: See TracBrowser for help on using the repository browser.