Neuron®
The Neuron® is the basis for the creation of open and secure federated networks for smart societies.
Loading...
Searching...
No Matches
XmppS2SEndpoint.cs
1using System;
2using System.Collections.Generic;
3using System.ComponentModel;
4using System.Net;
5using System.Security.Authentication;
6using System.Security.Cryptography;
7using System.Security.Cryptography.X509Certificates;
8using System.Text;
9using System.Threading;
10using System.Threading.Tasks;
11using System.Xml;
12using Waher.Content;
14using Waher.Events;
19
21{
26 {
27 private const int KeepAliveTimeSeconds = 30;
28 private const int MaxFragmentSize = 40000000;
29
30 private readonly Dictionary<string, bool> compressionMethods = new Dictionary<string, bool>();
31 private readonly X509Certificate localDomainCertificate = null;
32 private LinkedList<QueuedStanza> queue;
33 private CaseInsensitiveString[] remoteDomainCertificateDomains = null;
34 private TextTcpClient client = null;
35 private Timer secondTimer = null;
36 private DateTime nextPing = DateTime.MinValue;
37 private readonly StringBuilder fragment = new StringBuilder();
38 private int fragmentLength = 0;
39 private readonly XmppServer server;
40 private XmppS2sState state;
41 private readonly object synchObject = new object();
42 private XmppS2SEndpoint authConnection = null;
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;
48 private CaseInsensitiveString host;
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;
55 private int keepAliveSeconds = XmppS2SEndpoint.KeepAliveTimeSeconds;
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;
75
87 X509Certificate DomainCertificate, XmppServer Server, bool TrustRemoteCertificate, params ISniffer[] Sniffers)
89 {
90 this.port = Port;
91 this.host = RemoteDomain;
92 this.state = XmppS2sState.Connecting;
93 this.localDomainCertificate = DomainCertificate;
94 this.server = Server;
95 this.localStreamId = this.server.GetRandomHexString(16);
96 this.remoteStreamId = null;
97 this.trustServer = TrustRemoteCertificate;
98 this.incomingConnection = false;
99 }
100
109 public XmppS2SEndpoint(TextTcpClient Client, X509Certificate DomainCertificate, XmppServer Server, bool TrustRemoteCertificate,
110 params ISniffer[] Sniffers)
112 {
113 this.client = Client;
114 this.server = Server;
115 this.localStreamId = this.server.GetRandomHexString(16);
116 this.remoteStreamId = null;
117 this.localDomainCertificate = DomainCertificate;
118 this.trustServer = TrustRemoteCertificate;
119 this.incomingConnection = true;
120
121 this.state = XmppS2sState.StreamNegotiation;
122
123 this.ResetState();
124
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;
132 }
133
137 public Task<bool> Connect()
138 {
139 return this.Connect(this.host);
140 }
141
145 public override string Type => "XMPP";
146
150 public string RemoteEndpoint
151 {
152 get { return this.client?.Client?.Client?.RemoteEndPoint?.ToString(); }
153 }
154
159 public async Task<bool> Connect(string Host)
160 {
161 try
162 {
163 await this.DisposeClient();
164
165 this.host = Host;
166 this.checkConnection = true;
167 await this.SetState(XmppS2sState.Connecting);
168 this.pingResponse = true;
169 this.upgradeToTlsAsClient = false;
170 this.upgradeToTlsAsServer = false;
171
172 this.client = new TextTcpClient(XmppServer.encoding, true);
173
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;
181
182 if (!await this.client.ConnectAsync(this.host, this.port))
183 return false;
184
185 await this.SetState(XmppS2sState.StreamNegotiation);
186
187 if (!await this.BeginWrite("<?xml version='1.0' encoding='utf-8'?><stream:stream id='" + this.localStreamId + "' to='" + XML.Encode(this.remoteDomain) +
188 "' from='" + XML.Encode(this.localDomain) + "' version='1.0' xmlns='jabber:server' xmlns:db='" + DialbackNamespace +
189 "' xmlns:stream='" + XmppClientConnection.StreamNamespace + "'>", null, null))
190 {
191 return false;
192 }
193
194 this.ResetState();
195
196 return true;
197 }
198 catch (Exception ex)
199 {
200 await this.ConnectionError(ex);
201 return false;
202 }
203 }
204
208 public Task Close()
209 {
210 return this.DisposeClient();
211 }
212
213 private void ResetState()
214 {
215 this.inputState = 0;
216 this.inputDepth = 0;
217
218 this.compressionMethods.Clear();
219 }
220
221 private async Task ConnectionError(Exception ex)
222 {
223 await this.OnConnectionError.Raise(this, EventArgs.Empty);
224
225 await this.Exception(ex);
226
227 this.inputState = -1;
228 await this.DisposeClient();
229 await this.SetState(XmppS2sState.Error, ex.Message);
230
231 if (!(this.authConnection is null))
232 {
233 try
234 {
235 this.authConnection.KeyAuthenticated(false, "Connection Error: " + ex.Message);
236 }
237 catch (Exception ex2)
238 {
239 Log.Exception(ex2);
240 }
241
242 this.authConnection = null;
243 }
244 }
245
246 private async Task Error(Exception Exception)
247 {
248 Exception = Log.UnnestException(Exception);
249
250 if (Exception is AggregateException ex)
251 {
252 foreach (Exception ex2 in ex.InnerExceptions)
253 await this.Error(ex2);
254 }
255 else
256 {
257 await this.Error(Exception.Message);
258
259 await this.OnError.Raise(this, EventArgs.Empty);
260 }
261 }
262
267
271 public event EventHandlerAsync OnError = null;
272
276 public bool TrustServer
277 {
278 get => this.trustServer;
279 set => this.trustServer = value;
280 }
281
285 public X509Certificate RemoteDomainCertificate
286 {
287 get => this.client?.RemoteCertificate;
288 }
289
294 {
295 get => this.client?.RemoteCertificateValid ?? false;
296 }
297
301 public XmppS2sState State => this.state;
302
307 internal Task SetState(XmppS2sState NewState)
308 {
309 return this.SetState(NewState, null);
310 }
311
317 internal async Task SetState(XmppS2sState NewState, string Reason)
318 {
319 if (this.state != NewState)
320 {
321 this.state = NewState;
322
323 StringBuilder sb = new StringBuilder();
324
325 sb.Append("State changed to ");
326 sb.Append(NewState.ToString());
327
328 if (!string.IsNullOrEmpty(Reason))
329 {
330 sb.Append(" (");
331 sb.Append(Reason);
332 sb.Append(')');
333 }
334
335 await this.Information(sb.ToString());
336
337 await this.OnStateChanged.Raise(this, EventArgs.Empty);
338 }
339 }
340
345
349 public override Task DisposeAsync()
350 {
351 return this.DisposeAsync("Disposing object.");
352 }
353
358 public async Task DisposeAsync(string Reason)
359 {
360 this.checkConnection = false;
361
362 this.secondTimer?.Dispose();
363 this.secondTimer = null;
364
365 if (this.state == XmppS2sState.Connected ||
366 this.state == XmppS2sState.StreamOpened ||
367 this.state == XmppS2sState.StartingEncryptionAsClient ||
368 this.state == XmppS2sState.StartingEncryptionAsServer ||
369 this.state == XmppS2sState.Verifying ||
370 this.state == XmppS2sState.Dialback)
371 {
372 try
373 {
374 await this.BeginWrite(this.streamFooter, async (Sender, e) =>
375 {
376 await this.CleanUp(this, XmppS2sState.Offline, Reason);
377 }, null);
378 }
379 catch (Exception ex)
380 {
381 await this.CleanUp(this, XmppS2sState.Offline, ex.Message);
382 }
383 }
384 else
385 await this.CleanUp(this, EventArgs.Empty);
386 }
387
392 public Task HardOffline()
393 {
394 return this.CleanUp(this, XmppS2sState.Offline, "Goind hard-offline.");
395 }
396
397 private Task CleanUp(object Sender, EventArgs e)
398 {
399 return this.CleanUp(Sender, XmppS2sState.Offline, "Closing connection.");
400 }
401
402 private async Task CleanUp(object _, XmppS2sState State, string Reason)
403 {
404 await this.SetState(State, Reason);
405
406 this.compressionMethods?.Clear();
407
408 this.secondTimer?.Dispose();
409 this.secondTimer = null;
410
411 await this.DisposeClient();
412 }
413
414 private async Task DisposeClient()
415 {
416 TextTcpClient c = this.client;
417 this.client = null;
418
419 c?.DisposeWhenDone();
420
421 if (!string.IsNullOrEmpty(this.server.DomainSnifferPath))
422 this.server.CacheSniffers(this.Sniffers);
423
424 if (!(this.authConnection is null))
425 {
426 try
427 {
428 this.authConnection.KeyAuthenticated(false, "Disposing connection client.");
429 }
430 catch (Exception ex)
431 {
432 Log.Exception(ex);
433 }
434
435 this.authConnection = null;
436 }
437
438 await this.OnDisposed.Raise(this, EventArgs.Empty, false);
439 this.OnDisposed = null;
440
441 this.server.S2sEndpointDisposed(this);
442 }
443
447 public event EventHandlerAsync OnDisposed = null;
448
449 private Task<bool> BeginWrite(string Xml, EventHandlerAsync<DeliveryEventArgs> Callback, object State)
450 {
451 this.nextPing = DateTime.Now.AddMilliseconds(this.keepAliveSeconds * 500);
452 return this.client?.SendAsync(Xml, Callback, State) ?? Task.FromResult(false);
453 }
454
455 private async Task<bool> Client_OnSent(object Sender, string Text)
456 {
457 this.server?.DataTransmitted(this.client?.LastTransmittedBytes ?? 0);
458 await this.TransmitText(Text);
459 return true;
460 }
461
462 private async Task<string> Client_OnWarning(string Text)
463 {
464 await this.Warning(Text);
465 return Text;
466 }
467
468 private async Task<string> Client_OnInformation(string Text)
469 {
470 await this.Information(Text);
471 return Text;
472 }
473
474 private async Task<bool> Client_OnReceived(object Sender, string Text)
475 {
476 try
477 {
478 this.server?.DataReceived(this.client?.LastReceivedBytes ?? 0);
479
480 if (this.openBracketReceived)
481 {
482 this.openBracketReceived = false;
483 await this.ReceiveText("<" + Text);
484 }
485 else if (Text == "<")
486 this.openBracketReceived = true;
487 else
488 await this.ReceiveText(Text);
489
490 return await this.ParseIncoming(Text);
491 }
492 catch (Exception ex)
493 {
494 await this.Exception(ex);
495 await this.DisposeAsync(ex.Message);
496
497 return false;
498 }
499 }
500
501 private async Task Client_OnError(object Sender, Exception Exception)
502 {
503 await this.SetState(XmppS2sState.Error);
504 await this.Error(Exception.Message);
505 await this.DisposeAsync(Exception.Message);
506 }
507
508 private async Task Client_OnDisconnected(object Sender, EventArgs e)
509 {
510 await this.SetState(XmppS2sState.Offline);
511 await this.DisposeAsync("Client was disconnected.");
512 }
513
514 private const string FragmentTooBig = "Fragment too big.";
515 private const string IllegalCharacterReceived = "Illegal character received.";
516
517 private async Task<bool> ParseIncoming(string s)
518 {
519 bool Result = true;
520
521 foreach (char ch in s)
522 {
523 switch (this.inputState)
524 {
525 case 0: // Waiting for first <
526 if (ch == '<')
527 {
528 this.fragment.Append(ch);
529 if (++this.fragmentLength > MaxFragmentSize)
530 {
531 await this.ToError(FragmentTooBig);
532 return false;
533 }
534 else
535 this.inputState++;
536 }
537 else if (ch > ' ')
538 {
539 await this.ToError(IllegalCharacterReceived);
540 return false;
541 }
542 break;
543
544 case 1: // Waiting for ? or >
545 this.fragment.Append(ch);
546 if (++this.fragmentLength > MaxFragmentSize)
547 {
548 await this.ToError(FragmentTooBig);
549 return false;
550 }
551 else if (ch == '?')
552 this.inputState++;
553 else if (ch == '>')
554 {
555 this.inputState = 5;
556 this.inputDepth = 1;
557 if (!await this.ProcessStream(this.fragment.ToString()))
558 Result = false;
559 this.fragment.Clear();
560 this.fragmentLength = this.contentStart = this.contentEnd = 0;
561 }
562 break;
563
564 case 2: // In processing instruction. Waiting for ?>
565 if (++this.fragmentLength > MaxFragmentSize)
566 {
567 await this.ToError(FragmentTooBig);
568 return false;
569 }
570 else if (ch == '>')
571 {
572 this.fragment.Clear();
573 this.inputState++;
574 }
575 break;
576
577 case 3: // Waiting for <stream
578 this.fragment.Append(ch);
579 if (++this.fragmentLength > MaxFragmentSize)
580 {
581 await this.ToError(FragmentTooBig);
582 return false;
583 }
584 else if (ch == '<')
585 this.inputState++;
586 else if (ch > ' ')
587 {
588 await this.ToError(IllegalCharacterReceived);
589 return false;
590 }
591 break;
592
593 case 4: // Waiting for >
594 this.fragment.Append(ch);
595 if (++this.fragmentLength > MaxFragmentSize)
596 {
597 await this.ToError(FragmentTooBig);
598 return false;
599 }
600 else if (ch == '>')
601 {
602 this.inputState++;
603 this.inputDepth = 1;
604 if (!await this.ProcessStream(this.fragment.ToString()))
605 Result = false;
606 this.fragment.Clear();
607 this.fragmentLength = this.contentStart = this.contentEnd = 0;
608 }
609 break;
610
611 case 5: // Waiting for start element.
612 if (ch == '<')
613 {
614 this.fragment.Append(ch);
615 if (++this.fragmentLength > MaxFragmentSize)
616 {
617 await this.ToError(FragmentTooBig);
618 return false;
619 }
620 else
621 this.inputState++;
622 }
623 else if (this.inputDepth > 1)
624 {
625 this.fragment.Append(ch);
626 if (++this.fragmentLength > MaxFragmentSize)
627 {
628 await this.ToError(FragmentTooBig);
629 return false;
630 }
631 }
632 else if (ch > ' ')
633 {
634 await this.ToError(IllegalCharacterReceived);
635 return false;
636 }
637 break;
638
639 case 6: // Second character in tag
640 this.fragment.Append(ch);
641 if (++this.fragmentLength > MaxFragmentSize)
642 {
643 await this.ToError(FragmentTooBig);
644 return false;
645 }
646 else if (ch == '/')
647 {
648 if (this.inputDepth == 2)
649 this.contentEnd = this.fragmentLength - 2;
650
651 this.inputState++;
652 }
653 else if (ch == '?') // Unexpected processing instruction. Treat as if sender reinitiates connection on a reused connection without a graceful shutdown.
654 {
655 if (this.State != XmppS2sState.Connected && this.State != XmppS2sState.Verifying && this.State != XmppS2sState.Dialback)
656 {
657 await this.Warning("Processing instruction received. Assuming connection reused. Resetting state.");
658 await this.SetState(XmppS2sState.StreamNegotiation);
659 }
660
661 this.inputState = 2;
662 this.inputDepth = 0;
663 }
664 else if (ch == '!')
665 this.inputState = 13;
666 else
667 this.inputState += 2;
668 break;
669
670 case 7: // Waiting for end of closing tag
671 this.fragment.Append(ch);
672 if (++this.fragmentLength > MaxFragmentSize)
673 {
674 await this.ToError(FragmentTooBig);
675 return false;
676 }
677 else if (ch == '>')
678 {
679 this.inputDepth--;
680 if (this.inputDepth < 1)
681 {
682 this.inputState = -1;
683 await this.CleanUp(this, XmppS2sState.Offline, "Closing tag.");
684 return false;
685 }
686 else
687 {
688 if (this.inputDepth == 1)
689 {
690 if (!await this.ProcessFragment(this.fragment.ToString(), this.contentStart, this.contentEnd - this.contentStart))
691 Result = false;
692
693 this.fragment.Clear();
694 this.fragmentLength = this.contentStart = this.contentEnd = 0;
695 }
696
697 if (this.inputState > 0)
698 this.inputState = 5;
699 }
700 }
701 break;
702
703 case 8: // Wait for end of start tag
704 this.fragment.Append(ch);
705 if (++this.fragmentLength > MaxFragmentSize)
706 {
707 await this.ToError(FragmentTooBig);
708 return false;
709 }
710 else if (ch == '>')
711 {
712 if (this.inputDepth == 1)
713 this.contentStart = this.fragmentLength;
714
715 this.inputDepth++;
716 this.inputState = 5;
717 }
718 else if (ch == '/')
719 this.inputState++;
720 else if (ch <= ' ')
721 this.inputState += 2;
722 break;
723
724 case 9: // Check for end of childless tag.
725 this.fragment.Append(ch);
726 if (++this.fragmentLength > MaxFragmentSize)
727 {
728 await this.ToError(FragmentTooBig);
729 return false;
730 }
731 else if (ch == '>')
732 {
733 if (this.inputDepth == 1)
734 {
735 if (!await this.ProcessFragment(this.fragment.ToString(), this.contentStart, this.contentEnd - this.contentStart))
736 Result = false;
737
738 this.fragment.Clear();
739 this.fragmentLength = this.contentStart = this.contentEnd = 0;
740 }
741
742 if (this.inputState != 0)
743 this.inputState = 5;
744 }
745 else
746 this.inputState--;
747 break;
748
749 case 10: // Check for attributes.
750 this.fragment.Append(ch);
751 if (++this.fragmentLength > MaxFragmentSize)
752 {
753 await this.ToError(FragmentTooBig);
754 return false;
755 }
756 else if (ch == '>')
757 {
758 if (this.inputDepth == 1)
759 this.contentStart = this.fragmentLength;
760
761 this.inputDepth++;
762 this.inputState = 5;
763 }
764 else if (ch == '/')
765 this.inputState--;
766 else if (ch == '"')
767 this.inputState++;
768 else if (ch == '\'')
769 this.inputState += 2;
770 break;
771
772 case 11: // Double quote attribute.
773 this.fragment.Append(ch);
774 if (++this.fragmentLength > MaxFragmentSize)
775 {
776 await this.ToError(FragmentTooBig);
777 return false;
778 }
779 else if (ch == '"')
780 this.inputState--;
781 break;
782
783 case 12: // Single quote attribute.
784 this.fragment.Append(ch);
785 if (++this.fragmentLength > MaxFragmentSize)
786 {
787 await this.ToError(FragmentTooBig);
788 return false;
789 }
790 else if (ch == '\'')
791 this.inputState -= 2;
792 break;
793
794 case 13: // Third character in start of comment
795 this.fragment.Append(ch);
796 if (++this.fragmentLength > MaxFragmentSize)
797 {
798 await this.ToError(FragmentTooBig);
799 return false;
800 }
801 else if (ch == '-')
802 this.inputState++;
803 else if (ch == '[')
804 this.inputState = 18;
805 else
806 {
807 await this.ToError(IllegalCharacterReceived);
808 return false;
809 }
810 break;
811
812 case 14: // Fourth character in start of comment
813 this.fragment.Append(ch);
814 if (++this.fragmentLength > MaxFragmentSize)
815 {
816 await this.ToError(FragmentTooBig);
817 return false;
818 }
819 else if (ch == '-')
820 this.inputState++;
821 else
822 {
823 await this.ToError(IllegalCharacterReceived);
824 return false;
825 }
826 break;
827
828 case 15: // In comment
829 this.fragment.Append(ch);
830 if (++this.fragmentLength > MaxFragmentSize)
831 {
832 await this.ToError(FragmentTooBig);
833 return false;
834 }
835 else if (ch == '-')
836 this.inputState++;
837 break;
838
839 case 16: // Second character in end of comment
840 this.fragment.Append(ch);
841 if (++this.fragmentLength > MaxFragmentSize)
842 {
843 await this.ToError(FragmentTooBig);
844 return false;
845 }
846 else if (ch == '-')
847 this.inputState++;
848 else
849 this.inputState--;
850 break;
851
852 case 17: // Third character in end of comment
853 this.fragment.Append(ch);
854 if (++this.fragmentLength > MaxFragmentSize)
855 {
856 await this.ToError(FragmentTooBig);
857 return false;
858 }
859 else if (ch == '>')
860 this.inputState = 5;
861 else
862 this.inputState -= 2;
863 break;
864
865 case 18: // Fourth character in start of CDATA
866 this.fragment.Append(ch);
867 if (++this.fragmentLength > MaxFragmentSize)
868 {
869 await this.ToError(FragmentTooBig);
870 return false;
871 }
872 else if (ch == 'C')
873 this.inputState++;
874 else
875 {
876 await this.ToError(IllegalCharacterReceived);
877 return false;
878 }
879 break;
880
881 case 19: // Fifth character in start of CDATA
882 this.fragment.Append(ch);
883 if (++this.fragmentLength > MaxFragmentSize)
884 {
885 await this.ToError(FragmentTooBig);
886 return false;
887 }
888 else if (ch == 'D')
889 this.inputState++;
890 else
891 {
892 await this.ToError(IllegalCharacterReceived);
893 return false;
894 }
895 break;
896
897 case 20: // Sixth character in start of CDATA
898 this.fragment.Append(ch);
899 if (++this.fragmentLength > MaxFragmentSize)
900 {
901 await this.ToError(FragmentTooBig);
902 return false;
903 }
904 else if (ch == 'A')
905 this.inputState++;
906 else
907 {
908 await this.ToError(IllegalCharacterReceived);
909 return false;
910 }
911 break;
912
913 case 21: // Seventh character in start of CDATA
914 this.fragment.Append(ch);
915 if (++this.fragmentLength > MaxFragmentSize)
916 {
917 await this.ToError(FragmentTooBig);
918 return false;
919 }
920 else if (ch == 'T')
921 this.inputState++;
922 else
923 {
924 await this.ToError(IllegalCharacterReceived);
925 return false;
926 }
927 break;
928
929 case 22: // Eighth character in start of CDATA
930 this.fragment.Append(ch);
931 if (++this.fragmentLength > MaxFragmentSize)
932 {
933 await this.ToError(FragmentTooBig);
934 return false;
935 }
936 else if (ch == 'A')
937 this.inputState++;
938 else
939 {
940 await this.ToError(IllegalCharacterReceived);
941 return false;
942 }
943 break;
944
945 case 23: // Ninth character in start of CDATA
946 this.fragment.Append(ch);
947 if (++this.fragmentLength > MaxFragmentSize)
948 {
949 await this.ToError(FragmentTooBig);
950 return false;
951 }
952 else if (ch == '[')
953 this.inputState++;
954 else
955 {
956 await this.ToError(IllegalCharacterReceived);
957 return false;
958 }
959 break;
960
961 case 24: // In CDATA
962 this.fragment.Append(ch);
963 if (++this.fragmentLength > MaxFragmentSize)
964 {
965 await this.ToError(FragmentTooBig);
966 return false;
967 }
968 else if (ch == ']')
969 this.inputState++;
970 break;
971
972 case 25: // Second character in end of CDATA
973 this.fragment.Append(ch);
974 if (++this.fragmentLength > MaxFragmentSize)
975 {
976 await this.ToError(FragmentTooBig);
977 return false;
978 }
979 else if (ch == ']')
980 this.inputState++;
981 else
982 this.inputState--;
983 break;
984
985 case 26: // Third character in end of CDATA
986 this.fragment.Append(ch);
987 if (++this.fragmentLength > MaxFragmentSize)
988 {
989 await this.ToError(FragmentTooBig);
990 return false;
991 }
992 else if (ch == '>')
993 this.inputState = 5;
994 else if (ch != ']')
995 this.inputState -= 2;
996 break;
997
998 default:
999 break;
1000 }
1001 }
1002
1003 return Result;
1004 }
1005
1006 private Task ToError(string Reason)
1007 {
1008 this.inputState = -1;
1009 return this.CleanUp(this, XmppS2sState.Error, Reason);
1010 }
1011
1012 private async Task<bool> ProcessStream(string Xml)
1013 {
1014 try
1015 {
1016 this.streamHeader = Xml;
1017
1018 if (Xml.StartsWith("</"))
1019 {
1020 await this.ConnectionError(new System.Exception("Connection closed by the remote endpoint."));
1021 return false;
1022 }
1023
1024 int i = Xml.IndexOf(":stream");
1025 if (i < 0)
1026 this.streamFooter = "</stream>";
1027 else
1028 this.streamFooter = "</" + Xml.Substring(1, i - 1) + ":stream>";
1029
1030 XmlDocument Doc = new XmlDocument()
1031 {
1032 PreserveWhitespace = true
1033 };
1034 Doc.LoadXml(Xml + this.streamFooter);
1035
1036 if (Doc.DocumentElement.LocalName != "stream")
1037 throw new Exception("Invalid stream.");
1038
1039 XmlElement Stream = Doc.DocumentElement;
1040
1041 this.version = XML.Attribute(Stream, "version", 0.0);
1042 if (this.version < 1.0)
1043 throw new Exception("Version not supported.");
1044
1045 this.remoteStreamId = XML.Attribute(Stream, "id");
1046
1047 if (this.incomingConnection)
1048 {
1049 this.remoteDomain = XML.Attribute(Stream, "from");
1050
1052
1053 foreach (ISniffer Sniffer in this.Sniffers)
1054 {
1055 InMemorySniffer = Sniffer as InMemorySniffer;
1056 if (!(InMemorySniffer is null))
1057 break;
1058 }
1059
1060 if (!(InMemorySniffer is null))
1061 {
1062 this.Remove(InMemorySniffer);
1063 this.server.AddS2SSniffers(this, this.remoteDomain);
1064 await InMemorySniffer.ReplayAsync(this);
1065 }
1066
1067 string To = XML.Attribute(Stream, "to");
1068 if (!this.server.IsServerDomain(To, true))
1069 {
1070 if (string.IsNullOrEmpty(To))
1071 throw new Exception("No to attribute in S2S connection stream. From: " + XML.Attribute(Stream, "from"));
1072 else
1073 throw new Exception("Unexpected domain: " + To);
1074 }
1075
1076 if (string.IsNullOrEmpty(this.localDomain))
1077 this.localDomain = To;
1078
1079 this.trustServer = XmppServer.TrustCertificate(this.remoteDomain);
1080 }
1081 else if (this.state == XmppS2sState.Connected)
1082 {
1083 string From = XML.Attribute(Stream, "from");
1084 if (this.remoteDomain != From)
1085 throw new Exception("Unexpected domain: " + From + ". Expected: " + this.remoteDomain);
1086 }
1087
1088 if (this.state == XmppS2sState.Connected ||
1089 this.state == XmppS2sState.Verifying ||
1090 this.state == XmppS2sState.Dialback)
1091 {
1092 if (this.incomingConnection)
1093 {
1094 StringBuilder sb = new StringBuilder();
1095
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='");
1103 sb.Append(DialbackNamespace);
1104 sb.Append("' xmlns:stream='");
1106 sb.Append("'><stream:features><dialback xmlns='");
1107 sb.Append(DialbackFeaturesNamespace);
1108 sb.Append("'/></stream:features>");
1109
1110 if (!await this.BeginWrite(sb.ToString(), null, null))
1111 return false;
1112 }
1113
1114 if (this.verified)
1115 {
1116 if (!await this.SendQueued())
1117 return false;
1118 }
1119
1120 return true;
1121 }
1122
1123 if (this.incomingConnection)
1124 {
1125 StringBuilder sb = new StringBuilder();
1126
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='");
1134 sb.Append(DialbackNamespace);
1135 sb.Append("' xmlns:stream='");
1137 sb.Append("'><stream:features>");
1138
1139 if (this.client.IsEncrypted)
1140 {
1141 if (this.client.RemoteCertificateValid)
1142 {
1143 if (!(this.remoteDomainCertificateDomains is null) &&
1144 Array.IndexOf(this.remoteDomainCertificateDomains, this.remoteDomain) >= 0)
1145 {
1146 sb.Append("<mechanisms xmlns='");
1147 sb.Append(XmppServer.SaslNamespace);
1148 sb.Append("'><mechanism>EXTERNAL</mechanism></mechanisms>");
1149 }
1150 }
1151
1152 sb.Append("<dialback xmlns='");
1153 sb.Append(DialbackFeaturesNamespace);
1154 sb.Append("'><errors/></dialback>");
1155 }
1156 else
1157 {
1158 sb.Append("<starttls xmlns='");
1160 sb.Append("'><required/></starttls>");
1161 }
1162
1163 sb.Append("<bidi xmlns='");
1165 sb.Append("'/></stream:features>");
1166
1167 if (!await this.BeginWrite(sb.ToString(), null, null))
1168 return false;
1169 }
1170
1171 await this.SetState(XmppS2sState.StreamOpened);
1172 }
1173 catch (Exception ex)
1174 {
1175 await this.ConnectionError(ex);
1176 }
1177
1178 return true;
1179 }
1180
1181 private async Task<bool> SendQueued()
1182 {
1183 QueuedStanza Stanza;
1184 Tuple<string, int, int> OnHold;
1185 bool First = true;
1186
1187 do
1188 {
1189 lock (this.synchObject)
1190 {
1191 if (this.queue is null || this.queue.First is null)
1192 {
1193 this.queue = null;
1194 break;
1195 }
1196
1197 Stanza = this.queue.First.Value;
1198 this.queue.RemoveFirst();
1199 if (this.queue.First is null)
1200 this.queue = null;
1201 }
1202
1203 if (First)
1204 {
1205 First = false;
1206 await this.Information("Sending queued stanzas.");
1207 }
1208
1209 if (!await this.BeginWrite(Stanza.StanzaType, Stanza.Type, Stanza.Id, Stanza.To, Stanza.From, Stanza.Language, Stanza.ContentXml))
1210 {
1211 lock (this.synchObject)
1212 {
1213 if (this.queue is null)
1214 this.queue = new LinkedList<QueuedStanza>();
1215
1216 this.queue.AddFirst(Stanza);
1217 }
1218
1219 return false;
1220 }
1221 }
1222 while (!(Stanza is null));
1223
1224 First = true;
1225
1226 do
1227 {
1228 lock (this.synchObject)
1229 {
1230 if (this.stanzasOnHold is null || this.stanzasOnHold.First is null)
1231 {
1232 this.stanzasOnHold = null;
1233 break;
1234 }
1235
1236 OnHold = this.stanzasOnHold.First.Value;
1237 this.stanzasOnHold.RemoveFirst();
1238 if (this.stanzasOnHold.First is null)
1239 this.stanzasOnHold = null;
1240 }
1241
1242 if (First)
1243 {
1244 First = false;
1245 await this.Information("Processing incoming stanzas put on hold.");
1246 }
1247
1248 await this.Information(OnHold.Item1.Substring(OnHold.Item2, OnHold.Item3));
1249
1250 if (!await this.ProcessFragment(OnHold.Item1, OnHold.Item2, OnHold.Item3))
1251 {
1252 lock (this.synchObject)
1253 {
1254 if (this.stanzasOnHold is null)
1255 this.stanzasOnHold = new LinkedList<Tuple<string, int, int>>();
1256
1257 this.stanzasOnHold.AddFirst(OnHold);
1258 }
1259
1260 return false;
1261 }
1262 }
1263 while (!(OnHold is null));
1264
1265 return true;
1266 }
1267
1268 private async Task<bool> ProcessFragment(string Xml, int ContentStart, int ContentLen)
1269 {
1270 Stanza Stanza;
1271 XmlDocument Doc;
1272 XmlElement E;
1273 StringBuilder sb;
1274
1275 try
1276 {
1277 this.server.TouchServerConnection(this.remoteDomain);
1278
1279 Doc = new XmlDocument()
1280 {
1281 PreserveWhitespace = true
1282 };
1283 Doc.LoadXml(this.streamHeader + Xml + this.streamFooter);
1284
1285 Stanza = new Stanza(Doc.DocumentElement, Xml, ContentStart, ContentLen);
1287 if (E is null)
1288 return true;
1289
1290 switch (E.LocalName)
1291 {
1292 case "iq":
1293 if (this.state != XmppS2sState.Connected)
1294 {
1295 if (this.state == XmppS2sState.Dialback || this.state == XmppS2sState.Verifying)
1296 {
1297 lock (this.synchObject)
1298 {
1299 if (this.stanzasOnHold is null)
1300 this.stanzasOnHold = new LinkedList<Tuple<string, int, int>>();
1301
1302 this.stanzasOnHold.AddLast(new Tuple<string, int, int>(Xml, ContentStart, ContentLen));
1303 }
1304
1305 await this.Warning("Keeping stanza on hold while performing verification.");
1306 }
1307
1308 break;
1309 }
1310
1311 string Type = XML.Attribute(E, "type");
1312 string Id = XML.Attribute(E, "id");
1313 XmppAddress To = new XmppAddress(XML.Attribute(E, "to"));
1314 XmppAddress From = new XmppAddress(XML.Attribute(E, "from"));
1315 string Language = XML.Attribute(E, "xml:lang");
1316
1317 this.server.IncCounters("iq", Type, From, To, E);
1318
1319 if (!await this.CheckFrom(From))
1320 return true;
1321
1322 if (To.Address == this.localDomain && From.Address == this.remoteDomain && (Type == "result" || Type == "error") && Id == this.pingId)
1323 {
1324 this.pingResponse = true;
1325
1326 if (Type == "error")
1327 this.supportsPing = false;
1328
1329 break;
1330 }
1331
1332 this.ProcessIq(Id, To, From, Type, Language, Stanza);
1333 break;
1334
1335 case "message":
1336 if (this.state != XmppS2sState.Connected)
1337 {
1338 if (this.state == XmppS2sState.Dialback || this.state == XmppS2sState.Verifying)
1339 {
1340 lock (this.synchObject)
1341 {
1342 if (this.stanzasOnHold is null)
1343 this.stanzasOnHold = new LinkedList<Tuple<string, int, int>>();
1344
1345 this.stanzasOnHold.AddLast(new Tuple<string, int, int>(Xml, ContentStart, ContentLen));
1346 }
1347
1348 await this.Warning("Keeping stanza on hold while performing verification");
1349 }
1350
1351 break;
1352 }
1353
1354 Type = XML.Attribute(E, "type");
1355 Id = XML.Attribute(E, "id");
1356 To = new XmppAddress(XML.Attribute(E, "to"));
1357 From = new XmppAddress(XML.Attribute(E, "from"));
1358 Language = XML.Attribute(E, "xml:lang");
1359
1360 this.server.IncCounters("message", Type, From, To, E);
1361
1362 if (!await this.CheckFrom(From))
1363 return true;
1364
1365 this.ProcessMessage(Type, Id, To, From, Language, Stanza);
1366 break;
1367
1368 case "presence":
1369 if (this.state != XmppS2sState.Connected)
1370 {
1371 if (this.state == XmppS2sState.Dialback || this.state == XmppS2sState.Verifying)
1372 {
1373 lock (this.synchObject)
1374 {
1375 if (this.stanzasOnHold is null)
1376 this.stanzasOnHold = new LinkedList<Tuple<string, int, int>>();
1377
1378 this.stanzasOnHold.AddLast(new Tuple<string, int, int>(Xml, ContentStart, ContentLen));
1379 }
1380
1381 await this.Warning("Keeping stanza on hold while performing verification");
1382 }
1383
1384 break;
1385 }
1386
1387 Type = XML.Attribute(E, "type");
1388 Id = XML.Attribute(E, "id");
1389 To = new XmppAddress(XML.Attribute(E, "to"));
1390 From = new XmppAddress(XML.Attribute(E, "from"));
1391 Language = XML.Attribute(E, "xml:lang");
1392
1393 this.server.IncCounters("presence", Type, From, To, E);
1394
1395 if (!await this.CheckFrom(From))
1396 return true;
1397
1398 this.ProcessPresence(Type, Id, To, From, Language, Stanza);
1399 break;
1400
1401 case "features":
1402 if (E.FirstChild is null)
1403 this.DialbackCompleted(false, false, "No features available.");
1404 else
1405 {
1406 bool StartTls = false;
1407 bool Dialback = false;
1408 bool Bidirectional = false;
1409 bool ExternalAuth = false;
1410
1411 foreach (XmlNode N2 in E.ChildNodes)
1412 {
1413 switch (N2.LocalName)
1414 {
1415 case "starttls":
1416 StartTls = true;
1417 break;
1418
1419 case "compression":
1420 foreach (XmlNode N3 in N2.ChildNodes)
1421 {
1422 if (N3.LocalName == "method")
1423 this.compressionMethods[N3.InnerText.Trim().ToUpper()] = true;
1424 }
1425 break;
1426
1427 case "bidi":
1428 Bidirectional = true;
1429 break;
1430
1431 case "dialback":
1432 Dialback = true;
1433 break;
1434
1435 case "mechanisms":
1436 foreach (XmlNode N3 in N2.ChildNodes)
1437 {
1438 if (N3.LocalName == "mechanism")
1439 {
1440 switch (N3.InnerText)
1441 {
1442 case "EXTERNAL":
1443 ExternalAuth = true;
1444 break;
1445 }
1446 }
1447 }
1448 break;
1449
1450 default:
1451 break;
1452 }
1453 }
1454
1455 if (StartTls && this.allowEncryption)
1456 return await this.BeginWrite("<starttls xmlns='" + XmppClientConnection.TlsNamespace + "'/>", null, null);
1457 else if (ExternalAuth)
1458 {
1459 sb = new StringBuilder();
1460
1461 if (Bidirectional)
1462 {
1463 sb.Append("<bidi xmlns='");
1464 sb.Append(BidirectionalNamespace);
1465 sb.Append("'/>");
1466 }
1467
1468 sb.Append("<auth xmlns='");
1469 sb.Append(XmppServer.SaslNamespace);
1470 sb.Append("' mechanism='EXTERNAL'>");
1471 sb.Append(Convert.ToBase64String(Encoding.UTF8.GetBytes(this.localDomain)));
1472 sb.Append("</auth>");
1473
1474 return await this.BeginWrite(sb.ToString(), null, null);
1475 }
1476 else if (Dialback && !this.verified)
1477 {
1478 await this.SetState(XmppS2sState.Verifying);
1479
1480 sb = new StringBuilder();
1481
1482 if (!string.IsNullOrEmpty(this.authKey))
1483 {
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));
1490 sb.Append("'>");
1491 sb.Append(XML.Encode(this.authKey));
1492 sb.Append("</db:verify>");
1493
1494 this.authVerifyRequestSent = true;
1495 }
1496 else
1497 {
1498 this.authKey = this.server.GetDialbackKey(this.remoteDomain, this.localDomain, this.remoteStreamId);
1499 this.authResultRequestSent = true;
1500
1501 if (Bidirectional)
1502 {
1503 sb.Append("<bidi xmlns='");
1504 sb.Append(BidirectionalNamespace);
1505 sb.Append("'/>");
1506 }
1507
1508 sb.Append("<db:result from='");
1509 sb.Append(XML.Encode(this.localDomain));
1510 sb.Append("' to='");
1511 sb.Append(XML.Encode(this.remoteDomain));
1512 sb.Append("'>");
1513 sb.Append(this.authKey);
1514 sb.Append("</db:result>");
1515 }
1516
1517 return await this.BeginWrite(sb.ToString(), null, null);
1518 }
1519 }
1520 break;
1521
1522 case "proceed":
1523 this.upgradeToTlsAsClient = true;
1524 return false;
1525
1526 case "starttls":
1527 if (await this.BeginWrite("<proceed xmlns='" + XmppClientConnection.TlsNamespace + "'/>", null, null))
1528 this.upgradeToTlsAsServer = true;
1529 return false;
1530
1531 case "bidi":
1532 if (E.NamespaceURI == BidirectionalNamespace)
1533 this.bidirectional = true;
1534 break;
1535
1536 case "success":
1537 sb = new StringBuilder();
1538
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='");
1546 sb.Append(DialbackNamespace);
1547 sb.Append("' xmlns:stream='");
1549 sb.Append("'>");
1550
1551 if (!await this.BeginWrite(sb.ToString(), null, null))
1552 return false;
1553
1554 this.verified = true;
1555 this.DialbackCompleted(true, false, null);
1556 break;
1557
1558 case "failure":
1559 this.DialbackCompleted(false, false, "Authentication failed.");
1560 break;
1561
1562 case "auth":
1563 if (E.NamespaceURI == XmppServer.SaslNamespace && XML.Attribute(E, "mechanism") == "EXTERNAL")
1564 {
1565 string s = E.InnerText;
1566 CaseInsensitiveString Domain;
1567
1568 if (s == "=")
1569 Domain = this.remoteDomain;
1570 else
1571 {
1572 byte[] Bin = Convert.FromBase64String(s);
1573 Domain = Encoding.UTF8.GetString(Bin);
1574 }
1575
1576 bool RemoteCertificateValid = this.client.RemoteCertificateValid;
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;
1580
1581 if (RemoteCertificateValid && ValidDomain && RemoteDomainsInCertificate && DomainInCertificate)
1582 {
1583 if (!await this.BeginWrite("<success xmlns='" + XmppServer.SaslNamespace + "'/>", null, null))
1584 return false;
1585
1586 this.verified = true;
1587 this.DialbackCompleted(true, false, null);
1588 }
1589 else
1590 {
1591 if (!await this.BeginWrite("<failure xmlns='" + XmppServer.SaslNamespace + "'><not-authorized/></failure>", null, null))
1592 return false;
1593
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.");
1602 else
1603 this.DialbackCompleted(false, false, "Something went wrong.");
1604 }
1605 }
1606 break;
1607
1608 case "result":
1609 if (E.NamespaceURI == DialbackNamespace)
1610 {
1611 Type = XML.Attribute(E, "type");
1612 if (string.IsNullOrEmpty(Type))
1613 {
1614 if (this.verified)
1615 {
1616 sb = new StringBuilder();
1617
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'/>");
1623 }
1624 else
1625 {
1626 await this.Information("Performing dialback to validate domain name claim.");
1627 await this.SetState(XmppS2sState.Dialback);
1628
1629 try
1630 {
1631 IS2SEndpoint AuthEndpoint = await this.server.GetS2sEndpoint(this.localDomain, this.remoteDomain,
1632 false, "Performing dialback to validate domain name claim.");
1633
1634 if (AuthEndpoint is XmppS2SEndpoint XmppS2SEndpoint)
1635 {
1636 XmppS2SEndpoint.authKey = E.InnerText;
1637 XmppS2SEndpoint.authStreamId = this.localStreamId;
1638 XmppS2SEndpoint.authConnection = this;
1639 }
1640 }
1641 catch (Exception ex)
1642 {
1643 Log.Exception(ex);
1644
1645 sb = new StringBuilder();
1646
1647 XmppServer.GetErrorInformation(ex, out string ErrorType, out string ErrorXml);
1648
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='");
1654 sb.Append(ErrorType);
1655 sb.Append("'>");
1656 sb.Append(ErrorXml);
1657 sb.Append("<text xmlns='");
1658 sb.Append(XmppServer.StanzaNamespace);
1659 sb.Append("'>");
1660 sb.Append(XML.Encode(ex.Message));
1661 sb.Append("</text></error></db:result>");
1662
1663 if (!await this.BeginWrite(sb.ToString(), null, null))
1664 return false;
1665 }
1666 }
1667 }
1668 else if (this.authResultRequestSent)
1669 {
1670 XmppS2SEndpoint Endpoint = this.authConnection ?? this;
1671
1672 if (Type == "valid")
1673 {
1674 Endpoint.verified = true;
1675 Endpoint.DialbackCompleted(true, true, null);
1676 }
1677 else
1678 Endpoint.DialbackCompleted(false, true, "Remote end rejects authentication result.");
1679
1680 if (!(this.authConnection is null))
1681 {
1682 this.authConnection = null;
1683 await this.CleanUp(this, XmppS2sState.Offline, "Dialback check completed.");
1684 }
1685 }
1686 }
1687 break;
1688
1689 case "verify":
1690 if (E.NamespaceURI == DialbackNamespace)
1691 {
1692 From = new XmppAddress(XML.Attribute(E, "from"));
1693 To = new XmppAddress(XML.Attribute(E, "to"));
1694 Id = XML.Attribute(E, "id");
1695 Type = XML.Attribute(E, "type");
1696
1697 if (string.IsNullOrEmpty(Type))
1698 {
1699 string Key = this.server.GetDialbackKey(From.Address, To.Address, Id);
1700 sb = new StringBuilder();
1701
1702 this.verified = Key == E.InnerText;
1703
1704 sb.Append("<db:verify to='");
1705 sb.Append(XML.Encode(From.Address));
1706 sb.Append("' from='");
1707 sb.Append(XML.Encode(To.Address));
1708 sb.Append("' id='");
1709 sb.Append(XML.Encode(Id));
1710 sb.Append("' type='");
1711 sb.Append(this.verified ? "valid" : "invalid");
1712 sb.Append("'/>");
1713
1714 if (this.temporary)
1715 {
1716 await this.BeginWrite(sb.ToString(), (Sender, e) => this.DisposeAsync("Verification failed."), null);
1717 return false;
1718 }
1719 else
1720 {
1721 if (!await this.BeginWrite(sb.ToString(), null, null))
1722 return false;
1723
1724 if (this.verified && this.state == XmppS2sState.Connected)
1725 {
1726 if (!await this.SendQueued())
1727 return false;
1728 }
1729 }
1730 }
1731 else
1732 {
1733 this.verified = Type == "valid";
1734
1735 if (!(this.authConnection is null))
1736 {
1737 try
1738 {
1739 this.authConnection.KeyAuthenticated(this.verified, this.verified ? null : "Remote validation failed.");
1740 }
1741 catch (Exception ex)
1742 {
1743 Log.Exception(ex);
1744 }
1745
1746 this.authConnection = null;
1747 }
1748
1749 if (this.authVerifyRequestSent)
1750 {
1751 await this.DisposeAsync("Verification request already sent.");
1752 return false;
1753 }
1754 else if (this.verified)
1755 {
1756 if (!await this.SendQueued())
1757 return false;
1758 }
1759 }
1760 }
1761 break;
1762
1763 default:
1764 break;
1765 }
1766 }
1767 catch (Exception ex)
1768 {
1769 await this.ConnectionError(ex);
1770 return false;
1771 }
1772
1773 return true;
1774 }
1775
1776 private async void ProcessIq(string Id, XmppAddress To, XmppAddress From, string Type, string Language, Stanza Stanza)
1777 {
1778 try
1779 {
1780 await this.server.ProcessIq(Id, To, From, Type, Language, Stanza, this);
1781 }
1782 catch (Exception ex)
1783 {
1784 await this.Exception(ex);
1785 }
1786 }
1787
1788 private async void ProcessMessage(string Type, string Id, XmppAddress To, XmppAddress From, string Language, Stanza Stanza)
1789 {
1790 try
1791 {
1792 await this.server.Message(Type, Id, To, From, Language, Stanza, this);
1793 }
1794 catch (Exception ex)
1795 {
1796 await this.Exception(ex);
1797 }
1798 }
1799
1800 private async void ProcessPresence(string Type, string Id, XmppAddress To, XmppAddress From, string Language, Stanza Stanza)
1801 {
1802 try
1803 {
1804 await this.server.Presence(Type, Id, To, From, Language, Stanza, this);
1805 }
1806 catch (Exception ex)
1807 {
1808 await this.Exception(ex);
1809 }
1810 }
1811
1812 private async Task Client_OnPaused(object Sender, EventArgs e)
1813 {
1814 if (this.upgradeToTlsAsClient || this.upgradeToTlsAsServer)
1815 {
1816 bool AsClient = this.upgradeToTlsAsClient;
1817
1818 this.upgradeToTlsAsClient = false;
1819 this.upgradeToTlsAsServer = false;
1820
1821 string RemoteIpEndpoint;
1822 EndPoint EP = this.client.Client.Client.RemoteEndPoint;
1823
1824 if (EP is IPEndPoint IpEP)
1825 RemoteIpEndpoint = IpEP.Address.ToString();
1826 else
1827 RemoteIpEndpoint = EP.ToString();
1828
1829 if (AsClient || LoginAuditor.CanStartTls(RemoteIpEndpoint))
1830 {
1831 try
1832 {
1833 if (AsClient)
1834 {
1835 await this.SetState(XmppS2sState.StartingEncryptionAsClient);
1836 await this.client.UpgradeToTlsAsClient(this.localDomainCertificate, SslProtocols.Tls12, null, this.trustServer, this.remoteDomain);
1837 }
1838 else
1839 {
1840 await this.SetState(XmppS2sState.StartingEncryptionAsServer);
1841 await this.client.UpgradeToTlsAsServer(this.localDomainCertificate, SslProtocols.Tls12, ClientCertificates.Optional, null, this.trustServer);
1842 }
1843
1844 if (!(this.client.RemoteCertificate is null))
1845 {
1846 this.remoteDomainCertificateDomains = await this.GetIdentities(this.client.RemoteCertificate);
1847
1848 if (this.HasSniffers)
1849 {
1850 StringBuilder sb = new StringBuilder();
1851 string Subject = this.client.RemoteCertificate.Subject;
1852
1853 sb.Append("Remote Certificate received. Valid: ");
1854 sb.Append(this.client.RemoteCertificateValid.ToString());
1855 sb.Append(", Subject: ");
1856 sb.Append(Subject);
1857
1858 if (Subject.StartsWith("CN="))
1859 Subject = Subject.Substring(3);
1860
1861 bool First = true;
1862
1863 foreach (CaseInsensitiveString Name in this.remoteDomainCertificateDomains)
1864 {
1865 if (Name != Subject)
1866 {
1867 if (First)
1868 {
1869 sb.Append(", Alternative Names: ");
1870 First = false;
1871 }
1872 else
1873 sb.Append("; ");
1874
1875 sb.Append(Name);
1876 }
1877 }
1878
1879 sb.Append(", Issuer: ");
1880 sb.Append(this.client.RemoteCertificate.Issuer);
1881 sb.Append(", S/N: ");
1882 sb.Append(this.client.RemoteCertificate.GetSerialNumberString());
1883 sb.Append(", Hash: ");
1884 sb.Append(this.client.RemoteCertificate.GetCertHashString());
1885
1886 await this.Information(sb.ToString());
1887 }
1888 }
1889
1890 this.ResetState();
1891 this.client.Continue();
1892
1893 if (AsClient)
1894 {
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='" +
1898 XmppClientConnection.StreamNamespace + "'>", null, null);
1899 }
1900 }
1901 catch (AuthenticationException ex)
1902 {
1903 await this.LoginFailure(ex, AsClient, RemoteIpEndpoint);
1904 }
1905 catch (Win32Exception ex)
1906 {
1907 await this.LoginFailure(ex, AsClient, RemoteIpEndpoint);
1908 }
1909 catch (Exception ex)
1910 {
1911 await this.ConnectionError(ex);
1912 }
1913 }
1914 else
1915 await this.ConnectionError(new System.Exception("Remote endpoint rejected due to suspected TLS hacking."));
1916 }
1917 }
1918
1919 private async Task LoginFailure(Exception ex, bool AsClient, string RemoteIpEndpoint)
1920 {
1921 if (!AsClient)
1922 {
1923 Exception ex2 = Log.UnnestException(ex);
1924 await LoginAuditor.ReportTlsHackAttempt(RemoteIpEndpoint, "TLS handshake failed: " + ex2.Message, "XMPP S2S");
1925
1926 await this.ConnectionError(ex);
1927 }
1928 }
1929
1930 private async Task<bool> CheckFrom(XmppAddress From)
1931 {
1932 if (this.remoteDomain is null)
1933 return true;
1934
1935 CaseInsensitiveString FromDomain = From.Domain;
1936
1937 if (FromDomain == this.remoteDomain)
1938 return true;
1939
1940 if (FromDomain.EndsWith("." + this.remoteDomain, StringComparison.CurrentCultureIgnoreCase))
1941 return true;
1942
1943 await this.BeginWrite("<stream:error><invalid-from xmlns='urn:ietf:params:xml:ns:xmpp-streams'/></stream:error></stream:stream>", async (Sender, e) =>
1944 {
1945 await this.CleanUp(this, XmppS2sState.Error, "Invalid from attribute value.");
1946 }, null);
1947
1948 return false;
1949 }
1950
1951 private async void KeyAuthenticated(bool Valid, string Reason)
1952 {
1953 try
1954 {
1955 if (this.client?.Connected ?? false)
1956 {
1957 if (string.IsNullOrEmpty(this.authKey))
1958 {
1959 StringBuilder Xml = new StringBuilder();
1960
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");
1967 Xml.Append("'/>");
1968
1969 await this.BeginWrite(Xml.ToString(), null, null);
1970 }
1971
1972 if (Valid)
1973 {
1974 await this.SetState(XmppS2sState.Connected);
1975
1976 Log.Informational("XMPP S2S connection accepted.", this.remoteDomain, this.localDomain, "XmppIncomingS2SSuccess", EventLevel.Minor,
1977 new KeyValuePair<string, object>("RemoteEndpoint", this.client.Client.Client.RemoteEndPoint.ToString()));
1978
1979 if (this.bidirectional && !this.temporary)
1980 this.server.RegisterS2SEndpoint(this);
1981
1982 await this.SendQueued();
1983 }
1984 else
1985 {
1986 Log.Warning("XMPP S2S connection failed.", this.remoteDomain, this.localDomain,
1987 "XmppIncomingS2SFailure", EventLevel.Minor,
1988 new KeyValuePair<string, object>("RemoteEndpoint", this.client.Client.Client.RemoteEndPoint.ToString()),
1989 new KeyValuePair<string, object>("Reason", Reason));
1990 }
1991 }
1992 }
1993 catch (Exception ex)
1994 {
1995 await this.Exception(ex);
1996 }
1997 }
1998
1999 internal static Exception GetStreamExceptionObject(XmlElement E)
2000 {
2001 return GetExceptionObject(E, XmppServer.StreamsNamespace);
2002 }
2003
2004 internal static Exception GetExceptionObject(XmlElement E, string Namespace)
2005 {
2006 string Msg = string.Empty;
2007
2008 foreach (XmlNode N2 in E.ChildNodes)
2009 {
2010 if (N2.LocalName == "text")
2011 Msg = N2.InnerText.Trim();
2012 }
2013
2014 foreach (XmlNode N2 in E.ChildNodes)
2015 {
2016 if (N2.NamespaceURI == Namespace)
2017 {
2018 if (string.IsNullOrEmpty(Msg))
2019 Msg = N2.LocalName;
2020
2021 return new Exception(Msg);
2022 }
2023 }
2024
2025 return new Exception(string.IsNullOrEmpty(Msg) ? "Unspecified error returned." : Msg);
2026 }
2027
2028 internal static Exception GetStanzaExceptionObject(XmlElement E)
2029 {
2030 return GetExceptionObject(E, XmppServer.StanzaNamespace);
2031 }
2032
2033 internal static Exception GetSaslExceptionObject(XmlElement E)
2034 {
2035 return GetExceptionObject(E, XmppServer.SaslNamespace);
2036 }
2037
2038 internal async Task<CaseInsensitiveString[]> GetIdentities(X509Certificate certificate)
2039 {
2040 List<CaseInsensitiveString> Domains = new List<CaseInsensitiveString>();
2041 bool HasAlternativeNames = false;
2042
2043 try
2044 {
2045 foreach (string Part in certificate.Subject.Split(certificateSubjectSeparator, StringSplitOptions.None))
2046 {
2047 if (Part.StartsWith("CN="))
2048 Domains.Add(Part.Substring(3));
2049 else if (Part.StartsWith("SAN="))
2050 {
2051 Domains.Add(Part.Substring(4));
2052 HasAlternativeNames = true;
2053 }
2054 }
2055
2056 if (!HasAlternativeNames)
2057 {
2058 if (!(certificate is X509Certificate2 Cert2))
2059 {
2060 byte[] Bin = certificate.GetRawCertData();
2061 Cert2 = new X509Certificate2(Bin);
2062 }
2063
2064 foreach (X509Extension Extension in Cert2.Extensions)
2065 {
2066 if (Extension.Oid.Value == "2.5.29.17") // Subject Alternative Name
2067 {
2068 AsnEncodedData Parsed = new AsnEncodedData(Extension.Oid, Extension.RawData);
2069 string[] SAN = Parsed.Format(true).Split(CommonTypes.CRLF, StringSplitOptions.RemoveEmptyEntries);
2070
2071 foreach (string Name in SAN)
2072 {
2073 int i = Name.LastIndexOf('=');
2074 if (i > 0)
2075 Domains.Add(Name.Substring(i + 1));
2076 }
2077 }
2078 }
2079 }
2080 }
2081 catch (Exception ex)
2082 {
2083 StringBuilder sb = new StringBuilder();
2084
2085 sb.Append("Unable to extract domain names from certificate subject (");
2086 sb.Append(ex.Message);
2087 sb.Append(").");
2088
2089 try
2090 {
2091 byte[] Bin = certificate.GetRawCertData();
2092 sb.AppendLine();
2093 sb.AppendLine();
2094 sb.Append(Convert.ToBase64String(Bin, Base64FormattingOptions.InsertLineBreaks));
2095 }
2096 catch (Exception)
2097 {
2098 sb.Append(" No raw data in certificate.");
2099 }
2100
2101 await this.Error(sb.ToString());
2102 }
2103
2104 return Domains.ToArray();
2105 }
2106
2107 private readonly static string[] certificateSubjectSeparator = new string[] { ", " };
2108
2109 private async void DialbackCompleted(bool Successful, bool SendQueued, string Reason)
2110 {
2111 try
2112 {
2113 if (Successful)
2114 {
2115 await this.SetState(XmppS2sState.Connected);
2116 this.supportsPing = true;
2117 this.secondTimer = new Timer(this.SecondTimerCallback, null, 1000, 1000);
2118
2119 await this.Information("XMPP S2S connection successful.");
2120 Log.Informational("XMPP S2S connection successful.", this.remoteDomain, this.localDomain, "XmppOutgoingS2SSuccess", EventLevel.Minor,
2121 new KeyValuePair<string, object>("RemoteEndpoint", this.client.Client.Client.RemoteEndPoint.ToString()));
2122
2123 if (SendQueued)
2124 await this.SendQueued();
2125 }
2126 else
2127 {
2128 await this.SetState(XmppS2sState.Error, "Dialback failed.");
2129
2130 await this.Information("XMPP S2S connection failed: " + Reason);
2131 Log.Warning("XMPP S2S connection failed.", this.remoteDomain, this.localDomain, "XmppOutgoingS2SFailure", EventLevel.Minor,
2132 new KeyValuePair<string, object>("RemoteEndpoint", this.client.Client.Client.RemoteEndPoint.ToString()),
2133 new KeyValuePair<string, object>("Reason", Reason));
2134
2135 if (SendQueued)
2136 {
2137 lock (this.synchObject)
2138 {
2139 this.queue = null;
2140 this.stanzasOnHold = null;
2141 }
2142 }
2143 }
2144 }
2145 catch (Exception ex)
2146 {
2147 await this.Exception(ex);
2148 }
2149 }
2150
2156 {
2157 get => this.keepAliveSeconds;
2158 set
2159 {
2160 if (value <= 0)
2161 throw new ArgumentException("Value must be positive.", nameof(this.KeepAliveSeconds));
2162
2163 this.keepAliveSeconds = value;
2164 }
2165 }
2166
2167 private async void SecondTimerCallback(object State)
2168 {
2169 try
2170 {
2171 if (!this.checkConnection)
2172 return;
2173
2174 DateTime Now = DateTime.Now;
2175
2176 if (Now >= this.nextPing && this.state == XmppS2sState.Connected)
2177 {
2178 this.server.TouchServerConnection(this.remoteDomain);
2179
2180 this.nextPing = DateTime.Now.AddMilliseconds(this.keepAliveSeconds * 500);
2181 try
2182 {
2183 if (this.supportsPing)
2184 {
2185 if (this.pingResponse)
2186 {
2187 this.pingResponse = false;
2188 await this.SendPing();
2189 }
2190 else
2191 await this.DisposeAsync("No ping response.");
2192 }
2193 else
2194 await this.BeginWrite(" ", null, null);
2195 }
2196 catch (Exception ex)
2197 {
2198 Log.Exception(ex);
2199 await this.Exception(ex);
2200 await this.DisposeAsync(ex.Message);
2201 }
2202 }
2203 }
2204 catch (Exception ex)
2205 {
2206 Log.Exception(ex);
2207 }
2208 }
2209
2213 public Task<bool> SendPing()
2214 {
2215 StringBuilder Xml = new StringBuilder();
2216
2217 this.pingId = this.server.NewId(16);
2218
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='");
2226 Xml.Append(XmppServer.PingNamespace);
2227 Xml.Append("'/></iq>");
2228
2229 return this.BeginWrite(Xml.ToString(), null, null);
2230 }
2231
2232 #region IRecipient
2233
2235 public override Task<bool> IQ(string Type, string Id, XmppAddress To, XmppAddress From, string Language, Stanza Stanza, ISender Sender)
2236 {
2237 return this.SendStanza("iq", Type, Id, To, From, Language, Stanza?.Content, Sender);
2238 }
2239
2241 public override Task<bool> IQ(string Type, string Id, XmppAddress To, XmppAddress From, string Language, string ContentXml, ISender Sender)
2242 {
2243 return this.SendStanza("iq", Type, Id, To, From, Language, ContentXml, Sender);
2244 }
2245
2246 internal async Task<bool> SendStanza(string StanzaType, string Type, string Id, XmppAddress To, XmppAddress From, string Language, string ContentXml, ISender Sender)
2247 {
2248 bool Error;
2249
2250 lock (this.synchObject)
2251 {
2252 if (this.state == XmppS2sState.Error)
2253 Error = true;
2254 else if (this.state == XmppS2sState.Connected)
2255 Error = false;
2256 else
2257 {
2258 if (this.queue is null)
2259 this.queue = new LinkedList<QueuedStanza>();
2260
2261 this.queue.AddLast(new QueuedStanza()
2262 {
2263 StanzaType = StanzaType,
2264 Type = Type,
2265 Id = Id,
2266 To = To,
2267 From = From,
2268 Language = Language,
2269 ContentXml = ContentXml,
2270 Sender = Sender
2271 });
2272
2273 return true;
2274 }
2275 }
2276
2277 if (Error)
2278 {
2279 if (!(Sender is null))
2280 {
2281 switch (StanzaType.ToLower())
2282 {
2283 case "iq":
2284 if (Sender is null)
2285 return true;
2286 else
2287 return !(await Sender.IqError(Id, From, To, this.errorType, this.errorXml, "S2S connection failed.", "en") is null);
2288
2289 case "message":
2290 if (!await (Sender?.MessageError(Id, From, To, this.errorType, this.errorXml, "S2S connection failed.", "en") ?? Task.FromResult(true)))
2291 return false;
2292 break;
2293
2294 case "presence":
2295 if (!await (Sender?.PresenceError(Id, From, To, this.errorType, this.errorXml, "S2S connection failed.", "en") ?? Task.FromResult(true)))
2296 return false;
2297 break;
2298 }
2299 }
2300 }
2301 else
2302 {
2303 try
2304 {
2305 if (!await this.BeginWrite(StanzaType, Type, Id, To, From, Language, ContentXml))
2306 return false;
2307 }
2308 catch (Exception ex)
2309 {
2310 if (!(Sender is null))
2311 {
2312 try
2313 {
2314 switch (StanzaType.ToLower())
2315 {
2316 case "iq":
2317 if (Sender is null)
2318 return true;
2319 else
2320 return !(await Sender.IqError(Id, From, To, this.errorType, this.errorXml, "S2S connection failed: " + ex.Message, "en") is null);
2321
2322 case "message":
2323 if (!await (Sender?.MessageError(Id, From, To, this.errorType, this.errorXml, "S2S connection failed: " + ex.Message, "en") ?? Task.FromResult(true)))
2324 return false;
2325 break;
2326
2327 case "presence":
2328 if (!await (Sender?.PresenceError(Id, From, To, this.errorType, this.errorXml, "S2S connection failed: " + ex.Message, "en") ?? Task.FromResult(true)))
2329 return false;
2330 break;
2331 }
2332 }
2333 catch (Exception ex2)
2334 {
2335 Log.Exception(ex2);
2336 }
2337 }
2338 }
2339 }
2340
2341 return true;
2342 }
2343
2344 private Task<bool> BeginWrite(string StanzaType, string Type, string Id, XmppAddress To, XmppAddress From, string Language, string ContentXml)
2345 {
2346 StringBuilder Xml = new StringBuilder();
2347
2348 Xml.Append('<');
2349 Xml.Append(StanzaType);
2350
2351 if (!From.IsEmpty)
2352 {
2353 Xml.Append(" from='");
2354 Xml.Append(XML.Encode(From.Address));
2355 }
2356
2357 if (!To.IsEmpty)
2358 {
2359 Xml.Append("' to='");
2360 Xml.Append(XML.Encode(To.Address));
2361 }
2362
2363 if (!string.IsNullOrEmpty(Type))
2364 {
2365 Xml.Append("' type='");
2366 Xml.Append(Type);
2367 }
2368
2369 if (!string.IsNullOrEmpty(Id))
2370 {
2371 Xml.Append("' id='");
2372 Xml.Append(XML.Encode(Id));
2373 }
2374
2375 if (!string.IsNullOrEmpty(Language))
2376 {
2377 Xml.Append("' xml:lang='");
2378 Xml.Append(XML.Encode(Language));
2379 }
2380
2381 if (string.IsNullOrEmpty(ContentXml))
2382 Xml.Append("'/>");
2383 else
2384 {
2385 Xml.Append("'>");
2386 Xml.Append(ContentXml);
2387 Xml.Append("</");
2388 Xml.Append(StanzaType);
2389 Xml.Append('>');
2390 }
2391
2392 return this.BeginWrite(Xml.ToString(), null, null);
2393 }
2394
2396 public override Task<bool> Message(string Type, string Id, XmppAddress To, XmppAddress From, string Language, Stanza Stanza, ISender Sender)
2397 {
2398 return this.SendStanza("message", Type, Id, To, From, Language, Stanza?.Content, Sender);
2399 }
2400
2402 public override Task<bool> Message(string Type, string Id, XmppAddress To, XmppAddress From, string Language, string ContentXml, ISender Sender)
2403 {
2404 return this.SendStanza("message", Type, Id, To, From, Language, ContentXml, Sender);
2405 }
2406
2408 public override Task<bool> Presence(string Type, string Id, XmppAddress To, XmppAddress From, string Language, Stanza Stanza, ISender Sender)
2409 {
2410 return this.SendStanza("presence", Type, Id, To, From, Language, Stanza?.Content, Sender);
2411 }
2412
2414 public override Task<bool> Presence(string Type, string Id, XmppAddress To, XmppAddress From, string Language, string ContentXml, ISender Sender)
2415 {
2416 return this.SendStanza("presence", Type, Id, To, From, Language, ContentXml, Sender);
2417 }
2418
2419 #endregion
2420
2421 #region ISender
2422
2424 public override Task<bool> IqError(string Id, XmppAddress To, XmppAddress From, string ErrorXml)
2425 {
2426 StringBuilder Xml = new StringBuilder();
2427
2428 Xml.Append("<iq");
2429 XmppClientConnection.AppendParameters(Xml, "error", Id, To, From, string.Empty, (Stanza)null);
2430 Xml.Append('>');
2431 Xml.Append(ErrorXml);
2432 Xml.Append("</iq>");
2433
2434 return this.BeginWrite(Xml.ToString(), null, null);
2435 }
2436
2438 public override Task<string> IqError(string Id, XmppAddress To, XmppAddress From, Exception ex)
2439 {
2440 XmppServer.GetErrorInformation(ex, out string Type, out string Xml);
2441 return this.IqError(Id, To, From, Type, Xml, ex.Message + "\r\n\r\n" + Log.CleanStackTrace(ex.StackTrace), string.Empty);
2442 }
2443
2445 public override Task<bool> IqResult(string Id, XmppAddress To, XmppAddress From, string ResultXml)
2446 {
2447 StringBuilder Xml = new StringBuilder();
2448
2449 Xml.Append("<iq");
2450 XmppClientConnection.AppendParameters(Xml, "result", Id, To, From, string.Empty, (Stanza)null);
2451 Xml.Append('>');
2452 Xml.Append(ResultXml);
2453 Xml.Append("</iq>");
2454
2455 return this.BeginWrite(Xml.ToString(), null, null);
2456 }
2457
2459 public override Task<bool> Presence(string Type, string Id, XmppAddress To, XmppAddress From, string Language, string ContentXml)
2460 {
2461 return this.SendStanza("presence", Type, Id, To, From, Language, ContentXml, null);
2462 // TODO: Errors in SendStanza should be returned, which does not happen since last parameter is null.
2463 }
2464
2466 public override Task<bool> PresenceError(string Id, XmppAddress To, XmppAddress From, string ErrorXml)
2467 {
2468 StringBuilder Xml = new StringBuilder();
2469
2470 Xml.Append("<presence id='");
2471 Xml.Append(Id);
2472 Xml.Append("' from='");
2473 Xml.Append(From);
2474 Xml.Append("' to='");
2475 Xml.Append(To);
2476 Xml.Append("' type='error'>");
2477 Xml.Append(ErrorXml);
2478 Xml.Append("</presence>");
2479
2480 return this.BeginWrite(Xml.ToString(), null, null);
2481 }
2482
2484 public override Task<bool> PresenceError(string Id, XmppAddress To, XmppAddress From, Exception ex)
2485 {
2486 XmppServer.GetErrorInformation(ex, out string Type, out string Xml);
2487 return this.PresenceError(Id, To, From, Type, Xml, ex.Message + "\r\n\r\n" + Log.CleanStackTrace(ex.StackTrace), string.Empty);
2488 }
2489
2491 public override Task<bool> Message(string Type, string Id, XmppAddress To, XmppAddress From, string Language, string ContentXml)
2492 {
2493 return this.Message(Type, Id, To, From, Language, ContentXml, null);
2494 }
2495
2497 public override Task<bool> MessageError(string Id, XmppAddress To, XmppAddress From, Exception ex)
2498 {
2499 XmppServer.GetErrorInformation(ex, out string Type, out string Xml);
2500 return this.MessageError(Id, To, From, Type, Xml, ex.Message + "\r\n\r\n" + Log.CleanStackTrace(ex.StackTrace), string.Empty);
2501 }
2502
2503 #endregion
2504
2505 }
2506}
Helps with parsing of commong data types.
Definition: CommonTypes.cs:13
static readonly char[] CRLF
Contains the CR LF character sequence.
Definition: CommonTypes.cs:17
Helps with common XML-related tasks.
Definition: XML.cs:19
static string Attribute(XmlElement E, string Name)
Gets the value of an XML attribute.
Definition: XML.cs:914
static string Encode(string s)
Encodes a string for use in XML.
Definition: XML.cs:27
Static class managing the application event log. Applications and services log events on this static ...
Definition: Log.cs:13
static string CleanStackTrace(string StackTrace)
Cleans a Stack Trace string, removing entries from the asynchronous execution model,...
Definition: Log.cs:184
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.
Definition: Log.cs:1647
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.
Definition: Log.cs:566
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.
Definition: Log.cs:334
static Exception UnnestException(Exception Exception)
Unnests an exception, to extract the relevant inner exception.
Definition: Log.cs:818
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.
Definition: Stanza.cs:10
XmlElement StanzaElement
Stanza element.
Definition: Stanza.cs:114
Contains information about one XMPP address.
Definition: XmppAddress.cs:9
bool IsEmpty
If the address is empty.
Definition: XmppAddress.cs:183
CaseInsensitiveString Domain
Domain
Definition: XmppAddress.cs:97
CaseInsensitiveString Address
XMPP Address
Definition: XmppAddress.cs:37
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
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.
Definition: XmppServer.cs:689
static void GetErrorInformation(Exception ex, out string Type, out string Xml)
Converts an Exception to an XMPP error message.
Definition: XmppServer.cs:3076
const string SaslNamespace
urn:ietf:params:xml:ns:xmpp-sasl
Definition: XmppServer.cs:108
const string PingNamespace
urn:xmpp:ping (XEP-0199)
Definition: XmppServer.cs:178
const string StanzaNamespace
urn:ietf:params:xml:ns:xmpp-stanzas (RFC 6120)
Definition: XmppServer.cs:98
const string StreamsNamespace
urn:ietf:params:xml:ns:xmpp-streams
Definition: XmppServer.cs:103
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....
Definition: LoginAuditor.cs:26
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...
Definition: ISniffer.cs:11
Interface for XMPP S2S endpoints
Definition: IS2sEndpoint.cs:10
Interface for senders of stanzas.
Definition: ISender.cs:10
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.
EventLevel
Event level.
Definition: EventLevel.cs:7
XmppS2sState
State of XMPP connection.
Definition: XmppS2sState.cs:7
ClientCertificates
Client Certificate Options