+static void dcesrv_terminate_connection(struct dcesrv_connection *dce_conn, const char *reason)
+{
+ struct dcesrv_context *dce_ctx = dce_conn->dce_ctx;
+ struct stream_connection *srv_conn;
+ srv_conn = talloc_get_type(dce_conn->transport.private_data,
+ struct stream_connection);
+
+ if (dce_conn->pending_call_list == NULL) {
+ char *full_reason = talloc_asprintf(dce_conn, "dcesrv: %s", reason);
+
+ DLIST_REMOVE(dce_ctx->broken_connections, dce_conn);
+ stream_terminate_connection(srv_conn, full_reason ? full_reason : reason);
+ return;
+ }
+
+ if (dce_conn->terminate != NULL) {
+ return;
+ }
+
+ DEBUG(3,("dcesrv: terminating connection due to '%s' defered due to pending calls\n",
+ reason));
+ dce_conn->terminate = talloc_strdup(dce_conn, reason);
+ if (dce_conn->terminate == NULL) {
+ dce_conn->terminate = "dcesrv: defered terminating connection - no memory";
+ }
+ DLIST_ADD_END(dce_ctx->broken_connections, dce_conn);
+}
+
+static void dcesrv_cleanup_broken_connections(struct dcesrv_context *dce_ctx)
+{
+ struct dcesrv_connection *cur, *next;
+
+ next = dce_ctx->broken_connections;
+ while (next != NULL) {
+ cur = next;
+ next = cur->next;
+
+ if (cur->state_flags & DCESRV_CALL_STATE_FLAG_PROCESS_PENDING_CALL) {
+ struct dcesrv_connection_context *context_cur, *context_next;
+
+ context_next = cur->contexts;
+ while (context_next != NULL) {
+ context_cur = context_next;
+ context_next = context_cur->next;
+
+ dcesrv_connection_context_destructor(context_cur);
+ }
+ }
+
+ dcesrv_terminate_connection(cur, cur->terminate);
+ }
+}
+
+/* We need this include to be able to compile on some plateforms
+ * (ie. freebsd 7.2) as it seems that <sys/uio.h> is not included
+ * correctly.
+ * It has to be that deep because otherwise we have a conflict on
+ * const struct dcesrv_interface declaration.
+ * This is mostly due to socket_wrapper defining #define bind swrap_bind
+ * which conflict with the bind used before.
+ */
+#include "system/network.h"
+
+struct dcesrv_sock_reply_state {
+ struct dcesrv_connection *dce_conn;
+ struct dcesrv_call_state *call;
+ struct iovec iov;
+};
+
+static void dcesrv_sock_reply_done(struct tevent_req *subreq);
+
+static void dcesrv_sock_report_output_data(struct dcesrv_connection *dce_conn)
+{
+ struct dcesrv_call_state *call;
+
+ call = dce_conn->call_list;
+ if (!call || !call->replies) {
+ return;
+ }
+
+ while (call->replies) {
+ struct data_blob_list_item *rep = call->replies;
+ struct dcesrv_sock_reply_state *substate;
+ struct tevent_req *subreq;
+
+ substate = talloc(call, struct dcesrv_sock_reply_state);
+ if (!substate) {
+ dcesrv_terminate_connection(dce_conn, "no memory");
+ return;
+ }
+
+ substate->dce_conn = dce_conn;
+ substate->call = NULL;
+
+ DLIST_REMOVE(call->replies, rep);
+
+ if (call->replies == NULL) {
+ substate->call = call;
+ }
+
+ substate->iov.iov_base = (void *) rep->blob.data;
+ substate->iov.iov_len = rep->blob.length;
+
+ subreq = tstream_writev_queue_send(substate,
+ dce_conn->event_ctx,
+ dce_conn->stream,
+ dce_conn->send_queue,
+ &substate->iov, 1);
+ if (!subreq) {
+ dcesrv_terminate_connection(dce_conn, "no memory");
+ return;
+ }
+ tevent_req_set_callback(subreq, dcesrv_sock_reply_done,
+ substate);
+ }
+
+ DLIST_REMOVE(call->conn->call_list, call);
+ call->list = DCESRV_LIST_NONE;
+}
+
+static void dcesrv_sock_reply_done(struct tevent_req *subreq)
+{
+ struct dcesrv_sock_reply_state *substate = tevent_req_callback_data(subreq,
+ struct dcesrv_sock_reply_state);
+ int ret;
+ int sys_errno;
+ NTSTATUS status;
+ struct dcesrv_call_state *call = substate->call;
+
+ ret = tstream_writev_queue_recv(subreq, &sys_errno);
+ TALLOC_FREE(subreq);
+ if (ret == -1) {
+ status = map_nt_error_from_unix_common(sys_errno);
+ dcesrv_terminate_connection(substate->dce_conn, nt_errstr(status));
+ return;
+ }
+
+ talloc_free(substate);
+ if (call) {
+ talloc_free(call);
+ }
+}
+
+
+
+
+struct dcesrv_socket_context {
+ const struct dcesrv_endpoint *endpoint;
+ struct dcesrv_context *dcesrv_ctx;
+};
+
+
+static void dcesrv_read_fragment_done(struct tevent_req *subreq);
+
+static void dcesrv_sock_accept(struct stream_connection *srv_conn)
+{
+ NTSTATUS status;
+ struct dcesrv_socket_context *dcesrv_sock =
+ talloc_get_type(srv_conn->private_data, struct dcesrv_socket_context);
+ enum dcerpc_transport_t transport =
+ dcerpc_binding_get_transport(dcesrv_sock->endpoint->ep_description);
+ struct dcesrv_connection *dcesrv_conn = NULL;
+ int ret;
+ struct tevent_req *subreq;
+ struct loadparm_context *lp_ctx = dcesrv_sock->dcesrv_ctx->lp_ctx;
+
+ dcesrv_cleanup_broken_connections(dcesrv_sock->dcesrv_ctx);
+
+ if (!srv_conn->session_info) {
+ status = auth_anonymous_session_info(srv_conn,
+ lp_ctx,
+ &srv_conn->session_info);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(0,("dcesrv_sock_accept: auth_anonymous_session_info failed: %s\n",
+ nt_errstr(status)));
+ stream_terminate_connection(srv_conn, nt_errstr(status));
+ return;
+ }
+ }
+
+ status = dcesrv_endpoint_connect(dcesrv_sock->dcesrv_ctx,
+ srv_conn,
+ dcesrv_sock->endpoint,
+ srv_conn->session_info,
+ srv_conn->event.ctx,
+ srv_conn->msg_ctx,
+ srv_conn->server_id,
+ DCESRV_CALL_STATE_FLAG_MAY_ASYNC,
+ &dcesrv_conn);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(0,("dcesrv_sock_accept: dcesrv_endpoint_connect failed: %s\n",
+ nt_errstr(status)));
+ stream_terminate_connection(srv_conn, nt_errstr(status));
+ return;
+ }
+
+ dcesrv_conn->transport.private_data = srv_conn;
+ dcesrv_conn->transport.report_output_data = dcesrv_sock_report_output_data;
+
+ TALLOC_FREE(srv_conn->event.fde);
+
+ dcesrv_conn->send_queue = tevent_queue_create(dcesrv_conn, "dcesrv send queue");
+ if (!dcesrv_conn->send_queue) {
+ status = NT_STATUS_NO_MEMORY;
+ DEBUG(0,("dcesrv_sock_accept: tevent_queue_create(%s)\n",
+ nt_errstr(status)));
+ stream_terminate_connection(srv_conn, nt_errstr(status));
+ return;
+ }
+
+ if (transport == NCACN_NP) {
+ dcesrv_conn->auth_state.session_key = dcesrv_inherited_session_key;
+ dcesrv_conn->stream = talloc_move(dcesrv_conn,
+ &srv_conn->tstream);
+ } else {
+ ret = tstream_bsd_existing_socket(dcesrv_conn,
+ socket_get_fd(srv_conn->socket),
+ &dcesrv_conn->stream);
+ if (ret == -1) {
+ status = map_nt_error_from_unix_common(errno);
+ DEBUG(0, ("dcesrv_sock_accept: "
+ "failed to setup tstream: %s\n",
+ nt_errstr(status)));
+ stream_terminate_connection(srv_conn, nt_errstr(status));
+ return;
+ }
+ socket_set_flags(srv_conn->socket, SOCKET_FLAG_NOCLOSE);
+ }
+
+ dcesrv_conn->local_address = srv_conn->local_address;
+ dcesrv_conn->remote_address = srv_conn->remote_address;
+
+ if (transport == NCALRPC) {
+ uid_t uid;
+ gid_t gid;
+
+ ret = getpeereid(socket_get_fd(srv_conn->socket), &uid, &gid);
+ if (ret == -1) {
+ status = map_nt_error_from_unix_common(errno);
+ DEBUG(0, ("dcesrv_sock_accept: "
+ "getpeereid() failed for NCALRPC: %s\n",
+ nt_errstr(status)));
+ stream_terminate_connection(srv_conn, nt_errstr(status));
+ return;
+ }
+ if (uid == dcesrv_conn->dce_ctx->initial_euid) {
+ struct tsocket_address *r = NULL;
+
+ ret = tsocket_address_unix_from_path(dcesrv_conn,
+ "/root/ncalrpc_as_system",
+ &r);
+ if (ret == -1) {
+ status = map_nt_error_from_unix_common(errno);
+ DEBUG(0, ("dcesrv_sock_accept: "
+ "tsocket_address_unix_from_path() failed for NCALRPC: %s\n",
+ nt_errstr(status)));
+ stream_terminate_connection(srv_conn, nt_errstr(status));
+ return;
+ }
+ dcesrv_conn->remote_address = r;
+ }
+ }
+
+ srv_conn->private_data = dcesrv_conn;
+
+ irpc_add_name(srv_conn->msg_ctx, "rpc_server");
+
+ subreq = dcerpc_read_ncacn_packet_send(dcesrv_conn,
+ dcesrv_conn->event_ctx,
+ dcesrv_conn->stream);
+ if (!subreq) {
+ status = NT_STATUS_NO_MEMORY;
+ DEBUG(0,("dcesrv_sock_accept: dcerpc_read_fragment_buffer_send(%s)\n",
+ nt_errstr(status)));
+ stream_terminate_connection(srv_conn, nt_errstr(status));
+ return;
+ }
+ tevent_req_set_callback(subreq, dcesrv_read_fragment_done, dcesrv_conn);
+
+ return;
+}
+
+static void dcesrv_read_fragment_done(struct tevent_req *subreq)
+{
+ struct dcesrv_connection *dce_conn = tevent_req_callback_data(subreq,
+ struct dcesrv_connection);
+ struct dcesrv_context *dce_ctx = dce_conn->dce_ctx;
+ struct ncacn_packet *pkt;
+ DATA_BLOB buffer;
+ NTSTATUS status;
+
+ if (dce_conn->terminate) {
+ /*
+ * if the current connection is broken
+ * we need to clean it up before any other connection
+ */
+ dcesrv_terminate_connection(dce_conn, dce_conn->terminate);
+ dcesrv_cleanup_broken_connections(dce_ctx);
+ return;
+ }
+
+ dcesrv_cleanup_broken_connections(dce_ctx);
+
+ status = dcerpc_read_ncacn_packet_recv(subreq, dce_conn,
+ &pkt, &buffer);
+ TALLOC_FREE(subreq);
+ if (!NT_STATUS_IS_OK(status)) {
+ dcesrv_terminate_connection(dce_conn, nt_errstr(status));
+ return;
+ }
+
+ status = dcesrv_process_ncacn_packet(dce_conn, pkt, buffer);
+ if (!NT_STATUS_IS_OK(status)) {
+ dcesrv_terminate_connection(dce_conn, nt_errstr(status));
+ return;
+ }
+
+ subreq = dcerpc_read_ncacn_packet_send(dce_conn,
+ dce_conn->event_ctx,
+ dce_conn->stream);
+ if (!subreq) {
+ status = NT_STATUS_NO_MEMORY;
+ dcesrv_terminate_connection(dce_conn, nt_errstr(status));
+ return;
+ }
+ tevent_req_set_callback(subreq, dcesrv_read_fragment_done, dce_conn);
+}
+
+static void dcesrv_sock_recv(struct stream_connection *conn, uint16_t flags)
+{
+ struct dcesrv_connection *dce_conn = talloc_get_type(conn->private_data,
+ struct dcesrv_connection);
+ dcesrv_terminate_connection(dce_conn, "dcesrv_sock_recv triggered");
+}
+
+static void dcesrv_sock_send(struct stream_connection *conn, uint16_t flags)
+{
+ struct dcesrv_connection *dce_conn = talloc_get_type(conn->private_data,
+ struct dcesrv_connection);
+ dcesrv_terminate_connection(dce_conn, "dcesrv_sock_send triggered");
+}
+
+
+static const struct stream_server_ops dcesrv_stream_ops = {
+ .name = "rpc",
+ .accept_connection = dcesrv_sock_accept,
+ .recv_handler = dcesrv_sock_recv,
+ .send_handler = dcesrv_sock_send,
+};
+
+static NTSTATUS dcesrv_add_ep_unix(struct dcesrv_context *dce_ctx,
+ struct loadparm_context *lp_ctx,
+ struct dcesrv_endpoint *e,
+ struct tevent_context *event_ctx, const struct model_ops *model_ops)
+{
+ struct dcesrv_socket_context *dcesrv_sock;
+ uint16_t port = 1;
+ NTSTATUS status;
+ const char *endpoint;
+
+ dcesrv_sock = talloc(event_ctx, struct dcesrv_socket_context);
+ NT_STATUS_HAVE_NO_MEMORY(dcesrv_sock);
+
+ /* remember the endpoint of this socket */
+ dcesrv_sock->endpoint = e;
+ dcesrv_sock->dcesrv_ctx = talloc_reference(dcesrv_sock, dce_ctx);
+
+ endpoint = dcerpc_binding_get_string_option(e->ep_description, "endpoint");
+
+ status = stream_setup_socket(dcesrv_sock, event_ctx, lp_ctx,
+ model_ops, &dcesrv_stream_ops,
+ "unix", endpoint, &port,
+ lpcfg_socket_options(lp_ctx),
+ dcesrv_sock);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(0,("service_setup_stream_socket(path=%s) failed - %s\n",
+ endpoint, nt_errstr(status)));
+ }
+
+ return status;
+}
+
+static NTSTATUS dcesrv_add_ep_ncalrpc(struct dcesrv_context *dce_ctx,
+ struct loadparm_context *lp_ctx,
+ struct dcesrv_endpoint *e,
+ struct tevent_context *event_ctx, const struct model_ops *model_ops)
+{
+ struct dcesrv_socket_context *dcesrv_sock;
+ uint16_t port = 1;
+ char *full_path;
+ NTSTATUS status;
+ const char *endpoint;
+
+ endpoint = dcerpc_binding_get_string_option(e->ep_description, "endpoint");
+
+ if (endpoint == NULL) {
+ /*
+ * No identifier specified: use DEFAULT.
+ *
+ * TODO: DO NOT hardcode this value anywhere else. Rather, specify
+ * no endpoint and let the epmapper worry about it.
+ */
+ endpoint = "DEFAULT";
+ status = dcerpc_binding_set_string_option(e->ep_description,
+ "endpoint",
+ endpoint);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(0,("dcerpc_binding_set_string_option() failed - %s\n",
+ nt_errstr(status)));
+ return status;
+ }
+ }
+
+ full_path = talloc_asprintf(dce_ctx, "%s/%s", lpcfg_ncalrpc_dir(lp_ctx),
+ endpoint);
+
+ dcesrv_sock = talloc(event_ctx, struct dcesrv_socket_context);
+ NT_STATUS_HAVE_NO_MEMORY(dcesrv_sock);
+
+ /* remember the endpoint of this socket */
+ dcesrv_sock->endpoint = e;
+ dcesrv_sock->dcesrv_ctx = talloc_reference(dcesrv_sock, dce_ctx);
+
+ status = stream_setup_socket(dcesrv_sock, event_ctx, lp_ctx,
+ model_ops, &dcesrv_stream_ops,
+ "unix", full_path, &port,
+ lpcfg_socket_options(lp_ctx),
+ dcesrv_sock);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(0,("service_setup_stream_socket(identifier=%s,path=%s) failed - %s\n",
+ endpoint, full_path, nt_errstr(status)));
+ }
+ return status;
+}
+
+static NTSTATUS dcesrv_add_ep_np(struct dcesrv_context *dce_ctx,
+ struct loadparm_context *lp_ctx,
+ struct dcesrv_endpoint *e,
+ struct tevent_context *event_ctx, const struct model_ops *model_ops)
+{
+ struct dcesrv_socket_context *dcesrv_sock;
+ NTSTATUS status;
+ const char *endpoint;
+
+ endpoint = dcerpc_binding_get_string_option(e->ep_description, "endpoint");
+ if (endpoint == NULL) {
+ DEBUG(0, ("Endpoint mandatory for named pipes\n"));
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ dcesrv_sock = talloc(event_ctx, struct dcesrv_socket_context);
+ NT_STATUS_HAVE_NO_MEMORY(dcesrv_sock);
+
+ /* remember the endpoint of this socket */
+ dcesrv_sock->endpoint = e;
+ dcesrv_sock->dcesrv_ctx = talloc_reference(dcesrv_sock, dce_ctx);
+
+ status = tstream_setup_named_pipe(dce_ctx, event_ctx, lp_ctx,
+ model_ops, &dcesrv_stream_ops,
+ endpoint,
+ dcesrv_sock);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(0,("stream_setup_named_pipe(pipe=%s) failed - %s\n",
+ endpoint, nt_errstr(status)));
+ return status;
+ }
+
+ return NT_STATUS_OK;
+}
+