[337] | 1 | /***************************************************************************
|
---|
| 2 | * _ _ ____ _
|
---|
| 3 | * Project ___| | | | _ \| |
|
---|
| 4 | * / __| | | | |_) | |
|
---|
| 5 | * | (__| |_| | _ <| |___
|
---|
| 6 | * \___|\___/|_| \_\_____|
|
---|
| 7 | *
|
---|
| 8 | * Copyright (C) 2014, Bill Nagel <wnagel@tycoint.com>, Exacq Technologies
|
---|
| 9 | * Copyright (C) 2016-2017, Daniel Stenberg, <daniel@haxx.se>, et al.
|
---|
| 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.haxx.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 | ***************************************************************************/
|
---|
| 23 |
|
---|
| 24 | #include "curl_setup.h"
|
---|
| 25 |
|
---|
| 26 | #if !defined(CURL_DISABLE_SMB) && defined(USE_NTLM) && \
|
---|
| 27 | (CURL_SIZEOF_CURL_OFF_T > 4)
|
---|
| 28 |
|
---|
| 29 | #if !defined(USE_WINDOWS_SSPI) || defined(USE_WIN32_CRYPTO)
|
---|
| 30 |
|
---|
| 31 | #define BUILDING_CURL_SMB_C
|
---|
| 32 |
|
---|
| 33 | #ifdef HAVE_PROCESS_H
|
---|
| 34 | #include <process.h>
|
---|
| 35 | #ifdef CURL_WINDOWS_APP
|
---|
| 36 | #define getpid GetCurrentProcessId
|
---|
| 37 | #elif !defined(MSDOS)
|
---|
| 38 | #define getpid _getpid
|
---|
| 39 | #endif
|
---|
| 40 | #endif
|
---|
| 41 |
|
---|
| 42 | #include "smb.h"
|
---|
| 43 | #include "urldata.h"
|
---|
| 44 | #include "sendf.h"
|
---|
| 45 | #include "multiif.h"
|
---|
| 46 | #include "connect.h"
|
---|
| 47 | #include "progress.h"
|
---|
| 48 | #include "transfer.h"
|
---|
| 49 | #include "vtls/vtls.h"
|
---|
| 50 | #include "curl_ntlm_core.h"
|
---|
| 51 | #include "escape.h"
|
---|
| 52 | #include "curl_endian.h"
|
---|
| 53 |
|
---|
| 54 | /* The last #include files should be: */
|
---|
| 55 | #include "curl_memory.h"
|
---|
| 56 | #include "memdebug.h"
|
---|
| 57 |
|
---|
| 58 | /* Local API functions */
|
---|
| 59 | static CURLcode smb_setup_connection(struct connectdata *conn);
|
---|
| 60 | static CURLcode smb_connect(struct connectdata *conn, bool *done);
|
---|
| 61 | static CURLcode smb_connection_state(struct connectdata *conn, bool *done);
|
---|
| 62 | static CURLcode smb_request_state(struct connectdata *conn, bool *done);
|
---|
| 63 | static CURLcode smb_done(struct connectdata *conn, CURLcode status,
|
---|
| 64 | bool premature);
|
---|
| 65 | static CURLcode smb_disconnect(struct connectdata *conn, bool dead);
|
---|
| 66 | static int smb_getsock(struct connectdata *conn, curl_socket_t *socks,
|
---|
| 67 | int numsocks);
|
---|
| 68 | static CURLcode smb_parse_url_path(struct connectdata *conn);
|
---|
| 69 |
|
---|
| 70 | /*
|
---|
| 71 | * SMB handler interface
|
---|
| 72 | */
|
---|
| 73 | const struct Curl_handler Curl_handler_smb = {
|
---|
| 74 | "SMB", /* scheme */
|
---|
| 75 | smb_setup_connection, /* setup_connection */
|
---|
| 76 | ZERO_NULL, /* do_it */
|
---|
| 77 | smb_done, /* done */
|
---|
| 78 | ZERO_NULL, /* do_more */
|
---|
| 79 | smb_connect, /* connect_it */
|
---|
| 80 | smb_connection_state, /* connecting */
|
---|
| 81 | smb_request_state, /* doing */
|
---|
| 82 | smb_getsock, /* proto_getsock */
|
---|
| 83 | smb_getsock, /* doing_getsock */
|
---|
| 84 | ZERO_NULL, /* domore_getsock */
|
---|
| 85 | ZERO_NULL, /* perform_getsock */
|
---|
| 86 | smb_disconnect, /* disconnect */
|
---|
| 87 | ZERO_NULL, /* readwrite */
|
---|
| 88 | ZERO_NULL, /* connection_check */
|
---|
| 89 | PORT_SMB, /* defport */
|
---|
| 90 | CURLPROTO_SMB, /* protocol */
|
---|
| 91 | PROTOPT_NONE /* flags */
|
---|
| 92 | };
|
---|
| 93 |
|
---|
| 94 | #ifdef USE_SSL
|
---|
| 95 | /*
|
---|
| 96 | * SMBS handler interface
|
---|
| 97 | */
|
---|
| 98 | const struct Curl_handler Curl_handler_smbs = {
|
---|
| 99 | "SMBS", /* scheme */
|
---|
| 100 | smb_setup_connection, /* setup_connection */
|
---|
| 101 | ZERO_NULL, /* do_it */
|
---|
| 102 | smb_done, /* done */
|
---|
| 103 | ZERO_NULL, /* do_more */
|
---|
| 104 | smb_connect, /* connect_it */
|
---|
| 105 | smb_connection_state, /* connecting */
|
---|
| 106 | smb_request_state, /* doing */
|
---|
| 107 | smb_getsock, /* proto_getsock */
|
---|
| 108 | smb_getsock, /* doing_getsock */
|
---|
| 109 | ZERO_NULL, /* domore_getsock */
|
---|
| 110 | ZERO_NULL, /* perform_getsock */
|
---|
| 111 | smb_disconnect, /* disconnect */
|
---|
| 112 | ZERO_NULL, /* readwrite */
|
---|
| 113 | ZERO_NULL, /* connection_check */
|
---|
| 114 | PORT_SMBS, /* defport */
|
---|
| 115 | CURLPROTO_SMBS, /* protocol */
|
---|
| 116 | PROTOPT_SSL /* flags */
|
---|
| 117 | };
|
---|
| 118 | #endif
|
---|
| 119 |
|
---|
| 120 | #define MAX_PAYLOAD_SIZE 0x8000
|
---|
| 121 | #define MAX_MESSAGE_SIZE (MAX_PAYLOAD_SIZE + 0x1000)
|
---|
| 122 | #define CLIENTNAME "curl"
|
---|
| 123 | #define SERVICENAME "?????"
|
---|
| 124 |
|
---|
| 125 | /* Append a string to an SMB message */
|
---|
| 126 | #define MSGCAT(str) \
|
---|
| 127 | strcpy(p, (str)); \
|
---|
| 128 | p += strlen(str);
|
---|
| 129 |
|
---|
| 130 | /* Append a null-terminated string to an SMB message */
|
---|
| 131 | #define MSGCATNULL(str) \
|
---|
| 132 | strcpy(p, (str)); \
|
---|
| 133 | p += strlen(str) + 1;
|
---|
| 134 |
|
---|
| 135 | /* SMB is mostly little endian */
|
---|
| 136 | #if (defined(__BYTE_ORDER__) && __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__) || \
|
---|
| 137 | defined(__OS400__)
|
---|
| 138 | static unsigned short smb_swap16(unsigned short x)
|
---|
| 139 | {
|
---|
| 140 | return (unsigned short) ((x << 8) | ((x >> 8) & 0xff));
|
---|
| 141 | }
|
---|
| 142 |
|
---|
| 143 | static unsigned int smb_swap32(unsigned int x)
|
---|
| 144 | {
|
---|
| 145 | return (x << 24) | ((x << 8) & 0xff0000) | ((x >> 8) & 0xff00) |
|
---|
| 146 | ((x >> 24) & 0xff);
|
---|
| 147 | }
|
---|
| 148 |
|
---|
| 149 | #ifdef HAVE_LONGLONG
|
---|
| 150 | static unsigned long long smb_swap64(unsigned long long x)
|
---|
| 151 | {
|
---|
| 152 | return ((unsigned long long) smb_swap32((unsigned int) x) << 32) |
|
---|
| 153 | smb_swap32((unsigned int) (x >> 32));
|
---|
| 154 | }
|
---|
| 155 | #else
|
---|
| 156 | static unsigned __int64 smb_swap64(unsigned __int64 x)
|
---|
| 157 | {
|
---|
| 158 | return ((unsigned __int64) smb_swap32((unsigned int) x) << 32) |
|
---|
| 159 | smb_swap32((unsigned int) (x >> 32));
|
---|
| 160 | }
|
---|
| 161 | #endif
|
---|
| 162 | #else
|
---|
| 163 | # define smb_swap16(x) (x)
|
---|
| 164 | # define smb_swap32(x) (x)
|
---|
| 165 | # define smb_swap64(x) (x)
|
---|
| 166 | #endif
|
---|
| 167 |
|
---|
| 168 | /* SMB request state */
|
---|
| 169 | enum smb_req_state {
|
---|
| 170 | SMB_REQUESTING,
|
---|
| 171 | SMB_TREE_CONNECT,
|
---|
| 172 | SMB_OPEN,
|
---|
| 173 | SMB_DOWNLOAD,
|
---|
| 174 | SMB_UPLOAD,
|
---|
| 175 | SMB_CLOSE,
|
---|
| 176 | SMB_TREE_DISCONNECT,
|
---|
| 177 | SMB_DONE
|
---|
| 178 | };
|
---|
| 179 |
|
---|
| 180 | /* SMB request data */
|
---|
| 181 | struct smb_request {
|
---|
| 182 | enum smb_req_state state;
|
---|
| 183 | char *share;
|
---|
| 184 | char *path;
|
---|
| 185 | unsigned short tid; /* Even if we connect to the same tree as another */
|
---|
| 186 | unsigned short fid; /* request, the tid will be different */
|
---|
| 187 | CURLcode result;
|
---|
| 188 | };
|
---|
| 189 |
|
---|
| 190 | static void conn_state(struct connectdata *conn, enum smb_conn_state newstate)
|
---|
| 191 | {
|
---|
| 192 | struct smb_conn *smb = &conn->proto.smbc;
|
---|
| 193 | #if defined(DEBUGBUILD) && !defined(CURL_DISABLE_VERBOSE_STRINGS)
|
---|
| 194 | /* For debug purposes */
|
---|
| 195 | static const char * const names[] = {
|
---|
| 196 | "SMB_NOT_CONNECTED",
|
---|
| 197 | "SMB_CONNECTING",
|
---|
| 198 | "SMB_NEGOTIATE",
|
---|
| 199 | "SMB_SETUP",
|
---|
| 200 | "SMB_CONNECTED",
|
---|
| 201 | /* LAST */
|
---|
| 202 | };
|
---|
| 203 |
|
---|
| 204 | if(smb->state != newstate)
|
---|
| 205 | infof(conn->data, "SMB conn %p state change from %s to %s\n",
|
---|
| 206 | (void *)smb, names[smb->state], names[newstate]);
|
---|
| 207 | #endif
|
---|
| 208 |
|
---|
| 209 | smb->state = newstate;
|
---|
| 210 | }
|
---|
| 211 |
|
---|
| 212 | static void request_state(struct connectdata *conn,
|
---|
| 213 | enum smb_req_state newstate)
|
---|
| 214 | {
|
---|
| 215 | struct smb_request *req = conn->data->req.protop;
|
---|
| 216 | #if defined(DEBUGBUILD) && !defined(CURL_DISABLE_VERBOSE_STRINGS)
|
---|
| 217 | /* For debug purposes */
|
---|
| 218 | static const char * const names[] = {
|
---|
| 219 | "SMB_REQUESTING",
|
---|
| 220 | "SMB_TREE_CONNECT",
|
---|
| 221 | "SMB_OPEN",
|
---|
| 222 | "SMB_DOWNLOAD",
|
---|
| 223 | "SMB_UPLOAD",
|
---|
| 224 | "SMB_CLOSE",
|
---|
| 225 | "SMB_TREE_DISCONNECT",
|
---|
| 226 | "SMB_DONE",
|
---|
| 227 | /* LAST */
|
---|
| 228 | };
|
---|
| 229 |
|
---|
| 230 | if(req->state != newstate)
|
---|
| 231 | infof(conn->data, "SMB request %p state change from %s to %s\n",
|
---|
| 232 | (void *)req, names[req->state], names[newstate]);
|
---|
| 233 | #endif
|
---|
| 234 |
|
---|
| 235 | req->state = newstate;
|
---|
| 236 | }
|
---|
| 237 |
|
---|
| 238 | static CURLcode smb_setup_connection(struct connectdata *conn)
|
---|
| 239 | {
|
---|
| 240 | struct smb_request *req;
|
---|
| 241 |
|
---|
| 242 | /* Initialize the request state */
|
---|
| 243 | conn->data->req.protop = req = calloc(1, sizeof(struct smb_request));
|
---|
| 244 | if(!req)
|
---|
| 245 | return CURLE_OUT_OF_MEMORY;
|
---|
| 246 |
|
---|
| 247 | /* Parse the URL path */
|
---|
| 248 | return smb_parse_url_path(conn);
|
---|
| 249 | }
|
---|
| 250 |
|
---|
| 251 | static CURLcode smb_connect(struct connectdata *conn, bool *done)
|
---|
| 252 | {
|
---|
| 253 | struct smb_conn *smbc = &conn->proto.smbc;
|
---|
| 254 | char *slash;
|
---|
| 255 |
|
---|
| 256 | (void) done;
|
---|
| 257 |
|
---|
| 258 | /* Check we have a username and password to authenticate with */
|
---|
| 259 | if(!conn->bits.user_passwd)
|
---|
| 260 | return CURLE_LOGIN_DENIED;
|
---|
| 261 |
|
---|
| 262 | /* Initialize the connection state */
|
---|
| 263 | memset(smbc, 0, sizeof(*smbc));
|
---|
| 264 | smbc->state = SMB_CONNECTING;
|
---|
| 265 | smbc->recv_buf = malloc(MAX_MESSAGE_SIZE);
|
---|
| 266 | if(!smbc->recv_buf)
|
---|
| 267 | return CURLE_OUT_OF_MEMORY;
|
---|
| 268 |
|
---|
| 269 | /* Multiple requests are allowed with this connection */
|
---|
| 270 | connkeep(conn, "SMB default");
|
---|
| 271 |
|
---|
| 272 | /* Parse the username, domain, and password */
|
---|
| 273 | slash = strchr(conn->user, '/');
|
---|
| 274 | if(!slash)
|
---|
| 275 | slash = strchr(conn->user, '\\');
|
---|
| 276 |
|
---|
| 277 | if(slash) {
|
---|
| 278 | smbc->user = slash + 1;
|
---|
| 279 | smbc->domain = strdup(conn->user);
|
---|
| 280 | if(!smbc->domain)
|
---|
| 281 | return CURLE_OUT_OF_MEMORY;
|
---|
| 282 | smbc->domain[slash - conn->user] = 0;
|
---|
| 283 | }
|
---|
| 284 | else {
|
---|
| 285 | smbc->user = conn->user;
|
---|
| 286 | smbc->domain = strdup(conn->host.name);
|
---|
| 287 | if(!smbc->domain)
|
---|
| 288 | return CURLE_OUT_OF_MEMORY;
|
---|
| 289 | }
|
---|
| 290 |
|
---|
| 291 | return CURLE_OK;
|
---|
| 292 | }
|
---|
| 293 |
|
---|
| 294 | static CURLcode smb_recv_message(struct connectdata *conn, void **msg)
|
---|
| 295 | {
|
---|
| 296 | struct smb_conn *smbc = &conn->proto.smbc;
|
---|
| 297 | char *buf = smbc->recv_buf;
|
---|
| 298 | ssize_t bytes_read;
|
---|
| 299 | size_t nbt_size;
|
---|
| 300 | size_t msg_size;
|
---|
| 301 | size_t len = MAX_MESSAGE_SIZE - smbc->got;
|
---|
| 302 | CURLcode result;
|
---|
| 303 |
|
---|
| 304 | result = Curl_read(conn, FIRSTSOCKET, buf + smbc->got, len, &bytes_read);
|
---|
| 305 | if(result)
|
---|
| 306 | return result;
|
---|
| 307 |
|
---|
| 308 | if(!bytes_read)
|
---|
| 309 | return CURLE_OK;
|
---|
| 310 |
|
---|
| 311 | smbc->got += bytes_read;
|
---|
| 312 |
|
---|
| 313 | /* Check for a 32-bit nbt header */
|
---|
| 314 | if(smbc->got < sizeof(unsigned int))
|
---|
| 315 | return CURLE_OK;
|
---|
| 316 |
|
---|
| 317 | nbt_size = Curl_read16_be((const unsigned char *)
|
---|
| 318 | (buf + sizeof(unsigned short))) +
|
---|
| 319 | sizeof(unsigned int);
|
---|
| 320 | if(smbc->got < nbt_size)
|
---|
| 321 | return CURLE_OK;
|
---|
| 322 |
|
---|
| 323 | msg_size = sizeof(struct smb_header);
|
---|
| 324 | if(nbt_size >= msg_size + 1) {
|
---|
| 325 | /* Add the word count */
|
---|
| 326 | msg_size += 1 + ((unsigned char) buf[msg_size]) * sizeof(unsigned short);
|
---|
| 327 | if(nbt_size >= msg_size + sizeof(unsigned short)) {
|
---|
| 328 | /* Add the byte count */
|
---|
| 329 | msg_size += sizeof(unsigned short) +
|
---|
| 330 | Curl_read16_le((const unsigned char *)&buf[msg_size]);
|
---|
| 331 | if(nbt_size < msg_size)
|
---|
| 332 | return CURLE_READ_ERROR;
|
---|
| 333 | }
|
---|
| 334 | }
|
---|
| 335 |
|
---|
| 336 | *msg = buf;
|
---|
| 337 |
|
---|
| 338 | return CURLE_OK;
|
---|
| 339 | }
|
---|
| 340 |
|
---|
| 341 | static void smb_pop_message(struct connectdata *conn)
|
---|
| 342 | {
|
---|
| 343 | struct smb_conn *smbc = &conn->proto.smbc;
|
---|
| 344 |
|
---|
| 345 | smbc->got = 0;
|
---|
| 346 | }
|
---|
| 347 |
|
---|
| 348 | static void smb_format_message(struct connectdata *conn, struct smb_header *h,
|
---|
| 349 | unsigned char cmd, size_t len)
|
---|
| 350 | {
|
---|
| 351 | struct smb_conn *smbc = &conn->proto.smbc;
|
---|
| 352 | struct smb_request *req = conn->data->req.protop;
|
---|
| 353 | unsigned int pid;
|
---|
| 354 |
|
---|
| 355 | memset(h, 0, sizeof(*h));
|
---|
| 356 | h->nbt_length = htons((unsigned short) (sizeof(*h) - sizeof(unsigned int) +
|
---|
| 357 | len));
|
---|
| 358 | memcpy((char *)h->magic, "\xffSMB", 4);
|
---|
| 359 | h->command = cmd;
|
---|
| 360 | h->flags = SMB_FLAGS_CANONICAL_PATHNAMES | SMB_FLAGS_CASELESS_PATHNAMES;
|
---|
| 361 | h->flags2 = smb_swap16(SMB_FLAGS2_IS_LONG_NAME | SMB_FLAGS2_KNOWS_LONG_NAME);
|
---|
| 362 | h->uid = smb_swap16(smbc->uid);
|
---|
| 363 | h->tid = smb_swap16(req->tid);
|
---|
| 364 | pid = getpid();
|
---|
| 365 | h->pid_high = smb_swap16((unsigned short)(pid >> 16));
|
---|
| 366 | h->pid = smb_swap16((unsigned short) pid);
|
---|
| 367 | }
|
---|
| 368 |
|
---|
| 369 | static CURLcode smb_send(struct connectdata *conn, ssize_t len,
|
---|
| 370 | size_t upload_size)
|
---|
| 371 | {
|
---|
| 372 | struct smb_conn *smbc = &conn->proto.smbc;
|
---|
| 373 | ssize_t bytes_written;
|
---|
| 374 | CURLcode result;
|
---|
| 375 |
|
---|
| 376 | result = Curl_write(conn, FIRSTSOCKET, conn->data->state.uploadbuffer,
|
---|
| 377 | len, &bytes_written);
|
---|
| 378 | if(result)
|
---|
| 379 | return result;
|
---|
| 380 |
|
---|
| 381 | if(bytes_written != len) {
|
---|
| 382 | smbc->send_size = len;
|
---|
| 383 | smbc->sent = bytes_written;
|
---|
| 384 | }
|
---|
| 385 |
|
---|
| 386 | smbc->upload_size = upload_size;
|
---|
| 387 |
|
---|
| 388 | return CURLE_OK;
|
---|
| 389 | }
|
---|
| 390 |
|
---|
| 391 | static CURLcode smb_flush(struct connectdata *conn)
|
---|
| 392 | {
|
---|
| 393 | struct smb_conn *smbc = &conn->proto.smbc;
|
---|
| 394 | ssize_t bytes_written;
|
---|
| 395 | ssize_t len = smbc->send_size - smbc->sent;
|
---|
| 396 | CURLcode result;
|
---|
| 397 |
|
---|
| 398 | if(!smbc->send_size)
|
---|
| 399 | return CURLE_OK;
|
---|
| 400 |
|
---|
| 401 | result = Curl_write(conn, FIRSTSOCKET,
|
---|
| 402 | conn->data->state.uploadbuffer + smbc->sent,
|
---|
| 403 | len, &bytes_written);
|
---|
| 404 | if(result)
|
---|
| 405 | return result;
|
---|
| 406 |
|
---|
| 407 | if(bytes_written != len)
|
---|
| 408 | smbc->sent += bytes_written;
|
---|
| 409 | else
|
---|
| 410 | smbc->send_size = 0;
|
---|
| 411 |
|
---|
| 412 | return CURLE_OK;
|
---|
| 413 | }
|
---|
| 414 |
|
---|
| 415 | static CURLcode smb_send_message(struct connectdata *conn, unsigned char cmd,
|
---|
| 416 | const void *msg, size_t msg_len)
|
---|
| 417 | {
|
---|
| 418 | smb_format_message(conn, (struct smb_header *)conn->data->state.uploadbuffer,
|
---|
| 419 | cmd, msg_len);
|
---|
| 420 | memcpy(conn->data->state.uploadbuffer + sizeof(struct smb_header),
|
---|
| 421 | msg, msg_len);
|
---|
| 422 |
|
---|
| 423 | return smb_send(conn, sizeof(struct smb_header) + msg_len, 0);
|
---|
| 424 | }
|
---|
| 425 |
|
---|
| 426 | static CURLcode smb_send_negotiate(struct connectdata *conn)
|
---|
| 427 | {
|
---|
| 428 | const char *msg = "\x00\x0c\x00\x02NT LM 0.12";
|
---|
| 429 |
|
---|
| 430 | return smb_send_message(conn, SMB_COM_NEGOTIATE, msg, 15);
|
---|
| 431 | }
|
---|
| 432 |
|
---|
| 433 | static CURLcode smb_send_setup(struct connectdata *conn)
|
---|
| 434 | {
|
---|
| 435 | struct smb_conn *smbc = &conn->proto.smbc;
|
---|
| 436 | struct smb_setup msg;
|
---|
| 437 | char *p = msg.bytes;
|
---|
| 438 | unsigned char lm_hash[21];
|
---|
| 439 | unsigned char lm[24];
|
---|
| 440 | unsigned char nt_hash[21];
|
---|
| 441 | unsigned char nt[24];
|
---|
| 442 |
|
---|
| 443 | size_t byte_count = sizeof(lm) + sizeof(nt);
|
---|
| 444 | byte_count += strlen(smbc->user) + strlen(smbc->domain);
|
---|
| 445 | byte_count += strlen(OS) + strlen(CLIENTNAME) + 4; /* 4 null chars */
|
---|
| 446 | if(byte_count > sizeof(msg.bytes))
|
---|
| 447 | return CURLE_FILESIZE_EXCEEDED;
|
---|
| 448 |
|
---|
| 449 | Curl_ntlm_core_mk_lm_hash(conn->data, conn->passwd, lm_hash);
|
---|
| 450 | Curl_ntlm_core_lm_resp(lm_hash, smbc->challenge, lm);
|
---|
| 451 | #ifdef USE_NTRESPONSES
|
---|
| 452 | Curl_ntlm_core_mk_nt_hash(conn->data, conn->passwd, nt_hash);
|
---|
| 453 | Curl_ntlm_core_lm_resp(nt_hash, smbc->challenge, nt);
|
---|
| 454 | #else
|
---|
| 455 | memset(nt, 0, sizeof(nt));
|
---|
| 456 | #endif
|
---|
| 457 |
|
---|
| 458 | memset(&msg, 0, sizeof(msg));
|
---|
| 459 | msg.word_count = SMB_WC_SETUP_ANDX;
|
---|
| 460 | msg.andx.command = SMB_COM_NO_ANDX_COMMAND;
|
---|
| 461 | msg.max_buffer_size = smb_swap16(MAX_MESSAGE_SIZE);
|
---|
| 462 | msg.max_mpx_count = smb_swap16(1);
|
---|
| 463 | msg.vc_number = smb_swap16(1);
|
---|
| 464 | msg.session_key = smb_swap32(smbc->session_key);
|
---|
| 465 | msg.capabilities = smb_swap32(SMB_CAP_LARGE_FILES);
|
---|
| 466 | msg.lengths[0] = smb_swap16(sizeof(lm));
|
---|
| 467 | msg.lengths[1] = smb_swap16(sizeof(nt));
|
---|
| 468 | memcpy(p, lm, sizeof(lm));
|
---|
| 469 | p += sizeof(lm);
|
---|
| 470 | memcpy(p, nt, sizeof(nt));
|
---|
| 471 | p += sizeof(nt);
|
---|
| 472 | MSGCATNULL(smbc->user);
|
---|
| 473 | MSGCATNULL(smbc->domain);
|
---|
| 474 | MSGCATNULL(OS);
|
---|
| 475 | MSGCATNULL(CLIENTNAME);
|
---|
| 476 | byte_count = p - msg.bytes;
|
---|
| 477 | msg.byte_count = smb_swap16((unsigned short)byte_count);
|
---|
| 478 |
|
---|
| 479 | return smb_send_message(conn, SMB_COM_SETUP_ANDX, &msg,
|
---|
| 480 | sizeof(msg) - sizeof(msg.bytes) + byte_count);
|
---|
| 481 | }
|
---|
| 482 |
|
---|
| 483 | static CURLcode smb_send_tree_connect(struct connectdata *conn)
|
---|
| 484 | {
|
---|
| 485 | struct smb_request *req = conn->data->req.protop;
|
---|
| 486 | struct smb_tree_connect msg;
|
---|
| 487 | char *p = msg.bytes;
|
---|
| 488 |
|
---|
| 489 | size_t byte_count = strlen(conn->host.name) + strlen(req->share);
|
---|
| 490 | byte_count += strlen(SERVICENAME) + 5; /* 2 nulls and 3 backslashes */
|
---|
| 491 | if(byte_count > sizeof(msg.bytes))
|
---|
| 492 | return CURLE_FILESIZE_EXCEEDED;
|
---|
| 493 |
|
---|
| 494 | memset(&msg, 0, sizeof(msg));
|
---|
| 495 | msg.word_count = SMB_WC_TREE_CONNECT_ANDX;
|
---|
| 496 | msg.andx.command = SMB_COM_NO_ANDX_COMMAND;
|
---|
| 497 | msg.pw_len = 0;
|
---|
| 498 | MSGCAT("\\\\");
|
---|
| 499 | MSGCAT(conn->host.name);
|
---|
| 500 | MSGCAT("\\");
|
---|
| 501 | MSGCATNULL(req->share);
|
---|
| 502 | MSGCATNULL(SERVICENAME); /* Match any type of service */
|
---|
| 503 | byte_count = p - msg.bytes;
|
---|
| 504 | msg.byte_count = smb_swap16((unsigned short)byte_count);
|
---|
| 505 |
|
---|
| 506 | return smb_send_message(conn, SMB_COM_TREE_CONNECT_ANDX, &msg,
|
---|
| 507 | sizeof(msg) - sizeof(msg.bytes) + byte_count);
|
---|
| 508 | }
|
---|
| 509 |
|
---|
| 510 | static CURLcode smb_send_open(struct connectdata *conn)
|
---|
| 511 | {
|
---|
| 512 | struct smb_request *req = conn->data->req.protop;
|
---|
| 513 | struct smb_nt_create msg;
|
---|
| 514 | size_t byte_count;
|
---|
| 515 |
|
---|
| 516 | if((strlen(req->path) + 1) > sizeof(msg.bytes))
|
---|
| 517 | return CURLE_FILESIZE_EXCEEDED;
|
---|
| 518 |
|
---|
| 519 | memset(&msg, 0, sizeof(msg));
|
---|
| 520 | msg.word_count = SMB_WC_NT_CREATE_ANDX;
|
---|
| 521 | msg.andx.command = SMB_COM_NO_ANDX_COMMAND;
|
---|
| 522 | byte_count = strlen(req->path);
|
---|
| 523 | msg.name_length = smb_swap16((unsigned short)byte_count);
|
---|
| 524 | msg.share_access = smb_swap32(SMB_FILE_SHARE_ALL);
|
---|
| 525 | if(conn->data->set.upload) {
|
---|
| 526 | msg.access = smb_swap32(SMB_GENERIC_READ | SMB_GENERIC_WRITE);
|
---|
| 527 | msg.create_disposition = smb_swap32(SMB_FILE_OVERWRITE_IF);
|
---|
| 528 | }
|
---|
| 529 | else {
|
---|
| 530 | msg.access = smb_swap32(SMB_GENERIC_READ);
|
---|
| 531 | msg.create_disposition = smb_swap32(SMB_FILE_OPEN);
|
---|
| 532 | }
|
---|
| 533 | msg.byte_count = smb_swap16((unsigned short) ++byte_count);
|
---|
| 534 | strcpy(msg.bytes, req->path);
|
---|
| 535 |
|
---|
| 536 | return smb_send_message(conn, SMB_COM_NT_CREATE_ANDX, &msg,
|
---|
| 537 | sizeof(msg) - sizeof(msg.bytes) + byte_count);
|
---|
| 538 | }
|
---|
| 539 |
|
---|
| 540 | static CURLcode smb_send_close(struct connectdata *conn)
|
---|
| 541 | {
|
---|
| 542 | struct smb_request *req = conn->data->req.protop;
|
---|
| 543 | struct smb_close msg;
|
---|
| 544 |
|
---|
| 545 | memset(&msg, 0, sizeof(msg));
|
---|
| 546 | msg.word_count = SMB_WC_CLOSE;
|
---|
| 547 | msg.fid = smb_swap16(req->fid);
|
---|
| 548 |
|
---|
| 549 | return smb_send_message(conn, SMB_COM_CLOSE, &msg, sizeof(msg));
|
---|
| 550 | }
|
---|
| 551 |
|
---|
| 552 | static CURLcode smb_send_tree_disconnect(struct connectdata *conn)
|
---|
| 553 | {
|
---|
| 554 | struct smb_tree_disconnect msg;
|
---|
| 555 |
|
---|
| 556 | memset(&msg, 0, sizeof(msg));
|
---|
| 557 |
|
---|
| 558 | return smb_send_message(conn, SMB_COM_TREE_DISCONNECT, &msg, sizeof(msg));
|
---|
| 559 | }
|
---|
| 560 |
|
---|
| 561 | static CURLcode smb_send_read(struct connectdata *conn)
|
---|
| 562 | {
|
---|
| 563 | struct smb_request *req = conn->data->req.protop;
|
---|
| 564 | curl_off_t offset = conn->data->req.offset;
|
---|
| 565 | struct smb_read msg;
|
---|
| 566 |
|
---|
| 567 | memset(&msg, 0, sizeof(msg));
|
---|
| 568 | msg.word_count = SMB_WC_READ_ANDX;
|
---|
| 569 | msg.andx.command = SMB_COM_NO_ANDX_COMMAND;
|
---|
| 570 | msg.fid = smb_swap16(req->fid);
|
---|
| 571 | msg.offset = smb_swap32((unsigned int) offset);
|
---|
| 572 | msg.offset_high = smb_swap32((unsigned int) (offset >> 32));
|
---|
| 573 | msg.min_bytes = smb_swap16(MAX_PAYLOAD_SIZE);
|
---|
| 574 | msg.max_bytes = smb_swap16(MAX_PAYLOAD_SIZE);
|
---|
| 575 |
|
---|
| 576 | return smb_send_message(conn, SMB_COM_READ_ANDX, &msg, sizeof(msg));
|
---|
| 577 | }
|
---|
| 578 |
|
---|
| 579 | static CURLcode smb_send_write(struct connectdata *conn)
|
---|
| 580 | {
|
---|
| 581 | struct smb_write *msg = (struct smb_write *)conn->data->state.uploadbuffer;
|
---|
| 582 | struct smb_request *req = conn->data->req.protop;
|
---|
| 583 | curl_off_t offset = conn->data->req.offset;
|
---|
| 584 |
|
---|
| 585 | curl_off_t upload_size = conn->data->req.size - conn->data->req.bytecount;
|
---|
| 586 | if(upload_size >= MAX_PAYLOAD_SIZE - 1) /* There is one byte of padding */
|
---|
| 587 | upload_size = MAX_PAYLOAD_SIZE - 1;
|
---|
| 588 |
|
---|
| 589 | memset(msg, 0, sizeof(*msg));
|
---|
| 590 | msg->word_count = SMB_WC_WRITE_ANDX;
|
---|
| 591 | msg->andx.command = SMB_COM_NO_ANDX_COMMAND;
|
---|
| 592 | msg->fid = smb_swap16(req->fid);
|
---|
| 593 | msg->offset = smb_swap32((unsigned int) offset);
|
---|
| 594 | msg->offset_high = smb_swap32((unsigned int) (offset >> 32));
|
---|
| 595 | msg->data_length = smb_swap16((unsigned short) upload_size);
|
---|
| 596 | msg->data_offset = smb_swap16(sizeof(*msg) - sizeof(unsigned int));
|
---|
| 597 | msg->byte_count = smb_swap16((unsigned short) (upload_size + 1));
|
---|
| 598 |
|
---|
| 599 | smb_format_message(conn, &msg->h, SMB_COM_WRITE_ANDX,
|
---|
| 600 | sizeof(*msg) - sizeof(msg->h) + (size_t) upload_size);
|
---|
| 601 |
|
---|
| 602 | return smb_send(conn, sizeof(*msg), (size_t) upload_size);
|
---|
| 603 | }
|
---|
| 604 |
|
---|
| 605 | static CURLcode smb_send_and_recv(struct connectdata *conn, void **msg)
|
---|
| 606 | {
|
---|
| 607 | struct smb_conn *smbc = &conn->proto.smbc;
|
---|
| 608 | CURLcode result;
|
---|
| 609 |
|
---|
| 610 | /* Check if there is data in the transfer buffer */
|
---|
| 611 | if(!smbc->send_size && smbc->upload_size) {
|
---|
| 612 | int nread = smbc->upload_size > UPLOAD_BUFSIZE ? UPLOAD_BUFSIZE :
|
---|
| 613 | (int) smbc->upload_size;
|
---|
| 614 | conn->data->req.upload_fromhere = conn->data->state.uploadbuffer;
|
---|
| 615 | result = Curl_fillreadbuffer(conn, nread, &nread);
|
---|
| 616 | if(result && result != CURLE_AGAIN)
|
---|
| 617 | return result;
|
---|
| 618 | if(!nread)
|
---|
| 619 | return CURLE_OK;
|
---|
| 620 |
|
---|
| 621 | smbc->upload_size -= nread;
|
---|
| 622 | smbc->send_size = nread;
|
---|
| 623 | smbc->sent = 0;
|
---|
| 624 | }
|
---|
| 625 |
|
---|
| 626 | /* Check if there is data to send */
|
---|
| 627 | if(smbc->send_size) {
|
---|
| 628 | result = smb_flush(conn);
|
---|
| 629 | if(result)
|
---|
| 630 | return result;
|
---|
| 631 | }
|
---|
| 632 |
|
---|
| 633 | /* Check if there is still data to be sent */
|
---|
| 634 | if(smbc->send_size || smbc->upload_size)
|
---|
| 635 | return CURLE_AGAIN;
|
---|
| 636 |
|
---|
| 637 | return smb_recv_message(conn, msg);
|
---|
| 638 | }
|
---|
| 639 |
|
---|
| 640 | static CURLcode smb_connection_state(struct connectdata *conn, bool *done)
|
---|
| 641 | {
|
---|
| 642 | struct smb_conn *smbc = &conn->proto.smbc;
|
---|
| 643 | struct smb_negotiate_response *nrsp;
|
---|
| 644 | struct smb_header *h;
|
---|
| 645 | CURLcode result;
|
---|
| 646 | void *msg = NULL;
|
---|
| 647 |
|
---|
| 648 | if(smbc->state == SMB_CONNECTING) {
|
---|
| 649 | #ifdef USE_SSL
|
---|
| 650 | if((conn->handler->flags & PROTOPT_SSL)) {
|
---|
| 651 | bool ssl_done = FALSE;
|
---|
| 652 | result = Curl_ssl_connect_nonblocking(conn, FIRSTSOCKET, &ssl_done);
|
---|
| 653 | if(result && result != CURLE_AGAIN)
|
---|
| 654 | return result;
|
---|
| 655 | if(!ssl_done)
|
---|
| 656 | return CURLE_OK;
|
---|
| 657 | }
|
---|
| 658 | #endif
|
---|
| 659 |
|
---|
| 660 | result = smb_send_negotiate(conn);
|
---|
| 661 | if(result) {
|
---|
| 662 | connclose(conn, "SMB: failed to send negotiate message");
|
---|
| 663 | return result;
|
---|
| 664 | }
|
---|
| 665 |
|
---|
| 666 | conn_state(conn, SMB_NEGOTIATE);
|
---|
| 667 | }
|
---|
| 668 |
|
---|
| 669 | /* Send the previous message and check for a response */
|
---|
| 670 | result = smb_send_and_recv(conn, &msg);
|
---|
| 671 | if(result && result != CURLE_AGAIN) {
|
---|
| 672 | connclose(conn, "SMB: failed to communicate");
|
---|
| 673 | return result;
|
---|
| 674 | }
|
---|
| 675 |
|
---|
| 676 | if(!msg)
|
---|
| 677 | return CURLE_OK;
|
---|
| 678 |
|
---|
| 679 | h = msg;
|
---|
| 680 |
|
---|
| 681 | switch(smbc->state) {
|
---|
| 682 | case SMB_NEGOTIATE:
|
---|
| 683 | if(h->status || smbc->got < sizeof(*nrsp) + sizeof(smbc->challenge) - 1) {
|
---|
| 684 | connclose(conn, "SMB: negotiation failed");
|
---|
| 685 | return CURLE_COULDNT_CONNECT;
|
---|
| 686 | }
|
---|
| 687 | nrsp = msg;
|
---|
| 688 | memcpy(smbc->challenge, nrsp->bytes, sizeof(smbc->challenge));
|
---|
| 689 | smbc->session_key = smb_swap32(nrsp->session_key);
|
---|
| 690 | result = smb_send_setup(conn);
|
---|
| 691 | if(result) {
|
---|
| 692 | connclose(conn, "SMB: failed to send setup message");
|
---|
| 693 | return result;
|
---|
| 694 | }
|
---|
| 695 | conn_state(conn, SMB_SETUP);
|
---|
| 696 | break;
|
---|
| 697 |
|
---|
| 698 | case SMB_SETUP:
|
---|
| 699 | if(h->status) {
|
---|
| 700 | connclose(conn, "SMB: authentication failed");
|
---|
| 701 | return CURLE_LOGIN_DENIED;
|
---|
| 702 | }
|
---|
| 703 | smbc->uid = smb_swap16(h->uid);
|
---|
| 704 | conn_state(conn, SMB_CONNECTED);
|
---|
| 705 | *done = true;
|
---|
| 706 | break;
|
---|
| 707 |
|
---|
| 708 | default:
|
---|
| 709 | smb_pop_message(conn);
|
---|
| 710 | return CURLE_OK; /* ignore */
|
---|
| 711 | }
|
---|
| 712 |
|
---|
| 713 | smb_pop_message(conn);
|
---|
| 714 |
|
---|
| 715 | return CURLE_OK;
|
---|
| 716 | }
|
---|
| 717 |
|
---|
| 718 | /*
|
---|
| 719 | * Convert a timestamp from the Windows world (100 nsec units from
|
---|
| 720 | * 1 Jan 1601) to Posix time.
|
---|
| 721 | */
|
---|
| 722 | static void get_posix_time(long *_out, const void *_in)
|
---|
| 723 | {
|
---|
| 724 | #ifdef HAVE_LONGLONG
|
---|
| 725 | long long timestamp = *(long long *) _in;
|
---|
| 726 | #else
|
---|
| 727 | unsigned __int64 timestamp = *(unsigned __int64 *) _in;
|
---|
| 728 | #endif
|
---|
| 729 |
|
---|
| 730 | timestamp -= 116444736000000000ULL;
|
---|
| 731 | timestamp /= 10000000;
|
---|
| 732 | *_out = (long) timestamp;
|
---|
| 733 | }
|
---|
| 734 |
|
---|
| 735 | static CURLcode smb_request_state(struct connectdata *conn, bool *done)
|
---|
| 736 | {
|
---|
| 737 | struct smb_request *req = conn->data->req.protop;
|
---|
| 738 | struct smb_header *h;
|
---|
| 739 | struct smb_conn *smbc = &conn->proto.smbc;
|
---|
| 740 | enum smb_req_state next_state = SMB_DONE;
|
---|
| 741 | unsigned short len;
|
---|
| 742 | unsigned short off;
|
---|
| 743 | CURLcode result;
|
---|
| 744 | void *msg = NULL;
|
---|
| 745 | const struct smb_nt_create_response *smb_m;
|
---|
| 746 |
|
---|
| 747 | /* Start the request */
|
---|
| 748 | if(req->state == SMB_REQUESTING) {
|
---|
| 749 | result = smb_send_tree_connect(conn);
|
---|
| 750 | if(result) {
|
---|
| 751 | connclose(conn, "SMB: failed to send tree connect message");
|
---|
| 752 | return result;
|
---|
| 753 | }
|
---|
| 754 |
|
---|
| 755 | request_state(conn, SMB_TREE_CONNECT);
|
---|
| 756 | }
|
---|
| 757 |
|
---|
| 758 | /* Send the previous message and check for a response */
|
---|
| 759 | result = smb_send_and_recv(conn, &msg);
|
---|
| 760 | if(result && result != CURLE_AGAIN) {
|
---|
| 761 | connclose(conn, "SMB: failed to communicate");
|
---|
| 762 | return result;
|
---|
| 763 | }
|
---|
| 764 |
|
---|
| 765 | if(!msg)
|
---|
| 766 | return CURLE_OK;
|
---|
| 767 |
|
---|
| 768 | h = msg;
|
---|
| 769 |
|
---|
| 770 | switch(req->state) {
|
---|
| 771 | case SMB_TREE_CONNECT:
|
---|
| 772 | if(h->status) {
|
---|
| 773 | req->result = CURLE_REMOTE_FILE_NOT_FOUND;
|
---|
| 774 | if(h->status == smb_swap32(SMB_ERR_NOACCESS))
|
---|
| 775 | req->result = CURLE_REMOTE_ACCESS_DENIED;
|
---|
| 776 | break;
|
---|
| 777 | }
|
---|
| 778 | req->tid = smb_swap16(h->tid);
|
---|
| 779 | next_state = SMB_OPEN;
|
---|
| 780 | break;
|
---|
| 781 |
|
---|
| 782 | case SMB_OPEN:
|
---|
| 783 | if(h->status || smbc->got < sizeof(struct smb_nt_create_response)) {
|
---|
| 784 | req->result = CURLE_REMOTE_FILE_NOT_FOUND;
|
---|
| 785 | next_state = SMB_TREE_DISCONNECT;
|
---|
| 786 | break;
|
---|
| 787 | }
|
---|
| 788 | smb_m = (const struct smb_nt_create_response*) msg;
|
---|
| 789 | req->fid = smb_swap16(smb_m->fid);
|
---|
| 790 | conn->data->req.offset = 0;
|
---|
| 791 | if(conn->data->set.upload) {
|
---|
| 792 | conn->data->req.size = conn->data->state.infilesize;
|
---|
| 793 | Curl_pgrsSetUploadSize(conn->data, conn->data->req.size);
|
---|
| 794 | next_state = SMB_UPLOAD;
|
---|
| 795 | }
|
---|
| 796 | else {
|
---|
| 797 | smb_m = (const struct smb_nt_create_response*) msg;
|
---|
| 798 | conn->data->req.size = smb_swap64(smb_m->end_of_file);
|
---|
| 799 | Curl_pgrsSetDownloadSize(conn->data, conn->data->req.size);
|
---|
| 800 | if(conn->data->set.get_filetime)
|
---|
| 801 | get_posix_time(&conn->data->info.filetime, &smb_m->last_change_time);
|
---|
| 802 | next_state = SMB_DOWNLOAD;
|
---|
| 803 | }
|
---|
| 804 | break;
|
---|
| 805 |
|
---|
| 806 | case SMB_DOWNLOAD:
|
---|
| 807 | if(h->status || smbc->got < sizeof(struct smb_header) + 14) {
|
---|
| 808 | req->result = CURLE_RECV_ERROR;
|
---|
| 809 | next_state = SMB_CLOSE;
|
---|
| 810 | break;
|
---|
| 811 | }
|
---|
| 812 | len = Curl_read16_le(((const unsigned char *) msg) +
|
---|
| 813 | sizeof(struct smb_header) + 11);
|
---|
| 814 | off = Curl_read16_le(((const unsigned char *) msg) +
|
---|
| 815 | sizeof(struct smb_header) + 13);
|
---|
| 816 | if(len > 0) {
|
---|
| 817 | if(off + sizeof(unsigned int) + len > smbc->got) {
|
---|
| 818 | failf(conn->data, "Invalid input packet");
|
---|
| 819 | result = CURLE_RECV_ERROR;
|
---|
| 820 | }
|
---|
| 821 | else
|
---|
| 822 | result = Curl_client_write(conn, CLIENTWRITE_BODY,
|
---|
| 823 | (char *)msg + off + sizeof(unsigned int),
|
---|
| 824 | len);
|
---|
| 825 | if(result) {
|
---|
| 826 | req->result = result;
|
---|
| 827 | next_state = SMB_CLOSE;
|
---|
| 828 | break;
|
---|
| 829 | }
|
---|
| 830 | }
|
---|
| 831 | conn->data->req.bytecount += len;
|
---|
| 832 | conn->data->req.offset += len;
|
---|
| 833 | Curl_pgrsSetDownloadCounter(conn->data, conn->data->req.bytecount);
|
---|
| 834 | next_state = (len < MAX_PAYLOAD_SIZE) ? SMB_CLOSE : SMB_DOWNLOAD;
|
---|
| 835 | break;
|
---|
| 836 |
|
---|
| 837 | case SMB_UPLOAD:
|
---|
| 838 | if(h->status || smbc->got < sizeof(struct smb_header) + 6) {
|
---|
| 839 | req->result = CURLE_UPLOAD_FAILED;
|
---|
| 840 | next_state = SMB_CLOSE;
|
---|
| 841 | break;
|
---|
| 842 | }
|
---|
| 843 | len = Curl_read16_le(((const unsigned char *) msg) +
|
---|
| 844 | sizeof(struct smb_header) + 5);
|
---|
| 845 | conn->data->req.bytecount += len;
|
---|
| 846 | conn->data->req.offset += len;
|
---|
| 847 | Curl_pgrsSetUploadCounter(conn->data, conn->data->req.bytecount);
|
---|
| 848 | if(conn->data->req.bytecount >= conn->data->req.size)
|
---|
| 849 | next_state = SMB_CLOSE;
|
---|
| 850 | else
|
---|
| 851 | next_state = SMB_UPLOAD;
|
---|
| 852 | break;
|
---|
| 853 |
|
---|
| 854 | case SMB_CLOSE:
|
---|
| 855 | /* We don't care if the close failed, proceed to tree disconnect anyway */
|
---|
| 856 | next_state = SMB_TREE_DISCONNECT;
|
---|
| 857 | break;
|
---|
| 858 |
|
---|
| 859 | case SMB_TREE_DISCONNECT:
|
---|
| 860 | next_state = SMB_DONE;
|
---|
| 861 | break;
|
---|
| 862 |
|
---|
| 863 | default:
|
---|
| 864 | smb_pop_message(conn);
|
---|
| 865 | return CURLE_OK; /* ignore */
|
---|
| 866 | }
|
---|
| 867 |
|
---|
| 868 | smb_pop_message(conn);
|
---|
| 869 |
|
---|
| 870 | switch(next_state) {
|
---|
| 871 | case SMB_OPEN:
|
---|
| 872 | result = smb_send_open(conn);
|
---|
| 873 | break;
|
---|
| 874 |
|
---|
| 875 | case SMB_DOWNLOAD:
|
---|
| 876 | result = smb_send_read(conn);
|
---|
| 877 | break;
|
---|
| 878 |
|
---|
| 879 | case SMB_UPLOAD:
|
---|
| 880 | result = smb_send_write(conn);
|
---|
| 881 | break;
|
---|
| 882 |
|
---|
| 883 | case SMB_CLOSE:
|
---|
| 884 | result = smb_send_close(conn);
|
---|
| 885 | break;
|
---|
| 886 |
|
---|
| 887 | case SMB_TREE_DISCONNECT:
|
---|
| 888 | result = smb_send_tree_disconnect(conn);
|
---|
| 889 | break;
|
---|
| 890 |
|
---|
| 891 | case SMB_DONE:
|
---|
| 892 | result = req->result;
|
---|
| 893 | *done = true;
|
---|
| 894 | break;
|
---|
| 895 |
|
---|
| 896 | default:
|
---|
| 897 | break;
|
---|
| 898 | }
|
---|
| 899 |
|
---|
| 900 | if(result) {
|
---|
| 901 | connclose(conn, "SMB: failed to send message");
|
---|
| 902 | return result;
|
---|
| 903 | }
|
---|
| 904 |
|
---|
| 905 | request_state(conn, next_state);
|
---|
| 906 |
|
---|
| 907 | return CURLE_OK;
|
---|
| 908 | }
|
---|
| 909 |
|
---|
| 910 | static CURLcode smb_done(struct connectdata *conn, CURLcode status,
|
---|
| 911 | bool premature)
|
---|
| 912 | {
|
---|
| 913 | struct smb_request *req = conn->data->req.protop;
|
---|
| 914 |
|
---|
| 915 | (void) premature;
|
---|
| 916 |
|
---|
| 917 | Curl_safefree(req->share);
|
---|
| 918 | Curl_safefree(conn->data->req.protop);
|
---|
| 919 |
|
---|
| 920 | return status;
|
---|
| 921 | }
|
---|
| 922 |
|
---|
| 923 | static CURLcode smb_disconnect(struct connectdata *conn, bool dead)
|
---|
| 924 | {
|
---|
| 925 | struct smb_conn *smbc = &conn->proto.smbc;
|
---|
| 926 | struct smb_request *req = conn->data->req.protop;
|
---|
| 927 |
|
---|
| 928 | (void) dead;
|
---|
| 929 |
|
---|
| 930 | Curl_safefree(smbc->domain);
|
---|
| 931 | Curl_safefree(smbc->recv_buf);
|
---|
| 932 |
|
---|
| 933 | /* smb_done is not always called, so cleanup the request */
|
---|
| 934 | if(req) {
|
---|
| 935 | Curl_safefree(req->share);
|
---|
| 936 | }
|
---|
| 937 |
|
---|
| 938 | return CURLE_OK;
|
---|
| 939 | }
|
---|
| 940 |
|
---|
| 941 | static int smb_getsock(struct connectdata *conn, curl_socket_t *socks,
|
---|
| 942 | int numsocks)
|
---|
| 943 | {
|
---|
| 944 | struct smb_conn *smbc = &conn->proto.smbc;
|
---|
| 945 |
|
---|
| 946 | if(!numsocks)
|
---|
| 947 | return GETSOCK_BLANK;
|
---|
| 948 |
|
---|
| 949 | socks[0] = conn->sock[FIRSTSOCKET];
|
---|
| 950 |
|
---|
| 951 | if(smbc->send_size || smbc->upload_size)
|
---|
| 952 | return GETSOCK_WRITESOCK(0);
|
---|
| 953 |
|
---|
| 954 | return GETSOCK_READSOCK(0);
|
---|
| 955 | }
|
---|
| 956 |
|
---|
| 957 | static CURLcode smb_parse_url_path(struct connectdata *conn)
|
---|
| 958 | {
|
---|
| 959 | CURLcode result = CURLE_OK;
|
---|
| 960 | struct Curl_easy *data = conn->data;
|
---|
| 961 | struct smb_request *req = data->req.protop;
|
---|
| 962 | char *path;
|
---|
| 963 | char *slash;
|
---|
| 964 |
|
---|
| 965 | /* URL decode the path */
|
---|
| 966 | result = Curl_urldecode(data, data->state.path, 0, &path, NULL, TRUE);
|
---|
| 967 | if(result)
|
---|
| 968 | return result;
|
---|
| 969 |
|
---|
| 970 | /* Parse the path for the share */
|
---|
| 971 | req->share = strdup((*path == '/' || *path == '\\') ? path + 1 : path);
|
---|
| 972 | if(!req->share) {
|
---|
| 973 | free(path);
|
---|
| 974 |
|
---|
| 975 | return CURLE_OUT_OF_MEMORY;
|
---|
| 976 | }
|
---|
| 977 |
|
---|
| 978 | slash = strchr(req->share, '/');
|
---|
| 979 | if(!slash)
|
---|
| 980 | slash = strchr(req->share, '\\');
|
---|
| 981 |
|
---|
| 982 | /* The share must be present */
|
---|
| 983 | if(!slash) {
|
---|
| 984 | free(path);
|
---|
| 985 |
|
---|
| 986 | return CURLE_URL_MALFORMAT;
|
---|
| 987 | }
|
---|
| 988 |
|
---|
| 989 | /* Parse the path for the file path converting any forward slashes into
|
---|
| 990 | backslashes */
|
---|
| 991 | *slash++ = 0;
|
---|
| 992 | req->path = slash;
|
---|
| 993 | for(; *slash; slash++) {
|
---|
| 994 | if(*slash == '/')
|
---|
| 995 | *slash = '\\';
|
---|
| 996 | }
|
---|
| 997 |
|
---|
| 998 | free(path);
|
---|
| 999 |
|
---|
| 1000 | return CURLE_OK;
|
---|
| 1001 | }
|
---|
| 1002 |
|
---|
| 1003 | #endif /* !USE_WINDOWS_SSPI || USE_WIN32_CRYPTO */
|
---|
| 1004 |
|
---|
| 1005 | #endif /* CURL_DISABLE_SMB && USE_NTLM && CURL_SIZEOF_CURL_OFF_T > 4 */
|
---|