root/tls/session_impl.hh

Revision 08f5bd75ad170e6b8177b93e98f40d0f8a51b9d2, 14.7 KB (checked in by Antti-Juhani Kaijanaho <antti-juhani@…>, 22 months ago)

[tls::session] Use ring buffers instead of dynamically growing buffers

This way enbuffering cannot throw bad_alloc.

Closes #69.

Signed-off-by: Antti-Juhani Kaijanaho <antti-juhani@…>

  • Property mode set to 100644
Line 
1/*  This file is part of Alue, the multiprotocol Internet discussion daemon
2
3    Copyright © 2009, 2010 Antti-Juhani Kaijanaho
4
5    Alue is free software: you can redistribute it and/or modify it
6    under the terms of the GNU General Public License as published by
7    the Free Software Foundation, either version 3 of the License, or
8    (at your option) any later version.
9
10    Alue is distributed in the hope that it will be useful, but
11    WITHOUT ANY WARRANTY; without even the implied warranty of
12    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13    General Public License for more details.
14
15    You should have received a copy of the GNU General Public License
16    along with Alue.  If not, see <http://www.gnu.org/licenses/>.
17
18 */
19
20#ifndef GUARD_TLS_SESSION_IMPL_HH
21#define GUARD_TLS_SESSION_IMPL_HH
22
23#include "session.hh"
24
25namespace tls
26{
27
28        template <typename Stream>
29        void session_impl<Stream>::init(tls::init &the_init, conn_end ce)
30        {
31                gnutls_connection_end_t gce;
32                switch (ce)
33                {
34                case server:
35                        gce = GNUTLS_SERVER;
36                        break;
37                case client:
38                        gce = GNUTLS_CLIENT;
39                        break;
40                default:
41                        throw 0;
42                }
43                the_init.init_session(gs, gce);
44               
45                gnutls_transport_set_ptr(gs, this);
46                gnutls_transport_set_push_function(gs, push_static);
47                gnutls_transport_set_pull_function(gs, pull_static);
48                gnutls_transport_set_lowat(gs, 0);
49        }
50
51        template <typename Stream>
52        void session_impl<Stream>::kill()
53        {
54                kill_pending_actions();
55                stream.cancel();
56        }
57
58        template <typename Stream>
59        session_impl<Stream>::~session_impl()
60        {
61                gnutls_deinit(gs);
62        }
63
64        /* never call directly!  (called by gnutls) */
65        template <typename Stream>
66        ssize_t session_impl<Stream>::push(const void *b, size_t n)
67        {
68                if (outs.full())
69                {
70                        errno = EAGAIN;
71                        return -1;
72                }
73                return outs.put(static_cast<const unsigned char *>(b), n);
74        }
75
76        /* never call directly!  (called by gnutls) */
77        template <typename Stream>
78        ssize_t session_impl<Stream>::pull(void *b, size_t n)
79        {
80                if (ins.empty())
81                {
82                        errno = EAGAIN;
83                        return -1;
84                }
85                return ins.get(static_cast<unsigned char *>(b), n);
86        }
87
88        template <typename Stream>
89        void session_impl<Stream>::post_write()
90        {
91                if (write_active) return;
92                if (!outs.empty())
93                {
94                        stream.async_write_some
95                                (outs.read_area(),
96                                 strand.wrap(boost::bind
97                                             (&sent_some,
98                                              this->shared_from_this(),
99                                              _1, _2)));
100                        write_active = true;
101                }
102        }
103
104        template <typename Stream>
105        void session_impl<Stream>::post_read()
106        {
107                if (read_active) return;
108                stream.async_read_some
109                        (ins.write_area(),
110                         strand.wrap(boost::bind
111                                     (&received_some, this->shared_from_this(),
112                                      _1, _2)));
113                read_active = true;
114        }
115
116        template <typename Stream>
117        void session_impl<Stream>::check_sanity()
118        {
119                assert(!pre_handshake);
120                if (post_shutdown)
121                        pending_error = boost::asio::error::operation_aborted;
122        }
123
124        template <typename Stream>
125        void session_impl<Stream>::post_io_actions()
126        {
127                post_read();
128                post_write();
129        }
130
131        template <typename Stream>
132        void session_impl<Stream>::received_some(ptr self,
133                                                 boost::system::error_code ec,
134                                                 size_t n)
135        {
136                self->read_active = false;
137                if (ec == boost::asio::error::operation_aborted) return;
138                if (ec) self->pending_error = ec; else self->ins.written(n);
139                self->post_pending_actions();
140        }
141
142        template <typename Stream>
143        void session_impl<Stream>::sent_some(ptr self,
144                                             boost::system::error_code ec,
145                                             size_t n)
146        {
147                self->write_active = false;
148                if (ec == boost::asio::error::operation_aborted) return;
149                if (ec) { self->pending_error = ec; return; }
150                self->outs.read(n);
151                self->post_pending_actions();
152        }
153
154        template <typename Stream>
155        void session_impl<Stream>::post_pending_actions()
156        {
157                while (!pending_actions.empty())
158                {
159                        action spa = pending_actions.front();
160                        strand.post(spa);
161                        pending_actions.pop();
162                }
163        }
164
165        template <typename Stream>
166        void session_impl<Stream>::kill_pending_actions()
167        {
168                while (!pending_actions.empty())
169                {
170                        action spa = pending_actions.front();
171                        spa.kill();
172                        pending_actions.pop();
173                }
174        }
175
176        template <typename Handler>
177        class error_bound_handler
178        {
179                Handler h;
180                boost::system::error_code ec;
181        public:
182                error_bound_handler(Handler h,
183                                    boost::system::error_code ec =
184                                    boost::system::error_code())
185                        : h(h), ec(ec)
186                        {}
187                void operator()() { h(ec); }
188        };
189
190        template <typename Stream> template<typename Handler>
191        void session_impl<Stream>::initiator<Handler>::operator()
192                (boost::shared_ptr<session_impl<Stream>::initiator_base> spa)
193        {
194                ptr sess = initiator_base::sess;
195                sess->check_sanity();
196                if (sess->pending_error)
197                {
198                        sess->strand.post(error_bound_handler<Handler>
199                                          (handler, sess->pending_error));
200                        sess->pending_error = boost::system::error_code();
201                        return;
202                }
203                int r = gnutls_action();
204                switch (r)
205                {
206                case GNUTLS_E_AGAIN:
207                        sess->pending_actions.push(action(spa));
208                        sess->post_io_actions();
209                        return;
210                case GNUTLS_E_SUCCESS:
211                        sess->strand.post(error_bound_handler<Handler>
212                                          (handler));
213                        return;
214                default:
215                        sess->strand.post(error_bound_handler<Handler>
216                                         (handler, error_category::code(r)));
217                }
218        }
219
220        template <typename Stream> template<typename Handler>
221        void session_impl<Stream>::initiator<Handler>::kill
222                (boost::shared_ptr<session_impl<Stream>::initiator_base>)
223        {
224                ptr sess = initiator_base::sess;
225                sess->strand.post(error_bound_handler<Handler>
226                                  (handler,
227                                   boost::system::error_code
228                                   (boost::asio::error::operation_aborted)));
229        }
230
231        template <typename Stream> template<typename Handler>
232        void session_impl<Stream>::async_handshake(Handler handler)
233        {
234                assert(pre_handshake);
235                assert(!write_active);
236                assert(!read_active);
237                pre_handshake = false;
238                boost::shared_ptr<initiator_base>
239                        spa(new handshake_initiator<Handler>(this->shared_from_this(),
240                                                             handler));
241                pending_actions.push(action(spa));
242                post_io_actions();
243        }
244
245        template <typename Stream> template<typename Handler>
246        int session_impl<Stream>::handshake_initiator<Handler>::gnutls_action()
247        {
248                return gnutls_handshake(initiator_base::sess->gs);
249        }
250
251        template <typename Stream> template<typename Handler>
252        void session_impl<Stream>::async_shutdown(Handler handler)
253        {
254                check_sanity();
255                boost::shared_ptr<initiator_base>
256                        spa(new shutdown_initiator<Handler>(this->shared_from_this(),
257                                                            handler));
258                strand.post(action(spa));
259                post_io_actions();
260        }
261
262        template <typename Stream> template<typename Handler>
263        int session_impl<Stream>::shutdown_initiator<Handler>::gnutls_action()
264        {
265                int r = gnutls_bye(initiator_base::sess->gs,
266                                   GNUTLS_SHUT_RDWR);
267                if (r == GNUTLS_E_SUCCESS)
268                {
269                        initiator_base::sess->post_shutdown = true;
270                        initiator_base::sess->kill_pending_actions();
271                }
272                return r;
273        }
274
275        template <typename Handler>
276        class size_error_bound_handler
277        {
278                Handler h;
279                size_t size;
280                boost::system::error_code ec;
281        public:
282                size_error_bound_handler(Handler h,
283                                         size_t size,
284                                         boost::system::error_code ec =
285                                         boost::system::error_code())
286                        : h(h), size(size), ec(ec)
287                        {}
288                void operator()() { h(ec, size); }
289        };
290
291        template <typename Stream> template <typename Handler>
292        void session_impl<Stream>::io_initiator<Handler>::
293        operator()(boost::shared_ptr<initiator_base> spa)
294        {
295                ptr sess = initiator_base::sess;
296                sess->check_sanity();
297                if (sess->pending_error)
298                {
299                        sess->strand.post(size_error_bound_handler<Handler>
300                                         (handler, 0, sess->pending_error));
301                        sess->pending_error =
302                                boost::system::error_code();
303                        return;
304                }
305                int r = gnutls_action();
306                if (r > 0)
307                {
308                        sess->strand.post(size_error_bound_handler<Handler>
309                                         (handler, r));
310                        return;
311                }
312                switch (r)
313                {
314                case GNUTLS_E_AGAIN:
315                        sess->pending_actions.push(action(spa));
316                        sess->post_io_actions();
317                        return;
318                case GNUTLS_E_SUCCESS:
319                        sess->strand.post(size_error_bound_handler<Handler>
320                                         (handler, 0,
321                                          boost::system::error_code
322                                          (boost::asio::error::eof)));
323                default:
324                        sess->strand.post(size_error_bound_handler<Handler>
325                                         (handler, 0, error_category::code(r)));
326                }
327        }
328
329        template <typename Stream> template <typename Handler>
330        void session_impl<Stream>::io_initiator<Handler>::
331        kill(boost::shared_ptr<initiator_base>)
332        {
333                ptr sess = initiator_base::sess;
334                sess->strand.post(size_error_bound_handler<Handler>
335                                  (handler, 0,
336                                   boost::system::error_code
337                                   (boost::asio::error::operation_aborted)));
338        }
339
340        template <typename Stream> template <typename Handler>
341        int session_impl<Stream>::read_initiator<Handler>::gnutls_action()
342        {
343                return gnutls_record_recv(initiator_base::sess->gs,
344                                          boost::asio::buffer_cast<void*>(mb),
345                                          boost::asio::buffer_size(mb));
346        }
347
348        template <typename Stream>
349        template <typename MutableBufferSequence, typename Handler>
350        void session_impl<Stream>::async_read_some(MutableBufferSequence mbs,
351                                                   Handler handler)
352        {
353                if (pre_handshake || post_shutdown)
354                {
355                        stream.async_read_some(mbs, handler);
356                        return;
357                }
358                check_sanity();
359                boost::shared_ptr<initiator_base>
360                        spa(new read_initiator<Handler>
361                            (this->shared_from_this(), *mbs.begin(), handler));
362                strand.post(action(spa));
363                post_io_actions();
364        }
365
366        template <typename Stream> template <typename Handler>
367        int session_impl<Stream>::write_initiator<Handler>::gnutls_action()
368        {
369                return gnutls_record_send
370                        (initiator_base::sess->gs,
371                         boost::asio::buffer_cast<const void*>(cb),
372                         boost::asio::buffer_size(cb));
373        }
374
375        template <typename Stream>
376        template <typename ConstBufferSequence, typename Handler>
377        void session_impl<Stream>::async_write_some(ConstBufferSequence cbs,
378                                                    Handler handler)
379        {
380                if (pre_handshake || post_shutdown)
381                {
382                        stream.async_write_some(cbs, handler);
383                        return;
384                }
385                check_sanity();
386                boost::shared_ptr<initiator_base>
387                        spa(new write_initiator<Handler>
388                            (this->shared_from_this(), *cbs.begin(), handler));
389                strand.post(action(spa));
390                post_io_actions();
391        }       
392}
393
394#endif /* GUARD_TLS_SESSION_IMPL_HH */
Note: See TracBrowser for help on using the browser.