/// <summary> /// the only procedure which creates/adds streams /// manager thread /// this will initiate hello-level packets to remote peer /// </summary> /// <param name="streamId"> /// if null, it generates random ID, using optional notAllowedStreamIds /// </param> /// <returns> /// null is stream with this StreamId already exists /// </returns> internal ConnectedPeerStream TryAddStream(SocketWithReceiver socket, IPEndPoint remoteEP, StreamId streamId = null, IEnumerable <StreamId> notAllowedStreamIds = null) { if (streamId == null) { _retry: streamId = _localPeer.Manager.CreateNewUniqueStreamId(); if (Streams.ContainsKey(streamId)) { goto _retry; } if (notAllowedStreamIds != null) { if (notAllowedStreamIds.Any(x => x.Equals(streamId))) { goto _retry; } } } else { if (_localPeer.Manager.IsItUniqueStreamId(streamId) == false) { return(null); } } var r = new ConnectedPeerStream(_localPeer, this, streamId, remoteEP, socket); lock (Streams) Streams.Add(streamId, r); socket.OnCreatedDestroyedStream(r, true); return(r); }
void AddToPendingPeers(ConnectedPeerType type, IPEndPoint remoteEndpoint, SocketWithReceiver socket) { var cp = new ConnectedPeer(_localPeer, null, type, remoteEndpoint.Address); cp.TryAddStream(socket, remoteEndpoint, null, _pendingPeers.Values.Select(x => x.Streams.Values.Single().StreamId)); // all "pending" streams will have unique local stream ID _pendingPeers.Add(remoteEndpoint, cp); }
internal ConnectedPeerStream(LocalPeer localPeer, ConnectedPeer connectedPeer, StreamId streamId, IPEndPoint remoteEP, SocketWithReceiver socket) { _localPeer = localPeer; Created = localPeer.DateTimeNowUtc; StreamId = streamId; RemoteEndPoint = remoteEP; Socket = socket; Extensions = connectedPeer.Extensions.ToDictionary(ext => ext.Key, ext => ext.Value.OnConnectedPeerStream(this)); }
internal void ProcessReceivedNat1TestRequest(SocketWithReceiver socket, byte[] udpData, IPEndPoint remoteEndpoint) // receiver thread { // todo put some limits here per endpoint? or we don't care? var request = NatTest1RequestPacket.Decode(udpData); var response = new NatTest1ResponsePacket { RequesterEndpoint = remoteEndpoint, Token32 = request.Token32 }; var responseData = response.Encode(); socket.UdpSocket.Send(responseData, responseData.Length, remoteEndpoint); }
/// <summary> /// receiver thread /// changes state of connected peer /// </summary> internal void ProcessReceivedHello(byte[] data, IPEndPoint remoteEndpoint, SocketWithReceiver socket) { // enqueue into manager thread // reduce load of the receiver thread _actionsQueue.Enqueue(() => { var helloPacket = new PeerHelloPacket(data); if (helloPacket.ToPeerId == null) { if (helloPacket.Status.IsSetupOrPing()) { if (_localPeer.Configuration.RoleAsCoordinator) { ProcessReceivedHello_SetupRequestFromNewPeer(helloPacket, remoteEndpoint, socket, _localPeer.LocalPeerId); } 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); } else { // correct ToPeerId if (_connectedPeers.TryGetValue(helloPacket.FromPeerId, out var connectedPeer)) { // packet from already connected peer ProcessReceivedHello_FromExistingPeer(helloPacket, remoteEndpoint, socket, connectedPeer); } 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); } } }); }
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); } }
/// <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); } }