source: mod_gnutls/src/gnutls_io.c @ 62def2f

debian/masterdebian/stretch-backportsjessie-backportsmsvaupstream
Last change on this file since 62def2f was 62def2f, checked in by Nikos Mavrogiannopoulos <nmav@…>, 9 years ago

reduced warning level of TLS errors.

  • Property mode set to 100644
File size: 23.3 KB
RevLine 
[fcb122d]1/**
2 *  Copyright 2004-2005 Paul Querna
[7e2b223]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
[dae0aec]22 *  - Basicly the same as what mod_ssl does with OpenSSL.
23 *
[7e2b223]24 */
25
[dae0aec]26#define HTTP_ON_HTTPS_PORT \
27    "GET /" CRLF
[7e2b223]28
[dae0aec]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)
[7e2b223]33
[dae0aec]34static apr_status_t gnutls_io_filter_error(ap_filter_t * f,
35                                           apr_bucket_brigade * bb,
36                                           apr_status_t status)
[7e2b223]37{
[c301152]38    mgs_handle_t *ctxt = (mgs_handle_t *) f->ctx;
[dae0aec]39    apr_bucket *bucket;
[7e2b223]40
[dae0aec]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");
[7e2b223]48
[dae0aec]49        ctxt->status = -1;
[7e2b223]50
[dae0aec]51        /* fake the request line */
52        bucket = HTTP_ON_HTTPS_PORT_BUCKET(f->c->bucket_alloc);
53        break;
[7e2b223]54
[dae0aec]55    default:
56        return status;
[7e2b223]57    }
58
[dae0aec]59    APR_BRIGADE_INSERT_TAIL(bb, bucket);
60    bucket = apr_bucket_eos_create(f->c->bucket_alloc);
61    APR_BRIGADE_INSERT_TAIL(bb, bucket);
[7e2b223]62
[dae0aec]63    return APR_SUCCESS;
64}
[7e2b223]65
[c301152]66static int char_buffer_read(mgs_char_buffer_t * buffer, char *in,
[dae0aec]67                            int inl)
68{
69    if (!buffer->length) {
70        return 0;
71    }
[7e2b223]72
[dae0aec]73    if (buffer->length > inl) {
74        /* we have have enough to fill the caller's buffer */
[4fb2b3c]75        memmove(in, buffer->value, inl);
[dae0aec]76        buffer->value += inl;
77        buffer->length -= inl;
78    }
79    else {
80        /* swallow remainder of the buffer */
[4fb2b3c]81        memmove(in, buffer->value, buffer->length);
[dae0aec]82        inl = buffer->length;
83        buffer->value = NULL;
84        buffer->length = 0;
[7e2b223]85    }
86
[dae0aec]87    return inl;
88}
89
[c301152]90static int char_buffer_write(mgs_char_buffer_t * buffer, char *in,
[dae0aec]91                             int inl)
92{
93    buffer->value = in;
94    buffer->length = inl;
95    return inl;
[7e2b223]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
[c301152]184static apr_status_t gnutls_io_input_read(mgs_handle_t * ctxt,
[dae0aec]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    }
[368b574]224   
225    if (ctxt->session == NULL) {
226        return APR_EGENERAL;
227    }
[dae0aec]228
229    while (1) {
230
231        rc = gnutls_record_recv(ctxt->session, buf + bytes, 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, rc);
238            }
239            return ctxt->input_rc;
240        }
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            }
259            else {
260                if (*len > 0) {
261                    ctxt->input_rc = APR_SUCCESS;
262                }
263                else {
264                    ctxt->input_rc = APR_EOF;
265                }
266                break;
267            }
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_error(APLOG_MARK, APLOG_INFO, ctxt->input_rc,
274                             ctxt->c->base_server,
275                             "GnuTLS: Error reading data. Client Requested a New Handshake."
276                             " (%d) '%s'", rc, gnutls_strerror(rc));
277            }
[42307a9]278            else if (rc == GNUTLS_E_WARNING_ALERT_RECEIVED) {
279                rc = gnutls_alert_get(ctxt->session);
280                ap_log_error(APLOG_MARK, APLOG_INFO, ctxt->input_rc,
281                             ctxt->c->base_server,
282                             "GnuTLS: Warning Alert From Client: "
283                             " (%d) '%s'", rc, gnutls_alert_get_name(rc)); 
284            }
285            else if (rc == GNUTLS_E_FATAL_ALERT_RECEIVED) {
286                rc = gnutls_alert_get(ctxt->session);
287                ap_log_error(APLOG_MARK, APLOG_INFO, ctxt->input_rc,
288                             ctxt->c->base_server,
289                             "GnuTLS: Fatal Alert From Client: "
290                             "(%d) '%s'", rc, gnutls_alert_get_name(rc));
291                ctxt->input_rc = APR_EGENERAL;
292                break;
293            }
[dae0aec]294            else {
295                /* Some Other Error. Report it. Die. */
[a66e147]296                if(gnutls_error_is_fatal(rc)) {
297                    ap_log_error(APLOG_MARK, APLOG_INFO, ctxt->input_rc,
298                                 ctxt->c->base_server,
299                                 "GnuTLS: Error reading data. (%d) '%s'", rc,
300                                 gnutls_strerror(rc));
301                }
302                else if(*len > 0) {
303                    ctxt->input_rc = APR_SUCCESS;
304                    break;
305                }
[dae0aec]306            }
307
308            if (ctxt->input_rc == APR_SUCCESS) {
309                ctxt->input_rc = APR_EGENERAL;
310            }
311            break;
312        }
313    }
314    return ctxt->input_rc;
315}
316
[c301152]317static apr_status_t gnutls_io_input_getline(mgs_handle_t * ctxt,
[dae0aec]318                                            char *buf, apr_size_t * len)
319{
320    const char *pos = NULL;
321    apr_status_t status;
322    apr_size_t tmplen = *len, buflen = *len, offset = 0;
323
324    *len = 0;
325
326    while (tmplen > 0) {
327        status = gnutls_io_input_read(ctxt, buf + offset, &tmplen);
328
329        if (status != APR_SUCCESS) {
330            return status;
331        }
332
333        *len += tmplen;
334
335        if ((pos = memchr(buf, APR_ASCII_LF, *len))) {
336            break;
337        }
338
339        offset += tmplen;
340        tmplen = buflen - offset;
341    }
342
343    if (pos) {
344        char *value;
345        int length;
346        apr_size_t bytes = pos - buf;
347
348        bytes += 1;
349        value = buf + bytes;
350        length = *len - bytes;
351
352        char_buffer_write(&ctxt->input_cbuf, value, length);
353
354        *len = bytes;
355    }
356
357    return APR_SUCCESS;
358}
359
[0106b25]360#define HANDSHAKE_MAX_TRIES 1024
[c301152]361static int gnutls_do_handshake(mgs_handle_t * ctxt)
[dae0aec]362{
[32f2e60]363    int ret;
[42307a9]364    int errcode;
[8e33f2d]365    int maxtries = HANDSHAKE_MAX_TRIES;
366
[368b574]367    if (ctxt->status != 0 || ctxt->session == NULL) {
[e924ddd]368        return -1;
[fcb122d]369    }
370
371tryagain:
[e924ddd]372    do {
373        ret = gnutls_handshake(ctxt->session);
[8e33f2d]374        maxtries--;
[dcf1118]375    } while ((ret == GNUTLS_E_INTERRUPTED || ret == GNUTLS_E_AGAIN) && maxtries > 0);
[8e33f2d]376
377    if (maxtries < 1) {
378        ctxt->status = -1;
379#if USING_2_1_RECENT
380        ap_log_cerror(APLOG_MARK, APLOG_ERR, 0, ctxt->c,
381                     "GnuTLS: Handshake Failed. Hit Maximum Attempts");
382#else
383        ap_log_error(APLOG_MARK, APLOG_ERR, 0, ctxt->c->base_server,
384                     "GnuTLS: Handshake Failed. Hit Maximum Attempts");
385#endif
[1d48b0a]386        if (ctxt->session) {
387            gnutls_alert_send(ctxt->session, GNUTLS_AL_FATAL, 
[dcf1118]388                          gnutls_error_to_alert(GNUTLS_E_INTERNAL_ERROR, NULL));
[1d48b0a]389            gnutls_deinit(ctxt->session);
390        }
[a601e7d]391        ctxt->session = NULL;
[8e33f2d]392        return -1;
393    }
394
[fcb122d]395    if (ret < 0) {
396        if (ret == GNUTLS_E_WARNING_ALERT_RECEIVED
397            || ret == GNUTLS_E_FATAL_ALERT_RECEIVED) {
[42307a9]398            errcode = gnutls_alert_get(ctxt->session);
[62def2f]399            ap_log_error(APLOG_MARK, APLOG_INFO, 0, ctxt->c->base_server,
[42307a9]400                         "GnuTLS: Hanshake Alert (%d) '%s'.", errcode,
401                         gnutls_alert_get_name(errcode));
[b1f7f11]402        }
[fcb122d]403   
404        if (!gnutls_error_is_fatal(ret)) {
405            ap_log_error(APLOG_MARK, APLOG_INFO, 0, ctxt->c->base_server,
406                     "GnuTLS: Non-Fatal Handshake Error: (%d) '%s'", ret,
407                      gnutls_strerror(ret));
408            goto tryagain;
[b1f7f11]409        }
[316bd8c]410#if USING_2_1_RECENT
[62def2f]411        ap_log_cerror(APLOG_MARK, APLOG_INFO, 0, ctxt->c,
[fcb122d]412                     "GnuTLS: Handshake Failed (%d) '%s'", ret,
413                      gnutls_strerror(ret));
[316bd8c]414#else
[62def2f]415        ap_log_error(APLOG_MARK, APLOG_INFO, 0, ctxt->c->base_server,
[316bd8c]416                     "GnuTLS: Handshake Failed (%d) '%s'", ret,
417                     gnutls_strerror(ret));
418#endif
[fcb122d]419        ctxt->status = -1;
[1d48b0a]420        if (ctxt->session) {
421            gnutls_alert_send(ctxt->session, GNUTLS_AL_FATAL, 
[fcb122d]422                          gnutls_error_to_alert(ret, NULL));
[1d48b0a]423            gnutls_deinit(ctxt->session);
424        }
[a601e7d]425        ctxt->session = NULL;
[31645b2]426        return ret;
[fcb122d]427    }
428    else {
[31645b2]429        /* all done with the handshake */
[fcb122d]430        ctxt->status = 1;
[836417f]431        /* If the session was resumed, we did not set the correct
432         * server_rec in ctxt->sc.  Go Find it. (ick!)
433         */
434        if (gnutls_session_is_resumed(ctxt->session)) {
435            mgs_srvconf_rec* sc;
436            sc = mgs_find_sni_server(ctxt->session);
437            if (sc) {
438                ctxt->sc = sc;
439            }
440        }
[e924ddd]441        return 0;
[31645b2]442    }
443}
444
[c301152]445int mgs_rehandshake(mgs_handle_t * ctxt)
[31645b2]446{
447    int rv;
[368b574]448   
449    if (ctxt->session == NULL)
450        return -1;
[31645b2]451
452    rv = gnutls_rehandshake(ctxt->session);
453   
454    if (rv != 0) {
455        /* the client did not want to rehandshake. goodbye */
[62def2f]456        ap_log_error(APLOG_MARK, APLOG_WARNING, 0, ctxt->c->base_server,
[31645b2]457                     "GnuTLS: Client Refused Rehandshake request.");
458        return -1;
459    }
460   
461    ctxt->status = 0;
462
[e924ddd]463    rv = gnutls_do_handshake(ctxt);
464
465    return rv;
[dae0aec]466}
467
468
[c301152]469apr_status_t mgs_filter_input(ap_filter_t* f,
[dae0aec]470                                     apr_bucket_brigade * bb,
471                                     ap_input_mode_t mode,
472                                     apr_read_type_e block,
473                                     apr_off_t readbytes)
474{
475    apr_status_t status = APR_SUCCESS;
[c301152]476    mgs_handle_t *ctxt = (mgs_handle_t *) f->ctx;
[dae0aec]477    apr_size_t len = sizeof(ctxt->input_buffer);
478
479    if (f->c->aborted) {
480        apr_bucket *bucket = apr_bucket_eos_create(f->c->bucket_alloc);
481        APR_BRIGADE_INSERT_TAIL(bb, bucket);
482        return APR_ECONNABORTED;
483    }
484
485    if (ctxt->status == 0) {
486        gnutls_do_handshake(ctxt);
487    }
488
489    if (ctxt->status < 0) {
[0b3bc05]490        return ap_get_brigade(f->next, bb, mode, block, readbytes);
[dae0aec]491    }
492
493    /* XXX: we don't currently support anything other than these modes. */
494    if (mode != AP_MODE_READBYTES && mode != AP_MODE_GETLINE &&
495        mode != AP_MODE_SPECULATIVE && mode != AP_MODE_INIT) {
496        return APR_ENOTIMPL;
497    }
498
499    ctxt->input_mode = mode;
500    ctxt->input_block = block;
501
502    if (ctxt->input_mode == AP_MODE_READBYTES ||
503        ctxt->input_mode == AP_MODE_SPECULATIVE) {
504        /* Err. This is bad. readbytes *can* be a 64bit int! len.. is NOT */
505        if (readbytes < len) {
506            len = (apr_size_t) readbytes;
507        }
508        status = gnutls_io_input_read(ctxt, ctxt->input_buffer, &len);
509    }
510    else if (ctxt->input_mode == AP_MODE_GETLINE) {
511        status = gnutls_io_input_getline(ctxt, ctxt->input_buffer, &len);
512    }
513    else {
514        /* We have no idea what you are talking about, so return an error. */
515        return APR_ENOTIMPL;
516    }
517
518    if (status != APR_SUCCESS) {
519        return gnutls_io_filter_error(f, bb, status);
520    }
521
522    /* Create a transient bucket out of the decrypted data. */
523    if (len > 0) {
524        apr_bucket *bucket =
525            apr_bucket_transient_create(ctxt->input_buffer, len,
526                                        f->c->bucket_alloc);
527        APR_BRIGADE_INSERT_TAIL(bb, bucket);
528    }
529
530    return status;
531}
532
[c301152]533apr_status_t mgs_filter_output(ap_filter_t * f,
[dae0aec]534                                      apr_bucket_brigade * bb)
535{
[32f2e60]536    apr_size_t ret;
[42307a9]537    apr_bucket* e;
[c301152]538    mgs_handle_t *ctxt = (mgs_handle_t *) f->ctx;
[dae0aec]539    apr_status_t status = APR_SUCCESS;
540    apr_read_type_e rblock = APR_NONBLOCK_READ;
541
542    if (f->c->aborted) {
543        apr_brigade_cleanup(bb);
544        return APR_ECONNABORTED;
545    }
546
547    if (ctxt->status == 0) {
548        gnutls_do_handshake(ctxt);
549    }
550
551    if (ctxt->status < 0) {
552        return ap_pass_brigade(f->next, bb);
553    }
554
555    while (!APR_BRIGADE_EMPTY(bb)) {
556        apr_bucket *bucket = APR_BRIGADE_FIRST(bb);
[dcf1118]557       
[7ce01c9]558        if (AP_BUCKET_IS_EOC(bucket)) {
[c05a8bb]559            if (ctxt->session != NULL) {
560                do {
561                    ret = gnutls_bye( ctxt->session, GNUTLS_SHUT_WR);
562                } while(ret == GNUTLS_E_INTERRUPTED || ret == GNUTLS_E_AGAIN);
563            }
[42307a9]564
565            apr_bucket_copy(bucket, &e);
566            APR_BRIGADE_INSERT_TAIL(ctxt->output_bb, e);
[368b574]567 
[7ce01c9]568            if ((status = ap_pass_brigade(f->next, ctxt->output_bb)) != APR_SUCCESS) {
[42307a9]569                apr_brigade_cleanup(ctxt->output_bb);
570                return status;
571            }
[0b3bc05]572
[42307a9]573            apr_brigade_cleanup(ctxt->output_bb);
[daf3dc9]574            if (ctxt->session) {
[1d48b0a]575                gnutls_deinit(ctxt->session);
576                ctxt->session = NULL;
577            }
[42307a9]578            continue;
[7ce01c9]579        } else if (APR_BUCKET_IS_FLUSH(bucket) || APR_BUCKET_IS_EOS(bucket)) {
[0b3bc05]580
[42307a9]581            apr_bucket_copy(bucket, &e);
582            APR_BRIGADE_INSERT_TAIL(ctxt->output_bb, e);
[dae0aec]583            if ((status = ap_pass_brigade(f->next, bb)) != APR_SUCCESS) {
[42307a9]584                apr_brigade_cleanup(ctxt->output_bb);
[dae0aec]585                return status;
586            }
[7bebb42]587
[42307a9]588            apr_brigade_cleanup(ctxt->output_bb);
589            continue;
[dae0aec]590        }
591        else {
592            /* filter output */
593            const char *data;
594            apr_size_t len;
595
596            status = apr_bucket_read(bucket, &data, &len, rblock);
597
598            if (APR_STATUS_IS_EAGAIN(status)) {
599                rblock = APR_BLOCK_READ;
600                continue;       /* and try again with a blocking read. */
601            }
602
603            rblock = APR_NONBLOCK_READ;
604
605            if (!APR_STATUS_IS_EOF(status) && (status != APR_SUCCESS)) {
606                break;
607            }
[0e13d67]608           
609            if (len > 0) {
[dae0aec]610
[368b574]611                if (ctxt->session == NULL) {
612                    ret = GNUTLS_E_INVALID_REQUEST;
613                } else {
614                    do {
615                        ret = gnutls_record_send(ctxt->session, data, len);
616                    }
617                    while(ret == GNUTLS_E_INTERRUPTED || ret == GNUTLS_E_AGAIN);
[0e13d67]618                }
[dae0aec]619
[0e13d67]620                if (ret < 0) {
621                    /* error sending output */
622                    ap_log_error(APLOG_MARK, APLOG_INFO, ctxt->output_rc,
[dae0aec]623                             ctxt->c->base_server,
624                             "GnuTLS: Error writing data."
[7bebb42]625                             " (%d) '%s'", (int)ret, gnutls_strerror(ret));
[0e13d67]626                    if (ctxt->output_rc == APR_SUCCESS) {
627                        ctxt->output_rc = APR_EGENERAL;
628                    }
629                }
630                else if (ret != len) {
631                    /* Not able to send the entire bucket,
632                       split it and send it again. */
633                    apr_bucket_split(bucket, ret);
[dae0aec]634                }
635            }
636
637            apr_bucket_delete(bucket);
638
639            if (ctxt->output_rc != APR_SUCCESS) {
640                break;
641            }
642        }
643    }
644
645    return status;
646}
647
[c301152]648ssize_t mgs_transport_read(gnutls_transport_ptr_t ptr,
[7e2b223]649                                  void *buffer, size_t len)
650{
[c301152]651    mgs_handle_t *ctxt = ptr;
[7e2b223]652    apr_status_t rc;
653    apr_size_t in = len;
[dae0aec]654    apr_read_type_e block = ctxt->input_block;
655
656    ctxt->input_rc = APR_SUCCESS;
657
[7e2b223]658    /* If Len = 0, we don't do anything. */
659    if (!len)
660        return 0;
661
[dae0aec]662    if (!ctxt->input_bb) {
663        ctxt->input_rc = APR_EOF;
664        return -1;
665    }
666
[7e2b223]667    if (APR_BRIGADE_EMPTY(ctxt->input_bb)) {
668
669        rc = ap_get_brigade(ctxt->input_filter->next, ctxt->input_bb,
670                            AP_MODE_READBYTES, ctxt->input_block, in);
671
672        /* Not a problem, there was simply no data ready yet.
673         */
674        if (APR_STATUS_IS_EAGAIN(rc) || APR_STATUS_IS_EINTR(rc)
675            || (rc == APR_SUCCESS && APR_BRIGADE_EMPTY(ctxt->input_bb))) {
[9085f5b]676           
677            if (APR_STATUS_IS_EOF(ctxt->input_rc)) {
678                return 0;
679            } else {
[368b574]680                if (ctxt->session)
681                    gnutls_transport_set_errno(ctxt->session, EINTR);
[9085f5b]682                return -1;
683            }
[7e2b223]684        }
[9085f5b]685       
[7e2b223]686
687        if (rc != APR_SUCCESS) {
688            /* Unexpected errors discard the brigade */
689            apr_brigade_cleanup(ctxt->input_bb);
690            ctxt->input_bb = NULL;
691            return -1;
692        }
693    }
694
[dae0aec]695    ctxt->input_rc = brigade_consume(ctxt->input_bb, block, buffer, &len);
[7e2b223]696
[dae0aec]697    if (ctxt->input_rc == APR_SUCCESS) {
698        return (ssize_t) len;
699    }
[7e2b223]700
[dae0aec]701    if (APR_STATUS_IS_EAGAIN(ctxt->input_rc)
702        || APR_STATUS_IS_EINTR(ctxt->input_rc)) {
[9085f5b]703        if (len == 0) {
[368b574]704            if (ctxt->session)
705                gnutls_transport_set_errno(ctxt->session, EINTR);
[9085f5b]706            return -1;
707        }
708
[dae0aec]709        return (ssize_t) len;
710    }
[7e2b223]711
[dae0aec]712    /* Unexpected errors and APR_EOF clean out the brigade.
713     * Subsequent calls will return APR_EOF.
714     */
715    apr_brigade_cleanup(ctxt->input_bb);
716    ctxt->input_bb = NULL;
717
718    if (APR_STATUS_IS_EOF(ctxt->input_rc) && len) {
719        /* Provide the results of this read pass,
720         * without resetting the BIO retry_read flag
721         */
722        return (ssize_t) len;
723    }
724
725    return -1;
726}
727
728
[c301152]729static ssize_t write_flush(mgs_handle_t * ctxt)
[dae0aec]730{
731    apr_bucket *e;
732
733    if (!(ctxt->output_blen || ctxt->output_length)) {
734        ctxt->output_rc = APR_SUCCESS;
735        return 1;
736    }
737
738    if (ctxt->output_blen) {
739        e = apr_bucket_transient_create(ctxt->output_buffer,
740                                        ctxt->output_blen,
741                                        ctxt->output_bb->bucket_alloc);
742        /* we filled this buffer first so add it to the
743         * head of the brigade
744         */
745        APR_BRIGADE_INSERT_HEAD(ctxt->output_bb, e);
746        ctxt->output_blen = 0;
747    }
748
749    ctxt->output_length = 0;
750    e = apr_bucket_flush_create(ctxt->output_bb->bucket_alloc);
751    APR_BRIGADE_INSERT_TAIL(ctxt->output_bb, e);
752
753    ctxt->output_rc = ap_pass_brigade(ctxt->output_filter->next,
754                                      ctxt->output_bb);
[42307a9]755    /* clear the brigade to be ready for next time */
756    apr_brigade_cleanup(ctxt->output_bb);
757
[dae0aec]758    return (ctxt->output_rc == APR_SUCCESS) ? 1 : -1;
[7e2b223]759}
760
[c301152]761ssize_t mgs_transport_write(gnutls_transport_ptr_t ptr,
[7e2b223]762                                   const void *buffer, size_t len)
763{
[c301152]764    mgs_handle_t *ctxt = ptr;
[7e2b223]765
[42307a9]766    /* pass along the encrypted data
767     * need to flush since we're using SSL's malloc-ed buffer
768     * which will be overwritten once we leave here
769     */
770    apr_bucket *bucket = apr_bucket_transient_create(buffer, len,
771                                                    ctxt->output_bb->bucket_alloc);
772    ctxt->output_length += len;
773    APR_BRIGADE_INSERT_TAIL(ctxt->output_bb, bucket);
[7e2b223]774
[42307a9]775    if (write_flush(ctxt) < 0) {
776        return -1;
777    }
[dae0aec]778    return len;
[7e2b223]779}
Note: See TracBrowser for help on using the repository browser.