Line data Source code
1 : /*
2 : Unix SMB/CIFS implementation.
3 : SMB client transport context management functions
4 :
5 : Copyright (C) Andrew Tridgell 1994-2005
6 : Copyright (C) James Myers 2003 <myersjj@samba.org>
7 :
8 : This program is free software; you can redistribute it and/or modify
9 : it under the terms of the GNU General Public License as published by
10 : the Free Software Foundation; either version 3 of the License, or
11 : (at your option) any later version.
12 :
13 : This program is distributed in the hope that it will be useful,
14 : but WITHOUT ANY WARRANTY; without even the implied warranty of
15 : MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 : GNU General Public License for more details.
17 :
18 : You should have received a copy of the GNU General Public License
19 : along with this program. If not, see <http://www.gnu.org/licenses/>.
20 : */
21 :
22 : #include "includes.h"
23 : #include "system/network.h"
24 : #include "../lib/async_req/async_sock.h"
25 : #include "../lib/util/tevent_ntstatus.h"
26 : #include "libcli/raw/libcliraw.h"
27 : #include "libcli/raw/raw_proto.h"
28 : #include "lib/socket/socket.h"
29 : #include "lib/events/events.h"
30 : #include "librpc/gen_ndr/ndr_nbt.h"
31 : #include "../libcli/nbt/libnbt.h"
32 : #include "../libcli/smb/smbXcli_base.h"
33 : #include "../libcli/smb/read_smb.h"
34 :
35 : /*
36 : destroy a transport
37 : */
38 2792 : static int transport_destructor(struct smbcli_transport *transport)
39 : {
40 2792 : smbcli_transport_dead(transport, NT_STATUS_LOCAL_DISCONNECT);
41 2792 : return 0;
42 : }
43 :
44 : /*
45 : create a transport structure based on an established socket
46 : */
47 2837 : struct smbcli_transport *smbcli_transport_init(struct smbcli_socket *sock,
48 : TALLOC_CTX *parent_ctx,
49 : bool primary,
50 : struct smbcli_options *options)
51 : {
52 141 : struct smbcli_transport *transport;
53 141 : uint32_t smb1_capabilities;
54 :
55 2837 : transport = talloc_zero(parent_ctx, struct smbcli_transport);
56 2837 : if (!transport) return NULL;
57 :
58 2837 : transport->ev = sock->event.ctx;
59 2837 : transport->options = *options;
60 :
61 2837 : if (transport->options.max_protocol == PROTOCOL_DEFAULT) {
62 2783 : transport->options.max_protocol = PROTOCOL_NT1;
63 : }
64 :
65 2837 : if (transport->options.max_protocol > PROTOCOL_NT1) {
66 0 : transport->options.max_protocol = PROTOCOL_NT1;
67 : }
68 :
69 2837 : TALLOC_FREE(sock->event.fde);
70 2837 : TALLOC_FREE(sock->event.te);
71 :
72 2837 : smb1_capabilities = 0;
73 2837 : smb1_capabilities |= CAP_LARGE_FILES;
74 2837 : smb1_capabilities |= CAP_NT_SMBS | CAP_RPC_REMOTE_APIS;
75 2837 : smb1_capabilities |= CAP_LOCK_AND_READ | CAP_NT_FIND;
76 2837 : smb1_capabilities |= CAP_DFS | CAP_W2K_SMBS;
77 2837 : smb1_capabilities |= CAP_LARGE_READX|CAP_LARGE_WRITEX;
78 2837 : smb1_capabilities |= CAP_LWIO;
79 :
80 2837 : if (options->ntstatus_support) {
81 2829 : smb1_capabilities |= CAP_STATUS32;
82 : }
83 :
84 2837 : if (options->unicode) {
85 2837 : smb1_capabilities |= CAP_UNICODE;
86 : }
87 :
88 2837 : if (options->use_spnego) {
89 2817 : smb1_capabilities |= CAP_EXTENDED_SECURITY;
90 : }
91 :
92 2837 : if (options->use_level2_oplocks) {
93 2831 : smb1_capabilities |= CAP_LEVEL_II_OPLOCKS;
94 : }
95 :
96 5674 : transport->conn = smbXcli_conn_create(transport,
97 2837 : sock->sock->fd,
98 : sock->hostname,
99 : options->signing,
100 : smb1_capabilities,
101 : NULL, /* client_guid */
102 : 0, /* smb2_capabilities */
103 : NULL); /* smb3_ciphers */
104 2837 : if (transport->conn == NULL) {
105 0 : TALLOC_FREE(sock);
106 0 : TALLOC_FREE(transport);
107 0 : return NULL;
108 : }
109 2837 : sock->sock->fd = -1;
110 2837 : TALLOC_FREE(sock);
111 :
112 2837 : talloc_set_destructor(transport, transport_destructor);
113 :
114 2837 : return transport;
115 : }
116 :
117 : /*
118 : create a transport structure based on an established socket
119 : */
120 26 : NTSTATUS smbcli_transport_raw_init(TALLOC_CTX *mem_ctx,
121 : struct tevent_context *ev,
122 : struct smbXcli_conn **_conn,
123 : const struct smbcli_options *options,
124 : struct smbcli_transport **_transport)
125 : {
126 26 : struct smbcli_transport *transport = NULL;
127 0 : NTSTATUS status;
128 :
129 26 : if (*_conn == NULL) {
130 0 : return NT_STATUS_INVALID_PARAMETER;
131 : }
132 :
133 26 : transport = talloc_zero(mem_ctx, struct smbcli_transport);
134 26 : if (transport == NULL) {
135 0 : return NT_STATUS_NO_MEMORY;
136 : }
137 :
138 26 : transport->ev = ev;
139 26 : transport->options = *options;
140 :
141 : /*
142 : * First only set the pointer without move.
143 : */
144 26 : transport->conn = *_conn;
145 26 : status = smb_raw_negotiate_fill_transport(transport);
146 26 : if (!NT_STATUS_IS_OK(status)) {
147 0 : TALLOC_FREE(transport);
148 0 : return status;
149 : }
150 :
151 26 : talloc_set_destructor(transport, transport_destructor);
152 :
153 : /*
154 : * Now move it away from the caller...
155 : */
156 26 : transport->conn = talloc_move(transport, _conn);
157 26 : *_transport = transport;
158 26 : return NT_STATUS_OK;
159 : }
160 :
161 : /*
162 : mark the transport as dead
163 : */
164 2802 : void smbcli_transport_dead(struct smbcli_transport *transport, NTSTATUS status)
165 : {
166 2802 : if (NT_STATUS_EQUAL(NT_STATUS_UNSUCCESSFUL, status)) {
167 0 : status = NT_STATUS_UNEXPECTED_NETWORK_ERROR;
168 : }
169 2802 : if (NT_STATUS_IS_OK(status)) {
170 0 : status = NT_STATUS_LOCAL_DISCONNECT;
171 : }
172 :
173 2802 : smbXcli_conn_disconnect(transport->conn, status);
174 2802 : }
175 :
176 156 : static void idle_handler(struct tevent_context *ev,
177 : struct tevent_timer *te, struct timeval t, void *private_data)
178 : {
179 156 : struct smbcli_transport *transport = talloc_get_type(private_data,
180 : struct smbcli_transport);
181 0 : struct timeval next;
182 :
183 156 : transport->idle.func(transport, transport->idle.private_data);
184 :
185 156 : if (transport->idle.func == NULL) {
186 3 : return;
187 : }
188 :
189 156 : if (!smbXcli_conn_is_connected(transport->conn)) {
190 3 : return;
191 : }
192 :
193 153 : next = timeval_current_ofs_usec(transport->idle.period);
194 :
195 153 : transport->idle.te = tevent_add_timer(transport->ev,
196 : transport,
197 : next,
198 : idle_handler,
199 : transport);
200 : }
201 :
202 : /*
203 : setup the idle handler for a transport
204 : the period is in microseconds
205 : */
206 7 : _PUBLIC_ void smbcli_transport_idle_handler(struct smbcli_transport *transport,
207 : void (*idle_func)(struct smbcli_transport *, void *),
208 : uint64_t period,
209 : void *private_data)
210 : {
211 7 : TALLOC_FREE(transport->idle.te);
212 7 : ZERO_STRUCT(transport->idle);
213 :
214 7 : if (idle_func == NULL) {
215 0 : return;
216 : }
217 :
218 7 : if (!smbXcli_conn_is_connected(transport->conn)) {
219 0 : return;
220 : }
221 :
222 7 : transport->idle.func = idle_func;
223 7 : transport->idle.private_data = private_data;
224 7 : transport->idle.period = period;
225 :
226 7 : transport->idle.te = tevent_add_timer(transport->ev,
227 : transport,
228 : timeval_current_ofs_usec(period),
229 : idle_handler,
230 : transport);
231 : }
232 :
233 : /*
234 : process some read/write requests that are pending
235 : return false if the socket is dead
236 : */
237 458 : _PUBLIC_ bool smbcli_transport_process(struct smbcli_transport *transport)
238 : {
239 458 : struct tevent_req *subreq = NULL;
240 0 : int ret;
241 :
242 458 : if (!smbXcli_conn_is_connected(transport->conn)) {
243 0 : return false;
244 : }
245 :
246 458 : if (!smbXcli_conn_has_async_calls(transport->conn)) {
247 3 : return true;
248 : }
249 :
250 : /*
251 : * do not block for more than 500 micro seconds
252 : */
253 455 : subreq = tevent_wakeup_send(transport,
254 : transport->ev,
255 : timeval_current_ofs_usec(500));
256 455 : if (subreq == NULL) {
257 0 : return false;
258 : }
259 :
260 455 : ret = tevent_loop_once(transport->ev);
261 455 : if (ret != 0) {
262 0 : return false;
263 : }
264 :
265 455 : TALLOC_FREE(subreq);
266 :
267 455 : if (!smbXcli_conn_is_connected(transport->conn)) {
268 0 : return false;
269 : }
270 :
271 455 : return true;
272 : }
273 :
274 : static void smbcli_transport_break_handler(struct tevent_req *subreq);
275 : static void smbcli_request_done(struct tevent_req *subreq);
276 :
277 441628 : struct tevent_req *smbcli_transport_setup_subreq(struct smbcli_request *req)
278 : {
279 441628 : struct smbcli_transport *transport = req->transport;
280 6438 : uint8_t smb_command;
281 6438 : uint8_t additional_flags;
282 6438 : uint8_t clear_flags;
283 6438 : uint16_t additional_flags2;
284 6438 : uint16_t clear_flags2;
285 6438 : uint32_t pid;
286 441628 : struct smbXcli_tcon *tcon = NULL;
287 441628 : struct smbXcli_session *session = NULL;
288 441628 : uint32_t timeout_msec = transport->options.request_timeout * 1000;
289 441628 : struct iovec *bytes_iov = NULL;
290 441628 : struct tevent_req *subreq = NULL;
291 :
292 441628 : smb_command = SVAL(req->out.hdr, HDR_COM);
293 441628 : additional_flags = CVAL(req->out.hdr, HDR_FLG);
294 441628 : additional_flags2 = SVAL(req->out.hdr, HDR_FLG2);
295 441628 : pid = SVAL(req->out.hdr, HDR_PID);
296 441628 : pid |= SVAL(req->out.hdr, HDR_PIDHIGH)<<16;
297 :
298 441628 : clear_flags = ~additional_flags;
299 441628 : clear_flags2 = ~additional_flags2;
300 :
301 441628 : if (req->session) {
302 441621 : session = req->session->smbXcli;
303 : }
304 :
305 441628 : if (req->tree) {
306 432514 : tcon = req->tree->smbXcli;
307 : }
308 :
309 441628 : bytes_iov = talloc(req, struct iovec);
310 441628 : if (bytes_iov == NULL) {
311 0 : return NULL;
312 : }
313 441628 : bytes_iov->iov_base = (void *)req->out.data;
314 441628 : bytes_iov->iov_len = req->out.data_size;
315 :
316 448066 : subreq = smb1cli_req_create(req,
317 : transport->ev,
318 : transport->conn,
319 : smb_command,
320 : additional_flags,
321 : clear_flags,
322 : additional_flags2,
323 : clear_flags2,
324 : timeout_msec,
325 : pid,
326 : tcon,
327 : session,
328 441628 : req->out.wct,
329 441628 : (uint16_t *)req->out.vwv,
330 : 1, bytes_iov);
331 441628 : if (subreq == NULL) {
332 0 : return NULL;
333 : }
334 :
335 441628 : ZERO_STRUCT(req->out);
336 :
337 441628 : return subreq;
338 : }
339 :
340 : /*
341 : put a request into the send queue
342 : */
343 441610 : void smbcli_transport_send(struct smbcli_request *req)
344 : {
345 441610 : struct smbcli_transport *transport = req->transport;
346 6435 : NTSTATUS status;
347 441610 : bool need_pending_break = false;
348 441610 : struct tevent_req *subreq = NULL;
349 6435 : size_t i;
350 441610 : size_t num_subreqs = 0;
351 :
352 441610 : if (transport->oplock.handler) {
353 62227 : need_pending_break = true;
354 : }
355 :
356 441610 : if (transport->break_subreq) {
357 61874 : need_pending_break = false;
358 : }
359 :
360 441600 : if (need_pending_break) {
361 343 : subreq = smb1cli_req_create(transport,
362 : transport->ev,
363 : transport->conn,
364 : 0, /* smb_command */
365 : 0, /* additional_flags */
366 : 0, /* clear_flags */
367 : 0, /* additional_flags2 */
368 : 0, /* clear_flags2 */
369 : 0, /* timeout_msec */
370 : 0, /* pid */
371 : NULL, /* tcon */
372 : NULL, /* session */
373 : 0, /* wct */
374 : NULL, /* vwv */
375 : 0, /* iov_count */
376 : NULL); /* bytes_iov */
377 343 : if (subreq != NULL) {
378 343 : smb1cli_req_set_mid(subreq, 0xFFFF);
379 343 : smbXcli_req_set_pending(subreq);
380 343 : tevent_req_set_callback(subreq,
381 : smbcli_transport_break_handler,
382 : transport);
383 343 : transport->break_subreq = subreq;
384 343 : subreq = NULL;
385 : }
386 : }
387 :
388 441610 : subreq = smbcli_transport_setup_subreq(req);
389 441610 : if (subreq == NULL) {
390 0 : req->state = SMBCLI_REQUEST_ERROR;
391 0 : req->status = NT_STATUS_NO_MEMORY;
392 0 : return;
393 : }
394 :
395 883238 : for (i = 0; i < ARRAY_SIZE(req->subreqs); i++) {
396 883220 : if (req->subreqs[i] == NULL) {
397 883202 : req->subreqs[i] = subreq;
398 883202 : subreq = NULL;
399 : }
400 883220 : if (req->subreqs[i] == NULL) {
401 435160 : break;
402 : }
403 :
404 441628 : if (!tevent_req_is_in_progress(req->subreqs[i])) {
405 0 : req->state = SMBCLI_REQUEST_ERROR;
406 0 : req->status = NT_STATUS_INTERNAL_ERROR;
407 0 : return;
408 : }
409 : }
410 441610 : num_subreqs = i;
411 :
412 441610 : req->state = SMBCLI_REQUEST_RECV;
413 441610 : tevent_req_set_callback(req->subreqs[0], smbcli_request_done, req);
414 :
415 441610 : status = smb1cli_req_chain_submit(req->subreqs, num_subreqs);
416 441610 : if (!NT_STATUS_IS_OK(status)) {
417 4 : req->status = status;
418 4 : req->state = SMBCLI_REQUEST_ERROR;
419 4 : smbXcli_conn_disconnect(transport->conn, status);
420 : }
421 : }
422 :
423 441391 : static void smbcli_request_done(struct tevent_req *subreq)
424 : {
425 6435 : struct smbcli_request *req =
426 441391 : tevent_req_callback_data(subreq,
427 : struct smbcli_request);
428 441391 : struct smbcli_transport *transport = req->transport;
429 6435 : ssize_t len;
430 6435 : size_t i;
431 441391 : uint8_t *hdr = NULL;
432 441391 : uint8_t wct = 0;
433 441391 : uint16_t *vwv = NULL;
434 441391 : uint32_t num_bytes = 0;
435 441391 : uint8_t *bytes = NULL;
436 441391 : struct iovec *recv_iov = NULL;
437 441391 : uint8_t *inbuf = NULL;
438 :
439 441391 : req->status = smb1cli_req_recv(req->subreqs[0], req,
440 : &recv_iov,
441 : &hdr,
442 : &wct,
443 : &vwv,
444 : NULL, /* pvwv_offset */
445 : &num_bytes,
446 : &bytes,
447 : NULL, /* pbytes_offset */
448 : &inbuf,
449 : NULL, 0); /* expected */
450 441391 : TALLOC_FREE(req->subreqs[0]);
451 441391 : if (!NT_STATUS_IS_OK(req->status)) {
452 92071 : if (recv_iov == NULL) {
453 141 : req->state = SMBCLI_REQUEST_ERROR;
454 141 : transport->error.e.nt_status = req->status;
455 141 : transport->error.etype = ETYPE_SOCKET;
456 141 : if (req->async.fn) {
457 2 : req->async.fn(req);
458 : }
459 141 : return;
460 : }
461 : }
462 :
463 : /*
464 : * For SMBreadBraw hdr is NULL
465 : */
466 441250 : len = recv_iov[0].iov_len;
467 1323630 : for (i=1; hdr != NULL && i < 3; i++) {
468 882380 : uint8_t *p = recv_iov[i-1].iov_base;
469 882380 : uint8_t *c1 = recv_iov[i].iov_base;
470 882380 : uint8_t *c2 = p + recv_iov[i-1].iov_len;
471 :
472 882380 : len += recv_iov[i].iov_len;
473 :
474 882380 : c2 += i;
475 882380 : len += i;
476 :
477 882380 : if (recv_iov[i].iov_len == 0) {
478 673298 : continue;
479 : }
480 :
481 209082 : if (c1 != c2) {
482 0 : req->state = SMBCLI_REQUEST_ERROR;
483 0 : req->status = NT_STATUS_INTERNAL_ERROR;
484 0 : transport->error.e.nt_status = req->status;
485 0 : transport->error.etype = ETYPE_SMB;
486 0 : if (req->async.fn) {
487 0 : req->async.fn(req);
488 : }
489 0 : return;
490 : }
491 : }
492 :
493 : /* fill in the 'in' portion of the matching request */
494 441250 : req->in.buffer = inbuf;
495 441250 : req->in.size = NBT_HDR_SIZE + len;
496 441250 : req->in.allocated = req->in.size;
497 :
498 441250 : req->in.hdr = hdr;
499 441250 : req->in.vwv = (uint8_t *)vwv;
500 441250 : req->in.wct = wct;
501 441250 : req->in.data = bytes;
502 441250 : req->in.data_size = num_bytes;
503 441250 : req->in.ptr = req->in.data;
504 441250 : if (hdr != NULL) {
505 441190 : req->flags2 = SVAL(req->in.hdr, HDR_FLG2);
506 : }
507 :
508 441250 : smb_setup_bufinfo(req);
509 :
510 441250 : transport->error.e.nt_status = req->status;
511 441250 : if (NT_STATUS_IS_OK(req->status)) {
512 349320 : transport->error.etype = ETYPE_NONE;
513 : } else {
514 91930 : transport->error.etype = ETYPE_SMB;
515 : }
516 :
517 441250 : req->state = SMBCLI_REQUEST_DONE;
518 441250 : if (req->async.fn) {
519 72816 : req->async.fn(req);
520 : }
521 : }
522 :
523 154 : static void smbcli_transport_break_handler(struct tevent_req *subreq)
524 : {
525 0 : struct smbcli_transport *transport =
526 154 : tevent_req_callback_data(subreq,
527 : struct smbcli_transport);
528 0 : NTSTATUS status;
529 154 : struct iovec *recv_iov = NULL;
530 154 : uint8_t *hdr = NULL;
531 154 : uint16_t *vwv = NULL;
532 154 : const struct smb1cli_req_expected_response expected[] = {
533 : {
534 : .status = NT_STATUS_OK,
535 : .wct = 8,
536 : }
537 : };
538 0 : uint16_t tid;
539 0 : uint16_t fnum;
540 0 : uint8_t level;
541 :
542 154 : transport->break_subreq = NULL;
543 :
544 154 : status = smb1cli_req_recv(subreq, transport,
545 : &recv_iov,
546 : &hdr,
547 : NULL, /* pwct */
548 : &vwv,
549 : NULL, /* pvwv_offset */
550 : NULL, /* pnum_bytes */
551 : NULL, /* pbytes */
552 : NULL, /* pbytes_offset */
553 : NULL, /* pinbuf */
554 : expected,
555 : ARRAY_SIZE(expected));
556 154 : TALLOC_FREE(subreq);
557 154 : if (!NT_STATUS_IS_OK(status)) {
558 0 : TALLOC_FREE(recv_iov);
559 0 : smbcli_transport_dead(transport, status);
560 0 : return;
561 : }
562 :
563 : /*
564 : * Setup the subreq to handle the
565 : * next incoming SMB2 Break.
566 : */
567 154 : subreq = smb1cli_req_create(transport,
568 : transport->ev,
569 : transport->conn,
570 : 0, /* smb_command */
571 : 0, /* additional_flags */
572 : 0, /* clear_flags */
573 : 0, /* additional_flags2 */
574 : 0, /* clear_flags2 */
575 : 0, /* timeout_msec */
576 : 0, /* pid */
577 : NULL, /* tcon */
578 : NULL, /* session */
579 : 0, /* wct */
580 : NULL, /* vwv */
581 : 0, /* iov_count */
582 : NULL); /* bytes_iov */
583 154 : if (subreq != NULL) {
584 154 : smb1cli_req_set_mid(subreq, 0xFFFF);
585 154 : smbXcli_req_set_pending(subreq);
586 154 : tevent_req_set_callback(subreq,
587 : smbcli_transport_break_handler,
588 : transport);
589 154 : transport->break_subreq = subreq;
590 : }
591 :
592 154 : tid = SVAL(hdr, HDR_TID);
593 154 : fnum = SVAL(vwv+2, 0);
594 154 : level = CVAL(vwv+3, 1);
595 :
596 154 : TALLOC_FREE(recv_iov);
597 :
598 154 : if (transport->oplock.handler) {
599 154 : transport->oplock.handler(transport, tid, fnum, level,
600 : transport->oplock.private_data);
601 : } else {
602 0 : DEBUG(5,("Got SMB oplock break with no handler\n"));
603 : }
604 :
605 : }
606 :
607 :
608 : /****************************************************************************
609 : Send an SMBecho (async send)
610 : *****************************************************************************/
611 7 : _PUBLIC_ struct smbcli_request *smb_raw_echo_send(struct smbcli_transport *transport,
612 : struct smb_echo *p)
613 : {
614 0 : struct smbcli_request *req;
615 :
616 7 : req = smbcli_request_setup_transport(transport, SMBecho, 1, p->in.size);
617 7 : if (!req) return NULL;
618 :
619 7 : SSVAL(req->out.vwv, VWV(0), p->in.repeat_count);
620 :
621 7 : memcpy(req->out.data, p->in.data, p->in.size);
622 :
623 7 : ZERO_STRUCT(p->out);
624 :
625 7 : if (!smbcli_request_send(req)) {
626 0 : smbcli_request_destroy(req);
627 0 : return NULL;
628 : }
629 :
630 7 : return req;
631 : }
632 :
633 : /****************************************************************************
634 : raw echo interface (async recv)
635 : ****************************************************************************/
636 0 : NTSTATUS smb_raw_echo_recv(struct smbcli_request *req, TALLOC_CTX *mem_ctx,
637 : struct smb_echo *p)
638 : {
639 0 : if (!smbcli_request_receive(req) ||
640 0 : smbcli_request_is_error(req)) {
641 0 : goto failed;
642 : }
643 :
644 0 : SMBCLI_CHECK_WCT(req, 1);
645 0 : p->out.count++;
646 0 : p->out.sequence_number = SVAL(req->in.vwv, VWV(0));
647 0 : p->out.size = req->in.data_size;
648 0 : talloc_free(p->out.data);
649 0 : p->out.data = talloc_array(mem_ctx, uint8_t, p->out.size);
650 0 : NT_STATUS_HAVE_NO_MEMORY(p->out.data);
651 :
652 0 : if (!smbcli_raw_pull_data(&req->in.bufinfo, req->in.data, p->out.size, p->out.data)) {
653 0 : req->status = NT_STATUS_BUFFER_TOO_SMALL;
654 : }
655 :
656 0 : if (p->out.count == p->in.repeat_count) {
657 0 : return smbcli_request_destroy(req);
658 : }
659 :
660 0 : return NT_STATUS_OK;
661 :
662 0 : failed:
663 0 : return smbcli_request_destroy(req);
664 : }
665 :
666 : /****************************************************************************
667 : Send a echo (sync interface)
668 : *****************************************************************************/
669 7 : NTSTATUS smb_raw_echo(struct smbcli_transport *transport, struct smb_echo *p)
670 : {
671 7 : struct smbcli_request *req = smb_raw_echo_send(transport, p);
672 7 : return smbcli_request_simple_recv(req);
673 : }
|