diff options
-rw-r--r-- | source4/ldap_server/ldap_extended.c | 1 | ||||
-rw-r--r-- | source4/ldap_server/ldap_server.c | 4 | ||||
-rw-r--r-- | source4/lib/stream/packet.c | 38 | ||||
-rw-r--r-- | source4/lib/stream/packet.h | 1 | ||||
-rw-r--r-- | source4/libcli/ldap/ldap_client.c | 4 |
5 files changed, 47 insertions, 1 deletions
diff --git a/source4/ldap_server/ldap_extended.c b/source4/ldap_server/ldap_extended.c index 4479eab560..66ab4eea32 100644 --- a/source4/ldap_server/ldap_extended.c +++ b/source4/ldap_server/ldap_extended.c @@ -38,6 +38,7 @@ static void ldapsrv_start_tls(void *private_data) ctx->conn->sockets.tls = ctx->tls_socket; ctx->conn->connection->socket = ctx->tls_socket; packet_set_socket(ctx->conn->packet, ctx->conn->connection->socket); + packet_set_unreliable_select(ctx->conn->packet); } static NTSTATUS ldapsrv_StartTLS(struct ldapsrv_call *call, diff --git a/source4/ldap_server/ldap_server.c b/source4/ldap_server/ldap_server.c index 61ff387152..da44c02aa8 100644 --- a/source4/ldap_server/ldap_server.c +++ b/source4/ldap_server/ldap_server.c @@ -386,6 +386,10 @@ static void ldapsrv_accept(struct stream_connection *c) packet_set_event_context(conn->packet, c->event.ctx); packet_set_fde(conn->packet, c->event.fde); packet_set_serialise(conn->packet); + + if (conn->sockets.tls) { + packet_set_unreliable_select(conn->packet); + } /* Ensure we don't get packets until the database is ready below */ packet_recv_disable(conn->packet); diff --git a/source4/lib/stream/packet.c b/source4/lib/stream/packet.c index f614e9490a..f5e2b843cd 100644 --- a/source4/lib/stream/packet.c +++ b/source4/lib/stream/packet.c @@ -47,6 +47,8 @@ struct packet_context { bool busy; bool destructor_called; + bool unreliable_select; + struct send_element { struct send_element *next, *prev; DATA_BLOB blob; @@ -176,6 +178,21 @@ _PUBLIC_ void packet_set_nofree(struct packet_context *pc) pc->nofree = true; } +/* + tell the packet system that select/poll/epoll on the underlying + socket may not be a reliable way to determine if data is available + for receive. This happens with underlying socket systems such as the + one implemented on top of GNUTLS, where there may be data in + encryption/compression buffers that could be received by + socket_recv(), while there is no data waiting at the real socket + level as seen by select/poll/epoll. The GNUTLS library is supposed + to cope with this by always leaving some data sitting in the socket + buffer, but it does not seem to be reliable. + */ +_PUBLIC_ void packet_set_unreliable_select(struct packet_context *pc) +{ + pc->unreliable_select = true; +} /* tell the caller we have an error @@ -230,6 +247,7 @@ _PUBLIC_ void packet_recv(struct packet_context *pc) NTSTATUS status; size_t nread = 0; DATA_BLOB blob; + bool recv_retry = false; if (pc->processing) { EVENT_FD_NOT_READABLE(pc->fde); @@ -269,6 +287,8 @@ _PUBLIC_ void packet_recv(struct packet_context *pc) return; } +again: + if (npending + pc->num_read < npending) { packet_error(pc, NT_STATUS_INVALID_PARAMETER); return; @@ -308,17 +328,33 @@ _PUBLIC_ void packet_recv(struct packet_context *pc) packet_error(pc, status); return; } + if (recv_retry && NT_STATUS_EQUAL(status, STATUS_MORE_ENTRIES)) { + nread = 0; + status = NT_STATUS_OK; + } if (!NT_STATUS_IS_OK(status)) { return; } - if (nread == 0) { + if (nread == 0 && !recv_retry) { packet_eof(pc); return; } pc->num_read += nread; + if (pc->unreliable_select && nread != 0) { + recv_retry = true; + status = socket_pending(pc->sock, &npending); + if (!NT_STATUS_IS_OK(status)) { + packet_error(pc, status); + return; + } + if (npending != 0) { + goto again; + } + } + next_partial: if (pc->partial.length != pc->num_read) { if (!data_blob_realloc(pc, &pc->partial, pc->num_read)) { diff --git a/source4/lib/stream/packet.h b/source4/lib/stream/packet.h index 3c2fb0a683..85f0f26265 100644 --- a/source4/lib/stream/packet.h +++ b/source4/lib/stream/packet.h @@ -48,6 +48,7 @@ void packet_set_nofree(struct packet_context *pc); void packet_recv(struct packet_context *pc); void packet_recv_disable(struct packet_context *pc); void packet_recv_enable(struct packet_context *pc); +void packet_set_unreliable_select(struct packet_context *pc); NTSTATUS packet_send(struct packet_context *pc, DATA_BLOB blob); NTSTATUS packet_send_callback(struct packet_context *pc, DATA_BLOB blob, packet_send_callback_fn_t send_callback, diff --git a/source4/libcli/ldap/ldap_client.c b/source4/libcli/ldap/ldap_client.c index e30d5032fb..3e54d7fff0 100644 --- a/source4/libcli/ldap/ldap_client.c +++ b/source4/libcli/ldap/ldap_client.c @@ -438,6 +438,10 @@ static void ldap_connect_got_sock(struct composite_context *ctx, packet_set_fde(conn->packet, conn->event.fde); /* packet_set_serialise(conn->packet); */ + if (conn->ldaps) { + packet_set_unreliable_select(conn->packet); + } + composite_done(ctx); } |