From 134b2488c82ae13392121f71e4960178a38f3e01 Mon Sep 17 00:00:00 2001 From: Volker Lendecke Date: Fri, 28 Oct 2005 11:02:42 +0000 Subject: r11369: Implement socket_connect_multi: Connect to multiple ipv4 tcp ports in sequence, with a 2-millisecond timeout between firing the syn packets. Build smbcli_sock_connect_send upon that. Volker (This used to be commit 5718df44d90d113304c5deed1e2e7f82ff9e928f) --- source4/lib/socket/connect_multi.c | 266 +++++++++++++++++++++++++++++++++++++ 1 file changed, 266 insertions(+) create mode 100644 source4/lib/socket/connect_multi.c (limited to 'source4/lib/socket/connect_multi.c') diff --git a/source4/lib/socket/connect_multi.c b/source4/lib/socket/connect_multi.c new file mode 100644 index 0000000000..2724601ad0 --- /dev/null +++ b/source4/lib/socket/connect_multi.c @@ -0,0 +1,266 @@ +/* + Unix SMB/CIFS implementation. + + Fire connect requests to a host and a number of ports, with a timeout + between the connect request. Return if the first connect comes back + successfully or return the last error. + + 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 + the Free Software Foundation; either version 2 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, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#include "includes.h" +#include "lib/socket/socket.h" +#include "lib/events/events.h" +#include "libcli/composite/composite.h" + + +struct connect_multi_state { + struct composite_context *ctx; + const char *server_address; + int num_ports; + uint16_t *ports; + + struct socket_context *result; + uint16_t result_port; + + int num_connects_sent, num_connects_in_fly; + struct fd_event **write_events; + struct socket_context **sockets; + struct timed_event *next_timeout; +}; + +static void connect_multi_connect_handler(struct event_context *ev, + struct fd_event *fde, + uint16_t flags, void *p); +static NTSTATUS connect_multi_next_socket(struct connect_multi_state *state); +static void connect_multi_fire_next(struct event_context *ev, + struct timed_event *te, + struct timeval tv, void *p); + +struct composite_context *socket_connect_multi_send(TALLOC_CTX *mem_ctx, + const char *server_address, + int num_server_ports, + uint16_t *server_ports, + struct event_context *event_ctx) +{ + struct composite_context *result; + struct connect_multi_state *state; + int i; + + result = talloc_zero(mem_ctx, struct composite_context); + if (result == NULL) goto failed; + result->state = COMPOSITE_STATE_IN_PROGRESS; + result->event_ctx = event_ctx; + + state = talloc(result, struct connect_multi_state); + if (state == NULL) goto failed; + state->ctx = result; + result->private_data = state; + + state->server_address = talloc_strdup(state, server_address); + if (state->server_address == NULL) goto failed; + + state->num_ports = num_server_ports; + state->ports = talloc_array(state, uint16_t, state->num_ports); + if (state->ports == NULL) goto failed; + + for (i=0; inum_ports; i++) { + state->ports[i] = server_ports[i]; + } + + state->sockets = + talloc_array(state, struct socket_context *, state->num_ports); + if (state->sockets == NULL) goto failed; + + state->write_events = + talloc_array(state, struct fd_event *, state->num_ports); + if (state->write_events == NULL) goto failed; + + state->num_connects_sent = 0; + state->num_connects_in_fly = 0; + + result->status = connect_multi_next_socket(state); + + if (!NT_STATUS_IS_OK(result->status)) { + composite_trigger_error(result); + return result; + } + + return result; + + failed: + talloc_free(result); + return NULL; +} + +static NTSTATUS connect_multi_next_socket(struct connect_multi_state *state) +{ + NTSTATUS status; + int res, next = state->num_connects_sent; + + status = socket_create("ipv4", SOCKET_TYPE_STREAM, + &state->sockets[next], 0); + if (!NT_STATUS_IS_OK(status)) { + return status; + } + + res = set_blocking(socket_get_fd(state->sockets[next]), False); + if (res != 0) { + return map_nt_error_from_unix(errno); + } + + talloc_steal(state->sockets, state->sockets[next]); + + status = socket_connect(state->sockets[next], NULL, 0, + state->server_address, state->ports[next], 0); + + if (!NT_STATUS_IS_OK(status) && + !NT_STATUS_EQUAL(status, NT_STATUS_MORE_PROCESSING_REQUIRED)) { + return status; + } + + state->write_events[next] = + event_add_fd(state->ctx->event_ctx, state->write_events, + socket_get_fd(state->sockets[next]), + EVENT_FD_WRITE, + connect_multi_connect_handler, state); + + if (state->write_events[next] == NULL) { + return NT_STATUS_NO_MEMORY; + } + + state->num_connects_sent += 1; + state->num_connects_in_fly += 1; + + if (state->num_ports > state->num_connects_sent) { + state->next_timeout = + event_add_timed(state->ctx->event_ctx, state, + timeval_current_ofs(0, 2000), + connect_multi_fire_next, state); + if (state->next_timeout == NULL) { + talloc_free(state->sockets[next]); + state->sockets[next] = NULL; + talloc_free(state->write_events[next]); + state->write_events[next] = NULL; + return NT_STATUS_NO_MEMORY; + } + } + + return NT_STATUS_OK; +} + +static void connect_multi_fire_next(struct event_context *ev, + struct timed_event *te, + struct timeval tv, void *p) +{ + struct connect_multi_state *state = + talloc_get_type(p, struct connect_multi_state); + + state->ctx->status = connect_multi_next_socket(state); + if (!composite_is_ok(state->ctx)) return; +} + +static void connect_multi_connect_handler(struct event_context *ev, + struct fd_event *fde, + uint16_t flags, void *p) +{ + struct connect_multi_state *state = + talloc_get_type(p, struct connect_multi_state); + int i; + + for (i=0; inum_connects_sent; i++) { + if (fde == state->write_events[i]) { + break; + } + } + + if (i == state->num_connects_sent) { + composite_error(state->ctx, NT_STATUS_INTERNAL_ERROR); + return; + } + + state->num_connects_in_fly -= 1; + + state->ctx->status = socket_connect_complete(state->sockets[i], 0); + if (NT_STATUS_IS_OK(state->ctx->status)) { + state->result = talloc_steal(state, state->sockets[i]); + state->result_port = state->ports[i]; + talloc_free(state->sockets); + state->sockets = NULL; + talloc_free(state->write_events); + state->write_events = NULL; + composite_done(state->ctx); + return; + } + + talloc_free(state->sockets[i]); + state->sockets[i] = NULL; + + if ((state->num_connects_in_fly == 0) && + (state->num_connects_sent == state->num_ports)) { + composite_error(state->ctx, state->ctx->status); + return; + } + + if (state->num_connects_in_fly != 0) { + /* Waiting for something to happen on the net or the next + * timeout to trigger */ + return; + } + + SMB_ASSERT(state->num_connects_sent < state->num_ports); + SMB_ASSERT(state->next_timeout != NULL); + + /* There are ports left but nothing on the net, so trigger the next + * one immediately. */ + talloc_free(state->next_timeout); + state->next_timeout = + event_add_timed(state->ctx->event_ctx, state, timeval_zero(), + connect_multi_fire_next, state); + if (composite_nomem(state->next_timeout, state->ctx)) return; +} + +NTSTATUS socket_connect_multi_recv(struct composite_context *ctx, + TALLOC_CTX *mem_ctx, + struct socket_context **result, + uint16_t *port) +{ + NTSTATUS status = composite_wait(ctx); + if (NT_STATUS_IS_OK(status)) { + struct connect_multi_state *state = + talloc_get_type(ctx->private_data, + struct connect_multi_state); + *result = talloc_steal(mem_ctx, state->result); + *port = state->result_port; + } + talloc_free(ctx); + return status; +} + +NTSTATUS socket_connect_multi(TALLOC_CTX *mem_ctx, + const char *server_address, + int num_server_ports, uint16_t *server_ports, + struct event_context *event_ctx, + struct socket_context **result, + uint16_t *result_port) +{ + struct composite_context *ctx = + socket_connect_multi_send(mem_ctx, server_address, + num_server_ports, server_ports, + event_ctx); + return socket_connect_multi_recv(ctx, mem_ctx, result, result_port); +} -- cgit