From f42402da83afa97f821d36b7974de98ddd5a2880 Mon Sep 17 00:00:00 2001 From: Andrew Tridgell Date: Sun, 17 Oct 2004 05:07:07 +0000 Subject: r3013: added support for unix domain sockets in the generic socket library. I will shortly be using this for a rewrite of the intra-smbd messaging library, which is needed to get lock timeouts working properly (and share modes, oplocks etc) (This used to be commit 6f4926d846965a901e40d24546eab356c4a537c7) --- source4/lib/socket/config.m4 | 1 + source4/lib/socket/config.mk | 8 + source4/lib/socket/socket.c | 4 + source4/lib/socket/socket_ipv4.c | 3 +- source4/lib/socket/socket_unix.c | 319 +++++++++++++++++++++++++++++++++++++++ 5 files changed, 334 insertions(+), 1 deletion(-) create mode 100644 source4/lib/socket/socket_unix.c diff --git a/source4/lib/socket/config.m4 b/source4/lib/socket/config.m4 index a5d3d0916e..c91f21368f 100644 --- a/source4/lib/socket/config.m4 +++ b/source4/lib/socket/config.m4 @@ -1,4 +1,5 @@ SMB_MODULE_MK(socket_ipv4,SOCKET,STATIC,lib/socket/config.mk) +SMB_MODULE_MK(socket_unix,SOCKET,STATIC,lib/socket/config.mk) SMB_SUBSYSTEM_MK(SOCKET,lib/socket/config.mk) diff --git a/source4/lib/socket/config.mk b/source4/lib/socket/config.mk index 320fc7f3ee..6072f743ce 100644 --- a/source4/lib/socket/config.mk +++ b/source4/lib/socket/config.mk @@ -7,6 +7,14 @@ INIT_OBJ_FILES = \ # End MODULE socket_ipv4 ################################################ +################################################ +# Start MODULE socket_unix +[MODULE::socket_unix] +INIT_OBJ_FILES = \ + lib/socket/socket_unix.o +# End MODULE socket_unix +################################################ + ################################################ # Start SUBSYSTEM SOCKET [SUBSYSTEM::SOCKET] diff --git a/source4/lib/socket/socket.c b/source4/lib/socket/socket.c index 4fde41a3c0..f364ca7c9f 100644 --- a/source4/lib/socket/socket.c +++ b/source4/lib/socket/socket.c @@ -240,5 +240,9 @@ const struct socket_ops *socket_getops_byname(const char *name, enum socket_type return socket_ipv4_ops(); } + if (strequal("unix", name)) { + return socket_ipv4_ops(); + } + return NULL; } diff --git a/source4/lib/socket/socket_ipv4.c b/source4/lib/socket/socket_ipv4.c index 88bf611b67..23e34dd39b 100644 --- a/source4/lib/socket/socket_ipv4.c +++ b/source4/lib/socket/socket_ipv4.c @@ -150,7 +150,7 @@ static NTSTATUS ipv4_tcp_listen(struct socket_context *sock, static NTSTATUS ipv4_tcp_accept(struct socket_context *sock, struct socket_context **new_sock, uint32_t flags) { struct sockaddr_in cli_addr; - socklen_t cli_addr_len = 0; + socklen_t cli_addr_len = sizeof(cli_addr); int new_fd; new_fd = accept(sock->fd, (struct sockaddr *)&cli_addr, &cli_addr_len); @@ -167,6 +167,7 @@ static NTSTATUS ipv4_tcp_accept(struct socket_context *sock, struct socket_conte (*new_sock) = talloc_p(NULL, struct socket_context); if (!(*new_sock)) { + close(new_fd); return NT_STATUS_NO_MEMORY; } diff --git a/source4/lib/socket/socket_unix.c b/source4/lib/socket/socket_unix.c new file mode 100644 index 0000000000..d87eaf49c4 --- /dev/null +++ b/source4/lib/socket/socket_unix.c @@ -0,0 +1,319 @@ +/* + Unix SMB/CIFS implementation. + + unix domain socket functions + + Copyright (C) Stefan Metzmacher 2004 + Copyright (C) Andrew Tridgell 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" + +static NTSTATUS unixdom_init(struct socket_context *sock) +{ + sock->fd = socket(PF_UNIX, SOCK_STREAM, 0); + if (sock->fd == -1) { + return NT_STATUS_INSUFFICIENT_RESOURCES; + } + + return NT_STATUS_OK; +} + +static void unixdom_close(struct socket_context *sock) +{ + close(sock->fd); +} + +static NTSTATUS unixdom_connect(struct socket_context *sock, + const char *my_address, int my_port, + const char *srv_address, int srv_port, + uint32_t flags) +{ + struct sockaddr_un srv_addr; + int ret; + + if (strlen(srv_address)+1 > sizeof(srv_addr.sun_path)) { + return NT_STATUS_INVALID_PARAMETER; + } + + ZERO_STRUCT(srv_addr); + srv_addr.sun_family = AF_UNIX; + strncpy(srv_addr.sun_path, srv_address, sizeof(srv_addr.sun_path)); + + if (!(flags & SOCKET_FLAG_BLOCK)) { + ret = set_blocking(sock->fd, False); + if (ret == -1) { + return NT_STATUS_INVALID_PARAMETER; + } + } + + ret = connect(sock->fd, (const struct sockaddr *)&srv_addr, sizeof(srv_addr)); + if (ret == -1) { + return NT_STATUS_CONNECTION_REFUSED; + } + + sock->state = SOCKET_STATE_CLIENT_CONNECTED; + + return NT_STATUS_OK; +} + +static NTSTATUS unixdom_listen(struct socket_context *sock, + const char *my_address, int port, + int queue_size, uint32_t flags) +{ + struct sockaddr_un my_addr; + int ret; + + if (strlen(my_address)+1 > sizeof(my_addr.sun_path)) { + return NT_STATUS_INVALID_PARAMETER; + } + + ZERO_STRUCT(my_addr); + my_addr.sun_family = AF_UNIX; + strncpy(my_addr.sun_path, my_address, sizeof(my_addr.sun_path)); + + ret = bind(sock->fd, (struct sockaddr *)&my_addr, sizeof(my_addr)); + if (ret == -1) { + return NT_STATUS_UNSUCCESSFUL; + } + + ret = listen(sock->fd, queue_size); + if (ret == -1) { + return NT_STATUS_INSUFFICIENT_RESOURCES; + } + + if (!(flags & SOCKET_FLAG_BLOCK)) { + ret = set_blocking(sock->fd, False); + if (ret == -1) { + return NT_STATUS_INVALID_PARAMETER; + } + } + + sock->state = SOCKET_STATE_SERVER_LISTEN; + + return NT_STATUS_OK; +} + +static NTSTATUS unixdom_accept(struct socket_context *sock, + struct socket_context **new_sock, + uint32_t flags) +{ + struct sockaddr_un cli_addr; + socklen_t cli_addr_len = sizeof(cli_addr); + int new_fd; + + new_fd = accept(sock->fd, (struct sockaddr *)&cli_addr, &cli_addr_len); + if (new_fd == -1) { + return NT_STATUS_INSUFFICIENT_RESOURCES; + } + + (*new_sock) = talloc_p(NULL, struct socket_context); + if (!(*new_sock)) { + close(new_fd); + return NT_STATUS_NO_MEMORY; + } + + /* copy the socket_context */ + (*new_sock)->type = sock->type; + (*new_sock)->state = SOCKET_STATE_SERVER_CONNECTED; + (*new_sock)->flags = flags; + + (*new_sock)->fd = new_fd; + + (*new_sock)->private_data = NULL; + (*new_sock)->ops = sock->ops; + + return NT_STATUS_OK; +} + +static NTSTATUS unixdom_recv(struct socket_context *sock, TALLOC_CTX *mem_ctx, + DATA_BLOB *blob, size_t wantlen, uint32_t flags) +{ + ssize_t gotlen; + void *buf; + int flgs = 0; + + buf = talloc(mem_ctx, wantlen); + if (!buf) { + return NT_STATUS_NO_MEMORY; + } + + /* TODO: we need to map all flags here */ + if (flags & SOCKET_FLAG_PEEK) { + flgs |= MSG_PEEK; + } + + if (!(flags & SOCKET_FLAG_BLOCK)) { + flgs |= MSG_DONTWAIT; + } + + if (flags & SOCKET_FLAG_BLOCK) { + flgs |= MSG_WAITALL; + } + + gotlen = recv(sock->fd, buf, wantlen, flgs); + if (gotlen == 0) { + talloc_free(buf); + return NT_STATUS_END_OF_FILE; + } else if (gotlen == -1) { + NTSTATUS status = NT_STATUS_UNSUCCESSFUL; + switch (errno) { + case EBADF: + case ENOTCONN: + case ENOTSOCK: + case EFAULT: + case EINVAL: + status = NT_STATUS_INVALID_PARAMETER; + break; + case EAGAIN: + case EINTR: + status = STATUS_MORE_ENTRIES; + break; + case ECONNREFUSED: + status = NT_STATUS_CONNECTION_REFUSED; + break; + } + talloc_free(buf); + return status; + } + + blob->length = gotlen; + blob->data = talloc_realloc(mem_ctx, buf, gotlen); + if (!blob->data) { + return NT_STATUS_NO_MEMORY; + } + + return NT_STATUS_OK; +} + +static NTSTATUS unixdom_send(struct socket_context *sock, TALLOC_CTX *mem_ctx, + const DATA_BLOB *blob, size_t *sendlen, uint32_t flags) +{ + ssize_t len; + int flgs = 0; + + /* TODO: we need to map all flags here */ + if (!(flags & SOCKET_FLAG_BLOCK)) { + flgs |= MSG_DONTWAIT; + } + + len = send(sock->fd, blob->data, blob->length, flgs); + if (len == -1) { + NTSTATUS status = NT_STATUS_UNSUCCESSFUL; + switch (errno) { + case EBADF: + case ENOTSOCK: + case EFAULT: + case EINVAL: + status = NT_STATUS_INVALID_PARAMETER; + break; + case EMSGSIZE: + status = NT_STATUS_INVALID_BUFFER_SIZE; + break; + case EAGAIN: + /*case EWOULDBLOCK: this is an alis of EAGAIN --metze */ + case EINTR: + *sendlen = 0; + status = STATUS_MORE_ENTRIES; + break; + case ENOBUFS: + status = NT_STATUS_FOOBAR; + break; + case ENOMEM: + status = NT_STATUS_NO_MEMORY; + break; + case EPIPE: + status = NT_STATUS_CONNECTION_DISCONNECTED; + break; + } + return status; + } + + *sendlen = len; + + return NT_STATUS_OK; +} + +static NTSTATUS unixdom_set_option(struct socket_context *sock, + const char *option, const char *val) +{ + set_socket_options(sock->fd, option); + return NT_STATUS_OK; +} + +static char *unixdom_get_peer_name(struct socket_context *sock, TALLOC_CTX *mem_ctx) +{ + return talloc_strdup(mem_ctx, "LOCAL/unixdom"); +} + +static char *unixdom_get_peer_addr(struct socket_context *sock, TALLOC_CTX *mem_ctx) +{ + return talloc_strdup(mem_ctx, "LOCAL/unixdom"); +} + +static int unixdom_get_peer_port(struct socket_context *sock) +{ + return 0; +} + +static char *unixdom_get_my_addr(struct socket_context *sock, TALLOC_CTX *mem_ctx) +{ + return talloc_strdup(mem_ctx, "LOCAL/unixdom"); +} + +static int unixdom_get_my_port(struct socket_context *sock) +{ + return 0; +} + +static int unixdom_get_fd(struct socket_context *sock) +{ + return sock->fd; +} + +static const struct socket_ops unixdom_ops = { + .name = "unix", + .type = SOCKET_TYPE_STREAM, + + .init = unixdom_init, + .connect = unixdom_connect, + .listen = unixdom_listen, + .accept = unixdom_accept, + .recv = unixdom_recv, + .send = unixdom_send, + .close = unixdom_close, + + .set_option = unixdom_set_option, + + .get_peer_name = unixdom_get_peer_name, + .get_peer_addr = unixdom_get_peer_addr, + .get_peer_port = unixdom_get_peer_port, + .get_my_addr = unixdom_get_my_addr, + .get_my_port = unixdom_get_my_port, + + .get_fd = unixdom_get_fd +}; + +const struct socket_ops *socket_unixdom_ops(void) +{ + return &unixdom_ops; +} + +NTSTATUS socket_unixdom_init(void) +{ + return NT_STATUS_OK; +} -- cgit