import of davy's libwiikeyboard
[libogc.git] / libtinysmb / smb.c
blobd3d431b5ee0ea03caedf55fae80f03d26a415748
1 /****************************************************************************
2 * TinySMB
3 * Nintendo Wii/GameCube SMB implementation
5 * Copyright softdev
6 * Modified by Tantric to utilize NTLM authentication
7 * PathInfo added by rodries
8 * SMB devoptab by scip, rodries
10 * You will find WireShark (http://www.wireshark.org/)
11 * invaluable for debugging SAMBA implementations.
13 * Recommended Reading
14 * Implementing CIFS - Christopher R Hertel
15 * http://www.ubiqx.org/cifs/SMB.html
17 * License:
19 * This library is free software; you can redistribute it and/or
20 * modify it under the terms of the GNU Lesser General Public
21 * License as published by the Free Software Foundation; either
22 * version 2.1 of the License, or (at your option) any later version.
24 * This library is distributed in the hope that it will be useful,
25 * but WITHOUT ANY WARRANTY; without even the implied warranty of
26 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
27 * Lesser General Public License for more details.
29 * You should have received a copy of the GNU Lesser General Public
30 * License along with this library; if not, write to the Free Software
31 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
32 ****************************************************************************/
34 #include <asm.h>
35 #include <stdlib.h>
36 #include <stdio.h>
37 #include <string.h>
38 #include <malloc.h>
39 #include <ctype.h>
40 #include <gccore.h>
41 #include <network.h>
42 #include <processor.h>
43 #include <lwp_threads.h>
44 #include <lwp_objmgr.h>
45 #include <smb.h>
48 /**
49 * Field offsets.
51 #define SMB_OFFSET_PROTO 0
52 #define SMB_OFFSET_CMD 4
53 #define SMB_OFFSET_NTSTATUS 5
54 #define SMB_OFFSET_ECLASS 5
55 #define SMB_OFFSET_ECODE 7
56 #define SMB_OFFSET_FLAGS 9
57 #define SMB_OFFSET_FLAGS2 10
58 #define SMB_OFFSET_EXTRA 12
59 #define SMB_OFFSET_TID 24
60 #define SMB_OFFSET_PID 26
61 #define SMB_OFFSET_UID 28
62 #define SMB_OFFSET_MID 30
63 #define SMB_HEADER_SIZE 32 /*** SMB Headers are always 32 bytes long ***/
65 /**
66 * Message / Commands
68 #define NBT_SESSISON_MSG 0x00
70 #define SMB_NEG_PROTOCOL 0x72
71 #define SMB_SETUP_ANDX 0x73
72 #define SMB_TREEC_ANDX 0x75
74 /**
75 * SMBTrans2
77 #define SMB_TRANS2 0x32
79 #define SMB_OPEN2 0
80 #define SMB_FIND_FIRST2 1
81 #define SMB_FIND_NEXT2 2
82 #define SMB_QUERY_FS_INFO 3
83 #define SMB_QUERY_PATH_INFO 5
84 #define SMB_SET_PATH_INFO 6
85 #define SMB_QUERY_FILE_INFO 7
86 #define SMB_SET_FILE_INFO 8
87 #define SMB_CREATE_DIR 13
88 #define SMB_FIND_CLOSE2 0x34
90 /**
91 * File I/O
93 #define SMB_OPEN_ANDX 0x2d
94 #define SMB_WRITE_ANDX 0x2f
95 #define SMB_READ_ANDX 0x2e
96 #define SMB_CLOSE 0x04
99 /**
100 * TRANS2 Offsets
102 #define T2_WORD_CNT (SMB_HEADER_SIZE)
103 #define T2_PRM_CNT (T2_WORD_CNT+1)
104 #define T2_DATA_CNT (T2_PRM_CNT+2)
105 #define T2_MAXPRM_CNT (T2_DATA_CNT+2)
106 #define T2_MAXBUFFER (T2_MAXPRM_CNT+2)
107 #define T2_SETUP_CNT (T2_MAXBUFFER+2)
108 #define T2_SPRM_CNT (T2_SETUP_CNT+10)
109 #define T2_SPRM_OFS (T2_SPRM_CNT+2)
110 #define T2_SDATA_CNT (T2_SPRM_OFS+2)
111 #define T2_SDATA_OFS (T2_SDATA_CNT+2)
112 #define T2_SSETUP_CNT (T2_SDATA_OFS+2)
113 #define T2_SUB_CMD (T2_SSETUP_CNT+2)
114 #define T2_BYTE_CNT (T2_SUB_CMD+2)
117 #define SMB_PROTO 0x424d53ff
118 #define SMB_HANDLE_NULL 0xffffffff
119 #define SMB_MAX_BUFFERSIZE (62*1024) // cannot be larger than u16 (65536)
120 #define SMB_MAX_TRANSMIT_SIZE 7236
121 #define SMB_DEF_READOFFSET 59
123 #define SMB_CONNHANDLES_MAX 8
124 #define SMB_FILEHANDLES_MAX (32*SMB_CONNHANDLES_MAX)
126 #define SMB_OBJTYPE_HANDLE 7
127 #define SMB_CHECK_HANDLE(hndl) \
129 if(((hndl)==SMB_HANDLE_NULL) || (LWP_OBJTYPE(hndl)!=SMB_OBJTYPE_HANDLE)) \
130 return NULL; \
133 struct _smbfile
135 lwp_node node;
136 u16 sfid;
137 SMBCONN conn;
141 * NBT/SMB Wrapper
143 typedef struct _nbtsmb
145 u8 msg; /*** NBT Message ***/
146 u8 flags; /*** Not much here really ***/
147 u16 length; /*** Length, excluding NBT ***/
148 u8 smb[SMB_MAX_TRANSMIT_SIZE]; /*** Wii Actual is 7240 bytes ***/
149 } NBTSMB;
152 * Session Information
154 typedef struct _smbsession
156 u16 TID;
157 u16 PID;
158 u16 UID;
159 u16 MID;
160 u32 sKey;
161 u16 MaxBuffer;
162 u16 MaxMpx;
163 u16 MaxVCS;
164 u8 challenge[10];
165 u8 p_domain[64];
166 u16 sid;
167 u16 count;
168 u16 eos;
169 } SMBSESSION;
171 typedef struct _smbhandle
173 lwp_obj object;
174 char *user;
175 char *pwd;
176 char *share_name;
177 char *server_name;
178 s32 sck_server;
179 struct sockaddr_in server_addr;
180 BOOL conn_valid;
181 SMBSESSION session;
182 NBTSMB message;
183 } SMBHANDLE;
185 static u32 smb_dialectcnt = 1;
186 static BOOL smb_inited = FALSE;
187 static lwp_objinfo smb_handle_objects;
188 static lwp_queue smb_filehandle_queue;
189 static struct _smbfile smb_filehandles[SMB_FILEHANDLES_MAX];
190 static const char *smb_dialects[] = {"NT LM 0.12",NULL};
192 extern void ntlm_smb_nt_encrypt(const char *passwd, const u8 * challenge, u8 * answer);
195 * SMB Endian aware supporting functions
197 * SMB always uses Intel Little-Endian values, so htons etc are
198 * of little or no use :) ... Thanks M$
201 /*** get unsigned char ***/
202 static __inline__ u8 getUChar(u8 *buffer,u32 offset)
204 return (u8)buffer[offset];
207 /*** set unsigned char ***/
208 static __inline__ void setUChar(u8 *buffer,u32 offset,u8 value)
210 buffer[offset] = value;
213 /*** get unsigned short ***/
214 static __inline__ u16 getUShort(u8 *buffer,u32 offset)
216 return (u16)((buffer[offset+1]<<8)|(buffer[offset]));
219 /*** set unsigned short ***/
220 static __inline__ void setUShort(u8 *buffer,u32 offset,u16 value)
222 buffer[offset] = (value&0xff);
223 buffer[offset+1] = ((value&0xff00)>>8);
226 /*** get unsigned int ***/
227 static __inline__ u32 getUInt(u8 *buffer,u32 offset)
229 return (u32)((buffer[offset+3]<<24)|(buffer[offset+2]<<16)|(buffer[offset+1]<<8)|buffer[offset]);
232 /*** set unsigned int ***/
233 static __inline__ void setUInt(u8 *buffer,u32 offset,u32 value)
235 buffer[offset] = (value&0xff);
236 buffer[offset+1] = ((value&0xff00)>>8);
237 buffer[offset+2] = ((value&0xff0000)>>16);
238 buffer[offset+3] = ((value&0xff000000)>>24);
241 static __inline__ SMBHANDLE* __smb_handle_open(SMBCONN smbhndl)
243 u32 level;
244 SMBHANDLE *handle;
246 SMB_CHECK_HANDLE(smbhndl);
248 _CPU_ISR_Disable(level);
249 handle = (SMBHANDLE*)__lwp_objmgr_getnoprotection(&smb_handle_objects,LWP_OBJMASKID(smbhndl));
250 _CPU_ISR_Restore(level);
251 return handle;
255 static __inline__ void __smb_handle_free(SMBHANDLE *handle)
257 u32 level;
259 _CPU_ISR_Disable(level);
260 __lwp_objmgr_close(&smb_handle_objects,&handle->object);
261 __lwp_objmgr_free(&smb_handle_objects,&handle->object);
262 _CPU_ISR_Restore(level);
265 static void __smb_init()
267 smb_inited = TRUE;
268 __lwp_objmgr_initinfo(&smb_handle_objects,SMB_CONNHANDLES_MAX,sizeof(SMBHANDLE));
269 __lwp_queue_initialize(&smb_filehandle_queue,smb_filehandles,SMB_FILEHANDLES_MAX,sizeof(struct _smbfile));
272 static SMBHANDLE* __smb_allocate_handle()
274 u32 level;
275 SMBHANDLE *handle;
277 _CPU_ISR_Disable(level);
278 handle = (SMBHANDLE*)__lwp_objmgr_allocate(&smb_handle_objects);
279 if(handle) {
280 handle->user = NULL;
281 handle->pwd = NULL;
282 handle->server_name = NULL;
283 handle->share_name = NULL;
284 handle->sck_server = INVALID_SOCKET;
285 handle->conn_valid = FALSE;
286 __lwp_objmgr_open(&smb_handle_objects,&handle->object);
288 _CPU_ISR_Restore(level);
289 return handle;
292 static void __smb_free_handle(SMBHANDLE *handle)
294 if(handle->user) free(handle->user);
295 if(handle->pwd) free(handle->pwd);
296 if(handle->server_name) free(handle->server_name);
297 if(handle->share_name) free(handle->share_name);
299 handle->user = NULL;
300 handle->pwd = NULL;
301 handle->server_name = NULL;
302 handle->share_name = NULL;
303 handle->sck_server = INVALID_SOCKET;
305 __smb_handle_free(handle);
308 static void MakeSMBHeader(u8 command,u8 flags,u16 flags2,SMBHANDLE *handle)
310 u8 *ptr = handle->message.smb;
311 NBTSMB *nbt = &handle->message;
312 SMBSESSION *sess = &handle->session;
314 memset(nbt,0,sizeof(NBTSMB));
316 setUInt(ptr,SMB_OFFSET_PROTO,SMB_PROTO);
317 setUChar(ptr,SMB_OFFSET_CMD,command);
318 setUChar(ptr,SMB_OFFSET_FLAGS,flags);
319 setUShort(ptr,SMB_OFFSET_FLAGS2,flags2);
320 setUShort(ptr,SMB_OFFSET_TID,sess->TID);
321 setUShort(ptr,SMB_OFFSET_PID,sess->PID);
322 setUShort(ptr,SMB_OFFSET_UID,sess->UID);
323 setUShort(ptr,SMB_OFFSET_MID,sess->MID);
325 ptr[SMB_HEADER_SIZE] = 0;
329 * MakeTRANS2Hdr
331 static void MakeTRANS2Header(u8 subcommand,SMBHANDLE *handle)
333 u8 *ptr = handle->message.smb;
334 SMBSESSION *sess = &handle->session;
336 setUChar(ptr, T2_WORD_CNT, 15);
337 setUShort(ptr, T2_MAXPRM_CNT, 10);
338 setUShort(ptr, T2_MAXBUFFER, sess->MaxBuffer);
339 setUChar(ptr, T2_SSETUP_CNT, 1);
340 setUShort(ptr, T2_SUB_CMD, subcommand);
345 * SMBCheck
347 * Do very basic checking on the return SMB
349 static s32 SMBCheck(u8 command, s32 readlen,SMBHANDLE *handle)
351 s32 ret,recvd;
352 u8 *ptr = handle->message.smb;
353 NBTSMB *nbt = &handle->message;
355 memset(nbt,0,sizeof(NBTSMB));
356 recvd = net_recv(handle->sck_server,nbt,readlen,0);
357 if(recvd<12) return SMB_BAD_DATALEN;
359 /*** Do basic SMB Header checks ***/
360 ret = getUInt(ptr,SMB_OFFSET_PROTO);
361 if(ret!=SMB_PROTO) return SMB_BAD_PROTOCOL;
363 ret = getUChar(ptr, SMB_OFFSET_CMD);
364 if(ret!=command) return SMB_BAD_COMMAND;
366 ret = getUInt(ptr,SMB_OFFSET_NTSTATUS);
367 if(ret) return SMB_ERROR;
369 return SMB_SUCCESS;
373 * SMB_SetupAndX
375 * Setup the SMB session, including authentication with the
376 * magic 'NTLM Response'
378 static s32 SMB_SetupAndX(SMBHANDLE *handle)
380 s32 pos;
381 s32 bcpos;
382 s32 i, ret;
383 u8 *ptr = handle->message.smb;
384 SMBSESSION *sess = &handle->session;
385 char pwd[200], ntRespData[24];
387 MakeSMBHeader(SMB_SETUP_ANDX,0x08,0x01,handle);
388 pos = SMB_HEADER_SIZE;
390 setUChar(ptr,pos,13);
391 pos++; /*** Word Count ***/
392 setUChar(ptr,pos,0xff);
393 pos++; /*** Next AndX ***/
394 pos++; /*** Reserved ***/
395 pos += 2; /*** Next AndX Offset ***/
396 setUShort(ptr,pos,sess->MaxBuffer);
397 pos += 2;
398 setUShort(ptr,pos,sess->MaxMpx);
399 pos += 2;
400 setUShort(ptr,pos,sess->MaxVCS);
401 pos += 2;
402 setUInt(ptr,pos,sess->sKey);
403 pos += 4;
404 setUShort(ptr,pos,0); /*** Password length (case-insensitive) ***/
405 pos += 2;
406 setUShort(ptr,pos,24); /*** Password length (case-sensitive) ***/
407 pos += 2;
408 pos += 4; /*** Reserved ***/
409 pos += 4; /*** Capabilities ***/
410 bcpos = pos;
411 pos += 2; /*** Byte count ***/
413 /*** The magic 'NTLM Response' ***/
414 strcpy(pwd, handle->pwd);
415 ntlm_smb_nt_encrypt((const char *) pwd, (const u8 *) sess->challenge, (u8*) ntRespData);
417 /*** Build information ***/
418 memcpy(&ptr[pos],ntRespData,24);
419 pos += 24;
421 /*** Account ***/
422 strcpy(pwd, handle->user);
423 for(i=0;i<strlen(pwd);i++) pwd[i] = toupper(pwd[i]);
424 memcpy(&ptr[pos],pwd,strlen(pwd));
425 pos += strlen(pwd)+1;
427 /*** Primary Domain ***/
428 if(handle->user[0]=='\0') sess->p_domain[0] = '\0';
429 memcpy(&ptr[pos],sess->p_domain,strlen((const char*)sess->p_domain));
430 pos += strlen((const char*)sess->p_domain)+1;
432 /*** Native OS ***/
433 strcpy(pwd,"Unix (libOGC)");
434 memcpy(&ptr[pos],pwd,strlen(pwd));
435 pos += strlen(pwd)+1;
437 /*** Native LAN Manager ***/
438 strcpy(pwd,"Nintendo Wii");
439 memcpy(&ptr[pos],pwd,strlen(pwd));
440 pos += strlen (pwd)+1;
442 /*** Update byte count ***/
443 setUShort(ptr,bcpos,((pos-bcpos)-2));
445 handle->message.msg = NBT_SESSISON_MSG;
446 handle->message.length = htons (pos);
447 pos += 4;
449 ret = net_send(handle->sck_server,(char*)&handle->message,pos,0);
451 if((ret=SMBCheck(SMB_SETUP_ANDX,sizeof(NBTSMB),handle))==SMB_SUCCESS) {
452 /*** Collect UID ***/
453 sess->UID = getUShort(handle->message.smb,SMB_OFFSET_UID);
454 return SMB_SUCCESS;
456 return ret;
460 * SMB_TreeAndX
462 * Finally, net_connect to the remote share
464 static s32 SMB_TreeAndX(SMBHANDLE *handle)
466 s32 pos, bcpos, ret;
467 u8 path[256];
468 u8 *ptr = handle->message.smb;
469 SMBSESSION *sess = &handle->session;
471 MakeSMBHeader(SMB_TREEC_ANDX,0x08,0x01,handle);
472 pos = SMB_HEADER_SIZE;
474 setUChar(ptr,pos,4);
475 pos++; /*** Word Count ***/
476 setUChar(ptr,pos,0xff);
477 pos++; /*** Next AndX ***/
478 pos++; /*** Reserved ***/
479 pos += 2; /*** Next AndX Offset ***/
480 pos += 2; /*** Flags ***/
481 setUShort(ptr,pos,1);
482 pos += 2; /*** Password Length ***/
483 bcpos = pos;
484 pos += 2;
485 pos++; /*** NULL Password ***/
487 /*** Build server share path ***/
488 strcpy ((char*)path, "\\\\");
489 strcat ((char*)path, handle->server_name);
490 strcat ((char*)path, "\\");
491 strcat ((char*)path, handle->share_name);
492 for(ret=0;ret<strlen((const char*)path);ret++) path[ret] = toupper(path[ret]);
494 memcpy(&ptr[pos],path,strlen((const char*)path));
495 pos += strlen((const char*)path)+1;
497 /*** Service ***/
498 strcpy((char*)path,"?????");
499 memcpy(&ptr[pos],path,strlen((const char*)path));
500 pos += strlen((const char*)path)+1;
502 /*** Update byte count ***/
503 setUShort(ptr,bcpos,(pos-bcpos)-2);
505 handle->message.msg = NBT_SESSISON_MSG;
506 handle->message.length = htons (pos);
507 pos += 4;
509 ret = net_send(handle->sck_server,(char *)&handle->message,pos,0);
511 if((ret=SMBCheck(SMB_TREEC_ANDX,sizeof(NBTSMB),handle))==SMB_SUCCESS) {
512 /*** Collect Tree ID ***/
513 sess->TID = getUShort(handle->message.smb,SMB_OFFSET_TID);
514 return SMB_SUCCESS;
516 return ret;
520 * SMB_NegotiateProtocol
522 * The only protocol we admit to is 'NT LM 0.12'
524 static s32 SMB_NegotiateProtocol(const char *dialects[],int dialectc,SMBHANDLE *handle)
526 u8 *ptr;
527 s32 pos;
528 s32 bcnt,i,j;
529 s32 ret,len;
530 u16 bytecount;
531 u32 serverMaxBuffer;
532 SMBSESSION *sess;
534 if(!handle || !dialects || dialectc<=0)
535 return SMB_ERROR;
537 /*** Clear session variables ***/
538 sess = &handle->session;
539 memset(sess,0,sizeof(SMBSESSION));
540 sess->PID = 0xdead;
541 sess->MID = 1;
543 MakeSMBHeader(SMB_NEG_PROTOCOL,0x08,0x01,handle);
545 pos = SMB_HEADER_SIZE+3;
546 ptr = handle->message.smb;
547 for(i=0,bcnt=0;i<dialectc;i++) {
548 len = strlen(dialects[i])+1;
549 ptr[pos++] = '\x02';
550 memcpy(&ptr[pos],dialects[i],len);
551 pos += len;
552 bcnt += len+1;
554 /*** Update byte count ***/
555 setUShort(ptr,(SMB_HEADER_SIZE+1),bcnt);
557 /*** Set NBT information ***/
558 handle->message.msg = NBT_SESSISON_MSG;
559 handle->message.length = htons(pos);
560 pos += 4;
562 ret = net_send(handle->sck_server,(char*)&handle->message,pos,0);
563 if(ret<=0) return SMB_ERROR;
565 /*** Check response ***/
566 if((ret=SMBCheck(SMB_NEG_PROTOCOL,sizeof(NBTSMB),handle))==SMB_SUCCESS) {
567 pos = SMB_HEADER_SIZE;
568 ptr = handle->message.smb;
570 /*** Collect information ***/
571 if(getUChar(ptr,pos)!=17) return SMB_PROTO_FAIL; // UCHAR WordCount; Count of parameter words = 17
573 pos++;
574 if(getUShort(ptr,pos)) return SMB_PROTO_FAIL; // USHORT DialectIndex; Index of selected dialect - should always be 0 since we only supplied 1!
576 pos += 2;
577 if(getUChar(ptr,pos)!=3) return SMB_NOT_USER; //UCHAR SecurityMode; Security mode:
579 pos++;
580 sess->MaxMpx = getUShort(ptr, pos); //USHORT MaxMpxCount; Max pending outstanding requests
582 pos += 2;
583 sess->MaxVCS = getUShort(ptr, pos); //USHORT MaxNumberVcs; Max VCs between client and server
585 pos += 2;
586 serverMaxBuffer = getUInt(ptr, pos); //ULONG MaxBufferSize; Max transmit buffer size
587 if(serverMaxBuffer>SMB_MAX_TRANSMIT_SIZE)
588 sess->MaxBuffer = SMB_MAX_TRANSMIT_SIZE;
589 else
590 sess->MaxBuffer = serverMaxBuffer;
592 pos += 4;
593 pos += 4; //ULONG MaxRawSize; Maximum raw buffer size
594 sess->sKey = getUInt(ptr,pos);
595 pos += 4;
596 pos += 4; //ULONG Capabilities; Server capabilities
597 pos += 4; //ULONG SystemTimeLow; System (UTC) time of the server (low).
598 pos += 4; //ULONG SystemTimeHigh; System (UTC) time of the server (high).
599 pos += 2; //USHORT ServerTimeZone; Time zone of server (minutes from UTC)
600 if(getUChar(ptr,pos)!=8) return SMB_BAD_KEYLEN; //UCHAR EncryptionKeyLength - 0 or 8
602 pos++;
603 bytecount = getUShort(ptr,pos);
605 /*** Copy challenge key ***/
606 pos += 2;
607 memcpy(&sess->challenge,&ptr[pos],8);
609 /*** Primary domain ***/
610 pos += 8;
611 i = j = 0;
612 while(ptr[pos+j]!=0) {
613 sess->p_domain[i] = ptr[pos+j];
614 j += 2;
615 i++;
617 sess->p_domain[i] = '\0';
619 return SMB_SUCCESS;
622 return ret;
625 static s32 do_netconnect(SMBHANDLE *handle)
627 u32 nodelay;
628 s32 ret;
629 s32 sock;
631 /*** Create the global net_socket ***/
632 sock = net_socket(AF_INET, SOCK_STREAM, IPPROTO_IP);
633 if(sock==INVALID_SOCKET) return -1;
635 /*** Switch off Nagle, ON TCP_NODELAY ***/
636 nodelay = 1;
637 ret = net_setsockopt(sock,IPPROTO_TCP,TCP_NODELAY,&nodelay,sizeof(nodelay));
639 ret = net_connect(sock,(struct sockaddr*)&handle->server_addr,sizeof(handle->server_addr));
640 if(ret) {
641 net_close(sock);
642 return -1;
645 handle->sck_server = sock;
646 return 0;
649 static s32 do_smbconnect(SMBHANDLE *handle)
651 s32 ret;
653 ret = SMB_NegotiateProtocol(smb_dialects,smb_dialectcnt,handle);
654 if(ret!=SMB_SUCCESS) {
655 net_close(handle->sck_server);
656 return -1;
659 ret = SMB_SetupAndX(handle);
660 if(ret!=SMB_SUCCESS) {
661 net_close(handle->sck_server);
662 return -1;
665 ret = SMB_TreeAndX(handle);
666 if(ret!=SMB_SUCCESS) {
667 net_close(handle->sck_server);
668 return -1;
671 handle->conn_valid = TRUE;
672 return 0;
675 /****************************************************************************
676 * Primary setup, logon and connection all in one :)
677 ****************************************************************************/
678 s32 SMB_Connect(SMBCONN *smbhndl, const char *user, const char *password, const char *share, const char *IP)
680 s32 ret;
681 SMBHANDLE *handle;
683 *smbhndl = SMB_HANDLE_NULL;
685 if(smb_inited==FALSE) {
686 u32 level;
687 _CPU_ISR_Disable(level);
688 if(smb_inited==FALSE) __smb_init();
689 _CPU_ISR_Restore(level);
692 handle = __smb_allocate_handle();
693 if(!handle) return SMB_ERROR;
695 handle->user = strdup(user);
696 handle->pwd = strdup(password);
697 handle->server_name = strdup(IP);
698 handle->share_name = strdup(share);
700 handle->server_addr.sin_family = AF_INET;
701 handle->server_addr.sin_port = htons(445);
702 handle->server_addr.sin_addr.s_addr = inet_addr(IP);
704 ret = do_netconnect(handle);
705 if(ret==0) ret = do_smbconnect(handle);
706 if(ret!=0) {
707 __smb_free_handle(handle);
708 return SMB_ERROR;
710 *smbhndl =(SMBCONN)(LWP_OBJMASKTYPE(SMB_OBJTYPE_HANDLE)|LWP_OBJMASKID(handle->object.id));
711 return SMB_SUCCESS;
715 /****************************************************************************
716 * SMB_Destroy
718 * Probably NEVER called on GameCube, but here for completeness
719 ****************************************************************************/
720 void SMB_Close(SMBCONN smbhndl)
722 SMBHANDLE *handle;
724 handle = __smb_handle_open(smbhndl);
725 if(!handle) return;
727 if(handle->sck_server!=INVALID_SOCKET) net_close(handle->sck_server);
728 __smb_free_handle(handle);
731 s32 SMB_Reconnect(SMBCONN smbhndl, BOOL test_conn)
733 s32 ret = SMB_SUCCESS;
734 SMBHANDLE *handle = __smb_handle_open(smbhndl);
735 if(!handle)
736 return SMB_ERROR; // we have no handle, so we can't reconnect
738 if(handle->conn_valid && test_conn)
740 SMBDIRENTRY dentry;
741 SMB_PathInfo("\\", &dentry, smbhndl);
744 if(!handle->conn_valid)
746 // save connection details
747 const char * user = strdup(handle->user);
748 const char * pwd = strdup(handle->pwd);
749 const char * ip = strdup(handle->server_name);
750 const char * share = strdup(handle->share_name);
752 // shut down connection, and reopen
753 SMB_Close(smbhndl);
754 ret = SMB_Connect(&smbhndl, user, pwd, share, ip);
756 return ret;
759 SMBFILE SMB_OpenFile(const char *filename, u16 access, u16 creation,SMBCONN smbhndl)
761 s32 pos;
762 s32 bpos,ret;
763 u8 *ptr;
764 struct _smbfile *fid = NULL;
765 SMBHANDLE *handle;
766 char realfile[256];
768 if(SMB_Reconnect(smbhndl,TRUE)!=SMB_SUCCESS) return NULL;
770 handle = __smb_handle_open(smbhndl);
771 if(!handle) return NULL;
773 MakeSMBHeader(SMB_OPEN_ANDX,0x08,0x01,handle);
775 pos = SMB_HEADER_SIZE;
776 ptr = handle->message.smb;
777 setUChar(ptr, pos, 15);
778 pos++; /*** Word Count ***/
779 setUChar(ptr, pos, 0xff);
780 pos++; /*** Next AndX ***/
781 pos += 3; /*** Next AndX Offset ***/
783 pos += 2; /*** Flags ***/
784 setUShort(ptr, pos, access);
785 pos += 2; /*** Access mode ***/
786 setUShort(ptr, pos, 0x6);
787 pos += 2; /*** Type of file ***/
788 pos += 2; /*** Attributes ***/
789 pos += 4; /*** File time - don't care - let server decide ***/
790 setUShort(ptr, pos, creation);
791 pos += 2; /*** Creation flags ***/
792 pos += 4; /*** Allocation size ***/
793 pos += 8; /*** Reserved ***/
794 pos += 2; /*** Byte Count ***/
795 bpos = pos;
797 if (filename[0] != '\\') {
798 strcpy(realfile, "\\");
799 strcat(realfile,filename);
800 } else
801 strcpy(realfile,filename);
803 memcpy(&ptr[pos],realfile,strlen(realfile));
804 pos += strlen(realfile)+1;
806 setUShort(ptr,(bpos-2),(pos-bpos));
808 handle->message.msg = NBT_SESSISON_MSG;
809 handle->message.length = htons(pos);
811 pos += 4;
812 ret = net_send(handle->sck_server,(char*)&handle->message,pos,0);
813 if(ret<0) goto failed;
815 if(SMBCheck(SMB_OPEN_ANDX,sizeof(NBTSMB),handle)==SMB_SUCCESS) {
816 /*** Check file handle ***/
817 fid = (struct _smbfile*)__lwp_queue_get(&smb_filehandle_queue);
818 if(fid) {
819 fid->conn = smbhndl;
820 fid->sfid = getUShort(handle->message.smb,(SMB_HEADER_SIZE+5));
823 return (SMBFILE)fid;
825 failed:
826 handle->conn_valid = FALSE;
827 return NULL;
831 * SMB_Close
833 void SMB_CloseFile(SMBFILE sfid)
835 u8 *ptr;
836 s32 pos, ret;
837 SMBHANDLE *handle;
838 struct _smbfile *fid = (struct _smbfile*)sfid;
840 if(!fid) return;
842 handle = __smb_handle_open(fid->conn);
843 if(!handle) return;
845 MakeSMBHeader(SMB_CLOSE,0x08,0x01,handle);
847 pos = SMB_HEADER_SIZE;
848 ptr = handle->message.smb;
849 setUChar(ptr, pos, 3);
850 pos++; /** Word Count **/
851 setUShort(ptr, pos, fid->sfid);
852 pos += 2;
853 setUInt(ptr, pos, 0xffffffff);
854 pos += 4; /*** Last Write ***/
855 pos += 2; /*** Byte Count ***/
857 handle->message.msg = NBT_SESSISON_MSG;
858 handle->message.length = htons(pos);
860 pos += 4;
861 ret = net_send(handle->sck_server,(char*)&handle->message,pos,0);
862 if(ret<0) handle->conn_valid = FALSE;
864 SMBCheck(SMB_CLOSE,sizeof(NBTSMB),handle);
865 __lwp_queue_append(&smb_filehandle_queue,&fid->node);
869 * SMB_Read
871 s32 SMB_ReadFile(char *buffer, int size, int offset, SMBFILE sfid)
873 u8 *ptr,*dest;
874 s32 pos, ret, ofs;
875 u16 length = 0;
876 SMBHANDLE *handle;
877 struct _smbfile *fid = (struct _smbfile*)sfid;
879 if(!fid) return 0;
881 /*** Don't let the size exceed! ***/
882 if(size>SMB_MAX_BUFFERSIZE) return 0;
884 handle = __smb_handle_open(fid->conn);
885 if(!handle) return 0;
887 MakeSMBHeader(SMB_READ_ANDX,0x08,0x01,handle);
889 pos = SMB_HEADER_SIZE;
890 ptr = handle->message.smb;
891 setUChar(ptr, pos, 10);
892 pos++; /*** Word count ***/
893 setUChar(ptr, pos, 0xff);
894 pos++;
895 pos += 3; /*** Reserved, Next AndX Offset ***/
896 setUShort(ptr, pos, fid->sfid);
897 pos += 2; /*** FID ***/
898 setUInt(ptr, pos, offset);
899 pos += 4; /*** Offset ***/
901 setUShort(ptr, pos, size & 0xffff);
902 pos += 2;
903 setUShort(ptr, pos, size & 0xffff);
904 pos += 2;
905 setUInt(ptr, pos, 0);
906 pos += 4;
907 pos += 2; /*** Remaining ***/
908 pos += 2; /*** Byte count ***/
910 handle->message.msg = NBT_SESSISON_MSG;
911 handle->message.length = htons(pos);
913 pos += 4;
914 ret = net_send(handle->sck_server,(char*)&handle->message, pos, 0);
915 if(ret<0) goto failed;
917 /*** SMBCheck should now only read up to the end of a standard header ***/
918 if((ret=SMBCheck(SMB_READ_ANDX,(SMB_HEADER_SIZE+27+4),handle))==SMB_SUCCESS) {
919 ptr = handle->message.smb;
920 /*** Retrieve data length for this packet ***/
921 length = getUShort(ptr,(SMB_HEADER_SIZE+11));
922 /*** Retrieve offset to data ***/
923 ofs = getUShort(ptr,(SMB_HEADER_SIZE+13));
925 /*** Default offset, with no padding is 59, so grab any outstanding padding ***/
926 if(ofs>SMB_DEF_READOFFSET) {
927 char pad[1024];
928 ret = net_recv(handle->sck_server,pad,(ofs-SMB_DEF_READOFFSET), 0);
929 if(ret<0) return ret;
932 /*** Finally, go grab the data ***/
933 ofs = 0;
934 dest = (u8*)buffer;
935 if(length>0) {
936 while ((ret=net_recv(handle->sck_server,&dest[ofs],length, 0))!=0) {
937 if(ret<0) return ret;
939 ofs += ret;
940 if (ofs>=length) break;
943 return ofs;
945 return 0;
947 failed:
948 handle->conn_valid = FALSE;
949 return ret;
953 * SMB_Write
955 s32 SMB_WriteFile(const char *buffer, int size, int offset, SMBFILE sfid)
957 u8 *ptr,*src;
958 s32 pos, ret;
959 s32 blocks64;
960 u32 copy_len;
961 SMBHANDLE *handle;
962 struct _smbfile *fid = (struct _smbfile*)sfid;
964 if(!fid) return 0;
966 handle = __smb_handle_open(fid->conn);
967 if(!handle) return SMB_ERROR;
969 MakeSMBHeader(SMB_WRITE_ANDX,0x08,0x01,handle);
971 pos = SMB_HEADER_SIZE;
972 ptr = handle->message.smb;
973 setUChar(ptr, pos, 12);
974 pos++; /*** Word Count ***/
975 setUChar(ptr, pos, 0xff);
976 pos += 2; /*** Next AndX ***/
977 pos += 2; /*** Next AndX Offset ***/
979 setUShort(ptr, pos, fid->sfid);
980 pos += 2;
981 setUInt(ptr, pos, offset);
982 pos += 4;
983 pos += 4; /*** Reserved ***/
984 pos += 2; /*** Write Mode ***/
985 pos += 2; /*** Remaining ***/
987 blocks64 = size >> 16;
989 setUShort(ptr, pos, blocks64);
990 pos += 2; /*** Length High ***/
991 setUShort(ptr, pos, size & 0xffff);
992 pos += 2; /*** Length Low ***/
993 setUShort(ptr, pos, 59);
994 pos += 2; /*** Data Offset ***/
995 setUShort(ptr, pos, size & 0xffff);
996 pos += 2; /*** Data Byte Count ***/
998 handle->message.msg = NBT_SESSISON_MSG;
999 handle->message.length = htons(pos+size);
1001 src = (u8*)buffer;
1002 copy_len = size;
1003 if((copy_len+pos)>SMB_MAX_TRANSMIT_SIZE) copy_len = (SMB_MAX_TRANSMIT_SIZE-pos);
1005 memcpy(&ptr[pos],src,copy_len);
1006 size -= copy_len;
1007 src += copy_len;
1008 pos += copy_len;
1010 pos += 4;
1012 /*** Send Header Information ***/
1013 ret = net_send(handle->sck_server,(char*)&handle->message,pos,0);
1014 if(ret<0) goto failed;
1016 if(size>0) {
1017 /*** Send the data ***/
1018 ret = net_send(handle->sck_server,src,size,0);
1019 if(ret<0) goto failed;
1022 ret = 0;
1023 if(SMBCheck(SMB_WRITE_ANDX,sizeof(NBTSMB),handle)==SMB_SUCCESS) {
1024 ptr = handle->message.smb;
1025 ret = getUShort(ptr,(SMB_HEADER_SIZE+5));
1028 return ret;
1030 failed:
1031 handle->conn_valid = FALSE;
1032 return ret;
1036 * SMB_PathInfo
1038 s32 SMB_PathInfo(const char *filename, SMBDIRENTRY *sdir, SMBCONN smbhndl)
1040 u8 *ptr;
1041 s32 pos;
1042 s32 ret;
1043 s32 bpos;
1044 SMBHANDLE *handle;
1045 char realfile[256];
1047 handle = __smb_handle_open(smbhndl);
1048 if (!handle) return SMB_ERROR;
1050 MakeSMBHeader(SMB_TRANS2, 0x08, 0x01, handle);
1051 MakeTRANS2Header(SMB_QUERY_PATH_INFO, handle);
1053 bpos = pos = (T2_BYTE_CNT + 2);
1054 pos += 3; /*** Padding ***/
1055 ptr = handle->message.smb;
1056 setUShort(ptr, pos, 0x107); //SMB_QUERY_FILE_ALL_INFO
1058 pos += 2;
1059 setUInt(ptr, pos, 0);
1060 pos += 4; /*** reserved ***/
1062 if (filename[0] != '\\') {
1063 strcpy(realfile, "\\");
1064 strcat(realfile, filename);
1065 } else
1066 strcpy(realfile, filename);
1068 memcpy(&ptr[pos], realfile, strlen(realfile));
1069 pos += strlen(realfile) + 1; /*** Include padding ***/
1071 /*** Update counts ***/
1072 setUShort(ptr, T2_PRM_CNT, (7 + strlen(realfile)));
1073 setUShort(ptr, T2_SPRM_CNT, (7 + strlen(realfile)));
1074 setUShort(ptr, T2_SPRM_OFS, 68);
1075 setUShort(ptr, T2_SDATA_OFS, (75 + strlen(realfile)));
1076 setUShort(ptr, T2_BYTE_CNT, (pos - bpos));
1078 handle->message.msg = NBT_SESSISON_MSG;
1079 handle->message.length = htons(pos);
1081 pos += 4;
1082 ret = net_send(handle->sck_server, (char*) &handle->message, pos, 0);
1083 if(ret<0) goto failed;
1085 ret = SMB_ERROR;
1086 if (SMBCheck(SMB_TRANS2, sizeof(NBTSMB), handle) == SMB_SUCCESS) {
1088 ptr = handle->message.smb;
1089 /*** Get parameter offset ***/
1090 pos = getUShort(ptr, (SMB_HEADER_SIZE + 9));
1091 sdir->attributes = getUShort(ptr,pos+36);
1093 pos += 52;
1094 sdir->size_low = getUInt(ptr, pos);
1095 pos += 4;
1096 sdir->size_high = getUInt(ptr, pos);
1097 pos += 4;
1098 strcpy(sdir->name,realfile);
1100 ret = SMB_SUCCESS;
1102 return ret;
1104 failed:
1105 handle->conn_valid = FALSE;
1106 return ret;
1110 * SMB_FindFirst
1112 * Uses TRANS2 to support long filenames
1114 s32 SMB_FindFirst(const char *filename, unsigned short flags, SMBDIRENTRY *sdir, SMBCONN smbhndl)
1116 u8 *ptr;
1117 s32 pos;
1118 s32 ret;
1119 s32 bpos;
1120 SMBHANDLE *handle;
1121 SMBSESSION *sess;
1123 if(SMB_Reconnect(smbhndl,TRUE)!=SMB_SUCCESS) return SMB_ERROR;
1125 handle = __smb_handle_open(smbhndl);
1126 if(!handle) return SMB_ERROR;
1128 sess = &handle->session;
1129 MakeSMBHeader(SMB_TRANS2,0x08,0x01,handle);
1130 MakeTRANS2Header(SMB_FIND_FIRST2,handle);
1132 bpos = pos = (T2_BYTE_CNT+2);
1133 pos += 3; /*** Padding ***/
1134 ptr = handle->message.smb;
1135 setUShort(ptr, pos, flags);
1136 pos += 2; /*** Flags ***/
1137 setUShort(ptr, pos, 1);
1138 pos += 2; /*** Count ***/
1139 setUShort(ptr, pos, 6);
1140 pos += 2; /*** Internal Flags ***/
1141 setUShort(ptr, pos, 260);
1142 pos += 2; /*** Level of Interest ***/
1143 pos += 4; /*** Storage Type == 0 ***/
1144 memcpy(&ptr[pos], filename, strlen(filename));
1145 pos += strlen(filename)+1; /*** Include padding ***/
1147 /*** Update counts ***/
1148 setUShort(ptr, T2_PRM_CNT, (13+strlen(filename)));
1149 setUShort(ptr, T2_SPRM_CNT, (13+strlen(filename)));
1150 setUShort(ptr, T2_SPRM_OFS, 68);
1151 setUShort(ptr, T2_SDATA_OFS,(81+strlen(filename)));
1152 setUShort(ptr, T2_BYTE_CNT,(pos-bpos));
1154 handle->message.msg = NBT_SESSISON_MSG;
1155 handle->message.length = htons(pos);
1157 pos += 4;
1158 ret = net_send(handle->sck_server,(char*)&handle->message,pos,0);
1159 if(ret<0) goto failed;
1161 sess->eos = 1;
1162 sess->sid = 0;
1163 sess->count = 0;
1164 ret = SMB_ERROR;
1165 if(SMBCheck(SMB_TRANS2,sizeof(NBTSMB),handle)==SMB_SUCCESS) {
1166 ptr = handle->message.smb;
1167 /*** Get parameter offset ***/
1168 pos = getUShort(ptr,(SMB_HEADER_SIZE+9));
1169 sess->sid = getUShort(ptr, pos);
1170 pos += 2;
1171 sess->count = getUShort(ptr, pos);
1172 pos += 2;
1173 sess->eos = getUShort(ptr, pos);
1174 pos += 2;
1175 pos += 46;
1177 if (sess->count) {
1178 sdir->size_low = getUInt(ptr, pos);
1179 pos += 4;
1180 sdir->size_high = getUInt(ptr, pos);
1181 pos += 4;
1182 pos += 8;
1183 sdir->attributes = getUInt(ptr, pos);
1184 pos += 38;
1185 strcpy(sdir->name, (const char*)&ptr[pos]);
1187 ret = SMB_SUCCESS;
1190 return ret;
1192 failed:
1193 handle->conn_valid = FALSE;
1194 return ret;
1198 * SMB_FindNext
1200 s32 SMB_FindNext(SMBDIRENTRY *sdir,SMBCONN smbhndl)
1202 u8 *ptr;
1203 s32 pos;
1204 s32 ret;
1205 s32 bpos;
1206 SMBHANDLE *handle;
1207 SMBSESSION *sess;
1209 if(SMB_Reconnect(smbhndl,TRUE)!=SMB_SUCCESS) return SMB_ERROR;
1211 handle = __smb_handle_open(smbhndl);
1212 if(!handle) return SMB_ERROR;
1214 sess = &handle->session;
1215 if(sess->eos || sess->sid==0) return SMB_ERROR;
1217 MakeSMBHeader(SMB_TRANS2,0x08,0x01,handle);
1218 MakeTRANS2Header(SMB_FIND_NEXT2,handle);
1220 bpos = pos = (T2_BYTE_CNT+2);
1221 pos += 3; /*** Padding ***/
1222 ptr = handle->message.smb;
1223 setUShort(ptr, pos, sess->sid);
1224 pos += 2; /*** Search ID ***/
1225 setUShort(ptr, pos, 1);
1226 pos += 2; /*** Count ***/
1227 setUShort(ptr, pos, 260);
1228 pos += 2; /*** Level of Interest ***/
1229 pos += 4; /*** Storage Type == 0 ***/
1230 setUShort(ptr, pos, 12);
1231 pos+=2; /*** Search flags ***/
1232 pos++;
1234 /*** Update counts ***/
1235 setUShort(ptr, T2_PRM_CNT, 13);
1236 setUShort(ptr, T2_SPRM_CNT, 13);
1237 setUShort(ptr, T2_SPRM_OFS, 68);
1238 setUShort(ptr, T2_SDATA_OFS, 81);
1239 setUShort(ptr, T2_BYTE_CNT, (pos-bpos));
1241 handle->message.msg = NBT_SESSISON_MSG;
1242 handle->message.length = htons(pos);
1244 pos += 4;
1245 ret = net_send(handle->sck_server,(char*)&handle->message,pos,0);
1246 if(ret<0) goto failed;
1248 ret = SMB_ERROR;
1249 if (SMBCheck(SMB_TRANS2,sizeof(NBTSMB),handle)==SMB_SUCCESS) {
1250 ptr = handle->message.smb;
1251 /*** Get parameter offset ***/
1252 pos = getUShort(ptr,(SMB_HEADER_SIZE+9));
1253 sess->count = getUShort(ptr, pos);
1254 pos += 2;
1255 sess->eos = getUShort(ptr, pos);
1256 pos += 2;
1257 pos += 44;
1259 if (sess->count) {
1260 sdir->size_low = getUInt(ptr, pos);
1261 pos += 4;
1262 sdir->size_high = getUInt(ptr, pos);
1263 pos += 4;
1264 pos += 8;
1265 sdir->attributes = getUInt(ptr, pos);
1266 pos += 38;
1267 strcpy (sdir->name, (const char*)&ptr[pos]);
1269 ret = SMB_SUCCESS;
1272 return ret;
1274 failed:
1275 handle->conn_valid = FALSE;
1276 return ret;
1280 * SMB_FindClose
1282 s32 SMB_FindClose(SMBCONN smbhndl)
1284 u8 *ptr;
1285 s32 pos;
1286 s32 ret;
1287 SMBHANDLE *handle;
1288 SMBSESSION *sess;
1290 if(SMB_Reconnect(smbhndl,TRUE)!=SMB_SUCCESS) return SMB_ERROR;
1292 handle = __smb_handle_open(smbhndl);
1293 if(!handle) return SMB_ERROR;
1295 sess = &handle->session;
1296 if(sess->sid==0) return SMB_ERROR;
1298 MakeSMBHeader(SMB_FIND_CLOSE2,0x08,0x01,handle);
1300 pos = SMB_HEADER_SIZE;
1301 ptr = handle->message.smb;
1302 setUChar(ptr, pos, 1);
1303 pos++; /*** Word Count ***/
1304 setUShort(ptr, pos, sess->sid);
1305 pos += 2;
1306 pos += 2; /*** Byte Count ***/
1308 handle->message.msg = NBT_SESSISON_MSG;
1309 handle->message.length = htons(pos);
1311 pos += 4;
1312 ret = net_send(handle->sck_server,(char*)&handle->message,pos,0);
1313 if(ret<0) goto failed;
1315 ret = SMBCheck(SMB_FIND_CLOSE2,sizeof(NBTSMB),handle);
1316 return ret;
1318 failed:
1319 handle->conn_valid = FALSE;
1320 return ret;