samldb: Allow automatic generation of mAPIIDs
[sfrench/samba-autobuild/.git] / source4 / lib / socket / connect_multi.c
1 /* 
2    Unix SMB/CIFS implementation.
3
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.
7
8    Copyright (C) Volker Lendecke 2005
9    
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.
14    
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.
19    
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/>.
22 */
23
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"
29
30 #define MULTI_PORT_DELAY 2000 /* microseconds */
31
32 /*
33   overall state
34 */
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;
40
41         struct socket_context *sock;
42         uint16_t result_port;
43
44         int num_connects_sent, num_connects_recv;
45
46         struct socket_connect_multi_ex *ex;
47 };
48
49 /*
50   state of an individual socket_connect_send() call
51 */
52 struct connect_one_state {
53         struct composite_context *result;
54         struct socket_context *sock;
55         struct socket_address *addr;
56 };
57
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);
65
66 /*
67   setup an async socket_connect, with multiple ports
68 */
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)
77 {
78         struct composite_context *result;
79         struct connect_multi_state *multi;
80         int i;
81
82         struct nbt_name name;
83         struct composite_context *creq;
84                 
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;
89
90         multi = talloc_zero(result, struct connect_multi_state);
91         if (composite_nomem(multi, result)) goto failed;
92         result->private_data = multi;
93
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;
97
98         for (i=0; i<multi->num_ports; i++) {
99                 multi->ports[i] = server_ports[i];
100         }
101
102         multi->ex = ex;
103
104         /*  
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
108         */
109         make_nbt_name_server(&name, server_name);
110
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;
113
114         composite_continue(result, creq, continue_resolve_name, result);
115
116         return result;
117
118
119  failed:
120         composite_error(result, result->status);
121         return result;
122 }
123
124 /*
125   start connecting to the next socket/port in the list
126 */
127 static void connect_multi_next_socket(struct composite_context *result)
128 {
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;
134
135         if (next == multi->num_address * multi->num_ports) {
136                 /* don't do anything, just wait for the existing ones to finish */
137                 return;
138         }
139
140         if (multi->current_address == multi->num_address) {
141                 multi->current_address = 0;
142                 multi->current_port += 1;
143         }
144         multi->num_connects_sent += 1;
145
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;
149         }
150
151         state = talloc(multi, struct connect_one_state);
152         if (composite_nomem(state, result)) return;
153
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;
158
159         state->addr = socket_address_copy(state, multi->server_address[multi->current_address]);
160         if (composite_nomem(state->addr, result)) return;
161
162         socket_address_set_port(state->addr, multi->ports[multi->current_port]);
163
164         talloc_steal(state, state->sock);
165
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);
171
172         multi->current_address++;
173         composite_continue(result, creq, continue_one, state);
174
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);
185         }
186 }
187
188 /*
189   a timer has gone off telling us that we should try the next port
190 */
191 static void connect_multi_timer(struct tevent_context *ev,
192                                 struct tevent_timer *te,
193                                 struct timeval tv, void *p)
194 {
195         struct composite_context *result = talloc_get_type(p, struct composite_context);
196         connect_multi_next_socket(result);
197 }
198
199
200 /*
201   recv name resolution reply then send the next connect
202 */
203 static void continue_resolve_name(struct composite_context *creq)
204 {
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;
211
212         result->status = resolve_name_all_recv(creq, multi, &addr, NULL);
213         if (!composite_is_ok(result)) return;
214
215         for(i=0; addr[i]; i++);
216         multi->num_address = i;
217         multi->server_address = talloc_steal(multi, addr);
218
219         connect_multi_next_socket(result);
220 }
221
222 /*
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
225 */
226 static void continue_one(struct composite_context *creq)
227 {
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;
234
235         status = socket_connect_recv(creq);
236
237         if (multi->ex) {
238                 struct tevent_req *subreq;
239
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;
248         }
249
250         multi->num_connects_recv++;
251
252         if (NT_STATUS_IS_OK(status)) {
253                 multi->sock = talloc_steal(multi, state->sock);
254                 multi->result_port = state->addr->port;
255         }
256
257         talloc_free(state);
258
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;
264         }
265
266         /* try the next port */
267         connect_multi_next_socket(result);
268 }
269
270 /*
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
273 */
274 static void continue_one_ex(struct tevent_req *subreq)
275 {
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++;
285
286         status = multi->ex->establish_recv(subreq);
287         TALLOC_FREE(subreq);
288
289         if (NT_STATUS_IS_OK(status)) {
290                 multi->sock = talloc_steal(multi, state->sock);
291                 multi->result_port = state->addr->port;
292         }
293
294         talloc_free(state);
295
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;
301         }
302
303         /* try the next port */
304         connect_multi_next_socket(result);
305 }
306
307 /*
308   async recv routine for socket_connect_multi()
309  */
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)
314 {
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;
322         }
323         talloc_free(ctx);
324         return status;
325 }
326
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)
335 {
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);
343 }
344
345 /*
346   setup an async socket_connect, with multiple ports
347 */
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)
355 {
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 */
363 }
364
365 /*
366   async recv routine for socket_connect_multi()
367  */
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)
372 {
373         return socket_connect_multi_ex_recv(ctx, mem_ctx, sock, port);
374 }
375
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)
383 {
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);
393 }