source: mod_gnutls/src/gnutls_io.c @ 5674676

debian/masterdebian/stretch-backportsjessie-backportsupstream
Last change on this file since 5674676 was 9ee0464, checked in by Dash Shendy <neuromancer@…>, 7 years ago

Compilation Errors Cleanup

Signed-off-by: Dash Shendy <neuromancer@…>

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