source: mod_gnutls/src/gnutls_io.c @ fe0c93a

asynciodebian/masterdebian/stretch-backportsjessie-backportsmainmsvaproxy-ticketupstream
Last change on this file since fe0c93a was 485d28e, checked in by Dash Shendy <neuromancer@…>, 12 years ago

Major Legacy Code Cleanup

  • Property mode set to 100644
File size: 19.6 KB
Line 
1/**
2 *  Copyright 2004-2005 Paul Querna
3 *
4 *  Licensed under the Apache License, Version 2.0 (the "License");
5 *  you may not use this file except in compliance with the License.
6 *  You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 *  Unless required by applicable law or agreed to in writing, software
11 *  distributed under the License is distributed on an "AS IS" BASIS,
12 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 *  See the License for the specific language governing permissions and
14 *  limitations under the License.
15 *
16 */
17
18#include "mod_gnutls.h"
19
20/**
21 * Describe how the GnuTLS Filter system works here
22 *  - Basicly the same as what mod_ssl does with OpenSSL.
23 *
24 */
25
26#define HTTP_ON_HTTPS_PORT \
27    "GET /" CRLF
28
29#define HTTP_ON_HTTPS_PORT_BUCKET(alloc) \
30    apr_bucket_immortal_create(HTTP_ON_HTTPS_PORT, \
31                               sizeof(HTTP_ON_HTTPS_PORT) - 1, \
32                               alloc)
33
34static apr_status_t gnutls_io_filter_error(ap_filter_t * f,
35                                           apr_bucket_brigade * bb,
36                                           apr_status_t status)
37{
38        mgs_handle_t *ctxt = (mgs_handle_t *) f->ctx;
39        apr_bucket *bucket;
40
41        switch (status) {
42        case HTTP_BAD_REQUEST:
43                /* log the situation */
44                ap_log_error(APLOG_MARK, APLOG_INFO, 0,
45                             f->c->base_server,
46                             "GnuTLS handshake failed: HTTP spoken on HTTPS port; "
47                             "trying to send HTML error page");
48
49                ctxt->status = -1;
50
51                /* fake the request line */
52                bucket = HTTP_ON_HTTPS_PORT_BUCKET(f->c->bucket_alloc);
53                break;
54
55        default:
56                return status;
57        }
58
59        APR_BRIGADE_INSERT_TAIL(bb, bucket);
60        bucket = apr_bucket_eos_create(f->c->bucket_alloc);
61        APR_BRIGADE_INSERT_TAIL(bb, bucket);
62
63        return APR_SUCCESS;
64}
65
66static int char_buffer_read(mgs_char_buffer_t * buffer, char *in, int inl)
67{
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{
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{
104        apr_size_t actual = 0;
105        apr_status_t status = APR_SUCCESS;
106
107        while (!APR_BRIGADE_EMPTY(bb)) {
108                apr_bucket *b = APR_BRIGADE_FIRST(bb);
109                const char *str;
110                apr_size_t str_len;
111                apr_size_t consume;
112
113                /* Justin points out this is an http-ism that might
114                 * not fit if brigade_consume is added to APR.  Perhaps
115                 * apr_bucket_read(eos_bucket) should return APR_EOF?
116                 * Then this becomes mainline instead of a one-off.
117                 */
118                if (APR_BUCKET_IS_EOS(b)) {
119                        status = APR_EOF;
120                        break;
121                }
122
123                /* The reason I'm not offering brigade_consume yet
124                 * across to apr-util is that the following call
125                 * illustrates how borked that API really is.  For
126                 * this sort of case (caller provided buffer) it
127                 * would be much more trivial for apr_bucket_consume
128                 * to do all the work that follows, based on the
129                 * particular characteristics of the bucket we are
130                 * consuming here.
131                 */
132                status = apr_bucket_read(b, &str, &str_len, block);
133
134                if (status != APR_SUCCESS) {
135                        if (APR_STATUS_IS_EOF(status)) {
136                                /* This stream bucket was consumed */
137                                apr_bucket_delete(b);
138                                continue;
139                        }
140                        break;
141                }
142
143                if (str_len > 0) {
144                        /* Do not block once some data has been consumed */
145                        block = APR_NONBLOCK_READ;
146
147                        /* Assure we don't overflow. */
148                        consume =
149                            (str_len + actual >
150                             *len) ? *len - actual : str_len;
151
152                        memcpy(c, str, consume);
153
154                        c += consume;
155                        actual += consume;
156
157                        if (consume >= b->length) {
158                                /* This physical bucket was consumed */
159                                apr_bucket_delete(b);
160                        } else {
161                                /* Only part of this physical bucket was consumed */
162                                b->start += consume;
163                                b->length -= consume;
164                        }
165                } else if (b->length == 0) {
166                        apr_bucket_delete(b);
167                }
168
169                /* This could probably be actual == *len, but be safe from stray
170                 * photons. */
171                if (actual >= *len) {
172                        break;
173                }
174        }
175
176        *len = actual;
177        return status;
178}
179
180
181static apr_status_t gnutls_io_input_read(mgs_handle_t * ctxt,
182                                         char *buf, apr_size_t * len)
183{
184        apr_size_t wanted = *len;
185        apr_size_t bytes = 0;
186        int rc;
187
188        *len = 0;
189
190        /* If we have something leftover from last time, try that first. */
191        if ((bytes = char_buffer_read(&ctxt->input_cbuf, buf, wanted))) {
192                *len = bytes;
193                if (ctxt->input_mode == AP_MODE_SPECULATIVE) {
194                        /* We want to rollback this read. */
195                        if (ctxt->input_cbuf.length > 0) {
196                                ctxt->input_cbuf.value -= bytes;
197                                ctxt->input_cbuf.length += bytes;
198                        } else {
199                                char_buffer_write(&ctxt->input_cbuf, buf,
200                                                  (int) bytes);
201                        }
202                        return APR_SUCCESS;
203                }
204                /* This could probably be *len == wanted, but be safe from stray
205                 * photons.
206                 */
207                if (*len >= wanted) {
208                        return APR_SUCCESS;
209                }
210                if (ctxt->input_mode == AP_MODE_GETLINE) {
211                        if (memchr(buf, APR_ASCII_LF, *len)) {
212                                return APR_SUCCESS;
213                        }
214                } else {
215                        /* Down to a nonblock pattern as we have some data already
216                         */
217                        ctxt->input_block = APR_NONBLOCK_READ;
218                }
219        }
220
221        if (ctxt->session == NULL) {
222                return APR_EGENERAL;
223        }
224
225        while (1) {
226
227                rc = gnutls_record_recv(ctxt->session, buf + bytes,
228                                        wanted - bytes);
229
230                if (rc > 0) {
231                        *len += rc;
232                        if (ctxt->input_mode == AP_MODE_SPECULATIVE) {
233                                /* We want to rollback this read. */
234                                char_buffer_write(&ctxt->input_cbuf, buf,
235                                                  rc);
236                        }
237                        return ctxt->input_rc;
238                } else if (rc == 0) {
239                        /* If EAGAIN, we will loop given a blocking read,
240                         * otherwise consider ourselves at EOF.
241                         */
242                        if (APR_STATUS_IS_EAGAIN(ctxt->input_rc)
243                            || APR_STATUS_IS_EINTR(ctxt->input_rc)) {
244                                /* Already read something, return APR_SUCCESS instead.
245                                 * On win32 in particular, but perhaps on other kernels,
246                                 * a blocking call isn't 'always' blocking.
247                                 */
248                                if (*len > 0) {
249                                        ctxt->input_rc = APR_SUCCESS;
250                                        break;
251                                }
252                                if (ctxt->input_block == APR_NONBLOCK_READ) {
253                                        break;
254                                }
255                        } else {
256                                if (*len > 0) {
257                                        ctxt->input_rc = APR_SUCCESS;
258                                } else {
259                                        ctxt->input_rc = APR_EOF;
260                                }
261                                break;
262                        }
263                } else {        /* (rc < 0) */
264
265                        if (rc == GNUTLS_E_REHANDSHAKE) {
266                                /* A client has asked for a new Hankshake. Currently, we don't do it */
267                                ap_log_error(APLOG_MARK, APLOG_INFO,
268                                             ctxt->input_rc,
269                                             ctxt->c->base_server,
270                                             "GnuTLS: Error reading data. Client Requested a New Handshake."
271                                             " (%d) '%s'", rc,
272                                             gnutls_strerror(rc));
273                        } else if (rc == GNUTLS_E_WARNING_ALERT_RECEIVED) {
274                                rc = gnutls_alert_get(ctxt->session);
275                                ap_log_error(APLOG_MARK, APLOG_INFO,
276                                             ctxt->input_rc,
277                                             ctxt->c->base_server,
278                                             "GnuTLS: Warning Alert From Client: "
279                                             " (%d) '%s'", rc,
280                                             gnutls_alert_get_name(rc));
281                        } else if (rc == GNUTLS_E_FATAL_ALERT_RECEIVED) {
282                                rc = gnutls_alert_get(ctxt->session);
283                                ap_log_error(APLOG_MARK, APLOG_INFO,
284                                             ctxt->input_rc,
285                                             ctxt->c->base_server,
286                                             "GnuTLS: Fatal Alert From Client: "
287                                             "(%d) '%s'", rc,
288                                             gnutls_alert_get_name(rc));
289                                ctxt->input_rc = APR_EGENERAL;
290                                break;
291                        } else {
292                                /* Some Other Error. Report it. Die. */
293                                if (gnutls_error_is_fatal(rc)) {
294                                        ap_log_error(APLOG_MARK,
295                                                     APLOG_INFO,
296                                                     ctxt->input_rc,
297                                                     ctxt->c->base_server,
298                                                     "GnuTLS: Error reading data. (%d) '%s'",
299                                                     rc,
300                                                     gnutls_strerror(rc));
301                                } else if (*len > 0) {
302                                        ctxt->input_rc = APR_SUCCESS;
303                                        break;
304                                }
305                        }
306
307                        if (ctxt->input_rc == APR_SUCCESS) {
308                                ctxt->input_rc = APR_EGENERAL;
309                        }
310                        break;
311                }
312        }
313        return ctxt->input_rc;
314}
315
316static apr_status_t gnutls_io_input_getline(mgs_handle_t * ctxt,
317                                            char *buf, apr_size_t * len)
318{
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
360static int gnutls_do_handshake(mgs_handle_t * ctxt)
361{
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
370      tryagain:
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{
453        int rv;
454
455        if (ctxt->session == NULL)
456                return -1;
457
458        rv = gnutls_rehandshake(ctxt->session);
459
460        if (rv != 0) {
461                /* the client did not want to rehandshake. goodbye */
462                ap_log_error(APLOG_MARK, APLOG_WARNING, 0,
463                             ctxt->c->base_server,
464                             "GnuTLS: Client Refused Rehandshake request.");
465                return -1;
466        }
467
468        ctxt->status = 0;
469
470        rv = gnutls_do_handshake(ctxt);
471
472        return rv;
473}
474
475
476apr_status_t mgs_filter_input(ap_filter_t * f,
477                              apr_bucket_brigade * bb,
478                              ap_input_mode_t mode,
479                              apr_read_type_e block, apr_off_t readbytes)
480{
481        apr_status_t status = APR_SUCCESS;
482        mgs_handle_t *ctxt = (mgs_handle_t *) f->ctx;
483        apr_size_t len = sizeof(ctxt->input_buffer);
484
485        if (f->c->aborted) {
486                apr_bucket *bucket =
487                    apr_bucket_eos_create(f->c->bucket_alloc);
488                APR_BRIGADE_INSERT_TAIL(bb, bucket);
489                return APR_ECONNABORTED;
490        }
491
492        if (ctxt->status == 0) {
493                gnutls_do_handshake(ctxt);
494        }
495
496        if (ctxt->status < 0) {
497                return ap_get_brigade(f->next, bb, mode, block, readbytes);
498        }
499
500        /* XXX: we don't currently support anything other than these modes. */
501        if (mode != AP_MODE_READBYTES && mode != AP_MODE_GETLINE &&
502            mode != AP_MODE_SPECULATIVE && mode != AP_MODE_INIT) {
503                return APR_ENOTIMPL;
504        }
505
506        ctxt->input_mode = mode;
507        ctxt->input_block = block;
508
509        if (ctxt->input_mode == AP_MODE_READBYTES ||
510            ctxt->input_mode == AP_MODE_SPECULATIVE) {
511                /* Err. This is bad. readbytes *can* be a 64bit int! len.. is NOT */
512                if (readbytes < len) {
513                        len = (apr_size_t) readbytes;
514                }
515                status =
516                    gnutls_io_input_read(ctxt, ctxt->input_buffer, &len);
517        } else if (ctxt->input_mode == AP_MODE_GETLINE) {
518                status =
519                    gnutls_io_input_getline(ctxt, ctxt->input_buffer,
520                                            &len);
521        } else {
522                /* We have no idea what you are talking about, so return an error. */
523                return APR_ENOTIMPL;
524        }
525
526        if (status != APR_SUCCESS) {
527                return gnutls_io_filter_error(f, bb, status);
528        }
529
530        /* Create a transient bucket out of the decrypted data. */
531        if (len > 0) {
532                apr_bucket *bucket =
533                    apr_bucket_transient_create(ctxt->input_buffer, len,
534                                                f->c->bucket_alloc);
535                APR_BRIGADE_INSERT_TAIL(bb, bucket);
536        }
537
538        return status;
539}
540
541static ssize_t write_flush(mgs_handle_t * ctxt)
542{
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{
576        apr_size_t ret;
577        mgs_handle_t *ctxt = (mgs_handle_t *) f->ctx;
578        apr_status_t status = APR_SUCCESS;
579        apr_read_type_e rblock = APR_NONBLOCK_READ;
580
581        if (f->c->aborted) {
582                apr_brigade_cleanup(bb);
583                return APR_ECONNABORTED;
584        }
585
586        if (ctxt->status == 0) {
587                gnutls_do_handshake(ctxt);
588        }
589
590        if (ctxt->status < 0) {
591                return ap_pass_brigade(f->next, bb);
592        }
593
594        while (!APR_BRIGADE_EMPTY(bb)) {
595                apr_bucket *bucket = APR_BRIGADE_FIRST(bb);
596
597                if (APR_BUCKET_IS_EOS(bucket)) {
598                        return ap_pass_brigade(f->next, bb);
599                } else if (APR_BUCKET_IS_FLUSH(bucket)) {
600                /* Try Flush */
601                        if( write_flush(ctxt) < 0) {
602                        /* Flush Error */
603                                return ctxt->output_rc;
604                        }
605                        /* cleanup! */
606                        apr_bucket_delete(bucket);
607                } else if (AP_BUCKET_IS_EOC(bucket)) {
608                /* End Of Connection */
609                        if (ctxt->session != NULL) {
610                        /* Try A Clean Shutdown */
611                                do {
612                                         ret = gnutls_bye( ctxt->session, 
613                                                 GNUTLS_SHUT_WR);
614                                } while(ret == GNUTLS_E_INTERRUPTED || 
615                                        ret == GNUTLS_E_AGAIN);
616                        /* De-Initialize Session */
617                                gnutls_deinit(ctxt->session);
618                                ctxt->session = NULL;
619                        }
620                        /* Pass next brigade! */
621                        return ap_pass_brigade(f->next, bb);
622                } else {
623                        /* filter output */
624                        const char *data;
625                        apr_size_t len;
626
627                        status =
628                            apr_bucket_read(bucket, &data, &len, rblock);
629
630                        if (APR_STATUS_IS_EAGAIN(status)) {
631                        /* No data available so Flush! */
632                                if (write_flush(ctxt) < 0) {
633                                        return ctxt->output_rc;
634                                }
635                        /* Try again with a blocking read. */
636                                rblock = APR_BLOCK_READ;
637                                continue;       
638                        }
639
640                        rblock = APR_NONBLOCK_READ;
641
642                        if (!APR_STATUS_IS_EOF(status)
643                            && (status != APR_SUCCESS)) {
644                                return status;
645                        }
646
647                        if (len > 0) {
648
649                                if (ctxt->session == NULL) {
650                                        ret = GNUTLS_E_INVALID_REQUEST;
651                                } else {
652                                        do {
653                                                ret =
654                                                    gnutls_record_send
655                                                    (ctxt->session, data,
656                                                     len);
657                                        }
658                                        while (ret == GNUTLS_E_INTERRUPTED
659                                               || ret == GNUTLS_E_AGAIN);
660                                }
661
662                                if (ret < 0) {
663                                        /* error sending output */
664                                        ap_log_error(APLOG_MARK,
665                                                     APLOG_INFO,
666                                                     ctxt->output_rc,
667                                                     ctxt->c->base_server,
668                                                     "GnuTLS: Error writing data."
669                                                     " (%d) '%s'",
670                                                     (int) ret,
671                                                     gnutls_strerror(ret));
672                                        if (ctxt->output_rc == APR_SUCCESS) {
673                                                ctxt->output_rc =
674                                                    APR_EGENERAL;
675                                                return ctxt->output_rc;
676                                        }
677                                } else if (ret != len) {
678                                        /* Not able to send the entire bucket,
679                                           split it and send it again. */
680                                        apr_bucket_split(bucket, ret);
681                                }
682                        }
683
684                        apr_bucket_delete(bucket);
685                }
686        }
687
688        return status;
689}
690
691ssize_t mgs_transport_read(gnutls_transport_ptr_t ptr,
692                           void *buffer, size_t len)
693{
694        mgs_handle_t *ctxt = ptr;
695        apr_status_t rc;
696        apr_size_t in = len;
697        apr_read_type_e block = ctxt->input_block;
698
699        ctxt->input_rc = APR_SUCCESS;
700
701        /* If Len = 0, we don't do anything. */
702        if (!len || buffer == NULL) {
703                return 0;
704        }
705        if (!ctxt->input_bb) {
706                ctxt->input_rc = APR_EOF;
707                return -1;
708        }
709
710        if (APR_BRIGADE_EMPTY(ctxt->input_bb)) {
711
712                rc = ap_get_brigade(ctxt->input_filter->next,
713                                    ctxt->input_bb, AP_MODE_READBYTES,
714                                    ctxt->input_block, in);
715
716                /* Not a problem, there was simply no data ready yet.
717                 */
718                if (APR_STATUS_IS_EAGAIN(rc) || APR_STATUS_IS_EINTR(rc)
719                    || (rc == APR_SUCCESS
720                        && APR_BRIGADE_EMPTY(ctxt->input_bb))) {
721
722                        if (APR_STATUS_IS_EOF(ctxt->input_rc)) {
723                                return 0;
724                        } else {
725                                if (ctxt->session)
726                                        gnutls_transport_set_errno(ctxt->
727                                                                   session,
728                                                                   EINTR);
729                                return -1;
730                        }
731                }
732
733
734                if (rc != APR_SUCCESS) {
735                        /* Unexpected errors discard the brigade */
736                        apr_brigade_cleanup(ctxt->input_bb);
737                        ctxt->input_bb = NULL;
738                        return -1;
739                }
740        }
741
742        ctxt->input_rc =
743            brigade_consume(ctxt->input_bb, block, buffer, &len);
744
745        if (ctxt->input_rc == APR_SUCCESS) {
746                return (ssize_t) len;
747        }
748
749        if (APR_STATUS_IS_EAGAIN(ctxt->input_rc)
750            || APR_STATUS_IS_EINTR(ctxt->input_rc)) {
751                if (len == 0) {
752                        if (ctxt->session)
753                                gnutls_transport_set_errno(ctxt->session,
754                                                           EINTR);
755                        return -1;
756                }
757
758                return (ssize_t) len;
759        }
760
761        /* Unexpected errors and APR_EOF clean out the brigade.
762         * Subsequent calls will return APR_EOF.
763         */
764        apr_brigade_cleanup(ctxt->input_bb);
765        ctxt->input_bb = NULL;
766
767        if (APR_STATUS_IS_EOF(ctxt->input_rc) && len) {
768                /* Provide the results of this read pass,
769                 * without resetting the BIO retry_read flag
770                 */
771                return (ssize_t) len;
772        }
773
774        return -1;
775}
776
777ssize_t mgs_transport_write(gnutls_transport_ptr_t ptr,
778                            const void *buffer, size_t len)
779{
780        mgs_handle_t *ctxt = ptr;
781
782        /* pass along the encrypted data
783         * need to flush since we're using SSL's malloc-ed buffer
784         * which will be overwritten once we leave here
785         */
786        apr_bucket *bucket = apr_bucket_transient_create(buffer, len,
787                                                         ctxt->output_bb->
788                                                         bucket_alloc);
789        ctxt->output_length += len;
790        APR_BRIGADE_INSERT_TAIL(ctxt->output_bb, bucket);
791
792        if (write_flush(ctxt) < 0) {
793                return -1;
794        }
795        return len;
796}
Note: See TracBrowser for help on using the repository browser.