source: mod_gnutls/src/gnutls_io.c @ 694fc04

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

mod_proxy support

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