#define trace /* * * client library for remote checksum server * * functions for connecting to the server pipe, and reading * and writing messages. * * !! DEBUG HINT: !! * * - ENABLE Trace_Stat and Trace_Fil about 30 lines below here * so that they generate file output. * F11 from Windiff will do the trick on a debug build version! * * expects Trace_Error() to be defined in the client program. */ #include #include #include #include #include #include #include "..\server\sumserve.h" #include "..\windiff\windiff.h" // for TRACE_ERROR and Windiff_UI which it calls #include "ssclient.h" /* need to sort out header files !!! */ void SetNames(LPSTR names); /* from Windiff !!! */ void SetStatus(LPSTR cmd); ULONG ss_checksum_block(PSTR block, int size); DWORD WINAPI ReceiveFiles(LPVOID handle); #ifdef SOCKETS BOOL GetFile(SOCKET hpipe, PSTR localpath, PSSNEWRESP presp); #else BOOL GetFile(HANDLE hpipe, PSTR localpath, PSSNEWRESP presp); #endif HANDLE ConnectPipe(PSTR pipename); extern BOOL bTrace; /* in Windiff.c */ int CountRetries = 5; /* SOCKETS / NAMED PIPES macros */ #ifdef SOCKETS #define MAYBESOCKETTYPE SOCKET #define CLOSEHANDLE( handle ) closesocket( handle ) #else #define MAYBESOCKETTYPE HANDLE #define CLOSEHANDLE( handle ) CloseHandle( handle ) #endif /*--------------------------- DEBUG FUNCTIONS ----------------------------*/ void Trace_Stat(LPSTR str) { if (bTrace) { Trace_File(str); Trace_File("\n"); } Trace_Status(str); }/* Trace_Stat */ void Trace_Fil(LPSTR str) { if (bTrace) { Trace_File(str); } } /* Trace_Fil */ /*------------------------------------------------------------------------*/ static char MainPipeName[400]; /* pipe name for requests to server */ extern BOOL bAbort; /* abort flag from Windiff */ /* set up pipename for main pipe */ void InitPipeName(PSTR result, PSTR server, PSTR name) { sprintf(result, "\\\\%s\\pipe\\%s", server, name); } /* InitPipeName */ /* ss_connect: * make a connection to the server. * * create the correct pipe name \\server\pipe\NPNAME, * connect to the pipe and set the pipe to message mode. * * return INVALID_HANDLE_VALUE if failure */ HANDLE ss_connect(PSTR server) { char pipename[400]; InitPipeName(pipename, server, NPNAME); return ConnectPipe(pipename); } /* ss_connect */ VOID ss_setretries(int retries) { CountRetries = retries; } /* ss_connect: * make a connection to the pipe named. * * connect to the pipe and set the pipe to message mode. * * return INVALID_HANDLE_VALUE if failure */ HANDLE ConnectPipe(PSTR pipename) { HANDLE hpipe; DWORD dwState; int i; BOOL haderror = FALSE; { char msg[400]; wsprintf(msg, "ConnectPipe to %s\n", pipename); Trace_Fil(msg); } for (; ; ){ /* repeat if user asks */ int MsgBoxId; /* repeat connect attempt up to 5 times without asking. */ for (i= 0; i < CountRetries; i++) { if (bAbort) return INVALID_HANDLE_VALUE; /* connect to the named pipe */ hpipe = CreateFile(pipename, GENERIC_READ|GENERIC_WRITE, FILE_SHARE_READ|FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0); if (hpipe != INVALID_HANDLE_VALUE) { /* switch the pipe to message mode */ dwState = PIPE_WAIT | PIPE_READMODE_MESSAGE; SetNamedPipeHandleState(hpipe, &dwState, NULL, NULL); if (haderror) { Trace_Stat("connection ok"); } { char msg[80]; wsprintf(msg, "ConnectedPipe hpipe %x\n", HandleToUlong(hpipe)); Trace_Fil(msg); } return(hpipe); } else { DWORD errorcode = GetLastError(); char msg[400]; wsprintf(msg, "Error %d on Create Pipe %s", errorcode, pipename); Trace_Stat(msg); } /* connect failed - wait one seconds before retrying */ if (CountRetries > 1) { Sleep(1000); } /* * only report success with Trace_Stat if we also * reported an error (don't disturb the user * unnecessarily - he just see nothing unusual if all goes well */ haderror = TRUE; Trace_Stat("Retrying pipe connection..."); } /* retry 5 loop */ if (CountRetries > 1) { windiff_UI(TRUE); MsgBoxId = MessageBox( hwndClient , "Pipe connection failed 5 times. Retry some more?" , "Windiff: Network connection error" , MB_RETRYCANCEL ); windiff_UI(FALSE); if (MsgBoxId != IDRETRY) break; } else { break; } } /* ask loop */ Trace_Fil("ConnectPipe failed"); return(INVALID_HANDLE_VALUE); } /* ConnectPipe */ /* build and send a request message to the server. Check for network * errors, and retry (unless the pipe is broken) up to 10 times. * * if write succeeds - return TRUE. * if failure - return FALSE to indicate connection is dropped. */ BOOL ss_sendrequest(HANDLE hpipe, long lCode, PSTR szPath, int lenpath, DWORD dwFlags) { SSNEWREQ req; int size, count, errorcode; Trace_Fil("ss_sendrequest\n"); req.lCode = -lCode; /* negative code for versions greater than 0 */ req.lVersion = SS_VERSION; req.lRequest = LREQUEST; req.lFlags = dwFlags; if (szPath != NULL) { /* szPath may be more than one null-term string, * so copy the bytes rather than a strcpy(). */ for (size = 0; size < lenpath; size++) { req.szPath[size] = szPath[size]; } } else { req.szPath[0] = '\0'; } /* trace stuff */ { char msg[80]; wsprintf(msg, "Sending request: %d on pipe %x\n", req.lCode, HandleToUlong(hpipe)); Trace_Fil(msg); } /* loop retrying the send until it goes ok */ for (count = 0; count < CountRetries; count++) { if (bAbort) { CloseHandle(hpipe); return FALSE; } #ifdef trace { char msg[80]; wsprintf(msg, "Actually sending on pipe %x... ", HandleToUlong(hpipe)); Trace_Fil(msg); } #endif if (WriteFile(hpipe, &req, sizeof(req), (LPDWORD)(&size), NULL)) { #ifdef trace { char msg[80]; wsprintf(msg, "Sent req %d OK, pipe %x\n", req.lCode, HandleToUlong(hpipe)); Trace_Fil(msg); } #endif /* no error reported - was everything written?*/ if (size != sizeof(req)) { /* write was NOT ok - report and retry */ if (!TRACE_ERROR("pipe write size differs... Retry?", TRUE)) { return(FALSE); } continue; } else { /* all ok */ char msg[80]; wsprintf(msg, "Request %d sent on %x\n", req.lCode, HandleToUlong(hpipe)); Trace_Fil(msg); return(TRUE); } } #ifdef trace { char msg[80]; wsprintf(msg, "!!Bad send pipe %x\n", HandleToUlong(hpipe)); Trace_Fil(msg); } #endif /* an error occurred */ switch( (errorcode = (int)GetLastError())) { case ERROR_NO_DATA: case ERROR_BROKEN_PIPE: /* pipe connection lost - forget it */ Trace_Stat("pipe broken on write"); return(FALSE); default: { char msg[120]; wsprintf(msg, "pipe write error %d on pipe %x. Retrying..." , errorcode, HandleToUlong(hpipe)); Trace_Stat(msg); } Sleep(count*1000); /* total sleep possible is 45 sec */ break; /* from switch, not loop */ } } /* retry count reached - abandon this attempt */ TRACE_ERROR("retry count reached on pipe write error.", FALSE); return(FALSE); } /* ss_sendrequest */ /* read a message from a pipe, allowing for network errors * * if error occurs, retry up to 10 times unless error code * indicates that pipe is broken - in which case, give up. * * return size read if all ok, -1 to mean the connection is broken, * abort this client, 0 to mean other error. */ int ss_getblock(HANDLE hpipe, PSTR block, int blocksize) { int count; int size; int errorcode; static BOOL PipeError = FALSE; char msg[200]; wsprintf(msg, "ss_getblock. hpipe=%x\n", HandleToUlong(hpipe)); Trace_Fil(msg); /* retry up to 10 times */ for (count = 0; count < CountRetries; count++ ) { if (bAbort) { CloseHandle(hpipe); return -1; } #ifdef trace { wsprintf(msg, "Actual receive pipe %x...", HandleToUlong(hpipe)); Trace_Fil(msg); } #endif if (ReadFile(hpipe, block, blocksize, (LPDWORD)(&size), NULL)) { #ifdef trace { wsprintf(msg, "Good receive pipe %x\n", HandleToUlong(hpipe)); Trace_Fil(msg); } #endif /* check size of message */ if (size == 0) { Trace_Fil("zero length message\r\n"); continue; } /* everything ok */ { SSNEWRESP * ssp; ssp = (PSSNEWRESP) block; wsprintf( msg, "ss_getblock got block OK pipe %x: %x %x %x %x %x\n" , HandleToUlong(hpipe) , ssp->lVersion, ssp->lResponse, ssp->lCode, ssp->ulSize , ssp->fileattribs ); Trace_Fil ( msg ); } if (PipeError) { PipeError = FALSE; SetStatus("Pipe recovered"); } return size; } #ifdef trace { wsprintf(msg, "!!Bad receive pipe %x\n", HandleToUlong(hpipe)); Trace_Fil(msg); } #endif /* error occurred - check code */ switch((errorcode = (int)GetLastError())) { case ERROR_BROKEN_PIPE: /* connection broken. no point in retrying */ { wsprintf( msg, "pipe %x broken on read.", HandleToUlong(hpipe)); TRACE_ERROR(msg, FALSE); } return(-1); case ERROR_MORE_DATA: /* the message sent is larger than our buffer. * this is an internal error - report it and carry on */ { SSNEWRESP * ssp; wsprintf( msg, "message too large on pipe %x blocksize=%d data=" , HandleToUlong(hpipe), blocksize ); Trace_Fil(msg); ssp = (PSSNEWRESP) block; wsprintf( msg, "%8x %8x %8x %8x %8x\n" , ssp->lVersion, ssp->lResponse, ssp->lCode, ssp->ulSize , ssp->fileattribs ); Trace_Fil(msg); } /* Too low a level for message to user. Recoverable at higher level ** TRACE_ERROR("internal error- message too large", FALSE); */ return -2; default: { wsprintf(msg, "read error %d on pipe %x", errorcode, HandleToUlong(hpipe)); Trace_Stat(msg); } Sleep(count*1000); break; } } SetStatus("Pipe error"); PipeError = TRUE; TRACE_ERROR("retry count reached on pipe read error.", FALSE); return 0; } /* ss_getblock */ /* * read a standard response from the net, retrying if necessary. return * size if ok or <=0 if not. -1 means pipe broken. */ int ss_getresponse(HANDLE hpipe, PSSNEWRESP presp) { Trace_Fil("ss_getresponse\n"); return(ss_getblock(hpipe, (PSTR) presp, sizeof(SSNEWRESP))); } /* ss_getresponse */ /* * terminate the connection to the server. send an END message and * close the pipe */ void ss_terminate(HANDLE hpipe) { Trace_Fil("ss_terminate\n"); ss_sendrequest(hpipe, SSREQ_END, NULL, 0,0); CloseHandle(hpipe); } /* ss_terminate */ /* send a unc & password request. the password and the server strings * are both held in the buffer as two consecutive null-terminated strings */ BOOL ss_sendunc(HANDLE hpipe, PSTR password, PSTR server) { char buffer[MAX_PATH] = {0}; char * cp; int len; Trace_Fil("ss_sendunc\n"); strncat(buffer, password, sizeof(buffer)-1); cp = &buffer[strlen(buffer) + 1]; strcpy(cp,server); len = (int)((cp - buffer) + strlen(cp) + 1); return(ss_sendrequest(hpipe, SSREQ_UNC, buffer, len, 0)); } /* * checksum a single file using the checksum server */ BOOL ss_checksum_remote( HANDLE hpipe, PSTR path , ULONG * psum, FILETIME * pft, LONG * pSize, DWORD *pAttr ) { SSNEWRESP resp; char msg[400]; *psum = 0; if (!ss_sendrequest(hpipe, SSREQ_SCAN, path, strlen(path)+1, 0)) { return(FALSE); } if (0>=ss_getresponse(hpipe, &resp)) { return(FALSE); } if (resp.lResponse != LRESPONSE) { return(FALSE); } switch(resp.lCode) { case SSRESP_END: TRACE_ERROR("No remote files found", FALSE); return(FALSE); case SSRESP_ERROR: if (resp.ulSize!=0) { wsprintf( msg, "Checksum server could not read %s win32 code %d" , resp.szFile, resp.ulSize ); } else wsprintf(msg, "Checksum server could not read %s", resp.szFile); TRACE_ERROR(msg, FALSE); return(FALSE); case SSRESP_CANTOPEN: wsprintf(msg, "Checksum server could not open %s", resp.szFile); TRACE_ERROR(msg, FALSE); return(FALSE); case SSRESP_FILE: *psum = resp.ulSum; *pSize = resp.ulSize; *pft = resp.ft_lastwrite; *pAttr = resp.fileattribs; /* read and discard any further packets until SSRESP_END */ while(0=ss_getresponse(hpipe, &resp)) { Trace_Fil("Server connection lost (2)\n"); TRACE_ERROR("Server connection lost", FALSE); continue; } if (resp.lResponse != LRESPONSE) { Trace_Fil("Password - bad response\n"); TRACE_ERROR("Password - bad response", FALSE); return(FALSE); } if (resp.lCode != SSRESP_END) { Trace_Fil("Password attempt failed\n"); TRACE_ERROR("Password attempt failed", FALSE); return(FALSE); } } break; } /* retry loop */ if (hpipe == INVALID_HANDLE_VALUE) { return FALSE; } #ifdef SOCKETS if( !SocketsInitialized ) { WSADATA WSAData; if( ( WSAStartup( MAKEWORD( 1, 1 ), &WSAData ) ) == 0 ) { SocketsInitialized = TRUE; } else { TRACE_ERROR("WSAStartup failed", FALSE); } } #endif /* Tell server we want to send a file list */ if(!ss_sendrequest( hpipe, SSREQ_FILES, NULL, 0, 0)){ return FALSE; } /* expect a reply which names a data pipe */ { if ( 0>=ss_getresponse(hpipe, &resp) ){ Trace_Fil("Couldn't get data pipe name\n"); TRACE_ERROR("Couldn't get data pipe name", FALSE); CloseHandle(hpipe); hpipe = INVALID_HANDLE_VALUE; return FALSE; } if ( resp.lResponse!=LRESPONSE){ Trace_Fil("Bad RESPONSE when expecting data pipe name\n"); TRACE_ERROR("Bad RESPONSE when expecting data pipe name", FALSE); CloseHandle(hpipe); hpipe = INVALID_HANDLE_VALUE; return FALSE; } if ( resp.lCode!=SSRESP_PIPENAME){ char msg[80]; TRACE_ERROR("Wrong response when expecting data pipe name", FALSE); wsprintf(msg ,"Wrong response (%d) when expecting data pipe name\n" , resp.lCode); Trace_Fil(msg); CloseHandle(hpipe); hpipe = INVALID_HANDLE_VALUE; return FALSE; } { char msg[400]; wsprintf(msg, "data pipe name = '%s'\n", resp.szFile); Trace_Fil(msg); } #ifdef SOCKETS /* We put the TCP port in the high date time slot just for now: */ if( SOCKET_ERROR == SocketConnect( server, (u_short)resp.ft_lastwrite.dwHighDateTime, &hpData ) ) { Trace_Fil("Couldn't connect to socket\n"); TRACE_ERROR("Couldn't connect to socket", FALSE); CLOSEHANDLE(hpData); hpData = (SOCKET)INVALID_HANDLE_VALUE; return FALSE; } #else if (resp.szFile[0]=='\\') { /* hack to fix bug. Replace "." by server name */ char buff[70]; PSTR temp; temp = strtok(resp.szFile,"."); temp = strtok(NULL,"."); strcpy(buff, temp); strcat(resp.szFile, server); strcat(resp.szFile, buff); wsprintf(buff, "fixed data pipe name = %s\n", resp.szFile); Trace_Fil(buff); } hpData = ConnectPipe(resp.szFile); if (hpData == INVALID_HANDLE_VALUE) { Trace_Fil("Couldn't connect to data pipe\n"); TRACE_ERROR("Couldn't connect to data pipe", FALSE); CloseHandle(hpData); hpData = INVALID_HANDLE_VALUE; return FALSE; } #endif /* SOCKETS */ } /* Start a thread to listen for the SSRESPs that will come through */ Trace_Fil("Starting ReceiveFiles thread\n"); hThread = CreateThread( NULL, 0, ReceiveFiles, (LPVOID)hpData, 0, &ThreadId); Trace_Fil("End of ss_startCopy\n"); return TRUE; } /* ss_startcopy */ /* Collect files from hpData. See ..\server\files.c for the protocol. */ DWORD WINAPI ReceiveFiles(LPVOID handle) { LPSTR lname; BOOL Recovering = FALSE; SetThreadPriority(GetCurrentThread(),THREAD_PRIORITY_HIGHEST); #ifdef SOCKETS hpData = (SOCKET)handle; #else hpData = (HANDLE)handle; #endif { char msg[80]; wsprintf(msg, "Receiving Files on pipe %x\n", PtrToLong(hpData)); Trace_Fil(msg); } /* for each file... */ for (; ; ) { /* loop getting files until we get END (or error) */ SSNEWRESP Resp; char Guard = '\0'; /* to protect scanning for \0 in Resp */ int BuffSize; #ifdef SOCKETS BuffSize = recv(hpData, (PSTR) &Resp, sizeof(Resp), 0); if (BuffSize==SOCKET_ERROR) { /* We're out of step - almost certainly got a data packet ** when we expected a response block ** Try to recover by reading until we DO get a resp. */ if (Resp.lResponse==LRESPONSE) { CLOSEHANDLE(hpData); /* tough */ hpData = (SOCKET)INVALID_HANDLE_VALUE; return (DWORD)(-1); } if (!Recovering){ Recovering = TRUE; TRACE_ERROR("Network protocol error. OK to Resync? (else abort)", TRUE); } continue; } #else BuffSize = ss_getblock(hpData, (PSTR) &Resp, sizeof(Resp)); if (BuffSize==-2) { /* We're out of step - almost certainly got a data packet ** when we expected a response block ** Try to recover by reading until we DO get a resp. */ if (Resp.lResponse==LRESPONSE) { CloseHandle(hpData); /* tough */ hpData = INVALID_HANDLE_VALUE; return (DWORD)(-1); } if (!Recovering){ Recovering = TRUE; TRACE_ERROR("Network protocol error. OK to Resync? (else abort)", TRUE); } continue; } #endif /* SOCKETS */ if (Recovering && Resp.lResponse!=LRESPONSE) continue; Recovering = FALSE; if (BuffSize<=0) { Trace_Fil("Couldn't read pipe to get file header.\n"); CLOSEHANDLE(hpData); /* tough */ hpData = (MAYBESOCKETTYPE)INVALID_HANDLE_VALUE; return BuffSize; } Trace_Fil("ReceiveFiles got resp\n"); if (Resp.lResponse!=LRESPONSE) { Trace_Fil("Network protocol error. Not RESP block\n"); TRACE_ERROR("Network protocol error. Not RESP block", FALSE); continue; } if (Resp.lVersion!=SS_VERSION) { Trace_Fil("Network protocol error. Bad VERSION\n"); TRACE_ERROR("Network protocol error. Bad VERSION", FALSE); continue; /* maybe it will resync */ } if (Resp.lCode==SSRESP_END) /* normal ending. */ break; if ( Resp.lCode!=SSRESP_FILE && Resp.lCode!=SSRESP_NOTEMPPATH && Resp.lCode!=SSRESP_COMPRESSEXCEPT && Resp.lCode!=SSRESP_NOREADCOMP && Resp.lCode!=SSRESP_NOCOMPRESS && Resp.lCode!=SSRESP_COMPRESSFAIL ) { /// want a try finally here to protect the filename???!!! char msg[400]; wsprintf( msg, "Error code received: %d file:%s" , Resp.lCode , (Resp.szFile ? Resp.szFile : "NULL") ); Trace_Fil(msg); ++nBadFiles; if (!TRACE_ERROR(msg, TRUE)) { /* abort operation */ bAbort = TRUE; CLOSEHANDLE(hpData); /* tough */ hpData = (MAYBESOCKETTYPE)INVALID_HANDLE_VALUE; return (DWORD)-1; } continue; } /* Find the local name */ lname = &(Resp.szFile[0]) + strlen(Resp.szFile) +1; /* memmove(Resp.szLocal, lname, strlen(lname)+1); */ /* Assume Resp.(ulSize, ft, ulSum, bSumValid, szFile, szLocal are all valid */ if (!GetFile( hpData, lname, &Resp)) ++nBadFiles; /* If it's good it gets counted when decompressed */ } /* files loop */ return 0; } /* ReceiveFiles */ /* Read the file from hpipe as a series of SSNEWPACKs. Write them into a temporary file. Stop writing when a short one comes in. Spawn a thread to decompress it into localpath. check existing decompress threads for status and count the number of good/bad files. */ BOOL #ifdef SOCKETS GetFile(SOCKET hpipe, PSTR localpath, PSSNEWRESP presp) #else GetFile(HANDLE hpipe, PSTR localpath, PSSNEWRESP presp) #endif { HANDLE hfile; int sequence; ULONG size; SSNEWPACK packet; BOOL bOK = TRUE; BOOL bOKFile = TRUE; /* FALSE means output file nbg, but keep running along to stay in step with the pipe protocol */ char szTempname[MAX_PATH]; DWORD rc; { char msg[50+MAX_PATH]; wsprintf(msg, "GetFile %s\n", localpath); Trace_Fil(msg); } /* create a temporary name */ rc = GetTempPath(sizeof(szTempname), szTempname); if (rc==0) { char Msg[100]; wsprintf(Msg, "GetTempPath failed, error code=%ld", GetLastError()); TRACE_ERROR(Msg, FALSE); bOKFile = FALSE; } if (bOKFile){ rc = GetTempFileName(szTempname, "ssb", 0, szTempname); if (rc==0) { char Msg[100]; wsprintf(Msg, "GetTempFileName failed, error code=%ld", GetLastError()); TRACE_ERROR(Msg, FALSE); return FALSE; } } if (bOKFile){ /* try to create the temp file */ hfile = CreateFile(szTempname, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); if (hfile == INVALID_HANDLE_VALUE) { char Msg[500]; wsprintf(Msg, "GetFile: could not create temp file %s", szTempname); TRACE_ERROR(Msg, FALSE); bOKFile = FALSE; } } else hfile = INVALID_HANDLE_VALUE; for (sequence = 1; ; sequence++) { int SizeGotten; #ifdef SOCKETS SizeGotten = recv(hpipe, (PSTR) &packet, sizeof(packet), 0); #else SizeGotten = ss_getblock(hpipe, (PSTR) &packet, sizeof(packet)); #endif if (SizeGotten<=0) { /* error - could be an Abort request */ char msg[80]; wsprintf( msg, "Network error. Size received=%d\n", SizeGotten); Trace_Fil(msg); bOK = FALSE; break; } if (packet.lPacket!=LPACKET) { { char msg[200]; wsprintf( msg , "Network protocol error. Not PACKET: %x %x %x %x %x" , packet.lVersion , packet.lPacket , packet.lSequence , packet.ulSize , packet.ulSum ); /* and maybe someone will recognise what on earth it is */ Trace_Fil(msg); } TRACE_ERROR("Network protocol error. Not PACKET.", FALSE); bOK = FALSE; break; } if (sequence != packet.lSequence) { /* error or out of sequence */ char msg[200]; wsprintf( msg, "Packet out of sequence. Got %d expected %d\n" , packet.lSequence, sequence); Trace_Fil(msg); TRACE_ERROR("Packet sequence error.", FALSE); bOK = FALSE; break; } if (packet.ulSize ==0) { /* * this is really the last block (end of file). * * LATER * do SSATTRIBS on the end to support NTFS properly !!! */ Trace_Fil("End of file marker (0 length)\n"); break; } #if 1 /* check the block checksums */ if ( packet.ulSum != 0 ) { TRACE_ERROR("packet checksum error", FALSE); bOK = FALSE; break; } #else // Debug version. (Remember to enable server checksums too) // Also ensure that Trace_Fil is actually tracing. { ULONG PackSum; /* check the block checksums */ if ( (PackSum = ss_checksum_block(packet.Data, packet.ulSize)) != packet.ulSum ) { char msg[80]; wsprintf( msg, "Packet checksum error was %x should be %x\n" , PackSum, packet.ulSum ); Trace_Fil(msg); // but don't break; } } #endif if ( packet.ulSize==(ULONG)(-1) || packet.ulSize==(ULONG)(-2) ) { TRACE_ERROR("Error from server end", FALSE); bOK = FALSE; break; } if (bOKFile) { bOK = WriteFile(hfile, packet.Data, packet.ulSize, &size, NULL); { char msg[80]; wsprintf( msg,"Writing block to disk - size= %d\n", size); Trace_Fil(msg); } if (!bOK || (size != packet.ulSize)) { TRACE_ERROR("File write error", FALSE); bOK = FALSE; break; } } else bOK = FALSE; if (packet.ulSize < PACKDATALENGTH) { /* this is the last block (end of file) */ Trace_Fil("End of file marker (short packet)\n"); break; } } /* for each block */ CloseHandle(hfile); if (!bOK) { DeleteFile(szTempname); return FALSE; } SpawnDecompress(szTempname, localpath, presp); PurgeDecomps(Decomps); return bOK; } /* GetFile */ /* Spawn a thread to decompress TempPath into RealPath on a new thread Add the thread handle to the LIST Decomps. If presp->lCode is one of SSRESP_NOTEMPPATH SSRESP_COMPRESSEXCEPT SSRESP_NOREADCOMP SSRESP_NOCOMPRESS Then the file should just be copied, not decompressed. */ void SpawnDecompress(PSTR TempPath, PSTR RealPath, PSSNEWRESP presp) { DECOMPARGS * DecompArgs; HANDLE L_hThread; DWORD ThreadId; { char msg[MAX_PATH+60]; wsprintf(msg, "Spawning decompress of %s", TempPath); Trace_Fil(msg); } DecompArgs = (DECOMPARGS *)GlobalAlloc(GMEM_FIXED, sizeof(DECOMPARGS)); if (DecompArgs) { DecompArgs->fileattribs = presp->fileattribs; DecompArgs->ft_create = presp->ft_create; DecompArgs->ft_lastaccess = presp->ft_lastaccess; DecompArgs->ft_lastwrite = presp->ft_lastwrite; DecompArgs->ulSum = presp->ulSum; DecompArgs->lCode = presp->lCode; DecompArgs->bSumValid = presp->bSumValid; strcpy(DecompArgs->Temp, TempPath); strcpy(DecompArgs->Real, RealPath); strcpy(DecompArgs->Remote, presp->szFile); L_hThread = CreateThread( NULL, 0, (LPTHREAD_START_ROUTINE)Decompress, DecompArgs, 0, &ThreadId); List_AddLast( Decomps, (LPVOID)&L_hThread, sizeof(L_hThread)); } else { // shut up Prefix. no idea what is the right thing to do here, // without spending a lot of time to understand how this whole // add-on to windiff works. in some other product this would be // worth investing time on. but not this add-on to windiff that // most people don't even know exists, much less how to set up // the server it needs to talk to. } } /* SpawnDecompress */ /* Decompress da->Temp into da->Real Free the storage da->DECOMPARGS Return 1 (TRUE) if it worked Return 0 (FALSE) if it didn't work This return code acts as the exit code for the thread. */ int Decompress(DECOMPARGS * da) { OFSTRUCT os; int fh1, fh2; BOOL bOK = TRUE; /* Decompress the file to the original name If da->lCode is one of SSRESP_NOTEMPPATH SSRESP_COMPRESSEXCEPT SSRESP_NOREADCOMP SSRESP_NOCOMPRESS Then the file is just copied, not decompressed. */ { char msg[2*MAX_PATH+50]; wsprintf( msg, "Decompressing %s => %s\n" , da->Temp, da->Real); Trace_Fil(msg); wsprintf( msg, "%d done. Getting %s\n" , nGoodFiles, da->Real); SetNames(msg); } if ( da->lCode==SSRESP_NOTEMPPATH || da->lCode==SSRESP_COMPRESSEXCEPT || da->lCode==SSRESP_NOREADCOMP || da->lCode==SSRESP_NOCOMPRESS ) { /* Just copy, don't compress */ bOK = CopyFile(da->Temp, da->Real, FALSE); if (bOK) Trace_Fil("Uncompressed file copied.\n"); else Trace_Fil("Uncompressed file failed final copy.\n"); } else { fh1 = LZOpenFile(da->Temp, &os, OF_READ|OF_SHARE_DENY_WRITE); if (fh1 == -1) { char msg[500]; wsprintf( msg, "Packed temp file %s did not open for decompression into %s. Error code %d" , da->Temp, da->Real, GetLastError() ); TRACE_ERROR(msg, FALSE); bOK = FALSE; } else { fh2 = LZOpenFile(da->Real, &os, OF_CREATE|OF_READWRITE|OF_SHARE_DENY_NONE); if (fh2 == -1) { char msg[MAX_PATH]; Trace_Fil("Could not create target file\n"); wsprintf(msg, "Could not create target file %s", da->Real); if (!TRACE_ERROR(msg, TRUE)) { /* user hit cancel - abort operation */ bAbort = TRUE; } bOK = FALSE; LZClose(fh1); } else { long retcode; retcode = LZCopy(fh1, fh2); if (retcode<0){ bOK = FALSE; } LZClose(fh1); LZClose(fh2); } } } /* May want to keep it for debugging...? */ #ifndef LAURIE DeleteFile(da->Temp); #endif //LAURIE if (bOK) { HANDLE hfile; BOOL bChecked; LONG err; /* * check the file's checksum (end-to-end check) and size and * set file attributes and times according to the attribs * struct we received. Remember to set file times BEFORE * setting attributes in case the attributes include read-only! */ bChecked = ( da->ulSum == checksum_file(da->Real, &err) ); if (err!=0) bChecked = FALSE; if (!bChecked){ if (!bAbort){ char msg[200]; if (err>0) { /* negative error codes are internal errors, positive ones are meaningful to outsiders. */ wsprintf( msg , "error %ld, Will retry file %s." , err , da->Real ); } else { #if defined(LAURIE) wsprintf( msg , "Checksum error %ld on file %s. Sum should be %8x, temp file %s" , err , da->Real , da->ulSum , da->Temp ); TRACE_ERROR(msg, FALSE); #endif wsprintf( msg , "Checksum error. Will retry file %s." , da->Real ); } SetNames(msg); List_AddLast(Retries, (LPVOID)da, (UINT)sizeof(DECOMPARGS)); return(FALSE); /* Hm - kind of a lie correct later */ } else return FALSE; } hfile = CreateFile(da->Real, GENERIC_WRITE, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); if (!SetFileTime(hfile, &(da->ft_create), &(da->ft_lastaccess), &(da->ft_lastwrite)) ) { TRACE_ERROR("could not set file times", FALSE); } CloseHandle(hfile); if (!SetFileAttributes(da->Real, da->fileattribs)) { TRACE_ERROR("could not set attributes", FALSE); } } GlobalFree((HGLOBAL)da); return bOK; } /* Decompress */ /* safe strcmp of PTR against array, protecting against NULL PTR b is an array and had better NOT be null a NULL pointer is taken to match an empty arrray. */ int safecmp(LPSTR a, LPSTR b) { if (a==NULL) return (b[0]!='\0'); /* returns 0 if ==, else 1 */ else return strcmp(a,b); } /* safecmp */ /* * request to copy a file. Sends the request on (static global) hpipe * * returns TRUE if it succeeded or FALSE if the connection was lost * TRUE only means the REQUEST was sent. */ BOOL ss_bulkcopy(PSTR server, PSTR serverpath, PSTR clientpath, PSTR uncname, PSTR password) { char buffer[MAX_PATH*2] = {0}; LPSTR pstr; ++nFiles; Trace_Fil("ss_bulkcopy\n"); if (safecmp(server,Server)) { char msg[400]; wsprintf( msg, "Protocol error. Server change was %s is %s" , Server, (server?server:"NULL") ); Trace_Fil(msg); TRACE_ERROR("Protocol error. Server change", FALSE); return FALSE; } if (safecmp(uncname,UNCName)) { char msg[400]; wsprintf( msg, "Protocol error. UNCName change was %s is %s" , UNCName, (uncname?uncname:"NULL") ); Trace_Fil(msg); TRACE_ERROR("Protocol error. UNCName change", FALSE); return FALSE; } if (safecmp(password,Password)) { char msg[400]; wsprintf( msg, "Protocol error. Password change was %s is %s" , Password, (password?password:"NULL") ); Trace_Fil(msg); TRACE_ERROR("Protocol error. Password change", FALSE); return FALSE; } /* pack local and remote paths into buffer */ _snprintf(buffer, sizeof(buffer)-1, "%s%c%s", serverpath, 0, clientpath); return ss_sendrequest( hpipe , SSREQ_NEXTFILE , buffer , strlen(serverpath)+strlen(clientpath)+2 , 0 ); } /* ss_bulkcopy */ /* Remove from the decompressers list those that have finished. Don't hang about waiting. Leave the rest on the list Updating nGoodFiles and/or nBadFiles for those that finished. */ void PurgeDecomps(LIST Decs) { HANDLE * phThread, * Temp; DWORD ExitCode; phThread = List_First(Decs); while (phThread!=NULL) { if (GetExitCodeThread(*phThread, &ExitCode)) { Temp = phThread; phThread = List_Next((LPVOID)phThread); if (ExitCode==1) { ++nGoodFiles; CloseHandle(*Temp); Trace_Fil("Purged a good decomp\n"); List_Delete((LPVOID)Temp); } else if(ExitCode==0) { ++nBadFiles; CloseHandle(*Temp); Trace_Fil("Purged a bad decomp\n"); List_Delete((LPVOID)Temp); } else /* still active? */ ; } } /* traverse */ } /* PurgeDecomps */ /* WAIT for each of the hThreads on Decomps and report its status by updating nGoodFiles and/or nBadFiles This thread must be run on the receive thread or else after the receive thread has terminated (or else we need a critical section). */ void WaitForDecomps(LIST Decs) { HANDLE * phThread; DWORD ExitCode; List_TRAVERSE(Decs, phThread) { if (bAbort) return; Trace_Fil("Waiting for a decomp..."); for (; ; ){ DWORD rc; rc = WaitForSingleObject(*phThread, 5000); if (rc==0) break; /* timeout complete */ if (bAbort) { // This WILL leave a garbage thread and a temp file lying around!!!??? Trace_Fil("Aborting wait for decomp."); return; } } Trace_Fil(" Done waiting.\n"); GetExitCodeThread(*phThread, &ExitCode); if (ExitCode==1) ++nGoodFiles; else ++nBadFiles; CloseHandle(*phThread); } /* traverse */ Trace_Fil("All decompression finished."); List_Destroy(&Decs); } /* WaitForDecomps */ static void Retry(LIST Rets) { DECOMPARGS * da; if (List_IsEmpty(Rets)) return; List_TRAVERSE(Rets, da) { if (ss_copy_reliable( Server, da->Remote, da->Real, UNCName, Password)) { /* correct the lie we told when Decompress returned FALSE */ ++nGoodFiles; --nBadFiles; } } List_Destroy(&Rets); SetNames("All errors recovered"); }/* Retry */ /* end of bulk copy. Tidy everything up */ int ss_endcopy(void) { Trace_Fil("ss_endcopy\n"); ss_sendrequest( hpipe, SSREQ_ENDFILES, "", 1, 0); /* wait for receiving thread to complete (could be long) */ for (; ; ){ DWORD rc; rc = WaitForSingleObject( hThread, 5000); if (rc==0) break; /* thread complete */ if (bAbort) { if (hpData != (MAYBESOCKETTYPE)INVALID_HANDLE_VALUE) { CLOSEHANDLE(hpData); hpData = (MAYBESOCKETTYPE)INVALID_HANDLE_VALUE; } break; } } // don't close the connection until we've finished, otherwise // someone might think it's ok to reboot the server ss_sendrequest( hpipe, SSREQ_END, "", 1, 0); CloseHandle(hpipe); hpipe = INVALID_HANDLE_VALUE; WaitForDecomps(Decomps); Decomps = NULL; SetNames(NULL); Retry(Retries); Retries = NULL; BulkCopy = FALSE; if (nBadFiles+nGoodFiles > nFiles) return -99999; /* !!? */ if (nBadFiles+nGoodFiles < nFiles) nBadFiles = nFiles-nGoodFiles; if (nBadFiles>0) return -nBadFiles; else return nGoodFiles; } /* ss_endcopy */ #if 0 /* A SSREQ_FILES has been sent and possibly one or more files, but we have changed our minds. We try to send an Abort request. */ int ss_abortcopy(void) { Trace_Fil("ss_abortcopy\n"); ss_sendrequest( hpipe, SSREQ_ABORT, "", 1); { DWORD code; TerminateThread(hThread, &code); /* storage leak */ hThread = INVALID_HANDLE_VALUE; } Server[0] = '\0'; CloseHandle(hpipe); hpipe = INVALID_HANDLE_VALUE; CLOSEHANDLE(hpData); hpData = INVALID_HANDLE_VALUE; if (Decomps!=NULL){ HANDLE * phThread; List_TRAVERSE(Decomps, phThread){ DWORD code; TerminateThread(*phThread, &code); /* storage leak */ } } Decomps = NULL; BulkCopy = FALSE; } /* ss_abortcopy */ #endif // 0 /* * reliably copy a file (repeat (upto N times) until checksums match) * * returns TRUE if it succeeded or FALSE if the connection was lost */ BOOL ss_copy_reliable(PSTR server, PSTR remotepath, PSTR localpath, PSTR uncname, PSTR password) { ULONG sum_local, sum_remote; int retry; SSNEWRESP resp; HANDLE hpCopy = INVALID_HANDLE_VALUE; /* N.B. NOT the static global pipe! */ LONG err; FILETIME ft; LONG sz; DWORD attr; Trace_Fil("ss_copy_reliable\n"); // if (BulkCopy) { // TRACE_ERROR("Cannot do simple copy as bulk copy is in progress", FALSE); // return FALSE; // } for (retry = 0; retry < 10; retry++) { if (bAbort) return FALSE; /* abort requested from WINDIFF */ if (hpCopy!=INVALID_HANDLE_VALUE) { CloseHandle(hpCopy); hpCopy = INVALID_HANDLE_VALUE; } { char pipename[400]; InitPipeName(pipename, server, NPNAME); hpCopy = ConnectPipe(pipename); } if (hpCopy == INVALID_HANDLE_VALUE) { /* next retry in two seconds */ Trace_Stat("connect failed - retrying"); Sleep(1000); continue; } if ((uncname != NULL) && (uncname[0]!='\0') && (password != NULL) && (password[0]!='\0')) { /* send the password request */ if (!ss_sendunc(hpCopy, password, uncname)) { TRACE_ERROR("Server connection lost", FALSE); continue; } /* wait for password response */ if (0>=ss_getresponse(hpCopy, &resp)) { TRACE_ERROR("Server connection lost", FALSE); continue; } if (resp.lCode != SSRESP_END) { TRACE_ERROR("Password attempt failed", FALSE); return(FALSE); } } /* try to copy the file */ ss_copy_file(hpCopy, remotepath, localpath); /* whether or not he thinks he failed, we should look * to see if the file is really there. */ sum_local = checksum_file(localpath, &err); if (err!=0) continue; sum_remote = 0; if (!ss_checksum_remote(hpCopy, remotepath, &sum_remote, &ft, &sz, &attr)) { /* no remote checksum - better retry */ if (!TRACE_ERROR("remote checksum failed - retry?", TRUE)) { CloseHandle(hpCopy); return(FALSE); } continue; } if (sum_local == sum_remote) { /* copy succeeded */ ss_terminate(hpCopy); return(TRUE); } TRACE_ERROR("files different after apparently successful copy!!?", FALSE); } /*retry loop */ /* too many retries */ CloseHandle(hpCopy); return(FALSE); } /* ss_copy_reliable */ /* * copy one file using checksum server * * send a SSREQ_FILE for the file, and then loop reading * blocks until we get an error (sequence count is wrong or -1), * or the end of file (0-length block) * * File sent may be compressed. We write it to a temporary file, and * then decompress it using LZCopy. This will work even if the * file was sent uncompressed (eg because compress.exe could not be * executed on the server). */ BOOL ss_copy_file(HANDLE hpipe, PSTR remotepath, PSTR localpath) { HANDLE hfile; int sequence; ULONG size; SSPACKET packet; BOOL bOK; char szTempname[MAX_PATH]; OFSTRUCT os; int fh1, fh2; PSSATTRIBS attribs; Trace_Fil("ss_copy_file\n"); /* create a temporary name */ *szTempname = 0; GetTempPath(sizeof(szTempname), szTempname); GetTempFileName(szTempname, "ssc", 0, szTempname); /* try to create the temp file */ hfile = CreateFile(szTempname, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); if (hfile == INVALID_HANDLE_VALUE) { TRACE_ERROR("ss_copy: could not create temp file", FALSE); return(FALSE); } if (!ss_sendrequest(hpipe, SSREQ_FILE, remotepath, strlen(remotepath)+1, 0)){ CloseHandle(hfile); return(FALSE); } for (sequence = 0; ; sequence++) { bOK = 0 <= ss_getblock(hpipe, (PSTR) &packet, sizeof(packet)); if (!bOK || (sequence != packet.lSequence)) { /* error or out of sequence */ TRACE_ERROR("packet error", FALSE); CloseHandle(hfile); DeleteFile(szTempname); return(FALSE); } if (packet.ulSize == 0) { /* * this is the last block (end of file). * * the data field for this block contains a * SSATTRIBS struct that we can use to set the * file times and attributes (after decompression). */ attribs = (PSSATTRIBS) packet.Data; break; } /* check the block checksums */ if ( packet.ulSum!=0 ) { TRACE_ERROR("packet checksum error", FALSE); CloseHandle(hfile); DeleteFile(szTempname); return(FALSE); } bOK = WriteFile(hfile, packet.Data, packet.ulSize, &size, NULL); if (!bOK || (size != packet.ulSize)) { CloseHandle(hfile); DeleteFile(szTempname); return(FALSE); } } CloseHandle(hfile); /* decompress the file to the original name */ fh1 = LZOpenFile(szTempname, &os, OF_READ|OF_SHARE_DENY_WRITE); if (fh1 < 0) { TRACE_ERROR("Failed to open file for decompression", FALSE); } else { fh2 = LZOpenFile(localpath, &os, OF_CREATE|OF_READWRITE|OF_SHARE_DENY_NONE); if (fh2 < 0) { char msg[MAX_PATH+40]; wsprintf(msg, "Could not create target file %s", localpath); if (!TRACE_ERROR(msg, TRUE)) { bAbort = TRUE; } return(FALSE); } else { LZCopy(fh1, fh2); LZClose(fh1); LZClose(fh2); } } DeleteFile(szTempname); /* * now set file attributes and times according to the attribs * struct we received. Remember to set file times BEFORE * setting attributes in case the attributes include read-only! */ hfile = CreateFile(localpath, GENERIC_WRITE, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); if (!SetFileTime(hfile, &attribs->ft_create, &attribs->ft_lastaccess, &attribs->ft_lastwrite) ) { TRACE_ERROR("could not set file times", FALSE); } CloseHandle(hfile); if (!SetFileAttributes(localpath, attribs->fileattribs)) { TRACE_ERROR("could not set attributes", FALSE); } return(TRUE); } /* ss_copy_file */ /* produce a checksum of a block of data. * * This algorithm is compute bound. It's probably overkill anyway and for * version 1 is not used. It must match the one in server. * * Generate checksum by the formula * checksum = SUM( rnd(i)*(1+byte[i]) ) * where byte[i] is the i-th byte in the file, counting from 1 * rnd(x) is a pseudo-random number generated from the seed x. * * Adding 1 to byte ensures that all null bytes contribute, rather than * being ignored. Multiplying each such byte by a pseudo-random * function of its position ensures that "anagrams" of each other come * to different sums. The pseudorandom function chosen is successive * powers of 1664525 modulo 2**32. 1664525 is a magic number taken * from Donald Knuth's "The Art Of Computer Programming" */ ULONG ss_checksum_block(PSTR block, int size) { unsigned long lCheckSum = 0; /* grows into the checksum */ const unsigned long lSeed = 1664525; /* seed for random Knuth */ unsigned long lRand = 1; /* seed**n */ unsigned long lIndex = 1; /* byte number in block */ unsigned Byte; /* next byte to process in buffer */ unsigned length; /* unsigned copy of size */ Trace_Fil("ss_checksum_block\n"); length = size; for (Byte = 0; Byte < length ;++Byte, ++lIndex) { lRand = lRand*lSeed; lCheckSum += lIndex*(1+block[Byte])*lRand; } return(lCheckSum); }