Coverage Report

Created: 2026-03-07 07:03

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/curl/lib/smb.c
Line
Count
Source
1
/***************************************************************************
2
 *                                  _   _ ____  _
3
 *  Project                     ___| | | |  _ \| |
4
 *                             / __| | | | |_) | |
5
 *                            | (__| |_| |  _ <| |___
6
 *                             \___|\___/|_| \_\_____|
7
 *
8
 * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
9
 * Copyright (C) Bill Nagel <wnagel@tycoint.com>, Exacq Technologies
10
 *
11
 * This software is licensed as described in the file COPYING, which
12
 * you should have received as part of this distribution. The terms
13
 * are also available at https://curl.se/docs/copyright.html.
14
 *
15
 * You may opt to use, copy, modify, merge, publish, distribute and/or sell
16
 * copies of the Software, and permit persons to whom the Software is
17
 * furnished to do so, under the terms of the COPYING file.
18
 *
19
 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
20
 * KIND, either express or implied.
21
 *
22
 * SPDX-License-Identifier: curl
23
 *
24
 ***************************************************************************/
25
#include "curl_setup.h"
26
#include "urldata.h"
27
28
#if !defined(CURL_DISABLE_SMB) && defined(USE_CURL_NTLM_CORE)
29
30
#ifdef HAVE_ARPA_INET_H
31
#include <arpa/inet.h>  /* for htons() */
32
#endif
33
34
#include "smb.h"
35
#include "url.h"
36
#include "sendf.h"
37
#include "curl_trc.h"
38
#include "cfilters.h"
39
#include "connect.h"
40
#include "progress.h"
41
#include "transfer.h"
42
#include "select.h"
43
#include "curl_ntlm_core.h"
44
#include "escape.h"
45
#include "curl_endian.h"
46
#include "curlx/strcopy.h"
47
48
/* meta key for storing protocol meta at easy handle */
49
14.7M
#define CURL_META_SMB_EASY   "meta:proto:smb:easy"
50
/* meta key for storing protocol meta at connection */
51
14.7M
#define CURL_META_SMB_CONN   "meta:proto:smb:conn"
52
53
enum smb_conn_state {
54
  SMB_NOT_CONNECTED = 0,
55
  SMB_CONNECTING,
56
  SMB_NEGOTIATE,
57
  SMB_SETUP,
58
  SMB_CONNECTED
59
};
60
61
/* SMB connection data, kept at connection */
62
struct smb_conn {
63
  enum smb_conn_state state;
64
  char *user;
65
  char *domain;
66
  char *share;
67
  unsigned char challenge[8];
68
  unsigned int session_key;
69
  unsigned short uid;
70
  char *recv_buf;
71
  char *send_buf;
72
  size_t upload_size;
73
  size_t send_size;
74
  size_t sent;
75
  size_t got;
76
};
77
78
/* SMB request state */
79
enum smb_req_state {
80
  SMB_REQUESTING,
81
  SMB_TREE_CONNECT,
82
  SMB_OPEN,
83
  SMB_DOWNLOAD,
84
  SMB_UPLOAD,
85
  SMB_CLOSE,
86
  SMB_TREE_DISCONNECT,
87
  SMB_DONE
88
};
89
90
/* SMB request data, kept at easy handle */
91
struct smb_request {
92
  enum smb_req_state state;
93
  char *path;
94
  unsigned short tid; /* Even if we connect to the same tree as another */
95
  unsigned short fid; /* request, the tid will be different */
96
  CURLcode result;
97
};
98
99
/*
100
 * Definitions for SMB protocol data structures
101
 */
102
#if defined(_MSC_VER) || defined(__ILEC400__)
103
#  define PACK
104
#  pragma pack(push)
105
#  pragma pack(1)
106
#elif defined(__GNUC__)
107
#  define PACK __attribute__((packed))
108
#else
109
#  define PACK
110
#endif
111
112
219
#define SMB_COM_CLOSE                 0x04
113
154
#define SMB_COM_READ_ANDX             0x2e
114
133
#define SMB_COM_WRITE_ANDX            0x2f
115
146
#define SMB_COM_TREE_DISCONNECT       0x71
116
664
#define SMB_COM_NEGOTIATE             0x72
117
391
#define SMB_COM_SETUP_ANDX            0x73
118
329
#define SMB_COM_TREE_CONNECT_ANDX     0x75
119
296
#define SMB_COM_NT_CREATE_ANDX        0xa2
120
1.30k
#define SMB_COM_NO_ANDX_COMMAND       0xff
121
122
219
#define SMB_WC_CLOSE                  0x03
123
154
#define SMB_WC_READ_ANDX              0x0c
124
133
#define SMB_WC_WRITE_ANDX             0x0e
125
391
#define SMB_WC_SETUP_ANDX             0x0d
126
329
#define SMB_WC_TREE_CONNECT_ANDX      0x04
127
296
#define SMB_WC_NT_CREATE_ANDX         0x18
128
129
2.33k
#define SMB_FLAGS_CANONICAL_PATHNAMES 0x10
130
2.33k
#define SMB_FLAGS_CASELESS_PATHNAMES  0x08
131
/* #define SMB_FLAGS2_UNICODE_STRINGS    0x8000 */
132
#define SMB_FLAGS2_IS_LONG_NAME       0x0040
133
#define SMB_FLAGS2_KNOWS_LONG_NAME    0x0001
134
135
#define SMB_CAP_LARGE_FILES           0x08
136
#define SMB_GENERIC_WRITE             0x40000000
137
#define SMB_GENERIC_READ              0x80000000
138
#define SMB_FILE_SHARE_ALL            0x07
139
#define SMB_FILE_OPEN                 0x01
140
#define SMB_FILE_OVERWRITE_IF         0x05
141
142
#define SMB_ERR_NOACCESS              0x00050001
143
144
struct smb_header {
145
  unsigned char nbt_type;
146
  unsigned char nbt_flags;
147
  unsigned short nbt_length;
148
  unsigned char magic[4];
149
  unsigned char command;
150
  unsigned int status;
151
  unsigned char flags;
152
  unsigned short flags2;
153
  unsigned short pid_high;
154
  unsigned char signature[8];
155
  unsigned short pad;
156
  unsigned short tid;
157
  unsigned short pid;
158
  unsigned short uid;
159
  unsigned short mid;
160
} PACK;
161
162
struct smb_negotiate_response {
163
  struct smb_header h;
164
  unsigned char word_count;
165
  unsigned short dialect_index;
166
  unsigned char security_mode;
167
  unsigned short max_mpx_count;
168
  unsigned short max_number_vcs;
169
  unsigned int max_buffer_size;
170
  unsigned int max_raw_size;
171
  unsigned int session_key;
172
  unsigned int capabilities;
173
  unsigned int system_time_low;
174
  unsigned int system_time_high;
175
  unsigned short server_time_zone;
176
  unsigned char encryption_key_length;
177
  unsigned short byte_count;
178
  char bytes[1];
179
} PACK;
180
181
struct andx {
182
  unsigned char command;
183
  unsigned char pad;
184
  unsigned short offset;
185
} PACK;
186
187
struct smb_setup {
188
  unsigned char word_count;
189
  struct andx andx;
190
  unsigned short max_buffer_size;
191
  unsigned short max_mpx_count;
192
  unsigned short vc_number;
193
  unsigned int session_key;
194
  unsigned short lengths[2];
195
  unsigned int pad;
196
  unsigned int capabilities;
197
  unsigned short byte_count;
198
  char bytes[1024];
199
} PACK;
200
201
struct smb_tree_connect {
202
  unsigned char word_count;
203
  struct andx andx;
204
  unsigned short flags;
205
  unsigned short pw_len;
206
  unsigned short byte_count;
207
  char bytes[1024];
208
} PACK;
209
210
struct smb_nt_create {
211
  unsigned char word_count;
212
  struct andx andx;
213
  unsigned char pad;
214
  unsigned short name_length;
215
  unsigned int flags;
216
  unsigned int root_fid;
217
  unsigned int access;
218
  curl_off_t allocation_size;
219
  unsigned int ext_file_attributes;
220
  unsigned int share_access;
221
  unsigned int create_disposition;
222
  unsigned int create_options;
223
  unsigned int impersonation_level;
224
  unsigned char security_flags;
225
  unsigned short byte_count;
226
  char bytes[1024];
227
} PACK;
228
229
struct smb_nt_create_response {
230
  struct smb_header h;
231
  unsigned char word_count;
232
  struct andx andx;
233
  unsigned char op_lock_level;
234
  unsigned short fid;
235
  unsigned int create_disposition;
236
237
  curl_off_t create_time;
238
  curl_off_t last_access_time;
239
  curl_off_t last_write_time;
240
  curl_off_t last_change_time;
241
  unsigned int ext_file_attributes;
242
  curl_off_t allocation_size;
243
  curl_off_t end_of_file;
244
} PACK;
245
246
struct smb_read {
247
  unsigned char word_count;
248
  struct andx andx;
249
  unsigned short fid;
250
  unsigned int offset;
251
  unsigned short max_bytes;
252
  unsigned short min_bytes;
253
  unsigned int timeout;
254
  unsigned short remaining;
255
  unsigned int offset_high;
256
  unsigned short byte_count;
257
} PACK;
258
259
struct smb_write {
260
  struct smb_header h;
261
  unsigned char word_count;
262
  struct andx andx;
263
  unsigned short fid;
264
  unsigned int offset;
265
  unsigned int timeout;
266
  unsigned short write_mode;
267
  unsigned short remaining;
268
  unsigned short pad;
269
  unsigned short data_length;
270
  unsigned short data_offset;
271
  unsigned int offset_high;
272
  unsigned short byte_count;
273
  unsigned char pad2;
274
} PACK;
275
276
struct smb_close {
277
  unsigned char word_count;
278
  unsigned short fid;
279
  unsigned int last_mtime;
280
  unsigned short byte_count;
281
} PACK;
282
283
struct smb_tree_disconnect {
284
  unsigned char word_count;
285
  unsigned short byte_count;
286
} PACK;
287
288
#if defined(_MSC_VER) || defined(__ILEC400__)
289
#  pragma pack(pop)
290
#endif
291
292
14.7M
#define MAX_PAYLOAD_SIZE  0x8000
293
14.7M
#define MAX_MESSAGE_SIZE  (MAX_PAYLOAD_SIZE + 0x1000)
294
783
#define CLIENTNAME        "curl"
295
658
#define SERVICENAME       "?????"
296
297
/* SMB is mostly little endian */
298
#if (defined(__BYTE_ORDER__) && __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__) || \
299
  defined(__OS400__)
300
static unsigned short smb_swap16(unsigned short x)
301
{
302
  return (unsigned short)((x << 8) | ((x >> 8) & 0xff));
303
}
304
305
static unsigned int smb_swap32(unsigned int x)
306
{
307
  return (x << 24) | ((x << 8) & 0xff0000) | ((x >> 8) & 0xff00) |
308
    ((x >> 24) & 0xff);
309
}
310
311
static curl_off_t smb_swap64(curl_off_t x)
312
{
313
  return ((curl_off_t)smb_swap32((unsigned int)x) << 32) |
314
    smb_swap32((unsigned int)(x >> 32));
315
}
316
317
#else
318
17.0k
#  define smb_swap16(x) (x)
319
2.68k
#  define smb_swap32(x) (x)
320
181
#  define smb_swap64(x) (x)
321
#endif
322
323
static void conn_state(struct Curl_easy *data, struct smb_conn *smbc,
324
                       enum smb_conn_state newstate)
325
1.38k
{
326
1.38k
#if defined(DEBUGBUILD) && defined(CURLVERBOSE)
327
  /* For debug purposes */
328
1.38k
  static const char * const names[] = {
329
1.38k
    "SMB_NOT_CONNECTED",
330
1.38k
    "SMB_CONNECTING",
331
1.38k
    "SMB_NEGOTIATE",
332
1.38k
    "SMB_SETUP",
333
1.38k
    "SMB_CONNECTED",
334
    /* LAST */
335
1.38k
  };
336
337
1.38k
  if(smbc->state != newstate)
338
1.38k
    infof(data, "SMB conn %p state change from %s to %s",
339
1.38k
          (void *)smbc, names[smbc->state], names[newstate]);
340
#else
341
  (void)data;
342
#endif
343
1.38k
  smbc->state = newstate;
344
1.38k
}
345
346
static void request_state(struct Curl_easy *data,
347
                          enum smb_req_state newstate)
348
1.35k
{
349
1.35k
  struct smb_request *req = Curl_meta_get(data, CURL_META_SMB_EASY);
350
1.35k
  if(req) {
351
1.35k
#if defined(DEBUGBUILD) && defined(CURLVERBOSE)
352
    /* For debug purposes */
353
1.35k
    static const char * const names[] = {
354
1.35k
      "SMB_REQUESTING",
355
1.35k
      "SMB_TREE_CONNECT",
356
1.35k
      "SMB_OPEN",
357
1.35k
      "SMB_DOWNLOAD",
358
1.35k
      "SMB_UPLOAD",
359
1.35k
      "SMB_CLOSE",
360
1.35k
      "SMB_TREE_DISCONNECT",
361
1.35k
      "SMB_DONE",
362
      /* LAST */
363
1.35k
    };
364
365
1.35k
    if(req->state != newstate)
366
1.31k
      infof(data, "SMB request %p state change from %s to %s",
367
1.35k
            (void *)req, names[req->state], names[newstate]);
368
1.35k
#endif
369
370
1.35k
    req->state = newstate;
371
1.35k
  }
372
1.35k
}
373
374
static void smb_easy_dtor(void *key, size_t klen, void *entry)
375
5.21k
{
376
5.21k
  struct smb_request *req = entry;
377
5.21k
  (void)key;
378
5.21k
  (void)klen;
379
  /* `req->path` points to somewhere in `struct smb_conn` which is
380
   * kept at the connection meta. If the connection is destroyed first,
381
   * req->path points to free'd memory. */
382
5.21k
  curlx_free(req);
383
5.21k
}
384
385
static void smb_conn_dtor(void *key, size_t klen, void *entry)
386
5.21k
{
387
5.21k
  struct smb_conn *smbc = entry;
388
5.21k
  (void)key;
389
5.21k
  (void)klen;
390
5.21k
  Curl_safefree(smbc->share);
391
5.21k
  Curl_safefree(smbc->domain);
392
5.21k
  Curl_safefree(smbc->recv_buf);
393
5.21k
  Curl_safefree(smbc->send_buf);
394
5.21k
  curlx_free(smbc);
395
5.21k
}
396
397
static CURLcode smb_parse_url_path(struct Curl_easy *data,
398
                                   struct smb_conn *smbc,
399
                                   struct smb_request *req)
400
5.21k
{
401
5.21k
  char *path;
402
5.21k
  char *slash;
403
5.21k
  CURLcode result;
404
405
  /* URL decode the path */
406
5.21k
  result = Curl_urldecode(data->state.up.path, 0, &path, NULL, REJECT_CTRL);
407
5.21k
  if(result)
408
1
    return result;
409
410
  /* Parse the path for the share */
411
5.21k
  smbc->share = curlx_strdup((*path == '/' || *path == '\\')
412
5.21k
                             ? path + 1 : path);
413
5.21k
  curlx_free(path);
414
5.21k
  if(!smbc->share)
415
0
    return CURLE_OUT_OF_MEMORY;
416
417
5.21k
  slash = strchr(smbc->share, '/');
418
5.21k
  if(!slash)
419
252
    slash = strchr(smbc->share, '\\');
420
421
  /* The share must be present */
422
5.21k
  if(!slash) {
423
238
    Curl_safefree(smbc->share);
424
238
    failf(data, "missing share in URL path for SMB");
425
238
    return CURLE_URL_MALFORMAT;
426
238
  }
427
428
  /* Parse the path for the file path converting any forward slashes into
429
     backslashes */
430
4.97k
  *slash++ = 0;
431
4.97k
  req->path = slash;
432
433
67.1k
  for(; *slash; slash++) {
434
62.1k
    if(*slash == '/')
435
33.2k
      *slash = '\\';
436
62.1k
  }
437
4.97k
  return CURLE_OK;
438
5.21k
}
439
440
/* this should setup things in the connection, not in the easy
441
   handle */
442
static CURLcode smb_setup_connection(struct Curl_easy *data,
443
                                     struct connectdata *conn)
444
5.21k
{
445
5.21k
  struct smb_conn *smbc;
446
5.21k
  struct smb_request *req;
447
448
  /* Initialize the connection state */
449
5.21k
  smbc = curlx_calloc(1, sizeof(*smbc));
450
5.21k
  if(!smbc ||
451
5.21k
     Curl_conn_meta_set(conn, CURL_META_SMB_CONN, smbc, smb_conn_dtor))
452
0
    return CURLE_OUT_OF_MEMORY;
453
454
  /* Initialize the request state */
455
5.21k
  req = curlx_calloc(1, sizeof(*req));
456
5.21k
  if(!req ||
457
5.21k
     Curl_meta_set(data, CURL_META_SMB_EASY, req, smb_easy_dtor))
458
0
    return CURLE_OUT_OF_MEMORY;
459
460
  /* Parse the URL path */
461
5.21k
  return smb_parse_url_path(data, smbc, req);
462
5.21k
}
463
464
static CURLcode smb_connect(struct Curl_easy *data, bool *done)
465
782
{
466
782
  struct connectdata *conn = data->conn;
467
782
  struct smb_conn *smbc = Curl_conn_meta_get(conn, CURL_META_SMB_CONN);
468
782
  char *slash;
469
470
782
  (void)done;
471
782
  if(!smbc)
472
0
    return CURLE_FAILED_INIT;
473
474
  /* Check we have a username and password to authenticate with */
475
782
  if(!data->state.aptr.user)
476
118
    return CURLE_LOGIN_DENIED;
477
478
  /* Initialize the connection state */
479
664
  smbc->state = SMB_CONNECTING;
480
664
  smbc->recv_buf = curlx_malloc(MAX_MESSAGE_SIZE);
481
664
  if(!smbc->recv_buf)
482
0
    return CURLE_OUT_OF_MEMORY;
483
664
  smbc->send_buf = curlx_malloc(MAX_MESSAGE_SIZE);
484
664
  if(!smbc->send_buf)
485
0
    return CURLE_OUT_OF_MEMORY;
486
487
  /* Parse the username, domain, and password */
488
664
  slash = strchr(conn->user, '/');
489
664
  if(!slash)
490
659
    slash = strchr(conn->user, '\\');
491
492
664
  if(slash) {
493
5
    smbc->user = slash + 1;
494
5
    smbc->domain = curlx_strdup(conn->user);
495
5
    if(!smbc->domain)
496
0
      return CURLE_OUT_OF_MEMORY;
497
5
    smbc->domain[slash - conn->user] = 0;
498
5
  }
499
659
  else {
500
659
    smbc->user = conn->user;
501
659
    smbc->domain = curlx_strdup(conn->host.name);
502
659
    if(!smbc->domain)
503
0
      return CURLE_OUT_OF_MEMORY;
504
659
  }
505
506
664
  return CURLE_OK;
507
664
}
508
509
static CURLcode smb_recv_message(struct Curl_easy *data,
510
                                 struct smb_conn *smbc,
511
                                 void **msg)
512
13.4M
{
513
13.4M
  char *buf = smbc->recv_buf;
514
13.4M
  size_t bytes_read;
515
13.4M
  size_t nbt_size;
516
13.4M
  size_t msg_size = sizeof(struct smb_header);
517
13.4M
  size_t len = MAX_MESSAGE_SIZE - smbc->got;
518
13.4M
  CURLcode result;
519
520
13.4M
  result = Curl_xfer_recv(data, buf + smbc->got, len, &bytes_read);
521
13.4M
  if(result)
522
138k
    return result;
523
524
13.2M
  if(!bytes_read)
525
13.2M
    return CURLE_OK;
526
527
4.36k
  smbc->got += bytes_read;
528
529
  /* Check for a 32-bit nbt header */
530
4.36k
  if(smbc->got < sizeof(unsigned int))
531
11
    return CURLE_OK;
532
533
4.35k
  nbt_size = Curl_read16_be((const unsigned char *)
534
4.35k
                            (buf + sizeof(unsigned short))) +
535
4.35k
    sizeof(unsigned int);
536
4.35k
  if(nbt_size > MAX_MESSAGE_SIZE) {
537
10
    failf(data, "too large NetBIOS frame size %zu", nbt_size);
538
10
    return CURLE_RECV_ERROR;
539
10
  }
540
4.34k
  else if(nbt_size < msg_size) {
541
    /* Each SMB message must be at least this large, e.g. 32 bytes */
542
56
    failf(data, "too small NetBIOS frame size %zu", nbt_size);
543
56
    return CURLE_RECV_ERROR;
544
56
  }
545
546
4.28k
  if(smbc->got < nbt_size)
547
2.43k
    return CURLE_OK;
548
549
1.85k
  if(nbt_size >= msg_size + 1) {
550
    /* Add the word count */
551
1.85k
    msg_size += 1 + (((unsigned char)buf[msg_size]) * sizeof(unsigned short));
552
1.85k
    if(nbt_size >= msg_size + sizeof(unsigned short)) {
553
      /* Add the byte count */
554
1.64k
      msg_size += sizeof(unsigned short) +
555
1.64k
        Curl_read16_le((const unsigned char *)&buf[msg_size]);
556
1.64k
      if(nbt_size < msg_size)
557
7
        return CURLE_RECV_ERROR;
558
1.64k
    }
559
1.85k
  }
560
561
1.84k
  *msg = buf;
562
563
1.84k
  return CURLE_OK;
564
1.85k
}
565
566
static void smb_pop_message(struct smb_conn *smbc)
567
1.78k
{
568
1.78k
  smbc->got = 0;
569
1.78k
}
570
571
static void smb_format_message(struct smb_conn *smbc,
572
                               struct smb_request *req,
573
                               struct smb_header *h,
574
                               unsigned char cmd, size_t len)
575
2.33k
{
576
2.33k
  const unsigned int pid = 0xbad71d; /* made up */
577
578
2.33k
  memset(h, 0, sizeof(*h));
579
2.33k
  h->nbt_length = htons((unsigned short)(sizeof(*h) - sizeof(unsigned int) +
580
2.33k
                                         len));
581
2.33k
  memcpy((char *)h->magic, "\xffSMB", 4);
582
2.33k
  h->command = cmd;
583
2.33k
  h->flags = SMB_FLAGS_CANONICAL_PATHNAMES | SMB_FLAGS_CASELESS_PATHNAMES;
584
2.33k
  h->flags2 = smb_swap16(SMB_FLAGS2_IS_LONG_NAME | SMB_FLAGS2_KNOWS_LONG_NAME);
585
2.33k
  h->uid = smb_swap16(smbc->uid);
586
2.33k
  h->tid = smb_swap16(req->tid);
587
2.33k
  h->pid_high = smb_swap16((unsigned short)(pid >> 16));
588
2.33k
  h->pid = smb_swap16((unsigned short)pid);
589
2.33k
}
590
591
static CURLcode smb_send(struct Curl_easy *data, struct smb_conn *smbc,
592
                         size_t len, size_t upload_size)
593
2.33k
{
594
2.33k
  size_t bytes_written;
595
2.33k
  CURLcode result;
596
597
2.33k
  result = Curl_xfer_send(data, smbc->send_buf, len, FALSE, &bytes_written);
598
2.33k
  if(result)
599
0
    return result;
600
601
2.33k
  if(bytes_written != len) {
602
0
    smbc->send_size = len;
603
0
    smbc->sent = bytes_written;
604
0
  }
605
606
2.33k
  smbc->upload_size = upload_size;
607
608
2.33k
  return CURLE_OK;
609
2.33k
}
610
611
static CURLcode smb_flush(struct Curl_easy *data, struct smb_conn *smbc)
612
127
{
613
127
  size_t bytes_written;
614
127
  size_t len = smbc->send_size - smbc->sent;
615
127
  CURLcode result;
616
617
127
  if(!smbc->send_size)
618
0
    return CURLE_OK;
619
620
127
  result = Curl_xfer_send(data, smbc->send_buf + smbc->sent, len, FALSE,
621
127
                          &bytes_written);
622
127
  if(result)
623
0
    return result;
624
625
127
  if(bytes_written != len)
626
0
    smbc->sent += bytes_written;
627
127
  else
628
127
    smbc->send_size = 0;
629
630
127
  return CURLE_OK;
631
127
}
632
633
static CURLcode smb_send_message(struct Curl_easy *data,
634
                                 struct smb_conn *smbc,
635
                                 struct smb_request *req,
636
                                 unsigned char cmd,
637
                                 const void *msg, size_t msg_len)
638
2.19k
{
639
2.19k
  if((MAX_MESSAGE_SIZE - sizeof(struct smb_header)) < msg_len) {
640
0
    DEBUGASSERT(0);
641
0
    return CURLE_SEND_ERROR;
642
0
  }
643
2.19k
  smb_format_message(smbc, req, (struct smb_header *)smbc->send_buf,
644
2.19k
                     cmd, msg_len);
645
2.19k
  memcpy(smbc->send_buf + sizeof(struct smb_header), msg, msg_len);
646
647
2.19k
  return smb_send(data, smbc, sizeof(struct smb_header) + msg_len, 0);
648
2.19k
}
649
650
static CURLcode smb_send_negotiate(struct Curl_easy *data,
651
                                   struct smb_conn *smbc,
652
                                   struct smb_request *req)
653
664
{
654
664
  const char *msg = "\x00\x0c\x00\x02NT LM 0.12";
655
656
664
  return smb_send_message(data, smbc, req, SMB_COM_NEGOTIATE, msg, 15);
657
664
}
658
659
static CURLcode smb_send_setup(struct Curl_easy *data)
660
392
{
661
392
  struct connectdata *conn = data->conn;
662
392
  struct smb_conn *smbc = Curl_conn_meta_get(conn, CURL_META_SMB_CONN);
663
392
  struct smb_request *req = Curl_meta_get(data, CURL_META_SMB_EASY);
664
392
  struct smb_setup msg;
665
392
  char *p = msg.bytes;
666
392
  unsigned char lm_hash[21];
667
392
  unsigned char lm[24];
668
392
  unsigned char nt_hash[21];
669
392
  unsigned char nt[24];
670
392
  size_t byte_count;
671
672
392
  if(!smbc || !req)
673
0
    return CURLE_FAILED_INIT;
674
675
392
  byte_count = sizeof(lm) + sizeof(nt) +
676
392
    strlen(smbc->user) + strlen(smbc->domain) +
677
392
    strlen(CURL_OS) + strlen(CLIENTNAME) + 4; /* 4 null chars */
678
392
  if(byte_count > sizeof(msg.bytes))
679
1
    return CURLE_FILESIZE_EXCEEDED;
680
681
391
  Curl_ntlm_core_mk_lm_hash(conn->passwd, lm_hash);
682
391
  Curl_ntlm_core_lm_resp(lm_hash, smbc->challenge, lm);
683
391
  Curl_ntlm_core_mk_nt_hash(conn->passwd, nt_hash);
684
391
  Curl_ntlm_core_lm_resp(nt_hash, smbc->challenge, nt);
685
686
391
  memset(&msg, 0, sizeof(msg) - sizeof(msg.bytes));
687
391
  msg.word_count = SMB_WC_SETUP_ANDX;
688
391
  msg.andx.command = SMB_COM_NO_ANDX_COMMAND;
689
391
  msg.max_buffer_size = smb_swap16(MAX_MESSAGE_SIZE);
690
391
  msg.max_mpx_count = smb_swap16(1);
691
391
  msg.vc_number = smb_swap16(1);
692
391
  msg.session_key = smb_swap32(smbc->session_key);
693
391
  msg.capabilities = smb_swap32(SMB_CAP_LARGE_FILES);
694
391
  msg.lengths[0] = smb_swap16(sizeof(lm));
695
391
  msg.lengths[1] = smb_swap16(sizeof(nt));
696
391
  memcpy(p, lm, sizeof(lm));
697
391
  p += sizeof(lm);
698
391
  memcpy(p, nt, sizeof(nt));
699
391
  p += sizeof(nt);
700
391
  p += curl_msnprintf(p, byte_count - sizeof(nt) - sizeof(lm),
701
391
                      "%s%c"  /* user */
702
391
                      "%s%c"  /* domain */
703
391
                      "%s%c"  /* OS */
704
391
                      "%s", /* client name */
705
391
                      smbc->user, 0, smbc->domain, 0, CURL_OS, 0, CLIENTNAME);
706
391
  p++; /* count the final null-termination */
707
391
  DEBUGASSERT(byte_count == (size_t)(p - msg.bytes));
708
391
  msg.byte_count = smb_swap16((unsigned short)byte_count);
709
710
391
  return smb_send_message(data, smbc, req, SMB_COM_SETUP_ANDX, &msg,
711
391
                          sizeof(msg) - sizeof(msg.bytes) + byte_count);
712
391
}
713
714
static CURLcode smb_send_tree_connect(struct Curl_easy *data,
715
                                      struct smb_conn *smbc,
716
                                      struct smb_request *req)
717
329
{
718
329
  struct smb_tree_connect msg;
719
329
  struct connectdata *conn = data->conn;
720
329
  char *p = msg.bytes;
721
329
  const size_t byte_count = strlen(conn->host.name) + strlen(smbc->share) +
722
329
    strlen(SERVICENAME) + 5; /* 2 nulls and 3 backslashes */
723
724
329
  if(byte_count > sizeof(msg.bytes))
725
0
    return CURLE_FILESIZE_EXCEEDED;
726
727
329
  memset(&msg, 0, sizeof(msg) - sizeof(msg.bytes));
728
329
  msg.word_count = SMB_WC_TREE_CONNECT_ANDX;
729
329
  msg.andx.command = SMB_COM_NO_ANDX_COMMAND;
730
329
  msg.pw_len = 0;
731
732
329
  p += curl_msnprintf(p, byte_count,
733
329
                      "\\\\%s\\"  /* hostname */
734
329
                      "%s%c"      /* share */
735
329
                      "%s",       /* service */
736
329
                      conn->host.name, smbc->share, 0, SERVICENAME);
737
329
  p++; /* count the final null-termination */
738
329
  DEBUGASSERT(byte_count == (size_t)(p - msg.bytes));
739
329
  msg.byte_count = smb_swap16((unsigned short)byte_count);
740
741
329
  return smb_send_message(data, smbc, req, SMB_COM_TREE_CONNECT_ANDX, &msg,
742
329
                          sizeof(msg) - sizeof(msg.bytes) + byte_count);
743
329
}
744
745
static CURLcode smb_send_open(struct Curl_easy *data,
746
                              struct smb_conn *smbc,
747
                              struct smb_request *req)
748
296
{
749
296
  struct smb_nt_create msg;
750
296
  const size_t byte_count = strlen(req->path) + 1;
751
752
296
  if(byte_count > sizeof(msg.bytes))
753
0
    return CURLE_FILESIZE_EXCEEDED;
754
755
296
  memset(&msg, 0, sizeof(msg) - sizeof(msg.bytes));
756
296
  msg.word_count = SMB_WC_NT_CREATE_ANDX;
757
296
  msg.andx.command = SMB_COM_NO_ANDX_COMMAND;
758
296
  msg.name_length = smb_swap16((unsigned short)(byte_count - 1));
759
296
  msg.share_access = smb_swap32(SMB_FILE_SHARE_ALL);
760
296
  if(data->state.upload) {
761
102
    msg.access = smb_swap32(SMB_GENERIC_READ | SMB_GENERIC_WRITE);
762
102
    msg.create_disposition = smb_swap32(SMB_FILE_OVERWRITE_IF);
763
102
  }
764
194
  else {
765
194
    msg.access = smb_swap32(SMB_GENERIC_READ);
766
194
    msg.create_disposition = smb_swap32(SMB_FILE_OPEN);
767
194
  }
768
296
  msg.byte_count = smb_swap16((unsigned short)byte_count);
769
296
  curlx_strcopy(msg.bytes, sizeof(msg.bytes), req->path, byte_count - 1);
770
771
296
  return smb_send_message(data, smbc, req, SMB_COM_NT_CREATE_ANDX, &msg,
772
296
                          sizeof(msg) - sizeof(msg.bytes) + byte_count);
773
296
}
774
775
static CURLcode smb_send_close(struct Curl_easy *data,
776
                               struct smb_conn *smbc,
777
                               struct smb_request *req)
778
219
{
779
219
  struct smb_close msg;
780
781
219
  memset(&msg, 0, sizeof(msg));
782
219
  msg.word_count = SMB_WC_CLOSE;
783
219
  msg.fid = smb_swap16(req->fid);
784
785
219
  return smb_send_message(data, smbc, req, SMB_COM_CLOSE, &msg, sizeof(msg));
786
219
}
787
788
static CURLcode smb_send_tree_disconnect(struct Curl_easy *data,
789
                                         struct smb_conn *smbc,
790
                                         struct smb_request *req)
791
146
{
792
146
  struct smb_tree_disconnect msg;
793
146
  memset(&msg, 0, sizeof(msg));
794
146
  return smb_send_message(data, smbc, req, SMB_COM_TREE_DISCONNECT,
795
146
                          &msg, sizeof(msg));
796
146
}
797
798
static CURLcode smb_send_read(struct Curl_easy *data,
799
                              struct smb_conn *smbc,
800
                              struct smb_request *req)
801
154
{
802
154
  curl_off_t offset = data->req.offset;
803
154
  struct smb_read msg;
804
805
154
  memset(&msg, 0, sizeof(msg));
806
154
  msg.word_count = SMB_WC_READ_ANDX;
807
154
  msg.andx.command = SMB_COM_NO_ANDX_COMMAND;
808
154
  msg.fid = smb_swap16(req->fid);
809
154
  msg.offset = smb_swap32((unsigned int)offset);
810
154
  msg.offset_high = smb_swap32((unsigned int)(offset >> 32));
811
154
  msg.min_bytes = smb_swap16(MAX_PAYLOAD_SIZE);
812
154
  msg.max_bytes = smb_swap16(MAX_PAYLOAD_SIZE);
813
814
154
  return smb_send_message(data, smbc, req, SMB_COM_READ_ANDX,
815
154
                          &msg, sizeof(msg));
816
154
}
817
818
static CURLcode smb_send_write(struct Curl_easy *data,
819
                               struct smb_conn *smbc,
820
                               struct smb_request *req)
821
133
{
822
133
  struct smb_write *msg;
823
133
  curl_off_t offset = data->req.offset;
824
133
  curl_off_t upload_size = data->req.size - data->req.bytecount;
825
826
133
  msg = (struct smb_write *)smbc->send_buf;
827
133
  if(upload_size >= MAX_PAYLOAD_SIZE - 1) /* There is one byte of padding */
828
0
    upload_size = MAX_PAYLOAD_SIZE - 1;
829
830
133
  memset(msg, 0, sizeof(*msg));
831
133
  msg->word_count = SMB_WC_WRITE_ANDX;
832
133
  msg->andx.command = SMB_COM_NO_ANDX_COMMAND;
833
133
  msg->fid = smb_swap16(req->fid);
834
133
  msg->offset = smb_swap32((unsigned int)offset);
835
133
  msg->offset_high = smb_swap32((unsigned int)(offset >> 32));
836
133
  msg->data_length = smb_swap16((unsigned short)upload_size);
837
133
  msg->data_offset = smb_swap16(sizeof(*msg) - sizeof(unsigned int));
838
133
  msg->byte_count = smb_swap16((unsigned short)(upload_size + 1));
839
840
133
  smb_format_message(smbc, req, &msg->h, SMB_COM_WRITE_ANDX,
841
133
                     sizeof(*msg) - sizeof(msg->h) + (size_t)upload_size);
842
843
133
  return smb_send(data, smbc, sizeof(*msg), (size_t)upload_size);
844
133
}
845
846
static CURLcode smb_send_and_recv(struct Curl_easy *data,
847
                                  struct smb_conn *smbc, void **msg)
848
14.7M
{
849
14.7M
  CURLcode result;
850
14.7M
  *msg = NULL; /* if it returns early */
851
852
  /* Check if there is data in the transfer buffer */
853
14.7M
  if(!smbc->send_size && smbc->upload_size) {
854
1.37M
    size_t nread = smbc->upload_size > (size_t)MAX_MESSAGE_SIZE ?
855
1.37M
      (size_t)MAX_MESSAGE_SIZE : smbc->upload_size;
856
1.37M
    bool eos;
857
858
1.37M
    result = Curl_client_read(data, smbc->send_buf, nread, &nread, &eos);
859
1.37M
    if(result && result != CURLE_AGAIN)
860
0
      return result;
861
1.37M
    if(!nread)
862
1.37M
      return CURLE_OK;
863
864
127
    smbc->upload_size -= nread;
865
127
    smbc->send_size = nread;
866
127
    smbc->sent = 0;
867
127
  }
868
869
  /* Check if there is data to send */
870
13.4M
  if(smbc->send_size) {
871
127
    result = smb_flush(data, smbc);
872
127
    if(result)
873
0
      return result;
874
127
  }
875
876
  /* Check if there is still data to be sent */
877
13.4M
  if(smbc->send_size || smbc->upload_size)
878
27
    return CURLE_AGAIN;
879
880
13.4M
  return smb_recv_message(data, smbc, msg);
881
13.4M
}
882
883
static CURLcode smb_connection_state(struct Curl_easy *data, bool *done)
884
8.78M
{
885
8.78M
  struct connectdata *conn = data->conn;
886
8.78M
  struct smb_conn *smbc = Curl_conn_meta_get(conn, CURL_META_SMB_CONN);
887
8.78M
  struct smb_request *req = Curl_meta_get(data, CURL_META_SMB_EASY);
888
8.78M
  struct smb_negotiate_response *nrsp;
889
8.78M
  struct smb_header *h;
890
8.78M
  CURLcode result;
891
8.78M
  void *msg = NULL;
892
893
8.78M
  if(!smbc || !req)
894
0
    return CURLE_FAILED_INIT;
895
896
8.78M
  if(smbc->state == SMB_CONNECTING) {
897
664
#ifdef USE_SSL
898
664
    if(Curl_conn_is_ssl(conn, FIRSTSOCKET)) {
899
0
      bool ssl_done = FALSE;
900
0
      result = Curl_conn_connect(data, FIRSTSOCKET, FALSE, &ssl_done);
901
0
      if(result && result != CURLE_AGAIN)
902
0
        return result;
903
0
      if(!ssl_done)
904
0
        return CURLE_OK;
905
0
    }
906
664
#endif
907
908
664
    result = smb_send_negotiate(data, smbc, req);
909
664
    if(result) {
910
0
      connclose(conn, "SMB: failed to send negotiate message");
911
0
      return result;
912
0
    }
913
914
664
    conn_state(data, smbc, SMB_NEGOTIATE);
915
664
  }
916
917
  /* Send the previous message and check for a response */
918
8.78M
  result = smb_send_and_recv(data, smbc, &msg);
919
8.78M
  if(result && result != CURLE_AGAIN) {
920
26
    connclose(conn, "SMB: failed to communicate");
921
26
    return result;
922
26
  }
923
924
8.78M
  if(!msg)
925
8.78M
    return CURLE_OK;
926
927
777
  h = msg;
928
929
777
  switch(smbc->state) {
930
432
  case SMB_NEGOTIATE:
931
432
    if((smbc->got < sizeof(*nrsp) + sizeof(smbc->challenge) - 1) ||
932
429
       h->status) {
933
40
      connclose(conn, "SMB: negotiation failed");
934
40
      return CURLE_COULDNT_CONNECT;
935
40
    }
936
392
    nrsp = msg;
937
#if defined(__GNUC__) && __GNUC__ >= 13
938
#pragma GCC diagnostic push
939
/* error: 'memcpy' offset [74, 80] from the object at '<unknown>' is out of
940
   the bounds of referenced subobject 'bytes' with type 'char[1]' */
941
#pragma GCC diagnostic ignored "-Warray-bounds"
942
#endif
943
392
    memcpy(smbc->challenge, nrsp->bytes, sizeof(smbc->challenge));
944
#if defined(__GNUC__) && __GNUC__ >= 13
945
#pragma GCC diagnostic pop
946
#endif
947
392
    smbc->session_key = smb_swap32(nrsp->session_key);
948
392
    result = smb_send_setup(data);
949
392
    if(result) {
950
1
      connclose(conn, "SMB: failed to send setup message");
951
1
      return result;
952
1
    }
953
391
    conn_state(data, smbc, SMB_SETUP);
954
391
    break;
955
956
345
  case SMB_SETUP:
957
345
    if(h->status) {
958
16
      connclose(conn, "SMB: authentication failed");
959
16
      return CURLE_LOGIN_DENIED;
960
16
    }
961
329
    smbc->uid = smb_swap16(h->uid);
962
329
    conn_state(data, smbc, SMB_CONNECTED);
963
329
    *done = TRUE;
964
329
    break;
965
966
0
  default:
967
0
    smb_pop_message(smbc);
968
0
    return CURLE_OK; /* ignore */
969
777
  }
970
971
720
  smb_pop_message(smbc);
972
973
720
  return CURLE_OK;
974
777
}
975
976
/*
977
 * Convert a timestamp from the Windows world (100 nsec units from 1 Jan 1601)
978
 * to POSIX time. Cap the output to fit within a time_t.
979
 */
980
static void get_posix_time(time_t *out, curl_off_t timestamp)
981
6
{
982
6
  if(timestamp >= (curl_off_t)116444736000000000) {
983
3
    timestamp -= (curl_off_t)116444736000000000;
984
3
    timestamp /= 10000000;
985
#if SIZEOF_TIME_T < SIZEOF_CURL_OFF_T
986
    if(timestamp > TIME_T_MAX)
987
      *out = TIME_T_MAX;
988
    else if(timestamp < TIME_T_MIN)
989
      *out = TIME_T_MIN;
990
    else
991
#endif
992
3
      *out = (time_t)timestamp;
993
3
  }
994
3
  else
995
3
    *out = 0;
996
6
}
997
998
static CURLcode smb_request_state(struct Curl_easy *data, bool *done)
999
6.00M
{
1000
6.00M
  struct connectdata *conn = data->conn;
1001
6.00M
  struct smb_conn *smbc = Curl_conn_meta_get(conn, CURL_META_SMB_CONN);
1002
6.00M
  struct smb_request *req = Curl_meta_get(data, CURL_META_SMB_EASY);
1003
6.00M
  struct smb_header *h;
1004
6.00M
  enum smb_req_state next_state = SMB_DONE;
1005
6.00M
  unsigned short len;
1006
6.00M
  unsigned short off;
1007
6.00M
  CURLcode result;
1008
6.00M
  void *msg = NULL;
1009
6.00M
  const struct smb_nt_create_response *smb_m;
1010
1011
6.00M
  if(!smbc || !req)
1012
0
    return CURLE_FAILED_INIT;
1013
1014
6.00M
  if(data->state.upload && (data->state.infilesize < 0)) {
1015
0
    failf(data, "SMB upload needs to know the size up front");
1016
0
    return CURLE_SEND_ERROR;
1017
0
  }
1018
1019
  /* Start the request */
1020
6.00M
  if(req->state == SMB_REQUESTING) {
1021
329
    result = smb_send_tree_connect(data, smbc, req);
1022
329
    if(result) {
1023
0
      connclose(conn, "SMB: failed to send tree connect message");
1024
0
      return result;
1025
0
    }
1026
1027
329
    request_state(data, SMB_TREE_CONNECT);
1028
329
  }
1029
1030
  /* Send the previous message and check for a response */
1031
6.00M
  result = smb_send_and_recv(data, smbc, &msg);
1032
6.00M
  if(result && result != CURLE_AGAIN) {
1033
47
    connclose(conn, "SMB: failed to communicate");
1034
47
    return result;
1035
47
  }
1036
1037
6.00M
  if(!msg)
1038
6.00M
    return CURLE_OK;
1039
1040
1.06k
  h = msg;
1041
1042
1.06k
  switch(req->state) {
1043
322
  case SMB_TREE_CONNECT:
1044
322
    if(h->status) {
1045
26
      req->result = CURLE_REMOTE_FILE_NOT_FOUND;
1046
26
      if(h->status == smb_swap32(SMB_ERR_NOACCESS))
1047
1
        req->result = CURLE_REMOTE_ACCESS_DENIED;
1048
26
      break;
1049
26
    }
1050
296
    req->tid = smb_swap16(h->tid);
1051
296
    next_state = SMB_OPEN;
1052
296
    break;
1053
1054
295
  case SMB_OPEN:
1055
295
    if(h->status || smbc->got < sizeof(struct smb_nt_create_response)) {
1056
21
      req->result = CURLE_REMOTE_FILE_NOT_FOUND;
1057
21
      if(h->status == smb_swap32(SMB_ERR_NOACCESS))
1058
0
        req->result = CURLE_REMOTE_ACCESS_DENIED;
1059
21
      next_state = SMB_TREE_DISCONNECT;
1060
21
      break;
1061
21
    }
1062
274
    smb_m = (const struct smb_nt_create_response *)msg;
1063
274
    req->fid = smb_swap16(smb_m->fid);
1064
274
    data->req.offset = 0;
1065
274
    if(data->state.upload) {
1066
93
      data->req.size = data->state.infilesize;
1067
93
      Curl_pgrsSetUploadSize(data, data->req.size);
1068
93
      next_state = SMB_UPLOAD;
1069
93
    }
1070
181
    else {
1071
181
      data->req.size = smb_swap64(smb_m->end_of_file);
1072
181
      if(data->req.size < 0) {
1073
27
        req->result = CURLE_WEIRD_SERVER_REPLY;
1074
27
        next_state = SMB_CLOSE;
1075
27
      }
1076
154
      else {
1077
154
        Curl_pgrsSetDownloadSize(data, data->req.size);
1078
154
        if(data->set.get_filetime)
1079
6
          get_posix_time(&data->info.filetime, smb_m->last_change_time);
1080
154
        next_state = SMB_DOWNLOAD;
1081
154
      }
1082
181
    }
1083
274
    break;
1084
1085
136
  case SMB_DOWNLOAD:
1086
136
    if(h->status || smbc->got < sizeof(struct smb_header) + 15) {
1087
14
      req->result = CURLE_RECV_ERROR;
1088
14
      next_state = SMB_CLOSE;
1089
14
      break;
1090
14
    }
1091
122
    len = Curl_read16_le((const unsigned char *)msg +
1092
122
                         sizeof(struct smb_header) + 11);
1093
122
    off = Curl_read16_le((const unsigned char *)msg +
1094
122
                         sizeof(struct smb_header) + 13);
1095
122
    if(len > 0) {
1096
106
      if(off + sizeof(unsigned int) + len > smbc->got) {
1097
11
        failf(data, "Invalid input packet");
1098
11
        result = CURLE_RECV_ERROR;
1099
11
      }
1100
95
      else
1101
95
        result = Curl_client_write(data, CLIENTWRITE_BODY,
1102
95
                                   (char *)msg + off + sizeof(unsigned int),
1103
95
                                   len);
1104
106
      if(result) {
1105
12
        req->result = result;
1106
12
        next_state = SMB_CLOSE;
1107
12
        break;
1108
12
      }
1109
106
    }
1110
110
    data->req.offset += len;
1111
110
    next_state = (len < MAX_PAYLOAD_SIZE) ? SMB_CLOSE : SMB_DOWNLOAD;
1112
110
    break;
1113
1114
96
  case SMB_UPLOAD:
1115
96
    if(h->status || smbc->got < sizeof(struct smb_header) + 7) {
1116
6
      req->result = CURLE_UPLOAD_FAILED;
1117
6
      next_state = SMB_CLOSE;
1118
6
      break;
1119
6
    }
1120
90
    len = Curl_read16_le((const unsigned char *)msg +
1121
90
                         sizeof(struct smb_header) + 5);
1122
90
    data->req.bytecount += len;
1123
90
    data->req.offset += len;
1124
90
    Curl_pgrs_upload_inc(data, len);
1125
90
    if(data->req.bytecount >= data->req.size)
1126
50
      next_state = SMB_CLOSE;
1127
40
    else
1128
40
      next_state = SMB_UPLOAD;
1129
90
    break;
1130
1131
125
  case SMB_CLOSE:
1132
    /* We do not care if the close failed, proceed to tree disconnect anyway */
1133
125
    next_state = SMB_TREE_DISCONNECT;
1134
125
    break;
1135
1136
94
  case SMB_TREE_DISCONNECT:
1137
94
    next_state = SMB_DONE;
1138
94
    break;
1139
1140
0
  default:
1141
0
    smb_pop_message(smbc);
1142
0
    return CURLE_OK; /* ignore */
1143
1.06k
  }
1144
1145
1.06k
  smb_pop_message(smbc);
1146
1147
1.06k
  switch(next_state) {
1148
296
  case SMB_OPEN:
1149
296
    result = smb_send_open(data, smbc, req);
1150
296
    break;
1151
1152
154
  case SMB_DOWNLOAD:
1153
154
    result = smb_send_read(data, smbc, req);
1154
154
    break;
1155
1156
133
  case SMB_UPLOAD:
1157
133
    result = smb_send_write(data, smbc, req);
1158
133
    break;
1159
1160
219
  case SMB_CLOSE:
1161
219
    result = smb_send_close(data, smbc, req);
1162
219
    break;
1163
1164
146
  case SMB_TREE_DISCONNECT:
1165
146
    result = smb_send_tree_disconnect(data, smbc, req);
1166
146
    break;
1167
1168
120
  case SMB_DONE:
1169
120
    result = req->result;
1170
120
    *done = TRUE;
1171
120
    break;
1172
1173
0
  default:
1174
0
    break;
1175
1.06k
  }
1176
1177
1.06k
  if(result) {
1178
39
    connclose(conn, "SMB: failed to send message");
1179
39
    return result;
1180
39
  }
1181
1182
1.02k
  request_state(data, next_state);
1183
1184
1.02k
  return CURLE_OK;
1185
1.06k
}
1186
1187
static CURLcode smb_pollset(struct Curl_easy *data,
1188
                            struct easy_pollset *ps)
1189
14.7M
{
1190
14.7M
  return Curl_pollset_add_inout(data, ps, data->conn->sock[FIRSTSOCKET]);
1191
14.7M
}
1192
1193
static CURLcode smb_do(struct Curl_easy *data, bool *done)
1194
329
{
1195
329
  struct connectdata *conn = data->conn;
1196
329
  struct smb_conn *smbc = Curl_conn_meta_get(conn, CURL_META_SMB_CONN);
1197
1198
329
  *done = FALSE;
1199
329
  if(!smbc)
1200
0
    return CURLE_FAILED_INIT;
1201
329
  if(smbc->share)
1202
329
    return CURLE_OK;
1203
0
  return CURLE_URL_MALFORMAT;
1204
329
}
1205
1206
/*
1207
 * SMB handler interface
1208
 */
1209
static const struct Curl_protocol Curl_protocol_smb = {
1210
  smb_setup_connection,                 /* setup_connection */
1211
  smb_do,                               /* do_it */
1212
  ZERO_NULL,                            /* done */
1213
  ZERO_NULL,                            /* do_more */
1214
  smb_connect,                          /* connect_it */
1215
  smb_connection_state,                 /* connecting */
1216
  smb_request_state,                    /* doing */
1217
  smb_pollset,                          /* proto_pollset */
1218
  smb_pollset,                          /* doing_pollset */
1219
  ZERO_NULL,                            /* domore_pollset */
1220
  ZERO_NULL,                            /* perform_pollset */
1221
  ZERO_NULL,                            /* disconnect */
1222
  ZERO_NULL,                            /* write_resp */
1223
  ZERO_NULL,                            /* write_resp_hd */
1224
  ZERO_NULL,                            /* connection_check */
1225
  ZERO_NULL,                            /* attach connection */
1226
  ZERO_NULL,                            /* follow */
1227
};
1228
1229
#endif /* CURL_DISABLE_SMB && USE_CURL_NTLM_CORE && SIZEOF_CURL_OFF_T > 4 */
1230
1231
/*
1232
 * SMB handler interface
1233
 */
1234
const struct Curl_scheme Curl_scheme_smb = {
1235
  "smb",                                /* scheme */
1236
#if defined(CURL_DISABLE_SMB) || !defined(USE_CURL_NTLM_CORE)
1237
  ZERO_NULL,
1238
#else
1239
  &Curl_protocol_smb,
1240
#endif
1241
  CURLPROTO_SMB,                        /* protocol */
1242
  CURLPROTO_SMB,                        /* family */
1243
  PROTOPT_CONN_REUSE,                   /* flags */
1244
  PORT_SMB,                             /* defport */
1245
};
1246
1247
/*
1248
 * SMBS handler interface
1249
 */
1250
const struct Curl_scheme Curl_scheme_smbs = {
1251
  "smbs",                               /* scheme */
1252
#if defined(CURL_DISABLE_SMB) || !defined(USE_CURL_NTLM_CORE) || \
1253
  !defined(USE_SSL)
1254
  ZERO_NULL,
1255
#else
1256
  &Curl_protocol_smb,
1257
#endif
1258
  CURLPROTO_SMBS,                       /* protocol */
1259
  CURLPROTO_SMB,                        /* family */
1260
  PROTOPT_SSL | PROTOPT_CONN_REUSE,     /* flags */
1261
  PORT_SMBS,                            /* defport */
1262
};