2using System.Collections.Generic;
3using System.ComponentModel;
5using System.Security.Authentication;
6using System.Security.Cryptography;
7using System.Security.Cryptography.X509Certificates;
10using System.Threading.Tasks;
27 private const int KeepAliveTimeSeconds = 30;
28 private const int MaxFragmentSize = 40000000;
30 private readonly Dictionary<string, bool> compressionMethods =
new Dictionary<string, bool>();
31 private readonly X509Certificate localDomainCertificate =
null;
32 private LinkedList<QueuedStanza> queue;
35 private Timer secondTimer =
null;
36 private DateTime nextPing = DateTime.MinValue;
37 private readonly StringBuilder fragment =
new StringBuilder();
38 private int fragmentLength = 0;
41 private readonly
object synchObject =
new object();
43 private LinkedList<Tuple<string, int, int>> stanzasOnHold =
null;
44 private string authKey =
null;
45 private string authStreamId =
null;
46 private readonly
string localStreamId;
47 private string remoteStreamId;
49 private string streamHeader;
50 private string streamFooter;
51 private readonly
string errorType =
"cancel";
52 private readonly
string errorXml =
"<remote-server-not-found xmlns='" + XmppServer.StanzaNamespace +
"'/>";
53 private double version;
54 private readonly
int port;
56 private int inputState = 0;
57 private int inputDepth = 0;
58 private int contentStart = 0;
59 private int contentEnd = 0;
60 private string pingId =
string.Empty;
61 private bool trustServer =
false;
62 private bool supportsPing =
true;
63 private bool pingResponse =
true;
64 private readonly
bool allowEncryption =
true;
65 private bool checkConnection =
false;
66 private readonly
bool incomingConnection;
67 private bool authResultRequestSent =
false;
68 private bool authVerifyRequestSent =
false;
69 private bool bidirectional =
false;
70 private bool openBracketReceived =
false;
71 private bool verified =
false;
72 private readonly
bool temporary =
false;
73 private bool upgradeToTlsAsClient =
false;
74 private bool upgradeToTlsAsServer =
false;
93 this.localDomainCertificate = DomainCertificate;
96 this.remoteStreamId =
null;
97 this.trustServer = TrustRemoteCertificate;
98 this.incomingConnection =
false;
113 this.client = Client;
114 this.server = Server;
116 this.remoteStreamId =
null;
117 this.localDomainCertificate = DomainCertificate;
118 this.trustServer = TrustRemoteCertificate;
119 this.incomingConnection =
true;
125 this.client.OnDisconnected += this.Client_OnDisconnected;
126 this.client.OnError += this.Client_OnError;
127 this.client.OnReceived += this.Client_OnReceived;
128 this.client.OnSent += this.Client_OnSent;
129 this.client.OnPaused += this.Client_OnPaused;
130 this.client.OnInformation += this.Client_OnInformation;
131 this.client.OnWarning += this.Client_OnWarning;
139 return this.
Connect(this.host);
145 public override string Type =>
"XMPP";
152 get {
return this.client?.
Client?.Client?.RemoteEndPoint?.ToString(); }
163 await this.DisposeClient();
166 this.checkConnection =
true;
168 this.pingResponse =
true;
169 this.upgradeToTlsAsClient =
false;
170 this.upgradeToTlsAsServer =
false;
174 this.client.OnDisconnected += this.Client_OnDisconnected;
175 this.client.OnError += this.Client_OnError;
176 this.client.OnReceived += this.Client_OnReceived;
177 this.client.OnSent += this.Client_OnSent;
178 this.client.OnPaused += this.Client_OnPaused;
179 this.client.OnInformation += this.Client_OnInformation;
180 this.client.OnWarning += this.Client_OnWarning;
182 if (!await this.client.
ConnectAsync(
this.host,
this.port))
187 if (!await this.BeginWrite(
"<?xml version='1.0' encoding='utf-8'?><stream:stream id='" + this.localStreamId +
"' to='" +
XML.
Encode(
this.remoteDomain) +
200 await this.ConnectionError(ex);
210 return this.DisposeClient();
213 private void ResetState()
218 this.compressionMethods.Clear();
221 private async Task ConnectionError(Exception ex)
225 await this.Exception(ex);
227 this.inputState = -1;
228 await this.DisposeClient();
231 if (!(this.authConnection is
null))
235 this.authConnection.KeyAuthenticated(
false,
"Connection Error: " + ex.Message);
237 catch (Exception ex2)
242 this.authConnection =
null;
246 private async Task Error(Exception Exception)
250 if (Exception is AggregateException ex)
252 foreach (Exception ex2
in ex.InnerExceptions)
253 await this.Error(ex2);
257 await this.Error(Exception.Message);
259 await this.
OnError.Raise(
this, EventArgs.Empty);
278 get => this.trustServer;
279 set => this.trustServer = value;
309 return this.SetState(NewState,
null);
317 internal async Task SetState(
XmppS2sState NewState,
string Reason)
319 if (this.state != NewState)
321 this.state = NewState;
323 StringBuilder sb =
new StringBuilder();
325 sb.Append(
"State changed to ");
326 sb.Append(NewState.ToString());
328 if (!
string.IsNullOrEmpty(Reason))
360 this.checkConnection =
false;
362 this.secondTimer?.Dispose();
363 this.secondTimer =
null;
367 this.state ==
XmppS2sState.StartingEncryptionAsClient ||
368 this.state ==
XmppS2sState.StartingEncryptionAsServer ||
374 await this.BeginWrite(this.streamFooter, async (Sender, e) =>
381 await this.CleanUp(
this,
XmppS2sState.Offline, ex.Message);
385 await this.CleanUp(
this, EventArgs.Empty);
394 return this.CleanUp(
this,
XmppS2sState.Offline,
"Goind hard-offline.");
397 private Task CleanUp(
object Sender, EventArgs e)
399 return this.CleanUp(Sender,
XmppS2sState.Offline,
"Closing connection.");
404 await this.SetState(
State, Reason);
406 this.compressionMethods?.Clear();
408 this.secondTimer?.Dispose();
409 this.secondTimer =
null;
411 await this.DisposeClient();
414 private async Task DisposeClient()
421 if (!
string.IsNullOrEmpty(this.server.DomainSnifferPath))
422 this.server.CacheSniffers(this.
Sniffers);
424 if (!(this.authConnection is
null))
428 this.authConnection.KeyAuthenticated(
false,
"Disposing connection client.");
435 this.authConnection =
null;
438 await this.
OnDisposed.Raise(
this, EventArgs.Empty,
false);
439 this.OnDisposed =
null;
441 this.server.S2sEndpointDisposed(
this);
449 private Task<bool> BeginWrite(
string Xml, EventHandlerAsync<DeliveryEventArgs> Callback,
object State)
451 this.nextPing = DateTime.Now.AddMilliseconds(this.keepAliveSeconds * 500);
452 return this.client?.
SendAsync(Xml, Callback,
State) ?? Task.FromResult(
false);
455 private async Task<bool> Client_OnSent(
object Sender,
string Text)
457 this.server?.DataTransmitted(this.client?.LastTransmittedBytes ?? 0);
462 private async Task<string> Client_OnWarning(
string Text)
468 private async Task<string> Client_OnInformation(
string Text)
474 private async Task<bool> Client_OnReceived(
object Sender,
string Text)
478 this.server?.DataReceived(this.client?.LastReceivedBytes ?? 0);
480 if (this.openBracketReceived)
482 this.openBracketReceived =
false;
485 else if (Text ==
"<")
486 this.openBracketReceived =
true;
490 return await this.ParseIncoming(Text);
494 await this.Exception(ex);
501 private async Task Client_OnError(
object Sender, Exception Exception)
504 await this.Error(Exception.Message);
508 private async Task Client_OnDisconnected(
object Sender, EventArgs e)
514 private const string FragmentTooBig =
"Fragment too big.";
515 private const string IllegalCharacterReceived =
"Illegal character received.";
517 private async Task<bool> ParseIncoming(
string s)
521 foreach (
char ch
in s)
523 switch (this.inputState)
528 this.fragment.Append(ch);
529 if (++this.fragmentLength > MaxFragmentSize)
531 await this.ToError(FragmentTooBig);
539 await this.ToError(IllegalCharacterReceived);
545 this.fragment.Append(ch);
546 if (++this.fragmentLength > MaxFragmentSize)
548 await this.ToError(FragmentTooBig);
557 if (!await this.ProcessStream(this.fragment.ToString()))
559 this.fragment.Clear();
560 this.fragmentLength = this.contentStart = this.contentEnd = 0;
565 if (++this.fragmentLength > MaxFragmentSize)
567 await this.ToError(FragmentTooBig);
572 this.fragment.Clear();
578 this.fragment.Append(ch);
579 if (++this.fragmentLength > MaxFragmentSize)
581 await this.ToError(FragmentTooBig);
588 await this.ToError(IllegalCharacterReceived);
594 this.fragment.Append(ch);
595 if (++this.fragmentLength > MaxFragmentSize)
597 await this.ToError(FragmentTooBig);
604 if (!await this.ProcessStream(this.fragment.ToString()))
606 this.fragment.Clear();
607 this.fragmentLength = this.contentStart = this.contentEnd = 0;
614 this.fragment.Append(ch);
615 if (++this.fragmentLength > MaxFragmentSize)
617 await this.ToError(FragmentTooBig);
623 else if (this.inputDepth > 1)
625 this.fragment.Append(ch);
626 if (++this.fragmentLength > MaxFragmentSize)
628 await this.ToError(FragmentTooBig);
634 await this.ToError(IllegalCharacterReceived);
640 this.fragment.Append(ch);
641 if (++this.fragmentLength > MaxFragmentSize)
643 await this.ToError(FragmentTooBig);
648 if (this.inputDepth == 2)
649 this.contentEnd = this.fragmentLength - 2;
657 await this.
Warning(
"Processing instruction received. Assuming connection reused. Resetting state.");
665 this.inputState = 13;
667 this.inputState += 2;
671 this.fragment.Append(ch);
672 if (++this.fragmentLength > MaxFragmentSize)
674 await this.ToError(FragmentTooBig);
680 if (this.inputDepth < 1)
682 this.inputState = -1;
683 await this.CleanUp(
this,
XmppS2sState.Offline,
"Closing tag.");
688 if (this.inputDepth == 1)
690 if (!await this.ProcessFragment(this.fragment.ToString(),
this.contentStart,
this.contentEnd -
this.contentStart))
693 this.fragment.Clear();
694 this.fragmentLength = this.contentStart = this.contentEnd = 0;
697 if (this.inputState > 0)
704 this.fragment.Append(ch);
705 if (++this.fragmentLength > MaxFragmentSize)
707 await this.ToError(FragmentTooBig);
712 if (this.inputDepth == 1)
713 this.contentStart = this.fragmentLength;
721 this.inputState += 2;
725 this.fragment.Append(ch);
726 if (++this.fragmentLength > MaxFragmentSize)
728 await this.ToError(FragmentTooBig);
733 if (this.inputDepth == 1)
735 if (!await this.ProcessFragment(this.fragment.ToString(),
this.contentStart,
this.contentEnd -
this.contentStart))
738 this.fragment.Clear();
739 this.fragmentLength = this.contentStart = this.contentEnd = 0;
742 if (this.inputState != 0)
750 this.fragment.Append(ch);
751 if (++this.fragmentLength > MaxFragmentSize)
753 await this.ToError(FragmentTooBig);
758 if (this.inputDepth == 1)
759 this.contentStart = this.fragmentLength;
769 this.inputState += 2;
773 this.fragment.Append(ch);
774 if (++this.fragmentLength > MaxFragmentSize)
776 await this.ToError(FragmentTooBig);
784 this.fragment.Append(ch);
785 if (++this.fragmentLength > MaxFragmentSize)
787 await this.ToError(FragmentTooBig);
791 this.inputState -= 2;
795 this.fragment.Append(ch);
796 if (++this.fragmentLength > MaxFragmentSize)
798 await this.ToError(FragmentTooBig);
804 this.inputState = 18;
807 await this.ToError(IllegalCharacterReceived);
813 this.fragment.Append(ch);
814 if (++this.fragmentLength > MaxFragmentSize)
816 await this.ToError(FragmentTooBig);
823 await this.ToError(IllegalCharacterReceived);
829 this.fragment.Append(ch);
830 if (++this.fragmentLength > MaxFragmentSize)
832 await this.ToError(FragmentTooBig);
840 this.fragment.Append(ch);
841 if (++this.fragmentLength > MaxFragmentSize)
843 await this.ToError(FragmentTooBig);
853 this.fragment.Append(ch);
854 if (++this.fragmentLength > MaxFragmentSize)
856 await this.ToError(FragmentTooBig);
862 this.inputState -= 2;
866 this.fragment.Append(ch);
867 if (++this.fragmentLength > MaxFragmentSize)
869 await this.ToError(FragmentTooBig);
876 await this.ToError(IllegalCharacterReceived);
882 this.fragment.Append(ch);
883 if (++this.fragmentLength > MaxFragmentSize)
885 await this.ToError(FragmentTooBig);
892 await this.ToError(IllegalCharacterReceived);
898 this.fragment.Append(ch);
899 if (++this.fragmentLength > MaxFragmentSize)
901 await this.ToError(FragmentTooBig);
908 await this.ToError(IllegalCharacterReceived);
914 this.fragment.Append(ch);
915 if (++this.fragmentLength > MaxFragmentSize)
917 await this.ToError(FragmentTooBig);
924 await this.ToError(IllegalCharacterReceived);
930 this.fragment.Append(ch);
931 if (++this.fragmentLength > MaxFragmentSize)
933 await this.ToError(FragmentTooBig);
940 await this.ToError(IllegalCharacterReceived);
946 this.fragment.Append(ch);
947 if (++this.fragmentLength > MaxFragmentSize)
949 await this.ToError(FragmentTooBig);
956 await this.ToError(IllegalCharacterReceived);
962 this.fragment.Append(ch);
963 if (++this.fragmentLength > MaxFragmentSize)
965 await this.ToError(FragmentTooBig);
973 this.fragment.Append(ch);
974 if (++this.fragmentLength > MaxFragmentSize)
976 await this.ToError(FragmentTooBig);
986 this.fragment.Append(ch);
987 if (++this.fragmentLength > MaxFragmentSize)
989 await this.ToError(FragmentTooBig);
995 this.inputState -= 2;
1006 private Task ToError(
string Reason)
1008 this.inputState = -1;
1012 private async Task<bool> ProcessStream(
string Xml)
1016 this.streamHeader = Xml;
1018 if (Xml.StartsWith(
"</"))
1020 await this.ConnectionError(
new System.Exception(
"Connection closed by the remote endpoint."));
1024 int i = Xml.IndexOf(
":stream");
1026 this.streamFooter =
"</stream>";
1028 this.streamFooter =
"</" + Xml.Substring(1, i - 1) +
":stream>";
1030 XmlDocument Doc =
new XmlDocument()
1032 PreserveWhitespace =
true
1034 Doc.LoadXml(Xml + this.streamFooter);
1036 if (Doc.DocumentElement.LocalName !=
"stream")
1037 throw new Exception(
"Invalid stream.");
1039 XmlElement Stream = Doc.DocumentElement;
1042 if (this.version < 1.0)
1043 throw new Exception(
"Version not supported.");
1047 if (this.incomingConnection)
1062 this.
Remove(InMemorySniffer);
1068 if (!this.server.IsServerDomain(To,
true))
1070 if (
string.IsNullOrEmpty(To))
1071 throw new Exception(
"No to attribute in S2S connection stream. From: " +
XML.
Attribute(Stream,
"from"));
1073 throw new Exception(
"Unexpected domain: " + To);
1077 this.localDomain = To;
1085 throw new Exception(
"Unexpected domain: " + From +
". Expected: " + this.
remoteDomain);
1092 if (this.incomingConnection)
1094 StringBuilder sb =
new StringBuilder();
1096 sb.Append(
"<?xml version='1.0' encoding='utf-8'?><stream:stream id='");
1097 sb.Append(this.localStreamId);
1098 sb.Append(
"' to='");
1099 sb.Append(
XML.
Encode(
this.remoteDomain));
1100 sb.Append(
"' from='");
1101 sb.Append(
XML.
Encode(
this.localDomain));
1102 sb.Append(
"' version='1.0' xmlns='jabber:server' xmlns:db='");
1104 sb.Append(
"' xmlns:stream='");
1106 sb.Append(
"'><stream:features><dialback xmlns='");
1108 sb.Append(
"'/></stream:features>");
1110 if (!await this.BeginWrite(sb.ToString(),
null,
null))
1116 if (!await this.SendQueued())
1123 if (this.incomingConnection)
1125 StringBuilder sb =
new StringBuilder();
1127 sb.Append(
"<?xml version='1.0' encoding='utf-8'?><stream:stream id='");
1128 sb.Append(this.localStreamId);
1129 sb.Append(
"' to='");
1130 sb.Append(
XML.
Encode(
this.remoteDomain));
1131 sb.Append(
"' from='");
1132 sb.Append(
XML.
Encode(
this.localDomain));
1133 sb.Append(
"' version='1.0' xmlns='jabber:server' xmlns:db='");
1135 sb.Append(
"' xmlns:stream='");
1137 sb.Append(
"'><stream:features>");
1143 if (!(this.remoteDomainCertificateDomains is
null) &&
1144 Array.IndexOf(
this.remoteDomainCertificateDomains,
this.remoteDomain) >= 0)
1146 sb.Append(
"<mechanisms xmlns='");
1148 sb.Append(
"'><mechanism>EXTERNAL</mechanism></mechanisms>");
1152 sb.Append(
"<dialback xmlns='");
1154 sb.Append(
"'><errors/></dialback>");
1158 sb.Append(
"<starttls xmlns='");
1160 sb.Append(
"'><required/></starttls>");
1163 sb.Append(
"<bidi xmlns='");
1165 sb.Append(
"'/></stream:features>");
1167 if (!await this.BeginWrite(sb.ToString(),
null,
null))
1173 catch (Exception ex)
1175 await this.ConnectionError(ex);
1181 private async Task<bool> SendQueued()
1184 Tuple<string, int, int> OnHold;
1189 lock (this.synchObject)
1191 if (this.queue is
null || this.queue.First is
null)
1197 Stanza = this.queue.First.Value;
1198 this.queue.RemoveFirst();
1199 if (this.queue.First is
null)
1206 await this.
Information(
"Sending queued stanzas.");
1211 lock (this.synchObject)
1213 if (this.queue is
null)
1214 this.queue =
new LinkedList<QueuedStanza>();
1216 this.queue.AddFirst(
Stanza);
1222 while (!(
Stanza is
null));
1228 lock (this.synchObject)
1230 if (this.stanzasOnHold is
null || this.stanzasOnHold.First is
null)
1232 this.stanzasOnHold =
null;
1236 OnHold = this.stanzasOnHold.First.Value;
1237 this.stanzasOnHold.RemoveFirst();
1238 if (this.stanzasOnHold.First is
null)
1239 this.stanzasOnHold =
null;
1245 await this.
Information(
"Processing incoming stanzas put on hold.");
1248 await this.
Information(OnHold.Item1.Substring(OnHold.Item2, OnHold.Item3));
1250 if (!await this.ProcessFragment(OnHold.Item1, OnHold.Item2, OnHold.Item3))
1252 lock (this.synchObject)
1254 if (this.stanzasOnHold is
null)
1255 this.stanzasOnHold =
new LinkedList<Tuple<string, int, int>>();
1257 this.stanzasOnHold.AddFirst(OnHold);
1263 while (!(OnHold is
null));
1268 private async Task<bool> ProcessFragment(
string Xml,
int ContentStart,
int ContentLen)
1279 Doc =
new XmlDocument()
1281 PreserveWhitespace =
true
1283 Doc.LoadXml(this.streamHeader + Xml + this.streamFooter);
1285 Stanza =
new Stanza(Doc.DocumentElement, Xml, ContentStart, ContentLen);
1290 switch (E.LocalName)
1297 lock (this.synchObject)
1299 if (this.stanzasOnHold is
null)
1300 this.stanzasOnHold =
new LinkedList<Tuple<string, int, int>>();
1302 this.stanzasOnHold.AddLast(
new Tuple<string, int, int>(Xml, ContentStart, ContentLen));
1305 await this.
Warning(
"Keeping stanza on hold while performing verification.");
1317 this.server.IncCounters(
"iq",
Type, From, To, E);
1319 if (!await this.CheckFrom(From))
1322 if (To.
Address ==
this.localDomain && From.
Address ==
this.remoteDomain && (
Type ==
"result" ||
Type ==
"error") && Id ==
this.pingId)
1324 this.pingResponse =
true;
1326 if (
Type ==
"error")
1327 this.supportsPing =
false;
1332 this.ProcessIq(Id, To, From,
Type, Language,
Stanza);
1340 lock (this.synchObject)
1342 if (this.stanzasOnHold is
null)
1343 this.stanzasOnHold =
new LinkedList<Tuple<string, int, int>>();
1345 this.stanzasOnHold.AddLast(
new Tuple<string, int, int>(Xml, ContentStart, ContentLen));
1348 await this.
Warning(
"Keeping stanza on hold while performing verification");
1360 this.server.IncCounters(
"message",
Type, From, To, E);
1362 if (!await this.CheckFrom(From))
1365 this.ProcessMessage(
Type, Id, To, From, Language,
Stanza);
1373 lock (this.synchObject)
1375 if (this.stanzasOnHold is
null)
1376 this.stanzasOnHold =
new LinkedList<Tuple<string, int, int>>();
1378 this.stanzasOnHold.AddLast(
new Tuple<string, int, int>(Xml, ContentStart, ContentLen));
1381 await this.
Warning(
"Keeping stanza on hold while performing verification");
1393 this.server.IncCounters(
"presence",
Type, From, To, E);
1395 if (!await this.CheckFrom(From))
1398 this.ProcessPresence(
Type, Id, To, From, Language,
Stanza);
1402 if (E.FirstChild is
null)
1403 this.DialbackCompleted(
false,
false,
"No features available.");
1406 bool StartTls =
false;
1407 bool Dialback =
false;
1408 bool Bidirectional =
false;
1409 bool ExternalAuth =
false;
1411 foreach (XmlNode N2
in E.ChildNodes)
1413 switch (N2.LocalName)
1420 foreach (XmlNode N3
in N2.ChildNodes)
1422 if (N3.LocalName ==
"method")
1423 this.compressionMethods[N3.InnerText.Trim().ToUpper()] =
true;
1428 Bidirectional =
true;
1436 foreach (XmlNode N3
in N2.ChildNodes)
1438 if (N3.LocalName ==
"mechanism")
1440 switch (N3.InnerText)
1443 ExternalAuth =
true;
1455 if (StartTls && this.allowEncryption)
1457 else if (ExternalAuth)
1459 sb =
new StringBuilder();
1463 sb.Append(
"<bidi xmlns='");
1468 sb.Append(
"<auth xmlns='");
1470 sb.Append(
"' mechanism='EXTERNAL'>");
1471 sb.Append(Convert.ToBase64String(Encoding.UTF8.GetBytes(
this.localDomain)));
1472 sb.Append(
"</auth>");
1474 return await this.BeginWrite(sb.ToString(),
null,
null);
1476 else if (Dialback && !this.verified)
1480 sb =
new StringBuilder();
1482 if (!
string.IsNullOrEmpty(this.authKey))
1484 sb.Append(
"<db:verify from='");
1485 sb.Append(
XML.
Encode(
this.localDomain));
1486 sb.Append(
"' to='");
1487 sb.Append(
XML.
Encode(
this.remoteDomain));
1488 sb.Append(
"' id='");
1489 sb.Append(
XML.
Encode(
this.authStreamId));
1492 sb.Append(
"</db:verify>");
1494 this.authVerifyRequestSent =
true;
1499 this.authResultRequestSent =
true;
1503 sb.Append(
"<bidi xmlns='");
1508 sb.Append(
"<db:result from='");
1509 sb.Append(
XML.
Encode(
this.localDomain));
1510 sb.Append(
"' to='");
1511 sb.Append(
XML.
Encode(
this.remoteDomain));
1513 sb.Append(this.authKey);
1514 sb.Append(
"</db:result>");
1517 return await this.BeginWrite(sb.ToString(),
null,
null);
1523 this.upgradeToTlsAsClient =
true;
1528 this.upgradeToTlsAsServer =
true;
1533 this.bidirectional =
true;
1537 sb =
new StringBuilder();
1539 sb.Append(
"<?xml version='1.0' encoding='utf-8'?><stream:stream id='");
1540 sb.Append(this.localStreamId);
1541 sb.Append(
"' to='");
1542 sb.Append(
XML.
Encode(
this.remoteDomain));
1543 sb.Append(
"' from='");
1544 sb.Append(
XML.
Encode(
this.localDomain));
1545 sb.Append(
"' version='1.0' xmlns='jabber:server' xmlns:db='");
1547 sb.Append(
"' xmlns:stream='");
1551 if (!await this.BeginWrite(sb.ToString(),
null,
null))
1554 this.verified =
true;
1555 this.DialbackCompleted(
true,
false,
null);
1559 this.DialbackCompleted(
false,
false,
"Authentication failed.");
1565 string s = E.InnerText;
1572 byte[] Bin = Convert.FromBase64String(s);
1573 Domain = Encoding.UTF8.GetString(Bin);
1577 bool ValidDomain = (
string.IsNullOrEmpty(Domain) || Domain == this.
remoteDomain);
1578 bool RemoteDomainsInCertificate = !(this.remoteDomainCertificateDomains is
null);
1579 bool DomainInCertificate = Array.IndexOf(this.remoteDomainCertificateDomains, this.
remoteDomain) >= 0;
1581 if (RemoteCertificateValid && ValidDomain && RemoteDomainsInCertificate && DomainInCertificate)
1586 this.verified =
true;
1587 this.DialbackCompleted(
true,
false,
null);
1591 if (!await this.BeginWrite(
"<failure xmlns='" +
XmppServer.
SaslNamespace +
"'><not-authorized/></failure>",
null,
null))
1594 if (!RemoteCertificateValid)
1595 this.DialbackCompleted(
false,
false,
"Remote certificate is not valid.");
1596 else if (!ValidDomain)
1597 this.DialbackCompleted(
false,
false,
"Presented domain not valid.");
1598 else if (!RemoteDomainsInCertificate)
1599 this.DialbackCompleted(
false,
false,
"Remote domains not available in certificate.");
1600 else if (!DomainInCertificate)
1601 this.DialbackCompleted(
false,
false,
"Presented domain not in certificate.");
1603 this.DialbackCompleted(
false,
false,
"Something went wrong.");
1612 if (
string.IsNullOrEmpty(
Type))
1616 sb =
new StringBuilder();
1618 sb.Append(
"<db:result to='");
1619 sb.Append(
XML.
Encode(
this.remoteDomain));
1620 sb.Append(
"' from='");
1621 sb.Append(
XML.
Encode(
this.localDomain));
1622 sb.Append(
"' valid='true'/>");
1626 await this.
Information(
"Performing dialback to validate domain name claim.");
1632 false,
"Performing dialback to validate domain name claim.");
1636 XmppS2SEndpoint.authKey = E.InnerText;
1637 XmppS2SEndpoint.authStreamId = this.localStreamId;
1638 XmppS2SEndpoint.authConnection =
this;
1641 catch (Exception ex)
1645 sb =
new StringBuilder();
1649 sb.Append(
"<db:result to='");
1650 sb.Append(
XML.
Encode(
this.remoteDomain));
1651 sb.Append(
"' from='");
1652 sb.Append(
XML.
Encode(
this.localDomain));
1653 sb.Append(
"' type='error'><error type='");
1656 sb.Append(ErrorXml);
1657 sb.Append(
"<text xmlns='");
1661 sb.Append(
"</text></error></db:result>");
1663 if (!await this.BeginWrite(sb.ToString(),
null,
null))
1668 else if (this.authResultRequestSent)
1672 if (
Type ==
"valid")
1674 Endpoint.verified =
true;
1675 Endpoint.DialbackCompleted(
true,
true,
null);
1678 Endpoint.DialbackCompleted(
false,
true,
"Remote end rejects authentication result.");
1680 if (!(this.authConnection is
null))
1682 this.authConnection =
null;
1683 await this.CleanUp(
this,
XmppS2sState.Offline,
"Dialback check completed.");
1697 if (
string.IsNullOrEmpty(
Type))
1699 string Key = this.server.GetDialbackKey(From.
Address, To.
Address, Id);
1700 sb =
new StringBuilder();
1702 this.verified = Key == E.InnerText;
1704 sb.Append(
"<db:verify to='");
1706 sb.Append(
"' from='");
1708 sb.Append(
"' id='");
1710 sb.Append(
"' type='");
1711 sb.Append(this.verified ?
"valid" :
"invalid");
1716 await this.BeginWrite(sb.ToString(), (Sender, e) =>
this.DisposeAsync(
"Verification failed."),
null);
1721 if (!await this.BeginWrite(sb.ToString(),
null,
null))
1724 if (this.verified && this.state ==
XmppS2sState.Connected)
1726 if (!await this.SendQueued())
1733 this.verified =
Type ==
"valid";
1735 if (!(this.authConnection is
null))
1739 this.authConnection.KeyAuthenticated(this.verified, this.verified ?
null :
"Remote validation failed.");
1741 catch (Exception ex)
1746 this.authConnection =
null;
1749 if (this.authVerifyRequestSent)
1751 await this.
DisposeAsync(
"Verification request already sent.");
1754 else if (this.verified)
1756 if (!await this.SendQueued())
1767 catch (Exception ex)
1769 await this.ConnectionError(ex);
1780 await this.server.ProcessIq(Id, To, From,
Type, Language,
Stanza,
this);
1782 catch (Exception ex)
1784 await this.Exception(ex);
1792 await this.server.Message(
Type, Id, To, From, Language,
Stanza,
this);
1794 catch (Exception ex)
1796 await this.Exception(ex);
1804 await this.server.Presence(
Type, Id, To, From, Language,
Stanza,
this);
1806 catch (Exception ex)
1808 await this.Exception(ex);
1812 private async Task Client_OnPaused(
object Sender, EventArgs e)
1814 if (this.upgradeToTlsAsClient || this.upgradeToTlsAsServer)
1816 bool AsClient = this.upgradeToTlsAsClient;
1818 this.upgradeToTlsAsClient =
false;
1819 this.upgradeToTlsAsServer =
false;
1821 string RemoteIpEndpoint;
1822 EndPoint EP = this.client.
Client.Client.RemoteEndPoint;
1824 if (EP is IPEndPoint IpEP)
1825 RemoteIpEndpoint = IpEP.Address.ToString();
1827 RemoteIpEndpoint = EP.ToString();
1835 await this.SetState(
XmppS2sState.StartingEncryptionAsClient);
1836 await this.client.
UpgradeToTlsAsClient(this.localDomainCertificate, SslProtocols.Tls12,
null,
this.trustServer,
this.remoteDomain);
1840 await this.SetState(
XmppS2sState.StartingEncryptionAsServer);
1846 this.remoteDomainCertificateDomains = await this.GetIdentities(this.client.
RemoteCertificate);
1850 StringBuilder sb =
new StringBuilder();
1853 sb.Append(
"Remote Certificate received. Valid: ");
1855 sb.Append(
", Subject: ");
1858 if (Subject.StartsWith(
"CN="))
1859 Subject = Subject.Substring(3);
1865 if (Name != Subject)
1869 sb.Append(
", Alternative Names: ");
1879 sb.Append(
", Issuer: ");
1881 sb.Append(
", S/N: ");
1883 sb.Append(
", Hash: ");
1895 await this.BeginWrite(
"<?xml version='1.0' encoding='utf-8'?><stream:stream id='" + this.localStreamId +
1896 "' from='" +
XML.
Encode(
this.localDomain) +
"' to='" +
XML.
Encode(
this.remoteDomain) +
1897 "' version='1.0' xmlns='jabber:server' xmlns:db='" +
DialbackNamespace +
"' xmlns:stream='" +
1901 catch (AuthenticationException ex)
1903 await this.LoginFailure(ex, AsClient, RemoteIpEndpoint);
1905 catch (Win32Exception ex)
1907 await this.LoginFailure(ex, AsClient, RemoteIpEndpoint);
1909 catch (Exception ex)
1911 await this.ConnectionError(ex);
1915 await this.ConnectionError(
new System.Exception(
"Remote endpoint rejected due to suspected TLS hacking."));
1919 private async Task LoginFailure(Exception ex,
bool AsClient,
string RemoteIpEndpoint)
1926 await this.ConnectionError(ex);
1930 private async Task<bool> CheckFrom(
XmppAddress From)
1940 if (FromDomain.
EndsWith(
"." +
this.remoteDomain, StringComparison.CurrentCultureIgnoreCase))
1943 await this.BeginWrite(
"<stream:error><invalid-from xmlns='urn:ietf:params:xml:ns:xmpp-streams'/></stream:error></stream:stream>", async (Sender, e) =>
1945 await this.CleanUp(
this,
XmppS2sState.Error,
"Invalid from attribute value.");
1951 private async
void KeyAuthenticated(
bool Valid,
string Reason)
1957 if (
string.IsNullOrEmpty(this.authKey))
1959 StringBuilder Xml =
new StringBuilder();
1961 Xml.Append(
"<db:result from='");
1962 Xml.Append(
XML.
Encode(
this.localDomain));
1963 Xml.Append(
"' to='");
1964 Xml.Append(
XML.
Encode(
this.remoteDomain));
1965 Xml.Append(
"' type='");
1966 Xml.Append(Valid ?
"valid" :
"invalid");
1969 await this.BeginWrite(Xml.ToString(),
null,
null);
1977 new KeyValuePair<string, object>(
"RemoteEndpoint",
this.client.Client.Client.RemoteEndPoint.ToString()));
1979 if (this.bidirectional && !this.temporary)
1980 this.server.RegisterS2SEndpoint(
this);
1982 await this.SendQueued();
1988 new KeyValuePair<string, object>(
"RemoteEndpoint",
this.client.Client.Client.RemoteEndPoint.ToString()),
1989 new KeyValuePair<string, object>(
"Reason", Reason));
1993 catch (Exception ex)
1995 await this.Exception(ex);
1999 internal static Exception GetStreamExceptionObject(XmlElement E)
2004 internal static Exception GetExceptionObject(XmlElement E,
string Namespace)
2006 string Msg =
string.Empty;
2008 foreach (XmlNode N2
in E.ChildNodes)
2010 if (N2.LocalName ==
"text")
2011 Msg = N2.InnerText.Trim();
2014 foreach (XmlNode N2
in E.ChildNodes)
2016 if (N2.NamespaceURI == Namespace)
2018 if (
string.IsNullOrEmpty(Msg))
2021 return new Exception(Msg);
2025 return new Exception(
string.IsNullOrEmpty(Msg) ?
"Unspecified error returned." : Msg);
2028 internal static Exception GetStanzaExceptionObject(XmlElement E)
2033 internal static Exception GetSaslExceptionObject(XmlElement E)
2038 internal async Task<CaseInsensitiveString[]> GetIdentities(X509Certificate certificate)
2040 List<CaseInsensitiveString> Domains =
new List<CaseInsensitiveString>();
2041 bool HasAlternativeNames =
false;
2045 foreach (
string Part
in certificate.Subject.Split(certificateSubjectSeparator, StringSplitOptions.None))
2047 if (Part.StartsWith(
"CN="))
2048 Domains.Add(Part.Substring(3));
2049 else if (Part.StartsWith(
"SAN="))
2051 Domains.Add(Part.Substring(4));
2052 HasAlternativeNames =
true;
2056 if (!HasAlternativeNames)
2058 if (!(certificate is X509Certificate2 Cert2))
2060 byte[] Bin = certificate.GetRawCertData();
2061 Cert2 =
new X509Certificate2(Bin);
2064 foreach (X509Extension Extension
in Cert2.Extensions)
2066 if (Extension.Oid.Value ==
"2.5.29.17")
2068 AsnEncodedData Parsed =
new AsnEncodedData(Extension.Oid, Extension.RawData);
2069 string[] SAN = Parsed.Format(
true).Split(
CommonTypes.
CRLF, StringSplitOptions.RemoveEmptyEntries);
2071 foreach (
string Name
in SAN)
2073 int i = Name.LastIndexOf(
'=');
2075 Domains.Add(Name.Substring(i + 1));
2081 catch (Exception ex)
2083 StringBuilder sb =
new StringBuilder();
2085 sb.Append(
"Unable to extract domain names from certificate subject (");
2086 sb.Append(ex.Message);
2091 byte[] Bin = certificate.GetRawCertData();
2094 sb.Append(Convert.ToBase64String(Bin, Base64FormattingOptions.InsertLineBreaks));
2098 sb.Append(
" No raw data in certificate.");
2101 await this.Error(sb.ToString());
2104 return Domains.ToArray();
2107 private readonly
static string[] certificateSubjectSeparator =
new string[] {
", " };
2109 private async
void DialbackCompleted(
bool Successful,
bool SendQueued,
string Reason)
2116 this.supportsPing =
true;
2117 this.secondTimer =
new Timer(this.SecondTimerCallback,
null, 1000, 1000);
2119 await this.
Information(
"XMPP S2S connection successful.");
2121 new KeyValuePair<string, object>(
"RemoteEndpoint",
this.client.Client.Client.RemoteEndPoint.ToString()));
2124 await this.SendQueued();
2128 await this.SetState(
XmppS2sState.Error,
"Dialback failed.");
2130 await this.
Information(
"XMPP S2S connection failed: " + Reason);
2132 new KeyValuePair<string, object>(
"RemoteEndpoint",
this.client.Client.Client.RemoteEndPoint.ToString()),
2133 new KeyValuePair<string, object>(
"Reason", Reason));
2137 lock (this.synchObject)
2140 this.stanzasOnHold =
null;
2145 catch (Exception ex)
2147 await this.Exception(ex);
2157 get => this.keepAliveSeconds;
2161 throw new ArgumentException(
"Value must be positive.", nameof(this.
KeepAliveSeconds));
2163 this.keepAliveSeconds = value;
2167 private async
void SecondTimerCallback(
object State)
2171 if (!this.checkConnection)
2174 DateTime Now = DateTime.Now;
2176 if (Now >= this.nextPing && this.state ==
XmppS2sState.Connected)
2180 this.nextPing = DateTime.Now.AddMilliseconds(this.keepAliveSeconds * 500);
2183 if (this.supportsPing)
2185 if (this.pingResponse)
2187 this.pingResponse =
false;
2194 await this.BeginWrite(
" ",
null,
null);
2196 catch (Exception ex)
2199 await this.Exception(ex);
2204 catch (Exception ex)
2215 StringBuilder Xml =
new StringBuilder();
2217 this.pingId = this.server.NewId(16);
2219 Xml.Append(
"<iq type='get' id='");
2220 Xml.Append(this.pingId);
2221 Xml.Append(
"' from ='");
2222 Xml.Append(
XML.
Encode(
this.localDomain));
2223 Xml.Append(
"' to='");
2224 Xml.Append(
XML.
Encode(
this.remoteDomain));
2225 Xml.Append(
"'><ping xmlns='");
2227 Xml.Append(
"'/></iq>");
2229 return this.BeginWrite(Xml.ToString(),
null,
null);
2237 return this.SendStanza(
"iq",
Type, Id, To, From, Language,
Stanza?.Content, Sender);
2243 return this.SendStanza(
"iq",
Type, Id, To, From, Language, ContentXml, Sender);
2246 internal async Task<bool> SendStanza(
string StanzaType,
string Type,
string Id,
XmppAddress To,
XmppAddress From,
string Language,
string ContentXml,
ISender Sender)
2250 lock (this.synchObject)
2258 if (this.queue is
null)
2259 this.queue =
new LinkedList<QueuedStanza>();
2261 this.queue.AddLast(
new QueuedStanza()
2263 StanzaType = StanzaType,
2268 Language = Language,
2269 ContentXml = ContentXml,
2279 if (!(Sender is
null))
2281 switch (StanzaType.ToLower())
2287 return !(await Sender.
IqError(Id, From, To, this.errorType, this.errorXml,
"S2S connection failed.",
"en") is
null);
2290 if (!await (Sender?.
MessageError(Id, From, To, this.errorType, this.errorXml,
"S2S connection failed.",
"en") ?? Task.FromResult(
true)))
2295 if (!await (Sender?.
PresenceError(Id, From, To, this.errorType, this.errorXml,
"S2S connection failed.",
"en") ?? Task.FromResult(
true)))
2305 if (!await this.BeginWrite(StanzaType,
Type, Id, To, From, Language, ContentXml))
2308 catch (Exception ex)
2310 if (!(Sender is
null))
2314 switch (StanzaType.ToLower())
2320 return !(await Sender.
IqError(Id, From, To, this.errorType, this.errorXml,
"S2S connection failed: " + ex.Message,
"en") is
null);
2323 if (!await (Sender?.
MessageError(Id, From, To, this.errorType, this.errorXml,
"S2S connection failed: " + ex.Message,
"en") ?? Task.FromResult(
true)))
2328 if (!await (Sender?.
PresenceError(Id, From, To, this.errorType, this.errorXml,
"S2S connection failed: " + ex.Message,
"en") ?? Task.FromResult(
true)))
2333 catch (Exception ex2)
2344 private Task<bool> BeginWrite(
string StanzaType,
string Type,
string Id,
XmppAddress To,
XmppAddress From,
string Language,
string ContentXml)
2346 StringBuilder Xml =
new StringBuilder();
2349 Xml.Append(StanzaType);
2353 Xml.Append(
" from='");
2359 Xml.Append(
"' to='");
2363 if (!
string.IsNullOrEmpty(
Type))
2365 Xml.Append(
"' type='");
2369 if (!
string.IsNullOrEmpty(Id))
2371 Xml.Append(
"' id='");
2375 if (!
string.IsNullOrEmpty(Language))
2377 Xml.Append(
"' xml:lang='");
2381 if (
string.IsNullOrEmpty(ContentXml))
2386 Xml.Append(ContentXml);
2388 Xml.Append(StanzaType);
2392 return this.BeginWrite(Xml.ToString(),
null,
null);
2398 return this.SendStanza(
"message",
Type, Id, To, From, Language,
Stanza?.Content, Sender);
2404 return this.SendStanza(
"message",
Type, Id, To, From, Language, ContentXml, Sender);
2410 return this.SendStanza(
"presence",
Type, Id, To, From, Language,
Stanza?.Content, Sender);
2416 return this.SendStanza(
"presence",
Type, Id, To, From, Language, ContentXml, Sender);
2426 StringBuilder Xml =
new StringBuilder();
2431 Xml.Append(ErrorXml);
2432 Xml.Append(
"</iq>");
2434 return this.BeginWrite(Xml.ToString(),
null,
null);
2447 StringBuilder Xml =
new StringBuilder();
2452 Xml.Append(ResultXml);
2453 Xml.Append(
"</iq>");
2455 return this.BeginWrite(Xml.ToString(),
null,
null);
2461 return this.SendStanza(
"presence",
Type, Id, To, From, Language, ContentXml,
null);
2468 StringBuilder Xml =
new StringBuilder();
2470 Xml.Append(
"<presence id='");
2472 Xml.Append(
"' from='");
2474 Xml.Append(
"' to='");
2476 Xml.Append(
"' type='error'>");
2477 Xml.Append(ErrorXml);
2478 Xml.Append(
"</presence>");
2480 return this.BeginWrite(Xml.ToString(),
null,
null);
2493 return this.
Message(Type, Id, To, From, Language, ContentXml,
null);
Helps with parsing of commong data types.
static readonly char[] CRLF
Contains the CR LF character sequence.
Helps with common XML-related tasks.
static string Attribute(XmlElement E, string Name)
Gets the value of an XML attribute.
static string Encode(string s)
Encodes a string for use in XML.
Static class managing the application event log. Applications and services log events on this static ...
static string CleanStackTrace(string StackTrace)
Cleans a Stack Trace string, removing entries from the asynchronous execution model,...
static void Exception(Exception Exception, string Object, string Actor, string EventId, EventLevel Level, string Facility, string Module, params KeyValuePair< string, object >[] Tags)
Logs an exception. Event type will be determined by the severity of the exception.
static void Warning(string Message, string Object, string Actor, string EventId, EventLevel Level, string Facility, string Module, string StackTrace, params KeyValuePair< string, object >[] Tags)
Logs a warning event.
static void Informational(string Message, string Object, string Actor, string EventId, EventLevel Level, string Facility, string Module, string StackTrace, params KeyValuePair< string, object >[] Tags)
Logs an informational event.
static Exception UnnestException(Exception Exception)
Unnests an exception, to extract the relevant inner exception.
Task UpgradeToTlsAsClient(SslProtocols Protocols)
Upgrades a client connection to TLS.
bool RemoteCertificateValid
If the remote certificate is valid.
TcpClient Client
Underlying TcpClient object.
void DisposeWhenDone()
Disposes the client when done sending all data.
void Continue()
Continues reading from the socket, if paused in an event handler.
X509Certificate RemoteCertificate
Certificate used by the remote endpoint.
Task< bool > ConnectAsync(string Host, int Port)
Connects to a host using TCP.
Task UpgradeToTlsAsServer(X509Certificate ServerCertificate)
Upgrades a server connection to TLS.
bool IsEncrypted
If connection is encrypted or not.
Task Exception(Exception Exception)
Called to inform the viewer of an exception state.
virtual bool Remove(ISniffer Sniffer)
ICommunicationLayer.Remove
ISniffer[] Sniffers
Registered sniffers.
Task Information(string Comment)
Called to inform the viewer of something.
Task TransmitText(string Text)
Called when text has been transmitted.
bool HasSniffers
If there are sniffers registered on the object.
Task ReceiveText(string Text)
Called when text has been received.
Task Warning(string Warning)
Called to inform the viewer of a warning state.
Sniffer that stores events in memory.
async Task ReplayAsync(CommunicationLayer ComLayer)
Replays sniffer events.
Implements a text-based TCP Client, by using the thread-safe full-duplex BinaryTcpClient.
virtual Task< bool > SendAsync(string Text)
Sends a text packet.
const string TlsNamespace
urn:ietf:params:xml:ns:xmpp-tls
const string StreamNamespace
http://etherx.jabber.org/streams
Abstract base class for server connections.
const string BidirectionalNamespace
urn:xmpp:bidi
const string DialbackFeaturesNamespace
urn:xmpp:features:dialback
DateTime Connected
When endpoint connected.
CaseInsensitiveString remoteDomain
Remote domain
CaseInsensitiveString LocalDomain
Local domain name.
const string BidirectionalFeatureNamespaces
urn:xmpp:features:bidi
CaseInsensitiveString RemoteDomain
Connection to domain.
const string DialbackNamespace
urn:xmpp:dialback
CaseInsensitiveString localDomain
Local domain
Contains information about a stanza.
XmlElement StanzaElement
Stanza element.
Contains information about one XMPP address.
bool IsEmpty
If the address is empty.
CaseInsensitiveString Domain
Domain
CaseInsensitiveString Address
XMPP Address
Class managing a connection.
Manages an XMPP server-to-server connection.
override Task< string > IqError(string Id, XmppAddress To, XmppAddress From, Exception ex)
Sends an IQ error stanza. If stanza was sent.
override Task< bool > IQ(string Type, string Id, XmppAddress To, XmppAddress From, string Language, Stanza Stanza, ISender Sender)
Sends an IQ stanza. If stanza was sent.
EventHandlerAsync OnError
Event raised when an error was encountered.
XmppS2sState State
Current state of connection.
override Task< bool > PresenceError(string Id, XmppAddress To, XmppAddress From, Exception ex)
Sends a presence error stanza. If stanza was sent.
EventHandlerAsync OnStateChanged
Event raised whenever the internal state of the connection changes.
async Task DisposeAsync(string Reason)
Closes the connection and disposes of all resources.
Task< bool > SendPing()
Sends an XMPP ping request.
override Task< bool > Message(string Type, string Id, XmppAddress To, XmppAddress From, string Language, string ContentXml, ISender Sender)
Sends a message stanza. If stanza was sent.
override Task< bool > Presence(string Type, string Id, XmppAddress To, XmppAddress From, string Language, Stanza Stanza, ISender Sender)
Sends a presence stanza. If stanza was sent.
Task HardOffline()
Closes the connection the hard way. This might disrupt stream processing, but can simulate a lost con...
EventHandlerAsync OnDisposed
Event raised when object is disposed.
EventHandlerAsync OnConnectionError
Event raised when a connection to a broker could not be made.
override Task< bool > Presence(string Type, string Id, XmppAddress To, XmppAddress From, string Language, string ContentXml, ISender Sender)
Sends a presence stanza. If stanza was sent.
override string Type
Type of endpoint
Task Close()
Closes the connection.
XmppS2SEndpoint(TextTcpClient Client, X509Certificate DomainCertificate, XmppServer Server, bool TrustRemoteCertificate, params ISniffer[] Sniffers)
Manages an XMPP server-to-server connection.
Task< bool > Connect()
Connects to the server.
override Task< bool > IQ(string Type, string Id, XmppAddress To, XmppAddress From, string Language, string ContentXml, ISender Sender)
Sends an IQ stanza. If stanza was sent.
XmppS2SEndpoint(CaseInsensitiveString LocalDomain, CaseInsensitiveString RemoteDomain, int Port, X509Certificate DomainCertificate, XmppServer Server, bool TrustRemoteCertificate, params ISniffer[] Sniffers)
Manages an XMPP server-to-server connection.
override Task DisposeAsync()
Closes the connection and disposes of all resources.
override Task< bool > Message(string Type, string Id, XmppAddress To, XmppAddress From, string Language, Stanza Stanza, ISender Sender)
Sends a message stanza. If stanza was sent.
override Task< bool > IqResult(string Id, XmppAddress To, XmppAddress From, string ResultXml)
Sends an IQ result stanza. If stanza was sent.
bool ServerCertificateValid
If the server certificate is valid.
override Task< bool > PresenceError(string Id, XmppAddress To, XmppAddress From, string ErrorXml)
Sends a presence error stanza. If stanza was sent.
override Task< bool > IqError(string Id, XmppAddress To, XmppAddress From, string ErrorXml)
Sends an IQ error stanza. If stanza was sent.
int KeepAliveSeconds
Number of seconds before a network connection risks being closed by the network, if no communication ...
override Task< bool > Presence(string Type, string Id, XmppAddress To, XmppAddress From, string Language, string ContentXml)
Sends a presence stanza. If stanza was sent.
override Task< bool > MessageError(string Id, XmppAddress To, XmppAddress From, Exception ex)
Sends a message error stanza. If stanza was sent.
async Task< bool > Connect(string Host)
Connects to the server.
override Task< bool > Message(string Type, string Id, XmppAddress To, XmppAddress From, string Language, string ContentXml)
Sends a message stanza. If stanza was sent.
bool TrustServer
If server should be trusted, regardless if the operating system could validate its certificate or not...
X509Certificate RemoteDomainCertificate
Certificate used by the remote server.
string GetRandomHexString(int NrBytes)
Generates a random hexadecimal string.
static void GetErrorInformation(Exception ex, out string Type, out string Xml)
Converts an Exception to an XMPP error message.
const string SaslNamespace
urn:ietf:params:xml:ns:xmpp-sasl
const string PingNamespace
urn:xmpp:ping (XEP-0199)
const string StanzaNamespace
urn:ietf:params:xml:ns:xmpp-stanzas (RFC 6120)
const string StreamsNamespace
urn:ietf:params:xml:ns:xmpp-streams
Represents a case-insensitive string.
bool EndsWith(CaseInsensitiveString value, StringComparison comparisonType)
Determines whether the end of this string instance matches the specified string when compared using t...
Class that monitors login events, and help applications determine malicious intent....
static async Task ReportTlsHackAttempt(string RemoteEndpoint, string Message, string Protocol)
Reports a TLS hacking attempt from an endpoint. Can be used to deny TLS negotiation to proceed,...
static bool CanStartTls(string RemoteEndpoint)
Checks if TLS negotiation can start, for a given endpoint. If the endpoint has tries a TLS hack attem...
Login state information relating to a remote endpoint
Interface for sniffers. Sniffers can be added to ICommunicationLayer classes to eavesdrop on communic...
Interface for XMPP S2S endpoints
Interface for senders of stanzas.
Task< string > IqError(string Id, XmppAddress To, XmppAddress From, Exception ex)
Sends an IQ Error stanza.
delegate Task EventHandlerAsync(object Sender, EventArgs e)
Asynchronous version of EventArgs.
XmppS2sState
State of XMPP connection.
ClientCertificates
Client Certificate Options