source: mod_gnutls/src/gnutls_io.c @ b59327c

debian/masterdebian/stretch-backportsjessie-backportsmsvaupstream
Last change on this file since b59327c was e02dd8c, checked in by Nikos Mavrogiannopoulos <nmav@…>, 9 years ago

indented code

  • Property mode set to 100644
File size: 19.0 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
541apr_status_t mgs_filter_output(ap_filter_t * f, apr_bucket_brigade * bb)
542{
543        apr_size_t ret;
544        apr_bucket *e;
545        mgs_handle_t *ctxt = (mgs_handle_t *) f->ctx;
546        apr_status_t status = APR_SUCCESS;
547        apr_read_type_e rblock = APR_NONBLOCK_READ;
548
549        if (f->c->aborted) {
550                apr_brigade_cleanup(bb);
551                return APR_ECONNABORTED;
552        }
553
554        if (ctxt->status == 0) {
555                gnutls_do_handshake(ctxt);
556        }
557
558        if (ctxt->status < 0) {
559                return ap_pass_brigade(f->next, bb);
560        }
561
562        while (!APR_BRIGADE_EMPTY(bb)) {
563                apr_bucket *bucket = APR_BRIGADE_FIRST(bb);
564
565                if (AP_BUCKET_IS_EOC(bucket)) {
566                        if (ctxt->session != NULL) {
567                                do {
568                                        ret =
569                                            gnutls_bye(ctxt->session,
570                                                       GNUTLS_SHUT_WR);
571                                } while (ret == GNUTLS_E_INTERRUPTED
572                                         || ret == GNUTLS_E_AGAIN);
573                        }
574
575                        apr_bucket_copy(bucket, &e);
576                        APR_BRIGADE_INSERT_TAIL(ctxt->output_bb, e);
577
578                        if ((status =
579                             ap_pass_brigade(f->next,
580                                             ctxt->output_bb)) !=
581                            APR_SUCCESS) {
582                                apr_brigade_cleanup(ctxt->output_bb);
583                                return status;
584                        }
585
586                        apr_brigade_cleanup(ctxt->output_bb);
587                        if (ctxt->session) {
588                                gnutls_deinit(ctxt->session);
589                                ctxt->session = NULL;
590                        }
591                        continue;
592                } else if (APR_BUCKET_IS_FLUSH(bucket)
593                           || APR_BUCKET_IS_EOS(bucket)) {
594
595                        apr_bucket_copy(bucket, &e);
596                        APR_BRIGADE_INSERT_TAIL(ctxt->output_bb, e);
597                        if ((status =
598                             ap_pass_brigade(f->next,
599                                             bb)) != APR_SUCCESS) {
600                                apr_brigade_cleanup(ctxt->output_bb);
601                                return status;
602                        }
603
604                        apr_brigade_cleanup(ctxt->output_bb);
605                        continue;
606                } else {
607                        /* filter output */
608                        const char *data;
609                        apr_size_t len;
610
611                        status =
612                            apr_bucket_read(bucket, &data, &len, rblock);
613
614                        if (APR_STATUS_IS_EAGAIN(status)) {
615                                rblock = APR_BLOCK_READ;
616                                continue;       /* and try again with a blocking read. */
617                        }
618
619                        rblock = APR_NONBLOCK_READ;
620
621                        if (!APR_STATUS_IS_EOF(status)
622                            && (status != APR_SUCCESS)) {
623                                break;
624                        }
625
626                        if (len > 0) {
627
628                                if (ctxt->session == NULL) {
629                                        ret = GNUTLS_E_INVALID_REQUEST;
630                                } else {
631                                        do {
632                                                ret =
633                                                    gnutls_record_send
634                                                    (ctxt->session, data,
635                                                     len);
636                                        }
637                                        while (ret == GNUTLS_E_INTERRUPTED
638                                               || ret == GNUTLS_E_AGAIN);
639                                }
640
641                                if (ret < 0) {
642                                        /* error sending output */
643                                        ap_log_error(APLOG_MARK,
644                                                     APLOG_INFO,
645                                                     ctxt->output_rc,
646                                                     ctxt->c->base_server,
647                                                     "GnuTLS: Error writing data."
648                                                     " (%d) '%s'",
649                                                     (int) ret,
650                                                     gnutls_strerror(ret));
651                                        if (ctxt->output_rc == APR_SUCCESS) {
652                                                ctxt->output_rc =
653                                                    APR_EGENERAL;
654                                        }
655                                } else if (ret != len) {
656                                        /* Not able to send the entire bucket,
657                                           split it and send it again. */
658                                        apr_bucket_split(bucket, ret);
659                                }
660                        }
661
662                        apr_bucket_delete(bucket);
663
664                        if (ctxt->output_rc != APR_SUCCESS) {
665                                break;
666                        }
667                }
668        }
669
670        return status;
671}
672
673ssize_t mgs_transport_read(gnutls_transport_ptr_t ptr,
674                           void *buffer, size_t len)
675{
676        mgs_handle_t *ctxt = ptr;
677        apr_status_t rc;
678        apr_size_t in = len;
679        apr_read_type_e block = ctxt->input_block;
680
681        ctxt->input_rc = APR_SUCCESS;
682
683        /* If Len = 0, we don't do anything. */
684        if (!len)
685                return 0;
686
687        if (!ctxt->input_bb) {
688                ctxt->input_rc = APR_EOF;
689                return -1;
690        }
691
692        if (APR_BRIGADE_EMPTY(ctxt->input_bb)) {
693
694                rc = ap_get_brigade(ctxt->input_filter->next,
695                                    ctxt->input_bb, AP_MODE_READBYTES,
696                                    ctxt->input_block, in);
697
698                /* Not a problem, there was simply no data ready yet.
699                 */
700                if (APR_STATUS_IS_EAGAIN(rc) || APR_STATUS_IS_EINTR(rc)
701                    || (rc == APR_SUCCESS
702                        && APR_BRIGADE_EMPTY(ctxt->input_bb))) {
703
704                        if (APR_STATUS_IS_EOF(ctxt->input_rc)) {
705                                return 0;
706                        } else {
707                                if (ctxt->session)
708                                        gnutls_transport_set_errno(ctxt->
709                                                                   session,
710                                                                   EINTR);
711                                return -1;
712                        }
713                }
714
715
716                if (rc != APR_SUCCESS) {
717                        /* Unexpected errors discard the brigade */
718                        apr_brigade_cleanup(ctxt->input_bb);
719                        ctxt->input_bb = NULL;
720                        return -1;
721                }
722        }
723
724        ctxt->input_rc =
725            brigade_consume(ctxt->input_bb, block, buffer, &len);
726
727        if (ctxt->input_rc == APR_SUCCESS) {
728                return (ssize_t) len;
729        }
730
731        if (APR_STATUS_IS_EAGAIN(ctxt->input_rc)
732            || APR_STATUS_IS_EINTR(ctxt->input_rc)) {
733                if (len == 0) {
734                        if (ctxt->session)
735                                gnutls_transport_set_errno(ctxt->session,
736                                                           EINTR);
737                        return -1;
738                }
739
740                return (ssize_t) len;
741        }
742
743        /* Unexpected errors and APR_EOF clean out the brigade.
744         * Subsequent calls will return APR_EOF.
745         */
746        apr_brigade_cleanup(ctxt->input_bb);
747        ctxt->input_bb = NULL;
748
749        if (APR_STATUS_IS_EOF(ctxt->input_rc) && len) {
750                /* Provide the results of this read pass,
751                 * without resetting the BIO retry_read flag
752                 */
753                return (ssize_t) len;
754        }
755
756        return -1;
757}
758
759
760static ssize_t write_flush(mgs_handle_t * ctxt)
761{
762        apr_bucket *e;
763
764        if (!(ctxt->output_blen || ctxt->output_length)) {
765                ctxt->output_rc = APR_SUCCESS;
766                return 1;
767        }
768
769        if (ctxt->output_blen) {
770                e = apr_bucket_transient_create(ctxt->output_buffer,
771                                                ctxt->output_blen,
772                                                ctxt->output_bb->
773                                                bucket_alloc);
774                /* we filled this buffer first so add it to the
775                 * head of the brigade
776                 */
777                APR_BRIGADE_INSERT_HEAD(ctxt->output_bb, e);
778                ctxt->output_blen = 0;
779        }
780
781        ctxt->output_length = 0;
782        e = apr_bucket_flush_create(ctxt->output_bb->bucket_alloc);
783        APR_BRIGADE_INSERT_TAIL(ctxt->output_bb, e);
784
785        ctxt->output_rc = ap_pass_brigade(ctxt->output_filter->next,
786                                          ctxt->output_bb);
787        /* clear the brigade to be ready for next time */
788        apr_brigade_cleanup(ctxt->output_bb);
789
790        return (ctxt->output_rc == APR_SUCCESS) ? 1 : -1;
791}
792
793ssize_t mgs_transport_write(gnutls_transport_ptr_t ptr,
794                            const void *buffer, size_t len)
795{
796        mgs_handle_t *ctxt = ptr;
797
798        /* pass along the encrypted data
799         * need to flush since we're using SSL's malloc-ed buffer
800         * which will be overwritten once we leave here
801         */
802        apr_bucket *bucket = apr_bucket_transient_create(buffer, len,
803                                                         ctxt->output_bb->
804                                                         bucket_alloc);
805        ctxt->output_length += len;
806        APR_BRIGADE_INSERT_TAIL(ctxt->output_bb, bucket);
807
808        if (write_flush(ctxt) < 0) {
809                return -1;
810        }
811        return len;
812}
Note: See TracBrowser for help on using the repository browser.