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/>.
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 */
35 struct connect_multi_state {
36 const char *server_address;
40 struct resolve_context *resolve_ctx;
42 struct socket_context *sock;
45 int num_connects_sent, num_connects_recv;
49 state of an individual socket_connect_send() call
51 struct connect_one_state {
52 struct composite_context *result;
53 struct socket_context *sock;
54 struct socket_address *addr;
57 static void continue_resolve_name(struct composite_context *creq);
58 static void connect_multi_timer(struct event_context *ev,
59 struct timed_event *te,
60 struct timeval tv, void *p);
61 static void connect_multi_next_socket(struct composite_context *result);
62 static void continue_one(struct composite_context *creq);
65 setup an async socket_connect, with multiple ports
67 _PUBLIC_ struct composite_context *socket_connect_multi_send(
69 const char *server_address,
71 uint16_t *server_ports,
72 struct resolve_context *resolve_ctx,
73 struct event_context *event_ctx)
75 struct composite_context *result;
76 struct connect_multi_state *multi;
79 result = talloc_zero(mem_ctx, struct composite_context);
80 if (result == NULL) return NULL;
81 result->state = COMPOSITE_STATE_IN_PROGRESS;
82 result->event_ctx = event_ctx;
84 multi = talloc_zero(result, struct connect_multi_state);
85 if (composite_nomem(multi, result)) goto failed;
86 result->private_data = multi;
88 multi->server_address = talloc_strdup(multi, server_address);
89 if (composite_nomem(multi->server_address, result)) goto failed;
91 multi->num_ports = num_server_ports;
92 multi->resolve_ctx = talloc_reference(multi, resolve_ctx);
93 multi->ports = talloc_array(multi, uint16_t, multi->num_ports);
94 if (composite_nomem(multi->ports, result)) goto failed;
96 for (i=0; i<multi->num_ports; i++) {
97 multi->ports[i] = server_ports[i];
100 if (!is_ipaddress(server_address)) {
102 we don't want to do the name resolution separately
103 for each port, so start it now, then only start on
104 the real sockets once we have an IP
106 struct nbt_name name;
107 struct composite_context *creq;
108 make_nbt_name_server(&name, server_address);
109 creq = resolve_name_send(resolve_ctx, &name, result->event_ctx);
110 if (composite_nomem(creq, result)) goto failed;
111 composite_continue(result, creq, continue_resolve_name, result);
115 /* now we've setup the state we can process the first socket */
116 connect_multi_next_socket(result);
118 if (!NT_STATUS_IS_OK(result->status)) {
125 composite_error(result, result->status);
130 start connecting to the next socket/port in the list
132 static void connect_multi_next_socket(struct composite_context *result)
134 struct connect_multi_state *multi = talloc_get_type(result->private_data,
135 struct connect_multi_state);
136 struct connect_one_state *state;
137 struct composite_context *creq;
138 int next = multi->num_connects_sent;
140 if (next == multi->num_ports) {
141 /* don't do anything, just wait for the existing ones to finish */
145 multi->num_connects_sent += 1;
147 state = talloc(multi, struct connect_one_state);
148 if (composite_nomem(state, result)) return;
150 state->result = result;
151 result->status = socket_create("ipv4", SOCKET_TYPE_STREAM, &state->sock, 0);
152 if (!composite_is_ok(result)) return;
154 /* Form up the particular address we are interested in */
155 state->addr = socket_address_from_strings(state, state->sock->backend_name,
156 multi->server_address, multi->ports[next]);
157 if (composite_nomem(state->addr, result)) return;
159 talloc_steal(state, state->sock);
161 creq = socket_connect_send(state->sock, NULL,
162 state->addr, 0, multi->resolve_ctx,
164 if (composite_nomem(creq, result)) return;
165 talloc_steal(state, creq);
167 composite_continue(result, creq, continue_one, state);
169 /* if there are more ports 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_connects_sent) {
173 /* note that this timer is a child of the single
174 connect attempt state, so it will go away when this
176 event_add_timed(result->event_ctx, state,
177 timeval_current_ofs(0, 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 event_context *ev,
186 struct timed_event *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);
205 result->status = resolve_name_recv(creq, multi, &addr);
206 if (!composite_is_ok(result)) return;
208 multi->server_address = addr;
210 connect_multi_next_socket(result);
214 one of our socket_connect_send() calls hash finished. If it got a
215 connection or there are none left then we are done
217 static void continue_one(struct composite_context *creq)
219 struct connect_one_state *state = talloc_get_type(creq->async.private_data,
220 struct connect_one_state);
221 struct composite_context *result = state->result;
222 struct connect_multi_state *multi = talloc_get_type(result->private_data,
223 struct connect_multi_state);
225 multi->num_connects_recv++;
227 status = socket_connect_recv(creq);
229 if (NT_STATUS_IS_OK(status)) {
230 multi->sock = talloc_steal(multi, state->sock);
231 multi->result_port = state->addr->port;
236 if (NT_STATUS_IS_OK(status) ||
237 multi->num_connects_recv == multi->num_ports) {
238 result->status = status;
239 composite_done(result);
243 /* try the next port */
244 connect_multi_next_socket(result);
248 async recv routine for socket_connect_multi()
250 _PUBLIC_ NTSTATUS socket_connect_multi_recv(struct composite_context *ctx,
252 struct socket_context **sock,
255 NTSTATUS status = composite_wait(ctx);
256 if (NT_STATUS_IS_OK(status)) {
257 struct connect_multi_state *multi =
258 talloc_get_type(ctx->private_data,
259 struct connect_multi_state);
260 *sock = talloc_steal(mem_ctx, multi->sock);
261 *port = multi->result_port;
267 NTSTATUS socket_connect_multi(TALLOC_CTX *mem_ctx,
268 const char *server_address,
269 int num_server_ports, uint16_t *server_ports,
270 struct resolve_context *resolve_ctx,
271 struct event_context *event_ctx,
272 struct socket_context **result,
273 uint16_t *result_port)
275 struct composite_context *ctx =
276 socket_connect_multi_send(mem_ctx, server_address,
277 num_server_ports, server_ports,
280 return socket_connect_multi_recv(ctx, mem_ctx, result, result_port);