diff --git a/SharpXMPP.NUnit/Compat/TcpClientExTests.cs b/SharpXMPP.NUnit/Compat/TcpClientExTests.cs index 4c937f2..00c2a2f 100644 --- a/SharpXMPP.NUnit/Compat/TcpClientExTests.cs +++ b/SharpXMPP.NUnit/Compat/TcpClientExTests.cs @@ -37,7 +37,7 @@ public void TestCancellation() var token = cts.Token; using var client = new TcpClient(); - var task = client.ConnectWithCancellationAsync(IPAddress.Loopback, port, token); + var task = client.ConnectWithCancellationAsync(new[] { IPAddress.Loopback }, port, token); cts.Cancel(); try diff --git a/SharpXMPP.Shared/Compat/TcpClientEx.cs b/SharpXMPP.Shared/Compat/TcpClientEx.cs index 40493fb..21081d8 100644 --- a/SharpXMPP.Shared/Compat/TcpClientEx.cs +++ b/SharpXMPP.Shared/Compat/TcpClientEx.cs @@ -33,19 +33,6 @@ private static async Task AbandonOnCancel(this Task task, CancellationToken canc // the resources will be freed upon its destruction. Which means we are free to just abandon the task in // question. - public static Task ConnectWithCancellationAsync( - this TcpClient tcpClient, - IPAddress address, - int port, - CancellationToken cancellationToken) - { -#if NET5_0_OR_GREATER - return tcpClient.ConnectAsync(address, port, cancellationToken).AsTask(); -#else - return tcpClient.ConnectAsync(address, port).AbandonOnCancel(cancellationToken); -#endif - } - public static Task ConnectWithCancellationAsync( this TcpClient tcpClient, IPAddress[] addresses, diff --git a/SharpXMPP.Shared/Resolver.cs b/SharpXMPP.Shared/Resolver.cs deleted file mode 100644 index cf4ba82..0000000 --- a/SharpXMPP.Shared/Resolver.cs +++ /dev/null @@ -1,117 +0,0 @@ -using System; -using System.Collections.Generic; -using System.IO; -using System.Net; -using System.Net.Sockets; -using System.Text; -using System.Threading; -using System.Threading.Tasks; -using SharpXMPP.Compat; - -namespace SharpXMPP -{ - public struct SRVRecord - { - public string Host { get; set; } - public int Port { get; set; } - public long Ttl { get; set; } - } - - - public static class Resolver - { - [Obsolete("Call overload with CancellationToken")] - public static Task> ResolveXMPPClient(string domain) => - ResolveXMPPClient(domain, default); - - public static async Task> ResolveXMPPClient(string domain, CancellationToken cancellationToken) - { - using var client = new TcpClient(); - await client.ConnectWithCancellationAsync(IPAddress.Parse("1.1.1.1"), 53, cancellationToken); - using var stream = client.GetStream(); - var message = EncodeQuery(domain); - var lengthPrefix = IPAddress.HostToNetworkOrder((short)message.Length); - var lengthPrefixBytes = BitConverter.GetBytes(lengthPrefix); - stream.Write(lengthPrefixBytes, 0, 2); - stream.Write(message, 0, message.Length); - - var responseLengthBytes = new byte[2]; - stream.Read(responseLengthBytes, 0, 2); - var responseMessage = new byte[IPAddress.NetworkToHostOrder(BitConverter.ToInt16(responseLengthBytes, 0))]; - stream.Read(responseMessage, 0, responseMessage.Length); - return Decode(responseMessage); - } - - private static byte[] EncodeQuery(string domain) - { - var ms = new MemoryStream(); - var writer = new BinaryWriter(ms); - writer.Write(IPAddress.HostToNetworkOrder((short)0x666)); - writer.Write((byte)0x01); - // QR+Opcode+AA+TC+RD = 0000-0001b - RECURSION_DESIRED - writer.Write((byte)0x00); // RA+Z+RCode = 0000-0000b - writer.Write(IPAddress.HostToNetworkOrder((short)0x0001)); // queries count - writer.Write((short)0x0000); // answers count - writer.Write((short)0x0000); // authority record count - writer.Write((short)0x0000); // additions count - var buff = new StringBuilder(); - buff.Append("_xmpp-client._tcp.").Append(domain); - foreach (var chr in buff.ToString().Split('.')) { writer.Write(chr); }; - writer.Write((byte)0x00); - writer.Write(IPAddress.HostToNetworkOrder((short)0x0021)); - writer.Write(IPAddress.HostToNetworkOrder((short)0x0001)); - ms.Seek(0, SeekOrigin.Begin); - return ms.ToArray(); - } - - private static List Decode(byte[] response) - { - - var ms = new MemoryStream(response); - var reader = new BinaryReader(ms); - short id = IPAddress.NetworkToHostOrder(reader.ReadInt16()); // id - short flags = IPAddress.NetworkToHostOrder(reader.ReadInt16()); // flags - short questions = IPAddress.NetworkToHostOrder(reader.ReadInt16()); - int answers = IPAddress.NetworkToHostOrder(reader.ReadInt16()); - reader.ReadInt16(); - reader.ReadInt16(); - for (int i = 0; i < questions; ++i) - { - string question; - do - { - question = reader.ReadString(); - } while (question != string.Empty); - reader.ReadUInt16(); - reader.ReadUInt16(); - } - var res = new List(); - for (var i = 0; i < answers; ++i) - { - reader.ReadInt16(); ; // ... - short type = IPAddress.NetworkToHostOrder((short)reader.ReadUInt16()); // type - reader.ReadUInt16(); // class - int ttl = IPAddress.NetworkToHostOrder(reader.ReadInt32()); // ttl - int rdlength = reader.ReadUInt16(); // length - if (type == 0x0021) - { - // SRV - reader.ReadUInt16(); - reader.ReadUInt16(); - short port = IPAddress.NetworkToHostOrder((short)reader.ReadUInt16()); // port - var result = new StringBuilder(); - string name; - do - { - name = reader.ReadString(); - result.Append(name); - result.Append('.'); - } while (name != string.Empty); - var item = new SRVRecord { Host = result.ToString().Substring(0, result.Length - 2), Port = port, Ttl = ttl }; - res.Add(item); - } - } - return res; - } - } -} diff --git a/SharpXMPP.Shared/SharpXMPP.Shared.csproj b/SharpXMPP.Shared/SharpXMPP.Shared.csproj index dd26e57..754a56c 100644 --- a/SharpXMPP.Shared/SharpXMPP.Shared.csproj +++ b/SharpXMPP.Shared/SharpXMPP.Shared.csproj @@ -31,4 +31,8 @@ + + + + diff --git a/SharpXMPP.Shared/XmppTcpConnection.cs b/SharpXMPP.Shared/XmppTcpConnection.cs index 0c67adb..0d8b77a 100644 --- a/SharpXMPP.Shared/XmppTcpConnection.cs +++ b/SharpXMPP.Shared/XmppTcpConnection.cs @@ -8,6 +8,7 @@ using System.Threading.Tasks; using System.Xml; using System.Xml.Linq; +using DnsClient; using SharpXMPP.Compat; using SharpXMPP.Errors; using SharpXMPP.XMPP; @@ -24,6 +25,12 @@ public class XmppTcpConnection : XmppConnection private object _terminationLock = new object(); private TcpClient _client; + /// + /// A list of name servers to use for the DNS lookup. Uses 1.1.1.1 by default. If empty, then will use the name + /// servers configured by the local network adapter(s). + /// + public IPAddress[] NameServers { get; set; } = { IPAddress.Parse("1.1.1.1") }; + protected virtual int TcpPort { get { return 5222; } @@ -294,13 +301,18 @@ private async Task> ResolveHostAddresses(CancellationToken cance { List HostAddresses = new List(); - var srvs = await Resolver.ResolveXMPPClient(Jid.Domain, cancellationToken); - if (srvs.Any()) + var lookup = NameServers.Length > 0 ? new LookupClient(NameServers) : new LookupClient(); + + var response = await lookup.QueryAsync( + "_xmpp-client._tcp." + Jid.Domain, + QueryType.SRV, + cancellationToken: cancellationToken); + if (response.Answers.SrvRecords().Any()) { - foreach (var srv in srvs) + foreach (var srv in response.Answers.SrvRecords()) { cancellationToken.ThrowIfCancellationRequested(); - var addresses = await Dns.GetHostAddressesAsync(srv.Host); + var addresses = await Dns.GetHostAddressesAsync(srv.Target.Value); HostAddresses.AddRange(addresses); } }