source: mod_gnutls/src/gnutls_io.c @ 60cf11c

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

Proxy (TLS termination) patch

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

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