--- /dev/null
+/*
+ Unix SMB/CIFS implementation.
+
+ Copyright (C) Stefan Metzmacher 2009
+
+ 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 "system/network.h"
+#include "../util/tevent_unix.h"
+#include "../lib/tsocket/tsocket.h"
+#include "../lib/tsocket/tsocket_internal.h"
+#include "../librpc/gen_ndr/ndr_named_pipe_auth.h"
+#include "libcli/named_pipe_auth/npa_tsocket.h"
+
+static const struct tsocket_context_ops tsocket_context_npa_ops;
+static const struct tsocket_address_ops tsocket_address_npa_ops;
+
+struct tsocket_context_npa {
+ enum {
+ TSOCKET_CONTEXT_NPA_CLIENT = 1
+ } type;
+ struct tsocket_address *laddr;
+ struct tsocket_address *raddr;
+
+ int status;
+ struct tsocket_context *usock;
+
+ struct tevent_immediate *trigger;
+
+ bool writeable;
+ struct {
+ uint8_t buf[4096];
+ off_t ofs;
+ size_t remain;
+ struct iovec iov;
+ } write;
+
+ bool readable;
+ struct {
+ bool eof;
+ uint8_t buf[4096];
+ off_t ofs;
+ size_t remain;
+ } read;
+};
+
+struct tsocket_address_npa {
+ enum {
+ TSOCKET_ADDRESS_NPA_CLI_LOCAL = 1,
+ TSOCKET_ADDRESS_NPA_CLI_REMOTE
+ } type;
+ union {
+ struct {
+ const char *directory;
+ const char *npipe;
+ } cli_remote;
+ } u;
+};
+
+int _tsocket_address_npa_client_local(TALLOC_CTX *mem_ctx,
+ struct tsocket_address **_addr,
+ const char *location)
+{
+ struct tsocket_address *addr;
+ struct tsocket_address_npa *npaa;
+
+ addr = tsocket_address_create(mem_ctx,
+ &tsocket_address_npa_ops,
+ &npaa,
+ struct tsocket_address_npa,
+ location);
+ if (!addr) {
+ errno = ENOMEM;
+ return -1;
+ }
+
+ ZERO_STRUCTP(npaa);
+ npaa->type = TSOCKET_ADDRESS_NPA_CLI_LOCAL;
+
+ *_addr = addr;
+ return 0;
+}
+
+int _tsocket_address_npa_client_remote(TALLOC_CTX *mem_ctx,
+ const char *directory,
+ const char *npipe,
+ struct tsocket_address **_addr,
+ const char *location)
+{
+ struct tsocket_address *addr;
+ struct tsocket_address_npa *npaa;
+
+ if (!directory || directory[0] == '\0') {
+ errno = ENOTDIR;
+ return -1;
+ }
+
+ if (!npipe || npipe[0] == '\0') {
+ errno = EINVAL;
+ return -1;
+ }
+
+ addr = tsocket_address_create(mem_ctx,
+ &tsocket_address_npa_ops,
+ &npaa,
+ struct tsocket_address_npa,
+ location);
+ if (!addr) {
+ goto nomem;
+ }
+
+ ZERO_STRUCTP(npaa);
+
+ npaa->type = TSOCKET_ADDRESS_NPA_CLI_REMOTE;
+ npaa->u.cli_remote.directory = talloc_strdup(npaa, directory);
+ if (!npaa->u.cli_remote.directory) {
+ goto nomem;
+ }
+ npaa->u.cli_remote.npipe = talloc_strdup(npaa, npipe);
+ if (!npaa->u.cli_remote.npipe) {
+ goto nomem;
+ }
+
+ *_addr = addr;
+ return 0;
+nomem:
+ errno = ENOMEM;
+ return -1;
+}
+
+static char *tsocket_address_npa_string(const struct tsocket_address *addr,
+ TALLOC_CTX *mem_ctx)
+{
+ struct tsocket_address_npa *npaa = talloc_get_type(addr->private_data,
+ struct tsocket_address_npa);
+
+ switch (npaa->type) {
+ case TSOCKET_ADDRESS_NPA_CLI_LOCAL:
+ return talloc_strdup(mem_ctx, "npa:client:local");
+ case TSOCKET_ADDRESS_NPA_CLI_REMOTE:
+ return talloc_asprintf(mem_ctx,
+ "npa:client:remote[%s]",
+ npaa->u.cli_remote.npipe);
+ }
+
+ return NULL;
+}
+
+static struct tsocket_address *tsocket_address_npa_copy(
+ const struct tsocket_address *addr,
+ TALLOC_CTX *mem_ctx,
+ const char *location)
+{
+ struct tsocket_address_npa *npaa = talloc_get_type(addr->private_data,
+ struct tsocket_address_npa);
+ struct tsocket_address *copy;
+ int ret = -1;
+
+ switch (npaa->type) {
+ case TSOCKET_ADDRESS_NPA_CLI_LOCAL:
+ ret = _tsocket_address_npa_client_local(mem_ctx,
+ ©,
+ location);
+ break;
+ case TSOCKET_ADDRESS_NPA_CLI_REMOTE:
+ ret = _tsocket_address_npa_client_remote(mem_ctx,
+ npaa->u.cli_remote.directory,
+ npaa->u.cli_remote.npipe,
+ ©,
+ location);
+ break;
+ default:
+ errno = EINVAL;
+ ret = -1;
+ }
+ if (ret != 0) {
+ return NULL;
+ }
+
+ return copy;
+}
+
+static int tsocket_address_npa_create_socket(const struct tsocket_address *addr,
+ enum tsocket_type type,
+ TALLOC_CTX *mem_ctx,
+ struct tsocket_context **_sock,
+ const char *location)
+{
+ struct tsocket_address_npa *npaa = talloc_get_type(addr->private_data,
+ struct tsocket_address_npa);
+ struct tsocket_context *sock = NULL;
+ struct tsocket_context_npa *npas;
+ struct tsocket_address *unix_addr;
+ int ret;
+ int tmp_errno;
+
+ if (npaa->type != TSOCKET_ADDRESS_NPA_CLI_LOCAL) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ /*
+ * the client can only ask for STREAM,
+ * while the server can also choose MESSAGE.
+ */
+ switch (type) {
+ case TSOCKET_TYPE_STREAM:
+ break;
+ default:
+ errno = EPROTONOSUPPORT;
+ return -1;
+ }
+
+ sock = tsocket_context_create(mem_ctx,
+ &tsocket_context_npa_ops,
+ &npas,
+ struct tsocket_context_npa,
+ location);
+ if (!sock) {
+ goto nomem;
+ }
+
+ npas->writeable = false;
+ ZERO_STRUCT(npas->write);
+ npas->readable = false;
+ ZERO_STRUCT(npas->read);
+
+ npas->trigger = tevent_create_immediate(npas);
+ if (!npas->trigger) {
+ goto nomem;
+ }
+
+ npas->laddr = tsocket_address_copy(addr, npas);
+ if (!npas->laddr) {
+ goto nomem;
+ }
+ npas->raddr = NULL;
+ npas->status = ENOTCONN;
+
+ ret = tsocket_address_unix_from_path(npas, "", &unix_addr);
+ if (ret != 0) {
+ goto failed;
+ }
+
+ ret = tsocket_address_create_socket(unix_addr,
+ TSOCKET_TYPE_STREAM,
+ npas, &npas->usock);
+ if (ret != 0) {
+ goto failed;
+ }
+ talloc_free(unix_addr);
+
+ *_sock = sock;
+ return 0;
+nomem:
+ errno = ENOMEM;
+failed:
+ tmp_errno = errno;
+ talloc_free(sock);
+ errno = tmp_errno;
+ return -1;
+}
+
+static const struct tsocket_address_ops tsocket_address_npa_ops = {
+ .name = "npa",
+ .string = tsocket_address_npa_string,
+ .copy = tsocket_address_npa_copy,
+ .create_socket = tsocket_address_npa_create_socket
+};
+
+static int tsocket_context_npa_set_event_context(struct tsocket_context *sock,
+ struct tevent_context *ev)
+{
+ struct tsocket_context_npa *npas = talloc_get_type(sock->private_data,
+ struct tsocket_context_npa);
+
+ tevent_schedule_immediate(npas->trigger, NULL, NULL, NULL);
+ ZERO_STRUCT(sock->event);
+
+ if (!ev) {
+ return 0;
+ }
+
+ sock->event.ctx = ev;
+
+ return 0;
+}
+
+static void tsocket_context_npa_schedule_event(struct tsocket_context *sock);
+
+static void tsocket_context_npa_trigger_event(struct tevent_context *ev,
+ struct tevent_immediate *im,
+ void *private_data)
+{
+ struct tsocket_context *sock = talloc_get_type(private_data,
+ struct tsocket_context);
+ struct tsocket_context_npa *npas = talloc_get_type(sock->private_data,
+ struct tsocket_context_npa);
+
+ if (npas->writeable && sock->event.write_handler) {
+ sock->event.write_handler(sock, sock->event.write_private);
+ goto done;
+ }
+
+ if (npas->readable && sock->event.read_handler) {
+ sock->event.read_handler(sock, sock->event.read_private);
+ goto done;
+ }
+
+done:
+ /* maybe reschedule */
+ tsocket_context_npa_schedule_event(sock);
+}
+
+static void tsocket_context_npa_schedule_event(struct tsocket_context *sock)
+{
+ struct tsocket_context_npa *npas = talloc_get_type(sock->private_data,
+ struct tsocket_context_npa);
+ bool trigger = false;
+
+ if (npas->readable && sock->event.read_handler) {
+ trigger = true;
+ }
+
+ if (npas->writeable && sock->event.write_handler) {
+ trigger = true;
+ }
+
+ if (trigger) {
+ tevent_schedule_immediate(npas->trigger, sock->event.ctx,
+ tsocket_context_npa_trigger_event,
+ sock);
+ }
+}
+
+static int tsocket_context_npa_set_read_handler(struct tsocket_context *sock,
+ tsocket_event_handler_t handler,
+ void *private_data)
+{
+ sock->event.read_handler = handler;
+ sock->event.read_private = private_data;
+
+ tsocket_context_npa_schedule_event(sock);
+ return 0;
+}
+
+static int tsocket_context_npa_set_write_handler(struct tsocket_context *sock,
+ tsocket_event_handler_t handler,
+ void *private_data)
+{
+ sock->event.write_handler = handler;
+ sock->event.write_private = private_data;
+
+ tsocket_context_npa_schedule_event(sock);
+ return 0;
+}
+
+static struct tevent_req *tsocket_npa_connect_send(struct tsocket_context *sock,
+ const struct tsocket_address *remote);
+static void tsocket_connect_npa_done(struct tevent_req *subreq);
+
+static int tsocket_context_npa_connect_to(struct tsocket_context *sock,
+ const struct tsocket_address *remote)
+{
+ struct tsocket_context_npa *npas = talloc_get_type(sock->private_data,
+ struct tsocket_context_npa);
+ struct tsocket_address_npa *npaa = talloc_get_type(remote->private_data,
+ struct tsocket_address_npa);
+ struct tevent_req *req;
+
+ if (!npaa) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ if (npaa->type != TSOCKET_ADDRESS_NPA_CLI_REMOTE) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ if (npas->type != TSOCKET_CONTEXT_NPA_CLIENT) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ if (npas->status == EINPROGRESS) {
+ errno = EALREADY;
+ return -1;
+ }
+
+ if (npas->status != ENOTCONN) {
+ errno = npas->status;
+ return -1;
+ }
+
+ req = tsocket_npa_connect_send(sock, remote);
+ if (!req) {
+ return -1;
+ }
+ tevent_req_set_callback(req, tsocket_connect_npa_done, sock);
+
+ /* the connect is on its way */
+ npas->status = EINPROGRESS;
+ errno = EINPROGRESS;
+ return -1;
+}
+
+struct tsocket_npa_connect_state {
+ struct tsocket_context *sock;
+ const char *unix_path;
+ struct tsocket_address *unix_addr;
+ struct tsocket_address *raddr;
+
+ struct named_pipe_auth_req auth_req;
+ DATA_BLOB auth_req_blob;
+ struct iovec auth_req_iov;
+
+ struct named_pipe_auth_rep auth_rep;
+ uint8_t auth_rep_buf[20];
+ uint32_t auth_rep_remain;
+ DATA_BLOB auth_rep_blob;
+};
+
+static void tsocket_npa_connect_unix_done(struct tevent_req *subreq);
+
+static struct tevent_req *tsocket_npa_connect_send(struct tsocket_context *sock,
+ const struct tsocket_address *remote)
+{
+ struct tsocket_context_npa *npas = talloc_get_type(sock->private_data,
+ struct tsocket_context_npa);
+ struct tsocket_address_npa *npaa = talloc_get_type(remote->private_data,
+ struct tsocket_address_npa);
+ struct tevent_req *req, *subreq;
+ struct tsocket_npa_connect_state *state;
+ int ret;
+ enum ndr_err_code ndr_err;
+
+ req = tevent_req_create(npas, &state, struct tsocket_npa_connect_state);
+ if (!req) {
+ return NULL;
+ }
+
+ state->unix_path = talloc_asprintf(state, "%s/%s",
+ npaa->u.cli_remote.directory,
+ npaa->u.cli_remote.npipe);
+ if (tevent_req_nomem(state->unix_path, req)) {
+ goto post;
+ }
+
+ ret = tsocket_address_unix_from_path(state,
+ state->unix_path,
+ &state->unix_addr);
+ if (ret != 0) {
+ tevent_req_error(req, errno);
+ goto post;
+ }
+
+ state->raddr = tsocket_address_copy(remote, state);
+ if (tevent_req_nomem(state->raddr, req)) {
+ goto post;
+ }
+
+ /* we send no Info3 yet */
+ state->auth_req.level = 0;
+
+ ndr_err = ndr_push_struct_blob(&state->auth_req_blob,
+ state, NULL, &state->auth_req,
+ (ndr_push_flags_fn_t)ndr_push_named_pipe_auth_req);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ tevent_req_error(req, EINVAL);
+ goto post;
+ }
+
+ state->auth_req_iov.iov_base = state->auth_req_blob.data;
+ state->auth_req_iov.iov_len = state->auth_req_blob.length;
+
+ subreq = tsocket_connect_send(npas->usock, state, state->unix_addr);
+ if (tevent_req_nomem(subreq, req)) {
+ goto post;
+ }
+ tevent_req_set_callback(subreq, tsocket_npa_connect_unix_done, state);
+
+ return req;
+post:
+ return tevent_req_post(req, sock->event.ctx);
+}
+
+static void tsocket_npa_connect_writev_done(struct tevent_req *subreq);
+
+static void tsocket_npa_connect_unix_done(struct tevent_req *subreq)
+{
+ struct tevent_req *req =
+ tevent_req_callback_data(subreq,
+ struct tevent_req);
+ struct tsocket_npa_connect_state *state =
+ tevent_req_data(req,
+ struct tsocket_npa_connect_state);
+ struct tsocket_context_npa *npas =
+ talloc_get_type(state->sock->private_data,
+ struct tsocket_context_npa);
+ int ret;
+ int sys_errno;
+
+ ret = tsocket_connect_recv(subreq, &sys_errno);
+ talloc_free(subreq);
+ if (ret != 0) {
+ tevent_req_error(req, sys_errno);
+ return;
+ }
+
+ subreq = tsocket_writev_send(npas->usock, state,
+ &state->auth_req_iov, 1);
+ if (tevent_req_nomem(subreq, req)) {
+ return;
+ }
+ tevent_req_set_callback(subreq, tsocket_npa_connect_writev_done, state);
+}
+
+static int tsocket_npa_connect_readv_callback(struct tsocket_context *sock,
+ void *private_data,
+ TALLOC_CTX *mem_ctx,
+ struct iovec **_vector,
+ size_t *_count)
+{
+ struct tevent_req *req =
+ talloc_get_type(private_data,
+ struct tevent_req);
+ struct tsocket_npa_connect_state *state =
+ tevent_req_data(req,
+ struct tsocket_npa_connect_state);
+ struct iovec *vector;
+ size_t count = 1;
+
+ if (state->auth_rep_remain == 0) {
+ *_vector = NULL;
+ *_count = 0;
+ return 0;
+ }
+
+ vector = talloc_array(mem_ctx, struct iovec, count);
+ if (!vector) {
+ return -1;
+ }
+
+ vector[0].iov_base = state->auth_rep_buf;
+ vector[0].iov_len = sizeof(state->auth_rep_buf);
+
+ state->auth_rep_remain = 0;
+
+ *_vector = vector;
+ *_count = count;
+ return 0;
+}
+
+static void tsocket_npa_connect_readv_done(struct tevent_req *subreq);
+
+static void tsocket_npa_connect_writev_done(struct tevent_req *subreq)
+{
+ struct tevent_req *req =
+ tevent_req_callback_data(subreq,
+ struct tevent_req);
+ struct tsocket_npa_connect_state *state =
+ tevent_req_data(req,
+ struct tsocket_npa_connect_state);
+ struct tsocket_context_npa *npas =
+ talloc_get_type(state->sock->private_data,
+ struct tsocket_context_npa);
+ int ret;
+ int sys_errno;
+
+ ret = tsocket_writev_recv(subreq, &sys_errno);
+ talloc_free(subreq);
+ if (ret == -1) {
+ tevent_req_error(req, sys_errno);
+ return;
+ }
+
+ state->auth_rep_remain = sizeof(state->auth_rep_buf);
+ subreq = tsocket_readv_send(npas->usock, state,
+ tsocket_npa_connect_readv_callback,
+ state);
+ if (tevent_req_nomem(subreq, req)) {
+ return;
+ }
+ tevent_req_set_callback(subreq, tsocket_npa_connect_readv_done, state);
+}
+
+static void tsocket_npa_connect_readv_done(struct tevent_req *subreq)
+{
+ struct tevent_req *req =
+ tevent_req_callback_data(subreq,
+ struct tevent_req);
+ struct tsocket_npa_connect_state *state =
+ tevent_req_data(req,
+ struct tsocket_npa_connect_state);
+ int ret;
+ int sys_errno;
+ enum ndr_err_code ndr_err;
+
+ ret = tsocket_readv_recv(subreq, &sys_errno);
+ talloc_free(subreq);
+ if (ret == -1) {
+ tevent_req_error(req, sys_errno);
+ return;
+ }
+
+ state->auth_rep_blob = data_blob_const(state->auth_rep_buf,
+ sizeof(state->auth_rep_buf));
+
+ DEBUG(10,("name_pipe_auth_rep(client)[%u]\n",
+ (uint32_t)state->auth_rep_blob.length));
+ dump_data(10, state->auth_rep_blob.data, state->auth_rep_blob.length);
+
+ ndr_err = ndr_pull_struct_blob(
+ &state->auth_rep_blob, state, NULL, &state->auth_rep,
+ (ndr_pull_flags_fn_t)ndr_pull_named_pipe_auth_rep);
+
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ DEBUG(0, ("ndr_pull_named_pipe_auth_rep failed: %s\n",
+ "ndr_error"));//ndr_errstr(ndr_err)));
+ tevent_req_error(req, EIO);
+ return;
+ }
+
+ if (state->auth_rep.length != 16) {
+ DEBUG(0, ("req invalid length: %u != 16\n",
+ state->auth_req.length));
+ tevent_req_error(req, EIO);
+ return;
+ }
+
+ if (strcmp(NAMED_PIPE_AUTH_MAGIC, state->auth_rep.magic) != 0) {
+ DEBUG(0, ("req invalid magic: %s != %s\n",
+ state->auth_rep.magic, NAMED_PIPE_AUTH_MAGIC));
+ tevent_req_error(req, EIO);
+ return;
+ }
+
+ if (!NT_STATUS_IS_OK(state->auth_rep.status)) {
+ DEBUG(0, ("req failed: %s\n",
+ nt_errstr(state->auth_rep.status)));
+ tevent_req_error(req, EACCES);
+ return;
+ }
+
+ if (state->auth_rep.level != 1) {
+ DEBUG(0, ("req invalid level: %u != 1\n",
+ state->auth_rep.level));
+ tevent_req_error(req, EIO);
+ return;
+ }
+
+ tevent_req_done(req);
+}
+
+static int tsocket_npa_connect_recv(struct tevent_req *req,
+ int *perrno,
+ TALLOC_CTX *mem_ctx,
+ struct tsocket_address **raddr)
+{
+ struct tsocket_npa_connect_state *state =
+ tevent_req_data(req,
+ struct tsocket_npa_connect_state);
+
+ if (tevent_req_is_unix_error(req, perrno)) {
+ tevent_req_received(req);
+ return -1;
+ }
+
+ *raddr = talloc_move(mem_ctx, &state->raddr);
+ tevent_req_received(req);
+ return 0;
+}
+
+static void tsocket_connect_npa_done(struct tevent_req *subreq)
+{
+ struct tsocket_context *sock =
+ tevent_req_callback_data(subreq,
+ struct tsocket_context);
+ struct tsocket_context_npa *npas =
+ talloc_get_type(sock->private_data,
+ struct tsocket_context_npa);
+ int ret;
+ int sys_errno;
+
+ ret = tsocket_npa_connect_recv(subreq, &sys_errno,
+ npas, &npas->raddr);
+ talloc_free(subreq);
+ if (ret != 0) {
+ npas->status = sys_errno;
+ npas->readable = true;
+ tsocket_context_npa_schedule_event(sock);
+ return;
+ }
+
+ npas->status = 0;
+ npas->writeable = true;
+ npas->readable = true;
+ tsocket_context_npa_schedule_event(sock);
+}
+
+static int tsocket_context_npa_listen_on(struct tsocket_context *sock,
+ int queue_size)
+{
+ errno = ENOSYS;
+ return -1;
+}
+
+static int tsocket_context_npa_accept_new(struct tsocket_context *sock,
+ TALLOC_CTX *mem_ctx,
+ struct tsocket_context **_new_sock,
+ const char *location)
+{
+ errno = ENOSYS;
+ return -1;
+}
+
+static ssize_t tsocket_context_npa_pending_data(struct tsocket_context *sock)
+{
+ struct tsocket_context_npa *npas = talloc_get_type(sock->private_data,
+ struct tsocket_context_npa);
+
+ if (npas->status != 0) {
+ errno = npas->status;
+ return -1;
+ }
+
+ return npas->read.remain;
+}
+
+static int tsocket_npa_readv_callback(struct tsocket_context *usock,
+ void *private_data,
+ TALLOC_CTX *mem_ctx,
+ struct iovec **_vector,
+ size_t *_count)
+{
+ struct tsocket_context *sock =
+ talloc_get_type(private_data,
+ struct tsocket_context);
+ struct tsocket_context_npa *npas = talloc_get_type(sock->private_data,
+ struct tsocket_context_npa);
+ struct iovec *vector;
+ size_t count = 1;
+ ssize_t pending;
+
+ errno = ENOSYS;
+ return -1;
+
+ if (npas->read.ofs == sizeof(npas->read.buf)) {
+ *_vector = NULL;
+ *_count = 0;
+ return 0;
+ }
+
+ vector = talloc_array(mem_ctx, struct iovec, count);
+ if (!vector) {
+ return -1;
+ }
+
+ pending = tsocket_pending(npas->usock);
+ if (pending < 0) {
+ return pending;
+ }
+//TODO: fix the logic...
+ npas->read.ofs = MAX(pending, 1);
+
+ vector[0].iov_base = npas->read.buf;
+ vector[0].iov_len = npas->read.ofs;
+
+ *_vector = vector;
+ *_count = count;
+ return 0;
+}
+
+static void tsocket_context_npa_readv_done(struct tevent_req *subreq);
+
+static int tsocket_context_npa_readv_data(struct tsocket_context *sock,
+ const struct iovec *vector,
+ size_t count)
+{
+ struct tsocket_context_npa *npas = talloc_get_type(sock->private_data,
+ struct tsocket_context_npa);
+ struct tevent_req *subreq;
+ size_t i;
+ int ret = 0;
+
+ /* return 0 as eof only once and then an error */
+ if (npas->read.eof) {
+ npas->read.eof = false;
+ return 0;
+ }
+
+ if (npas->status != 0) {
+ errno = npas->status;
+ return -1;
+ }
+
+ if (npas->read.remain == 0) {
+ errno = EAGAIN;
+ return -1;
+ }
+
+ for (i=0; i < count; i++) {
+ size_t len;
+
+ if (vector[i].iov_len == 0) {
+ continue;
+ }
+
+ len = MIN(vector[i].iov_len, npas->read.remain);
+
+ if (len == 0) {
+ break;
+ }
+
+ memcpy(vector[i].iov_base,
+ npas->read.buf + npas->read.ofs,
+ len);
+
+ ret += len;
+ npas->read.ofs += len;
+ npas->read.remain -= len;
+ }
+
+ /* memmove ...*/
+ if (npas->read.remain > 0) {
+ return ret;
+ }
+
+ npas->readable = false;
+
+ subreq = tsocket_readv_send(npas->usock, npas,
+ tsocket_npa_readv_callback,
+ sock);
+ if (!subreq) {
+ /* we report the error async */
+ tsocket_disconnect(npas->usock);
+ npas->status = ENOMEM;
+ npas->readable = true;
+ tsocket_context_npa_schedule_event(sock);
+
+ /* don't return an error here */
+ return ret;
+ }
+ tevent_req_set_callback(subreq, tsocket_context_npa_readv_done, sock);
+
+ return ret;
+}
+
+static void tsocket_context_npa_readv_done(struct tevent_req *subreq)
+{
+ struct tsocket_context *sock =
+ tevent_req_callback_data(subreq,
+ struct tsocket_context);
+ struct tsocket_context_npa *npas =
+ talloc_get_type(sock->private_data,
+ struct tsocket_context_npa);
+ int ret;
+ int sys_errno;
+
+ ret = tsocket_readv_recv(subreq, &sys_errno);
+ talloc_free(subreq);
+ if (ret == -1) {
+ npas->status = sys_errno;
+ npas->readable = true;
+ tsocket_context_npa_schedule_event(sock);
+ return;
+ }
+
+ npas->readable = true;
+ tsocket_context_npa_schedule_event(sock);
+
+}
+
+static void tsocket_context_npa_writev_done(struct tevent_req *subreq);
+
+static int tsocket_context_npa_writev_data(struct tsocket_context *sock,
+ const struct iovec *vector,
+ size_t count)
+{
+ struct tsocket_context_npa *npas = talloc_get_type(sock->private_data,
+ struct tsocket_context_npa);
+ struct tevent_req *subreq;
+ size_t i;
+ int ret = 0;
+
+ if (npas->status != 0) {
+ errno = npas->status;
+ return -1;
+ }
+
+ if (npas->write.ofs > 0) {
+ errno = EAGAIN;
+ return -1;
+ }
+
+ for (i=0; i < count; i++) {
+ size_t len;
+
+ if (vector[i].iov_len == 0) {
+ continue;
+ }
+
+ len = MIN(vector[i].iov_len, npas->write.remain);
+
+ if (len == 0) {
+ break;
+ }
+
+ memcpy(npas->write.buf + npas->write.ofs,
+ vector[i].iov_base,
+ len);
+
+ ret += len;
+ npas->write.ofs += len;
+ }
+
+ /* memmove ...*/
+ if (npas->read.ofs == 0) {
+ return 0;
+ }
+
+ npas->write.iov.iov_base = npas->write.buf;
+ npas->write.iov.iov_len = npas->write.ofs;
+
+ subreq = tsocket_writev_send(npas->usock, npas,
+ &npas->write.iov, 1);
+ if (!subreq) {
+ npas->write.ofs = 0;
+ errno = ENOMEM;
+ return -1;
+ }
+ tevent_req_set_callback(subreq, tsocket_context_npa_writev_done, sock);
+
+ npas->writeable = false;
+ return ret;
+}
+
+static void tsocket_context_npa_writev_done(struct tevent_req *subreq)
+{
+ struct tsocket_context *sock =
+ tevent_req_callback_data(subreq,
+ struct tsocket_context);
+ struct tsocket_context_npa *npas =
+ talloc_get_type(sock->private_data,
+ struct tsocket_context_npa);
+ int ret;
+ int sys_errno;
+
+ ret = tsocket_writev_recv(subreq, &sys_errno);
+ talloc_free(subreq);
+ if (ret == -1) {
+ npas->status = sys_errno;
+ npas->readable = true;
+ tsocket_context_npa_schedule_event(sock);
+ return;
+ }
+
+ npas->write.ofs = 0;
+ npas->writeable = true;
+ tsocket_context_npa_schedule_event(sock);
+}
+
+static ssize_t tsocket_context_npa_recvfrom_data(struct tsocket_context *sock,
+ uint8_t *data, size_t len,
+ TALLOC_CTX *addr_ctx,
+ struct tsocket_address **remote)
+{
+ errno = ENOSYS;
+ return -1;
+}
+
+static ssize_t tsocket_context_npa_sendto_data(struct tsocket_context *sock,
+ const uint8_t *data, size_t len,
+ const struct tsocket_address *remote)
+{
+ errno = ENOSYS;
+ return -1;
+}
+
+static int tsocket_context_npa_get_status(const struct tsocket_context *sock)
+{
+ struct tsocket_context_npa *npas = talloc_get_type(sock->private_data,
+ struct tsocket_context_npa);
+ return npas->status;
+}
+
+static int tsocket_context_npa_get_local_address(const struct tsocket_context *sock,
+ TALLOC_CTX *mem_ctx,
+ struct tsocket_address **_addr,
+ const char *location)
+{
+ struct tsocket_context_npa *npas = talloc_get_type(sock->private_data,
+ struct tsocket_context_npa);
+ struct tsocket_address *addr;
+
+ addr = _tsocket_address_copy(npas->laddr, mem_ctx, location);
+ if (!addr) {
+ return -1;
+ }
+
+ *_addr = addr;
+ return 0;
+}
+
+static int tsocket_context_npa_get_remote_address(const struct tsocket_context *sock,
+ TALLOC_CTX *mem_ctx,
+ struct tsocket_address **_addr,
+ const char *location)
+{
+ struct tsocket_context_npa *npas = talloc_get_type(sock->private_data,
+ struct tsocket_context_npa);
+ struct tsocket_address *addr;
+
+ if (!npas->raddr) {
+ errno = ENOTCONN;
+ return -1;
+ }
+
+ addr = _tsocket_address_copy(npas->raddr, mem_ctx, location);
+ if (!addr) {
+ return -1;
+ }
+
+ *_addr = addr;
+ return 0;
+}
+
+static int tsocket_context_npa_get_option(const struct tsocket_context *sock,
+ const char *option,
+ TALLOC_CTX *mem_ctx,
+ char **_value)
+{
+ errno = ENOSYS;
+ return -1;
+}
+
+static int tsocket_context_npa_set_option(const struct tsocket_context *sock,
+ const char *option,
+ bool force,
+ const char *value)
+{
+ if (!force) {
+ return 0;
+ }
+
+ errno = ENOSYS;
+ return -1;
+}
+
+static void tsocket_context_npa_disconnect(struct tsocket_context *sock)
+{
+ struct tsocket_context_npa *npas = talloc_get_type(sock->private_data,
+ struct tsocket_context_npa);
+
+ tsocket_context_npa_set_event_context(sock, NULL);
+
+ if (npas->usock) {
+ tsocket_disconnect(npas->usock);
+ }
+}
+
+static const struct tsocket_context_ops tsocket_context_npa_ops = {
+ .name = "nap",
+
+ .set_event_context = tsocket_context_npa_set_event_context,
+ .set_read_handler = tsocket_context_npa_set_read_handler,
+ .set_write_handler = tsocket_context_npa_set_write_handler,
+
+ .connect_to = tsocket_context_npa_connect_to,
+ .listen_on = tsocket_context_npa_listen_on,
+ .accept_new = tsocket_context_npa_accept_new,
+
+ .pending_data = tsocket_context_npa_pending_data,
+ .readv_data = tsocket_context_npa_readv_data,
+ .writev_data = tsocket_context_npa_writev_data,
+ .recvfrom_data = tsocket_context_npa_recvfrom_data,
+ .sendto_data = tsocket_context_npa_sendto_data,
+
+ .get_status = tsocket_context_npa_get_status,
+ .get_local_address = tsocket_context_npa_get_local_address,
+ .get_remote_address = tsocket_context_npa_get_remote_address,
+
+ .get_option = tsocket_context_npa_get_option,
+ .set_option = tsocket_context_npa_set_option,
+
+ .disconnect = tsocket_context_npa_disconnect
+};
+