/// <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)); } } }