2 * Copyright (C) 2010 Martin Willi
3 * Copyright (C) 2010 revosec AG
5 * This program is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License as published by the
7 * Free Software Foundation; either version 2 of the License, or (at your
8 * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
10 * This program is distributed in the hope that it will be useful, but
11 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
12 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
16 #include "tls_socket.h"
21 #include <utils/debug.h>
22 #include <threading/thread.h>
25 * Buffer size for plain side I/O
27 #define PLAIN_BUF_SIZE TLS_MAX_FRAGMENT_LEN
30 * Buffer size for encrypted side I/O
32 #define CRYPTO_BUF_SIZE TLS_MAX_FRAGMENT_LEN + 2048
34 typedef struct private_tls_socket_t private_tls_socket_t
;
35 typedef struct private_tls_application_t private_tls_application_t
;
37 struct private_tls_application_t
{
40 * Implements tls_application layer.
42 tls_application_t application
;
45 * Output buffer to write to
50 * Number of bytes written to out
55 * Input buffer to read to
60 * Number of bytes read to in
70 * Bytes consumed in cache
75 * Close TLS connection?
81 * Private data of an tls_socket_t object.
83 struct private_tls_socket_t
{
86 * Public tls_socket_t interface.
91 * TLS application implementation
93 private_tls_application_t app
;
101 * Underlying OS socket
106 * Whether the socket returned EOF
111 METHOD(tls_application_t
, process
, status_t
,
112 private_tls_application_t
*this, bio_reader_t
*reader
)
121 len
= min(reader
->remaining(reader
), this->in
.len
- this->in_done
);
123 { /* copy to read buffer as much as fits in */
124 if (!reader
->read_data(reader
, len
, &data
))
128 memcpy(this->in
.ptr
+ this->in_done
, data
.ptr
, data
.len
);
129 this->in_done
+= data
.len
;
132 { /* read buffer is full, cache for next read */
133 if (!reader
->read_data(reader
, reader
->remaining(reader
), &data
))
137 this->cache
= chunk_cat("mc", this->cache
, data
);
142 METHOD(tls_application_t
, build
, status_t
,
143 private_tls_application_t
*this, bio_writer_t
*writer
)
149 if (this->out
.len
> this->out_done
)
151 writer
->write_data(writer
, this->out
);
152 this->out_done
= this->out
.len
;
155 return INVALID_STATE
;
159 * TLS data exchange loop
161 static bool exchange(private_tls_socket_t
*this, bool wr
, bool block
)
163 char buf
[CRYPTO_BUF_SIZE
], *pos
;
173 switch (this->tls
->build(this->tls
, buf
, &len
, NULL
))
180 out
= write(this->fd
, pos
, len
);
183 DBG1(DBG_TLS
, "TLS crypto write error: %s",
196 if (!wr
&& this->app
.in_done
> 0)
197 { /* return data after proper termination via fatal close
198 * notify to which we responded with one */
208 if (this->app
.out_done
== this->app
.out
.len
)
209 { /* all data written */
215 if (this->app
.in_done
== this->app
.in
.len
)
216 { /* buffer fully received */
222 if (this->app
.out_done
== this->app
.out
.len
)
224 if (!block
|| this->app
.in_done
)
226 flags
|= MSG_DONTWAIT
;
229 in
= recv(this->fd
, buf
, sizeof(buf
), flags
);
232 if (errno
== EAGAIN
|| errno
== EWOULDBLOCK
)
234 if (this->app
.in_done
== 0)
236 /* reading, nothing got yet, and call would block */
238 this->app
.in_done
= -1;
249 switch (this->tls
->process(this->tls
, buf
, in
))
261 METHOD(tls_socket_t
, read_
, ssize_t
,
262 private_tls_socket_t
*this, void *buf
, size_t len
, bool block
)
264 if (this->app
.cache
.len
)
268 cache
= min(len
, this->app
.cache
.len
- this->app
.cache_done
);
269 memcpy(buf
, this->app
.cache
.ptr
+ this->app
.cache_done
, cache
);
271 this->app
.cache_done
+= cache
;
272 if (this->app
.cache_done
== this->app
.cache
.len
)
274 chunk_free(&this->app
.cache
);
275 this->app
.cache_done
= 0;
283 this->app
.in
.ptr
= buf
;
284 this->app
.in
.len
= len
;
285 this->app
.in_done
= 0;
286 if (exchange(this, FALSE
, block
))
288 if (!this->app
.in_done
&& !this->eof
)
293 return this->app
.in_done
;
298 METHOD(tls_socket_t
, write_
, ssize_t
,
299 private_tls_socket_t
*this, void *buf
, size_t len
)
301 this->app
.out
.ptr
= buf
;
302 this->app
.out
.len
= len
;
303 this->app
.out_done
= 0;
304 if (exchange(this, TRUE
, FALSE
))
306 return this->app
.out_done
;
311 METHOD(tls_socket_t
, splice
, bool,
312 private_tls_socket_t
*this, int rfd
, int wfd
)
314 char buf
[PLAIN_BUF_SIZE
], *pos
;
316 bool old
, crypto_eof
= FALSE
;
317 struct pollfd pfd
[] = {
318 { .fd
= this->fd
, .events
= POLLIN
, },
319 { .fd
= rfd
, .events
= POLLIN
, },
322 while (!this->eof
&& !crypto_eof
)
324 old
= thread_cancelability(TRUE
);
325 in
= poll(pfd
, countof(pfd
), -1);
326 thread_cancelability(old
);
329 DBG1(DBG_TLS
, "TLS select error: %s", strerror(errno
));
332 while (!this->eof
&& pfd
[0].revents
& (POLLIN
| POLLHUP
| POLLNVAL
))
334 in
= read_(this, buf
, sizeof(buf
), FALSE
);
338 if (errno
!= EWOULDBLOCK
)
340 DBG1(DBG_TLS
, "TLS read error: %s", strerror(errno
));
348 out
= write(wfd
, pos
, in
);
351 DBG1(DBG_TLS
, "TLS plain write error: %s",
362 if (!crypto_eof
&& pfd
[1].revents
& (POLLIN
| POLLHUP
| POLLNVAL
))
364 in
= read(rfd
, buf
, sizeof(buf
));
371 DBG1(DBG_TLS
, "TLS plain read error: %s", strerror(errno
));
377 out
= write_(this, pos
, in
);
380 DBG1(DBG_TLS
, "TLS write error");
393 METHOD(tls_socket_t
, get_fd
, int,
394 private_tls_socket_t
*this)
399 METHOD(tls_socket_t
, get_server_id
, identification_t
*,
400 private_tls_socket_t
*this)
402 return this->tls
->get_server_id(this->tls
);
405 METHOD(tls_socket_t
, get_peer_id
, identification_t
*,
406 private_tls_socket_t
*this)
408 return this->tls
->get_peer_id(this->tls
);
411 METHOD(tls_socket_t
, destroy
, void,
412 private_tls_socket_t
*this)
414 /* send a TLS close notify if not done yet */
415 this->app
.close
= TRUE
;
416 write_(this, NULL
, 0);
417 free(this->app
.cache
.ptr
);
418 this->tls
->destroy(this->tls
);
425 tls_socket_t
*tls_socket_create(bool is_server
, identification_t
*server
,
426 identification_t
*peer
, int fd
,
427 tls_cache_t
*cache
, tls_version_t min_version
,
428 tls_version_t max_version
, tls_flag_t flags
)
430 private_tls_socket_t
*this;
438 .get_server_id
= _get_server_id
,
439 .get_peer_id
= _get_peer_id
,
446 .destroy
= (void*)nop
,
452 this->tls
= tls_create(is_server
, server
, peer
, TLS_PURPOSE_GENERIC
,
453 &this->app
.application
, cache
, flags
);
455 !this->tls
->set_version(this->tls
, min_version
, max_version
))
460 return &this->public;