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"
29 #include "param/param.h"
31 #define MULTI_PORT_DELAY 2000 /* microseconds */
36 struct connect_multi_state {
37 const char *server_address;
41 struct socket_context *sock;
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 event_context *ev,
58 struct timed_event *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(
68 const char *server_address,
70 uint16_t *server_ports,
71 struct event_context *event_ctx)
73 struct composite_context *result;
74 struct connect_multi_state *multi;
77 result = talloc_zero(mem_ctx, struct composite_context);
78 if (result == NULL) return NULL;
79 result->state = COMPOSITE_STATE_IN_PROGRESS;
80 result->event_ctx = event_ctx;
82 multi = talloc_zero(result, struct connect_multi_state);
83 if (composite_nomem(multi, result)) goto failed;
84 result->private_data = multi;
86 multi->server_address = talloc_strdup(multi, server_address);
87 if (composite_nomem(multi->server_address, result)) goto failed;
89 multi->num_ports = num_server_ports;
90 multi->ports = talloc_array(multi, uint16_t, multi->num_ports);
91 if (composite_nomem(multi->ports, result)) goto failed;
93 for (i=0; i<multi->num_ports; i++) {
94 multi->ports[i] = server_ports[i];
97 if (!is_ipaddress(server_address)) {
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 struct nbt_name name;
104 struct composite_context *creq;
105 make_nbt_name_client(&name, server_address);
106 creq = resolve_name_send(&name, result->event_ctx,
107 lp_name_resolve_order(global_loadparm));
108 if (composite_nomem(creq, result)) goto failed;
109 composite_continue(result, creq, continue_resolve_name, result);
113 /* now we've setup the state we can process the first socket */
114 connect_multi_next_socket(result);
116 if (!NT_STATUS_IS_OK(result->status)) {
123 composite_error(result, result->status);
128 start connecting to the next socket/port in the list
130 static void connect_multi_next_socket(struct composite_context *result)
132 struct connect_multi_state *multi = talloc_get_type(result->private_data,
133 struct connect_multi_state);
134 struct connect_one_state *state;
135 struct composite_context *creq;
136 int next = multi->num_connects_sent;
138 if (next == multi->num_ports) {
139 /* don't do anything, just wait for the existing ones to finish */
143 multi->num_connects_sent += 1;
145 state = talloc(multi, struct connect_one_state);
146 if (composite_nomem(state, result)) return;
148 state->result = result;
149 result->status = socket_create("ipv4", SOCKET_TYPE_STREAM, &state->sock, 0);
150 if (!composite_is_ok(result)) return;
152 /* Form up the particular address we are interested in */
153 state->addr = socket_address_from_strings(state, state->sock->backend_name,
154 multi->server_address, multi->ports[next]);
155 if (composite_nomem(state->addr, result)) return;
157 talloc_steal(state, state->sock);
159 creq = socket_connect_send(state->sock, NULL,
160 state->addr, 0, result->event_ctx);
161 if (composite_nomem(creq, result)) return;
162 talloc_steal(state, creq);
164 composite_continue(result, creq, continue_one, state);
166 /* if there are more ports to go then setup a timer to fire when we have waited
167 for a couple of milli-seconds, when that goes off we try the next port regardless
168 of whether this port has completed */
169 if (multi->num_ports > multi->num_connects_sent) {
170 /* note that this timer is a child of the single
171 connect attempt state, so it will go away when this
173 event_add_timed(result->event_ctx, state,
174 timeval_current_ofs(0, MULTI_PORT_DELAY),
175 connect_multi_timer, result);
180 a timer has gone off telling us that we should try the next port
182 static void connect_multi_timer(struct event_context *ev,
183 struct timed_event *te,
184 struct timeval tv, void *p)
186 struct composite_context *result = talloc_get_type(p, struct composite_context);
187 connect_multi_next_socket(result);
192 recv name resolution reply then send the next connect
194 static void continue_resolve_name(struct composite_context *creq)
196 struct composite_context *result = talloc_get_type(creq->async.private_data,
197 struct composite_context);
198 struct connect_multi_state *multi = talloc_get_type(result->private_data,
199 struct connect_multi_state);
202 result->status = resolve_name_recv(creq, multi, &addr);
203 if (!composite_is_ok(result)) return;
205 multi->server_address = addr;
207 connect_multi_next_socket(result);
211 one of our socket_connect_send() calls hash finished. If it got a
212 connection or there are none left then we are done
214 static void continue_one(struct composite_context *creq)
216 struct connect_one_state *state = talloc_get_type(creq->async.private_data,
217 struct connect_one_state);
218 struct composite_context *result = state->result;
219 struct connect_multi_state *multi = talloc_get_type(result->private_data,
220 struct connect_multi_state);
222 multi->num_connects_recv++;
224 status = socket_connect_recv(creq);
226 if (NT_STATUS_IS_OK(status)) {
227 multi->sock = talloc_steal(multi, state->sock);
228 multi->result_port = state->addr->port;
233 if (NT_STATUS_IS_OK(status) ||
234 multi->num_connects_recv == multi->num_ports) {
235 result->status = status;
236 composite_done(result);
240 /* try the next port */
241 connect_multi_next_socket(result);
245 async recv routine for socket_connect_multi()
247 _PUBLIC_ NTSTATUS socket_connect_multi_recv(struct composite_context *ctx,
249 struct socket_context **sock,
252 NTSTATUS status = composite_wait(ctx);
253 if (NT_STATUS_IS_OK(status)) {
254 struct connect_multi_state *multi =
255 talloc_get_type(ctx->private_data,
256 struct connect_multi_state);
257 *sock = talloc_steal(mem_ctx, multi->sock);
258 *port = multi->result_port;
264 NTSTATUS socket_connect_multi(TALLOC_CTX *mem_ctx,
265 const char *server_address,
266 int num_server_ports, uint16_t *server_ports,
267 struct event_context *event_ctx,
268 struct socket_context **result,
269 uint16_t *result_port)
271 struct composite_context *ctx =
272 socket_connect_multi_send(mem_ctx, server_address,
273 num_server_ports, server_ports,
275 return socket_connect_multi_recv(ctx, mem_ctx, result, result_port);