00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
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
00051 #include "curl_memory.h"
00052 #include "memdebug.h"
00053
00054
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
00068
00069 const struct Curl_handler Curl_handler_smb = {
00070 "SMB",
00071 smb_setup_connection,
00072 ZERO_NULL,
00073 smb_done,
00074 ZERO_NULL,
00075 smb_connect,
00076 smb_connection_state,
00077 smb_request_state,
00078 smb_getsock,
00079 smb_getsock,
00080 ZERO_NULL,
00081 ZERO_NULL,
00082 smb_disconnect,
00083 ZERO_NULL,
00084 PORT_SMB,
00085 CURLPROTO_SMB,
00086 PROTOPT_NONE
00087 };
00088
00089 #ifdef USE_SSL
00090
00091
00092
00093 const struct Curl_handler Curl_handler_smbs = {
00094 "SMBS",
00095 smb_setup_connection,
00096 ZERO_NULL,
00097 smb_done,
00098 ZERO_NULL,
00099 smb_connect,
00100 smb_connection_state,
00101 smb_request_state,
00102 smb_getsock,
00103 smb_getsock,
00104 ZERO_NULL,
00105 ZERO_NULL,
00106 smb_disconnect,
00107 ZERO_NULL,
00108 PORT_SMBS,
00109 CURLPROTO_SMBS,
00110 PROTOPT_SSL
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
00120 #define MSGCAT(str) \
00121 strcpy(p, (str)); \
00122 p += strlen(str);
00123
00124
00125 #define MSGCATNULL(str) \
00126 strcpy(p, (str)); \
00127 p += strlen(str) + 1;
00128
00129
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
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
00175 struct smb_request {
00176 enum smb_req_state state;
00177 char *share;
00178 char *path;
00179 unsigned short tid;
00180 unsigned short fid;
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
00189 static const char * const names[] = {
00190 "SMB_NOT_CONNECTED",
00191 "SMB_CONNECTING",
00192 "SMB_NEGOTIATE",
00193 "SMB_SETUP",
00194 "SMB_CONNECTED",
00195
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
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
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
00237 conn->data->req.protop = req = calloc(1, sizeof(struct smb_request));
00238 if(!req)
00239 return CURLE_OUT_OF_MEMORY;
00240
00241
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
00253 if(!conn->bits.user_passwd)
00254 return CURLE_LOGIN_DENIED;
00255
00256
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
00264 connkeep(conn, "SMB default");
00265
00266
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
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
00319 msg_size += 1 + ((unsigned char) buf[msg_size]) * sizeof(unsigned short);
00320 if(nbt_size >= msg_size + sizeof(unsigned short)) {
00321
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;
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;
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);
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)
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
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
00620 if(smbc->send_size) {
00621 result = smb_flush(conn);
00622 if(result)
00623 return result;
00624 }
00625
00626
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
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;
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
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
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
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;
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
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
00938 result = Curl_urldecode(data, data->state.path, 0, &path, NULL, TRUE);
00939 if(result)
00940 return result;
00941
00942
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
00955 if(!slash) {
00956 free(path);
00957
00958 return CURLE_URL_MALFORMAT;
00959 }
00960
00961
00962
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
00976
00977 #endif