smb.c
Go to the documentation of this file.
00001 /***************************************************************************
00002  *                                  _   _ ____  _
00003  *  Project                     ___| | | |  _ \| |
00004  *                             / __| | | | |_) | |
00005  *                            | (__| |_| |  _ <| |___
00006  *                             \___|\___/|_| \_\_____|
00007  *
00008  * Copyright (C) 2014, Bill Nagel <wnagel@tycoint.com>, Exacq Technologies
00009  * Copyright (C) 2016, Daniel Stenberg, <daniel@haxx.se>, et al.
00010  *
00011  * This software is licensed as described in the file COPYING, which
00012  * you should have received as part of this distribution. The terms
00013  * are also available at https://curl.haxx.se/docs/copyright.html.
00014  *
00015  * You may opt to use, copy, modify, merge, publish, distribute and/or sell
00016  * copies of the Software, and permit persons to whom the Software is
00017  * furnished to do so, under the terms of the COPYING file.
00018  *
00019  * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
00020  * KIND, either express or implied.
00021  *
00022  ***************************************************************************/
00023 
00024 #include "curl_setup.h"
00025 
00026 #if !defined(CURL_DISABLE_SMB) && defined(USE_NTLM) && \
00027     (CURL_SIZEOF_CURL_OFF_T > 4)
00028 
00029 #if !defined(USE_WINDOWS_SSPI) || defined(USE_WIN32_CRYPTO)
00030 
00031 #define BUILDING_CURL_SMB_C
00032 
00033 #ifdef HAVE_PROCESS_H
00034 #include <process.h>
00035 #define getpid _getpid
00036 #endif
00037 
00038 #include "smb.h"
00039 #include "urldata.h"
00040 #include "sendf.h"
00041 #include "multiif.h"
00042 #include "connect.h"
00043 #include "progress.h"
00044 #include "transfer.h"
00045 #include "vtls/vtls.h"
00046 #include "curl_ntlm_core.h"
00047 #include "escape.h"
00048 #include "curl_endian.h"
00049 
00050 /* The last #include files should be: */
00051 #include "curl_memory.h"
00052 #include "memdebug.h"
00053 
00054 /* Local API functions */
00055 static CURLcode smb_setup_connection(struct connectdata *conn);
00056 static CURLcode smb_connect(struct connectdata *conn, bool *done);
00057 static CURLcode smb_connection_state(struct connectdata *conn, bool *done);
00058 static CURLcode smb_request_state(struct connectdata *conn, bool *done);
00059 static CURLcode smb_done(struct connectdata *conn, CURLcode status,
00060                          bool premature);
00061 static CURLcode smb_disconnect(struct connectdata *conn, bool dead);
00062 static int smb_getsock(struct connectdata *conn, curl_socket_t *socks,
00063                        int numsocks);
00064 static CURLcode smb_parse_url_path(struct connectdata *conn);
00065 
00066 /*
00067  * SMB handler interface
00068  */
00069 const struct Curl_handler Curl_handler_smb = {
00070   "SMB",                                /* scheme */
00071   smb_setup_connection,                 /* setup_connection */
00072   ZERO_NULL,                            /* do_it */
00073   smb_done,                             /* done */
00074   ZERO_NULL,                            /* do_more */
00075   smb_connect,                          /* connect_it */
00076   smb_connection_state,                 /* connecting */
00077   smb_request_state,                    /* doing */
00078   smb_getsock,                          /* proto_getsock */
00079   smb_getsock,                          /* doing_getsock */
00080   ZERO_NULL,                            /* domore_getsock */
00081   ZERO_NULL,                            /* perform_getsock */
00082   smb_disconnect,                       /* disconnect */
00083   ZERO_NULL,                            /* readwrite */
00084   PORT_SMB,                             /* defport */
00085   CURLPROTO_SMB,                        /* protocol */
00086   PROTOPT_NONE                          /* flags */
00087 };
00088 
00089 #ifdef USE_SSL
00090 /*
00091  * SMBS handler interface
00092  */
00093 const struct Curl_handler Curl_handler_smbs = {
00094   "SMBS",                               /* scheme */
00095   smb_setup_connection,                 /* setup_connection */
00096   ZERO_NULL,                            /* do_it */
00097   smb_done,                             /* done */
00098   ZERO_NULL,                            /* do_more */
00099   smb_connect,                          /* connect_it */
00100   smb_connection_state,                 /* connecting */
00101   smb_request_state,                    /* doing */
00102   smb_getsock,                          /* proto_getsock */
00103   smb_getsock,                          /* doing_getsock */
00104   ZERO_NULL,                            /* domore_getsock */
00105   ZERO_NULL,                            /* perform_getsock */
00106   smb_disconnect,                       /* disconnect */
00107   ZERO_NULL,                            /* readwrite */
00108   PORT_SMBS,                            /* defport */
00109   CURLPROTO_SMBS,                       /* protocol */
00110   PROTOPT_SSL                           /* flags */
00111 };
00112 #endif
00113 
00114 #define MAX_PAYLOAD_SIZE  0x8000
00115 #define MAX_MESSAGE_SIZE  (MAX_PAYLOAD_SIZE + 0x1000)
00116 #define CLIENTNAME        "curl"
00117 #define SERVICENAME       "?????"
00118 
00119 /* Append a string to an SMB message */
00120 #define MSGCAT(str) \
00121   strcpy(p, (str)); \
00122   p += strlen(str);
00123 
00124 /* Append a null-terminated string to an SMB message */
00125 #define MSGCATNULL(str) \
00126   strcpy(p, (str)); \
00127   p += strlen(str) + 1;
00128 
00129 /* SMB is mostly little endian */
00130 #if (defined(__BYTE_ORDER__) && __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__) || \
00131    defined(__OS400__)
00132 static unsigned short smb_swap16(unsigned short x)
00133 {
00134   return (unsigned short) ((x << 8) | ((x >> 8) & 0xff));
00135 }
00136 
00137 static unsigned int smb_swap32(unsigned int x)
00138 {
00139   return (x << 24) | ((x << 8) & 0xff0000) | ((x >> 8) & 0xff00) |
00140          ((x >> 24) & 0xff);
00141 }
00142 
00143 #ifdef HAVE_LONGLONG
00144 static unsigned long long smb_swap64(unsigned long long x)
00145 {
00146   return ((unsigned long long) smb_swap32((unsigned int) x) << 32) |
00147           smb_swap32((unsigned int) (x >> 32));
00148 }
00149 #else
00150 static unsigned __int64 smb_swap64(unsigned __int64 x)
00151 {
00152   return ((unsigned __int64) smb_swap32((unsigned int) x) << 32) |
00153           smb_swap32((unsigned int) (x >> 32));
00154 }
00155 #endif
00156 #else
00157 #  define smb_swap16(x) (x)
00158 #  define smb_swap32(x) (x)
00159 #  define smb_swap64(x) (x)
00160 #endif
00161 
00162 /* SMB request state */
00163 enum smb_req_state {
00164   SMB_REQUESTING,
00165   SMB_TREE_CONNECT,
00166   SMB_OPEN,
00167   SMB_DOWNLOAD,
00168   SMB_UPLOAD,
00169   SMB_CLOSE,
00170   SMB_TREE_DISCONNECT,
00171   SMB_DONE
00172 };
00173 
00174 /* SMB request data */
00175 struct smb_request {
00176   enum smb_req_state state;
00177   char *share;
00178   char *path;
00179   unsigned short tid; /* Even if we connect to the same tree as another */
00180   unsigned short fid; /* request, the tid will be different */
00181   CURLcode result;
00182 };
00183 
00184 static void conn_state(struct connectdata *conn, enum smb_conn_state newstate)
00185 {
00186   struct smb_conn *smb = &conn->proto.smbc;
00187 #if defined(DEBUGBUILD) && !defined(CURL_DISABLE_VERBOSE_STRINGS)
00188   /* For debug purposes */
00189   static const char * const names[] = {
00190     "SMB_NOT_CONNECTED",
00191     "SMB_CONNECTING",
00192     "SMB_NEGOTIATE",
00193     "SMB_SETUP",
00194     "SMB_CONNECTED",
00195     /* LAST */
00196   };
00197 
00198   if(smb->state != newstate)
00199     infof(conn->data, "SMB conn %p state change from %s to %s\n",
00200     (void *)smb, names[smb->state], names[newstate]);
00201 #endif
00202 
00203   smb->state = newstate;
00204 }
00205 
00206 static void request_state(struct connectdata *conn,
00207                           enum smb_req_state newstate)
00208 {
00209   struct smb_request *req = conn->data->req.protop;
00210 #if defined(DEBUGBUILD) && !defined(CURL_DISABLE_VERBOSE_STRINGS)
00211   /* For debug purposes */
00212   static const char * const names[] = {
00213     "SMB_REQUESTING",
00214     "SMB_TREE_CONNECT",
00215     "SMB_OPEN",
00216     "SMB_DOWNLOAD",
00217     "SMB_UPLOAD",
00218     "SMB_CLOSE",
00219     "SMB_TREE_DISCONNECT",
00220     "SMB_DONE",
00221     /* LAST */
00222   };
00223 
00224   if(req->state != newstate)
00225     infof(conn->data, "SMB request %p state change from %s to %s\n",
00226     (void *)req, names[req->state], names[newstate]);
00227 #endif
00228 
00229   req->state = newstate;
00230 }
00231 
00232 static CURLcode smb_setup_connection(struct connectdata *conn)
00233 {
00234   struct smb_request *req;
00235 
00236   /* Initialize the request state */
00237   conn->data->req.protop = req = calloc(1, sizeof(struct smb_request));
00238   if(!req)
00239     return CURLE_OUT_OF_MEMORY;
00240 
00241   /* Parse the URL path */
00242   return smb_parse_url_path(conn);
00243 }
00244 
00245 static CURLcode smb_connect(struct connectdata *conn, bool *done)
00246 {
00247   struct smb_conn *smbc = &conn->proto.smbc;
00248   char *slash;
00249 
00250   (void) done;
00251 
00252   /* Check we have a username and password to authenticate with */
00253   if(!conn->bits.user_passwd)
00254     return CURLE_LOGIN_DENIED;
00255 
00256   /* Initialize the connection state */
00257   memset(smbc, 0, sizeof(*smbc));
00258   smbc->state = SMB_CONNECTING;
00259   smbc->recv_buf = malloc(MAX_MESSAGE_SIZE);
00260   if(!smbc->recv_buf)
00261     return CURLE_OUT_OF_MEMORY;
00262 
00263   /* Multiple requests are allowed with this connection */
00264   connkeep(conn, "SMB default");
00265 
00266   /* Parse the username, domain, and password */
00267   slash = strchr(conn->user, '/');
00268   if(!slash)
00269     slash = strchr(conn->user, '\\');
00270 
00271   if(slash) {
00272     smbc->user = slash + 1;
00273     smbc->domain = strdup(conn->user);
00274     if(!smbc->domain)
00275       return CURLE_OUT_OF_MEMORY;
00276     smbc->domain[slash - conn->user] = 0;
00277   }
00278   else {
00279     smbc->user = conn->user;
00280     smbc->domain = strdup(conn->host.name);
00281     if(!smbc->domain)
00282       return CURLE_OUT_OF_MEMORY;
00283   }
00284 
00285   return CURLE_OK;
00286 }
00287 
00288 static CURLcode smb_recv_message(struct connectdata *conn, void **msg)
00289 {
00290   struct smb_conn *smbc = &conn->proto.smbc;
00291   char *buf = smbc->recv_buf;
00292   ssize_t bytes_read;
00293   size_t nbt_size;
00294   size_t msg_size;
00295   size_t len = MAX_MESSAGE_SIZE - smbc->got;
00296   CURLcode result;
00297 
00298   result = Curl_read(conn, FIRSTSOCKET, buf + smbc->got, len, &bytes_read);
00299   if(result)
00300     return result;
00301 
00302   if(!bytes_read)
00303     return CURLE_OK;
00304 
00305   smbc->got += bytes_read;
00306 
00307   /* Check for a 32-bit nbt header */
00308   if(smbc->got < sizeof(unsigned int))
00309     return CURLE_OK;
00310 
00311   nbt_size = Curl_read16_be((const unsigned char *)(buf +
00312              sizeof(unsigned short))) + sizeof(unsigned int);
00313   if(smbc->got < nbt_size)
00314     return CURLE_OK;
00315 
00316   msg_size = sizeof(struct smb_header);
00317   if(nbt_size >= msg_size + 1) {
00318     /* Add the word count */
00319     msg_size += 1 + ((unsigned char) buf[msg_size]) * sizeof(unsigned short);
00320     if(nbt_size >= msg_size + sizeof(unsigned short)) {
00321       /* Add the byte count */
00322       msg_size += sizeof(unsigned short) +
00323                   Curl_read16_le((const unsigned char *)&buf[msg_size]);
00324       if(nbt_size < msg_size)
00325         return CURLE_READ_ERROR;
00326     }
00327   }
00328 
00329   *msg = buf;
00330 
00331   return CURLE_OK;
00332 }
00333 
00334 static void smb_pop_message(struct connectdata *conn)
00335 {
00336   struct smb_conn *smbc = &conn->proto.smbc;
00337 
00338   smbc->got = 0;
00339 }
00340 
00341 static void smb_format_message(struct connectdata *conn, struct smb_header *h,
00342                                unsigned char cmd, size_t len)
00343 {
00344   struct smb_conn *smbc = &conn->proto.smbc;
00345   struct smb_request *req = conn->data->req.protop;
00346   unsigned int pid;
00347 
00348   memset(h, 0, sizeof(*h));
00349   h->nbt_length = htons((unsigned short) (sizeof(*h) - sizeof(unsigned int) +
00350                                           len));
00351   memcpy((char *)h->magic, "\xffSMB", 4);
00352   h->command = cmd;
00353   h->flags = SMB_FLAGS_CANONICAL_PATHNAMES | SMB_FLAGS_CASELESS_PATHNAMES;
00354   h->flags2 = smb_swap16(SMB_FLAGS2_IS_LONG_NAME | SMB_FLAGS2_KNOWS_LONG_NAME);
00355   h->uid = smb_swap16(smbc->uid);
00356   h->tid = smb_swap16(req->tid);
00357   pid = getpid();
00358   h->pid_high = smb_swap16((unsigned short)(pid >> 16));
00359   h->pid = smb_swap16((unsigned short) pid);
00360 }
00361 
00362 static CURLcode smb_send(struct connectdata *conn, ssize_t len,
00363                          size_t upload_size)
00364 {
00365   struct smb_conn *smbc = &conn->proto.smbc;
00366   ssize_t bytes_written;
00367   CURLcode result;
00368 
00369   result = Curl_write(conn, FIRSTSOCKET, conn->data->state.uploadbuffer,
00370                       len, &bytes_written);
00371   if(result)
00372     return result;
00373 
00374   if(bytes_written != len) {
00375     smbc->send_size = len;
00376     smbc->sent = bytes_written;
00377   }
00378 
00379   smbc->upload_size = upload_size;
00380 
00381   return CURLE_OK;
00382 }
00383 
00384 static CURLcode smb_flush(struct connectdata *conn)
00385 {
00386   struct smb_conn *smbc = &conn->proto.smbc;
00387   ssize_t bytes_written;
00388   ssize_t len = smbc->send_size - smbc->sent;
00389   CURLcode result;
00390 
00391   if(!smbc->send_size)
00392     return CURLE_OK;
00393 
00394   result = Curl_write(conn, FIRSTSOCKET,
00395                       conn->data->state.uploadbuffer + smbc->sent,
00396                       len, &bytes_written);
00397   if(result)
00398     return result;
00399 
00400   if(bytes_written != len)
00401     smbc->sent += bytes_written;
00402   else
00403     smbc->send_size = 0;
00404 
00405   return CURLE_OK;
00406 }
00407 
00408 static CURLcode smb_send_message(struct connectdata *conn, unsigned char cmd,
00409                                  const void *msg, size_t msg_len)
00410 {
00411   smb_format_message(conn, (struct smb_header *)conn->data->state.uploadbuffer,
00412                      cmd, msg_len);
00413   memcpy(conn->data->state.uploadbuffer + sizeof(struct smb_header),
00414          msg, msg_len);
00415 
00416   return smb_send(conn, sizeof(struct smb_header) + msg_len, 0);
00417 }
00418 
00419 static CURLcode smb_send_negotiate(struct connectdata *conn)
00420 {
00421   const char *msg = "\x00\x0c\x00\x02NT LM 0.12";
00422 
00423   return smb_send_message(conn, SMB_COM_NEGOTIATE, msg, 15);
00424 }
00425 
00426 static CURLcode smb_send_setup(struct connectdata *conn)
00427 {
00428   struct smb_conn *smbc = &conn->proto.smbc;
00429   struct smb_setup msg;
00430   char *p = msg.bytes;
00431   unsigned char lm_hash[21];
00432   unsigned char lm[24];
00433   unsigned char nt_hash[21];
00434   unsigned char nt[24];
00435 
00436   size_t byte_count = sizeof(lm) + sizeof(nt);
00437   byte_count += strlen(smbc->user) + strlen(smbc->domain);
00438   byte_count += strlen(OS) + strlen(CLIENTNAME) + 4; /* 4 null chars */
00439   if(byte_count > sizeof(msg.bytes))
00440     return CURLE_FILESIZE_EXCEEDED;
00441 
00442   Curl_ntlm_core_mk_lm_hash(conn->data, conn->passwd, lm_hash);
00443   Curl_ntlm_core_lm_resp(lm_hash, smbc->challenge, lm);
00444 #if USE_NTRESPONSES
00445   Curl_ntlm_core_mk_nt_hash(conn->data, conn->passwd, nt_hash);
00446   Curl_ntlm_core_lm_resp(nt_hash, smbc->challenge, nt);
00447 #else
00448   memset(nt, 0, sizeof(nt));
00449 #endif
00450 
00451   memset(&msg, 0, sizeof(msg));
00452   msg.word_count = SMB_WC_SETUP_ANDX;
00453   msg.andx.command = SMB_COM_NO_ANDX_COMMAND;
00454   msg.max_buffer_size = smb_swap16(MAX_MESSAGE_SIZE);
00455   msg.max_mpx_count = smb_swap16(1);
00456   msg.vc_number = smb_swap16(1);
00457   msg.session_key = smb_swap32(smbc->session_key);
00458   msg.capabilities = smb_swap32(SMB_CAP_LARGE_FILES);
00459   msg.lengths[0] = smb_swap16(sizeof(lm));
00460   msg.lengths[1] = smb_swap16(sizeof(nt));
00461   memcpy(p, lm, sizeof(lm));
00462   p += sizeof(lm);
00463   memcpy(p, nt, sizeof(nt));
00464   p += sizeof(nt);
00465   MSGCATNULL(smbc->user);
00466   MSGCATNULL(smbc->domain);
00467   MSGCATNULL(OS);
00468   MSGCATNULL(CLIENTNAME);
00469   byte_count = p - msg.bytes;
00470   msg.byte_count = smb_swap16((unsigned short)byte_count);
00471 
00472   return smb_send_message(conn, SMB_COM_SETUP_ANDX, &msg,
00473                           sizeof(msg) - sizeof(msg.bytes) + byte_count);
00474 }
00475 
00476 static CURLcode smb_send_tree_connect(struct connectdata *conn)
00477 {
00478   struct smb_request *req = conn->data->req.protop;
00479   struct smb_tree_connect msg;
00480   char *p = msg.bytes;
00481 
00482   size_t byte_count = strlen(conn->host.name) + strlen(req->share);
00483   byte_count += strlen(SERVICENAME) + 5; /* 2 nulls and 3 backslashes */
00484   if(byte_count > sizeof(msg.bytes))
00485     return CURLE_FILESIZE_EXCEEDED;
00486 
00487   memset(&msg, 0, sizeof(msg));
00488   msg.word_count = SMB_WC_TREE_CONNECT_ANDX;
00489   msg.andx.command = SMB_COM_NO_ANDX_COMMAND;
00490   msg.pw_len = 0;
00491   MSGCAT("\\\\");
00492   MSGCAT(conn->host.name);
00493   MSGCAT("\\");
00494   MSGCATNULL(req->share);
00495   MSGCATNULL(SERVICENAME); /* Match any type of service */
00496   byte_count = p - msg.bytes;
00497   msg.byte_count = smb_swap16((unsigned short)byte_count);
00498 
00499   return smb_send_message(conn, SMB_COM_TREE_CONNECT_ANDX, &msg,
00500                           sizeof(msg) - sizeof(msg.bytes) + byte_count);
00501 }
00502 
00503 static CURLcode smb_send_open(struct connectdata *conn)
00504 {
00505   struct smb_request *req = conn->data->req.protop;
00506   struct smb_nt_create msg;
00507   size_t byte_count;
00508 
00509   if((strlen(req->path) + 1) > sizeof(msg.bytes))
00510     return CURLE_FILESIZE_EXCEEDED;
00511 
00512   memset(&msg, 0, sizeof(msg));
00513   msg.word_count = SMB_WC_NT_CREATE_ANDX;
00514   msg.andx.command = SMB_COM_NO_ANDX_COMMAND;
00515   byte_count = strlen(req->path);
00516   msg.name_length = smb_swap16((unsigned short)byte_count);
00517   msg.share_access = smb_swap32(SMB_FILE_SHARE_ALL);
00518   if(conn->data->set.upload) {
00519     msg.access = smb_swap32(SMB_GENERIC_READ | SMB_GENERIC_WRITE);
00520     msg.create_disposition = smb_swap32(SMB_FILE_OVERWRITE_IF);
00521   }
00522   else {
00523     msg.access = smb_swap32(SMB_GENERIC_READ);
00524     msg.create_disposition = smb_swap32(SMB_FILE_OPEN);
00525   }
00526   msg.byte_count = smb_swap16((unsigned short) ++byte_count);
00527   strcpy(msg.bytes, req->path);
00528 
00529   return smb_send_message(conn, SMB_COM_NT_CREATE_ANDX, &msg,
00530                           sizeof(msg) - sizeof(msg.bytes) + byte_count);
00531 }
00532 
00533 static CURLcode smb_send_close(struct connectdata *conn)
00534 {
00535   struct smb_request *req = conn->data->req.protop;
00536   struct smb_close msg;
00537 
00538   memset(&msg, 0, sizeof(msg));
00539   msg.word_count = SMB_WC_CLOSE;
00540   msg.fid = smb_swap16(req->fid);
00541 
00542   return smb_send_message(conn, SMB_COM_CLOSE, &msg, sizeof(msg));
00543 }
00544 
00545 static CURLcode smb_send_tree_disconnect(struct connectdata *conn)
00546 {
00547   struct smb_tree_disconnect msg;
00548 
00549   memset(&msg, 0, sizeof(msg));
00550 
00551   return smb_send_message(conn, SMB_COM_TREE_DISCONNECT, &msg, sizeof(msg));
00552 }
00553 
00554 static CURLcode smb_send_read(struct connectdata *conn)
00555 {
00556   struct smb_request *req = conn->data->req.protop;
00557   curl_off_t offset = conn->data->req.offset;
00558   struct smb_read msg;
00559 
00560   memset(&msg, 0, sizeof(msg));
00561   msg.word_count = SMB_WC_READ_ANDX;
00562   msg.andx.command = SMB_COM_NO_ANDX_COMMAND;
00563   msg.fid = smb_swap16(req->fid);
00564   msg.offset = smb_swap32((unsigned int) offset);
00565   msg.offset_high = smb_swap32((unsigned int) (offset >> 32));
00566   msg.min_bytes = smb_swap16(MAX_PAYLOAD_SIZE);
00567   msg.max_bytes = smb_swap16(MAX_PAYLOAD_SIZE);
00568 
00569   return smb_send_message(conn, SMB_COM_READ_ANDX, &msg, sizeof(msg));
00570 }
00571 
00572 static CURLcode smb_send_write(struct connectdata *conn)
00573 {
00574   struct smb_write *msg = (struct smb_write *)conn->data->state.uploadbuffer;
00575   struct smb_request *req = conn->data->req.protop;
00576   curl_off_t offset = conn->data->req.offset;
00577 
00578   curl_off_t upload_size = conn->data->req.size - conn->data->req.bytecount;
00579   if(upload_size >= MAX_PAYLOAD_SIZE - 1) /* There is one byte of padding */
00580     upload_size = MAX_PAYLOAD_SIZE - 1;
00581 
00582   memset(msg, 0, sizeof(*msg));
00583   msg->word_count = SMB_WC_WRITE_ANDX;
00584   msg->andx.command = SMB_COM_NO_ANDX_COMMAND;
00585   msg->fid = smb_swap16(req->fid);
00586   msg->offset = smb_swap32((unsigned int) offset);
00587   msg->offset_high = smb_swap32((unsigned int) (offset >> 32));
00588   msg->data_length = smb_swap16((unsigned short) upload_size);
00589   msg->data_offset = smb_swap16(sizeof(*msg) - sizeof(unsigned int));
00590   msg->byte_count = smb_swap16((unsigned short) (upload_size + 1));
00591 
00592   smb_format_message(conn, &msg->h, SMB_COM_WRITE_ANDX,
00593                      sizeof(*msg) - sizeof(msg->h) + (size_t) upload_size);
00594 
00595   return smb_send(conn, sizeof(*msg), (size_t) upload_size);
00596 }
00597 
00598 static CURLcode smb_send_and_recv(struct connectdata *conn, void **msg)
00599 {
00600   struct smb_conn *smbc = &conn->proto.smbc;
00601   CURLcode result;
00602 
00603   /* Check if there is data in the transfer buffer */
00604   if(!smbc->send_size && smbc->upload_size) {
00605     int nread = smbc->upload_size > BUFSIZE ? BUFSIZE :
00606                                               (int) smbc->upload_size;
00607     conn->data->req.upload_fromhere = conn->data->state.uploadbuffer;
00608     result = Curl_fillreadbuffer(conn, nread, &nread);
00609     if(result && result != CURLE_AGAIN)
00610       return result;
00611     if(!nread)
00612       return CURLE_OK;
00613 
00614     smbc->upload_size -= nread;
00615     smbc->send_size = nread;
00616     smbc->sent = 0;
00617   }
00618 
00619   /* Check if there is data to send */
00620   if(smbc->send_size) {
00621     result = smb_flush(conn);
00622     if(result)
00623       return result;
00624   }
00625 
00626   /* Check if there is still data to be sent */
00627   if(smbc->send_size || smbc->upload_size)
00628     return CURLE_AGAIN;
00629 
00630   return smb_recv_message(conn, msg);
00631 }
00632 
00633 static CURLcode smb_connection_state(struct connectdata *conn, bool *done)
00634 {
00635   struct smb_conn *smbc = &conn->proto.smbc;
00636   struct smb_negotiate_response *nrsp;
00637   struct smb_header *h;
00638   CURLcode result;
00639   void *msg = NULL;
00640 
00641   if(smbc->state == SMB_CONNECTING) {
00642 #ifdef USE_SSL
00643     if((conn->handler->flags & PROTOPT_SSL)) {
00644       bool ssl_done;
00645       result = Curl_ssl_connect_nonblocking(conn, FIRSTSOCKET, &ssl_done);
00646       if(result && result != CURLE_AGAIN)
00647         return result;
00648       if(!ssl_done)
00649         return CURLE_OK;
00650     }
00651 #endif
00652 
00653     result = smb_send_negotiate(conn);
00654     if(result) {
00655       connclose(conn, "SMB: failed to send negotiate message");
00656       return result;
00657     }
00658 
00659     conn_state(conn, SMB_NEGOTIATE);
00660   }
00661 
00662   /* Send the previous message and check for a response */
00663   result = smb_send_and_recv(conn, &msg);
00664   if(result && result != CURLE_AGAIN) {
00665     connclose(conn, "SMB: failed to communicate");
00666     return result;
00667   }
00668 
00669   if(!msg)
00670     return CURLE_OK;
00671 
00672   h = msg;
00673 
00674   switch(smbc->state) {
00675   case SMB_NEGOTIATE:
00676     if(h->status || smbc->got < sizeof(*nrsp) + sizeof(smbc->challenge) - 1) {
00677       connclose(conn, "SMB: negotiation failed");
00678       return CURLE_COULDNT_CONNECT;
00679     }
00680     nrsp = msg;
00681     memcpy(smbc->challenge, nrsp->bytes, sizeof(smbc->challenge));
00682     smbc->session_key = smb_swap32(nrsp->session_key);
00683     result = smb_send_setup(conn);
00684     if(result) {
00685       connclose(conn, "SMB: failed to send setup message");
00686       return result;
00687     }
00688     conn_state(conn, SMB_SETUP);
00689     break;
00690 
00691   case SMB_SETUP:
00692     if(h->status) {
00693       connclose(conn, "SMB: authentication failed");
00694       return CURLE_LOGIN_DENIED;
00695     }
00696     smbc->uid = smb_swap16(h->uid);
00697     conn_state(conn, SMB_CONNECTED);
00698     *done = true;
00699     break;
00700 
00701   default:
00702     smb_pop_message(conn);
00703     return CURLE_OK; /* ignore */
00704   }
00705 
00706   smb_pop_message(conn);
00707 
00708   return CURLE_OK;
00709 }
00710 
00711 static CURLcode smb_request_state(struct connectdata *conn, bool *done)
00712 {
00713   struct smb_request *req = conn->data->req.protop;
00714   struct smb_header *h;
00715   struct smb_conn *smbc = &conn->proto.smbc;
00716   enum smb_req_state next_state = SMB_DONE;
00717   unsigned short len;
00718   unsigned short off;
00719   CURLcode result;
00720   void *msg = NULL;
00721 
00722   /* Start the request */
00723   if(req->state == SMB_REQUESTING) {
00724     result = smb_send_tree_connect(conn);
00725     if(result) {
00726       connclose(conn, "SMB: failed to send tree connect message");
00727       return result;
00728     }
00729 
00730     request_state(conn, SMB_TREE_CONNECT);
00731   }
00732 
00733   /* Send the previous message and check for a response */
00734   result = smb_send_and_recv(conn, &msg);
00735   if(result && result != CURLE_AGAIN) {
00736     connclose(conn, "SMB: failed to communicate");
00737     return result;
00738   }
00739 
00740   if(!msg)
00741     return CURLE_OK;
00742 
00743   h = msg;
00744 
00745   switch(req->state) {
00746   case SMB_TREE_CONNECT:
00747     if(h->status) {
00748       req->result = CURLE_REMOTE_FILE_NOT_FOUND;
00749       if(h->status == smb_swap32(SMB_ERR_NOACCESS))
00750         req->result = CURLE_REMOTE_ACCESS_DENIED;
00751       break;
00752     }
00753     req->tid = smb_swap16(h->tid);
00754     next_state = SMB_OPEN;
00755     break;
00756 
00757   case SMB_OPEN:
00758     if(h->status || smbc->got < sizeof(struct smb_nt_create_response)) {
00759       req->result = CURLE_REMOTE_FILE_NOT_FOUND;
00760       next_state = SMB_TREE_DISCONNECT;
00761       break;
00762     }
00763     req->fid = smb_swap16(((struct smb_nt_create_response *)msg)->fid);
00764     conn->data->req.offset = 0;
00765     if(conn->data->set.upload) {
00766       conn->data->req.size = conn->data->state.infilesize;
00767       Curl_pgrsSetUploadSize(conn->data, conn->data->req.size);
00768       next_state = SMB_UPLOAD;
00769     }
00770     else {
00771       conn->data->req.size =
00772         smb_swap64(((struct smb_nt_create_response *)msg)->end_of_file);
00773       Curl_pgrsSetDownloadSize(conn->data, conn->data->req.size);
00774       next_state = SMB_DOWNLOAD;
00775     }
00776     break;
00777 
00778   case SMB_DOWNLOAD:
00779     if(h->status || smbc->got < sizeof(struct smb_header) + 14) {
00780       req->result = CURLE_RECV_ERROR;
00781       next_state = SMB_CLOSE;
00782       break;
00783     }
00784     len = Curl_read16_le(((const unsigned char *) msg) +
00785                          sizeof(struct smb_header) + 11);
00786     off = Curl_read16_le(((const unsigned char *) msg) +
00787                          sizeof(struct smb_header) + 13);
00788     if(len > 0) {
00789       if(off + sizeof(unsigned int) + len > smbc->got) {
00790         failf(conn->data, "Invalid input packet");
00791         result = CURLE_RECV_ERROR;
00792       }
00793       else
00794         result = Curl_client_write(conn, CLIENTWRITE_BODY,
00795                                    (char *)msg + off + sizeof(unsigned int),
00796                                    len);
00797       if(result) {
00798         req->result = result;
00799         next_state = SMB_CLOSE;
00800         break;
00801       }
00802     }
00803     conn->data->req.bytecount += len;
00804     conn->data->req.offset += len;
00805     Curl_pgrsSetDownloadCounter(conn->data, conn->data->req.bytecount);
00806     next_state = (len < MAX_PAYLOAD_SIZE) ? SMB_CLOSE : SMB_DOWNLOAD;
00807     break;
00808 
00809   case SMB_UPLOAD:
00810     if(h->status || smbc->got < sizeof(struct smb_header) + 6) {
00811       req->result = CURLE_UPLOAD_FAILED;
00812       next_state = SMB_CLOSE;
00813       break;
00814     }
00815     len = Curl_read16_le(((const unsigned char *) msg) +
00816                          sizeof(struct smb_header) + 5);
00817     conn->data->req.bytecount += len;
00818     conn->data->req.offset += len;
00819     Curl_pgrsSetUploadCounter(conn->data, conn->data->req.bytecount);
00820     if(conn->data->req.bytecount >= conn->data->req.size)
00821       next_state = SMB_CLOSE;
00822     else
00823       next_state = SMB_UPLOAD;
00824     break;
00825 
00826   case SMB_CLOSE:
00827     /* We don't care if the close failed, proceed to tree disconnect anyway */
00828     next_state = SMB_TREE_DISCONNECT;
00829     break;
00830 
00831   case SMB_TREE_DISCONNECT:
00832     next_state = SMB_DONE;
00833     break;
00834 
00835   default:
00836     smb_pop_message(conn);
00837     return CURLE_OK; /* ignore */
00838   }
00839 
00840   smb_pop_message(conn);
00841 
00842   switch(next_state) {
00843   case SMB_OPEN:
00844     result = smb_send_open(conn);
00845     break;
00846 
00847   case SMB_DOWNLOAD:
00848     result = smb_send_read(conn);
00849     break;
00850 
00851   case SMB_UPLOAD:
00852     result = smb_send_write(conn);
00853     break;
00854 
00855   case SMB_CLOSE:
00856     result = smb_send_close(conn);
00857     break;
00858 
00859   case SMB_TREE_DISCONNECT:
00860     result = smb_send_tree_disconnect(conn);
00861     break;
00862 
00863   case SMB_DONE:
00864     result = req->result;
00865     *done = true;
00866     break;
00867 
00868   default:
00869     break;
00870   }
00871 
00872   if(result) {
00873     connclose(conn, "SMB: failed to send message");
00874     return result;
00875   }
00876 
00877   request_state(conn, next_state);
00878 
00879   return CURLE_OK;
00880 }
00881 
00882 static CURLcode smb_done(struct connectdata *conn, CURLcode status,
00883                          bool premature)
00884 {
00885   struct smb_request *req = conn->data->req.protop;
00886 
00887   (void) premature;
00888 
00889   Curl_safefree(req->share);
00890   Curl_safefree(conn->data->req.protop);
00891 
00892   return status;
00893 }
00894 
00895 static CURLcode smb_disconnect(struct connectdata *conn, bool dead)
00896 {
00897   struct smb_conn *smbc = &conn->proto.smbc;
00898   struct smb_request *req = conn->data->req.protop;
00899 
00900   (void) dead;
00901 
00902   Curl_safefree(smbc->domain);
00903   Curl_safefree(smbc->recv_buf);
00904 
00905   /* smb_done is not always called, so cleanup the request */
00906   if(req) {
00907     Curl_safefree(req->share);
00908   }
00909 
00910   return CURLE_OK;
00911 }
00912 
00913 static int smb_getsock(struct connectdata *conn, curl_socket_t *socks,
00914                        int numsocks)
00915 {
00916   struct smb_conn *smbc = &conn->proto.smbc;
00917 
00918   if(!numsocks)
00919     return GETSOCK_BLANK;
00920 
00921   socks[0] = conn->sock[FIRSTSOCKET];
00922 
00923   if(smbc->send_size || smbc->upload_size)
00924     return GETSOCK_WRITESOCK(0);
00925 
00926   return GETSOCK_READSOCK(0);
00927 }
00928 
00929 static CURLcode smb_parse_url_path(struct connectdata *conn)
00930 {
00931   CURLcode result = CURLE_OK;
00932   struct Curl_easy *data = conn->data;
00933   struct smb_request *req = data->req.protop;
00934   char *path;
00935   char *slash;
00936 
00937   /* URL decode the path */
00938   result = Curl_urldecode(data, data->state.path, 0, &path, NULL, TRUE);
00939   if(result)
00940     return result;
00941 
00942   /* Parse the path for the share */
00943   req->share = strdup((*path == '/' || *path == '\\') ? path + 1 : path);
00944   if(!req->share) {
00945     free(path);
00946 
00947     return CURLE_OUT_OF_MEMORY;
00948   }
00949 
00950   slash = strchr(req->share, '/');
00951   if(!slash)
00952     slash = strchr(req->share, '\\');
00953 
00954   /* The share must be present */
00955   if(!slash) {
00956     free(path);
00957 
00958     return CURLE_URL_MALFORMAT;
00959   }
00960 
00961   /* Parse the path for the file path converting any forward slashes into
00962      backslashes */
00963   *slash++ = 0;
00964   req->path = slash;
00965   for(; *slash; slash++) {
00966     if(*slash == '/')
00967       *slash = '\\';
00968   }
00969 
00970   free(path);
00971 
00972   return CURLE_OK;
00973 }
00974 
00975 #endif /* !USE_WINDOWS_SSPI || USE_WIN32_CRYPTO */
00976 
00977 #endif /* CURL_DISABLE_SMB && USE_NTLM && CURL_SIZEOF_CURL_OFF_T > 4 */


rc_visard_driver
Author(s): Heiko Hirschmueller , Christian Emmerich , Felix Ruess
autogenerated on Thu Jun 6 2019 20:43:06