summaryrefslogtreecommitdiff
path: root/source3/lib
diff options
context:
space:
mode:
authorJelmer Vernooij <jelmer@samba.org>2009-01-04 22:48:23 +0100
committerJelmer Vernooij <jelmer@samba.org>2009-01-04 22:48:23 +0100
commitaf744e0954bbe9ddfa2e3da173e79de65e640a4c (patch)
tree78b98c28e6d54c5339c12f2f942883d5088fda84 /source3/lib
parentce47b69d8e318bbb3642d27aa0451e2914c92be7 (diff)
parent2c0faaf5d921fe57a88d3b999067458e8774c6f6 (diff)
downloadsamba-af744e0954bbe9ddfa2e3da173e79de65e640a4c.tar.gz
samba-af744e0954bbe9ddfa2e3da173e79de65e640a4c.tar.bz2
samba-af744e0954bbe9ddfa2e3da173e79de65e640a4c.zip
Merge branch 'master' of ssh://git.samba.org/data/git/samba
Diffstat (limited to 'source3/lib')
-rw-r--r--source3/lib/async_req.c129
-rw-r--r--source3/lib/async_sock.c245
-rw-r--r--source3/lib/ctdbd_conn.c49
-rw-r--r--source3/lib/ldb/include/ldb.h2
-rw-r--r--source3/lib/ldb/tools/ldbadd.c13
-rw-r--r--source3/lib/ldb/tools/ldbmodify.c12
-rw-r--r--source3/lib/packet.c44
-rw-r--r--source3/lib/util_sock.c341
-rw-r--r--source3/lib/wb_reqtrans.c542
-rw-r--r--source3/lib/wbclient.c774
10 files changed, 1925 insertions, 226 deletions
diff --git a/source3/lib/async_req.c b/source3/lib/async_req.c
index 159666f15c..ac06df65a3 100644
--- a/source3/lib/async_req.c
+++ b/source3/lib/async_req.c
@@ -49,7 +49,7 @@ char *async_req_print(TALLOC_CTX *mem_ctx, struct async_req *req)
* The new async request will be initialized in state ASYNC_REQ_IN_PROGRESS
*/
-struct async_req *async_req_new(TALLOC_CTX *mem_ctx, struct event_context *ev)
+struct async_req *async_req_new(TALLOC_CTX *mem_ctx)
{
struct async_req *result;
@@ -58,7 +58,6 @@ struct async_req *async_req_new(TALLOC_CTX *mem_ctx, struct event_context *ev)
return NULL;
}
result->state = ASYNC_REQ_IN_PROGRESS;
- result->event_ctx = ev;
result->print = async_req_print;
return result;
}
@@ -135,12 +134,12 @@ static void async_trigger(struct event_context *ev, struct timed_event *te,
* conventions, independent of whether the request was actually deferred.
*/
-bool async_post_status(struct async_req *req, NTSTATUS status)
+bool async_post_status(struct async_req *req, struct event_context *ev,
+ NTSTATUS status)
{
req->status = status;
- if (event_add_timed(req->event_ctx, req, timeval_zero(),
- "async_trigger",
+ if (event_add_timed(ev, req, timeval_zero(), "async_trigger",
async_trigger, req) == NULL) {
return false;
}
@@ -195,3 +194,123 @@ NTSTATUS async_req_simple_recv(struct async_req *req)
}
return NT_STATUS_OK;
}
+
+static void async_req_timedout(struct event_context *ev,
+ struct timed_event *te,
+ const struct timeval *now,
+ void *priv)
+{
+ struct async_req *req = talloc_get_type_abort(
+ priv, struct async_req);
+ TALLOC_FREE(te);
+ async_req_error(req, NT_STATUS_IO_TIMEOUT);
+}
+
+bool async_req_set_timeout(struct async_req *req, struct event_context *ev,
+ struct timeval to)
+{
+ return (event_add_timed(ev, req,
+ timeval_current_ofs(to.tv_sec, to.tv_usec),
+ "async_req_timedout", async_req_timedout, req)
+ != NULL);
+}
+
+struct async_req *async_wait_send(TALLOC_CTX *mem_ctx,
+ struct event_context *ev,
+ struct timeval to)
+{
+ struct async_req *result;
+
+ result = async_req_new(mem_ctx);
+ if (result == NULL) {
+ return result;
+ }
+ if (!async_req_set_timeout(result, ev, to)) {
+ TALLOC_FREE(result);
+ return NULL;
+ }
+ return result;
+}
+
+NTSTATUS async_wait_recv(struct async_req *req)
+{
+ return NT_STATUS_OK;
+}
+
+struct async_queue_entry {
+ struct async_queue_entry *prev, *next;
+ struct async_req_queue *queue;
+ struct async_req *req;
+ void (*trigger)(struct async_req *req);
+};
+
+struct async_req_queue {
+ struct async_queue_entry *queue;
+};
+
+struct async_req_queue *async_req_queue_init(TALLOC_CTX *mem_ctx)
+{
+ return TALLOC_ZERO_P(mem_ctx, struct async_req_queue);
+}
+
+static int async_queue_entry_destructor(struct async_queue_entry *e)
+{
+ struct async_req_queue *queue = e->queue;
+
+ DLIST_REMOVE(queue->queue, e);
+
+ if (queue->queue != NULL) {
+ queue->queue->trigger(queue->queue->req);
+ }
+
+ return 0;
+}
+
+static void async_req_immediate_trigger(struct event_context *ev,
+ struct timed_event *te,
+ const struct timeval *now,
+ void *priv)
+{
+ struct async_queue_entry *e = talloc_get_type_abort(
+ priv, struct async_queue_entry);
+
+ TALLOC_FREE(te);
+ e->trigger(e->req);
+}
+
+bool async_req_enqueue(struct async_req_queue *queue, struct event_context *ev,
+ struct async_req *req,
+ void (*trigger)(struct async_req *req))
+{
+ struct async_queue_entry *e;
+ bool busy;
+
+ busy = (queue->queue != NULL);
+
+ e = talloc(req, struct async_queue_entry);
+ if (e == NULL) {
+ return false;
+ }
+
+ e->req = req;
+ e->trigger = trigger;
+ e->queue = queue;
+
+ DLIST_ADD_END(queue->queue, e, struct async_queue_entry *);
+ talloc_set_destructor(e, async_queue_entry_destructor);
+
+ if (!busy) {
+ struct timed_event *te;
+
+ te = event_add_timed(ev, e, timeval_zero(),
+ "async_req_immediate_trigger",
+ async_req_immediate_trigger,
+ e);
+ if (te == NULL) {
+ TALLOC_FREE(e);
+ return false;
+ }
+ }
+
+ return true;
+}
diff --git a/source3/lib/async_sock.c b/source3/lib/async_sock.c
index 225cc7b195..bb89a1353a 100644
--- a/source3/lib/async_sock.c
+++ b/source3/lib/async_sock.c
@@ -106,7 +106,7 @@ static struct async_req *async_syscall_new(TALLOC_CTX *mem_ctx,
struct async_req *result;
struct async_syscall_state *state;
- result = async_req_new(mem_ctx, ev);
+ result = async_req_new(mem_ctx);
if (result == NULL) {
return NULL;
}
@@ -209,7 +209,7 @@ size_t async_syscall_result_size_t(struct async_req *req, int *perrno)
* @retval The return value from the asynchronously called syscall
*/
-ssize_t async_syscall_result_int(struct async_req *req, int *perrno)
+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);
@@ -236,7 +236,10 @@ static void async_send_callback(struct event_context *ev,
req->private_data, struct async_syscall_state);
struct param_send *p = &state->param.param_send;
- SMB_ASSERT(state->syscall_type == ASYNC_SYSCALL_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);
@@ -300,7 +303,10 @@ static void async_sendall_callback(struct event_context *ev,
req->private_data, struct async_syscall_state);
struct param_sendall *p = &state->param.param_sendall;
- SMB_ASSERT(state->syscall_type == ASYNC_SYSCALL_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, (char *)p->buffer + p->sent,
p->length - p->sent, p->flags);
@@ -317,7 +323,10 @@ static void async_sendall_callback(struct event_context *ev,
}
p->sent += state->result.result_ssize_t;
- SMB_ASSERT(p->sent <= p->length);
+ if (p->sent > p->length) {
+ async_req_error(req, NT_STATUS_INTERNAL_ERROR);
+ return;
+ }
if (p->sent == p->length) {
TALLOC_FREE(state->fde);
@@ -385,7 +394,10 @@ static void async_recv_callback(struct event_context *ev,
req->private_data, struct async_syscall_state);
struct param_recv *p = &state->param.param_recv;
- SMB_ASSERT(state->syscall_type == ASYNC_SYSCALL_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);
@@ -450,7 +462,10 @@ static void async_recvall_callback(struct event_context *ev,
req->private_data, struct async_syscall_state);
struct param_recvall *p = &state->param.param_recvall;
- SMB_ASSERT(state->syscall_type == ASYNC_SYSCALL_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,
@@ -468,7 +483,10 @@ static void async_recvall_callback(struct event_context *ev,
}
p->received += state->result.result_ssize_t;
- SMB_ASSERT(p->received <= p->length);
+ if (p->received > p->length) {
+ async_req_error(req, NT_STATUS_INTERNAL_ERROR);
+ return;
+ }
if (p->received == p->length) {
TALLOC_FREE(state->fde);
@@ -517,60 +535,16 @@ NTSTATUS recvall_recv(struct async_req *req)
return async_req_simple_recv(req);
}
-/**
- * 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_callback(struct event_context *ev,
- struct fd_event *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_connect *p = &state->param.param_connect;
-
- SMB_ASSERT(state->syscall_type == ASYNC_SYSCALL_CONNECT);
-
- TALLOC_FREE(state->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 & (EVENT_FD_READ|EVENT_FD_WRITE))
- == (EVENT_FD_READ|EVENT_FD_WRITE)) {
- int sockerr;
- socklen_t err_len = sizeof(sockerr);
-
- if (getsockopt(p->fd, SOL_SOCKET, SO_ERROR,
- (void *)&sockerr, &err_len) == 0) {
- errno = sockerr;
- }
-
- state->sys_errno = errno;
-
- DEBUG(10, ("connect returned %s\n", strerror(errno)));
-
- sys_fcntl_long(p->fd, F_SETFL, p->old_sockflags);
-
- async_req_error(req, map_nt_error_from_unix(state->sys_errno));
- return;
- }
-
- sys_fcntl_long(p->fd, F_SETFL, p->old_sockflags);
-
- state->result.result_int = 0;
- state->sys_errno = 0;
+struct async_connect_state {
+ int fd;
+ int result;
+ int sys_errno;
+ long old_sockflags;
+};
- async_req_done(req);
-}
+static void async_connect_connected(struct event_context *ev,
+ struct fd_event *fde, uint16_t flags,
+ void *priv);
/**
* @brief async version of connect(2)
@@ -585,47 +559,46 @@ static void async_connect_callback(struct event_context *ev,
* connect in an async state. This will be reset when the request is finished.
*/
-struct async_req *async_connect(TALLOC_CTX *mem_ctx, struct event_context *ev,
- int fd, const struct sockaddr *address,
- socklen_t address_len)
+struct async_req *async_connect_send(TALLOC_CTX *mem_ctx,
+ struct event_context *ev,
+ int fd, const struct sockaddr *address,
+ socklen_t address_len)
{
struct async_req *result;
- struct async_syscall_state *state;
- struct param_connect *p;
+ struct async_connect_state *state;
+ struct fd_event *fde;
+ NTSTATUS status;
- result = async_syscall_new(mem_ctx, ev, ASYNC_SYSCALL_CONNECT, &state);
+ result = async_req_new(mem_ctx);
if (result == NULL) {
return NULL;
}
- p = &state->param.param_connect;
+ state = talloc(result, struct async_connect_state);
+ if (state == NULL) {
+ goto fail;
+ }
+ result->private_data = state;
/**
* We have to set the socket to nonblocking for async connect(2). Keep
* the old sockflags around.
*/
- p->old_sockflags = sys_fcntl_long(fd, F_GETFL, 0);
+ state->fd = fd;
+ state->sys_errno = 0;
- if (p->old_sockflags == -1) {
- if (async_post_status(result, map_nt_error_from_unix(errno))) {
- return result;
- }
- TALLOC_FREE(result);
- return NULL;
+ state->old_sockflags = sys_fcntl_long(fd, F_GETFL, 0);
+ if (state->old_sockflags == -1) {
+ goto post_errno;
}
- set_blocking(fd, true);
+ set_blocking(fd, false);
- state->result.result_int = connect(fd, address, address_len);
-
- if (state->result.result_int == 0) {
+ state->result = connect(fd, address, address_len);
+ if (state->result == 0) {
state->sys_errno = 0;
- if (async_post_status(result, NT_STATUS_OK)) {
- return result;
- }
- sys_fcntl_long(fd, F_SETFL, p->old_sockflags);
- TALLOC_FREE(result);
- return NULL;
+ status = NT_STATUS_OK;
+ goto post_status;
}
/**
@@ -640,31 +613,93 @@ struct async_req *async_connect(TALLOC_CTX *mem_ctx, struct event_context *ev,
errno == EISCONN ||
#endif
errno == EAGAIN || errno == EINTR)) {
+ goto post_errno;
+ }
- state->sys_errno = errno;
-
- if (async_post_status(result, map_nt_error_from_unix(errno))) {
- return result;
- }
- sys_fcntl_long(fd, F_SETFL, p->old_sockflags);
- TALLOC_FREE(result);
- return NULL;
+ fde = event_add_fd(ev, state, fd, EVENT_FD_READ | EVENT_FD_WRITE,
+ async_connect_connected, result);
+ if (fde == NULL) {
+ status = NT_STATUS_NO_MEMORY;
+ goto post_status;
}
+ return result;
- state->fde = event_add_fd(ev, state, fd,
- EVENT_FD_READ | EVENT_FD_WRITE,
- async_connect_callback, result);
- if (state->fde == NULL) {
- sys_fcntl_long(fd, F_SETFL, p->old_sockflags);
- TALLOC_FREE(result);
- return NULL;
+ post_errno:
+ state->sys_errno = errno;
+ status = map_nt_error_from_unix(state->sys_errno);
+ post_status:
+ sys_fcntl_long(fd, F_SETFL, state->old_sockflags);
+ if (!async_post_status(result, ev, status)) {
+ goto fail;
}
- result->private_data = state;
+ 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
+ */
- state->param.param_connect.fd = fd;
- state->param.param_connect.address = address;
- state->param.param_connect.address_len = address_len;
+static void async_connect_connected(struct event_context *ev,
+ struct fd_event *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);
- return result;
+ 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 & (EVENT_FD_READ|EVENT_FD_WRITE))
+ == (EVENT_FD_READ|EVENT_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)));
+
+ sys_fcntl_long(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;
+
+ sys_fcntl_long(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);
+}
diff --git a/source3/lib/ctdbd_conn.c b/source3/lib/ctdbd_conn.c
index 8d97606b85..75a513312e 100644
--- a/source3/lib/ctdbd_conn.c
+++ b/source3/lib/ctdbd_conn.c
@@ -155,17 +155,17 @@ static NTSTATUS ctdbd_connect(TALLOC_CTX *mem_ctx,
* Do we have a complete ctdb packet in the queue?
*/
-static bool ctdb_req_complete(const DATA_BLOB *data,
+static bool ctdb_req_complete(const uint8_t *buf, size_t available,
size_t *length,
void *private_data)
{
uint32 msglen;
- if (data->length < sizeof(msglen)) {
+ if (available < sizeof(msglen)) {
return False;
}
- msglen = *((uint32 *)data->data);
+ msglen = *((uint32 *)buf);
DEBUG(10, ("msglen = %d\n", msglen));
@@ -176,12 +176,12 @@ static bool ctdb_req_complete(const DATA_BLOB *data,
cluster_fatal("ctdbd protocol error\n");
}
- if (data->length >= msglen) {
- *length = msglen;
- return True;
+ if (available < msglen) {
+ return false;
}
- return False;
+ *length = msglen;
+ return true;
}
/*
@@ -220,16 +220,13 @@ struct req_pull_state {
* Pull a ctdb request out of the incoming packet queue
*/
-static NTSTATUS ctdb_req_pull(const DATA_BLOB *data,
+static NTSTATUS ctdb_req_pull(uint8_t *buf, size_t length,
void *private_data)
{
struct req_pull_state *state = (struct req_pull_state *)private_data;
- state->req = data_blob_talloc(state->mem_ctx, data->data,
- data->length);
- if (state->req.data == NULL) {
- return NT_STATUS_NO_MEMORY;
- }
+ state->req.data = talloc_move(state->mem_ctx, &buf);
+ state->req.length = length;
return NT_STATUS_OK;
}
@@ -497,7 +494,7 @@ NTSTATUS ctdbd_messaging_connection(TALLOC_CTX *mem_ctx,
/*
* Packet handler to receive and handle a ctdb message
*/
-static NTSTATUS ctdb_handle_message(const DATA_BLOB *data,
+static NTSTATUS ctdb_handle_message(uint8_t *buf, size_t length,
void *private_data)
{
struct ctdbd_connection *conn = talloc_get_type_abort(
@@ -505,11 +502,12 @@ static NTSTATUS ctdb_handle_message(const DATA_BLOB *data,
struct ctdb_req_message *msg;
struct messaging_rec *msg_rec;
- msg = (struct ctdb_req_message *)data->data;
+ msg = (struct ctdb_req_message *)buf;
if (msg->hdr.operation != CTDB_REQ_MESSAGE) {
DEBUG(0, ("Received async msg of type %u, discarding\n",
msg->hdr.operation));
+ TALLOC_FREE(buf);
return NT_STATUS_INVALID_PARAMETER;
}
@@ -519,6 +517,7 @@ static NTSTATUS ctdb_handle_message(const DATA_BLOB *data,
DEBUG(10, ("received CTDB_SRVID_RELEASE_IP\n"));
conn->release_ip_handler((const char *)msg->data,
conn->release_ip_priv);
+ TALLOC_FREE(buf);
return NT_STATUS_OK;
}
@@ -540,6 +539,8 @@ static NTSTATUS ctdb_handle_message(const DATA_BLOB *data,
*/
message_send_all(conn->msg_ctx, MSG_SMB_UNLOCK, NULL, 0, NULL);
+ TALLOC_FREE(buf);
+
return NT_STATUS_OK;
}
@@ -548,17 +549,20 @@ static NTSTATUS ctdb_handle_message(const DATA_BLOB *data,
if (msg->srvid != sys_getpid() && msg->srvid != MSG_SRVID_SAMBA) {
DEBUG(0,("Got unexpected message with srvid=%llu\n",
(unsigned long long)msg->srvid));
+ TALLOC_FREE(buf);
return NT_STATUS_OK;
}
- if (!(msg_rec = ctdb_pull_messaging_rec(NULL, data->length, msg))) {
+ if (!(msg_rec = ctdb_pull_messaging_rec(NULL, length, msg))) {
DEBUG(10, ("ctdb_pull_messaging_rec failed\n"));
+ TALLOC_FREE(buf);
return NT_STATUS_NO_MEMORY;
}
messaging_dispatch_rec(conn->msg_ctx, msg_rec);
TALLOC_FREE(msg_rec);
+ TALLOC_FREE(buf);
return NT_STATUS_OK;
}
@@ -1025,7 +1029,7 @@ struct ctdbd_traverse_state {
* Handle a traverse record coming in on the ctdbd connection
*/
-static NTSTATUS ctdb_traverse_handler(const DATA_BLOB *blob,
+static NTSTATUS ctdb_traverse_handler(uint8_t *buf, size_t length,
void *private_data)
{
struct ctdbd_traverse_state *state =
@@ -1035,11 +1039,11 @@ static NTSTATUS ctdb_traverse_handler(const DATA_BLOB *blob,
struct ctdb_rec_data *d;
TDB_DATA key, data;
- m = (struct ctdb_req_message *)blob->data;
+ m = (struct ctdb_req_message *)buf;
- if (blob->length < sizeof(*m) || m->hdr.length != blob->length) {
- DEBUG(0, ("Got invalid message of length %d\n",
- (int)blob->length));
+ if (length < sizeof(*m) || m->hdr.length != length) {
+ DEBUG(0, ("Got invalid message of length %d\n", (int)length));
+ TALLOC_FREE(buf);
return NT_STATUS_UNEXPECTED_IO_ERROR;
}
@@ -1047,6 +1051,7 @@ static NTSTATUS ctdb_traverse_handler(const DATA_BLOB *blob,
if (m->datalen < sizeof(uint32_t) || m->datalen != d->length) {
DEBUG(0, ("Got invalid traverse data of length %d\n",
(int)m->datalen));
+ TALLOC_FREE(buf);
return NT_STATUS_UNEXPECTED_IO_ERROR;
}
@@ -1063,6 +1068,7 @@ static NTSTATUS ctdb_traverse_handler(const DATA_BLOB *blob,
if (data.dsize < sizeof(struct ctdb_ltdb_header)) {
DEBUG(0, ("Got invalid ltdb header length %d\n",
(int)data.dsize));
+ TALLOC_FREE(buf);
return NT_STATUS_UNEXPECTED_IO_ERROR;
}
data.dsize -= sizeof(struct ctdb_ltdb_header);
@@ -1072,6 +1078,7 @@ static NTSTATUS ctdb_traverse_handler(const DATA_BLOB *blob,
state->fn(key, data, state->private_data);
}
+ TALLOC_FREE(buf);
return NT_STATUS_OK;
}
diff --git a/source3/lib/ldb/include/ldb.h b/source3/lib/ldb/include/ldb.h
index 0a745742d9..3891c1c6a3 100644
--- a/source3/lib/ldb/include/ldb.h
+++ b/source3/lib/ldb/include/ldb.h
@@ -991,7 +991,7 @@ int ldb_search(struct ldb_context *ldb,
const struct ldb_dn *base,
enum ldb_scope scope,
const char *expression,
- const char * const *attrs, struct ldb_result **res);
+ const char * const *attrs, struct ldb_result **_res);
/*
* a useful search function where you can easily define the expression and
diff --git a/source3/lib/ldb/tools/ldbadd.c b/source3/lib/ldb/tools/ldbadd.c
index 4dde2a1ef5..155395e065 100644
--- a/source3/lib/ldb/tools/ldbadd.c
+++ b/source3/lib/ldb/tools/ldbadd.c
@@ -35,8 +35,6 @@
#include "ldb/include/includes.h"
#include "ldb/tools/cmdline.h"
-static int failures;
-
static void usage(void)
{
printf("Usage: ldbadd <options> <ldif...>\n");
@@ -53,7 +51,8 @@ static void usage(void)
/*
add records from an opened file
*/
-static int process_file(struct ldb_context *ldb, FILE *f, int *count)
+static int process_file(struct ldb_context *ldb, FILE *f, int *count,
+ int *failures)
{
struct ldb_ldif *ldif;
int ret = LDB_SUCCESS;
@@ -71,7 +70,7 @@ static int process_file(struct ldb_context *ldb, FILE *f, int *count)
if (ret != LDB_SUCCESS) {
fprintf(stderr, "ERR: \"%s\" on DN %s\n",
ldb_errstring(ldb), ldb_dn_linearize(ldb, ldif->msg->dn));
- failures++;
+ (*failures)++;
} else {
(*count)++;
}
@@ -86,7 +85,7 @@ static int process_file(struct ldb_context *ldb, FILE *f, int *count)
int main(int argc, const char **argv)
{
struct ldb_context *ldb;
- int i, ret=0, count=0;
+ int i, ret=0, count=0, failures=0;
struct ldb_cmdline *options;
ldb_global_init();
@@ -96,7 +95,7 @@ int main(int argc, const char **argv)
options = ldb_cmdline_process(ldb, argc, argv, usage);
if (options->argc == 0) {
- ret = process_file(ldb, stdin, &count);
+ ret = process_file(ldb, stdin, &count, &failures);
} else {
for (i=0;i<options->argc;i++) {
const char *fname = options->argv[i];
@@ -106,7 +105,7 @@ int main(int argc, const char **argv)
perror(fname);
exit(1);
}
- ret = process_file(ldb, f, &count);
+ ret = process_file(ldb, f, &count, &failures);
fclose(f);
}
}
diff --git a/source3/lib/ldb/tools/ldbmodify.c b/source3/lib/ldb/tools/ldbmodify.c
index 368b4cf996..f12387a8f6 100644
--- a/source3/lib/ldb/tools/ldbmodify.c
+++ b/source3/lib/ldb/tools/ldbmodify.c
@@ -35,8 +35,6 @@
#include "ldb/include/includes.h"
#include "ldb/tools/cmdline.h"
-static int failures;
-
static void usage(void)
{
printf("Usage: ldbmodify <options> <ldif...>\n");
@@ -52,7 +50,8 @@ static void usage(void)
/*
process modifies for one file
*/
-static int process_file(struct ldb_context *ldb, FILE *f, int *count)
+static int process_file(struct ldb_context *ldb, FILE *f, int *count,
+ int *failures)
{
struct ldb_ldif *ldif;
int ret = LDB_SUCCESS;
@@ -73,7 +72,7 @@ static int process_file(struct ldb_context *ldb, FILE *f, int *count)
if (ret != LDB_SUCCESS) {
fprintf(stderr, "ERR: \"%s\" on DN %s\n",
ldb_errstring(ldb), ldb_dn_linearize(ldb, ldif->msg->dn));
- failures++;
+ (*failures)++;
} else {
(*count)++;
}
@@ -87,6 +86,7 @@ int main(int argc, const char **argv)
{
struct ldb_context *ldb;
int count=0;
+ int failures=0;
int i, ret=LDB_SUCCESS;
struct ldb_cmdline *options;
@@ -97,7 +97,7 @@ int main(int argc, const char **argv)
options = ldb_cmdline_process(ldb, argc, argv, usage);
if (options->argc == 0) {
- ret = process_file(ldb, stdin, &count);
+ ret = process_file(ldb, stdin, &count, &failures);
} else {
for (i=0;i<options->argc;i++) {
const char *fname = options->argv[i];
@@ -107,7 +107,7 @@ int main(int argc, const char **argv)
perror(fname);
exit(1);
}
- ret = process_file(ldb, f, &count);
+ ret = process_file(ldb, f, &count, &failures);
}
}
diff --git a/source3/lib/packet.c b/source3/lib/packet.c
index e4cab6ba87..ef28bf9f62 100644
--- a/source3/lib/packet.c
+++ b/source3/lib/packet.c
@@ -120,33 +120,43 @@ NTSTATUS packet_fd_read_sync(struct packet_context *ctx)
}
bool packet_handler(struct packet_context *ctx,
- bool (*full_req)(const DATA_BLOB *data,
+ bool (*full_req)(const uint8_t *buf,
+ size_t available,
size_t *length,
- void *private_data),
- NTSTATUS (*callback)(const DATA_BLOB *data,
- void *private_data),
- void *private_data,
- NTSTATUS *status)
+ void *priv),
+ NTSTATUS (*callback)(uint8_t *buf, size_t length,
+ void *priv),
+ void *priv, NTSTATUS *status)
{
size_t length;
- DATA_BLOB data;
+ uint8_t *buf;
- if (!full_req(&ctx->in, &length, private_data)) {
+ if (!full_req(ctx->in.data, ctx->in.length, &length, priv)) {
return False;
}
- SMB_ASSERT(length <= ctx->in.length);
-
- data = data_blob(ctx->in.data, length);
-
- memmove(ctx->in.data, ctx->in.data + length,
- ctx->in.length - length);
- ctx->in.length -= length;
+ if (length > ctx->in.length) {
+ *status = NT_STATUS_INTERNAL_ERROR;
+ return true;
+ }
- *status = callback(&data, private_data);
+ if (length == ctx->in.length) {
+ buf = ctx->in.data;
+ ctx->in.data = NULL;
+ ctx->in.length = 0;
+ } else {
+ buf = (uint8_t *)TALLOC_MEMDUP(ctx, ctx->in.data, length);
+ if (buf == NULL) {
+ *status = NT_STATUS_NO_MEMORY;
+ return true;
+ }
- data_blob_free(&data);
+ memmove(ctx->in.data, ctx->in.data + length,
+ ctx->in.length - length);
+ ctx->in.length -= length;
+ }
+ *status = callback(buf, length, priv);
return True;
}
diff --git a/source3/lib/util_sock.c b/source3/lib/util_sock.c
index d23758ad6a..e913b35d60 100644
--- a/source3/lib/util_sock.c
+++ b/source3/lib/util_sock.c
@@ -707,10 +707,6 @@ ssize_t write_data_iov(int fd, const struct iovec *orig_iov, int iovcnt)
Write data to a fd.
****************************************************************************/
-/****************************************************************************
- Write data to a fd.
-****************************************************************************/
-
ssize_t write_data(int fd, const char *buffer, size_t N)
{
ssize_t ret;
@@ -948,102 +944,319 @@ int open_socket_in(int type,
return( res );
}
+struct open_socket_out_state {
+ int fd;
+ struct event_context *ev;
+ struct sockaddr_storage ss;
+ socklen_t salen;
+ uint16_t port;
+ int wait_nsec;
+};
+
+static void open_socket_out_connected(struct async_req *subreq);
+
+static int open_socket_out_state_destructor(struct open_socket_out_state *s)
+{
+ if (s->fd != -1) {
+ close(s->fd);
+ }
+ return 0;
+}
+
/****************************************************************************
Create an outgoing socket. timeout is in milliseconds.
**************************************************************************/
-int open_socket_out(int type,
- const struct sockaddr_storage *pss,
- uint16_t port,
- int timeout)
+struct async_req *open_socket_out_send(TALLOC_CTX *mem_ctx,
+ struct event_context *ev,
+ const struct sockaddr_storage *pss,
+ uint16_t port,
+ int timeout)
{
char addr[INET6_ADDRSTRLEN];
- struct sockaddr_storage sock_out = *pss;
- int res,ret;
- int connect_loop = 10;
- int increment = 10;
+ struct async_req *result, *subreq;
+ struct open_socket_out_state *state;
+ NTSTATUS status;
- /* create a socket to write to */
- res = socket(pss->ss_family, type, 0);
- if (res == -1) {
- DEBUG(0,("socket error (%s)\n", strerror(errno)));
- return -1;
+ result = async_req_new(mem_ctx);
+ if (result == NULL) {
+ return NULL;
}
+ state = talloc(result, struct open_socket_out_state);
+ if (state == NULL) {
+ goto fail;
+ }
+ result->private_data = state;
+
+ state->ev = ev;
+ state->ss = *pss;
+ state->port = port;
+ state->wait_nsec = 10000;
+ state->salen = -1;
- if (type != SOCK_STREAM) {
- return res;
+ state->fd = socket(state->ss.ss_family, SOCK_STREAM, 0);
+ if (state->fd == -1) {
+ status = map_nt_error_from_unix(errno);
+ goto post_status;
+ }
+ talloc_set_destructor(state, open_socket_out_state_destructor);
+
+ if (!async_req_set_timeout(result, ev, timeval_set(0, timeout*1000))) {
+ goto fail;
}
#if defined(HAVE_IPV6)
if (pss->ss_family == AF_INET6) {
- struct sockaddr_in6 *psa6 = (struct sockaddr_in6 *)&sock_out;
+ struct sockaddr_in6 *psa6;
+ psa6 = (struct sockaddr_in6 *)&state->ss;
psa6->sin6_port = htons(port);
- if (psa6->sin6_scope_id == 0 &&
- IN6_IS_ADDR_LINKLOCAL(&psa6->sin6_addr)) {
- setup_linklocal_scope_id((struct sockaddr *)&sock_out);
+ if (psa6->sin6_scope_id == 0
+ && IN6_IS_ADDR_LINKLOCAL(&psa6->sin6_addr)) {
+ setup_linklocal_scope_id(
+ (struct sockaddr *)&(state->ss));
}
+ state->salen = sizeof(struct sockaddr_in6);
}
#endif
if (pss->ss_family == AF_INET) {
- struct sockaddr_in *psa = (struct sockaddr_in *)&sock_out;
+ struct sockaddr_in *psa;
+ psa = (struct sockaddr_in *)&state->ss;
psa->sin_port = htons(port);
+ state->salen = sizeof(struct sockaddr_in);
}
- /* set it non-blocking */
- set_blocking(res,false);
+ print_sockaddr(addr, sizeof(addr), &state->ss);
+ DEBUG(3,("Connecting to %s at port %u\n", addr, (unsigned int)port));
- print_sockaddr(addr, sizeof(addr), &sock_out);
- DEBUG(3,("Connecting to %s at port %u\n",
- addr,
- (unsigned int)port));
+ subreq = async_connect_send(state, state->ev, state->fd,
+ (struct sockaddr *)&state->ss,
+ state->salen);
+ if ((subreq == NULL)
+ || !async_req_set_timeout(subreq, state->ev,
+ timeval_set(0, state->wait_nsec))) {
+ status = NT_STATUS_NO_MEMORY;
+ goto post_status;
+ }
+ subreq->async.fn = open_socket_out_connected;
+ subreq->async.priv = result;
+ return result;
- /* and connect it to the destination */
- connect_again:
+ post_status:
+ if (!async_post_status(result, ev, status)) {
+ goto fail;
+ }
+ return result;
+ fail:
+ TALLOC_FREE(result);
+ return NULL;
+}
- ret = sys_connect(res, (struct sockaddr *)&sock_out);
+static void open_socket_out_connected(struct async_req *subreq)
+{
+ struct async_req *req = talloc_get_type_abort(
+ subreq->async.priv, struct async_req);
+ struct open_socket_out_state *state = talloc_get_type_abort(
+ req->private_data, struct open_socket_out_state);
+ NTSTATUS status;
+ int sys_errno;
- /* Some systems return EAGAIN when they mean EINPROGRESS */
- if (ret < 0 && (errno == EINPROGRESS || errno == EALREADY ||
- errno == EAGAIN) && (connect_loop < timeout) ) {
- smb_msleep(connect_loop);
- timeout -= connect_loop;
- connect_loop += increment;
- if (increment < 250) {
- /* After 8 rounds we end up at a max of 255 msec */
- increment *= 1.5;
- }
- goto connect_again;
+ status = async_connect_recv(subreq, &sys_errno);
+ TALLOC_FREE(subreq);
+ if (NT_STATUS_IS_OK(status)) {
+ async_req_done(req);
+ return;
}
- if (ret < 0 && (errno == EINPROGRESS || errno == EALREADY ||
- errno == EAGAIN)) {
- DEBUG(1,("timeout connecting to %s:%u\n",
- addr,
- (unsigned int)port));
- close(res);
- return -1;
+ if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT)
+ || (sys_errno == EINPROGRESS)
+ || (sys_errno == EALREADY)
+ || (sys_errno == EAGAIN)) {
+
+ /*
+ * retry
+ */
+
+ if (state->wait_nsec < 250000) {
+ state->wait_nsec *= 1.5;
+ }
+
+ subreq = async_connect_send(state, state->ev, state->fd,
+ (struct sockaddr *)&state->ss,
+ state->salen);
+ if (async_req_nomem(subreq, req)) {
+ return;
+ }
+ if (!async_req_set_timeout(subreq, state->ev,
+ timeval_set(0, state->wait_nsec))) {
+ async_req_error(req, NT_STATUS_NO_MEMORY);
+ return;
+ }
+ subreq->async.fn = open_socket_out_connected;
+ subreq->async.priv = req;
+ return;
}
#ifdef EISCONN
- if (ret < 0 && errno == EISCONN) {
- errno = 0;
- ret = 0;
+ if (sys_errno == EISCONN) {
+ async_req_done(req);
+ return;
}
#endif
- if (ret < 0) {
- DEBUG(2,("error connecting to %s:%d (%s)\n",
- addr,
- (unsigned int)port,
- strerror(errno)));
- close(res);
- return -1;
+ /* real error */
+ async_req_error(req, map_nt_error_from_unix(sys_errno));
+}
+
+NTSTATUS open_socket_out_recv(struct async_req *req, int *pfd)
+{
+ struct open_socket_out_state *state = talloc_get_type_abort(
+ req->private_data, struct open_socket_out_state);
+ NTSTATUS status;
+
+ if (async_req_is_error(req, &status)) {
+ return status;
+ }
+ *pfd = state->fd;
+ state->fd = -1;
+ return NT_STATUS_OK;
+}
+
+NTSTATUS open_socket_out(const struct sockaddr_storage *pss, uint16_t port,
+ int timeout, int *pfd)
+{
+ TALLOC_CTX *frame = talloc_stackframe();
+ struct event_context *ev;
+ struct async_req *req;
+ NTSTATUS status = NT_STATUS_NO_MEMORY;
+
+ ev = event_context_init(frame);
+ if (ev == NULL) {
+ goto fail;
}
- /* set it blocking again */
- set_blocking(res,true);
+ req = open_socket_out_send(frame, ev, pss, port, timeout);
+ if (req == NULL) {
+ goto fail;
+ }
+ while (req->state < ASYNC_REQ_DONE) {
+ event_loop_once(ev);
+ }
- return res;
+ status = open_socket_out_recv(req, pfd);
+ fail:
+ TALLOC_FREE(frame);
+ return status;
+}
+
+struct open_socket_out_defer_state {
+ struct event_context *ev;
+ struct sockaddr_storage ss;
+ uint16_t port;
+ int timeout;
+ int fd;
+};
+
+static void open_socket_out_defer_waited(struct async_req *subreq);
+static void open_socket_out_defer_connected(struct async_req *subreq);
+
+struct async_req *open_socket_out_defer_send(TALLOC_CTX *mem_ctx,
+ struct event_context *ev,
+ struct timeval wait_time,
+ const struct sockaddr_storage *pss,
+ uint16_t port,
+ int timeout)
+{
+ struct async_req *result, *subreq;
+ struct open_socket_out_defer_state *state;
+ NTSTATUS status;
+
+ result = async_req_new(mem_ctx);
+ if (result == NULL) {
+ return NULL;
+ }
+ state = talloc(result, struct open_socket_out_defer_state);
+ if (state == NULL) {
+ goto fail;
+ }
+ result->private_data = state;
+
+ state->ev = ev;
+ state->ss = *pss;
+ state->port = port;
+ state->timeout = timeout;
+
+ subreq = async_wait_send(state, ev, wait_time);
+ if (subreq == NULL) {
+ status = NT_STATUS_NO_MEMORY;
+ goto post_status;
+ }
+ subreq->async.fn = open_socket_out_defer_waited;
+ subreq->async.priv = result;
+ return result;
+
+ post_status:
+ if (!async_post_status(result, ev, status)) {
+ goto fail;
+ }
+ return result;
+ fail:
+ TALLOC_FREE(result);
+ return NULL;
+}
+
+static void open_socket_out_defer_waited(struct async_req *subreq)
+{
+ struct async_req *req = talloc_get_type_abort(
+ subreq->async.priv, struct async_req);
+ struct open_socket_out_defer_state *state = talloc_get_type_abort(
+ req->private_data, struct open_socket_out_defer_state);
+ NTSTATUS status;
+
+ status = async_wait_recv(subreq);
+ TALLOC_FREE(subreq);
+ if (!NT_STATUS_IS_OK(status)) {
+ async_req_error(req, status);
+ return;
+ }
+
+ subreq = open_socket_out_send(state, state->ev, &state->ss,
+ state->port, state->timeout);
+ if (async_req_nomem(subreq, req)) {
+ return;
+ }
+ subreq->async.fn = open_socket_out_defer_connected;
+ subreq->async.priv = req;
+}
+
+static void open_socket_out_defer_connected(struct async_req *subreq)
+{
+ struct async_req *req = talloc_get_type_abort(
+ subreq->async.priv, struct async_req);
+ struct open_socket_out_defer_state *state = talloc_get_type_abort(
+ req->private_data, struct open_socket_out_defer_state);
+ NTSTATUS status;
+
+ status = open_socket_out_recv(subreq, &state->fd);
+ TALLOC_FREE(subreq);
+ if (!NT_STATUS_IS_OK(status)) {
+ async_req_error(req, status);
+ return;
+ }
+ async_req_done(req);
+}
+
+NTSTATUS open_socket_out_defer_recv(struct async_req *req, int *pfd)
+{
+ struct open_socket_out_defer_state *state = talloc_get_type_abort(
+ req->private_data, struct open_socket_out_defer_state);
+ NTSTATUS status;
+
+ if (async_req_is_error(req, &status)) {
+ return status;
+ }
+ *pfd = state->fd;
+ state->fd = -1;
+ return NT_STATUS_OK;
}
/*******************************************************************
diff --git a/source3/lib/wb_reqtrans.c b/source3/lib/wb_reqtrans.c
new file mode 100644
index 0000000000..1f5f181aa1
--- /dev/null
+++ b/source3/lib/wb_reqtrans.c
@@ -0,0 +1,542 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ Async transfer of winbindd_request and _response structs
+
+ 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 "winbindd/winbindd.h"
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_WINBIND
+
+struct req_read_state {
+ struct winbindd_request *wb_req;
+ struct event_context *ev;
+ size_t max_extra_data;
+ int fd;
+};
+
+static void wb_req_read_len(struct async_req *subreq);
+static void wb_req_read_main(struct async_req *subreq);
+static void wb_req_read_extra(struct async_req *subreq);
+
+struct async_req *wb_req_read_send(TALLOC_CTX *mem_ctx,
+ struct event_context *ev,
+ int fd, size_t max_extra_data)
+{
+ struct async_req *result, *subreq;
+ struct req_read_state *state;
+
+ result = async_req_new(mem_ctx);
+ if (result == NULL) {
+ return NULL;
+ }
+
+ state = talloc(result, struct req_read_state);
+ if (state == NULL) {
+ goto nomem;
+ }
+ result->private_data = state;
+
+ state->fd = fd;
+ state->ev = ev;
+ state->max_extra_data = max_extra_data;
+ state->wb_req = talloc(state, struct winbindd_request);
+ if (state->wb_req == NULL) {
+ goto nomem;
+ }
+
+ subreq = recvall_send(state, ev, state->fd, &(state->wb_req->length),
+ sizeof(state->wb_req->length), 0);
+ if (subreq == NULL) {
+ goto nomem;
+ }
+
+ subreq->async.fn = wb_req_read_len;
+ subreq->async.priv = result;
+ return result;
+
+ nomem:
+ TALLOC_FREE(result);
+ return NULL;
+}
+
+static void wb_req_read_len(struct async_req *subreq)
+{
+ struct async_req *req = talloc_get_type_abort(
+ subreq->async.priv, struct async_req);
+ struct req_read_state *state = talloc_get_type_abort(
+ req->private_data, struct req_read_state);
+ NTSTATUS status;
+
+ status = recvall_recv(subreq);
+ TALLOC_FREE(subreq);
+ if (!NT_STATUS_IS_OK(status)) {
+ async_req_error(req, status);
+ return;
+ }
+
+ if (state->wb_req->length != sizeof(struct winbindd_request)) {
+ DEBUG(0, ("wb_req_read_len: Invalid request size received: "
+ "%d (expected %d)\n", (int)state->wb_req->length,
+ (int)sizeof(struct winbindd_request)));
+ async_req_error(req, NT_STATUS_INVALID_BUFFER_SIZE);
+ return;
+ }
+
+ subreq = recvall_send(
+ req, state->ev, state->fd, (uint32 *)(state->wb_req)+1,
+ sizeof(struct winbindd_request) - sizeof(uint32), 0);
+ if (async_req_nomem(subreq, req)) {
+ return;
+ }
+
+ subreq->async.fn = wb_req_read_main;
+ subreq->async.priv = req;
+}
+
+static void wb_req_read_main(struct async_req *subreq)
+{
+ struct async_req *req = talloc_get_type_abort(
+ subreq->async.priv, struct async_req);
+ struct req_read_state *state = talloc_get_type_abort(
+ req->private_data, struct req_read_state);
+ NTSTATUS status;
+
+ status = recvall_recv(subreq);
+ TALLOC_FREE(subreq);
+ if (!NT_STATUS_IS_OK(status)) {
+ async_req_error(req, status);
+ return;
+ }
+
+ if ((state->max_extra_data != 0)
+ && (state->wb_req->extra_len > state->max_extra_data)) {
+ DEBUG(3, ("Got request with %d bytes extra data on "
+ "unprivileged socket\n",
+ (int)state->wb_req->extra_len));
+ async_req_error(req, NT_STATUS_INVALID_BUFFER_SIZE);
+ return;
+ }
+
+ if (state->wb_req->extra_len == 0) {
+ async_req_done(req);
+ return;
+ }
+
+ state->wb_req->extra_data.data = TALLOC_ARRAY(
+ state->wb_req, char, state->wb_req->extra_len + 1);
+ if (async_req_nomem(state->wb_req->extra_data.data, req)) {
+ return;
+ }
+
+ state->wb_req->extra_data.data[state->wb_req->extra_len] = 0;
+
+ subreq = recvall_send(
+ req, state->ev, state->fd, state->wb_req->extra_data.data,
+ state->wb_req->extra_len, 0);
+ if (async_req_nomem(subreq, req)) {
+ return;
+ }
+
+ subreq->async.fn = wb_req_read_extra;
+ subreq->async.priv = req;
+}
+
+static void wb_req_read_extra(struct async_req *subreq)
+{
+ struct async_req *req = talloc_get_type_abort(
+ subreq->async.priv, struct async_req);
+ NTSTATUS status;
+
+ status = recvall_recv(subreq);
+ TALLOC_FREE(subreq);
+ if (!NT_STATUS_IS_OK(status)) {
+ async_req_error(req, status);
+ return;
+ }
+ async_req_done(req);
+}
+
+
+NTSTATUS wb_req_read_recv(struct async_req *req, TALLOC_CTX *mem_ctx,
+ struct winbindd_request **preq)
+{
+ struct req_read_state *state = talloc_get_type_abort(
+ req->private_data, struct req_read_state);
+ NTSTATUS status;
+
+ if (async_req_is_error(req, &status)) {
+ return status;
+ }
+ *preq = talloc_move(mem_ctx, &state->wb_req);
+ return NT_STATUS_OK;
+}
+
+struct req_write_state {
+ struct winbindd_request *wb_req;
+ struct event_context *ev;
+ int fd;
+};
+
+static void wb_req_write_main(struct async_req *subreq);
+static void wb_req_write_extra(struct async_req *subreq);
+
+struct async_req *wb_req_write_send(TALLOC_CTX *mem_ctx,
+ struct event_context *ev, int fd,
+ struct winbindd_request *wb_req)
+{
+ struct async_req *result, *subreq;
+ struct req_write_state *state;
+
+ result = async_req_new(mem_ctx);
+ if (result == NULL) {
+ return NULL;
+ }
+
+ state = talloc(result, struct req_write_state);
+ if (state == NULL) {
+ goto nomem;
+ }
+ result->private_data = state;
+
+ state->fd = fd;
+ state->ev = ev;
+ state->wb_req = wb_req;
+
+ subreq = sendall_send(state, state->ev, state->fd, state->wb_req,
+ sizeof(struct winbindd_request), 0);
+ if (subreq == NULL) {
+ goto nomem;
+ }
+
+ subreq->async.fn = wb_req_write_main;
+ subreq->async.priv = result;
+ return result;
+
+ nomem:
+ TALLOC_FREE(result);
+ return NULL;
+}
+
+static void wb_req_write_main(struct async_req *subreq)
+{
+ struct async_req *req = talloc_get_type_abort(
+ subreq->async.priv, struct async_req);
+ struct req_write_state *state = talloc_get_type_abort(
+ req->private_data, struct req_write_state);
+ NTSTATUS status;
+
+ status = sendall_recv(subreq);
+ TALLOC_FREE(subreq);
+ if (!NT_STATUS_IS_OK(status)) {
+ async_req_error(req, status);
+ return;
+ }
+
+ if (state->wb_req->extra_len == 0) {
+ async_req_done(req);
+ return;
+ }
+
+ subreq = sendall_send(state, state->ev, state->fd,
+ state->wb_req->extra_data.data,
+ state->wb_req->extra_len, 0);
+ if (async_req_nomem(subreq, req)) {
+ return;
+ }
+
+ subreq->async.fn = wb_req_write_extra;
+ subreq->async.priv = req;
+}
+
+static void wb_req_write_extra(struct async_req *subreq)
+{
+ struct async_req *req = talloc_get_type_abort(
+ subreq->async.priv, struct async_req);
+ NTSTATUS status;
+
+ status = sendall_recv(subreq);
+ TALLOC_FREE(subreq);
+ if (!NT_STATUS_IS_OK(status)) {
+ async_req_error(req, status);
+ return;
+ }
+
+ async_req_done(req);
+}
+
+NTSTATUS wb_req_write_recv(struct async_req *req)
+{
+ return async_req_simple_recv(req);
+}
+
+struct resp_read_state {
+ struct winbindd_response *wb_resp;
+ struct event_context *ev;
+ size_t max_extra_data;
+ int fd;
+};
+
+static void wb_resp_read_len(struct async_req *subreq);
+static void wb_resp_read_main(struct async_req *subreq);
+static void wb_resp_read_extra(struct async_req *subreq);
+
+struct async_req *wb_resp_read_send(TALLOC_CTX *mem_ctx,
+ struct event_context *ev, int fd)
+{
+ struct async_req *result, *subreq;
+ struct resp_read_state *state;
+
+ result = async_req_new(mem_ctx);
+ if (result == NULL) {
+ return NULL;
+ }
+
+ state = talloc(result, struct resp_read_state);
+ if (state == NULL) {
+ goto nomem;
+ }
+ result->private_data = state;
+
+ state->fd = fd;
+ state->ev = ev;
+ state->wb_resp = talloc(state, struct winbindd_response);
+ if (state->wb_resp == NULL) {
+ goto nomem;
+ }
+
+ subreq = recvall_send(state, ev, state->fd, &(state->wb_resp->length),
+ sizeof(state->wb_resp->length), 0);
+ if (subreq == NULL) {
+ goto nomem;
+ }
+
+ subreq->async.fn = wb_resp_read_len;
+ subreq->async.priv = result;
+ return result;
+
+ nomem:
+ TALLOC_FREE(result);
+ return NULL;
+}
+
+static void wb_resp_read_len(struct async_req *subreq)
+{
+ struct async_req *req = talloc_get_type_abort(
+ subreq->async.priv, struct async_req);
+ struct resp_read_state *state = talloc_get_type_abort(
+ req->private_data, struct resp_read_state);
+ NTSTATUS status;
+
+ status = recvall_recv(subreq);
+ TALLOC_FREE(subreq);
+ if (!NT_STATUS_IS_OK(status)) {
+ async_req_error(req, status);
+ return;
+ }
+
+ if (state->wb_resp->length < sizeof(struct winbindd_response)) {
+ DEBUG(0, ("wb_resp_read_len: Invalid response size received: "
+ "%d (expected at least%d)\n",
+ (int)state->wb_resp->length,
+ (int)sizeof(struct winbindd_response)));
+ async_req_error(req, NT_STATUS_INVALID_BUFFER_SIZE);
+ return;
+ }
+
+ subreq = recvall_send(
+ req, state->ev, state->fd, (uint32 *)(state->wb_resp)+1,
+ sizeof(struct winbindd_response) - sizeof(uint32), 0);
+ if (async_req_nomem(subreq, req)) {
+ return;
+ }
+
+ subreq->async.fn = wb_resp_read_main;
+ subreq->async.priv = req;
+}
+
+static void wb_resp_read_main(struct async_req *subreq)
+{
+ struct async_req *req = talloc_get_type_abort(
+ subreq->async.priv, struct async_req);
+ struct resp_read_state *state = talloc_get_type_abort(
+ req->private_data, struct resp_read_state);
+ NTSTATUS status;
+ size_t extra_len;
+
+ status = recvall_recv(subreq);
+ TALLOC_FREE(subreq);
+ if (!NT_STATUS_IS_OK(status)) {
+ async_req_error(req, status);
+ return;
+ }
+
+ extra_len = state->wb_resp->length - sizeof(struct winbindd_response);
+ if (extra_len == 0) {
+ async_req_done(req);
+ return;
+ }
+
+ state->wb_resp->extra_data.data = TALLOC_ARRAY(
+ state->wb_resp, char, extra_len+1);
+ if (async_req_nomem(state->wb_resp->extra_data.data, req)) {
+ return;
+ }
+ ((char *)state->wb_resp->extra_data.data)[extra_len] = 0;
+
+ subreq = recvall_send(
+ req, state->ev, state->fd, state->wb_resp->extra_data.data,
+ extra_len, 0);
+ if (async_req_nomem(subreq, req)) {
+ return;
+ }
+
+ subreq->async.fn = wb_resp_read_extra;
+ subreq->async.priv = req;
+}
+
+static void wb_resp_read_extra(struct async_req *subreq)
+{
+ struct async_req *req = talloc_get_type_abort(
+ subreq->async.priv, struct async_req);
+ NTSTATUS status;
+
+ status = recvall_recv(subreq);
+ TALLOC_FREE(subreq);
+ if (!NT_STATUS_IS_OK(status)) {
+ async_req_error(req, status);
+ return;
+ }
+ async_req_done(req);
+}
+
+
+NTSTATUS wb_resp_read_recv(struct async_req *req, TALLOC_CTX *mem_ctx,
+ struct winbindd_response **presp)
+{
+ struct resp_read_state *state = talloc_get_type_abort(
+ req->private_data, struct resp_read_state);
+ NTSTATUS status;
+
+ if (async_req_is_error(req, &status)) {
+ return status;
+ }
+ *presp = talloc_move(mem_ctx, &state->wb_resp);
+ return NT_STATUS_OK;
+}
+
+struct resp_write_state {
+ struct winbindd_response *wb_resp;
+ struct event_context *ev;
+ int fd;
+};
+
+static void wb_resp_write_main(struct async_req *subreq);
+static void wb_resp_write_extra(struct async_req *subreq);
+
+struct async_req *wb_resp_write_send(TALLOC_CTX *mem_ctx,
+ struct event_context *ev, int fd,
+ struct winbindd_response *wb_resp)
+{
+ struct async_req *result, *subreq;
+ struct resp_write_state *state;
+
+ result = async_req_new(mem_ctx);
+ if (result == NULL) {
+ return NULL;
+ }
+
+ state = talloc(result, struct resp_write_state);
+ if (state == NULL) {
+ goto nomem;
+ }
+ result->private_data = state;
+
+ state->fd = fd;
+ state->ev = ev;
+ state->wb_resp = wb_resp;
+
+ subreq = sendall_send(state, state->ev, state->fd, state->wb_resp,
+ sizeof(struct winbindd_response), 0);
+ if (subreq == NULL) {
+ goto nomem;
+ }
+
+ subreq->async.fn = wb_resp_write_main;
+ subreq->async.priv = result;
+ return result;
+
+ nomem:
+ TALLOC_FREE(result);
+ return NULL;
+}
+
+static void wb_resp_write_main(struct async_req *subreq)
+{
+ struct async_req *req = talloc_get_type_abort(
+ subreq->async.priv, struct async_req);
+ struct resp_write_state *state = talloc_get_type_abort(
+ req->private_data, struct resp_write_state);
+ NTSTATUS status;
+
+ status = sendall_recv(subreq);
+ TALLOC_FREE(subreq);
+ if (!NT_STATUS_IS_OK(status)) {
+ async_req_error(req, status);
+ return;
+ }
+
+ if (state->wb_resp->length == sizeof(struct winbindd_response)) {
+ async_req_done(req);
+ return;
+ }
+
+ subreq = sendall_send(
+ state, state->ev, state->fd,
+ state->wb_resp->extra_data.data,
+ state->wb_resp->length - sizeof(struct winbindd_response), 0);
+ if (async_req_nomem(subreq, req)) {
+ return;
+ }
+
+ subreq->async.fn = wb_resp_write_extra;
+ subreq->async.priv = req;
+}
+
+static void wb_resp_write_extra(struct async_req *subreq)
+{
+ struct async_req *req = talloc_get_type_abort(
+ subreq->async.priv, struct async_req);
+ NTSTATUS status;
+
+ status = sendall_recv(subreq);
+ TALLOC_FREE(subreq);
+ if (!NT_STATUS_IS_OK(status)) {
+ async_req_error(req, status);
+ return;
+ }
+
+ async_req_done(req);
+}
+
+NTSTATUS wb_resp_write_recv(struct async_req *req)
+{
+ return async_req_simple_recv(req);
+}
diff --git a/source3/lib/wbclient.c b/source3/lib/wbclient.c
new file mode 100644
index 0000000000..d58c934c07
--- /dev/null
+++ b/source3/lib/wbclient.c
@@ -0,0 +1,774 @@
+/*
+ Unix SMB/CIFS implementation.
+ Infrastructure for async winbind requests
+ 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 "winbindd/winbindd.h"
+#include "winbindd/winbindd_proto.h"
+
+static int make_nonstd_fd(int fd)
+{
+ int i;
+ int sys_errno = 0;
+ int fds[3];
+ int num_fds = 0;
+
+ if (fd == -1) {
+ return -1;
+ }
+ while (fd < 3) {
+ fds[num_fds++] = fd;
+ fd = dup(fd);
+ if (fd == -1) {
+ sys_errno = errno;
+ break;
+ }
+ }
+ for (i=0; i<num_fds; i++) {
+ close(fds[i]);
+ }
+ if (fd == -1) {
+ errno = sys_errno;
+ }
+ return fd;
+}
+
+/****************************************************************************
+ Set a fd into blocking/nonblocking mode. Uses POSIX O_NONBLOCK if available,
+ else
+ if SYSV use O_NDELAY
+ if BSD use FNDELAY
+ Set close on exec also.
+****************************************************************************/
+
+static int make_safe_fd(int fd)
+{
+ int result, flags;
+ int new_fd = make_nonstd_fd(fd);
+
+ if (new_fd == -1) {
+ goto fail;
+ }
+
+ /* Socket should be nonblocking. */
+
+#ifdef O_NONBLOCK
+#define FLAG_TO_SET O_NONBLOCK
+#else
+#ifdef SYSV
+#define FLAG_TO_SET O_NDELAY
+#else /* BSD */
+#define FLAG_TO_SET FNDELAY
+#endif
+#endif
+
+ if ((flags = fcntl(new_fd, F_GETFL)) == -1) {
+ goto fail;
+ }
+
+ flags |= FLAG_TO_SET;
+ if (fcntl(new_fd, F_SETFL, flags) == -1) {
+ goto fail;
+ }
+
+#undef FLAG_TO_SET
+
+ /* Socket should be closed on exec() */
+#ifdef FD_CLOEXEC
+ result = flags = fcntl(new_fd, F_GETFD, 0);
+ if (flags >= 0) {
+ flags |= FD_CLOEXEC;
+ result = fcntl( new_fd, F_SETFD, flags );
+ }
+ if (result < 0) {
+ goto fail;
+ }
+#endif
+ return new_fd;
+
+ fail:
+ if (new_fd != -1) {
+ int sys_errno = errno;
+ close(new_fd);
+ errno = sys_errno;
+ }
+ return -1;
+}
+
+static bool winbind_closed_fd(int fd)
+{
+ struct timeval tv;
+ fd_set r_fds;
+
+ if (fd == -1) {
+ return true;
+ }
+
+ FD_ZERO(&r_fds);
+ FD_SET(fd, &r_fds);
+ ZERO_STRUCT(tv);
+
+ if ((select(fd+1, &r_fds, NULL, NULL, &tv) == -1)
+ || FD_ISSET(fd, &r_fds)) {
+ return true;
+ }
+
+ return false;
+}
+
+struct wb_context {
+ struct async_req_queue *queue;
+ int fd;
+ bool is_priv;
+};
+
+struct wb_context *wb_context_init(TALLOC_CTX *mem_ctx)
+{
+ struct wb_context *result;
+
+ result = talloc(mem_ctx, struct wb_context);
+ if (result == NULL) {
+ return NULL;
+ }
+ result->queue = async_req_queue_init(result);
+ if (result->queue == NULL) {
+ TALLOC_FREE(result);
+ return NULL;
+ }
+ result->fd = -1;
+ return result;
+}
+
+static struct async_req *wb_connect_send(TALLOC_CTX *mem_ctx,
+ struct event_context *ev,
+ struct wb_context *wb_ctx,
+ const char *dir)
+{
+ struct async_req *req;
+ struct sockaddr_un sunaddr;
+ struct stat st;
+ char *path = NULL;
+ NTSTATUS status;
+
+ if (wb_ctx->fd != -1) {
+ close(wb_ctx->fd);
+ wb_ctx->fd = -1;
+ }
+
+ /* Check permissions on unix socket directory */
+
+ if (lstat(dir, &st) == -1) {
+ status = NT_STATUS_OBJECT_NAME_NOT_FOUND;
+ goto post_status;
+ }
+
+ if (!S_ISDIR(st.st_mode) ||
+ (st.st_uid != 0 && st.st_uid != geteuid())) {
+ status = NT_STATUS_OBJECT_NAME_NOT_FOUND;
+ goto post_status;
+ }
+
+ /* Connect to socket */
+
+ path = talloc_asprintf(talloc_tos(), "%s/%s", dir,
+ WINBINDD_SOCKET_NAME);
+ if (path == NULL) {
+ goto nomem;
+ }
+
+ sunaddr.sun_family = AF_UNIX;
+ strlcpy(sunaddr.sun_path, path, sizeof(sunaddr.sun_path));
+ TALLOC_FREE(path);
+
+ /* If socket file doesn't exist, don't bother trying to connect
+ with retry. This is an attempt to make the system usable when
+ the winbindd daemon is not running. */
+
+ if ((lstat(sunaddr.sun_path, &st) == -1)
+ || !S_ISSOCK(st.st_mode)
+ || (st.st_uid != 0 && st.st_uid != geteuid())) {
+ status = NT_STATUS_OBJECT_NAME_NOT_FOUND;
+ goto post_status;
+ }
+
+ wb_ctx->fd = make_safe_fd(socket(AF_UNIX, SOCK_STREAM, 0));
+ if (wb_ctx->fd == -1) {
+ status = map_nt_error_from_unix(errno);
+ goto post_status;
+ }
+
+ req = async_connect_send(mem_ctx, ev, wb_ctx->fd,
+ (struct sockaddr *)&sunaddr,
+ sizeof(sunaddr));
+ if (req == NULL) {
+ goto nomem;
+ }
+ if (!async_req_set_timeout(req, ev, timeval_set(30, 0))) {
+ TALLOC_FREE(req);
+ goto nomem;
+ }
+
+ return req;
+
+ nomem:
+ status = NT_STATUS_NO_MEMORY;
+ post_status:
+ req = async_req_new(mem_ctx);
+ if (req == NULL) {
+ return NULL;
+ }
+ if (async_post_status(req, ev, status)) {
+ return req;
+ }
+ TALLOC_FREE(req);
+ return NULL;
+}
+
+static NTSTATUS wb_connect_recv(struct async_req *req)
+{
+ int dummy;
+
+ return async_connect_recv(req, &dummy);
+}
+
+static struct winbindd_request *winbindd_request_copy(
+ TALLOC_CTX *mem_ctx,
+ const struct winbindd_request *req)
+{
+ struct winbindd_request *result;
+
+ result = (struct winbindd_request *)TALLOC_MEMDUP(
+ mem_ctx, req, sizeof(struct winbindd_request));
+ if (result == NULL) {
+ return NULL;
+ }
+
+ if (result->extra_len == 0) {
+ return result;
+ }
+
+ result->extra_data.data = (char *)TALLOC_MEMDUP(
+ result, result->extra_data.data, result->extra_len);
+ if (result->extra_data.data == NULL) {
+ TALLOC_FREE(result);
+ return NULL;
+ }
+ return result;
+}
+
+struct wb_int_trans_state {
+ struct event_context *ev;
+ int fd;
+ struct winbindd_request *wb_req;
+ struct winbindd_response *wb_resp;
+};
+
+static void wb_int_trans_write_done(struct async_req *subreq);
+static void wb_int_trans_read_done(struct async_req *subreq);
+
+static struct async_req *wb_int_trans_send(TALLOC_CTX *mem_ctx,
+ struct event_context *ev, int fd,
+ struct winbindd_request *wb_req)
+{
+ struct async_req *result;
+ struct async_req *subreq;
+ struct wb_int_trans_state *state;
+
+ result = async_req_new(mem_ctx);
+ if (result == NULL) {
+ return NULL;
+ }
+ state = talloc(result, struct wb_int_trans_state);
+ if (state == NULL) {
+ goto fail;
+ }
+ result->private_data = state;
+
+ if (winbind_closed_fd(fd)) {
+ if (!async_post_status(result, ev,
+ NT_STATUS_PIPE_DISCONNECTED)) {
+ goto fail;
+ }
+ return result;
+ }
+
+ state->ev = ev;
+ state->fd = fd;
+ state->wb_req = wb_req;
+
+ state->wb_req->length = sizeof(struct winbindd_request);
+ state->wb_req->pid = getpid();
+
+ subreq = wb_req_write_send(state, state->ev, state->fd, state->wb_req);
+ if (subreq == NULL) {
+ goto fail;
+ }
+ subreq->async.fn = wb_int_trans_write_done;
+ subreq->async.priv = result;
+
+ return result;
+
+ fail:
+ TALLOC_FREE(result);
+ return NULL;
+}
+
+static void wb_int_trans_write_done(struct async_req *subreq)
+{
+ struct async_req *req = talloc_get_type_abort(
+ subreq->async.priv, struct async_req);
+ struct wb_int_trans_state *state = talloc_get_type_abort(
+ req->private_data, struct wb_int_trans_state);
+ NTSTATUS status;
+
+ status = wb_req_write_recv(subreq);
+ TALLOC_FREE(subreq);
+ if (!NT_STATUS_IS_OK(status)) {
+ async_req_error(req, status);
+ return;
+ }
+
+ subreq = wb_resp_read_send(state, state->ev, state->fd);
+ if (subreq == NULL) {
+ async_req_error(req, NT_STATUS_NO_MEMORY);
+ }
+ subreq->async.fn = wb_int_trans_read_done;
+ subreq->async.priv = req;
+}
+
+static void wb_int_trans_read_done(struct async_req *subreq)
+{
+ struct async_req *req = talloc_get_type_abort(
+ subreq->async.priv, struct async_req);
+ struct wb_int_trans_state *state = talloc_get_type_abort(
+ req->private_data, struct wb_int_trans_state);
+ NTSTATUS status;
+
+ status = wb_resp_read_recv(subreq, state, &state->wb_resp);
+ TALLOC_FREE(subreq);
+ if (!NT_STATUS_IS_OK(status)) {
+ async_req_error(req, status);
+ return;
+ }
+
+ async_req_done(req);
+}
+
+static NTSTATUS wb_int_trans_recv(struct async_req *req,
+ TALLOC_CTX *mem_ctx,
+ struct winbindd_response **presponse)
+{
+ struct wb_int_trans_state *state = talloc_get_type_abort(
+ req->private_data, struct wb_int_trans_state);
+ NTSTATUS status;
+
+ if (async_req_is_error(req, &status)) {
+ return status;
+ }
+
+ *presponse = talloc_move(mem_ctx, &state->wb_resp);
+ return NT_STATUS_OK;
+}
+
+static const char *winbindd_socket_dir(void)
+{
+#ifdef SOCKET_WRAPPER
+ const char *env_dir;
+
+ env_dir = getenv(WINBINDD_SOCKET_DIR_ENVVAR);
+ if (env_dir) {
+ return env_dir;
+ }
+#endif
+
+ return WINBINDD_SOCKET_DIR;
+}
+
+struct wb_open_pipe_state {
+ struct wb_context *wb_ctx;
+ struct event_context *ev;
+ bool need_priv;
+ struct winbindd_request wb_req;
+};
+
+static void wb_open_pipe_connect_nonpriv_done(struct async_req *subreq);
+static void wb_open_pipe_ping_done(struct async_req *subreq);
+static void wb_open_pipe_getpriv_done(struct async_req *subreq);
+static void wb_open_pipe_connect_priv_done(struct async_req *subreq);
+
+static struct async_req *wb_open_pipe_send(TALLOC_CTX *mem_ctx,
+ struct event_context *ev,
+ struct wb_context *wb_ctx,
+ bool need_priv)
+{
+ struct async_req *result;
+ struct async_req *subreq;
+ struct wb_open_pipe_state *state;
+
+ result = async_req_new(mem_ctx);
+ if (result == NULL) {
+ return NULL;
+ }
+ state = talloc(result, struct wb_open_pipe_state);
+ if (state == NULL) {
+ goto fail;
+ }
+ result->private_data = state;
+
+ state->wb_ctx = wb_ctx;
+ state->ev = ev;
+ state->need_priv = need_priv;
+
+ if (wb_ctx->fd != -1) {
+ close(wb_ctx->fd);
+ wb_ctx->fd = -1;
+ }
+
+ subreq = wb_connect_send(state, ev, wb_ctx, winbindd_socket_dir());
+ if (subreq == NULL) {
+ goto fail;
+ }
+
+ subreq->async.fn = wb_open_pipe_connect_nonpriv_done;
+ subreq->async.priv = result;
+ return result;
+
+ fail:
+ TALLOC_FREE(result);
+ return NULL;
+}
+
+static void wb_open_pipe_connect_nonpriv_done(struct async_req *subreq)
+{
+ struct async_req *req = talloc_get_type_abort(
+ subreq->async.priv, struct async_req);
+ struct wb_open_pipe_state *state = talloc_get_type_abort(
+ req->private_data, struct wb_open_pipe_state);
+ NTSTATUS status;
+
+ status = wb_connect_recv(subreq);
+ TALLOC_FREE(subreq);
+ if (!NT_STATUS_IS_OK(status)) {
+ state->wb_ctx->is_priv = true;
+ async_req_error(req, status);
+ return;
+ }
+
+ ZERO_STRUCT(state->wb_req);
+ state->wb_req.cmd = WINBINDD_INTERFACE_VERSION;
+
+ subreq = wb_int_trans_send(state, state->ev, state->wb_ctx->fd,
+ &state->wb_req);
+ if (async_req_nomem(subreq, req)) {
+ return;
+ }
+
+ subreq->async.fn = wb_open_pipe_ping_done;
+ subreq->async.priv = req;
+}
+
+static void wb_open_pipe_ping_done(struct async_req *subreq)
+{
+ struct async_req *req = talloc_get_type_abort(
+ subreq->async.priv, struct async_req);
+ struct wb_open_pipe_state *state = talloc_get_type_abort(
+ req->private_data, struct wb_open_pipe_state);
+ struct winbindd_response *wb_resp;
+ NTSTATUS status;
+
+ status = wb_int_trans_recv(subreq, state, &wb_resp);
+ TALLOC_FREE(subreq);
+ if (!NT_STATUS_IS_OK(status)) {
+ async_req_error(req, status);
+ return;
+ }
+
+ if (!state->need_priv) {
+ async_req_done(req);
+ return;
+ }
+
+ state->wb_req.cmd = WINBINDD_PRIV_PIPE_DIR;
+
+ subreq = wb_int_trans_send(state, state->ev, state->wb_ctx->fd,
+ &state->wb_req);
+ if (async_req_nomem(subreq, req)) {
+ return;
+ }
+
+ subreq->async.fn = wb_open_pipe_getpriv_done;
+ subreq->async.priv = req;
+}
+
+static void wb_open_pipe_getpriv_done(struct async_req *subreq)
+{
+ struct async_req *req = talloc_get_type_abort(
+ subreq->async.priv, struct async_req);
+ struct wb_open_pipe_state *state = talloc_get_type_abort(
+ req->private_data, struct wb_open_pipe_state);
+ struct winbindd_response *wb_resp = NULL;
+ NTSTATUS status;
+
+ status = wb_int_trans_recv(subreq, state, &wb_resp);
+ TALLOC_FREE(subreq);
+ if (!NT_STATUS_IS_OK(status)) {
+ async_req_error(req, status);
+ return;
+ }
+
+ close(state->wb_ctx->fd);
+ state->wb_ctx->fd = -1;
+
+ subreq = wb_connect_send(state, state->ev, state->wb_ctx,
+ (char *)wb_resp->extra_data.data);
+ TALLOC_FREE(wb_resp);
+ if (async_req_nomem(subreq, req)) {
+ return;
+ }
+
+ subreq->async.fn = wb_open_pipe_connect_priv_done;
+ subreq->async.priv = req;
+}
+
+static void wb_open_pipe_connect_priv_done(struct async_req *subreq)
+{
+ struct async_req *req = talloc_get_type_abort(
+ subreq->async.priv, struct async_req);
+ struct wb_open_pipe_state *state = talloc_get_type_abort(
+ req->private_data, struct wb_open_pipe_state);
+ NTSTATUS status;
+
+ status = wb_connect_recv(subreq);
+ TALLOC_FREE(subreq);
+ if (!NT_STATUS_IS_OK(status)) {
+ async_req_error(req, status);
+ return;
+ }
+ state->wb_ctx->is_priv = true;
+ async_req_done(req);
+}
+
+static NTSTATUS wb_open_pipe_recv(struct async_req *req)
+{
+ return async_req_simple_recv(req);
+}
+
+struct wb_trans_state {
+ struct wb_trans_state *prev, *next;
+ struct wb_context *wb_ctx;
+ struct event_context *ev;
+ struct winbindd_request *wb_req;
+ struct winbindd_response *wb_resp;
+ int num_retries;
+ bool need_priv;
+};
+
+static void wb_trans_connect_done(struct async_req *subreq);
+static void wb_trans_done(struct async_req *subreq);
+static void wb_trans_retry_wait_done(struct async_req *subreq);
+
+static void wb_trigger_trans(struct async_req *req)
+{
+ struct wb_trans_state *state = talloc_get_type_abort(
+ req->private_data, struct wb_trans_state);
+ struct async_req *subreq;
+
+ if ((state->wb_ctx->fd == -1)
+ || (state->need_priv && !state->wb_ctx->is_priv)) {
+
+ subreq = wb_open_pipe_send(state, state->ev, state->wb_ctx,
+ state->need_priv);
+ if (async_req_nomem(subreq, req)) {
+ return;
+ }
+ subreq->async.fn = wb_trans_connect_done;
+ subreq->async.priv = req;
+ return;
+ }
+
+ subreq = wb_int_trans_send(state, state->ev, state->wb_ctx->fd,
+ state->wb_req);
+ if (async_req_nomem(subreq, req)) {
+ return;
+ }
+ subreq->async.fn = wb_trans_done;
+ subreq->async.priv = req;
+}
+
+struct async_req *wb_trans_send(TALLOC_CTX *mem_ctx, struct event_context *ev,
+ struct wb_context *wb_ctx, bool need_priv,
+ const struct winbindd_request *wb_req)
+{
+ struct async_req *result;
+ struct wb_trans_state *state;
+
+ result = async_req_new(mem_ctx);
+ if (result == NULL) {
+ return NULL;
+ }
+ state = talloc(result, struct wb_trans_state);
+ if (state == NULL) {
+ goto fail;
+ }
+ result->private_data = state;
+
+ state->wb_ctx = wb_ctx;
+ state->ev = ev;
+ state->wb_req = winbindd_request_copy(state, wb_req);
+ if (state->wb_req == NULL) {
+ goto fail;
+ }
+ state->num_retries = 10;
+ state->need_priv = need_priv;
+
+ if (!async_req_enqueue(wb_ctx->queue, ev, result, wb_trigger_trans)) {
+ goto fail;
+ }
+ return result;
+
+ fail:
+ TALLOC_FREE(result);
+ return NULL;
+}
+
+static bool wb_trans_retry(struct async_req *req,
+ struct wb_trans_state *state,
+ NTSTATUS status)
+{
+ struct async_req *subreq;
+
+ if (NT_STATUS_IS_OK(status)) {
+ return false;
+ }
+
+ if (NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_NAME_NOT_FOUND)
+ || NT_STATUS_EQUAL(status, NT_STATUS_ACCESS_DENIED)) {
+ /*
+ * Winbind not around or we can't connect to the pipe. Fail
+ * immediately.
+ */
+ async_req_error(req, status);
+ return true;
+ }
+
+ state->num_retries -= 1;
+ if (state->num_retries == 0) {
+ async_req_error(req, status);
+ return true;
+ }
+
+ /*
+ * The transfer as such failed, retry after one second
+ */
+
+ if (state->wb_ctx->fd != -1) {
+ close(state->wb_ctx->fd);
+ state->wb_ctx->fd = -1;
+ }
+
+ subreq = async_wait_send(state, state->ev, timeval_set(1, 0));
+ if (async_req_nomem(subreq, req)) {
+ return true;
+ }
+
+ subreq->async.fn = wb_trans_retry_wait_done;
+ subreq->async.priv = req;
+ return true;
+}
+
+static void wb_trans_retry_wait_done(struct async_req *subreq)
+{
+ struct async_req *req = talloc_get_type_abort(
+ subreq->async.priv, struct async_req);
+ struct wb_trans_state *state = talloc_get_type_abort(
+ req->private_data, struct wb_trans_state);
+ NTSTATUS status;
+
+ status = async_wait_recv(subreq);
+ TALLOC_FREE(subreq);
+ if (!NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT)) {
+ async_req_error(req, status);
+ return;
+ }
+
+ subreq = wb_open_pipe_send(state, state->ev, state->wb_ctx,
+ state->need_priv);
+ if (async_req_nomem(subreq, req)) {
+ return;
+ }
+ subreq->async.fn = wb_trans_connect_done;
+ subreq->async.priv = req;
+}
+
+static void wb_trans_connect_done(struct async_req *subreq)
+{
+ struct async_req *req = talloc_get_type_abort(
+ subreq->async.priv, struct async_req);
+ struct wb_trans_state *state = talloc_get_type_abort(
+ req->private_data, struct wb_trans_state);
+ NTSTATUS status;
+
+ status = wb_open_pipe_recv(subreq);
+ TALLOC_FREE(subreq);
+
+ if (wb_trans_retry(req, state, status)) {
+ return;
+ }
+
+ subreq = wb_int_trans_send(state, state->ev, state->wb_ctx->fd,
+ state->wb_req);
+ if (async_req_nomem(subreq, req)) {
+ return;
+ }
+
+ subreq->async.fn = wb_trans_done;
+ subreq->async.priv = req;
+}
+
+static void wb_trans_done(struct async_req *subreq)
+{
+ struct async_req *req = talloc_get_type_abort(
+ subreq->async.priv, struct async_req);
+ struct wb_trans_state *state = talloc_get_type_abort(
+ req->private_data, struct wb_trans_state);
+ NTSTATUS status;
+
+ status = wb_int_trans_recv(subreq, state, &state->wb_resp);
+ TALLOC_FREE(subreq);
+
+ if (wb_trans_retry(req, state, status)) {
+ return;
+ }
+
+ async_req_done(req);
+}
+
+NTSTATUS wb_trans_recv(struct async_req *req, TALLOC_CTX *mem_ctx,
+ struct winbindd_response **presponse)
+{
+ struct wb_trans_state *state = talloc_get_type_abort(
+ req->private_data, struct wb_trans_state);
+ NTSTATUS status;
+
+ if (async_req_is_error(req, &status)) {
+ return status;
+ }
+
+ *presponse = talloc_move(mem_ctx, &state->wb_resp);
+ return NT_STATUS_OK;
+}