2 Unix SMB/CIFS implementation.
4 Fire connect requests to a host and a number of ports, with a timeout
5 between the connect request. Return if the first connect comes back
6 successfully or return the last error.
8 Copyright (C) Volker Lendecke 2005
10 This program is free software; you can redistribute it and/or modify
11 it under the terms of the GNU General Public License as published by
12 the Free Software Foundation; either version 2 of the License, or
13 (at your option) any later version.
15 This program is distributed in the hope that it will be useful,
16 but WITHOUT ANY WARRANTY; without even the implied warranty of
17 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 GNU General Public License for more details.
20 You should have received a copy of the GNU General Public License
21 along with this program; if not, write to the Free Software
22 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
26 #include "lib/socket/socket.h"
27 #include "lib/events/events.h"
28 #include "libcli/composite/composite.h"
29 #include "librpc/gen_ndr/nbt.h"
31 #define MULTI_PORT_DELAY 2000 /* microseconds */
36 struct connect_multi_state {
37 const char *server_address;
41 struct socket_context *sock;
44 int num_connects_sent, num_connects_recv;
48 state of an individual socket_connect_send() call
50 struct connect_one_state {
51 struct composite_context *result;
52 struct socket_context *sock;
56 static void continue_resolve_name(struct composite_context *creq);
57 static void connect_multi_timer(struct event_context *ev,
58 struct timed_event *te,
59 struct timeval tv, void *p);
60 static void connect_multi_next_socket(struct composite_context *result);
61 static void continue_one(struct composite_context *creq);
64 setup an async socket_connect, with multiple ports
66 struct composite_context *socket_connect_multi_send(TALLOC_CTX *mem_ctx,
67 const char *server_address,
69 uint16_t *server_ports,
70 struct event_context *event_ctx)
72 struct composite_context *result;
73 struct connect_multi_state *multi;
76 result = talloc_zero(mem_ctx, struct composite_context);
77 if (result == NULL) return NULL;
78 result->state = COMPOSITE_STATE_IN_PROGRESS;
79 result->event_ctx = event_ctx;
81 multi = talloc_zero(result, struct connect_multi_state);
82 if (composite_nomem(multi, result)) goto failed;
83 result->private_data = multi;
85 multi->server_address = talloc_strdup(multi, server_address);
86 if (composite_nomem(multi->server_address, result)) goto failed;
88 multi->num_ports = num_server_ports;
89 multi->ports = talloc_array(multi, uint16_t, multi->num_ports);
90 if (composite_nomem(multi->ports, result)) goto failed;
92 for (i=0; i<multi->num_ports; i++) {
93 multi->ports[i] = server_ports[i];
96 if (!is_ipaddress(server_address)) {
98 we don't want to do the name resolution separately
99 for each port, so start it now, then only start on
100 the real sockets once we have an IP
102 struct nbt_name name;
103 struct composite_context *creq;
104 make_nbt_name_client(&name, server_address);
105 creq = resolve_name_send(&name, result->event_ctx,
106 lp_name_resolve_order());
107 if (composite_nomem(creq, result)) goto failed;
108 composite_continue(result, creq, continue_resolve_name, result);
112 /* now we've setup the state we can process the first socket */
113 connect_multi_next_socket(result);
115 if (!NT_STATUS_IS_OK(result->status)) {
122 composite_trigger_error(result);
127 start connecting to the next socket/port in the list
129 static void connect_multi_next_socket(struct composite_context *result)
131 struct connect_multi_state *multi = talloc_get_type(result->private_data,
132 struct connect_multi_state);
133 struct connect_one_state *state;
134 struct composite_context *creq;
135 int next = multi->num_connects_sent;
137 if (next == multi->num_ports) {
138 /* don't do anything, just wait for the existing ones to finish */
142 multi->num_connects_sent += 1;
144 state = talloc(multi, struct connect_one_state);
145 if (composite_nomem(state, result)) return;
147 state->result = result;
148 state->port = multi->ports[next];
150 result->status = socket_create("ipv4", SOCKET_TYPE_STREAM, &state->sock, 0);
151 if (!composite_is_ok(result)) return;
153 talloc_steal(state, state->sock);
155 creq = socket_connect_send(state->sock, NULL, 0,
156 multi->server_address, state->port, 0, result->event_ctx);
157 if (composite_nomem(creq, result)) return;
158 talloc_steal(state, creq);
160 composite_continue(result, creq, continue_one, state);
162 /* if there are more ports to go then setup a timer to fire when we have waited
163 for a couple of milli-seconds, when that goes off we try the next port regardless
164 of whether this port has completed */
165 if (multi->num_ports > multi->num_connects_sent) {
166 /* note that this timer is a child of the single
167 connect attempt state, so it will go away when this
169 event_add_timed(result->event_ctx, state,
170 timeval_current_ofs(0, MULTI_PORT_DELAY),
171 connect_multi_timer, result);
176 a timer has gone off telling us that we should try the next port
178 static void connect_multi_timer(struct event_context *ev,
179 struct timed_event *te,
180 struct timeval tv, void *p)
182 struct composite_context *result = talloc_get_type(p, struct composite_context);
183 connect_multi_next_socket(result);
188 recv name resolution reply then send the next connect
190 static void continue_resolve_name(struct composite_context *creq)
192 struct composite_context *result = talloc_get_type(creq->async.private_data,
193 struct composite_context);
194 struct connect_multi_state *multi = talloc_get_type(result->private_data,
195 struct connect_multi_state);
198 result->status = resolve_name_recv(creq, multi, &addr);
199 if (!composite_is_ok(result)) return;
201 multi->server_address = addr;
203 connect_multi_next_socket(result);
207 one of our socket_connect_send() calls hash finished. If it got a
208 connection or there are none left then we are done
210 static void continue_one(struct composite_context *creq)
212 struct connect_one_state *state = talloc_get_type(creq->async.private_data,
213 struct connect_one_state);
214 struct composite_context *result = state->result;
215 struct connect_multi_state *multi = talloc_get_type(result->private_data,
216 struct connect_multi_state);
218 multi->num_connects_recv++;
220 status = socket_connect_recv(creq);
222 if (NT_STATUS_IS_OK(status)) {
223 multi->sock = talloc_steal(multi, state->sock);
224 multi->result_port = state->port;
229 if (NT_STATUS_IS_OK(status) ||
230 multi->num_connects_recv == multi->num_ports) {
231 result->status = status;
232 composite_done(result);
236 /* try the next port */
237 connect_multi_next_socket(result);
241 async recv routine for socket_connect_multi()
243 NTSTATUS socket_connect_multi_recv(struct composite_context *ctx,
245 struct socket_context **sock,
248 NTSTATUS status = composite_wait(ctx);
249 if (NT_STATUS_IS_OK(status)) {
250 struct connect_multi_state *multi =
251 talloc_get_type(ctx->private_data,
252 struct connect_multi_state);
253 *sock = talloc_steal(mem_ctx, multi->sock);
254 *port = multi->result_port;
260 NTSTATUS socket_connect_multi(TALLOC_CTX *mem_ctx,
261 const char *server_address,
262 int num_server_ports, uint16_t *server_ports,
263 struct event_context *event_ctx,
264 struct socket_context **result,
265 uint16_t *result_port)
267 struct composite_context *ctx =
268 socket_connect_multi_send(mem_ctx, server_address,
269 num_server_ports, server_ports,
271 return socket_connect_multi_recv(ctx, mem_ctx, result, result_port);