Example #1
0
        /// <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();
            }
        }
Example #2
0
        /// <summary>
        /// Sends a packet with increasing wait times. 
        /// <para>After 4 attempts fires a SessionError with optional cancellation token.</para>
        /// </summary>
        /// 
        /// <param name="PacketStream">The packet to send</param>
        private void Throttle(MemoryStream PacketStream)
        {
            int maxwait = 10;

            for (int i = 0; i < 4; i++)
            {
                try
                {
                    Wait(maxwait);
                    _clientSocket.SendAsync(PacketStream);

                    break;
                }
                catch (CryptoSocketException ce)
                {
                    SocketException se = ce.InnerException as SocketException;

                    if (se.SocketErrorCode == SocketError.WouldBlock ||
                        se.SocketErrorCode == SocketError.IOPending ||
                        se.SocketErrorCode == SocketError.NoBufferSpaceAvailable)
                    {
                        // buffer is full
                        maxwait *= 2;
                    }
                    else
                    {
                        // possible connection dropped, alert app
                        if (SessionError != null)
                        {
                            DtmErrorEventArgs args = new DtmErrorEventArgs(ce, DtmErrorSeverity.Warning);
                            SessionError(this, args);

                            if (args.Cancel == true)
                                Disconnect();
                        }
                    }
                }
            }

            // all attempts have failed
            if (maxwait > 160)
            {
                // possible connection dropped, alert app
                if (SessionError != null)
                {
                    DtmErrorEventArgs args = new DtmErrorEventArgs(new SocketException((int)SocketError.HostUnreachable), DtmErrorSeverity.DataLoss);
                    SessionError(this, args);

                    if (args.Cancel == true)
                        Disconnect();
                }
            }
        }
Example #3
0
        /// <summary>
        /// Frame and Transmit the packet to the remote client
        /// </summary>
        /// 
        /// <param name="PacketType">The packet class</param>
        /// <param name="PacketFlag">The packet message type flag</param>
        /// <param name="OptionFlag">The option flag</param>
        /// <param name="Payload">The packet payload flag</param>
        /// <param name="Blocking">Blocking or Async transmit</param>
        private void Transmit(DtmPacketTypes PacketType, short PacketFlag, long OptionFlag = 0, MemoryStream Payload = null, bool Blocking = false)
        {
            lock (_sendLock)
            {
                long pldLen = Payload == null ? 0 : Payload.Length;
                // create a new packet: packet flag, payload size, sequence, and state flag
                MemoryStream pktStm = new DtmPacket(PacketType, pldLen, _sndSequence, PacketFlag, OptionFlag).ToStream();

                // add payload
                if (Payload != null)
                {
                    // store total encrypted bytes sent
                    if (_isEstablished)
                        _bytesSent += Payload.Length;

                    // copy to output
                    pktStm.Seek(0, SeekOrigin.End);
                    Payload.WriteTo(pktStm);
                    pktStm.Seek(0, SeekOrigin.Begin);
                }

                // service requests are not buffered
                if (PacketType != DtmPacketTypes.Service)
                {
                    // store in the packet buffer
                    _sndBuffer.Push(_sndSequence, pktStm);
                }

                // increment send counter
                _sndSequence++;

                // transmit to remote client
                if (_clientSocket.IsConnected)
                {
                    if (Blocking)
                    {
                        try
                        {
                            _clientSocket.SendAsync(pktStm);
                        }
                        catch (CryptoSocketException ce)
                        {
                            SocketException se = ce.InnerException as SocketException;

                            if (se.SocketErrorCode == SocketError.WouldBlock ||
                                se.SocketErrorCode == SocketError.IOPending ||
                                se.SocketErrorCode == SocketError.NoBufferSpaceAvailable)
                            {
                                // buffer is full, slow down
                                Throttle(pktStm);
                            }
                            else if (se.SocketErrorCode != SocketError.Success)
                            {
                                // possible connection dropped, alert app
                                if (SessionError != null)
                                {
                                    DtmErrorEventArgs args = new DtmErrorEventArgs(ce, DtmErrorSeverity.Connection);
                                    SessionError(this, args);

                                    if (args.Cancel == true)
                                        Disconnect();
                                }
                            }
                        }
                    }
                    else
                    {
                        try
                        {
                            pktStm.WriteTo(_clientSocket.TcpStream);
                        }
                        catch (Exception ex)
                        {
                            // internal error, alert app
                            if (SessionError != null)
                            {
                                DtmErrorEventArgs args = new DtmErrorEventArgs(ex, DtmErrorSeverity.Critical);
                                SessionError(this, args);

                                if (args.Cancel == true)
                                    Disconnect();
                            }
                        }
                    }

                    // notify app
                    if (PacketSent != null)
                        PacketSent(this, new DtmPacketEventArgs((short)_exchangeState, pldLen));
                }
                else
                {
                    // possible connection dropped, alert app
                    if (SessionError != null)
                    {
                        DtmErrorEventArgs args = new DtmErrorEventArgs(new SocketException((int)SocketError.ConnectionReset), DtmErrorSeverity.Connection);
                        SessionError(this, args);

                        if (args.Cancel == true)
                            Disconnect();
                    }
                }
            }
        }
Example #4
0
        /// <summary>
        /// Used to process a resync response.
        /// <para>The remote host has sent the number of bytes encrypted as the OptionFlag in the DtmPacket.
        /// The resynchronization of the crypto stream involves first encrypting an equal sized array, 
        /// and then testing for validity by decrypting the payload and comparing it to the stored client id.
        /// If the Resync fails, the client Disconnects, notifies the application, and performs a teardown of the VPN.</para>
        /// </summary>
        /// 
        /// <param name="PacketStream">A resync packet</param>
        private void ProcessResync(MemoryStream PacketStream)
        {
            // get the header
            DtmPacket pktHdr = new DtmPacket(PacketStream);
            int len = (int)(pktHdr.OptionFlag - pktHdr.PayloadLength - _bytesReceived);

            if (len > 0)
            {
                byte[] pad = new byte[len];
                // sync the cipher stream
                SymmetricTransform(_cltSymProcessor, pad);
            }
            else if (len < 0)
            {
                // can't resync, alert user and disconnect
                DtmErrorEventArgs args = new DtmErrorEventArgs(new InvalidDataException("The data stream could not be resynced, connection aborted!"), DtmErrorSeverity.Critical);
                if (SessionError != null)
                    SessionError(this, args);

                Disconnect();
                return;
            }

            // read the packet
            byte[] data = new byte[pktHdr.PayloadLength];
            // get the encrypted data
            PacketStream.Read(data, 0, data.Length);
            // decrypt the payload
            byte[] id = SymmetricTransform(_cltSymProcessor, data);
            // remove random padding
            id = UnwrapMessage(id);

            // compare to stored id
            if (!ArrayUtils.AreEqual(id, _cltIdentity.Identity))
            {
                // resync failed, abort connection
                DtmErrorEventArgs args = new DtmErrorEventArgs(new InvalidDataException("The data stream could not be resynced, connection aborted!"), DtmErrorSeverity.Critical);
                if (SessionError != null)
                    SessionError(this, args);

                Disconnect();
                return;
            }
        }
Example #5
0
        /// <summary>
        /// Resend a packet to a host
        /// </summary>
        private void Resend(DtmPacket Packet)
        {
            if (_sndBuffer.Exists(Packet.Sequence))
            {
                _maxSendCounter++;

                // limit attack scope with session resend max
                if (_maxSendCounter > MaxResend)
                {
                    // let the app decide what to do next
                    DtmErrorEventArgs args = new DtmErrorEventArgs(new InvalidDataException("The stream has encountered data loss, attempting to resync.."), DtmErrorSeverity.DataLoss);
                    if (SessionError != null)
                        SessionError(this, args);

                    if (args.Cancel)
                    {
                        Disconnect();
                        return;
                    }
                }

                try
                {
                    MemoryStream pktStm = _sndBuffer.Peek(Packet.Sequence);
                    if (pktStm != null)
                    {
                        if (pktStm.Length > 0)
                            pktStm.WriteTo(_clientSocket.TcpStream);

                        _sndSequence++;
                    }
                }
                catch
                {
                    // packet lost, request a resync
                    Transmit(DtmPacketTypes.Service, (short)DtmServiceFlags.DataLost);
                }
            }
            else
            {
                // packet lost, request a resync
                Transmit(DtmPacketTypes.Service, (short)DtmServiceFlags.DataLost);
            }
        }
Example #6
0
        /// <summary>
        /// Process a message.
        /// <para>Use this method to process <see cref="DtmPacket"/> data sent to the server</para>
        /// </summary>
        private void Process(MemoryStream PacketStream)
        {
            try
            {
                // increment rcv sequence
                _rcvSequence++;
                // get the header
                DtmPacket pktHdr = new DtmPacket(PacketStream);
                PacketStream.Seek(0, SeekOrigin.Begin);

                switch (pktHdr.PacketType)
                {
                    // message stream
                    case DtmPacketTypes.Message:
                        {
                            // process message
                            switch ((DtmMessageFlags)pktHdr.PacketFlag)
                            {
                                case DtmMessageFlags.Transmission:
                                    {
                                        try
                                        {
                                            // received stream data
                                            ReceiveMessage(PacketStream);
                                        }
                                        catch (Exception)
                                        {
                                            // packet corrupted, request a retransmission and exit
                                            Transmit(DtmPacketTypes.Service, (short)DtmServiceFlags.Resend, pktHdr.Sequence);
                                            return;
                                        }

                                        // echo the packet to remove it from remote buffer
                                        Transmit(DtmPacketTypes.Service, (short)DtmServiceFlags.Echo, pktHdr.Sequence);
                                        break;
                                    }
                            }
                            break;
                        }
                    // service messages
                    case DtmPacketTypes.Service:
                        {
                            switch ((DtmServiceFlags)pktHdr.PacketFlag)
                            {
                                case DtmServiceFlags.KeepAlive:
                                    {
                                        // reset the keep alive counter
                                        _pulseCounter = 0;
                                        break;
                                    }
                                // process echo
                                case DtmServiceFlags.Echo:
                                    {
                                        // remove from buffer
                                        if (_sndBuffer.Exists(pktHdr.OptionFlag))
                                            _sndBuffer.Destroy(pktHdr.OptionFlag);

                                        break;
                                    }
                                case DtmServiceFlags.Resend:
                                    {
                                        // attempt resend, if not in buffer transmission, attempts a resync
                                        Resend(pktHdr);
                                        break;
                                    }
                                case DtmServiceFlags.DataLost:
                                    {
                                        // remote packet lost, try resync. note: if this happens often, increase buffer size in ctor + tcp
                                        MemoryStream pktData = CreateResync();
                                        _bytesSent += pktData.Length;
                                        Transmit(DtmPacketTypes.Service, (short)DtmServiceFlags.Resync, _bytesSent, pktData);
                                        break;
                                    }
                                case DtmServiceFlags.Resync:
                                    {
                                        // attempt to resync the crypto stream
                                        ProcessResync(PacketStream);
                                        break;
                                    }
                                case DtmServiceFlags.Refusal:
                                    {
                                        DtmErrorEventArgs args = new DtmErrorEventArgs(new ApplicationException("The session was refused by the remote host."), DtmErrorSeverity.Connection);
                                        if (SessionError != null)
                                            SessionError(this, args);

                                        if (args.Cancel)
                                            Disconnect();

                                        break;
                                    }
                                case DtmServiceFlags.Terminate:
                                    {
                                        // reserved
                                        DtmErrorEventArgs args = new DtmErrorEventArgs(new ApplicationException("The session was terminated by the remote host."), DtmErrorSeverity.Critical);
                                        if (SessionError != null)
                                            SessionError(this, args);

                                        Disconnect();
                                        break;
                                    }
                            }

                            break;
                        }
                    // file transfer
                    case DtmPacketTypes.Transfer:
                        {
                            switch ((DtmTransferFlags)pktHdr.PacketFlag)
                            {
                                case DtmTransferFlags.Request:
                                    {
                                        // received file transfer request
                                        ReceiveFile(PacketStream);
                                        break;
                                    }
                                case DtmTransferFlags.Refused:
                                    {
                                        // refused by remote
                                        DtmErrorEventArgs args = new DtmErrorEventArgs(new ApplicationException("The session was refused by the remote host."), DtmErrorSeverity.Connection);
                                        if (SessionError != null)
                                            SessionError(this, args);

                                        CloseTransfer(pktHdr.OptionFlag);
                                        break;
                                    }
                                case DtmTransferFlags.Received:
                                    {
                                        // refused by remote
                                        CloseTransfer(pktHdr.OptionFlag);
                                        break;
                                    }
                            }
                            break;
                        }
                    // key exchange
                    case DtmPacketTypes.Exchange:
                    {
                        // process message
                        switch ((DtmExchangeFlags)pktHdr.PacketFlag)
                        {
                            case DtmExchangeFlags.Connect:
                                {
                                    // received public id
                                    ProcessConnect(PacketStream);
                                    break;
                                }
                            case DtmExchangeFlags.Init:
                                {
                                    // received auth-stage params
                                    ProcessInit(PacketStream);
                                    break;
                                }
                            case DtmExchangeFlags.PreAuth:
                                {
                                    // received public key
                                    ProcessPreAuth(PacketStream);
                                    break;
                                }
                            case DtmExchangeFlags.AuthEx:
                                {
                                    // received symmetric key
                                    ProcessAuthEx(PacketStream);
                                    break;
                                }
                            case DtmExchangeFlags.Auth:
                                {
                                    // received private id
                                    ProcessAuth(PacketStream);
                                    break;
                                }
                            case DtmExchangeFlags.Sync:
                                {
                                    // received primary public key params
                                    ProcessSync(PacketStream);
                                    break;
                                }
                            case DtmExchangeFlags.Primary:
                                {
                                    // received primary public key
                                    ProcessPrimary(PacketStream);
                                    break;
                                }
                            case DtmExchangeFlags.PrimeEx:
                                {
                                    // received primary session key
                                    ProcessPrimeEx(PacketStream);
                                    break;
                                }
                            case DtmExchangeFlags.Established:
                                {
                                    // received ack established
                                    ProcessEstablish(PacketStream);
                                    break;
                                }
                        }

                        break;
                    }
                    default:
                    {
                        if (SessionError != null)
                            SessionError(this, new DtmErrorEventArgs(new CryptoProcessingException("DtmKex:Process", "The data transmission encountered an error!", new InvalidDataException()), DtmErrorSeverity.Critical));

                        break;
                    }
                }

                // notify app
                if (PacketReceived != null)
                    PacketReceived(this, new DtmPacketEventArgs(pktHdr.PacketFlag, pktHdr.PayloadLength));
            }
            catch (Exception ex)
            {
                if (SessionError != null)
                    SessionError(this, new DtmErrorEventArgs(new CryptoProcessingException("DtmKex:Process", "The data transmission encountered an error!", ex), DtmErrorSeverity.Critical));
            }
        }
Example #7
0
        /// <summary>
        /// The keep alive timer event handler
        /// </summary>
        private void OnTimerPulse(object sender, ElapsedEventArgs e)
        {
            _pulseCounter++;

            // default trigger is 30 seconds without a keep alive
            if (_pulseCounter > ConnectionTimeOut)
            {
                if (_autoReconnect)
                {
                    // attempt to reconnect
                    if (!Reconnect())
                    {
                        // connection unvailable
                        if (SessionError != null)
                        {
                            DtmErrorEventArgs args = new DtmErrorEventArgs(new SocketException((int)SocketError.ConnectionReset), DtmErrorSeverity.Critical);
                            SessionError(this, args);
                            Disconnect();
                        }
                    }
                    else
                    {
                        // resync the crypto stream
                        Transmit(DtmPacketTypes.Service, (short)DtmServiceFlags.DataLost);
                    }
                }
                else
                {
                    // possible connection dropped, alert app
                    if (SessionError != null)
                    {
                        DtmErrorEventArgs args = new DtmErrorEventArgs(new SocketException((int)SocketError.ConnectionReset), DtmErrorSeverity.Critical);
                        SessionError(this, args);

                        if (args.Cancel == true)
                            Disconnect();
                    }
                }
            }
            else
            {
                Transmit(DtmPacketTypes.Service, (short)DtmServiceFlags.KeepAlive);
            }
        }
Example #8
0
        /// <summary>
        /// Used to read a blocking message response
        /// </summary>
        private MemoryStream BlockingReceive()
        {
            MemoryStream pktStm = null;

            try
            {
                // get the header
                pktStm = _clientSocket.GetStreamData(DtmPacket.GetHeaderSize(), EXCHTIMEOUT);
                DtmPacket pktHdr = new DtmPacket(pktStm);

                // add the payload
                if (pktHdr.PayloadLength > 0)
                    _clientSocket.GetStreamData((int)pktHdr.PayloadLength, EXCHTIMEOUT).WriteTo(pktStm);

                pktStm.Seek(0, SeekOrigin.Begin);
            }
            catch (ObjectDisposedException)
            {
                // host is disconnected, notify app
                DtmErrorEventArgs args = new DtmErrorEventArgs(new SocketException((int)SocketError.HostDown), DtmErrorSeverity.Connection);
                if (SessionError != null)
                    SessionError(this, args);

                if (args.Cancel == true)
                    Disconnect();
            }

            if (pktStm == null || pktStm.Length == 0)
            {
                // exchange failed

                if (SessionError != null)
                    SessionError(this, new DtmErrorEventArgs(new SocketException((int)SocketError.HostUnreachable), DtmErrorSeverity.Critical));

                Disconnect();
            }

            return pktStm;
        }
Example #9
0
        private void Transmit(DtmPacketTypes PacketType, short PacketFlag, long OptionFlag = 0, MemoryStream Payload = null)
        {
            lock (_sndLock)
            {
                long pldLen = Payload == null ? 0 : Payload.Length;
                // create a new packet: packet flag, payload size, sequence, and state flag
                MemoryStream pktStm = new DtmPacket(PacketType, pldLen, _sndSequence, PacketFlag, OptionFlag).ToStream();

                // add payload
                if (Payload != null)
                {
                    // copy to output
                    pktStm.Seek(0, SeekOrigin.End);
                    Payload.WriteTo(pktStm);
                    pktStm.Seek(0, SeekOrigin.Begin);
                }

                // store in the file packet buffer
                _sndBuffer.Push(_sndSequence, pktStm);
                // increment file send counter
                _sndSequence++;

                // transmit to remote client
                if (_clientSocket.IsConnected)
                {
                    try
                    {
                        _clientSocket.SendAsync(pktStm);
                    }
                    catch (CryptoSocketException ce)
                    {
                        SocketException se = ce.InnerException as SocketException;

                        if (se.SocketErrorCode == SocketError.WouldBlock ||
                            se.SocketErrorCode == SocketError.IOPending ||
                            se.SocketErrorCode == SocketError.NoBufferSpaceAvailable)
                        {
                            // buffer is full, throttle down
                            Throttle(pktStm);
                        }
                        else
                        {
                            // possible connection dropped, alert app
                            if (SessionError != null)
                            {
                                DtmErrorEventArgs args = new DtmErrorEventArgs(ce, DtmErrorSeverity.Connection);
                                SessionError(this, args);
                            }
                        }
                    }
                    catch (Exception)
                    {

                    }

                    // notify app
                    if (PacketSent != null)
                        PacketSent(this, new DtmPacketEventArgs((short)DtmTransferFlags.DataChunk, pldLen));
                }
            }
        }
Example #10
0
 /// <summary>
 /// Fires when an error has occured; contains the exception and the errors operational severity
 /// </summary>
 private void OnSessionError(object owner, DtmErrorEventArgs args)
 {
     // in case window is closed; should call disconnect in a forms closing event
     if (_dtmServer.IsConnected)
         Console.WriteLine(CON_TITLE + "Severity:" + (DtmErrorSeverity)args.Severity + "Message: " + args.Message);
 }