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 struct socket_address
*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 tevent_context
*ev
,
57 struct tevent_timer
*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(
67 const char *server_name
,
69 uint16_t *server_ports
,
70 struct resolve_context
*resolve_ctx
,
71 struct tevent_context
*event_ctx
)
73 struct composite_context
*result
;
74 struct connect_multi_state
*multi
;
78 struct composite_context
*creq
;
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
->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
];
98 we don't want to do the name resolution separately
99 for each port, so start it now, then only start on
100 the real sockets once we have an IP
102 make_nbt_name_server(&name
, server_name
);
104 creq
= resolve_name_all_send(resolve_ctx
, multi
, 0, multi
->ports
[0], &name
, result
->event_ctx
);
105 if (composite_nomem(creq
, result
)) goto failed
;
107 composite_continue(result
, creq
, continue_resolve_name
, result
);
113 composite_error(result
, result
->status
);
118 start connecting to the next socket/port in the list
120 static void connect_multi_next_socket(struct composite_context
*result
)
122 struct connect_multi_state
*multi
= talloc_get_type(result
->private_data
,
123 struct connect_multi_state
);
124 struct connect_one_state
*state
;
125 struct composite_context
*creq
;
126 int next
= multi
->num_connects_sent
;
128 if (next
== multi
->num_ports
) {
129 /* don't do anything, just wait for the existing ones to finish */
133 multi
->num_connects_sent
+= 1;
135 state
= talloc(multi
, struct connect_one_state
);
136 if (composite_nomem(state
, result
)) return;
138 state
->result
= result
;
139 result
->status
= socket_create("ipv4", SOCKET_TYPE_STREAM
, &state
->sock
, 0);
140 if (!composite_is_ok(result
)) return;
142 state
->addr
= socket_address_copy(state
, multi
->server_address
);
143 if (composite_nomem(state
->addr
, result
)) return;
145 socket_address_set_port(state
->addr
, multi
->ports
[next
]);
147 talloc_steal(state
, state
->sock
);
149 creq
= socket_connect_send(state
->sock
, NULL
,
152 if (composite_nomem(creq
, result
)) return;
153 talloc_steal(state
, creq
);
155 composite_continue(result
, creq
, continue_one
, state
);
157 /* if there are more ports to go then setup a timer to fire when we have waited
158 for a couple of milli-seconds, when that goes off we try the next port regardless
159 of whether this port has completed */
160 if (multi
->num_ports
> multi
->num_connects_sent
) {
161 /* note that this timer is a child of the single
162 connect attempt state, so it will go away when this
164 event_add_timed(result
->event_ctx
, state
,
165 timeval_current_ofs(0, MULTI_PORT_DELAY
),
166 connect_multi_timer
, result
);
171 a timer has gone off telling us that we should try the next port
173 static void connect_multi_timer(struct tevent_context
*ev
,
174 struct tevent_timer
*te
,
175 struct timeval tv
, void *p
)
177 struct composite_context
*result
= talloc_get_type(p
, struct composite_context
);
178 connect_multi_next_socket(result
);
183 recv name resolution reply then send the next connect
185 static void continue_resolve_name(struct composite_context
*creq
)
187 struct composite_context
*result
= talloc_get_type(creq
->async
.private_data
,
188 struct composite_context
);
189 struct connect_multi_state
*multi
= talloc_get_type(result
->private_data
,
190 struct connect_multi_state
);
191 struct socket_address
**addr
;
193 result
->status
= resolve_name_all_recv(creq
, multi
, &addr
, NULL
);
194 if (!composite_is_ok(result
)) return;
196 /* Let's just go for the first for now */
197 multi
->server_address
= addr
[0];
199 connect_multi_next_socket(result
);
203 one of our socket_connect_send() calls hash finished. If it got a
204 connection or there are none left then we are done
206 static void continue_one(struct composite_context
*creq
)
208 struct connect_one_state
*state
= talloc_get_type(creq
->async
.private_data
,
209 struct connect_one_state
);
210 struct composite_context
*result
= state
->result
;
211 struct connect_multi_state
*multi
= talloc_get_type(result
->private_data
,
212 struct connect_multi_state
);
214 multi
->num_connects_recv
++;
216 status
= socket_connect_recv(creq
);
218 if (NT_STATUS_IS_OK(status
)) {
219 multi
->sock
= talloc_steal(multi
, state
->sock
);
220 multi
->result_port
= state
->addr
->port
;
225 if (NT_STATUS_IS_OK(status
) ||
226 multi
->num_connects_recv
== multi
->num_ports
) {
227 result
->status
= status
;
228 composite_done(result
);
232 /* try the next port */
233 connect_multi_next_socket(result
);
237 async recv routine for socket_connect_multi()
239 _PUBLIC_ NTSTATUS
socket_connect_multi_recv(struct composite_context
*ctx
,
241 struct socket_context
**sock
,
244 NTSTATUS status
= composite_wait(ctx
);
245 if (NT_STATUS_IS_OK(status
)) {
246 struct connect_multi_state
*multi
=
247 talloc_get_type(ctx
->private_data
,
248 struct connect_multi_state
);
249 *sock
= talloc_steal(mem_ctx
, multi
->sock
);
250 *port
= multi
->result_port
;
256 NTSTATUS
socket_connect_multi(TALLOC_CTX
*mem_ctx
,
257 const char *server_address
,
258 int num_server_ports
, uint16_t *server_ports
,
259 struct resolve_context
*resolve_ctx
,
260 struct tevent_context
*event_ctx
,
261 struct socket_context
**result
,
262 uint16_t *result_port
)
264 struct composite_context
*ctx
=
265 socket_connect_multi_send(mem_ctx
, server_address
,
266 num_server_ports
, server_ports
,
269 return socket_connect_multi_recv(ctx
, mem_ctx
, result
, result_port
);