source: mod_gnutls/src/gnutls_io.c @ beb14d9

debian/masterdebian/stretch-backportsjessie-backportsupstream
Last change on this file since beb14d9 was beb14d9, checked in by Thomas Klute <thomas2.klute@…>, 5 years ago

Proof of concept: Support for proxy back end connections using TLS

This commit enables TLS on proxy back end connections if requested from
mod_proxy. Since mod_gnutls acts as client instead of server on proxy
back end connections, TLS session setup is quite different.

Note that this implementation is not finished, in particular the proxy
back end connection is hard coded to use the same X.509 credentials as
the server side, which severely restricts usable certificate
combinations.

Some typos in comments and an error message related to TLS handshake are
fixed as well.

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