Coverage Report

Created: 2026-09-03 07:24

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