source: mod_gnutls/src/gnutls_io.c @ 1d48b0a

asynciodebian/masterdebian/stretch-backportsjessie-backportsmainmsvaproxy-ticketupstream
Last change on this file since 1d48b0a was 1d48b0a, checked in by Nokis Mavrogiannopoulos <nmav@…>, 14 years ago

More fixes related to bug #102

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