blob: 5bf526d505ce9b0bf32fcb838e161484d78c574c [file] [log] [blame]
Emeric Vigier2f625822012-08-06 11:09:52 -04001// Copyright (C) 1999-2005 Open Source Telecom Corporation.
2// Copyright (C) 2006-2010 David Sugar, Tycho Softworks.
3//
4// This program is free software; you can redistribute it and/or modify
5// it under the terms of the GNU General Public License as published by
6// the Free Software Foundation; either version 2 of the License, or
7// (at your option) any later version.
8//
9// This program is distributed in the hope that it will be useful,
10// but WITHOUT ANY WARRANTY; without even the implied warranty of
11// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12// GNU General Public License for more details.
13//
14// You should have received a copy of the GNU General Public License
15// along with this program; if not, write to the Free Software
16// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
17//
18// As a special exception, you may use this file as part of a free software
19// library without restriction. Specifically, if other files instantiate
20// templates or use macros or inline functions from this file, or you compile
21// this file and link it with other files to produce an executable, this
22// file does not by itself cause the resulting executable to be covered by
23// the GNU General Public License. This exception does not however
24// invalidate any other reasons why the executable file might be covered by
25// the GNU General Public License.
26//
27// This exception applies only to the code released under the name GNU
28// Common C++. If you copy code from other releases into a copy of GNU
29// Common C++, as the General Public License permits, the exception does
30// not apply to the code that you add in this way. To avoid misleading
31// anyone as to the status of such modified files, you must delete
32// this exception notice from them.
33//
34// If you write modifications of your own for GNU Common C++, it is your choice
35// whether to permit this exception to apply to your modifications.
36// If you do not wish that, delete this exception notice.
37//
38
39#include <cc++/config.h>
40#ifdef CCXX_WITHOUT_EXTRAS
41#include <cc++/export.h>
42#endif
43#include <cc++/thread.h>
44#include <cc++/exception.h>
45#ifndef CCXX_WITHOUT_EXTRAS
46#include <cc++/export.h>
47#endif
48
49#ifdef CCXX_SSL
50
51#include <cc++/ssl.h>
52#include <cerrno>
53#include <cstdlib>
54#include <cstdarg>
55#include <cstdio>
56
57#ifdef CCXX_GNUTLS
58#include <gcrypt.h>
59#endif
60
61#ifdef WIN32
62#include <io.h>
63#define socket_errno WSAGetLastError()
64#else
65#define socket_errno errno
66#endif
67
68#ifdef CCXX_NAMESPACES
69namespace ost {
70using namespace std;
71#endif
72
73#ifdef CCXX_GNUTLS
74
75#ifndef WIN32
76static int _gcry_mutex_init(Mutex **priv)
77{
78 Mutex *m = new Mutex();
79
80 *priv = m;
81 return 0;
82}
83
84static int _gcry_mutex_destroy(Mutex **priv)
85{
86 delete *priv;
87 return 0;
88}
89
90static int _gcry_mutex_lock(Mutex **priv)
91{
92 (*priv)->enter();
93 return 0;
94}
95
96static int _gcry_mutex_unlock(Mutex **priv)
97{
98 (*priv)->leave();
99 return 0;
100}
101
102extern "C" {
103 static int _wrap_mutex_init(void **priv)
104 {
105 return _gcry_mutex_init((Mutex **)(priv));
106 }
107
108 static int _wrap_mutex_destroy(void **priv)
109 {
110 return _gcry_mutex_destroy((Mutex **)(priv));
111 }
112
113 static int _wrap_mutex_lock(void **priv)
114 {
115 return _gcry_mutex_lock((Mutex **)(priv));
116 }
117
118 static int _wrap_mutex_unlock(void **priv)
119 {
120 return _gcry_mutex_unlock((Mutex **)(priv));
121 }
122
123 static struct gcry_thread_cbs _gcry_threads =
124 {
125 GCRY_THREAD_OPTION_PTHREAD, NULL,
126 _wrap_mutex_init, _wrap_mutex_destroy,
127 _wrap_mutex_lock, _wrap_mutex_unlock
128 };
129
130};
131
132#endif
133
134static class _ssl_global {
135public:
136 _ssl_global() {
137#ifndef WIN32
138 gcry_control(GCRYCTL_SET_THREAD_CBS, &_gcry_threads);
139#endif
140 gnutls_global_init();
141 }
142
143 ~_ssl_global() {
144 gnutls_global_deinit();
145 }
146
147} _ssl_global;
148#endif
149
150#ifdef CCXX_OPENSSL
151static Mutex *ssl_mutex = NULL;
152
153extern "C" {
154 static void ssl_lock(int mode, int n, const char *file, int line)
155 {
156 if(mode && CRYPTO_LOCK)
157 ssl_mutex[n].enter();
158 else
159 ssl_mutex[n].leave();
160 }
161
162 static unsigned long ssl_thread(void)
163 {
164 #ifdef WIN32
165 return GetCurrentThreadId();
166 #else
167 return (unsigned long)pthread_self();
168 #endif
169 }
170} // extern "C"
171
172static class _ssl_global {
173public:
174 _ssl_global() {
175 if(ssl_mutex)
176 return;
177
178 if(CRYPTO_get_id_callback() != NULL)
179 return;
180
181 ssl_mutex = new Mutex[CRYPTO_num_locks()];
182 CRYPTO_set_id_callback(ssl_thread);
183 CRYPTO_set_locking_callback(ssl_lock);
184 }
185
186 ~_ssl_global() {
187 if(!ssl_mutex)
188 return;
189 CRYPTO_set_id_callback(NULL);
190 CRYPTO_set_locking_callback(NULL);
191 delete[] ssl_mutex;
192 ssl_mutex = NULL;
193 }
194} _ssl_global;
195#endif
196
197
198SSLStream::SSLStream(Family f, bool tf, timeout_t to) :
199TCPStream(f, tf, to)
200{
201 ssl = NULL;
202}
203
204SSLStream::SSLStream(const IPV4Host &h, tpport_t p, unsigned mss, bool tf, timeout_t to) :
205TCPStream(h, p, mss, tf, to)
206{
207 ssl = NULL;
208}
209
210#ifdef CCXX_IPV6
211SSLStream::SSLStream(const IPV6Host &h, tpport_t p, unsigned mss, bool tf, timeout_t to) :
212TCPStream(h, p, mss, tf, to)
213{
214 ssl = NULL;
215}
216#endif
217
218SSLStream::SSLStream(const char *name, Family f, unsigned mss, bool tf, timeout_t to) :
219TCPStream(name, f, mss, tf, to)
220{
221 ssl = NULL;
222}
223
224ssize_t SSLStream::readLine(char *str, size_t request, timeout_t timeout)
225{
226 ssize_t nstat;
227 unsigned count = 0;
228
229 if(!ssl)
230 return Socket::readLine(str, request, timeout);
231
232 while(count < request) {
233 if(timeout && !isPending(pendingInput, timeout)) {
234 error(errTimeout, "Read timeout", 0);
235 return -1;
236 }
237
238#ifdef CCXX_GNUTLS
239 nstat = gnutls_record_recv(ssl->session, str + count, 1);
240#else
241 nstat = SSL_read(ssl, str + count, 1);
242#endif
243 if(nstat <= 0) {
244 error(errInput, "Could not read from socket", socket_errno);
245 return -1;
246 }
247
248 if(str[count] == '\n') {
249 if(count > 0 && str[count - 1] == '\r')
250 --count;
251 break;
252 }
253
254 ++count;
255 }
256 str[count] = 0;
257 return count;
258}
259
260ssize_t SSLStream::writeData(void *source, size_t size, timeout_t timeout)
261{
262 ssize_t nstat, count = 0;
263 if(size < 1)
264 return 0;
265
266 const char *slide = (const char *)source;
267
268 while(size) {
269 if(timeout && !isPending(pendingOutput, timeout)) {
270 error(errOutput);
271 return -1;
272 }
273
274#ifdef CCXX_GNUTLS
275 nstat = gnutls_record_send(ssl->session, slide, size);
276#else
277 nstat = SSL_write(ssl, slide, size);
278#endif
279 if(nstat <= 0) {
280 error(errOutput);
281 return -1;
282 }
283 count += nstat;
284 size -= nstat;
285 slide += nstat;
286 }
287 return count;
288}
289
290ssize_t SSLStream::readData(void *target, size_t size, char separator, timeout_t timeout)
291{
292 char *str = (char *)target;
293 ssize_t nstat;
294 unsigned count = 0;
295 if(!ssl)
296 return Socket::readData(target, size, separator, timeout);
297
298 if(separator == 0x0d || separator == 0x0a)
299 return readLine((char *)target, size, timeout);
300
301 if(separator) {
302 while(count < size) {
303 if(timeout && !isPending(pendingInput, timeout)) {
304 error(errTimeout, "Read timeout", 0);
305 return -1;
306 }
307
308#ifdef CCXX_GNUTLS
309 nstat = gnutls_record_recv(ssl->session, str + count, 1);
310#else
311 nstat = SSL_read(ssl, str + count, 1);
312#endif
313 if(nstat <= 0) {
314 error(errInput, "Could not read from socket", socket_errno);
315 return -1;
316 }
317 if(str[count] == separator)
318 break;
319 ++count;
320 }
321 if(str[count] == separator)
322 str[count] = 0;
323 return count;
324 }
325
326 if(timeout && !isPending(pendingInput, timeout)) {
327 error(errTimeout);
328 return -1;
329 }
330
331#ifdef CCXX_GNUTLS
332 nstat = gnutls_record_recv(ssl->session, target, size);
333#else
334 nstat = SSL_read(ssl, target, size);
335#endif
336
337 if(nstat < 0) {
338 error(errInput);
339 return -1;
340 }
341 return nstat;
342}
343
344#ifdef CCXX_GNUTLS
345bool SSLStream::getSession(void)
346{
347 const int cert_priority[3] =
348 {GNUTLS_CRT_X509, GNUTLS_CRT_OPENPGP, 0};
349
350 if(ssl)
351 return true;
352
353 if(so == INVALID_SOCKET)
354 return false;
355
356 ssl = new SSL;
357 if(gnutls_init(&ssl->session, GNUTLS_CLIENT)) {
358 delete ssl;
359 ssl = NULL;
360 return false;
361 }
362
363 gnutls_set_default_priority(ssl->session);
364 gnutls_certificate_allocate_credentials(&ssl->xcred);
365 gnutls_certificate_type_set_priority(ssl->session, cert_priority);
366 gnutls_credentials_set(ssl->session, GNUTLS_CRD_CERTIFICATE, ssl->xcred);
367 gnutls_transport_set_ptr(ssl->session, (gnutls_transport_ptr)so);
368 if(gnutls_handshake(ssl->session)) {
369 gnutls_deinit(ssl->session);
370 gnutls_certificate_free_credentials(ssl->xcred);
371 delete ssl;
372 ssl = NULL;
373 return false;
374 }
375 return true;
376}
377#else
378bool SSLStream::getSession(void)
379{
380 SSL_CTX *ctx;
381 int err;
382
383 if(ssl)
384 return true;
385
386 if(so == INVALID_SOCKET)
387 return false;
388
389 ctx = SSL_CTX_new(SSLv3_client_method());
390 if(!ctx) {
391 SSL_CTX_free(ctx);
392 return false;
393 }
394
395 ssl = SSL_new(ctx);
396 if(!ssl) {
397 SSL_CTX_free(ctx);
398 return false;
399 }
400
401 SSL_set_fd(ssl, so);
402 SSL_set_connect_state(ssl);
403 err = SSL_connect(ssl);
404
405 if(err < 0)
406 SSL_shutdown(ssl);
407
408 if(err <= 0) {
409 SSL_free(ssl);
410 SSL_CTX_free(ctx);
411 ssl = NULL;
412 return false;
413 }
414 return true;
415}
416#endif
417
418#ifdef CCXX_GNUTLS
419void SSLStream::endStream(void)
420{
421 if(ssl && so != INVALID_SOCKET)
422 gnutls_bye(ssl->session, GNUTLS_SHUT_WR);
423 TCPStream::endStream();
424 if(ssl) {
425 gnutls_deinit(ssl->session);
426 gnutls_certificate_free_credentials(ssl->xcred);
427 delete ssl;
428 ssl = NULL;
429 }
430}
431
432void SSLStream::disconnect(void)
433{
434 if(ssl && so != INVALID_SOCKET)
435 gnutls_bye(ssl->session, GNUTLS_SHUT_WR);
436
437 if(so != INVALID_SOCKET)
438 TCPStream::disconnect();
439 if(ssl) {
440 gnutls_deinit(ssl->session);
441 gnutls_certificate_free_credentials(ssl->xcred);
442 delete ssl;
443 ssl = NULL;
444 }
445}
446#else
447void SSLStream::disconnect(void)
448{
449 if(ssl) {
450 if(so != INVALID_SOCKET)
451 SSL_shutdown(ssl);
452 SSL_free(ssl);
453 ssl = NULL;
454 }
455 TCPStream::disconnect();
456}
457
458void SSLStream::endStream(void)
459{
460 if(ssl) {
461 if(so != INVALID_SOCKET)
462 SSL_shutdown(ssl);
463 SSL_free(ssl);
464 ssl = NULL;
465 }
466 TCPStream::endStream();
467}
468#endif
469
470SSLStream::~SSLStream()
471{
472#ifdef CCXX_EXCEPTIONS
473 try { endStream(); }
474 catch( ...) { if ( ! std::uncaught_exception()) throw;};
475#else
476 endStream();
477#endif
478}
479
480#ifdef CCXX_NAMESPACES
481}
482#endif
483
484#endif