summaryrefslogtreecommitdiffstatsabout
path: root/src/gnutls_io.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/gnutls_io.c')
-rw-r--r--src/gnutls_io.c307
1 files changed, 307 insertions, 0 deletions
diff --git a/src/gnutls_io.c b/src/gnutls_io.c
new file mode 100644
index 0000000..6b0a15b
--- /dev/null
+++ b/src/gnutls_io.c
@@ -0,0 +1,307 @@
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 gnutls_handle_t *ctxt = (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 gnutls_handle_t *ctxt = (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 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 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}