+ if (timeval_is_zero(&conn->limits.endtime)) {
+ conn->limits.endtime =
+ timeval_current_ofs(conn->limits.initial_timeout, 0);
+ } else {
+ conn->limits.endtime =
+ timeval_current_ofs(conn->limits.conn_idle_time, 0);
+ }
+
+ /*
+ * The minimun size of a LDAP pdu is 7 bytes
+ *
+ * dumpasn1 -hh ldap-unbind-min.dat
+ *
+ * <30 05 02 01 09 42 00>
+ * 0 5: SEQUENCE {
+ * <02 01 09>
+ * 2 1: INTEGER 9
+ * <42 00>
+ * 5 0: [APPLICATION 2]
+ * : Error: Object has zero length.
+ * : }
+ *
+ * dumpasn1 -hh ldap-unbind-windows.dat
+ *
+ * <30 84 00 00 00 05 02 01 09 42 00>
+ * 0 5: SEQUENCE {
+ * <02 01 09>
+ * 6 1: INTEGER 9
+ * <42 00>
+ * 9 0: [APPLICATION 2]
+ * : Error: Object has zero length.
+ * : }
+ *
+ * This means using an initial read size
+ * of 7 is ok.
+ */
+ subreq = tstream_read_pdu_blob_send(conn,
+ conn->connection->event.ctx,
+ conn->sockets.active,
+ 7, /* initial_read_size */
+ ldap_full_packet,
+ conn);
+ if (subreq == NULL) {
+ ldapsrv_terminate_connection(conn, "ldapsrv_call_read_next: "
+ "no memory for tstream_read_pdu_blob_send");
+ return false;
+ }
+ tevent_req_set_endtime(subreq,
+ conn->connection->event.ctx,
+ conn->limits.endtime);
+ tevent_req_set_callback(subreq, ldapsrv_call_read_done, conn);
+ return true;
+}
+
+static void ldapsrv_call_process_done(struct tevent_req *subreq);
+
+static void ldapsrv_call_read_done(struct tevent_req *subreq)
+{
+ struct ldapsrv_connection *conn =
+ tevent_req_callback_data(subreq,
+ struct ldapsrv_connection);
+ NTSTATUS status;
+ struct ldapsrv_call *call;
+ struct asn1_data *asn1;
+ DATA_BLOB blob;
+
+ call = talloc_zero(conn, struct ldapsrv_call);
+ if (!call) {
+ ldapsrv_terminate_connection(conn, "no memory");
+ return;
+ }
+
+ call->conn = conn;
+
+ status = tstream_read_pdu_blob_recv(subreq,
+ call,
+ &blob);
+ TALLOC_FREE(subreq);
+ if (!NT_STATUS_IS_OK(status)) {
+ const char *reason;
+
+ reason = talloc_asprintf(call, "ldapsrv_call_loop: "
+ "tstream_read_pdu_blob_recv() - %s",
+ nt_errstr(status));
+ if (!reason) {
+ reason = nt_errstr(status);
+ }
+
+ ldapsrv_terminate_connection(conn, reason);
+ return;
+ }
+
+ asn1 = asn1_init(call);
+ if (asn1 == NULL) {
+ ldapsrv_terminate_connection(conn, "no memory");
+ return;
+ }
+
+ call->request = talloc(call, struct ldap_message);
+ if (call->request == NULL) {
+ ldapsrv_terminate_connection(conn, "no memory");
+ return;
+ }
+
+ if (!asn1_load(asn1, blob)) {
+ ldapsrv_terminate_connection(conn, "asn1_load failed");
+ return;
+ }
+
+ status = ldap_decode(asn1, samba_ldap_control_handlers(),
+ call->request);
+ if (!NT_STATUS_IS_OK(status)) {
+ ldapsrv_terminate_connection(conn, nt_errstr(status));
+ return;
+ }
+
+ data_blob_free(&blob);
+
+
+ /* queue the call in the global queue */
+ subreq = ldapsrv_process_call_send(call,
+ conn->connection->event.ctx,
+ conn->service->call_queue,
+ call);
+ if (subreq == NULL) {
+ ldapsrv_terminate_connection(conn, "ldapsrv_process_call_send failed");
+ return;
+ }
+ tevent_req_set_callback(subreq, ldapsrv_call_process_done, call);
+ conn->active_call = subreq;
+}
+
+static void ldapsrv_call_writev_done(struct tevent_req *subreq);
+
+static void ldapsrv_call_process_done(struct tevent_req *subreq)
+{
+ struct ldapsrv_call *call =
+ tevent_req_callback_data(subreq,
+ struct ldapsrv_call);
+ struct ldapsrv_connection *conn = call->conn;
+ NTSTATUS status;
+ DATA_BLOB blob = data_blob_null;
+
+ conn->active_call = NULL;
+
+ status = ldapsrv_process_call_recv(subreq);
+ TALLOC_FREE(subreq);
+ if (!NT_STATUS_IS_OK(status)) {
+ ldapsrv_terminate_connection(conn, nt_errstr(status));
+ return;
+ }
+
+ /* build all the replies into a single blob */
+ while (call->replies) {
+ DATA_BLOB b;
+ bool ret;
+
+ if (!ldap_encode(call->replies->msg, samba_ldap_control_handlers(), &b, call)) {
+ DEBUG(0,("Failed to encode ldap reply of type %d\n",
+ call->replies->msg->type));
+ ldapsrv_terminate_connection(conn, "ldap_encode failed");
+ return;
+ }
+
+ ret = data_blob_append(call, &blob, b.data, b.length);
+ data_blob_free(&b);
+
+ talloc_set_name_const(blob.data, "Outgoing, encoded LDAP packet");
+
+ if (!ret) {
+ ldapsrv_terminate_connection(conn, "data_blob_append failed");
+ return;
+ }
+
+ DLIST_REMOVE(call->replies, call->replies);
+ }
+
+ if (blob.length == 0) {
+ TALLOC_FREE(call);
+
+ ldapsrv_call_read_next(conn);
+ return;
+ }
+
+ call->out_iov.iov_base = blob.data;
+ call->out_iov.iov_len = blob.length;
+
+ subreq = tstream_writev_queue_send(call,
+ conn->connection->event.ctx,
+ conn->sockets.active,
+ conn->sockets.send_queue,
+ &call->out_iov, 1);
+ if (subreq == NULL) {
+ ldapsrv_terminate_connection(conn, "stream_writev_queue_send failed");
+ return;
+ }
+ tevent_req_set_callback(subreq, ldapsrv_call_writev_done, call);
+}
+
+static void ldapsrv_call_postprocess_done(struct tevent_req *subreq);
+
+static void ldapsrv_call_writev_done(struct tevent_req *subreq)
+{
+ struct ldapsrv_call *call =
+ tevent_req_callback_data(subreq,
+ struct ldapsrv_call);
+ struct ldapsrv_connection *conn = call->conn;
+ int sys_errno;
+ int rc;
+
+ rc = tstream_writev_queue_recv(subreq, &sys_errno);
+ TALLOC_FREE(subreq);
+ if (rc == -1) {
+ const char *reason;
+
+ reason = talloc_asprintf(call, "ldapsrv_call_writev_done: "
+ "tstream_writev_queue_recv() - %d:%s",
+ sys_errno, strerror(sys_errno));
+ if (reason == NULL) {
+ reason = "ldapsrv_call_writev_done: "
+ "tstream_writev_queue_recv() failed";
+ }
+
+ ldapsrv_terminate_connection(conn, reason);
+ return;
+ }
+
+ if (call->postprocess_send) {
+ subreq = call->postprocess_send(call,
+ conn->connection->event.ctx,
+ call->postprocess_private);
+ if (subreq == NULL) {
+ ldapsrv_terminate_connection(conn, "ldapsrv_call_writev_done: "
+ "call->postprocess_send - no memory");
+ return;
+ }
+ tevent_req_set_callback(subreq,
+ ldapsrv_call_postprocess_done,
+ call);
+ return;
+ }
+
+ TALLOC_FREE(call);
+
+ ldapsrv_call_read_next(conn);
+}
+
+static void ldapsrv_call_postprocess_done(struct tevent_req *subreq)
+{
+ struct ldapsrv_call *call =
+ tevent_req_callback_data(subreq,
+ struct ldapsrv_call);
+ struct ldapsrv_connection *conn = call->conn;
+ NTSTATUS status;
+
+ status = call->postprocess_recv(subreq);
+ TALLOC_FREE(subreq);
+ if (!NT_STATUS_IS_OK(status)) {
+ const char *reason;
+
+ reason = talloc_asprintf(call, "ldapsrv_call_postprocess_done: "
+ "call->postprocess_recv() - %s",
+ nt_errstr(status));
+ if (reason == NULL) {
+ reason = nt_errstr(status);
+ }
+
+ ldapsrv_terminate_connection(conn, reason);
+ return;
+ }
+
+ TALLOC_FREE(call);
+
+ ldapsrv_call_read_next(conn);
+}
+
+struct ldapsrv_process_call_state {
+ struct ldapsrv_call *call;
+};
+
+static void ldapsrv_process_call_trigger(struct tevent_req *req,
+ void *private_data);
+
+static struct tevent_req *ldapsrv_process_call_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct tevent_queue *call_queue,
+ struct ldapsrv_call *call)
+{
+ struct tevent_req *req;
+ struct ldapsrv_process_call_state *state;
+ bool ok;
+
+ req = tevent_req_create(mem_ctx, &state,
+ struct ldapsrv_process_call_state);
+ if (req == NULL) {
+ return req;
+ }
+
+ state->call = call;
+
+ ok = tevent_queue_add(call_queue, ev, req,
+ ldapsrv_process_call_trigger, NULL);
+ if (!ok) {
+ tevent_req_nomem(NULL, req);
+ return tevent_req_post(req, ev);
+ }
+
+ return req;
+}
+
+static void ldapsrv_process_call_trigger(struct tevent_req *req,
+ void *private_data)
+{
+ struct ldapsrv_process_call_state *state =
+ tevent_req_data(req,
+ struct ldapsrv_process_call_state);
+ NTSTATUS status;
+
+ /* make the call */
+ status = ldapsrv_do_call(state->call);
+ if (!NT_STATUS_IS_OK(status)) {
+ tevent_req_nterror(req, status);
+ return;
+ }
+
+ tevent_req_done(req);
+}
+
+static NTSTATUS ldapsrv_process_call_recv(struct tevent_req *req)
+{
+ NTSTATUS status;
+
+ if (tevent_req_is_nterror(req, &status)) {
+ tevent_req_received(req);
+ return status;
+ }
+
+ tevent_req_received(req);
+ return NT_STATUS_OK;