s4-socket: allow connect_multi_next_socket to try all the IP for a given host
[Samba/gebeck_regimport.git] / source4 / lib / socket / connect_multi.c
blob2b926c8bd99b62e0cec50b21fbb170bb78acf36a
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;
48 state of an individual socket_connect_send() call
50 struct connect_one_state {
51 struct composite_context *result;
52 struct socket_context *sock;
53 struct socket_address *addr;
56 static void continue_resolve_name(struct composite_context *creq);
57 static void connect_multi_timer(struct tevent_context *ev,
58 struct tevent_timer *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 _PUBLIC_ struct composite_context *socket_connect_multi_send(
67 TALLOC_CTX *mem_ctx,
68 const char *server_name,
69 int num_server_ports,
70 uint16_t *server_ports,
71 struct resolve_context *resolve_ctx,
72 struct tevent_context *event_ctx)
74 struct composite_context *result;
75 struct connect_multi_state *multi;
76 int i;
78 struct nbt_name name;
79 struct composite_context *creq;
81 result = talloc_zero(mem_ctx, struct composite_context);
82 if (result == NULL) return NULL;
83 result->state = COMPOSITE_STATE_IN_PROGRESS;
84 result->event_ctx = event_ctx;
86 multi = talloc_zero(result, struct connect_multi_state);
87 if (composite_nomem(multi, result)) goto failed;
88 result->private_data = multi;
90 multi->num_ports = num_server_ports;
91 multi->ports = talloc_array(multi, uint16_t, multi->num_ports);
92 if (composite_nomem(multi->ports, result)) goto failed;
94 for (i=0; i<multi->num_ports; i++) {
95 multi->ports[i] = server_ports[i];
98 /*
99 we don't want to do the name resolution separately
100 for each port, so start it now, then only start on
101 the real sockets once we have an IP
103 make_nbt_name_server(&name, server_name);
105 creq = resolve_name_all_send(resolve_ctx, multi, 0, multi->ports[0], &name, result->event_ctx);
106 if (composite_nomem(creq, result)) goto failed;
108 composite_continue(result, creq, continue_resolve_name, result);
110 return result;
113 failed:
114 composite_error(result, result->status);
115 return result;
119 start connecting to the next socket/port in the list
121 static void connect_multi_next_socket(struct composite_context *result)
123 struct connect_multi_state *multi = talloc_get_type(result->private_data,
124 struct connect_multi_state);
125 struct connect_one_state *state;
126 struct composite_context *creq;
127 int next = multi->num_connects_sent;
129 if (next == multi->num_address * multi->num_ports) {
130 /* don't do anything, just wait for the existing ones to finish */
131 return;
134 if (multi->current_address == multi->num_address) {
135 multi->current_address = 0;
136 multi->current_port += 1;
138 multi->num_connects_sent += 1;
140 if (multi->server_address == NULL || multi->server_address[multi->current_address] == NULL) {
141 composite_error(result, NT_STATUS_OBJECT_NAME_NOT_FOUND);
142 return;
145 state = talloc(multi, struct connect_one_state);
146 if (composite_nomem(state, result)) return;
148 state->result = result;
149 result->status = socket_create(multi->server_address[multi->current_address]->family,
150 SOCKET_TYPE_STREAM, &state->sock, 0);
151 if (!composite_is_ok(result)) return;
153 state->addr = socket_address_copy(state, multi->server_address[multi->current_address]);
154 if (composite_nomem(state->addr, result)) return;
156 socket_address_set_port(state->addr, multi->ports[multi->current_port]);
158 talloc_steal(state, state->sock);
160 creq = socket_connect_send(state->sock, NULL,
161 state->addr, 0,
162 result->event_ctx);
163 if (composite_nomem(creq, result)) return;
164 talloc_steal(state, creq);
166 multi->current_address++;
167 composite_continue(result, creq, continue_one, state);
169 /* if there are more ports / addresses to go then setup a timer to fire when we have waited
170 for a couple of milli-seconds, when that goes off we try the next port regardless
171 of whether this port has completed */
172 if (multi->num_ports * multi->num_address > multi->num_connects_sent) {
173 /* note that this timer is a child of the single
174 connect attempt state, so it will go away when this
175 request completes */
176 tevent_add_timer(result->event_ctx, state,
177 timeval_current_ofs_usec(MULTI_PORT_DELAY),
178 connect_multi_timer, result);
183 a timer has gone off telling us that we should try the next port
185 static void connect_multi_timer(struct tevent_context *ev,
186 struct tevent_timer *te,
187 struct timeval tv, void *p)
189 struct composite_context *result = talloc_get_type(p, struct composite_context);
190 connect_multi_next_socket(result);
195 recv name resolution reply then send the next connect
197 static void continue_resolve_name(struct composite_context *creq)
199 struct composite_context *result = talloc_get_type(creq->async.private_data,
200 struct composite_context);
201 struct connect_multi_state *multi = talloc_get_type(result->private_data,
202 struct connect_multi_state);
203 struct socket_address **addr;
204 unsigned i;
206 result->status = resolve_name_all_recv(creq, multi, &addr, NULL);
207 if (!composite_is_ok(result)) return;
209 for(i=0; addr[i]; i++);
210 multi->num_address = i;
211 multi->server_address = talloc_steal(multi, addr);
213 connect_multi_next_socket(result);
217 one of our socket_connect_send() calls hash finished. If it got a
218 connection or there are none left then we are done
220 static void continue_one(struct composite_context *creq)
222 struct connect_one_state *state = talloc_get_type(creq->async.private_data,
223 struct connect_one_state);
224 struct composite_context *result = state->result;
225 struct connect_multi_state *multi = talloc_get_type(result->private_data,
226 struct connect_multi_state);
227 NTSTATUS status;
228 multi->num_connects_recv++;
230 status = socket_connect_recv(creq);
232 if (NT_STATUS_IS_OK(status)) {
233 multi->sock = talloc_steal(multi, state->sock);
234 multi->result_port = state->addr->port;
237 talloc_free(state);
239 if (NT_STATUS_IS_OK(status) ||
240 multi->num_connects_recv == (multi->num_address * multi->num_ports)) {
241 result->status = status;
242 composite_done(result);
243 return;
246 /* try the next port */
247 connect_multi_next_socket(result);
251 async recv routine for socket_connect_multi()
253 _PUBLIC_ NTSTATUS socket_connect_multi_recv(struct composite_context *ctx,
254 TALLOC_CTX *mem_ctx,
255 struct socket_context **sock,
256 uint16_t *port)
258 NTSTATUS status = composite_wait(ctx);
259 if (NT_STATUS_IS_OK(status)) {
260 struct connect_multi_state *multi =
261 talloc_get_type(ctx->private_data,
262 struct connect_multi_state);
263 *sock = talloc_steal(mem_ctx, multi->sock);
264 *port = multi->result_port;
266 talloc_free(ctx);
267 return status;
270 NTSTATUS socket_connect_multi(TALLOC_CTX *mem_ctx,
271 const char *server_address,
272 int num_server_ports, uint16_t *server_ports,
273 struct resolve_context *resolve_ctx,
274 struct tevent_context *event_ctx,
275 struct socket_context **result,
276 uint16_t *result_port)
278 struct composite_context *ctx =
279 socket_connect_multi_send(mem_ctx, server_address,
280 num_server_ports, server_ports,
281 resolve_ctx,
282 event_ctx);
283 return socket_connect_multi_recv(ctx, mem_ctx, result, result_port);