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 socket_context *sock;
43 int num_connects_sent, num_connects_recv;
47 state of an individual socket_connect_send() call
49 struct connect_one_state {
50 struct composite_context *result;
51 struct socket_context *sock;
52 struct socket_address *addr;
55 static void continue_resolve_name(struct composite_context *creq);
56 static void connect_multi_timer(struct event_context *ev,
57 struct timed_event *te,
58 struct timeval tv, void *p);
59 static void connect_multi_next_socket(struct composite_context *result);
60 static void continue_one(struct composite_context *creq);
63 setup an async socket_connect, with multiple ports
65 _PUBLIC_ struct composite_context *socket_connect_multi_send(TALLOC_CTX *mem_ctx,
66 const char *server_address,
68 uint16_t *server_ports,
69 struct event_context *event_ctx)
71 struct composite_context *result;
72 struct connect_multi_state *multi;
75 result = talloc_zero(mem_ctx, struct composite_context);
76 if (result == NULL) return NULL;
77 result->state = COMPOSITE_STATE_IN_PROGRESS;
78 result->event_ctx = event_ctx;
80 multi = talloc_zero(result, struct connect_multi_state);
81 if (composite_nomem(multi, result)) goto failed;
82 result->private_data = multi;
84 multi->server_address = talloc_strdup(multi, server_address);
85 if (composite_nomem(multi->server_address, result)) goto failed;
87 multi->num_ports = num_server_ports;
88 multi->ports = talloc_array(multi, uint16_t, multi->num_ports);
89 if (composite_nomem(multi->ports, result)) goto failed;
91 for (i=0; i<multi->num_ports; i++) {
92 multi->ports[i] = server_ports[i];
95 if (!is_ipaddress(server_address)) {
97 we don't want to do the name resolution separately
98 for each port, so start it now, then only start on
99 the real sockets once we have an IP
101 struct nbt_name name;
102 struct composite_context *creq;
103 make_nbt_name_client(&name, server_address);
104 creq = resolve_name_send(&name, result->event_ctx,
105 lp_name_resolve_order());
106 if (composite_nomem(creq, result)) goto failed;
107 composite_continue(result, creq, continue_resolve_name, result);
111 /* now we've setup the state we can process the first socket */
112 connect_multi_next_socket(result);
114 if (!NT_STATUS_IS_OK(result->status)) {
121 composite_error(result, result->status);
126 start connecting to the next socket/port in the list
128 static void connect_multi_next_socket(struct composite_context *result)
130 struct connect_multi_state *multi = talloc_get_type(result->private_data,
131 struct connect_multi_state);
132 struct connect_one_state *state;
133 struct composite_context *creq;
134 int next = multi->num_connects_sent;
136 if (next == multi->num_ports) {
137 /* don't do anything, just wait for the existing ones to finish */
141 multi->num_connects_sent += 1;
143 state = talloc(multi, struct connect_one_state);
144 if (composite_nomem(state, result)) return;
146 state->result = result;
147 result->status = socket_create("ipv4", SOCKET_TYPE_STREAM, &state->sock, 0);
148 if (!composite_is_ok(result)) return;
150 /* Form up the particular address we are interested in */
151 state->addr = socket_address_from_strings(state, state->sock->backend_name,
152 multi->server_address, multi->ports[next]);
153 if (composite_nomem(state->addr, result)) return;
155 talloc_steal(state, state->sock);
157 creq = socket_connect_send(state->sock, NULL,
158 state->addr, 0, result->event_ctx);
159 if (composite_nomem(creq, result)) return;
160 talloc_steal(state, creq);
162 composite_continue(result, creq, continue_one, state);
164 /* if there are more ports to go then setup a timer to fire when we have waited
165 for a couple of milli-seconds, when that goes off we try the next port regardless
166 of whether this port has completed */
167 if (multi->num_ports > multi->num_connects_sent) {
168 /* note that this timer is a child of the single
169 connect attempt state, so it will go away when this
171 event_add_timed(result->event_ctx, state,
172 timeval_current_ofs(0, MULTI_PORT_DELAY),
173 connect_multi_timer, result);
178 a timer has gone off telling us that we should try the next port
180 static void connect_multi_timer(struct event_context *ev,
181 struct timed_event *te,
182 struct timeval tv, void *p)
184 struct composite_context *result = talloc_get_type(p, struct composite_context);
185 connect_multi_next_socket(result);
190 recv name resolution reply then send the next connect
192 static void continue_resolve_name(struct composite_context *creq)
194 struct composite_context *result = talloc_get_type(creq->async.private_data,
195 struct composite_context);
196 struct connect_multi_state *multi = talloc_get_type(result->private_data,
197 struct connect_multi_state);
200 result->status = resolve_name_recv(creq, multi, &addr);
201 if (!composite_is_ok(result)) return;
203 multi->server_address = addr;
205 connect_multi_next_socket(result);
209 one of our socket_connect_send() calls hash finished. If it got a
210 connection or there are none left then we are done
212 static void continue_one(struct composite_context *creq)
214 struct connect_one_state *state = talloc_get_type(creq->async.private_data,
215 struct connect_one_state);
216 struct composite_context *result = state->result;
217 struct connect_multi_state *multi = talloc_get_type(result->private_data,
218 struct connect_multi_state);
220 multi->num_connects_recv++;
222 status = socket_connect_recv(creq);
224 if (NT_STATUS_IS_OK(status)) {
225 multi->sock = talloc_steal(multi, state->sock);
226 multi->result_port = state->addr->port;
231 if (NT_STATUS_IS_OK(status) ||
232 multi->num_connects_recv == multi->num_ports) {
233 result->status = status;
234 composite_done(result);
238 /* try the next port */
239 connect_multi_next_socket(result);
243 async recv routine for socket_connect_multi()
245 _PUBLIC_ NTSTATUS socket_connect_multi_recv(struct composite_context *ctx,
247 struct socket_context **sock,
250 NTSTATUS status = composite_wait(ctx);
251 if (NT_STATUS_IS_OK(status)) {
252 struct connect_multi_state *multi =
253 talloc_get_type(ctx->private_data,
254 struct connect_multi_state);
255 *sock = talloc_steal(mem_ctx, multi->sock);
256 *port = multi->result_port;
262 NTSTATUS socket_connect_multi(TALLOC_CTX *mem_ctx,
263 const char *server_address,
264 int num_server_ports, uint16_t *server_ports,
265 struct event_context *event_ctx,
266 struct socket_context **result,
267 uint16_t *result_port)
269 struct composite_context *ctx =
270 socket_connect_multi_send(mem_ctx, server_address,
271 num_server_ports, server_ports,
273 return socket_connect_multi_recv(ctx, mem_ctx, result, result_port);