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 const char **name_resolve_order;
43 struct socket_context *sock;
46 int num_connects_sent, num_connects_recv;
50 state of an individual socket_connect_send() call
52 struct connect_one_state {
53 struct composite_context *result;
54 struct socket_context *sock;
55 struct socket_address *addr;
58 static void continue_resolve_name(struct composite_context *creq);
59 static void connect_multi_timer(struct event_context *ev,
60 struct timed_event *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);
66 setup an async socket_connect, with multiple ports
68 _PUBLIC_ struct composite_context *socket_connect_multi_send(
70 const char *server_address,
72 uint16_t *server_ports,
73 const char **name_resolve_order,
74 struct event_context *event_ctx)
76 struct composite_context *result;
77 struct connect_multi_state *multi;
80 result = talloc_zero(mem_ctx, struct composite_context);
81 if (result == NULL) return NULL;
82 result->state = COMPOSITE_STATE_IN_PROGRESS;
83 result->event_ctx = event_ctx;
85 multi = talloc_zero(result, struct connect_multi_state);
86 if (composite_nomem(multi, result)) goto failed;
87 result->private_data = multi;
89 multi->server_address = talloc_strdup(multi, server_address);
90 if (composite_nomem(multi->server_address, result)) goto failed;
92 multi->num_ports = num_server_ports;
93 multi->name_resolve_order = str_list_copy(multi, name_resolve_order);
94 multi->ports = talloc_array(multi, uint16_t, multi->num_ports);
95 if (composite_nomem(multi->ports, result)) goto failed;
97 for (i=0; i<multi->num_ports; i++) {
98 multi->ports[i] = server_ports[i];
101 if (!is_ipaddress(server_address)) {
103 we don't want to do the name resolution separately
104 for each port, so start it now, then only start on
105 the real sockets once we have an IP
107 struct nbt_name name;
108 struct composite_context *creq;
109 make_nbt_name_client(&name, server_address);
110 creq = resolve_name_send(&name, result->event_ctx,
112 if (composite_nomem(creq, result)) goto failed;
113 composite_continue(result, creq, continue_resolve_name, result);
117 /* now we've setup the state we can process the first socket */
118 connect_multi_next_socket(result);
120 if (!NT_STATUS_IS_OK(result->status)) {
127 composite_error(result, result->status);
132 start connecting to the next socket/port in the list
134 static void connect_multi_next_socket(struct composite_context *result)
136 struct connect_multi_state *multi = talloc_get_type(result->private_data,
137 struct connect_multi_state);
138 struct connect_one_state *state;
139 struct composite_context *creq;
140 int next = multi->num_connects_sent;
142 if (next == multi->num_ports) {
143 /* don't do anything, just wait for the existing ones to finish */
147 multi->num_connects_sent += 1;
149 state = talloc(multi, struct connect_one_state);
150 if (composite_nomem(state, result)) return;
152 state->result = result;
153 result->status = socket_create("ipv4", SOCKET_TYPE_STREAM, &state->sock, 0);
154 if (!composite_is_ok(result)) return;
156 /* Form up the particular address we are interested in */
157 state->addr = socket_address_from_strings(state, state->sock->backend_name,
158 multi->server_address, multi->ports[next]);
159 if (composite_nomem(state->addr, result)) return;
161 talloc_steal(state, state->sock);
163 creq = socket_connect_send(state->sock, NULL,
164 state->addr, 0, multi->name_resolve_order,
166 if (composite_nomem(creq, result)) return;
167 talloc_steal(state, creq);
169 composite_continue(result, creq, continue_one, state);
171 /* if there are more ports to go then setup a timer to fire when we have waited
172 for a couple of milli-seconds, when that goes off we try the next port regardless
173 of whether this port has completed */
174 if (multi->num_ports > multi->num_connects_sent) {
175 /* note that this timer is a child of the single
176 connect attempt state, so it will go away when this
178 event_add_timed(result->event_ctx, state,
179 timeval_current_ofs(0, MULTI_PORT_DELAY),
180 connect_multi_timer, result);
185 a timer has gone off telling us that we should try the next port
187 static void connect_multi_timer(struct event_context *ev,
188 struct timed_event *te,
189 struct timeval tv, void *p)
191 struct composite_context *result = talloc_get_type(p, struct composite_context);
192 connect_multi_next_socket(result);
197 recv name resolution reply then send the next connect
199 static void continue_resolve_name(struct composite_context *creq)
201 struct composite_context *result = talloc_get_type(creq->async.private_data,
202 struct composite_context);
203 struct connect_multi_state *multi = talloc_get_type(result->private_data,
204 struct connect_multi_state);
207 result->status = resolve_name_recv(creq, multi, &addr);
208 if (!composite_is_ok(result)) return;
210 multi->server_address = addr;
212 connect_multi_next_socket(result);
216 one of our socket_connect_send() calls hash finished. If it got a
217 connection or there are none left then we are done
219 static void continue_one(struct composite_context *creq)
221 struct connect_one_state *state = talloc_get_type(creq->async.private_data,
222 struct connect_one_state);
223 struct composite_context *result = state->result;
224 struct connect_multi_state *multi = talloc_get_type(result->private_data,
225 struct connect_multi_state);
227 multi->num_connects_recv++;
229 status = socket_connect_recv(creq);
231 if (NT_STATUS_IS_OK(status)) {
232 multi->sock = talloc_steal(multi, state->sock);
233 multi->result_port = state->addr->port;
238 if (NT_STATUS_IS_OK(status) ||
239 multi->num_connects_recv == multi->num_ports) {
240 result->status = status;
241 composite_done(result);
245 /* try the next port */
246 connect_multi_next_socket(result);
250 async recv routine for socket_connect_multi()
252 _PUBLIC_ NTSTATUS socket_connect_multi_recv(struct composite_context *ctx,
254 struct socket_context **sock,
257 NTSTATUS status = composite_wait(ctx);
258 if (NT_STATUS_IS_OK(status)) {
259 struct connect_multi_state *multi =
260 talloc_get_type(ctx->private_data,
261 struct connect_multi_state);
262 *sock = talloc_steal(mem_ctx, multi->sock);
263 *port = multi->result_port;
269 NTSTATUS socket_connect_multi(TALLOC_CTX *mem_ctx,
270 const char *server_address,
271 int num_server_ports, uint16_t *server_ports,
272 const char **name_resolve_order,
273 struct event_context *event_ctx,
274 struct socket_context **result,
275 uint16_t *result_port)
277 struct composite_context *ctx =
278 socket_connect_multi_send(mem_ctx, server_address,
279 num_server_ports, server_ports,
282 return socket_connect_multi_recv(ctx, mem_ctx, result, result_port);