/// <summary> /// Used to initialize the file transfer sequence. /// <para>Sends a file request with the file id, name, and size.</para> /// </summary> /// /// <param name="FilePath">The full path to the file to send</param> public void SendFile(string FilePath) { // store file length long len = new FileInfo(FilePath).Length; // increment file id _fileCounter++; // get an open port int port = _clientSocket.NextOpenPort(); // create the file info header byte[] btInfo = new DtmFileInfo(Path.GetFileName(FilePath), len, port).ToBytes(); // create a new symmetric key KeyParams fileKey = GenerateSymmetricKey(_srvIdentity.Session); MemoryStream keyStrm = (MemoryStream)KeyParams.Serialize(fileKey); // add the key btInfo = ArrayUtils.Concat(btInfo, keyStrm.ToArray()); // wrap the request btInfo = WrapMessage(btInfo, _dtmParameters.MaxMessageAppend, _dtmParameters.MaxMessagePrePend); // encrypt with master btInfo = SymmetricTransform(_srvSymProcessor, btInfo); // initialize the files unique crypto processor ICipherMode fileSymProcessor = SymmetricInit(_srvIdentity.Session, fileKey); // tune for parallel processing int blockSize = ((int)MessageBufferSize - DtmPacket.GetHeaderSize()) - ((int)MessageBufferSize - DtmPacket.GetHeaderSize()) % ((CTR)fileSymProcessor).ParallelMinimumSize; ((CTR)fileSymProcessor).ParallelBlockSize = blockSize; // build the file transfer instance DtmFileTransfer fileTransfer = new DtmFileTransfer(fileSymProcessor, _fileCounter, 1024, (int)FileBufferSize); fileTransfer.FileTransferred += new DtmFileTransfer.FileTransferredDelegate(OnFileSent); fileTransfer.ProgressPercent += new DtmFileTransfer.ProgressDelegate(OnFileSentProgress); // add to dictionary _transQueue.TryAdd(_fileCounter, fileTransfer); // send header to the remote host in a file request Transmit(DtmPacketTypes.Transfer, (short)DtmTransferFlags.Request, _fileCounter, new MemoryStream(btInfo)); // initiate with non-blocking listen fileTransfer.StartSend(_clientSocket.LocalAddress, port, FilePath); if (fileTransfer.IsConnected) { try { // start on a new thread Task socketTask = Task.Factory.StartNew(() => { fileTransfer.SendFile(); }); socketTask.Wait(10); } catch (AggregateException ae) { if (SessionError != null) SessionError(this, new DtmErrorEventArgs(ae.GetBaseException(), DtmErrorSeverity.Warning)); } } else { // remove from pending and dispose CloseTransfer(_fileCounter); // alert app DtmErrorEventArgs args = new DtmErrorEventArgs(new SocketException((int)SocketError.ConnectionAborted), DtmErrorSeverity.Connection); if (SessionError != null) SessionError(this, args); if (args.Cancel == true) Disconnect(); } }
/// <summary> /// Used Post-Exchange to setup a file transfer from the remote host /// </summary> /// /// <param name="PacketStream">The stream containing the file transfer request</param> private void ReceiveFile(Stream PacketStream) { // asynchronous transfer by sending a file key and info, and running the entire transfer on another socket.. if (!_isEstablished) throw new CryptoProcessingException("DtmKex:ReceiveFile", "The VPN has not been established!", new InvalidOperationException()); if (FileRequest == null) throw new CryptoProcessingException("DtmKex:ReceiveFile", "The FileRequest and FileReceived must be connected to perform a file transfer, read the documentation!", new InvalidOperationException()); if (FileReceived == null) throw new CryptoProcessingException("DtmKex:ReceiveFile", "The FileRequest and FileReceived must be connected to perform a file transfer, read the documentation!", new InvalidOperationException()); // get the header DtmPacket pktHdr = new DtmPacket(PacketStream); // read the packet byte[] enc = new byte[pktHdr.PayloadLength]; // get the encrypted data PacketStream.Read(enc, 0, enc.Length); // decrypt it using client crypto processor byte[] dec = SymmetricTransform(_cltSymProcessor, enc); // remove padding dec = UnwrapMessage(dec); MemoryStream pktStm = new MemoryStream(dec); // get file info header DtmFileInfo pktFi = new DtmFileInfo(pktStm); // get the key KeyParams fileKey = KeyParams.DeSerialize(pktStm); // forward request to app DtmFileRequestEventArgs args = new DtmFileRequestEventArgs(pktFi.FileName); FileRequest(this, args); // accept file or refuse and exit; app must send back a valid path or cancel; if cancel, send a refuse notice which will signal the end of the transfer, otherwise store file path and port if (args.Cancel || string.IsNullOrEmpty(args.FilePath) || args.FilePath.Equals(pktFi.FileName) || !Directory.Exists(Path.GetDirectoryName(args.FilePath))) { // send refuse and exit Transmit(DtmPacketTypes.Transfer, (short)DtmTransferFlags.Refused, pktHdr.OptionFlag); } else { // create the files crypto processor ICipherMode fileSymProcessor = SymmetricInit(_cltIdentity.Session, fileKey); // enable parallel decryption int blockSize = ((int)MessageBufferSize - DtmPacket.GetHeaderSize()) - ((int)MessageBufferSize - DtmPacket.GetHeaderSize()) % ((CTR)fileSymProcessor).ParallelMinimumSize; ((CTR)fileSymProcessor).ParallelBlockSize = blockSize; // init the file transfer host DtmFileTransfer fileTransfer = new DtmFileTransfer(fileSymProcessor, pktHdr.OptionFlag, 1024, (int)FileBufferSize); fileTransfer.FileTransferred += new DtmFileTransfer.FileTransferredDelegate(OnFileReceived); fileTransfer.ProgressPercent += new DtmFileTransfer.ProgressDelegate(OnFileReceivedProgress); // add to dictionary _transQueue.TryAdd(pktHdr.OptionFlag, fileTransfer); try { // start the transfer on a new thread Task socketTask = Task.Factory.StartNew(() => { fileTransfer.StartReceive(_clientSocket.RemoteAddress, (int)pktFi.OptionsFlag, args.FilePath); }); socketTask.Wait(10); } catch (AggregateException ae) { if (SessionError != null) SessionError(this, new DtmErrorEventArgs(ae.GetBaseException(), DtmErrorSeverity.Warning)); } } }
public void SendFile() { int bytesRead = 0; long len = new FileInfo(_filePath).Length; DtmFileInfo flHdr = new DtmFileInfo(_filePath, len, 0); int ckSize = _clientSocket.SendBufferSize - (flHdr.GetHeaderSize() + DtmPacket.GetHeaderSize()); byte[] inputBuffer = new byte[ckSize]; try { using (FileStream inStream = new FileStream(_filePath, FileMode.Open, FileAccess.Read, FileShare.Read)) { // loop through file while ((bytesRead = inStream.Read(inputBuffer, 0, ckSize)) > 0) { // wrap in a file info; option flag is used for payload length flHdr.OptionsFlag = bytesRead; byte[] hdrArr = flHdr.ToBytes(); // add data if (bytesRead == ckSize) hdrArr = ArrayUtils.Concat(hdrArr, inputBuffer); else hdrArr = ArrayUtils.Concat(hdrArr, ArrayUtils.GetRange(inputBuffer, 0, bytesRead)); // encrypt the header and data byte[] enc = SymmetricTransform(_fileSymProcessor, hdrArr); // send to the remote host Transmit(DtmPacketTypes.Transfer, (short)DtmTransferFlags.DataChunk, _fileId, new MemoryStream(enc)); // increment counter _bytesSent += bytesRead; // progress if (ProgressPercent != null) { double progress = 100.0 * (double)_bytesSent / inStream.Length; ProgressPercent(this, new System.ComponentModel.ProgressChangedEventArgs((int)progress, (object)inStream.Length)); } } } // notify app if (FileTransferred != null) FileTransferred(this, new DtmPacketEventArgs((short)DtmTransferFlags.Sent, _fileId)); } catch (Exception ex) { throw new CryptoFileTransferException("DtmFileTransfer:SendFile", "The file transfer did not complete!", ex); } finally { // flush SendFlush(); } }
private void Receive(Stream PacketStream) { // get the packet header DtmPacket prcPacket = new DtmPacket(PacketStream); // read the packet byte[] enc = new byte[prcPacket.PayloadLength]; // get the encrypted data PacketStream.Read(enc, 0, enc.Length); // decrypt it using file crypto processor byte[] dec = SymmetricTransform(_fileSymProcessor, enc); // get file info header DtmFileInfo pktFi = new DtmFileInfo(dec); // store file name and size string fileName = pktFi.FileName; long fileSize = pktFi.FileSize; long streamLen = 0; try { using (FileStream outStream = new FileStream(_tempPath, FileMode.Append, FileAccess.Write, FileShare.Read)) { // calculate offsets int hdrSize = pktFi.GetHeaderSize(); int len = dec.Length - hdrSize; // write to file outStream.Write(ArrayUtils.GetRange(dec, hdrSize, len), 0, len); // store length streamLen = outStream.Length; // progress if (ProgressPercent != null) { double progress = 100.0 * (double)pktFi.OptionsFlag / fileSize; ProgressPercent(this, new System.ComponentModel.ProgressChangedEventArgs((int)progress, (object)fileSize)); } } // transfer completed if (streamLen == fileSize) { // reset attributes File.SetAttributes(_tempPath, File.GetAttributes(_tempPath) & ~FileAttributes.Hidden); // rename the file File.Move(_tempPath, VTDev.Libraries.CEXEngine.Tools.FileTools.GetUniqueName(_filePath)); // notify app if (FileTransferred != null) FileTransferred(this, new DtmPacketEventArgs((short)DtmTransferFlags.Received, prcPacket.OptionFlag)); // flush and close ReceiveClose(); } } catch (Exception ex) { throw new CryptoFileTransferException("DtmFileTransfer:Receive", "The file transfer did not complete!", ex); } }
/// <summary> /// Serialize an DtmFileInfo structure /// </summary> /// /// <param name="FileInfo">A DtmFileInfo structure</param> /// /// <returns>A stream containing the DtmFileInfo data</returns> public static Stream Serialize(DtmFileInfo FileInfo) { return FileInfo.ToStream(); }