~ubuntu-branches/ubuntu/saucy/mod-gnutls/saucy

« back to all changes in this revision

Viewing changes to src/gnutls_io.c

  • Committer: Bazaar Package Importer
  • Author(s): Jack Bates
  • Date: 2008-03-11 19:08:46 UTC
  • Revision ID: james.westby@ubuntu.com-20080311190846-armokauo0lg7omu7
Tags: upstream-0.5.1
ImportĀ upstreamĀ versionĀ 0.5.1

Show diffs side-by-side

added added

removed removed

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