source: mod_gnutls/src/gnutls_io.c @ 55dc3f0

debian/masterdebian/stretch-backportsjessie-backportsupstream
Last change on this file since 55dc3f0 was 55dc3f0, checked in by Daniel Kahn Gillmor <dkg@…>, 6 years ago

Make Apache 2.4 display the correct module in error logs

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