/* Unix SMB/CIFS implementation. Copyright (C) Stefan Metzmacher 2009 ** NOTE! The following LGPL license applies to the tsocket ** library. This does NOT imply that all of Samba is released ** under the LGPL This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. This library 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 Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, see . */ #include "replace.h" #include "system/filesys.h" #include "tsocket.h" #include "tsocket_internal.h" int tsocket_simple_int_recv(struct tevent_req *req, int *perrno) { enum tevent_req_state state; uint64_t error; if (!tevent_req_is_error(req, &state, &error)) { return 0; } switch (state) { case TEVENT_REQ_NO_MEMORY: *perrno = ENOMEM; return -1; case TEVENT_REQ_TIMED_OUT: *perrno = ETIMEDOUT; return -1; case TEVENT_REQ_USER_ERROR: *perrno = (int)error; return -1; default: break; } *perrno = EIO; return -1; } struct tsocket_address *_tsocket_address_create(TALLOC_CTX *mem_ctx, const struct tsocket_address_ops *ops, void *pstate, size_t psize, const char *type, const char *location) { void **ppstate = (void **)pstate; struct tsocket_address *addr; addr = talloc_zero(mem_ctx, struct tsocket_address); if (!addr) { return NULL; } addr->ops = ops; addr->location = location; addr->private_data = talloc_size(addr, psize); if (!addr->private_data) { talloc_free(addr); return NULL; } talloc_set_name_const(addr->private_data, type); *ppstate = addr->private_data; return addr; } char *tsocket_address_string(const struct tsocket_address *addr, TALLOC_CTX *mem_ctx) { if (!addr) { return talloc_strdup(mem_ctx, "NULL"); } return addr->ops->string(addr, mem_ctx); } struct tsocket_address *_tsocket_address_copy(const struct tsocket_address *addr, TALLOC_CTX *mem_ctx, const char *location) { return addr->ops->copy(addr, mem_ctx, location); } struct tdgram_context { const char *location; const struct tdgram_context_ops *ops; void *private_data; struct tevent_req *recvfrom_req; struct tevent_req *sendto_req; }; static int tdgram_context_destructor(struct tdgram_context *dgram) { if (dgram->recvfrom_req) { tevent_req_received(dgram->recvfrom_req); } if (dgram->sendto_req) { tevent_req_received(dgram->sendto_req); } return 0; } struct tdgram_context *_tdgram_context_create(TALLOC_CTX *mem_ctx, const struct tdgram_context_ops *ops, void *pstate, size_t psize, const char *type, const char *location) { struct tdgram_context *dgram; void **ppstate = (void **)pstate; void *state; dgram = talloc(mem_ctx, struct tdgram_context); if (dgram == NULL) { return NULL; } dgram->location = location; dgram->ops = ops; dgram->recvfrom_req = NULL; dgram->sendto_req = NULL; state = talloc_size(dgram, psize); if (state == NULL) { talloc_free(dgram); return NULL; } talloc_set_name_const(state, type); dgram->private_data = state; talloc_set_destructor(dgram, tdgram_context_destructor); *ppstate = state; return dgram; } void *_tdgram_context_data(struct tdgram_context *dgram) { return dgram->private_data; } struct tdgram_recvfrom_state { const struct tdgram_context_ops *ops; struct tdgram_context *dgram; uint8_t *buf; size_t len; struct tsocket_address *src; }; static int tdgram_recvfrom_destructor(struct tdgram_recvfrom_state *state) { if (state->dgram) { state->dgram->recvfrom_req = NULL; } return 0; } static void tdgram_recvfrom_done(struct tevent_req *subreq); struct tevent_req *tdgram_recvfrom_send(TALLOC_CTX *mem_ctx, struct tevent_context *ev, struct tdgram_context *dgram) { struct tevent_req *req; struct tdgram_recvfrom_state *state; struct tevent_req *subreq; req = tevent_req_create(mem_ctx, &state, struct tdgram_recvfrom_state); if (req == NULL) { return NULL; } state->ops = dgram->ops; state->dgram = dgram; state->buf = NULL; state->len = 0; state->src = NULL; if (dgram->recvfrom_req) { tevent_req_error(req, EBUSY); goto post; } dgram->recvfrom_req = req; talloc_set_destructor(state, tdgram_recvfrom_destructor); subreq = state->ops->recvfrom_send(state, ev, dgram); if (tevent_req_nomem(subreq, req)) { goto post; } tevent_req_set_callback(subreq, tdgram_recvfrom_done, req); return req; post: tevent_req_post(req, ev); return req; } static void tdgram_recvfrom_done(struct tevent_req *subreq) { struct tevent_req *req = tevent_req_callback_data(subreq, struct tevent_req); struct tdgram_recvfrom_state *state = tevent_req_data(req, struct tdgram_recvfrom_state); ssize_t ret; int sys_errno; ret = state->ops->recvfrom_recv(subreq, &sys_errno, state, &state->buf, &state->src); if (ret == -1) { tevent_req_error(req, sys_errno); return; } state->len = ret; tevent_req_done(req); } ssize_t tdgram_recvfrom_recv(struct tevent_req *req, int *perrno, TALLOC_CTX *mem_ctx, uint8_t **buf, struct tsocket_address **src) { struct tdgram_recvfrom_state *state = tevent_req_data(req, struct tdgram_recvfrom_state); ssize_t ret; ret = tsocket_simple_int_recv(req, perrno); if (ret == 0) { *buf = talloc_move(mem_ctx, &state->buf); ret = state->len; if (src) { *src = talloc_move(mem_ctx, &state->src); } } tevent_req_received(req); return ret; } struct tdgram_sendto_state { const struct tdgram_context_ops *ops; struct tdgram_context *dgram; ssize_t ret; }; static int tdgram_sendto_destructor(struct tdgram_sendto_state *state) { if (state->dgram) { state->dgram->sendto_req = NULL; } return 0; } static void tdgram_sendto_done(struct tevent_req *subreq); struct tevent_req *tdgram_sendto_send(TALLOC_CTX *mem_ctx, struct tevent_context *ev, struct tdgram_context *dgram, const uint8_t *buf, size_t len, const struct tsocket_address *dst) { struct tevent_req *req; struct tdgram_sendto_state *state; struct tevent_req *subreq; req = tevent_req_create(mem_ctx, &state, struct tdgram_sendto_state); if (req == NULL) { return NULL; } state->ops = dgram->ops; state->dgram = dgram; state->ret = -1; if (len == 0) { tevent_req_error(req, EINVAL); goto post; } if (dgram->sendto_req) { tevent_req_error(req, EBUSY); goto post; } dgram->sendto_req = req; talloc_set_destructor(state, tdgram_sendto_destructor); subreq = state->ops->sendto_send(state, ev, dgram, buf, len, dst); if (tevent_req_nomem(subreq, req)) { goto post; } tevent_req_set_callback(subreq, tdgram_sendto_done, req); return req; post: tevent_req_post(req, ev); return req; } static void tdgram_sendto_done(struct tevent_req *subreq) { struct tevent_req *req = tevent_req_callback_data(subreq, struct tevent_req); struct tdgram_sendto_state *state = tevent_req_data(req, struct tdgram_sendto_state); ssize_t ret; int sys_errno; ret = state->ops->sendto_recv(subreq, &sys_errno); if (ret == -1) { tevent_req_error(req, sys_errno); return; } state->ret = ret; tevent_req_done(req); } ssize_t tdgram_sendto_recv(struct tevent_req *req, int *perrno) { struct tdgram_sendto_state *state = tevent_req_data(req, struct tdgram_sendto_state); ssize_t ret; ret = tsocket_simple_int_recv(req, perrno); if (ret == 0) { ret = state->ret; } tevent_req_received(req); return ret; } struct tdgram_disconnect_state { const struct tdgram_context_ops *ops; }; static void tdgram_disconnect_done(struct tevent_req *subreq); struct tevent_req *tdgram_disconnect_send(TALLOC_CTX *mem_ctx, struct tevent_context *ev, struct tdgram_context *dgram) { struct tevent_req *req; struct tdgram_disconnect_state *state; struct tevent_req *subreq; req = tevent_req_create(mem_ctx, &state, struct tdgram_disconnect_state); if (req == NULL) { return NULL; } state->ops = dgram->ops; if (dgram->recvfrom_req || dgram->sendto_req) { tevent_req_error(req, EBUSY); goto post; } subreq = state->ops->disconnect_send(state, ev, dgram); if (tevent_req_nomem(subreq, req)) { goto post; } tevent_req_set_callback(subreq, tdgram_disconnect_done, req); return req; post: tevent_req_post(req, ev); return req; } static void tdgram_disconnect_done(struct tevent_req *subreq) { struct tevent_req *req = tevent_req_callback_data(subreq, struct tevent_req); struct tdgram_disconnect_state *state = tevent_req_data(req, struct tdgram_disconnect_state); int ret; int sys_errno; ret = state->ops->disconnect_recv(subreq, &sys_errno); if (ret == -1) { tevent_req_error(req, sys_errno); return; } tevent_req_done(req); } int tdgram_disconnect_recv(struct tevent_req *req, int *perrno) { int ret; ret = tsocket_simple_int_recv(req, perrno); tevent_req_received(req); return ret; } struct tstream_context { const char *location; const struct tstream_context_ops *ops; void *private_data; struct tevent_req *readv_req; struct tevent_req *writev_req; }; static int tstream_context_destructor(struct tstream_context *stream) { if (stream->readv_req) { tevent_req_received(stream->readv_req); } if (stream->writev_req) { tevent_req_received(stream->writev_req); } return 0; } struct tstream_context *_tstream_context_create(TALLOC_CTX *mem_ctx, const struct tstream_context_ops *ops, void *pstate, size_t psize, const char *type, const char *location) { struct tstream_context *stream; void **ppstate = (void **)pstate; void *state; stream = talloc(mem_ctx, struct tstream_context); if (stream == NULL) { return NULL; } stream->location = location; stream->ops = ops; stream->readv_req = NULL; stream->writev_req = NULL; state = talloc_size(stream, psize); if (state == NULL) { talloc_free(stream); return NULL; } talloc_set_name_const(state, type); stream->private_data = state; talloc_set_destructor(stream, tstream_context_destructor); *ppstate = state; return stream; } void *_tstream_context_data(struct tstream_context *stream) { return stream->private_data; } ssize_t tstream_pending_bytes(struct tstream_context *stream) { return stream->ops->pending_bytes(stream); } struct tstream_readv_state { const struct tstream_context_ops *ops; struct tstream_context *stream; int ret; }; static int tstream_readv_destructor(struct tstream_readv_state *state) { if (state->stream) { state->stream->readv_req = NULL; } return 0; } static void tstream_readv_done(struct tevent_req *subreq); struct tevent_req *tstream_readv_send(TALLOC_CTX *mem_ctx, struct tevent_context *ev, struct tstream_context *stream, struct iovec *vector, size_t count) { struct tevent_req *req; struct tstream_readv_state *state; struct tevent_req *subreq; int to_read = 0; size_t i; req = tevent_req_create(mem_ctx, &state, struct tstream_readv_state); if (req == NULL) { return NULL; } state->ops = stream->ops; state->stream = stream; state->ret = -1; /* first check if the input is ok */ #ifdef IOV_MAX if (count > IOV_MAX) { tevent_req_error(req, EMSGSIZE); goto post; } #endif for (i=0; i < count; i++) { int tmp = to_read; tmp += vector[i].iov_len; if (tmp < to_read) { tevent_req_error(req, EMSGSIZE); goto post; } to_read = tmp; } if (to_read == 0) { tevent_req_error(req, EINVAL); goto post; } if (stream->readv_req) { tevent_req_error(req, EBUSY); goto post; } stream->readv_req = req; talloc_set_destructor(state, tstream_readv_destructor); subreq = state->ops->readv_send(state, ev, stream, vector, count); if (tevent_req_nomem(subreq, req)) { goto post; } tevent_req_set_callback(subreq, tstream_readv_done, req); return req; post: tevent_req_post(req, ev); return req; } static void tstream_readv_done(struct tevent_req *subreq) { struct tevent_req *req = tevent_req_callback_data(subreq, struct tevent_req); struct tstream_readv_state *state = tevent_req_data(req, struct tstream_readv_state); ssize_t ret; int sys_errno; ret = state->ops->readv_recv(subreq, &sys_errno); TALLOC_FREE(subreq); if (ret == -1) { tevent_req_error(req, sys_errno); return; } state->ret = ret; tevent_req_done(req); } int tstream_readv_recv(struct tevent_req *req, int *perrno) { struct tstream_readv_state *state = tevent_req_data(req, struct tstream_readv_state); int ret; ret = tsocket_simple_int_recv(req, perrno); if (ret == 0) { ret = state->ret; } tevent_req_received(req); return ret; } struct tstream_writev_state { const struct tstream_context_ops *ops; struct tstream_context *stream; int ret; }; static int tstream_writev_destructor(struct tstream_writev_state *state) { if (state->stream) { state->stream->writev_req = NULL; } return 0; } static void tstream_writev_done(struct tevent_req *subreq); struct tevent_req *tstream_writev_send(TALLOC_CTX *mem_ctx, struct tevent_context *ev, struct tstream_context *stream, const struct iovec *vector, size_t count) { struct tevent_req *req; struct tstream_writev_state *state; struct tevent_req *subreq; int to_write = 0; size_t i; req = tevent_req_create(mem_ctx, &state, struct tstream_writev_state); if (req == NULL) { return NULL; } state->ops = stream->ops; state->stream = stream; state->ret = -1; /* first check if the input is ok */ #ifdef IOV_MAX if (count > IOV_MAX) { tevent_req_error(req, EMSGSIZE); goto post; } #endif for (i=0; i < count; i++) { int tmp = to_write; tmp += vector[i].iov_len; if (tmp < to_write) { tevent_req_error(req, EMSGSIZE); goto post; } to_write = tmp; } if (to_write == 0) { tevent_req_error(req, EINVAL); goto post; } if (stream->writev_req) { tevent_req_error(req, EBUSY); goto post; } stream->writev_req = req; talloc_set_destructor(state, tstream_writev_destructor); subreq = state->ops->writev_send(state, ev, stream, vector, count); if (tevent_req_nomem(subreq, req)) { goto post; } tevent_req_set_callback(subreq, tstream_writev_done, req); return req; post: tevent_req_post(req, ev); return req; } static void tstream_writev_done(struct tevent_req *subreq) { struct tevent_req *req = tevent_req_callback_data(subreq, struct tevent_req); struct tstream_writev_state *state = tevent_req_data(req, struct tstream_writev_state); ssize_t ret; int sys_errno; ret = state->ops->writev_recv(subreq, &sys_errno); if (ret == -1) { tevent_req_error(req, sys_errno); return; } state->ret = ret; tevent_req_done(req); } int tstream_writev_recv(struct tevent_req *req, int *perrno) { struct tstream_writev_state *state = tevent_req_data(req, struct tstream_writev_state); int ret; ret = tsocket_simple_int_recv(req, perrno); if (ret == 0) { ret = state->ret; } tevent_req_received(req); return ret; } struct tstream_disconnect_state { const struct tstream_context_ops *ops; }; static void tstream_disconnect_done(struct tevent_req *subreq); struct tevent_req *tstream_disconnect_send(TALLOC_CTX *mem_ctx, struct tevent_context *ev, struct tstream_context *stream) { struct tevent_req *req; struct tstream_disconnect_state *state; struct tevent_req *subreq; req = tevent_req_create(mem_ctx, &state, struct tstream_disconnect_state); if (req == NULL) { return NULL; } state->ops = stream->ops; if (stream->readv_req || stream->writev_req) { tevent_req_error(req, EBUSY); goto post; } subreq = state->ops->disconnect_send(state, ev, stream); if (tevent_req_nomem(subreq, req)) { goto post; } tevent_req_set_callback(subreq, tstream_disconnect_done, req); return req; post: tevent_req_post(req, ev); return req; } static void tstream_disconnect_done(struct tevent_req *subreq) { struct tevent_req *req = tevent_req_callback_data(subreq, struct tevent_req); struct tstream_disconnect_state *state = tevent_req_data(req, struct tstream_disconnect_state); int ret; int sys_errno; ret = state->ops->disconnect_recv(subreq, &sys_errno); if (ret == -1) { tevent_req_error(req, sys_errno); return; } tevent_req_done(req); } int tstream_disconnect_recv(struct tevent_req *req, int *perrno) { int ret; ret = tsocket_simple_int_recv(req, perrno); tevent_req_received(req); return ret; }