source: mod_gnutls/src/gnutls_io.c @ 02a6a18

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

gnutls_io.c: Don't mix EAGAIN and EINTR in mgs_transport_read

Properly distinguishing EAGAIN and EINTR should help with connection
status handling. Previously EINTR was passed to GnuTLS on APR_EINTR and
APR_EAGAIN alike.

  • Property mode set to 100644
File size: 26.1 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        do
246            rc = gnutls_record_recv(ctxt->session, buf + bytes,
247                                    wanted - bytes);
248        while (rc == GNUTLS_E_INTERRUPTED || rc == GNUTLS_E_AGAIN);
249
250        if (rc > 0) {
251            *len += rc;
252            if (ctxt->input_mode == AP_MODE_SPECULATIVE) {
253                /* We want to rollback this read. */
254                char_buffer_write(&ctxt->input_cbuf, buf,
255                        rc);
256            }
257            return ctxt->input_rc;
258        } else if (rc == 0) {
259            /* If EAGAIN, we will loop given a blocking read,
260             * otherwise consider ourselves at EOF.
261             */
262            if (APR_STATUS_IS_EAGAIN(ctxt->input_rc)
263                    || APR_STATUS_IS_EINTR(ctxt->input_rc)) {
264                /* Already read something, return APR_SUCCESS instead.
265                 * On win32 in particular, but perhaps on other kernels,
266                 * a blocking call isn't 'always' blocking.
267                 */
268                if (*len > 0) {
269                    ctxt->input_rc = APR_SUCCESS;
270                    break;
271                }
272                if (ctxt->input_block == APR_NONBLOCK_READ) {
273                    break;
274                }
275            } else {
276                if (*len > 0) {
277                    ctxt->input_rc = APR_SUCCESS;
278                } else {
279                    ctxt->input_rc = APR_EOF;
280                }
281                break;
282            }
283        } else { /* (rc < 0) */
284
285            if (rc == GNUTLS_E_REHANDSHAKE) {
286                /* A client has asked for a new Hankshake. Currently, we don't do it */
287                ap_log_cerror(APLOG_MARK, APLOG_INFO,
288                        ctxt->input_rc,
289                        ctxt->c,
290                        "GnuTLS: Error reading data. Client Requested a New Handshake."
291                        " (%d) '%s'", rc,
292                        gnutls_strerror(rc));
293            } else if (rc == GNUTLS_E_WARNING_ALERT_RECEIVED) {
294                rc = gnutls_alert_get(ctxt->session);
295                ap_log_cerror(APLOG_MARK, APLOG_INFO,
296                        ctxt->input_rc,
297                        ctxt->c,
298                        "GnuTLS: Warning Alert From Client: "
299                        " (%d) '%s'", rc,
300                        gnutls_alert_get_name(rc));
301            } else if (rc == GNUTLS_E_FATAL_ALERT_RECEIVED) {
302                rc = gnutls_alert_get(ctxt->session);
303                ap_log_cerror(APLOG_MARK, APLOG_INFO,
304                        ctxt->input_rc,
305                        ctxt->c,
306                        "GnuTLS: Fatal Alert From Client: "
307                        "(%d) '%s'", rc,
308                        gnutls_alert_get_name(rc));
309                ctxt->input_rc = APR_EGENERAL;
310                break;
311            } else {
312                /* Some Other Error. Report it. Die. */
313                if (gnutls_error_is_fatal(rc)) {
314                    ap_log_cerror(APLOG_MARK,
315                            APLOG_INFO,
316                            ctxt->input_rc,
317                            ctxt->c,
318                            "GnuTLS: Error reading data. (%d) '%s'",
319                            rc,
320                            gnutls_strerror(rc));
321                } else if (*len > 0) {
322                    ctxt->input_rc = APR_SUCCESS;
323                    break;
324                }
325            }
326
327            if (ctxt->input_rc == APR_SUCCESS) {
328                ap_log_cerror(APLOG_MARK, APLOG_INFO, ctxt->input_rc, ctxt->c,
329                              "%s: GnuTLS error: %s (%d)",
330                              __func__, gnutls_strerror(rc), rc);
331                ctxt->input_rc = APR_EGENERAL;
332            }
333            break;
334        }
335    }
336    return ctxt->input_rc;
337}
338
339static apr_status_t gnutls_io_input_getline(mgs_handle_t * ctxt,
340        char *buf, apr_size_t * len) {
341    const char *pos = NULL;
342    apr_status_t status;
343    apr_size_t tmplen = *len, buflen = *len, offset = 0;
344
345    *len = 0;
346
347    while (tmplen > 0) {
348        status = gnutls_io_input_read(ctxt, buf + offset, &tmplen);
349
350        if (status != APR_SUCCESS) {
351            return status;
352        }
353
354        *len += tmplen;
355
356        if ((pos = memchr(buf, APR_ASCII_LF, *len))) {
357            break;
358        }
359
360        offset += tmplen;
361        tmplen = buflen - offset;
362    }
363
364    if (pos) {
365        char *value;
366        int length;
367        apr_size_t bytes = pos - buf;
368
369        bytes += 1;
370        value = buf + bytes;
371        length = *len - bytes;
372
373        char_buffer_write(&ctxt->input_cbuf, value, length);
374
375        *len = bytes;
376    }
377
378    return APR_SUCCESS;
379}
380
381#define HANDSHAKE_MAX_TRIES 1024
382
383static int gnutls_do_handshake(mgs_handle_t * ctxt) {
384    int ret;
385    int errcode;
386    int maxtries = HANDSHAKE_MAX_TRIES;
387
388    if (ctxt->status != 0 || ctxt->session == NULL) {
389        return -1;
390    }
391
392tryagain:
393    do {
394        ret = gnutls_handshake(ctxt->session);
395        maxtries--;
396    } while ((ret == GNUTLS_E_INTERRUPTED || ret == GNUTLS_E_AGAIN)
397            && maxtries > 0);
398
399    if (maxtries < 1) {
400        ctxt->status = -1;
401#if USING_2_1_RECENT
402        ap_log_cerror(APLOG_MARK, APLOG_ERR, 0, ctxt->c,
403                "GnuTLS: Handshake Failed. Hit Maximum Attempts");
404#else
405        ap_log_error(APLOG_MARK, APLOG_ERR, 0,
406                ctxt->c->base_server,
407                "GnuTLS: Handshake Failed. Hit Maximum Attempts");
408#endif
409        if (ctxt->session) {
410            gnutls_alert_send(ctxt->session, GNUTLS_AL_FATAL,
411                    gnutls_error_to_alert
412                    (GNUTLS_E_INTERNAL_ERROR, NULL));
413            gnutls_deinit(ctxt->session);
414        }
415        ctxt->session = NULL;
416        return -1;
417    }
418
419    if (ret < 0) {
420        if (ret == GNUTLS_E_WARNING_ALERT_RECEIVED
421                || ret == GNUTLS_E_FATAL_ALERT_RECEIVED) {
422            errcode = gnutls_alert_get(ctxt->session);
423            ap_log_error(APLOG_MARK, APLOG_INFO, 0,
424                    ctxt->c->base_server,
425                    "GnuTLS: Handshake Alert (%d) '%s'.",
426                    errcode,
427                    gnutls_alert_get_name(errcode));
428        }
429
430        if (!gnutls_error_is_fatal(ret)) {
431            ap_log_error(APLOG_MARK, APLOG_INFO, 0,
432                    ctxt->c->base_server,
433                    "GnuTLS: Non-Fatal Handshake Error: (%d) '%s'",
434                    ret, gnutls_strerror(ret));
435            goto tryagain;
436        }
437#if USING_2_1_RECENT
438        ap_log_cerror(APLOG_MARK, APLOG_INFO, 0, ctxt->c,
439                "GnuTLS: Handshake Failed (%d) '%s'", ret,
440                gnutls_strerror(ret));
441#else
442        ap_log_error(APLOG_MARK, APLOG_INFO, 0,
443                ctxt->c->base_server,
444                "GnuTLS: Handshake Failed (%d) '%s'", ret,
445                gnutls_strerror(ret));
446#endif
447        ctxt->status = -1;
448        if (ctxt->session) {
449            gnutls_alert_send(ctxt->session, GNUTLS_AL_FATAL,
450                    gnutls_error_to_alert(ret,
451                    NULL));
452            gnutls_deinit(ctxt->session);
453        }
454        ctxt->session = NULL;
455        return ret;
456    } else {
457        /* all done with the handshake */
458        ctxt->status = 1;
459        /* If the session was resumed, we did not set the correct
460         * server_rec in ctxt->sc.  Go Find it. (ick!)
461         */
462        if (gnutls_session_is_resumed(ctxt->session)) {
463            mgs_srvconf_rec *sc;
464            sc = mgs_find_sni_server(ctxt->session);
465            if (sc) {
466                ctxt->sc = sc;
467            }
468        }
469        return GNUTLS_E_SUCCESS;
470    }
471}
472
473int mgs_rehandshake(mgs_handle_t * ctxt) {
474    int rv;
475
476    if (ctxt->session == NULL)
477        return -1;
478
479    rv = gnutls_rehandshake(ctxt->session);
480
481    if (rv != 0) {
482        /* the client did not want to rehandshake. goodbye */
483        ap_log_error(APLOG_MARK, APLOG_WARNING, 0,
484                ctxt->c->base_server,
485                "GnuTLS: Client Refused Rehandshake request.");
486        return -1;
487    }
488
489    ctxt->status = 0;
490
491    rv = gnutls_do_handshake(ctxt);
492
493    return rv;
494}
495
496apr_status_t mgs_filter_input(ap_filter_t * f,
497        apr_bucket_brigade * bb,
498        ap_input_mode_t mode,
499        apr_read_type_e block, apr_off_t readbytes)
500{
501    apr_status_t status = APR_SUCCESS;
502    mgs_handle_t *ctxt = (mgs_handle_t *) f->ctx;
503    apr_size_t len = sizeof (ctxt->input_buffer);
504
505    if (f->c->aborted) {
506        apr_bucket *bucket =
507                apr_bucket_eos_create(f->c->bucket_alloc);
508        APR_BRIGADE_INSERT_TAIL(bb, bucket);
509        ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, ctxt->c,
510                      "%s: %sconnection aborted",
511                      __func__, IS_PROXY_STR(ctxt));
512        return APR_ECONNABORTED;
513    }
514
515    if (ctxt->status == 0) {
516        int ret = gnutls_do_handshake(ctxt);
517        if (ret == GNUTLS_E_SUCCESS)
518            ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, ctxt->c,
519                          "%s: TLS %sconnection opened.",
520                          __func__, IS_PROXY_STR(ctxt));
521    }
522
523    if (ctxt->status < 0) {
524        ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, ctxt->c,
525                      "%s %s: ap_get_brigade", __func__, IS_PROXY_STR(ctxt));
526        return ap_get_brigade(f->next, bb, mode, block, readbytes);
527    }
528
529    /* XXX: we don't currently support anything other than these modes. */
530    if (mode != AP_MODE_READBYTES && mode != AP_MODE_GETLINE &&
531            mode != AP_MODE_SPECULATIVE && mode != AP_MODE_INIT) {
532        return APR_ENOTIMPL;
533    }
534
535    ctxt->input_mode = mode;
536    ctxt->input_block = block;
537
538    if (ctxt->input_mode == AP_MODE_READBYTES ||
539            ctxt->input_mode == AP_MODE_SPECULATIVE) {
540        if (readbytes < 0) {
541            /* you're asking us to speculatively read a negative number of bytes! */
542            return APR_ENOTIMPL;
543        }
544        /* Err. This is bad. readbytes *can* be a 64bit int! len.. is NOT */
545        if ((apr_size_t) readbytes < len) {
546            len = (apr_size_t) readbytes;
547        }
548        status =
549                gnutls_io_input_read(ctxt, ctxt->input_buffer, &len);
550    } else if (ctxt->input_mode == AP_MODE_GETLINE) {
551        status =
552                gnutls_io_input_getline(ctxt, ctxt->input_buffer,
553                &len);
554    } else {
555        /* We have no idea what you are talking about, so return an error. */
556        return APR_ENOTIMPL;
557    }
558
559    if (status != APR_SUCCESS) {
560        return gnutls_io_filter_error(f, bb, status);
561    }
562
563    /* Create a transient bucket out of the decrypted data. */
564    if (len > 0) {
565        apr_bucket *bucket =
566                apr_bucket_transient_create(ctxt->input_buffer, len,
567                f->c->bucket_alloc);
568        APR_BRIGADE_INSERT_TAIL(bb, bucket);
569    }
570
571    return status;
572}
573
574static ssize_t write_flush(mgs_handle_t * ctxt) {
575    apr_bucket *e;
576
577    if (!(ctxt->output_blen || ctxt->output_length)) {
578        ctxt->output_rc = APR_SUCCESS;
579        return 1;
580    }
581
582    if (ctxt->output_blen) {
583        e = apr_bucket_transient_create(ctxt->output_buffer,
584                ctxt->output_blen,
585                ctxt->output_bb->
586                bucket_alloc);
587        /* we filled this buffer first so add it to the
588         *               * head of the brigade
589         *                               */
590        APR_BRIGADE_INSERT_HEAD(ctxt->output_bb, e);
591        ctxt->output_blen = 0;
592    }
593
594    ctxt->output_length = 0;
595    e = apr_bucket_flush_create(ctxt->output_bb->bucket_alloc);
596    APR_BRIGADE_INSERT_TAIL(ctxt->output_bb, e);
597
598    ctxt->output_rc = ap_pass_brigade(ctxt->output_filter->next,
599            ctxt->output_bb);
600    /* clear the brigade to be ready for next time */
601    apr_brigade_cleanup(ctxt->output_bb);
602
603    return (ctxt->output_rc == APR_SUCCESS) ? 1 : -1;
604}
605
606apr_status_t mgs_filter_output(ap_filter_t * f, apr_bucket_brigade * bb) {
607    int ret;
608    mgs_handle_t *ctxt = (mgs_handle_t *) f->ctx;
609    apr_status_t status = APR_SUCCESS;
610    apr_read_type_e rblock = APR_NONBLOCK_READ;
611
612    if (f->c->aborted) {
613        apr_brigade_cleanup(bb);
614        return APR_ECONNABORTED;
615    }
616
617    if (ctxt->status == 0) {
618        ret = gnutls_do_handshake(ctxt);
619        if (ret == GNUTLS_E_SUCCESS)
620            ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, ctxt->c,
621                          "%s: TLS %sconnection opened.",
622                          __func__, IS_PROXY_STR(ctxt));
623    }
624
625    if (ctxt->status < 0) {
626        return ap_pass_brigade(f->next, bb);
627    }
628
629    while (!APR_BRIGADE_EMPTY(bb)) {
630        apr_bucket *bucket = APR_BRIGADE_FIRST(bb);
631
632        if (APR_BUCKET_IS_EOS(bucket)) {
633            return ap_pass_brigade(f->next, bb);
634        } else if (APR_BUCKET_IS_FLUSH(bucket)) {
635            /* Try Flush */
636            if (write_flush(ctxt) < 0) {
637                /* Flush Error */
638                return ctxt->output_rc;
639            }
640            /* cleanup! */
641            apr_bucket_delete(bucket);
642        } else if (AP_BUCKET_IS_EOC(bucket)) {
643            /* End Of Connection */
644            if (ctxt->session != NULL) {
645                /* Try A Clean Shutdown */
646                do {
647                    ret = gnutls_bye(ctxt->session, GNUTLS_SHUT_WR);
648                } while (ret == GNUTLS_E_INTERRUPTED || ret == GNUTLS_E_AGAIN);
649                if (ret != GNUTLS_E_SUCCESS)
650                    ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, ctxt->c,
651                                  "%s: Error while closing TLS %sconnection: "
652                                  "'%s' (%d)",
653                                  __func__, IS_PROXY_STR(ctxt),
654                                  gnutls_strerror(ret), (int) ret);
655                else
656                    ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, ctxt->c,
657                                  "%s: TLS %sconnection closed.",
658                                  __func__, IS_PROXY_STR(ctxt));
659                /* De-Initialize Session */
660                gnutls_deinit(ctxt->session);
661                ctxt->session = NULL;
662            }
663            /* cleanup! */
664            apr_bucket_delete(bucket);
665            /* Pass next brigade! */
666            return ap_pass_brigade(f->next, bb);
667        } else {
668            /* filter output */
669            const char *data;
670            apr_size_t len;
671
672            status = apr_bucket_read(bucket, &data, &len, rblock);
673
674            if (APR_STATUS_IS_EAGAIN(status)) {
675                /* No data available so Flush! */
676                if (write_flush(ctxt) < 0) {
677                    return ctxt->output_rc;
678                }
679                /* Try again with a blocking read. */
680                rblock = APR_BLOCK_READ;
681                continue;
682            }
683
684            rblock = APR_NONBLOCK_READ;
685
686            if (!APR_STATUS_IS_EOF(status)
687                    && (status != APR_SUCCESS)) {
688                return status;
689            }
690
691            if (len > 0) {
692
693                if (ctxt->session == NULL) {
694                    ret = GNUTLS_E_INVALID_REQUEST;
695                } else {
696                    do {
697                        ret =
698                                gnutls_record_send
699                                (ctxt->session, data,
700                                len);
701                    } while (ret == GNUTLS_E_INTERRUPTED
702                            || ret == GNUTLS_E_AGAIN);
703                }
704
705                if (ret < 0) {
706                    /* error sending output */
707                    ap_log_error(APLOG_MARK,
708                            APLOG_INFO,
709                            ctxt->output_rc,
710                            ctxt->c->base_server,
711                            "GnuTLS: Error writing data."
712                            " (%d) '%s'",
713                            (int) ret,
714                            gnutls_strerror(ret));
715                    if (ctxt->output_rc == APR_SUCCESS) {
716                        ctxt->output_rc =
717                                APR_EGENERAL;
718                        return ctxt->output_rc;
719                    }
720                } else if ((apr_size_t)(ret) != len) {
721                    /* we know the above cast is OK because len > 0 and ret >= 0 */
722                    /* Not able to send the entire bucket,
723                       split it and send it again. */
724                    apr_bucket_split(bucket, ret);
725                }
726            }
727
728            apr_bucket_delete(bucket);
729        }
730    }
731
732    return status;
733}
734
735/**
736 * Pull function for GnuTLS
737 */
738ssize_t mgs_transport_read(gnutls_transport_ptr_t ptr,
739                           void *buffer, size_t len)
740{
741    mgs_handle_t *ctxt = ptr;
742    apr_status_t rc;
743    apr_size_t in = len;
744    apr_read_type_e block = ctxt->input_block;
745
746    ctxt->input_rc = APR_SUCCESS;
747
748    /* If Len = 0, we don't do anything. */
749    if (!len || buffer == NULL) {
750        return 0;
751    }
752    if (!ctxt->input_bb) {
753        ctxt->input_rc = APR_EOF;
754        return -1;
755    }
756
757    if (APR_BRIGADE_EMPTY(ctxt->input_bb)) {
758
759        rc = ap_get_brigade(ctxt->input_filter->next,
760                ctxt->input_bb, AP_MODE_READBYTES,
761                ctxt->input_block, in);
762
763        /* Not a problem, there was simply no data ready yet.
764         */
765        if (APR_STATUS_IS_EAGAIN(rc) || APR_STATUS_IS_EINTR(rc)
766            || (rc == APR_SUCCESS
767                && APR_BRIGADE_EMPTY(ctxt->input_bb)))
768        {
769            if (APR_STATUS_IS_EOF(ctxt->input_rc))
770            {
771                return 0;
772            }
773            else
774            {
775                if (ctxt->session)
776                    gnutls_transport_set_errno(ctxt->session,
777                                               EAI_APR_TO_RAW(ctxt->input_rc));
778                return -1;
779            }
780        }
781
782        if (rc != APR_SUCCESS) {
783            /* Unexpected errors discard the brigade */
784            apr_brigade_cleanup(ctxt->input_bb);
785            ctxt->input_bb = NULL;
786            return -1;
787        }
788    }
789
790    ctxt->input_rc =
791            brigade_consume(ctxt->input_bb, block, buffer, &len);
792
793    if (ctxt->input_rc == APR_SUCCESS) {
794        return (ssize_t) len;
795    }
796
797    if (APR_STATUS_IS_EAGAIN(ctxt->input_rc)
798        || APR_STATUS_IS_EINTR(ctxt->input_rc))
799    {
800        if (len == 0)
801        {
802            if (ctxt->session)
803                gnutls_transport_set_errno(ctxt->session,
804                                           EAI_APR_TO_RAW(ctxt->input_rc));
805            return -1;
806        }
807
808        return (ssize_t) len;
809    }
810
811    /* Unexpected errors and APR_EOF clean out the brigade.
812     * Subsequent calls will return APR_EOF.
813     */
814    apr_brigade_cleanup(ctxt->input_bb);
815    ctxt->input_bb = NULL;
816
817    if (APR_STATUS_IS_EOF(ctxt->input_rc) && len) {
818        /* Provide the results of this read pass,
819         * without resetting the BIO retry_read flag
820         */
821        return (ssize_t) len;
822    }
823
824    return -1;
825}
826
827ssize_t mgs_transport_write(gnutls_transport_ptr_t ptr,
828        const void *buffer, size_t len) {
829    mgs_handle_t *ctxt = ptr;
830
831    /* pass along the encrypted data
832     * need to flush since we're using SSL's malloc-ed buffer
833     * which will be overwritten once we leave here
834     */
835    apr_bucket *bucket = apr_bucket_transient_create(buffer, len,
836            ctxt->output_bb->
837            bucket_alloc);
838    ctxt->output_length += len;
839    APR_BRIGADE_INSERT_TAIL(ctxt->output_bb, bucket);
840
841    if (write_flush(ctxt) < 0) {
842        return -1;
843    }
844    return len;
845}
Note: See TracBrowser for help on using the repository browser.