source: mod_gnutls/src/gnutls_io.c @ 9a9bc1e

debian/masterdebian/stretch-backportsjessie-backportsmsvaupstream
Last change on this file since 9a9bc1e was 9a9bc1e, checked in by Dash Shendy <neuromancer@…>, 8 years ago

Added fix for unclean shutdowns, we're now ignoring SIGPIPE Signals

  • Property mode set to 100644
File size: 23.8 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            ctxt->status = -1;
51
52            /* fake the request line */
53            bucket = HTTP_ON_HTTPS_PORT_BUCKET(f->c->bucket_alloc);
54            break;
55
56        default:
57            return status;
58    }
59
60    APR_BRIGADE_INSERT_TAIL(bb, bucket);
61    bucket = apr_bucket_eos_create(f->c->bucket_alloc);
62    APR_BRIGADE_INSERT_TAIL(bb, bucket);
63
64    return APR_SUCCESS;
65}
66
67static int char_buffer_read(mgs_char_buffer_t * buffer, char *in, int inl) {
68    if (!buffer->length) {
69        return 0;
70    }
71
72    if (buffer->length > inl) {
73        /* we have have enough to fill the caller's buffer */
74        memmove(in, buffer->value, inl);
75        buffer->value += inl;
76        buffer->length -= inl;
77    } else {
78        /* swallow remainder of the buffer */
79        memmove(in, buffer->value, buffer->length);
80        inl = buffer->length;
81        buffer->value = NULL;
82        buffer->length = 0;
83    }
84
85    return inl;
86}
87
88static int char_buffer_write(mgs_char_buffer_t * buffer, char *in, int inl) {
89    buffer->value = in;
90    buffer->length = inl;
91    return inl;
92}
93
94/**
95 * From mod_ssl / ssl_engine_io.c
96 * This function will read from a brigade and discard the read buckets as it
97 * proceeds.  It will read at most *len bytes.
98 */
99static apr_status_t brigade_consume(apr_bucket_brigade * bb,
100        apr_read_type_e block,
101        char *c, apr_size_t * len) {
102    apr_size_t actual = 0;
103    apr_status_t status = APR_SUCCESS;
104
105    while (!APR_BRIGADE_EMPTY(bb)) {
106        apr_bucket *b = APR_BRIGADE_FIRST(bb);
107        const char *str;
108        apr_size_t str_len;
109        apr_size_t consume;
110
111        /* Justin points out this is an http-ism that might
112         * not fit if brigade_consume is added to APR.  Perhaps
113         * apr_bucket_read(eos_bucket) should return APR_EOF?
114         * Then this becomes mainline instead of a one-off.
115         */
116        if (APR_BUCKET_IS_EOS(b)) {
117            status = APR_EOF;
118            break;
119        }
120
121        /* The reason I'm not offering brigade_consume yet
122         * across to apr-util is that the following call
123         * illustrates how borked that API really is.  For
124         * this sort of case (caller provided buffer) it
125         * would be much more trivial for apr_bucket_consume
126         * to do all the work that follows, based on the
127         * particular characteristics of the bucket we are
128         * consuming here.
129         */
130        status = apr_bucket_read(b, &str, &str_len, block);
131
132        if (status != APR_SUCCESS) {
133            if (APR_STATUS_IS_EOF(status)) {
134                /* This stream bucket was consumed */
135                apr_bucket_delete(b);
136                continue;
137            }
138            break;
139        }
140
141        if (str_len > 0) {
142            /* Do not block once some data has been consumed */
143            block = APR_NONBLOCK_READ;
144
145            /* Assure we don't overflow. */
146            consume =
147                    (str_len + actual >
148                    *len) ? *len - actual : str_len;
149
150            memcpy(c, str, consume);
151
152            c += consume;
153            actual += consume;
154
155            if (consume >= b->length) {
156                /* This physical bucket was consumed */
157                apr_bucket_delete(b);
158            } else {
159                /* Only part of this physical bucket was consumed */
160                b->start += consume;
161                b->length -= consume;
162            }
163        } else if (b->length == 0) {
164            apr_bucket_delete(b);
165        }
166
167        /* This could probably be actual == *len, but be safe from stray
168         * photons. */
169        if (actual >= *len) {
170            break;
171        }
172    }
173
174    *len = actual;
175    return status;
176}
177
178static apr_status_t gnutls_io_input_read(mgs_handle_t * ctxt,
179        char *buf, apr_size_t * len) {
180    apr_size_t wanted = *len;
181    apr_size_t bytes = 0;
182    int rc;
183
184    *len = 0;
185
186    /* If we have something leftover from last time, try that first. */
187    if ((bytes = char_buffer_read(&ctxt->input_cbuf, buf, wanted))) {
188        *len = bytes;
189        if (ctxt->input_mode == AP_MODE_SPECULATIVE) {
190            /* We want to rollback this read. */
191            if (ctxt->input_cbuf.length > 0) {
192                ctxt->input_cbuf.value -= bytes;
193                ctxt->input_cbuf.length += bytes;
194            } else {
195                char_buffer_write(&ctxt->input_cbuf, buf,
196                        (int) bytes);
197            }
198            return APR_SUCCESS;
199        }
200        /* This could probably be *len == wanted, but be safe from stray
201         * photons.
202         */
203        if (*len >= wanted) {
204            return APR_SUCCESS;
205        }
206        if (ctxt->input_mode == AP_MODE_GETLINE) {
207            if (memchr(buf, APR_ASCII_LF, *len)) {
208                return APR_SUCCESS;
209            }
210        } else {
211            /* Down to a nonblock pattern as we have some data already
212             */
213            ctxt->input_block = APR_NONBLOCK_READ;
214        }
215    }
216
217    if (ctxt->session == NULL) {
218        return APR_EGENERAL;
219    }
220
221    while (1) {
222
223        rc = gnutls_record_recv(ctxt->session, buf + bytes,
224                wanted - bytes);
225
226        if (rc > 0) {
227            *len += rc;
228            if (ctxt->input_mode == AP_MODE_SPECULATIVE) {
229                /* We want to rollback this read. */
230                char_buffer_write(&ctxt->input_cbuf, buf,
231                        rc);
232            }
233            return ctxt->input_rc;
234        } else if (rc == 0) {
235            /* If EAGAIN, we will loop given a blocking read,
236             * otherwise consider ourselves at EOF.
237             */
238            if (APR_STATUS_IS_EAGAIN(ctxt->input_rc)
239                    || APR_STATUS_IS_EINTR(ctxt->input_rc)) {
240                /* Already read something, return APR_SUCCESS instead.
241                 * On win32 in particular, but perhaps on other kernels,
242                 * a blocking call isn't 'always' blocking.
243                 */
244                if (*len > 0) {
245                    ctxt->input_rc = APR_SUCCESS;
246                    break;
247                }
248                if (ctxt->input_block == APR_NONBLOCK_READ) {
249                    break;
250                }
251            } else {
252                if (*len > 0) {
253                    ctxt->input_rc = APR_SUCCESS;
254                } else {
255                    ctxt->input_rc = APR_EOF;
256                }
257                break;
258            }
259        } else { /* (rc < 0) */
260
261            if (rc == GNUTLS_E_REHANDSHAKE) {
262                /* A client has asked for a new Hankshake. Currently, we don't do it */
263                ap_log_error(APLOG_MARK, APLOG_INFO,
264                        ctxt->input_rc,
265                        ctxt->c->base_server,
266                        "GnuTLS: Error reading data. Client Requested a New Handshake."
267                        " (%d) '%s'", rc,
268                        gnutls_strerror(rc));
269            } else if (rc == GNUTLS_E_WARNING_ALERT_RECEIVED) {
270                rc = gnutls_alert_get(ctxt->session);
271                ap_log_error(APLOG_MARK, APLOG_INFO,
272                        ctxt->input_rc,
273                        ctxt->c->base_server,
274                        "GnuTLS: Warning Alert From Client: "
275                        " (%d) '%s'", rc,
276                        gnutls_alert_get_name(rc));
277            } else if (rc == GNUTLS_E_FATAL_ALERT_RECEIVED) {
278                rc = gnutls_alert_get(ctxt->session);
279                ap_log_error(APLOG_MARK, APLOG_INFO,
280                        ctxt->input_rc,
281                        ctxt->c->base_server,
282                        "GnuTLS: Fatal Alert From Client: "
283                        "(%d) '%s'", rc,
284                        gnutls_alert_get_name(rc));
285                ctxt->input_rc = APR_EGENERAL;
286                break;
287            } else {
288                /* Some Other Error. Report it. Die. */
289                if (gnutls_error_is_fatal(rc)) {
290                    ap_log_error(APLOG_MARK,
291                            APLOG_INFO,
292                            ctxt->input_rc,
293                            ctxt->c->base_server,
294                            "GnuTLS: Error reading data. (%d) '%s'",
295                            rc,
296                            gnutls_strerror(rc));
297                } else if (*len > 0) {
298                    ctxt->input_rc = APR_SUCCESS;
299                    break;
300                }
301            }
302
303            if (ctxt->input_rc == APR_SUCCESS) {
304                ctxt->input_rc = APR_EGENERAL;
305            }
306            break;
307        }
308    }
309    return ctxt->input_rc;
310}
311
312static apr_status_t gnutls_io_input_getline(mgs_handle_t * ctxt,
313        char *buf, apr_size_t * len) {
314    const char *pos = NULL;
315    apr_status_t status;
316    apr_size_t tmplen = *len, buflen = *len, offset = 0;
317
318    *len = 0;
319
320    while (tmplen > 0) {
321        status = gnutls_io_input_read(ctxt, buf + offset, &tmplen);
322
323        if (status != APR_SUCCESS) {
324            return status;
325        }
326
327        *len += tmplen;
328
329        if ((pos = memchr(buf, APR_ASCII_LF, *len))) {
330            break;
331        }
332
333        offset += tmplen;
334        tmplen = buflen - offset;
335    }
336
337    if (pos) {
338        char *value;
339        int length;
340        apr_size_t bytes = pos - buf;
341
342        bytes += 1;
343        value = buf + bytes;
344        length = *len - bytes;
345
346        char_buffer_write(&ctxt->input_cbuf, value, length);
347
348        *len = bytes;
349    }
350
351    return APR_SUCCESS;
352}
353
354#define HANDSHAKE_MAX_TRIES 1024
355
356static int gnutls_do_handshake(mgs_handle_t * ctxt) {
357    int ret;
358    int errcode;
359    int maxtries = HANDSHAKE_MAX_TRIES;
360
361    if (ctxt->status != 0 || ctxt->session == NULL) {
362        return -1;
363    }
364
365tryagain:
366    do {
367        ret = gnutls_handshake(ctxt->session);
368        maxtries--;
369    } while ((ret == GNUTLS_E_INTERRUPTED || ret == GNUTLS_E_AGAIN)
370            && maxtries > 0);
371
372    if (maxtries < 1) {
373        ctxt->status = -1;
374#if USING_2_1_RECENT
375        ap_log_cerror(APLOG_MARK, APLOG_ERR, 0, ctxt->c,
376                "GnuTLS: Handshake Failed. Hit Maximum Attempts");
377#else
378        ap_log_error(APLOG_MARK, APLOG_ERR, 0,
379                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
385                    (GNUTLS_E_INTERNAL_ERROR, NULL));
386            gnutls_deinit(ctxt->session);
387        }
388        ctxt->session = NULL;
389        return -1;
390    }
391
392    if (ret < 0) {
393        if (ret == GNUTLS_E_WARNING_ALERT_RECEIVED
394                || ret == GNUTLS_E_FATAL_ALERT_RECEIVED) {
395            errcode = gnutls_alert_get(ctxt->session);
396            ap_log_error(APLOG_MARK, APLOG_INFO, 0,
397                    ctxt->c->base_server,
398                    "GnuTLS: Hanshake Alert (%d) '%s'.",
399                    errcode,
400                    gnutls_alert_get_name(errcode));
401        }
402
403        if (!gnutls_error_is_fatal(ret)) {
404            ap_log_error(APLOG_MARK, APLOG_INFO, 0,
405                    ctxt->c->base_server,
406                    "GnuTLS: Non-Fatal Handshake Error: (%d) '%s'",
407                    ret, gnutls_strerror(ret));
408            goto tryagain;
409        }
410#if USING_2_1_RECENT
411        ap_log_cerror(APLOG_MARK, APLOG_INFO, 0, ctxt->c,
412                "GnuTLS: Handshake Failed (%d) '%s'", ret,
413                gnutls_strerror(ret));
414#else
415        ap_log_error(APLOG_MARK, APLOG_INFO, 0,
416                ctxt->c->base_server,
417                "GnuTLS: Handshake Failed (%d) '%s'", ret,
418                gnutls_strerror(ret));
419#endif
420        ctxt->status = -1;
421        if (ctxt->session) {
422            gnutls_alert_send(ctxt->session, GNUTLS_AL_FATAL,
423                    gnutls_error_to_alert(ret,
424                    NULL));
425            gnutls_deinit(ctxt->session);
426        }
427        ctxt->session = NULL;
428        return ret;
429    } else {
430        /* all done with the handshake */
431        ctxt->status = 1;
432        /* If the session was resumed, we did not set the correct
433         * server_rec in ctxt->sc.  Go Find it. (ick!)
434         */
435        if (gnutls_session_is_resumed(ctxt->session)) {
436            mgs_srvconf_rec *sc;
437            sc = mgs_find_sni_server(ctxt->session);
438            if (sc) {
439                ctxt->sc = sc;
440            }
441        }
442        return 0;
443    }
444}
445
446int mgs_rehandshake(mgs_handle_t * ctxt) {
447    int rv;
448
449    if (ctxt->session == NULL)
450        return -1;
451
452    rv = gnutls_rehandshake(ctxt->session);
453
454    if (rv != 0) {
455        /* the client did not want to rehandshake. goodbye */
456        ap_log_error(APLOG_MARK, APLOG_WARNING, 0,
457                ctxt->c->base_server,
458                "GnuTLS: Client Refused Rehandshake request.");
459        return -1;
460    }
461
462    ctxt->status = 0;
463
464    rv = gnutls_do_handshake(ctxt);
465
466    return rv;
467}
468
469apr_status_t mgs_filter_input(ap_filter_t * f,
470        apr_bucket_brigade * bb,
471        ap_input_mode_t mode,
472        apr_read_type_e block, apr_off_t readbytes) {
473    apr_status_t status = APR_SUCCESS;
474    mgs_handle_t *ctxt = (mgs_handle_t *) f->ctx;
475    apr_size_t len = sizeof (ctxt->input_buffer);
476
477    if (f->c->aborted) {
478        apr_bucket *bucket =
479                apr_bucket_eos_create(f->c->bucket_alloc);
480        APR_BRIGADE_INSERT_TAIL(bb, bucket);
481        return APR_ECONNABORTED;
482    }
483
484    if (ctxt->status == 0) {
485        gnutls_do_handshake(ctxt);
486    }
487
488    if (ctxt->status < 0) {
489        return ap_get_brigade(f->next, bb, mode, block, readbytes);
490    }
491
492    /* XXX: we don't currently support anything other than these modes. */
493    if (mode != AP_MODE_READBYTES && mode != AP_MODE_GETLINE &&
494            mode != AP_MODE_SPECULATIVE && mode != AP_MODE_INIT) {
495        return APR_ENOTIMPL;
496    }
497
498    ctxt->input_mode = mode;
499    ctxt->input_block = block;
500
501    if (ctxt->input_mode == AP_MODE_READBYTES ||
502            ctxt->input_mode == AP_MODE_SPECULATIVE) {
503        /* Err. This is bad. readbytes *can* be a 64bit int! len.. is NOT */
504        if (readbytes < len) {
505            len = (apr_size_t) readbytes;
506        }
507        status =
508                gnutls_io_input_read(ctxt, ctxt->input_buffer, &len);
509    } else if (ctxt->input_mode == AP_MODE_GETLINE) {
510        status =
511                gnutls_io_input_getline(ctxt, ctxt->input_buffer,
512                &len);
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
533static ssize_t write_flush(mgs_handle_t * ctxt) {
534    apr_bucket *e;
535
536    if (!(ctxt->output_blen || ctxt->output_length)) {
537        ctxt->output_rc = APR_SUCCESS;
538        return 1;
539    }
540
541    if (ctxt->output_blen) {
542        e = apr_bucket_transient_create(ctxt->output_buffer,
543                ctxt->output_blen,
544                ctxt->output_bb->
545                bucket_alloc);
546        /* we filled this buffer first so add it to the
547         *               * head of the brigade
548         *                               */
549        APR_BRIGADE_INSERT_HEAD(ctxt->output_bb, e);
550        ctxt->output_blen = 0;
551    }
552
553    ctxt->output_length = 0;
554    e = apr_bucket_flush_create(ctxt->output_bb->bucket_alloc);
555    APR_BRIGADE_INSERT_TAIL(ctxt->output_bb, e);
556
557    ctxt->output_rc = ap_pass_brigade(ctxt->output_filter->next,
558            ctxt->output_bb);
559    /* clear the brigade to be ready for next time */
560    apr_brigade_cleanup(ctxt->output_bb);
561
562    return (ctxt->output_rc == APR_SUCCESS) ? 1 : -1;
563}
564
565apr_status_t mgs_filter_output(ap_filter_t * f, apr_bucket_brigade * bb) {
566    apr_size_t ret;
567    mgs_handle_t *ctxt = (mgs_handle_t *) f->ctx;
568    apr_status_t status = APR_SUCCESS;
569    apr_read_type_e rblock = APR_NONBLOCK_READ;
570
571    /* Block SIGPIPE Signals */
572    status = apr_signal_block(SIGPIPE); 
573    if(status != APR_SUCCESS) {
574        /* error sending output */
575        ap_log_error(APLOG_MARK,APLOG_INFO,ctxt->output_rc,ctxt->c->base_server,
576                "GnuTLS: Error Blocking SIGPIPE Signal!");       
577        return status;
578    }
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.