From 45a85bdd353418828df8017a9d7eb7c14f6f0cd3 Mon Sep 17 00:00:00 2001 From: Stefan Metzmacher Date: Tue, 13 Jul 2004 21:04:56 +0000 Subject: r1486: commit the start of the generic server infastructure the idea is to have services as modules (smb, dcerpc, swat, ...) the process_model don't know about the service it self anymore. TODO: - the smbsrv should use the smbsrv_send function - the service subsystem init should be done like for other modules - we need to have a generic socket subsystem, which handle stream, datagram, and virtuell other sockets( e.g. for the ntvfs_ipc module to connect to the dcerpc server , or for smb or dcerpc or whatever to connect to a server wide auth service) - and other fixes... NOTE: process model pthread seems to be broken( but also before this patch!) metze (This used to be commit bbe5e00715ca4013ff0dbc345aa97adc6b5c2458) --- source4/auth/auth.c | 5 + source4/include/includes.h | 2 + source4/include/local.h | 7 - source4/ntvfs/cifs/vfs_cifs.c | 6 +- source4/param/loadparm.c | 5 + source4/rpc_server/dcerpc_server.c | 105 ++++++++++- source4/rpc_server/dcerpc_server.h | 4 +- source4/rpc_server/dcerpc_tcp.c | 350 ++++++++++++++----------------------- source4/smb_server/connection.c | 2 +- source4/smb_server/negprot.c | 4 +- source4/smb_server/reply.c | 2 +- source4/smb_server/request.c | 10 +- source4/smb_server/service.c | 2 +- source4/smb_server/smb_server.c | 191 ++++++++++---------- source4/smb_server/smb_server.h | 6 +- source4/smbd/config.m4 | 7 +- source4/smbd/config.mk | 24 ++- source4/smbd/process_model.c | 10 +- source4/smbd/process_model.h | 17 +- source4/smbd/process_single.c | 119 ++++++++----- source4/smbd/process_standard.c | 138 ++++++++------- source4/smbd/process_thread.c | 153 +++++++++------- source4/smbd/rewrite.c | 6 +- source4/smbd/server.c | 42 +++-- source4/smbd/server.h | 40 +++++ source4/smbd/service.c | 241 +++++++++++++++++++++++++ source4/smbd/service.h | 127 ++++++++++++++ 27 files changed, 1076 insertions(+), 549 deletions(-) create mode 100644 source4/smbd/server.h create mode 100644 source4/smbd/service.c create mode 100644 source4/smbd/service.h diff --git a/source4/auth/auth.c b/source4/auth/auth.c index 703df21d45..32913f9996 100644 --- a/source4/auth/auth.c +++ b/source4/auth/auth.c @@ -501,3 +501,8 @@ BOOL auth_init(void) DEBUG(3,("AUTH subsystem version %d initialised\n", AUTH_INTERFACE_VERSION)); return True; } + +NTSTATUS server_service_auth_init(void) +{ + return NT_STATUS_OK; +} diff --git a/source4/include/includes.h b/source4/include/includes.h index 058d547736..9ce2b71982 100644 --- a/source4/include/includes.h +++ b/source4/include/includes.h @@ -668,6 +668,8 @@ extern int errno; #include "librpc/rpc/dcerpc.h" +#include "smbd/server.h" +#include "smbd/service.h" #include "rpc_server/dcerpc_server.h" #include "smb_server/smb_server.h" #include "ntvfs/ntvfs.h" diff --git a/source4/include/local.h b/source4/include/local.h index 3f0f8618aa..67b83c316d 100644 --- a/source4/include/local.h +++ b/source4/include/local.h @@ -224,11 +224,4 @@ /* Max number of simultaneous winbindd socket connections. */ #define WINBINDD_MAX_SIMULTANEOUS_CLIENTS 200 -/* size of listen() backlog in smbd */ -#define SMBD_LISTEN_BACKLOG 10 - -/* the range of ports to try for dcerpc over tcp endpoints */ -#define DCERPC_TCP_LOW_PORT 1024 -#define DCERPC_TCP_HIGH_PORT 1300 - #endif diff --git a/source4/ntvfs/cifs/vfs_cifs.c b/source4/ntvfs/cifs/vfs_cifs.c index 93b9f2630e..b6d3486ad8 100644 --- a/source4/ntvfs/cifs/vfs_cifs.c +++ b/source4/ntvfs/cifs/vfs_cifs.c @@ -51,7 +51,7 @@ struct async_info { static void idle_func(struct cli_transport *transport, void *p_private) { struct cvfs_private *private = p_private; - if (socket_pending(private->tcon->smb_conn->socket.fd)) { + if (socket_pending(private->tcon->smb_conn->connection->socket->fde->fd)) { smbd_process_async(private->tcon->smb_conn); } } @@ -164,7 +164,7 @@ static NTSTATUS cvfs_connect(struct smbsrv_request *req, const char *sharename) fde.private = private; fde.handler = cifs_socket_handler; - event_add_fd(tcon->smb_conn->events, &fde); + event_add_fd(tcon->smb_conn->connection->event.ctx, &fde); /* we need to receive oplock break requests from the server */ cli_oplock_handler(private->transport, oplock_handler, private); @@ -180,7 +180,7 @@ static NTSTATUS cvfs_disconnect(struct smbsrv_tcon *tcon) { struct cvfs_private *private = tcon->ntvfs_private; - event_remove_fd_all(tcon->smb_conn->events, private->transport->socket->fd); + event_remove_fd_all(tcon->smb_conn->connection->event.ctx, private->transport->socket->fd); smb_tree_disconnect(private->tree); cli_tree_close(private->tree); diff --git a/source4/param/loadparm.c b/source4/param/loadparm.c index 5493b2617c..293df3bb1f 100644 --- a/source4/param/loadparm.c +++ b/source4/param/loadparm.c @@ -137,6 +137,7 @@ typedef struct char *szWINSHook; char *szWINSPartners; char **dcerpc_ep_servers; + char **server_services; char *szWinbindUID; char *szWinbindGID; char *szNonUnixAccountRange; @@ -568,6 +569,7 @@ static struct parm_struct parm_table[] = { {"bind interfaces only", P_BOOL, P_GLOBAL, &Globals.bBindInterfacesOnly, NULL, NULL, FLAG_ADVANCED | FLAG_WIZARD | FLAG_DEVELOPER}, {"ntvfs handler", P_STRING, P_LOCAL, &sDefault.ntvfs_handler, NULL, NULL, FLAG_ADVANCED}, {"dcerpc endpoint servers", P_LIST, P_GLOBAL, &Globals.dcerpc_ep_servers, NULL, NULL, FLAG_ADVANCED}, + {"server services", P_LIST, P_GLOBAL, &Globals.server_services, NULL, NULL, FLAG_ADVANCED}, {"Security Options", P_SEP, P_SEPARATOR}, @@ -959,6 +961,8 @@ static void init_globals(void) Globals.dcerpc_ep_servers = str_list_make("epmapper srvsvc wkssvc rpcecho samr netlogon lsarpc spoolss", NULL); + Globals.server_services = str_list_make("smb rpc", NULL); + Globals.AuthMethods = str_list_make("guest sam_ignoredomain", NULL); string_set(&Globals.szSMBPasswdFile, dyn_SMB_PASSWD_FILE); @@ -1218,6 +1222,7 @@ FN_GLOBAL_STRING(lp_printcapname, &Globals.szPrintcapname) FN_GLOBAL_STRING(lp_lockdir, &Globals.szLockDir) FN_GLOBAL_STRING(lp_piddir, &Globals.szPidDir) FN_GLOBAL_LIST(lp_dcerpc_endpoint_servers, &Globals.dcerpc_ep_servers) +FN_GLOBAL_LIST(lp_server_services, &Globals.server_services) FN_GLOBAL_STRING(lp_rootdir, &Globals.szRootdir) FN_GLOBAL_STRING(lp_defaultservice, &Globals.szDefaultService) FN_GLOBAL_STRING(lp_hosts_equiv, &Globals.szHostsEquiv) diff --git a/source4/rpc_server/dcerpc_server.c b/source4/rpc_server/dcerpc_server.c index e5c4c120a5..34756349c6 100644 --- a/source4/rpc_server/dcerpc_server.c +++ b/source4/rpc_server/dcerpc_server.c @@ -271,6 +271,7 @@ NTSTATUS dcesrv_endpoint_connect(struct dcesrv_context *dce_ctx, (*p)->auth_state.auth_info = NULL; (*p)->auth_state.gensec_security = NULL; (*p)->auth_state.session_info = NULL; + (*p)->srv_conn = NULL; return NT_STATUS_OK; } @@ -1016,6 +1017,87 @@ NTSTATUS dcesrv_init_context(struct dcesrv_context *dce_ctx) return NT_STATUS_OK; } +static void dcesrv_init(struct server_service *service, const struct model_ops *model_ops) +{ + TALLOC_CTX *mem_ctx; + struct dcesrv_context *dce_ctx; + int i; + const char **endpoint_servers = lp_dcerpc_endpoint_servers(); + + DEBUG(0,("dcesrv_init\n")); + + + if (!endpoint_servers) { + DEBUG(0,("dcesrv_init_context: no endpoint servers configured\n")); + return; + } + + mem_ctx = talloc_init("struct dcesrv_context"); + + dce_ctx = talloc_p(mem_ctx, struct dcesrv_context); + if (!dce_ctx) { + DEBUG(0,("talloc_p(mem_ctx, struct dcesrv_context) failed\n")); + return; + } + + ZERO_STRUCTP(dce_ctx); + dce_ctx->mem_ctx = mem_ctx; + dce_ctx->endpoint_list = NULL; + + for (i=0;endpoint_servers[i];i++) { + NTSTATUS ret; + const struct dcesrv_endpoint_server *ep_server; + + ep_server = dcesrv_ep_server_byname(endpoint_servers[i]); + if (!ep_server) { + DEBUG(0,("dcesrv_init_context: failed to find endpoint server = '%s'\n", endpoint_servers[i])); + return; + } + + ret = ep_server->init_server(dce_ctx, ep_server); + if (!NT_STATUS_IS_OK(ret)) { + DEBUG(0,("dcesrv_init_context: failed to init endpoint server = '%s'\n", endpoint_servers[i])); + return; + } + } + + dcesrv_tcp_init(service, model_ops, dce_ctx); + + return; +} + +static void dcesrv_accept(struct server_connection *srv_conn) +{ + dcesrv_tcp_accept(srv_conn); +} + +static void dcesrv_recv(struct server_connection *srv_conn, time_t t, uint16_t flags) +{ + dcesrv_tcp_recv(srv_conn, t, flags); +} + +static void dcesrv_send(struct server_connection *srv_conn, time_t t, uint16_t flags) +{ + dcesrv_tcp_send(srv_conn, t, flags); +} + +static void dcesrv_idle(struct server_connection *srv_conn, time_t t) +{ + dcesrv_tcp_idle(srv_conn, t); +} + +static void dcesrv_close(struct server_connection *srv_conn, const char *reason) +{ + dcesrv_tcp_close(srv_conn, reason); + return; +} + +static void dcesrv_exit(struct server_service *service, const char *reason) +{ + dcesrv_tcp_exit(service, reason); + return; +} + /* the list of currently registered DCERPC endpoint servers. */ static struct { @@ -1101,7 +1183,7 @@ const struct dcesrv_critical_sizes *dcerpc_module_version(void) /* initialise the DCERPC subsystem */ -BOOL dcesrv_init(void) +BOOL subsystem_dcerpc_init(void) { NTSTATUS status; @@ -1116,3 +1198,24 @@ BOOL dcesrv_init(void) DEBUG(3,("DCERPC subsystem version %d initialised\n", DCERPC_MODULE_VERSION)); return True; } + +static const struct server_service_ops dcesrv_ops = { + .name = "rpc", + .service_init = dcesrv_init, + .accept_connection = dcesrv_accept, + .recv_handler = dcesrv_recv, + .send_handler = dcesrv_send, + .idle_handler = dcesrv_idle, + .close_connection = dcesrv_close, + .service_exit = dcesrv_exit, +}; + +const struct server_service_ops *dcesrv_get_ops(void) +{ + return &dcesrv_ops; +} + +NTSTATUS server_service_rpc_init(void) +{ + return NT_STATUS_OK; +} diff --git a/source4/rpc_server/dcerpc_server.h b/source4/rpc_server/dcerpc_server.h index b1754dd4a3..15da3e38bb 100644 --- a/source4/rpc_server/dcerpc_server.h +++ b/source4/rpc_server/dcerpc_server.h @@ -39,7 +39,7 @@ struct dcesrv_ep_description { enum endpoint_type type; union { const char *smb_pipe; - uint32_t tcp_port; + uint16_t tcp_port; } info; }; @@ -132,6 +132,8 @@ struct dcesrv_connection { /* the current authentication state */ struct dcesrv_auth auth_state; + + struct server_connection *srv_conn; }; diff --git a/source4/rpc_server/dcerpc_tcp.c b/source4/rpc_server/dcerpc_tcp.c index 83a9140dd1..c506cf16cf 100644 --- a/source4/rpc_server/dcerpc_tcp.c +++ b/source4/rpc_server/dcerpc_tcp.c @@ -23,38 +23,11 @@ #include "includes.h" -struct rpc_server_context { - struct dcesrv_ep_description *ep_description; +struct dcesrv_socket_context { const struct dcesrv_endpoint *endpoint; - const struct model_ops *model_ops; - struct dcesrv_connection *dce_conn; - struct dcesrv_context dcesrv_context; - int socket_fd; - struct event_context *events; + struct dcesrv_context *dcesrv_ctx; }; -/* - a callback from the process model termination routine -*/ -void rpc_server_terminate(void *rr) -{ - struct rpc_server_context *r = rr; - - dcesrv_endpoint_disconnect(r->dce_conn); - close(r->socket_fd); - event_remove_fd_all(r->events, r->socket_fd); - free(r); -} - -/* - called when a rpc session needs to be shutdown -*/ -static void terminate_rpc_session(struct rpc_server_context *r, const char *reason) -{ - r->model_ops->terminate_rpc_connection(r, reason); -} - - /* write_fn callback for dcesrv_output() */ @@ -69,45 +42,128 @@ static ssize_t dcerpc_write_fn(void *private, const void *buf, size_t count) return ret; } +void dcesrv_terminate_connection(struct dcesrv_connection *dce_conn, const char *reason) +{ + server_terminate_connection(dce_conn->srv_conn, reason); +} + /* - called when a RPC socket becomes writable + add a socket address to the list of events, one event per dcerpc endpoint */ -static void dcerpc_write_handler(struct event_context *ev, struct fd_event *fde, - time_t t, uint16_t flags) +static void add_socket_rpc(struct server_service *service, + const struct model_ops *model_ops, + struct socket_context *socket_ctx, + struct dcesrv_context *dce_ctx, + struct in_addr *ifip) { - struct rpc_server_context *r = fde->private; - NTSTATUS status; + struct dcesrv_endpoint *e; - status = dcesrv_output(r->dce_conn, fde, dcerpc_write_fn); - if (NT_STATUS_IS_ERR(status)) { - /* TODO: destroy fd_event? */ + for (e=dce_ctx->endpoint_list;e;e=e->next) { + if (e->ep_description.type == ENDPOINT_TCP) { + struct server_socket *sock; + struct dcesrv_socket_context *dcesrv_sock; + + sock = service_setup_socket(service,model_ops,socket_ctx,ifip, &e->ep_description.info.tcp_port); + if (!sock) { + DEBUG(0,("service_setup_socket(port=%u) failed\n",e->ep_description.info.tcp_port)); + continue; + } + + dcesrv_sock = talloc_p(sock->mem_ctx, struct dcesrv_socket_context); + if (!dcesrv_sock) { + DEBUG(0,("talloc_p(sock->mem_ctx, struct dcesrv_socket_context) failed\n")); + continue; + } + + /* remeber the enpoint of this socket */ + dcesrv_sock->endpoint = e; + dcesrv_sock->dcesrv_ctx = dce_ctx; + + sock->private_data = dcesrv_sock; + } } +} - if (!r->dce_conn->call_list || !r->dce_conn->call_list->replies) { - fde->flags &= ~EVENT_FD_WRITE; +/**************************************************************************** + Open the listening sockets for RPC over TCP +****************************************************************************/ +void dcesrv_tcp_init(struct server_service *service, const struct model_ops *model_ops, struct dcesrv_context *dce_ctx) +{ + DEBUG(1,("dcesrv_tcp_init\n")); + + if (lp_interfaces() && lp_bind_interfaces_only()) { + int num_interfaces = iface_count(); + int i; + for(i = 0; i < num_interfaces; i++) { + struct in_addr *ifip = iface_n_ip(i); + if (ifip == NULL) { + continue; + } + add_socket_rpc(service, model_ops, NULL, dce_ctx, ifip); + } + } else { + struct in_addr *ifip; + TALLOC_CTX *mem_ctx = talloc_init("open_sockets_smbd"); + if (!mem_ctx) { + smb_panic("No memory"); + } + + ifip = interpret_addr2(mem_ctx, lp_socket_address()); + add_socket_rpc(service, model_ops, NULL, dce_ctx, ifip); + talloc_destroy(mem_ctx); } + + return; } -/* - called when a RPC socket becomes readable -*/ -static void dcerpc_read_handler(struct event_context *ev, struct fd_event *fde, - time_t t, uint16_t flags) +void dcesrv_tcp_accept(struct server_connection *conn) +{ + NTSTATUS status; + struct dcesrv_socket_context *dcesrv_sock = conn->server_socket->private_data; + struct dcesrv_connection *dcesrv_conn = NULL; + + DEBUG(5,("dcesrv_tcp_accept\n")); + + + + status = dcesrv_endpoint_connect(dcesrv_sock->dcesrv_ctx, dcesrv_sock->endpoint, &dcesrv_conn); + if (!NT_STATUS_IS_OK(status)) { + DEBUG(0,("dcesrv_tcp_accept: dcesrv_endpoint_connect failed: %s\n", + nt_errstr(status))); + return; + } + + dcesrv_conn->srv_conn = conn; + + conn->private_data = dcesrv_conn; + + /* TODO: this should to the generic code + * but the smb server can't handle it yet + * --metze + */ + set_blocking(conn->socket->fde->fd, False); + + return; +} + +void dcesrv_tcp_recv(struct server_connection *conn, time_t t, uint16_t flags) { - struct rpc_server_context *r = fde->private; + struct dcesrv_connection *dce_conn = conn->private_data; DATA_BLOB blob; ssize_t ret; + DEBUG(10,("dcesrv_tcp_recv\n")); + blob = data_blob(NULL, 0x4000); if (!blob.data) { - terminate_rpc_session(r, "out of memory"); + dcesrv_terminate_connection(dce_conn, "eof on socket"); return; } - ret = read(fde->fd, blob.data, blob.length); + ret = read(conn->socket->fde->fd, blob.data, blob.length); if (ret == 0 || (ret == -1 && errno != EINTR)) { data_blob_free(&blob); - terminate_rpc_session(r, "eof on socket"); + dcesrv_terminate_connection(dce_conn, "eof on socket"); return; } if (ret == -1) { @@ -117,197 +173,61 @@ static void dcerpc_read_handler(struct event_context *ev, struct fd_event *fde, blob.length = ret; - dcesrv_input(r->dce_conn, &blob); + dcesrv_input(dce_conn, &blob); data_blob_free(&blob); - if (r->dce_conn->call_list && r->dce_conn->call_list->replies) { - fde->flags |= EVENT_FD_WRITE; + if (dce_conn->call_list && dce_conn->call_list->replies) { + conn->socket->fde->flags |= EVENT_FD_WRITE; } -} - - - -/* - called when a RPC socket becomes readable -*/ -static void dcerpc_io_handler(struct event_context *ev, struct fd_event *fde, - time_t t, uint16_t flags) -{ - if (flags & EVENT_FD_WRITE) { - dcerpc_write_handler(ev, fde, t, flags); - } - - if (flags & EVENT_FD_READ) { - dcerpc_read_handler(ev, fde, t, flags); - } + return; } - -/* - initialise a server_context from a open socket and register a event handler - for reading from that socket -*/ -void init_rpc_session(struct event_context *ev, void *private, int fd) + +void dcesrv_tcp_send(struct server_connection *conn, time_t t, uint16_t flags) { - struct fd_event fde; - struct rpc_server_context *r = private; + struct dcesrv_connection *dce_conn = conn->private_data; NTSTATUS status; - r = memdup(r, sizeof(struct rpc_server_context)); - - r->events = ev; - r->socket_fd = fd; + DEBUG(10,("dcesrv_tcp_send\n")); - set_socket_options(fd,"SO_KEEPALIVE"); - set_socket_options(fd, lp_socket_options()); - - status = dcesrv_endpoint_connect(&r->dcesrv_context, r->endpoint, &r->dce_conn); - if (!NT_STATUS_IS_OK(status)) { - close(fd); - free(r); - DEBUG(0,("init_rpc_session: connection to endpoint failed: %s\n", - nt_errstr(status))); - return; + status = dcesrv_output(dce_conn, conn->socket->fde, dcerpc_write_fn); + if (NT_STATUS_IS_ERR(status)) { + /* TODO: destroy fd_event? */ } - r->dce_conn->dce_ctx = &r->dcesrv_context; - - set_blocking(fd, False); - - /* setup a event handler for this socket. We are initially - only interested in reading from the socket */ - fde.fd = fd; - fde.handler = dcerpc_io_handler; - fde.private = r; - fde.flags = EVENT_FD_READ; + if (!dce_conn->call_list || !dce_conn->call_list->replies) { + conn->socket->fde->flags &= ~EVENT_FD_WRITE; + } - event_add_fd(ev, &fde); + return; } - -/* - setup a single rpc listener - */ -static void setup_listen_rpc(struct event_context *events, - const struct model_ops *model_ops, - struct in_addr *ifip, uint32_t *port, - struct rpc_server_context *r, - const struct dcesrv_endpoint *endpoint) +void dcesrv_tcp_idle(struct server_connection *conn, time_t t) { - struct fd_event fde; - int i; - - if (*port == 0) { - fde.fd = -1; - for (i=DCERPC_TCP_LOW_PORT;i<= DCERPC_TCP_HIGH_PORT;i++) { - fde.fd = open_socket_in(SOCK_STREAM, i, 0, ifip->s_addr, True); - if (fde.fd != -1) break; - } - if (fde.fd != -1) { - *port = i; - } - } else { - fde.fd = open_socket_in(SOCK_STREAM, *port, 0, ifip->s_addr, True); - } + DEBUG(10,("dcesrv_tcp_idle\n")); + conn->event.idle->next_event = t + 5; - if (fde.fd == -1) { - DEBUG(0,("Failed to open socket on %s:%u - %s\n", - inet_ntoa(*ifip), *port, strerror(errno))); - return; - } - - /* each listening socket has separate state, so must use a different context */ - r = memdup(r, sizeof(struct rpc_server_context)); - if (!r) { - smb_panic("out of memory"); - } - - r->ep_description = malloc(sizeof(struct dcesrv_ep_description)); - if (!r->ep_description) { - smb_panic("out of memory"); - } - r->ep_description->type = ENDPOINT_TCP; - r->ep_description->info.tcp_port = *port; - - r->endpoint = endpoint; - - /* ready to listen */ - set_socket_options(fde.fd, "SO_KEEPALIVE"); - set_socket_options(fde.fd, lp_socket_options()); - - if (listen(fde.fd, SMBD_LISTEN_BACKLOG) == -1) { - DEBUG(0,("Failed to listen on %s:%d - %s\n", - inet_ntoa(*ifip), *port, strerror(errno))); - close(fde.fd); - return; - } - - /* we are only interested in read events on the listen socket */ - fde.flags = EVENT_FD_READ; - fde.private = r; - fde.handler = model_ops->accept_rpc_connection; - - event_add_fd(events, &fde); + return; } -/* - add a socket address to the list of events, one event per dcerpc endpoint -*/ -static void add_socket_rpc(struct event_context *events, - const struct model_ops *model_ops, - struct in_addr *ifip) +void dcesrv_tcp_close(struct server_connection *conn, const char *reason) { - struct dcesrv_endpoint *e; - struct rpc_server_context *r; + struct dcesrv_connection *dce_conn = conn->private_data; - r = malloc(sizeof(struct rpc_server_context)); - if (!r) { - smb_panic("out of memory"); - } + DEBUG(5,("dcesrv_tcp_close: %s\n",reason)); - r->dcesrv_context.endpoint_list = NULL; - dcesrv_init_context(&r->dcesrv_context); - r->ep_description = NULL; - r->model_ops = model_ops; - r->dce_conn = NULL; - r->socket_fd = -1; - r->events = NULL; - - for (e=r->dcesrv_context.endpoint_list;e;e=e->next) { - if (e->ep_description.type == ENDPOINT_TCP) { - setup_listen_rpc(events, model_ops, ifip, - &e->ep_description.info.tcp_port, - r, e); - } - } + close(conn->event.fde->fd); + event_remove_fd_all(conn->event.ctx, conn->socket->fde->fd); + event_remove_timed(conn->event.ctx, conn->event.idle); - free(r); + talloc_destroy(dce_conn->mem_ctx); + + return; } -/**************************************************************************** - Open the listening sockets for RPC over TCP -****************************************************************************/ -void open_sockets_rpc(struct event_context *events, - const struct model_ops *model_ops) +void dcesrv_tcp_exit(struct server_service *service, const char *reason) { - if (lp_interfaces() && lp_bind_interfaces_only()) { - int num_interfaces = iface_count(); - int i; - for(i = 0; i < num_interfaces; i++) { - struct in_addr *ifip = iface_n_ip(i); - if (ifip == NULL) { - continue; - } - add_socket_rpc(events, model_ops, ifip); - } - } else { - TALLOC_CTX *mem_ctx = talloc_init("open_sockets_smbd"); - struct in_addr *ifip = interpret_addr2(mem_ctx, lp_socket_address()); - if (!mem_ctx) { - smb_panic("No memory"); - } - add_socket_rpc(events, model_ops, ifip); - talloc_destroy(mem_ctx); - } + DEBUG(1,("dcesrv_tcp_exit: %s\n",reason)); + return; } diff --git a/source4/smb_server/connection.c b/source4/smb_server/connection.c index 77f2395d9e..4cb4f2168a 100644 --- a/source4/smb_server/connection.c +++ b/source4/smb_server/connection.c @@ -166,7 +166,7 @@ BOOL claim_connection(struct smbsrv_tcon *tcon, const char *name,int max_connect crec.bcast_msg_flags = msg_flags; StrnCpy(crec.machine,sub_get_remote_machine(),sizeof(crec.machine)-1); - StrnCpy(crec.addr,tcon?tcon->smb_conn->socket.client_addr:"NONE",sizeof(crec.addr)-1); + StrnCpy(crec.addr,tcon?tcon->smb_conn->connection->socket->client_addr:"NONE",sizeof(crec.addr)-1); dbuf.dptr = (char *)&crec; dbuf.dsize = sizeof(crec); diff --git a/source4/smb_server/negprot.c b/source4/smb_server/negprot.c index f239e6ae81..c9c775e62f 100644 --- a/source4/smb_server/negprot.c +++ b/source4/smb_server/negprot.c @@ -342,7 +342,7 @@ static void reply_nt1(struct smbsrv_request *req, uint16_t choice) memcpy(req->out.ptr, blob.data, blob.length); DEBUG(3,("using SPNEGO\n")); #else - exit_server(req->smb_conn, "no SPNEGO please"); + smbsrv_terminate_connection(req->smb_conn, "no SPNEGO please"); #endif } @@ -450,7 +450,7 @@ void reply_negprot(struct smbsrv_request *req) int arch = ARCH_ALL; if (req->smb_conn->negotiate.done_negprot) { - exit_server(req->smb_conn, "multiple negprot's are not permitted"); + smbsrv_terminate_connection(req->smb_conn, "multiple negprot's are not permitted"); } req->smb_conn->negotiate.done_negprot = True; diff --git a/source4/smb_server/reply.c b/source4/smb_server/reply.c index 1c3ffd58bd..bc79892204 100644 --- a/source4/smb_server/reply.c +++ b/source4/smb_server/reply.c @@ -2300,7 +2300,7 @@ void reply_special(struct smbsrv_request *req) switch (msg_type) { case 0x81: /* session request */ if (req->smb_conn->negotiate.done_nbt_session) { - exit_server(req->smb_conn, "multiple session request not permitted"); + smbsrv_terminate_connection(req->smb_conn, "multiple session request not permitted"); } SCVAL(buf,0,0x82); diff --git a/source4/smb_server/request.c b/source4/smb_server/request.c index 3bfe6a4e73..f4cdba79cc 100644 --- a/source4/smb_server/request.c +++ b/source4/smb_server/request.c @@ -54,12 +54,12 @@ struct smbsrv_request *init_smb_request(struct smbsrv_connection *smb_conn) structure itself is also allocated inside this context, so we need to allocate it before we construct the request */ - mem_ctx = talloc_init("request_context[%d]", smb_conn->socket.pkt_count); + mem_ctx = talloc_init("request_context[%d]", smb_conn->connection->socket->pkt_count); if (!mem_ctx) { return NULL; } - smb_conn->socket.pkt_count++; + smb_conn->connection->socket->pkt_count++; req = talloc(mem_ctx, sizeof(*req)); if (!req) { @@ -91,7 +91,7 @@ static void req_setup_chain_reply(struct smbsrv_request *req, uint_t wct, uint_t req->out.buffer = talloc_realloc(req->mem_ctx, req->out.buffer, req->out.allocated); if (!req->out.buffer) { - exit_server(req->smb_conn, "allocation failed"); + smbsrv_terminate_connection(req->smb_conn, "allocation failed"); } req->out.hdr = req->out.buffer + NBT_HDR_SIZE; @@ -125,7 +125,7 @@ void req_setup_reply(struct smbsrv_request *req, uint_t wct, uint_t buflen) req->out.buffer = talloc(req->mem_ctx, req->out.allocated); if (!req->out.buffer) { - exit_server(req->smb_conn, "allocation failed"); + smbsrv_terminate_connection(req->smb_conn, "allocation failed"); } req->out.hdr = req->out.buffer + NBT_HDR_SIZE; @@ -262,7 +262,7 @@ void req_send_reply_nosign(struct smbsrv_request *req) _smb_setlen(req->out.buffer, req->out.size - NBT_HDR_SIZE); } - if (write_data(req->smb_conn->socket.fd, req->out.buffer, req->out.size) != req->out.size) { + if (write_data(req->smb_conn->connection->socket->fde->fd, req->out.buffer, req->out.size) != req->out.size) { smb_panic("failed to send reply\n"); } diff --git a/source4/smb_server/service.c b/source4/smb_server/service.c index 44aae340b2..0a06e2edfa 100644 --- a/source4/smb_server/service.c +++ b/source4/smb_server/service.c @@ -259,7 +259,7 @@ close a cnum void close_cnum(struct smbsrv_tcon *tcon) { DEBUG(3,("%s closed connection to service %s\n", - tcon->smb_conn->socket.client_addr, lp_servicename(SNUM(tcon)))); + tcon->smb_conn->connection->socket->client_addr, lp_servicename(SNUM(tcon)))); yield_connection(tcon, lp_servicename(SNUM(tcon))); diff --git a/source4/smb_server/smb_server.c b/source4/smb_server/smb_server.c index 16d1d774c0..351d9ddf4e 100644 --- a/source4/smb_server/smb_server.c +++ b/source4/smb_server/smb_server.c @@ -63,7 +63,7 @@ static struct smbsrv_request *receive_smb_request(struct smbsrv_connection *smb_ char header[4]; struct smbsrv_request *req; - len = read_data(smb_conn->socket.fd, header, 4); + len = read_data(smb_conn->connection->socket->fde->fd, header, 4); if (len != 4) { return NULL; } @@ -81,7 +81,7 @@ static struct smbsrv_request *receive_smb_request(struct smbsrv_connection *smb_ /* fill in the already received header */ memcpy(req->in.buffer, header, 4); - len2 = read_data(smb_conn->socket.fd, req->in.buffer + NBT_HDR_SIZE, len); + len2 = read_data(smb_conn->connection->socket->fde->fd, req->in.buffer + NBT_HDR_SIZE, len); if (len2 != len) { return NULL; } @@ -467,7 +467,7 @@ static void switch_message(int type, struct smbsrv_request *req) if (req->user_ctx) { req->user_ctx->vuid = session_tag; } - DEBUG(3,("switch message %s (task_id %d)\n",smb_fn_name(type), smb_conn->model_ops->get_id(req))); + DEBUG(3,("switch message %s (task_id %d)\n",smb_fn_name(type), smb_conn->connection->service->model_ops->get_id(req))); /* does this protocol need to be run as root? */ if (!(flags & AS_USER)) { @@ -562,19 +562,19 @@ static void construct_reply(struct smbsrv_request *req) if (memcmp(req->in.hdr,"\377SMB",4) != 0) { DEBUG(2,("Non-SMB packet of length %d. Terminating connection\n", req->in.size)); - exit_server(req->smb_conn, "Non-SMB packet"); + smbsrv_terminate_connection(req->smb_conn, "Non-SMB packet"); return; } if (NBT_HDR_SIZE + MIN_SMB_SIZE + 2*req->in.wct > req->in.size) { DEBUG(2,("Invalid SMB word count %d\n", req->in.wct)); - exit_server(req->smb_conn, "Invalid SMB packet"); + smbsrv_terminate_connection(req->smb_conn, "Invalid SMB packet"); return; } if (NBT_HDR_SIZE + MIN_SMB_SIZE + 2*req->in.wct + req->in.data_size > req->in.size) { DEBUG(2,("Invalid SMB buffer length count %d\n", req->in.data_size)); - exit_server(req->smb_conn, "Invalid SMB packet"); + smbsrv_terminate_connection(req->smb_conn, "Invalid SMB packet"); return; } @@ -667,64 +667,26 @@ error: /* close the socket and shutdown a server_context */ -void server_terminate(struct smbsrv_connection *smb_conn) +void smbsrv_terminate_connection(struct smbsrv_connection *smb_conn, const char *reason) { - close(smb_conn->socket.fd); - event_remove_fd_all(smb_conn->events, smb_conn->socket.fd); - - conn_close_all(smb_conn); - - talloc_destroy(smb_conn->mem_ctx); + server_terminate_connection(smb_conn->connection, reason); } /* called on a fatal error that should cause this server to terminate */ -void exit_server(struct smbsrv_connection *smb_conn, const char *reason) -{ - smb_conn->model_ops->terminate_connection(smb_conn, reason); -} - -/* - setup a single listener of any type - */ -static void setup_listen(struct event_context *events, - const struct model_ops *model_ops, - void (*accept_handler)(struct event_context *,struct fd_event *,time_t,uint16_t), - struct in_addr *ifip, uint_t port) +static void smbsrv_exit(struct server_service *service, const char *reason) { - struct fd_event fde; - fde.fd = open_socket_in(SOCK_STREAM, port, 0, ifip->s_addr, True); - if (fde.fd == -1) { - DEBUG(0,("Failed to open socket on %s:%u - %s\n", - inet_ntoa(*ifip), port, strerror(errno))); - return; - } - - /* ready to listen */ - set_socket_options(fde.fd, "SO_KEEPALIVE"); - set_socket_options(fde.fd, lp_socket_options()); - - if (listen(fde.fd, SMBD_LISTEN_BACKLOG) == -1) { - DEBUG(0,("Failed to listen on %s:%d - %s\n", - inet_ntoa(*ifip), port, strerror(errno))); - close(fde.fd); - return; - } - - /* we are only interested in read events on the listen socket */ - fde.flags = EVENT_FD_READ; - fde.private = model_ops; - fde.handler = accept_handler; - - event_add_fd(events, &fde); + DEBUG(1,("smbsrv_exit\n")); + return; } /* add a socket address to the list of events, one event per port */ -static void add_socket(struct event_context *events, - const struct model_ops *model_ops, +static void add_socket(struct server_service *service, + const struct model_ops *model_ops, + struct socket_context *socket_ctx, struct in_addr *ifip) { char *ptr, *tok; @@ -733,18 +695,19 @@ static void add_socket(struct event_context *events, for (tok=strtok_r(lp_smb_ports(), delim, &ptr); tok; tok=strtok_r(NULL, delim, &ptr)) { - uint_t port = atoi(tok); + uint16_t port = atoi(tok); if (port == 0) continue; - setup_listen(events, model_ops, model_ops->accept_connection, ifip, port); + service_setup_socket(service, model_ops, socket_ctx, ifip, &port); } } /**************************************************************************** Open the socket communication. ****************************************************************************/ -void open_sockets_smbd(struct event_context *events, - const struct model_ops *model_ops) -{ +static void smbsrv_init(struct server_service *service, const struct model_ops *model_ops) +{ + DEBUG(1,("smbsrv_init\n")); + if (lp_interfaces() && lp_bind_interfaces_only()) { int num_interfaces = iface_count(); int i; @@ -761,33 +724,37 @@ void open_sockets_smbd(struct event_context *events, continue; } - add_socket(events, model_ops, ifip); + add_socket(service, model_ops, NULL, ifip); } } else { + struct in_addr *ifip; TALLOC_CTX *mem_ctx = talloc_init("open_sockets_smbd"); - - struct in_addr *ifip = interpret_addr2(mem_ctx, lp_socket_address()); - /* Just bind to lp_socket_address() (usually 0.0.0.0) */ + if (!mem_ctx) { smb_panic("No memory"); - } - add_socket(events, model_ops, ifip); + } + + /* Just bind to lp_socket_address() (usually 0.0.0.0) */ + ifip = interpret_addr2(mem_ctx, lp_socket_address()); + add_socket(service, model_ops, NULL, ifip); + talloc_destroy(mem_ctx); - } + } } /* called when a SMB socket becomes readable */ -void smbd_read_handler(struct event_context *ev, struct fd_event *fde, - time_t t, uint16_t flags) +static void smbsrv_recv(struct server_connection *conn, time_t t, uint16_t flags) { struct smbsrv_request *req; - struct smbsrv_connection *smb_conn = fde->private; - + struct smbsrv_connection *smb_conn = conn->private_data; + + DEBUG(10,("smbsrv_recv\n")); + req = receive_smb_request(smb_conn); if (!req) { - smb_conn->model_ops->terminate_connection(smb_conn, "receive error"); + smbsrv_terminate_connection(smb_conn, "receive error"); return; } @@ -795,8 +762,44 @@ void smbd_read_handler(struct event_context *ev, struct fd_event *fde, /* free up temporary memory */ lp_talloc_free(); + return; +} + +/* + called when a SMB socket becomes writable +*/ +static void smbsrv_send(struct server_connection *conn, time_t t, uint16_t flags) +{ + DEBUG(10,("smbsrv_send\n")); + return; +} + +/* + called when connection is idle +*/ +static void smbsrv_idle(struct server_connection *conn, time_t t) +{ + DEBUG(10,("smbsrv_idle: not implemented!\n")); + conn->event.idle->next_event = t + 5; + + return; } +static void smbsrv_close(struct server_connection *conn, const char *reason) +{ + struct smbsrv_connection *smb_conn = conn->private_data; + + DEBUG(5,("smbsrv_close: %s\n",reason)); + + close(conn->event.fde->fd); + event_remove_fd_all(conn->event.ctx, conn->socket->fde->fd); + event_remove_timed(conn->event.ctx, conn->event.idle); + + conn_close_all(smb_conn); + + talloc_destroy(smb_conn->mem_ctx); + return; +} /* process a message from an SMB socket while still processing a @@ -810,7 +813,7 @@ void smbd_process_async(struct smbsrv_connection *smb_conn) req = receive_smb_request(smb_conn); if (!req) { - smb_conn->model_ops->terminate_connection(smb_conn, "receive error"); + smbsrv_terminate_connection(smb_conn, "receive error"); return; } @@ -822,18 +825,15 @@ void smbd_process_async(struct smbsrv_connection *smb_conn) initialise a server_context from a open socket and register a event handler for reading from that socket */ -void init_smbsession(struct event_context *ev, struct model_ops *model_ops, int fd, - void (*read_handler)(struct event_context *, struct fd_event *, time_t, uint16_t)) +void smbsrv_accept(struct server_connection *conn) { struct smbsrv_connection *smb_conn; TALLOC_CTX *mem_ctx; - struct fd_event fde; char *socket_addr; - set_socket_options(fd,"SO_KEEPALIVE"); - set_socket_options(fd, lp_socket_options()); + DEBUG(5,("smbsrv_accept\n")); - mem_ctx = talloc_init("server_context"); + mem_ctx = talloc_init("smbsrv_context"); smb_conn = talloc_p(mem_ctx, struct smbsrv_connection); if (!smb_conn) return; @@ -841,16 +841,14 @@ void init_smbsession(struct event_context *ev, struct model_ops *model_ops, int ZERO_STRUCTP(smb_conn); smb_conn->mem_ctx = mem_ctx; - smb_conn->socket.fd = fd; smb_conn->pid = getpid(); sub_set_context(&smb_conn->substitute); /* set an initial client name based on its IP address. This will be replaced with the netbios name later if it gives us one */ - socket_addr = get_socket_addr(smb_conn->mem_ctx, fd); + socket_addr = get_socket_addr(smb_conn->mem_ctx, conn->socket->fde->fd); sub_set_remote_machine(socket_addr); - smb_conn->socket.client_addr = socket_addr; /* now initialise a few default values associated with this smb socket */ smb_conn->negotiate.max_send = 0xFFFF; @@ -862,21 +860,36 @@ void init_smbsession(struct event_context *ev, struct model_ops *model_ops, int smb_conn->negotiate.zone_offset = get_time_zone(time(NULL)); smb_conn->users.next_vuid = VUID_OFFSET; - - smb_conn->events = ev; - smb_conn->model_ops = model_ops; conn_init(smb_conn); - /* setup a event handler for this socket. We are initially - only interested in reading from the socket */ - fde.fd = fd; - fde.handler = read_handler; - fde.private = smb_conn; - fde.flags = EVENT_FD_READ; + smb_conn->connection = conn; - event_add_fd(ev, &fde); + conn->private_data = smb_conn; /* setup the DCERPC server subsystem */ dcesrv_init_context(&smb_conn->dcesrv); + + return; +} + +static const struct server_service_ops smb_server_ops = { + .name = "smb", + .service_init = smbsrv_init, + .accept_connection = smbsrv_accept, + .recv_handler = smbsrv_recv, + .send_handler = smbsrv_send, + .idle_handler = smbsrv_idle, + .close_connection = smbsrv_close, + .service_exit = smbsrv_exit, +}; + +const struct server_service_ops *smbsrv_get_ops(void) +{ + return &smb_server_ops; +} + +NTSTATUS server_service_smb_init(void) +{ + return NT_STATUS_OK; } diff --git a/source4/smb_server/smb_server.h b/source4/smb_server/smb_server.h index 6dc40aee08..e7b0ecfb02 100644 --- a/source4/smb_server/smb_server.h +++ b/source4/smb_server/smb_server.h @@ -342,10 +342,8 @@ struct smbsrv_connection { struct dcesrv_context dcesrv; - const struct model_ops *model_ops; - - struct event_context *events; - /* the pid of the process handling this session */ pid_t pid; + + struct server_connection *connection; }; diff --git a/source4/smbd/config.m4 b/source4/smbd/config.m4 index dd7a707714..52ccb68aa8 100644 --- a/source4/smbd/config.m4 +++ b/source4/smbd/config.m4 @@ -1,9 +1,10 @@ dnl # server subsystem -SMB_MODULE_MK(server_smb,SERVER,STATIC,smbd/config.mk) -SMB_MODULE_MK(server_rpc,SERVER,STATIC,smbd/config.mk) -SMB_MODULE_MK(server_auth,SERVER,STATIC,smbd/config.mk) +SMB_MODULE_MK(server_service_auth,SERVER_SERVICE,STATIC,smbd/config.mk) +SMB_MODULE_MK(server_service_smb,SERVER_SERVICE,STATIC,smbd/config.mk) +SMB_MODULE_MK(server_service_rpc,SERVER_SERVICE,STATIC,smbd/config.mk) +SMB_SUBSYSTEM_MK(SERVER_SERVICE,smbd/config.mk) SMB_SUBSYSTEM_MK(SERVER,smbd/config.mk) SMB_BINARY_MK(smbd, smbd/config.mk) diff --git a/source4/smbd/config.mk b/source4/smbd/config.mk index cd0d80150b..f002341a26 100644 --- a/source4/smbd/config.mk +++ b/source4/smbd/config.mk @@ -1,39 +1,47 @@ # server subsystem ################################################ -# Start MODULE server_auth -[MODULE::server_auth] +# Start MODULE server_service_auth +[MODULE::server_service_auth] REQUIRED_SUBSYSTEMS = \ AUTH # End MODULE server_auth ################################################ ################################################ -# Start MODULE server_smb -[MODULE::server_smb] +# Start MODULE server_service_smb +[MODULE::server_service_smb] REQUIRED_SUBSYSTEMS = \ SMB # End MODULE server_smb ################################################ ################################################ -# Start MODULE server_rpc -[MODULE::server_rpc] +# Start MODULE server_service_rpc +[MODULE::server_service_rpc] REQUIRED_SUBSYSTEMS = \ DCERPC # End MODULE server_rpc ################################################ +####################### +# Start SUBSYSTEM SERVICE +[SUBSYSTEM::SERVER_SERVICE] +INIT_OBJ_FILES = \ + smbd/service.o +# End SUBSYSTEM SERVER +####################### + ####################### # Start SUBSYSTEM SERVER [SUBSYSTEM::SERVER] INIT_OBJ_FILES = \ smbd/server.o ADD_OBJ_FILES = \ - smbd/build_options.o \ smbd/rewrite.o REQUIRED_SUBSYSTEMS = \ - PROCESS_MODEL + PROCESS_MODEL \ + SERVER_SERVICE # End SUBSYSTEM SERVER ####################### diff --git a/source4/smbd/process_model.c b/source4/smbd/process_model.c index f981b36798..0bdb316317 100644 --- a/source4/smbd/process_model.c +++ b/source4/smbd/process_model.c @@ -24,8 +24,7 @@ /* setup the events for the chosen process model */ -void process_model_startup(struct event_context *events, - const char *model) +const struct model_ops *process_model_startup(const char *model) { const struct model_ops *ops; @@ -37,12 +36,7 @@ void process_model_startup(struct event_context *events, ops->model_startup(); - /* now setup the listening sockets, adding - event handlers to the events structure */ - open_sockets_smbd(events, ops); - - /* setup any sockets we need to listen on for RPC over TCP */ - open_sockets_rpc(events, ops); + return ops; } /* the list of currently registered process models */ diff --git a/source4/smbd/process_model.h b/source4/smbd/process_model.h index c4c3aa68df..376b9a8ef8 100644 --- a/source4/smbd/process_model.h +++ b/source4/smbd/process_model.h @@ -3,6 +3,7 @@ process model manager - main loop Copyright (C) Andrew Tridgell 1992-2003 Copyright (C) James J Myers 2003 + Copyright (C) Stefan (metze) Metzmacher 2004 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 @@ -22,6 +23,8 @@ #ifndef SAMBA_PROCESS_MODEL_H #define SAMBA_PROCESS_MODEL_H +struct server_service_connection; + /* modules can use the following to determine if the interface has changed * please increment the version number after each interface change * with a comment and maybe update struct process_model_critical_sizes. @@ -40,19 +43,13 @@ struct model_ops { /* function to accept new connection */ void (*accept_connection)(struct event_context *, struct fd_event *, time_t, uint16_t); - - /* function to accept new rpc over tcp connection */ - void (*accept_rpc_connection)(struct event_context *, struct fd_event *, time_t, uint16_t); - + /* function to terminate a connection */ - void (*terminate_connection)(struct smbsrv_connection *smb, const char *reason); + void (*terminate_connection)(struct server_connection *srv_conn, const char *reason); - /* function to terminate a connection */ - void (*terminate_rpc_connection)(void *r, const char *reason); - /* function to exit server */ - void (*exit_server)(struct smbsrv_connection *smb, const char *reason); - + void (*exit_server)(struct server_context *srv_ctx, const char *reason); + /* returns process or thread id */ int (*get_id)(struct smbsrv_request *req); }; diff --git a/source4/smbd/process_single.c b/source4/smbd/process_single.c index 9c92d8569a..b65418cd01 100644 --- a/source4/smbd/process_single.c +++ b/source4/smbd/process_single.c @@ -3,6 +3,7 @@ process model: process (1 process handles all client connections) Copyright (C) Andrew Tridgell 2003 Copyright (C) James J Myers 2003 + Copyright (C) Stefan (metze) Metzmacher 2004 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 @@ -21,10 +22,11 @@ #include "includes.h" + /* called when the process model is selected */ -static void model_startup(void) +static void single_start_server(void) { smbd_process_init(); } @@ -32,67 +34,102 @@ static void model_startup(void) /* called when a listening socket becomes readable */ -static void accept_connection(struct event_context *ev, struct fd_event *fde, time_t t, uint16_t flags) +static void single_accept_connection(struct event_context *ev, struct fd_event *srv_fde, time_t t, uint16_t flags) { int accepted_fd; struct sockaddr addr; socklen_t in_addrlen = sizeof(addr); - struct model_ops *model_ops = fde->private; - + struct fd_event fde; + struct timed_event idle; + struct server_socket *server_socket = srv_fde->private; + struct server_connection *conn; + TALLOC_CTX *mem_ctx; + /* accept an incoming connection. */ - accepted_fd = accept(fde->fd,&addr,&in_addrlen); + accepted_fd = accept(srv_fde->fd,&addr,&in_addrlen); if (accepted_fd == -1) { DEBUG(0,("accept_connection_single: accept: %s\n", strerror(errno))); return; } - /* create a smb server context and add it to out event - handling */ - init_smbsession(ev, model_ops, accepted_fd, smbd_read_handler); + mem_ctx = talloc_init("server_service_connection"); + if (!mem_ctx) { + DEBUG(0,("talloc_init(server_service_connection) failed\n")); + return; + } - /* return to event handling */ -} + conn = talloc_p(mem_ctx, struct server_connection); + if (!conn) { + DEBUG(0,("talloc_p(mem_ctx, struct server_service_connection) failed\n")); + talloc_destroy(mem_ctx); + return; + } + ZERO_STRUCTP(conn); + conn->mem_ctx = mem_ctx; -/* - called when a rpc listening socket becomes readable -*/ -static void accept_rpc_connection(struct event_context *ev, struct fd_event *fde, time_t t, uint16_t flags) -{ - int accepted_fd; - struct sockaddr addr; - socklen_t in_addrlen = sizeof(addr); - - /* accept an incoming connection. */ - accepted_fd = accept(fde->fd,&addr,&in_addrlen); - if (accepted_fd == -1) { - DEBUG(0,("accept_connection_single: accept: %s\n", - strerror(errno))); + fde.private = conn; + fde.fd = accepted_fd; + fde.flags = EVENT_FD_READ; + fde.handler = server_io_handler; + + idle.private = conn; + idle.next_event = t + 300; + idle.handler = server_idle_handler; + + conn->event.ctx = ev; + conn->event.fde = &fde; + conn->event.idle = &idle; + conn->event.idle_time = 300; + + conn->server_socket = server_socket; + conn->service = server_socket->service; + + /* TODO: we need a generic socket subsystem */ + conn->socket = talloc_p(conn->mem_ctx, struct socket_context); + if (!conn->socket) { + DEBUG(0,("talloc_p(conn->mem_ctx, struct socket_context) failed\n")); + talloc_destroy(mem_ctx); return; } + conn->socket->private_data = NULL; + conn->socket->ops = NULL; + conn->socket->client_addr = NULL; + conn->socket->pkt_count = 0; + conn->socket->fde = conn->event.fde; - init_rpc_session(ev, fde->private, accepted_fd); -} + /* create a smb server context and add it to out event + handling */ + server_socket->service->ops->accept_connection(conn); -/* called when a SMB connection goes down */ -static void terminate_connection(struct smbsrv_connection *server, const char *reason) -{ - server_terminate(server); + /* accpect_connection() of the service may changed idle.next_event */ + conn->event.fde = event_add_fd(ev,&fde); + conn->event.idle = event_add_timed(ev,&idle); + + conn->socket->fde = conn->event.fde; + + DLIST_ADD(server_socket->connection_list,conn); + + /* return to event handling */ + return; } -/* called when a rpc connection goes down */ -static void terminate_rpc_connection(void *r, const char *reason) + + +/* called when a SMB connection goes down */ +static void single_terminate_connection(struct server_connection *conn, const char *reason) { - rpc_server_terminate(r); + DEBUG(0,("single_terminate_connection: reason[%s]\n",reason)); + conn->service->ops->close_connection(conn,reason); } -static int get_id(struct smbsrv_request *req) +static int single_get_id(struct smbsrv_request *req) { return (int)req->smb_conn->pid; } -static void single_exit_server(struct smbsrv_connection *smb, const char *reason) +static void single_exit_server(struct server_context *srv_ctx, const char *reason) { DEBUG(1,("single_exit_server: reason[%s]\n",reason)); } @@ -111,13 +148,11 @@ NTSTATUS process_model_single_init(void) ops.name = "single"; /* fill in all the operations */ - ops.model_startup = model_startup; - ops.accept_connection = accept_connection; - ops.accept_rpc_connection = accept_rpc_connection; - ops.terminate_connection = terminate_connection; - ops.terminate_rpc_connection = terminate_rpc_connection; - ops.exit_server = single_exit_server; - ops.get_id = get_id; + ops.model_startup = single_start_server; + ops.accept_connection = single_accept_connection; + ops.terminate_connection = single_terminate_connection; + ops.exit_server = single_exit_server; + ops.get_id = single_get_id; /* register ourselves with the PROCESS_MODEL subsystem. */ ret = register_backend("process_model", &ops); diff --git a/source4/smbd/process_standard.c b/source4/smbd/process_standard.c index 2ee486e1d2..cc02e84d57 100644 --- a/source4/smbd/process_standard.c +++ b/source4/smbd/process_standard.c @@ -3,6 +3,7 @@ process model: standard (1 process per client connection) Copyright (C) Andrew Tridgell 1992-2003 Copyright (C) James J Myers 2003 + Copyright (C) Stefan (metze) Metzmacher 2004 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 @@ -24,25 +25,31 @@ /* called when the process model is selected */ -static void model_startup(void) +static void standard_model_startup(void) { signal(SIGCHLD, SIG_IGN); + smbd_process_init(); } /* called when a listening socket becomes readable */ -static void accept_connection(struct event_context *ev, struct fd_event *fde, time_t t, uint16_t flags) +static void standard_accept_connection(struct event_context *ev, struct fd_event *srv_fde, time_t t, uint16_t flags) { int accepted_fd; struct sockaddr addr; socklen_t in_addrlen = sizeof(addr); pid_t pid; - struct model_ops *model_ops = fde->private; - - accepted_fd = accept(fde->fd,&addr,&in_addrlen); + struct fd_event fde; + struct timed_event idle; + struct server_socket *server_socket = srv_fde->private; + struct server_connection *conn; + TALLOC_CTX *mem_ctx; + + /* accept an incoming connection. */ + accepted_fd = accept(srv_fde->fd,&addr,&in_addrlen); if (accepted_fd == -1) { - DEBUG(0,("accept_connection_standard: accept: %s\n", + DEBUG(0,("standard_accept_connection: accept: %s\n", strerror(errno))); return; } @@ -60,82 +67,91 @@ static void accept_connection(struct event_context *ev, struct fd_event *fde, ti /* Child code ... */ /* close all the listening sockets */ - event_remove_fd_all_handler(ev, model_ops->accept_connection); - event_remove_fd_all_handler(ev, model_ops->accept_rpc_connection); + event_remove_fd_all_handler(ev, standard_accept_connection); /* tdb needs special fork handling */ if (tdb_reopen_all() == -1) { - DEBUG(0,("accept_connection_standard: tdb_reopen_all failed.\n")); + DEBUG(0,("standard_accept_connection: tdb_reopen_all failed.\n")); } - /* Load DSO's */ - init_modules(); - - /* initialize new process */ - smbd_process_init(); - - init_smbsession(ev, model_ops, accepted_fd, smbd_read_handler); - - /* return to the event loop */ -} - -/* - called when a rpc listening socket becomes readable -*/ -static void accept_rpc_connection(struct event_context *ev, struct fd_event *fde, time_t t, uint16_t flags) -{ - int accepted_fd; - struct sockaddr addr; - socklen_t in_addrlen = sizeof(addr); - pid_t pid; + mem_ctx = talloc_init("server_service_connection"); + if (!mem_ctx) { + DEBUG(0,("talloc_init(server_service_connection) failed\n")); + return; + } - accepted_fd = accept(fde->fd,&addr,&in_addrlen); - if (accepted_fd == -1) { - DEBUG(0,("accept_connection_standard: accept: %s\n", - strerror(errno))); + conn = talloc_p(mem_ctx, struct server_connection); + if (!conn) { + DEBUG(0,("talloc_p(mem_ctx, struct server_service_connection) failed\n")); + talloc_destroy(mem_ctx); return; } - pid = fork(); + ZERO_STRUCTP(conn); + conn->mem_ctx = mem_ctx; - if (pid != 0) { - /* parent or error code ... */ - close(accepted_fd); - /* go back to the event loop */ + fde.private = conn; + fde.fd = accepted_fd; + fde.flags = EVENT_FD_READ; + fde.handler = server_io_handler; + + idle.private = conn; + idle.next_event = t + 300; + idle.handler = server_idle_handler; + + + conn->event.ctx = ev; + conn->event.fde = &fde; + conn->event.idle = &idle; + conn->event.idle_time = 300; + + conn->server_socket = server_socket; + conn->service = server_socket->service; + + /* TODO: we need a generic socket subsystem */ + conn->socket = talloc_p(conn->mem_ctx, struct socket_context); + if (!conn->socket) { + DEBUG(0,("talloc_p(conn->mem_ctx, struct socket_context) failed\n")); + talloc_destroy(mem_ctx); return; } + conn->socket->private_data = NULL; + conn->socket->ops = NULL; + conn->socket->client_addr = NULL; + conn->socket->pkt_count = 0; + conn->socket->fde = conn->event.fde; - /* Child code ... */ + /* create a smb server context and add it to out event + handling */ + server_socket->service->ops->accept_connection(conn); - /* close all the listening sockets */ - event_remove_fd_all_handler(ev, accept_connection); - event_remove_fd_all_handler(ev, accept_rpc_connection); - - init_rpc_session(ev, fde->private, accepted_fd); -} + /* accpect_connection() of the service may changed idle.next_event */ + conn->event.fde = event_add_fd(ev,&fde); + conn->event.idle = event_add_timed(ev,&idle); -/* called when a SMB connection goes down */ -static void terminate_connection(struct smbsrv_connection *server, const char *reason) -{ - server_terminate(server); - /* terminate this process */ - exit(0); + conn->socket->fde = conn->event.fde; + + DLIST_ADD(server_socket->connection_list,conn); + + /* return to the event loop */ } -/* called when a rpc connection goes down */ -static void terminate_rpc_connection(void *r, const char *reason) + +/* called when a SMB connection goes down */ +static void standard_terminate_connection(struct server_connection *conn, const char *reason) { - rpc_server_terminate(r); + DEBUG(0,("single_terminate_connection: reason[%s]\n",reason)); + conn->service->ops->close_connection(conn,reason); /* terminate this process */ exit(0); } -static int get_id(struct smbsrv_request *req) +static int standard_get_id(struct smbsrv_request *req) { return (int)req->smb_conn->pid; } -static void standard_exit_server(struct smbsrv_connection *smb, const char *reason) +static void standard_exit_server(struct server_context *srv_ctx, const char *reason) { DEBUG(1,("standard_exit_server: reason[%s]\n",reason)); } @@ -154,13 +170,11 @@ NTSTATUS process_model_standard_init(void) ops.name = "standard"; /* fill in all the operations */ - ops.model_startup = model_startup; - ops.accept_connection = accept_connection; - ops.accept_rpc_connection = accept_rpc_connection; - ops.terminate_connection = terminate_connection; - ops.terminate_rpc_connection = terminate_rpc_connection; + ops.model_startup = standard_model_startup; + ops.accept_connection = standard_accept_connection; + ops.terminate_connection = standard_terminate_connection; ops.exit_server = standard_exit_server; - ops.get_id = get_id; + ops.get_id = standard_get_id; /* register ourselves with the PROCESS_MODEL subsystem. */ ret = register_backend("process_model", &ops); diff --git a/source4/smbd/process_thread.c b/source4/smbd/process_thread.c index f79f34e389..553e67feeb 100644 --- a/source4/smbd/process_thread.c +++ b/source4/smbd/process_thread.c @@ -3,6 +3,7 @@ thread model: standard (1 thread per client connection) Copyright (C) Andrew Tridgell 2003 Copyright (C) James J Myers 2003 + Copyright (C) Stefan (metze) Metzmacher 2004 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 @@ -25,19 +26,19 @@ #include "execinfo.h" #endif -static void *connection_thread(void *thread_parm) +static void *thread_connection_fn(void *thread_parm) { struct event_context *ev = thread_parm; /* wait for action */ event_loop_wait(ev); - + #if 0 pthread_cleanup_pop(1); /* will invoke terminate_mt_connection() */ #endif return NULL; } -static int get_id(struct smbsrv_request *req) +static int thread_get_id(struct smbsrv_request *req) { return (int)pthread_self(); } @@ -45,21 +46,24 @@ static int get_id(struct smbsrv_request *req) /* called when a listening socket becomes readable */ -static void accept_connection(struct event_context *ev, struct fd_event *fde, +static void thread_accept_connection(struct event_context *ev, struct fd_event *srv_fde, time_t t, uint16_t flags) -{ +{ int accepted_fd, rc; struct sockaddr addr; socklen_t in_addrlen = sizeof(addr); pthread_t thread_id; pthread_attr_t thread_attr; - struct model_ops *model_ops = fde->private; - - /* accept an incoming connection */ - accepted_fd = accept(fde->fd,&addr,&in_addrlen); - + struct fd_event fde; + struct timed_event idle; + struct server_socket *server_socket = srv_fde->private; + struct server_connection *conn; + TALLOC_CTX *mem_ctx; + + /* accept an incoming connection. */ + accepted_fd = accept(srv_fde->fd,&addr,&in_addrlen); if (accepted_fd == -1) { - DEBUG(0,("accept_connection_thread: accept: %s\n", + DEBUG(0,("standard_accept_connection: accept: %s\n", strerror(errno))); return; } @@ -70,52 +74,80 @@ static void accept_connection(struct event_context *ev, struct fd_event *fde, with the main event loop, then return. When we return the main event_context is continued. */ + + ev = event_context_init(); - MUTEX_LOCK_BY_ID(MUTEX_SMBD); - init_smbsession(ev, model_ops, accepted_fd, smbd_read_handler); - MUTEX_UNLOCK_BY_ID(MUTEX_SMBD); - - pthread_attr_init(&thread_attr); - pthread_attr_setdetachstate(&thread_attr, PTHREAD_CREATE_DETACHED); - rc = pthread_create(&thread_id, &thread_attr, &connection_thread, ev); - pthread_attr_destroy(&thread_attr); - if (rc == 0) { - DEBUG(4,("accept_connection_thread: created thread_id=%lu for fd=%d\n", - (unsigned long int)thread_id, accepted_fd)); - } else { - DEBUG(0,("accept_connection_thread: thread create failed for fd=%d, rc=%d\n", accepted_fd, rc)); + if (!ev) { + DEBUG(0,("thread_accept_connection: failed to create event_context!\n")); + return; } -} + mem_ctx = talloc_init("server_service_connection"); + if (!mem_ctx) { + DEBUG(0,("talloc_init(server_service_connection) failed\n")); + return; + } -/* - called when a rpc listening socket becomes readable -*/ -static void accept_rpc_connection(struct event_context *ev, struct fd_event *fde, time_t t, uint16_t flags) -{ - int accepted_fd, rc; - struct sockaddr addr; - socklen_t in_addrlen = sizeof(addr); - pthread_t thread_id; - pthread_attr_t thread_attr; - - /* accept an incoming connection */ - accepted_fd = accept(fde->fd,&addr,&in_addrlen); - - if (accepted_fd == -1) { - DEBUG(0,("accept_connection_thread: accept: %s\n", - strerror(errno))); + conn = talloc_p(mem_ctx, struct server_connection); + if (!conn) { + DEBUG(0,("talloc_p(mem_ctx, struct server_service_connection) failed\n")); + talloc_destroy(mem_ctx); return; } - - ev = event_context_init(); + + ZERO_STRUCTP(conn); + conn->mem_ctx = mem_ctx; + + fde.private = conn; + fde.fd = accepted_fd; + fde.flags = EVENT_FD_READ; + fde.handler = server_io_handler; + + idle.private = conn; + idle.next_event = t + 300; + idle.handler = server_idle_handler; + + conn->event.ctx = ev; + conn->event.fde = &fde; + conn->event.idle = &idle; + conn->event.idle_time = 300; + + conn->server_socket = server_socket; + conn->service = server_socket->service; + + /* TODO: we need a generic socket subsystem */ + conn->socket = talloc_p(conn->mem_ctx, struct socket_context); + if (!conn->socket) { + DEBUG(0,("talloc_p(conn->mem_ctx, struct socket_context) failed\n")); + talloc_destroy(mem_ctx); + return; + } + conn->socket->private_data = NULL; + conn->socket->ops = NULL; + conn->socket->client_addr = NULL; + conn->socket->pkt_count = 0; + conn->socket->fde = conn->event.fde; + + /* create a smb server context and add it to out event + handling */ + server_socket->service->ops->accept_connection(conn); + + /* accpect_connection() of the service may changed idle.next_event */ + conn->event.fde = event_add_fd(ev,&fde); + conn->event.idle = event_add_timed(ev,&idle); + + conn->socket->fde = conn->event.fde; + + /* TODO: is this MUTEX_LOCK in the right place here? + * --metze + */ MUTEX_LOCK_BY_ID(MUTEX_SMBD); - init_rpc_session(ev, fde->private, accepted_fd); + DLIST_ADD(server_socket->connection_list,conn); MUTEX_UNLOCK_BY_ID(MUTEX_SMBD); pthread_attr_init(&thread_attr); pthread_attr_setdetachstate(&thread_attr, PTHREAD_CREATE_DETACHED); - rc = pthread_create(&thread_id, &thread_attr, &connection_thread, ev); + rc = pthread_create(&thread_id, &thread_attr, thread_connection_fn, ev); pthread_attr_destroy(&thread_attr); if (rc == 0) { DEBUG(4,("accept_connection_thread: created thread_id=%lu for fd=%d\n", @@ -126,19 +158,10 @@ static void accept_rpc_connection(struct event_context *ev, struct fd_event *fde } /* called when a SMB connection goes down */ -static void terminate_connection(struct smbsrv_connection *server, const char *reason) +static void thread_terminate_connection(struct server_connection *conn, const char *reason) { - server_terminate(server); - - /* terminate this thread */ - pthread_exit(NULL); /* thread cleanup routine will do actual cleanup */ -} - -/* called when a rpc connection goes down */ -static void terminate_rpc_connection(void *r, const char *reason) -{ - rpc_server_terminate(r); - + DEBUG(0,("thread_terminate_connection: reason[%s]\n",reason)); + conn->service->ops->close_connection(conn,reason); /* terminate this thread */ pthread_exit(NULL); /* thread cleanup routine will do actual cleanup */ } @@ -431,7 +454,7 @@ static void thread_fault_handler(int sig) /* called when the process model is selected */ -static void model_startup(void) +static void thread_model_startup(void) { struct mutex_ops m_ops; struct debug_ops d_ops; @@ -465,7 +488,7 @@ static void model_startup(void) register_debug_handlers("thread", &d_ops); } -static void thread_exit_server(struct smbsrv_connection *smb, const char *reason) +static void thread_exit_server(struct server_context *srv_ctx, const char *reason) { DEBUG(1,("thread_exit_server: reason[%s]\n",reason)); } @@ -484,13 +507,11 @@ NTSTATUS process_model_thread_init(void) ops.name = "thread"; /* fill in all the operations */ - ops.model_startup = model_startup; - ops.accept_connection = accept_connection; - ops.accept_rpc_connection = accept_rpc_connection; - ops.terminate_connection = terminate_connection; - ops.terminate_rpc_connection = terminate_rpc_connection; + ops.model_startup = thread_model_startup; + ops.accept_connection = thread_accept_connection; + ops.terminate_connection = thread_terminate_connection; ops.exit_server = thread_exit_server; - ops.get_id = get_id; + ops.get_id = thread_get_id; /* register ourselves with the PROCESS_MODEL subsystem. */ ret = register_backend("process_model", &ops); diff --git a/source4/smbd/rewrite.c b/source4/smbd/rewrite.c index 5bc826bf71..d0a4bad374 100644 --- a/source4/smbd/rewrite.c +++ b/source4/smbd/rewrite.c @@ -79,6 +79,10 @@ void init_subsystems(void) if (!process_model_init()) exit(1); + /* Setup the SERVER_SERVICE subsystem */ + if (!server_service_init()) + exit(1); + /* Setup the AUTH subsystem */ if (!auth_init()) exit(1); @@ -88,7 +92,7 @@ void init_subsystems(void) exit(1); /* Setup the DCERPC subsystem */ - if (!dcesrv_init()) + if (!subsystem_dcerpc_init()) exit(1); } diff --git a/source4/smbd/server.c b/source4/smbd/server.c index 3a579b846a..e748795177 100644 --- a/source4/smbd/server.c +++ b/source4/smbd/server.c @@ -23,10 +23,16 @@ #include "includes.h" +static void exit_server(const char *reason) +{ + DEBUG(3,("Server exit (%s)\n", (reason ? reason : ""))); + exit(0); +} + /**************************************************************************** - main program. + main server. ****************************************************************************/ - int main(int argc,const char *argv[]) +static int binary_smbd_main(int argc,const char *argv[]) { BOOL is_daemon = False; BOOL interactive = False; @@ -34,7 +40,7 @@ BOOL log_stdout = False; int opt; poptContext pc; - struct event_context *events; + struct server_context *srv_ctx; const char *model = "standard"; struct poptOption long_options[] = { POPT_AUTOHELP @@ -42,7 +48,6 @@ {"interactive", 'i', POPT_ARG_VAL, &interactive, True, "Run interactive (not a daemon)"}, {"foreground", 'F', POPT_ARG_VAL, &Fork, False, "Run daemon in foreground (for daemontools & etc)" }, {"log-stdout", 'S', POPT_ARG_VAL, &log_stdout, True, "Log to stdout" }, - {"build-options", 'b', POPT_ARG_NONE, NULL, 'b', "Print build options" }, {"port", 'p', POPT_ARG_STRING, NULL, 0, "Listen on the specified ports"}, {"model", 'M', POPT_ARG_STRING, &model, 0, "select process model"}, POPT_COMMON_SAMBA @@ -53,11 +58,6 @@ while((opt = poptGetNextOpt(pc)) != -1) { switch (opt) { - case 'b': - /* Display output to screen as well as debug */ - build_options(True); - exit(0); - break; case 'p': lp_set_cmdline("smb ports", poptGetOptArg(pc)); break; @@ -65,8 +65,6 @@ } poptFreeContext(pc); - events = event_context_init(); - load_case_tables(); if (interactive) { @@ -110,15 +108,11 @@ DEBUG(0,("smbd version %s started.\n", SAMBA_VERSION_STRING)); DEBUGADD(0,("Copyright Andrew Tridgell and the Samba Team 1992-2004\n")); - /* Output the build options to the debug log */ - build_options(False); - - if (sizeof(uint16_t) < 2 || sizeof(uint32_t) < 4) { + if (sizeof(uint16_t) < 2 || sizeof(uint32_t) < 4 || sizeof(uint64_t) < 8) { DEBUG(0,("ERROR: Samba is not configured correctly for the word size on your machine\n")); exit(1); } - DEBUG(0,("Using %s process model\n", model)); - + if (!reload_services(NULL, False)) return(-1); @@ -149,8 +143,18 @@ init_subsystems(); - process_model_startup(events, model); + DEBUG(0,("Using %s process model\n", model)); + srv_ctx = server_service_startup(model); + if (!srv_ctx) { + DEBUG(0,("Starting Services failed.\n")); + return 1; + } /* wait for events */ - return event_loop_wait(events); + return event_loop_wait(srv_ctx->events); +} + + int main(int argc, const char *argv[]) +{ + return binary_smbd_main(argc, argv); } diff --git a/source4/smbd/server.h b/source4/smbd/server.h new file mode 100644 index 0000000000..c47cf02d8a --- /dev/null +++ b/source4/smbd/server.h @@ -0,0 +1,40 @@ +/* + Unix SMB/CIFS implementation. + + Copyright (C) Stefan (metze) Metzmacher 2004 + + 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. +*/ + +#ifndef _SERVER_H +#define _SERVER_H + +struct server_service; +struct event_context; + +struct server_context { + TALLOC_CTX *mem_ctx; + struct server_service *service_list; + struct event_context *events; +}; + +/* size of listen() backlog in smbd */ +#define SERVER_LISTEN_BACKLOG 10 + +/* the range of ports to try for dcerpc over tcp endpoints */ +#define SERVER_TCP_LOW_PORT 1024 +#define SERVER_TCP_HIGH_PORT 1300 + +#endif /* _SERVER_H */ diff --git a/source4/smbd/service.c b/source4/smbd/service.c new file mode 100644 index 0000000000..2b6e0579fa --- /dev/null +++ b/source4/smbd/service.c @@ -0,0 +1,241 @@ +/* + Unix SMB/CIFS implementation. + + SERVER SERVICE code + + Copyright (C) Andrew Tridgell 2003 + Copyright (C) Stefan (metze) Metzmacher 2004 + + 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" + +struct server_context *server_service_startup(const char *model) +{ + int i; + const char **server_services = lp_server_services(); + TALLOC_CTX *mem_ctx; + struct server_context *srv_ctx; + const struct model_ops *model_ops; + + if (!server_services) { + DEBUG(0,("process_model_startup: no endpoint servers configured\n")); + return NULL; + } + + model_ops = process_model_startup(model); + if (!model_ops) { + DEBUG(0,("process_model_startup('%s') failed\n", model)); + return NULL; + } + + mem_ctx = talloc_init("server_context"); + if (!mem_ctx) { + DEBUG(0,("talloc_init(server_context) failed\n")); + return NULL; + } + + srv_ctx = talloc_p(mem_ctx, struct server_context); + if (!srv_ctx) { + DEBUG(0,("talloc_p(mem_ctx, struct server_context) failed\n")); + return NULL; + } + + ZERO_STRUCTP(srv_ctx); + srv_ctx->mem_ctx = mem_ctx; + + srv_ctx->events = event_context_init(); + if (!srv_ctx->events) { + DEBUG(0,("event_context_init() failed\n")); + return NULL; + } + + + for (i=0;server_services[i];i++) { + TALLOC_CTX *mem_ctx2; + const struct server_service_ops *service_ops; + struct server_service *service; + + service_ops = server_service_byname(server_services[i]); + if (!service_ops) { + DEBUG(0,("process_model_startup: failed to find server service = '%s'\n", server_services[i])); + return NULL; + } + + mem_ctx2 = talloc_init("server_service"); + + service = talloc_p(mem_ctx2, struct server_service); + if (!service) { + DEBUG(0,("talloc_p(mem_ctx, struct server_service) failed\n")); + return NULL; + } + + ZERO_STRUCTP(service); + service->mem_ctx = mem_ctx2; + service->ops = service_ops; + service->model_ops = model_ops; + service->srv_ctx = srv_ctx; + + /* TODO: service_init() should return a result */ + service->ops->service_init(service, model_ops); + } + + return srv_ctx; +} + +/* + setup a single listener of any type + if you pass *port == 0, then a port < 1024 is used + */ +struct server_socket *service_setup_socket(struct server_service *service, + const struct model_ops *model_ops, + struct socket_context *socket_ctx, + struct in_addr *ifip, uint16_t *port) +{ + TALLOC_CTX *mem_ctx; + struct server_socket *sock; + struct fd_event fde; + int i; + + mem_ctx = talloc_init("struct server_socket"); + + sock = talloc_p(mem_ctx, struct server_socket); + if (!sock) { + DEBUG(0,("talloc_p(mem_ctx, struct server_socket) failed\n")); + return NULL; + } + + if (*port == 0) { + fde.fd = -1; + for (i=SERVER_TCP_LOW_PORT;i<= SERVER_TCP_HIGH_PORT;i++) { + fde.fd = open_socket_in(SOCK_STREAM, i, 0, ifip->s_addr, True); + if (fde.fd != -1) break; + } + if (fde.fd != -1) { + *port = i; + } + } else { + fde.fd = open_socket_in(SOCK_STREAM, *port, 0, ifip->s_addr, True); + } + + if (fde.fd == -1) { + DEBUG(0,("Failed to open socket on %s:%u - %s\n", + inet_ntoa(*ifip), *port, strerror(errno))); + return NULL; + } + + /* ready to listen */ + set_socket_options(fde.fd, "SO_KEEPALIVE"); + set_socket_options(fde.fd, lp_socket_options()); + + if (listen(fde.fd, SERVER_LISTEN_BACKLOG) == -1) { + DEBUG(0,("Failed to listen on %s:%u - %s\n", + inet_ntoa(*ifip), *port, strerror(errno))); + close(fde.fd); + return NULL; + } + + /* we are only interested in read events on the listen socket */ + fde.flags = EVENT_FD_READ; + fde.private = sock; + fde.handler = model_ops->accept_connection; + + ZERO_STRUCTP(sock); + sock->mem_ctx = mem_ctx; + sock->service = service; + sock->socket = socket_ctx; + sock->event.ctx = service->srv_ctx->events; + sock->event.fde = event_add_fd(sock->event.ctx, &fde); + if (!sock->event.fde) { + DEBUG(0,("event_add_fd(sock->event.ctx, &fde) failed\n")); + return NULL; + } + + DLIST_ADD(service->socket_list, sock); + + return sock; +} + +/* + close the socket and shutdown a server_context +*/ +void server_terminate_connection(struct server_connection *srv_conn, const char *reason) +{ + DEBUG(0,("server_terminate_connection\n")); + srv_conn->service->model_ops->terminate_connection(srv_conn, reason); +} + +void server_io_handler(struct event_context *ev, struct fd_event *fde, time_t t, uint16_t flags) +{ + struct server_connection *conn = fde->private; + + if (flags & EVENT_FD_WRITE) { + conn->service->ops->send_handler(conn, t, flags); + conn->event.idle->next_event = t + conn->event.idle_time; + } + + if (flags & EVENT_FD_READ) { + conn->service->ops->recv_handler(conn, t, flags); + conn->event.idle->next_event = t + conn->event.idle_time; + } + +} + +void server_idle_handler(struct event_context *ev, struct timed_event *idle, time_t t) +{ + struct server_connection *conn = idle->private; + + conn->event.idle->next_event = t + conn->event.idle_time; + + conn->service->ops->idle_handler(conn,t); +} +/* + return the operations structure for a named backend of the specified type +*/ +const struct server_service_ops *server_service_byname(const char *name) +{ + if (strcmp("smb",name)==0) { + return smbsrv_get_ops(); + } + if (strcmp("rpc",name)==0) { + return dcesrv_get_ops(); + } + return NULL; +} + +static NTSTATUS register_server_service_ops(const void *_ops) +{ + return NT_STATUS_NOT_IMPLEMENTED; +} + +/* + initialise the SERVER SERVICE subsystem +*/ +BOOL server_service_init(void) +{ + NTSTATUS status; + + status = register_subsystem("service", register_server_service_ops); + if (!NT_STATUS_IS_OK(status)) { + return False; + } + + /* FIXME: Perhaps panic if a basic endpoint server, such as EPMAPER, fails to initialise? */ + static_init_server_service; + + DEBUG(3,("SERVER SERVICE subsystem version %d initialised\n", SERVER_SERVICE_VERSION)); + return True; +} diff --git a/source4/smbd/service.h b/source4/smbd/service.h new file mode 100644 index 0000000000..41ad381f9e --- /dev/null +++ b/source4/smbd/service.h @@ -0,0 +1,127 @@ +/* + Unix SMB/CIFS implementation. + + SERVER SERVICE code + + Copyright (C) Stefan (metze) Metzmacher 2004 + + 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. +*/ + +#ifndef _SERVER_SERVICE_H +#define _SERVER_SERVICE_H + +struct event_context; +struct model_ops; +struct server_context; + +struct server_connection; +struct server_service; + +/* modules can use the following to determine if the interface has changed + * please increment the version number after each interface change + * with a comment and maybe update struct process_model_critical_sizes. + */ +/* version 1 - initial version - metze */ +#define SERVER_SERVICE_VERSION 1 + +struct server_service_ops { + /* the name of the server_service */ + const char *name; + + /* called at startup when the server_service is selected */ + void (*service_init)(struct server_service *service, const struct model_ops *ops); + + /* function to accept new connection */ + void (*accept_connection)(struct server_connection *); + + /* function to accept new connection */ + void (*recv_handler)(struct server_connection *, time_t, uint16_t); + + /* function to accept new connection */ + void (*send_handler)(struct server_connection *, time_t, uint16_t); + + /* function to accept new connection */ + void (*idle_handler)(struct server_connection *, time_t); + + /* function to close a connection */ + void (*close_connection)(struct server_connection *, const char *reason); + + /* function to exit server */ + void (*service_exit)(struct server_service *srv_ctx, const char *reason); +}; + +struct socket_ops { + int dummy; +}; + +struct socket_context { + void *private_data; + struct socket_ops *ops; + const char *client_addr; + uint_t pkt_count; + struct fd_event *fde; +}; + +struct server_socket { + struct server_socket *next,*prev; + TALLOC_CTX *mem_ctx; + void *private_data; + + struct { + struct event_context *ctx; + struct fd_event *fde; + } event; + + struct socket_context *socket; + + struct server_service *service; + + struct server_connection *connection_list; +}; + +struct server_service { + struct server_service *next,*prev; + TALLOC_CTX *mem_ctx; + void *private_data; + const struct server_service_ops *ops; + + const struct model_ops *model_ops; + + struct server_socket *socket_list; + + struct server_context *srv_ctx; +}; + +struct server_connection { + struct server_connection *next,*prev; + TALLOC_CTX *mem_ctx; + void *private_data; + + struct { + struct event_context *ctx; + struct fd_event *fde; + struct timed_event *idle; + time_t idle_time; + } event; + + struct socket_context *socket; + + struct server_socket *server_socket; + + struct server_service *service; +}; + +#endif /* _SERVER_SERVICE_H */ -- cgit