113498266Sopenharmony_ci/***************************************************************************
213498266Sopenharmony_ci *                                  _   _ ____  _
313498266Sopenharmony_ci *  Project                     ___| | | |  _ \| |
413498266Sopenharmony_ci *                             / __| | | | |_) | |
513498266Sopenharmony_ci *                            | (__| |_| |  _ <| |___
613498266Sopenharmony_ci *                             \___|\___/|_| \_\_____|
713498266Sopenharmony_ci *
813498266Sopenharmony_ci * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
913498266Sopenharmony_ci * Copyright (C) Bill Nagel <wnagel@tycoint.com>, Exacq Technologies
1013498266Sopenharmony_ci *
1113498266Sopenharmony_ci * This software is licensed as described in the file COPYING, which
1213498266Sopenharmony_ci * you should have received as part of this distribution. The terms
1313498266Sopenharmony_ci * are also available at https://curl.se/docs/copyright.html.
1413498266Sopenharmony_ci *
1513498266Sopenharmony_ci * You may opt to use, copy, modify, merge, publish, distribute and/or sell
1613498266Sopenharmony_ci * copies of the Software, and permit persons to whom the Software is
1713498266Sopenharmony_ci * furnished to do so, under the terms of the COPYING file.
1813498266Sopenharmony_ci *
1913498266Sopenharmony_ci * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
2013498266Sopenharmony_ci * KIND, either express or implied.
2113498266Sopenharmony_ci *
2213498266Sopenharmony_ci * SPDX-License-Identifier: curl
2313498266Sopenharmony_ci *
2413498266Sopenharmony_ci ***************************************************************************/
2513498266Sopenharmony_ci
2613498266Sopenharmony_ci#include "curl_setup.h"
2713498266Sopenharmony_ci
2813498266Sopenharmony_ci#if !defined(CURL_DISABLE_SMB) && defined(USE_CURL_NTLM_CORE)
2913498266Sopenharmony_ci
3013498266Sopenharmony_ci#ifdef _WIN32
3113498266Sopenharmony_ci#define getpid GetCurrentProcessId
3213498266Sopenharmony_ci#endif
3313498266Sopenharmony_ci
3413498266Sopenharmony_ci#include "smb.h"
3513498266Sopenharmony_ci#include "urldata.h"
3613498266Sopenharmony_ci#include "sendf.h"
3713498266Sopenharmony_ci#include "multiif.h"
3813498266Sopenharmony_ci#include "cfilters.h"
3913498266Sopenharmony_ci#include "connect.h"
4013498266Sopenharmony_ci#include "progress.h"
4113498266Sopenharmony_ci#include "transfer.h"
4213498266Sopenharmony_ci#include "vtls/vtls.h"
4313498266Sopenharmony_ci#include "curl_ntlm_core.h"
4413498266Sopenharmony_ci#include "escape.h"
4513498266Sopenharmony_ci#include "curl_endian.h"
4613498266Sopenharmony_ci
4713498266Sopenharmony_ci/* The last #include files should be: */
4813498266Sopenharmony_ci#include "curl_memory.h"
4913498266Sopenharmony_ci#include "memdebug.h"
5013498266Sopenharmony_ci
5113498266Sopenharmony_ci/*
5213498266Sopenharmony_ci * Definitions for SMB protocol data structures
5313498266Sopenharmony_ci */
5413498266Sopenharmony_ci#if defined(_MSC_VER) || defined(__ILEC400__)
5513498266Sopenharmony_ci#  define PACK
5613498266Sopenharmony_ci#  pragma pack(push)
5713498266Sopenharmony_ci#  pragma pack(1)
5813498266Sopenharmony_ci#elif defined(__GNUC__)
5913498266Sopenharmony_ci#  define PACK __attribute__((packed))
6013498266Sopenharmony_ci#else
6113498266Sopenharmony_ci#  define PACK
6213498266Sopenharmony_ci#endif
6313498266Sopenharmony_ci
6413498266Sopenharmony_ci#define SMB_COM_CLOSE                 0x04
6513498266Sopenharmony_ci#define SMB_COM_READ_ANDX             0x2e
6613498266Sopenharmony_ci#define SMB_COM_WRITE_ANDX            0x2f
6713498266Sopenharmony_ci#define SMB_COM_TREE_DISCONNECT       0x71
6813498266Sopenharmony_ci#define SMB_COM_NEGOTIATE             0x72
6913498266Sopenharmony_ci#define SMB_COM_SETUP_ANDX            0x73
7013498266Sopenharmony_ci#define SMB_COM_TREE_CONNECT_ANDX     0x75
7113498266Sopenharmony_ci#define SMB_COM_NT_CREATE_ANDX        0xa2
7213498266Sopenharmony_ci#define SMB_COM_NO_ANDX_COMMAND       0xff
7313498266Sopenharmony_ci
7413498266Sopenharmony_ci#define SMB_WC_CLOSE                  0x03
7513498266Sopenharmony_ci#define SMB_WC_READ_ANDX              0x0c
7613498266Sopenharmony_ci#define SMB_WC_WRITE_ANDX             0x0e
7713498266Sopenharmony_ci#define SMB_WC_SETUP_ANDX             0x0d
7813498266Sopenharmony_ci#define SMB_WC_TREE_CONNECT_ANDX      0x04
7913498266Sopenharmony_ci#define SMB_WC_NT_CREATE_ANDX         0x18
8013498266Sopenharmony_ci
8113498266Sopenharmony_ci#define SMB_FLAGS_CANONICAL_PATHNAMES 0x10
8213498266Sopenharmony_ci#define SMB_FLAGS_CASELESS_PATHNAMES  0x08
8313498266Sopenharmony_ci#define SMB_FLAGS2_UNICODE_STRINGS    0x8000
8413498266Sopenharmony_ci#define SMB_FLAGS2_IS_LONG_NAME       0x0040
8513498266Sopenharmony_ci#define SMB_FLAGS2_KNOWS_LONG_NAME    0x0001
8613498266Sopenharmony_ci
8713498266Sopenharmony_ci#define SMB_CAP_LARGE_FILES           0x08
8813498266Sopenharmony_ci#define SMB_GENERIC_WRITE             0x40000000
8913498266Sopenharmony_ci#define SMB_GENERIC_READ              0x80000000
9013498266Sopenharmony_ci#define SMB_FILE_SHARE_ALL            0x07
9113498266Sopenharmony_ci#define SMB_FILE_OPEN                 0x01
9213498266Sopenharmony_ci#define SMB_FILE_OVERWRITE_IF         0x05
9313498266Sopenharmony_ci
9413498266Sopenharmony_ci#define SMB_ERR_NOACCESS              0x00050001
9513498266Sopenharmony_ci
9613498266Sopenharmony_cistruct smb_header {
9713498266Sopenharmony_ci  unsigned char nbt_type;
9813498266Sopenharmony_ci  unsigned char nbt_flags;
9913498266Sopenharmony_ci  unsigned short nbt_length;
10013498266Sopenharmony_ci  unsigned char magic[4];
10113498266Sopenharmony_ci  unsigned char command;
10213498266Sopenharmony_ci  unsigned int status;
10313498266Sopenharmony_ci  unsigned char flags;
10413498266Sopenharmony_ci  unsigned short flags2;
10513498266Sopenharmony_ci  unsigned short pid_high;
10613498266Sopenharmony_ci  unsigned char signature[8];
10713498266Sopenharmony_ci  unsigned short pad;
10813498266Sopenharmony_ci  unsigned short tid;
10913498266Sopenharmony_ci  unsigned short pid;
11013498266Sopenharmony_ci  unsigned short uid;
11113498266Sopenharmony_ci  unsigned short mid;
11213498266Sopenharmony_ci} PACK;
11313498266Sopenharmony_ci
11413498266Sopenharmony_cistruct smb_negotiate_response {
11513498266Sopenharmony_ci  struct smb_header h;
11613498266Sopenharmony_ci  unsigned char word_count;
11713498266Sopenharmony_ci  unsigned short dialect_index;
11813498266Sopenharmony_ci  unsigned char security_mode;
11913498266Sopenharmony_ci  unsigned short max_mpx_count;
12013498266Sopenharmony_ci  unsigned short max_number_vcs;
12113498266Sopenharmony_ci  unsigned int max_buffer_size;
12213498266Sopenharmony_ci  unsigned int max_raw_size;
12313498266Sopenharmony_ci  unsigned int session_key;
12413498266Sopenharmony_ci  unsigned int capabilities;
12513498266Sopenharmony_ci  unsigned int system_time_low;
12613498266Sopenharmony_ci  unsigned int system_time_high;
12713498266Sopenharmony_ci  unsigned short server_time_zone;
12813498266Sopenharmony_ci  unsigned char encryption_key_length;
12913498266Sopenharmony_ci  unsigned short byte_count;
13013498266Sopenharmony_ci  char bytes[1];
13113498266Sopenharmony_ci} PACK;
13213498266Sopenharmony_ci
13313498266Sopenharmony_cistruct andx {
13413498266Sopenharmony_ci  unsigned char command;
13513498266Sopenharmony_ci  unsigned char pad;
13613498266Sopenharmony_ci  unsigned short offset;
13713498266Sopenharmony_ci} PACK;
13813498266Sopenharmony_ci
13913498266Sopenharmony_cistruct smb_setup {
14013498266Sopenharmony_ci  unsigned char word_count;
14113498266Sopenharmony_ci  struct andx andx;
14213498266Sopenharmony_ci  unsigned short max_buffer_size;
14313498266Sopenharmony_ci  unsigned short max_mpx_count;
14413498266Sopenharmony_ci  unsigned short vc_number;
14513498266Sopenharmony_ci  unsigned int session_key;
14613498266Sopenharmony_ci  unsigned short lengths[2];
14713498266Sopenharmony_ci  unsigned int pad;
14813498266Sopenharmony_ci  unsigned int capabilities;
14913498266Sopenharmony_ci  unsigned short byte_count;
15013498266Sopenharmony_ci  char bytes[1024];
15113498266Sopenharmony_ci} PACK;
15213498266Sopenharmony_ci
15313498266Sopenharmony_cistruct smb_tree_connect {
15413498266Sopenharmony_ci  unsigned char word_count;
15513498266Sopenharmony_ci  struct andx andx;
15613498266Sopenharmony_ci  unsigned short flags;
15713498266Sopenharmony_ci  unsigned short pw_len;
15813498266Sopenharmony_ci  unsigned short byte_count;
15913498266Sopenharmony_ci  char bytes[1024];
16013498266Sopenharmony_ci} PACK;
16113498266Sopenharmony_ci
16213498266Sopenharmony_cistruct smb_nt_create {
16313498266Sopenharmony_ci  unsigned char word_count;
16413498266Sopenharmony_ci  struct andx andx;
16513498266Sopenharmony_ci  unsigned char pad;
16613498266Sopenharmony_ci  unsigned short name_length;
16713498266Sopenharmony_ci  unsigned int flags;
16813498266Sopenharmony_ci  unsigned int root_fid;
16913498266Sopenharmony_ci  unsigned int access;
17013498266Sopenharmony_ci  curl_off_t allocation_size;
17113498266Sopenharmony_ci  unsigned int ext_file_attributes;
17213498266Sopenharmony_ci  unsigned int share_access;
17313498266Sopenharmony_ci  unsigned int create_disposition;
17413498266Sopenharmony_ci  unsigned int create_options;
17513498266Sopenharmony_ci  unsigned int impersonation_level;
17613498266Sopenharmony_ci  unsigned char security_flags;
17713498266Sopenharmony_ci  unsigned short byte_count;
17813498266Sopenharmony_ci  char bytes[1024];
17913498266Sopenharmony_ci} PACK;
18013498266Sopenharmony_ci
18113498266Sopenharmony_cistruct smb_nt_create_response {
18213498266Sopenharmony_ci  struct smb_header h;
18313498266Sopenharmony_ci  unsigned char word_count;
18413498266Sopenharmony_ci  struct andx andx;
18513498266Sopenharmony_ci  unsigned char op_lock_level;
18613498266Sopenharmony_ci  unsigned short fid;
18713498266Sopenharmony_ci  unsigned int create_disposition;
18813498266Sopenharmony_ci
18913498266Sopenharmony_ci  curl_off_t create_time;
19013498266Sopenharmony_ci  curl_off_t last_access_time;
19113498266Sopenharmony_ci  curl_off_t last_write_time;
19213498266Sopenharmony_ci  curl_off_t last_change_time;
19313498266Sopenharmony_ci  unsigned int ext_file_attributes;
19413498266Sopenharmony_ci  curl_off_t allocation_size;
19513498266Sopenharmony_ci  curl_off_t end_of_file;
19613498266Sopenharmony_ci} PACK;
19713498266Sopenharmony_ci
19813498266Sopenharmony_cistruct smb_read {
19913498266Sopenharmony_ci  unsigned char word_count;
20013498266Sopenharmony_ci  struct andx andx;
20113498266Sopenharmony_ci  unsigned short fid;
20213498266Sopenharmony_ci  unsigned int offset;
20313498266Sopenharmony_ci  unsigned short max_bytes;
20413498266Sopenharmony_ci  unsigned short min_bytes;
20513498266Sopenharmony_ci  unsigned int timeout;
20613498266Sopenharmony_ci  unsigned short remaining;
20713498266Sopenharmony_ci  unsigned int offset_high;
20813498266Sopenharmony_ci  unsigned short byte_count;
20913498266Sopenharmony_ci} PACK;
21013498266Sopenharmony_ci
21113498266Sopenharmony_cistruct smb_write {
21213498266Sopenharmony_ci  struct smb_header h;
21313498266Sopenharmony_ci  unsigned char word_count;
21413498266Sopenharmony_ci  struct andx andx;
21513498266Sopenharmony_ci  unsigned short fid;
21613498266Sopenharmony_ci  unsigned int offset;
21713498266Sopenharmony_ci  unsigned int timeout;
21813498266Sopenharmony_ci  unsigned short write_mode;
21913498266Sopenharmony_ci  unsigned short remaining;
22013498266Sopenharmony_ci  unsigned short pad;
22113498266Sopenharmony_ci  unsigned short data_length;
22213498266Sopenharmony_ci  unsigned short data_offset;
22313498266Sopenharmony_ci  unsigned int offset_high;
22413498266Sopenharmony_ci  unsigned short byte_count;
22513498266Sopenharmony_ci  unsigned char pad2;
22613498266Sopenharmony_ci} PACK;
22713498266Sopenharmony_ci
22813498266Sopenharmony_cistruct smb_close {
22913498266Sopenharmony_ci  unsigned char word_count;
23013498266Sopenharmony_ci  unsigned short fid;
23113498266Sopenharmony_ci  unsigned int last_mtime;
23213498266Sopenharmony_ci  unsigned short byte_count;
23313498266Sopenharmony_ci} PACK;
23413498266Sopenharmony_ci
23513498266Sopenharmony_cistruct smb_tree_disconnect {
23613498266Sopenharmony_ci  unsigned char word_count;
23713498266Sopenharmony_ci  unsigned short byte_count;
23813498266Sopenharmony_ci} PACK;
23913498266Sopenharmony_ci
24013498266Sopenharmony_ci#if defined(_MSC_VER) || defined(__ILEC400__)
24113498266Sopenharmony_ci#  pragma pack(pop)
24213498266Sopenharmony_ci#endif
24313498266Sopenharmony_ci
24413498266Sopenharmony_ci/* Local API functions */
24513498266Sopenharmony_cistatic CURLcode smb_setup_connection(struct Curl_easy *data,
24613498266Sopenharmony_ci                                     struct connectdata *conn);
24713498266Sopenharmony_cistatic CURLcode smb_connect(struct Curl_easy *data, bool *done);
24813498266Sopenharmony_cistatic CURLcode smb_connection_state(struct Curl_easy *data, bool *done);
24913498266Sopenharmony_cistatic CURLcode smb_do(struct Curl_easy *data, bool *done);
25013498266Sopenharmony_cistatic CURLcode smb_request_state(struct Curl_easy *data, bool *done);
25113498266Sopenharmony_cistatic CURLcode smb_disconnect(struct Curl_easy *data,
25213498266Sopenharmony_ci                               struct connectdata *conn, bool dead);
25313498266Sopenharmony_cistatic int smb_getsock(struct Curl_easy *data, struct connectdata *conn,
25413498266Sopenharmony_ci                       curl_socket_t *socks);
25513498266Sopenharmony_cistatic CURLcode smb_parse_url_path(struct Curl_easy *data,
25613498266Sopenharmony_ci                                   struct connectdata *conn);
25713498266Sopenharmony_ci
25813498266Sopenharmony_ci/*
25913498266Sopenharmony_ci * SMB handler interface
26013498266Sopenharmony_ci */
26113498266Sopenharmony_ciconst struct Curl_handler Curl_handler_smb = {
26213498266Sopenharmony_ci  "SMB",                                /* scheme */
26313498266Sopenharmony_ci  smb_setup_connection,                 /* setup_connection */
26413498266Sopenharmony_ci  smb_do,                               /* do_it */
26513498266Sopenharmony_ci  ZERO_NULL,                            /* done */
26613498266Sopenharmony_ci  ZERO_NULL,                            /* do_more */
26713498266Sopenharmony_ci  smb_connect,                          /* connect_it */
26813498266Sopenharmony_ci  smb_connection_state,                 /* connecting */
26913498266Sopenharmony_ci  smb_request_state,                    /* doing */
27013498266Sopenharmony_ci  smb_getsock,                          /* proto_getsock */
27113498266Sopenharmony_ci  smb_getsock,                          /* doing_getsock */
27213498266Sopenharmony_ci  ZERO_NULL,                            /* domore_getsock */
27313498266Sopenharmony_ci  ZERO_NULL,                            /* perform_getsock */
27413498266Sopenharmony_ci  smb_disconnect,                       /* disconnect */
27513498266Sopenharmony_ci  ZERO_NULL,                            /* write_resp */
27613498266Sopenharmony_ci  ZERO_NULL,                            /* connection_check */
27713498266Sopenharmony_ci  ZERO_NULL,                            /* attach connection */
27813498266Sopenharmony_ci  PORT_SMB,                             /* defport */
27913498266Sopenharmony_ci  CURLPROTO_SMB,                        /* protocol */
28013498266Sopenharmony_ci  CURLPROTO_SMB,                        /* family */
28113498266Sopenharmony_ci  PROTOPT_NONE                          /* flags */
28213498266Sopenharmony_ci};
28313498266Sopenharmony_ci
28413498266Sopenharmony_ci#ifdef USE_SSL
28513498266Sopenharmony_ci/*
28613498266Sopenharmony_ci * SMBS handler interface
28713498266Sopenharmony_ci */
28813498266Sopenharmony_ciconst struct Curl_handler Curl_handler_smbs = {
28913498266Sopenharmony_ci  "SMBS",                               /* scheme */
29013498266Sopenharmony_ci  smb_setup_connection,                 /* setup_connection */
29113498266Sopenharmony_ci  smb_do,                               /* do_it */
29213498266Sopenharmony_ci  ZERO_NULL,                            /* done */
29313498266Sopenharmony_ci  ZERO_NULL,                            /* do_more */
29413498266Sopenharmony_ci  smb_connect,                          /* connect_it */
29513498266Sopenharmony_ci  smb_connection_state,                 /* connecting */
29613498266Sopenharmony_ci  smb_request_state,                    /* doing */
29713498266Sopenharmony_ci  smb_getsock,                          /* proto_getsock */
29813498266Sopenharmony_ci  smb_getsock,                          /* doing_getsock */
29913498266Sopenharmony_ci  ZERO_NULL,                            /* domore_getsock */
30013498266Sopenharmony_ci  ZERO_NULL,                            /* perform_getsock */
30113498266Sopenharmony_ci  smb_disconnect,                       /* disconnect */
30213498266Sopenharmony_ci  ZERO_NULL,                            /* write_resp */
30313498266Sopenharmony_ci  ZERO_NULL,                            /* connection_check */
30413498266Sopenharmony_ci  ZERO_NULL,                            /* attach connection */
30513498266Sopenharmony_ci  PORT_SMBS,                            /* defport */
30613498266Sopenharmony_ci  CURLPROTO_SMBS,                       /* protocol */
30713498266Sopenharmony_ci  CURLPROTO_SMB,                        /* family */
30813498266Sopenharmony_ci  PROTOPT_SSL                           /* flags */
30913498266Sopenharmony_ci};
31013498266Sopenharmony_ci#endif
31113498266Sopenharmony_ci
31213498266Sopenharmony_ci#define MAX_PAYLOAD_SIZE  0x8000
31313498266Sopenharmony_ci#define MAX_MESSAGE_SIZE  (MAX_PAYLOAD_SIZE + 0x1000)
31413498266Sopenharmony_ci#define CLIENTNAME        "curl"
31513498266Sopenharmony_ci#define SERVICENAME       "?????"
31613498266Sopenharmony_ci
31713498266Sopenharmony_ci/* Append a string to an SMB message */
31813498266Sopenharmony_ci#define MSGCAT(str)                             \
31913498266Sopenharmony_ci  do {                                          \
32013498266Sopenharmony_ci    strcpy(p, (str));                           \
32113498266Sopenharmony_ci    p += strlen(str);                           \
32213498266Sopenharmony_ci  } while(0)
32313498266Sopenharmony_ci
32413498266Sopenharmony_ci/* Append a null-terminated string to an SMB message */
32513498266Sopenharmony_ci#define MSGCATNULL(str)                         \
32613498266Sopenharmony_ci  do {                                          \
32713498266Sopenharmony_ci    strcpy(p, (str));                           \
32813498266Sopenharmony_ci    p += strlen(str) + 1;                       \
32913498266Sopenharmony_ci  } while(0)
33013498266Sopenharmony_ci
33113498266Sopenharmony_ci/* SMB is mostly little endian */
33213498266Sopenharmony_ci#if (defined(__BYTE_ORDER__) && __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__) || \
33313498266Sopenharmony_ci  defined(__OS400__)
33413498266Sopenharmony_cistatic unsigned short smb_swap16(unsigned short x)
33513498266Sopenharmony_ci{
33613498266Sopenharmony_ci  return (unsigned short) ((x << 8) | ((x >> 8) & 0xff));
33713498266Sopenharmony_ci}
33813498266Sopenharmony_ci
33913498266Sopenharmony_cistatic unsigned int smb_swap32(unsigned int x)
34013498266Sopenharmony_ci{
34113498266Sopenharmony_ci  return (x << 24) | ((x << 8) & 0xff0000) | ((x >> 8) & 0xff00) |
34213498266Sopenharmony_ci    ((x >> 24) & 0xff);
34313498266Sopenharmony_ci}
34413498266Sopenharmony_ci
34513498266Sopenharmony_cistatic curl_off_t smb_swap64(curl_off_t x)
34613498266Sopenharmony_ci{
34713498266Sopenharmony_ci  return ((curl_off_t) smb_swap32((unsigned int) x) << 32) |
34813498266Sopenharmony_ci    smb_swap32((unsigned int) (x >> 32));
34913498266Sopenharmony_ci}
35013498266Sopenharmony_ci
35113498266Sopenharmony_ci#else
35213498266Sopenharmony_ci#  define smb_swap16(x) (x)
35313498266Sopenharmony_ci#  define smb_swap32(x) (x)
35413498266Sopenharmony_ci#  define smb_swap64(x) (x)
35513498266Sopenharmony_ci#endif
35613498266Sopenharmony_ci
35713498266Sopenharmony_ci/* SMB request state */
35813498266Sopenharmony_cienum smb_req_state {
35913498266Sopenharmony_ci  SMB_REQUESTING,
36013498266Sopenharmony_ci  SMB_TREE_CONNECT,
36113498266Sopenharmony_ci  SMB_OPEN,
36213498266Sopenharmony_ci  SMB_DOWNLOAD,
36313498266Sopenharmony_ci  SMB_UPLOAD,
36413498266Sopenharmony_ci  SMB_CLOSE,
36513498266Sopenharmony_ci  SMB_TREE_DISCONNECT,
36613498266Sopenharmony_ci  SMB_DONE
36713498266Sopenharmony_ci};
36813498266Sopenharmony_ci
36913498266Sopenharmony_ci/* SMB request data */
37013498266Sopenharmony_cistruct smb_request {
37113498266Sopenharmony_ci  enum smb_req_state state;
37213498266Sopenharmony_ci  char *path;
37313498266Sopenharmony_ci  unsigned short tid; /* Even if we connect to the same tree as another */
37413498266Sopenharmony_ci  unsigned short fid; /* request, the tid will be different */
37513498266Sopenharmony_ci  CURLcode result;
37613498266Sopenharmony_ci};
37713498266Sopenharmony_ci
37813498266Sopenharmony_cistatic void conn_state(struct Curl_easy *data, enum smb_conn_state newstate)
37913498266Sopenharmony_ci{
38013498266Sopenharmony_ci  struct smb_conn *smbc = &data->conn->proto.smbc;
38113498266Sopenharmony_ci#if defined(DEBUGBUILD) && !defined(CURL_DISABLE_VERBOSE_STRINGS)
38213498266Sopenharmony_ci  /* For debug purposes */
38313498266Sopenharmony_ci  static const char * const names[] = {
38413498266Sopenharmony_ci    "SMB_NOT_CONNECTED",
38513498266Sopenharmony_ci    "SMB_CONNECTING",
38613498266Sopenharmony_ci    "SMB_NEGOTIATE",
38713498266Sopenharmony_ci    "SMB_SETUP",
38813498266Sopenharmony_ci    "SMB_CONNECTED",
38913498266Sopenharmony_ci    /* LAST */
39013498266Sopenharmony_ci  };
39113498266Sopenharmony_ci
39213498266Sopenharmony_ci  if(smbc->state != newstate)
39313498266Sopenharmony_ci    infof(data, "SMB conn %p state change from %s to %s",
39413498266Sopenharmony_ci          (void *)smbc, names[smbc->state], names[newstate]);
39513498266Sopenharmony_ci#endif
39613498266Sopenharmony_ci
39713498266Sopenharmony_ci  smbc->state = newstate;
39813498266Sopenharmony_ci}
39913498266Sopenharmony_ci
40013498266Sopenharmony_cistatic void request_state(struct Curl_easy *data,
40113498266Sopenharmony_ci                          enum smb_req_state newstate)
40213498266Sopenharmony_ci{
40313498266Sopenharmony_ci  struct smb_request *req = data->req.p.smb;
40413498266Sopenharmony_ci#if defined(DEBUGBUILD) && !defined(CURL_DISABLE_VERBOSE_STRINGS)
40513498266Sopenharmony_ci  /* For debug purposes */
40613498266Sopenharmony_ci  static const char * const names[] = {
40713498266Sopenharmony_ci    "SMB_REQUESTING",
40813498266Sopenharmony_ci    "SMB_TREE_CONNECT",
40913498266Sopenharmony_ci    "SMB_OPEN",
41013498266Sopenharmony_ci    "SMB_DOWNLOAD",
41113498266Sopenharmony_ci    "SMB_UPLOAD",
41213498266Sopenharmony_ci    "SMB_CLOSE",
41313498266Sopenharmony_ci    "SMB_TREE_DISCONNECT",
41413498266Sopenharmony_ci    "SMB_DONE",
41513498266Sopenharmony_ci    /* LAST */
41613498266Sopenharmony_ci  };
41713498266Sopenharmony_ci
41813498266Sopenharmony_ci  if(req->state != newstate)
41913498266Sopenharmony_ci    infof(data, "SMB request %p state change from %s to %s",
42013498266Sopenharmony_ci          (void *)req, names[req->state], names[newstate]);
42113498266Sopenharmony_ci#endif
42213498266Sopenharmony_ci
42313498266Sopenharmony_ci  req->state = newstate;
42413498266Sopenharmony_ci}
42513498266Sopenharmony_ci
42613498266Sopenharmony_ci/* this should setup things in the connection, not in the easy
42713498266Sopenharmony_ci   handle */
42813498266Sopenharmony_cistatic CURLcode smb_setup_connection(struct Curl_easy *data,
42913498266Sopenharmony_ci                                     struct connectdata *conn)
43013498266Sopenharmony_ci{
43113498266Sopenharmony_ci  struct smb_request *req;
43213498266Sopenharmony_ci
43313498266Sopenharmony_ci  /* Initialize the request state */
43413498266Sopenharmony_ci  data->req.p.smb = req = calloc(1, sizeof(struct smb_request));
43513498266Sopenharmony_ci  if(!req)
43613498266Sopenharmony_ci    return CURLE_OUT_OF_MEMORY;
43713498266Sopenharmony_ci
43813498266Sopenharmony_ci  /* Parse the URL path */
43913498266Sopenharmony_ci  return smb_parse_url_path(data, conn);
44013498266Sopenharmony_ci}
44113498266Sopenharmony_ci
44213498266Sopenharmony_cistatic CURLcode smb_connect(struct Curl_easy *data, bool *done)
44313498266Sopenharmony_ci{
44413498266Sopenharmony_ci  struct connectdata *conn = data->conn;
44513498266Sopenharmony_ci  struct smb_conn *smbc = &conn->proto.smbc;
44613498266Sopenharmony_ci  char *slash;
44713498266Sopenharmony_ci
44813498266Sopenharmony_ci  (void) done;
44913498266Sopenharmony_ci
45013498266Sopenharmony_ci  /* Check we have a username and password to authenticate with */
45113498266Sopenharmony_ci  if(!data->state.aptr.user)
45213498266Sopenharmony_ci    return CURLE_LOGIN_DENIED;
45313498266Sopenharmony_ci
45413498266Sopenharmony_ci  /* Initialize the connection state */
45513498266Sopenharmony_ci  smbc->state = SMB_CONNECTING;
45613498266Sopenharmony_ci  smbc->recv_buf = malloc(MAX_MESSAGE_SIZE);
45713498266Sopenharmony_ci  if(!smbc->recv_buf)
45813498266Sopenharmony_ci    return CURLE_OUT_OF_MEMORY;
45913498266Sopenharmony_ci
46013498266Sopenharmony_ci  /* Multiple requests are allowed with this connection */
46113498266Sopenharmony_ci  connkeep(conn, "SMB default");
46213498266Sopenharmony_ci
46313498266Sopenharmony_ci  /* Parse the username, domain, and password */
46413498266Sopenharmony_ci  slash = strchr(conn->user, '/');
46513498266Sopenharmony_ci  if(!slash)
46613498266Sopenharmony_ci    slash = strchr(conn->user, '\\');
46713498266Sopenharmony_ci
46813498266Sopenharmony_ci  if(slash) {
46913498266Sopenharmony_ci    smbc->user = slash + 1;
47013498266Sopenharmony_ci    smbc->domain = strdup(conn->user);
47113498266Sopenharmony_ci    if(!smbc->domain)
47213498266Sopenharmony_ci      return CURLE_OUT_OF_MEMORY;
47313498266Sopenharmony_ci    smbc->domain[slash - conn->user] = 0;
47413498266Sopenharmony_ci  }
47513498266Sopenharmony_ci  else {
47613498266Sopenharmony_ci    smbc->user = conn->user;
47713498266Sopenharmony_ci    smbc->domain = strdup(conn->host.name);
47813498266Sopenharmony_ci    if(!smbc->domain)
47913498266Sopenharmony_ci      return CURLE_OUT_OF_MEMORY;
48013498266Sopenharmony_ci  }
48113498266Sopenharmony_ci
48213498266Sopenharmony_ci  return CURLE_OK;
48313498266Sopenharmony_ci}
48413498266Sopenharmony_ci
48513498266Sopenharmony_cistatic CURLcode smb_recv_message(struct Curl_easy *data, void **msg)
48613498266Sopenharmony_ci{
48713498266Sopenharmony_ci  struct connectdata *conn = data->conn;
48813498266Sopenharmony_ci  curl_socket_t sockfd = conn->sock[FIRSTSOCKET];
48913498266Sopenharmony_ci  struct smb_conn *smbc = &conn->proto.smbc;
49013498266Sopenharmony_ci  char *buf = smbc->recv_buf;
49113498266Sopenharmony_ci  ssize_t bytes_read;
49213498266Sopenharmony_ci  size_t nbt_size;
49313498266Sopenharmony_ci  size_t msg_size;
49413498266Sopenharmony_ci  size_t len = MAX_MESSAGE_SIZE - smbc->got;
49513498266Sopenharmony_ci  CURLcode result;
49613498266Sopenharmony_ci
49713498266Sopenharmony_ci  result = Curl_read(data, sockfd, buf + smbc->got, len, &bytes_read);
49813498266Sopenharmony_ci  if(result)
49913498266Sopenharmony_ci    return result;
50013498266Sopenharmony_ci
50113498266Sopenharmony_ci  if(!bytes_read)
50213498266Sopenharmony_ci    return CURLE_OK;
50313498266Sopenharmony_ci
50413498266Sopenharmony_ci  smbc->got += bytes_read;
50513498266Sopenharmony_ci
50613498266Sopenharmony_ci  /* Check for a 32-bit nbt header */
50713498266Sopenharmony_ci  if(smbc->got < sizeof(unsigned int))
50813498266Sopenharmony_ci    return CURLE_OK;
50913498266Sopenharmony_ci
51013498266Sopenharmony_ci  nbt_size = Curl_read16_be((const unsigned char *)
51113498266Sopenharmony_ci                            (buf + sizeof(unsigned short))) +
51213498266Sopenharmony_ci    sizeof(unsigned int);
51313498266Sopenharmony_ci  if(smbc->got < nbt_size)
51413498266Sopenharmony_ci    return CURLE_OK;
51513498266Sopenharmony_ci
51613498266Sopenharmony_ci  msg_size = sizeof(struct smb_header);
51713498266Sopenharmony_ci  if(nbt_size >= msg_size + 1) {
51813498266Sopenharmony_ci    /* Add the word count */
51913498266Sopenharmony_ci    msg_size += 1 + ((unsigned char) buf[msg_size]) * sizeof(unsigned short);
52013498266Sopenharmony_ci    if(nbt_size >= msg_size + sizeof(unsigned short)) {
52113498266Sopenharmony_ci      /* Add the byte count */
52213498266Sopenharmony_ci      msg_size += sizeof(unsigned short) +
52313498266Sopenharmony_ci        Curl_read16_le((const unsigned char *)&buf[msg_size]);
52413498266Sopenharmony_ci      if(nbt_size < msg_size)
52513498266Sopenharmony_ci        return CURLE_READ_ERROR;
52613498266Sopenharmony_ci    }
52713498266Sopenharmony_ci  }
52813498266Sopenharmony_ci
52913498266Sopenharmony_ci  *msg = buf;
53013498266Sopenharmony_ci
53113498266Sopenharmony_ci  return CURLE_OK;
53213498266Sopenharmony_ci}
53313498266Sopenharmony_ci
53413498266Sopenharmony_cistatic void smb_pop_message(struct connectdata *conn)
53513498266Sopenharmony_ci{
53613498266Sopenharmony_ci  struct smb_conn *smbc = &conn->proto.smbc;
53713498266Sopenharmony_ci
53813498266Sopenharmony_ci  smbc->got = 0;
53913498266Sopenharmony_ci}
54013498266Sopenharmony_ci
54113498266Sopenharmony_cistatic void smb_format_message(struct Curl_easy *data, struct smb_header *h,
54213498266Sopenharmony_ci                               unsigned char cmd, size_t len)
54313498266Sopenharmony_ci{
54413498266Sopenharmony_ci  struct connectdata *conn = data->conn;
54513498266Sopenharmony_ci  struct smb_conn *smbc = &conn->proto.smbc;
54613498266Sopenharmony_ci  struct smb_request *req = data->req.p.smb;
54713498266Sopenharmony_ci  unsigned int pid;
54813498266Sopenharmony_ci
54913498266Sopenharmony_ci  memset(h, 0, sizeof(*h));
55013498266Sopenharmony_ci  h->nbt_length = htons((unsigned short) (sizeof(*h) - sizeof(unsigned int) +
55113498266Sopenharmony_ci                                          len));
55213498266Sopenharmony_ci  memcpy((char *)h->magic, "\xffSMB", 4);
55313498266Sopenharmony_ci  h->command = cmd;
55413498266Sopenharmony_ci  h->flags = SMB_FLAGS_CANONICAL_PATHNAMES | SMB_FLAGS_CASELESS_PATHNAMES;
55513498266Sopenharmony_ci  h->flags2 = smb_swap16(SMB_FLAGS2_IS_LONG_NAME | SMB_FLAGS2_KNOWS_LONG_NAME);
55613498266Sopenharmony_ci  h->uid = smb_swap16(smbc->uid);
55713498266Sopenharmony_ci  h->tid = smb_swap16(req->tid);
55813498266Sopenharmony_ci  pid = getpid();
55913498266Sopenharmony_ci  h->pid_high = smb_swap16((unsigned short)(pid >> 16));
56013498266Sopenharmony_ci  h->pid = smb_swap16((unsigned short) pid);
56113498266Sopenharmony_ci}
56213498266Sopenharmony_ci
56313498266Sopenharmony_cistatic CURLcode smb_send(struct Curl_easy *data, ssize_t len,
56413498266Sopenharmony_ci                         size_t upload_size)
56513498266Sopenharmony_ci{
56613498266Sopenharmony_ci  struct connectdata *conn = data->conn;
56713498266Sopenharmony_ci  struct smb_conn *smbc = &conn->proto.smbc;
56813498266Sopenharmony_ci  ssize_t bytes_written;
56913498266Sopenharmony_ci  CURLcode result;
57013498266Sopenharmony_ci
57113498266Sopenharmony_ci  result = Curl_nwrite(data, FIRSTSOCKET, data->state.ulbuf,
57213498266Sopenharmony_ci                      len, &bytes_written);
57313498266Sopenharmony_ci  if(result)
57413498266Sopenharmony_ci    return result;
57513498266Sopenharmony_ci
57613498266Sopenharmony_ci  if(bytes_written != len) {
57713498266Sopenharmony_ci    smbc->send_size = len;
57813498266Sopenharmony_ci    smbc->sent = bytes_written;
57913498266Sopenharmony_ci  }
58013498266Sopenharmony_ci
58113498266Sopenharmony_ci  smbc->upload_size = upload_size;
58213498266Sopenharmony_ci
58313498266Sopenharmony_ci  return CURLE_OK;
58413498266Sopenharmony_ci}
58513498266Sopenharmony_ci
58613498266Sopenharmony_cistatic CURLcode smb_flush(struct Curl_easy *data)
58713498266Sopenharmony_ci{
58813498266Sopenharmony_ci  struct connectdata *conn = data->conn;
58913498266Sopenharmony_ci  struct smb_conn *smbc = &conn->proto.smbc;
59013498266Sopenharmony_ci  ssize_t bytes_written;
59113498266Sopenharmony_ci  ssize_t len = smbc->send_size - smbc->sent;
59213498266Sopenharmony_ci  CURLcode result;
59313498266Sopenharmony_ci
59413498266Sopenharmony_ci  if(!smbc->send_size)
59513498266Sopenharmony_ci    return CURLE_OK;
59613498266Sopenharmony_ci
59713498266Sopenharmony_ci  result = Curl_nwrite(data, FIRSTSOCKET,
59813498266Sopenharmony_ci                       data->state.ulbuf + smbc->sent,
59913498266Sopenharmony_ci                       len, &bytes_written);
60013498266Sopenharmony_ci  if(result)
60113498266Sopenharmony_ci    return result;
60213498266Sopenharmony_ci
60313498266Sopenharmony_ci  if(bytes_written != len)
60413498266Sopenharmony_ci    smbc->sent += bytes_written;
60513498266Sopenharmony_ci  else
60613498266Sopenharmony_ci    smbc->send_size = 0;
60713498266Sopenharmony_ci
60813498266Sopenharmony_ci  return CURLE_OK;
60913498266Sopenharmony_ci}
61013498266Sopenharmony_ci
61113498266Sopenharmony_cistatic CURLcode smb_send_message(struct Curl_easy *data, unsigned char cmd,
61213498266Sopenharmony_ci                                 const void *msg, size_t msg_len)
61313498266Sopenharmony_ci{
61413498266Sopenharmony_ci  CURLcode result = Curl_get_upload_buffer(data);
61513498266Sopenharmony_ci  if(result)
61613498266Sopenharmony_ci    return result;
61713498266Sopenharmony_ci  smb_format_message(data, (struct smb_header *)data->state.ulbuf,
61813498266Sopenharmony_ci                     cmd, msg_len);
61913498266Sopenharmony_ci  memcpy(data->state.ulbuf + sizeof(struct smb_header),
62013498266Sopenharmony_ci         msg, msg_len);
62113498266Sopenharmony_ci
62213498266Sopenharmony_ci  return smb_send(data, sizeof(struct smb_header) + msg_len, 0);
62313498266Sopenharmony_ci}
62413498266Sopenharmony_ci
62513498266Sopenharmony_cistatic CURLcode smb_send_negotiate(struct Curl_easy *data)
62613498266Sopenharmony_ci{
62713498266Sopenharmony_ci  const char *msg = "\x00\x0c\x00\x02NT LM 0.12";
62813498266Sopenharmony_ci
62913498266Sopenharmony_ci  return smb_send_message(data, SMB_COM_NEGOTIATE, msg, 15);
63013498266Sopenharmony_ci}
63113498266Sopenharmony_ci
63213498266Sopenharmony_cistatic CURLcode smb_send_setup(struct Curl_easy *data)
63313498266Sopenharmony_ci{
63413498266Sopenharmony_ci  struct connectdata *conn = data->conn;
63513498266Sopenharmony_ci  struct smb_conn *smbc = &conn->proto.smbc;
63613498266Sopenharmony_ci  struct smb_setup msg;
63713498266Sopenharmony_ci  char *p = msg.bytes;
63813498266Sopenharmony_ci  unsigned char lm_hash[21];
63913498266Sopenharmony_ci  unsigned char lm[24];
64013498266Sopenharmony_ci  unsigned char nt_hash[21];
64113498266Sopenharmony_ci  unsigned char nt[24];
64213498266Sopenharmony_ci
64313498266Sopenharmony_ci  size_t byte_count = sizeof(lm) + sizeof(nt);
64413498266Sopenharmony_ci  byte_count += strlen(smbc->user) + strlen(smbc->domain);
64513498266Sopenharmony_ci  byte_count += strlen(OS) + strlen(CLIENTNAME) + 4; /* 4 null chars */
64613498266Sopenharmony_ci  if(byte_count > sizeof(msg.bytes))
64713498266Sopenharmony_ci    return CURLE_FILESIZE_EXCEEDED;
64813498266Sopenharmony_ci
64913498266Sopenharmony_ci  Curl_ntlm_core_mk_lm_hash(conn->passwd, lm_hash);
65013498266Sopenharmony_ci  Curl_ntlm_core_lm_resp(lm_hash, smbc->challenge, lm);
65113498266Sopenharmony_ci  Curl_ntlm_core_mk_nt_hash(conn->passwd, nt_hash);
65213498266Sopenharmony_ci  Curl_ntlm_core_lm_resp(nt_hash, smbc->challenge, nt);
65313498266Sopenharmony_ci
65413498266Sopenharmony_ci  memset(&msg, 0, sizeof(msg));
65513498266Sopenharmony_ci  msg.word_count = SMB_WC_SETUP_ANDX;
65613498266Sopenharmony_ci  msg.andx.command = SMB_COM_NO_ANDX_COMMAND;
65713498266Sopenharmony_ci  msg.max_buffer_size = smb_swap16(MAX_MESSAGE_SIZE);
65813498266Sopenharmony_ci  msg.max_mpx_count = smb_swap16(1);
65913498266Sopenharmony_ci  msg.vc_number = smb_swap16(1);
66013498266Sopenharmony_ci  msg.session_key = smb_swap32(smbc->session_key);
66113498266Sopenharmony_ci  msg.capabilities = smb_swap32(SMB_CAP_LARGE_FILES);
66213498266Sopenharmony_ci  msg.lengths[0] = smb_swap16(sizeof(lm));
66313498266Sopenharmony_ci  msg.lengths[1] = smb_swap16(sizeof(nt));
66413498266Sopenharmony_ci  memcpy(p, lm, sizeof(lm));
66513498266Sopenharmony_ci  p += sizeof(lm);
66613498266Sopenharmony_ci  memcpy(p, nt, sizeof(nt));
66713498266Sopenharmony_ci  p += sizeof(nt);
66813498266Sopenharmony_ci  MSGCATNULL(smbc->user);
66913498266Sopenharmony_ci  MSGCATNULL(smbc->domain);
67013498266Sopenharmony_ci  MSGCATNULL(OS);
67113498266Sopenharmony_ci  MSGCATNULL(CLIENTNAME);
67213498266Sopenharmony_ci  byte_count = p - msg.bytes;
67313498266Sopenharmony_ci  msg.byte_count = smb_swap16((unsigned short)byte_count);
67413498266Sopenharmony_ci
67513498266Sopenharmony_ci  return smb_send_message(data, SMB_COM_SETUP_ANDX, &msg,
67613498266Sopenharmony_ci                          sizeof(msg) - sizeof(msg.bytes) + byte_count);
67713498266Sopenharmony_ci}
67813498266Sopenharmony_ci
67913498266Sopenharmony_cistatic CURLcode smb_send_tree_connect(struct Curl_easy *data)
68013498266Sopenharmony_ci{
68113498266Sopenharmony_ci  struct smb_tree_connect msg;
68213498266Sopenharmony_ci  struct connectdata *conn = data->conn;
68313498266Sopenharmony_ci  struct smb_conn *smbc = &conn->proto.smbc;
68413498266Sopenharmony_ci  char *p = msg.bytes;
68513498266Sopenharmony_ci
68613498266Sopenharmony_ci  size_t byte_count = strlen(conn->host.name) + strlen(smbc->share);
68713498266Sopenharmony_ci  byte_count += strlen(SERVICENAME) + 5; /* 2 nulls and 3 backslashes */
68813498266Sopenharmony_ci  if(byte_count > sizeof(msg.bytes))
68913498266Sopenharmony_ci    return CURLE_FILESIZE_EXCEEDED;
69013498266Sopenharmony_ci
69113498266Sopenharmony_ci  memset(&msg, 0, sizeof(msg));
69213498266Sopenharmony_ci  msg.word_count = SMB_WC_TREE_CONNECT_ANDX;
69313498266Sopenharmony_ci  msg.andx.command = SMB_COM_NO_ANDX_COMMAND;
69413498266Sopenharmony_ci  msg.pw_len = 0;
69513498266Sopenharmony_ci  MSGCAT("\\\\");
69613498266Sopenharmony_ci  MSGCAT(conn->host.name);
69713498266Sopenharmony_ci  MSGCAT("\\");
69813498266Sopenharmony_ci  MSGCATNULL(smbc->share);
69913498266Sopenharmony_ci  MSGCATNULL(SERVICENAME); /* Match any type of service */
70013498266Sopenharmony_ci  byte_count = p - msg.bytes;
70113498266Sopenharmony_ci  msg.byte_count = smb_swap16((unsigned short)byte_count);
70213498266Sopenharmony_ci
70313498266Sopenharmony_ci  return smb_send_message(data, SMB_COM_TREE_CONNECT_ANDX, &msg,
70413498266Sopenharmony_ci                          sizeof(msg) - sizeof(msg.bytes) + byte_count);
70513498266Sopenharmony_ci}
70613498266Sopenharmony_ci
70713498266Sopenharmony_cistatic CURLcode smb_send_open(struct Curl_easy *data)
70813498266Sopenharmony_ci{
70913498266Sopenharmony_ci  struct smb_request *req = data->req.p.smb;
71013498266Sopenharmony_ci  struct smb_nt_create msg;
71113498266Sopenharmony_ci  size_t byte_count;
71213498266Sopenharmony_ci
71313498266Sopenharmony_ci  if((strlen(req->path) + 1) > sizeof(msg.bytes))
71413498266Sopenharmony_ci    return CURLE_FILESIZE_EXCEEDED;
71513498266Sopenharmony_ci
71613498266Sopenharmony_ci  memset(&msg, 0, sizeof(msg));
71713498266Sopenharmony_ci  msg.word_count = SMB_WC_NT_CREATE_ANDX;
71813498266Sopenharmony_ci  msg.andx.command = SMB_COM_NO_ANDX_COMMAND;
71913498266Sopenharmony_ci  byte_count = strlen(req->path);
72013498266Sopenharmony_ci  msg.name_length = smb_swap16((unsigned short)byte_count);
72113498266Sopenharmony_ci  msg.share_access = smb_swap32(SMB_FILE_SHARE_ALL);
72213498266Sopenharmony_ci  if(data->state.upload) {
72313498266Sopenharmony_ci    msg.access = smb_swap32(SMB_GENERIC_READ | SMB_GENERIC_WRITE);
72413498266Sopenharmony_ci    msg.create_disposition = smb_swap32(SMB_FILE_OVERWRITE_IF);
72513498266Sopenharmony_ci  }
72613498266Sopenharmony_ci  else {
72713498266Sopenharmony_ci    msg.access = smb_swap32(SMB_GENERIC_READ);
72813498266Sopenharmony_ci    msg.create_disposition = smb_swap32(SMB_FILE_OPEN);
72913498266Sopenharmony_ci  }
73013498266Sopenharmony_ci  msg.byte_count = smb_swap16((unsigned short) ++byte_count);
73113498266Sopenharmony_ci  strcpy(msg.bytes, req->path);
73213498266Sopenharmony_ci
73313498266Sopenharmony_ci  return smb_send_message(data, SMB_COM_NT_CREATE_ANDX, &msg,
73413498266Sopenharmony_ci                          sizeof(msg) - sizeof(msg.bytes) + byte_count);
73513498266Sopenharmony_ci}
73613498266Sopenharmony_ci
73713498266Sopenharmony_cistatic CURLcode smb_send_close(struct Curl_easy *data)
73813498266Sopenharmony_ci{
73913498266Sopenharmony_ci  struct smb_request *req = data->req.p.smb;
74013498266Sopenharmony_ci  struct smb_close msg;
74113498266Sopenharmony_ci
74213498266Sopenharmony_ci  memset(&msg, 0, sizeof(msg));
74313498266Sopenharmony_ci  msg.word_count = SMB_WC_CLOSE;
74413498266Sopenharmony_ci  msg.fid = smb_swap16(req->fid);
74513498266Sopenharmony_ci
74613498266Sopenharmony_ci  return smb_send_message(data, SMB_COM_CLOSE, &msg, sizeof(msg));
74713498266Sopenharmony_ci}
74813498266Sopenharmony_ci
74913498266Sopenharmony_cistatic CURLcode smb_send_tree_disconnect(struct Curl_easy *data)
75013498266Sopenharmony_ci{
75113498266Sopenharmony_ci  struct smb_tree_disconnect msg;
75213498266Sopenharmony_ci
75313498266Sopenharmony_ci  memset(&msg, 0, sizeof(msg));
75413498266Sopenharmony_ci
75513498266Sopenharmony_ci  return smb_send_message(data, SMB_COM_TREE_DISCONNECT, &msg, sizeof(msg));
75613498266Sopenharmony_ci}
75713498266Sopenharmony_ci
75813498266Sopenharmony_cistatic CURLcode smb_send_read(struct Curl_easy *data)
75913498266Sopenharmony_ci{
76013498266Sopenharmony_ci  struct smb_request *req = data->req.p.smb;
76113498266Sopenharmony_ci  curl_off_t offset = data->req.offset;
76213498266Sopenharmony_ci  struct smb_read msg;
76313498266Sopenharmony_ci
76413498266Sopenharmony_ci  memset(&msg, 0, sizeof(msg));
76513498266Sopenharmony_ci  msg.word_count = SMB_WC_READ_ANDX;
76613498266Sopenharmony_ci  msg.andx.command = SMB_COM_NO_ANDX_COMMAND;
76713498266Sopenharmony_ci  msg.fid = smb_swap16(req->fid);
76813498266Sopenharmony_ci  msg.offset = smb_swap32((unsigned int) offset);
76913498266Sopenharmony_ci  msg.offset_high = smb_swap32((unsigned int) (offset >> 32));
77013498266Sopenharmony_ci  msg.min_bytes = smb_swap16(MAX_PAYLOAD_SIZE);
77113498266Sopenharmony_ci  msg.max_bytes = smb_swap16(MAX_PAYLOAD_SIZE);
77213498266Sopenharmony_ci
77313498266Sopenharmony_ci  return smb_send_message(data, SMB_COM_READ_ANDX, &msg, sizeof(msg));
77413498266Sopenharmony_ci}
77513498266Sopenharmony_ci
77613498266Sopenharmony_cistatic CURLcode smb_send_write(struct Curl_easy *data)
77713498266Sopenharmony_ci{
77813498266Sopenharmony_ci  struct smb_write *msg;
77913498266Sopenharmony_ci  struct smb_request *req = data->req.p.smb;
78013498266Sopenharmony_ci  curl_off_t offset = data->req.offset;
78113498266Sopenharmony_ci  curl_off_t upload_size = data->req.size - data->req.bytecount;
78213498266Sopenharmony_ci  CURLcode result = Curl_get_upload_buffer(data);
78313498266Sopenharmony_ci  if(result)
78413498266Sopenharmony_ci    return result;
78513498266Sopenharmony_ci  msg = (struct smb_write *)data->state.ulbuf;
78613498266Sopenharmony_ci
78713498266Sopenharmony_ci  if(upload_size >= MAX_PAYLOAD_SIZE - 1) /* There is one byte of padding */
78813498266Sopenharmony_ci    upload_size = MAX_PAYLOAD_SIZE - 1;
78913498266Sopenharmony_ci
79013498266Sopenharmony_ci  memset(msg, 0, sizeof(*msg));
79113498266Sopenharmony_ci  msg->word_count = SMB_WC_WRITE_ANDX;
79213498266Sopenharmony_ci  msg->andx.command = SMB_COM_NO_ANDX_COMMAND;
79313498266Sopenharmony_ci  msg->fid = smb_swap16(req->fid);
79413498266Sopenharmony_ci  msg->offset = smb_swap32((unsigned int) offset);
79513498266Sopenharmony_ci  msg->offset_high = smb_swap32((unsigned int) (offset >> 32));
79613498266Sopenharmony_ci  msg->data_length = smb_swap16((unsigned short) upload_size);
79713498266Sopenharmony_ci  msg->data_offset = smb_swap16(sizeof(*msg) - sizeof(unsigned int));
79813498266Sopenharmony_ci  msg->byte_count = smb_swap16((unsigned short) (upload_size + 1));
79913498266Sopenharmony_ci
80013498266Sopenharmony_ci  smb_format_message(data, &msg->h, SMB_COM_WRITE_ANDX,
80113498266Sopenharmony_ci                     sizeof(*msg) - sizeof(msg->h) + (size_t) upload_size);
80213498266Sopenharmony_ci
80313498266Sopenharmony_ci  return smb_send(data, sizeof(*msg), (size_t) upload_size);
80413498266Sopenharmony_ci}
80513498266Sopenharmony_ci
80613498266Sopenharmony_cistatic CURLcode smb_send_and_recv(struct Curl_easy *data, void **msg)
80713498266Sopenharmony_ci{
80813498266Sopenharmony_ci  struct connectdata *conn = data->conn;
80913498266Sopenharmony_ci  struct smb_conn *smbc = &conn->proto.smbc;
81013498266Sopenharmony_ci  CURLcode result;
81113498266Sopenharmony_ci  *msg = NULL; /* if it returns early */
81213498266Sopenharmony_ci
81313498266Sopenharmony_ci  /* Check if there is data in the transfer buffer */
81413498266Sopenharmony_ci  if(!smbc->send_size && smbc->upload_size) {
81513498266Sopenharmony_ci    size_t nread = smbc->upload_size > (size_t)data->set.upload_buffer_size ?
81613498266Sopenharmony_ci      (size_t)data->set.upload_buffer_size : smbc->upload_size;
81713498266Sopenharmony_ci    data->req.upload_fromhere = data->state.ulbuf;
81813498266Sopenharmony_ci    result = Curl_fillreadbuffer(data, nread, &nread);
81913498266Sopenharmony_ci    if(result && result != CURLE_AGAIN)
82013498266Sopenharmony_ci      return result;
82113498266Sopenharmony_ci    if(!nread)
82213498266Sopenharmony_ci      return CURLE_OK;
82313498266Sopenharmony_ci
82413498266Sopenharmony_ci    smbc->upload_size -= nread;
82513498266Sopenharmony_ci    smbc->send_size = nread;
82613498266Sopenharmony_ci    smbc->sent = 0;
82713498266Sopenharmony_ci  }
82813498266Sopenharmony_ci
82913498266Sopenharmony_ci  /* Check if there is data to send */
83013498266Sopenharmony_ci  if(smbc->send_size) {
83113498266Sopenharmony_ci    result = smb_flush(data);
83213498266Sopenharmony_ci    if(result)
83313498266Sopenharmony_ci      return result;
83413498266Sopenharmony_ci  }
83513498266Sopenharmony_ci
83613498266Sopenharmony_ci  /* Check if there is still data to be sent */
83713498266Sopenharmony_ci  if(smbc->send_size || smbc->upload_size)
83813498266Sopenharmony_ci    return CURLE_AGAIN;
83913498266Sopenharmony_ci
84013498266Sopenharmony_ci  return smb_recv_message(data, msg);
84113498266Sopenharmony_ci}
84213498266Sopenharmony_ci
84313498266Sopenharmony_cistatic CURLcode smb_connection_state(struct Curl_easy *data, bool *done)
84413498266Sopenharmony_ci{
84513498266Sopenharmony_ci  struct connectdata *conn = data->conn;
84613498266Sopenharmony_ci  struct smb_conn *smbc = &conn->proto.smbc;
84713498266Sopenharmony_ci  struct smb_negotiate_response *nrsp;
84813498266Sopenharmony_ci  struct smb_header *h;
84913498266Sopenharmony_ci  CURLcode result;
85013498266Sopenharmony_ci  void *msg = NULL;
85113498266Sopenharmony_ci
85213498266Sopenharmony_ci  if(smbc->state == SMB_CONNECTING) {
85313498266Sopenharmony_ci#ifdef USE_SSL
85413498266Sopenharmony_ci    if((conn->handler->flags & PROTOPT_SSL)) {
85513498266Sopenharmony_ci      bool ssl_done = FALSE;
85613498266Sopenharmony_ci      result = Curl_conn_connect(data, FIRSTSOCKET, FALSE, &ssl_done);
85713498266Sopenharmony_ci      if(result && result != CURLE_AGAIN)
85813498266Sopenharmony_ci        return result;
85913498266Sopenharmony_ci      if(!ssl_done)
86013498266Sopenharmony_ci        return CURLE_OK;
86113498266Sopenharmony_ci    }
86213498266Sopenharmony_ci#endif
86313498266Sopenharmony_ci
86413498266Sopenharmony_ci    result = smb_send_negotiate(data);
86513498266Sopenharmony_ci    if(result) {
86613498266Sopenharmony_ci      connclose(conn, "SMB: failed to send negotiate message");
86713498266Sopenharmony_ci      return result;
86813498266Sopenharmony_ci    }
86913498266Sopenharmony_ci
87013498266Sopenharmony_ci    conn_state(data, SMB_NEGOTIATE);
87113498266Sopenharmony_ci  }
87213498266Sopenharmony_ci
87313498266Sopenharmony_ci  /* Send the previous message and check for a response */
87413498266Sopenharmony_ci  result = smb_send_and_recv(data, &msg);
87513498266Sopenharmony_ci  if(result && result != CURLE_AGAIN) {
87613498266Sopenharmony_ci    connclose(conn, "SMB: failed to communicate");
87713498266Sopenharmony_ci    return result;
87813498266Sopenharmony_ci  }
87913498266Sopenharmony_ci
88013498266Sopenharmony_ci  if(!msg)
88113498266Sopenharmony_ci    return CURLE_OK;
88213498266Sopenharmony_ci
88313498266Sopenharmony_ci  h = msg;
88413498266Sopenharmony_ci
88513498266Sopenharmony_ci  switch(smbc->state) {
88613498266Sopenharmony_ci  case SMB_NEGOTIATE:
88713498266Sopenharmony_ci    if((smbc->got < sizeof(*nrsp) + sizeof(smbc->challenge) - 1) ||
88813498266Sopenharmony_ci       h->status) {
88913498266Sopenharmony_ci      connclose(conn, "SMB: negotiation failed");
89013498266Sopenharmony_ci      return CURLE_COULDNT_CONNECT;
89113498266Sopenharmony_ci    }
89213498266Sopenharmony_ci    nrsp = msg;
89313498266Sopenharmony_ci    memcpy(smbc->challenge, nrsp->bytes, sizeof(smbc->challenge));
89413498266Sopenharmony_ci    smbc->session_key = smb_swap32(nrsp->session_key);
89513498266Sopenharmony_ci    result = smb_send_setup(data);
89613498266Sopenharmony_ci    if(result) {
89713498266Sopenharmony_ci      connclose(conn, "SMB: failed to send setup message");
89813498266Sopenharmony_ci      return result;
89913498266Sopenharmony_ci    }
90013498266Sopenharmony_ci    conn_state(data, SMB_SETUP);
90113498266Sopenharmony_ci    break;
90213498266Sopenharmony_ci
90313498266Sopenharmony_ci  case SMB_SETUP:
90413498266Sopenharmony_ci    if(h->status) {
90513498266Sopenharmony_ci      connclose(conn, "SMB: authentication failed");
90613498266Sopenharmony_ci      return CURLE_LOGIN_DENIED;
90713498266Sopenharmony_ci    }
90813498266Sopenharmony_ci    smbc->uid = smb_swap16(h->uid);
90913498266Sopenharmony_ci    conn_state(data, SMB_CONNECTED);
91013498266Sopenharmony_ci    *done = true;
91113498266Sopenharmony_ci    break;
91213498266Sopenharmony_ci
91313498266Sopenharmony_ci  default:
91413498266Sopenharmony_ci    smb_pop_message(conn);
91513498266Sopenharmony_ci    return CURLE_OK; /* ignore */
91613498266Sopenharmony_ci  }
91713498266Sopenharmony_ci
91813498266Sopenharmony_ci  smb_pop_message(conn);
91913498266Sopenharmony_ci
92013498266Sopenharmony_ci  return CURLE_OK;
92113498266Sopenharmony_ci}
92213498266Sopenharmony_ci
92313498266Sopenharmony_ci/*
92413498266Sopenharmony_ci * Convert a timestamp from the Windows world (100 nsec units from 1 Jan 1601)
92513498266Sopenharmony_ci * to Posix time. Cap the output to fit within a time_t.
92613498266Sopenharmony_ci */
92713498266Sopenharmony_cistatic void get_posix_time(time_t *out, curl_off_t timestamp)
92813498266Sopenharmony_ci{
92913498266Sopenharmony_ci  timestamp -= 116444736000000000;
93013498266Sopenharmony_ci  timestamp /= 10000000;
93113498266Sopenharmony_ci#if SIZEOF_TIME_T < SIZEOF_CURL_OFF_T
93213498266Sopenharmony_ci  if(timestamp > TIME_T_MAX)
93313498266Sopenharmony_ci    *out = TIME_T_MAX;
93413498266Sopenharmony_ci  else if(timestamp < TIME_T_MIN)
93513498266Sopenharmony_ci    *out = TIME_T_MIN;
93613498266Sopenharmony_ci  else
93713498266Sopenharmony_ci#endif
93813498266Sopenharmony_ci    *out = (time_t) timestamp;
93913498266Sopenharmony_ci}
94013498266Sopenharmony_ci
94113498266Sopenharmony_cistatic CURLcode smb_request_state(struct Curl_easy *data, bool *done)
94213498266Sopenharmony_ci{
94313498266Sopenharmony_ci  struct connectdata *conn = data->conn;
94413498266Sopenharmony_ci  struct smb_request *req = data->req.p.smb;
94513498266Sopenharmony_ci  struct smb_header *h;
94613498266Sopenharmony_ci  struct smb_conn *smbc = &conn->proto.smbc;
94713498266Sopenharmony_ci  enum smb_req_state next_state = SMB_DONE;
94813498266Sopenharmony_ci  unsigned short len;
94913498266Sopenharmony_ci  unsigned short off;
95013498266Sopenharmony_ci  CURLcode result;
95113498266Sopenharmony_ci  void *msg = NULL;
95213498266Sopenharmony_ci  const struct smb_nt_create_response *smb_m;
95313498266Sopenharmony_ci
95413498266Sopenharmony_ci  if(data->state.upload && (data->state.infilesize < 0)) {
95513498266Sopenharmony_ci    failf(data, "SMB upload needs to know the size up front");
95613498266Sopenharmony_ci    return CURLE_SEND_ERROR;
95713498266Sopenharmony_ci  }
95813498266Sopenharmony_ci
95913498266Sopenharmony_ci  /* Start the request */
96013498266Sopenharmony_ci  if(req->state == SMB_REQUESTING) {
96113498266Sopenharmony_ci    result = smb_send_tree_connect(data);
96213498266Sopenharmony_ci    if(result) {
96313498266Sopenharmony_ci      connclose(conn, "SMB: failed to send tree connect message");
96413498266Sopenharmony_ci      return result;
96513498266Sopenharmony_ci    }
96613498266Sopenharmony_ci
96713498266Sopenharmony_ci    request_state(data, SMB_TREE_CONNECT);
96813498266Sopenharmony_ci  }
96913498266Sopenharmony_ci
97013498266Sopenharmony_ci  /* Send the previous message and check for a response */
97113498266Sopenharmony_ci  result = smb_send_and_recv(data, &msg);
97213498266Sopenharmony_ci  if(result && result != CURLE_AGAIN) {
97313498266Sopenharmony_ci    connclose(conn, "SMB: failed to communicate");
97413498266Sopenharmony_ci    return result;
97513498266Sopenharmony_ci  }
97613498266Sopenharmony_ci
97713498266Sopenharmony_ci  if(!msg)
97813498266Sopenharmony_ci    return CURLE_OK;
97913498266Sopenharmony_ci
98013498266Sopenharmony_ci  h = msg;
98113498266Sopenharmony_ci
98213498266Sopenharmony_ci  switch(req->state) {
98313498266Sopenharmony_ci  case SMB_TREE_CONNECT:
98413498266Sopenharmony_ci    if(h->status) {
98513498266Sopenharmony_ci      req->result = CURLE_REMOTE_FILE_NOT_FOUND;
98613498266Sopenharmony_ci      if(h->status == smb_swap32(SMB_ERR_NOACCESS))
98713498266Sopenharmony_ci        req->result = CURLE_REMOTE_ACCESS_DENIED;
98813498266Sopenharmony_ci      break;
98913498266Sopenharmony_ci    }
99013498266Sopenharmony_ci    req->tid = smb_swap16(h->tid);
99113498266Sopenharmony_ci    next_state = SMB_OPEN;
99213498266Sopenharmony_ci    break;
99313498266Sopenharmony_ci
99413498266Sopenharmony_ci  case SMB_OPEN:
99513498266Sopenharmony_ci    if(h->status || smbc->got < sizeof(struct smb_nt_create_response)) {
99613498266Sopenharmony_ci      req->result = CURLE_REMOTE_FILE_NOT_FOUND;
99713498266Sopenharmony_ci      if(h->status == smb_swap32(SMB_ERR_NOACCESS))
99813498266Sopenharmony_ci        req->result = CURLE_REMOTE_ACCESS_DENIED;
99913498266Sopenharmony_ci      next_state = SMB_TREE_DISCONNECT;
100013498266Sopenharmony_ci      break;
100113498266Sopenharmony_ci    }
100213498266Sopenharmony_ci    smb_m = (const struct smb_nt_create_response*) msg;
100313498266Sopenharmony_ci    req->fid = smb_swap16(smb_m->fid);
100413498266Sopenharmony_ci    data->req.offset = 0;
100513498266Sopenharmony_ci    if(data->state.upload) {
100613498266Sopenharmony_ci      data->req.size = data->state.infilesize;
100713498266Sopenharmony_ci      Curl_pgrsSetUploadSize(data, data->req.size);
100813498266Sopenharmony_ci      next_state = SMB_UPLOAD;
100913498266Sopenharmony_ci    }
101013498266Sopenharmony_ci    else {
101113498266Sopenharmony_ci      data->req.size = smb_swap64(smb_m->end_of_file);
101213498266Sopenharmony_ci      if(data->req.size < 0) {
101313498266Sopenharmony_ci        req->result = CURLE_WEIRD_SERVER_REPLY;
101413498266Sopenharmony_ci        next_state = SMB_CLOSE;
101513498266Sopenharmony_ci      }
101613498266Sopenharmony_ci      else {
101713498266Sopenharmony_ci        Curl_pgrsSetDownloadSize(data, data->req.size);
101813498266Sopenharmony_ci        if(data->set.get_filetime)
101913498266Sopenharmony_ci          get_posix_time(&data->info.filetime, smb_m->last_change_time);
102013498266Sopenharmony_ci        next_state = SMB_DOWNLOAD;
102113498266Sopenharmony_ci      }
102213498266Sopenharmony_ci    }
102313498266Sopenharmony_ci    break;
102413498266Sopenharmony_ci
102513498266Sopenharmony_ci  case SMB_DOWNLOAD:
102613498266Sopenharmony_ci    if(h->status || smbc->got < sizeof(struct smb_header) + 14) {
102713498266Sopenharmony_ci      req->result = CURLE_RECV_ERROR;
102813498266Sopenharmony_ci      next_state = SMB_CLOSE;
102913498266Sopenharmony_ci      break;
103013498266Sopenharmony_ci    }
103113498266Sopenharmony_ci    len = Curl_read16_le(((const unsigned char *) msg) +
103213498266Sopenharmony_ci                         sizeof(struct smb_header) + 11);
103313498266Sopenharmony_ci    off = Curl_read16_le(((const unsigned char *) msg) +
103413498266Sopenharmony_ci                         sizeof(struct smb_header) + 13);
103513498266Sopenharmony_ci    if(len > 0) {
103613498266Sopenharmony_ci      if(off + sizeof(unsigned int) + len > smbc->got) {
103713498266Sopenharmony_ci        failf(data, "Invalid input packet");
103813498266Sopenharmony_ci        result = CURLE_RECV_ERROR;
103913498266Sopenharmony_ci      }
104013498266Sopenharmony_ci      else
104113498266Sopenharmony_ci        result = Curl_client_write(data, CLIENTWRITE_BODY,
104213498266Sopenharmony_ci                                   (char *)msg + off + sizeof(unsigned int),
104313498266Sopenharmony_ci                                   len);
104413498266Sopenharmony_ci      if(result) {
104513498266Sopenharmony_ci        req->result = result;
104613498266Sopenharmony_ci        next_state = SMB_CLOSE;
104713498266Sopenharmony_ci        break;
104813498266Sopenharmony_ci      }
104913498266Sopenharmony_ci    }
105013498266Sopenharmony_ci    data->req.offset += len;
105113498266Sopenharmony_ci    next_state = (len < MAX_PAYLOAD_SIZE) ? SMB_CLOSE : SMB_DOWNLOAD;
105213498266Sopenharmony_ci    break;
105313498266Sopenharmony_ci
105413498266Sopenharmony_ci  case SMB_UPLOAD:
105513498266Sopenharmony_ci    if(h->status || smbc->got < sizeof(struct smb_header) + 6) {
105613498266Sopenharmony_ci      req->result = CURLE_UPLOAD_FAILED;
105713498266Sopenharmony_ci      next_state = SMB_CLOSE;
105813498266Sopenharmony_ci      break;
105913498266Sopenharmony_ci    }
106013498266Sopenharmony_ci    len = Curl_read16_le(((const unsigned char *) msg) +
106113498266Sopenharmony_ci                         sizeof(struct smb_header) + 5);
106213498266Sopenharmony_ci    data->req.bytecount += len;
106313498266Sopenharmony_ci    data->req.offset += len;
106413498266Sopenharmony_ci    Curl_pgrsSetUploadCounter(data, data->req.bytecount);
106513498266Sopenharmony_ci    if(data->req.bytecount >= data->req.size)
106613498266Sopenharmony_ci      next_state = SMB_CLOSE;
106713498266Sopenharmony_ci    else
106813498266Sopenharmony_ci      next_state = SMB_UPLOAD;
106913498266Sopenharmony_ci    break;
107013498266Sopenharmony_ci
107113498266Sopenharmony_ci  case SMB_CLOSE:
107213498266Sopenharmony_ci    /* We don't care if the close failed, proceed to tree disconnect anyway */
107313498266Sopenharmony_ci    next_state = SMB_TREE_DISCONNECT;
107413498266Sopenharmony_ci    break;
107513498266Sopenharmony_ci
107613498266Sopenharmony_ci  case SMB_TREE_DISCONNECT:
107713498266Sopenharmony_ci    next_state = SMB_DONE;
107813498266Sopenharmony_ci    break;
107913498266Sopenharmony_ci
108013498266Sopenharmony_ci  default:
108113498266Sopenharmony_ci    smb_pop_message(conn);
108213498266Sopenharmony_ci    return CURLE_OK; /* ignore */
108313498266Sopenharmony_ci  }
108413498266Sopenharmony_ci
108513498266Sopenharmony_ci  smb_pop_message(conn);
108613498266Sopenharmony_ci
108713498266Sopenharmony_ci  switch(next_state) {
108813498266Sopenharmony_ci  case SMB_OPEN:
108913498266Sopenharmony_ci    result = smb_send_open(data);
109013498266Sopenharmony_ci    break;
109113498266Sopenharmony_ci
109213498266Sopenharmony_ci  case SMB_DOWNLOAD:
109313498266Sopenharmony_ci    result = smb_send_read(data);
109413498266Sopenharmony_ci    break;
109513498266Sopenharmony_ci
109613498266Sopenharmony_ci  case SMB_UPLOAD:
109713498266Sopenharmony_ci    result = smb_send_write(data);
109813498266Sopenharmony_ci    break;
109913498266Sopenharmony_ci
110013498266Sopenharmony_ci  case SMB_CLOSE:
110113498266Sopenharmony_ci    result = smb_send_close(data);
110213498266Sopenharmony_ci    break;
110313498266Sopenharmony_ci
110413498266Sopenharmony_ci  case SMB_TREE_DISCONNECT:
110513498266Sopenharmony_ci    result = smb_send_tree_disconnect(data);
110613498266Sopenharmony_ci    break;
110713498266Sopenharmony_ci
110813498266Sopenharmony_ci  case SMB_DONE:
110913498266Sopenharmony_ci    result = req->result;
111013498266Sopenharmony_ci    *done = true;
111113498266Sopenharmony_ci    break;
111213498266Sopenharmony_ci
111313498266Sopenharmony_ci  default:
111413498266Sopenharmony_ci    break;
111513498266Sopenharmony_ci  }
111613498266Sopenharmony_ci
111713498266Sopenharmony_ci  if(result) {
111813498266Sopenharmony_ci    connclose(conn, "SMB: failed to send message");
111913498266Sopenharmony_ci    return result;
112013498266Sopenharmony_ci  }
112113498266Sopenharmony_ci
112213498266Sopenharmony_ci  request_state(data, next_state);
112313498266Sopenharmony_ci
112413498266Sopenharmony_ci  return CURLE_OK;
112513498266Sopenharmony_ci}
112613498266Sopenharmony_ci
112713498266Sopenharmony_cistatic CURLcode smb_disconnect(struct Curl_easy *data,
112813498266Sopenharmony_ci                               struct connectdata *conn, bool dead)
112913498266Sopenharmony_ci{
113013498266Sopenharmony_ci  struct smb_conn *smbc = &conn->proto.smbc;
113113498266Sopenharmony_ci  (void) dead;
113213498266Sopenharmony_ci  (void) data;
113313498266Sopenharmony_ci  Curl_safefree(smbc->share);
113413498266Sopenharmony_ci  Curl_safefree(smbc->domain);
113513498266Sopenharmony_ci  Curl_safefree(smbc->recv_buf);
113613498266Sopenharmony_ci  return CURLE_OK;
113713498266Sopenharmony_ci}
113813498266Sopenharmony_ci
113913498266Sopenharmony_cistatic int smb_getsock(struct Curl_easy *data,
114013498266Sopenharmony_ci                       struct connectdata *conn, curl_socket_t *socks)
114113498266Sopenharmony_ci{
114213498266Sopenharmony_ci  (void)data;
114313498266Sopenharmony_ci  socks[0] = conn->sock[FIRSTSOCKET];
114413498266Sopenharmony_ci  return GETSOCK_READSOCK(0) | GETSOCK_WRITESOCK(0);
114513498266Sopenharmony_ci}
114613498266Sopenharmony_ci
114713498266Sopenharmony_cistatic CURLcode smb_do(struct Curl_easy *data, bool *done)
114813498266Sopenharmony_ci{
114913498266Sopenharmony_ci  struct connectdata *conn = data->conn;
115013498266Sopenharmony_ci  struct smb_conn *smbc = &conn->proto.smbc;
115113498266Sopenharmony_ci
115213498266Sopenharmony_ci  *done = FALSE;
115313498266Sopenharmony_ci  if(smbc->share) {
115413498266Sopenharmony_ci    return CURLE_OK;
115513498266Sopenharmony_ci  }
115613498266Sopenharmony_ci  return CURLE_URL_MALFORMAT;
115713498266Sopenharmony_ci}
115813498266Sopenharmony_ci
115913498266Sopenharmony_cistatic CURLcode smb_parse_url_path(struct Curl_easy *data,
116013498266Sopenharmony_ci                                   struct connectdata *conn)
116113498266Sopenharmony_ci{
116213498266Sopenharmony_ci  struct smb_request *req = data->req.p.smb;
116313498266Sopenharmony_ci  struct smb_conn *smbc = &conn->proto.smbc;
116413498266Sopenharmony_ci  char *path;
116513498266Sopenharmony_ci  char *slash;
116613498266Sopenharmony_ci
116713498266Sopenharmony_ci  /* URL decode the path */
116813498266Sopenharmony_ci  CURLcode result = Curl_urldecode(data->state.up.path, 0, &path, NULL,
116913498266Sopenharmony_ci                                   REJECT_CTRL);
117013498266Sopenharmony_ci  if(result)
117113498266Sopenharmony_ci    return result;
117213498266Sopenharmony_ci
117313498266Sopenharmony_ci  /* Parse the path for the share */
117413498266Sopenharmony_ci  smbc->share = strdup((*path == '/' || *path == '\\') ? path + 1 : path);
117513498266Sopenharmony_ci  free(path);
117613498266Sopenharmony_ci  if(!smbc->share)
117713498266Sopenharmony_ci    return CURLE_OUT_OF_MEMORY;
117813498266Sopenharmony_ci
117913498266Sopenharmony_ci  slash = strchr(smbc->share, '/');
118013498266Sopenharmony_ci  if(!slash)
118113498266Sopenharmony_ci    slash = strchr(smbc->share, '\\');
118213498266Sopenharmony_ci
118313498266Sopenharmony_ci  /* The share must be present */
118413498266Sopenharmony_ci  if(!slash) {
118513498266Sopenharmony_ci    Curl_safefree(smbc->share);
118613498266Sopenharmony_ci    failf(data, "missing share in URL path for SMB");
118713498266Sopenharmony_ci    return CURLE_URL_MALFORMAT;
118813498266Sopenharmony_ci  }
118913498266Sopenharmony_ci
119013498266Sopenharmony_ci  /* Parse the path for the file path converting any forward slashes into
119113498266Sopenharmony_ci     backslashes */
119213498266Sopenharmony_ci  *slash++ = 0;
119313498266Sopenharmony_ci  req->path = slash;
119413498266Sopenharmony_ci
119513498266Sopenharmony_ci  for(; *slash; slash++) {
119613498266Sopenharmony_ci    if(*slash == '/')
119713498266Sopenharmony_ci      *slash = '\\';
119813498266Sopenharmony_ci  }
119913498266Sopenharmony_ci  return CURLE_OK;
120013498266Sopenharmony_ci}
120113498266Sopenharmony_ci
120213498266Sopenharmony_ci#endif /* CURL_DISABLE_SMB && USE_CURL_NTLM_CORE &&
120313498266Sopenharmony_ci          SIZEOF_CURL_OFF_T > 4 */
1204