/* * Copyright (C) 2002 Mike McCormack * * CIFS implementation for WINE * * This is a WINE's implementation of the Common Internet File System * * for specification see: * * http://www.codefx.com/CIFS_Explained.htm * http://www.ubiqx.org/cifs/rfc-draft/rfc1002.html * http://www.ubiqx.org/cifs/rfc-draft/draft-leach-cifs-v1-spec-02.html * http://ubiqx.org/cifs/ * http://www.samba.org * * 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 "config.h" #include "wine/port.h" #include #include #include #include #include #include #include #ifdef HAVE_SYS_ERRNO_H #include #endif #include #include #ifdef HAVE_SYS_MMAN_H #include #endif #include #include #include #include #include #ifdef HAVE_SYS_SOCKET_H # include #endif #include #ifdef HAVE_NETINET_IN_SYSTM_H #include #endif #ifdef HAVE_NETINET_IN_H #include #endif #ifdef HAVE_NETINET_IP_H #include #endif #ifdef HAVE_ARPA_INET_H #include #endif #ifdef HAVE_NETDB_H #include #endif #include "winerror.h" #include "windef.h" #include "winbase.h" #include "file.h" #include "heap.h" #include "smb.h" #include "wine/server.h" #include "wine/debug.h" WINE_DEFAULT_DEBUG_CHANNEL(file); #define MAX_HOST_NAME 15 #define NB_TIMEOUT 10000 USHORT SMB_MultiplexId = 0; static int netbios_name(const char *p, unsigned char *buffer) { char ch; int i,len=0; buffer[len++]=' '; for(i=0; i<=MAX_HOST_NAME; i++) { if(i> 4) + 'A'; buffer[len++] = (ch&0x0f) + 'A'; } buffer[len++] = 0; /* add second terminator */ return len; } static DWORD NB_NameReq(LPCSTR host, unsigned char *buffer, int len) { int trn = 1234,i=0; NBR_ADDWORD(&buffer[i],trn); i+=2; NBR_ADDWORD(&buffer[i],0x0110); i+=2; NBR_ADDWORD(&buffer[i],0x0001); i+=2; NBR_ADDWORD(&buffer[i],0x0000); i+=2; NBR_ADDWORD(&buffer[i],0x0000); i+=2; NBR_ADDWORD(&buffer[i],0x0000); i+=2; i += netbios_name(host,&buffer[i]); NBR_ADDWORD(&buffer[i],0x0020); i+=2; NBR_ADDWORD(&buffer[i],0x0001); i+=2; ERR("packet is %d bytes in length\n",i); { int j; for(j=0; jsin_addr, &buffer[58], sizeof addr->sin_addr); close(fd); return TRUE; err: close(fd); return FALSE; } #define NB_FIRST 0x40 #define NB_HDRSIZE 4 #define NB_SESSION_MSG 0x00 #define NB_SESSION_REQ 0x81 /* RFC 1002, section 4.3.2 */ static BOOL NB_SessionReq(int fd, char *called, char *calling) { unsigned char buffer[0x100]; int len = 0,r; struct pollfd fds; ERR("called %s, calling %s\n",called,calling); buffer[0] = NB_SESSION_REQ; buffer[1] = NB_FIRST; netbios_name(called, &buffer[NB_HDRSIZE]); len += 34; netbios_name(calling, &buffer[NB_HDRSIZE+len]); len += 34; NBR_ADDWORD(&buffer[2],len); /* for(i=0; i<(len+NB_HDRSIZE); i++) DPRINTF("%02X%c",buffer[i],(((i+1)!=(len+4))&&((i+1)%16))?' ':'\n'); */ r = write(fd,buffer,len+4); if(r<0) { ERR("Write failed\n"); return FALSE; } fds.fd = fd; fds.events = POLLIN; fds.revents = 0; r = poll(&fds,1,NB_TIMEOUT); if(r!=1) { ERR("Poll failed\n"); return FALSE; } r = read(fd, buffer, NB_HDRSIZE); if((r!=NB_HDRSIZE) || (buffer[0]!=0x82)) { ERR("Received %d bytes\n",r); ERR("%02x %02x %02x %02x\n", buffer[0],buffer[1],buffer[2],buffer[3]); return FALSE; } return TRUE; } static BOOL NB_SendData(int fd, unsigned char *data, int size) { unsigned char buffer[NB_HDRSIZE]; int r; /* CHECK: is it always OK to do this in two writes? */ /* perhaps use scatter gather sendmsg instead? */ buffer[0] = NB_SESSION_MSG; buffer[1] = NB_FIRST; NBR_ADDWORD(&buffer[2],size); r = write(fd, buffer, NB_HDRSIZE); if(r!=NB_HDRSIZE) return FALSE; r = write(fd, data, size); if(r!=size) { ERR("write failed\n"); return FALSE; } return TRUE; } static BOOL NB_RecvData(int fd, unsigned char *data, int *outlen) { int r,len; unsigned char buffer[NB_HDRSIZE]; r = read(fd, buffer, NB_HDRSIZE); if((r!=NB_HDRSIZE) || (buffer[0]!=NB_SESSION_MSG)) { ERR("Received %d bytes\n",r); return FALSE; } len = NBR_GETWORD(&buffer[2]); r = read(fd, data, len); if(len!=r) { ERR("Received %d bytes\n",r); return FALSE; } *outlen = len; return TRUE; } static BOOL NB_Transaction(int fd, unsigned char *buffer, int len, int *outlen) { int r,i; struct pollfd fds; DPRINTF("Sending request:\n"); for(i=0; i len ) { ERR("Bad parameter count %d\n",pcount); return FALSE; } DPRINTF("SMB_COM_SESSION_SETUP response, %d args: ",pcount); for(i=0; i len ) { ERR("parameter count %x, buffer count %x, len %x\n",pcount,bcount,len); return FALSE; } DPRINTF("response buffer %d bytes: ",bcount); for(i=0; i len ) { ERR("Bad parameter count %d\n",pcount); return FALSE; } ERR("response, %d args: ",pcount); for(i=0; i len ) { HeapFree(GetProcessHeap(),0,buffer); ERR("Bad parameter count %d\n",n); return FALSE; } ERR("response, %d args: ",n); for(i=0; icount) n=count; memcpy( out, &SMB_BUFFER(buffer,3), n); ERR("Read %d bytes\n",n); *read = n; HeapFree(GetProcessHeap(),0,buffer); return TRUE; } static int SMB_GetSocket(LPCSTR host) { int fd=-1,r; struct sockaddr_in sin; struct hostent *he; ERR("host %s\n",host); if(NB_Lookup(host,&sin)) goto connect; he = gethostbyname(host); if(he) { memcpy(&sin.sin_addr,he->h_addr, sizeof (sin.sin_addr)); goto connect; } /* FIXME: resolve by WINS too */ ERR("couldn't resolve SMB host %s\n", host); return -1; connect: sin.sin_family = AF_INET; sin.sin_port = htons(139); /* netbios session */ fd = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP); if(fd<0) return fd; { unsigned char *x = (unsigned char *)&sin.sin_addr; ERR("Connecting to %d.%d.%d.%d ...\n", x[0],x[1],x[2],x[3]); } r = connect(fd, &sin, sizeof sin); if(!NB_SessionReq(fd, "*SMBSERVER", "WINE")) { close(fd); return -1; } return fd; } static BOOL SMB_LoginAndConnect(int fd, LPCSTR host, LPCSTR share, USHORT *tree_id, USHORT *user_id, USHORT *dialect) { LPSTR name=NULL; ERR("host %s share %s\n",host,share); if(!SMB_NegotiateProtocol(fd, dialect)) return FALSE; if(!SMB_SessionSetup(fd, user_id)) return FALSE; name = HeapAlloc(GetProcessHeap(),0,strlen(host)+strlen(share)+5); if(!name) return FALSE; sprintf(name,"\\\\%s\\%s",host,share); if(!SMB_TreeConnect(fd,*user_id,name,tree_id)) { HeapFree(GetProcessHeap(),0,name); return FALSE; } return TRUE; } static HANDLE SMB_RegisterFile( int fd, USHORT tree_id, USHORT user_id, USHORT dialect, USHORT file_id) { int r; HANDLE ret; wine_server_send_fd( fd ); SERVER_START_REQ( create_smb ) { req->tree_id = tree_id; req->user_id = user_id; req->file_id = file_id; req->dialect = 0; req->fd = fd; SetLastError(0); r = wine_server_call_err( req ); ret = reply->handle; } SERVER_END_REQ; if(!r) ERR("created wineserver smb object, handle = %04x\n",ret); else SetLastError( ERROR_PATH_NOT_FOUND ); return ret; } HANDLE WINAPI SMB_CreateFileA( LPCSTR uncname, DWORD access, DWORD sharing, LPSECURITY_ATTRIBUTES sa, DWORD creation, DWORD attributes, HANDLE template ) { int fd; USHORT tree_id=0, user_id=0, dialect=0, file_id=0; LPSTR name,host,share,file; HANDLE handle = INVALID_HANDLE_VALUE; name = HeapAlloc(GetProcessHeap(),0,lstrlenA(uncname)); if(!name) return handle; lstrcpyA(name,uncname); if( !UNC_SplitName(name, &host, &share, &file) ) { HeapFree(GetProcessHeap(),0,name); return handle; } ERR("server is %s, share is %s, file is %s\n", host, share, file); fd = SMB_GetSocket(host); if(fd < 0) goto done; if(!SMB_LoginAndConnect(fd, host, share, &tree_id, &user_id, &dialect)) goto done; #if 0 if(!SMB_NtCreateOpen(fd, tree_id, user_id, dialect, file, access, sharing, sa, creation, attributes, template, &file_id )) { close(fd); ERR("CreateOpen failed\n"); goto done; } #endif if(!SMB_Open(fd, tree_id, user_id, dialect, file, access, sharing, creation, attributes, &file_id )) { close(fd); ERR("CreateOpen failed\n"); goto done; } handle = SMB_RegisterFile(fd, tree_id, user_id, dialect, file_id); if(!handle) { ERR("register failed\n"); close(fd); } done: HeapFree(GetProcessHeap(),0,name); return handle; } static BOOL SMB_GetSmbInfo(HANDLE hFile, USHORT *tree_id, USHORT *user_id, USHORT *dialect, USHORT *file_id, LPDWORD offset) { int r; SERVER_START_REQ( get_smb_info ) { req->handle = hFile; req->flags = 0; SetLastError(0); r = wine_server_call_err( req ); if(tree_id) *tree_id = reply->tree_id; if(user_id) *user_id = reply->user_id; if(file_id) *file_id = reply->file_id; if(dialect) *dialect = reply->dialect; if(offset) *offset = reply->offset; } SERVER_END_REQ; return !r; } static BOOL SMB_SetOffset(HANDLE hFile, DWORD offset) { int r; ERR("offset = %08lx\n",offset); SERVER_START_REQ( get_smb_info ) { req->handle = hFile; req->flags = SMBINFO_SET_OFFSET; req->offset = offset; SetLastError(0); r = wine_server_call_err( req ); /* if(offset) *offset = reply->offset; */ } SERVER_END_REQ; return !r; } BOOL WINAPI SMB_ReadFile(HANDLE hFile, LPVOID buffer, DWORD bytesToRead, LPDWORD bytesRead, LPOVERLAPPED lpOverlapped) { int fd; DWORD total, count, offset; USHORT user_id, tree_id, dialect, file_id, read; BOOL r=TRUE; ERR("%04x %p %ld %p\n", hFile, buffer, bytesToRead, bytesRead); if(!SMB_GetSmbInfo(hFile, &tree_id, &user_id, &dialect, &file_id, &offset)) return FALSE; fd = FILE_GetUnixHandle(hFile, GENERIC_READ); if(fd<0) return FALSE; total = 0; while(1) { count = bytesToRead - total; if(count>0x400) count = 0x400; if(count==0) break; read = 0; r = SMB_Read(fd, tree_id, user_id, dialect, file_id, offset, buffer, count, &read); if(!r) break; if(!read) break; total += read; buffer += read; offset += read; if(total>=bytesToRead) break; } close(fd); if(bytesRead) *bytesRead = total; if(!SMB_SetOffset(hFile, offset)) return FALSE; return r; }