mirror of
https://github.com/cshepherd/gscifs.git
synced 2024-09-29 09:54:58 +00:00
2334 lines
54 KiB
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;
|
|
}
|