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
;
37 unsigned num_address
, current_address
, current_port
;
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 tevent_context
*ev
,
58 struct tevent_timer
*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_name
,
70 uint16_t *server_ports
,
71 struct resolve_context
*resolve_ctx
,
72 struct tevent_context
*event_ctx
)
74 struct composite_context
*result
;
75 struct connect_multi_state
*multi
;
79 struct composite_context
*creq
;
81 result
= talloc_zero(mem_ctx
, struct composite_context
);
82 if (result
== NULL
) return NULL
;
83 result
->state
= COMPOSITE_STATE_IN_PROGRESS
;
84 result
->event_ctx
= event_ctx
;
86 multi
= talloc_zero(result
, struct connect_multi_state
);
87 if (composite_nomem(multi
, result
)) goto failed
;
88 result
->private_data
= multi
;
90 multi
->num_ports
= num_server_ports
;
91 multi
->ports
= talloc_array(multi
, uint16_t, multi
->num_ports
);
92 if (composite_nomem(multi
->ports
, result
)) goto failed
;
94 for (i
=0; i
<multi
->num_ports
; i
++) {
95 multi
->ports
[i
] = server_ports
[i
];
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 make_nbt_name_server(&name
, server_name
);
105 creq
= resolve_name_all_send(resolve_ctx
, multi
, 0, multi
->ports
[0], &name
, result
->event_ctx
);
106 if (composite_nomem(creq
, result
)) goto failed
;
108 composite_continue(result
, creq
, continue_resolve_name
, result
);
114 composite_error(result
, result
->status
);
119 start connecting to the next socket/port in the list
121 static void connect_multi_next_socket(struct composite_context
*result
)
123 struct connect_multi_state
*multi
= talloc_get_type(result
->private_data
,
124 struct connect_multi_state
);
125 struct connect_one_state
*state
;
126 struct composite_context
*creq
;
127 int next
= multi
->num_connects_sent
;
129 if (next
== multi
->num_address
* multi
->num_ports
) {
130 /* don't do anything, just wait for the existing ones to finish */
134 if (multi
->current_address
== multi
->num_address
) {
135 multi
->current_address
= 0;
136 multi
->current_port
+= 1;
138 multi
->num_connects_sent
+= 1;
140 if (multi
->server_address
== NULL
|| multi
->server_address
[multi
->current_address
] == NULL
) {
141 composite_error(result
, NT_STATUS_OBJECT_NAME_NOT_FOUND
);
145 state
= talloc(multi
, struct connect_one_state
);
146 if (composite_nomem(state
, result
)) return;
148 state
->result
= result
;
149 result
->status
= socket_create(multi
->server_address
[multi
->current_address
]->family
,
150 SOCKET_TYPE_STREAM
, &state
->sock
, 0);
151 if (!composite_is_ok(result
)) return;
153 state
->addr
= socket_address_copy(state
, multi
->server_address
[multi
->current_address
]);
154 if (composite_nomem(state
->addr
, result
)) return;
156 socket_address_set_port(state
->addr
, multi
->ports
[multi
->current_port
]);
158 talloc_steal(state
, state
->sock
);
160 creq
= socket_connect_send(state
->sock
, NULL
,
163 if (composite_nomem(creq
, result
)) return;
164 talloc_steal(state
, creq
);
166 multi
->current_address
++;
167 composite_continue(result
, creq
, continue_one
, state
);
169 /* if there are more ports / addresses 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_address
> 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 tevent_add_timer(result
->event_ctx
, state
,
177 timeval_current_ofs_usec(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 tevent_context
*ev
,
186 struct tevent_timer
*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
);
203 struct socket_address
**addr
;
206 result
->status
= resolve_name_all_recv(creq
, multi
, &addr
, NULL
);
207 if (!composite_is_ok(result
)) return;
209 for(i
=0; addr
[i
]; i
++);
210 multi
->num_address
= i
;
211 multi
->server_address
= talloc_steal(multi
, addr
);
213 connect_multi_next_socket(result
);
217 one of our socket_connect_send() calls hash finished. If it got a
218 connection or there are none left then we are done
220 static void continue_one(struct composite_context
*creq
)
222 struct connect_one_state
*state
= talloc_get_type(creq
->async
.private_data
,
223 struct connect_one_state
);
224 struct composite_context
*result
= state
->result
;
225 struct connect_multi_state
*multi
= talloc_get_type(result
->private_data
,
226 struct connect_multi_state
);
228 multi
->num_connects_recv
++;
230 status
= socket_connect_recv(creq
);
232 if (NT_STATUS_IS_OK(status
)) {
233 multi
->sock
= talloc_steal(multi
, state
->sock
);
234 multi
->result_port
= state
->addr
->port
;
239 if (NT_STATUS_IS_OK(status
) ||
240 multi
->num_connects_recv
== (multi
->num_address
* multi
->num_ports
)) {
241 result
->status
= status
;
242 composite_done(result
);
246 /* try the next port */
247 connect_multi_next_socket(result
);
251 async recv routine for socket_connect_multi()
253 _PUBLIC_ NTSTATUS
socket_connect_multi_recv(struct composite_context
*ctx
,
255 struct socket_context
**sock
,
258 NTSTATUS status
= composite_wait(ctx
);
259 if (NT_STATUS_IS_OK(status
)) {
260 struct connect_multi_state
*multi
=
261 talloc_get_type(ctx
->private_data
,
262 struct connect_multi_state
);
263 *sock
= talloc_steal(mem_ctx
, multi
->sock
);
264 *port
= multi
->result_port
;
270 NTSTATUS
socket_connect_multi(TALLOC_CTX
*mem_ctx
,
271 const char *server_address
,
272 int num_server_ports
, uint16_t *server_ports
,
273 struct resolve_context
*resolve_ctx
,
274 struct tevent_context
*event_ctx
,
275 struct socket_context
**result
,
276 uint16_t *result_port
)
278 struct composite_context
*ctx
=
279 socket_connect_multi_send(mem_ctx
, server_address
,
280 num_server_ports
, server_ports
,
283 return socket_connect_multi_recv(ctx
, mem_ctx
, result
, result_port
);