s4:librpc/rpc: don't do async requests if gensec doesn't support async replies (bug...
[Samba/gebeck_regimport.git] / source4 / lib / socket / connect_multi.c
blobc8231b3cff3c95a3258beb1ce51d087b3cf0dd6e
1 /*
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 3 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, see <http://www.gnu.org/licenses/>.
24 #include "includes.h"
25 #include "lib/socket/socket.h"
26 #include "lib/events/events.h"
27 #include "libcli/composite/composite.h"
28 #include "libcli/resolve/resolve.h"
30 #define MULTI_PORT_DELAY 2000 /* microseconds */
33 overall state
35 struct connect_multi_state {
36 struct socket_address **server_address;
37 unsigned num_address, current_address, current_port;
38 int num_ports;
39 uint16_t *ports;
41 struct socket_context *sock;
42 uint16_t result_port;
44 int num_connects_sent, num_connects_recv;
46 struct socket_connect_multi_ex *ex;
50 state of an individual socket_connect_send() call
52 struct connect_one_state {
53 struct composite_context *result;
54 struct socket_context *sock;
55 struct socket_address *addr;
58 static void continue_resolve_name(struct composite_context *creq);
59 static void connect_multi_timer(struct tevent_context *ev,
60 struct tevent_timer *te,
61 struct timeval tv, void *p);
62 static void connect_multi_next_socket(struct composite_context *result);
63 static void continue_one(struct composite_context *creq);
64 static void continue_one_ex(struct tevent_req *subreq);
67 setup an async socket_connect, with multiple ports
69 _PUBLIC_ struct composite_context *socket_connect_multi_ex_send(
70 TALLOC_CTX *mem_ctx,
71 const char *server_name,
72 int num_server_ports,
73 uint16_t *server_ports,
74 struct resolve_context *resolve_ctx,
75 struct tevent_context *event_ctx,
76 struct socket_connect_multi_ex *ex)
78 struct composite_context *result;
79 struct connect_multi_state *multi;
80 int i;
82 struct nbt_name name;
83 struct composite_context *creq;
85 result = talloc_zero(mem_ctx, struct composite_context);
86 if (result == NULL) return NULL;
87 result->state = COMPOSITE_STATE_IN_PROGRESS;
88 result->event_ctx = event_ctx;
90 multi = talloc_zero(result, struct connect_multi_state);
91 if (composite_nomem(multi, result)) goto failed;
92 result->private_data = multi;
94 multi->num_ports = num_server_ports;
95 multi->ports = talloc_array(multi, uint16_t, multi->num_ports);
96 if (composite_nomem(multi->ports, result)) goto failed;
98 for (i=0; i<multi->num_ports; i++) {
99 multi->ports[i] = server_ports[i];
102 multi->ex = ex;
105 we don't want to do the name resolution separately
106 for each port, so start it now, then only start on
107 the real sockets once we have an IP
109 make_nbt_name_server(&name, server_name);
111 creq = resolve_name_all_send(resolve_ctx, multi, 0, multi->ports[0], &name, result->event_ctx);
112 if (composite_nomem(creq, result)) goto failed;
114 composite_continue(result, creq, continue_resolve_name, result);
116 return result;
119 failed:
120 composite_error(result, result->status);
121 return result;
125 start connecting to the next socket/port in the list
127 static void connect_multi_next_socket(struct composite_context *result)
129 struct connect_multi_state *multi = talloc_get_type(result->private_data,
130 struct connect_multi_state);
131 struct connect_one_state *state;
132 struct composite_context *creq;
133 int next = multi->num_connects_sent;
135 if (next == multi->num_address * multi->num_ports) {
136 /* don't do anything, just wait for the existing ones to finish */
137 return;
140 if (multi->current_address == multi->num_address) {
141 multi->current_address = 0;
142 multi->current_port += 1;
144 multi->num_connects_sent += 1;
146 if (multi->server_address == NULL || multi->server_address[multi->current_address] == NULL) {
147 composite_error(result, NT_STATUS_OBJECT_NAME_NOT_FOUND);
148 return;
151 state = talloc(multi, struct connect_one_state);
152 if (composite_nomem(state, result)) return;
154 state->result = result;
155 result->status = socket_create(multi->server_address[multi->current_address]->family,
156 SOCKET_TYPE_STREAM, &state->sock, 0);
157 if (!composite_is_ok(result)) return;
159 state->addr = socket_address_copy(state, multi->server_address[multi->current_address]);
160 if (composite_nomem(state->addr, result)) return;
162 socket_address_set_port(state->addr, multi->ports[multi->current_port]);
164 talloc_steal(state, state->sock);
166 creq = socket_connect_send(state->sock, NULL,
167 state->addr, 0,
168 result->event_ctx);
169 if (composite_nomem(creq, result)) return;
170 talloc_steal(state, creq);
172 multi->current_address++;
173 composite_continue(result, creq, continue_one, state);
175 /* if there are more ports / addresses to go then setup a timer to fire when we have waited
176 for a couple of milli-seconds, when that goes off we try the next port regardless
177 of whether this port has completed */
178 if (multi->num_ports * multi->num_address > multi->num_connects_sent) {
179 /* note that this timer is a child of the single
180 connect attempt state, so it will go away when this
181 request completes */
182 tevent_add_timer(result->event_ctx, state,
183 timeval_current_ofs_usec(MULTI_PORT_DELAY),
184 connect_multi_timer, result);
189 a timer has gone off telling us that we should try the next port
191 static void connect_multi_timer(struct tevent_context *ev,
192 struct tevent_timer *te,
193 struct timeval tv, void *p)
195 struct composite_context *result = talloc_get_type(p, struct composite_context);
196 connect_multi_next_socket(result);
201 recv name resolution reply then send the next connect
203 static void continue_resolve_name(struct composite_context *creq)
205 struct composite_context *result = talloc_get_type(creq->async.private_data,
206 struct composite_context);
207 struct connect_multi_state *multi = talloc_get_type(result->private_data,
208 struct connect_multi_state);
209 struct socket_address **addr;
210 unsigned i;
212 result->status = resolve_name_all_recv(creq, multi, &addr, NULL);
213 if (!composite_is_ok(result)) return;
215 for(i=0; addr[i]; i++);
216 multi->num_address = i;
217 multi->server_address = talloc_steal(multi, addr);
219 connect_multi_next_socket(result);
223 one of our socket_connect_send() calls hash finished. If it got a
224 connection or there are none left then we are done
226 static void continue_one(struct composite_context *creq)
228 struct connect_one_state *state = talloc_get_type(creq->async.private_data,
229 struct connect_one_state);
230 struct composite_context *result = state->result;
231 struct connect_multi_state *multi = talloc_get_type(result->private_data,
232 struct connect_multi_state);
233 NTSTATUS status;
235 status = socket_connect_recv(creq);
237 if (multi->ex) {
238 struct tevent_req *subreq;
240 subreq = multi->ex->establish_send(state,
241 result->event_ctx,
242 state->sock,
243 state->addr,
244 multi->ex->private_data);
245 if (composite_nomem(subreq, result)) return;
246 tevent_req_set_callback(subreq, continue_one_ex, state);
247 return;
250 multi->num_connects_recv++;
252 if (NT_STATUS_IS_OK(status)) {
253 multi->sock = talloc_steal(multi, state->sock);
254 multi->result_port = state->addr->port;
257 talloc_free(state);
259 if (NT_STATUS_IS_OK(status) ||
260 multi->num_connects_recv == (multi->num_address * multi->num_ports)) {
261 result->status = status;
262 composite_done(result);
263 return;
266 /* try the next port */
267 connect_multi_next_socket(result);
271 one of our multi->ex->establish_send() calls hash finished. If it got a
272 connection or there are none left then we are done
274 static void continue_one_ex(struct tevent_req *subreq)
276 struct connect_one_state *state =
277 tevent_req_callback_data(subreq,
278 struct connect_one_state);
279 struct composite_context *result = state->result;
280 struct connect_multi_state *multi =
281 talloc_get_type_abort(result->private_data,
282 struct connect_multi_state);
283 NTSTATUS status;
284 multi->num_connects_recv++;
286 status = multi->ex->establish_recv(subreq);
287 TALLOC_FREE(subreq);
289 if (NT_STATUS_IS_OK(status)) {
290 multi->sock = talloc_steal(multi, state->sock);
291 multi->result_port = state->addr->port;
294 talloc_free(state);
296 if (NT_STATUS_IS_OK(status) ||
297 multi->num_connects_recv == (multi->num_address * multi->num_ports)) {
298 result->status = status;
299 composite_done(result);
300 return;
303 /* try the next port */
304 connect_multi_next_socket(result);
308 async recv routine for socket_connect_multi()
310 _PUBLIC_ NTSTATUS socket_connect_multi_ex_recv(struct composite_context *ctx,
311 TALLOC_CTX *mem_ctx,
312 struct socket_context **sock,
313 uint16_t *port)
315 NTSTATUS status = composite_wait(ctx);
316 if (NT_STATUS_IS_OK(status)) {
317 struct connect_multi_state *multi =
318 talloc_get_type(ctx->private_data,
319 struct connect_multi_state);
320 *sock = talloc_steal(mem_ctx, multi->sock);
321 *port = multi->result_port;
323 talloc_free(ctx);
324 return status;
327 NTSTATUS socket_connect_multi_ex(TALLOC_CTX *mem_ctx,
328 const char *server_address,
329 int num_server_ports, uint16_t *server_ports,
330 struct resolve_context *resolve_ctx,
331 struct tevent_context *event_ctx,
332 struct socket_connect_multi_ex *ex,
333 struct socket_context **result,
334 uint16_t *result_port)
336 struct composite_context *ctx =
337 socket_connect_multi_ex_send(mem_ctx, server_address,
338 num_server_ports, server_ports,
339 resolve_ctx,
340 event_ctx,
341 ex);
342 return socket_connect_multi_ex_recv(ctx, mem_ctx, result, result_port);
346 setup an async socket_connect, with multiple ports
348 _PUBLIC_ struct composite_context *socket_connect_multi_send(
349 TALLOC_CTX *mem_ctx,
350 const char *server_name,
351 int num_server_ports,
352 uint16_t *server_ports,
353 struct resolve_context *resolve_ctx,
354 struct tevent_context *event_ctx)
356 return socket_connect_multi_ex_send(mem_ctx,
357 server_name,
358 num_server_ports,
359 server_ports,
360 resolve_ctx,
361 event_ctx,
362 NULL); /* ex */
366 async recv routine for socket_connect_multi()
368 _PUBLIC_ NTSTATUS socket_connect_multi_recv(struct composite_context *ctx,
369 TALLOC_CTX *mem_ctx,
370 struct socket_context **sock,
371 uint16_t *port)
373 return socket_connect_multi_ex_recv(ctx, mem_ctx, sock, port);
376 NTSTATUS socket_connect_multi(TALLOC_CTX *mem_ctx,
377 const char *server_address,
378 int num_server_ports, uint16_t *server_ports,
379 struct resolve_context *resolve_ctx,
380 struct tevent_context *event_ctx,
381 struct socket_context **result,
382 uint16_t *result_port)
384 return socket_connect_multi_ex(mem_ctx,
385 server_address,
386 num_server_ports,
387 server_ports,
388 resolve_ctx,
389 event_ctx,
390 NULL, /* ex */
391 result,
392 result_port);