source: mod_gnutls/src/gnutls_io.c @ f5a36ee

debian/masterdebian/stretch-backportsjessie-backportsupstream
Last change on this file since f5a36ee was f5a36ee, checked in by Thomas Klute <thomas2.klute@…>, 4 years ago

Pass EAGAIN/EINTR from gnutls_io_input_read to input filter

Unconditionally repeating calls to gnutls_record_recv on EAGAIN or
EINTR can lead to busy wait loops on nonblocking reads, which
particularly affects Keep-Alive connections waiting for new
requests. Passing the error code up to the filter instead lets the
higher levels deal with the interruption as appropriate in the given
context.

However, the input filter (mgs_filter_input) needs to handle a special
case for nonblocking reads: Those (strangely) return APR_EINTR if no
data is available. This has to be caught and APR_EAGAIN returned
instead, or mod_proxy will assume that the connection failed,
potentially breaking transmissions.

  • Property mode set to 100644
File size: 26.3 KB
Line 
1/**
2 *  Copyright 2004-2005 Paul Querna
3 *  Copyright 2008 Nikos Mavrogiannopoulos
4 *  Copyright 2011 Dash Shendy
5 *  Copyright 2015 Thomas Klute
6 *
7 *  Licensed under the Apache License, Version 2.0 (the "License");
8 *  you may not use this file except in compliance with the License.
9 *  You may obtain a copy of the License at
10 *
11 *      http://www.apache.org/licenses/LICENSE-2.0
12 *
13 *  Unless required by applicable law or agreed to in writing, software
14 *  distributed under the License is distributed on an "AS IS" BASIS,
15 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 *  See the License for the specific language governing permissions and
17 *  limitations under the License.
18 *
19 */
20
21#include "mod_gnutls.h"
22
23#ifdef APLOG_USE_MODULE
24APLOG_USE_MODULE(gnutls);
25#endif
26
27/**
28 * Describe how the GnuTLS Filter system works here
29 *  - Basicly the same as what mod_ssl does with OpenSSL.
30 *
31 */
32
33#define HTTP_ON_HTTPS_PORT \
34    "GET /" CRLF
35
36#define HTTP_ON_HTTPS_PORT_BUCKET(alloc) \
37    apr_bucket_immortal_create(HTTP_ON_HTTPS_PORT, \
38                               sizeof(HTTP_ON_HTTPS_PORT) - 1, \
39                               alloc)
40
41#define IS_PROXY_STR(c) \
42    ((c->is_proxy == GNUTLS_ENABLED_TRUE) ? "proxy " : "")
43
44/**
45 * Convert APR_EINTR or APR_EAGAIN to the match raw error code. Needed
46 * to pass the status on to GnuTLS from the pull function.
47 */
48#define EAI_APR_TO_RAW(s) (APR_STATUS_IS_EAGAIN(s) ? EAGAIN : EINTR)
49
50
51
52static apr_status_t gnutls_io_filter_error(ap_filter_t * f,
53        apr_bucket_brigade * bb,
54        apr_status_t status) {
55    mgs_handle_t *ctxt = (mgs_handle_t *) f->ctx;
56    apr_bucket *bucket;
57
58    switch (status) {
59    case HTTP_BAD_REQUEST:
60        /* log the situation */
61        ap_log_error(APLOG_MARK, APLOG_INFO, 0,
62                     f->c->base_server,
63                     "GnuTLS handshake failed: HTTP spoken on HTTPS port; "
64                     "trying to send HTML error page");
65        mgs_srvconf_rec *sc = (mgs_srvconf_rec *)
66            ap_get_module_config(f->c->base_server->module_config,
67                                 &gnutls_module);
68        ctxt->status = -1;
69        sc->non_ssl_request = 1;
70
71        /* fake the request line */
72        bucket = HTTP_ON_HTTPS_PORT_BUCKET(f->c->bucket_alloc);
73        break;
74
75    default:
76        return status;
77    }
78
79    APR_BRIGADE_INSERT_TAIL(bb, bucket);
80    bucket = apr_bucket_eos_create(f->c->bucket_alloc);
81    APR_BRIGADE_INSERT_TAIL(bb, bucket);
82
83    return APR_SUCCESS;
84}
85
86static int char_buffer_read(mgs_char_buffer_t * buffer, char *in, int inl) {
87    if (!buffer->length) {
88        return 0;
89    }
90
91    if (buffer->length > inl) {
92        /* we have have enough to fill the caller's buffer */
93        memmove(in, buffer->value, inl);
94        buffer->value += inl;
95        buffer->length -= inl;
96    } else {
97        /* swallow remainder of the buffer */
98        memmove(in, buffer->value, buffer->length);
99        inl = buffer->length;
100        buffer->value = NULL;
101        buffer->length = 0;
102    }
103
104    return inl;
105}
106
107static int char_buffer_write(mgs_char_buffer_t * buffer, char *in, int inl) {
108    buffer->value = in;
109    buffer->length = inl;
110    return inl;
111}
112
113/**
114 * From mod_ssl / ssl_engine_io.c
115 * This function will read from a brigade and discard the read buckets as it
116 * proceeds.  It will read at most *len bytes.
117 */
118static apr_status_t brigade_consume(apr_bucket_brigade * bb,
119        apr_read_type_e block,
120        char *c, apr_size_t * len) {
121    apr_size_t actual = 0;
122    apr_status_t status = APR_SUCCESS;
123
124    while (!APR_BRIGADE_EMPTY(bb)) {
125        apr_bucket *b = APR_BRIGADE_FIRST(bb);
126        const char *str;
127        apr_size_t str_len;
128        apr_size_t consume;
129
130        /* Justin points out this is an http-ism that might
131         * not fit if brigade_consume is added to APR.  Perhaps
132         * apr_bucket_read(eos_bucket) should return APR_EOF?
133         * Then this becomes mainline instead of a one-off.
134         */
135        if (APR_BUCKET_IS_EOS(b)) {
136            status = APR_EOF;
137            break;
138        }
139
140        /* The reason I'm not offering brigade_consume yet
141         * across to apr-util is that the following call
142         * illustrates how borked that API really is.  For
143         * this sort of case (caller provided buffer) it
144         * would be much more trivial for apr_bucket_consume
145         * to do all the work that follows, based on the
146         * particular characteristics of the bucket we are
147         * consuming here.
148         */
149        status = apr_bucket_read(b, &str, &str_len, block);
150
151        if (status != APR_SUCCESS) {
152            if (APR_STATUS_IS_EOF(status)) {
153                /* This stream bucket was consumed */
154                apr_bucket_delete(b);
155                continue;
156            }
157            break;
158        }
159
160        if (str_len > 0) {
161            /* Do not block once some data has been consumed */
162            block = APR_NONBLOCK_READ;
163
164            /* Assure we don't overflow. */
165            consume =
166                    (str_len + actual >
167                    *len) ? *len - actual : str_len;
168
169            memcpy(c, str, consume);
170
171            c += consume;
172            actual += consume;
173
174            if (consume >= b->length) {
175                /* This physical bucket was consumed */
176                apr_bucket_delete(b);
177            } else {
178                /* Only part of this physical bucket was consumed */
179                b->start += consume;
180                b->length -= consume;
181            }
182        } else if (b->length == 0) {
183            apr_bucket_delete(b);
184        }
185
186        /* This could probably be actual == *len, but be safe from stray
187         * photons. */
188        if (actual >= *len) {
189            break;
190        }
191    }
192
193    *len = actual;
194    return status;
195}
196
197static apr_status_t gnutls_io_input_read(mgs_handle_t * ctxt,
198        char *buf, apr_size_t * len)
199{
200    apr_size_t wanted = *len;
201    apr_size_t bytes = 0;
202    int rc;
203
204    *len = 0;
205
206    /* If we have something leftover from last time, try that first. */
207    if ((bytes = char_buffer_read(&ctxt->input_cbuf, buf, wanted))) {
208        *len = bytes;
209        if (ctxt->input_mode == AP_MODE_SPECULATIVE) {
210            /* We want to rollback this read. */
211            if (ctxt->input_cbuf.length > 0) {
212                ctxt->input_cbuf.value -= bytes;
213                ctxt->input_cbuf.length += bytes;
214            } else {
215                char_buffer_write(&ctxt->input_cbuf, buf,
216                        (int) bytes);
217            }
218            return APR_SUCCESS;
219        }
220        /* This could probably be *len == wanted, but be safe from stray
221         * photons.
222         */
223        if (*len >= wanted) {
224            return APR_SUCCESS;
225        }
226        if (ctxt->input_mode == AP_MODE_GETLINE) {
227            if (memchr(buf, APR_ASCII_LF, *len)) {
228                return APR_SUCCESS;
229            }
230        } else {
231            /* Down to a nonblock pattern as we have some data already
232             */
233            ctxt->input_block = APR_NONBLOCK_READ;
234        }
235    }
236
237    if (ctxt->session == NULL) {
238        ap_log_cerror(APLOG_MARK, APLOG_INFO, 0, ctxt->c,
239                      "%s: GnuTLS session is NULL!", __func__);
240        return APR_EGENERAL;
241    }
242
243    while (1)
244    {
245        rc = gnutls_record_recv(ctxt->session, buf + bytes, wanted - bytes);
246
247        if (rc == GNUTLS_E_INTERRUPTED)
248            ctxt->input_rc = APR_EINTR;
249        else if (rc == GNUTLS_E_AGAIN)
250            ctxt->input_rc = APR_EAGAIN;
251
252        if (rc > 0) {
253            *len += rc;
254            if (ctxt->input_mode == AP_MODE_SPECULATIVE) {
255                /* We want to rollback this read. */
256                char_buffer_write(&ctxt->input_cbuf, buf,
257                        rc);
258            }
259            return ctxt->input_rc;
260        } else if (rc == 0) {
261            /* If EAGAIN, we will loop given a blocking read,
262             * otherwise consider ourselves at EOF.
263             */
264            if (APR_STATUS_IS_EAGAIN(ctxt->input_rc)
265                    || APR_STATUS_IS_EINTR(ctxt->input_rc)) {
266                /* Already read something, return APR_SUCCESS instead.
267                 * On win32 in particular, but perhaps on other kernels,
268                 * a blocking call isn't 'always' blocking.
269                 */
270                if (*len > 0) {
271                    ctxt->input_rc = APR_SUCCESS;
272                    break;
273                }
274                if (ctxt->input_block == APR_NONBLOCK_READ) {
275                    break;
276                }
277            } else {
278                if (*len > 0) {
279                    ctxt->input_rc = APR_SUCCESS;
280                } else {
281                    ctxt->input_rc = APR_EOF;
282                }
283                break;
284            }
285        } else { /* (rc < 0) */
286
287            if (rc == GNUTLS_E_REHANDSHAKE) {
288                /* A client has asked for a new Hankshake. Currently, we don't do it */
289                ap_log_cerror(APLOG_MARK, APLOG_INFO,
290                        ctxt->input_rc,
291                        ctxt->c,
292                        "GnuTLS: Error reading data. Client Requested a New Handshake."
293                        " (%d) '%s'", rc,
294                        gnutls_strerror(rc));
295            } else if (rc == GNUTLS_E_WARNING_ALERT_RECEIVED) {
296                rc = gnutls_alert_get(ctxt->session);
297                ap_log_cerror(APLOG_MARK, APLOG_INFO,
298                        ctxt->input_rc,
299                        ctxt->c,
300                        "GnuTLS: Warning Alert From Client: "
301                        " (%d) '%s'", rc,
302                        gnutls_alert_get_name(rc));
303            } else if (rc == GNUTLS_E_FATAL_ALERT_RECEIVED) {
304                rc = gnutls_alert_get(ctxt->session);
305                ap_log_cerror(APLOG_MARK, APLOG_INFO,
306                        ctxt->input_rc,
307                        ctxt->c,
308                        "GnuTLS: Fatal Alert From Client: "
309                        "(%d) '%s'", rc,
310                        gnutls_alert_get_name(rc));
311                ctxt->input_rc = APR_EGENERAL;
312                break;
313            } else {
314                /* Some Other Error. Report it. Die. */
315                if (gnutls_error_is_fatal(rc)) {
316                    ap_log_cerror(APLOG_MARK,
317                            APLOG_INFO,
318                            ctxt->input_rc,
319                            ctxt->c,
320                            "GnuTLS: Error reading data. (%d) '%s'",
321                            rc,
322                            gnutls_strerror(rc));
323                } else if (*len > 0) {
324                    ctxt->input_rc = APR_SUCCESS;
325                    break;
326                }
327            }
328
329            if (ctxt->input_rc == APR_SUCCESS) {
330                ap_log_cerror(APLOG_MARK, APLOG_INFO, ctxt->input_rc, ctxt->c,
331                              "%s: GnuTLS error: %s (%d)",
332                              __func__, gnutls_strerror(rc), rc);
333                ctxt->input_rc = APR_EGENERAL;
334            }
335            break;
336        }
337    }
338    return ctxt->input_rc;
339}
340
341static apr_status_t gnutls_io_input_getline(mgs_handle_t * ctxt,
342        char *buf, apr_size_t * len) {
343    const char *pos = NULL;
344    apr_status_t status;
345    apr_size_t tmplen = *len, buflen = *len, offset = 0;
346
347    *len = 0;
348
349    while (tmplen > 0) {
350        status = gnutls_io_input_read(ctxt, buf + offset, &tmplen);
351
352        if (status != APR_SUCCESS) {
353            return status;
354        }
355
356        *len += tmplen;
357
358        if ((pos = memchr(buf, APR_ASCII_LF, *len))) {
359            break;
360        }
361
362        offset += tmplen;
363        tmplen = buflen - offset;
364    }
365
366    if (pos) {
367        char *value;
368        int length;
369        apr_size_t bytes = pos - buf;
370
371        bytes += 1;
372        value = buf + bytes;
373        length = *len - bytes;
374
375        char_buffer_write(&ctxt->input_cbuf, value, length);
376
377        *len = bytes;
378    }
379
380    return APR_SUCCESS;
381}
382
383#define HANDSHAKE_MAX_TRIES 1024
384
385static int gnutls_do_handshake(mgs_handle_t * ctxt) {
386    int ret;
387    int errcode;
388    int maxtries = HANDSHAKE_MAX_TRIES;
389
390    if (ctxt->status != 0 || ctxt->session == NULL) {
391        return -1;
392    }
393
394tryagain:
395    do {
396        ret = gnutls_handshake(ctxt->session);
397        maxtries--;
398    } while ((ret == GNUTLS_E_INTERRUPTED || ret == GNUTLS_E_AGAIN)
399            && maxtries > 0);
400
401    if (maxtries < 1) {
402        ctxt->status = -1;
403#if USING_2_1_RECENT
404        ap_log_cerror(APLOG_MARK, APLOG_ERR, 0, ctxt->c,
405                "GnuTLS: Handshake Failed. Hit Maximum Attempts");
406#else
407        ap_log_error(APLOG_MARK, APLOG_ERR, 0,
408                ctxt->c->base_server,
409                "GnuTLS: Handshake Failed. Hit Maximum Attempts");
410#endif
411        if (ctxt->session) {
412            gnutls_alert_send(ctxt->session, GNUTLS_AL_FATAL,
413                    gnutls_error_to_alert
414                    (GNUTLS_E_INTERNAL_ERROR, NULL));
415            gnutls_deinit(ctxt->session);
416        }
417        ctxt->session = NULL;
418        return -1;
419    }
420
421    if (ret < 0) {
422        if (ret == GNUTLS_E_WARNING_ALERT_RECEIVED
423                || ret == GNUTLS_E_FATAL_ALERT_RECEIVED) {
424            errcode = gnutls_alert_get(ctxt->session);
425            ap_log_error(APLOG_MARK, APLOG_INFO, 0,
426                    ctxt->c->base_server,
427                    "GnuTLS: Handshake Alert (%d) '%s'.",
428                    errcode,
429                    gnutls_alert_get_name(errcode));
430        }
431
432        if (!gnutls_error_is_fatal(ret)) {
433            ap_log_error(APLOG_MARK, APLOG_INFO, 0,
434                    ctxt->c->base_server,
435                    "GnuTLS: Non-Fatal Handshake Error: (%d) '%s'",
436                    ret, gnutls_strerror(ret));
437            goto tryagain;
438        }
439#if USING_2_1_RECENT
440        ap_log_cerror(APLOG_MARK, APLOG_INFO, 0, ctxt->c,
441                "GnuTLS: Handshake Failed (%d) '%s'", ret,
442                gnutls_strerror(ret));
443#else
444        ap_log_error(APLOG_MARK, APLOG_INFO, 0,
445                ctxt->c->base_server,
446                "GnuTLS: Handshake Failed (%d) '%s'", ret,
447                gnutls_strerror(ret));
448#endif
449        ctxt->status = -1;
450        if (ctxt->session) {
451            gnutls_alert_send(ctxt->session, GNUTLS_AL_FATAL,
452                    gnutls_error_to_alert(ret,
453                    NULL));
454            gnutls_deinit(ctxt->session);
455        }
456        ctxt->session = NULL;
457        return ret;
458    } else {
459        /* all done with the handshake */
460        ctxt->status = 1;
461        /* If the session was resumed, we did not set the correct
462         * server_rec in ctxt->sc.  Go Find it. (ick!)
463         */
464        if (gnutls_session_is_resumed(ctxt->session)) {
465            mgs_srvconf_rec *sc;
466            sc = mgs_find_sni_server(ctxt->session);
467            if (sc) {
468                ctxt->sc = sc;
469            }
470        }
471        return GNUTLS_E_SUCCESS;
472    }
473}
474
475int mgs_rehandshake(mgs_handle_t * ctxt) {
476    int rv;
477
478    if (ctxt->session == NULL)
479        return -1;
480
481    rv = gnutls_rehandshake(ctxt->session);
482
483    if (rv != 0) {
484        /* the client did not want to rehandshake. goodbye */
485        ap_log_error(APLOG_MARK, APLOG_WARNING, 0,
486                ctxt->c->base_server,
487                "GnuTLS: Client Refused Rehandshake request.");
488        return -1;
489    }
490
491    ctxt->status = 0;
492
493    rv = gnutls_do_handshake(ctxt);
494
495    return rv;
496}
497
498apr_status_t mgs_filter_input(ap_filter_t * f,
499        apr_bucket_brigade * bb,
500        ap_input_mode_t mode,
501        apr_read_type_e block, apr_off_t readbytes)
502{
503    apr_status_t status = APR_SUCCESS;
504    mgs_handle_t *ctxt = (mgs_handle_t *) f->ctx;
505    apr_size_t len = sizeof (ctxt->input_buffer);
506
507    if (f->c->aborted) {
508        apr_bucket *bucket =
509                apr_bucket_eos_create(f->c->bucket_alloc);
510        APR_BRIGADE_INSERT_TAIL(bb, bucket);
511        ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, ctxt->c,
512                      "%s: %sconnection aborted",
513                      __func__, IS_PROXY_STR(ctxt));
514        return APR_ECONNABORTED;
515    }
516
517    if (ctxt->status == 0) {
518        int ret = gnutls_do_handshake(ctxt);
519        if (ret == GNUTLS_E_SUCCESS)
520            ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, ctxt->c,
521                          "%s: TLS %sconnection opened.",
522                          __func__, IS_PROXY_STR(ctxt));
523    }
524
525    if (ctxt->status < 0) {
526        ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, ctxt->c,
527                      "%s %s: ap_get_brigade", __func__, IS_PROXY_STR(ctxt));
528        return ap_get_brigade(f->next, bb, mode, block, readbytes);
529    }
530
531    /* XXX: we don't currently support anything other than these modes. */
532    if (mode != AP_MODE_READBYTES && mode != AP_MODE_GETLINE &&
533            mode != AP_MODE_SPECULATIVE && mode != AP_MODE_INIT) {
534        return APR_ENOTIMPL;
535    }
536
537    ctxt->input_mode = mode;
538    ctxt->input_block = block;
539
540    if (ctxt->input_mode == AP_MODE_READBYTES ||
541            ctxt->input_mode == AP_MODE_SPECULATIVE) {
542        if (readbytes < 0) {
543            /* you're asking us to speculatively read a negative number of bytes! */
544            return APR_ENOTIMPL;
545        }
546        /* Err. This is bad. readbytes *can* be a 64bit int! len.. is NOT */
547        if ((apr_size_t) readbytes < len) {
548            len = (apr_size_t) readbytes;
549        }
550        status =
551                gnutls_io_input_read(ctxt, ctxt->input_buffer, &len);
552    } else if (ctxt->input_mode == AP_MODE_GETLINE) {
553        status =
554                gnutls_io_input_getline(ctxt, ctxt->input_buffer,
555                &len);
556    } else {
557        /* We have no idea what you are talking about, so return an error. */
558        return APR_ENOTIMPL;
559    }
560
561    if (status != APR_SUCCESS)
562    {
563        /* no data for nonblocking read, return APR_EAGAIN */
564        if ((block == APR_NONBLOCK_READ) && (status == APR_EINTR))
565            return APR_EAGAIN;
566
567        return gnutls_io_filter_error(f, bb, status);
568    }
569
570    /* Create a transient bucket out of the decrypted data. */
571    if (len > 0) {
572        apr_bucket *bucket =
573                apr_bucket_transient_create(ctxt->input_buffer, len,
574                f->c->bucket_alloc);
575        APR_BRIGADE_INSERT_TAIL(bb, bucket);
576    }
577
578    return status;
579}
580
581static ssize_t write_flush(mgs_handle_t * ctxt) {
582    apr_bucket *e;
583
584    if (!(ctxt->output_blen || ctxt->output_length)) {
585        ctxt->output_rc = APR_SUCCESS;
586        return 1;
587    }
588
589    if (ctxt->output_blen) {
590        e = apr_bucket_transient_create(ctxt->output_buffer,
591                ctxt->output_blen,
592                ctxt->output_bb->
593                bucket_alloc);
594        /* we filled this buffer first so add it to the
595         *               * head of the brigade
596         *                               */
597        APR_BRIGADE_INSERT_HEAD(ctxt->output_bb, e);
598        ctxt->output_blen = 0;
599    }
600
601    ctxt->output_length = 0;
602    e = apr_bucket_flush_create(ctxt->output_bb->bucket_alloc);
603    APR_BRIGADE_INSERT_TAIL(ctxt->output_bb, e);
604
605    ctxt->output_rc = ap_pass_brigade(ctxt->output_filter->next,
606            ctxt->output_bb);
607    /* clear the brigade to be ready for next time */
608    apr_brigade_cleanup(ctxt->output_bb);
609
610    return (ctxt->output_rc == APR_SUCCESS) ? 1 : -1;
611}
612
613apr_status_t mgs_filter_output(ap_filter_t * f, apr_bucket_brigade * bb) {
614    int ret;
615    mgs_handle_t *ctxt = (mgs_handle_t *) f->ctx;
616    apr_status_t status = APR_SUCCESS;
617    apr_read_type_e rblock = APR_NONBLOCK_READ;
618
619    if (f->c->aborted) {
620        apr_brigade_cleanup(bb);
621        return APR_ECONNABORTED;
622    }
623
624    if (ctxt->status == 0) {
625        ret = gnutls_do_handshake(ctxt);
626        if (ret == GNUTLS_E_SUCCESS)
627            ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, ctxt->c,
628                          "%s: TLS %sconnection opened.",
629                          __func__, IS_PROXY_STR(ctxt));
630    }
631
632    if (ctxt->status < 0) {
633        return ap_pass_brigade(f->next, bb);
634    }
635
636    while (!APR_BRIGADE_EMPTY(bb)) {
637        apr_bucket *bucket = APR_BRIGADE_FIRST(bb);
638
639        if (APR_BUCKET_IS_EOS(bucket)) {
640            return ap_pass_brigade(f->next, bb);
641        } else if (APR_BUCKET_IS_FLUSH(bucket)) {
642            /* Try Flush */
643            if (write_flush(ctxt) < 0) {
644                /* Flush Error */
645                return ctxt->output_rc;
646            }
647            /* cleanup! */
648            apr_bucket_delete(bucket);
649        } else if (AP_BUCKET_IS_EOC(bucket)) {
650            /* End Of Connection */
651            if (ctxt->session != NULL) {
652                /* Try A Clean Shutdown */
653                do {
654                    ret = gnutls_bye(ctxt->session, GNUTLS_SHUT_WR);
655                } while (ret == GNUTLS_E_INTERRUPTED || ret == GNUTLS_E_AGAIN);
656                if (ret != GNUTLS_E_SUCCESS)
657                    ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, ctxt->c,
658                                  "%s: Error while closing TLS %sconnection: "
659                                  "'%s' (%d)",
660                                  __func__, IS_PROXY_STR(ctxt),
661                                  gnutls_strerror(ret), (int) ret);
662                else
663                    ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, ctxt->c,
664                                  "%s: TLS %sconnection closed.",
665                                  __func__, IS_PROXY_STR(ctxt));
666                /* De-Initialize Session */
667                gnutls_deinit(ctxt->session);
668                ctxt->session = NULL;
669            }
670            /* cleanup! */
671            apr_bucket_delete(bucket);
672            /* Pass next brigade! */
673            return ap_pass_brigade(f->next, bb);
674        } else {
675            /* filter output */
676            const char *data;
677            apr_size_t len;
678
679            status = apr_bucket_read(bucket, &data, &len, rblock);
680
681            if (APR_STATUS_IS_EAGAIN(status)) {
682                /* No data available so Flush! */
683                if (write_flush(ctxt) < 0) {
684                    return ctxt->output_rc;
685                }
686                /* Try again with a blocking read. */
687                rblock = APR_BLOCK_READ;
688                continue;
689            }
690
691            rblock = APR_NONBLOCK_READ;
692
693            if (!APR_STATUS_IS_EOF(status)
694                    && (status != APR_SUCCESS)) {
695                return status;
696            }
697
698            if (len > 0) {
699
700                if (ctxt->session == NULL) {
701                    ret = GNUTLS_E_INVALID_REQUEST;
702                } else {
703                    do {
704                        ret =
705                                gnutls_record_send
706                                (ctxt->session, data,
707                                len);
708                    } while (ret == GNUTLS_E_INTERRUPTED
709                            || ret == GNUTLS_E_AGAIN);
710                }
711
712                if (ret < 0) {
713                    /* error sending output */
714                    ap_log_error(APLOG_MARK,
715                            APLOG_INFO,
716                            ctxt->output_rc,
717                            ctxt->c->base_server,
718                            "GnuTLS: Error writing data."
719                            " (%d) '%s'",
720                            (int) ret,
721                            gnutls_strerror(ret));
722                    if (ctxt->output_rc == APR_SUCCESS) {
723                        ctxt->output_rc =
724                                APR_EGENERAL;
725                        return ctxt->output_rc;
726                    }
727                } else if ((apr_size_t)(ret) != len) {
728                    /* we know the above cast is OK because len > 0 and ret >= 0 */
729                    /* Not able to send the entire bucket,
730                       split it and send it again. */
731                    apr_bucket_split(bucket, ret);
732                }
733            }
734
735            apr_bucket_delete(bucket);
736        }
737    }
738
739    return status;
740}
741
742/**
743 * Pull function for GnuTLS
744 */
745ssize_t mgs_transport_read(gnutls_transport_ptr_t ptr,
746                           void *buffer, size_t len)
747{
748    mgs_handle_t *ctxt = ptr;
749    apr_status_t rc;
750    apr_size_t in = len;
751    apr_read_type_e block = ctxt->input_block;
752
753    ctxt->input_rc = APR_SUCCESS;
754
755    /* If Len = 0, we don't do anything. */
756    if (!len || buffer == NULL) {
757        return 0;
758    }
759    if (!ctxt->input_bb) {
760        ctxt->input_rc = APR_EOF;
761        return -1;
762    }
763
764    if (APR_BRIGADE_EMPTY(ctxt->input_bb)) {
765
766        rc = ap_get_brigade(ctxt->input_filter->next,
767                ctxt->input_bb, AP_MODE_READBYTES,
768                ctxt->input_block, in);
769
770        /* Not a problem, there was simply no data ready yet.
771         */
772        if (APR_STATUS_IS_EAGAIN(rc) || APR_STATUS_IS_EINTR(rc)
773            || (rc == APR_SUCCESS
774                && APR_BRIGADE_EMPTY(ctxt->input_bb)))
775        {
776            if (APR_STATUS_IS_EOF(ctxt->input_rc))
777            {
778                return 0;
779            }
780            else
781            {
782                if (ctxt->session)
783                    gnutls_transport_set_errno(ctxt->session,
784                                               EAI_APR_TO_RAW(ctxt->input_rc));
785                return -1;
786            }
787        }
788
789        if (rc != APR_SUCCESS) {
790            /* Unexpected errors discard the brigade */
791            apr_brigade_cleanup(ctxt->input_bb);
792            ctxt->input_bb = NULL;
793            return -1;
794        }
795    }
796
797    ctxt->input_rc =
798            brigade_consume(ctxt->input_bb, block, buffer, &len);
799
800    if (ctxt->input_rc == APR_SUCCESS) {
801        return (ssize_t) len;
802    }
803
804    if (APR_STATUS_IS_EAGAIN(ctxt->input_rc)
805        || APR_STATUS_IS_EINTR(ctxt->input_rc))
806    {
807        if (len == 0)
808        {
809            if (ctxt->session)
810                gnutls_transport_set_errno(ctxt->session,
811                                           EAI_APR_TO_RAW(ctxt->input_rc));
812            return -1;
813        }
814
815        return (ssize_t) len;
816    }
817
818    /* Unexpected errors and APR_EOF clean out the brigade.
819     * Subsequent calls will return APR_EOF.
820     */
821    apr_brigade_cleanup(ctxt->input_bb);
822    ctxt->input_bb = NULL;
823
824    if (APR_STATUS_IS_EOF(ctxt->input_rc) && len) {
825        /* Provide the results of this read pass,
826         * without resetting the BIO retry_read flag
827         */
828        return (ssize_t) len;
829    }
830
831    return -1;
832}
833
834ssize_t mgs_transport_write(gnutls_transport_ptr_t ptr,
835        const void *buffer, size_t len) {
836    mgs_handle_t *ctxt = ptr;
837
838    /* pass along the encrypted data
839     * need to flush since we're using SSL's malloc-ed buffer
840     * which will be overwritten once we leave here
841     */
842    apr_bucket *bucket = apr_bucket_transient_create(buffer, len,
843            ctxt->output_bb->
844            bucket_alloc);
845    ctxt->output_length += len;
846    APR_BRIGADE_INSERT_TAIL(ctxt->output_bb, bucket);
847
848    if (write_flush(ctxt) < 0) {
849        return -1;
850    }
851    return len;
852}
Note: See TracBrowser for help on using the repository browser.