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