diff options
author | Jelmer Vernooij <jelmer@samba.org> | 2004-10-24 15:48:19 +0000 |
---|---|---|
committer | Gerald (Jerry) Carter <jerry@samba.org> | 2007-10-10 13:02:28 -0500 |
commit | 78e5bc76b602f9ccf49e2f139cbd1f20b458def4 (patch) | |
tree | 529bd5985200a637b7f0c88f828e2eabea881b18 | |
parent | 40c5a1d99fba2144fc3b5d4d8ed5959a269600ca (diff) | |
download | samba-78e5bc76b602f9ccf49e2f139cbd1f20b458def4.tar.gz samba-78e5bc76b602f9ccf49e2f139cbd1f20b458def4.tar.bz2 samba-78e5bc76b602f9ccf49e2f139cbd1f20b458def4.zip |
r3163: Add server side support for ncalrpc: and ncacn_unix_stream:
Examples of binding strings are :
ncalrpc:[EPMAPPER]
ncacn_unix_stream:[/tmp/epmapper]
N.B. The unix socket support in lib/socket/ appears to close and remove the
socket it is listening on after the first client disconnects so until
that has been fixed, it is only possible to do one ncalrpc: or ncacn_unix_stream: request per instance of smbd :-)
Support for looking up NCALRPC names via the endpoint mapper will be added later.
(This used to be commit 426f3e63cae3d306dcdc13ee4b655eed30057ff8)
-rw-r--r-- | source4/build/smb_build/makefile.pl | 2 | ||||
-rw-r--r-- | source4/rpc_server/config.mk | 2 | ||||
-rw-r--r-- | source4/rpc_server/dcerpc_server.c | 14 | ||||
-rw-r--r-- | source4/rpc_server/dcerpc_sock.c | 328 | ||||
-rw-r--r-- | source4/rpc_server/dcerpc_tcp.c | 228 | ||||
-rwxr-xr-x | source4/script/tests/test_binding_string.sh | 1 |
6 files changed, 338 insertions, 237 deletions
diff --git a/source4/build/smb_build/makefile.pl b/source4/build/smb_build/makefile.pl index 108e1a45dc..5e24829263 100644 --- a/source4/build/smb_build/makefile.pl +++ b/source4/build/smb_build/makefile.pl @@ -65,7 +65,7 @@ PATH_FLAGS3 = \$(PATH_FLAGS2) -DLMHOSTSFILE=\\\"\$(LMHOSTSFILE)\\\" PATH_FLAGS4 = \$(PATH_FLAGS3) -DLOCKDIR=\\\"\$(LOCKDIR)\\\" -DPIDDIR=\\\"\$(PIDDIR)\\\" PATH_FLAGS5 = \$(PATH_FLAGS4) -DLIBDIR=\\\"\$(LIBDIR)\\\" \\ -DLOGFILEBASE=\\\"\$(LOGFILEBASE)\\\" -DSHLIBEXT=\\\"\@SHLIBEXT\@\\\" -PATH_FLAGS6 = \$(PATH_FLAGS5) -DCONFIGDIR=\\\"\$(CONFIGDIR)\\\" -DNCALRPCDIR=\\\"\@NCALRPCDIR\@\\\" +PATH_FLAGS6 = \$(PATH_FLAGS5) -DCONFIGDIR=\\\"\$(CONFIGDIR)\\\" -DNCALRPCDIR=\\\"\$(NCALRPCDIR)\\\" PATH_FLAGS = \$(PATH_FLAGS6) \$(PASSWD_FLAGS) "; return $output; diff --git a/source4/rpc_server/config.mk b/source4/rpc_server/config.mk index 9c0751a1b9..4e08181ab0 100644 --- a/source4/rpc_server/config.mk +++ b/source4/rpc_server/config.mk @@ -167,7 +167,7 @@ REQUIRED_SUBSYSTEMS = \ INIT_OBJ_FILES = \ rpc_server/dcerpc_server.o ADD_OBJ_FILES = \ - rpc_server/dcerpc_tcp.o \ + rpc_server/dcerpc_sock.o \ rpc_server/dcesrv_auth.o \ rpc_server/handles.o REQUIRED_SUBSYSTEMS = \ diff --git a/source4/rpc_server/dcerpc_server.c b/source4/rpc_server/dcerpc_server.c index a4e31712bd..cf078b9426 100644 --- a/source4/rpc_server/dcerpc_server.c +++ b/source4/rpc_server/dcerpc_server.c @@ -1048,40 +1048,40 @@ static void dcesrv_init(struct server_service *service, const struct model_ops * } } - dcesrv_tcp_init(service, model_ops, dce_ctx); + dcesrv_sock_init(service, model_ops, dce_ctx); return; } static void dcesrv_accept(struct server_connection *srv_conn) { - dcesrv_tcp_accept(srv_conn); + dcesrv_sock_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); + dcesrv_sock_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); + dcesrv_sock_send(srv_conn, t, flags); } static void dcesrv_idle(struct server_connection *srv_conn, time_t t) { - dcesrv_tcp_idle(srv_conn, t); + dcesrv_sock_idle(srv_conn, t); } static void dcesrv_close(struct server_connection *srv_conn, const char *reason) { - dcesrv_tcp_close(srv_conn, reason); + dcesrv_sock_close(srv_conn, reason); return; } static void dcesrv_exit(struct server_service *service, const char *reason) { - dcesrv_tcp_exit(service, reason); + dcesrv_sock_exit(service, reason); return; } diff --git a/source4/rpc_server/dcerpc_sock.c b/source4/rpc_server/dcerpc_sock.c new file mode 100644 index 0000000000..d5646a7c7a --- /dev/null +++ b/source4/rpc_server/dcerpc_sock.c @@ -0,0 +1,328 @@ +/* + Unix SMB/CIFS implementation. + + server side dcerpc using various kinds of sockets (tcp, unix domain) + + Copyright (C) Andrew Tridgell 2003 + Copyright (C) Stefan (metze) Metzmacher 2004 + Copyright (C) Jelmer Vernooij 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 dcesrv_socket_context { + const struct dcesrv_endpoint *endpoint; + struct dcesrv_context *dcesrv_ctx; +}; + +/* + write_fn callback for dcesrv_output() +*/ +static ssize_t dcerpc_write_fn(void *private, DATA_BLOB *out) +{ + NTSTATUS status; + struct socket_context *sock = private; + size_t sendlen; + + status = socket_send(sock, sock, out, &sendlen, 0); + if (!NT_STATUS_IS_OK(status)) { + return -1; + } + + return sendlen; +} + +void dcesrv_terminate_connection(struct dcesrv_connection *dce_conn, const char *reason) +{ + server_terminate_connection(dce_conn->srv_conn, reason); +} + +static void add_socket_rpc_unix(struct server_service *service, + const struct model_ops *model_ops, + struct dcesrv_context *dce_ctx, + struct dcesrv_endpoint *e) +{ + struct server_socket *sock; + struct dcesrv_socket_context *dcesrv_sock; + uint16_t port = 1; + + sock = service_setup_socket(service,model_ops, "unix", e->ep_description.options[0], &port); + if (!sock) { + DEBUG(0,("service_setup_socket(path=%s) failed\n",e->ep_description.options[0])); + return; + } + + dcesrv_sock = talloc_p(sock, struct dcesrv_socket_context); + if (!dcesrv_sock) { + DEBUG(0,("talloc_p(sock->mem_ctx, struct dcesrv_socket_context) failed\n")); + return; + } + + /* remember the endpoint of this socket */ + dcesrv_sock->endpoint = e; + dcesrv_sock->dcesrv_ctx = dce_ctx; + + sock->private_data = dcesrv_sock; +} + +static void add_socket_rpc_ncalrpc(struct server_service *service, + const struct model_ops *model_ops, + struct dcesrv_context *dce_ctx, + struct dcesrv_endpoint *e) +{ + struct server_socket *sock; + struct dcesrv_socket_context *dcesrv_sock; + uint16_t port = 1; + char *full_path; + + if (!e->ep_description.options) { + e->ep_description.options = talloc_array_p(dce_ctx, const char *, 2); + e->ep_description.options[0] = NULL; + } + + if (!e->ep_description.options[0]) { + /* No identifier specified: generate one */ + e->ep_description.options[0] = generate_random_str(dce_ctx, 10); + e->ep_description.options[1] = NULL; + } + + full_path = talloc_asprintf(dce_ctx, "%s/%s", lp_ncalrpc_dir(), e->ep_description.options[0]); + + sock = service_setup_socket(service,model_ops, "unix", full_path, &port); + if (!sock) { + DEBUG(0,("service_setup_socket(identifier=%s,path=%s) failed\n",e->ep_description.options[0], full_path)); + return; + } + + dcesrv_sock = talloc_p(sock, struct dcesrv_socket_context); + if (!dcesrv_sock) { + DEBUG(0,("talloc_p(sock->mem_ctx, struct dcesrv_socket_context) failed\n")); + return; + } + + /* remember the endpoint of this socket */ + dcesrv_sock->endpoint = e; + dcesrv_sock->dcesrv_ctx = dce_ctx; + + sock->private_data = dcesrv_sock; + + return; +} + +/* + add a socket address to the list of events, one event per dcerpc endpoint +*/ +static void add_socket_rpc_tcp_iface(struct server_service *service, + const struct model_ops *model_ops, + struct dcesrv_context *dce_ctx, + struct dcesrv_endpoint *e, + struct in_addr *ifip) +{ + struct server_socket *sock; + struct dcesrv_socket_context *dcesrv_sock; + uint16_t port = 0; + const char *ip_str = talloc_strdup(service, inet_ntoa(*ifip)); + + if (e->ep_description.options && e->ep_description.options[0]) + port = atoi(e->ep_description.options[0]); + + sock = service_setup_socket(service,model_ops, "ipv4", ip_str, &port); + if (!sock) { + DEBUG(0,("service_setup_socket(port=%u) failed\n",port)); + return; + } + + /* And put the settings back into the binding. This will + * go away once we store the 'encoded' endpoint instead of a + * string describing it */ + if (e->ep_description.options == NULL) { + e->ep_description.options = talloc_array_p(dce_ctx, const char *, 2); + e->ep_description.options[0] = talloc_asprintf(dce_ctx, "%d", port); + e->ep_description.options[1] = NULL; + } + + dcesrv_sock = talloc_p(sock, struct dcesrv_socket_context); + if (!dcesrv_sock) { + DEBUG(0,("talloc_p(sock->mem_ctx, struct dcesrv_socket_context) failed\n")); + return; + } + + /* remember the endpoint of this socket */ + dcesrv_sock->endpoint = e; + dcesrv_sock->dcesrv_ctx = dce_ctx; + + sock->private_data = dcesrv_sock; + + talloc_free(ip_str); + + return; +} + +static void add_socket_rpc_tcp(struct server_service *service, + const struct model_ops *model_ops, + struct dcesrv_context *dce_ctx, + struct dcesrv_endpoint *e) +{ + /* Add TCP/IP sockets */ + 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_tcp_iface(service, model_ops, dce_ctx, e, ifip); + } + } else { + struct in_addr *ifip; + ifip = interpret_addr2(dce_ctx, lp_socket_address()); + add_socket_rpc_tcp_iface(service, model_ops, dce_ctx, e, ifip); + talloc_free(ifip); + } + + return; +} + +/**************************************************************************** + Open the listening sockets for RPC over NCACN_IP_TCP/NCALRPC/NCACN_UNIX_STREAM +****************************************************************************/ +void dcesrv_sock_init(struct server_service *service, const struct model_ops *model_ops, struct dcesrv_context *dce_ctx) +{ + struct dcesrv_endpoint *e; + + DEBUG(1,("dcesrv_sock_init\n")); + + for (e=dce_ctx->endpoint_list;e;e=e->next) { + switch (e->ep_description.transport) { + case NCACN_UNIX_STREAM: + add_socket_rpc_unix(service, model_ops, dce_ctx, e); + break; + + case NCALRPC: + add_socket_rpc_ncalrpc(service, model_ops, dce_ctx, e); + break; + + case NCACN_IP_TCP: + add_socket_rpc_tcp(service, model_ops, dce_ctx, e); + break; + + default: + break; + } + } + + return; +} + +void dcesrv_sock_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_sock_accept\n")); + + status = dcesrv_endpoint_connect(dcesrv_sock->dcesrv_ctx, dcesrv_sock->endpoint, &dcesrv_conn); + if (!NT_STATUS_IS_OK(status)) { + DEBUG(0,("dcesrv_sock_accept: dcesrv_endpoint_connect failed: %s\n", + nt_errstr(status))); + return; + } + + dcesrv_conn->srv_conn = conn; + + conn->private_data = dcesrv_conn; + + return; +} + +void dcesrv_sock_recv(struct server_connection *conn, time_t t, uint16_t flags) +{ + NTSTATUS status; + struct dcesrv_connection *dce_conn = conn->private_data; + DATA_BLOB tmp_blob; + + DEBUG(10,("dcesrv_sock_recv\n")); + + status = socket_recv(conn->socket, conn->socket, &tmp_blob, 0x4000, 0); + if (!NT_STATUS_IS_OK(status)) { + if (NT_STATUS_IS_ERR(status)) { + dcesrv_terminate_connection(dce_conn, "eof on socket"); + return; + } + return; + } + + status = dcesrv_input(dce_conn, &tmp_blob); + talloc_free(tmp_blob.data); + if (!NT_STATUS_IS_OK(status)) { + dcesrv_terminate_connection(dce_conn, "eof on socket"); + return; + } + + if (dce_conn->call_list && dce_conn->call_list->replies) { + conn->event.fde->flags |= EVENT_FD_WRITE; + } + + return; +} + +void dcesrv_sock_send(struct server_connection *conn, time_t t, uint16_t flags) +{ + struct dcesrv_connection *dce_conn = conn->private_data; + NTSTATUS status; + + DEBUG(10,("dcesrv_sock_send\n")); + + status = dcesrv_output(dce_conn, conn->socket, dcerpc_write_fn); + if (!NT_STATUS_IS_OK(status)) { + dcesrv_terminate_connection(dce_conn, "eof on socket"); + return; + } + + if (!dce_conn->call_list || !dce_conn->call_list->replies) { + conn->event.fde->flags &= ~EVENT_FD_WRITE; + } + + return; +} + +void dcesrv_sock_idle(struct server_connection *conn, time_t t) +{ + DEBUG(10,("dcesrv_sock_idle\n")); + conn->event.idle->next_event = t + 5; + + return; +} + +void dcesrv_sock_close(struct server_connection *conn, const char *reason) +{ + struct dcesrv_connection *dce_conn = conn->private_data; + + DEBUG(5,("dcesrv_sock_close: %s\n",reason)); + + talloc_free(dce_conn); + + return; +} + +void dcesrv_sock_exit(struct server_service *service, const char *reason) +{ + DEBUG(1,("dcesrv_sock_exit: %s\n",reason)); + return; +} diff --git a/source4/rpc_server/dcerpc_tcp.c b/source4/rpc_server/dcerpc_tcp.c deleted file mode 100644 index adfddae14f..0000000000 --- a/source4/rpc_server/dcerpc_tcp.c +++ /dev/null @@ -1,228 +0,0 @@ -/* - Unix SMB/CIFS implementation. - - server side dcerpc over tcp 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 dcesrv_socket_context { - const struct dcesrv_endpoint *endpoint; - struct dcesrv_context *dcesrv_ctx; -}; - -/* - write_fn callback for dcesrv_output() -*/ -static ssize_t dcerpc_write_fn(void *private, DATA_BLOB *out) -{ - NTSTATUS status; - struct socket_context *sock = private; - size_t sendlen; - - status = socket_send(sock, sock, out, &sendlen, 0); - if (!NT_STATUS_IS_OK(status)) { - return -1; - } - - return sendlen; -} - -void dcesrv_terminate_connection(struct dcesrv_connection *dce_conn, const char *reason) -{ - server_terminate_connection(dce_conn->srv_conn, reason); -} - -/* - add a socket address to the list of events, one event per dcerpc endpoint -*/ -static void add_socket_rpc(struct server_service *service, - const struct model_ops *model_ops, - struct dcesrv_context *dce_ctx, - struct in_addr *ifip) -{ - struct dcesrv_endpoint *e; - char *ip_str = talloc_strdup(service, inet_ntoa(*ifip)); - - for (e=dce_ctx->endpoint_list;e;e=e->next) { - if (e->ep_description.transport == NCACN_IP_TCP) { - struct server_socket *sock; - struct dcesrv_socket_context *dcesrv_sock; - uint16_t port = 0; - - if (e->ep_description.options && e->ep_description.options[0]) - port = atoi(e->ep_description.options[0]); - - sock = service_setup_socket(service,model_ops, "ipv4", ip_str, &port); - if (!sock) { - DEBUG(0,("service_setup_socket(port=%u) failed\n",port)); - continue; - } - - /* And put the settings back into the binding. This will - * go away once we store the 'encoded' endpoint instead of a - * string describing it */ - if (e->ep_description.options == NULL) { - e->ep_description.options = talloc_array_p(dce_ctx, const char *, 2); - e->ep_description.options[0] = talloc_asprintf(dce_ctx, "%d", port); - e->ep_description.options[1] = NULL; - } - - dcesrv_sock = talloc_p(sock, struct dcesrv_socket_context); - if (!dcesrv_sock) { - DEBUG(0,("talloc_p(sock->mem_ctx, struct dcesrv_socket_context) failed\n")); - continue; - } - - /* remember the endpoint of this socket */ - dcesrv_sock->endpoint = e; - dcesrv_sock->dcesrv_ctx = dce_ctx; - - sock->private_data = dcesrv_sock; - } - } - - talloc_free(ip_str); -} - -/**************************************************************************** - 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, dce_ctx, ifip); - } - } else { - struct in_addr *ifip; - ifip = interpret_addr2(dce_ctx, lp_socket_address()); - add_socket_rpc(service, model_ops, dce_ctx, ifip); - talloc_free(ifip); - } - - return; -} - -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; - - return; -} - -void dcesrv_tcp_recv(struct server_connection *conn, time_t t, uint16_t flags) -{ - NTSTATUS status; - struct dcesrv_connection *dce_conn = conn->private_data; - DATA_BLOB tmp_blob; - - DEBUG(10,("dcesrv_tcp_recv\n")); - - status = socket_recv(conn->socket, conn->socket, &tmp_blob, 0x4000, 0); - if (!NT_STATUS_IS_OK(status)) { - if (NT_STATUS_IS_ERR(status)) { - dcesrv_terminate_connection(dce_conn, "eof on socket"); - return; - } - return; - } - - status = dcesrv_input(dce_conn, &tmp_blob); - talloc_free(tmp_blob.data); - if (!NT_STATUS_IS_OK(status)) { - dcesrv_terminate_connection(dce_conn, "eof on socket"); - return; - } - - if (dce_conn->call_list && dce_conn->call_list->replies) { - conn->event.fde->flags |= EVENT_FD_WRITE; - } - - return; -} - -void dcesrv_tcp_send(struct server_connection *conn, time_t t, uint16_t flags) -{ - struct dcesrv_connection *dce_conn = conn->private_data; - NTSTATUS status; - - DEBUG(10,("dcesrv_tcp_send\n")); - - status = dcesrv_output(dce_conn, conn->socket, dcerpc_write_fn); - if (!NT_STATUS_IS_OK(status)) { - dcesrv_terminate_connection(dce_conn, "eof on socket"); - return; - } - - if (!dce_conn->call_list || !dce_conn->call_list->replies) { - conn->event.fde->flags &= ~EVENT_FD_WRITE; - } - - return; -} - -void dcesrv_tcp_idle(struct server_connection *conn, time_t t) -{ - DEBUG(10,("dcesrv_tcp_idle\n")); - conn->event.idle->next_event = t + 5; - - return; -} - -void dcesrv_tcp_close(struct server_connection *conn, const char *reason) -{ - struct dcesrv_connection *dce_conn = conn->private_data; - - DEBUG(5,("dcesrv_tcp_close: %s\n",reason)); - - talloc_free(dce_conn); - - return; -} - -void dcesrv_tcp_exit(struct server_service *service, const char *reason) -{ - DEBUG(1,("dcesrv_tcp_exit: %s\n",reason)); - return; -} diff --git a/source4/script/tests/test_binding_string.sh b/source4/script/tests/test_binding_string.sh index 5a5e190672..8f8177cf56 100755 --- a/source4/script/tests/test_binding_string.sh +++ b/source4/script/tests/test_binding_string.sh @@ -31,6 +31,7 @@ for I in "ncacn_np:$server" \ "ncacn_np:$server[/pipe/rpcecho,sign,seal]" \ "ncacn_np:$server[,sign]" \ "ncacn_ip_tcp:$server[,sign]" \ + "ncalrpc:" \ "308FB580-1EB2-11CA-923B-08002B1075A7@ncacn_np:$server" \ "308FB580-1EB2-11CA-923B-08002B1075A7@ncacn_ip_tcp:$server" do |