void SendHelloRequestToPeer(DateTime now, ConnectedPeer peer, ConnectedPeerStream stream, bool requestIpLocation) { var data = new PeerHelloPacket(_localPeer, peer, stream, stream.LastTimeReceivedAccepted.HasValue ? PeerHelloRequestStatus.ping : PeerHelloRequestStatus.setup, requestIpLocation).Encode(); stream.LastTimeSentRequest = now; stream.Socket.UdpSocket.Send(data, data.Length, stream.RemoteEndPoint); // send from all local sockets to all remote sockets to open P2P connection in the NAT }
/// <summary> /// receiver thread /// changes state of connected peer /// </summary> internal void ProcessReceivedHello(byte[] udpData, IPEndPoint remoteEndpoint, SocketWithReceiver socket, uint packetReceivedTimestamp32) { // enqueue into manager thread // reduce load of the receiver thread _actionsQueue.Enqueue(() => { var helloPacket = new PeerHelloPacket(udpData); if (_localPeer.IpLocationScraper == null && !String.IsNullOrEmpty(helloPacket.RequestedFromIp)) { _localPeer.IpLocationScraper = new IpLocationScraper(helloPacket.RequestedFromIp, _localPeer); } if (helloPacket.ToPeerId == null) { if (helloPacket.Status.IsSetupOrPing()) { if (_localPeer.Configuration.RoleAsCoordinator) { ProcessReceivedHello_SetupRequestFromNewPeer(helloPacket, remoteEndpoint, socket, _localPeer.LocalPeerId, packetReceivedTimestamp32); } else { _localPeer.Firewall.OnUnauthenticatedReceivedPacket(remoteEndpoint); PeerHelloPacket.Respond(helloPacket, PeerHelloRequestStatus.rejected_dontTryLater, null, socket, remoteEndpoint); } } else { _localPeer.Firewall.OnUnauthenticatedReceivedPacket(remoteEndpoint); } } else if (helloPacket.ToPeerId.Equals(_localPeer.LocalPeerId) == false) { // bad ToPeerId // can happen if this peer restarts _localPeer.Firewall.OnUnauthenticatedReceivedPacket(remoteEndpoint); if (helloPacket.Status.IsSetupOrPing()) { PeerHelloPacket.Respond(helloPacket, PeerHelloRequestStatus.rejected_tryCleanSetup, null, socket, remoteEndpoint); } } else if (_pendingPeers.ContainsKey(remoteEndpoint)) {// correct ToPeerId and remote endpoint is in _initiallyNotRespondedServers ProcessReceivedHello_FromPendingPeer(helloPacket, remoteEndpoint, packetReceivedTimestamp32); } else { // correct ToPeerId if (_connectedPeers.TryGetValue(helloPacket.FromPeerId, out var connectedPeer)) { // packet from already connected peer ProcessReceivedHello_FromExistingPeer(helloPacket, remoteEndpoint, socket, connectedPeer, packetReceivedTimestamp32); } else if (helloPacket.Status.IsSetupOrPing()) { // FromPeerId is unknown, ToPeerId is this one // request to set up connection from new peer ProcessReceivedHello_SetupRequestFromNewPeer(helloPacket, remoteEndpoint, socket, null, packetReceivedTimestamp32); } } }, "ProcessReceivedHello634569"); }
/// <summary> /// for both coordinatorServer and userPeer /// accepts connection, adds peer and/or stream to lists, responds with 'accepted' /// </summary> void ProcessReceivedHello_SetupRequestFromNewPeer(PeerHelloPacket helloPacket, IPEndPoint remoteEndpoint, SocketWithReceiver socket, PeerId localPeerIdForResponse) { if (_localPeer.Configuration.RoleAsCoordinator) { if (_connectedPeers.Count > LocalLogicConfiguration.CoordinatorPeer_MaxConnectedPeersToAccept) { _localPeer.SysAdminFeedbackChannel.OnReachedMaxConnectedPeersAtThisCoordinatorServer(); PeerHelloPacket.Respond(helloPacket, PeerHelloRequestStatus.rejected_tryLater, null, socket, remoteEndpoint); return; } } else if (_localPeer.Configuration.RoleAsSharedPassive) { if (_connectedPeers.Count > LocalLogicConfiguration.SharedPeer_MaxConnectedPeersToAccept) { _localPeer.SysAdminFeedbackChannel.OnReachedMaxConnectedPeersAtThisSharedPeer(); _localPeer.WriteToLog(LogModules.Hello, $"rejecting request from {remoteEndpoint}: _connectedPeers.Count={_connectedPeers.Count} > LocalLogicConfiguration.SharedPeer_MaxConnectedPeersToAccept"); PeerHelloPacket.Respond(helloPacket, PeerHelloRequestStatus.rejected_dontTryLater, null, socket, remoteEndpoint); return; } } else if (_localPeer.Configuration.RoleAsUser) { if (_connectedPeers.Count > LocalLogicConfiguration.UserPeer_MaxConnectedPeersToAccept) { _localPeer.WriteToLog(LogModules.Hello, $"rejecting request from {remoteEndpoint}: _connectedPeers.Count={_connectedPeers.Count} > LocalLogicConfiguration.UserPeer_MaxConnectedPeersToAccept"); PeerHelloPacket.Respond(helloPacket, PeerHelloRequestStatus.rejected_dontTryLater, null, socket, remoteEndpoint); return; } } if (!_connectedPeers.TryGetValue(helloPacket.FromPeerId, out var peer)) { peer = new ConnectedPeer(_localPeer, helloPacket.FromPeerId) { Type = ConnectedPeerType.fromPeerAccepted }; lock (_connectedPeers) _connectedPeers.Add(helloPacket.FromPeerId, peer); } if (peer.Streams.Count > LocalLogicConfiguration.ConnectedPeerMaxStreamsCount) { _localPeer.WriteToLog(LogModules.Hello, $"rejecting request from {remoteEndpoint}: peer.Streams.Count={peer.Streams.Count} > LocalLogicConfiguration.ConnectedPeerMaxStreamsCount"); PeerHelloPacket.Respond(helloPacket, PeerHelloRequestStatus.rejected_dontTryLater, null, socket, remoteEndpoint); } else { var stream = peer.TryAddStream(socket, remoteEndpoint, helloPacket.StreamId); _localPeer.WriteToLog(LogModules.Hello, $"created new stream {stream} from new peer {peer}"); PeerHelloPacket.Respond(helloPacket, PeerHelloRequestStatus.accepted, localPeerIdForResponse, socket, remoteEndpoint, _localPeer.Configuration.RoleAsUser); } }
void ProcessReceivedAcceptedHello_UpdateHelloLevelFields(ConnectedPeer connectedPeer, ConnectedPeerStream stream, PeerHelloPacket helloResponsePacket, uint responseReceivedTime32) { var rtt = TimeSpan.FromTicks(unchecked (responseReceivedTime32 - helloResponsePacket.RequestTime32)) - TimeSpan.FromMilliseconds(helloResponsePacket.ResponseCpuDelayMs ?? 0); if (rtt < TimeSpan.Zero) { rtt = TimeSpan.Zero; // avoid abnormal measurements } stream.LatestHelloRtt = rtt; stream.LocalPeerPublicIp = helloResponsePacket.RequestedFromIp; stream.LastTimeReceivedAccepted = _localPeer.DateTimeNowUtc; connectedPeer.ProtocolVersion = helloResponsePacket.ProtocolVersion; connectedPeer.LibraryVersion = MiscProcedures.Uint32secondsToDateTime(helloResponsePacket.LibraryVersion); connectedPeer.TotalHelloAcceptedPacketsReceived++; stream.TotalHelloAcceptedPacketsReceived++; stream.RemotePeerRoleIsUser = helloResponsePacket.RoleFlagIsUser; if (helloResponsePacket.IpLocationData != null && connectedPeer.RemoteIpLocationData == null) { _localPeer.WriteToLog_higherLevelDetail(LogModules.Hello, $"got IP location from peer {connectedPeer}: {helloResponsePacket.IpLocationData}"); connectedPeer.RemoteIpLocationData = helloResponsePacket.IpLocationData; } if (helloResponsePacket.ExtensionIds != null && helloResponsePacket.ExtensionIds.Length != 0) { connectedPeer.LatestReceivedRemoteExtensionIds = new HashSet <string>(helloResponsePacket.ExtensionIds.Distinct()); } }
void ProcessReceivedHello_FromPendingPeer(PeerHelloPacket helloPacket, IPEndPoint remoteEndpoint, uint packetReceivedTime32) { var pendingPeer = _pendingPeers[remoteEndpoint]; var stream = pendingPeer.Streams.Values.Single(); // must be only 1 stream in the ConnectedPeer (as initially configured) if (!stream.StreamId.Equals(helloPacket.StreamId)) { _localPeer.Firewall.OnUnauthenticatedReceivedPacket(remoteEndpoint); return; } switch (helloPacket.Status) { case PeerHelloRequestStatus.accepted: { _pendingPeers.Remove(remoteEndpoint); if (!_connectedPeers.TryGetValue(helloPacket.FromPeerId, out var alreadyRespondedPeer)) { // first-stream response from this server pendingPeer.RemotePeerId = helloPacket.FromPeerId; _localPeer.WriteToLog_deepDetail(LogModules.Hello, $"received initial hello response from pending peer {pendingPeer}"); lock (_connectedPeers) _connectedPeers.Add(helloPacket.FromPeerId, pendingPeer); ProcessReceivedAcceptedHello_UpdateHelloLevelFields(pendingPeer, stream, helloPacket, packetReceivedTime32); } else { // secondary streams response: move ConnectedPeerStream _localPeer.WriteToLog_deepDetail(LogModules.Hello, $"received secondary-stream hello response from {remoteEndpoint}, pending peer {alreadyRespondedPeer}"); if (!alreadyRespondedPeer.Streams.ContainsKey(stream.StreamId)) { pendingPeer.RemoveStream(stream); alreadyRespondedPeer.TryAddStream(stream.Socket, stream.RemoteEndPoint, stream.StreamId); // alreadyRespondedPeer.Streams.Add(stream.StreamId, stream); // foreach (var sx in stream.Extensions) // sx.Value.OnMovedToConnectedPeer(); ProcessReceivedAcceptedHello_UpdateHelloLevelFields(alreadyRespondedPeer, stream, helloPacket, packetReceivedTime32); } else { throw new Exception("alreadyRespondedPeer.Streams.ContainsKey(stream.StreamId)"); } } } break; case PeerHelloRequestStatus.rejected_dontTryLater: { _localPeer.WriteToLog_deepDetail(LogModules.Hello, $"received {helloPacket.Status} hello response from pending peer {pendingPeer}"); _pendingPeers.Remove(remoteEndpoint); } break; case PeerHelloRequestStatus.rejected_tryCleanSetup: { _localPeer.WriteToLog_deepDetail(LogModules.Hello, $"received {helloPacket.Status} hello response from pending peer {pendingPeer}"); stream.StreamId = _localPeer.Manager.CreateNewUniqueStreamId(); _pendingPeers.Remove(remoteEndpoint); } break; } }
void ProcessReceivedHello_FromExistingPeer(PeerHelloPacket helloPacket, IPEndPoint remoteEndpoint, SocketWithReceiver socket, ConnectedPeer connectedPeer, uint packetReceivedTimestamp32) { // current situation: // received hello packet; this peer wants to accept connection // in hello packet FromPeerId matches to existing connected peer // there is already at least 1 stream in the existing peer; it could be request via this stream or via another stream // it could be request (ping) or response // it could be packet to new stream, or to existing stream // it could be packet from changed remote endpoint to same stream ID // it could be packet from wrong remote endpoint to new (non-existing locally) stream ID ??????????????????????? if (!connectedPeer.Streams.TryGetValue(helloPacket.StreamId, out var stream)) { // received packet in new streamId if (helloPacket.Status.IsSetupOrPing()) { if (connectedPeer.Streams.Count > LocalLogicConfiguration.ConnectedPeerMaxStreamsCount) { _localPeer.WriteToLog_deepDetail(LogModules.Hello, $"rejecting request from {remoteEndpoint}: connectedPeer.Streams.Count={connectedPeer.Streams.Count} > LocalLogicConfiguration.ConnectedPeerMaxStreamsCount"); PeerHelloPacket.Respond(helloPacket, PeerHelloRequestStatus.rejected_dontTryLater, null, socket, remoteEndpoint); return; } stream = connectedPeer.TryAddStream(socket, remoteEndpoint, helloPacket.StreamId); // _localPeer.WriteToLog(LogModules.Hello, $"peer {connectedPeer} received hello from new stream {stream}"); if (stream == null) { throw new InvalidOperationException(); } } else { _localPeer.Firewall.OnUnauthenticatedReceivedPacket(remoteEndpoint); return; } } else {// received packet in existing streamId // check source endpoint if (!remoteEndpoint.Equals(stream.RemoteEndPoint)) { // dont allow change of source ip/port within same stream _localPeer.Firewall.OnUnauthenticatedReceivedPacket(remoteEndpoint); if (helloPacket.Status.IsSetupOrPing()) { PeerHelloPacket.Respond(helloPacket, PeerHelloRequestStatus.rejected_tryCleanSetup, null, socket, remoteEndpoint); } return; } } // current situation: we got connectedPeer, stream. we handle request or response if (!RemoteVersionIsAcceptableForNewConnection(helloPacket)) { _localPeer.WriteToLog_deepDetail(LogModules.Hello, $"peer {connectedPeer} got response with too old version"); connectedPeer.RemoveStream(stream); _connectedPeers.Remove(connectedPeer.RemotePeerId); return; } switch (helloPacket.Status) { case PeerHelloRequestStatus.setup: case PeerHelloRequestStatus.ping: var responseCpuDelayMs = (ushort)Math.Round(TimeSpan.FromTicks(unchecked (_localPeer.Time32 - packetReceivedTimestamp32)).TotalMilliseconds); PeerHelloPacket.Respond(helloPacket, PeerHelloRequestStatus.accepted, null, socket, remoteEndpoint, responseCpuDelayMs, _localPeer.Configuration.RoleAsUser, _localPeer.IpLocationScraper?.IpLocationData); break; case PeerHelloRequestStatus.accepted: ProcessReceivedAcceptedHello_UpdateHelloLevelFields(connectedPeer, stream, helloPacket, packetReceivedTimestamp32); break; case PeerHelloRequestStatus.rejected_tryCleanSetup: _localPeer.WriteToLog_deepDetail(LogModules.Hello, $"peer {connectedPeer} received {helloPacket.Status} from stream {stream}"); connectedPeer.RemoveStream(stream); AddToPendingPeers(connectedPeer.Type, remoteEndpoint, socket); break; case PeerHelloRequestStatus.rejected_dontTryLater: _localPeer.WriteToLog_deepDetail(LogModules.Hello, $"peer {connectedPeer} received {helloPacket.Status} from stream {stream}"); connectedPeer.RemoveStream(stream); break; case PeerHelloRequestStatus.rejected_tryLater: _localPeer.WriteToLog_deepDetail(LogModules.Hello, $"peer {connectedPeer} received {helloPacket.Status} from stream {stream}"); // it will try anyway on timer break; } }
/// <summary> /// for both coordinatorServer and userPeer /// accepts connection, adds peer and/or stream to lists, responds with 'accepted' /// </summary> void ProcessReceivedHello_SetupRequestFromNewPeer(PeerHelloPacket helloPacket, IPEndPoint remoteEndpoint, SocketWithReceiver socket, PeerId localPeerIdForResponse, uint requestReceivedTimestamp32) { if (_localPeer.Configuration.RoleAsCoordinator) { if (_connectedPeers.Count > LocalLogicConfiguration.CoordinatorPeer_MaxConnectedPeersToAccept) { _localPeer.SysAdminFeedbackChannel.OnReachedMaxConnectedPeersAtThisCoordinatorServer(); PeerHelloPacket.Respond(helloPacket, PeerHelloRequestStatus.rejected_tryLater, null, socket, remoteEndpoint); return; } } else if (_localPeer.Configuration.RoleAsSharedPassive) { if (_connectedPeers.Count > LocalLogicConfiguration.SharedPeer_MaxConnectedPeersToAccept) { _localPeer.SysAdminFeedbackChannel.OnReachedMaxConnectedPeersAtThisSharedPeer(); _localPeer.WriteToLog_deepDetail(LogModules.Hello, $"rejecting request from {remoteEndpoint}: _connectedPeers.Count={_connectedPeers.Count} > LocalLogicConfiguration.SharedPeer_MaxConnectedPeersToAccept"); PeerHelloPacket.Respond(helloPacket, PeerHelloRequestStatus.rejected_dontTryLater, null, socket, remoteEndpoint); return; } } else if (_localPeer.Configuration.RoleAsUser) { if (_connectedPeers.Count > LocalLogicConfiguration.UserPeer_MaxConnectedPeersToAccept) { _localPeer.WriteToLog_deepDetail(LogModules.Hello, $"rejecting request from {remoteEndpoint}: _connectedPeers.Count={_connectedPeers.Count} > LocalLogicConfiguration.UserPeer_MaxConnectedPeersToAccept"); PeerHelloPacket.Respond(helloPacket, PeerHelloRequestStatus.rejected_dontTryLater, null, socket, remoteEndpoint); return; } if (!RemoteVersionIsAcceptableForNewConnection(helloPacket)) { _localPeer.WriteToLog_deepDetail(LogModules.Hello, $"rejecting request from {remoteEndpoint}: old version"); PeerHelloPacket.Respond(helloPacket, PeerHelloRequestStatus.rejected_dontTryLater, null, socket, remoteEndpoint); return; } } if (!_connectedPeers.TryGetValue(helloPacket.FromPeerId, out var peer)) { peer = new ConnectedPeer(_localPeer, helloPacket.FromPeerId, ConnectedPeerType.fromPeerAccepted, remoteEndpoint.Address); _localPeer.WriteToLog_deepDetail(LogModules.Hello, $"accepted initial hello from peer {peer}"); lock (_connectedPeers) _connectedPeers.Add(helloPacket.FromPeerId, peer); } if (peer.Streams.Count > LocalLogicConfiguration.ConnectedPeerMaxStreamsCount) { _localPeer.WriteToLog_deepDetail(LogModules.Hello, $"rejecting request from {remoteEndpoint}: peer.Streams.Count={peer.Streams.Count} > LocalLogicConfiguration.ConnectedPeerMaxStreamsCount"); PeerHelloPacket.Respond(helloPacket, PeerHelloRequestStatus.rejected_dontTryLater, null, socket, remoteEndpoint); } else { var stream = peer.TryAddStream(socket, remoteEndpoint, helloPacket.StreamId); // _localPeer.WriteToLog(LogModules.Hello, $"created new stream {stream} from new peer {peer}"); var responseCpuDelayMs = (ushort)Math.Round(TimeSpan.FromTicks(unchecked (_localPeer.Time32 - requestReceivedTimestamp32)).TotalMilliseconds); PeerHelloPacket.Respond(helloPacket, PeerHelloRequestStatus.accepted, localPeerIdForResponse, socket, remoteEndpoint, responseCpuDelayMs, _localPeer.Configuration.RoleAsUser); } }
bool RemoteVersionIsAcceptableForNewConnection(PeerHelloPacket remoteHello) { return(MiscProcedures.Uint32secondsToDateTime(remoteHello.LibraryVersion) > MiscProcedures.MinPeerCompilationDateTimeUtcExclusive); }
void ProcessReceivedAcceptedHello_UpdateHelloLevelFields(ConnectedPeer connectedPeer, ConnectedPeerStream stream, PeerHelloPacket helloPacket) { stream.LatestHelloRtt = TimeSpan.FromTicks(unchecked (_localPeer.Time32 - helloPacket.RequestTime32)); stream.LastTimeReceivedAccepted = _localPeer.DateTimeNowUtc; connectedPeer.ProtocolVersion = helloPacket.ProtocolVersion; connectedPeer.LibraryVersion = CompilationInfo.ToDateTime(helloPacket.LibraryVersion); connectedPeer.TotalHelloAcceptedPacketsReceived++; stream.TotalHelloAcceptedPacketsReceived++; stream.RemotePeerRoleIsUser = helloPacket.RoleFlagIsUser; if (helloPacket.ExtensionIds != null && helloPacket.ExtensionIds.Length != 0) { connectedPeer.LatestReceivedRemoteExtensionIds = new HashSet <string>(helloPacket.ExtensionIds.Distinct()); } }