/src/samba/source3/libsmb/smbsock_connect.c
Line | Count | Source (jump to first uncovered line) |
1 | | /* |
2 | | Unix SMB/CIFS implementation. |
3 | | Connect to 445 and 139/nbsesssetup |
4 | | Copyright (C) Volker Lendecke 2010 |
5 | | |
6 | | This program is free software; you can redistribute it and/or modify |
7 | | it under the terms of the GNU General Public License as published by |
8 | | the Free Software Foundation; either version 3 of the License, or |
9 | | (at your option) any later version. |
10 | | |
11 | | This program is distributed in the hope that it will be useful, |
12 | | but WITHOUT ANY WARRANTY; without even the implied warranty of |
13 | | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
14 | | GNU General Public License for more details. |
15 | | |
16 | | You should have received a copy of the GNU General Public License |
17 | | along with this program. If not, see <http://www.gnu.org/licenses/>. |
18 | | */ |
19 | | |
20 | | #include "includes.h" |
21 | | #include "../lib/param/param.h" |
22 | | #include "../lib/async_req/async_sock.h" |
23 | | #include "../lib/util/tevent_ntstatus.h" |
24 | | #include "../lib/util/tevent_unix.h" |
25 | | #include "../lib/tsocket/tsocket.h" |
26 | | #include "client.h" |
27 | | #include "../libcli/smb/smbXcli_base.h" |
28 | | #include "async_smb.h" |
29 | | #include "../libcli/smb/read_smb.h" |
30 | | #include "libsmb/nmblib.h" |
31 | | #include "libsmb/smbsock_connect.h" |
32 | | #include "../source4/lib/tls/tls.h" |
33 | | |
34 | | #ifdef HAVE_LIBQUIC |
35 | | #include <netinet/quic.h> |
36 | | #endif |
37 | | |
38 | | struct cli_session_request_state { |
39 | | struct tevent_context *ev; |
40 | | int sock; |
41 | | uint32_t len_hdr; |
42 | | struct iovec iov[3]; |
43 | | uint8_t nb_session_response; |
44 | | }; |
45 | | |
46 | | static void cli_session_request_sent(struct tevent_req *subreq); |
47 | | static void cli_session_request_recvd(struct tevent_req *subreq); |
48 | | |
49 | | static struct tevent_req *cli_session_request_send(TALLOC_CTX *mem_ctx, |
50 | | struct tevent_context *ev, |
51 | | int sock, |
52 | | const struct nmb_name *called, |
53 | | const struct nmb_name *calling) |
54 | 0 | { |
55 | 0 | struct tevent_req *req, *subreq; |
56 | 0 | struct cli_session_request_state *state; |
57 | |
|
58 | 0 | req = tevent_req_create(mem_ctx, &state, |
59 | 0 | struct cli_session_request_state); |
60 | 0 | if (req == NULL) { |
61 | 0 | return NULL; |
62 | 0 | } |
63 | 0 | state->ev = ev; |
64 | 0 | state->sock = sock; |
65 | |
|
66 | 0 | state->iov[1].iov_base = name_mangle( |
67 | 0 | state, called->name, called->name_type); |
68 | 0 | if (tevent_req_nomem(state->iov[1].iov_base, req)) { |
69 | 0 | return tevent_req_post(req, ev); |
70 | 0 | } |
71 | 0 | state->iov[1].iov_len = name_len( |
72 | 0 | (unsigned char *)state->iov[1].iov_base, |
73 | 0 | talloc_get_size(state->iov[1].iov_base)); |
74 | |
|
75 | 0 | state->iov[2].iov_base = name_mangle( |
76 | 0 | state, calling->name, calling->name_type); |
77 | 0 | if (tevent_req_nomem(state->iov[2].iov_base, req)) { |
78 | 0 | return tevent_req_post(req, ev); |
79 | 0 | } |
80 | 0 | state->iov[2].iov_len = name_len( |
81 | 0 | (unsigned char *)state->iov[2].iov_base, |
82 | 0 | talloc_get_size(state->iov[2].iov_base)); |
83 | |
|
84 | 0 | _smb_setlen(((char *)&state->len_hdr), |
85 | 0 | state->iov[1].iov_len + state->iov[2].iov_len); |
86 | 0 | SCVAL((char *)&state->len_hdr, 0, 0x81); |
87 | |
|
88 | 0 | state->iov[0].iov_base = &state->len_hdr; |
89 | 0 | state->iov[0].iov_len = sizeof(state->len_hdr); |
90 | |
|
91 | 0 | subreq = writev_send(state, ev, NULL, sock, true, state->iov, 3); |
92 | 0 | if (tevent_req_nomem(subreq, req)) { |
93 | 0 | return tevent_req_post(req, ev); |
94 | 0 | } |
95 | 0 | tevent_req_set_callback(subreq, cli_session_request_sent, req); |
96 | 0 | return req; |
97 | 0 | } |
98 | | |
99 | | static void cli_session_request_sent(struct tevent_req *subreq) |
100 | 0 | { |
101 | 0 | struct tevent_req *req = tevent_req_callback_data( |
102 | 0 | subreq, struct tevent_req); |
103 | 0 | struct cli_session_request_state *state = tevent_req_data( |
104 | 0 | req, struct cli_session_request_state); |
105 | 0 | ssize_t ret; |
106 | 0 | int err; |
107 | |
|
108 | 0 | ret = writev_recv(subreq, &err); |
109 | 0 | TALLOC_FREE(subreq); |
110 | 0 | if (ret == -1) { |
111 | 0 | tevent_req_error(req, err); |
112 | 0 | return; |
113 | 0 | } |
114 | 0 | subreq = read_smb_send(state, state->ev, state->sock); |
115 | 0 | if (tevent_req_nomem(subreq, req)) { |
116 | 0 | return; |
117 | 0 | } |
118 | 0 | tevent_req_set_callback(subreq, cli_session_request_recvd, req); |
119 | 0 | } |
120 | | |
121 | | static void cli_session_request_recvd(struct tevent_req *subreq) |
122 | 0 | { |
123 | 0 | struct tevent_req *req = tevent_req_callback_data( |
124 | 0 | subreq, struct tevent_req); |
125 | 0 | struct cli_session_request_state *state = tevent_req_data( |
126 | 0 | req, struct cli_session_request_state); |
127 | 0 | uint8_t *buf; |
128 | 0 | ssize_t ret; |
129 | 0 | int err; |
130 | |
|
131 | 0 | ret = read_smb_recv(subreq, talloc_tos(), &buf, &err); |
132 | 0 | TALLOC_FREE(subreq); |
133 | |
|
134 | 0 | if (ret < 4) { |
135 | 0 | ret = -1; |
136 | 0 | err = EIO; |
137 | 0 | } |
138 | 0 | if (ret == -1) { |
139 | 0 | tevent_req_error(req, err); |
140 | 0 | return; |
141 | 0 | } |
142 | | /* |
143 | | * In case of an error there is more information in the data |
144 | | * portion according to RFC1002. We're not subtle enough to |
145 | | * respond to the different error conditions, so drop the |
146 | | * error info here. |
147 | | */ |
148 | 0 | state->nb_session_response = CVAL(buf, 0); |
149 | 0 | tevent_req_done(req); |
150 | 0 | } |
151 | | |
152 | | static bool cli_session_request_recv(struct tevent_req *req, int *err, uint8_t *resp) |
153 | 0 | { |
154 | 0 | struct cli_session_request_state *state = tevent_req_data( |
155 | 0 | req, struct cli_session_request_state); |
156 | |
|
157 | 0 | if (tevent_req_is_unix_error(req, err)) { |
158 | 0 | return false; |
159 | 0 | } |
160 | 0 | *resp = state->nb_session_response; |
161 | 0 | return true; |
162 | 0 | } |
163 | | |
164 | | struct nb_connect_state { |
165 | | struct tevent_context *ev; |
166 | | const struct sockaddr_storage *addr; |
167 | | const char *called_name; |
168 | | int sock; |
169 | | struct tevent_req *session_subreq; |
170 | | struct nmb_name called; |
171 | | struct nmb_name calling; |
172 | | }; |
173 | | |
174 | | static void nb_connect_cleanup(struct tevent_req *req, |
175 | | enum tevent_req_state req_state); |
176 | | static void nb_connect_connected(struct tevent_req *subreq); |
177 | | static void nb_connect_done(struct tevent_req *subreq); |
178 | | |
179 | | static struct tevent_req *nb_connect_send(TALLOC_CTX *mem_ctx, |
180 | | struct tevent_context *ev, |
181 | | const struct sockaddr_storage *addr, |
182 | | const char *called_name, |
183 | | int called_type, |
184 | | const char *calling_name, |
185 | | int calling_type, |
186 | | uint16_t port) |
187 | 0 | { |
188 | 0 | struct tevent_req *req, *subreq; |
189 | 0 | struct nb_connect_state *state; |
190 | |
|
191 | 0 | req = tevent_req_create(mem_ctx, &state, struct nb_connect_state); |
192 | 0 | if (req == NULL) { |
193 | 0 | return NULL; |
194 | 0 | } |
195 | 0 | state->ev = ev; |
196 | 0 | state->called_name = called_name; |
197 | 0 | state->addr = addr; |
198 | |
|
199 | 0 | state->sock = -1; |
200 | 0 | make_nmb_name(&state->called, called_name, called_type); |
201 | 0 | make_nmb_name(&state->calling, calling_name, calling_type); |
202 | |
|
203 | 0 | tevent_req_set_cleanup_fn(req, nb_connect_cleanup); |
204 | |
|
205 | 0 | subreq = open_socket_out_send(state, |
206 | 0 | ev, |
207 | 0 | IPPROTO_TCP, |
208 | 0 | addr, |
209 | 0 | port, |
210 | 0 | 5000); |
211 | 0 | if (tevent_req_nomem(subreq, req)) { |
212 | 0 | return tevent_req_post(req, ev); |
213 | 0 | } |
214 | 0 | tevent_req_set_callback(subreq, nb_connect_connected, req); |
215 | 0 | return req; |
216 | 0 | } |
217 | | |
218 | | static void nb_connect_cleanup(struct tevent_req *req, |
219 | | enum tevent_req_state req_state) |
220 | 0 | { |
221 | 0 | struct nb_connect_state *state = tevent_req_data( |
222 | 0 | req, struct nb_connect_state); |
223 | | |
224 | | /* |
225 | | * we need to free a pending request before closing the |
226 | | * socket, see bug #11141 |
227 | | */ |
228 | 0 | TALLOC_FREE(state->session_subreq); |
229 | |
|
230 | 0 | if (req_state == TEVENT_REQ_DONE) { |
231 | | /* |
232 | | * we keep the socket open for the caller to use |
233 | | */ |
234 | 0 | return; |
235 | 0 | } |
236 | | |
237 | 0 | if (state->sock != -1) { |
238 | 0 | close(state->sock); |
239 | 0 | state->sock = -1; |
240 | 0 | } |
241 | |
|
242 | 0 | return; |
243 | 0 | } |
244 | | |
245 | | static void nb_connect_connected(struct tevent_req *subreq) |
246 | 0 | { |
247 | 0 | struct tevent_req *req = tevent_req_callback_data( |
248 | 0 | subreq, struct tevent_req); |
249 | 0 | struct nb_connect_state *state = tevent_req_data( |
250 | 0 | req, struct nb_connect_state); |
251 | 0 | NTSTATUS status; |
252 | |
|
253 | 0 | status = open_socket_out_recv(subreq, &state->sock); |
254 | 0 | TALLOC_FREE(subreq); |
255 | 0 | if (tevent_req_nterror(req, status)) { |
256 | 0 | return; |
257 | 0 | } |
258 | 0 | subreq = cli_session_request_send(state, state->ev, state->sock, |
259 | 0 | &state->called, &state->calling); |
260 | 0 | if (tevent_req_nomem(subreq, req)) { |
261 | 0 | return; |
262 | 0 | } |
263 | 0 | tevent_req_set_callback(subreq, nb_connect_done, req); |
264 | 0 | state->session_subreq = subreq; |
265 | 0 | } |
266 | | |
267 | | static void nb_connect_done(struct tevent_req *subreq) |
268 | 0 | { |
269 | 0 | struct tevent_req *req = tevent_req_callback_data( |
270 | 0 | subreq, struct tevent_req); |
271 | 0 | struct nb_connect_state *state = tevent_req_data( |
272 | 0 | req, struct nb_connect_state); |
273 | 0 | bool ret; |
274 | 0 | int err; |
275 | 0 | uint8_t resp; |
276 | |
|
277 | 0 | state->session_subreq = NULL; |
278 | |
|
279 | 0 | ret = cli_session_request_recv(subreq, &err, &resp); |
280 | 0 | TALLOC_FREE(subreq); |
281 | 0 | if (!ret) { |
282 | 0 | tevent_req_nterror(req, map_nt_error_from_unix(err)); |
283 | 0 | return; |
284 | 0 | } |
285 | | |
286 | | /* |
287 | | * RFC1002: 0x82 - POSITIVE SESSION RESPONSE |
288 | | */ |
289 | | |
290 | 0 | if (resp != 0x82) { |
291 | | /* |
292 | | * The server did not like our session request |
293 | | */ |
294 | 0 | close(state->sock); |
295 | 0 | state->sock = -1; |
296 | |
|
297 | 0 | if (strequal(state->called_name, "*SMBSERVER")) { |
298 | | /* |
299 | | * Here we could try a name status request and |
300 | | * use the first 0x20 type name. |
301 | | */ |
302 | 0 | tevent_req_nterror( |
303 | 0 | req, NT_STATUS_RESOURCE_NAME_NOT_FOUND); |
304 | 0 | return; |
305 | 0 | } |
306 | | |
307 | | /* |
308 | | * We could be subtle and distinguish between |
309 | | * different failure modes, but what we do here |
310 | | * instead is just retry with *SMBSERVER type 0x20. |
311 | | */ |
312 | 0 | state->called_name = "*SMBSERVER"; |
313 | 0 | make_nmb_name(&state->called, state->called_name, 0x20); |
314 | |
|
315 | 0 | subreq = open_socket_out_send(state, |
316 | 0 | state->ev, |
317 | 0 | IPPROTO_TCP, |
318 | 0 | state->addr, |
319 | 0 | NBT_SMB_PORT, |
320 | 0 | 5000); |
321 | 0 | if (tevent_req_nomem(subreq, req)) { |
322 | 0 | return; |
323 | 0 | } |
324 | 0 | tevent_req_set_callback(subreq, nb_connect_connected, req); |
325 | 0 | return; |
326 | 0 | } |
327 | | |
328 | 0 | tevent_req_done(req); |
329 | 0 | return; |
330 | 0 | } |
331 | | |
332 | | static NTSTATUS nb_connect_recv(struct tevent_req *req, int *sock) |
333 | 0 | { |
334 | 0 | struct nb_connect_state *state = tevent_req_data( |
335 | 0 | req, struct nb_connect_state); |
336 | 0 | NTSTATUS status; |
337 | |
|
338 | 0 | if (tevent_req_is_nterror(req, &status)) { |
339 | 0 | tevent_req_received(req); |
340 | 0 | return status; |
341 | 0 | } |
342 | 0 | *sock = state->sock; |
343 | 0 | state->sock = -1; |
344 | 0 | tevent_req_received(req); |
345 | 0 | return NT_STATUS_OK; |
346 | 0 | } |
347 | | |
348 | | struct smb_transports smbsock_transports_from_port(uint16_t port) |
349 | 0 | { |
350 | 0 | struct smb_transports ts = { .num_transports = 0, }; |
351 | |
|
352 | 0 | if (port == 0) { |
353 | 0 | ts = smb_transports_parse("client smb transports", |
354 | 0 | lp_client_smb_transports()); |
355 | 0 | } else if (port == NBT_SMB_PORT) { |
356 | 0 | struct smb_transport *t = &ts.transports[0]; |
357 | |
|
358 | 0 | *t = (struct smb_transport) { |
359 | 0 | .type = SMB_TRANSPORT_TYPE_NBT, |
360 | 0 | .port = NBT_SMB_PORT, |
361 | 0 | }; |
362 | 0 | ts.num_transports = 1; |
363 | 0 | } else { |
364 | 0 | struct smb_transport *t = &ts.transports[0]; |
365 | |
|
366 | 0 | *t = (struct smb_transport) { |
367 | 0 | .type = SMB_TRANSPORT_TYPE_TCP, |
368 | 0 | .port = port, |
369 | 0 | }; |
370 | 0 | ts.num_transports = 1; |
371 | 0 | } |
372 | |
|
373 | 0 | return ts; |
374 | 0 | } |
375 | | |
376 | | bool smbsock_connect_require_bsd_socket; |
377 | | |
378 | | struct smbsock_connect_substate { |
379 | | struct tevent_req *req; |
380 | | size_t idx; |
381 | | struct smb_transport transport; |
382 | | struct tevent_req *subreq; |
383 | | int sockfd; |
384 | | struct samba_sockaddr laddr; |
385 | | struct samba_sockaddr raddr; |
386 | | }; |
387 | | |
388 | | struct smbsock_connect_state { |
389 | | struct tevent_context *ev; |
390 | | const struct sockaddr_storage *addr; |
391 | | const char *target_name; |
392 | | const char *called_name; |
393 | | uint8_t called_type; |
394 | | const char *calling_name; |
395 | | uint8_t calling_type; |
396 | | struct tstream_tls_params *quic_tlsp; |
397 | | bool allow_ngtcp2; |
398 | | bool force_ngtcp2; |
399 | | struct tevent_req *wake_subreq; |
400 | | uint8_t num_substates; |
401 | | uint8_t submit_idx; |
402 | | uint8_t num_pending; |
403 | | struct smbsock_connect_substate substates[SMB_TRANSPORTS_MAX_TRANSPORTS]; |
404 | | struct smbXcli_transport *transport; |
405 | | struct smbXcli_transport *(*create_bsd_transport)( |
406 | | TALLOC_CTX *mem_ctx, |
407 | | int *fd, |
408 | | const struct smb_transport *tp); |
409 | | }; |
410 | | |
411 | | static void smbsock_connect_cleanup(struct tevent_req *req, |
412 | | enum tevent_req_state req_state); |
413 | | static bool smbsock_connect_submit_next(struct tevent_req *req); |
414 | | static void smbsock_connect_waited(struct tevent_req *subreq); |
415 | | static void smbsock_connect_nbt_connected(struct tevent_req *subreq); |
416 | | static void smbsock_connect_tcp_connected(struct tevent_req *subreq); |
417 | | #ifdef HAVE_LIBQUIC |
418 | | static void smbsock_connect_quic_connected(struct tevent_req *subreq); |
419 | | static void smbsock_connect_quic_ready(struct tevent_req *subreq); |
420 | | #endif /* HAVE_LIBQUIC */ |
421 | | #ifdef HAVE_LIBNGTCP2 |
422 | | static NTSTATUS smbsock_connect_ngtcp2_udp_sock( |
423 | | const struct sockaddr_storage *addr, |
424 | | uint16_t port, |
425 | | int *_sockfd, |
426 | | struct samba_sockaddr *laddr, |
427 | | struct samba_sockaddr *raddr); |
428 | | static void smbsock_connect_ngtcp2_ready(struct tevent_req *subreq); |
429 | | #endif /* HAVE_LIBNGTCP2 */ |
430 | | |
431 | | struct tevent_req *smbsock_connect_send(TALLOC_CTX *mem_ctx, |
432 | | struct tevent_context *ev, |
433 | | struct loadparm_context *lp_ctx, |
434 | | const struct sockaddr_storage *addr, |
435 | | const struct smb_transports *transports, |
436 | | const char *called_name, |
437 | | int called_type, |
438 | | const char *calling_name, |
439 | | int calling_type) |
440 | 0 | { |
441 | 0 | struct tevent_req *req; |
442 | 0 | struct smbsock_connect_state *state; |
443 | 0 | bool force_bsd_tstream = false; |
444 | 0 | uint8_t num_unsupported = 0; |
445 | 0 | struct smb_transports ts = *transports; |
446 | 0 | uint8_t ti; |
447 | 0 | bool ok; |
448 | 0 | bool request_quic = false; |
449 | 0 | bool try_quic = false; |
450 | |
|
451 | 0 | req = tevent_req_create(mem_ctx, &state, struct smbsock_connect_state); |
452 | 0 | if (req == NULL) { |
453 | 0 | return NULL; |
454 | 0 | } |
455 | 0 | state->ev = ev; |
456 | 0 | state->addr = addr; |
457 | 0 | state->target_name = called_name; |
458 | 0 | state->called_name = |
459 | 0 | (called_name != NULL) ? called_name : "*SMBSERVER"; |
460 | 0 | state->called_type = |
461 | 0 | (called_type != -1) ? called_type : 0x20; |
462 | 0 | state->calling_name = |
463 | 0 | (calling_name != NULL) ? calling_name : lp_netbios_name(); |
464 | 0 | state->calling_type = |
465 | 0 | (calling_type != -1) ? calling_type : 0x00; |
466 | |
|
467 | 0 | force_bsd_tstream = lpcfg_parm_bool(lp_ctx, |
468 | 0 | NULL, |
469 | 0 | "client smb transport", |
470 | 0 | "force_bsd_tstream", |
471 | 0 | false); |
472 | 0 | if (force_bsd_tstream) { |
473 | 0 | state->create_bsd_transport = smbXcli_transport_bsd_tstream; |
474 | 0 | } else { |
475 | 0 | state->create_bsd_transport = smbXcli_transport_bsd; |
476 | 0 | } |
477 | |
|
478 | 0 | tevent_req_set_cleanup_fn(req, smbsock_connect_cleanup); |
479 | |
|
480 | 0 | SMB_ASSERT(ts.num_transports <= ARRAY_SIZE(state->substates)); |
481 | | |
482 | 0 | for (ti = 0; ti < ts.num_transports; ti++) { |
483 | 0 | const struct smb_transport *t = &ts.transports[ti]; |
484 | |
|
485 | 0 | if (t->type != SMB_TRANSPORT_TYPE_QUIC) { |
486 | 0 | continue; |
487 | 0 | } |
488 | | |
489 | 0 | if (state->target_name != NULL) { |
490 | 0 | request_quic = true; |
491 | 0 | break; |
492 | 0 | } |
493 | 0 | } |
494 | |
|
495 | 0 | if (request_quic) { |
496 | 0 | NTSTATUS status; |
497 | |
|
498 | 0 | status = tstream_tls_params_client_lpcfg(state, |
499 | 0 | lp_ctx, |
500 | 0 | state->target_name, |
501 | 0 | &state->quic_tlsp); |
502 | 0 | if (tevent_req_nterror(req, status)) { |
503 | 0 | return tevent_req_post(req, ev); |
504 | 0 | } |
505 | | |
506 | 0 | status = tstream_tls_params_quic_prepare(state->quic_tlsp); |
507 | 0 | if (tevent_req_nterror(req, status)) { |
508 | 0 | return tevent_req_post(req, ev); |
509 | 0 | } |
510 | | |
511 | 0 | try_quic = tstream_tls_params_quic_enabled(state->quic_tlsp); |
512 | |
|
513 | 0 | state->allow_ngtcp2 = lpcfg_parm_bool(lp_ctx, |
514 | 0 | NULL, |
515 | 0 | "client smb transport", |
516 | 0 | "allow_ngtcp2_quic", |
517 | 0 | try_quic); |
518 | 0 | state->force_ngtcp2 = lpcfg_parm_bool(lp_ctx, |
519 | 0 | NULL, |
520 | 0 | "client smb transport", |
521 | 0 | "force_ngtcp2_quic", |
522 | 0 | false); |
523 | 0 | } |
524 | | |
525 | 0 | if (smbsock_connect_require_bsd_socket) { |
526 | | /* |
527 | | * This is libsmbclient in use |
528 | | * there's no periodic connection |
529 | | * monitoring, so we can't use |
530 | | * the ngtcp2 over udp quic support. |
531 | | */ |
532 | 0 | state->allow_ngtcp2 = false; |
533 | 0 | } |
534 | |
|
535 | 0 | for (ti = 0; ti < ts.num_transports; ti++) { |
536 | 0 | const struct smb_transport *t = &ts.transports[ti]; |
537 | 0 | struct smbsock_connect_substate *s = |
538 | 0 | &state->substates[state->num_substates]; |
539 | |
|
540 | 0 | switch (t->type) { |
541 | 0 | case SMB_TRANSPORT_TYPE_UNKNOWN: |
542 | | /* |
543 | | * Should never happen |
544 | | */ |
545 | 0 | smb_panic(__location__); |
546 | 0 | continue; |
547 | 0 | case SMB_TRANSPORT_TYPE_NBT: |
548 | 0 | if (lp_disable_netbios()) { |
549 | 0 | num_unsupported += 1; |
550 | 0 | continue; |
551 | 0 | } |
552 | 0 | break; |
553 | 0 | case SMB_TRANSPORT_TYPE_TCP: |
554 | 0 | break; |
555 | 0 | case SMB_TRANSPORT_TYPE_QUIC: |
556 | 0 | if (try_quic) { |
557 | 0 | break; |
558 | 0 | } |
559 | | |
560 | | /* |
561 | | * Not supported yet or no |
562 | | * called_name as peer name |
563 | | * available. |
564 | | */ |
565 | 0 | continue; |
566 | 0 | } |
567 | | |
568 | 0 | s->req = req; |
569 | 0 | s->idx = state->num_substates; |
570 | 0 | s->transport = *t; |
571 | 0 | s->sockfd = -1; |
572 | |
|
573 | 0 | state->num_substates += 1; |
574 | 0 | } |
575 | | |
576 | 0 | if (state->num_substates == 0 && num_unsupported != 0) { |
577 | 0 | tevent_req_nterror(req, NT_STATUS_NOT_SUPPORTED); |
578 | 0 | return tevent_req_post(req, ev); |
579 | 0 | } |
580 | | |
581 | 0 | if (state->num_substates == 0) { |
582 | 0 | tevent_req_nterror(req, NT_STATUS_PORT_NOT_SET); |
583 | 0 | return tevent_req_post(req, ev); |
584 | 0 | } |
585 | | |
586 | 0 | ok = smbsock_connect_submit_next(req); |
587 | 0 | if (!ok) { |
588 | 0 | return tevent_req_post(req, ev); |
589 | 0 | } |
590 | | |
591 | 0 | if (state->submit_idx == state->num_substates) { |
592 | | /* only one transport */ |
593 | 0 | return req; |
594 | 0 | } |
595 | | |
596 | | /* |
597 | | * After 5 msecs, fire all remaining requests |
598 | | */ |
599 | 0 | state->wake_subreq = tevent_wakeup_send(state, |
600 | 0 | ev, |
601 | 0 | timeval_current_ofs(0, 5000)); |
602 | 0 | if (tevent_req_nomem(state->wake_subreq, req)) { |
603 | 0 | return tevent_req_post(req, ev); |
604 | 0 | } |
605 | 0 | tevent_req_set_callback(state->wake_subreq, |
606 | 0 | smbsock_connect_waited, |
607 | 0 | req); |
608 | |
|
609 | 0 | return req; |
610 | 0 | } |
611 | | |
612 | | static void smbsock_connect_cleanup(struct tevent_req *req, |
613 | | enum tevent_req_state req_state) |
614 | 0 | { |
615 | 0 | struct smbsock_connect_state *state = tevent_req_data( |
616 | 0 | req, struct smbsock_connect_state); |
617 | 0 | uint8_t si; |
618 | | |
619 | | /* |
620 | | * we need to free a pending request before closing the |
621 | | * socket, see bug #11141 |
622 | | */ |
623 | 0 | TALLOC_FREE(state->wake_subreq); |
624 | 0 | for (si = 0; si < state->num_substates; si++) { |
625 | 0 | struct smbsock_connect_substate *s = |
626 | 0 | &state->substates[si]; |
627 | |
|
628 | 0 | TALLOC_FREE(s->subreq); |
629 | 0 | if (s->sockfd != -1) { |
630 | 0 | close(s->sockfd); |
631 | 0 | s->sockfd = -1; |
632 | 0 | } |
633 | 0 | } |
634 | |
|
635 | 0 | if (req_state == TEVENT_REQ_DONE) { |
636 | | /* |
637 | | * we keep the socket open for the caller to use |
638 | | */ |
639 | 0 | return; |
640 | 0 | } |
641 | | |
642 | 0 | TALLOC_FREE(state->transport); |
643 | |
|
644 | 0 | return; |
645 | 0 | } |
646 | | |
647 | | static bool smbsock_connect_submit_next(struct tevent_req *req) |
648 | 0 | { |
649 | 0 | struct smbsock_connect_state *state = |
650 | 0 | tevent_req_data(req, |
651 | 0 | struct smbsock_connect_state); |
652 | 0 | struct smbsock_connect_substate *s = |
653 | 0 | &state->substates[state->submit_idx]; |
654 | | #ifdef HAVE_LIBNGTCP2 |
655 | | NTSTATUS status; |
656 | | #endif /* HAVE_LIBNGTCP2 */ |
657 | |
|
658 | 0 | SMB_ASSERT(state->submit_idx < state->num_substates); |
659 | | |
660 | 0 | switch (s->transport.type) { |
661 | 0 | case SMB_TRANSPORT_TYPE_UNKNOWN: |
662 | 0 | break; |
663 | | |
664 | 0 | case SMB_TRANSPORT_TYPE_NBT: |
665 | 0 | s->subreq = nb_connect_send(state, |
666 | 0 | state->ev, |
667 | 0 | state->addr, |
668 | 0 | state->called_name, |
669 | 0 | state->called_type, |
670 | 0 | state->calling_name, |
671 | 0 | state->calling_type, |
672 | 0 | s->transport.port); |
673 | 0 | if (tevent_req_nomem(s->subreq, req)) { |
674 | 0 | return false; |
675 | 0 | } |
676 | 0 | tevent_req_set_callback(s->subreq, |
677 | 0 | smbsock_connect_nbt_connected, |
678 | 0 | s); |
679 | 0 | break; |
680 | | |
681 | 0 | case SMB_TRANSPORT_TYPE_TCP: |
682 | 0 | s->subreq = open_socket_out_send(state, |
683 | 0 | state->ev, |
684 | 0 | IPPROTO_TCP, |
685 | 0 | state->addr, |
686 | 0 | s->transport.port, |
687 | 0 | 5000); |
688 | 0 | if (tevent_req_nomem(s->subreq, req)) { |
689 | 0 | return false; |
690 | 0 | } |
691 | 0 | tevent_req_set_callback(s->subreq, |
692 | 0 | smbsock_connect_tcp_connected, |
693 | 0 | s); |
694 | 0 | break; |
695 | | |
696 | 0 | case SMB_TRANSPORT_TYPE_QUIC: |
697 | | #ifdef HAVE_LIBQUIC |
698 | | if (state->force_ngtcp2) { |
699 | | goto try_ngtcp2; |
700 | | } |
701 | | |
702 | | s->subreq = open_socket_out_send(state, |
703 | | state->ev, |
704 | | IPPROTO_QUIC, |
705 | | state->addr, |
706 | | s->transport.port, |
707 | | 5000); |
708 | | if (tevent_req_nomem(s->subreq, req)) { |
709 | | return false; |
710 | | } |
711 | | tevent_req_set_callback(s->subreq, |
712 | | smbsock_connect_quic_connected, |
713 | | s); |
714 | | break; |
715 | | try_ngtcp2: |
716 | | #define __HAVE_LIBQUIC_OR_LIBNGTCP2 1 |
717 | | #endif /* HAVE_LIBQUIC */ |
718 | | #ifdef HAVE_LIBNGTCP2 |
719 | | if (!state->allow_ngtcp2) { |
720 | | tevent_req_nterror(req, NT_STATUS_PROTOCOL_NOT_SUPPORTED); |
721 | | return false; |
722 | | } |
723 | | |
724 | | status = smbsock_connect_ngtcp2_udp_sock(state->addr, |
725 | | s->transport.port, |
726 | | &s->sockfd, |
727 | | &s->laddr, |
728 | | &s->raddr); |
729 | | if (tevent_req_nterror(req, status)) { |
730 | | return false; |
731 | | } |
732 | | |
733 | | s->subreq = tstream_tls_ngtcp2_connect_send(state, |
734 | | state->ev, |
735 | | state->quic_tlsp, |
736 | | 5000, |
737 | | "smb", |
738 | | &s->sockfd); |
739 | | if (tevent_req_nomem(s->subreq, req)) { |
740 | | return false; |
741 | | } |
742 | | tevent_req_set_callback(s->subreq, |
743 | | smbsock_connect_ngtcp2_ready, |
744 | | s); |
745 | | break; |
746 | | #define __HAVE_LIBQUIC_OR_LIBNGTCP2 1 |
747 | | #endif /* HAVE_LIBNGTCP2 */ |
748 | 0 | #ifndef __HAVE_LIBQUIC_OR_LIBNGTCP2 |
749 | | /* |
750 | | * Not supported yet, should already be |
751 | | * checked above. |
752 | | */ |
753 | 0 | smb_panic(__location__); |
754 | 0 | break; |
755 | 0 | #endif /* ! __HAVE_LIBQUIC_OR_LIBNGTCP2 */ |
756 | 0 | } |
757 | | |
758 | 0 | if (s->subreq == NULL) { |
759 | 0 | tevent_req_nterror(req, NT_STATUS_INTERNAL_ERROR); |
760 | 0 | return false; |
761 | 0 | } |
762 | | |
763 | 0 | state->num_pending += 1; |
764 | 0 | state->submit_idx += 1; |
765 | 0 | if (state->submit_idx == state->num_substates) { |
766 | 0 | TALLOC_FREE(state->wake_subreq); |
767 | 0 | } |
768 | |
|
769 | 0 | return true; |
770 | 0 | } |
771 | | |
772 | | static void smbsock_connect_waited(struct tevent_req *subreq) |
773 | 0 | { |
774 | 0 | struct tevent_req *req = tevent_req_callback_data( |
775 | 0 | subreq, struct tevent_req); |
776 | 0 | struct smbsock_connect_state *state = tevent_req_data( |
777 | 0 | req, struct smbsock_connect_state); |
778 | 0 | bool ok; |
779 | |
|
780 | 0 | SMB_ASSERT(state->wake_subreq == subreq); |
781 | 0 | state->wake_subreq = NULL; |
782 | |
|
783 | 0 | ok = tevent_wakeup_recv(subreq); |
784 | 0 | TALLOC_FREE(subreq); |
785 | 0 | if (!ok) { |
786 | 0 | tevent_req_nterror(req, NT_STATUS_INTERNAL_ERROR); |
787 | 0 | return; |
788 | 0 | } |
789 | | |
790 | 0 | SMB_ASSERT(state->submit_idx < state->num_substates); |
791 | | |
792 | 0 | while (state->submit_idx < state->num_substates) { |
793 | 0 | ok = smbsock_connect_submit_next(req); |
794 | 0 | if (!ok) { |
795 | 0 | return; |
796 | 0 | } |
797 | 0 | } |
798 | 0 | } |
799 | | |
800 | | static void smbsock_connect_nbt_connected(struct tevent_req *subreq) |
801 | 0 | { |
802 | 0 | struct smbsock_connect_substate *s = |
803 | 0 | (struct smbsock_connect_substate *) |
804 | 0 | tevent_req_callback_data_void(subreq); |
805 | 0 | struct tevent_req *req = s->req; |
806 | 0 | struct smbsock_connect_state *state = |
807 | 0 | tevent_req_data(req, |
808 | 0 | struct smbsock_connect_state); |
809 | 0 | NTSTATUS status; |
810 | |
|
811 | 0 | SMB_ASSERT(s->subreq == subreq); |
812 | 0 | s->subreq = NULL; |
813 | 0 | SMB_ASSERT(state->num_pending > 0); |
814 | 0 | state->num_pending -= 1; |
815 | |
|
816 | 0 | status = nb_connect_recv(subreq, &s->sockfd); |
817 | 0 | TALLOC_FREE(subreq); |
818 | 0 | if (NT_STATUS_IS_OK(status)) { |
819 | | /* |
820 | | * smbsock_connect_cleanup() |
821 | | * will free all other subreqs |
822 | | */ |
823 | 0 | set_socket_options(s->sockfd, lp_socket_options()); |
824 | 0 | state->transport = state->create_bsd_transport(state, |
825 | 0 | &s->sockfd, |
826 | 0 | &s->transport); |
827 | 0 | if (tevent_req_nomem(state->transport, req)) { |
828 | 0 | return; |
829 | 0 | } |
830 | 0 | tevent_req_done(req); |
831 | 0 | return; |
832 | 0 | } |
833 | | |
834 | | /* |
835 | | * Do nothing, wait for the remaining |
836 | | * requests to come here. |
837 | | * |
838 | | * Submit the next requests if there |
839 | | * are unsubmitted requests remaining. |
840 | | */ |
841 | 0 | if (state->submit_idx < state->num_substates) { |
842 | 0 | bool ok; |
843 | |
|
844 | 0 | ok = smbsock_connect_submit_next(req); |
845 | 0 | if (!ok) { |
846 | 0 | return; |
847 | 0 | } |
848 | 0 | } |
849 | | |
850 | 0 | if (state->num_pending == 0) { |
851 | | /* |
852 | | * All requests failed |
853 | | * |
854 | | * smbsock_connect_cleanup() |
855 | | * will free all other subreqs |
856 | | */ |
857 | 0 | tevent_req_nterror(req, status); |
858 | 0 | return; |
859 | 0 | } |
860 | 0 | } |
861 | | |
862 | | static void smbsock_connect_tcp_connected(struct tevent_req *subreq) |
863 | 0 | { |
864 | 0 | struct smbsock_connect_substate *s = |
865 | 0 | (struct smbsock_connect_substate *) |
866 | 0 | tevent_req_callback_data_void(subreq); |
867 | 0 | struct tevent_req *req = s->req; |
868 | 0 | struct smbsock_connect_state *state = |
869 | 0 | tevent_req_data(req, |
870 | 0 | struct smbsock_connect_state); |
871 | 0 | NTSTATUS status; |
872 | |
|
873 | 0 | SMB_ASSERT(s->subreq == subreq); |
874 | 0 | s->subreq = NULL; |
875 | 0 | SMB_ASSERT(state->num_pending > 0); |
876 | 0 | state->num_pending -= 1; |
877 | |
|
878 | 0 | status = open_socket_out_recv(subreq, &s->sockfd); |
879 | 0 | TALLOC_FREE(subreq); |
880 | 0 | if (NT_STATUS_IS_OK(status)) { |
881 | | /* |
882 | | * smbsock_connect_cleanup() |
883 | | * will free all other subreqs |
884 | | */ |
885 | 0 | set_socket_options(s->sockfd, lp_socket_options()); |
886 | 0 | state->transport = state->create_bsd_transport(state, |
887 | 0 | &s->sockfd, |
888 | 0 | &s->transport); |
889 | 0 | if (tevent_req_nomem(state->transport, req)) { |
890 | 0 | return; |
891 | 0 | } |
892 | 0 | tevent_req_done(req); |
893 | 0 | return; |
894 | 0 | } |
895 | | |
896 | | /* |
897 | | * Do nothing, wait for the remaining |
898 | | * requests to come here. |
899 | | * |
900 | | * Submit the next requests if there |
901 | | * are unsubmitted requests remaining. |
902 | | */ |
903 | 0 | if (state->submit_idx < state->num_substates) { |
904 | 0 | bool ok; |
905 | |
|
906 | 0 | ok = smbsock_connect_submit_next(req); |
907 | 0 | if (!ok) { |
908 | 0 | return; |
909 | 0 | } |
910 | 0 | } |
911 | | |
912 | 0 | if (state->num_pending == 0) { |
913 | | /* |
914 | | * All requests failed |
915 | | * |
916 | | * smbsock_connect_cleanup() |
917 | | * will free all other subreqs |
918 | | */ |
919 | 0 | tevent_req_nterror(req, status); |
920 | 0 | return; |
921 | 0 | } |
922 | 0 | } |
923 | | |
924 | | #ifdef HAVE_LIBQUIC |
925 | | static void smbsock_connect_quic_connected(struct tevent_req *subreq) |
926 | | { |
927 | | struct smbsock_connect_substate *s = |
928 | | (struct smbsock_connect_substate *) |
929 | | tevent_req_callback_data_void(subreq); |
930 | | struct tevent_req *req = s->req; |
931 | | struct smbsock_connect_state *state = |
932 | | tevent_req_data(req, |
933 | | struct smbsock_connect_state); |
934 | | NTSTATUS status; |
935 | | |
936 | | SMB_ASSERT(s->subreq == subreq); |
937 | | s->subreq = NULL; |
938 | | SMB_ASSERT(state->num_pending > 0); |
939 | | state->num_pending -= 1; |
940 | | |
941 | | status = open_socket_out_recv(subreq, &s->sockfd); |
942 | | TALLOC_FREE(subreq); |
943 | | #ifdef HAVE_LIBNGTCP2 |
944 | | if (NT_STATUS_EQUAL(status, NT_STATUS_PROTOCOL_NOT_SUPPORTED)) { |
945 | | /* |
946 | | * fallback to ngtcp2 over udp sockets |
947 | | * in order to implement QUIC |
948 | | */ |
949 | | |
950 | | if (!state->allow_ngtcp2) { |
951 | | goto fail; |
952 | | } |
953 | | |
954 | | status = smbsock_connect_ngtcp2_udp_sock(state->addr, |
955 | | s->transport.port, |
956 | | &s->sockfd, |
957 | | &s->laddr, |
958 | | &s->raddr); |
959 | | if (!NT_STATUS_IS_OK(status)) { |
960 | | goto fail; |
961 | | } |
962 | | |
963 | | s->subreq = tstream_tls_ngtcp2_connect_send(state, |
964 | | state->ev, |
965 | | state->quic_tlsp, |
966 | | 5000, |
967 | | "smb", |
968 | | &s->sockfd); |
969 | | if (s->subreq == NULL) { |
970 | | status = NT_STATUS_NO_MEMORY; |
971 | | goto fail; |
972 | | } |
973 | | tevent_req_set_callback(s->subreq, |
974 | | smbsock_connect_ngtcp2_ready, |
975 | | s); |
976 | | state->num_pending += 1; |
977 | | return; |
978 | | } |
979 | | #endif /* HAVE_LIBNGTCP2 */ |
980 | | if (NT_STATUS_IS_OK(status)) { |
981 | | s->subreq = tstream_tls_quic_handshake_send(state, |
982 | | state->ev, |
983 | | state->quic_tlsp, |
984 | | false, /* is_server */ |
985 | | 5000, |
986 | | "smb", |
987 | | s->sockfd); |
988 | | if (s->subreq == NULL) { |
989 | | status = NT_STATUS_NO_MEMORY; |
990 | | goto fail; |
991 | | } |
992 | | tevent_req_set_callback(s->subreq, |
993 | | smbsock_connect_quic_ready, |
994 | | s); |
995 | | state->num_pending += 1; |
996 | | return; |
997 | | } |
998 | | |
999 | | fail: |
1000 | | /* |
1001 | | * Do nothing, wait for the remaining |
1002 | | * requests to come here. |
1003 | | * |
1004 | | * Submit the next requests if there |
1005 | | * are unsubmitted requests remaining. |
1006 | | */ |
1007 | | if (state->submit_idx < state->num_substates) { |
1008 | | bool ok; |
1009 | | |
1010 | | ok = smbsock_connect_submit_next(req); |
1011 | | if (!ok) { |
1012 | | return; |
1013 | | } |
1014 | | } |
1015 | | |
1016 | | if (state->num_pending == 0) { |
1017 | | /* |
1018 | | * All requests failed |
1019 | | * |
1020 | | * smbsock_connect_cleanup() |
1021 | | * will free all other subreqs |
1022 | | */ |
1023 | | tevent_req_nterror(req, status); |
1024 | | return; |
1025 | | } |
1026 | | } |
1027 | | |
1028 | | static void smbsock_connect_quic_ready(struct tevent_req *subreq) |
1029 | | { |
1030 | | struct smbsock_connect_substate *s = |
1031 | | (struct smbsock_connect_substate *) |
1032 | | tevent_req_callback_data_void(subreq); |
1033 | | struct tevent_req *req = s->req; |
1034 | | struct smbsock_connect_state *state = |
1035 | | tevent_req_data(req, |
1036 | | struct smbsock_connect_state); |
1037 | | NTSTATUS status; |
1038 | | |
1039 | | SMB_ASSERT(s->subreq == subreq); |
1040 | | s->subreq = NULL; |
1041 | | SMB_ASSERT(state->num_pending > 0); |
1042 | | state->num_pending -= 1; |
1043 | | |
1044 | | status = tstream_tls_quic_handshake_recv(subreq); |
1045 | | TALLOC_FREE(subreq); |
1046 | | if (NT_STATUS_IS_OK(status)) { |
1047 | | /* |
1048 | | * smbsock_connect_cleanup() |
1049 | | * will free all other subreqs |
1050 | | */ |
1051 | | state->transport = state->create_bsd_transport(state, |
1052 | | &s->sockfd, |
1053 | | &s->transport); |
1054 | | if (tevent_req_nomem(state->transport, req)) { |
1055 | | return; |
1056 | | } |
1057 | | tevent_req_done(req); |
1058 | | return; |
1059 | | } |
1060 | | |
1061 | | /* |
1062 | | * Do nothing, wait for the remaining |
1063 | | * requests to come here. |
1064 | | * |
1065 | | * Submit the next requests if there |
1066 | | * are unsubmitted requests remaining. |
1067 | | */ |
1068 | | if (state->submit_idx < state->num_substates) { |
1069 | | bool ok; |
1070 | | |
1071 | | ok = smbsock_connect_submit_next(req); |
1072 | | if (!ok) { |
1073 | | return; |
1074 | | } |
1075 | | } |
1076 | | |
1077 | | if (state->num_pending == 0) { |
1078 | | /* |
1079 | | * All requests failed |
1080 | | * |
1081 | | * smbsock_connect_cleanup() |
1082 | | * will free all other subreqs |
1083 | | */ |
1084 | | tevent_req_nterror(req, status); |
1085 | | return; |
1086 | | } |
1087 | | } |
1088 | | #endif /* HAVE_LIBQUIC */ |
1089 | | |
1090 | | #ifdef HAVE_LIBNGTCP2 |
1091 | | static NTSTATUS smbsock_connect_ngtcp2_udp_sock( |
1092 | | const struct sockaddr_storage *_addr, |
1093 | | uint16_t port, |
1094 | | int *_sockfd, |
1095 | | struct samba_sockaddr *laddr, |
1096 | | struct samba_sockaddr *raddr) |
1097 | | { |
1098 | | int sockfd = -1; |
1099 | | int ret; |
1100 | | |
1101 | | *laddr = (struct samba_sockaddr) { |
1102 | | .sa_socklen = sizeof(laddr->u), |
1103 | | }; |
1104 | | *raddr = (struct samba_sockaddr) { |
1105 | | .sa_socklen = sizeof(*_addr), |
1106 | | .u = { |
1107 | | .ss = *_addr, |
1108 | | }, |
1109 | | }; |
1110 | | |
1111 | | sockfd = socket(raddr->u.sa.sa_family, SOCK_DGRAM, IPPROTO_UDP); |
1112 | | if (sockfd == -1) { |
1113 | | return map_nt_error_from_unix_common(errno); |
1114 | | } |
1115 | | |
1116 | | switch (raddr->u.sa.sa_family) { |
1117 | | case AF_INET: |
1118 | | raddr->sa_socklen = sizeof(struct sockaddr_in); |
1119 | | break; |
1120 | | case AF_INET6: |
1121 | | raddr->sa_socklen = sizeof(struct sockaddr_in6); |
1122 | | break; |
1123 | | } |
1124 | | |
1125 | | set_sockaddr_port(&raddr->u.sa, port); |
1126 | | |
1127 | | ret = connect(sockfd, &raddr->u.sa, raddr->sa_socklen); |
1128 | | if (ret == -1) { |
1129 | | int saved_errno = errno; |
1130 | | close(sockfd); |
1131 | | return map_nt_error_from_unix_common(saved_errno); |
1132 | | } |
1133 | | |
1134 | | ret = getsockname(sockfd, &laddr->u.sa, &laddr->sa_socklen); |
1135 | | if (ret == -1) { |
1136 | | int saved_errno = errno; |
1137 | | close(sockfd); |
1138 | | return map_nt_error_from_unix_common(saved_errno); |
1139 | | } |
1140 | | |
1141 | | *_sockfd = sockfd; |
1142 | | return NT_STATUS_OK; |
1143 | | } |
1144 | | |
1145 | | static void smbsock_connect_ngtcp2_ready(struct tevent_req *subreq) |
1146 | | { |
1147 | | struct smbsock_connect_substate *s = |
1148 | | (struct smbsock_connect_substate *) |
1149 | | tevent_req_callback_data_void(subreq); |
1150 | | struct tevent_req *req = s->req; |
1151 | | struct smbsock_connect_state *state = |
1152 | | tevent_req_data(req, |
1153 | | struct smbsock_connect_state); |
1154 | | NTSTATUS status; |
1155 | | struct tstream_context *tstream = NULL; |
1156 | | int ret; |
1157 | | int error; |
1158 | | |
1159 | | SMB_ASSERT(s->subreq == subreq); |
1160 | | s->subreq = NULL; |
1161 | | SMB_ASSERT(state->num_pending > 0); |
1162 | | state->num_pending -= 1; |
1163 | | |
1164 | | ret = tstream_tls_ngtcp2_connect_recv(subreq, |
1165 | | &error, |
1166 | | state, |
1167 | | &tstream); |
1168 | | TALLOC_FREE(subreq); |
1169 | | if (ret == 0) { |
1170 | | /* |
1171 | | * smbsock_connect_cleanup() |
1172 | | * will free all other subreqs |
1173 | | */ |
1174 | | state->transport = smbXcli_transport_tstream(state, |
1175 | | &tstream, |
1176 | | &s->laddr, |
1177 | | &s->raddr, |
1178 | | &s->transport); |
1179 | | if (tevent_req_nomem(state->transport, req)) { |
1180 | | return; |
1181 | | } |
1182 | | tevent_req_done(req); |
1183 | | return; |
1184 | | } |
1185 | | status = map_nt_error_from_unix_common(error); |
1186 | | |
1187 | | /* |
1188 | | * Do nothing, wait for the remaining |
1189 | | * requests to come here. |
1190 | | * |
1191 | | * Submit the next requests if there |
1192 | | * are unsubmitted requests remaining. |
1193 | | */ |
1194 | | if (state->submit_idx < state->num_substates) { |
1195 | | bool ok; |
1196 | | |
1197 | | ok = smbsock_connect_submit_next(req); |
1198 | | if (!ok) { |
1199 | | return; |
1200 | | } |
1201 | | } |
1202 | | |
1203 | | if (state->num_pending == 0) { |
1204 | | /* |
1205 | | * All requests failed |
1206 | | * |
1207 | | * smbsock_connect_cleanup() |
1208 | | * will free all other subreqs |
1209 | | */ |
1210 | | tevent_req_nterror(req, status); |
1211 | | return; |
1212 | | } |
1213 | | } |
1214 | | #endif /* HAVE_LIBNGTCP2 */ |
1215 | | |
1216 | | NTSTATUS smbsock_connect_recv(struct tevent_req *req, |
1217 | | TALLOC_CTX *mem_ctx, |
1218 | | struct smbXcli_transport **ptransport) |
1219 | 0 | { |
1220 | 0 | struct smbsock_connect_state *state = tevent_req_data( |
1221 | 0 | req, struct smbsock_connect_state); |
1222 | 0 | NTSTATUS status; |
1223 | |
|
1224 | 0 | if (tevent_req_is_nterror(req, &status)) { |
1225 | 0 | tevent_req_received(req); |
1226 | 0 | return status; |
1227 | 0 | } |
1228 | 0 | *ptransport = talloc_move(mem_ctx, &state->transport); |
1229 | 0 | tevent_req_received(req); |
1230 | 0 | return NT_STATUS_OK; |
1231 | 0 | } |
1232 | | |
1233 | | NTSTATUS smbsock_connect(const struct sockaddr_storage *addr, |
1234 | | struct loadparm_context *lp_ctx, |
1235 | | const struct smb_transports *transports, |
1236 | | const char *called_name, int called_type, |
1237 | | const char *calling_name, int calling_type, |
1238 | | int sec_timeout, |
1239 | | TALLOC_CTX *mem_ctx, |
1240 | | struct smbXcli_transport **ptransport) |
1241 | 0 | { |
1242 | 0 | TALLOC_CTX *frame = talloc_stackframe(); |
1243 | 0 | struct tevent_context *ev; |
1244 | 0 | struct tevent_req *req; |
1245 | 0 | NTSTATUS status = NT_STATUS_NO_MEMORY; |
1246 | |
|
1247 | 0 | ev = samba_tevent_context_init(frame); |
1248 | 0 | if (ev == NULL) { |
1249 | 0 | goto fail; |
1250 | 0 | } |
1251 | 0 | req = smbsock_connect_send(frame, ev, lp_ctx, addr, transports, |
1252 | 0 | called_name, called_type, |
1253 | 0 | calling_name, calling_type); |
1254 | 0 | if (req == NULL) { |
1255 | 0 | goto fail; |
1256 | 0 | } |
1257 | 0 | if ((sec_timeout != 0) && |
1258 | 0 | !tevent_req_set_endtime( |
1259 | 0 | req, ev, timeval_current_ofs(sec_timeout, 0))) { |
1260 | 0 | goto fail; |
1261 | 0 | } |
1262 | 0 | if (!tevent_req_poll_ntstatus(req, ev, &status)) { |
1263 | 0 | goto fail; |
1264 | 0 | } |
1265 | 0 | status = smbsock_connect_recv(req, mem_ctx, ptransport); |
1266 | 0 | fail: |
1267 | 0 | TALLOC_FREE(frame); |
1268 | 0 | return status; |
1269 | 0 | } |
1270 | | |
1271 | | struct smbsock_any_connect_state { |
1272 | | struct tevent_context *ev; |
1273 | | struct loadparm_context *lp_ctx; |
1274 | | const struct sockaddr_storage *addrs; |
1275 | | const char **called_names; |
1276 | | int *called_types; |
1277 | | const char **calling_names; |
1278 | | int *calling_types; |
1279 | | size_t num_addrs; |
1280 | | struct smb_transports transports; |
1281 | | |
1282 | | struct tevent_req **requests; |
1283 | | size_t num_sent; |
1284 | | size_t num_received; |
1285 | | |
1286 | | struct smbXcli_transport *transport; |
1287 | | size_t chosen_index; |
1288 | | }; |
1289 | | |
1290 | | static void smbsock_any_connect_cleanup(struct tevent_req *req, |
1291 | | enum tevent_req_state req_state); |
1292 | | static bool smbsock_any_connect_send_next( |
1293 | | struct tevent_req *req, struct smbsock_any_connect_state *state); |
1294 | | static void smbsock_any_connect_trynext(struct tevent_req *subreq); |
1295 | | static void smbsock_any_connect_connected(struct tevent_req *subreq); |
1296 | | |
1297 | | struct tevent_req *smbsock_any_connect_send(TALLOC_CTX *mem_ctx, |
1298 | | struct tevent_context *ev, |
1299 | | struct loadparm_context *lp_ctx, |
1300 | | const struct sockaddr_storage *addrs, |
1301 | | const char **called_names, |
1302 | | int *called_types, |
1303 | | const char **calling_names, |
1304 | | int *calling_types, |
1305 | | size_t num_addrs, |
1306 | | const struct smb_transports *transports) |
1307 | 0 | { |
1308 | 0 | struct tevent_req *req, *subreq; |
1309 | 0 | struct smbsock_any_connect_state *state; |
1310 | |
|
1311 | 0 | req = tevent_req_create(mem_ctx, &state, |
1312 | 0 | struct smbsock_any_connect_state); |
1313 | 0 | if (req == NULL) { |
1314 | 0 | return NULL; |
1315 | 0 | } |
1316 | 0 | state->ev = ev; |
1317 | 0 | state->lp_ctx = lp_ctx; |
1318 | 0 | state->addrs = addrs; |
1319 | 0 | state->num_addrs = num_addrs; |
1320 | 0 | state->called_names = called_names; |
1321 | 0 | state->called_types = called_types; |
1322 | 0 | state->calling_names = calling_names; |
1323 | 0 | state->calling_types = calling_types; |
1324 | 0 | state->transports = *transports; |
1325 | |
|
1326 | 0 | tevent_req_set_cleanup_fn(req, smbsock_any_connect_cleanup); |
1327 | |
|
1328 | 0 | if (num_addrs == 0) { |
1329 | 0 | tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER); |
1330 | 0 | return tevent_req_post(req, ev); |
1331 | 0 | } |
1332 | | |
1333 | 0 | state->requests = talloc_zero_array(state, struct tevent_req *, |
1334 | 0 | num_addrs); |
1335 | 0 | if (tevent_req_nomem(state->requests, req)) { |
1336 | 0 | return tevent_req_post(req, ev); |
1337 | 0 | } |
1338 | 0 | if (!smbsock_any_connect_send_next(req, state)) { |
1339 | 0 | return tevent_req_post(req, ev); |
1340 | 0 | } |
1341 | 0 | if (state->num_sent >= state->num_addrs) { |
1342 | 0 | return req; |
1343 | 0 | } |
1344 | 0 | subreq = tevent_wakeup_send(state, ev, timeval_current_ofs(0, 10000)); |
1345 | 0 | if (tevent_req_nomem(subreq, req)) { |
1346 | 0 | return tevent_req_post(req, ev); |
1347 | 0 | } |
1348 | 0 | tevent_req_set_callback(subreq, smbsock_any_connect_trynext, req); |
1349 | 0 | return req; |
1350 | 0 | } |
1351 | | |
1352 | | static void smbsock_any_connect_cleanup(struct tevent_req *req, |
1353 | | enum tevent_req_state req_state) |
1354 | 0 | { |
1355 | 0 | struct smbsock_any_connect_state *state = tevent_req_data( |
1356 | 0 | req, struct smbsock_any_connect_state); |
1357 | |
|
1358 | 0 | TALLOC_FREE(state->requests); |
1359 | |
|
1360 | 0 | if (req_state == TEVENT_REQ_DONE) { |
1361 | | /* |
1362 | | * Keep the socket open for the caller. |
1363 | | */ |
1364 | 0 | return; |
1365 | 0 | } |
1366 | | |
1367 | 0 | TALLOC_FREE(state->transport); |
1368 | 0 | } |
1369 | | |
1370 | | static void smbsock_any_connect_trynext(struct tevent_req *subreq) |
1371 | 0 | { |
1372 | 0 | struct tevent_req *req = tevent_req_callback_data( |
1373 | 0 | subreq, struct tevent_req); |
1374 | 0 | struct smbsock_any_connect_state *state = tevent_req_data( |
1375 | 0 | req, struct smbsock_any_connect_state); |
1376 | 0 | bool ret; |
1377 | |
|
1378 | 0 | ret = tevent_wakeup_recv(subreq); |
1379 | 0 | TALLOC_FREE(subreq); |
1380 | 0 | if (!ret) { |
1381 | 0 | tevent_req_nterror(req, NT_STATUS_INTERNAL_ERROR); |
1382 | 0 | return; |
1383 | 0 | } |
1384 | 0 | if (!smbsock_any_connect_send_next(req, state)) { |
1385 | 0 | return; |
1386 | 0 | } |
1387 | 0 | if (state->num_sent >= state->num_addrs) { |
1388 | 0 | return; |
1389 | 0 | } |
1390 | 0 | subreq = tevent_wakeup_send(state, state->ev, |
1391 | 0 | tevent_timeval_set(0, 10000)); |
1392 | 0 | if (tevent_req_nomem(subreq, req)) { |
1393 | 0 | return; |
1394 | 0 | } |
1395 | 0 | tevent_req_set_callback(subreq, smbsock_any_connect_trynext, req); |
1396 | 0 | } |
1397 | | |
1398 | | static bool smbsock_any_connect_send_next( |
1399 | | struct tevent_req *req, struct smbsock_any_connect_state *state) |
1400 | 0 | { |
1401 | 0 | struct tevent_req *subreq; |
1402 | |
|
1403 | 0 | if (state->num_sent >= state->num_addrs) { |
1404 | 0 | tevent_req_nterror(req, NT_STATUS_INTERNAL_ERROR); |
1405 | 0 | return false; |
1406 | 0 | } |
1407 | 0 | subreq = smbsock_connect_send( |
1408 | 0 | state->requests, |
1409 | 0 | state->ev, |
1410 | 0 | state->lp_ctx, |
1411 | 0 | &state->addrs[state->num_sent], |
1412 | 0 | &state->transports, |
1413 | 0 | (state->called_names != NULL) |
1414 | 0 | ? state->called_names[state->num_sent] : NULL, |
1415 | 0 | (state->called_types != NULL) |
1416 | 0 | ? state->called_types[state->num_sent] : -1, |
1417 | 0 | (state->calling_names != NULL) |
1418 | 0 | ? state->calling_names[state->num_sent] : NULL, |
1419 | 0 | (state->calling_types != NULL) |
1420 | 0 | ? state->calling_types[state->num_sent] : -1); |
1421 | 0 | if (tevent_req_nomem(subreq, req)) { |
1422 | 0 | return false; |
1423 | 0 | } |
1424 | 0 | tevent_req_set_callback(subreq, smbsock_any_connect_connected, req); |
1425 | |
|
1426 | 0 | state->requests[state->num_sent] = subreq; |
1427 | 0 | state->num_sent += 1; |
1428 | |
|
1429 | 0 | return true; |
1430 | 0 | } |
1431 | | |
1432 | | static void smbsock_any_connect_connected(struct tevent_req *subreq) |
1433 | 0 | { |
1434 | 0 | struct tevent_req *req = tevent_req_callback_data( |
1435 | 0 | subreq, struct tevent_req); |
1436 | 0 | struct smbsock_any_connect_state *state = tevent_req_data( |
1437 | 0 | req, struct smbsock_any_connect_state); |
1438 | 0 | NTSTATUS status; |
1439 | 0 | size_t i; |
1440 | 0 | size_t chosen_index = 0; |
1441 | |
|
1442 | 0 | for (i=0; i<state->num_sent; i++) { |
1443 | 0 | if (state->requests[i] == subreq) { |
1444 | 0 | chosen_index = i; |
1445 | 0 | break; |
1446 | 0 | } |
1447 | 0 | } |
1448 | 0 | if (i == state->num_sent) { |
1449 | 0 | tevent_req_nterror(req, NT_STATUS_INTERNAL_ERROR); |
1450 | 0 | return; |
1451 | 0 | } |
1452 | | |
1453 | 0 | status = smbsock_connect_recv(subreq, state, &state->transport); |
1454 | |
|
1455 | 0 | TALLOC_FREE(subreq); |
1456 | 0 | state->requests[chosen_index] = NULL; |
1457 | |
|
1458 | 0 | if (NT_STATUS_IS_OK(status)) { |
1459 | 0 | state->chosen_index = chosen_index; |
1460 | 0 | tevent_req_done(req); |
1461 | 0 | return; |
1462 | 0 | } |
1463 | | |
1464 | 0 | state->num_received += 1; |
1465 | 0 | if (state->num_received < state->num_addrs) { |
1466 | | /* |
1467 | | * More addrs pending, wait for the others |
1468 | | */ |
1469 | 0 | return; |
1470 | 0 | } |
1471 | | |
1472 | | /* |
1473 | | * This is the last response, none succeeded. |
1474 | | */ |
1475 | 0 | tevent_req_nterror(req, status); |
1476 | 0 | return; |
1477 | 0 | } |
1478 | | |
1479 | | NTSTATUS smbsock_any_connect_recv(struct tevent_req *req, |
1480 | | TALLOC_CTX *mem_ctx, |
1481 | | struct smbXcli_transport **ptransport, |
1482 | | size_t *chosen_index) |
1483 | 0 | { |
1484 | 0 | struct smbsock_any_connect_state *state = tevent_req_data( |
1485 | 0 | req, struct smbsock_any_connect_state); |
1486 | 0 | NTSTATUS status; |
1487 | |
|
1488 | 0 | if (tevent_req_is_nterror(req, &status)) { |
1489 | 0 | tevent_req_received(req); |
1490 | 0 | return status; |
1491 | 0 | } |
1492 | 0 | *ptransport = talloc_move(mem_ctx, &state->transport); |
1493 | 0 | if (chosen_index != NULL) { |
1494 | 0 | *chosen_index = state->chosen_index; |
1495 | 0 | } |
1496 | 0 | tevent_req_received(req); |
1497 | 0 | return NT_STATUS_OK; |
1498 | 0 | } |
1499 | | |
1500 | | NTSTATUS smbsock_any_connect(const struct sockaddr_storage *addrs, |
1501 | | const char **called_names, |
1502 | | int *called_types, |
1503 | | const char **calling_names, |
1504 | | int *calling_types, |
1505 | | size_t num_addrs, |
1506 | | struct loadparm_context *lp_ctx, |
1507 | | const struct smb_transports *transports, |
1508 | | int sec_timeout, |
1509 | | TALLOC_CTX *mem_ctx, |
1510 | | struct smbXcli_transport **ptransport, |
1511 | | size_t *chosen_index) |
1512 | 0 | { |
1513 | 0 | TALLOC_CTX *frame = talloc_stackframe(); |
1514 | 0 | struct tevent_context *ev; |
1515 | 0 | struct tevent_req *req; |
1516 | 0 | NTSTATUS status = NT_STATUS_NO_MEMORY; |
1517 | |
|
1518 | 0 | ev = samba_tevent_context_init(frame); |
1519 | 0 | if (ev == NULL) { |
1520 | 0 | goto fail; |
1521 | 0 | } |
1522 | 0 | req = smbsock_any_connect_send(frame, ev, lp_ctx, addrs, |
1523 | 0 | called_names, called_types, |
1524 | 0 | calling_names, calling_types, |
1525 | 0 | num_addrs, transports); |
1526 | 0 | if (req == NULL) { |
1527 | 0 | goto fail; |
1528 | 0 | } |
1529 | 0 | if ((sec_timeout != 0) && |
1530 | 0 | !tevent_req_set_endtime( |
1531 | 0 | req, ev, timeval_current_ofs(sec_timeout, 0))) { |
1532 | 0 | goto fail; |
1533 | 0 | } |
1534 | 0 | if (!tevent_req_poll_ntstatus(req, ev, &status)) { |
1535 | 0 | goto fail; |
1536 | 0 | } |
1537 | 0 | status = smbsock_any_connect_recv(req, mem_ctx, ptransport, chosen_index); |
1538 | 0 | fail: |
1539 | 0 | TALLOC_FREE(frame); |
1540 | 0 | return status; |
1541 | 0 | } |