PS2SDK
PS2 Homebrew Libraries
Loading...
Searching...
No Matches
smb.c
1/*
2 Copyright 2009-2010, jimmikaelkael
3 Licenced under Academic Free License version 3.0
4*/
5
6#include <types.h>
7#include <errno.h>
8#include <irx.h>
9#include <limits.h>
10#include <stdio.h>
11#include <sysclib.h>
12#include <ps2ip.h>
13#include <ioman.h>
14
15#include "smb.h"
16#include "auth.h"
17#include "poll.h"
18#include "debug.h"
19
20// Round up the erasure amount, so that memset can erase memory word-by-word.
21#define ZERO_PKT_ALIGNED(hdr, hdrSize) memset((hdr), 0, ((hdrSize) + 3) & ~3)
22
23static server_specs_t server_specs;
24
25#define LM_AUTH 0
26#define NTLM_AUTH 1
27
28#define CLIENT_MAX_BUFFER_SIZE USHRT_MAX // Allow up to 65535 bytes to be received.
29#define CLIENT_MAX_XFER_SIZE USHRT_MAX // Allow up to 65535 bytes to be transferred.
30
31static int main_socket = -1;
32
33static struct
34{
35 // Direct transport packet header. This is also a NetBIOS session header.
36 u32 sessionHeader; // The lower 24 bytes are the length of the payload in network byte-order, while the upper 8 bits must be set to 0 (Session Message Packet).
37 union
38 {
39 u8 u8buff[MAX_SMB_BUF + MAX_SMB_BUF_HDR];
40 u16 u16buff[(MAX_SMB_BUF + MAX_SMB_BUF_HDR) / sizeof(u16)];
41 s16 s16buff[(MAX_SMB_BUF + MAX_SMB_BUF_HDR) / sizeof(s16)];
42 NegotiateProtocolRequest_t negotiateProtocolRequest;
43 NegotiateProtocolResponse_t negotiateProtocolResponse;
44 SessionSetupAndXRequest_t sessionSetupAndXRequest;
45 SessionSetupAndXResponse_t sessionSetupAndXResponse;
46 TreeConnectAndXRequest_t treeConnectAndXRequest;
47 TreeConnectAndXResponse_t treeConnectAndXResponse;
48 TreeDisconnectRequest_t treeDisconnectRequest;
49 TreeDisconnectResponse_t treeDisconnectResponse;
50 NetShareEnumRequest_t netShareEnumRequest;
51 NetShareEnumResponse_t netShareEnumResponse;
52 LogOffAndXRequest_t logOffAndXRequest;
53 LogOffAndXResponse_t logOffAndXResponse;
54 EchoRequest_t echoRequest;
55 EchoResponse_t echoResponse;
56 QueryInformationDiskRequest_t queryInformationDiskRequest;
57 QueryInformationDiskResponse_t queryInformationDiskResponse;
58 QueryPathInformationRequest_t queryPathInformationRequest;
59 QueryPathInformationResponse_t queryPathInformationResponse;
60 FindFirstNext2Request_t findFirstNext2Request;
61 FindFirstNext2Response_t findFirstNext2Response;
62 NTCreateAndXRequest_t ntCreateAndXRequest;
63 NTCreateAndXResponse_t ntCreateAndXResponse;
64 OpenAndXRequest_t openAndXRequest;
65 OpenAndXResponse_t openAndXResponse;
66 ReadAndXRequest_t readAndXRequest;
67 ReadAndXResponse_t readAndXResponse;
68 WriteAndXRequest_t writeAndXRequest;
69 WriteAndXResponse_t writeAndXResponse;
70 CloseRequest_t closeRequest;
71 CloseResponse_t closeResponse;
72 DeleteRequest_t deleteRequest;
73 DeleteResponse_t deleteResponse;
74 ManageDirectoryRequest_t manageDirectoryRequest;
75 ManageDirectoryResponse_t manageDirectoryResponse;
76 RenameRequest_t renameRequest;
77 RenameResponse_t renameResponse;
78 } smb;
79} __attribute__((packed)) SMB_buf;
80
81//-------------------------------------------------------------------------
82server_specs_t *getServerSpecs(void)
83{
84 return &server_specs;
85}
86
87//-------------------------------------------------------------------------
88static void nb_SetSessionMessage(u32 size) // Write Session Service header: careful it's raw TCP transport here and not NBT transport
89{
90 // maximum for raw TCP transport (24 bits) !!!
91 // Byte-swap length into network byte-order.
92 SMB_buf.sessionHeader = ((size & 0xff0000) >> 8) | ((size & 0xff00) << 8) | ((size & 0xff) << 24);
93}
94
95//-------------------------------------------------------------------------
96static int nb_GetSessionMessageLength(void) // Read Session Service header length: careful it's raw TCP transport here and not NBT transport
97{
98 u32 size;
99
100 // maximum for raw TCP transport (24 bits) !!!
101 // Byte-swap length from network byte-order.
102 size = ((SMB_buf.sessionHeader << 8) & 0xff0000) | ((SMB_buf.sessionHeader >> 8) & 0xff00) | ((SMB_buf.sessionHeader >> 24) & 0xff);
103
104 return (int)size;
105}
106
107static u8 nb_GetPacketType(void) // Read Session Service header type.
108{
109 // Byte-swap length from network byte-order.
110 return ((u8)(SMB_buf.sessionHeader & 0xff));
111}
112
113//-------------------------------------------------------------------------
114static int OpenTCPSession(struct in_addr dst_IP, u16 dst_port, int *sock)
115{
116 int sck, ret, opt;
117 struct sockaddr_in sock_addr;
118
119 *sock = -1;
120
121 // Creating socket
122 sck = lwip_socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
123 if (sck < 0)
124 return -1;
125
126 *sock = sck;
127
128 opt = 1;
129 lwip_setsockopt(sck, IPPROTO_TCP, TCP_NODELAY, (char *)&opt, sizeof(opt));
130
131 memset(&sock_addr, 0, sizeof(sock_addr));
132 sock_addr.sin_addr = dst_IP;
133 sock_addr.sin_family = AF_INET;
134 sock_addr.sin_port = htons(dst_port);
135
136 ret = lwip_connect(sck, (struct sockaddr *)&sock_addr, sizeof(sock_addr));
137 if (ret < 0)
138 return -2;
139
140 return 0;
141}
142
143//-------------------------------------------------------------------------
144/* RecvTimeout() is used instead of just recv(), in order to prevent
145 lockups in the event of a network failure in-between calls to socket functions.
146
147 TCP only protects against failures that occur during calls to socket functions.
148
149 Just to illustrate its importance here, imagine what would happen if recv() is called,
150 right before the server terminates the connection without the PlayStation 2 ever
151 receiving the TCP retransmission and the TCP RST packets. */
152static int RecvTimeout(int sock, void *buf, int bsize, int timeout_ms)
153{
154 int ret;
155 struct pollfd pollfd[1];
156
157 pollfd->fd = sock;
158 pollfd->events = POLLIN;
159 pollfd->revents = 0;
160
161 ret = poll(pollfd, 1, timeout_ms);
162
163 // a result less than 0 is an error
164 if (ret < 0)
165 return -1;
166
167 // 0 is a timeout
168 if (ret == 0)
169 return 0;
170
171 // receive the packet
172 ret = lwip_recv(sock, buf, bsize, 0);
173 if (ret < 0)
174 return -2;
175
176 return ret;
177}
178
179static int SendData(int sock, char *buf, int size)
180{
181 int remaining;
182 char *ptr;
183
184 ptr = buf;
185 remaining = size;
186 while (remaining > 0) {
187 int result;
188
189 result = lwip_send(sock, ptr, remaining, 0);
190 if (result <= 0)
191 return result;
192
193 ptr += result;
194 remaining -= result;
195 }
196
197 return size;
198}
199
200static int RecvData(int sock, char *buf, int size, int timeout_ms)
201{
202 int remaining;
203 char *ptr;
204
205 ptr = buf;
206 remaining = size;
207 while (remaining > 0) {
208 int result;
209
210 result = RecvTimeout(sock, ptr, remaining, timeout_ms);
211 if (result <= 0)
212 return result;
213
214 ptr += result;
215 remaining -= result;
216 }
217
218 return size;
219}
220
221//-------------------------------------------------------------------------
222static int GetSMBServerReply(int shdrlen, void *spayload, int rhdrlen)
223{
224 int rcv_size, totalpkt_size, size;
225
226 totalpkt_size = nb_GetSessionMessageLength() + 4;
227
228 if (shdrlen == 0) {
229 // Send the whole message, including the 4-byte direct transport packet header.
230 rcv_size = SendData(main_socket, (char *)&SMB_buf, totalpkt_size);
231 if (rcv_size <= 0)
232 return -1;
233 } else {
234 size = shdrlen + 4;
235
236 // Send the headers, followed by the payload.
237 rcv_size = SendData(main_socket, (char *)&SMB_buf, size);
238 if (rcv_size <= 0)
239 return -1;
240
241 rcv_size = SendData(main_socket, spayload, totalpkt_size - size);
242 if (rcv_size <= 0)
243 return -1;
244 }
245
246 // Read NetBIOS session message header. Drop NBSS Session Keep alive messages (type == 0x85, with no body), but process session messages (type == 0x00).
247 do {
248 rcv_size = RecvData(main_socket, (char *)&SMB_buf.sessionHeader, sizeof(SMB_buf.sessionHeader), 10000); // 10s before the packet is considered lost
249 if (rcv_size <= 0)
250 return -2;
251 } while (nb_GetPacketType() != 0);
252
253 totalpkt_size = nb_GetSessionMessageLength();
254
255 // If rhdrlen is not specified, retrieve the whole packet. Otherwise, retrieve only the headers (caller will retrieve the payload separately).
256 size = (rhdrlen == 0) ? totalpkt_size : rhdrlen;
257 rcv_size = RecvData(main_socket, (char *)&SMB_buf.smb, size, 3000); // 3s before the packet is considered lost
258 if (rcv_size <= 0)
259 return -2;
260
261 return totalpkt_size;
262}
263
264//-------------------------------------------------------------------------
265// These functions will process UTF-16 characters on a byte-level, so that they will be safe for use with byte-alignment.
266static int asciiToUtf16(char *out, const char *in)
267{
268 int len;
269 const char *pIn;
270 char *pOut;
271
272 for (pIn = in, pOut = out, len = 0; *pIn != '\0'; pIn++, pOut += 2, len += 2) {
273 pOut[0] = *pIn;
274 pOut[1] = '\0';
275 }
276
277 pOut[0] = '\0'; // NULL terminate.
278 pOut[1] = '\0';
279 len += 2;
280
281 return len;
282}
283
284static int utf16ToAscii(char *out, const char *in, int inbytes)
285{
286 int len, bytesProcessed;
287 const char *pIn;
288 char *pOut;
289 u16 wchar;
290
291 for (pIn = in, pOut = out, len = 0, bytesProcessed = 0; (inbytes == 0) || (bytesProcessed < inbytes); len++) {
292 wchar = pIn[0] | ((u16)pIn[1] << 8);
293 if (wchar == '\0')
294 break;
295
296 if (wchar >= 0xD800 && wchar < 0xDC00) { // Skip surrogate. Replace unsupported character with '?'.
297 *pOut = '?';
298
299 pIn += 4;
300 bytesProcessed += 4;
301 pOut++;
302 } else {
303 // Write decoded character. Replace unsupported characters with '?'.
304 *pOut = (wchar > 128) ? '?' : (char)wchar;
305
306 pIn += 2;
307 bytesProcessed += 2;
308 pOut++;
309 }
310 }
311
312 pOut[0] = '\0'; // NULL terminate.
313 len++;
314
315 return len;
316}
317
318static int setStringField(char *out, const char *in)
319{
320 int len;
321
322 if (server_specs.Capabilities & SERVER_CAP_UNICODE) {
323 len = asciiToUtf16(out, in);
324 } else {
325 len = strlen(in) + 1;
326 strcpy(out, in);
327 }
328
329 return len;
330}
331
332static int getStringField(char *out, const char *in, int inbytes)
333{
334 int len;
335
336 if (server_specs.Capabilities & SERVER_CAP_UNICODE) {
337 len = utf16ToAscii(out, in, inbytes);
338 } else {
339 if (inbytes != 0) {
340 strncpy(out, in, inbytes);
341 out[inbytes] = '\0';
342 } else
343 strcpy(out, in);
344 len = strlen(out) + 1;
345 }
346
347 return len;
348}
349
350//-------------------------------------------------------------------------
351int smb_NegotiateProtocol(u32 *capabilities)
352{
353 static char *dialect = "NT LM 0.12";
354 int r, length, retry_count;
355 NegotiateProtocolRequest_t *NPR = &SMB_buf.smb.negotiateProtocolRequest;
356 NegotiateProtocolResponse_t *NPRsp = &SMB_buf.smb.negotiateProtocolResponse;
357
358 retry_count = 0;
359
360negotiate_retry:
361
362 ZERO_PKT_ALIGNED(NPR, sizeof(NegotiateProtocolRequest_t));
363
364 NPR->smbH.Magic = SMB_MAGIC;
365 NPR->smbH.Cmd = SMB_COM_NEGOTIATE;
366 NPR->smbH.Flags = SMB_FLAGS_CASELESS_PATHNAMES;
367 NPR->smbH.Flags2 = SMB_FLAGS2_KNOWS_LONG_NAMES | SMB_FLAGS2_32BIT_STATUS;
368 length = strlen(dialect);
369 NPR->ByteCount = length + sizeof(NPR->DialectFormat) + 1;
370 NPR->DialectFormat = 0x02;
371 strcpy(NPR->DialectName, dialect);
372
373 nb_SetSessionMessage(sizeof(NegotiateProtocolRequest_t) + length + 1);
374 r = GetSMBServerReply(0, NULL, 0);
375 if (r <= 0)
376 goto negotiate_error;
377
378 // check sanity of SMB header
379 if (NPRsp->smbH.Magic != SMB_MAGIC)
380 goto negotiate_error;
381
382 // check there's no error
383 if (NPRsp->smbH.Eclass != STATUS_SUCCESS)
384 goto negotiate_error;
385
386 if (NPRsp->smbWordcount != 17)
387 goto negotiate_error;
388
389 *capabilities = NPRsp->Capabilities & (CLIENT_CAP_LARGE_WRITEX | CLIENT_CAP_LARGE_READX | CLIENT_CAP_UNICODE | CLIENT_CAP_LARGE_FILES | CLIENT_CAP_STATUS32);
390
391 server_specs.Capabilities = NPRsp->Capabilities;
392 server_specs.SecurityMode = (NPRsp->SecurityMode & NEGOTIATE_SECURITY_USER_LEVEL) ? SERVER_USER_SECURITY_LEVEL : SERVER_SHARE_SECURITY_LEVEL;
393 server_specs.PasswordType = (NPRsp->SecurityMode & NEGOTIATE_SECURITY_CHALLENGE_RESPONSE) ? SERVER_USE_ENCRYPTED_PASSWORD : SERVER_USE_PLAINTEXT_PASSWORD;
394
395 // copy to global to keep needed information for further communication
396 server_specs.MaxBufferSize = NPRsp->MaxBufferSize;
397 server_specs.MaxMpxCount = NPRsp->MaxMpxCount;
398 server_specs.SessionKey = NPRsp->SessionKey;
399 memcpy(server_specs.EncryptionKey, NPRsp->ByteField, NPRsp->KeyLength);
400 getStringField(server_specs.PrimaryDomainServerName, (char *)&NPRsp->ByteField[NPRsp->KeyLength], 0);
401
402 return 0;
403
404negotiate_error:
405 retry_count++;
406
407 if (retry_count < 3)
408 goto negotiate_retry;
409
410 return -1;
411}
412
413//-------------------------------------------------------------------------
414static int AddPassword(char *Password, int PasswordType, int AuthType, void *AnsiPassLen, void *UnicodePassLen, u8 *Buffer)
415{
416 u8 passwordhash[16];
417 u8 LMresponse[24];
418 u16 passwordlen = 0;
419
420 if ((Password) && (PasswordType != NO_PASSWORD)) {
421 if (server_specs.PasswordType == SERVER_USE_ENCRYPTED_PASSWORD) {
422 passwordlen = 24;
423 switch (PasswordType) {
424 case HASHED_PASSWORD:
425 if (AuthType == LM_AUTH) {
426 memcpy(passwordhash, Password, 16);
427 memcpy(AnsiPassLen, &passwordlen, 2);
428 }
429 if (AuthType == NTLM_AUTH) {
430 memcpy(passwordhash, &Password[16], 16);
431 memcpy(UnicodePassLen, &passwordlen, 2);
432 }
433 break;
434
435 default:
436 if (AuthType == LM_AUTH) {
437 LM_Password_Hash((const unsigned char *)Password, passwordhash);
438 memcpy(AnsiPassLen, &passwordlen, 2);
439 } else if (AuthType == NTLM_AUTH) {
440 NTLM_Password_Hash((const unsigned char *)Password, passwordhash);
441 memcpy(UnicodePassLen, &passwordlen, 2);
442 }
443 }
444 LM_Response(passwordhash, server_specs.EncryptionKey, LMresponse);
445 memcpy(Buffer, LMresponse, passwordlen);
446 } else if (server_specs.PasswordType == SERVER_USE_PLAINTEXT_PASSWORD) {
447 // It seems that PlainText passwords and Unicode isn't meant to be...
448 passwordlen = strlen(Password);
449 if (passwordlen > 14)
450 passwordlen = 14;
451 else if (passwordlen == 0)
452 passwordlen = 1;
453 memcpy(AnsiPassLen, &passwordlen, 2);
454 memcpy(Buffer, Password, passwordlen);
455 }
456 } else {
457 if (server_specs.SecurityMode == SERVER_SHARE_SECURITY_LEVEL) {
458 passwordlen = 1;
459 memcpy(AnsiPassLen, &passwordlen, 2);
460 Buffer[0] = 0;
461 }
462 }
463
464 return passwordlen;
465}
466
467//-------------------------------------------------------------------------
468int smb_SessionSetupAndX(char *User, char *Password, int PasswordType, u32 capabilities)
469{
470 SessionSetupAndXRequest_t *SSR = &SMB_buf.smb.sessionSetupAndXRequest;
471 SessionSetupAndXResponse_t *SSRsp = &SMB_buf.smb.sessionSetupAndXResponse;
472 int r, offset, useUnicode;
473 int passwordlen = 0;
474 int AuthType = NTLM_AUTH;
475
476lbl_session_setup:
477
478 ZERO_PKT_ALIGNED(SSR, sizeof(SessionSetupAndXRequest_t));
479
480 useUnicode = (server_specs.Capabilities & SERVER_CAP_UNICODE) ? 1 : 0;
481
482 SSR->smbH.Magic = SMB_MAGIC;
483 SSR->smbH.Cmd = SMB_COM_SESSION_SETUP_ANDX;
484 SSR->smbH.Flags = SMB_FLAGS_CASELESS_PATHNAMES;
485 SSR->smbH.Flags2 = SMB_FLAGS2_KNOWS_LONG_NAMES | SMB_FLAGS2_32BIT_STATUS;
486 if (useUnicode)
487 SSR->smbH.Flags2 |= SMB_FLAGS2_UNICODE_STRING;
488 SSR->smbWordcount = 13;
489 SSR->smbAndxCmd = SMB_COM_NONE; // no ANDX command
490 SSR->MaxBufferSize = CLIENT_MAX_BUFFER_SIZE;
491 SSR->MaxMpxCount = server_specs.MaxMpxCount >= 2 ? 2 : (u16)server_specs.MaxMpxCount;
492 SSR->VCNumber = 1;
493 SSR->SessionKey = server_specs.SessionKey;
494 SSR->Capabilities = capabilities;
495
496 // Fill ByteField
497 offset = 0;
498
499 if (server_specs.SecurityMode == SERVER_USER_SECURITY_LEVEL) {
500 passwordlen = AddPassword(Password, PasswordType, AuthType, &SSR->AnsiPasswordLength, &SSR->UnicodePasswordLength, &SSR->ByteField[0]);
501 offset += passwordlen;
502 }
503
504 if ((useUnicode) && (!(passwordlen & 1))) {
505 SSR->ByteField[offset] = '\0';
506 offset++; // pad needed only for unicode as aligment fix if password length is even
507 }
508
509 // Add User name
510 offset += setStringField((char *)&SSR->ByteField[offset], User);
511
512 // PrimaryDomain, acquired from Negotiate Protocol Response data
513 offset += setStringField((char *)&SSR->ByteField[offset], server_specs.PrimaryDomainServerName);
514
515 // NativeOS
516 if (useUnicode && ((offset & 1) != 0)) { // If Unicode is used, the field must begin on a 16-bit aligned address.
517 SSR->ByteField[offset] = '\0';
518 offset++;
519 }
520 offset += setStringField((char *)&SSR->ByteField[offset], "PlayStation 2");
521
522 // NativeLanMan
523 if (useUnicode && ((offset & 1) != 0)) { // If Unicode is used, the field must begin on a 16-bit aligned address.
524 SSR->ByteField[offset] = '\0';
525 offset++;
526 }
527 offset += setStringField((char *)&SSR->ByteField[offset], "SMBMAN");
528
529 SSR->ByteCount = offset;
530
531 nb_SetSessionMessage(sizeof(SessionSetupAndXRequest_t) + offset);
532 r = GetSMBServerReply(0, NULL, 0);
533 if (r <= 0)
534 return -EIO;
535
536 // check sanity of SMB header
537 if (SSRsp->smbH.Magic != SMB_MAGIC)
538 return -EIO;
539
540 // check there's no error (NT STATUS error type!)
541 switch (SSRsp->smbH.Eclass | (SSRsp->smbH.Ecode << 16)) {
542 case STATUS_SUCCESS:
543 break;
544 case STATUS_LOGON_FAILURE: // check there's no auth failure
545 if ((server_specs.SecurityMode == SERVER_USER_SECURITY_LEVEL) && (AuthType == NTLM_AUTH)) {
546 AuthType = LM_AUTH;
547 goto lbl_session_setup;
548 }
549
550 return -EACCES;
551 default:
552 return -EIO;
553 }
554
555 // return UID
556 return (int)SSRsp->smbH.UID;
557}
558
559//-------------------------------------------------------------------------
560int smb_TreeConnectAndX(int UID, char *ShareName, char *Password, int PasswordType) // PasswordType: 0 = PlainText, 1 = Hash
561{
562 TreeConnectAndXRequest_t *TCR = &SMB_buf.smb.treeConnectAndXRequest;
563 TreeConnectAndXResponse_t *TCRsp = &SMB_buf.smb.treeConnectAndXResponse;
564 int r, offset;
565 int passwordlen = 0;
566 int AuthType = NTLM_AUTH;
567
568lbl_tree_connect:
569
570 ZERO_PKT_ALIGNED(TCR, sizeof(TreeConnectAndXRequest_t));
571
572 TCR->smbH.Magic = SMB_MAGIC;
573 TCR->smbH.Cmd = SMB_COM_TREE_CONNECT_ANDX;
574 TCR->smbH.Flags = SMB_FLAGS_CASELESS_PATHNAMES;
575 TCR->smbH.Flags2 = SMB_FLAGS2_KNOWS_LONG_NAMES | SMB_FLAGS2_32BIT_STATUS;
576 if (server_specs.Capabilities & SERVER_CAP_UNICODE)
577 TCR->smbH.Flags2 |= SMB_FLAGS2_UNICODE_STRING;
578 TCR->smbH.UID = UID;
579 TCR->smbWordcount = 4;
580 TCR->smbAndxCmd = SMB_COM_NONE; // no ANDX command
581
582 // Fill ByteField
583 offset = 0;
584
585 if (server_specs.SecurityMode == SERVER_SHARE_SECURITY_LEVEL)
586 passwordlen = AddPassword(Password, PasswordType, AuthType, &TCR->PasswordLength, &TCR->PasswordLength, &TCR->ByteField[offset]);
587 else {
588 passwordlen = 1;
589 TCR->PasswordLength = passwordlen;
590 }
591 offset += passwordlen;
592
593 if ((server_specs.Capabilities & SERVER_CAP_UNICODE) && (!(passwordlen & 1))) {
594 TCR->ByteField[offset] = '\0';
595 offset++; // pad needed only for unicode as aligment fix is password len is even
596 }
597
598 // Add share name
599 offset += setStringField((char *)&TCR->ByteField[offset], ShareName);
600
601 memcpy(&TCR->ByteField[offset], "?????\0", 6); // Service, any type of device
602 offset += 6;
603
604 TCR->ByteCount = offset;
605
606 nb_SetSessionMessage(sizeof(TreeConnectAndXRequest_t) + offset);
607 r = GetSMBServerReply(0, NULL, 0);
608 if (r <= 0)
609 return -EIO;
610
611 // check sanity of SMB header
612 if (TCRsp->smbH.Magic != SMB_MAGIC)
613 return -EIO;
614
615 // check there's no error (NT STATUS error type!)
616 switch (TCRsp->smbH.Eclass | (TCRsp->smbH.Ecode << 16)) {
617 case STATUS_SUCCESS:
618 break;
619 case STATUS_LOGON_FAILURE: // check there's no auth failure
620 if ((server_specs.SecurityMode == SERVER_USER_SECURITY_LEVEL) && (AuthType == NTLM_AUTH)) {
621 AuthType = LM_AUTH;
622 goto lbl_tree_connect;
623 }
624
625 return -EACCES;
626 default:
627 return -EIO;
628 }
629
630 // return TID
631 return (int)TCRsp->smbH.TID;
632}
633
634//-------------------------------------------------------------------------
635int smb_NetShareEnum(int UID, int TID, ShareEntry_t *shareEntries, int index, int maxEntries)
636{
637 int r, i;
638 int count = 0;
639 NetShareEnumRequest_t *NSER = &SMB_buf.smb.netShareEnumRequest;
640 NetShareEnumResponse_t *NSERsp = &SMB_buf.smb.netShareEnumResponse;
641
642 ZERO_PKT_ALIGNED(NSER, sizeof(NetShareEnumRequest_t));
643
644 NSER->smbH.Magic = SMB_MAGIC;
645 NSER->smbH.Cmd = SMB_COM_TRANSACTION;
646 NSER->smbH.UID = (u16)UID;
647 NSER->smbH.TID = (u16)TID;
648 NSER->smbWordcount = 14;
649
650 NSER->smbTrans.TotalParamCount = NSER->smbTrans.ParamCount = 19;
651 NSER->smbTrans.MaxParamCount = 1024;
652 NSER->smbTrans.MaxDataCount = MAX_SMB_BUF - NSER->smbTrans.MaxParamCount;
653 NSER->smbTrans.ParamOffset = 76;
654 NSER->smbTrans.DataOffset = 95;
655
656 NSER->ByteCount = 32;
657
658 // SMB PIPE PROTOCOL
659 // Transaction Name: "\PIPE\LANMAN"
660 // Function Code : 0x0000 = NetShareEnum
661 // Parameter Descriptor: "WrLeh"
662 // Return Descriptor: "B13BWz"
663 // Detail Level: 0x0001
664 // Receive Buffer Length: 0x1fa0
665 memcpy(&NSER->ByteField[0], "\\PIPE\\LANMAN\0\0\0WrLeh\0B13BWz\0\x01\0\xa0\x1f", 32);
666
667 nb_SetSessionMessage(sizeof(NetShareEnumRequest_t) + NSER->ByteCount);
668 r = GetSMBServerReply(0, NULL, 0);
669 if (r <= 0)
670 return -EIO;
671
672 // check sanity of SMB header
673 if (NSERsp->smbH.Magic != SMB_MAGIC)
674 return -EIO;
675
676 switch (NSERsp->smbH.Eclass | (NSERsp->smbH.Ecode << 16)) {
677 // check there's no error
678 case STATUS_SUCCESS:
679 break;
680 // check if access denied
681 case STATUS_ACCESS_DENIED:
682 return -EACCES;
683 default:
684 return -EIO;
685 }
686
687 // API status must be 0
688 if (SMB_buf.smb.u16buff[NSERsp->smbTrans.ParamOffset / sizeof(u16)] != 0)
689 return -EIO;
690
691 // available entries
692 int AvailableEntries = SMB_buf.smb.s16buff[(NSERsp->smbTrans.ParamOffset + 6) / sizeof(s16)];
693
694 // data start
695 char *data = (char *)&SMB_buf.smb.u8buff[NSERsp->smbTrans.DataOffset];
696 char *p = data;
697
698 for (i = 0; i < AvailableEntries; i++) {
699
700 // calculate the padding after the Share name
701 int padding = (strlen(p) + 1 + 2) % 16 ? 16 - ((strlen(p) + 1) % 16) : 0;
702
703 if (*((u16 *)&p[strlen(p) + 1 + padding - 2]) == 0) { // Directory Tree type
704 if (maxEntries > 0) {
705 if ((count < maxEntries) && (i >= index)) {
706 count++;
707 strncpy(shareEntries->ShareName, p, 256);
708 strncpy(shareEntries->ShareComment, &data[*((u16 *)&p[strlen(p) + 1 + padding])], 256);
709 shareEntries++;
710 }
711 } else // if maxEntries is 0 then we're just counting shares
712 count++;
713 }
714 p += strlen(p) + 1 + padding + 4;
715 }
716
717 return count;
718}
719
720//-------------------------------------------------------------------------
721int smb_QueryInformationDisk(int UID, int TID, smbQueryDiskInfo_out_t *QueryInformationDisk)
722{
723 int r;
724 QueryInformationDiskRequest_t *QIDR = &SMB_buf.smb.queryInformationDiskRequest;
725 QueryInformationDiskResponse_t *QIDRsp = &SMB_buf.smb.queryInformationDiskResponse;
726
727 ZERO_PKT_ALIGNED(QIDR, sizeof(QueryInformationDiskRequest_t));
728
729 QIDR->smbH.Magic = SMB_MAGIC;
730 QIDR->smbH.Cmd = SMB_COM_QUERY_INFORMATION_DISK;
731 QIDR->smbH.UID = (u16)UID;
732 QIDR->smbH.TID = (u16)TID;
733
734 nb_SetSessionMessage(sizeof(QueryInformationDiskRequest_t));
735 r = GetSMBServerReply(0, NULL, 0);
736 if (r <= 0)
737 return -EIO;
738
739 // check sanity of SMB header
740 if (QIDRsp->smbH.Magic != SMB_MAGIC)
741 return -EIO;
742
743 switch (QIDRsp->smbH.Eclass | (QIDRsp->smbH.Ecode << 16)) {
744 // check there's no error
745 case STATUS_SUCCESS:
746 break;
747 // check if access denied
748 case STATUS_ACCESS_DENIED:
749 return -EACCES;
750 default:
751 return -EIO;
752 }
753
754 QueryInformationDisk->TotalUnits = QIDRsp->TotalUnits;
755 QueryInformationDisk->BlocksPerUnit = QIDRsp->BlocksPerUnit;
756 QueryInformationDisk->BlockSize = QIDRsp->BlockSize;
757 QueryInformationDisk->FreeUnits = QIDRsp->FreeUnits;
758
759 return 0;
760}
761
762//-------------------------------------------------------------------------
763int smb_QueryPathInformation(int UID, int TID, PathInformation_t *Info, char *Path)
764{
765 int r, PathLen, queryType;
766 QueryPathInformationRequest_t *QPIR = &SMB_buf.smb.queryPathInformationRequest;
767 QueryPathInformationResponse_t *QPIRsp = &SMB_buf.smb.queryPathInformationResponse;
768
769 queryType = SMB_QUERY_FILE_BASIC_INFO;
770
771query:
772
773 ZERO_PKT_ALIGNED(QPIR, sizeof(QueryPathInformationRequest_t));
774
775 QPIR->smbH.Magic = SMB_MAGIC;
776 QPIR->smbH.Cmd = SMB_COM_TRANSACTION2;
777 QPIR->smbH.Flags = SMB_FLAGS_CANONICAL_PATHNAMES;
778 QPIR->smbH.Flags2 = SMB_FLAGS2_KNOWS_LONG_NAMES | SMB_FLAGS2_32BIT_STATUS;
779 if (server_specs.Capabilities & SERVER_CAP_UNICODE)
780 QPIR->smbH.Flags2 |= SMB_FLAGS2_UNICODE_STRING;
781 QPIR->smbH.UID = (u16)UID;
782 QPIR->smbH.TID = (u16)TID;
783 QPIR->smbWordcount = 15;
784
785 QPIR->smbTrans.SetupCount = 1;
786 QPIR->SubCommand = TRANS2_QUERY_PATH_INFORMATION;
787
788 QPIR->smbTrans.ParamOffset = 68;
789 QPIR->smbTrans.MaxParamCount = 256; // Max Parameters len in reply
790 QPIR->smbTrans.MaxDataCount = MAX_SMB_BUF - QPIR->smbTrans.MaxParamCount; // Max Data len in reply
791
792 QueryPathInformationRequestParam_t *QPIRParam = (QueryPathInformationRequestParam_t *)&SMB_buf.smb.u8buff[QPIR->smbTrans.ParamOffset];
793
794 QPIRParam->LevelOfInterest = queryType;
795 QPIRParam->Reserved = 0;
796
797 // Add path
798 PathLen = setStringField(QPIRParam->FileName, Path);
799
800 QPIR->smbTrans.TotalParamCount = QPIR->smbTrans.ParamCount = 2 + 4 + PathLen;
801
802 QPIR->ByteCount = 3 + QPIR->smbTrans.TotalParamCount;
803
804 QPIR->smbTrans.DataOffset = QPIR->smbTrans.ParamOffset + QPIR->smbTrans.TotalParamCount;
805
806 nb_SetSessionMessage(QPIR->smbTrans.DataOffset);
807 r = GetSMBServerReply(0, NULL, 0);
808 if (r <= 0)
809 return -EIO;
810
811 // check sanity of SMB header
812 if (QPIRsp->smbH.Magic != SMB_MAGIC)
813 return -EIO;
814
815 // check there's no error
816 switch (QPIRsp->smbH.Eclass | (QPIRsp->smbH.Ecode << 16)) {
817 // check there's no error
818 case STATUS_SUCCESS:
819 break;
820 // check if access denied
821 case STATUS_ACCESS_DENIED:
822 return -EACCES;
823 default:
824 return -EIO;
825 }
826
827 if (queryType == SMB_QUERY_FILE_BASIC_INFO) {
828
829 BasicFileInfo_t *BFI = (BasicFileInfo_t *)&SMB_buf.smb.u8buff[QPIRsp->smbTrans.DataOffset];
830
831 Info->Created = BFI->Created;
832 Info->LastAccess = BFI->LastAccess;
833 Info->LastWrite = BFI->LastWrite;
834 Info->Change = BFI->Change;
835 Info->FileAttributes = BFI->FileAttributes;
836
837 // a 2nd query is done with SMB_QUERY_FILE_STANDARD_INFO LevelOfInterest to get a valid 64bit size
838 queryType = SMB_QUERY_FILE_STANDARD_INFO;
839 goto query;
840 } else if (queryType == SMB_QUERY_FILE_STANDARD_INFO) {
841
842 StandardFileInfo_t *SFI = (StandardFileInfo_t *)&SMB_buf.smb.u8buff[QPIRsp->smbTrans.DataOffset];
843
844 Info->AllocationSize = SFI->AllocationSize;
845 Info->EndOfFile = SFI->EndOfFile;
846 Info->LinkCount = SFI->LinkCount;
847 Info->DeletePending = SFI->DeletePending;
848 Info->IsDirectory = SFI->IsDirectory;
849 }
850
851 return 0;
852}
853
854//-------------------------------------------------------------------------
855int smb_NTCreateAndX(int UID, int TID, char *filename, s64 *filesize, int mode)
856{
857 NTCreateAndXRequest_t *NTCR = &SMB_buf.smb.ntCreateAndXRequest;
858 NTCreateAndXResponse_t *NTCRsp = &SMB_buf.smb.ntCreateAndXResponse;
859 int r, offset;
860
861 ZERO_PKT_ALIGNED(NTCR, sizeof(NTCreateAndXRequest_t));
862
863 NTCR->smbH.Magic = SMB_MAGIC;
864 NTCR->smbH.Cmd = SMB_COM_NT_CREATE_ANDX;
865 NTCR->smbH.Flags = SMB_FLAGS_CANONICAL_PATHNAMES;
866 NTCR->smbH.Flags2 = SMB_FLAGS2_KNOWS_LONG_NAMES | SMB_FLAGS2_32BIT_STATUS;
867 if (server_specs.Capabilities & SERVER_CAP_UNICODE)
868 NTCR->smbH.Flags2 |= SMB_FLAGS2_UNICODE_STRING;
869 NTCR->smbH.UID = (u16)UID;
870 NTCR->smbH.TID = (u16)TID;
871 NTCR->smbWordcount = 24;
872 NTCR->smbAndxCmd = SMB_COM_NONE; // no ANDX command
873 NTCR->AccessMask = ((mode & O_RDWR) == O_RDWR || (mode & O_WRONLY)) ? 0x2019f : 0x20089;
874 NTCR->FileAttributes = ((mode & O_RDWR) == O_RDWR || (mode & O_WRONLY)) ? EXT_ATTR_NORMAL : EXT_ATTR_READONLY;
875 NTCR->ShareAccess = 0x01; // Share in read mode only
876 if (mode & O_CREAT)
877 NTCR->CreateDisposition |= 0x02;
878 if (mode & O_TRUNC)
879 NTCR->CreateDisposition |= 0x04;
880 else
881 NTCR->CreateDisposition |= 0x01;
882 if (NTCR->CreateDisposition == 0x06)
883 NTCR->CreateDisposition = 0x05;
884 NTCR->ImpersonationLevel = 2;
885 NTCR->SecurityFlags = 0x03;
886
887 offset = 0;
888 if (server_specs.Capabilities & SERVER_CAP_UNICODE) {
889 NTCR->ByteField[offset] = '\0';
890 offset++; // pad needed only for unicode as aligment fix
891 }
892
893 // Add filename
894 NTCR->NameLength = setStringField((char *)&NTCR->ByteField[offset], filename);
895 offset += NTCR->NameLength;
896
897 NTCR->ByteCount = offset;
898
899 nb_SetSessionMessage(sizeof(NTCreateAndXRequest_t) + offset + 1);
900 r = GetSMBServerReply(0, NULL, 0);
901 if (r <= 0)
902 return -EIO;
903
904 // check sanity of SMB header
905 if (NTCRsp->smbH.Magic != SMB_MAGIC)
906 return -EIO;
907
908 switch (NTCRsp->smbH.Eclass | (NTCRsp->smbH.Ecode << 16)) {
909 // check there's no error
910 case STATUS_SUCCESS:
911 break;
912 // check if access denied
913 case STATUS_ACCESS_DENIED:
914 return -EACCES;
915 default:
916 return -EIO;
917 }
918
919 *filesize = NTCRsp->FileSize;
920
921 return (int)NTCRsp->FID;
922}
923
924//-------------------------------------------------------------------------
925int smb_OpenAndX(int UID, int TID, char *filename, s64 *filesize, int mode)
926{
927 // does not supports filesize > 4Gb, so we'll have to use
928 // smb_QueryPathInformation to find the real size.
929 // OpenAndX is needed for a few NAS units that doesn't supports
930 // NT SMB commands set.
931
933 OpenAndXRequest_t *OR = &SMB_buf.smb.openAndXRequest;
934 OpenAndXResponse_t *ORsp = &SMB_buf.smb.openAndXResponse;
935 int r, offset;
936
937 if (server_specs.Capabilities & SERVER_CAP_NT_SMBS)
938 return smb_NTCreateAndX(UID, TID, filename, filesize, mode);
939
940 ZERO_PKT_ALIGNED(OR, sizeof(OpenAndXRequest_t));
941
942 OR->smbH.Magic = SMB_MAGIC;
943 OR->smbH.Cmd = SMB_COM_OPEN_ANDX;
944 OR->smbH.Flags = SMB_FLAGS_CANONICAL_PATHNAMES;
945 OR->smbH.Flags2 = SMB_FLAGS2_KNOWS_LONG_NAMES | SMB_FLAGS2_32BIT_STATUS;
946 if (server_specs.Capabilities & SERVER_CAP_UNICODE)
947 OR->smbH.Flags2 |= SMB_FLAGS2_UNICODE_STRING;
948 OR->smbH.UID = (u16)UID;
949 OR->smbH.TID = (u16)TID;
950 OR->smbWordcount = 15;
951 OR->smbAndxCmd = SMB_COM_NONE; // no ANDX command
952 OR->AccessMask = ((mode & O_RDWR) == O_RDWR || (mode & O_WRONLY)) ? 0x02 : 0x00;
953 OR->FileAttributes = ((mode & O_RDWR) == O_RDWR || (mode & O_WRONLY)) ? EXT_ATTR_NORMAL : EXT_ATTR_READONLY;
954 if (mode & O_CREAT)
955 OR->CreateOptions |= 0x10;
956 if (mode & O_TRUNC)
957 OR->CreateOptions |= 0x02;
958 else
959 OR->CreateOptions |= 0x01;
960
961 offset = 0;
962 if (server_specs.Capabilities & SERVER_CAP_UNICODE) {
963 OR->ByteField[offset] = '\0';
964 offset++; // pad needed only for unicode as aligment fix
965 }
966
967 // Add filename
968 offset += setStringField((char *)&OR->ByteField[offset], filename);
969 OR->ByteCount = offset;
970
971 nb_SetSessionMessage(sizeof(OpenAndXRequest_t) + offset + 1);
972 r = GetSMBServerReply(0, NULL, 0);
973 if (r <= 0)
974 return -EIO;
975
976 // check sanity of SMB header
977 if (ORsp->smbH.Magic != SMB_MAGIC)
978 return -EIO;
979
980 switch (ORsp->smbH.Eclass | (ORsp->smbH.Ecode << 16)) {
981 // check there's no error
982 case STATUS_SUCCESS:
983 break;
984 // check if access denied
985 case STATUS_ACCESS_DENIED:
986 return -EACCES;
987 default:
988 return -EIO;
989 }
990
991 r = (int)ORsp->FID;
992
993 if (smb_QueryPathInformation(UID, TID, &info, filename) >= 0)
994 *filesize = info.EndOfFile;
995 else
996 r = -1;
997
998 return r;
999}
1000
1001//-------------------------------------------------------------------------
1002int smb_ReadAndX(int UID, int TID, int FID, s64 fileoffset, void *readbuf, int nbytes)
1003{
1004 ReadAndXRequest_t *RR = &SMB_buf.smb.readAndXRequest;
1005 ReadAndXResponse_t *RRsp = &SMB_buf.smb.readAndXResponse;
1006 int r, padding, DataLength;
1007
1008 ZERO_PKT_ALIGNED(RR, sizeof(ReadAndXRequest_t));
1009
1010 RR->smbH.Magic = SMB_MAGIC;
1011 RR->smbH.Flags2 = SMB_FLAGS2_32BIT_STATUS;
1012 RR->smbH.Cmd = SMB_COM_READ_ANDX;
1013 RR->smbH.UID = (u16)UID;
1014 RR->smbH.TID = (u16)TID;
1015 RR->smbWordcount = 12;
1016 RR->smbAndxCmd = SMB_COM_NONE; // no ANDX command
1017 RR->FID = (u16)FID;
1018 RR->OffsetLow = (u32)(fileoffset & 0xffffffff);
1019 RR->OffsetHigh = (u32)((fileoffset >> 32) & 0xffffffff);
1020 RR->MaxCountLow = (u16)nbytes;
1021 RR->MaxCountHigh = (u16)(nbytes >> 16);
1022
1023 nb_SetSessionMessage(sizeof(ReadAndXRequest_t));
1024 r = GetSMBServerReply(0, NULL, sizeof(ReadAndXResponse_t));
1025 if (r <= 0)
1026 return -EIO;
1027
1028 // check sanity of SMB header
1029 if (RRsp->smbH.Magic != SMB_MAGIC)
1030 return -EIO;
1031
1032 // check there's no error
1033 if ((RRsp->smbH.Eclass | (RRsp->smbH.Ecode << 16)) != STATUS_SUCCESS)
1034 return -EIO;
1035
1036 // Skip any padding bytes.
1037 padding = RRsp->DataOffset - sizeof(ReadAndXResponse_t);
1038 if (padding > 0) {
1039 r = RecvData(main_socket, (char *)(RRsp + 1), padding, 3000); // 3s before the packet is considered lost
1040 if (r <= 0)
1041 return -2;
1042 }
1043
1044 DataLength = (int)(((u32)RRsp->DataLengthHigh << 16) | RRsp->DataLengthLow);
1045 if (DataLength > 0) {
1046 r = RecvData(main_socket, readbuf, DataLength, 3000); // 3s before the packet is considered lost
1047 if (r <= 0)
1048 return -2;
1049 }
1050
1051 r = DataLength;
1052
1053 return r;
1054}
1055
1056int smb_ReadFile(int UID, int TID, int FID, s64 fileoffset, void *readbuf, int nbytes)
1057{
1058 int remaining;
1059 char *ptr;
1060 s64 pos;
1061
1062 remaining = nbytes;
1063 ptr = readbuf;
1064 pos = fileoffset;
1065
1066 while (remaining > 0) {
1067 int result, toRead;
1068 toRead = remaining > CLIENT_MAX_XFER_SIZE ? CLIENT_MAX_XFER_SIZE : remaining;
1069
1070 result = smb_ReadAndX(UID, TID, FID, pos, ptr, toRead);
1071 if (result <= 0)
1072 return result;
1073
1074 pos += result;
1075 ptr += result;
1076 remaining -= result;
1077 }
1078
1079 return nbytes;
1080}
1081
1082//-------------------------------------------------------------------------
1083int smb_WriteAndX(int UID, int TID, int FID, s64 fileoffset, void *writebuf, int nbytes)
1084{
1085 int r;
1086 const int padding = 1; // 1 padding byte, to keep the payload aligned for writing performance.
1087 WriteAndXRequest_t *WR = &SMB_buf.smb.writeAndXRequest;
1088 WriteAndXResponse_t *WRsp = &SMB_buf.smb.writeAndXResponse;
1089
1090 ZERO_PKT_ALIGNED(WR, sizeof(WriteAndXRequest_t) + padding);
1091
1092 WR->smbH.Magic = SMB_MAGIC;
1093 WR->smbH.Flags2 = SMB_FLAGS2_32BIT_STATUS;
1094 WR->smbH.Cmd = SMB_COM_WRITE_ANDX;
1095 WR->smbH.UID = (u16)UID;
1096 WR->smbH.TID = (u16)TID;
1097 WR->smbWordcount = 14;
1098 WR->smbAndxCmd = SMB_COM_NONE; // no ANDX command
1099 WR->FID = (u16)FID;
1100 WR->OffsetLow = (u32)(fileoffset & 0xffffffff);
1101 WR->OffsetHigh = (u32)((fileoffset >> 32) & 0xffffffff);
1102 WR->WriteMode = 0x0001; // WritethroughMode
1103 WR->DataOffset = sizeof(WriteAndXRequest_t) + padding;
1104 WR->Remaining = (u16)nbytes;
1105 WR->DataLengthLow = (u16)nbytes;
1106 WR->DataLengthHigh = (u16)(nbytes >> 16);
1107 WR->ByteCount = (u16)nbytes + padding;
1108
1109 nb_SetSessionMessage(sizeof(WriteAndXRequest_t) + padding + nbytes);
1110 r = GetSMBServerReply(sizeof(WriteAndXRequest_t) + padding, writebuf, 0);
1111 if (r <= 0)
1112 return -EIO;
1113
1114 // check sanity of SMB header
1115 if (WRsp->smbH.Magic != SMB_MAGIC)
1116 return -EIO;
1117
1118 // check there's no error
1119 if ((WRsp->smbH.Eclass | (WRsp->smbH.Ecode << 16)) != STATUS_SUCCESS)
1120 return -EIO;
1121
1122 return nbytes;
1123}
1124
1125int smb_WriteFile(int UID, int TID, int FID, s64 fileoffset, void *writebuf, int nbytes)
1126{
1127 int remaining;
1128 char *ptr;
1129 s64 pos;
1130
1131 remaining = nbytes;
1132 ptr = writebuf;
1133 pos = fileoffset;
1134
1135 while (remaining > 0) {
1136 int result, toWrite;
1137
1138 toWrite = remaining > CLIENT_MAX_XFER_SIZE ? CLIENT_MAX_XFER_SIZE : remaining;
1139
1140 result = smb_WriteAndX(UID, TID, FID, pos, ptr, toWrite);
1141 if (result <= 0)
1142 return result;
1143
1144 pos += result;
1145 ptr += result;
1146 remaining -= result;
1147 }
1148
1149 return nbytes;
1150}
1151
1152//-------------------------------------------------------------------------
1153int smb_Close(int UID, int TID, int FID)
1154{
1155 int r;
1156 CloseRequest_t *CR = &SMB_buf.smb.closeRequest;
1157 CloseResponse_t *CRsp = &SMB_buf.smb.closeResponse;
1158
1159 ZERO_PKT_ALIGNED(CR, sizeof(CloseRequest_t));
1160
1161 CR->smbH.Magic = SMB_MAGIC;
1162 CR->smbH.Cmd = SMB_COM_CLOSE;
1163 CR->smbH.Flags = SMB_FLAGS_CANONICAL_PATHNAMES;
1164 CR->smbH.Flags2 = SMB_FLAGS2_KNOWS_LONG_NAMES | SMB_FLAGS2_32BIT_STATUS;
1165 CR->smbH.UID = (u16)UID;
1166 CR->smbH.TID = (u16)TID;
1167 CR->smbWordcount = 3;
1168 CR->FID = (u16)FID;
1169
1170 nb_SetSessionMessage(sizeof(CloseRequest_t));
1171 r = GetSMBServerReply(0, NULL, 0);
1172 if (r <= 0)
1173 return -EIO;
1174
1175 // check sanity of SMB header
1176 if (CRsp->smbH.Magic != SMB_MAGIC)
1177 return -EIO;
1178
1179 // check there's no error
1180 if ((CRsp->smbH.Eclass | (CRsp->smbH.Ecode << 16)) != STATUS_SUCCESS)
1181 return -EIO;
1182
1183 return 0;
1184}
1185
1186//-------------------------------------------------------------------------
1187int smb_Delete(int UID, int TID, char *Path)
1188{
1189 int r, PathLen;
1190 DeleteRequest_t *DR = &SMB_buf.smb.deleteRequest;
1191 DeleteResponse_t *DRsp = &SMB_buf.smb.deleteResponse;
1192
1193 ZERO_PKT_ALIGNED(DR, sizeof(DeleteRequest_t));
1194
1195 DR->smbH.Magic = SMB_MAGIC;
1196 DR->smbH.Cmd = SMB_COM_DELETE;
1197 DR->smbH.Flags = SMB_FLAGS_CANONICAL_PATHNAMES;
1198 DR->smbH.Flags2 = SMB_FLAGS2_KNOWS_LONG_NAMES | SMB_FLAGS2_32BIT_STATUS;
1199 if (server_specs.Capabilities & SERVER_CAP_UNICODE)
1200 DR->smbH.Flags2 |= SMB_FLAGS2_UNICODE_STRING;
1201 DR->smbH.UID = (u16)UID;
1202 DR->smbH.TID = (u16)TID;
1203 DR->smbWordcount = 1;
1204 DR->SearchAttributes = 0; // coud be other attributes to find Hidden/System files
1205 DR->BufferFormat = 0x04;
1206
1207 // Add path
1208 PathLen = setStringField(DR->FileName, Path);
1209 DR->ByteCount = PathLen + 1; // +1 for the BufferFormat byte
1210
1211 nb_SetSessionMessage(sizeof(DeleteRequest_t) + PathLen);
1212 r = GetSMBServerReply(0, NULL, 0);
1213 if (r <= 0)
1214 return -EIO;
1215
1216 // check sanity of SMB header
1217 if (DRsp->smbH.Magic != SMB_MAGIC)
1218 return -EIO;
1219
1220 // check there's no error
1221 if ((DRsp->smbH.Eclass | (DRsp->smbH.Ecode << 16)) != STATUS_SUCCESS)
1222 return -EIO;
1223
1224 return 0;
1225}
1226
1227//-------------------------------------------------------------------------
1228int smb_ManageDirectory(int UID, int TID, char *Path, int cmd)
1229{
1230 int r, PathLen;
1231 ManageDirectoryRequest_t *MDR = &SMB_buf.smb.manageDirectoryRequest;
1232 ManageDirectoryResponse_t *MDRsp = &SMB_buf.smb.manageDirectoryResponse;
1233
1234 ZERO_PKT_ALIGNED(MDR, sizeof(ManageDirectoryRequest_t));
1235
1236 MDR->smbH.Magic = SMB_MAGIC;
1237
1238 MDR->smbH.Cmd = (u8)cmd;
1239 MDR->smbH.Flags = SMB_FLAGS_CANONICAL_PATHNAMES;
1240 MDR->smbH.Flags2 = SMB_FLAGS2_KNOWS_LONG_NAMES | SMB_FLAGS2_32BIT_STATUS;
1241 if (server_specs.Capabilities & SERVER_CAP_UNICODE)
1242 MDR->smbH.Flags2 |= SMB_FLAGS2_UNICODE_STRING;
1243 MDR->smbH.UID = (u16)UID;
1244 MDR->smbH.TID = (u16)TID;
1245 MDR->BufferFormat = 0x04;
1246
1247 // Add path
1248 PathLen = setStringField(MDR->DirectoryName, Path);
1249 MDR->ByteCount = PathLen + 1; // +1 for the BufferFormat byte
1250
1251 nb_SetSessionMessage(sizeof(ManageDirectoryRequest_t) + PathLen);
1252 r = GetSMBServerReply(0, NULL, 0);
1253 if (r <= 0)
1254 return -EIO;
1255
1256 // check sanity of SMB header
1257 if (MDRsp->smbH.Magic != SMB_MAGIC)
1258 return -EIO;
1259
1260 switch (MDRsp->smbH.Eclass | (MDRsp->smbH.Ecode << 16)) {
1261 // check there's no error
1262 case STATUS_SUCCESS:
1263 break;
1264 // check if access denied
1265 case STATUS_ACCESS_DENIED:
1266 return -EACCES;
1267 default:
1268 return -EIO;
1269 }
1270
1271 return 0;
1272}
1273
1274//-------------------------------------------------------------------------
1275int smb_Rename(int UID, int TID, char *oldPath, char *newPath)
1276{
1277 int r, offset;
1278 RenameRequest_t *RR = &SMB_buf.smb.renameRequest;
1279 RenameResponse_t *RRsp = &SMB_buf.smb.renameResponse;
1280
1281 ZERO_PKT_ALIGNED(RR, sizeof(RenameRequest_t));
1282
1283 RR->smbH.Magic = SMB_MAGIC;
1284 RR->smbH.Cmd = SMB_COM_RENAME;
1285 RR->smbH.Flags = SMB_FLAGS_CANONICAL_PATHNAMES;
1286 RR->smbH.Flags2 = SMB_FLAGS2_KNOWS_LONG_NAMES | SMB_FLAGS2_32BIT_STATUS;
1287 if (server_specs.Capabilities & SERVER_CAP_UNICODE)
1288 RR->smbH.Flags2 |= SMB_FLAGS2_UNICODE_STRING;
1289 RR->smbH.UID = (u16)UID;
1290 RR->smbH.TID = (u16)TID;
1291 RR->smbWordcount = 1;
1292
1293 // NOTE: on samba seems it doesn't care of attribute to rename directories
1294 // to be tested on windows
1295 RR->SearchAttributes = 0; // coud be other attributes to find Hidden/System files /Directories
1296
1297 offset = 0;
1298
1299 // Add oldPath
1300 RR->ByteField[offset] = 0x04; // BufferFormat
1301 offset++;
1302 offset += setStringField((char *)&RR->ByteField[offset], oldPath);
1303
1304 // Add newPath
1305 RR->ByteField[offset] = 0x04; // BufferFormat
1306 offset++;
1307 if (server_specs.Capabilities & SERVER_CAP_UNICODE) {
1308 RR->ByteField[offset] = '\0';
1309 offset++; // pad needed for unicode
1310 }
1311 offset += setStringField((char *)&RR->ByteField[offset], newPath);
1312
1313 RR->ByteCount = offset;
1314
1315 nb_SetSessionMessage(sizeof(RenameRequest_t) + offset);
1316 r = GetSMBServerReply(0, NULL, 0);
1317 if (r <= 0)
1318 return -EIO;
1319
1320 // check sanity of SMB header
1321 if (RRsp->smbH.Magic != SMB_MAGIC)
1322 return -EIO;
1323
1324 // check there's no error
1325 switch (RRsp->smbH.Eclass | (RRsp->smbH.Ecode << 16)) {
1326 // check there's no error
1327 case STATUS_SUCCESS:
1328 break;
1329 // check if access denied
1330 case STATUS_ACCESS_DENIED:
1331 return -EACCES;
1332 default:
1333 return -EIO;
1334 }
1335
1336 return 0;
1337}
1338
1339//-------------------------------------------------------------------------
1340int smb_FindFirstNext2(int UID, int TID, char *Path, int cmd, SearchInfo_t *info)
1341{
1342 int r, PathLen, offset;
1343 FindFirstNext2Request_t *FFNR = &SMB_buf.smb.findFirstNext2Request;
1344 FindFirstNext2Response_t *FFNRsp = &SMB_buf.smb.findFirstNext2Response;
1345
1346 ZERO_PKT_ALIGNED(FFNR, sizeof(FindFirstNext2Request_t));
1347
1348 FFNR->smbH.Magic = SMB_MAGIC;
1349 FFNR->smbH.Cmd = SMB_COM_TRANSACTION2;
1350 FFNR->smbH.Flags = SMB_FLAGS_CANONICAL_PATHNAMES;
1351 FFNR->smbH.Flags2 = SMB_FLAGS2_KNOWS_LONG_NAMES | SMB_FLAGS2_32BIT_STATUS;
1352 if (server_specs.Capabilities & SERVER_CAP_UNICODE)
1353 FFNR->smbH.Flags2 |= SMB_FLAGS2_UNICODE_STRING;
1354 FFNR->smbH.UID = (u16)UID;
1355 FFNR->smbH.TID = (u16)TID;
1356 FFNR->smbWordcount = 15;
1357
1358 FFNR->smbTrans.SetupCount = 1;
1359 FFNR->SubCommand = (u8)cmd;
1360
1361 FFNR->smbTrans.ParamOffset = (sizeof(FindFirstNext2Request_t) + 3) & ~3; // Keep aligned to a 4-byte boundary as required.
1362 FFNR->smbTrans.MaxParamCount = 256; // Max Parameters len in reply
1363 FFNR->smbTrans.MaxDataCount = MAX_SMB_BUF - FFNR->smbTrans.MaxParamCount; // Max Data len in reply
1364
1365 // Zero Pad1.
1366 memset((void *)(FFNR + 1), 0, FFNR->smbTrans.ParamOffset - sizeof(FindFirstNext2Request_t));
1367
1368 if (cmd == TRANS2_FIND_FIRST2) {
1369 FindFirst2RequestParam_t *FFRParam = (FindFirst2RequestParam_t *)&SMB_buf.smb.u8buff[FFNR->smbTrans.ParamOffset];
1370
1371 FFRParam->SearchAttributes = ATTR_READONLY | ATTR_HIDDEN | ATTR_SYSTEM | ATTR_DIRECTORY | ATTR_ARCHIVE;
1372 FFRParam->SearchCount = 1;
1373 FFRParam->Flags = CLOSE_SEARCH_IF_EOS | RESUME_SEARCH;
1374 FFRParam->LevelOfInterest = SMB_FIND_FILE_BOTH_DIRECTORY_INFO;
1375 FFRParam->StorageType = 0;
1376
1377 // Add path
1378 PathLen = setStringField(FFRParam->SearchPattern, Path);
1379
1380 FFNR->smbTrans.TotalParamCount = FFNR->smbTrans.ParamCount = sizeof(FindFirst2RequestParam_t) + PathLen;
1381 } else {
1382 FindNext2RequestParam_t *FNRParam = (FindNext2RequestParam_t *)&SMB_buf.smb.u8buff[FFNR->smbTrans.ParamOffset];
1383
1384 FNRParam->SearchID = (u16)info->SID;
1385 FNRParam->SearchCount = 1;
1386 FNRParam->LevelOfInterest = SMB_FIND_FILE_BOTH_DIRECTORY_INFO;
1387 FNRParam->ResumeKey = 0;
1388 FNRParam->Flags = CLOSE_SEARCH_IF_EOS | RESUME_SEARCH | CONTINUE_SEARCH;
1389
1390 // Add path
1391 PathLen = setStringField(FNRParam->SearchPattern, "");
1392
1393 FFNR->smbTrans.TotalParamCount = FFNR->smbTrans.ParamCount = sizeof(FindNext2RequestParam_t) + PathLen;
1394 }
1395
1396 FFNR->ByteCount = 3 + FFNR->smbTrans.TotalParamCount;
1397 offset = FFNR->smbTrans.ParamOffset + FFNR->smbTrans.TotalParamCount;
1398
1399 // No data, so no Pad2. As DataCount is 0, the client may set DataOffset to 0.
1400 // FFNR->smbTrans.DataOffset = (u16)offset;
1401
1402 nb_SetSessionMessage(offset);
1403 r = GetSMBServerReply(0, NULL, 0);
1404 if (r <= 0)
1405 return -EIO;
1406
1407 // check sanity of SMB header
1408 if (FFNRsp->smbH.Magic != SMB_MAGIC)
1409 return -EIO;
1410
1411 // check there's no error
1412 switch (FFNRsp->smbH.Eclass | (FFNRsp->smbH.Ecode << 16)) {
1413 // check there's no error
1414 case STATUS_SUCCESS:
1415 break;
1416 // check if access denied
1417 case STATUS_ACCESS_DENIED:
1418 return -EACCES;
1419 default:
1420 return -EIO;
1421 }
1422
1423 FindFirstNext2ResponseParam_t *FFNRspParam;
1424
1425 if (cmd == TRANS2_FIND_FIRST2) {
1426 FFNRspParam = (FindFirstNext2ResponseParam_t *)&SMB_buf.smb.u8buff[FFNRsp->smbTrans.ParamOffset];
1427 info->SID = FFNRspParam->SearchID;
1428 } else
1429 FFNRspParam = (FindFirstNext2ResponseParam_t *)&SMB_buf.smb.u8buff[FFNRsp->smbTrans.ParamOffset - 2];
1430
1431 FindFirst2ResponseData_t *FFRspData = (FindFirst2ResponseData_t *)&SMB_buf.smb.u8buff[FFNRsp->smbTrans.DataOffset];
1432
1433 info->EOS = FFNRspParam->EndOfSearch;
1434
1435 if (FFNRspParam->SearchCount == 0)
1436 return -EINVAL;
1437
1438 info->fileInfo.Created = FFRspData->Created;
1439 info->fileInfo.LastAccess = FFRspData->LastAccess;
1440 info->fileInfo.LastWrite = FFRspData->LastWrite;
1441 info->fileInfo.Change = FFRspData->Change;
1442 info->fileInfo.FileAttributes = FFRspData->FileAttributes;
1443 if (FFRspData->FileAttributes & EXT_ATTR_DIRECTORY)
1444 info->fileInfo.IsDirectory = 1;
1445 info->fileInfo.AllocationSize = FFRspData->AllocationSize;
1446 info->fileInfo.EndOfFile = FFRspData->EndOfFile;
1447 getStringField(info->FileName, FFRspData->FileName, FFRspData->FileNameLen);
1448
1449 return 0;
1450}
1451
1452//-------------------------------------------------------------------------
1453int smb_TreeDisconnect(int UID, int TID)
1454{
1455 int r;
1456 TreeDisconnectRequest_t *TDR = &SMB_buf.smb.treeDisconnectRequest;
1457 TreeDisconnectResponse_t *TDRsp = &SMB_buf.smb.treeDisconnectResponse;
1458
1459 ZERO_PKT_ALIGNED(TDR, sizeof(TreeDisconnectRequest_t));
1460
1461 TDR->smbH.Magic = SMB_MAGIC;
1462 TDR->smbH.Cmd = SMB_COM_TREE_DISCONNECT;
1463 TDR->smbH.UID = (u16)UID;
1464 TDR->smbH.TID = (u16)TID;
1465
1466 nb_SetSessionMessage(sizeof(TreeDisconnectRequest_t));
1467 r = GetSMBServerReply(0, NULL, 0);
1468 if (r <= 0)
1469 return -EIO;
1470
1471 // check sanity of SMB header
1472 if (TDRsp->smbH.Magic != SMB_MAGIC)
1473 return -EIO;
1474
1475 // check there's no error
1476 if ((TDRsp->smbH.Eclass | (TDRsp->smbH.Ecode << 16)) != STATUS_SUCCESS)
1477 return -EIO;
1478
1479 return 0;
1480}
1481
1482//-------------------------------------------------------------------------
1483int smb_LogOffAndX(int UID)
1484{
1485 int r;
1486 LogOffAndXRequest_t *LR = &SMB_buf.smb.logOffAndXRequest;
1487 LogOffAndXResponse_t *LRsp = &SMB_buf.smb.logOffAndXResponse;
1488
1489 ZERO_PKT_ALIGNED(LR, sizeof(LogOffAndXRequest_t));
1490
1491 LR->smbH.Magic = SMB_MAGIC;
1492 LR->smbH.Cmd = SMB_COM_LOGOFF_ANDX;
1493 LR->smbH.UID = (u16)UID;
1494 LR->smbWordcount = 2;
1495 LR->smbAndxCmd = SMB_COM_NONE; // no ANDX command
1496
1497 nb_SetSessionMessage(sizeof(LogOffAndXRequest_t));
1498 r = GetSMBServerReply(0, NULL, 0);
1499 if (r <= 0)
1500 return -EIO;
1501
1502 // check sanity of SMB header
1503 if (LRsp->smbH.Magic != SMB_MAGIC)
1504 return -EIO;
1505
1506 // check there's no error
1507 if ((LRsp->smbH.Eclass | (LRsp->smbH.Ecode << 16)) != STATUS_SUCCESS)
1508 return -EIO;
1509
1510 return 0;
1511}
1512
1513//-------------------------------------------------------------------------
1514int smb_Echo(void *echo, int len)
1515{
1516 int r;
1517 EchoRequest_t *ER = &SMB_buf.smb.echoRequest;
1518 EchoResponse_t *ERsp = &SMB_buf.smb.echoResponse;
1519
1520 ZERO_PKT_ALIGNED(ER, sizeof(EchoRequest_t));
1521
1522 ER->smbH.Magic = SMB_MAGIC;
1523 ER->smbH.Cmd = SMB_COM_ECHO;
1524 ER->smbWordcount = 1;
1525 ER->EchoCount = 1;
1526
1527 memcpy(&ER->ByteField[0], echo, (u16)len);
1528 ER->ByteCount = (u16)len;
1529
1530 nb_SetSessionMessage(sizeof(EchoRequest_t) + (u16)len);
1531 r = GetSMBServerReply(0, NULL, 0);
1532 if (r <= 0)
1533 return -EIO;
1534
1535 // check sanity of SMB header
1536 if (ERsp->smbH.Magic != SMB_MAGIC)
1537 return -EIO;
1538
1539 // check there's no error
1540 if ((ERsp->smbH.Eclass | (ERsp->smbH.Ecode << 16)) != STATUS_SUCCESS)
1541 return -EIO;
1542
1543 if (memcmp(&ERsp->ByteField[0], echo, len))
1544 return -EIO;
1545
1546 return 0;
1547}
1548
1549//-------------------------------------------------------------------------
1550int smb_Connect(char *SMBServerIP, int SMBServerPort)
1551{
1552 int r;
1553 struct in_addr dst_addr;
1554
1555 dst_addr.s_addr = inet_addr(SMBServerIP);
1556
1557 // Close the connection if it was already opened
1558 smb_Disconnect();
1559
1560 // Opening TCP session
1561 r = OpenTCPSession(dst_addr, SMBServerPort, &main_socket);
1562 if (r < 0) {
1563 return -1;
1564 }
1565
1566 // We keep the server IP for SMB logon
1567 strncpy(server_specs.ServerIP, SMBServerIP, 16);
1568
1569 return 0;
1570}
1571
1572//-------------------------------------------------------------------------
1573int smb_Disconnect(void)
1574{
1575 char dummy;
1576
1577 if (main_socket != -1) {
1578 // Ensure that all data has been received by the other side before closing.
1579 shutdown(main_socket, SHUT_WR);
1580 // Wait for the remote end to close the socket, which shall happen after all sent data has been received.
1581 while (recv(main_socket, &dummy, sizeof(dummy), 0) > 0)
1582 ;
1583
1584 lwip_close(main_socket);
1585 main_socket = -1;
1586 }
1587
1588 return 0;
1589}
#define EINVAL
Definition errno.h:63
#define EIO
Definition errno.h:29
#define EACCES
Definition errno.h:45
Definition ps2smb.h:80
Definition poll.h:5
u32 count
start sector of fragmented bd/file