gscifs/reference/libtinysmb/smb.c

2334 lines
54 KiB
C

/****************************************************************************
* 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 <asm.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <malloc.h>
#include <ctype.h>
#include <wchar.h>
#include <gccore.h>
#include <network.h>
#include <processor.h>
#include <lwp_threads.h>
#include <lwp_objmgr.h>
#include <ogc/lwp_watchdog.h>
#include <sys/statvfs.h>
#include <errno.h>
#include <fcntl.h>
#include <smb.h>
#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 <readlen> bytes
* if <readlen>==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;i<strlen(pwd);i++)
pwd[i] = toupper((int)pwd[i]);
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;
}
/*** 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;ret<strlen((const char*)path);ret++)
path[ret] = (char)toupper((int)path[ret]);
if(handle->unicode)
{
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;i<dialectc;i++) {
len = strlen(dialects[i])+1;
ptr[pos++] = '\x02';
memcpy(&ptr[pos],dialects[i],len);
pos += len;
bcnt += len+1;
}
/*** Update byte count ***/
setUShort(ptr,(SMB_HEADER_SIZE+1),bcnt);
/*** Set NBT information ***/
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;
/*** 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;
}