source: mod_gnutls/src/gnutls_io.c @ 2e12226

debian/masterdebian/stretch-backportsjessie-backportsmsvaupstream
Last change on this file since 2e12226 was 2e12226, checked in by Paul Querna <chip@…>, 15 years ago

rename structures.
properly prefix all non-static functions with mod_gnutls_
fix build for GnuTLS 1.0.X. (redefine the changed structure names)

  • Property mode set to 100644
File size: 9.4 KB
Line 
1/* ====================================================================
2 *  Copyright 2004 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 *  - It is basicly the same as what mod_ssl uses in that respect.
23 */
24
25apr_status_t mod_gnutls_filter_input(ap_filter_t * f,
26                                     apr_bucket_brigade * bb,
27                                     ap_input_mode_t mode,
28                                     apr_read_type_e block,
29                                     apr_off_t readbytes)
30{
31    apr_bucket *b;
32    apr_status_t status = APR_SUCCESS;
33    mod_gnutls_handle_t *ctxt = (mod_gnutls_handle_t *) f->ctx;
34
35    if (f->c->aborted) {
36        apr_bucket *bucket = apr_bucket_eos_create(f->c->bucket_alloc);
37        APR_BRIGADE_INSERT_TAIL(bb, bucket);
38        return APR_ECONNABORTED;
39    }
40
41#if 0
42    for (b = APR_BRIGADE_FIRST(bb);
43         b != APR_BRIGADE_SENTINEL(bb); b = APR_BUCKET_NEXT(b)) {
44        if (APR_BUCKET_IS_EOS(b)) {
45            /* end of connection */
46        }
47        else if (apr_bucket_read(b, &buf, &bytes, APR_BLOCK_READ)
48                 == APR_SUCCESS) {
49            /* more data */
50        }
51    }
52#endif
53    return status;
54}
55
56#define GNUTLS_HANDSHAKE_ATTEMPTS 10
57
58apr_status_t mod_gnutls_filter_output(ap_filter_t * f,
59                                      apr_bucket_brigade * bb)
60{
61    int ret, i;
62    const char *buf = 0;
63    apr_size_t bytes = 0;
64    mod_gnutls_handle_t *ctxt = (mod_gnutls_handle_t *) f->ctx;
65    apr_status_t status = APR_SUCCESS;
66    apr_read_type_e rblock = APR_NONBLOCK_READ;
67
68    if (f->c->aborted) {
69        apr_brigade_cleanup(bb);
70        return APR_ECONNABORTED;
71    }
72
73    if (ctxt->status == 0) {
74        for (i = GNUTLS_HANDSHAKE_ATTEMPTS; i > 0; i--) {
75            ret = gnutls_handshake(ctxt->session);
76
77            if (ret == GNUTLS_E_INTERRUPTED || ret == GNUTLS_E_AGAIN) {
78                continue;
79            }
80
81            if (ret < 0) {
82                if (ret == GNUTLS_E_WARNING_ALERT_RECEIVED
83                    || ret == GNUTLS_E_FATAL_ALERT_RECEIVED) {
84                    ret = gnutls_alert_get(ctxt->session);
85                    ap_log_error(APLOG_MARK, APLOG_ERR, 0, f->c->base_server,
86                                 "GnuTLS: Hanshake Alert (%d) '%s'.\n", ret,
87                                 gnutls_alert_get_name(ret));
88                }
89
90                if (gnutls_error_is_fatal(ret) != 0) {
91                    gnutls_deinit(ctxt->session);
92                    ap_log_error(APLOG_MARK, APLOG_ERR, 0, f->c->base_server,
93                                 "GnuTLS: Handshake Failed (%d) '%s'", ret,
94                                 gnutls_strerror(ret));
95                    ctxt->status = -1;
96                    break;
97                }
98            }
99            else {
100                ctxt->status = 1;
101                break;          /* all done with the handshake */
102            }
103        }
104    }
105
106    if (ctxt->status < 0) {
107        return ap_pass_brigade(f->next, bb);
108    }
109
110    while (!APR_BRIGADE_EMPTY(bb)) {
111        apr_bucket *bucket = APR_BRIGADE_FIRST(bb);
112        if (APR_BUCKET_IS_EOS(bucket) || APR_BUCKET_IS_FLUSH(bucket)) {
113            /** TODO: GnuTLS doesn't have a special flush method? **/
114            if ((status = ap_pass_brigade(f->next, bb)) != APR_SUCCESS) {
115                return status;
116            }
117            break;
118        }
119        else if (AP_BUCKET_IS_EOC(bucket)) {
120            gnutls_bye(ctxt->session, GNUTLS_SHUT_WR);
121
122            if ((status = ap_pass_brigade(f->next, bb)) != APR_SUCCESS) {
123                return status;
124            }
125            break;
126        }
127        else {
128            /* filter output */
129            const char *data;
130            apr_size_t len;
131
132            status = apr_bucket_read(bucket, &data, &len, rblock);
133
134            if (APR_STATUS_IS_EAGAIN(status)) {
135                rblock = APR_BLOCK_READ;
136                continue;       /* and try again with a blocking read. */
137            }
138
139            rblock = APR_NONBLOCK_READ;
140
141            if (!APR_STATUS_IS_EOF(status) && (status != APR_SUCCESS)) {
142                break;
143            }
144
145            ret = gnutls_record_send(ctxt->session, data, len);
146            if (ret < 0) {
147                /* error sending output */
148            }
149            else if ((apr_size_t) ret != len) {
150                /* not all of the data was sent. */
151                /* mod_ssl basicly errors out here.. this doesn't seem right? */
152            }
153            else {
154                /* send complete */
155
156            }
157
158            apr_bucket_delete(bucket);
159
160            if (status != APR_SUCCESS) {
161                break;
162            }
163
164        }
165    }
166
167    return status;
168}
169
170/**
171 * From mod_ssl / ssl_engine_io.c
172 * This function will read from a brigade and discard the read buckets as it
173 * proceeds.  It will read at most *len bytes.
174 */
175static apr_status_t brigade_consume(apr_bucket_brigade * bb,
176                                    apr_read_type_e block,
177                                    char *c, apr_size_t * len)
178{
179    apr_size_t actual = 0;
180    apr_status_t status = APR_SUCCESS;
181
182    while (!APR_BRIGADE_EMPTY(bb)) {
183        apr_bucket *b = APR_BRIGADE_FIRST(bb);
184        const char *str;
185        apr_size_t str_len;
186        apr_size_t consume;
187
188        /* Justin points out this is an http-ism that might
189         * not fit if brigade_consume is added to APR.  Perhaps
190         * apr_bucket_read(eos_bucket) should return APR_EOF?
191         * Then this becomes mainline instead of a one-off.
192         */
193        if (APR_BUCKET_IS_EOS(b)) {
194            status = APR_EOF;
195            break;
196        }
197
198        /* The reason I'm not offering brigade_consume yet
199         * across to apr-util is that the following call
200         * illustrates how borked that API really is.  For
201         * this sort of case (caller provided buffer) it
202         * would be much more trivial for apr_bucket_consume
203         * to do all the work that follows, based on the
204         * particular characteristics of the bucket we are
205         * consuming here.
206         */
207        status = apr_bucket_read(b, &str, &str_len, block);
208
209        if (status != APR_SUCCESS) {
210            if (APR_STATUS_IS_EOF(status)) {
211                /* This stream bucket was consumed */
212                apr_bucket_delete(b);
213                continue;
214            }
215            break;
216        }
217
218        if (str_len > 0) {
219            /* Do not block once some data has been consumed */
220            block = APR_NONBLOCK_READ;
221
222            /* Assure we don't overflow. */
223            consume = (str_len + actual > *len) ? *len - actual : str_len;
224
225            memcpy(c, str, consume);
226
227            c += consume;
228            actual += consume;
229
230            if (consume >= b->length) {
231                /* This physical bucket was consumed */
232                apr_bucket_delete(b);
233            }
234            else {
235                /* Only part of this physical bucket was consumed */
236                b->start += consume;
237                b->length -= consume;
238            }
239        }
240        else if (b->length == 0) {
241            apr_bucket_delete(b);
242        }
243
244        /* This could probably be actual == *len, but be safe from stray
245         * photons. */
246        if (actual >= *len) {
247            break;
248        }
249    }
250
251    *len = actual;
252    return status;
253}
254
255
256ssize_t mod_gnutls_transport_read(gnutls_transport_ptr_t ptr,
257                                  void *buffer, size_t len)
258{
259    mod_gnutls_handle_t *ctxt = ptr;
260    apr_status_t rc;
261    apr_size_t in = len;
262    /* If Len = 0, we don't do anything. */
263    if (!len)
264        return 0;
265
266    if (APR_BRIGADE_EMPTY(ctxt->input_bb)) {
267
268        rc = ap_get_brigade(ctxt->input_filter->next, ctxt->input_bb,
269                            AP_MODE_READBYTES, ctxt->input_block, in);
270
271        /* Not a problem, there was simply no data ready yet.
272         */
273        if (APR_STATUS_IS_EAGAIN(rc) || APR_STATUS_IS_EINTR(rc)
274            || (rc == APR_SUCCESS && APR_BRIGADE_EMPTY(ctxt->input_bb))) {
275            return 0;
276        }
277
278        if (rc != APR_SUCCESS) {
279            /* Unexpected errors discard the brigade */
280            apr_brigade_cleanup(ctxt->input_bb);
281            ctxt->input_bb = NULL;
282            return -1;
283        }
284    }
285
286//    brigade_consume(ctxt->input_bb, ctxt->input_block, buffer, &len);
287
288
289    ap_get_brigade(ctxt->input_filter->next, ctxt->input_bb,
290                   AP_MODE_READBYTES, ctxt->input_block, len);
291
292    return len;
293}
294
295ssize_t mod_gnutls_transport_write(gnutls_transport_ptr_t ptr,
296                                   const void *buffer, size_t len)
297{
298    mod_gnutls_handle_t *ctxt = ptr;
299
300//    apr_bucket *bucket = apr_bucket_transient_create(in, inl,
301//                                                     outctx->bb->
302//                                                     bucket_alloc);
303
304    //  outctx->length += inl;
305    //APR_BRIGADE_INSERT_TAIL(outctx->bb, bucket);
306    return 0;
307}
Note: See TracBrowser for help on using the repository browser.