mirror of
https://github.com/space-wizards/RobustToolbox.git
synced 2026-02-14 19:29:36 +01:00
* oops
* fixes serialization il
* copytest
* typo & misc fixes
* 139 moment
* boxing
* mesa dum
* stuff
* goodbye bad friend
* last commit before the big (4) rewrite
* adds datanodes
* kills yamlobjserializer in favor of the new system
* adds more serializers, actually implements them & removes most of the last of the old system
* changed yamlfieldattribute namespace
* adds back iselfserialize
* refactors consts&flags
* renames everything to data(field/definition)
* adds afterserialization
* help
* dataclassgen
* fuggen help me mannen
* Fix most errors on content
* Fix engine errors except map loader
* maploader & misc fix
* misc fixes
* thing
* help
* refactors datanodes
* help me mannen
* Separate ITypeSerializer into reader and writer
* Convert all type serializers
* priority
* adds alot
* il fixes
* adds robustgen
* argh
* adds array & enum serialization
* fixes dataclasses
* adds vec2i / misc fixes
* fixes inheritance
* a very notcursed todo
* fixes some custom dataclasses
* push dis
* Remove data classes
* boutta box
* yes
* Add angle and regex serializer tests
* Make TypeSerializerTest abstract
* sets up ioc etc
* remove pushinheritance
* fixes
* Merge fixes, fix yaml hot reloading
* General fixes2
* Make enum serialization ignore case
* Fix the tag not being copied in data nodes
* Fix not properly serializing flag enums
* Fix component serialization on startup
* Implement ValueDataNode ToString
* Serialization IL fixes, fix return and string equality
* Remove async from prototype manager
* Make serializing unsupported node as enum exception more descriptive
* Fix serv3 tryread casting to serializer instead of reader
* Add constructor for invalid node type exception
* Temporary fix for SERV3: Turn populate delegate into regular code
* Fix not copying the data of non primitive types
* Fix not using the data definition found in copying
* Make ISerializationHooks require explicit implementations
* Add test for serialization inheritance
* Improve IsOverridenIn method
* Fix error message when a data definition is null
* Add method to cast a read value in Serv3Manager
* Rename IServ3Manager to ISerializationManager
* Rename usages of serv3manager, add generic copy method
* Fix IL copy method lookup
* Rename old usages of serv3manager
* Add ITypeCopier
* resistance is futile
* we will conquer this codebase
* Add copy method to all serializers
* Make primitive mismatch error message more descriptive
* bing bong im going to freacking heck
* oopsie moment
* hello are you interested in my wares
* does generic serializers under new architecture
* Convert every non generic serializer to the new format, general fixes
* Update usgaes of generic serializers, cleanup
* does some pushinheritance logic
* finishes pushinheritance FRAMEWORK
* shed
* Add box2, color and component registry serializer tests
* Create more deserialized types and store prototypes with their deserialized results
* Fixes and serializer updates
* Add serialization manager extensions
* adds pushinheritance
* Update all prototypes to have a parent and have consistent id/parent properties
* Fix grammar component serialization
* Add generic serializer tests
* thonk
* Add array serializer test
* Replace logger warning calls with exceptions
* fixes
* Move redundant methods to serialization manager extensions, cleanup
* Add array serialization
* fixes context
* more fixes
* argh
* inheritance
* this should do it
* fixes
* adds copiers & fixes some stuff
* copiers use context v1
* finishing copy context
* more context fixes
* Test fixes
* funky maps
* Fix server user interface component serialization
* Fix value tuple serialization
* Add copying for value types and arrays. Fix copy internal for primitives, enums and strings
* fixes
* fixes more stuff
* yes
* Make abstract/interface skips debugs instead of warnings
* Fix typo
* Make some dictionaries readonly
* Add checks for the serialization manager initializing and already being initialized
* Add base type required and usage for MeansDataDefinition and ImplicitDataDefinitionForInheritorsAttribute
* copy by ref
* Fix exception wording
* Update data field required summary with the new forbidden docs
* Use extension in map loader
* wanna erp
* Change serializing to not use il temporarily
* Make writing work with nullable types
* pushing
* check
* cuddling slaps HARD
* Add serialization priority test
* important fix
* a serialization thing
* serializer moment
* Add validation for some type serializers
* adds context
* moar context
* fixes
* Do the thing for appearance
* yoo lmao
* push haha pp
* Temporarily make copy delegate regular c# code
* Create deserialized component registry to handle not inheriting conflicting references
* YAML LINTER BABY
* ayes
* Fix sprite component norot not being default true like in latest master
* Remove redundant todos
* Add summary doc to every ISerializationManager method
* icon fixes
* Add skip hook argument to readers and copiers
* Merge fixes
* Fix ordering of arguments in read and copy reflection call
* Fix user interface components deserialization
* pew pew
* i am going to HECK
* Add MustUseReturnValue to copy-over methods
* Make serialization log calls use the same sawmill
* gamin
* Fix doc errors in ISerializationManager.cs
* goodbye brave soldier
* fixes
* WIP merge fixes and entity serialization
* aaaaaaaaaaaaaaa
* aaaaaaaaaaaaaaa
* adds inheritancebehaviour
* test/datafield fixes
* forgot that one
* adds more verbose validation
* This fixes the YAML hot reloading
* Replace yield break with Enumerable.Empty
* adds copiers
* aaaaaaaaaaaaa
* array fix
priority fix
misc fixes
* fix(?)
* fix.
* funny map serialization (wip)
* funny map serialization (wip)
* Add TODO
* adds proper info the validation
* Make yaml linter 5 times faster (~80% less execution time)
* Improves the error message for missing fields in the linter
* Include component name in unknown component type error node
* adds alwaysrelevant usa
* fixes mapsaving
* moved surpressor to analyzers proj
* warning cleanup & moves surpressor
* removes old msbuild targets
* Revert "Make yaml linter 5 times faster (~80% less execution time)"
This reverts commit 2ee4cc2c26.
* Add serialization to RobustServerSimulation and mock reflection methods
Fixes container tests
* Fix nullability warnings
* Improve yaml linter message feedback
* oops moment
* Add IEquatable, IComparable, ToString and operators to DataPosition
Rename it to NodeMark
Make it a readonly struct
* Remove try catch from enum parsing
* Make dependency management in serialization less bad
* Make dependencies an argument instead of a property on the serialization manager
* Clean up type serializers
* Improve validation messages and resourc epath checking
* Fix sprite error message
* reached perfection
Co-authored-by: Paul <ritter.paul1+git@googlemail.com>
Co-authored-by: DrSmugleaf <DrSmugleaf@users.noreply.github.com>
Co-authored-by: Vera Aguilera Puerto <zddm@outlook.es>
515 lines
20 KiB
C#
515 lines
20 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.Linq;
|
|
using System.Net;
|
|
using System.Net.Http;
|
|
using System.Net.Http.Headers;
|
|
using System.Net.Mime;
|
|
using System.Net.Sockets;
|
|
using System.Security.Cryptography;
|
|
using System.Threading;
|
|
using System.Threading.Tasks;
|
|
using Lidgren.Network;
|
|
using Newtonsoft.Json;
|
|
using Robust.Shared.Log;
|
|
using Robust.Shared.Network.Messages;
|
|
using Robust.Shared.Network.Messages.Handshake;
|
|
using Robust.Shared.Utility;
|
|
|
|
namespace Robust.Shared.Network
|
|
{
|
|
public partial class NetManager
|
|
{
|
|
private CancellationTokenSource? _cancelConnectTokenSource;
|
|
private ClientConnectionState _clientConnectState;
|
|
|
|
public ClientConnectionState ClientConnectState
|
|
{
|
|
get => _clientConnectState;
|
|
private set
|
|
{
|
|
_clientConnectState = value;
|
|
ClientConnectStateChanged?.Invoke(value);
|
|
}
|
|
}
|
|
|
|
public event Action<ClientConnectionState>? ClientConnectStateChanged;
|
|
|
|
private readonly
|
|
Dictionary<NetConnection, (CancellationTokenRegistration reg, TaskCompletionSource<string> tcs)>
|
|
_awaitingStatusChange
|
|
= new();
|
|
|
|
private readonly
|
|
Dictionary<NetConnection, (CancellationTokenRegistration, TaskCompletionSource<NetIncomingMessage>)>
|
|
_awaitingData =
|
|
new();
|
|
|
|
|
|
/// <inheritdoc />
|
|
public async void ClientConnect(string host, int port, string userNameRequest)
|
|
{
|
|
DebugTools.Assert(!IsServer, "Should never be called on the server.");
|
|
if (ClientConnectState == ClientConnectionState.Connected)
|
|
{
|
|
throw new InvalidOperationException("The client is already connected to a server.");
|
|
}
|
|
|
|
if (ClientConnectState != ClientConnectionState.NotConnecting)
|
|
{
|
|
throw new InvalidOperationException("A connect attempt is already in progress. Cancel it first.");
|
|
}
|
|
|
|
_cancelConnectTokenSource = new CancellationTokenSource();
|
|
var mainCancelToken = _cancelConnectTokenSource.Token;
|
|
|
|
ClientConnectState = ClientConnectionState.ResolvingHost;
|
|
|
|
Logger.DebugS("net", "Attempting to connect to {0} port {1}", host, port);
|
|
|
|
var resolveResult = await CCResolveHost(host, mainCancelToken);
|
|
if (resolveResult == null)
|
|
{
|
|
ClientConnectState = ClientConnectionState.NotConnecting;
|
|
return;
|
|
}
|
|
|
|
var (first, second) = resolveResult.Value;
|
|
|
|
ClientConnectState = ClientConnectionState.EstablishingConnection;
|
|
|
|
Logger.DebugS("net", "First attempt IP address is {0}, second attempt {1}", first, second);
|
|
|
|
var result = await CCHappyEyeballs(port, first, second, mainCancelToken);
|
|
|
|
if (result == null)
|
|
{
|
|
ClientConnectState = ClientConnectionState.NotConnecting;
|
|
return;
|
|
}
|
|
|
|
var (winningPeer, winningConnection) = result.Value;
|
|
|
|
ClientConnectState = ClientConnectionState.Handshake;
|
|
|
|
// We're connected start handshaking.
|
|
|
|
try
|
|
{
|
|
await CCDoHandshake(winningPeer, winningConnection, userNameRequest, mainCancelToken);
|
|
}
|
|
catch (TaskCanceledException)
|
|
{
|
|
winningPeer.Peer.Shutdown("Cancelled");
|
|
_toCleanNetPeers.Add(winningPeer.Peer);
|
|
ClientConnectState = ClientConnectionState.NotConnecting;
|
|
return;
|
|
}
|
|
catch (Exception e)
|
|
{
|
|
OnConnectFailed(e.Message);
|
|
Logger.ErrorS("net", "Exception during handshake: {0}", e);
|
|
winningPeer.Peer.Shutdown("Something happened.");
|
|
_toCleanNetPeers.Add(winningPeer.Peer);
|
|
ClientConnectState = ClientConnectionState.NotConnecting;
|
|
return;
|
|
}
|
|
|
|
ClientConnectState = ClientConnectionState.Connected;
|
|
Logger.DebugS("net", "Handshake completed, connection established.");
|
|
}
|
|
|
|
private async Task CCDoHandshake(NetPeerData peer, NetConnection connection, string userNameRequest,
|
|
CancellationToken cancel)
|
|
{
|
|
var authToken = _authManager.Token;
|
|
var pubKey = _authManager.PubKey;
|
|
var authServer = _authManager.Server;
|
|
var userId = _authManager.UserId;
|
|
|
|
var hasPubKey = !string.IsNullOrEmpty(pubKey);
|
|
var authenticate = !string.IsNullOrEmpty(authToken);
|
|
|
|
var msgLogin = new MsgLoginStart
|
|
{
|
|
UserName = userNameRequest,
|
|
CanAuth = authenticate,
|
|
NeedPubKey = !hasPubKey
|
|
};
|
|
|
|
var outLoginMsg = peer.Peer.CreateMessage();
|
|
msgLogin.WriteToBuffer(outLoginMsg);
|
|
peer.Peer.SendMessage(outLoginMsg, connection, NetDeliveryMethod.ReliableOrdered);
|
|
|
|
NetEncryption? encryption = null;
|
|
var response = await AwaitData(connection, cancel);
|
|
var loginSuccess = response.ReadBoolean();
|
|
response.ReadPadBits();
|
|
if (!loginSuccess)
|
|
{
|
|
// Need to authenticate, packet is MsgEncryptionRequest
|
|
var encRequest = new MsgEncryptionRequest();
|
|
encRequest.ReadFromBuffer(response);
|
|
|
|
var sharedSecret = new byte[AesKeyLength];
|
|
RandomNumberGenerator.Fill(sharedSecret);
|
|
|
|
encryption = new NetAESEncryption(peer.Peer, sharedSecret, 0, sharedSecret.Length);
|
|
|
|
byte[] keyBytes;
|
|
if (hasPubKey)
|
|
{
|
|
// public key provided by launcher.
|
|
keyBytes = Convert.FromBase64String(pubKey!);
|
|
}
|
|
else
|
|
{
|
|
// public key is gotten from handshake.
|
|
keyBytes = encRequest.PublicKey;
|
|
}
|
|
|
|
var rsaKey = RSA.Create();
|
|
rsaKey.ImportRSAPublicKey(keyBytes, out _);
|
|
|
|
var encryptedSecret = rsaKey.Encrypt(sharedSecret, RSAEncryptionPadding.OaepSHA256);
|
|
var encryptedVerifyToken = rsaKey.Encrypt(encRequest.VerifyToken, RSAEncryptionPadding.OaepSHA256);
|
|
|
|
var authHashBytes = MakeAuthHash(sharedSecret, keyBytes);
|
|
var authHash = Convert.ToBase64String(authHashBytes);
|
|
|
|
var joinReq = new JoinRequest {Hash = authHash};
|
|
var httpClient = new HttpClient();
|
|
httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("SS14Auth", authToken);
|
|
var joinJson = JsonConvert.SerializeObject(joinReq);
|
|
var joinResp = await httpClient.PostAsync(authServer + "api/session/join",
|
|
new StringContent(joinJson, EncodingHelpers.UTF8, MediaTypeNames.Application.Json), cancel);
|
|
|
|
joinResp.EnsureSuccessStatusCode();
|
|
|
|
var encryptionResponse = new MsgEncryptionResponse
|
|
{
|
|
SharedSecret = encryptedSecret,
|
|
VerifyToken = encryptedVerifyToken,
|
|
UserId = userId!.Value.UserId
|
|
};
|
|
|
|
var outEncRespMsg = peer.Peer.CreateMessage();
|
|
encryptionResponse.WriteToBuffer(outEncRespMsg);
|
|
peer.Peer.SendMessage(outEncRespMsg, connection, NetDeliveryMethod.ReliableOrdered);
|
|
|
|
// Expect login success here.
|
|
response = await AwaitData(connection, cancel);
|
|
encryption.Decrypt(response);
|
|
}
|
|
|
|
var msgSuc = new MsgLoginSuccess();
|
|
msgSuc.ReadFromBuffer(response);
|
|
|
|
var channel = new NetChannel(this, connection, msgSuc.UserData, msgSuc.Type);
|
|
_channels.Add(connection, channel);
|
|
peer.AddChannel(channel);
|
|
|
|
_clientEncryption = encryption;
|
|
}
|
|
|
|
private static byte[] MakeAuthHash(byte[] sharedSecret, byte[] pkBytes)
|
|
{
|
|
Logger.DebugS("auth", "shared: {0}, pk: {1}", Convert.ToBase64String(sharedSecret), Convert.ToBase64String(pkBytes));
|
|
|
|
var incHash = IncrementalHash.CreateHash(HashAlgorithmName.SHA256);
|
|
incHash.AppendData(sharedSecret);
|
|
incHash.AppendData(pkBytes);
|
|
return incHash.GetHashAndReset();
|
|
}
|
|
|
|
private async Task<(IPAddress first, IPAddress? second)?>
|
|
CCResolveHost(string host, CancellationToken mainCancelToken)
|
|
{
|
|
// Get list of potential IP addresses for the domain.
|
|
var endPoints = await ResolveDnsAsync(host);
|
|
|
|
if (mainCancelToken.IsCancellationRequested)
|
|
{
|
|
return null;
|
|
}
|
|
|
|
if (endPoints == null)
|
|
{
|
|
OnConnectFailed($"Unable to resolve domain '{host}'");
|
|
return null;
|
|
}
|
|
|
|
// Try to get an IPv6 and IPv4 address.
|
|
var ipv6 = endPoints.FirstOrDefault(a => a.AddressFamily == AddressFamily.InterNetworkV6);
|
|
var ipv4 = endPoints.FirstOrDefault(a => a.AddressFamily == AddressFamily.InterNetwork);
|
|
|
|
if (ipv4 == null && ipv6 == null)
|
|
{
|
|
OnConnectFailed($"Domain '{host}' has no associated IP addresses");
|
|
return null;
|
|
}
|
|
|
|
IPAddress first;
|
|
IPAddress? second = null;
|
|
if (ipv6 != null)
|
|
{
|
|
// If there's an IPv6 address try it first then the IPv4.
|
|
first = ipv6;
|
|
second = ipv4;
|
|
}
|
|
else
|
|
{
|
|
first = ipv4!;
|
|
}
|
|
|
|
return (first, second);
|
|
}
|
|
|
|
private async Task<(NetPeerData winningPeer, NetConnection winningConnection)?>
|
|
CCHappyEyeballs(int port, IPAddress first, IPAddress? second, CancellationToken mainCancelToken)
|
|
{
|
|
NetPeerData CreatePeerForIp(IPAddress address)
|
|
{
|
|
var config = _getBaseNetPeerConfig();
|
|
if (address.AddressFamily == AddressFamily.InterNetworkV6)
|
|
{
|
|
config.LocalAddress = IPAddress.IPv6Any;
|
|
}
|
|
else
|
|
{
|
|
config.LocalAddress = IPAddress.Any;
|
|
}
|
|
|
|
var peer = new NetPeer(config);
|
|
peer.Start();
|
|
var data = new NetPeerData(peer);
|
|
_netPeers.Add(data);
|
|
return data;
|
|
}
|
|
|
|
// Create first peer.
|
|
var firstPeer = CreatePeerForIp(first);
|
|
var firstConnection = firstPeer.Peer.Connect(new IPEndPoint(first, port));
|
|
NetPeerData? secondPeer = null;
|
|
NetConnection? secondConnection = null;
|
|
string? secondReason = null;
|
|
|
|
async Task<string> AwaitNonInitStatusChange(NetConnection connection, CancellationToken cancellationToken)
|
|
{
|
|
NetConnectionStatus status;
|
|
string reason;
|
|
|
|
do
|
|
{
|
|
reason = await AwaitStatusChange(connection, cancellationToken);
|
|
status = connection.Status;
|
|
} while (status == NetConnectionStatus.InitiatedConnect);
|
|
|
|
return reason;
|
|
}
|
|
|
|
async Task ConnectSecondDelayed(CancellationToken cancellationToken)
|
|
{
|
|
DebugTools.AssertNotNull(second);
|
|
// Connecting via second peer is delayed by 25ms to give an advantage to IPv6, if it works.
|
|
await Task.Delay(25, cancellationToken);
|
|
if (cancellationToken.IsCancellationRequested)
|
|
{
|
|
return;
|
|
}
|
|
|
|
secondPeer = CreatePeerForIp(second);
|
|
secondConnection = secondPeer.Peer.Connect(new IPEndPoint(second, port));
|
|
|
|
secondReason = await AwaitNonInitStatusChange(secondConnection, cancellationToken);
|
|
}
|
|
|
|
NetPeerData? winningPeer;
|
|
NetConnection? winningConnection;
|
|
string? firstReason = null;
|
|
try
|
|
{
|
|
if (second != null)
|
|
{
|
|
// We have two addresses to try.
|
|
var cancellation = CancellationTokenSource.CreateLinkedTokenSource(mainCancelToken);
|
|
var firstPeerChanged = AwaitNonInitStatusChange(firstConnection, cancellation.Token);
|
|
var secondPeerChanged = ConnectSecondDelayed(cancellation.Token);
|
|
|
|
var firstChange = await Task.WhenAny(firstPeerChanged, secondPeerChanged);
|
|
|
|
if (firstChange == firstPeerChanged)
|
|
{
|
|
Logger.DebugS("net", "First peer status changed.");
|
|
// First peer responded first.
|
|
if (firstConnection.Status == NetConnectionStatus.Connected)
|
|
{
|
|
// First peer won!
|
|
Logger.DebugS("net", "First peer succeeded.");
|
|
cancellation.Cancel();
|
|
if (secondPeer != null)
|
|
{
|
|
secondPeer.Peer.Shutdown("First connection attempt won.");
|
|
_toCleanNetPeers.Add(secondPeer.Peer);
|
|
}
|
|
|
|
winningPeer = firstPeer;
|
|
winningConnection = firstConnection;
|
|
}
|
|
else
|
|
{
|
|
// First peer failed, try the second one I guess.
|
|
Logger.DebugS("net", "First peer failed.");
|
|
firstPeer.Peer.Shutdown("You failed.");
|
|
_toCleanNetPeers.Add(firstPeer.Peer);
|
|
firstReason = firstPeerChanged.Result;
|
|
await secondPeerChanged;
|
|
winningPeer = secondPeer;
|
|
winningConnection = secondConnection;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (secondConnection!.Status == NetConnectionStatus.Connected)
|
|
{
|
|
// Second peer won!
|
|
Logger.DebugS("net", "Second peer succeeded.");
|
|
cancellation.Cancel();
|
|
firstPeer.Peer.Shutdown("Second connection attempt won.");
|
|
_toCleanNetPeers.Add(firstPeer.Peer);
|
|
winningPeer = secondPeer;
|
|
winningConnection = secondConnection;
|
|
}
|
|
else
|
|
{
|
|
// First peer failed, try the second one I guess.
|
|
Logger.DebugS("net", "Second peer failed.");
|
|
secondPeer!.Peer.Shutdown("You failed.");
|
|
_toCleanNetPeers.Add(secondPeer.Peer);
|
|
firstReason = await firstPeerChanged;
|
|
winningPeer = firstPeer;
|
|
winningConnection = firstConnection;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Only one address to try. Pretty straight forward.
|
|
firstReason = await AwaitNonInitStatusChange(firstConnection, mainCancelToken);
|
|
winningPeer = firstPeer;
|
|
winningConnection = firstConnection;
|
|
}
|
|
}
|
|
catch (TaskCanceledException)
|
|
{
|
|
firstPeer.Peer.Shutdown("Cancelled");
|
|
_toCleanNetPeers.Add(firstPeer.Peer);
|
|
if (secondPeer != null)
|
|
{
|
|
// ReSharper disable once PossibleNullReferenceException
|
|
secondPeer.Peer.Shutdown("Cancelled");
|
|
_toCleanNetPeers.Add(secondPeer.Peer);
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
// winningPeer can still be failed at this point.
|
|
// If it is, neither succeeded. RIP.
|
|
if (winningConnection!.Status != NetConnectionStatus.Connected)
|
|
{
|
|
winningPeer!.Peer.Shutdown("You failed");
|
|
_toCleanNetPeers.Add(winningPeer.Peer);
|
|
OnConnectFailed((secondReason ?? firstReason)!);
|
|
return null;
|
|
}
|
|
|
|
return (winningPeer!, winningConnection);
|
|
}
|
|
|
|
private Task<string> AwaitStatusChange(NetConnection connection, CancellationToken cancellationToken = default)
|
|
{
|
|
if (_awaitingStatusChange.ContainsKey(connection))
|
|
{
|
|
throw new InvalidOperationException();
|
|
}
|
|
|
|
var tcs = new TaskCompletionSource<string>();
|
|
CancellationTokenRegistration reg = default;
|
|
if (cancellationToken != default)
|
|
{
|
|
reg = cancellationToken.Register(() =>
|
|
{
|
|
_awaitingStatusChange.Remove(connection);
|
|
tcs.TrySetCanceled();
|
|
});
|
|
}
|
|
|
|
_awaitingStatusChange.Add(connection, (reg, tcs));
|
|
return tcs.Task;
|
|
}
|
|
|
|
private Task<NetIncomingMessage> AwaitData(NetConnection connection,
|
|
CancellationToken cancellationToken = default)
|
|
{
|
|
if (_awaitingData.ContainsKey(connection))
|
|
{
|
|
throw new InvalidOperationException("Cannot await data twice.");
|
|
}
|
|
|
|
DebugTools.Assert(!_channels.ContainsKey(connection),
|
|
"AwaitData cannot be used once a proper channel for the connection has been constructed, as it does not support encryption.");
|
|
|
|
var tcs = new TaskCompletionSource<NetIncomingMessage>();
|
|
CancellationTokenRegistration reg = default;
|
|
if (cancellationToken != default)
|
|
{
|
|
reg = cancellationToken.Register(() =>
|
|
{
|
|
_awaitingData.Remove(connection);
|
|
tcs.TrySetCanceled();
|
|
});
|
|
}
|
|
|
|
_awaitingData.Add(connection, (reg, tcs));
|
|
return tcs.Task;
|
|
}
|
|
|
|
public static async Task<IPAddress[]?> ResolveDnsAsync(string ipOrHost)
|
|
{
|
|
if (string.IsNullOrEmpty(ipOrHost))
|
|
{
|
|
throw new ArgumentException("Supplied string must not be empty", nameof(ipOrHost));
|
|
}
|
|
|
|
ipOrHost = ipOrHost.Trim();
|
|
|
|
if (IPAddress.TryParse(ipOrHost, out var ipAddress))
|
|
{
|
|
if (ipAddress.AddressFamily == AddressFamily.InterNetwork
|
|
|| ipAddress.AddressFamily == AddressFamily.InterNetworkV6)
|
|
{
|
|
return new[] {ipAddress};
|
|
}
|
|
|
|
throw new ArgumentException("This method will not currently resolve other than IPv4 or IPv6 addresses");
|
|
}
|
|
|
|
try
|
|
{
|
|
var entry = await Dns.GetHostEntryAsync(ipOrHost);
|
|
return entry.AddressList;
|
|
}
|
|
catch (SocketException)
|
|
{
|
|
return null;
|
|
}
|
|
}
|
|
|
|
private sealed class JoinRequest
|
|
{
|
|
public string Hash = default!;
|
|
}
|
|
}
|
|
}
|