source: mod_gnutls/src/gnutls_io.c @ dfd5837

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

Log possible error messages from gnutls_bye

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