/src/samba/source4/lib/socket/connect_multi.c
Line | Count | Source |
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 | 0 | { |
78 | 0 | struct composite_context *result; |
79 | 0 | struct connect_multi_state *multi; |
80 | 0 | int i; |
81 | |
|
82 | 0 | struct nbt_name name; |
83 | 0 | struct composite_context *creq; |
84 | | |
85 | 0 | result = talloc_zero(mem_ctx, struct composite_context); |
86 | 0 | if (result == NULL) return NULL; |
87 | 0 | result->state = COMPOSITE_STATE_IN_PROGRESS; |
88 | 0 | result->event_ctx = event_ctx; |
89 | |
|
90 | 0 | multi = talloc_zero(result, struct connect_multi_state); |
91 | 0 | if (composite_nomem(multi, result)) goto failed; |
92 | 0 | result->private_data = multi; |
93 | |
|
94 | 0 | multi->num_ports = num_server_ports; |
95 | 0 | multi->ports = talloc_array(multi, uint16_t, multi->num_ports); |
96 | 0 | if (composite_nomem(multi->ports, result)) goto failed; |
97 | | |
98 | 0 | for (i=0; i<multi->num_ports; i++) { |
99 | 0 | multi->ports[i] = server_ports[i]; |
100 | 0 | } |
101 | |
|
102 | 0 | 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 | 0 | make_nbt_name_server(&name, server_name); |
110 | |
|
111 | 0 | creq = resolve_name_all_send(resolve_ctx, multi, 0, multi->ports[0], &name, result->event_ctx); |
112 | 0 | if (composite_nomem(creq, result)) goto failed; |
113 | | |
114 | 0 | composite_continue(result, creq, continue_resolve_name, result); |
115 | |
|
116 | 0 | return result; |
117 | | |
118 | | |
119 | 0 | failed: |
120 | 0 | composite_error(result, result->status); |
121 | 0 | return result; |
122 | 0 | } |
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 | 0 | { |
129 | 0 | struct connect_multi_state *multi = talloc_get_type(result->private_data, |
130 | 0 | struct connect_multi_state); |
131 | 0 | struct connect_one_state *state; |
132 | 0 | struct composite_context *creq; |
133 | 0 | int next = multi->num_connects_sent; |
134 | |
|
135 | 0 | if (next == multi->num_address * multi->num_ports) { |
136 | | /* don't do anything, just wait for the existing ones to finish */ |
137 | 0 | return; |
138 | 0 | } |
139 | | |
140 | 0 | if (multi->current_address == multi->num_address) { |
141 | 0 | multi->current_address = 0; |
142 | 0 | multi->current_port += 1; |
143 | 0 | } |
144 | 0 | multi->num_connects_sent += 1; |
145 | |
|
146 | 0 | if (multi->server_address == NULL || multi->server_address[multi->current_address] == NULL) { |
147 | 0 | composite_error(result, NT_STATUS_OBJECT_NAME_NOT_FOUND); |
148 | 0 | return; |
149 | 0 | } |
150 | | |
151 | 0 | state = talloc(multi, struct connect_one_state); |
152 | 0 | if (composite_nomem(state, result)) return; |
153 | | |
154 | 0 | state->result = result; |
155 | 0 | result->status = socket_create( |
156 | 0 | state, multi->server_address[multi->current_address]->family, |
157 | 0 | SOCKET_TYPE_STREAM, &state->sock, 0); |
158 | 0 | if (!composite_is_ok(result)) return; |
159 | | |
160 | 0 | state->addr = socket_address_copy(state, multi->server_address[multi->current_address]); |
161 | 0 | if (composite_nomem(state->addr, result)) return; |
162 | | |
163 | 0 | socket_address_set_port(state->addr, multi->ports[multi->current_port]); |
164 | |
|
165 | 0 | creq = socket_connect_send(state->sock, NULL, |
166 | 0 | state->addr, 0, |
167 | 0 | result->event_ctx); |
168 | 0 | if (composite_nomem(creq, result)) return; |
169 | 0 | talloc_steal(state, creq); |
170 | |
|
171 | 0 | multi->current_address++; |
172 | 0 | composite_continue(result, creq, continue_one, state); |
173 | | |
174 | | /* if there are more ports / addresses to go then setup a timer to fire when we have waited |
175 | | for a couple of milli-seconds, when that goes off we try the next port regardless |
176 | | of whether this port has completed */ |
177 | 0 | if (multi->num_ports * multi->num_address > multi->num_connects_sent) { |
178 | | /* note that this timer is a child of the single |
179 | | connect attempt state, so it will go away when this |
180 | | request completes */ |
181 | 0 | tevent_add_timer(result->event_ctx, state, |
182 | 0 | timeval_current_ofs_usec(MULTI_PORT_DELAY), |
183 | 0 | connect_multi_timer, result); |
184 | 0 | } |
185 | 0 | } |
186 | | |
187 | | /* |
188 | | a timer has gone off telling us that we should try the next port |
189 | | */ |
190 | | static void connect_multi_timer(struct tevent_context *ev, |
191 | | struct tevent_timer *te, |
192 | | struct timeval tv, void *p) |
193 | 0 | { |
194 | 0 | struct composite_context *result = talloc_get_type(p, struct composite_context); |
195 | 0 | connect_multi_next_socket(result); |
196 | 0 | } |
197 | | |
198 | | |
199 | | /* |
200 | | recv name resolution reply then send the next connect |
201 | | */ |
202 | | static void continue_resolve_name(struct composite_context *creq) |
203 | 0 | { |
204 | 0 | struct composite_context *result = talloc_get_type(creq->async.private_data, |
205 | 0 | struct composite_context); |
206 | 0 | struct connect_multi_state *multi = talloc_get_type(result->private_data, |
207 | 0 | struct connect_multi_state); |
208 | 0 | struct socket_address **addr; |
209 | 0 | unsigned i; |
210 | |
|
211 | 0 | result->status = resolve_name_all_recv(creq, multi, &addr, NULL); |
212 | 0 | if (!composite_is_ok(result)) return; |
213 | | |
214 | 0 | for(i=0; addr[i]; i++); |
215 | 0 | multi->num_address = i; |
216 | 0 | multi->server_address = talloc_steal(multi, addr); |
217 | |
|
218 | 0 | connect_multi_next_socket(result); |
219 | 0 | } |
220 | | |
221 | | /* |
222 | | one of our socket_connect_send() calls hash finished. If it got a |
223 | | connection or there are none left then we are done |
224 | | */ |
225 | | static void continue_one(struct composite_context *creq) |
226 | 0 | { |
227 | 0 | struct connect_one_state *state = talloc_get_type(creq->async.private_data, |
228 | 0 | struct connect_one_state); |
229 | 0 | struct composite_context *result = state->result; |
230 | 0 | struct connect_multi_state *multi = talloc_get_type(result->private_data, |
231 | 0 | struct connect_multi_state); |
232 | 0 | NTSTATUS status; |
233 | |
|
234 | 0 | status = socket_connect_recv(creq); |
235 | |
|
236 | 0 | if (multi->ex) { |
237 | 0 | struct tevent_req *subreq; |
238 | |
|
239 | 0 | subreq = multi->ex->establish_send(state, |
240 | 0 | result->event_ctx, |
241 | 0 | state->sock, |
242 | 0 | state->addr, |
243 | 0 | multi->ex->private_data); |
244 | 0 | if (composite_nomem(subreq, result)) return; |
245 | 0 | tevent_req_set_callback(subreq, continue_one_ex, state); |
246 | 0 | return; |
247 | 0 | } |
248 | | |
249 | 0 | multi->num_connects_recv++; |
250 | |
|
251 | 0 | if (NT_STATUS_IS_OK(status)) { |
252 | 0 | multi->sock = talloc_steal(multi, state->sock); |
253 | 0 | multi->result_port = state->addr->port; |
254 | 0 | } |
255 | |
|
256 | 0 | talloc_free(state); |
257 | |
|
258 | 0 | if (NT_STATUS_IS_OK(status) || |
259 | 0 | multi->num_connects_recv == (multi->num_address * multi->num_ports)) { |
260 | 0 | result->status = status; |
261 | 0 | composite_done(result); |
262 | 0 | return; |
263 | 0 | } |
264 | | |
265 | | /* try the next port */ |
266 | 0 | connect_multi_next_socket(result); |
267 | 0 | } |
268 | | |
269 | | /* |
270 | | one of our multi->ex->establish_send() calls hash finished. If it got a |
271 | | connection or there are none left then we are done |
272 | | */ |
273 | | static void continue_one_ex(struct tevent_req *subreq) |
274 | 0 | { |
275 | 0 | struct connect_one_state *state = |
276 | 0 | tevent_req_callback_data(subreq, |
277 | 0 | struct connect_one_state); |
278 | 0 | struct composite_context *result = state->result; |
279 | 0 | struct connect_multi_state *multi = |
280 | 0 | talloc_get_type_abort(result->private_data, |
281 | 0 | struct connect_multi_state); |
282 | 0 | NTSTATUS status; |
283 | 0 | multi->num_connects_recv++; |
284 | |
|
285 | 0 | status = multi->ex->establish_recv(subreq); |
286 | 0 | TALLOC_FREE(subreq); |
287 | |
|
288 | 0 | if (NT_STATUS_IS_OK(status)) { |
289 | 0 | multi->sock = talloc_steal(multi, state->sock); |
290 | 0 | multi->result_port = state->addr->port; |
291 | 0 | } |
292 | |
|
293 | 0 | talloc_free(state); |
294 | |
|
295 | 0 | if (NT_STATUS_IS_OK(status) || |
296 | 0 | multi->num_connects_recv == (multi->num_address * multi->num_ports)) { |
297 | 0 | result->status = status; |
298 | 0 | composite_done(result); |
299 | 0 | return; |
300 | 0 | } |
301 | | |
302 | | /* try the next port */ |
303 | 0 | connect_multi_next_socket(result); |
304 | 0 | } |
305 | | |
306 | | /* |
307 | | async recv routine for socket_connect_multi() |
308 | | */ |
309 | | _PUBLIC_ NTSTATUS socket_connect_multi_ex_recv(struct composite_context *ctx, |
310 | | TALLOC_CTX *mem_ctx, |
311 | | struct socket_context **sock, |
312 | | uint16_t *port) |
313 | 0 | { |
314 | 0 | NTSTATUS status = composite_wait(ctx); |
315 | 0 | if (NT_STATUS_IS_OK(status)) { |
316 | 0 | struct connect_multi_state *multi = |
317 | 0 | talloc_get_type(ctx->private_data, |
318 | 0 | struct connect_multi_state); |
319 | 0 | *sock = talloc_steal(mem_ctx, multi->sock); |
320 | 0 | *port = multi->result_port; |
321 | 0 | } |
322 | 0 | talloc_free(ctx); |
323 | 0 | return status; |
324 | 0 | } |
325 | | |
326 | | NTSTATUS socket_connect_multi_ex(TALLOC_CTX *mem_ctx, |
327 | | const char *server_address, |
328 | | int num_server_ports, uint16_t *server_ports, |
329 | | struct resolve_context *resolve_ctx, |
330 | | struct tevent_context *event_ctx, |
331 | | struct socket_connect_multi_ex *ex, |
332 | | struct socket_context **result, |
333 | | uint16_t *result_port) |
334 | 0 | { |
335 | 0 | struct composite_context *ctx = |
336 | 0 | socket_connect_multi_ex_send(mem_ctx, server_address, |
337 | 0 | num_server_ports, server_ports, |
338 | 0 | resolve_ctx, |
339 | 0 | event_ctx, |
340 | 0 | ex); |
341 | 0 | return socket_connect_multi_ex_recv(ctx, mem_ctx, result, result_port); |
342 | 0 | } |
343 | | |
344 | | /* |
345 | | setup an async socket_connect, with multiple ports |
346 | | */ |
347 | | _PUBLIC_ struct composite_context *socket_connect_multi_send( |
348 | | TALLOC_CTX *mem_ctx, |
349 | | const char *server_name, |
350 | | int num_server_ports, |
351 | | uint16_t *server_ports, |
352 | | struct resolve_context *resolve_ctx, |
353 | | struct tevent_context *event_ctx) |
354 | 0 | { |
355 | 0 | return socket_connect_multi_ex_send(mem_ctx, |
356 | 0 | server_name, |
357 | 0 | num_server_ports, |
358 | 0 | server_ports, |
359 | 0 | resolve_ctx, |
360 | 0 | event_ctx, |
361 | 0 | NULL); /* ex */ |
362 | 0 | } |
363 | | |
364 | | /* |
365 | | async recv routine for socket_connect_multi() |
366 | | */ |
367 | | _PUBLIC_ NTSTATUS socket_connect_multi_recv(struct composite_context *ctx, |
368 | | TALLOC_CTX *mem_ctx, |
369 | | struct socket_context **sock, |
370 | | uint16_t *port) |
371 | 0 | { |
372 | 0 | return socket_connect_multi_ex_recv(ctx, mem_ctx, sock, port); |
373 | 0 | } |
374 | | |
375 | | NTSTATUS socket_connect_multi(TALLOC_CTX *mem_ctx, |
376 | | const char *server_address, |
377 | | int num_server_ports, uint16_t *server_ports, |
378 | | struct resolve_context *resolve_ctx, |
379 | | struct tevent_context *event_ctx, |
380 | | struct socket_context **result, |
381 | | uint16_t *result_port) |
382 | 0 | { |
383 | 0 | return socket_connect_multi_ex(mem_ctx, |
384 | 0 | server_address, |
385 | 0 | num_server_ports, |
386 | 0 | server_ports, |
387 | 0 | resolve_ctx, |
388 | 0 | event_ctx, |
389 | | NULL, /* ex */ |
390 | 0 | result, |
391 | 0 | result_port); |
392 | 0 | } |