r10426: - restructure the winbind server code a bit
authorStefan Metzmacher <metze@samba.org>
Thu, 22 Sep 2005 18:35:08 +0000 (18:35 +0000)
committerGerald (Jerry) Carter <jerry@samba.org>
Wed, 10 Oct 2007 18:38:44 +0000 (13:38 -0500)
- remove the echo test stuff
- abstract out the used protocol
- we have a seperate handler for the samba3 protocol now
- the backend can easy do async replies
  by setting WBSRV_CALL_FLAGS_REPLY_ASYNC in wbsrv_call
  and then call wbsrv_queue_reply() later

metze
(This used to be commit 32f3e68a569e9273b8d34fbd797c0a28494e5b6d)

source4/include/structs.h
source4/winbind/SConscript
source4/winbind/config.mk
source4/winbind/wb_samba3_cmd.c [new file with mode: 0644]
source4/winbind/wb_samba3_protocol.c [new file with mode: 0644]
source4/winbind/wb_samba3_protocol.h [new file with mode: 0644]
source4/winbind/wb_server.c
source4/winbind/wb_server.h [new file with mode: 0644]

index 997e1445ef4d68a1a77189f73b657c92a755208e..bc37b0dec6e92a7fb3301c92340eee8330fb209d 100644 (file)
@@ -282,6 +282,13 @@ struct samba3_secrets;
 struct samba3_share_info;
 struct samba3;
 
+struct wbsrv_service;
+struct wbsrv_protocol_ops;
+struct wbsrv_listen_socket;
+struct wbsrv_connection;
+struct wbsrv_call;
+struct wbsrv_samba3_call;
+
 struct ldb_map_attribute;
 struct ldb_map_objectclass;
 
index c3855a379c65a58c83971b4ecdf1a978216245aa..d8a10bb452472d045734e91aceb174014b1a4da9 100644 (file)
@@ -1,3 +1,7 @@
 Import('hostenv')
 
-hostenv.StaticLibrary('winbind',['wb_server.c'])
+hostenv.StaticLibrary('winbind',[
+                       'wb_server.c',
+                       'wb_samba3_protocol.c'
+                       'wb_samba3_cmd.c'
+                       ])
index 948b6285626d5c95fd43d23466bb26ad79644e13..dea0782575882a40c3b2f7c0568ed6e06ce1afa0 100644 (file)
@@ -1,12 +1,14 @@
 # server subsystem
 
 ################################################
-# Start MODULE server_service_auth
+# Start MODULE server_service_winbind
 [MODULE::server_service_winbind]
 INIT_FUNCTION = server_service_winbind_init
 SUBSYSTEM = SERVER_SERVICE
 INIT_OBJ_FILES = \
-               winbind/wb_server.o
+               winbind/wb_server.o \
+               winbind/wb_samba3_protocol.o \
+               winbind/wb_samba3_cmd.o
 REQUIRED_SUBSYSTEMS = 
 # End MODULE server_service_winbind
 ################################################
diff --git a/source4/winbind/wb_samba3_cmd.c b/source4/winbind/wb_samba3_cmd.c
new file mode 100644 (file)
index 0000000..14e9ac9
--- /dev/null
@@ -0,0 +1,50 @@
+/* 
+   Unix SMB/CIFS implementation.
+   Main winbindd samba3 server routines
+
+   Copyright (C) Stefan Metzmacher     2005
+   Copyright (C) Volker Lendecke       2005
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2 of the License, or
+   (at your option) any later version.
+   
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+   
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "includes.h"
+#include "smbd/service_stream.h"
+#include "nsswitch/winbind_nss_config.h"
+#include "nsswitch/winbindd_nss.h"
+#include "winbind/wb_server.h"
+#include "winbind/wb_samba3_protocol.h"
+
+NTSTATUS wbsrv_samba3_interface_version(struct wbsrv_samba3_call *s3call)
+{
+       s3call->response.result                 = WINBINDD_OK;
+       s3call->response.data.interface_version = WINBIND_INTERFACE_VERSION;
+       return NT_STATUS_OK;
+}
+
+NTSTATUS wbsrv_samba3_priv_pipe_dir(struct wbsrv_samba3_call *s3call)
+{
+       s3call->response.result                 = WINBINDD_OK;
+       s3call->response.extra_data             = smbd_tmp_path(s3call,
+                                                 WINBINDD_SAMBA3_PRIVILEGED_SOCKET);
+       NT_STATUS_HAVE_NO_MEMORY(s3call->response.extra_data);
+       return NT_STATUS_OK;
+}
+
+NTSTATUS wbsrv_samba3_ping(struct wbsrv_samba3_call *s3call)
+{
+       s3call->response.result                 = WINBINDD_OK;
+       return NT_STATUS_OK;
+}
diff --git a/source4/winbind/wb_samba3_protocol.c b/source4/winbind/wb_samba3_protocol.c
new file mode 100644 (file)
index 0000000..f981240
--- /dev/null
@@ -0,0 +1,116 @@
+/* 
+   Unix SMB/CIFS implementation.
+   Main winbindd samba3 server routines
+
+   Copyright (C) Stefan Metzmacher     2005
+   Copyright (C) Volker Lendecke       2005
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2 of the License, or
+   (at your option) any later version.
+   
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+   
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "includes.h"
+#include "smbd/service_stream.h"
+#include "nsswitch/winbind_nss_config.h"
+#include "nsswitch/winbindd_nss.h"
+#include "winbind/wb_server.h"
+#include "winbind/wb_samba3_protocol.h"
+
+uint32_t wbsrv_samba3_packet_length(DATA_BLOB blob)
+{
+       uint32_t *len = (uint32_t *)blob.data;
+       return *len;
+}
+
+NTSTATUS wbsrv_samba3_pull_request(DATA_BLOB blob, TALLOC_CTX *mem_ctx, struct wbsrv_call **_call)
+{
+       struct wbsrv_call *call;
+       struct wbsrv_samba3_call *s3_call;
+
+       if (blob.length != sizeof(s3_call->request)) {
+               DEBUG(0,("wbsrv_samba3_pull_request: invalid blob length %u should be %u\n"
+                        " make sure you use the correct winbind client tools!\n",
+                        blob.length, sizeof(s3_call->request)));
+               return NT_STATUS_INVALID_PARAMETER;
+       }
+
+       call = talloc_zero(mem_ctx, struct wbsrv_call);
+       NT_STATUS_HAVE_NO_MEMORY(call);
+
+       s3_call = talloc_zero(call, struct wbsrv_samba3_call);
+       NT_STATUS_HAVE_NO_MEMORY(s3_call);
+
+       /* the packet layout is the same as the in memory layout of the request, so just copy it */
+       memcpy(&s3_call->request, blob.data, sizeof(s3_call->request));
+
+       call->private_data = s3_call;
+
+       *_call = call;
+       return NT_STATUS_OK;
+}
+
+NTSTATUS wbsrv_samba3_handle_call(struct wbsrv_call *call)
+{
+       struct wbsrv_samba3_call *s3call = talloc_get_type(call->private_data,
+                                                          struct wbsrv_samba3_call);
+
+       DEBUG(10, ("Got winbind samba3 request %d\n", s3call->request.cmd));
+
+       switch(s3call->request.cmd) {
+       case WINBINDD_INTERFACE_VERSION:
+               return wbsrv_samba3_interface_version(s3call);
+
+       case WINBINDD_PRIV_PIPE_DIR:
+               return wbsrv_samba3_priv_pipe_dir(s3call);
+
+       case WINBINDD_PING:
+               return wbsrv_samba3_ping(s3call);
+       }
+
+       s3call->response.result = WINBINDD_ERROR;
+       return NT_STATUS_OK;
+}
+
+NTSTATUS wbsrv_samba3_push_reply(struct wbsrv_call *call, TALLOC_CTX *mem_ctx, DATA_BLOB *_blob)
+{
+       struct wbsrv_samba3_call *s3call = talloc_get_type(call->private_data,
+                                                          struct wbsrv_samba3_call);
+       DATA_BLOB blob;
+       uint8_t *extra_data;
+       size_t extra_data_len = 0;
+
+       extra_data = s3call->response.extra_data;
+       if (extra_data) {
+               extra_data_len = strlen((char *)s3call->response.extra_data) + 1;
+       }
+
+       blob = data_blob_talloc(mem_ctx, NULL, sizeof(s3call->response) + extra_data_len);
+       NT_STATUS_HAVE_NO_MEMORY(blob.data);
+
+       /* don't push real pointer values into sockets */
+       if (extra_data) {
+               s3call->response.extra_data = (void *)0xFFFFFFFF;
+       }
+       s3call->response.length = sizeof(s3call->response) + extra_data_len;
+       memcpy(blob.data, &s3call->response, sizeof(s3call->response));
+       /* set back the pointer */
+       s3call->response.extra_data = extra_data;
+
+       if (extra_data) {
+               memcpy(blob.data + sizeof(s3call->response), extra_data, extra_data_len);
+       }
+
+       *_blob = blob;
+       return NT_STATUS_OK;
+}
diff --git a/source4/winbind/wb_samba3_protocol.h b/source4/winbind/wb_samba3_protocol.h
new file mode 100644 (file)
index 0000000..473a91b
--- /dev/null
@@ -0,0 +1,35 @@
+/* 
+   Unix SMB/CIFS implementation.
+   Main winbindd samba3 server routines
+
+   Copyright (C) Stefan Metzmacher     2005
+   Copyright (C) Volker Lendecke       2005
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2 of the License, or
+   (at your option) any later version.
+   
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+   
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+struct wbsrv_samba3_call {
+       /* pointer back to the generic winbind call */
+       struct wbsrv_call *call;
+
+       /* here the backend can store stuff like composite_context's ... */
+       void *private_data;
+
+       /* the request structure of the samba3 protocol */
+       struct winbindd_request request;
+       
+       /* the response structure of the samba3 protocol*/
+       struct winbindd_response response;
+};
index 17a25f57d5156989adf8b0cc038c32433068df0c..ee9a1863d54a5883342940768709553d38b8cb00 100644 (file)
 #include "smbd/service_stream.h"
 #include "nsswitch/winbind_nss_config.h"
 #include "nsswitch/winbindd_nss.h"
+#include "winbind/wb_server.h"
 
-#define WINBINDD_DIR "/tmp/.winbindd/"
-#define WINBINDD_ECHO_SOCKET  WINBINDD_DIR"echo"
-#define WINBINDD_ADDR_PREFIX "127.0.255."
-#define WINBINDD_ECHO_ADDR WINBINDD_ADDR_PREFIX"1"
-#define WINBINDD_ECHO_PORT 55555
-#define WINBINDD_SAMBA3_SOCKET WINBINDD_DIR"pipe"
-
-/*
-  state of an open winbind connection
-*/
-struct wbserver_connection {
-       DATA_BLOB input;
-       struct data_blob_list_item *send_queue;
-};
-
+void wbsrv_terminate_connection(struct wbsrv_connection *wbconn, const char *reason)
+{
+       stream_terminate_connection(wbconn->conn, reason);
+}
 
 /*
   called when we get a new connection
 */
-static void winbind_accept(struct stream_connection *conn)
+static void wbsrv_accept(struct stream_connection *conn)
 {
-       struct wbserver_connection *wbconn;
+       struct wbsrv_listen_socket *listen_socket = talloc_get_type(conn->private,
+                                                                   struct wbsrv_listen_socket);
+       struct wbsrv_connection *wbconn;
 
-       wbconn = talloc_zero(conn, struct wbserver_connection);
-       wbconn->input = data_blob_talloc(wbconn, NULL, 1024);
-       
+       wbconn = talloc_zero(conn, struct wbsrv_connection);
+       if (!wbconn) {
+               stream_terminate_connection(conn, "wbsrv_accept: out of memory");
+               return;
+       }
+       wbconn->conn            = conn;
+       wbconn->listen_socket   = listen_socket;
        conn->private = wbconn;
 }
 
 /*
   receive some data on a winbind connection
 */
-static void winbind_recv(struct stream_connection *conn, uint16_t flags)
+static void wbsrv_recv(struct stream_connection *conn, uint16_t flags)
 {
-       struct wbserver_connection *wbconn = talloc_get_type(conn->private, struct wbserver_connection);
-       NTSTATUS status;
+       struct wbsrv_connection *wbconn = talloc_get_type(conn->private, struct wbsrv_connection);
+       const struct wbsrv_protocol_ops *ops = wbconn->listen_socket->ops;
+       struct wbsrv_call *call;
+       NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
        size_t nread;
-       struct data_blob_list_item *q;
 
-       status = socket_recv(conn->socket, wbconn->input.data, wbconn->input.length, &nread, 0);
-       if (NT_STATUS_IS_ERR(status)) {
-               DEBUG(10,("socket_recv: %s\n",nt_errstr(status)));
-               stream_terminate_connection(conn, "socket_recv: failed\n");
+       /* avoid recursion, because of half async code */
+       if (wbconn->processing) {
+               EVENT_FD_NOT_READABLE(conn->event.fde);
                return;
        }
 
-       /* just reflect the data back down the socket */
-       q = talloc(wbconn, struct data_blob_list_item);
-       if (q == NULL) {
-               stream_terminate_connection(conn, "winbind_recv: out of memory\n");
+       /* if the used protocol doesn't support pending requests disallow them */
+       if (wbconn->pending_calls && !ops->allow_pending_calls) {
+               EVENT_FD_NOT_READABLE(conn->event.fde);
                return;
        }
 
-       q->blob = data_blob_talloc(q, wbconn->input.data, nread);
-       if (q->blob.data == NULL) {
-               stream_terminate_connection(conn, "winbind_recv: out of memory\n");
-               return;
-       }
+       if (wbconn->partial.length == 0) {
+               wbconn->partial = data_blob_talloc(wbconn, NULL, 4);
+               if (!wbconn->partial.data) goto nomem;
 
-       DLIST_ADD_END(wbconn->send_queue, q, struct data_blob_list_item *);
+               wbconn->partial_read = 0;
+       }
 
-       EVENT_FD_WRITEABLE(conn->event.fde);
-}
+       /* read in the packet length */
+       if (wbconn->partial_read < 4) {
+               uint32_t packet_length;
 
-/*
-  called when we can write to a connection
-*/
-static void winbind_send(struct stream_connection *conn, uint16_t flags)
-{
-       struct wbserver_connection *wbconn = talloc_get_type(conn->private, struct wbserver_connection);
+               status = socket_recv(conn->socket, 
+                                    wbconn->partial.data + wbconn->partial_read,
+                                    4 - wbconn->partial_read,
+                                    &nread, 0);
+               if (NT_STATUS_IS_ERR(status)) goto failed;
+               if (!NT_STATUS_IS_OK(status)) return;
 
-       while (wbconn->send_queue) {
-               struct data_blob_list_item *q = wbconn->send_queue;
-               NTSTATUS status;
-               size_t sendlen;
+               wbconn->partial_read += nread;
+               if (wbconn->partial_read != 4) return;
 
-               status = socket_send(conn->socket, &q->blob, &sendlen, 0);
-               if (NT_STATUS_IS_ERR(status)) {
-                       DEBUG(10,("socket_send() %s\n",nt_errstr(status)));
-                       stream_terminate_connection(conn, "socket_send: failed\n");
-                       return;
-               }
-               if (!NT_STATUS_IS_OK(status)) {
-                       return;
-               }
+               packet_length = ops->packet_length(wbconn->partial);
 
-               q->blob.length -= sendlen;
-               q->blob.data   += sendlen;
+               wbconn->partial.data = talloc_realloc(wbconn, wbconn->partial.data, 
+                                                     uint8_t, packet_length);
+               if (!wbconn->partial.data) goto nomem;
 
-               if (q->blob.length == 0) {
-                       DLIST_REMOVE(wbconn->send_queue, q);
-                       talloc_free(q);
-               }
+               wbconn->partial.length = packet_length;
        }
 
-       EVENT_FD_NOT_WRITEABLE(conn->event.fde);
-}
-
-static const struct stream_server_ops winbind_echo_ops = {
-       .name                   = "winbind_echo",
-       .accept_connection      = winbind_accept,
-       .recv_handler           = winbind_recv,
-       .send_handler           = winbind_send,
-};
-
-struct winbind3_connection {
-       struct winbindd_request *request;
-       struct winbindd_response *response;
-       DATA_BLOB partial;
-       size_t nsent;
-};
-
-static void winbind_samba3_accept(struct stream_connection *conn)
-{
-       struct winbind3_connection *wbconn;
-
-       wbconn = talloc(conn, struct winbind3_connection);
-       if (wbconn == NULL) {
-               DEBUG(0, ("talloc failed\n"));
-               stream_terminate_connection(conn, "talloc failed");
+       /* read in the body */
+       status = socket_recv(conn->socket, 
+                            wbconn->partial.data + wbconn->partial_read,
+                            wbconn->partial.length - wbconn->partial_read,
+                            &nread, 0);
+       if (NT_STATUS_IS_ERR(status)) goto failed;
+       if (!NT_STATUS_IS_OK(status)) return;
+
+       wbconn->partial_read += nread;
+       if (wbconn->partial_read != wbconn->partial.length) return;
+
+       /* we have a full request - parse it */
+       status = ops->pull_request(wbconn->partial, wbconn, &call);
+       if (!NT_STATUS_IS_OK(status)) goto failed;
+       call->wbconn = wbconn;
+
+       /*
+        * we have parsed the request, so we can reset the wbconn->partial_read,
+        * maybe we could also free wbconn->partial, but for now we keep it,
+        * and overwrite it the next time
+        */
+       wbconn->partial_read = 0;
+
+       /* actually process the request */
+       wbconn->pending_calls++;
+       wbconn->processing = True;
+       status = ops->handle_call(call);
+       wbconn->processing = False;
+       if (!NT_STATUS_IS_OK(status)) goto failed;
+
+       /* if the backend want to reply later just return here */
+       if (call->flags & WBSRV_CALL_FLAGS_REPLY_ASYNC) {
                return;
        }
 
-       wbconn->request = NULL;
-       wbconn->response = NULL;
-       ZERO_STRUCT(wbconn->partial);
-       conn->private = wbconn;
+       /*
+        * and queue the reply, this implies talloc_free(call),
+        * and set the socket to readable again
+        */
+       status = wbsrv_queue_reply(call);
+       if (!NT_STATUS_IS_OK(status)) goto failed;
+
+       return;
+nomem:
+       status = NT_STATUS_NO_MEMORY;
+failed:
+       wbsrv_terminate_connection(wbconn, nt_errstr(status));
 }
 
-static void winbind_samba3_recv(struct stream_connection *conn, uint16_t flags)
+/*
+ * queue a wbsrv_call reply on a wbsrv_connection
+ * NOTE: that this implies talloc_free(call),
+ *       use talloc_reference(call) if you need it after
+ *       calling wbsrv_queue_reply
+ * NOTE: if this function desn't return NT_STATUS_OK,
+ *       the caller needs to call
+ *           wbsrv_terminate_connection(call->wbconn, "reason...");
+ *           return;
+ *       to drop the connection
+ */
+NTSTATUS wbsrv_queue_reply(struct wbsrv_call *call)
 {
-       struct winbind3_connection *wbconn =
-               talloc_get_type(conn->private, struct winbind3_connection);
-       size_t npending, received;
-       NTSTATUS res;
-
-       if (!NT_STATUS_IS_OK(socket_pending(conn->socket, &npending))) {
-               stream_terminate_connection(conn, "socket_pending() failed");
-               return;
-       }
-
-       if (npending == 0) {
-               stream_terminate_connection(conn, "EOF from client");
-               return;
-       }
-
-       if (wbconn->partial.length + npending >
-           sizeof(struct winbindd_request)) {
-               npending = sizeof(struct winbindd_request) -
-                       wbconn->partial.length;
-       }
-
-       wbconn->partial.data =
-               talloc_realloc_size(wbconn, wbconn->partial.data,
-                                   wbconn->partial.length + npending);
-       if (wbconn->partial.data == NULL) {
-               stream_terminate_connection(conn, "talloc_realloc failed");
-               return;
-       }
-
-       res = socket_recv(conn->socket,
-                         &wbconn->partial.data[wbconn->partial.length],
-                         npending, &received, 0);
-
-       if (!NT_STATUS_IS_OK(res)) {
-               DEBUG(5, ("sock_recv failed: %s\n", nt_errstr(res)));
-               stream_terminate_connection(conn, "talloc_realloc failed");
-               return;
-       }
-
-       wbconn->partial.length += received;
-
-       if (wbconn->partial.length < sizeof(struct winbindd_request)) {
-               return;
-       }
-
-       wbconn->request = (struct winbindd_request *)wbconn->partial.data;
-
-       SMB_ASSERT(wbconn->response == NULL);
-
-       wbconn->response = talloc_zero(wbconn, struct winbindd_response);
-       if (wbconn->response == NULL) {
-               stream_terminate_connection(conn, "talloc_zero failed");
-               return;
-       }
+       struct wbsrv_connection *wbconn = call->wbconn;
+       const struct wbsrv_protocol_ops *ops = wbconn->listen_socket->ops;
+       struct data_blob_list_item *rep;
+       NTSTATUS status;
 
-       wbconn->response->length = sizeof(struct winbindd_response);
-       wbconn->response->result = WINBINDD_ERROR;
+       /* and now encode the reply */
+       rep = talloc(wbconn, struct data_blob_list_item);
+       NT_STATUS_HAVE_NO_MEMORY(rep);
 
-       if (wbconn->request->length != sizeof(struct winbindd_request)) {
-               DEBUG(10, ("Got invalid request length %d\n",
-                          wbconn->request->length));
-               goto done;
-       }
+       status = ops->push_reply(call, rep, &rep->blob);
+       NT_STATUS_NOT_OK_RETURN(status);
 
-       DEBUG(10, ("Got winbind request %d\n", wbconn->request->cmd));
-
-       switch(wbconn->request->cmd) {
-       case WINBINDD_INTERFACE_VERSION:
-               wbconn->response->result = WINBINDD_OK;
-               wbconn->response->data.interface_version =
-                       WINBIND_INTERFACE_VERSION;
-               break;
-       case WINBINDD_PRIV_PIPE_DIR:
-               wbconn->response->result = WINBINDD_OK;
-               wbconn->response->extra_data =
-                       smbd_tmp_path(wbconn->response, "winbind_priv/pipe");
-               if (wbconn->response->extra_data == NULL) {
-                       stream_terminate_connection(conn,
-                                                   "smbd_tmp_path failed");
-                       return;
-               }
-               wbconn->response->length +=
-                       strlen(wbconn->response->extra_data) + 1;
-               break;
-       case WINBINDD_PING:
-               wbconn->response->result = WINBINDD_OK;
-               break;
-       default:
-               break;
+       if (!wbconn->send_queue) {
+               EVENT_FD_WRITEABLE(wbconn->conn->event.fde);
        }
+       DLIST_ADD_END(wbconn->send_queue, rep, struct data_blob_list_item *);
 
- done:
-       talloc_free(wbconn->partial.data);
-       wbconn->partial.data = NULL;
-       wbconn->nsent = 0;
-
-       wbconn->partial.data = (char *)wbconn->response;
-       wbconn->partial.length = sizeof(struct winbindd_response);
+       EVENT_FD_READABLE(wbconn->conn->event.fde);
 
-       EVENT_FD_NOT_READABLE(conn->event.fde);
-       EVENT_FD_WRITEABLE(conn->event.fde);
+       /* the call isn't needed any more */
+       wbconn->pending_calls--;
+       talloc_free(call);
+       return NT_STATUS_OK;
 }
 
-static void winbind_samba3_send(struct stream_connection *conn, uint16_t flags)
+/*
+  called when we can write to a connection
+*/
+static void wbsrv_send(struct stream_connection *conn, uint16_t flags)
 {
-       struct winbind3_connection *wbconn =
-               talloc_get_type(conn->private, struct winbind3_connection);
-       size_t nsent;
-       NTSTATUS res;
-
-       res = socket_send(conn->socket, &wbconn->partial, &nsent, 0);
-       if (!NT_STATUS_IS_OK(res)) {
-               stream_terminate_connection(conn, "socket_send() failed");
-               return;
-       }
+       struct wbsrv_connection *wbconn = talloc_get_type(conn->private, struct wbsrv_connection);
+       NTSTATUS status;
 
-       wbconn->partial.data += nsent;
-       wbconn->partial.length -= nsent;
+       while (wbconn->send_queue) {
+               struct data_blob_list_item *q = wbconn->send_queue;
+               size_t sendlen;
 
-       if (wbconn->partial.length != 0) {
-               return;
-       }
+               status = socket_send(conn->socket, &q->blob, &sendlen, 0);
+               if (NT_STATUS_IS_ERR(status)) goto failed;
+               if (!NT_STATUS_IS_OK(status)) return;
 
-       if (wbconn->response->extra_data != NULL) {
-               wbconn->partial.data = wbconn->response->extra_data;
-               wbconn->partial.length = wbconn->response->length -
-                       sizeof(struct winbindd_response);
-               wbconn->response->extra_data = NULL;
-               return;
+               q->blob.length -= sendlen;
+               q->blob.data   += sendlen;
+
+               if (q->blob.length == 0) {
+                       DLIST_REMOVE(wbconn->send_queue, q);
+                       talloc_free(q);
+               }
        }
 
-       talloc_free(wbconn->response);
-       wbconn->response = NULL;
-       wbconn->partial.data = NULL;
        EVENT_FD_NOT_WRITEABLE(conn->event.fde);
-       EVENT_FD_READABLE(conn->event.fde);
+       return;
+failed:
+       wbsrv_terminate_connection(wbconn, nt_errstr(status));
 }
 
-static const struct stream_server_ops winbind_samba3_ops = {
-       .name                   = "winbind_samba3",
-       .accept_connection      = winbind_samba3_accept,
-       .recv_handler           = winbind_samba3_recv,
-       .send_handler           = winbind_samba3_send,
+static const struct stream_server_ops wbsrv_ops = {
+       .name                   = "winbind",
+       .accept_connection      = wbsrv_accept,
+       .recv_handler           = wbsrv_recv,
+       .send_handler           = wbsrv_send
+};
+
+static const struct wbsrv_protocol_ops wbsrv_samba3_protocol_ops = {
+       .name                   = "winbind samba3 protocol",
+       .allow_pending_calls    = False,
+       .packet_length          = wbsrv_samba3_packet_length,
+       .pull_request           = wbsrv_samba3_pull_request,
+       .handle_call            = wbsrv_samba3_handle_call,
+       .push_reply             = wbsrv_samba3_push_reply
 };
 
 /*
@@ -315,6 +251,8 @@ static void winbind_task_init(struct task_server *task)
        uint16_t port = 1;
        const struct model_ops *model_ops;
        NTSTATUS status;
+       struct wbsrv_service *service;
+       struct wbsrv_listen_socket *listen_socket;
 
        /* within the winbind task we want to be a single process, so
           ask for the single process model ops and pass these to the
@@ -330,36 +268,46 @@ static void winbind_task_init(struct task_server *task)
                mkdir(WINBINDD_DIR, 0755);
        }
 
-       status = stream_setup_socket(task->event_ctx, model_ops, &winbind_echo_ops, 
-                                    "unix", WINBINDD_ECHO_SOCKET, &port, NULL);
-       if (!NT_STATUS_IS_OK(status)) {
-               DEBUG(0,("service_setup_stream_socket(path=%s) failed - %s\n",
-                        WINBINDD_ECHO_SOCKET, nt_errstr(status)));
-               task_server_terminate(task, "winbind Failed to find to ECHO unix socket");
-               return;
-       }
-
+       service = talloc_zero(task, struct wbsrv_service);
+       if (!service) goto nomem;
+       service->task   = task;
+
+       /* setup the unprivileged samba3 socket */
+       listen_socket = talloc(service, struct wbsrv_listen_socket);
+       if (!listen_socket) goto nomem;
+       listen_socket->socket_path      = WINBINDD_SAMBA3_SOCKET;
+       if (!listen_socket->socket_path) goto nomem;
+       listen_socket->service          = service;
+       listen_socket->privileged       = False;
+       listen_socket->ops              = &wbsrv_samba3_protocol_ops;
        status = stream_setup_socket(task->event_ctx, model_ops,
-                                    &winbind_samba3_ops, "unix",
-                                    WINBINDD_SAMBA3_SOCKET, &port, NULL);
-       if (!NT_STATUS_IS_OK(status)) {
-               DEBUG(0,("service_setup_stream_socket(path=%s) failed - %s\n",
-                        WINBINDD_ECHO_SOCKET, nt_errstr(status)));
-               task_server_terminate(task, "winbind Failed to find to "
-                                     "SAMBA3 unix socket");
-               return;
-       }
-
-       port = WINBINDD_ECHO_PORT;
-
-       status = stream_setup_socket(task->event_ctx, model_ops, &winbind_echo_ops,
-                                    "ipv4", WINBINDD_ECHO_ADDR, &port, NULL);
-       if (!NT_STATUS_IS_OK(status)) {
-               DEBUG(0,("service_setup_stream_socket(address=%s,port=%u) failed - %s\n",
-                        WINBINDD_ECHO_ADDR, port, nt_errstr(status)));
-               task_server_terminate(task, "winbind Failed to find to ECHO tcp socket");
-               return;
-       }
+                                    &wbsrv_ops, "unix",
+                                    listen_socket->socket_path, &port, listen_socket);
+       if (!NT_STATUS_IS_OK(status)) goto listen_failed;
+
+       /* setup the privileged samba3 socket */
+       listen_socket = talloc(service, struct wbsrv_listen_socket);
+       if (!listen_socket) goto nomem;
+       listen_socket->socket_path      = smbd_tmp_path(listen_socket, WINBINDD_SAMBA3_PRIVILEGED_SOCKET);
+       if (!listen_socket->socket_path) goto nomem;
+       listen_socket->service          = service;
+       listen_socket->privileged       = True;
+       listen_socket->ops              = &wbsrv_samba3_protocol_ops;
+       status = stream_setup_socket(task->event_ctx, model_ops,
+                                    &wbsrv_ops, "unix",
+                                    listen_socket->socket_path, &port, listen_socket);
+       if (!NT_STATUS_IS_OK(status)) goto listen_failed;
+
+       return;
+
+listen_failed:
+       DEBUG(0,("stream_setup_socket(path=%s) failed - %s\n",
+                listen_socket->socket_path, nt_errstr(status)));
+       task_server_terminate(task, nt_errstr(status));
+       return;
+nomem:
+       task_server_terminate(task, nt_errstr(NT_STATUS_NO_MEMORY));
+       return;
 }
 
 /*
diff --git a/source4/winbind/wb_server.h b/source4/winbind/wb_server.h
new file mode 100644 (file)
index 0000000..2369e22
--- /dev/null
@@ -0,0 +1,115 @@
+/* 
+   Unix SMB/CIFS implementation.
+   Main winbindd server routines
+
+   Copyright (C) Stefan Metzmacher     2005
+   Copyright (C) Andrew Tridgell       2005
+   
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2 of the License, or
+   (at your option) any later version.
+   
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+   
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#define WINBINDD_DIR "/tmp/.winbindd/"
+#define WINBINDD_SOCKET WINBINDD_DIR"socket"
+/* the privileged socket is in smbd_tmp_dir() */
+#define WINBINDD_PRIVILEGED_SOCKET "winbind_socket"
+
+#define WINBINDD_SAMBA3_SOCKET WINBINDD_DIR"pipe"
+/* the privileged socket is in smbd_tmp_dir() */
+#define WINBINDD_SAMBA3_PRIVILEGED_SOCKET "winbind_pipe"
+
+/* this struct stores global data for the winbind task */
+struct wbsrv_service {
+       struct task_server *task;
+};
+
+/* 
+  this is an abstraction for the actual protocol being used,
+  so that we can listen on different sockets with different protocols
+  e.g. the old samba3 protocol on one socket and a new protocol on another socket
+*/
+struct wbsrv_protocol_ops {
+       const char *name;
+       BOOL allow_pending_calls;
+       uint32_t (*packet_length)(DATA_BLOB blob);
+       NTSTATUS (*pull_request)(DATA_BLOB blob, TALLOC_CTX *mem_ctx, struct wbsrv_call **call);
+       NTSTATUS (*handle_call)(struct wbsrv_call *call);
+       NTSTATUS (*push_reply)(struct wbsrv_call *call, TALLOC_CTX *mem_ctx, DATA_BLOB *blob);
+};
+
+/*
+  state of a listen socket and it's protocol information
+*/
+struct wbsrv_listen_socket {
+       const char *socket_path;
+       struct wbsrv_service *service;
+       BOOL privileged;
+       const struct wbsrv_protocol_ops *ops;
+};
+
+/*
+  state of an open winbind connection
+*/
+struct wbsrv_connection {
+       /* stream connection we belong to */
+       struct stream_connection *conn;
+
+       /* the listening socket we belong to, it holds protocol hooks */
+       struct wbsrv_listen_socket *listen_socket;
+
+       /* storage for protocol specific data */
+       void *protocol_private_data;
+
+       /* the partial data we've receiced yet */
+       DATA_BLOB partial;
+
+       /* the amount that we used yet from the partial buffer */
+       uint32_t partial_read;
+
+       /* prevent loops when we use half async code, while processing a requuest */
+       BOOL processing;
+
+       /* how many calls are pending */
+       uint32_t pending_calls;
+
+       struct data_blob_list_item *send_queue;
+};
+
+/*
+  state of one request
+
+  NOTE about async replies:
+   if the backend wants to reply later:
+   - it should set the WBSRV_CALL_FLAGS_REPLY_ASYNC flag, and may set a talloc_destructor
+     on the this structure or on the private_data (if it's a talloc child of this structure),
+     so that wbsrv_terminate_connection called by another call clean up the whole connection
+     correct.
+   - When the backend is ready to reply it should call wbsrv_queue_reply(call),
+     wbsrv_queue_reply implies talloc_free(call), so the backend should use talloc_reference(call),
+     if it needs it later. 
+   - If wbsrv_queue_reply doesn't return NT_STATUS_OK, the backend function should call,
+       wbsrv_terminate_connection(call->wbconn, nt_errstr(status));
+       return;
+
+*/
+struct wbsrv_call {
+#define WBSRV_CALL_FLAGS_REPLY_ASYNC 0x00000001
+       uint32_t flags;
+
+       /* the connection the call belongs to */
+       struct wbsrv_connection *wbconn;
+
+       /* storage for protocol specific data */
+       void *private_data;
+};