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)
98 return Task.CompletedTask;
107 string ThisExternalIp = this.p2pNetwork.ExternalAddress is
null ? string.Empty : this.p2pNetwork.ExternalAddress.ToString();
109 lock (this.addressesByFullJid)
111 if (this.addressesByFullJid.TryGetValue(FullJID, out
AddressInfo Info))
113 this.addressesByFullJid.Remove(FullJID);
115 if (this.addressesByExternalIPPort.TryGetValue(Info.ExternalIp, out Dictionary<int, AddressInfo> Infos))
117 if (Infos.Remove(Info.ExternalPort) && Infos.Count == 0)
118 this.addressesByExternalIPPort.Remove(Info.ExternalIp);
121 if (Info.ExternalIp == ThisExternalIp)
123 if (this.addressesByLocalIPPort.TryGetValue(Info.LocalIp, out Infos))
125 if (Infos.Remove(Info.LocalPort) && Infos.Count == 0)
126 this.addressesByLocalIPPort.Remove(Info.LocalIp);
134 this.
Information(
"Removing JID from set of recognized JIDs: " + FullJID);
152 public async Task
ReportPeerAddresses(
string FullJID,
string ExternalIp,
int ExternalPort,
string LocalIp,
int LocalPort)
154 Dictionary<int, AddressInfo> Infos;
155 string ThisExternalIp;
157 if (this.p2pNetwork.ExternalAddress is
null)
158 ThisExternalIp =
string.Empty;
160 ThisExternalIp = this.p2pNetwork.ExternalAddress.ToString();
162 lock (this.addressesByFullJid)
164 if (this.addressesByFullJid.TryGetValue(FullJID, out
AddressInfo Info))
166 if (Info.ExternalIp == ExternalIp && Info.ExternalPort == ExternalPort &&
167 Info.LocalIp == LocalIp && Info.LocalPort == LocalPort)
172 if (Info.ExternalIp != ExternalIp)
174 if (this.addressesByExternalIPPort.TryGetValue(Info.ExternalIp, out Infos))
176 if (Infos.Remove(Info.ExternalPort) && Infos.Count == 0)
177 this.addressesByExternalIPPort.Remove(Info.ExternalIp);
181 if (ExternalIp == ThisExternalIp && Info.LocalIp != LocalIp)
183 if (this.addressesByLocalIPPort.TryGetValue(Info.LocalIp, out Infos))
185 if (Infos.Remove(Info.LocalPort) && Infos.Count == 0)
186 this.addressesByLocalIPPort.Remove(Info.LocalIp);
191 Info =
new AddressInfo(FullJID, ExternalIp, ExternalPort, LocalIp, LocalPort);
192 this.addressesByFullJid[FullJID] = Info;
194 if (!this.addressesByExternalIPPort.TryGetValue(ExternalIp, out Infos))
196 Infos =
new Dictionary<int, AddressInfo>();
197 this.addressesByExternalIPPort[ExternalIp] = Infos;
200 Infos[ExternalPort] = Info;
202 if (ExternalIp == ThisExternalIp)
204 if (!this.addressesByLocalIPPort.TryGetValue(LocalIp, out Infos))
206 Infos =
new Dictionary<int, AddressInfo>();
207 this.addressesByLocalIPPort[LocalIp] = Infos;
210 Infos[LocalPort] = Info;
214 this.
Information(
"P2P information available for " + FullJID +
". External: " + ExternalIp +
":" + ExternalPort.ToString() +
215 ", Local: " + LocalIp +
":" + LocalPort.ToString());
225 internal string AuthenticatePeer(
PeerConnection Peer,
string FullJID)
229 lock (this.addressesByFullJid)
231 if (!this.addressesByFullJid.TryGetValue(FullJID, out Info))
232 return "Peer JID " + FullJID +
" not recognized.";
235 if (Info.
ExternalIp ==
this.p2pNetwork.ExternalAddress.ToString())
239 return "Expected connection from " + Info.LocalIp +
", but was from " +
247 return "Expected connection from " + Info.ExternalIp +
", but was from " +
257 internal void PeerAuthenticated(
PeerState State)
259 lock (this.peersByFullJid)
267 internal async Task NewXmppClient(XmppClient Client,
string LocalJid,
string RemoteJid)
269 this.
Information(
"Serverless XMPP connection established with " + RemoteJid);
274 await this.
OnNewXmppClient.Raise(
this,
new PeerConnectionEventArgs(Client,
null, LocalJid, RemoteJid));
282 internal void PeerClosed(
PeerState State)
286 if (!(this.peersByFullJid is
null))
288 lock (this.peersByFullJid)
302 public Task
GetPeerConnection(
string FullJID, EventHandlerAsync<PeerConnectionEventArgs> Callback,
object State)
314 TaskCompletionSource<PeerConnectionEventArgs> Result =
new TaskCompletionSource<PeerConnectionEventArgs>();
318 Result.TrySetResult(e);
319 return Task.CompletedTask;
322 return await Result.Task;
328 public event EventHandlerAsync<ResynchEventArgs>
OnResynch =
null;
339 lock (this.addressesByFullJid)
341 if (!this.addressesByFullJid.TryGetValue(FullJID, out Info))
345 return !
string.IsNullOrEmpty(Info.
ExternalIp);
356 lock (this.addressesByFullJid)
358 return this.addressesByFullJid.TryGetValue(FullJID, out Address);
362 private async Task
GetPeerConnection(
string FullJID, EventHandlerAsync<PeerConnectionEventArgs> Callback,
object State, EventHandlerAsync<ResynchEventArgs> ResynchMethod)
367 string Header =
null;
376 lock (this.addressesByFullJid)
378 b = this.addressesByFullJid.TryGetValue(FullJID, out Info);
383 await Callback.Raise(
this,
new PeerConnectionEventArgs(
null, State, this.fullJid, FullJID));
387 lock (this.peersByFullJid)
389 b = this.peersByFullJid.TryGetValue(FullJID, out Result);
393 if (Result.AgeSeconds >= 30 && (Result.HasCallbacks || Result.XmppClient is
null || !Result.Peer.Tcp.Connected))
395 this.peersByFullJid.Remove(FullJID);
400 else if (Result.State !=
XmppState.Connected)
402 Result.AddCallback(Callback, State);
409 Header =
"<?xml version='1.0'?><stream:stream xmlns='jabber:client' xmlns:stream='http://etherx.jabber.org/streams' from='" +
410 this.fullJid +
"' to='" + FullJID +
"' version='1.0'>";
412 Result =
new PeerState(
null,
this, FullJID, Header,
"</stream:stream>",
string.Empty, 1.0, Callback, State);
413 this.peersByFullJid[FullJID] = Result;
419 await Callback.Raise(
this,
new PeerConnectionEventArgs(Result.XmppClient, State,
this.fullJid, FullJID));
422 else if (!(Old is
null))
424 await Old.CallCallbacks();
428 _ = Task.Run(async () =>
434 Connection = await this.ConnectToAsync(FullJID, Info);
441 if (!(ResynchMethod is
null))
445 ResynchEventArgs e =
new ResynchEventArgs(FullJID, async (sender, e2) =>
450 await
this.GetPeerConnection(FullJID, Callback, State,
null);
453 lock (this.peersByFullJid)
455 this.peersByFullJid.Remove(FullJID);
458 await Result.CallCallbacks();
461 catch (Exception ex2)
467 await ResynchMethod(
this, e);
469 catch (Exception ex2)
478 if (Connection is
null)
480 lock (this.peersByFullJid)
482 this.peersByFullJid.Remove(FullJID);
485 await Result.CallCallbacks();
489 Result.Peer = Connection;
490 Connection.
Start(async (Sender, e) =>
492 if (!(ResynchMethod is
null))
494 if (!await ResynchMethod.Raise(
this,
new ResynchEventArgs(FullJID, async (sender2, e2) =>
501 Connection = await this.ConnectToAsync(FullJID, Info);
502 Result.Peer = Connection;
504 Result.HeaderSent = true;
505 await Result.SendAsync(Header);
506 this.TransmitText(Header);
509 await Result.CallCallbacks();
517 await Result.CallCallbacks();
521 await Result.CallCallbacks();
524 Result.HeaderSent =
true;
525 await Result.SendAsync(Header);
531 private async Task<PeerConnection> ConnectToAsync(
string FullJID, AddressInfo Info)
537 if (Info.ExternalIp ==
this.p2pNetwork.ExternalAddress.ToString())
540 Port = Info.LocalPort;
544 Ip = Info.ExternalIp;
545 Port = Info.ExternalPort;
548 if (IPAddress.TryParse(Ip, out IPAddress Addr))
550 this.Information(
"Connecting to " + Ip +
":" + Port.ToString() +
" (" + FullJID +
")");
551 Connection = await this.p2pNetwork.ConnectToPeer(
new IPEndPoint(Addr, Port));
552 this.Information(
"Connected to to " + Ip +
":" + Port.ToString() +
" (" + FullJID +
")");
563 [Obsolete(
"Use the DisposeAsync() method.")]
566 this.DisposeAsync().Wait();
576 if (!(this.p2pNetwork is
null))
578 await this.p2pNetwork.DisposeAsync();
579 this.p2pNetwork =
null;
582 if (!(this.peersByFullJid is
null))
586 lock (this.peersByFullJid)
588 States =
new PeerState[this.peersByFullJid.Count];
589 this.peersByFullJid.Values.CopyTo(States, 0);
591 this.peersByFullJid.Clear();
594 this.peersByFullJid =
null;
598 State.ClearCallbacks();
603 this.disposed =
true;
613 if (!(this.p2pNetwork is
null) &&
615 !(
this.p2pNetwork.ExternalEndpoint is
null) &&
616 !
this.p2pNetwork.OnPublicNetwork())
618 Xml.Append(
"<p2p xmlns='");
620 Xml.Append(
"' extIp='");
621 Xml.Append(this.p2pNetwork.ExternalAddress.ToString());
622 Xml.Append(
"' extPort='");
623 Xml.Append(this.p2pNetwork.ExternalEndpoint.Port.ToString());
624 Xml.Append(
"' locIp='");
625 Xml.Append(this.p2pNetwork.LocalAddress.ToString());
626 Xml.Append(
"' locPort='");
627 Xml.Append(this.p2pNetwork.LocalEndpoint.Port.ToString());
647 foreach (XmlAttribute Attr
in P2P.Attributes)
652 if (IPAddress.TryParse(Attr.Value, out IPAddress Addr) &&
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.
void TransmitText(string Text)
Called when text has been transmitted.
void Exception(Exception Exception)
Called to inform the viewer of an exception state.
ISniffer[] Sniffers
Registered sniffers.
void Information(string Comment)
Called to inform the viewer of something.
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.
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
void Dispose()
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.
Interface for asynchronously disposable objects.
Interface for sniffers. Sniffers can be added to ICommunicationLayer classes to eavesdrop on communic...
class Header(ISimulationNode Parent, Model Model)
Represents an identity property.
PeerToPeerNetworkState
State of Peer-to-peer network.
XmppState
State of XMPP connection.