diff options
Diffstat (limited to 'lib/tsocket/tsocket.c')
-rw-r--r-- | lib/tsocket/tsocket.c | 812 |
1 files changed, 812 insertions, 0 deletions
diff --git a/lib/tsocket/tsocket.c b/lib/tsocket/tsocket.c new file mode 100644 index 0000000..674858d --- /dev/null +++ b/lib/tsocket/tsocket.c @@ -0,0 +1,812 @@ +/* + 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 <http://www.gnu.org/licenses/>. +*/ + +#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; +} + |