feat: add option to reject new clients

Also check for magic bytes for new protocol change.
This commit is contained in:
Anna 2020-10-26 16:00:06 -04:00
parent 1c3eb05621
commit 902d9af11f
3 changed files with 52 additions and 3 deletions

View File

@ -17,6 +17,8 @@ namespace XIVChatPlugin {
public bool PairingMode { get; set; } = true;
public bool AcceptNewClients { get; set; } = true;
public Dictionary<Guid, Tuple<string, byte[]>> TrustedKeys { get; set; } = new Dictionary<Guid, Tuple<string, byte[]>>();
public KeyPair KeyPair { get; set; } = null;

View File

@ -138,8 +138,8 @@ namespace XIVChatPlugin {
this.plugin.Config.SendBattle = sendBattle;
this.plugin.Config.Save();
}
ImGui.TextUnformatted("Changing this setting will not affect messages already in the backlog.");
ImGui.SameLine();
HelpMarker("Changing this setting will not affect messages already in the backlog.");
ImGui.Spacing();
@ -150,6 +150,16 @@ namespace XIVChatPlugin {
}
ImGui.SameLine();
HelpMarker("While in pairing mode, XIVChat Server will listen for information requests from clients broadcast on your local network and respond with information about the server. This will make it easier to add your server to a client, but this should be turned off when not actively adding new devices.");
ImGui.Spacing();
bool acceptNew = this.plugin.Config.AcceptNewClients;
if (WithWhiteText(() => ImGui.Checkbox("Accept new clients", ref acceptNew))) {
this.plugin.Config.AcceptNewClients = acceptNew;
this.plugin.Config.Save();
}
ImGui.SameLine();
HelpMarker("If this is disabled, XIVChat Server will only allow clients with already-trusted keys to connect.");
}
if (WithWhiteText(() => ImGui.CollapsingHeader("Trusted keys"))) {

View File

@ -243,6 +243,12 @@ namespace XIVChatPlugin {
this.plugin.Functions.ProcessChatBox(message);
}
private static readonly IReadOnlyList<byte> MAGIC = new byte[] {
14,
20,
67,
};
private void SpawnClientTask(TcpClient conn) {
if (conn == null) {
return;
@ -251,6 +257,28 @@ namespace XIVChatPlugin {
Task.Run(async () => {
var stream = conn.GetStream();
// get ready for reading magic bytes
var magic = new byte[MAGIC.Count];
var read = 0;
// only listen for magic for five seconds
using var cts = new CancellationTokenSource();
cts.CancelAfter(TimeSpan.FromSeconds(5));
// read magic bytes
while (read < magic.Length) {
if (cts.IsCancellationRequested) {
return;
}
read += await stream.ReadAsync(magic, read, magic.Length - read, cts.Token);
}
// ignore this connection if incorrect magic bytes
if (!magic.SequenceEqual(MAGIC)) {
return;
}
var handshake = await KeyExchange.ServerHandshake(this.plugin.Config.KeyPair, stream);
var newClient = new Client(conn) {
Handshake = handshake,
@ -258,6 +286,11 @@ namespace XIVChatPlugin {
// if this public key isn't trusted, prompt first
if (!this.plugin.Config.TrustedKeys.Values.Any(entry => entry.Item2.SequenceEqual(handshake.RemotePublicKey))) {
// if configured to not accept new clients, reject connection
if (!this.plugin.Config.AcceptNewClients) {
return;
}
var accepted = Channel.CreateBounded<bool>(1);
await this.pendingClients.Writer.WriteAsync(Tuple.Create(newClient, accepted));
@ -392,7 +425,11 @@ namespace XIVChatPlugin {
this.clients.TryRemove(id, out var _);
PluginLog.Log($"Client thread ended: {id}");
});
}).ContinueWith(_ => {
try {
conn.Close();
} catch (ObjectDisposedException) { }
}); ;
}
private static readonly Regex colorRegex = new Regex(@"<Color\(.+?\)>", RegexOptions.Compiled);