summaryrefslogtreecommitdiff
path: root/lib/async_req/async_sock.c
diff options
context:
space:
mode:
authorKai Blin <kai@samba.org>2009-01-24 10:00:13 +0100
committerKai Blin <kai@samba.org>2009-01-28 11:35:32 +0100
commitab63bad94bfc378aca08d3314692de90e1aa1bf3 (patch)
tree931be3a7c4b7c2c28c836fdea89b42b8464fe0f9 /lib/async_req/async_sock.c
parentc2a2006f8a8fdb8941158fc121ce551604cd1bae (diff)
downloadsamba-ab63bad94bfc378aca08d3314692de90e1aa1bf3.tar.gz
samba-ab63bad94bfc378aca08d3314692de90e1aa1bf3.tar.bz2
samba-ab63bad94bfc378aca08d3314692de90e1aa1bf3.zip
async_sock: Move to top level
Diffstat (limited to 'lib/async_req/async_sock.c')
-rw-r--r--lib/async_req/async_sock.c703
1 files changed, 703 insertions, 0 deletions
diff --git a/lib/async_req/async_sock.c b/lib/async_req/async_sock.c
new file mode 100644
index 0000000000..7febc54fd4
--- /dev/null
+++ b/lib/async_req/async_sock.c
@@ -0,0 +1,703 @@
+/*
+ Unix SMB/CIFS implementation.
+ async socket syscalls
+ Copyright (C) Volker Lendecke 2008
+
+ 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 3 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, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "lib/talloc/talloc.h"
+#include "lib/tevent/tevent.h"
+#include "lib/async_req/async_req.h"
+#include "lib/async_req/async_sock.h"
+#include <fcntl.h>
+
+#ifndef TALLOC_FREE
+#define TALLOC_FREE(ctx) do { talloc_free(ctx); ctx=NULL; } while(0)
+#endif
+
+/**
+ * Discriminator for async_syscall_state
+ */
+enum async_syscall_type {
+ ASYNC_SYSCALL_SEND,
+ ASYNC_SYSCALL_SENDALL,
+ ASYNC_SYSCALL_RECV,
+ ASYNC_SYSCALL_RECVALL,
+ ASYNC_SYSCALL_CONNECT
+};
+
+/**
+ * Holder for syscall arguments and the result
+ */
+
+struct async_syscall_state {
+ enum async_syscall_type syscall_type;
+ struct tevent_fd *fde;
+
+ union {
+ struct param_send {
+ int fd;
+ const void *buffer;
+ size_t length;
+ int flags;
+ } param_send;
+ struct param_sendall {
+ int fd;
+ const void *buffer;
+ size_t length;
+ int flags;
+ size_t sent;
+ } param_sendall;
+ struct param_recv {
+ int fd;
+ void *buffer;
+ size_t length;
+ int flags;
+ } param_recv;
+ struct param_recvall {
+ int fd;
+ void *buffer;
+ size_t length;
+ int flags;
+ size_t received;
+ } param_recvall;
+ struct param_connect {
+ /**
+ * connect needs to be done on a nonblocking
+ * socket. Keep the old flags around
+ */
+ long old_sockflags;
+ int fd;
+ const struct sockaddr *address;
+ socklen_t address_len;
+ } param_connect;
+ } param;
+
+ union {
+ ssize_t result_ssize_t;
+ size_t result_size_t;
+ int result_int;
+ } result;
+ int sys_errno;
+};
+
+/**
+ * @brief Create a new async syscall req
+ * @param[in] mem_ctx The memory context to hang the result off
+ * @param[in] ev The event context to work from
+ * @param[in] type Which syscall will this be
+ * @param[in] pstate Where to put the newly created private_data state
+ * @retval The new request
+ *
+ * This is a helper function to prepare a new struct async_req with an
+ * associated struct async_syscall_state. The async_syscall_state will be put
+ * into the async_req as private_data.
+ */
+
+static struct async_req *async_syscall_new(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ enum async_syscall_type type,
+ struct async_syscall_state **pstate)
+{
+ struct async_req *result;
+ struct async_syscall_state *state;
+
+ if (!async_req_setup(mem_ctx, &result, &state,
+ struct async_syscall_state)) {
+ return NULL;
+ }
+ state->syscall_type = type;
+
+ result->private_data = state;
+
+ *pstate = state;
+
+ return result;
+}
+
+/**
+ * @brief Create a new async syscall req based on a fd
+ * @param[in] mem_ctx The memory context to hang the result off
+ * @param[in] ev The event context to work from
+ * @param[in] type Which syscall will this be
+ * @param[in] fd The file descriptor we work on
+ * @param[in] fde_flags TEVENT_FD_READ/WRITE -- what are we interested in?
+ * @param[in] fde_cb The callback function for the file descriptor event
+ * @param[in] pstate Where to put the newly created private_data state
+ * @retval The new request
+ *
+ * This is a helper function to prepare a new struct async_req with an
+ * associated struct async_syscall_state and an associated file descriptor
+ * event.
+ */
+
+static struct async_req *async_fde_syscall_new(
+ TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ enum async_syscall_type type,
+ int fd,
+ uint16_t fde_flags,
+ void (*fde_cb)(struct tevent_context *ev,
+ struct tevent_fd *fde, uint16_t flags,
+ void *priv),
+ struct async_syscall_state **pstate)
+{
+ struct async_req *result;
+ struct async_syscall_state *state;
+
+ result = async_syscall_new(mem_ctx, ev, type, &state);
+ if (result == NULL) {
+ return NULL;
+ }
+
+ state->fde = tevent_add_fd(ev, state, fd, fde_flags, fde_cb, result);
+ if (state->fde == NULL) {
+ TALLOC_FREE(result);
+ return NULL;
+ }
+ *pstate = state;
+ return result;
+}
+
+/**
+ * Retrieve a ssize_t typed result from an async syscall
+ * @param[in] req The syscall that has just finished
+ * @param[out] perrno Where to put the syscall's errno
+ * @retval The return value from the asynchronously called syscall
+ */
+
+ssize_t async_syscall_result_ssize_t(struct async_req *req, int *perrno)
+{
+ struct async_syscall_state *state = talloc_get_type_abort(
+ req->private_data, struct async_syscall_state);
+
+ *perrno = state->sys_errno;
+ return state->result.result_ssize_t;
+}
+
+/**
+ * Retrieve a size_t typed result from an async syscall
+ * @param[in] req The syscall that has just finished
+ * @param[out] perrno Where to put the syscall's errno
+ * @retval The return value from the asynchronously called syscall
+ */
+
+size_t async_syscall_result_size_t(struct async_req *req, int *perrno)
+{
+ struct async_syscall_state *state = talloc_get_type_abort(
+ req->private_data, struct async_syscall_state);
+
+ *perrno = state->sys_errno;
+ return state->result.result_size_t;
+}
+
+/**
+ * Retrieve a int typed result from an async syscall
+ * @param[in] req The syscall that has just finished
+ * @param[out] perrno Where to put the syscall's errno
+ * @retval The return value from the asynchronously called syscall
+ */
+
+int async_syscall_result_int(struct async_req *req, int *perrno)
+{
+ struct async_syscall_state *state = talloc_get_type_abort(
+ req->private_data, struct async_syscall_state);
+
+ *perrno = state->sys_errno;
+ return state->result.result_int;
+}
+
+/**
+ * fde event handler for the "send" syscall
+ * @param[in] ev The event context that sent us here
+ * @param[in] fde The file descriptor event associated with the send
+ * @param[in] flags Can only be TEVENT_FD_WRITE here
+ * @param[in] priv private data, "struct async_req *" in this case
+ */
+
+static void async_send_callback(struct tevent_context *ev,
+ struct tevent_fd *fde, uint16_t flags,
+ void *priv)
+{
+ struct async_req *req = talloc_get_type_abort(
+ priv, struct async_req);
+ struct async_syscall_state *state = talloc_get_type_abort(
+ req->private_data, struct async_syscall_state);
+ struct param_send *p = &state->param.param_send;
+
+ if (state->syscall_type != ASYNC_SYSCALL_SEND) {
+ async_req_error(req, NT_STATUS_INTERNAL_ERROR);
+ return;
+ }
+
+ state->result.result_ssize_t = send(p->fd, p->buffer, p->length,
+ p->flags);
+ state->sys_errno = errno;
+
+ TALLOC_FREE(state->fde);
+
+ async_req_done(req);
+}
+
+/**
+ * Async version of send(2)
+ * @param[in] mem_ctx The memory context to hang the result off
+ * @param[in] ev The event context to work from
+ * @param[in] fd The socket to send to
+ * @param[in] buffer The buffer to send
+ * @param[in] length How many bytes to send
+ * @param[in] flags flags passed to send(2)
+ *
+ * This function is a direct counterpart of send(2)
+ */
+
+struct async_req *async_send(TALLOC_CTX *mem_ctx, struct tevent_context *ev,
+ int fd, const void *buffer, size_t length,
+ int flags)
+{
+ struct async_req *result;
+ struct async_syscall_state *state;
+
+ result = async_fde_syscall_new(
+ mem_ctx, ev, ASYNC_SYSCALL_SEND,
+ fd, TEVENT_FD_WRITE, async_send_callback,
+ &state);
+ if (result == NULL) {
+ return NULL;
+ }
+
+ state->param.param_send.fd = fd;
+ state->param.param_send.buffer = buffer;
+ state->param.param_send.length = length;
+ state->param.param_send.flags = flags;
+
+ return result;
+}
+
+/**
+ * fde event handler for the "sendall" syscall group
+ * @param[in] ev The event context that sent us here
+ * @param[in] fde The file descriptor event associated with the send
+ * @param[in] flags Can only be TEVENT_FD_WRITE here
+ * @param[in] priv private data, "struct async_req *" in this case
+ */
+
+static void async_sendall_callback(struct tevent_context *ev,
+ struct tevent_fd *fde, uint16_t flags,
+ void *priv)
+{
+ struct async_req *req = talloc_get_type_abort(
+ priv, struct async_req);
+ struct async_syscall_state *state = talloc_get_type_abort(
+ req->private_data, struct async_syscall_state);
+ struct param_sendall *p = &state->param.param_sendall;
+
+ if (state->syscall_type != ASYNC_SYSCALL_SENDALL) {
+ async_req_error(req, NT_STATUS_INTERNAL_ERROR);
+ return;
+ }
+
+ state->result.result_ssize_t = send(p->fd,
+ (const char *)p->buffer + p->sent,
+ p->length - p->sent, p->flags);
+ state->sys_errno = errno;
+
+ if (state->result.result_ssize_t == -1) {
+ async_req_error(req, map_nt_error_from_unix(state->sys_errno));
+ return;
+ }
+
+ if (state->result.result_ssize_t == 0) {
+ async_req_error(req, NT_STATUS_END_OF_FILE);
+ return;
+ }
+
+ p->sent += state->result.result_ssize_t;
+ if (p->sent > p->length) {
+ async_req_error(req, NT_STATUS_INTERNAL_ERROR);
+ return;
+ }
+
+ if (p->sent == p->length) {
+ TALLOC_FREE(state->fde);
+ async_req_done(req);
+ }
+}
+
+/**
+ * @brief Send all bytes to a socket
+ * @param[in] mem_ctx The memory context to hang the result off
+ * @param[in] ev The event context to work from
+ * @param[in] fd The socket to send to
+ * @param[in] buffer The buffer to send
+ * @param[in] length How many bytes to send
+ * @param[in] flags flags passed to send(2)
+ *
+ * async_sendall calls send(2) as long as it is necessary to send all of the
+ * "length" bytes
+ */
+
+struct async_req *sendall_send(TALLOC_CTX *mem_ctx, struct tevent_context *ev,
+ int fd, const void *buffer, size_t length,
+ int flags)
+{
+ struct async_req *result;
+ struct async_syscall_state *state;
+
+ result = async_fde_syscall_new(
+ mem_ctx, ev, ASYNC_SYSCALL_SENDALL,
+ fd, TEVENT_FD_WRITE, async_sendall_callback,
+ &state);
+ if (result == NULL) {
+ return NULL;
+ }
+
+ state->param.param_sendall.fd = fd;
+ state->param.param_sendall.buffer = buffer;
+ state->param.param_sendall.length = length;
+ state->param.param_sendall.flags = flags;
+ state->param.param_sendall.sent = 0;
+
+ return result;
+}
+
+NTSTATUS sendall_recv(struct async_req *req)
+{
+ return async_req_simple_recv(req);
+}
+
+/**
+ * fde event handler for the "recv" syscall
+ * @param[in] ev The event context that sent us here
+ * @param[in] fde The file descriptor event associated with the recv
+ * @param[in] flags Can only be TEVENT_FD_READ here
+ * @param[in] priv private data, "struct async_req *" in this case
+ */
+
+static void async_recv_callback(struct tevent_context *ev,
+ struct tevent_fd *fde, uint16_t flags,
+ void *priv)
+{
+ struct async_req *req = talloc_get_type_abort(
+ priv, struct async_req);
+ struct async_syscall_state *state = talloc_get_type_abort(
+ req->private_data, struct async_syscall_state);
+ struct param_recv *p = &state->param.param_recv;
+
+ if (state->syscall_type != ASYNC_SYSCALL_RECV) {
+ async_req_error(req, NT_STATUS_INTERNAL_ERROR);
+ return;
+ }
+
+ state->result.result_ssize_t = recv(p->fd, p->buffer, p->length,
+ p->flags);
+ state->sys_errno = errno;
+
+ TALLOC_FREE(state->fde);
+
+ async_req_done(req);
+}
+
+/**
+ * Async version of recv(2)
+ * @param[in] mem_ctx The memory context to hang the result off
+ * @param[in] ev The event context to work from
+ * @param[in] fd The socket to recv from
+ * @param[in] buffer The buffer to recv into
+ * @param[in] length How many bytes to recv
+ * @param[in] flags flags passed to recv(2)
+ *
+ * This function is a direct counterpart of recv(2)
+ */
+
+struct async_req *async_recv(TALLOC_CTX *mem_ctx, struct tevent_context *ev,
+ int fd, void *buffer, size_t length,
+ int flags)
+{
+ struct async_req *result;
+ struct async_syscall_state *state;
+
+ result = async_fde_syscall_new(
+ mem_ctx, ev, ASYNC_SYSCALL_RECV,
+ fd, TEVENT_FD_READ, async_recv_callback,
+ &state);
+
+ if (result == NULL) {
+ return NULL;
+ }
+
+ state->param.param_recv.fd = fd;
+ state->param.param_recv.buffer = buffer;
+ state->param.param_recv.length = length;
+ state->param.param_recv.flags = flags;
+
+ return result;
+}
+
+/**
+ * fde event handler for the "recvall" syscall group
+ * @param[in] ev The event context that sent us here
+ * @param[in] fde The file descriptor event associated with the recv
+ * @param[in] flags Can only be TEVENT_FD_READ here
+ * @param[in] priv private data, "struct async_req *" in this case
+ */
+
+static void async_recvall_callback(struct tevent_context *ev,
+ struct tevent_fd *fde, uint16_t flags,
+ void *priv)
+{
+ struct async_req *req = talloc_get_type_abort(
+ priv, struct async_req);
+ struct async_syscall_state *state = talloc_get_type_abort(
+ req->private_data, struct async_syscall_state);
+ struct param_recvall *p = &state->param.param_recvall;
+
+ if (state->syscall_type != ASYNC_SYSCALL_RECVALL) {
+ async_req_error(req, NT_STATUS_INTERNAL_ERROR);
+ return;
+ }
+
+ state->result.result_ssize_t = recv(p->fd,
+ (char *)p->buffer + p->received,
+ p->length - p->received, p->flags);
+ state->sys_errno = errno;
+
+ if (state->result.result_ssize_t == -1) {
+ async_req_error(req, map_nt_error_from_unix(state->sys_errno));
+ return;
+ }
+
+ if (state->result.result_ssize_t == 0) {
+ async_req_error(req, NT_STATUS_END_OF_FILE);
+ return;
+ }
+
+ p->received += state->result.result_ssize_t;
+ if (p->received > p->length) {
+ async_req_error(req, NT_STATUS_INTERNAL_ERROR);
+ return;
+ }
+
+ if (p->received == p->length) {
+ TALLOC_FREE(state->fde);
+ async_req_done(req);
+ }
+}
+
+/**
+ * Receive a specified number of bytes from a socket
+ * @param[in] mem_ctx The memory context to hang the result off
+ * @param[in] ev The event context to work from
+ * @param[in] fd The socket to recv from
+ * @param[in] buffer The buffer to recv into
+ * @param[in] length How many bytes to recv
+ * @param[in] flags flags passed to recv(2)
+ *
+ * async_recvall will call recv(2) until "length" bytes are received
+ */
+
+struct async_req *recvall_send(TALLOC_CTX *mem_ctx, struct tevent_context *ev,
+ int fd, void *buffer, size_t length,
+ int flags)
+{
+ struct async_req *result;
+ struct async_syscall_state *state;
+
+ result = async_fde_syscall_new(
+ mem_ctx, ev, ASYNC_SYSCALL_RECVALL,
+ fd, TEVENT_FD_READ, async_recvall_callback,
+ &state);
+ if (result == NULL) {
+ return NULL;
+ }
+
+ state->param.param_recvall.fd = fd;
+ state->param.param_recvall.buffer = buffer;
+ state->param.param_recvall.length = length;
+ state->param.param_recvall.flags = flags;
+ state->param.param_recvall.received = 0;
+
+ return result;
+}
+
+NTSTATUS recvall_recv(struct async_req *req)
+{
+ return async_req_simple_recv(req);
+}
+
+struct async_connect_state {
+ int fd;
+ int result;
+ int sys_errno;
+ long old_sockflags;
+};
+
+static void async_connect_connected(struct tevent_context *ev,
+ struct tevent_fd *fde, uint16_t flags,
+ void *priv);
+
+/**
+ * @brief async version of connect(2)
+ * @param[in] mem_ctx The memory context to hang the result off
+ * @param[in] ev The event context to work from
+ * @param[in] fd The socket to recv from
+ * @param[in] address Where to connect?
+ * @param[in] address_len Length of *address
+ * @retval The async request
+ *
+ * This function sets the socket into non-blocking state to be able to call
+ * connect in an async state. This will be reset when the request is finished.
+ */
+
+struct async_req *async_connect_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ int fd, const struct sockaddr *address,
+ socklen_t address_len)
+{
+ struct async_req *result;
+ struct async_connect_state *state;
+ struct tevent_fd *fde;
+ NTSTATUS status;
+
+ if (!async_req_setup(mem_ctx, &result, &state,
+ struct async_connect_state)) {
+ return NULL;
+ }
+
+ /**
+ * We have to set the socket to nonblocking for async connect(2). Keep
+ * the old sockflags around.
+ */
+
+ state->fd = fd;
+ state->sys_errno = 0;
+
+ state->old_sockflags = fcntl(fd, F_GETFL, 0);
+ if (state->old_sockflags == -1) {
+ goto post_errno;
+ }
+
+ set_blocking(fd, false);
+
+ state->result = connect(fd, address, address_len);
+ if (state->result == 0) {
+ state->sys_errno = 0;
+ status = NT_STATUS_OK;
+ goto post_status;
+ }
+
+ /**
+ * A number of error messages show that something good is progressing
+ * and that we have to wait for readability.
+ *
+ * If none of them are present, bail out.
+ */
+
+ if (!(errno == EINPROGRESS || errno == EALREADY ||
+#ifdef EISCONN
+ errno == EISCONN ||
+#endif
+ errno == EAGAIN || errno == EINTR)) {
+ goto post_errno;
+ }
+
+ fde = tevent_add_fd(ev, state, fd, TEVENT_FD_READ | TEVENT_FD_WRITE,
+ async_connect_connected, result);
+ if (fde == NULL) {
+ status = NT_STATUS_NO_MEMORY;
+ goto post_status;
+ }
+ return result;
+
+ post_errno:
+ state->sys_errno = errno;
+ status = map_nt_error_from_unix(state->sys_errno);
+ post_status:
+ fcntl(fd, F_SETFL, state->old_sockflags);
+ if (!async_post_status(result, ev, status)) {
+ goto fail;
+ }
+ return result;
+ fail:
+ TALLOC_FREE(result);
+ return NULL;
+}
+
+/**
+ * fde event handler for connect(2)
+ * @param[in] ev The event context that sent us here
+ * @param[in] fde The file descriptor event associated with the connect
+ * @param[in] flags Indicate read/writeability of the socket
+ * @param[in] priv private data, "struct async_req *" in this case
+ */
+
+static void async_connect_connected(struct tevent_context *ev,
+ struct tevent_fd *fde, uint16_t flags,
+ void *priv)
+{
+ struct async_req *req = talloc_get_type_abort(
+ priv, struct async_req);
+ struct async_connect_state *state = talloc_get_type_abort(
+ req->private_data, struct async_connect_state);
+
+ TALLOC_FREE(fde);
+
+ /*
+ * Stevens, Network Programming says that if there's a
+ * successful connect, the socket is only writable. Upon an
+ * error, it's both readable and writable.
+ */
+ if ((flags & (TEVENT_FD_READ|TEVENT_FD_WRITE))
+ == (TEVENT_FD_READ|TEVENT_FD_WRITE)) {
+ int sockerr;
+ socklen_t err_len = sizeof(sockerr);
+
+ if (getsockopt(state->fd, SOL_SOCKET, SO_ERROR,
+ (void *)&sockerr, &err_len) == 0) {
+ errno = sockerr;
+ }
+
+ state->sys_errno = errno;
+
+ DEBUG(10, ("connect returned %s\n", strerror(errno)));
+
+ fcntl(state->fd, F_SETFL, state->old_sockflags);
+ async_req_error(req, map_nt_error_from_unix(state->sys_errno));
+ return;
+ }
+
+ state->sys_errno = 0;
+ async_req_done(req);
+}
+
+NTSTATUS async_connect_recv(struct async_req *req, int *perrno)
+{
+ struct async_connect_state *state = talloc_get_type_abort(
+ req->private_data, struct async_connect_state);
+ NTSTATUS status;
+
+ fcntl(state->fd, F_SETFL, state->old_sockflags);
+
+ *perrno = state->sys_errno;
+
+ if (async_req_is_error(req, &status)) {
+ return status;
+ }
+ if (state->sys_errno == 0) {
+ return NT_STATUS_OK;
+ }
+ return map_nt_error_from_unix(state->sys_errno);
+}