/**************************************************************************** * TinySMB * Nintendo Wii/GameCube SMB implementation * * Copyright softdev * Modified by Tantric to utilize NTLM authentication * PathInfo added by rodries * SMB devoptab by scip, rodries * * You will find WireShark (http://www.wireshark.org/) * invaluable for debugging SAMBA implementations. * * Recommended Reading * Implementing CIFS - Christopher R Hertel * http://www.ubiqx.org/cifs/SMB.html * * License: * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ****************************************************************************/ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define IOS_O_NONBLOCK 0x04 #define RECV_TIMEOUT 3000 // in ms #define CONN_TIMEOUT 6000 /** * Field offsets. */ #define SMB_OFFSET_PROTO 0 #define SMB_OFFSET_CMD 4 #define SMB_OFFSET_NTSTATUS 5 #define SMB_OFFSET_ECLASS 5 #define SMB_OFFSET_ECODE 7 #define SMB_OFFSET_FLAGS 9 #define SMB_OFFSET_FLAGS2 10 #define SMB_OFFSET_EXTRA 12 #define SMB_OFFSET_TID 24 #define SMB_OFFSET_PID 26 #define SMB_OFFSET_UID 28 #define SMB_OFFSET_MID 30 #define SMB_HEADER_SIZE 32 /*** SMB Headers are always 32 bytes long ***/ /** * Message / Commands */ #define NBT_SESSISON_MSG 0x00 #define SMB_NEG_PROTOCOL 0x72 #define SMB_SETUP_ANDX 0x73 #define SMB_TREEC_ANDX 0x75 #define NBT_KEEPALIVE_MSG 0x85 #define KEEPALIVE_SIZE 4 /** * SMBTrans2 */ #define SMB_TRANS2 0x32 #define SMB_OPEN2 0 #define SMB_FIND_FIRST2 1 #define SMB_FIND_NEXT2 2 #define SMB_QUERY_FS_INFO 3 #define SMB_QUERY_PATH_INFO 5 #define SMB_SET_PATH_INFO 6 #define SMB_QUERY_FILE_INFO 7 #define SMB_SET_FILE_INFO 8 #define SMB_CREATE_DIR 13 #define SMB_FIND_CLOSE2 0x34 #define SMB_QUERY_FILE_ALL_INFO 0x107 /** * File I/O */ #define SMB_OPEN_ANDX 0x2d #define SMB_WRITE_ANDX 0x2f #define SMB_READ_ANDX 0x2e #define SMB_CLOSE 0x04 /** * SMB_COM */ #define SMB_COM_CREATE_DIRECTORY 0x00 #define SMB_COM_DELETE_DIRECTORY 0x01 #define SMB_COM_DELETE 0x06 #define SMB_COM_RENAME 0x07 #define SMB_COM_QUERY_INFORMATION_DISK 0x80 /** * TRANS2 Offsets */ #define T2_WORD_CNT (SMB_HEADER_SIZE) #define T2_PRM_CNT (T2_WORD_CNT+1) #define T2_DATA_CNT (T2_PRM_CNT+2) #define T2_MAXPRM_CNT (T2_DATA_CNT+2) #define T2_MAXBUFFER (T2_MAXPRM_CNT+2) #define T2_SETUP_CNT (T2_MAXBUFFER+2) #define T2_SPRM_CNT (T2_SETUP_CNT+10) #define T2_SPRM_OFS (T2_SPRM_CNT+2) #define T2_SDATA_CNT (T2_SPRM_OFS+2) #define T2_SDATA_OFS (T2_SDATA_CNT+2) #define T2_SSETUP_CNT (T2_SDATA_OFS+2) #define T2_SUB_CMD (T2_SSETUP_CNT+2) #define T2_BYTE_CNT (T2_SUB_CMD+2) #define SMB_PROTO 0x424d53ff #define SMB_HANDLE_NULL 0xffffffff #define SMB_MAX_NET_READ_SIZE (16*1024) // see smb_recv #define SMB_MAX_NET_WRITE_SIZE 4096 // see smb_sendv #define SMB_MAX_TRANSMIT_SIZE 65472 #define CAP_LARGE_FILES 0x00000008 // 64-bit file sizes and offsets supported #define CAP_UNICODE 0x00000004 // Unicode supported #define CIFS_FLAGS1 0x08 // Paths are caseless #define CIFS_FLAGS2_UNICODE 0x8001 // Server may return long components in paths in the response - use 0x8001 for Unicode support #define CIFS_FLAGS2 0x0001 // Server may return long components in paths in the response - use 0x0001 for ASCII support #define SMB_CONNHANDLES_MAX 8 #define SMB_FILEHANDLES_MAX (32*SMB_CONNHANDLES_MAX) #define SMB_OBJTYPE_HANDLE 7 #define SMB_CHECK_HANDLE(hndl) \ { \ if(((hndl)==SMB_HANDLE_NULL) || (LWP_OBJTYPE(hndl)!=SMB_OBJTYPE_HANDLE)) \ return NULL; \ } /* NBT Session Service Packet Type Codes */ #define SESS_MSG 0x00 #define SESS_REQ 0x81 #define SESS_POS_RESP 0x82 #define SESS_NEG_RESP 0x83 #define SESS_RETARGET 0x84 #define SESS_KEEPALIVE 0x85 struct _smbfile { lwp_node node; u16 sfid; SMBCONN conn; }; /** * NBT/SMB Wrapper */ typedef struct _nbtsmb { u8 msg; /*** NBT Message ***/ u8 length_high; u16 length; /*** Length, excluding NBT ***/ u8 smb[SMB_MAX_TRANSMIT_SIZE+128]; } NBTSMB; /** * Session Information */ typedef struct _smbsession { u16 TID; u16 PID; u16 UID; u16 MID; u32 sKey; u32 capabilities; u32 MaxBuffer; u16 MaxMpx; u16 MaxVCS; u8 challenge[10]; u8 p_domain[64]; s64 timeOffset; u16 count; u16 eos; bool challengeUsed; u8 securityLevel; } SMBSESSION; typedef struct _smbhandle { lwp_obj object; char *user; char *pwd; char *share_name; char *server_name; s32 sck_server; struct sockaddr_in server_addr; bool conn_valid; SMBSESSION session; NBTSMB message; bool unicode; } SMBHANDLE; static u32 smb_dialectcnt = 1; static bool smb_inited = false; static lwp_objinfo smb_handle_objects; static lwp_queue smb_filehandle_queue; static struct _smbfile smb_filehandles[SMB_FILEHANDLES_MAX]; static const char *smb_dialects[] = {"NT LM 0.12",NULL}; extern void ntlm_smb_nt_encrypt(const char *passwd, const u8 * challenge, u8 * answer); // UTF conversion functions size_t utf16_to_utf8(char* dst, char* src, size_t len) { mbstate_t ps; size_t count = 0; int bytes; char buff[MB_CUR_MAX]; int i; unsigned short c; memset(&ps, 0, sizeof(mbstate_t)); while (count < len && *src != '\0') { c = *(src + 1) << 8 | *src; // little endian if (c == 0) break; bytes = wcrtomb(buff, c, &ps); if (bytes < 0) { *dst = '\0'; return -1; } if (bytes > 0) { for (i = 0; i < bytes; i++) { *dst++ = buff[i]; } src += 2; count += 2; } else { break; } } *dst = '\0'; return count; } size_t utf8_to_utf16(char* dst, char* src, size_t len) { mbstate_t ps; wchar_t tempWChar; char *tempChar; int bytes; size_t count = 0; tempChar = (char*) &tempWChar; memset(&ps, 0, sizeof(mbstate_t)); while (count < len - 1 && src != '\0') { bytes = mbrtowc(&tempWChar, src, MB_CUR_MAX, &ps); if (bytes > 0) { *dst = tempChar[3]; dst++; *dst = tempChar[2]; dst++; src += bytes; count += 2; } else if (bytes == 0) { break; } else { *dst = '\0'; dst++; *dst = '\0'; return -1; } } *dst = '\0'; dst++; *dst = '\0'; return count; } /** * SMB Endian aware supporting functions * * SMB always uses Intel Little-Endian values, so htons etc are * of little or no use :) ... Thanks M$ */ /*** get unsigned char ***/ static __inline__ u8 getUChar(u8 *buffer,u32 offset) { return (u8)buffer[offset]; } /*** set unsigned char ***/ static __inline__ void setUChar(u8 *buffer,u32 offset,u8 value) { buffer[offset] = value; } /*** get signed short ***/ static __inline__ s16 getShort(u8 *buffer,u32 offset) { return (s16)((buffer[offset+1]<<8)|(buffer[offset])); } /*** get unsigned short ***/ static __inline__ u16 getUShort(u8 *buffer,u32 offset) { return (u16)((buffer[offset+1]<<8)|(buffer[offset])); } /*** set unsigned short ***/ static __inline__ void setUShort(u8 *buffer,u32 offset,u16 value) { buffer[offset] = (value&0xff); buffer[offset+1] = ((value&0xff00)>>8); } /*** get unsigned int ***/ static __inline__ u32 getUInt(u8 *buffer,u32 offset) { return (u32)((buffer[offset+3]<<24)|(buffer[offset+2]<<16)|(buffer[offset+1]<<8)|buffer[offset]); } /*** set unsigned int ***/ static __inline__ void setUInt(u8 *buffer,u32 offset,u32 value) { buffer[offset] = (value&0xff); buffer[offset+1] = ((value&0xff00)>>8); buffer[offset+2] = ((value&0xff0000)>>16); buffer[offset+3] = ((value&0xff000000)>>24); } /*** get unsigned long long ***/ static __inline__ u64 getULongLong(u8 *buffer,u32 offset) { return (u64)(getUInt(buffer, offset) | (u64)getUInt(buffer, offset+4) << 32); } static __inline__ SMBHANDLE* __smb_handle_open(SMBCONN smbhndl) { u32 level; SMBHANDLE *handle; SMB_CHECK_HANDLE(smbhndl); _CPU_ISR_Disable(level); handle = (SMBHANDLE*)__lwp_objmgr_getnoprotection(&smb_handle_objects,LWP_OBJMASKID(smbhndl)); _CPU_ISR_Restore(level); return handle; } static __inline__ void __smb_handle_free(SMBHANDLE *handle) { u32 level; _CPU_ISR_Disable(level); __lwp_objmgr_close(&smb_handle_objects,&handle->object); __lwp_objmgr_free(&smb_handle_objects,&handle->object); _CPU_ISR_Restore(level); } static void __smb_init() { smb_inited = true; __lwp_objmgr_initinfo(&smb_handle_objects,SMB_CONNHANDLES_MAX,sizeof(SMBHANDLE)); __lwp_queue_initialize(&smb_filehandle_queue,smb_filehandles,SMB_FILEHANDLES_MAX,sizeof(struct _smbfile)); } static SMBHANDLE* __smb_allocate_handle() { u32 level; SMBHANDLE *handle; _CPU_ISR_Disable(level); handle = (SMBHANDLE*)__lwp_objmgr_allocate(&smb_handle_objects); if(handle) { handle->user = NULL; handle->pwd = NULL; handle->server_name = NULL; handle->share_name = NULL; handle->sck_server = INVALID_SOCKET; handle->conn_valid = false; __lwp_objmgr_open(&smb_handle_objects,&handle->object); } _CPU_ISR_Restore(level); return handle; } static void __smb_free_handle(SMBHANDLE *handle) { if(handle->user) free(handle->user); if(handle->pwd) free(handle->pwd); if(handle->server_name) free(handle->server_name); if(handle->share_name) free(handle->share_name); handle->user = NULL; handle->pwd = NULL; handle->server_name = NULL; handle->share_name = NULL; handle->sck_server = INVALID_SOCKET; __smb_handle_free(handle); } static void MakeSMBHeader(u8 command,u8 flags,u16 flags2,SMBHANDLE *handle) { u8 *ptr = handle->message.smb; NBTSMB *nbt = &handle->message; SMBSESSION *sess = &handle->session; memset(nbt,0,sizeof(NBTSMB)); setUInt(ptr,SMB_OFFSET_PROTO,SMB_PROTO); setUChar(ptr,SMB_OFFSET_CMD,command); setUChar(ptr,SMB_OFFSET_FLAGS,flags); setUShort(ptr,SMB_OFFSET_FLAGS2,flags2); setUShort(ptr,SMB_OFFSET_TID,sess->TID); setUShort(ptr,SMB_OFFSET_PID,sess->PID); setUShort(ptr,SMB_OFFSET_UID,sess->UID); setUShort(ptr,SMB_OFFSET_MID,sess->MID); ptr[SMB_HEADER_SIZE] = 0; } /** * MakeTRANS2Hdr */ static void MakeTRANS2Header(u8 subcommand,SMBHANDLE *handle) { u8 *ptr = handle->message.smb; setUChar(ptr, T2_WORD_CNT, 15); setUShort(ptr, T2_MAXPRM_CNT, 10); setUShort(ptr, T2_MAXBUFFER, 16384); setUChar(ptr, T2_SSETUP_CNT, 1); setUShort(ptr, T2_SUB_CMD, subcommand); } /** * smb_send * * blocking call with timeout * will return when ALL data has been sent. Number of bytes sent is returned. * OR timeout. Timeout will return -1 * OR network error. -ve value will be returned */ static inline s32 smb_send(s32 s,const void *data,s32 size) { u64 t1,t2; s32 ret, len = size, nextsend; t1=ticks_to_millisecs(gettime()); while(len>0) { nextsend=len; if(nextsend>SMB_MAX_NET_WRITE_SIZE) nextsend=SMB_MAX_NET_WRITE_SIZE; //optimized value ret=net_send(s,data,nextsend,0); if(ret==-EAGAIN) { t2=ticks_to_millisecs(gettime()); if( (t2 - t1) > RECV_TIMEOUT) { return -1; // timeout } usleep(100); // allow system to perform work. Stabilizes system continue; } else if(ret<0) { return ret; // an error occurred } else { data+=ret; len-=ret; if(len==0) return size; t1=ticks_to_millisecs(gettime()); } usleep(100); } return size; } /** * smb_recv * * blocking call with timeout * will return when ANY data has been read from socket. Number of bytes read is returned. * OR timeout. Timeout will return -1 * OR network error. -ve value will be returned */ static s32 smb_recv(s32 s,void *mem,s32 len) { s32 ret,read,readtotal=0; u64 t1,t2; t1=ticks_to_millisecs(gettime()); while(len > 0) { read=len; if(read>SMB_MAX_NET_READ_SIZE) read=SMB_MAX_NET_READ_SIZE; // optimized value ret=net_recv(s,mem+readtotal,read,0); if(ret>0) { readtotal+=ret; len-=ret; if(len==0) return readtotal; } else { if(ret!=-EAGAIN) return ret; t2=ticks_to_millisecs(gettime()); if( (t2 - t1) > RECV_TIMEOUT) return -1; } usleep(1000); } return readtotal; } static void clear_network(s32 s,u8 *ptr) { u64 t1,t2; t1=ticks_to_millisecs(gettime()); while(true) { net_recv(s,ptr,SMB_MAX_NET_READ_SIZE,0); t2=ticks_to_millisecs(gettime()); if( (t2 - t1) > 600) return; usleep(100); } } /** * SMBCheck * * Do very basic checking on the return SMB * Read bytes * if ==0 then read a single SMB packet * discard any non NBT_SESSISON_MSG packets along the way. */ static s32 SMBCheck(u8 command,SMBHANDLE *handle) { s32 ret; u8 *ptr = handle->message.smb; NBTSMB *nbt = &handle->message; u32 readlen; u64 t1,t2; if(handle->sck_server == INVALID_SOCKET) return SMB_ERROR; memset(nbt,0xFF,sizeof(NBTSMB)); //NBT_SESSISON_MSG is 0x00 so fill mem with 0xFF t1=ticks_to_millisecs(gettime()); /*keep going till we get a NBT session message*/ do{ ret=smb_recv(handle->sck_server, (u8*)nbt, 4); if(ret!=4) goto failed; if(nbt->msg!=NBT_SESSISON_MSG) { readlen=(u32)((nbt->length_high<<16)|nbt->length); if(readlen>0) { t1=ticks_to_millisecs(gettime()); smb_recv(handle->sck_server, ptr, readlen); //clear unexpected NBT message } } t2=ticks_to_millisecs(gettime()); if( (t2 - t1) > RECV_TIMEOUT * 2) goto failed; } while(nbt->msg!=NBT_SESSISON_MSG); /* obtain required length from NBT header if readlen==0*/ readlen=(u32)((nbt->length_high<<16)|nbt->length); // Get server message block ret=smb_recv(handle->sck_server, ptr, readlen); if(readlen!=ret) goto failed; /*** Do basic SMB Header checks ***/ ret = getUInt(ptr,SMB_OFFSET_PROTO); if(ret!=SMB_PROTO) goto failed; ret = getUChar(ptr, SMB_OFFSET_CMD); if(ret!=command) goto failed; ret = getUInt(ptr,SMB_OFFSET_NTSTATUS); if(ret) goto failed; return SMB_SUCCESS; failed: clear_network(handle->sck_server,ptr); return SMB_ERROR; } /** * SMB_SetupAndX * * Setup the SMB session, including authentication with the * magic 'NTLM Response' */ static s32 SMB_SetupAndX(SMBHANDLE *handle) { s32 pos; s32 bcpos; s32 i, ret; u8 *ptr = handle->message.smb; SMBSESSION *sess = &handle->session; char pwd[30], ntRespData[24]; if(handle->sck_server == INVALID_SOCKET) return SMB_ERROR; MakeSMBHeader(SMB_SETUP_ANDX,CIFS_FLAGS1,handle->unicode?CIFS_FLAGS2_UNICODE:CIFS_FLAGS2,handle); pos = SMB_HEADER_SIZE; setUChar(ptr,pos,13); pos++; /*** Word Count ***/ setUChar(ptr,pos,0xff); pos++; /*** Next AndX ***/ setUChar(ptr,pos,0); pos++; /*** Reserved ***/ pos += 2; /*** Next AndX Offset ***/ setUShort(ptr,pos,sess->MaxBuffer); pos += 2; setUShort(ptr,pos,sess->MaxMpx); pos += 2; setUShort(ptr,pos,sess->MaxVCS); pos += 2; setUInt(ptr,pos,sess->sKey); pos += 4; setUShort(ptr,pos,24); /*** Password length (case-insensitive) ***/ pos += 2; setUShort(ptr,pos,24); /*** Password length (case-sensitive) ***/ pos += 2; setUInt(ptr,pos,0); pos += 4; /*** Reserved ***/ setUInt(ptr,pos,sess->capabilities); pos += 4; /*** Capabilities ***/ bcpos = pos; pos += 2; /*** Byte count ***/ /*** The magic 'NTLM Response' ***/ strcpy(pwd, handle->pwd); if (sess->challengeUsed) ntlm_smb_nt_encrypt((const char *) pwd, (const u8 *) sess->challenge, (u8*) ntRespData); /*** Build information ***/ memset(&ptr[pos],0,24); pos += 24; memcpy(&ptr[pos],ntRespData,24); pos += 24; pos++; /*** Account ***/ strcpy(pwd, handle->user); for(i=0;iunicode) { pos += utf8_to_utf16((char*)&ptr[pos],pwd,SMB_MAXPATH-2); pos += 2; } else { memcpy(&ptr[pos],pwd,strlen(pwd)); pos += strlen(pwd)+1; } /*** Primary Domain ***/ if(handle->user[0]=='\0') sess->p_domain[0] = '\0'; if(handle->unicode) { pos += utf8_to_utf16((char*)&ptr[pos],(char*)sess->p_domain,SMB_MAXPATH-2); pos += 2; } else { memcpy(&ptr[pos],sess->p_domain,strlen((const char*)sess->p_domain)); pos += strlen((const char*)sess->p_domain)+1; } /*** Native OS ***/ strcpy(pwd,"Unix (libOGC)"); if(handle->unicode) { pos += utf8_to_utf16((char*)&ptr[pos],pwd,SMB_MAXPATH-2); pos += 2; } else { memcpy(&ptr[pos],pwd,strlen(pwd)); pos += strlen(pwd)+1; } /*** Native LAN Manager ***/ strcpy(pwd,"Nintendo Wii"); if(handle->unicode) { pos += utf8_to_utf16((char*)&ptr[pos],pwd,SMB_MAXPATH-2); pos += 2; } else { memcpy(&ptr[pos],pwd,strlen(pwd)); pos += strlen (pwd)+1; } /*** Update byte count ***/ setUShort(ptr,bcpos,((pos-bcpos)-2)); handle->message.msg = NBT_SESSISON_MSG; handle->message.length = htons (pos); pos += 4; ret = smb_send(handle->sck_server,(char*)&handle->message,pos); if(ret<=0) return SMB_ERROR; if((ret=SMBCheck(SMB_SETUP_ANDX,handle))==SMB_SUCCESS) { /*** Collect UID ***/ sess->UID = getUShort(handle->message.smb,SMB_OFFSET_UID); return SMB_SUCCESS; } return ret; } /** * SMB_TreeAndX * * Finally, net_connect to the remote share */ static s32 SMB_TreeAndX(SMBHANDLE *handle) { s32 pos, bcpos, ret; char path[512]; u8 *ptr = handle->message.smb; SMBSESSION *sess = &handle->session; if(handle->sck_server == INVALID_SOCKET) return SMB_ERROR; MakeSMBHeader(SMB_TREEC_ANDX,CIFS_FLAGS1,handle->unicode?CIFS_FLAGS2_UNICODE:CIFS_FLAGS2,handle); pos = SMB_HEADER_SIZE; setUChar(ptr,pos,4); pos++; /*** Word Count ***/ setUChar(ptr,pos,0xff); pos++; /*** Next AndX ***/ pos++; /*** Reserved ***/ pos += 2; /*** Next AndX Offset ***/ pos += 2; /*** Flags ***/ setUShort(ptr,pos,1); pos += 2; /*** Password Length ***/ bcpos = pos; pos += 2; pos++; /*** NULL Password ***/ /*** Build server share path ***/ strcpy ((char*)path, "\\\\"); strcat ((char*)path, handle->server_name); strcat ((char*)path, "\\"); strcat ((char*)path, handle->share_name); for(ret=0;retunicode) { pos += utf8_to_utf16((char*)&ptr[pos],path,SMB_MAXPATH-2); pos += 2; } else { memcpy(&ptr[pos],path,strlen((const char*)path)); pos += strlen((const char*)path)+1; } /*** Service ***/ strcpy((char*)path,"?????"); memcpy(&ptr[pos],path,strlen((const char*)path)); pos += strlen((const char*)path)+1; /*** Update byte count ***/ setUShort(ptr,bcpos,(pos-bcpos)-2); handle->message.msg = NBT_SESSISON_MSG; handle->message.length = htons (pos); pos += 4; ret = smb_send(handle->sck_server,(char *)&handle->message,pos); if(ret<=0) return SMB_ERROR; if((ret=SMBCheck(SMB_TREEC_ANDX,handle))==SMB_SUCCESS) { /*** Collect Tree ID ***/ sess->TID = getUShort(handle->message.smb,SMB_OFFSET_TID); return SMB_SUCCESS; } return ret; } /** * SMB_NegotiateProtocol * * The only protocol we admit to is 'NT LM 0.12' */ static s32 SMB_NegotiateProtocol(const char *dialects[],int dialectc,SMBHANDLE *handle) { u8 *ptr; s32 pos; s32 bcnt,i,j; s32 ret,len; u32 serverMaxBuffer; SMBSESSION *sess; if(!handle || !dialects || dialectc<=0) return SMB_ERROR; if(handle->sck_server == INVALID_SOCKET) return SMB_ERROR; /*** Clear session variables ***/ sess = &handle->session; memset(sess,0,sizeof(SMBSESSION)); sess->PID = 0xdead; sess->MID = 1; sess->capabilities = 0; MakeSMBHeader(SMB_NEG_PROTOCOL,CIFS_FLAGS1,handle->unicode?CIFS_FLAGS2_UNICODE:CIFS_FLAGS2,handle); pos = SMB_HEADER_SIZE+3; ptr = handle->message.smb; for(i=0,bcnt=0;imessage.msg = NBT_SESSISON_MSG; handle->message.length = htons(pos); pos += 4; ret = smb_send(handle->sck_server,(char*)&handle->message,pos); if(ret<=0) return SMB_ERROR; /*** Check response ***/ if((ret=SMBCheck(SMB_NEG_PROTOCOL,handle))==SMB_SUCCESS) { pos = SMB_HEADER_SIZE; ptr = handle->message.smb; /*** Collect information ***/ if(getUChar(ptr,pos)!=17) return SMB_PROTO_FAIL; // UCHAR WordCount; Count of parameter words = 17 pos++; if(getUShort(ptr,pos)!=0) return SMB_PROTO_FAIL; // USHORT DialectIndex; Index of selected dialect - should always be 0 since we only supplied 1! pos += 2; if(getUChar(ptr,pos) & 1) { // user level security sess->securityLevel = 1; } else { // share level security - can we skip SetupAndX? If so, we would need to specify the password in TreeAndX sess->securityLevel = 0; } pos++; sess->MaxMpx = getUShort(ptr, pos); //USHORT MaxMpxCount; Max pending outstanding requests pos += 2; sess->MaxVCS = getUShort(ptr, pos); //USHORT MaxNumberVcs; Max VCs between client and server pos += 2; serverMaxBuffer = getUInt(ptr, pos); //ULONG MaxBufferSize; Max transmit buffer size if(serverMaxBuffer>SMB_MAX_TRANSMIT_SIZE) sess->MaxBuffer = SMB_MAX_TRANSMIT_SIZE; else sess->MaxBuffer = serverMaxBuffer; pos += 4; pos += 4; //ULONG MaxRawSize; Maximum raw buffer size sess->sKey = getUInt(ptr,pos); pos += 4; u32 servcap = getUInt(ptr,pos); pos += 4; //ULONG Capabilities; Server capabilities pos += 4; //ULONG SystemTimeLow; System (UTC) time of the server (low). pos += 4; //ULONG SystemTimeHigh; System (UTC) time of the server (high). sess->timeOffset = getShort(ptr,pos) * 600000000LL; pos += 2; //SHORT ServerTimeZone; Time zone of server (minutes from UTC) //UCHAR EncryptionKeyLength - 0 or 8 if(getUChar(ptr,pos)!=8) { if (getUChar(ptr,pos)!=0) { return SMB_BAD_KEYLEN; } else { // Challenge key not used sess->challengeUsed = false; } } else { sess->challengeUsed = true; } pos++; getUShort(ptr,pos); // byte count if (sess->challengeUsed) { /*** Copy challenge key ***/ pos += 2; memcpy(&sess->challenge,&ptr[pos],8); } /*** Primary domain ***/ pos += 8; i = j = 0; while(ptr[pos+j]!=0) { sess->p_domain[i] = ptr[pos+j]; j += 2; i++; } sess->p_domain[i] = '\0'; // setup capabilities //if(servcap & CAP_LARGE_FILES) // sess->capabilities |= CAP_LARGE_FILES; if(servcap & CAP_UNICODE) { sess->capabilities |= CAP_UNICODE; handle->unicode = true; } return SMB_SUCCESS; } return ret; } static s32 do_netconnect(SMBHANDLE *handle) { u32 set = 1; s32 ret; s32 sock; u64 t1,t2; handle->sck_server = INVALID_SOCKET; /*** Create the global net_socket ***/ sock = net_socket(AF_INET, SOCK_STREAM, IPPROTO_IP); if(sock==INVALID_SOCKET) return -1; // Switch off Nagle with TCP_NODELAY net_setsockopt(sock,IPPROTO_TCP,TCP_NODELAY,&set,sizeof(set)); // create non blocking socket ret = net_ioctl(sock, FIONBIO, &set); if (ret < 0) { net_close(sock); return ret; } t1=ticks_to_millisecs(gettime()); while(1) { ret = net_connect(sock,(struct sockaddr*)&handle->server_addr,sizeof(handle->server_addr)); if(ret==-EISCONN) break; t2=ticks_to_millisecs(gettime()); usleep(1000); if((t2-t1) > CONN_TIMEOUT) break; // usually not more than 90ms } if(ret!=-EISCONN) { net_close(sock); return -1; } handle->sck_server = sock; return 0; } static s32 do_smbconnect(SMBHANDLE *handle) { s32 ret; if(handle->sck_server == INVALID_SOCKET) return -1; ret = SMB_NegotiateProtocol(smb_dialects,smb_dialectcnt,handle); if(ret!=SMB_SUCCESS) { net_close(handle->sck_server); handle->sck_server = INVALID_SOCKET; return -1; } ret = SMB_SetupAndX(handle); if(ret!=SMB_SUCCESS) { net_close(handle->sck_server); handle->sck_server = INVALID_SOCKET; return -1; } ret = SMB_TreeAndX(handle); if(ret!=SMB_SUCCESS) { net_close(handle->sck_server); handle->sck_server = INVALID_SOCKET; return -1; } handle->conn_valid = true; return 0; } /**************************************************************************** * Create an NBT SESSION REQUEST message. ****************************************************************************/ static int MakeSessReq(unsigned char *bufr, unsigned char *Called, unsigned char *Calling) { // Write the header. bufr[0] = SESS_REQ; bufr[1] = 0; bufr[2] = 0; bufr[3] = 68; // 2x34 bytes in length. // Copy the Called and Calling names into the buffer. (void) memcpy(&bufr[4], Called, 34); (void) memcpy(&bufr[38], Calling, 34); // Return the total message length. return 72; } static unsigned char *L1_Encode(unsigned char *dst, const unsigned char *name, const unsigned char pad, const unsigned char sfx) { int i = 0; int j = 0; int k = 0; while (('\0' != name[i]) && (i < 15)) { k = toupper(name[i++]); dst[j++] = 'A' + ((k & 0xF0) >> 4); dst[j++] = 'A' + (k & 0x0F); } i = 'A' + ((pad & 0xF0) >> 4); k = 'A' + (pad & 0x0F); while (j < 30) { dst[j++] = i; dst[j++] = k; } dst[30] = 'A' + ((sfx & 0xF0) >> 4); dst[31] = 'A' + (sfx & 0x0F); dst[32] = '\0'; return (dst); } static int L2_Encode(unsigned char *dst, const unsigned char *name, const unsigned char pad, const unsigned char sfx, const unsigned char *scope) { int lenpos; int i; int j; if (NULL == L1_Encode(&dst[1], name, pad, sfx)) return (-1); dst[0] = 0x20; lenpos = 33; if ('\0' != *scope) { do { for (i = 0, j = (lenpos + 1); ('.' != scope[i]) && ('\0' != scope[i]); i++, j++) dst[j] = toupper(scope[i]); dst[lenpos] = (unsigned char) i; lenpos += i + 1; scope += i; } while ('.' == *(scope++)); dst[lenpos] = '\0'; } return (lenpos + 1); } /**************************************************************************** * Send an NBT SESSION REQUEST over the TCP connection, then wait for a reply. ****************************************************************************/ static s32 SMB_RequestNBTSession(SMBHANDLE *handle) { unsigned char Called[34]; unsigned char Calling[34]; unsigned char bufr[128]; int result; if(handle->sck_server == INVALID_SOCKET) return -1; L2_Encode(Called, (const unsigned char*) "*SMBSERVER", 0x20, 0x20, (const unsigned char*) ""); L2_Encode(Calling, (const unsigned char*) "SMBCLIENT", 0x20, 0x00, (const unsigned char*) ""); // Create the NBT Session Request message. result = MakeSessReq(bufr, Called, Calling); //Send the NBT Session Request message. result = smb_send(handle->sck_server, bufr, result); if (result < 0) { // Error sending Session Request message return -1; } // Now wait for and handle the reply (2 seconds). result = smb_recv(handle->sck_server, bufr, 128); if (result <= 0) { // Timeout waiting for NBT Session Response return -1; } switch (*bufr) { case SESS_POS_RESP: // Positive Session Response return 0; case SESS_NEG_RESP: // Negative Session Response return -1; case SESS_RETARGET: // Retarget Session Response return -1; default: // Unexpected Session Response return -1; } } /**************************************************************************** * Primary setup, logon and connection all in one :) ****************************************************************************/ s32 SMB_Connect(SMBCONN *smbhndl, const char *user, const char *password, const char *share, const char *server) { s32 ret = 0; SMBHANDLE *handle; struct in_addr val; *smbhndl = SMB_HANDLE_NULL; if(!user || !password || !share || !server || strlen(user) > 20 || strlen(password) > 14 || strlen(share) > 80 || strlen(server) > 80) { return SMB_BAD_LOGINDATA; } if(!smb_inited) { u32 level; _CPU_ISR_Disable(level); __smb_init(); _CPU_ISR_Restore(level); } handle = __smb_allocate_handle(); if(!handle) return SMB_ERROR; handle->user = strdup(user); handle->pwd = strdup(password); handle->server_name = strdup(server); handle->share_name = strdup(share); handle->server_addr.sin_family = AF_INET; handle->server_addr.sin_port = htons(445); handle->unicode = false; if(strlen(server) < 16 && inet_aton(server, &val)) { handle->server_addr.sin_addr.s_addr = val.s_addr; } else // might be a hostname { #ifdef HW_RVL struct hostent *hp = net_gethostbyname(server); if (!hp || !(hp->h_addrtype == PF_INET)) ret = SMB_BAD_LOGINDATA; else memcpy((char *)&handle->server_addr.sin_addr.s_addr, hp->h_addr_list[0], hp->h_length); #else __smb_free_handle(handle); return SMB_ERROR; #endif } *smbhndl =(SMBCONN)(LWP_OBJMASKTYPE(SMB_OBJTYPE_HANDLE)|LWP_OBJMASKID(handle->object.id)); if(ret==0) { ret = do_netconnect(handle); if(ret==0) ret = do_smbconnect(handle); if(ret!=0) { // try port 139 handle->server_addr.sin_port = htons(139); ret = do_netconnect(handle); if(ret==0) ret = SMB_RequestNBTSession(handle); if(ret==0) ret = do_smbconnect(handle); } } if(ret!=0) { __smb_free_handle(handle); return SMB_ERROR; } return SMB_SUCCESS; } /**************************************************************************** * SMB_Destroy ****************************************************************************/ void SMB_Close(SMBCONN smbhndl) { SMBHANDLE *handle = __smb_handle_open(smbhndl); if(!handle) return; if(handle->sck_server!=INVALID_SOCKET) net_close(handle->sck_server); __smb_free_handle(handle); } s32 SMB_Reconnect(SMBCONN *_smbhndl, bool test_conn) { s32 ret = SMB_SUCCESS; SMBCONN smbhndl = *_smbhndl; SMBHANDLE *handle = __smb_handle_open(smbhndl); if(!handle) return SMB_ERROR; // we have no handle, so we can't reconnect if(handle->conn_valid && test_conn) { SMBDIRENTRY dentry; if(SMB_PathInfo("\\", &dentry, smbhndl)==SMB_SUCCESS) return SMB_SUCCESS; // no need to reconnect handle->conn_valid = false; // else connection is invalid } if(!handle->conn_valid) { // shut down connection if(handle->sck_server!=INVALID_SOCKET) { net_close(handle->sck_server); handle->sck_server = INVALID_SOCKET; } // reconnect if(handle->server_addr.sin_port > 0) { ret = do_netconnect(handle); if(ret==0 && handle->server_addr.sin_port == htons(139)) ret = SMB_RequestNBTSession(handle); if(ret==0) ret = do_smbconnect(handle); } else // initial connection { handle->server_addr.sin_port = htons(445); ret = do_netconnect(handle); if(ret==0) ret = do_smbconnect(handle); if(ret != 0) { // try port 139 handle->server_addr.sin_port = htons(139); ret = do_netconnect(handle); if(ret==0) ret = SMB_RequestNBTSession(handle); if(ret==0) ret = do_smbconnect(handle); } if(ret != 0) handle->server_addr.sin_port = 0; } } return ret; } SMBFILE SMB_OpenFile(const char *filename, u16 access, u16 creation,SMBCONN smbhndl) { s32 pos; s32 bpos,ret; u8 *ptr; struct _smbfile *fid = NULL; SMBHANDLE *handle; char realfile[512]; if(filename == NULL) return NULL; if(SMB_Reconnect(&smbhndl,true)!=SMB_SUCCESS) return NULL; handle = __smb_handle_open(smbhndl); if(!handle) return NULL; MakeSMBHeader(SMB_OPEN_ANDX,CIFS_FLAGS1,handle->unicode?CIFS_FLAGS2_UNICODE:CIFS_FLAGS2,handle); pos = SMB_HEADER_SIZE; ptr = handle->message.smb; setUChar(ptr, pos, 15); pos++; /*** Word Count ***/ setUChar(ptr, pos, 0xff); pos++; /*** AndXCommand 0xFF = None ***/ setUChar(ptr, pos, 0); pos++; /*** AndX Reserved must be 0 ***/ pos += 2; /*** Next AndX Offset to next Command ***/ pos += 2; /*** Flags ***/ setUShort(ptr, pos, access); pos += 2; /*** Access mode ***/ setUShort(ptr, pos, 0x6); pos += 2; /*** Type of file ***/ pos += 2; /*** File Attributes ***/ pos += 4; /*** File time - don't care - let server decide ***/ setUShort(ptr, pos, creation); pos += 2; /*** Creation flags ***/ pos += 4; /*** Allocation size ***/ setUInt(ptr, pos, 0); pos += 4; /*** Reserved[0] must be 0 ***/ setUInt(ptr, pos, 0); pos += 4; /*** Reserved[1] must be 0 ***/ pos += 2; /*** Byte Count ***/ bpos = pos; setUChar(ptr, pos, 0x04); /** Bufferformat **/ pos++; realfile[0]='\0'; if (filename[0] != '\\') strcpy(realfile,"\\"); strcat(realfile,filename); if(handle->unicode) { pos += utf8_to_utf16((char*)&ptr[pos],realfile,SMB_MAXPATH-2); pos += 2; } else { memcpy(&ptr[pos],realfile,strlen(realfile)); pos += strlen(realfile)+1; } setUShort(ptr,(bpos-2),(pos-bpos)); handle->message.msg = NBT_SESSISON_MSG; handle->message.length = htons(pos); pos += 4; ret = smb_send(handle->sck_server,(char*)&handle->message,pos); if(ret<0) goto failed; if(SMBCheck(SMB_OPEN_ANDX,handle)==SMB_SUCCESS) { /*** Check file handle ***/ fid = (struct _smbfile*)__lwp_queue_get(&smb_filehandle_queue); if(fid) { fid->conn = smbhndl; fid->sfid = getUShort(handle->message.smb,(SMB_HEADER_SIZE+5)); } } return (SMBFILE)fid; failed: handle->conn_valid = false; return NULL; } /** * SMB_CloseFile */ void SMB_CloseFile(SMBFILE sfid) { u8 *ptr; s32 pos, ret; SMBHANDLE *handle; struct _smbfile *fid = (struct _smbfile*)sfid; if(!fid) return; handle = __smb_handle_open(fid->conn); if(!handle) return; MakeSMBHeader(SMB_CLOSE,CIFS_FLAGS1,handle->unicode?CIFS_FLAGS2_UNICODE:CIFS_FLAGS2,handle); pos = SMB_HEADER_SIZE; ptr = handle->message.smb; setUChar(ptr, pos, 3); pos++; /** Word Count **/ setUShort(ptr, pos, fid->sfid); pos += 2; setUInt(ptr, pos, 0xffffffff); pos += 4; /*** Last Write ***/ pos += 2; /*** Byte Count ***/ handle->message.msg = NBT_SESSISON_MSG; handle->message.length = htons(pos); pos += 4; ret = smb_send(handle->sck_server,(char*)&handle->message,pos); if(ret<0) handle->conn_valid = false; else SMBCheck(SMB_CLOSE,handle); __lwp_queue_append(&smb_filehandle_queue,&fid->node); } /** * SMB_CreateDirectory */ s32 SMB_CreateDirectory(const char *dirname, SMBCONN smbhndl) { s32 pos; s32 bpos,ret; u8 *ptr; SMBHANDLE *handle; char realfile[512]; if(dirname == NULL) return -1; if(SMB_Reconnect(&smbhndl,true)!=SMB_SUCCESS) return -1; handle = __smb_handle_open(smbhndl); if(!handle) return -1; MakeSMBHeader(SMB_COM_CREATE_DIRECTORY,CIFS_FLAGS1, handle->unicode?CIFS_FLAGS2_UNICODE:CIFS_FLAGS2,handle); pos = SMB_HEADER_SIZE; ptr = handle->message.smb; setUChar(ptr, pos, 0); pos++; /*** Word Count ***/ pos += 2; /*** Byte Count ***/ bpos = pos; setUChar(ptr, pos, 0x04); /*** Buffer format ***/ pos++; realfile[0]='\0'; if (dirname[0] != '\\') strcpy(realfile,"\\"); strcat(realfile,dirname); if(handle->unicode) { pos += utf8_to_utf16((char*)&ptr[pos],realfile,SMB_MAXPATH-2); pos += 2; } else { memcpy(&ptr[pos],realfile,strlen(realfile)); pos += strlen(realfile)+1; } setUShort(ptr,(bpos-2),(pos-bpos)); handle->message.msg = NBT_SESSISON_MSG; handle->message.length = htons(pos); pos += 4; ret = smb_send(handle->sck_server,(char*)&handle->message,pos); if(ret < 0) goto failed; ret = SMBCheck(SMB_COM_CREATE_DIRECTORY,handle); return ret; failed: return ret; } /** * SMB_DeleteDirectory */ s32 SMB_DeleteDirectory(const char *dirname, SMBCONN smbhndl) { s32 pos; s32 bpos,ret; u8 *ptr; SMBHANDLE *handle; char realfile[512]; if(dirname == NULL) return -1; if(SMB_Reconnect(&smbhndl,true)!=SMB_SUCCESS) return -1; handle = __smb_handle_open(smbhndl); if(!handle) return -1; MakeSMBHeader(SMB_COM_DELETE_DIRECTORY,CIFS_FLAGS1, handle->unicode?CIFS_FLAGS2_UNICODE:CIFS_FLAGS2,handle); pos = SMB_HEADER_SIZE; ptr = handle->message.smb; setUChar(ptr, pos, 0); pos++; /*** Word Count ***/ pos += 2; /*** Byte Count ***/ bpos = pos; setUChar(ptr, pos, 0x04); /** Bufferformat **/ pos++; realfile[0]='\0'; if (dirname[0] != '\\') strcpy(realfile,"\\"); strcat(realfile,dirname); if(handle->unicode) { pos += utf8_to_utf16((char*)&ptr[pos],realfile,SMB_MAXPATH-2); pos += 2; } else { memcpy(&ptr[pos],realfile,strlen(realfile)); pos += strlen(realfile)+1; } setUShort(ptr,(bpos-2),(pos-bpos)); handle->message.msg = NBT_SESSISON_MSG; handle->message.length = htons(pos); pos += 4; ret = smb_send(handle->sck_server,(char*)&handle->message,pos); if(ret < 0) goto failed; ret = SMBCheck(SMB_COM_DELETE_DIRECTORY,handle); return ret; failed: return ret; } /** * SMB_DeleteFile */ s32 SMB_DeleteFile(const char *filename, SMBCONN smbhndl) { s32 pos; s32 bpos,ret; u8 *ptr; SMBHANDLE *handle; char realfile[512]; if(filename == NULL) return -1; if(SMB_Reconnect(&smbhndl,true)!=SMB_SUCCESS) return -1; handle = __smb_handle_open(smbhndl); if(!handle) return -1; MakeSMBHeader(SMB_COM_DELETE,CIFS_FLAGS1, handle->unicode?CIFS_FLAGS2_UNICODE:CIFS_FLAGS2,handle); pos = SMB_HEADER_SIZE; ptr = handle->message.smb; setUChar(ptr, pos, 1); pos++; /*** Word Count ***/ setUShort(ptr, pos, SMB_SRCH_HIDDEN); pos += 2; /*** SearchAttributes ***/ pos += 2; /*** Byte Count ***/ bpos = pos; setUChar(ptr, pos, 0x04); /** Bufferformat **/ pos++; realfile[0]='\0'; if (filename[0] != '\\') strcpy(realfile,"\\"); strcat(realfile,filename); if(handle->unicode) { pos += utf8_to_utf16((char*)&ptr[pos],realfile,SMB_MAXPATH-2); pos += 2; } else { memcpy(&ptr[pos],realfile,strlen(realfile)); pos += strlen(realfile)+1; } setUShort(ptr,(bpos-2),(pos-bpos)); handle->message.msg = NBT_SESSISON_MSG; handle->message.length = htons(pos); pos += 4; ret = smb_send(handle->sck_server,(char*)&handle->message,pos); if(ret < 0) goto failed; ret = SMBCheck(SMB_COM_DELETE,handle); return ret; failed: return ret; } /** * SMB_Rename */ s32 SMB_Rename(const char *filename, const char * newfilename, SMBCONN smbhndl) { s32 pos; s32 bpos,ret; u8 *ptr; SMBHANDLE *handle; char realfile[512]; char newrealfile[512]; if(filename == NULL || newfilename == NULL) return -1; if(SMB_Reconnect(&smbhndl,true)!=SMB_SUCCESS) return -1; handle = __smb_handle_open(smbhndl); if(!handle) return -1; MakeSMBHeader(SMB_COM_RENAME,CIFS_FLAGS1, handle->unicode?CIFS_FLAGS2_UNICODE:CIFS_FLAGS2,handle); pos = SMB_HEADER_SIZE; ptr = handle->message.smb; setUChar(ptr, pos, 1); pos++; /*** Word Count ***/ setUShort(ptr, pos, SMB_SRCH_HIDDEN); pos += 2; /*** SearchAttributes ***/ pos += 2; /*** Byte Count ***/ bpos = pos; setUChar(ptr, pos, 0x04); /** Bufferformat **/ pos++; realfile[0]='\0'; if (filename[0] != '\\') strcpy(realfile,"\\"); strcat(realfile,filename); if(handle->unicode) { pos += utf8_to_utf16((char*)&ptr[pos],realfile,SMB_MAXPATH-2); pos += 2; } else { memcpy(&ptr[pos],realfile,strlen(realfile)); pos += strlen(realfile)+1; } pos++; setUChar(ptr, pos, 0x04); /** Bufferformat **/ pos++; newrealfile[0]='\0'; if (newfilename[0] != '\\') strcpy(newrealfile,"\\"); strcat(newrealfile,newfilename); if(handle->unicode) { pos += utf8_to_utf16((char*)&ptr[pos],newrealfile,SMB_MAXPATH-2); pos += 2; } else { memcpy(&ptr[pos],newrealfile,strlen(newrealfile)); pos += strlen(newrealfile)+1; } setUShort(ptr,(bpos-2),(pos-bpos)); handle->message.msg = NBT_SESSISON_MSG; handle->message.length = htons(pos); pos += 4; ret = smb_send(handle->sck_server,(char*)&handle->message,pos); if(ret < 0) goto failed; ret = SMBCheck(SMB_COM_RENAME,handle); return ret; failed: return ret; } /** * SMB_DiskInformation */ s32 SMB_DiskInformation(struct statvfs *buf, SMBCONN smbhndl) { s32 pos; s32 ret; u16 TotalUnits, BlocksPerUnit, BlockSize, FreeUnits; u8 *ptr; SMBHANDLE *handle; if(SMB_Reconnect(&smbhndl,true)!=SMB_SUCCESS) return -1; handle = __smb_handle_open(smbhndl); if(!handle) return -1; MakeSMBHeader(SMB_COM_QUERY_INFORMATION_DISK,CIFS_FLAGS1, handle->unicode?CIFS_FLAGS2_UNICODE:CIFS_FLAGS2,handle); pos = SMB_HEADER_SIZE; ptr = handle->message.smb; setUChar(ptr, pos, 0); pos++; /*** Word Count ***/ setUShort(ptr, pos, 0); pos += 2; /*** Byte Count ***/ handle->message.msg = NBT_SESSISON_MSG; handle->message.length = htons(pos); pos += 4; ret = smb_send(handle->sck_server,(char*)&handle->message,pos); if(ret < 0) goto failed; if((ret=SMBCheck(SMB_COM_QUERY_INFORMATION_DISK, handle))==SMB_SUCCESS) { ptr = handle->message.smb; /** Read the received data ***/ /** WordCount **/ s32 recv_pos = 1; /** TotalUnits **/ TotalUnits = getUShort(ptr,(SMB_HEADER_SIZE+recv_pos)); recv_pos += 2; /** BlocksPerUnit **/ BlocksPerUnit = getUShort(ptr,(SMB_HEADER_SIZE+recv_pos)); recv_pos += 2; /** BlockSize **/ BlockSize = getUShort(ptr,(SMB_HEADER_SIZE+recv_pos)); recv_pos += 2; /** FreeUnits **/ FreeUnits = getUShort(ptr,(SMB_HEADER_SIZE+recv_pos)); recv_pos += 2; buf->f_bsize = (unsigned long) BlockSize; // File system block size. buf->f_frsize = (unsigned long) BlockSize; // Fundamental file system block size. buf->f_blocks = (fsblkcnt_t) (TotalUnits*BlocksPerUnit); // Total number of blocks on file system in units of f_frsize. buf->f_bfree = (fsblkcnt_t) (FreeUnits*BlocksPerUnit); // Total number of free blocks. buf->f_bavail = 0; // Number of free blocks available to non-privileged process. buf->f_files = 0; // Total number of file serial numbers. buf->f_ffree = 0; // Total number of free file serial numbers. buf->f_favail = 0; // Number of file serial numbers available to non-privileged process. buf->f_fsid = 0; // File system ID. 32bit ioType value buf->f_flag = 0; // Bit mask of f_flag values. buf->f_namemax = SMB_MAXPATH; // Maximum filename length. return SMB_SUCCESS; } failed: handle->conn_valid = false; return ret; } /** * SMB_Read */ s32 SMB_ReadFile(char *buffer, size_t size, off_t offset, SMBFILE sfid) { u8 *ptr; u32 pos, ret, ofs; u16 length = 0; SMBHANDLE *handle; size_t totalread=0,nextread; struct _smbfile *fid = (struct _smbfile*)sfid; if(!fid) return -1; // Check for invalid size if(size == 0) return -1; handle = __smb_handle_open(fid->conn); if(!handle) return -1; while(totalread < size) { if((size-totalread) > SMB_MAX_TRANSMIT_SIZE) nextread=SMB_MAX_TRANSMIT_SIZE; else nextread=size-totalread; MakeSMBHeader(SMB_READ_ANDX,CIFS_FLAGS1,handle->unicode?CIFS_FLAGS2_UNICODE:CIFS_FLAGS2,handle); pos = SMB_HEADER_SIZE; ptr = handle->message.smb; setUChar(ptr, pos, 12); pos++; /*** Word count ***/ setUChar(ptr, pos, 0xff); pos++; setUChar(ptr, pos, 0); pos++; /*** Reserved must be 0 ***/ pos += 2; /*** Next AndX Offset ***/ setUShort(ptr, pos, fid->sfid); pos += 2; /*** FID ***/ setUInt(ptr, pos, (offset+totalread) & 0xffffffff); pos += 4; /*** Offset ***/ setUShort(ptr, pos, nextread & 0xffff); pos += 2; setUShort(ptr, pos, nextread & 0xffff); pos += 2; setUInt(ptr, pos, 0); pos += 4; /*** Reserved must be 0 ***/ setUShort(ptr, pos, nextread & 0xffff); pos += 2; /*** Remaining ***/ setUInt(ptr, pos, (offset+totalread) >> 32); // offset high pos += 4; /*** OffsetHIGH ***/ pos += 2; /*** Byte count ***/ handle->message.msg = NBT_SESSISON_MSG; handle->message.length = htons(pos); pos += 4; ret = smb_send(handle->sck_server,(char*)&handle->message, pos); if(ret<0) goto failed; /*** SMBCheck ***/ if(SMBCheck(SMB_READ_ANDX,handle)!=SMB_SUCCESS) goto failed; ptr = handle->message.smb; // Retrieve data length for this packet length = getUShort(ptr,(SMB_HEADER_SIZE+11)); if(length==0) break; // Retrieve offset to data ofs = getUShort(ptr,(SMB_HEADER_SIZE+13)); memcpy(&buffer[totalread],&ptr[ofs],length); totalread+=length; } return size; failed: handle->conn_valid = false; return SMB_ERROR; } /** * SMB_Write */ s32 SMB_WriteFile(const char *buffer, size_t size, off_t offset, SMBFILE sfid) { u8 *ptr,*src; s32 pos, ret; s32 blocks64; u32 copy_len; SMBHANDLE *handle; struct _smbfile *fid = (struct _smbfile*)sfid; if(!fid) return -1; handle = __smb_handle_open(fid->conn); if(!handle) return -1; MakeSMBHeader(SMB_WRITE_ANDX,CIFS_FLAGS1,handle->unicode?CIFS_FLAGS2_UNICODE:CIFS_FLAGS2,handle); pos = SMB_HEADER_SIZE; ptr = handle->message.smb; setUChar(ptr, pos, 14); pos++; /*** Word Count ***/ setUChar(ptr, pos, 0xff); pos += 2; /*** Next AndX ***/ pos += 2; /*** Next AndX Offset ***/ setUShort(ptr, pos, fid->sfid); pos += 2; setUInt(ptr, pos, offset & 0xffffffff); pos += 4; setUInt(ptr, pos, 0); /*** Reserved, must be 0 ***/ pos += 4; setUShort(ptr, pos, 0); /*** Write Mode ***/ pos += 2; pos += 2; /*** Remaining ***/ blocks64 = size >> 16; setUShort(ptr, pos, blocks64); pos += 2; /*** Length High ***/ setUShort(ptr, pos, size & 0xffff); pos += 2; /*** Length Low ***/ setUShort(ptr, pos, 63); pos += 2; /*** Data Offset ***/ setUInt(ptr, pos, offset >> 32); /*** OffsetHigh ***/ pos += 4; setUShort(ptr, pos, size & 0xffff); pos += 2; /*** Data Byte Count ***/ handle->message.msg = NBT_SESSISON_MSG; handle->message.length = htons(pos+size); src = (u8*)buffer; copy_len = size; if((copy_len+pos)>SMB_MAX_TRANSMIT_SIZE) copy_len = (SMB_MAX_TRANSMIT_SIZE-pos); memcpy(&ptr[pos],src,copy_len); size -= copy_len; src += copy_len; pos += copy_len; pos += 4; /*** Send Header Information ***/ ret = smb_send(handle->sck_server,(char*)&handle->message,pos); if(ret<0) goto failed; if(size>0) { /*** Send the data ***/ ret = smb_send(handle->sck_server,src,size); if(ret<0) goto failed; } ret = 0; if(SMBCheck(SMB_WRITE_ANDX,handle)==SMB_SUCCESS) { ptr = handle->message.smb; ret = getUShort(ptr,(SMB_HEADER_SIZE+5)); } return ret; failed: handle->conn_valid = false; return ret; } /** * SMB_PathInfo */ s32 SMB_PathInfo(const char *filename, SMBDIRENTRY *sdir, SMBCONN smbhndl) { u8 *ptr; s32 pos; s32 ret; s32 bpos; int len; SMBHANDLE *handle; char realfile[512]; if(filename == NULL) return SMB_ERROR; handle = __smb_handle_open(smbhndl); if (!handle) return SMB_ERROR; MakeSMBHeader(SMB_TRANS2, CIFS_FLAGS1, handle->unicode?CIFS_FLAGS2_UNICODE:CIFS_FLAGS2, handle); MakeTRANS2Header(SMB_QUERY_PATH_INFO, handle); bpos = pos = (T2_BYTE_CNT + 2); pos += 3; /*** Padding ***/ ptr = handle->message.smb; setUShort(ptr, pos, SMB_QUERY_FILE_ALL_INFO); pos += 2; /*** Level of information requested ***/ setUInt(ptr, pos, 0); pos += 4; /*** reserved ***/ realfile[0] = '\0'; if (filename[0] != '\\') strcpy(realfile,"\\"); strcat(realfile,filename); if(handle->unicode) { len = utf8_to_utf16((char*)&ptr[pos],realfile,SMB_MAXPATH-2); pos += len+2; len+=1; } else { len = strlen(realfile); memcpy(&ptr[pos],realfile,len); pos += len+1; } /*** Update counts ***/ setUShort(ptr, T2_PRM_CNT, (7 + len)); setUShort(ptr, T2_SPRM_CNT, (7 + len)); setUShort(ptr, T2_SPRM_OFS, 68); setUShort(ptr, T2_BYTE_CNT, (pos - bpos)); handle->message.msg = NBT_SESSISON_MSG; handle->message.length = htons(pos); pos += 4; ret = smb_send(handle->sck_server, (char*) &handle->message, pos); if(ret<0) goto failed; ret = SMB_ERROR; if (SMBCheck(SMB_TRANS2, handle) == SMB_SUCCESS) { ptr = handle->message.smb; /*** Get parameter offset ***/ pos = getUShort(ptr, (SMB_HEADER_SIZE + 9)); pos += 4; // padding sdir->ctime = getULongLong(ptr, pos) - handle->session.timeOffset; pos += 8; // ULONGLONG - creation time sdir->atime = getULongLong(ptr, pos) - handle->session.timeOffset; pos += 8; // ULONGLONG - access time sdir->mtime = getULongLong(ptr, pos) - handle->session.timeOffset; pos += 8; // ULONGLONG - write time pos += 8; // ULONGLONG - change time sdir->attributes = getUInt(ptr,pos); pos += 4; // ULONG - file attributes pos += 4; // padding pos += 8; // ULONGLONG - allocated file size sdir->size = getULongLong(ptr, pos); pos += 8; // ULONGLONG - file size pos += 4; // ULONG NumberOfLinks; pos += 2; // UCHAR DeletePending; pos += 2; // UCHAR Directory; pos += 2; // USHORT Pad2; // for alignment only pos += 4; // ULONG EaSize; pos += 4; // ULONG FileNameLength; strcpy(sdir->name,realfile); ret = SMB_SUCCESS; } return ret; failed: handle->conn_valid = false; return ret; } /** * SMB_FindFirst * * Uses TRANS2 to support long filenames */ s32 SMB_FindFirst(const char *filename, unsigned short flags, SMBDIRENTRY *sdir, SMBCONN smbhndl) { u8 *ptr; s32 pos; s32 ret; s32 bpos; unsigned int len; SMBHANDLE *handle; SMBSESSION *sess; if(filename == NULL) return SMB_ERROR; if(SMB_Reconnect(&smbhndl,true)!=SMB_SUCCESS) return SMB_ERROR; handle = __smb_handle_open(smbhndl); if(!handle) return SMB_ERROR; sess = &handle->session; MakeSMBHeader(SMB_TRANS2,CIFS_FLAGS1,handle->unicode?CIFS_FLAGS2_UNICODE:CIFS_FLAGS2,handle); MakeTRANS2Header(SMB_FIND_FIRST2,handle); ptr = handle->message.smb; bpos = pos = (T2_BYTE_CNT+2); pos += 3; /*** Padding ***/ setUShort(ptr, pos, flags); pos += 2; /*** Flags ***/ setUShort(ptr, pos, 1); pos += 2; /*** Count ***/ setUShort(ptr, pos, 6); pos += 2; /*** Internal Flags ***/ setUShort(ptr, pos, 260); // SMB_FIND_FILE_BOTH_DIRECTORY_INFO pos += 2; /*** Level of Interest ***/ pos += 4; /*** Storage Type == 0 ***/ if(handle->unicode) { len = utf8_to_utf16((char*)&ptr[pos], (char*)filename,SMB_MAXPATH-2); pos += len+2; len++; } else { len = strlen(filename); memcpy(&ptr[pos],filename,len); pos += len+1; } /*** Update counts ***/ setUShort(ptr, T2_PRM_CNT, (13+len)); setUShort(ptr, T2_SPRM_CNT, (13+len)); setUShort(ptr, T2_SPRM_OFS, 68); setUShort(ptr, T2_BYTE_CNT,(pos-bpos)); handle->message.msg = NBT_SESSISON_MSG; handle->message.length = htons(pos); pos += 4; ret = smb_send(handle->sck_server,(char*)&handle->message,pos); if(ret<0) goto failed; sess->eos = 1; sess->count = 0; ret = SMB_ERROR; if(SMBCheck(SMB_TRANS2,handle)==SMB_SUCCESS) { ptr = handle->message.smb; /*** Get parameter offset ***/ pos = getUShort(ptr,(SMB_HEADER_SIZE+9)); sdir->sid = getUShort(ptr, pos); pos += 2; sess->count = getUShort(ptr, pos); pos += 2; sess->eos = getUShort(ptr, pos); pos += 2; pos += 2; // USHORT EaErrorOffset; pos += 2; // USHORT LastNameOffset; pos += 2; // padding? if (sess->count) { pos += 4; // ULONG NextEntryOffset; pos += 4; // ULONG FileIndex; sdir->ctime = getULongLong(ptr, pos) - handle->session.timeOffset; pos += 8; // ULONGLONG - creation time sdir->atime = getULongLong(ptr, pos) - handle->session.timeOffset; pos += 8; // ULONGLONG - access time sdir->mtime = getULongLong(ptr, pos) - handle->session.timeOffset; pos += 8; // ULONGLONG - write time pos += 8; // ULONGLONG - change time low sdir->size = getULongLong(ptr, pos); pos += 8; pos += 8; sdir->attributes = getUInt(ptr, pos); pos += 4; len=getUInt(ptr, pos); pos += 34; if(handle->unicode) utf16_to_utf8(sdir->name,(char*)&ptr[pos],len); else strcpy(sdir->name,(const char*)&ptr[pos]); ret = SMB_SUCCESS; } } return ret; failed: handle->conn_valid = false; return ret; } /** * SMB_FindNext */ s32 SMB_FindNext(SMBDIRENTRY *sdir,SMBCONN smbhndl) { u8 *ptr; s32 pos; s32 ret; s32 bpos; SMBHANDLE *handle; SMBSESSION *sess; handle = __smb_handle_open(smbhndl); if(!handle) return SMB_ERROR; sess = &handle->session; if(sess->eos || sdir->sid==0) return SMB_ERROR; MakeSMBHeader(SMB_TRANS2,CIFS_FLAGS1,handle->unicode?CIFS_FLAGS2_UNICODE:CIFS_FLAGS2,handle); MakeTRANS2Header(SMB_FIND_NEXT2,handle); bpos = pos = (T2_BYTE_CNT+2); pos += 3; /*** Padding ***/ ptr = handle->message.smb; setUShort(ptr, pos, sdir->sid); pos += 2; /*** Search ID ***/ setUShort(ptr, pos, 1); pos += 2; /*** Count ***/ setUShort(ptr, pos, 260); // SMB_FIND_FILE_BOTH_DIRECTORY_INFO pos += 2; /*** Level of Interest ***/ pos += 4; /*** Storage Type == 0 ***/ setUShort(ptr, pos, 12); pos+=2; /*** Search flags ***/ pos++; int pad=0; if(handle->unicode)pad=1; pos+=pad; /*** Update counts ***/ setUShort(ptr, T2_PRM_CNT, 13+pad); setUShort(ptr, T2_SPRM_CNT, 13+pad); setUShort(ptr, T2_SPRM_OFS, 68); setUShort(ptr, T2_BYTE_CNT, (pos-bpos)); handle->message.msg = NBT_SESSISON_MSG; handle->message.length = htons(pos); pos += 4; ret = smb_send(handle->sck_server,(char*)&handle->message,pos); if(ret<0) goto failed; ret = SMB_ERROR; if (SMBCheck(SMB_TRANS2,handle)==SMB_SUCCESS) { ptr = handle->message.smb; /*** Get parameter offset ***/ pos = getUShort(ptr,(SMB_HEADER_SIZE+9)); sess->count = getUShort(ptr, pos); pos += 2; sess->eos = getUShort(ptr, pos); pos += 2; pos += 2; // USHORT EaErrorOffset; pos += 2; // USHORT LastNameOffset; if (sess->count) { int len; pos += 4; // ULONG NextEntryOffset; pos += 4; // ULONG FileIndex; sdir->ctime = getULongLong(ptr, pos) - handle->session.timeOffset; pos += 8; // ULONGLONG - creation time sdir->atime = getULongLong(ptr, pos) - handle->session.timeOffset; pos += 8; // ULONGLONG - access time sdir->mtime = getULongLong(ptr, pos) - handle->session.timeOffset; pos += 8; // ULONGLONG - write time pos += 8; // ULONGLONG - change time low sdir->size = getULongLong(ptr, pos); pos += 8; pos += 8; sdir->attributes = getUInt(ptr, pos); pos += 4; len=getUInt(ptr, pos); pos += 34; if(handle->unicode) utf16_to_utf8(sdir->name, (char*)&ptr[pos],len); else strcpy (sdir->name, (const char*)&ptr[pos]); ret = SMB_SUCCESS; } } return ret; failed: handle->conn_valid = false; return ret; } /** * SMB_FindClose */ s32 SMB_FindClose(SMBDIRENTRY *sdir,SMBCONN smbhndl) { u8 *ptr; s32 pos; s32 ret; SMBHANDLE *handle; handle = __smb_handle_open(smbhndl); if(!handle) return SMB_ERROR; if(sdir->sid==0) return SMB_ERROR; MakeSMBHeader(SMB_FIND_CLOSE2,CIFS_FLAGS1,handle->unicode?CIFS_FLAGS2_UNICODE:CIFS_FLAGS2,handle); pos = SMB_HEADER_SIZE; ptr = handle->message.smb; setUChar(ptr, pos, 1); pos++; /*** Word Count ***/ setUShort(ptr, pos, sdir->sid); pos += 2; pos += 2; /*** Byte Count ***/ handle->message.msg = NBT_SESSISON_MSG; handle->message.length = htons(pos); pos += 4; ret = smb_send(handle->sck_server,(char*)&handle->message,pos); if(ret<0) goto failed; ret = SMBCheck(SMB_FIND_CLOSE2,handle); return ret; failed: handle->conn_valid = false; return ret; }