From a3847dd5c19d4097927d79ffb01577afead7af22 Mon Sep 17 00:00:00 2001 From: Volker Lendecke Date: Mon, 24 Oct 2005 09:34:12 +0000 Subject: r11274: Start a connection attempt to the DC's port 389. To do this properly, make socket_connect and ldap_connect properly async. Volker --- source/lib/socket/connect.c | 230 ++++++++++++++++++++++++++++++++------------ 1 file changed, 167 insertions(+), 63 deletions(-) (limited to 'source/lib/socket/connect.c') diff --git a/source/lib/socket/connect.c b/source/lib/socket/connect.c index 04902442e3e..244fdcbb063 100644 --- a/source/lib/socket/connect.c +++ b/source/lib/socket/connect.c @@ -1,10 +1,11 @@ /* Unix SMB/CIFS implementation. - implements a non-blocking connect operation that is aware of the samba4 events - system + implements a non-blocking connect operation that is aware of the samba4 + events system Copyright (C) Andrew Tridgell 2005 + Copyright (C) Volker Lendecke 2005 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 @@ -25,87 +26,190 @@ #include "lib/socket/socket.h" #include "lib/events/events.h" #include "librpc/gen_ndr/nbt.h" +#include "libcli/composite/composite.h" + + +struct connect_state { + struct composite_context *ctx; + struct socket_context *sock; + const char *my_address; + int my_port; + const char *server_address; + int server_port; + uint32_t flags; + struct fd_event *connect_ev; +}; + +static void socket_connect_handler(struct event_context *ev, + struct fd_event *fde, + uint16_t flags, void *private); +static void socket_connect_recv_addr(struct composite_context *ctx); +static void socket_connect_recv_conn(struct composite_context *ctx); + +struct composite_context *socket_connect_send(struct socket_context *sock, + const char *my_address, + int my_port, + const char *server_address, + int server_port, + uint32_t flags, + struct event_context *event_ctx) +{ + struct composite_context *result, *ctx; + struct connect_state *state; + + result = talloc_zero(NULL, struct composite_context); + if (result == NULL) goto failed; + result->state = COMPOSITE_STATE_IN_PROGRESS; + result->async.fn = NULL; + result->event_ctx = event_ctx; + + state = talloc(result, struct connect_state); + if (state == NULL) goto failed; + state->ctx = result; + result->private_data = state; + + state->sock = talloc_reference(state, sock); + if (state->sock == NULL) goto failed; + state->my_address = talloc_strdup(state, my_address); + if (state->sock == NULL) goto failed; + state->my_port = my_port; + state->server_address = talloc_strdup(state, server_address); + if (state->sock == NULL) goto failed; + state->server_port = server_port; + state->flags = flags; + set_blocking(socket_get_fd(sock), False); -/* - async name resolution handler for socket_connect_ev, returnes either - an IP address or 'localhost' (which is specially recognised) -*/ -static NTSTATUS connect_resolve(TALLOC_CTX *mem_ctx, const char *address, - struct event_context *ev, const char **ret_address) -{ - struct nbt_name name; + if (strcmp(sock->backend_name, "ipv4") == 0) { + struct nbt_name name; + make_nbt_name_client(&name, server_address); + ctx = resolve_name_send(&name, result->event_ctx, + lp_name_resolve_order()); + if (ctx == NULL) goto failed; + ctx->async.fn = socket_connect_recv_addr; + ctx->async.private_data = state; + return result; + } - name.name = address; - name.scope = NULL; - name.type = NBT_NAME_CLIENT; + ctx = talloc_zero(state, struct composite_context); + if (ctx == NULL) goto failed; + ctx->state = COMPOSITE_STATE_IN_PROGRESS; + ctx->event_ctx = event_ctx; + ctx->async.fn = socket_connect_recv_conn; + ctx->async.private_data = state; + + state->ctx->status = socket_connect(sock, my_address, my_port, + server_address, server_port, + flags); + if (NT_STATUS_IS_ERR(state->ctx->status) && + !NT_STATUS_EQUAL(state->ctx->status, + NT_STATUS_MORE_PROCESSING_REQUIRED)) { + composite_trigger_error(state->ctx); + return result; + } - return resolve_name(&name, mem_ctx, ret_address, ev); -} + state->connect_ev = event_add_fd(state->ctx->event_ctx, state, + socket_get_fd(sock), EVENT_FD_WRITE, + socket_connect_handler, ctx); + if (state->connect_ev == NULL) { + state->ctx->status = NT_STATUS_NO_MEMORY; + composite_trigger_error(state->ctx); + return result; + } + return result; + + failed: + talloc_free(result); + return NULL; +} /* handle write events on connect completion */ -static void socket_connect_handler(struct event_context *ev, struct fd_event *fde, +static void socket_connect_handler(struct event_context *ev, + struct fd_event *fde, uint16_t flags, void *private) { - NTSTATUS *status = (NTSTATUS *)private; - *status = NT_STATUS_OK; -} + struct composite_context *ctx = + talloc_get_type(private, struct composite_context); + struct connect_state *state = + talloc_get_type(ctx->async.private_data, + struct connect_state); + ctx->status = socket_connect_complete(state->sock, state->flags); + if (!composite_is_ok(ctx)) return; -/* - just like socket_connect() but other events can happen while the - connect is ongoing. This isn't as good as making the calling code - fully async during its connect phase, but at least it means that any - calling code that uses this won't interfere with code that is - properly async - */ -NTSTATUS socket_connect_ev(struct socket_context *sock, - const char *my_address, int my_port, - const char *server_address, int server_port, - uint32_t flags, struct event_context *ev) -{ - TALLOC_CTX *tmp_ctx = talloc_new(sock); - NTSTATUS status; - - /* - we resolve separately to ensure that the name resolutions happens - asynchronously + composite_done(ctx); +} - the check for ipv4 is ugly, but how to do it better? We don't want - to try to resolve unix pathnames here - */ - if (strcmp(sock->backend_name, "ipv4") == 0) { - status = connect_resolve(tmp_ctx, server_address, ev, &server_address); - if (!NT_STATUS_IS_OK(status)) { - talloc_free(tmp_ctx); - return status; - } +static void socket_connect_recv_addr(struct composite_context *ctx) +{ + struct connect_state *state = + talloc_get_type(ctx->async.private_data, struct connect_state); + const char *addr; + + state->ctx->status = resolve_name_recv(ctx, state, &addr); + if (!composite_is_ok(state->ctx)) return; + + ctx = talloc_zero(state, struct composite_context); + if (composite_nomem(ctx, state->ctx)) return; + ctx->state = COMPOSITE_STATE_IN_PROGRESS; + ctx->event_ctx = state->ctx->event_ctx; + ctx->async.fn = socket_connect_recv_conn; + ctx->async.private_data = state; + + state->ctx->status = socket_connect(state->sock, + state->my_address, + state->my_port, + state->server_address, + state->server_port, + state->flags); + if (NT_STATUS_IS_ERR(state->ctx->status) && + !NT_STATUS_EQUAL(state->ctx->status, + NT_STATUS_MORE_PROCESSING_REQUIRED)) { + composite_error(state->ctx, state->ctx->status); + return; } - set_blocking(socket_get_fd(sock), False); + state->connect_ev = event_add_fd(ctx->event_ctx, state, + socket_get_fd(state->sock), + EVENT_FD_WRITE, + socket_connect_handler, ctx); + if (composite_nomem(state->connect_ev, state->ctx)) return; - status = socket_connect(sock, my_address, my_port, - server_address, server_port, flags); - if (NT_STATUS_IS_ERR(status) && - !NT_STATUS_EQUAL(status, NT_STATUS_MORE_PROCESSING_REQUIRED)) { - return status; - } + return; +} - event_add_fd(ev, tmp_ctx, socket_get_fd(sock), EVENT_FD_WRITE, - socket_connect_handler, &status); +static void socket_connect_recv_conn(struct composite_context *ctx) +{ + struct connect_state *state = + talloc_get_type(ctx->async.private_data, struct connect_state); - while (NT_STATUS_EQUAL(status, STATUS_MORE_ENTRIES)) { - if (event_loop_once(ev) != 0) { - talloc_free(tmp_ctx); - return NT_STATUS_INTERNAL_ERROR; - } - } + state->ctx->status = ctx->status; + if (!composite_is_ok(state->ctx)) return; - status = socket_connect_complete(sock, flags); + /* We have to free the event context here because the continuation + * function might add an event for this socket directly. */ + talloc_free(state->connect_ev); - talloc_free(tmp_ctx); + composite_done(state->ctx); +} + +NTSTATUS socket_connect_recv(struct composite_context *ctx) +{ + NTSTATUS status = composite_wait(ctx); + talloc_free(ctx); return status; } + +NTSTATUS socket_connect_ev(struct socket_context *sock, + const char *my_address, int my_port, + const char *server_address, int server_port, + uint32_t flags, struct event_context *ev) +{ + struct composite_context *ctx; + ctx = socket_connect_send(sock, my_address, my_port, + server_address, server_port, flags, ev); + return socket_connect_recv(ctx); +} -- cgit