2using System.Collections.Generic;
4using System.Threading.Tasks;
18 private Dictionary<string, PeerState> peersByFullJid =
new Dictionary<string, PeerState>(StringComparer.CurrentCultureIgnoreCase);
19 private readonly Dictionary<string, AddressInfo> addressesByFullJid =
new Dictionary<string, AddressInfo>(StringComparer.CurrentCultureIgnoreCase);
20 private readonly Dictionary<string, Dictionary<int, AddressInfo>> addressesByExternalIPPort =
new Dictionary<string, Dictionary<int, AddressInfo>>();
21 private readonly Dictionary<string, Dictionary<int, AddressInfo>> addressesByLocalIPPort =
new Dictionary<string, Dictionary<int, AddressInfo>>();
23 private string fullJid;
24 private bool disposed =
false;
67 EncapsulatePackets =
false
72 this.p2pNetwork.OnPeerConnected += this.P2PNetwork_OnPeerConnected;
86 set => this.fullJid = value;
94 private Task P2PNetwork_OnPeerConnected(
object Listener,
PeerConnection Peer)
106 string ThisExternalIp = this.p2pNetwork.ExternalAddress is
null ? string.Empty : this.p2pNetwork.ExternalAddress.ToString();
108 lock (this.addressesByFullJid)
110 if (this.addressesByFullJid.TryGetValue(FullJID, out
AddressInfo Info))
112 this.addressesByFullJid.Remove(FullJID);
114 if (this.addressesByExternalIPPort.TryGetValue(Info.ExternalIp, out Dictionary<int, AddressInfo> Infos))
116 if (Infos.Remove(Info.ExternalPort) && Infos.Count == 0)
117 this.addressesByExternalIPPort.Remove(Info.ExternalIp);
120 if (Info.ExternalIp == ThisExternalIp)
122 if (this.addressesByLocalIPPort.TryGetValue(Info.LocalIp, out Infos))
124 if (Infos.Remove(Info.LocalPort) && Infos.Count == 0)
125 this.addressesByLocalIPPort.Remove(Info.LocalIp);
133 await this.
Information(
"Removing JID from set of recognized JIDs: " + FullJID);
151 public async Task
ReportPeerAddresses(
string FullJID,
string ExternalIp,
int ExternalPort,
string LocalIp,
int LocalPort)
153 Dictionary<int, AddressInfo> Infos;
154 string ThisExternalIp;
156 if (this.p2pNetwork.ExternalAddress is
null)
157 ThisExternalIp =
string.Empty;
159 ThisExternalIp = this.p2pNetwork.ExternalAddress.ToString();
161 lock (this.addressesByFullJid)
163 if (this.addressesByFullJid.TryGetValue(FullJID, out
AddressInfo Info))
165 if (Info.ExternalIp == ExternalIp && Info.ExternalPort == ExternalPort &&
166 Info.LocalIp == LocalIp && Info.LocalPort == LocalPort)
171 if (Info.ExternalIp != ExternalIp)
173 if (this.addressesByExternalIPPort.TryGetValue(Info.ExternalIp, out Infos))
175 if (Infos.Remove(Info.ExternalPort) && Infos.Count == 0)
176 this.addressesByExternalIPPort.Remove(Info.ExternalIp);
180 if (ExternalIp == ThisExternalIp && Info.LocalIp != LocalIp)
182 if (this.addressesByLocalIPPort.TryGetValue(Info.LocalIp, out Infos))
184 if (Infos.Remove(Info.LocalPort) && Infos.Count == 0)
185 this.addressesByLocalIPPort.Remove(Info.LocalIp);
190 Info =
new AddressInfo(FullJID, ExternalIp, ExternalPort, LocalIp, LocalPort);
191 this.addressesByFullJid[FullJID] = Info;
193 if (!this.addressesByExternalIPPort.TryGetValue(ExternalIp, out Infos))
195 Infos =
new Dictionary<int, AddressInfo>();
196 this.addressesByExternalIPPort[ExternalIp] = Infos;
199 Infos[ExternalPort] = Info;
201 if (ExternalIp == ThisExternalIp)
203 if (!this.addressesByLocalIPPort.TryGetValue(LocalIp, out Infos))
205 Infos =
new Dictionary<int, AddressInfo>();
206 this.addressesByLocalIPPort[LocalIp] = Infos;
209 Infos[LocalPort] = Info;
213 await this.
Information(
"P2P information available for " + FullJID +
". External: " + ExternalIp +
":" + ExternalPort.ToString() +
214 ", Local: " + LocalIp +
":" + LocalPort.ToString());
224 internal void AuthenticatePeer(
PeerConnection Peer,
string FullJID)
228 lock (this.addressesByFullJid)
230 if (!this.addressesByFullJid.TryGetValue(FullJID, out Info))
231 throw new XmppException(
"Peer JID " + FullJID +
" not recognized.");
234 if (Info.
ExternalIp ==
this.p2pNetwork.ExternalAddress.ToString())
254 internal Task PeerAuthenticated(
PeerState State)
256 lock (this.peersByFullJid)
264 internal async Task NewXmppClient(XmppClient Client,
string LocalJid,
string RemoteJid)
266 await this.
Information(
"Serverless XMPP connection established with " + RemoteJid);
271 await this.
OnNewXmppClient.Raise(
this,
new PeerConnectionEventArgs(Client,
null, LocalJid, RemoteJid));
279 internal async Task PeerClosed(
PeerState State)
283 if (!(this.peersByFullJid is
null))
285 lock (this.peersByFullJid)
299 public Task
GetPeerConnection(
string FullJID, EventHandlerAsync<PeerConnectionEventArgs> Callback,
object State)
311 TaskCompletionSource<PeerConnectionEventArgs> Result =
new TaskCompletionSource<PeerConnectionEventArgs>();
315 Result.TrySetResult(e);
316 return Task.CompletedTask;
319 return await Result.Task;
325 public event EventHandlerAsync<ResynchEventArgs>
OnResynch =
null;
336 lock (this.addressesByFullJid)
338 if (!this.addressesByFullJid.TryGetValue(FullJID, out Info))
342 return !
string.IsNullOrEmpty(Info.
ExternalIp);
353 lock (this.addressesByFullJid)
355 return this.addressesByFullJid.TryGetValue(FullJID, out Address);
359 private async Task
GetPeerConnection(
string FullJID, EventHandlerAsync<PeerConnectionEventArgs> Callback,
object State, EventHandlerAsync<ResynchEventArgs> ResynchMethod)
364 string Header =
null;
373 lock (this.addressesByFullJid)
375 b = this.addressesByFullJid.TryGetValue(FullJID, out Info);
380 await Callback.Raise(
this,
new PeerConnectionEventArgs(
null, State, this.fullJid, FullJID));
384 lock (this.peersByFullJid)
386 b = this.peersByFullJid.TryGetValue(FullJID, out Result);
390 if (Result.AgeSeconds >= 30 && (Result.HasCallbacks || Result.XmppClient is
null || !Result.Peer.Tcp.Connected))
392 this.peersByFullJid.Remove(FullJID);
397 else if (Result.State !=
XmppState.Connected)
399 Result.AddCallback(Callback, State);
406 Header =
"<?xml version='1.0'?><stream:stream xmlns='jabber:client' xmlns:stream='http://etherx.jabber.org/streams' from='" +
407 this.fullJid +
"' to='" + FullJID +
"' version='1.0'>";
409 Result =
new PeerState(
null,
this, FullJID, Header,
"</stream:stream>",
string.Empty, 1.0, Callback, State);
410 this.peersByFullJid[FullJID] = Result;
416 await Callback.Raise(
this,
new PeerConnectionEventArgs(Result.XmppClient, State,
this.fullJid, FullJID));
419 else if (!(Old is
null))
425 _ = Task.Run(async () =>
431 Connection = await this.ConnectToAsync(FullJID, Info);
438 if (!(ResynchMethod is
null))
442 ResynchEventArgs e =
new ResynchEventArgs(FullJID, async (sender, e2) =>
447 await
this.GetPeerConnection(FullJID, Callback, State,
null);
450 lock (this.peersByFullJid)
452 this.peersByFullJid.Remove(FullJID);
455 Result.CallCallbacks();
458 catch (Exception ex2)
464 await ResynchMethod(
this, e);
466 catch (Exception ex2)
475 if (Connection is
null)
477 lock (this.peersByFullJid)
479 this.peersByFullJid.Remove(FullJID);
482 Result.CallCallbacks();
486 Result.Peer = Connection;
487 Connection.
Start(async (Sender, e) =>
489 if (!(ResynchMethod is
null))
491 if (!await ResynchMethod.Raise(
this,
new ResynchEventArgs(FullJID, async (sender2, e2) =>
498 Connection = await this.ConnectToAsync(FullJID, Info);
499 Result.Peer = Connection;
501 Result.HeaderSent = true;
502 await Result.SendAsync(Header);
503 await this.TransmitText(Header);
506 Result.CallCallbacks();
514 Result.CallCallbacks();
518 Result.CallCallbacks();
521 Result.HeaderSent =
true;
522 await Result.SendAsync(Header);
528 private async Task<PeerConnection> ConnectToAsync(
string FullJID, AddressInfo Info)
534 if (Info.ExternalIp ==
this.p2pNetwork.ExternalAddress.ToString())
537 Port = Info.LocalPort;
541 Ip = Info.ExternalIp;
542 Port = Info.ExternalPort;
545 if (IPAddress.TryParse(Ip, out IPAddress Addr))
547 await this.Information(
"Connecting to " + Ip +
":" + Port.ToString() +
" (" + FullJID +
")");
548 Connection = await this.p2pNetwork.ConnectToPeer(
new IPEndPoint(Addr, Port));
549 await this.Information(
"Connected to to " + Ip +
":" + Port.ToString() +
" (" + FullJID +
")");
560 [Obsolete(
"Use the DisposeAsync() method.")]
565 await this.DisposeAsync();
580 if (!(this.p2pNetwork is
null))
582 await this.p2pNetwork.DisposeAsync();
583 this.p2pNetwork =
null;
586 if (!(this.peersByFullJid is
null))
590 lock (this.peersByFullJid)
592 States =
new PeerState[this.peersByFullJid.Count];
593 this.peersByFullJid.Values.CopyTo(States, 0);
595 this.peersByFullJid.Clear();
598 this.peersByFullJid =
null;
602 State.ClearCallbacks();
607 this.disposed =
true;
617 if (!(this.p2pNetwork is
null) &&
619 !(
this.p2pNetwork.ExternalEndpoint is
null))
621 Xml.Append(
"<p2p xmlns='");
623 Xml.Append(
"' extIp='");
624 Xml.Append(this.p2pNetwork.ExternalAddress.ToString());
625 Xml.Append(
"' extPort='");
626 Xml.Append(this.p2pNetwork.ExternalEndpoint.Port.ToString());
627 Xml.Append(
"' locIp='");
628 Xml.Append(this.p2pNetwork.LocalAddress.ToString());
629 Xml.Append(
"' locPort='");
630 Xml.Append(this.p2pNetwork.LocalEndpoint.Port.ToString());
650 foreach (XmlAttribute Attr
in P2P.Attributes)
660 if (
int.TryParse(Attr.Value, out
int i) && i >= 0 && i < 65536)
671 if (
int.TryParse(Attr.Value, out i) && i >= 0 && i < 65536)
680 if (!(ExtIp is
null) && ExtPort.HasValue && !(LocIp is
null) && LocPort.HasValue)
682 await this.ReportPeerAddresses(FullJID, ExtIp, ExtPort.Value, LocIp, LocPort.Value);
687 await this.RemovePeerAddresses(FullJID);
Static class managing the application event log. Applications and services log events on this static ...
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.
Simple base class for classes implementing communication protocols.
Task Exception(Exception Exception)
Called to inform the viewer of an exception state.
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.
Manages registration of TCP and UDP ports in an Internet Gateway
static bool IsPublicAddress(IPAddress Address)
Checks if an IPv4 address is public.
Maintains a peer connection
IPEndPoint RemoteEndpoint
Remote endpoint.
void Start()
Starts receiving on the connection.
Manages a peer-to-peer network that can receive connections from outside of a NAT-enabled firewall.
Contains information about peer addresses.
string ExternalIp
External IP Address.
string LocalIp
Local IP Address.
Class managing end-to-end encryption.
const string IoTHarmonizationP2PCurrent
Current namespace for peer-to-peer communication
Peer address event arguments.
Peer connection event arguments.
Task DisposeAsync()
IDisposable.Dispose
string RemoteFullJid
Remote Full JID
async Task Close()
CLoses the connection.
Class managing peer-to-peer serveless XMPP communication.
async Task ReportPeerAddresses(string FullJID, string ExternalIp, int ExternalPort, string LocalIp, int LocalPort)
Reports recognized peer addresses.
bool TryGetAddressInfo(string FullJID, out AddressInfo Address)
Gets peer-to-peer address information
EventHandlerAsync< PeerAddressEventArgs > PeerAddressReceived
Event raised when address information about a peer has been received.
PeerToPeerNetwork Network
Peer-to-peer network.
async void Dispose()
IDisposable.Dispose
XmppServerlessMessaging(string ApplicationName, string FullJid, ushort LocalPort, ushort ExternalPort, params ISniffer[] Sniffers)
Class managing peer-to-peer serveless XMPP communication.
Task GetPeerConnection(string FullJID, EventHandlerAsync< PeerConnectionEventArgs > Callback, object State)
Gets a peer XMPP connection.
async Task< bool > AddPeerAddressInfo(string FullJID, XmlElement P2P)
Adds P2P address information about a peer.
EventHandlerAsync< PeerAddressEventArgs > PeerAddressRemoved
Event raised when address information about a peer has been removed.
EventHandlerAsync< ResynchEventArgs > OnResynch
Event raised when the peer-to-peer connection parameters need to be updated for a given remote JID.
async Task RemovePeerAddresses(string FullJID)
Removes a JID from the recognized set of JIDs.
async Task< PeerConnectionEventArgs > GetPeerConnectionAsync(string FullJID)
Gets a peer XMPP connection.
async Task DisposeAsync()
IDisposable.Dispose
bool Disposed
If the object has been disposed.
EventHandlerAsync< PeerConnectionEventArgs > OnNewXmppClient
Event raised when a new XMPP client has been created.
void AppendP2pInfo(StringBuilder Xml)
Appends P2P information to XML.
XmppServerlessMessaging(string ApplicationName, string FullJid, ushort LocalPort, ushort ExternalPort, int Backlog, params ISniffer[] Sniffers)
Class managing peer-to-peer serveless XMPP communication.
XmppServerlessMessaging(string ApplicationName, string FullJid, params ISniffer[] Sniffers)
Class managing peer-to-peer serveless XMPP communication.
bool CanConnectToPeer(string FullJID)
If it is possible to connect directly to a given peer, given it's bare JID.
Base class of XMPP exceptions
Interface for sniffers. Sniffers can be added to ICommunicationLayer classes to eavesdrop on communic...
PeerToPeerNetworkState
State of Peer-to-peer network.
XmppState
State of XMPP connection.