Esempio n. 1
0
        static void HandleConnection(System.IO.DirectoryInfo info, TcpClient client)
        {
            Area ws = null;
            ClientStateInfo clientInfo = new ClientStateInfo();
            using (client)
            using (SharedNetwork.SharedNetworkInfo sharedInfo = new SharedNetwork.SharedNetworkInfo())
            {
                try
                {
                    var stream = client.GetStream();
                    Handshake hs = ProtoBuf.Serializer.DeserializeWithLengthPrefix<Handshake>(stream, ProtoBuf.PrefixStyle.Fixed32);
                    DomainInfo domainInfo = null;
                    lock (SyncObject)
                    {
                        if (hs.RequestedModule == null)
                            hs.RequestedModule = string.Empty;
                        if (!Domains.TryGetValue(hs.RequestedModule, out domainInfo))
                        {
                            domainInfo = Domains.Where(x => x.Key.Equals(hs.RequestedModule, StringComparison.OrdinalIgnoreCase)).Select(x => x.Value).FirstOrDefault();
                        }
                        if (domainInfo == null)
                        {
                            if (!Config.AllowVaultCreation || !Config.RequiresAuthentication || string.IsNullOrEmpty(hs.RequestedModule) || System.IO.Directory.Exists(System.IO.Path.Combine(info.FullName, hs.RequestedModule)))
                            {
                                Network.StartTransaction startSequence = Network.StartTransaction.CreateRejection();
                                Printer.PrintDiagnostics("Rejecting client due to invalid domain: \"{0}\".", hs.RequestedModule);
                                ProtoBuf.Serializer.SerializeWithLengthPrefix<Network.StartTransaction>(stream, startSequence, ProtoBuf.PrefixStyle.Fixed32);
                                return;
                            }
                            domainInfo = new DomainInfo()
                            {
                                Bare = true,
                                Directory = null
                            };
                        }
                    }
                    try
                    {
                        ws = Area.Load(domainInfo.Directory, true, true);
                        if (domainInfo.Bare)
                            throw new Exception("Domain is bare, but workspace could be loaded!");
                    }
                    catch
                    {
                        if (!domainInfo.Bare)
                            throw new Exception("Domain not bare, but couldn't load workspace!");
                    }
                    Printer.PrintDiagnostics("Received handshake - protocol: {0}", hs.VersionrProtocol);
                    SharedNetwork.Protocol? clientProtocol = hs.CheckProtocol();
                    bool valid = true;
                    if (clientProtocol == null)
                        valid = false;
                    else
                    {
                        valid = SharedNetwork.AllowedProtocols.Contains(clientProtocol.Value);
                        if (Config.RequiresAuthentication && !SharedNetwork.SupportsAuthentication(clientProtocol.Value))
                            valid = false;
                    }
                    if (valid)
                    {
                        sharedInfo.CommunicationProtocol = clientProtocol.Value;
                        Network.StartTransaction startSequence = null;
                        clientInfo.Access = Rights.Read | Rights.Write;
                        clientInfo.BareAccessRequired = domainInfo.Bare;
                        if (PrivateKey != null)
                        {
                            startSequence = Network.StartTransaction.Create(domainInfo.Bare ? string.Empty : ws.Domain.ToString(), PublicKey, clientProtocol.Value);
                            Printer.PrintDiagnostics("Sending RSA key...");
                            ProtoBuf.Serializer.SerializeWithLengthPrefix<Network.StartTransaction>(stream, startSequence, ProtoBuf.PrefixStyle.Fixed32);
                            if (!HandleAuthentication(clientInfo, client, sharedInfo))
                                throw new Exception("Authentication failed.");
                            StartClientTransaction clientKey = ProtoBuf.Serializer.DeserializeWithLengthPrefix<StartClientTransaction>(stream, ProtoBuf.PrefixStyle.Fixed32);
                            System.Security.Cryptography.RSAOAEPKeyExchangeDeformatter exch = new System.Security.Cryptography.RSAOAEPKeyExchangeDeformatter(PrivateKey);
                            byte[] aesKey = exch.DecryptKeyExchange(clientKey.Key);
                            byte[] aesIV = exch.DecryptKeyExchange(clientKey.IV);
                            Printer.PrintDiagnostics("Got client key: {0}", System.Convert.ToBase64String(aesKey));

                            var aesCSP = System.Security.Cryptography.AesManaged.Create();

                            sharedInfo.DecryptorFunction = () => { return aesCSP.CreateDecryptor(aesKey, aesIV); };
                            sharedInfo.EncryptorFunction = () => { return aesCSP.CreateEncryptor(aesKey, aesIV); };
                        }
                        else
                        {
                            startSequence = Network.StartTransaction.Create(domainInfo.Bare ? string.Empty : ws.Domain.ToString(), clientProtocol.Value);
                            ProtoBuf.Serializer.SerializeWithLengthPrefix<Network.StartTransaction>(stream, startSequence, ProtoBuf.PrefixStyle.Fixed32);
                            if (!HandleAuthentication(clientInfo, client, sharedInfo))
                                throw new Exception("Authentication failed.");
                            StartClientTransaction clientKey = ProtoBuf.Serializer.DeserializeWithLengthPrefix<StartClientTransaction>(stream, ProtoBuf.PrefixStyle.Fixed32);
                        }
                        sharedInfo.Stream = stream;
                        sharedInfo.Workspace = ws;
                        sharedInfo.ChecksumType = Config.ChecksumType;

                        clientInfo.SharedInfo = sharedInfo;

                        while (true)
                        {
                            NetCommand command = ProtoBuf.Serializer.DeserializeWithLengthPrefix<NetCommand>(stream, ProtoBuf.PrefixStyle.Fixed32);
                            if (command.Type == NetCommandType.Close)
                            {
                                Printer.PrintDiagnostics("Client closing connection.");
                                break;
                            }
                            else if (command.Type == NetCommandType.PushInitialVersion)
                            {
                                bool fresh = false;
                                if (domainInfo.Directory == null)
                                {
                                    if (!clientInfo.Access.HasFlag(Rights.Create))
                                        throw new Exception("Access denied.");
                                    fresh = true;
                                    System.IO.DirectoryInfo newDirectory = new System.IO.DirectoryInfo(System.IO.Path.Combine(info.FullName, hs.RequestedModule));
                                    newDirectory.Create();
                                    domainInfo.Directory = newDirectory;
                                    if (!newDirectory.Exists)
                                        throw new Exception("Access denied.");
                                }
                                if (!clientInfo.Access.HasFlag(Rights.Write))
                                    throw new Exception("Access denied.");
                                lock (SyncObject)
                                {
                                    ws = Area.InitRemote(domainInfo.Directory, Utilities.ReceiveEncrypted<ClonePayload>(clientInfo.SharedInfo));
                                    clientInfo.SharedInfo.Workspace = ws;
                                    domainInfo.Bare = false;
                                    if (fresh)
                                        Domains[hs.RequestedModule] = domainInfo;
                                }
                            }
                            else if (command.Type == NetCommandType.PushBranchJournal)
                            {
                                if (!clientInfo.Access.HasFlag(Rights.Write))
                                    throw new Exception("Access denied.");
                                SharedNetwork.ReceiveBranchJournal(sharedInfo);
                            }
                            else if (command.Type == NetCommandType.QueryBranchID)
                            {
                                if (!clientInfo.Access.HasFlag(Rights.Read))
                                    throw new Exception("Access denied.");
                                Printer.PrintDiagnostics("Client is requesting a branch info for {0}", string.IsNullOrEmpty(command.AdditionalPayload) ? "<root>" : "\"" + command.AdditionalPayload + "\"");
                                bool multiple = false;
                                Objects.Branch branch = string.IsNullOrEmpty(command.AdditionalPayload) ? ws.RootBranch : ws.GetBranchByPartialName(command.AdditionalPayload, out multiple);
                                if (branch != null)
                                {
                                    ProtoBuf.Serializer.SerializeWithLengthPrefix<NetCommand>(stream, new NetCommand() { Type = NetCommandType.Acknowledge, AdditionalPayload = branch.ID.ToString() }, ProtoBuf.PrefixStyle.Fixed32);
                                }
                                else if (!multiple)
                                {
                                    ProtoBuf.Serializer.SerializeWithLengthPrefix<NetCommand>(stream, new NetCommand() { Type = NetCommandType.Error, AdditionalPayload = "branch not recognized" }, ProtoBuf.PrefixStyle.Fixed32);
                                }
                                else
                                {
                                    ProtoBuf.Serializer.SerializeWithLengthPrefix<NetCommand>(stream, new NetCommand() { Type = NetCommandType.Error, AdditionalPayload = "multiple branches with that name!" }, ProtoBuf.PrefixStyle.Fixed32);
                                }
                            }
                            else if (command.Type == NetCommandType.ListBranches)
                            {
                                if (!clientInfo.Access.HasFlag(Rights.Read))
                                    throw new Exception("Access denied.");
                                Printer.PrintDiagnostics("Client is requesting a branch list.");
                                ProtoBuf.Serializer.SerializeWithLengthPrefix<NetCommand>(stream, new NetCommand() { Type = NetCommandType.Acknowledge }, ProtoBuf.PrefixStyle.Fixed32);
                                if (command.Identifier == 1) // send extra data
                                {
                                    BranchList bl = new BranchList();
                                    bl.Branches = clientInfo.SharedInfo.Workspace.Branches.ToArray();
                                    Dictionary<Guid, Objects.Version> importantVersions = new Dictionary<Guid, Objects.Version>();
                                    List<KeyValuePair<Guid, Guid>> allHeads = new List<KeyValuePair<Guid, Guid>>();
                                    foreach (var x in bl.Branches)
                                    {
                                        if (x.Terminus.HasValue && !importantVersions.ContainsKey(x.Terminus.Value))
                                        {
                                            importantVersions[x.Terminus.Value] = clientInfo.SharedInfo.Workspace.GetVersion(x.Terminus.Value);
                                            continue;
                                        }
                                        var heads = clientInfo.SharedInfo.Workspace.GetBranchHeads(x);
                                        foreach (var head in heads)
                                        {
                                            if (!importantVersions.ContainsKey(head.Version))
                                                importantVersions[head.Version] = clientInfo.SharedInfo.Workspace.GetVersion(head.Version);
                                        }
                                        allHeads.AddRange(heads.Select(y => new KeyValuePair<Guid, Guid>(y.Branch, y.Version)));
                                    }
                                    bl.Heads = allHeads.ToArray();
                                    bl.ImportantVersions = importantVersions.Values.ToArray();
                                    Utilities.SendEncrypted<BranchList>(clientInfo.SharedInfo, bl);
                                }
                                else
                                {
                                    BranchList bl = new BranchList();
                                    bl.Branches = clientInfo.SharedInfo.Workspace.Branches.ToArray();
                                    List<KeyValuePair<Guid, Guid>> allHeads = new List<KeyValuePair<Guid, Guid>>();
                                    foreach (var x in bl.Branches)
                                    {
                                        if (x.Terminus.HasValue)
                                            continue;
                                        var heads = clientInfo.SharedInfo.Workspace.GetBranchHeads(x);
                                        if (heads.Count == 1)
                                            allHeads.Add(new KeyValuePair<Guid, Guid>(x.ID, heads[0].Version));
                                    }
                                    bl.Heads = allHeads.ToArray();
                                    Utilities.SendEncrypted<BranchList>(clientInfo.SharedInfo, bl);
                                }
                            }
                            else if (command.Type == NetCommandType.RequestRecordUnmapped)
                            {
                                if (!clientInfo.Access.HasFlag(Rights.Read))
                                    throw new Exception("Access denied.");
                                Printer.PrintDiagnostics("Client is requesting specific record data blobs.");
                                SharedNetwork.SendRecordDataUnmapped(sharedInfo);
                            }
                            else if (command.Type == NetCommandType.Clone)
                            {
                                if (!clientInfo.Access.HasFlag(Rights.Read))
                                    throw new Exception("Access denied.");
                                Printer.PrintDiagnostics("Client is requesting to clone the vault.");
                                Objects.Version initialRevision = ws.GetVersion(ws.Domain);
                                Objects.Branch initialBranch = ws.GetBranch(initialRevision.Branch);
                                Utilities.SendEncrypted<ClonePayload>(sharedInfo, new ClonePayload() { InitialBranch = initialBranch, RootVersion = initialRevision });
                            }
                            else if (command.Type == NetCommandType.PullVersions)
                            {
                                if (!clientInfo.Access.HasFlag(Rights.Read))
                                    throw new Exception("Access denied.");
                                Printer.PrintDiagnostics("Client asking for remote version information.");
                                Branch branch = ws.GetBranch(new Guid(command.AdditionalPayload));
                                if (branch == null)
                                    ProtoBuf.Serializer.SerializeWithLengthPrefix<NetCommand>(stream, new NetCommand() { Type = NetCommandType.Error, AdditionalPayload = string.Format("Unknown branch {0}", command.AdditionalPayload) }, ProtoBuf.PrefixStyle.Fixed32);
                                else
                                    ProtoBuf.Serializer.SerializeWithLengthPrefix<NetCommand>(stream, new NetCommand() { Type = NetCommandType.Acknowledge }, ProtoBuf.PrefixStyle.Fixed32);
                                Stack<Objects.Branch> branchesToSend = new Stack<Branch>();
                                Stack<Objects.Version> versionsToSend = new Stack<Objects.Version>();
                                if (!SharedNetwork.SendBranchJournal(sharedInfo))
                                    throw new Exception();
                                if (!SharedNetwork.GetVersionList(sharedInfo, sharedInfo.Workspace.GetBranchHeadVersion(branch), out branchesToSend, out versionsToSend))
                                    throw new Exception();
                                if (!SharedNetwork.SendBranches(sharedInfo, branchesToSend))
                                    throw new Exception();
                                if (!SharedNetwork.SendVersions(sharedInfo, versionsToSend))
                                    throw new Exception();
                            }
                            else if (command.Type == NetCommandType.PushObjectQuery)
                            {
                                if (!clientInfo.Access.HasFlag(Rights.Write))
                                    throw new Exception("Access denied.");
                                Printer.PrintDiagnostics("Client asking about objects on the server...");
                                SharedNetwork.ProcesPushObjectQuery(sharedInfo);
                            }
                            else if (command.Type == NetCommandType.PushBranch)
                            {
                                if (!clientInfo.Access.HasFlag(Rights.Write))
                                    throw new Exception("Access denied.");
                                Printer.PrintDiagnostics("Client attempting to send branch data...");
                                SharedNetwork.ReceiveBranches(sharedInfo);
                            }
                            else if (command.Type == NetCommandType.PushVersions)
                            {
                                if (!clientInfo.Access.HasFlag(Rights.Write))
                                    throw new Exception("Access denied.");
                                Printer.PrintDiagnostics("Client attempting to send version data...");
                                SharedNetwork.ReceiveVersions(sharedInfo);
                            }
                            else if (command.Type == NetCommandType.PushHead)
                            {
                                if (!clientInfo.Access.HasFlag(Rights.Write))
                                    throw new Exception("Access denied.");
                                Printer.PrintDiagnostics("Determining head information.");
                                string errorData;
                                lock (ws)
                                {
                                    clientInfo.SharedInfo.Workspace.RunLocked(() =>
                                    {
                                        try
                                        {
                                            clientInfo.SharedInfo.Workspace.BeginDatabaseTransaction();
                                            if (!SharedNetwork.ImportBranchJournal(clientInfo.SharedInfo, false))
                                            {
                                                clientInfo.SharedInfo.Workspace.RollbackDatabaseTransaction();
                                                return false;
                                            }
                                            if (AcceptHeads(clientInfo, ws, out errorData))
                                            {
                                                ImportVersions(ws, clientInfo);
                                                clientInfo.SharedInfo.Workspace.CommitDatabaseTransaction();
                                                ProtoBuf.Serializer.SerializeWithLengthPrefix<NetCommand>(stream, new NetCommand() { Type = NetCommandType.AcceptPush }, ProtoBuf.PrefixStyle.Fixed32);
                                                return true;
                                            }
                                            else
                                                ProtoBuf.Serializer.SerializeWithLengthPrefix<NetCommand>(stream, new NetCommand() { Type = NetCommandType.RejectPush, AdditionalPayload = errorData }, ProtoBuf.PrefixStyle.Fixed32);
                                            clientInfo.SharedInfo.Workspace.RollbackDatabaseTransaction();
                                            return false;
                                        }
                                        catch
                                        {
                                            clientInfo.SharedInfo.Workspace.RollbackDatabaseTransaction();
                                            return false;
                                        }
                                    }, false);
                                }
                            }
                            else if (command.Type == NetCommandType.SynchronizeRecords)
                            {
                                if (!clientInfo.Access.HasFlag(Rights.Read))
                                    throw new Exception("Access denied.");
                                Printer.PrintDiagnostics("Received {0} versions in version pack, but need {1} records to commit data.", sharedInfo.PushedVersions.Count, sharedInfo.UnknownRecords.Count);
                                Printer.PrintDiagnostics("Beginning record synchronization...");
                                if (sharedInfo.UnknownRecords.Count > 0)
                                {
                                    Printer.PrintDiagnostics("Requesting record metadata...");
                                    SharedNetwork.RequestRecordMetadata(clientInfo.SharedInfo);
                                    Printer.PrintDiagnostics("Requesting record data...");
                                    SharedNetwork.RequestRecordData(sharedInfo);
                                    if (!sharedInfo.Workspace.RunLocked(() =>
                                    {
                                        return SharedNetwork.ImportRecords(sharedInfo);
                                    }, false))
                                    {
                                        throw new Exception("Unable to import records!");
                                    }
                                }
                                ProtoBuf.Serializer.SerializeWithLengthPrefix<NetCommand>(stream, new NetCommand() { Type = NetCommandType.Synchronized }, ProtoBuf.PrefixStyle.Fixed32);
                            }
                            else if (command.Type == NetCommandType.FullClone)
                            {
                                if (!clientInfo.Access.HasFlag(Rights.Read))
                                    throw new Exception("Access denied.");
                                ProtoBuf.Serializer.SerializeWithLengthPrefix<NetCommand>(stream, new NetCommand() { Type = NetCommandType.Acknowledge, Identifier = (int)ws.DatabaseVersion }, ProtoBuf.PrefixStyle.Fixed32);
                                command = ProtoBuf.Serializer.DeserializeWithLengthPrefix<NetCommand>(stream, ProtoBuf.PrefixStyle.Fixed32);
                                if (command.Type == NetCommandType.Acknowledge)
                                {
                                    bool accept = false;
                                    BackupInfo backupInfo = null;
                                    lock (domainInfo)
                                    {
                                        string backupKey = ws.LastVersion + "-" + ws.LastBranch + "-" + ws.BranchJournalTipID.ToString();
                                        backupInfo = domainInfo.Backup;
                                        if (backupKey != backupInfo.Key)
                                        {
                                            Printer.PrintMessage("Backup key out of date for domain DB[{0}] - {1}", domainInfo.Directory, backupKey);
                                            if (System.Threading.Interlocked.Decrement(ref backupInfo.Refs) == 0)
                                                backupInfo.Backup.Delete();
                                            backupInfo = new BackupInfo();
                                            domainInfo.Backup = backupInfo;
                                            var directory = new System.IO.DirectoryInfo(System.IO.Path.Combine(ws.AdministrationFolder.FullName, "backups"));
                                            directory.Create();
                                            backupInfo.Backup = new System.IO.FileInfo(System.IO.Path.Combine(directory.FullName, System.IO.Path.GetRandomFileName()));
                                            if (ws.BackupDB(backupInfo.Backup))
                                            {
                                                System.Threading.Interlocked.Increment(ref backupInfo.Refs);
                                                accept = true;
                                                backupInfo.Key = backupKey;
                                            }
                                        }
                                        else
                                        {
                                            accept = true;
                                        }

                                        if (accept)
                                            System.Threading.Interlocked.Increment(ref backupInfo.Refs);
                                    }
                                    if (accept)
                                    {
                                        Printer.PrintDiagnostics("Backup complete. Sending data.");
                                        byte[] blob = new byte[256 * 1024];
                                        long filesize = backupInfo.Backup.Length;
                                        long position = 0;
                                        using (System.IO.FileStream reader = backupInfo.Backup.OpenRead())
                                        {
                                            while (true)
                                            {
                                                long remainder = filesize - position;
                                                int count = blob.Length;
                                                if (count > remainder)
                                                    count = (int)remainder;
                                                reader.Read(blob, 0, count);
                                                position += count;
                                                Printer.PrintDiagnostics("Sent {0}/{1} bytes.", position, filesize);
                                                if (count == remainder)
                                                {
                                                    Utilities.SendEncrypted(sharedInfo, new DataPayload()
                                                    {
                                                        Data = blob.Take(count).ToArray(),
                                                        EndOfStream = true
                                                    });
                                                    break;
                                                }
                                                else
                                                {
                                                    Utilities.SendEncrypted(sharedInfo, new DataPayload()
                                                    {
                                                        Data = blob,
                                                        EndOfStream = false
                                                    });
                                                }
                                            }
                                        }
                                        ProtoBuf.Serializer.SerializeWithLengthPrefix<NetCommand>(stream, new NetCommand() { Type = NetCommandType.Acknowledge }, ProtoBuf.PrefixStyle.Fixed32);
                                        lock (domainInfo)
                                        {
                                            if (System.Threading.Interlocked.Decrement(ref backupInfo.Refs) == 0)
                                                backupInfo.Backup.Delete();
                                        }
                                    }
                                    else
                                    {
                                        Utilities.SendEncrypted<DataPayload>(sharedInfo, new DataPayload() { Data = new byte[0], EndOfStream = true });
                                        ProtoBuf.Serializer.SerializeWithLengthPrefix<NetCommand>(stream, new NetCommand() { Type = NetCommandType.Error }, ProtoBuf.PrefixStyle.Fixed32);
                                    }
                                }
                            }
                            else
                            {
                                Printer.PrintDiagnostics("Client sent invalid command: {0}", command.Type);
                                throw new Exception();
                            }
                        }
                    }
                    else
                    {
                        Network.StartTransaction startSequence = Network.StartTransaction.CreateRejection();
                        Printer.PrintDiagnostics("Rejecting client due to protocol mismatch.");
                        ProtoBuf.Serializer.SerializeWithLengthPrefix<Network.StartTransaction>(stream, startSequence, ProtoBuf.PrefixStyle.Fixed32);
                        return;
                    }
                }
                catch (Exception e)
                {
                    Printer.PrintDiagnostics("Client was a terrible person, because: {0}", e);
                }
                finally
                {
                    if (ws != null)
                        ws.Dispose();
                }
            }

            Printer.PrintDiagnostics("Ended client processor task!");
        }
Esempio n. 2
0
        public bool Connect(string host, int port, string module, bool requirewrite = false)
        {
            if (port == -1)
                port = VersionrDefaultPort;
            IEnumerator<SharedNetwork.Protocol> protocols = SharedNetwork.AllowedProtocols.Cast<SharedNetwork.Protocol>().GetEnumerator();
            Retry:
            if (!protocols.MoveNext())
            {
                Printer.PrintMessage("#e#No valid protocols available.##");
                return false;
            }
            Host = host;
            Port = port;
            Module = module;
            Connected = false;
            try
            {
                Connection = new System.Net.Sockets.TcpClient();
                var connectionTask = Connection.ConnectAsync(Host, Port);
                if (!connectionTask.Wait(5000))
                {
                    throw new Exception(string.Format("Couldn't connect to target: {0}", this.VersionrURL));
                }
            }
            catch (Exception e)
            {
                Printer.PrintError(e.Message);
                return false;
            }
            if (Connection.Connected)
            {
                try
                {
                    Printer.PrintDiagnostics("Connected to server at {0}:{1}", host, port);
                    Handshake hs = Handshake.Create(protocols.Current);
                    hs.RequestedModule = Module;
                    Printer.PrintDiagnostics("Sending handshake...");
                    Connection.NoDelay = true;
                    ProtoBuf.Serializer.SerializeWithLengthPrefix<Handshake>(Connection.GetStream(), hs, ProtoBuf.PrefixStyle.Fixed32);

                    var startTransaction = ProtoBuf.Serializer.DeserializeWithLengthPrefix<Network.StartTransaction>(Connection.GetStream(), ProtoBuf.PrefixStyle.Fixed32);
                    if (startTransaction == null || !startTransaction.Accepted)
                    {
                        Printer.PrintError("#b#Server rejected connection.##");
                        if (startTransaction != null && hs.VersionrProtocol != startTransaction.ServerHandshake.VersionrProtocol)
                            Printer.PrintError("## Protocol mismatch - local: {0}, remote: {1}", hs.VersionrProtocol, startTransaction.ServerHandshake.VersionrProtocol);
                        else
                        {
                            if (startTransaction == null)
                                Printer.PrintError("## Connection terminated unexpectedly.");
                            else
                                Printer.PrintError("## Rejected request.");
                            return false;
                        }
                        Printer.PrintError("#b#Attempting to retry with a lower protocol.##");
                        goto Retry;
                    }
                    Printer.PrintDiagnostics("Server domain: {0}", startTransaction.Domain);
                    if (Workspace != null && !string.IsNullOrEmpty(startTransaction.Domain) && startTransaction.Domain != Workspace.Domain.ToString())
                    {
                        Printer.PrintError("Server domain doesn't match client domain. Disconnecting.");
                        return false;
                    }

                    RemoteDomain = startTransaction.Domain;

                    if (SharedNetwork.SupportsAuthentication(startTransaction.ServerHandshake.CheckProtocol().Value))
                    {
                        var command = ProtoBuf.Serializer.DeserializeWithLengthPrefix<NetCommand>(Connection.GetStream(), ProtoBuf.PrefixStyle.Fixed32);
                        if (command.Type == NetCommandType.Authenticate)
                        {
                            bool runauth = true;
                            var challenge = ProtoBuf.Serializer.DeserializeWithLengthPrefix<AuthenticationChallenge>(Connection.GetStream(), ProtoBuf.PrefixStyle.Fixed32);
                            if ((!requirewrite && (command.Identifier & 1) != 0) ||
                                (requirewrite && (command.Identifier & 2) != 0)) // server supports unauthenticated access
                            {
                                AuthenticationResponse response = new AuthenticationResponse()
                                {
                                    IdentifierToken = string.Empty,
                                    Mode = AuthenticationMode.Guest
                                };
                                ProtoBuf.Serializer.SerializeWithLengthPrefix(Connection.GetStream(), response, ProtoBuf.PrefixStyle.Fixed32);
                                command = ProtoBuf.Serializer.DeserializeWithLengthPrefix<NetCommand>(Connection.GetStream(), ProtoBuf.PrefixStyle.Fixed32);
                                if (command.Type == NetCommandType.Acknowledge)
                                    runauth = false;
                            }
                            if (runauth)
                            {
                                bool q = Printer.Quiet;
                                Printer.Quiet = false;
                                Printer.PrintMessage("Server at #b#{0}## requires authentication.", VersionrURL);
                                while (true)
                                {
                                    if (challenge.AvailableModes.Contains(AuthenticationMode.Simple))
                                    {
                                        System.Console.CursorVisible = true;
                                        Printer.PrintMessageSingleLine("#b#Username:## ");
                                        string user = System.Console.ReadLine();
                                        Printer.PrintMessageSingleLine("#b#Password:## ");
                                        string pass = GetPassword();
                                        System.Console.CursorVisible = false;

                                        user = user.Trim(new char[] { '\r', '\n', ' ' });

                                        AuthenticationResponse response = new AuthenticationResponse()
                                        {
                                            IdentifierToken = user,
                                            Mode = AuthenticationMode.Simple,
                                            Payload = System.Text.ASCIIEncoding.ASCII.GetBytes(BCrypt.Net.BCrypt.HashPassword(pass, challenge.Salt))
                                        };
                                        Printer.PrintMessage("\n");
                                        ProtoBuf.Serializer.SerializeWithLengthPrefix(Connection.GetStream(), response, ProtoBuf.PrefixStyle.Fixed32);
                                        command = ProtoBuf.Serializer.DeserializeWithLengthPrefix<NetCommand>(Connection.GetStream(), ProtoBuf.PrefixStyle.Fixed32);
                                        if (command.Type == NetCommandType.AuthRetry)
                                            Printer.PrintError("#e#Authentication failed.## Retry.");
                                        if (command.Type == NetCommandType.AuthFail)
                                        {
                                            Printer.PrintError("#e#Authentication failed.##");
                                            return false;
                                        }
                                        if (command.Type == NetCommandType.Acknowledge)
                                            break;
                                    }
                                    else
                                    {
                                        Printer.PrintError("Unsupported authentication requirements!");
                                        return false;
                                    }
                                }
                                Printer.Quiet = q;
                            }
                        }
                    }
                    if (startTransaction.Encrypted)
                    {
                        var key = startTransaction.RSAKey;
                        Printer.PrintDiagnostics("Server RSA Key: {0}", key.Fingerprint());

                        var publicKey = new System.Security.Cryptography.RSACryptoServiceProvider();
                        publicKey.ImportParameters(key);

                        Printer.PrintDiagnostics("Generating secret key for data channel...");
                        System.Security.Cryptography.RSAOAEPKeyExchangeFormatter exch = new System.Security.Cryptography.RSAOAEPKeyExchangeFormatter(publicKey);

                        System.Security.Cryptography.AesManaged aesProvider = new System.Security.Cryptography.AesManaged();
                        aesProvider.KeySize = 256;
                        aesProvider.GenerateIV();
                        aesProvider.GenerateKey();

                        AESProvider = aesProvider;
                        AESKey = aesProvider.Key;
                        AESIV = aesProvider.IV;

                        Printer.PrintDiagnostics("Key: {0}", System.Convert.ToBase64String(aesProvider.Key));
                        var keyExchangeObject = new Network.StartClientTransaction() { Key = exch.CreateKeyExchange(aesProvider.Key), IV = exch.CreateKeyExchange(aesProvider.IV) };

                        ProtoBuf.Serializer.SerializeWithLengthPrefix<StartClientTransaction>(Connection.GetStream(), keyExchangeObject, ProtoBuf.PrefixStyle.Fixed32);
                        Connection.GetStream().Flush();
                        Connected = true;
                        SharedNetwork.SharedNetworkInfo sharedInfo = new SharedNetwork.SharedNetworkInfo()
                        {
                            DecryptorFunction = () => { return Decryptor; },
                            EncryptorFunction = () => { return Encryptor; },
                            Stream = Connection.GetStream(),
                            Workspace = Workspace,
                            Client = true,
                            CommunicationProtocol = protocols.Current
                        };

                        SharedInfo = sharedInfo;
                    }
                    else
                    {
                        Printer.PrintDiagnostics("Using cleartext communication");
                        var keyExchangeObject = new Network.StartClientTransaction();
                        ProtoBuf.Serializer.SerializeWithLengthPrefix<StartClientTransaction>(Connection.GetStream(), keyExchangeObject, ProtoBuf.PrefixStyle.Fixed32);
                        Connection.GetStream().Flush();
                        Connected = true;
                        SharedNetwork.SharedNetworkInfo sharedInfo = new SharedNetwork.SharedNetworkInfo()
                        {
                            DecryptorFunction = null,
                            EncryptorFunction = null,
                            Stream = Connection.GetStream(),
                            Workspace = Workspace,
                            Client = true,
                            CommunicationProtocol = protocols.Current
                        };
                        SharedInfo = sharedInfo;
                    }
                    return true;
                }
                catch (Exception e)
                {
                    Printer.PrintError("Error encountered: {0}", e);
                    return false;
                }
            }
            else
                return false;
        }