1 /*
2  Copyright (C) 2007 Justin Karneges <justin@affinix.com>
3 
4  Permission is hereby granted, free of charge, to any person obtaining a copy
5  of this software and associated documentation files (the "Software"), to deal
6  in the Software without restriction, including without limitation the rights
7  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8  copies of the Software, and to permit persons to whom the Software is
9  furnished to do so, subject to the following conditions:
10 
11  The above copyright notice and this permission notice shall be included in
12  all copies or substantial portions of the Software.
13 
14  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
17  AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
18  AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
19  CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
20 */
21 
22 #include "tlssocket.h"
23 
24 #ifdef QT_STATICPLUGIN
25 #include "import_plugins.h"
26 #endif
27 
28 class TLSSocket::Private : public QObject
29 {
30     Q_OBJECT
31 public:
32     TLSSocket *       q;
33     QTcpSocket *      sock;
34     QCA::TLS *        tls;
35     QString           host;
36     bool              encrypted;
37     bool              error, done;
38     QByteArray        readbuf, writebuf;
39     QCA::Synchronizer sync;
40     bool              waiting;
41 
Private(TLSSocket * _q)42     Private(TLSSocket *_q)
43         : QObject(_q)
44         , q(_q)
45         , sync(_q)
46     {
47         sock = new QTcpSocket(this);
48         connect(sock, &QTcpSocket::connected, this, &TLSSocket::Private::sock_connected);
49         connect(sock, &QTcpSocket::readyRead, this, &TLSSocket::Private::sock_readyRead);
50         connect(sock, &QTcpSocket::bytesWritten, this, &TLSSocket::Private::sock_bytesWritten);
51 #if QT_VERSION >= QT_VERSION_CHECK(5, 15, 0)
52         connect(sock, &QTcpSocket::errorOccurred, this, &TLSSocket::Private::sock_error);
53 #else
54         connect(sock,
55                 QOverload<QAbstractSocket::SocketError>::of(&QTcpSocket::error),
56                 this,
57                 &TLSSocket::Private::sock_error);
58 #endif
59 
60         tls = new QCA::TLS(this);
61         connect(tls, &QCA::TLS::handshaken, this, &TLSSocket::Private::tls_handshaken);
62         connect(tls, &QCA::TLS::readyRead, this, &TLSSocket::Private::tls_readyRead);
63         connect(tls, &QCA::TLS::readyReadOutgoing, this, &TLSSocket::Private::tls_readyReadOutgoing);
64         connect(tls, &QCA::TLS::closed, this, &TLSSocket::Private::tls_closed);
65         connect(tls, &QCA::TLS::error, this, &TLSSocket::Private::tls_error);
66         tls->setTrustedCertificates(QCA::systemStore());
67         encrypted = false;
68         error     = false;
69         waiting   = false;
70         done      = false;
71     }
72 
waitForReadyRead(int msecs)73     bool waitForReadyRead(int msecs)
74     {
75         waiting = true;
76         bool ok = sync.waitForCondition(msecs);
77         // while(1)
78         //	QCoreApplication::instance()->processEvents();
79         waiting = false;
80         if (error || done)
81             return false;
82         return ok;
83     }
84 
85 private Q_SLOTS:
sock_connected()86     void sock_connected()
87     {
88         // printf("sock connected\n");
89         tls->startClient(host);
90     }
91 
sock_readyRead()92     void sock_readyRead()
93     {
94         // printf("sock ready read\n");
95         QByteArray buf = sock->readAll();
96         // printf("%d bytes\n", buf.size());
97         tls->writeIncoming(buf);
98     }
99 
sock_bytesWritten(qint64 x)100     void sock_bytesWritten(qint64 x)
101     {
102         Q_UNUSED(x);
103         // printf("sock bytes written: %d\n", (int)x);
104     }
105 
sock_error(QAbstractSocket::SocketError x)106     void sock_error(QAbstractSocket::SocketError x)
107     {
108         // printf("sock error: %d\n", x);
109         Q_UNUSED(x);
110         done = true;
111         if (waiting)
112             sync.conditionMet();
113     }
114 
tls_handshaken()115     void tls_handshaken()
116     {
117         // printf("tls handshaken\n");
118         if (tls->peerIdentityResult() != QCA::TLS::Valid) {
119             printf("not valid\n");
120             sock->abort();
121             tls->reset();
122             error = true;
123         } else {
124             // printf("valid\n");
125             encrypted = true;
126             // printf("%d bytes in writebuf\n", writebuf.size());
127             if (!writebuf.isEmpty()) {
128                 // printf("[%s]\n", writebuf.data());
129                 tls->write(writebuf);
130                 writebuf.clear();
131             }
132         }
133         if (waiting)
134             sync.conditionMet();
135     }
136 
tls_readyRead()137     void tls_readyRead()
138     {
139         // printf("tls ready read\n");
140         if (waiting)
141             sync.conditionMet();
142     }
143 
tls_readyReadOutgoing()144     void tls_readyReadOutgoing()
145     {
146         // printf("tls ready read outgoing\n");
147         QByteArray buf = tls->readOutgoing();
148         // printf("%d bytes\n", buf.size());
149         sock->write(buf);
150     }
151 
tls_closed()152     void tls_closed()
153     {
154         // printf("tls closed\n");
155     }
156 
tls_error()157     void tls_error()
158     {
159         // printf("tls error\n");
160     }
161 };
162 
TLSSocket(QObject * parent)163 TLSSocket::TLSSocket(QObject *parent)
164     : QTcpSocket(parent)
165 {
166     d = new Private(this);
167 }
168 
~TLSSocket()169 TLSSocket::~TLSSocket()
170 {
171     delete d;
172 }
173 
connectToHostEncrypted(const QString & host,quint16 port)174 void TLSSocket::connectToHostEncrypted(const QString &host, quint16 port)
175 {
176     d->host = host;
177     setOpenMode(QIODevice::ReadWrite);
178     d->sock->connectToHost(host, port);
179 }
180 
tls()181 QCA::TLS *TLSSocket::tls()
182 {
183     return d->tls;
184 }
185 
waitForReadyRead(int msecs)186 bool TLSSocket::waitForReadyRead(int msecs)
187 {
188     /*if(d->readbuf.isEmpty())
189         return false;
190 
191     if(d->tls->bytesAvailable() == 0)
192         return false;*/
193 
194     return d->waitForReadyRead(msecs);
195 }
196 
readData(char * data,qint64 maxlen)197 qint64 TLSSocket::readData(char *data, qint64 maxlen)
198 {
199     if (!d->error)
200         d->readbuf += d->tls->read();
201     unsigned char *p        = (unsigned char *)d->readbuf.data();
202     int            size     = d->readbuf.size();
203     int            readsize = qMin(size, (int)maxlen);
204     int            newsize  = size - readsize;
205     memcpy(data, p, readsize);
206     memmove(p, p + readsize, newsize);
207     d->readbuf.resize(newsize);
208     return readsize;
209 }
210 
writeData(const char * data,qint64 len)211 qint64 TLSSocket::writeData(const char *data, qint64 len)
212 {
213     // printf("write %d bytes\n", (int)len);
214     QByteArray buf(data, len);
215     if (d->encrypted)
216         d->tls->write(buf);
217     else
218         d->writebuf += buf;
219     return len;
220 }
221 
222 #include "tlssocket.moc"
223