correct array size
[libogc.git] / libtinysmb / smb.c
blob2eeacca239320d053b168d1869decc0e4ae889ae
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 <unistd.h>
36 #include <limits.h>
37 #include <stdlib.h>
38 #include <stdio.h>
39 #include <string.h>
40 #include <malloc.h>
41 #include <ctype.h>
42 #include <gccore.h>
43 #include <network.h>
44 #include <processor.h>
45 #include <lwp_threads.h>
46 #include <lwp_objmgr.h>
47 #include <ogc/lwp_watchdog.h>
48 #include <errno.h>
49 #include <fcntl.h>
50 #include <smb.h>
52 #define IOS_O_NONBLOCK 0x04
53 #define RECV_TIMEOUT 3000 // in ms
55 /**
56 * Field offsets.
58 #define SMB_OFFSET_PROTO 0
59 #define SMB_OFFSET_CMD 4
60 #define SMB_OFFSET_NTSTATUS 5
61 #define SMB_OFFSET_ECLASS 5
62 #define SMB_OFFSET_ECODE 7
63 #define SMB_OFFSET_FLAGS 9
64 #define SMB_OFFSET_FLAGS2 10
65 #define SMB_OFFSET_EXTRA 12
66 #define SMB_OFFSET_TID 24
67 #define SMB_OFFSET_PID 26
68 #define SMB_OFFSET_UID 28
69 #define SMB_OFFSET_MID 30
70 #define SMB_HEADER_SIZE 32 /*** SMB Headers are always 32 bytes long ***/
72 /**
73 * Message / Commands
75 #define NBT_SESSISON_MSG 0x00
77 #define SMB_NEG_PROTOCOL 0x72
78 #define SMB_SETUP_ANDX 0x73
79 #define SMB_TREEC_ANDX 0x75
82 #define NBT_KEEPALIVE_MSG 0x85
83 #define KEEPALIVE_SIZE 4
85 /**
86 * SMBTrans2
88 #define SMB_TRANS2 0x32
90 #define SMB_OPEN2 0
91 #define SMB_FIND_FIRST2 1
92 #define SMB_FIND_NEXT2 2
93 #define SMB_QUERY_FS_INFO 3
94 #define SMB_QUERY_PATH_INFO 5
95 #define SMB_SET_PATH_INFO 6
96 #define SMB_QUERY_FILE_INFO 7
97 #define SMB_SET_FILE_INFO 8
98 #define SMB_CREATE_DIR 13
99 #define SMB_FIND_CLOSE2 0x34
100 #define SMB_QUERY_FILE_ALL_INFO 0x107
103 * File I/O
105 #define SMB_OPEN_ANDX 0x2d
106 #define SMB_WRITE_ANDX 0x2f
107 #define SMB_READ_ANDX 0x2e
108 #define SMB_CLOSE 0x04
112 * TRANS2 Offsets
114 #define T2_WORD_CNT (SMB_HEADER_SIZE)
115 #define T2_PRM_CNT (T2_WORD_CNT+1)
116 #define T2_DATA_CNT (T2_PRM_CNT+2)
117 #define T2_MAXPRM_CNT (T2_DATA_CNT+2)
118 #define T2_MAXBUFFER (T2_MAXPRM_CNT+2)
119 #define T2_SETUP_CNT (T2_MAXBUFFER+2)
120 #define T2_SPRM_CNT (T2_SETUP_CNT+10)
121 #define T2_SPRM_OFS (T2_SPRM_CNT+2)
122 #define T2_SDATA_CNT (T2_SPRM_OFS+2)
123 #define T2_SDATA_OFS (T2_SDATA_CNT+2)
124 #define T2_SSETUP_CNT (T2_SDATA_OFS+2)
125 #define T2_SUB_CMD (T2_SSETUP_CNT+2)
126 #define T2_BYTE_CNT (T2_SUB_CMD+2)
129 #define SMB_PROTO 0x424d53ff
130 #define SMB_HANDLE_NULL 0xffffffff
131 #define SMB_MAX_BUFFERSIZE ((1024*64)-64) // cannot be larger than u16 (65536) - samba header (64)
132 #define SMB_MAX_NET_READ_SIZE 7300 // see smb_recv
133 #define SMB_MAX_NET_WRITE_SIZE 4096 // see smb_sendv
134 #define SMB_MAX_TRANSMIT_SIZE 16384
135 #define SMB_DEF_READOFFSET 63
137 #define SMB_CONNHANDLES_MAX 8
138 #define SMB_FILEHANDLES_MAX (32*SMB_CONNHANDLES_MAX)
140 #define SMB_OBJTYPE_HANDLE 7
141 #define SMB_CHECK_HANDLE(hndl) \
143 if(((hndl)==SMB_HANDLE_NULL) || (LWP_OBJTYPE(hndl)!=SMB_OBJTYPE_HANDLE)) \
144 return NULL; \
147 /* NBT Session Service Packet Type Codes
150 #define SESS_MSG 0x00
151 #define SESS_REQ 0x81
152 #define SESS_POS_RESP 0x82
153 #define SESS_NEG_RESP 0x83
154 #define SESS_RETARGET 0x84
155 #define SESS_KEEPALIVE 0x85
157 struct _smbfile
159 lwp_node node;
160 u16 sfid;
161 SMBCONN conn;
165 * NBT/SMB Wrapper
167 typedef struct _nbtsmb
169 u8 msg; /*** NBT Message ***/
170 u8 flags; /*** Not much here really ***/
171 u16 length; /*** Length, excluding NBT ***/
172 u8 smb[SMB_MAX_TRANSMIT_SIZE];
173 } NBTSMB;
176 * Session Information
178 typedef struct _smbsession
180 u16 TID;
181 u16 PID;
182 u16 UID;
183 u16 MID;
184 u32 sKey;
185 u16 MaxBuffer;
186 u16 MaxMpx;
187 u16 MaxVCS;
188 u8 challenge[10];
189 u8 p_domain[64];
190 s64 timeOffset;
191 u16 sid;
192 u16 count;
193 u16 eos;
194 bool challengeUsed;
195 } SMBSESSION;
197 typedef struct _smbhandle
199 lwp_obj object;
200 char *user;
201 char *pwd;
202 char *share_name;
203 char *server_name;
204 s32 sck_server;
205 struct sockaddr_in server_addr;
206 BOOL conn_valid;
207 SMBSESSION session;
208 NBTSMB message;
209 } SMBHANDLE;
211 static u32 smb_dialectcnt = 1;
212 static BOOL smb_inited = FALSE;
213 static lwp_objinfo smb_handle_objects;
214 static lwp_queue smb_filehandle_queue;
215 static struct _smbfile smb_filehandles[SMB_FILEHANDLES_MAX];
216 static const char *smb_dialects[] = {"NT LM 0.12",NULL};
218 extern void ntlm_smb_nt_encrypt(const char *passwd, const u8 * challenge, u8 * answer);
221 * SMB Endian aware supporting functions
223 * SMB always uses Intel Little-Endian values, so htons etc are
224 * of little or no use :) ... Thanks M$
227 /*** get unsigned char ***/
228 static __inline__ u8 getUChar(u8 *buffer,u32 offset)
230 return (u8)buffer[offset];
233 /*** set unsigned char ***/
234 static __inline__ void setUChar(u8 *buffer,u32 offset,u8 value)
236 buffer[offset] = value;
239 /*** get signed short ***/
240 static __inline__ s16 getShort(u8 *buffer,u32 offset)
242 return (s16)((buffer[offset+1]<<8)|(buffer[offset]));
245 /*** get unsigned short ***/
246 static __inline__ u16 getUShort(u8 *buffer,u32 offset)
248 return (u16)((buffer[offset+1]<<8)|(buffer[offset]));
251 /*** set unsigned short ***/
252 static __inline__ void setUShort(u8 *buffer,u32 offset,u16 value)
254 buffer[offset] = (value&0xff);
255 buffer[offset+1] = ((value&0xff00)>>8);
258 /*** get unsigned int ***/
259 static __inline__ u32 getUInt(u8 *buffer,u32 offset)
261 return (u32)((buffer[offset+3]<<24)|(buffer[offset+2]<<16)|(buffer[offset+1]<<8)|buffer[offset]);
264 /*** set unsigned int ***/
265 static __inline__ void setUInt(u8 *buffer,u32 offset,u32 value)
267 buffer[offset] = (value&0xff);
268 buffer[offset+1] = ((value&0xff00)>>8);
269 buffer[offset+2] = ((value&0xff0000)>>16);
270 buffer[offset+3] = ((value&0xff000000)>>24);
273 /*** get unsigned long long ***/
274 static __inline__ u64 getULongLong(u8 *buffer,u32 offset)
276 return (u64)(getUInt(buffer, offset) | (u64)getUInt(buffer, offset+4) << 32);
279 static __inline__ SMBHANDLE* __smb_handle_open(SMBCONN smbhndl)
281 u32 level;
282 SMBHANDLE *handle;
284 SMB_CHECK_HANDLE(smbhndl);
286 _CPU_ISR_Disable(level);
287 handle = (SMBHANDLE*)__lwp_objmgr_getnoprotection(&smb_handle_objects,LWP_OBJMASKID(smbhndl));
288 _CPU_ISR_Restore(level);
289 return handle;
293 static __inline__ void __smb_handle_free(SMBHANDLE *handle)
295 u32 level;
297 _CPU_ISR_Disable(level);
298 __lwp_objmgr_close(&smb_handle_objects,&handle->object);
299 __lwp_objmgr_free(&smb_handle_objects,&handle->object);
300 _CPU_ISR_Restore(level);
303 static void __smb_init()
305 smb_inited = TRUE;
306 __lwp_objmgr_initinfo(&smb_handle_objects,SMB_CONNHANDLES_MAX,sizeof(SMBHANDLE));
307 __lwp_queue_initialize(&smb_filehandle_queue,smb_filehandles,SMB_FILEHANDLES_MAX,sizeof(struct _smbfile));
310 static SMBHANDLE* __smb_allocate_handle()
312 u32 level;
313 SMBHANDLE *handle;
315 _CPU_ISR_Disable(level);
316 handle = (SMBHANDLE*)__lwp_objmgr_allocate(&smb_handle_objects);
317 if(handle) {
318 handle->user = NULL;
319 handle->pwd = NULL;
320 handle->server_name = NULL;
321 handle->share_name = NULL;
322 handle->sck_server = INVALID_SOCKET;
323 handle->conn_valid = FALSE;
324 __lwp_objmgr_open(&smb_handle_objects,&handle->object);
326 _CPU_ISR_Restore(level);
327 return handle;
330 static void __smb_free_handle(SMBHANDLE *handle)
332 if(handle->user) free(handle->user);
333 if(handle->pwd) free(handle->pwd);
334 if(handle->server_name) free(handle->server_name);
335 if(handle->share_name) free(handle->share_name);
337 handle->user = NULL;
338 handle->pwd = NULL;
339 handle->server_name = NULL;
340 handle->share_name = NULL;
341 handle->sck_server = INVALID_SOCKET;
343 __smb_handle_free(handle);
346 static void MakeSMBHeader(u8 command,u8 flags,u16 flags2,SMBHANDLE *handle)
348 u8 *ptr = handle->message.smb;
349 NBTSMB *nbt = &handle->message;
350 SMBSESSION *sess = &handle->session;
352 memset(nbt,0,sizeof(NBTSMB));
354 setUInt(ptr,SMB_OFFSET_PROTO,SMB_PROTO);
355 setUChar(ptr,SMB_OFFSET_CMD,command);
356 setUChar(ptr,SMB_OFFSET_FLAGS,flags);
357 setUShort(ptr,SMB_OFFSET_FLAGS2,flags2);
358 setUShort(ptr,SMB_OFFSET_TID,sess->TID);
359 setUShort(ptr,SMB_OFFSET_PID,sess->PID);
360 setUShort(ptr,SMB_OFFSET_UID,sess->UID);
361 setUShort(ptr,SMB_OFFSET_MID,sess->MID);
363 ptr[SMB_HEADER_SIZE] = 0;
367 * MakeTRANS2Hdr
369 static void MakeTRANS2Header(u8 subcommand,SMBHANDLE *handle)
371 u8 *ptr = handle->message.smb;
372 SMBSESSION *sess = &handle->session;
374 setUChar(ptr, T2_WORD_CNT, 15);
375 setUShort(ptr, T2_MAXPRM_CNT, 10);
376 setUShort(ptr, T2_MAXBUFFER, sess->MaxBuffer);
377 setUChar(ptr, T2_SSETUP_CNT, 1);
378 setUShort(ptr, T2_SUB_CMD, subcommand);
382 * smb_send
384 * blocking call with timeout
385 * will return when ALL data has been sent. Number of bytes sent is returned.
386 * OR timeout. Timeout will return -1
387 * OR network error. -ve value will be returned
389 static inline s32 smb_send(s32 s,const void *data,s32 size)
391 u64 t1,t2;
392 s32 ret, len = size, nextsend;
394 t1=ticks_to_millisecs(gettime());
395 while(len>0)
397 nextsend=len;
399 if(nextsend>SMB_MAX_NET_WRITE_SIZE)
400 nextsend=SMB_MAX_NET_WRITE_SIZE; //optimized value
402 ret=net_send(s,data,nextsend,0);
403 if(ret==-EAGAIN)
405 t2=ticks_to_millisecs(gettime());
406 if( (t2 - t1) > RECV_TIMEOUT)
408 return -1; // timeout
410 usleep(100); // allow system to perform work. Stabilizes system
411 continue;
413 else if(ret<0)
415 return ret; // an error occurred
417 else
419 data+=ret;
420 len-=ret;
421 if(len==0) return size;
422 t1=ticks_to_millisecs(gettime());
425 return size;
429 * smb_recv
431 * blocking call with timeout
432 * will return when ANY data has been read from socket. Number of bytes read is returned.
433 * OR timeout. Timeout will return -1
434 * OR network error. -ve value will be returned
436 static s32 smb_recv(s32 s,void *mem,s32 len)
438 s32 ret,read,readtotal=0;
439 u64 t1,t2;
441 t1=ticks_to_millisecs(gettime());
442 while(len > 0)
444 read=len;
445 if(read>SMB_MAX_NET_READ_SIZE)
446 read=SMB_MAX_NET_READ_SIZE; // optimized value
448 ret=net_recv(s,mem+readtotal,read,0);
449 if(ret>=0)
451 readtotal+=ret;
452 len-=ret;
453 if(len==0) return readtotal;
455 else
457 if(ret!=-EAGAIN) return ret;
458 t2=ticks_to_millisecs(gettime());
459 if( (t2 - t1) > RECV_TIMEOUT) return -1;
460 usleep(100); // allow system to perform work. Stabilizes system
463 return readtotal;
467 * SMBCheck
469 * Do very basic checking on the return SMB
470 * Read <readlen> bytes
471 * if <readlen>==0 then read a single SMB packet
472 * discard any NBT_KEEPALIVE_MSG packets along the way.
474 static s32 SMBCheck(u8 command, s32 readlen,SMBHANDLE *handle)
476 s32 ret,recvd = 0;
477 u8 *ptr = handle->message.smb;
478 NBTSMB *nbt = &handle->message;
479 u8 *ptr2 = (u8*)nbt;
480 u8 tempLength = (readlen==0);
482 if(handle->sck_server == INVALID_SOCKET) return SMB_ERROR;
484 /* if length is not specified,*/
485 if(tempLength)
487 /* read entire header if available. if not, just get whatever available.*/
488 readlen = SMB_HEADER_SIZE+4;
491 memset(nbt,0,sizeof(NBTSMB));
494 /*keep going till we get all the data we wanted*/
495 while(recvd<readlen)
497 ret=smb_recv(handle->sck_server, ptr2+recvd, readlen-recvd);
498 if(ret<0)
500 return SMB_ERROR;
502 recvd+=ret;
503 /* discard any and all keepalive packets */
504 while( (nbt->msg==NBT_KEEPALIVE_MSG) && (recvd>=KEEPALIVE_SIZE) )
506 recvd-=KEEPALIVE_SIZE;
507 memmove(ptr2, ptr2+KEEPALIVE_SIZE, recvd);
509 /* obtain required length from NBT header if readlen==0*/
510 if(tempLength && recvd>=KEEPALIVE_SIZE)
512 /* get the length header */
513 if(nbt->flags!=0 || nbt->length>SMB_MAX_TRANSMIT_SIZE)
515 /*length too big to be supported*/
516 return SMB_BAD_DATALEN;
518 else
520 readlen = nbt->length+KEEPALIVE_SIZE;
521 tempLength = 0;
526 /*** Do basic SMB Header checks ***/
527 ret = getUInt(ptr,SMB_OFFSET_PROTO);
528 if(ret!=SMB_PROTO) return SMB_BAD_PROTOCOL;
530 ret = getUChar(ptr, SMB_OFFSET_CMD);
531 if(ret!=command) return SMB_BAD_COMMAND;
533 ret = getUInt(ptr,SMB_OFFSET_NTSTATUS);
534 if(ret) return SMB_ERROR;
536 return SMB_SUCCESS;
540 * SMB_SetupAndX
542 * Setup the SMB session, including authentication with the
543 * magic 'NTLM Response'
545 static s32 SMB_SetupAndX(SMBHANDLE *handle)
547 s32 pos;
548 s32 bcpos;
549 s32 i, ret;
550 u8 *ptr = handle->message.smb;
551 SMBSESSION *sess = &handle->session;
552 char pwd[15], ntRespData[24];
554 if(handle->sck_server == INVALID_SOCKET) return SMB_ERROR;
556 MakeSMBHeader(SMB_SETUP_ANDX,0x08,0x01,handle);
557 pos = SMB_HEADER_SIZE;
559 setUChar(ptr,pos,13);
560 pos++; /*** Word Count ***/
561 setUChar(ptr,pos,0xff);
562 pos++; /*** Next AndX ***/
563 pos++; /*** Reserved ***/
564 pos += 2; /*** Next AndX Offset ***/
565 setUShort(ptr,pos,sess->MaxBuffer);
566 pos += 2;
567 setUShort(ptr,pos,sess->MaxMpx);
568 pos += 2;
569 setUShort(ptr,pos,sess->MaxVCS);
570 pos += 2;
571 setUInt(ptr,pos,sess->sKey);
572 pos += 4;
573 setUShort(ptr,pos,0); /*** Password length (case-insensitive) ***/
574 pos += 2;
575 setUShort(ptr,pos,24); /*** Password length (case-sensitive) ***/
576 pos += 2;
577 pos += 4; /*** Reserved ***/
578 pos += 4; /*** Capabilities ***/
579 bcpos = pos;
580 pos += 2; /*** Byte count ***/
582 /*** The magic 'NTLM Response' ***/
583 strcpy(pwd, handle->pwd);
584 if (sess->challengeUsed)
585 ntlm_smb_nt_encrypt((const char *) pwd, (const u8 *) sess->challenge, (u8*) ntRespData);
587 /*** Build information ***/
588 memcpy(&ptr[pos],ntRespData,24);
589 pos += 24;
591 /*** Account ***/
592 strcpy(pwd, handle->user);
593 for(i=0;i<strlen(pwd);i++) pwd[i] = toupper((int)pwd[i]);
594 memcpy(&ptr[pos],pwd,strlen(pwd));
595 pos += strlen(pwd)+1;
597 /*** Primary Domain ***/
598 if(handle->user[0]=='\0') sess->p_domain[0] = '\0';
599 memcpy(&ptr[pos],sess->p_domain,strlen((const char*)sess->p_domain));
600 pos += strlen((const char*)sess->p_domain)+1;
602 /*** Native OS ***/
603 strcpy(pwd,"Unix (libOGC)");
604 memcpy(&ptr[pos],pwd,strlen(pwd));
605 pos += strlen(pwd)+1;
607 /*** Native LAN Manager ***/
608 strcpy(pwd,"Nintendo Wii");
609 memcpy(&ptr[pos],pwd,strlen(pwd));
610 pos += strlen (pwd)+1;
612 /*** Update byte count ***/
613 setUShort(ptr,bcpos,((pos-bcpos)-2));
615 handle->message.msg = NBT_SESSISON_MSG;
616 handle->message.length = htons (pos);
617 pos += 4;
619 ret = smb_send(handle->sck_server,(char*)&handle->message,pos);
620 if(ret<=0) return SMB_ERROR;
622 if((ret=SMBCheck(SMB_SETUP_ANDX,0,handle))==SMB_SUCCESS) {
623 /*** Collect UID ***/
624 sess->UID = getUShort(handle->message.smb,SMB_OFFSET_UID);
625 return SMB_SUCCESS;
627 return ret;
631 * SMB_TreeAndX
633 * Finally, net_connect to the remote share
635 static s32 SMB_TreeAndX(SMBHANDLE *handle)
637 s32 pos, bcpos, ret;
638 u8 path[256];
639 u8 *ptr = handle->message.smb;
640 SMBSESSION *sess = &handle->session;
642 if(handle->sck_server == INVALID_SOCKET) return SMB_ERROR;
644 MakeSMBHeader(SMB_TREEC_ANDX,0x08,0x01,handle);
645 pos = SMB_HEADER_SIZE;
647 setUChar(ptr,pos,4);
648 pos++; /*** Word Count ***/
649 setUChar(ptr,pos,0xff);
650 pos++; /*** Next AndX ***/
651 pos++; /*** Reserved ***/
652 pos += 2; /*** Next AndX Offset ***/
653 pos += 2; /*** Flags ***/
654 setUShort(ptr,pos,1);
655 pos += 2; /*** Password Length ***/
656 bcpos = pos;
657 pos += 2;
658 pos++; /*** NULL Password ***/
660 /*** Build server share path ***/
661 strcpy ((char*)path, "\\\\");
662 strcat ((char*)path, handle->server_name);
663 strcat ((char*)path, "\\");
664 strcat ((char*)path, handle->share_name);
665 for(ret=0;ret<strlen((const char*)path);ret++) path[ret] = toupper(path[ret]);
667 memcpy(&ptr[pos],path,strlen((const char*)path));
668 pos += strlen((const char*)path)+1;
670 /*** Service ***/
671 strcpy((char*)path,"?????");
672 memcpy(&ptr[pos],path,strlen((const char*)path));
673 pos += strlen((const char*)path)+1;
675 /*** Update byte count ***/
676 setUShort(ptr,bcpos,(pos-bcpos)-2);
678 handle->message.msg = NBT_SESSISON_MSG;
679 handle->message.length = htons (pos);
680 pos += 4;
682 ret = smb_send(handle->sck_server,(char *)&handle->message,pos);
683 if(ret<=0) return SMB_ERROR;
685 if((ret=SMBCheck(SMB_TREEC_ANDX,0,handle))==SMB_SUCCESS) {
686 /*** Collect Tree ID ***/
687 sess->TID = getUShort(handle->message.smb,SMB_OFFSET_TID);
688 return SMB_SUCCESS;
690 return ret;
694 * SMB_NegotiateProtocol
696 * The only protocol we admit to is 'NT LM 0.12'
698 static s32 SMB_NegotiateProtocol(const char *dialects[],int dialectc,SMBHANDLE *handle)
700 u8 *ptr;
701 s32 pos;
702 s32 bcnt,i,j;
703 s32 ret,len;
704 u16 bytecount;
705 u32 serverMaxBuffer;
706 SMBSESSION *sess;
708 if(!handle || !dialects || dialectc<=0)
709 return SMB_ERROR;
711 if(handle->sck_server == INVALID_SOCKET) return SMB_ERROR;
713 /*** Clear session variables ***/
714 sess = &handle->session;
715 memset(sess,0,sizeof(SMBSESSION));
716 sess->PID = 0xdead;
717 sess->MID = 1;
719 MakeSMBHeader(SMB_NEG_PROTOCOL,0x08,0x01,handle);
721 pos = SMB_HEADER_SIZE+3;
722 ptr = handle->message.smb;
723 for(i=0,bcnt=0;i<dialectc;i++) {
724 len = strlen(dialects[i])+1;
725 ptr[pos++] = '\x02';
726 memcpy(&ptr[pos],dialects[i],len);
727 pos += len;
728 bcnt += len+1;
730 /*** Update byte count ***/
731 setUShort(ptr,(SMB_HEADER_SIZE+1),bcnt);
733 /*** Set NBT information ***/
734 handle->message.msg = NBT_SESSISON_MSG;
735 handle->message.length = htons(pos);
736 pos += 4;
738 ret = smb_send(handle->sck_server,(char*)&handle->message,pos);
739 if(ret<=0) return SMB_ERROR;
741 /*** Check response ***/
742 if((ret=SMBCheck(SMB_NEG_PROTOCOL,0,handle))==SMB_SUCCESS)
744 pos = SMB_HEADER_SIZE;
745 ptr = handle->message.smb;
747 /*** Collect information ***/
748 if(getUChar(ptr,pos)!=17) return SMB_PROTO_FAIL; // UCHAR WordCount; Count of parameter words = 17
750 pos++;
751 if(getUShort(ptr,pos)!=0) return SMB_PROTO_FAIL; // USHORT DialectIndex; Index of selected dialect - should always be 0 since we only supplied 1!
753 pos += 2;
754 if(getUChar(ptr,pos)!=3)
756 // user level security
758 else
760 // share level security
763 pos++;
764 sess->MaxMpx = getUShort(ptr, pos); //USHORT MaxMpxCount; Max pending outstanding requests
766 pos += 2;
767 sess->MaxVCS = getUShort(ptr, pos); //USHORT MaxNumberVcs; Max VCs between client and server
769 pos += 2;
770 serverMaxBuffer = getUInt(ptr, pos); //ULONG MaxBufferSize; Max transmit buffer size
772 if(serverMaxBuffer>SMB_MAX_TRANSMIT_SIZE)
773 sess->MaxBuffer = SMB_MAX_TRANSMIT_SIZE;
774 else
775 sess->MaxBuffer = serverMaxBuffer;
777 pos += 4;
778 pos += 4; //ULONG MaxRawSize; Maximum raw buffer size
779 sess->sKey = getUInt(ptr,pos); pos += 4;
780 pos += 4; //ULONG Capabilities; Server capabilities
781 pos += 4; //ULONG SystemTimeLow; System (UTC) time of the server (low).
782 pos += 4; //ULONG SystemTimeHigh; System (UTC) time of the server (high).
783 sess->timeOffset = getShort(ptr,pos) * 600000000LL; pos += 2; //SHORT ServerTimeZone; Time zone of server (minutes from UTC)
785 //UCHAR EncryptionKeyLength - 0 or 8
786 if(getUChar(ptr,pos)!=8)
788 if (getUChar(ptr,pos)!=0)
790 return SMB_BAD_KEYLEN;
792 else
794 // Challenge key not used
795 sess->challengeUsed = false;
798 else
800 sess->challengeUsed = true;
803 pos++;
804 bytecount = getUShort(ptr,pos);
806 if (sess->challengeUsed)
808 /*** Copy challenge key ***/
809 pos += 2;
810 memcpy(&sess->challenge,&ptr[pos],8);
813 /*** Primary domain ***/
814 pos += 8;
815 i = j = 0;
816 while(ptr[pos+j]!=0) {
817 sess->p_domain[i] = ptr[pos+j];
818 j += 2;
819 i++;
821 sess->p_domain[i] = '\0';
823 return SMB_SUCCESS;
826 return ret;
829 static s32 do_netconnect(SMBHANDLE *handle)
831 u32 nodelay;
832 s32 ret;
833 s32 sock;
834 u32 flags=0;
835 u64 t1,t2;
837 handle->sck_server = INVALID_SOCKET;
838 /*** Create the global net_socket ***/
839 sock = net_socket(AF_INET, SOCK_STREAM, IPPROTO_IP);
840 if(sock==INVALID_SOCKET) return -1;
842 /*** Switch off Nagle, ON TCP_NODELAY ***/
843 nodelay = 1;
844 ret = net_setsockopt(sock,IPPROTO_TCP,TCP_NODELAY,&nodelay,sizeof(nodelay));
846 //create non blocking socket
847 flags = net_fcntl(sock, F_GETFL, 0);
848 flags |= IOS_O_NONBLOCK;
849 ret=net_fcntl(sock, F_SETFL, flags);
851 t1=ticks_to_millisecs(gettime());
852 while(1)
854 ret = net_connect(sock,(struct sockaddr*)&handle->server_addr,sizeof(handle->server_addr));
855 if(ret==-127) break;
856 t2=ticks_to_millisecs(gettime());
857 usleep(1000);
858 if(t2-t1 > 3000) break; // 3 secs to try to connect to handle->server_addr
861 if(ret!=-127)
863 net_close(sock);
864 return -1;
867 handle->sck_server = sock;
868 return 0;
871 static s32 do_smbconnect(SMBHANDLE *handle)
873 s32 ret;
875 if(handle->sck_server == INVALID_SOCKET) return -1;
877 ret = SMB_NegotiateProtocol(smb_dialects,smb_dialectcnt,handle);
878 if(ret!=SMB_SUCCESS)
880 net_close(handle->sck_server);
881 handle->sck_server = INVALID_SOCKET;
882 return -1;
885 ret = SMB_SetupAndX(handle);
886 if(ret!=SMB_SUCCESS)
888 net_close(handle->sck_server);
889 handle->sck_server = INVALID_SOCKET;
890 return -1;
893 ret = SMB_TreeAndX(handle);
894 if(ret!=SMB_SUCCESS)
896 net_close(handle->sck_server);
897 handle->sck_server = INVALID_SOCKET;
898 return -1;
901 handle->conn_valid = TRUE;
902 return 0;
905 /****************************************************************************
906 * Create an NBT SESSION REQUEST message.
907 ****************************************************************************/
908 static int MakeSessReq(unsigned char *bufr, unsigned char *Called, unsigned char *Calling)
910 // Write the header.
911 bufr[0] = SESS_REQ;
912 bufr[1] = 0;
913 bufr[2] = 0;
914 bufr[3] = 68; // 2x34 bytes in length.
916 // Copy the Called and Calling names into the buffer.
917 (void) memcpy(&bufr[4], Called, 34);
918 (void) memcpy(&bufr[38], Calling, 34);
920 // Return the total message length.
921 return 72;
924 static unsigned char *L1_Encode(unsigned char *dst, const unsigned char *name,
925 const unsigned char pad, const unsigned char sfx)
927 int i = 0;
928 int j = 0;
929 int k = 0;
931 while (('\0' != name[i]) && (i < 15))
933 k = toupper(name[i++]);
934 dst[j++] = 'A' + ((k & 0xF0) >> 4);
935 dst[j++] = 'A' + (k & 0x0F);
938 i = 'A' + ((pad & 0xF0) >> 4);
939 k = 'A' + (pad & 0x0F);
940 while (j < 30)
942 dst[j++] = i;
943 dst[j++] = k;
946 dst[30] = 'A' + ((sfx & 0xF0) >> 4);
947 dst[31] = 'A' + (sfx & 0x0F);
948 dst[32] = '\0';
950 return (dst);
953 static int L2_Encode(unsigned char *dst, const unsigned char *name,
954 const unsigned char pad, const unsigned char sfx,
955 const unsigned char *scope)
957 int lenpos;
958 int i;
959 int j;
961 if (NULL == L1_Encode(&dst[1], name, pad, sfx))
962 return (-1);
964 dst[0] = 0x20;
965 lenpos = 33;
967 if ('\0' != *scope)
971 for (i = 0, j = (lenpos + 1); ('.' != scope[i]) && ('\0'
972 != scope[i]); i++, j++)
973 dst[j] = toupper(scope[i]);
975 dst[lenpos] = (unsigned char) i;
976 lenpos += i + 1;
977 scope += i;
978 } while ('.' == *(scope++));
980 dst[lenpos] = '\0';
983 return (lenpos + 1);
986 /****************************************************************************
987 * Send an NBT SESSION REQUEST over the TCP connection, then wait for a reply.
988 ****************************************************************************/
989 static s32 SMB_RequestNBTSession(SMBHANDLE *handle)
991 unsigned char Called[34];
992 unsigned char Calling[34];
993 unsigned char bufr[128];
994 int result;
996 if(handle->sck_server == INVALID_SOCKET) return -1;
998 L2_Encode(Called, (const unsigned char*) "*SMBSERVER", 0x20, 0x20,
999 (const unsigned char*) "");
1000 L2_Encode(Calling, (const unsigned char*) "SMBCLIENT", 0x20, 0x00,
1001 (const unsigned char*) "");
1003 // Create the NBT Session Request message.
1004 result = MakeSessReq(bufr, Called, Calling);
1006 //Send the NBT Session Request message.
1007 result = smb_send(handle->sck_server, bufr, result);
1008 if (result < 0)
1010 // Error sending Session Request message
1011 return -1;
1014 // Now wait for and handle the reply (2 seconds).
1015 result = smb_recv(handle->sck_server, bufr, 128);
1016 if (result <= 0)
1018 // Timeout waiting for NBT Session Response
1019 return -1;
1022 switch (*bufr)
1024 case SESS_POS_RESP:
1025 // Positive Session Response
1026 return 0;
1028 case SESS_NEG_RESP:
1029 // Negative Session Response
1030 return -1;
1032 case SESS_RETARGET:
1033 // Retarget Session Response
1034 return -1;
1036 default:
1037 // Unexpected Session Response
1038 return -1;
1042 /****************************************************************************
1043 * Primary setup, logon and connection all in one :)
1044 ****************************************************************************/
1045 s32 SMB_Connect(SMBCONN *smbhndl, const char *user, const char *password, const char *share, const char *server)
1047 s32 ret = 0;
1048 SMBHANDLE *handle;
1049 struct hostent *hp;
1050 struct in_addr val;
1052 *smbhndl = SMB_HANDLE_NULL;
1054 if(!user || !password || !share || !server ||
1055 strlen(user) > 20 || strlen(password) > 14 ||
1056 strlen(share) > 80 || strlen(server) > 80)
1058 return SMB_BAD_LOGINDATA;
1061 if(smb_inited==FALSE)
1063 u32 level;
1064 _CPU_ISR_Disable(level);
1065 __smb_init();
1066 _CPU_ISR_Restore(level);
1069 handle = __smb_allocate_handle();
1070 if(!handle) return SMB_ERROR;
1072 handle->user = strdup(user);
1073 handle->pwd = strdup(password);
1074 handle->server_name = strdup(server);
1075 handle->share_name = strdup(share);
1076 handle->server_addr.sin_family = AF_INET;
1077 handle->server_addr.sin_port = htons(445);
1079 if(strlen(server) < 16 && inet_aton(server, &val))
1081 handle->server_addr.sin_addr.s_addr = val.s_addr;
1083 else // might be a hostname
1085 hp = net_gethostbyname(server);
1086 if (!hp || !(hp->h_addrtype == PF_INET))
1087 ret = SMB_BAD_LOGINDATA;
1088 else
1089 memcpy((char *)&handle->server_addr.sin_addr.s_addr, hp->h_addr_list[0], hp->h_length);
1092 *smbhndl =(SMBCONN)(LWP_OBJMASKTYPE(SMB_OBJTYPE_HANDLE)|LWP_OBJMASKID(handle->object.id));
1094 if(ret==0)
1096 ret = do_netconnect(handle);
1097 if(ret==0) ret = do_smbconnect(handle);
1099 if(ret!=0)
1101 // try port 139
1102 handle->server_addr.sin_port = htons(139);
1103 ret = do_netconnect(handle);
1104 if(ret==0) ret = SMB_RequestNBTSession(handle);
1105 if(ret==0) ret = do_smbconnect(handle);
1108 if(ret!=0)
1110 handle->server_addr.sin_port = 0;
1111 return SMB_ERROR;
1114 return SMB_SUCCESS;
1117 /****************************************************************************
1118 * SMB_Destroy
1120 * Probably NEVER called on GameCube, but here for completeness
1121 ****************************************************************************/
1122 void SMB_Close(SMBCONN smbhndl)
1124 SMBHANDLE *handle;
1126 handle = __smb_handle_open(smbhndl);
1127 if(!handle) return;
1129 if(handle->sck_server!=INVALID_SOCKET) net_close(handle->sck_server);
1130 __smb_free_handle(handle);
1133 s32 SMB_Reconnect(SMBCONN *_smbhndl, BOOL test_conn)
1135 s32 ret = SMB_SUCCESS;
1136 SMBCONN smbhndl = *_smbhndl;
1137 SMBHANDLE *handle = __smb_handle_open(smbhndl);
1138 if(!handle)
1139 return SMB_ERROR; // we have no handle, so we can't reconnect
1141 if(handle->conn_valid && test_conn)
1143 SMBDIRENTRY dentry;
1144 if(SMB_PathInfo("\\", &dentry, smbhndl)==SMB_SUCCESS) return SMB_SUCCESS; // no need to reconnect
1145 handle->conn_valid = FALSE; // else connection is invalid
1147 if(!handle->conn_valid)
1149 // shut down connection
1150 if(handle->sck_server!=INVALID_SOCKET)
1152 net_close(handle->sck_server);
1153 handle->sck_server = INVALID_SOCKET;
1156 // reconnect
1157 if(handle->server_addr.sin_port > 0)
1159 ret = do_netconnect(handle);
1160 if(ret==0 && handle->server_addr.sin_port == htons(139))
1161 ret = SMB_RequestNBTSession(handle);
1162 if(ret==0)
1163 ret = do_smbconnect(handle);
1165 else // initial connection
1167 handle->server_addr.sin_port = htons(445);
1168 ret = do_netconnect(handle);
1169 if(ret==0) ret = do_smbconnect(handle);
1171 if(ret != 0)
1173 // try port 139
1174 handle->server_addr.sin_port = htons(139);
1175 ret = do_netconnect(handle);
1176 if(ret==0) ret = SMB_RequestNBTSession(handle);
1177 if(ret==0) ret = do_smbconnect(handle);
1180 if(ret != 0)
1181 handle->server_addr.sin_port = 0;
1184 return ret;
1187 SMBFILE SMB_OpenFile(const char *filename, u16 access, u16 creation,SMBCONN smbhndl)
1189 s32 pos;
1190 s32 bpos,ret;
1191 u8 *ptr;
1192 struct _smbfile *fid = NULL;
1193 SMBHANDLE *handle;
1194 char realfile[256];
1196 if(filename == NULL)
1197 return NULL;
1199 if(SMB_Reconnect(&smbhndl,TRUE)!=SMB_SUCCESS) return NULL;
1201 handle = __smb_handle_open(smbhndl);
1202 if(!handle) return NULL;
1204 MakeSMBHeader(SMB_OPEN_ANDX,0x08,0x01,handle);
1206 pos = SMB_HEADER_SIZE;
1207 ptr = handle->message.smb;
1208 setUChar(ptr, pos, 15);
1209 pos++; /*** Word Count ***/
1210 setUChar(ptr, pos, 0xff);
1211 pos++; /*** Next AndX ***/
1212 pos += 3; /*** Next AndX Offset ***/
1214 pos += 2; /*** Flags ***/
1215 setUShort(ptr, pos, access);
1216 pos += 2; /*** Access mode ***/
1217 setUShort(ptr, pos, 0x6);
1218 pos += 2; /*** Type of file ***/
1219 pos += 2; /*** Attributes ***/
1220 pos += 4; /*** File time - don't care - let server decide ***/
1221 setUShort(ptr, pos, creation);
1222 pos += 2; /*** Creation flags ***/
1223 pos += 4; /*** Allocation size ***/
1224 pos += 8; /*** Reserved ***/
1225 pos += 2; /*** Byte Count ***/
1226 bpos = pos;
1228 if (filename[0] != '\\') {
1229 strcpy(realfile, "\\");
1230 strcat(realfile,filename);
1231 } else
1232 strcpy(realfile,filename);
1234 memcpy(&ptr[pos],realfile,strlen(realfile));
1235 pos += strlen(realfile)+1;
1237 setUShort(ptr,(bpos-2),(pos-bpos));
1239 handle->message.msg = NBT_SESSISON_MSG;
1240 handle->message.length = htons(pos);
1242 pos += 4;
1243 ret = smb_send(handle->sck_server,(char*)&handle->message,pos);
1244 if(ret<0) goto failed;
1246 if(SMBCheck(SMB_OPEN_ANDX,0,handle)==SMB_SUCCESS) {
1247 /*** Check file handle ***/
1248 fid = (struct _smbfile*)__lwp_queue_get(&smb_filehandle_queue);
1249 if(fid) {
1250 fid->conn = smbhndl;
1251 fid->sfid = getUShort(handle->message.smb,(SMB_HEADER_SIZE+5));
1254 return (SMBFILE)fid;
1256 failed:
1257 handle->conn_valid = FALSE;
1258 return NULL;
1262 * SMB_Close
1264 void SMB_CloseFile(SMBFILE sfid)
1266 u8 *ptr;
1267 s32 pos, ret;
1268 SMBHANDLE *handle;
1269 struct _smbfile *fid = (struct _smbfile*)sfid;
1271 if(!fid) return;
1273 handle = __smb_handle_open(fid->conn);
1274 if(!handle) return;
1276 MakeSMBHeader(SMB_CLOSE,0x08,0x01,handle);
1278 pos = SMB_HEADER_SIZE;
1279 ptr = handle->message.smb;
1280 setUChar(ptr, pos, 3);
1281 pos++; /** Word Count **/
1282 setUShort(ptr, pos, fid->sfid);
1283 pos += 2;
1284 setUInt(ptr, pos, 0xffffffff);
1285 pos += 4; /*** Last Write ***/
1286 pos += 2; /*** Byte Count ***/
1288 handle->message.msg = NBT_SESSISON_MSG;
1289 handle->message.length = htons(pos);
1291 pos += 4;
1292 ret = smb_send(handle->sck_server,(char*)&handle->message,pos);
1293 if(ret<0) handle->conn_valid = FALSE;
1294 else SMBCheck(SMB_CLOSE,0,handle);
1295 __lwp_queue_append(&smb_filehandle_queue,&fid->node);
1299 * SMB_Read
1301 s32 SMB_ReadFile(char *buffer, size_t size, off_t offset, SMBFILE sfid)
1303 u8 *ptr,*dest;
1304 s32 pos, ret, ofs;
1305 u16 length = 0;
1306 SMBHANDLE *handle;
1307 struct _smbfile *fid = (struct _smbfile*)sfid;
1309 if(!fid) return -1;
1311 // Check for invalid size
1312 if(size == 0 || size > SMB_MAX_BUFFERSIZE) return -1;
1314 handle = __smb_handle_open(fid->conn);
1315 if(!handle) return -1;
1317 MakeSMBHeader(SMB_READ_ANDX,0x08,0x01,handle);
1319 pos = SMB_HEADER_SIZE;
1320 ptr = handle->message.smb;
1321 setUChar(ptr, pos, 12);
1322 pos++; /*** Word count ***/
1323 setUChar(ptr, pos, 0xff);
1324 pos++;
1325 pos += 3; /*** Reserved, Next AndX Offset ***/
1326 setUShort(ptr, pos, fid->sfid);
1327 pos += 2; /*** FID ***/
1328 setUInt(ptr, pos, offset & 0xffffffff);
1329 pos += 4; /*** Offset ***/
1331 setUShort(ptr, pos, size & 0xffff);
1332 pos += 2;
1333 setUShort(ptr, pos, size & 0xffff);
1334 pos += 2;
1335 setUInt(ptr, pos, 0);
1336 pos += 4;
1337 pos += 2; /*** Remaining ***/
1338 setUInt(ptr, pos, offset >> 32); // offset high
1339 pos += 4;
1340 pos += 2; /*** Byte count ***/
1342 handle->message.msg = NBT_SESSISON_MSG;
1343 handle->message.length = htons(pos);
1345 pos += 4;
1347 ret = smb_send(handle->sck_server,(char*)&handle->message, pos);
1348 if(ret<0) goto failed;
1350 /*** SMBCheck should now only read up to the end of a standard header ***/
1351 if((ret=SMBCheck(SMB_READ_ANDX,(SMB_HEADER_SIZE+27+4),handle))==SMB_SUCCESS) {
1352 ptr = handle->message.smb;
1353 /*** Retrieve data length for this packet ***/
1354 length = getUShort(ptr,(SMB_HEADER_SIZE+11));
1355 /*** Retrieve offset to data ***/
1356 ofs = getUShort(ptr,(SMB_HEADER_SIZE+13));
1358 /*** Default offset, with no padding is 63, so grab any outstanding padding ***/
1359 while(ofs>SMB_DEF_READOFFSET) {
1360 char pad[1024];
1361 ret = smb_recv(handle->sck_server,pad,(ofs-SMB_DEF_READOFFSET));
1362 if(ret<0) goto failed;
1363 ofs-=ret;
1366 /*** Finally, go grab the data ***/
1367 ofs = 0;
1368 dest = (u8*)buffer;
1369 if(length>0) {
1370 while (1)
1372 ret=smb_recv(handle->sck_server,&dest[ofs],length-ofs);
1373 if(ret<0) goto failed;
1374 ofs += ret;
1375 if (ofs>=length) break;
1378 return ofs;
1380 return -1;
1382 failed:
1383 handle->conn_valid = FALSE;
1384 return ret;
1388 * SMB_Write
1390 s32 SMB_WriteFile(const char *buffer, size_t size, off_t offset, SMBFILE sfid)
1392 u8 *ptr,*src;
1393 s32 pos, ret;
1394 s32 blocks64;
1395 u32 copy_len;
1396 SMBHANDLE *handle;
1397 struct _smbfile *fid = (struct _smbfile*)sfid;
1399 if(!fid) return -1;
1401 handle = __smb_handle_open(fid->conn);
1402 if(!handle) return -1;
1404 MakeSMBHeader(SMB_WRITE_ANDX,0x08,0x01,handle);
1406 pos = SMB_HEADER_SIZE;
1407 ptr = handle->message.smb;
1408 setUChar(ptr, pos, 14);
1409 pos++; /*** Word Count ***/
1410 setUChar(ptr, pos, 0xff);
1411 pos += 2; /*** Next AndX ***/
1412 pos += 2; /*** Next AndX Offset ***/
1414 setUShort(ptr, pos, fid->sfid);
1415 pos += 2;
1416 setUInt(ptr, pos, offset & 0xffffffff);
1417 pos += 4;
1418 setUInt(ptr, pos, 0); /*** Reserved, must be 0 ***/
1419 pos += 4;
1420 setUShort(ptr, pos, 0); /*** Write Mode ***/
1421 pos += 2;
1422 pos += 2; /*** Remaining ***/
1424 blocks64 = size >> 16;
1426 setUShort(ptr, pos, blocks64);
1427 pos += 2; /*** Length High ***/
1428 setUShort(ptr, pos, size & 0xffff);
1429 pos += 2; /*** Length Low ***/
1430 setUShort(ptr, pos, 63);
1431 pos += 2; /*** Data Offset ***/
1432 setUInt(ptr, pos, offset >> 32); /*** OffsetHigh ***/
1433 pos += 4;
1434 setUShort(ptr, pos, size & 0xffff);
1435 pos += 2; /*** Data Byte Count ***/
1437 handle->message.msg = NBT_SESSISON_MSG;
1438 handle->message.length = htons(pos+size);
1440 src = (u8*)buffer;
1441 copy_len = size;
1442 if((copy_len+pos)>SMB_MAX_TRANSMIT_SIZE) copy_len = (SMB_MAX_TRANSMIT_SIZE-pos);
1444 memcpy(&ptr[pos],src,copy_len);
1445 size -= copy_len;
1446 src += copy_len;
1447 pos += copy_len;
1449 pos += 4;
1451 /*** Send Header Information ***/
1452 ret = smb_send(handle->sck_server,(char*)&handle->message,pos);
1453 if(ret<0) goto failed;
1455 if(size>0) {
1456 /*** Send the data ***/
1457 ret = smb_send(handle->sck_server,src,size);
1458 if(ret<0) goto failed;
1461 ret = 0;
1462 if(SMBCheck(SMB_WRITE_ANDX,0,handle)==SMB_SUCCESS) {
1463 ptr = handle->message.smb;
1464 ret = getUShort(ptr,(SMB_HEADER_SIZE+5));
1467 return ret;
1469 failed:
1470 handle->conn_valid = FALSE;
1471 return ret;
1475 * SMB_PathInfo
1477 s32 SMB_PathInfo(const char *filename, SMBDIRENTRY *sdir, SMBCONN smbhndl)
1479 u8 *ptr;
1480 s32 pos;
1481 s32 ret;
1482 s32 bpos;
1483 SMBHANDLE *handle;
1484 char realfile[256];
1486 if(filename == NULL)
1487 return SMB_ERROR;
1489 handle = __smb_handle_open(smbhndl);
1490 if (!handle) return SMB_ERROR;
1492 MakeSMBHeader(SMB_TRANS2, 0x08, 0x01, handle);
1493 MakeTRANS2Header(SMB_QUERY_PATH_INFO, handle);
1495 bpos = pos = (T2_BYTE_CNT + 2);
1496 pos += 3; /*** Padding ***/
1497 ptr = handle->message.smb;
1498 setUShort(ptr, pos, SMB_QUERY_FILE_ALL_INFO);
1500 pos += 2;
1501 setUInt(ptr, pos, 0);
1502 pos += 4; /*** reserved ***/
1504 if (filename[0] != '\\') {
1505 strcpy(realfile, "\\");
1506 strcat(realfile, filename);
1507 } else
1508 strcpy(realfile, filename);
1510 memcpy(&ptr[pos], realfile, strlen(realfile));
1511 pos += strlen(realfile) + 1; /*** Include padding ***/
1513 /*** Update counts ***/
1514 setUShort(ptr, T2_PRM_CNT, (7 + strlen(realfile)));
1515 setUShort(ptr, T2_SPRM_CNT, (7 + strlen(realfile)));
1516 setUShort(ptr, T2_SPRM_OFS, 68);
1517 setUShort(ptr, T2_SDATA_OFS, (75 + strlen(realfile)));
1518 setUShort(ptr, T2_BYTE_CNT, (pos - bpos));
1520 handle->message.msg = NBT_SESSISON_MSG;
1521 handle->message.length = htons(pos);
1523 pos += 4;
1524 ret = smb_send(handle->sck_server, (char*) &handle->message, pos);
1525 if(ret<0) goto failed;
1527 ret = SMB_ERROR;
1528 if (SMBCheck(SMB_TRANS2, 0, handle) == SMB_SUCCESS) {
1530 ptr = handle->message.smb;
1531 /*** Get parameter offset ***/
1532 pos = getUShort(ptr, (SMB_HEADER_SIZE + 9));
1533 pos += 4; // padding
1534 sdir->ctime = getULongLong(ptr, pos) - handle->session.timeOffset; pos += 8; // ULONGLONG - creation time
1535 sdir->atime = getULongLong(ptr, pos) - handle->session.timeOffset; pos += 8; // ULONGLONG - access time
1536 sdir->mtime = getULongLong(ptr, pos) - handle->session.timeOffset; pos += 8; // ULONGLONG - write time
1537 pos += 8; // ULONGLONG - change time
1538 sdir->attributes = getUInt(ptr,pos); pos += 4; // ULONG - file attributes
1539 pos += 4; // padding
1540 pos += 8; // ULONGLONG - allocated file size
1541 sdir->size = getULongLong(ptr, pos); pos += 8; // ULONGLONG - file size
1542 pos += 4; // ULONG NumberOfLinks;
1543 pos += 2; // UCHAR DeletePending;
1544 pos += 2; // UCHAR Directory;
1545 pos += 2; // USHORT Pad2; // for alignment only
1546 pos += 4; // ULONG EaSize;
1547 pos += 4; // ULONG FileNameLength;
1549 strcpy(sdir->name,realfile);
1551 ret = SMB_SUCCESS;
1553 return ret;
1555 failed:
1556 handle->conn_valid = FALSE;
1557 return ret;
1561 * SMB_FindFirst
1563 * Uses TRANS2 to support long filenames
1565 s32 SMB_FindFirst(const char *filename, unsigned short flags, SMBDIRENTRY *sdir, SMBCONN smbhndl)
1567 u8 *ptr;
1568 s32 pos;
1569 s32 ret;
1570 s32 bpos;
1571 SMBHANDLE *handle;
1572 SMBSESSION *sess;
1574 if(filename == NULL)
1575 return SMB_ERROR;
1577 if(SMB_Reconnect(&smbhndl,TRUE)!=SMB_SUCCESS) return SMB_ERROR;
1579 handle = __smb_handle_open(smbhndl);
1580 if(!handle) return SMB_ERROR;
1582 sess = &handle->session;
1583 MakeSMBHeader(SMB_TRANS2,0x08,0x01,handle);
1584 MakeTRANS2Header(SMB_FIND_FIRST2,handle);
1586 bpos = pos = (T2_BYTE_CNT+2);
1587 pos += 3; /*** Padding ***/
1588 ptr = handle->message.smb;
1589 setUShort(ptr, pos, flags);
1590 pos += 2; /*** Flags ***/
1591 setUShort(ptr, pos, 1);
1592 pos += 2; /*** Count ***/
1593 setUShort(ptr, pos, 6);
1594 pos += 2; /*** Internal Flags ***/
1595 setUShort(ptr, pos, 260); // SMB_FIND_FILE_BOTH_DIRECTORY_INFO
1596 pos += 2; /*** Level of Interest ***/
1597 pos += 4; /*** Storage Type == 0 ***/
1598 memcpy(&ptr[pos], filename, strlen(filename));
1599 pos += strlen(filename)+1; /*** Include padding ***/
1601 /*** Update counts ***/
1602 setUShort(ptr, T2_PRM_CNT, (13+strlen(filename)));
1603 setUShort(ptr, T2_SPRM_CNT, (13+strlen(filename)));
1604 setUShort(ptr, T2_SPRM_OFS, 68);
1605 setUShort(ptr, T2_SDATA_OFS,(81+strlen(filename)));
1606 setUShort(ptr, T2_BYTE_CNT,(pos-bpos));
1608 handle->message.msg = NBT_SESSISON_MSG;
1609 handle->message.length = htons(pos);
1611 pos += 4;
1612 ret = smb_send(handle->sck_server,(char*)&handle->message,pos);
1613 if(ret<0) goto failed;
1615 sess->eos = 1;
1616 sess->sid = 0;
1617 sess->count = 0;
1618 ret = SMB_ERROR;
1619 if(SMBCheck(SMB_TRANS2,0,handle)==SMB_SUCCESS) {
1620 ptr = handle->message.smb;
1621 /*** Get parameter offset ***/
1622 pos = getUShort(ptr,(SMB_HEADER_SIZE+9));
1623 sess->sid = getUShort(ptr, pos); pos += 2;
1624 sess->count = getUShort(ptr, pos); pos += 2;
1625 sess->eos = getUShort(ptr, pos); pos += 2;
1626 pos += 2; // USHORT EaErrorOffset;
1627 pos += 2; // USHORT LastNameOffset;
1628 pos += 2; // padding?
1630 if (sess->count)
1632 pos += 4; // ULONG NextEntryOffset;
1633 pos += 4; // ULONG FileIndex;
1634 sdir->ctime = getULongLong(ptr, pos) - handle->session.timeOffset; pos += 8; // ULONGLONG - creation time
1635 sdir->atime = getULongLong(ptr, pos) - handle->session.timeOffset; pos += 8; // ULONGLONG - access time
1636 sdir->mtime = getULongLong(ptr, pos) - handle->session.timeOffset; pos += 8; // ULONGLONG - write time
1637 pos += 8; // ULONGLONG - change time low
1638 sdir->size = getULongLong(ptr, pos); pos += 8;
1639 pos += 8;
1640 sdir->attributes = getUInt(ptr, pos); pos += 4;
1641 pos += 34;
1642 strcpy(sdir->name, (const char*)&ptr[pos]);
1644 ret = SMB_SUCCESS;
1647 return ret;
1649 failed:
1650 handle->conn_valid = FALSE;
1651 return ret;
1655 * SMB_FindNext
1657 s32 SMB_FindNext(SMBDIRENTRY *sdir,SMBCONN smbhndl)
1659 u8 *ptr;
1660 s32 pos;
1661 s32 ret;
1662 s32 bpos;
1663 SMBHANDLE *handle;
1664 SMBSESSION *sess;
1666 handle = __smb_handle_open(smbhndl);
1667 if(!handle) return SMB_ERROR;
1669 sess = &handle->session;
1670 if(sess->eos || sess->sid==0) return SMB_ERROR;
1672 MakeSMBHeader(SMB_TRANS2,0x08,0x01,handle);
1673 MakeTRANS2Header(SMB_FIND_NEXT2,handle);
1675 bpos = pos = (T2_BYTE_CNT+2);
1676 pos += 3; /*** Padding ***/
1677 ptr = handle->message.smb;
1678 setUShort(ptr, pos, sess->sid);
1679 pos += 2; /*** Search ID ***/
1680 setUShort(ptr, pos, 1);
1681 pos += 2; /*** Count ***/
1682 setUShort(ptr, pos, 260); // SMB_FIND_FILE_BOTH_DIRECTORY_INFO
1683 pos += 2; /*** Level of Interest ***/
1684 pos += 4; /*** Storage Type == 0 ***/
1685 setUShort(ptr, pos, 12);
1686 pos+=2; /*** Search flags ***/
1687 pos++;
1689 /*** Update counts ***/
1690 setUShort(ptr, T2_PRM_CNT, 13);
1691 setUShort(ptr, T2_SPRM_CNT, 13);
1692 setUShort(ptr, T2_SPRM_OFS, 68);
1693 setUShort(ptr, T2_SDATA_OFS, 81);
1694 setUShort(ptr, T2_BYTE_CNT, (pos-bpos));
1696 handle->message.msg = NBT_SESSISON_MSG;
1697 handle->message.length = htons(pos);
1699 pos += 4;
1700 ret = smb_send(handle->sck_server,(char*)&handle->message,pos);
1701 if(ret<0) goto failed;
1703 ret = SMB_ERROR;
1704 if (SMBCheck(SMB_TRANS2,0,handle)==SMB_SUCCESS) {
1705 ptr = handle->message.smb;
1706 /*** Get parameter offset ***/
1707 pos = getUShort(ptr,(SMB_HEADER_SIZE+9));
1708 sess->count = getUShort(ptr, pos); pos += 2;
1709 sess->eos = getUShort(ptr, pos); pos += 2;
1710 pos += 2; // USHORT EaErrorOffset;
1711 pos += 2; // USHORT LastNameOffset;
1713 if (sess->count)
1715 pos += 4; // ULONG NextEntryOffset;
1716 pos += 4; // ULONG FileIndex;
1717 sdir->ctime = getULongLong(ptr, pos) - handle->session.timeOffset; pos += 8; // ULONGLONG - creation time
1718 sdir->atime = getULongLong(ptr, pos) - handle->session.timeOffset; pos += 8; // ULONGLONG - access time
1719 sdir->mtime = getULongLong(ptr, pos) - handle->session.timeOffset; pos += 8; // ULONGLONG - write time
1720 pos += 8; // ULONGLONG - change time low
1721 sdir->size = getULongLong(ptr, pos); pos += 8;
1722 pos += 8;
1723 sdir->attributes = getUInt(ptr, pos); pos += 4;
1724 pos += 34;
1725 strcpy (sdir->name, (const char*)&ptr[pos]);
1727 ret = SMB_SUCCESS;
1730 return ret;
1732 failed:
1733 handle->conn_valid = FALSE;
1734 return ret;
1738 * SMB_FindClose
1740 s32 SMB_FindClose(SMBCONN smbhndl)
1742 u8 *ptr;
1743 s32 pos;
1744 s32 ret;
1745 SMBHANDLE *handle;
1746 SMBSESSION *sess;
1748 handle = __smb_handle_open(smbhndl);
1749 if(!handle) return SMB_ERROR;
1751 sess = &handle->session;
1752 if(sess->sid==0) return SMB_ERROR;
1754 MakeSMBHeader(SMB_FIND_CLOSE2,0x08,0x01,handle);
1756 pos = SMB_HEADER_SIZE;
1757 ptr = handle->message.smb;
1758 setUChar(ptr, pos, 1);
1759 pos++; /*** Word Count ***/
1760 setUShort(ptr, pos, sess->sid);
1761 pos += 2;
1762 pos += 2; /*** Byte Count ***/
1764 handle->message.msg = NBT_SESSISON_MSG;
1765 handle->message.length = htons(pos);
1767 pos += 4;
1768 ret = smb_send(handle->sck_server,(char*)&handle->message,pos);
1769 if(ret<0) goto failed;
1771 ret = SMBCheck(SMB_FIND_CLOSE2,0,handle);
1772 return ret;
1774 failed:
1775 handle->conn_valid = FALSE;
1776 return ret;