source: mod_gnutls/src/gnutls_io.c @ 4fefa39

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

src/gnutls_io.c: Reformat gnutls_io_filter_error for readability

No functional changes, just adjusted line breaks and indentation.

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