source: mod_gnutls/src/gnutls_io.c @ 4261999

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

gnutls_io_input_read: Retry gnutls_record_recv if necessary

While testing TLS proxy back end connection, requests occasionally
failed (empty response) with strange log messages like these:

[http:info] [pid 17004:tid 140701602649856] (20014)Internal error: [remote A.B.C.D:443] AH01590: Error reading chunk
[proxy_http:error] [pid 17004:tid 140701602649856] (28)No space left on device: [client A.B.C.E:36002] AH01110: error reading response

Investigation of the Apache source code (in particular mod_proxy_http
and the default http filter) indicated that these were the results of
APR_EGENERAL errors returned from the mod_gnutls input filter
(mgs_filter_input) while receiving the response from the back end
server.

The reason behind these errors was that gnutls_io_input_read (called
directly or through gnutls_io_input_getline) did not handle
interruptions of the GnuTLS receive function gnutls_record_recv
properly. The GnuTLS documentation states that "If GNUTLS_E_INTERRUPTED
or GNUTLS_E_AGAIN is returned, you must call this function again to get
the data." Adding a retry loop like the ones already used for some other
GnuTLS library calls in gnutls_io.c around the gnutls_record_recv call
solved the problem.

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