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 | |
42 | Private(TLSSocket *_q) |
43 | : QObject(_q) |
44 | , q(_q) |
45 | , sync(_q) |
46 | { |
47 | sock = new QTcpSocket(this); |
48 | connect(sender: sock, signal: &QTcpSocket::connected, context: this, slot: &TLSSocket::Private::sock_connected); |
49 | connect(sender: sock, signal: &QTcpSocket::readyRead, context: this, slot: &TLSSocket::Private::sock_readyRead); |
50 | connect(sender: sock, signal: &QTcpSocket::bytesWritten, context: this, slot: &TLSSocket::Private::sock_bytesWritten); |
51 | #if QT_VERSION >= QT_VERSION_CHECK(5, 15, 0) |
52 | connect(sender: sock, signal: &QTcpSocket::errorOccurred, context: this, slot: &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(sender: tls, signal: &QCA::TLS::handshaken, context: this, slot: &TLSSocket::Private::tls_handshaken); |
62 | connect(sender: tls, signal: &QCA::TLS::readyRead, context: this, slot: &TLSSocket::Private::tls_readyRead); |
63 | connect(sender: tls, signal: &QCA::TLS::readyReadOutgoing, context: this, slot: &TLSSocket::Private::tls_readyReadOutgoing); |
64 | connect(sender: tls, signal: &QCA::TLS::closed, context: this, slot: &TLSSocket::Private::tls_closed); |
65 | connect(sender: tls, signal: &QCA::TLS::error, context: this, slot: &TLSSocket::Private::tls_error); |
66 | tls->setTrustedCertificates(QCA::systemStore()); |
67 | encrypted = false; |
68 | error = false; |
69 | waiting = false; |
70 | done = false; |
71 | } |
72 | |
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: |
86 | void sock_connected() |
87 | { |
88 | // printf("sock connected\n"); |
89 | tls->startClient(host); |
90 | } |
91 | |
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(a: buf); |
98 | } |
99 | |
100 | void sock_bytesWritten(qint64 x) |
101 | { |
102 | Q_UNUSED(x); |
103 | // printf("sock bytes written: %d\n", (int)x); |
104 | } |
105 | |
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 | |
115 | void tls_handshaken() |
116 | { |
117 | // printf("tls handshaken\n"); |
118 | if (tls->peerIdentityResult() != QCA::TLS::Valid) { |
119 | printf(format: "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(a: writebuf); |
130 | writebuf.clear(); |
131 | } |
132 | } |
133 | if (waiting) |
134 | sync.conditionMet(); |
135 | } |
136 | |
137 | void tls_readyRead() |
138 | { |
139 | // printf("tls ready read\n"); |
140 | if (waiting) |
141 | sync.conditionMet(); |
142 | } |
143 | |
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(data: buf); |
150 | } |
151 | |
152 | void tls_closed() |
153 | { |
154 | // printf("tls closed\n"); |
155 | } |
156 | |
157 | void tls_error() |
158 | { |
159 | // printf("tls error\n"); |
160 | } |
161 | }; |
162 | |
163 | TLSSocket::TLSSocket(QObject *parent) |
164 | : QTcpSocket(parent) |
165 | { |
166 | d = new Private(this); |
167 | } |
168 | |
169 | TLSSocket::~TLSSocket() |
170 | { |
171 | delete d; |
172 | } |
173 | |
174 | void TLSSocket::connectToHostEncrypted(const QString &host, quint16 port) |
175 | { |
176 | d->host = host; |
177 | setOpenMode(QIODevice::ReadWrite); |
178 | d->sock->connectToHost(hostName: host, port); |
179 | } |
180 | |
181 | QCA::TLS *TLSSocket::tls() |
182 | { |
183 | return d->tls; |
184 | } |
185 | |
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 | |
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(a: size, b: (int)maxlen); |
204 | int newsize = size - readsize; |
205 | memcpy(dest: data, src: p, n: readsize); |
206 | memmove(dest: p, src: p + readsize, n: newsize); |
207 | d->readbuf.resize(size: newsize); |
208 | return readsize; |
209 | } |
210 | |
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(a: buf); |
217 | else |
218 | d->writebuf += buf; |
219 | return len; |
220 | } |
221 | |
222 | #include "tlssocket.moc" |
223 | |