source: mod_gnutls/src/gnutls_io.c @ 398d1a0

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

Improved logging for gnutls_io_input_read

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