diff options
author | Stefan Metzmacher <metze@samba.org> | 2010-10-08 10:19:07 +0200 |
---|---|---|
committer | Stefan Metzmacher <metze@samba.org> | 2010-10-08 11:53:08 +0200 |
commit | 9300f922ae570756651c6f9979dd3e490ce2fd4e (patch) | |
tree | 80c28679ad21112188a019933254029de9cd7c14 /source4/lib | |
parent | a3d44d5504e1aa2b9e2e5564f644512bf36c01ee (diff) | |
download | samba-9300f922ae570756651c6f9979dd3e490ce2fd4e.tar.gz samba-9300f922ae570756651c6f9979dd3e490ce2fd4e.tar.bz2 samba-9300f922ae570756651c6f9979dd3e490ce2fd4e.zip |
s4:lib/tls: buffer writes in tstream_tls_push_function()
This works arround bugs in gnutls_handshake(),
which diesn't handle EAGAIN correctly, when they use the
push function.
Thanks to Marcel.Ritter@rrze.uni-erlangen.de and
Matthieu Patou <mat@samba.org> for the debugging work
on bug #7218.
metze
Diffstat (limited to 'source4/lib')
-rw-r--r-- | source4/lib/tls/tls_tstream.c | 86 |
1 files changed, 76 insertions, 10 deletions
diff --git a/source4/lib/tls/tls_tstream.c b/source4/lib/tls/tls_tstream.c index f7d27ebbf4..7f37643a65 100644 --- a/source4/lib/tls/tls_tstream.c +++ b/source4/lib/tls/tls_tstream.c @@ -51,9 +51,17 @@ struct tstream_tls { struct { uint8_t buffer[1024]; + off_t ofs; + struct iovec iov; + struct tevent_req *subreq; + struct tevent_immediate *im; + } push; + + struct { + uint8_t buffer[1024]; struct iovec iov; struct tevent_req *subreq; - } push, pull; + } pull; struct { struct tevent_req *req; @@ -132,7 +140,9 @@ static void tstream_tls_retry_trigger(struct tevent_context *ctx, } #if ENABLE_GNUTLS -static void tstream_tls_push_done(struct tevent_req *subreq); +static void tstream_tls_push_trigger_write(struct tevent_context *ev, + struct tevent_immediate *im, + void *private_data); static ssize_t tstream_tls_push_function(gnutls_transport_ptr ptr, const void *buf, size_t size) @@ -143,7 +153,7 @@ static ssize_t tstream_tls_push_function(gnutls_transport_ptr ptr, struct tstream_tls *tlss = tstream_context_data(stream, struct tstream_tls); - struct tevent_req *subreq; + size_t len; if (tlss->error != 0) { errno = tlss->error; @@ -155,24 +165,79 @@ static ssize_t tstream_tls_push_function(gnutls_transport_ptr ptr, return -1; } - tlss->push.iov.iov_base = tlss->push.buffer; - tlss->push.iov.iov_len = MIN(size, sizeof(tlss->push.buffer)); + if (tlss->push.ofs == sizeof(tlss->push.buffer)) { + errno = EAGAIN; + return -1; + } + + len = MIN(size, sizeof(tlss->push.buffer) - tlss->push.ofs); + memcpy(tlss->push.buffer + tlss->push.ofs, buf, len); + + if (tlss->push.im == NULL) { + tlss->push.im = tevent_create_immediate(tlss); + if (tlss->push.im == NULL) { + errno = ENOMEM; + return -1; + } + } + + if (tlss->push.ofs == 0) { + /* + * We'll do start the tstream_writev + * in the next event cycle. + * + * This way we can batch all push requests, + * if they fit into the buffer. + * + * This is important as gnutls_handshake() + * had a bug in some versions e.g. 2.4.1 + * and others (See bug #7218) and it doesn't + * handle EAGAIN. + */ + tevent_schedule_immediate(tlss->push.im, + tlss->current_ev, + tstream_tls_push_trigger_write, + stream); + } + + tlss->push.ofs += len; + return len; +} + +static void tstream_tls_push_done(struct tevent_req *subreq); + +static void tstream_tls_push_trigger_write(struct tevent_context *ev, + struct tevent_immediate *im, + void *private_data) +{ + struct tstream_context *stream = + talloc_get_type_abort(private_data, + struct tstream_context); + struct tstream_tls *tlss = + tstream_context_data(stream, + struct tstream_tls); + struct tevent_req *subreq; + + if (tlss->push.subreq) { + /* nothing todo */ + return; + } - memcpy(tlss->push.buffer, buf, tlss->push.iov.iov_len); + tlss->push.iov.iov_base = (char *)tlss->push.buffer; + tlss->push.iov.iov_len = tlss->push.ofs; subreq = tstream_writev_send(tlss, tlss->current_ev, tlss->plain_stream, &tlss->push.iov, 1); if (subreq == NULL) { - errno = ENOMEM; - return -1; + tlss->error = ENOMEM; + tstream_tls_retry(stream, false); + return; } tevent_req_set_callback(subreq, tstream_tls_push_done, stream); tlss->push.subreq = subreq; - - return tlss->push.iov.iov_len; } static void tstream_tls_push_done(struct tevent_req *subreq) @@ -188,6 +253,7 @@ static void tstream_tls_push_done(struct tevent_req *subreq) tlss->push.subreq = NULL; ZERO_STRUCT(tlss->push.iov); + tlss->push.ofs = 0; ret = tstream_writev_recv(subreq, &sys_errno); TALLOC_FREE(subreq); |