/// <returns>null if no neighbors found for routing</returns> public ConnectionToNeighbor RouteInviteRequest(LocalDrpPeer localDrpPeer, RoutedRequest routedRequest) { var req = routedRequest.InviteReq; var logger = routedRequest.Logger; ConnectionToNeighbor r = null; RegistrationIdDistance minDistance = null; var connectedNeighborsForRouting = localDrpPeer.GetConnectedNeighborsForRouting(routedRequest).ToList(); foreach (var connectedPeer in connectedNeighborsForRouting) { var distanceToConnectedPeer = req.ResponderRegistrationId.GetDistanceTo(_cryptoLibrary, connectedPeer.RemoteRegistrationId, NumberOfDimensions); logger.WriteToLog_routing_detail($"distanceToConnectedPeer={distanceToConnectedPeer} from {req} to {connectedPeer}"); if (minDistance == null || minDistance.IsGreaterThan(distanceToConnectedPeer)) { minDistance = distanceToConnectedPeer; r = connectedPeer; } } if (r == null) { logger.WriteToLog_routing_detail($"no neighbors found for routing"); } return(r); }
public IEnumerable <ConnectionToNeighbor> GetConnectedNeighborsForRouting(RoutedRequest routedRequest) { foreach (var connectedPeer in ConnectedNeighborsCanBeUsedForNewRequests) { if (routedRequest.ReceivedFromNeighborNullable != null && connectedPeer == routedRequest.ReceivedFromNeighborNullable) { if (routedRequest.Logger.WriteToLog_deepDetail_enabled) { routedRequest.Logger.WriteToLog_deepDetail($"skipping routing back to source peer {connectedPeer}"); } continue; } if (routedRequest.TriedNeighbors.Contains(connectedPeer)) { if (routedRequest.Logger.WriteToLog_deepDetail_enabled) { routedRequest.Logger.WriteToLog_deepDetail($"skipping routing to previously tried peer {connectedPeer}"); } continue; } if (routedRequest.RequesterRegistrationId.Equals(connectedPeer.RemoteRegistrationId)) { if (routedRequest.Logger.WriteToLog_deepDetail_enabled) { routedRequest.Logger.WriteToLog_deepDetail($"skipping routing to peer with same regID {connectedPeer}"); } continue; } yield return(connectedPeer); } }
async Task ProcessRegisterReqAtoEpPacket2Async(IPEndPoint requesterEndpoint, RegisterRequestPacket req, Stopwatch reqReceivedSW) { var firstLocalPeer = LocalPeers.Values.FirstOrDefault(); if (firstLocalPeer != null) { var logger = new Logger(this, firstLocalPeer, req, VisionChannelModuleName_reg); var routedRequest = new RoutedRequest(logger, null, requesterEndpoint, reqReceivedSW, null, req); await ProcessRegisterRequestAsync(null, routedRequest); } else { WriteToLog_reg_responderSide_higherLevelDetail($"no local peers to accept {req}"); } }
public RoutedRequest(Logger logger, ConnectionToNeighbor receivedFromNeighborNullable, IPEndPoint receivedFromEndpoint, Stopwatch reqReceivedSwNullable, InviteRequestPacket inviteReqNullable, RegisterRequestPacket registerReqNullable, RoutedRequest previousTrialRoutedRequestNullable = null) { InviteReq = inviteReqNullable; RegisterReq = registerReqNullable; if (InviteReq == null && RegisterReq == null) { throw new ArgumentException(); } if (InviteReq != null && RegisterReq != null) { throw new ArgumentException(); } ReceivedFromNeighborNullable = receivedFromNeighborNullable; ReceivedFromEndpoint = receivedFromEndpoint; Logger = logger; _engine = logger.Engine; ReqReceivedSwNullable = reqReceivedSwNullable; if (InviteReq != null) { ReqP2pSeq16 = InviteReq.ReqP2pSeq16; } else { ReqP2pSeq16 = RegisterReq.ReqP2pSeq16; } if (previousTrialRoutedRequestNullable != null) { _repliedWithNPA = previousTrialRoutedRequestNullable._repliedWithNPA; TriedNeighbors = previousTrialRoutedRequestNullable.TriedNeighbors; } }
/// <summary> /// main register responder proc for both A-EP and P2P modes /// in P2P mode Timestamp32S, NeighborToken32 and NeighborHMAC are verified at this time /// </summary> /// <param name="receivedFromInP2pMode"> /// is null in A-EP mode /// </param> internal async Task AcceptRegisterRequestAsync(LocalDrpPeer acceptAt, RoutedRequest routedRequest) // engine thread { var logger = routedRequest.Logger; logger.ModuleName = VisionChannelModuleName_reg_responderSide; var req = routedRequest.RegisterReq; if (req.RequesterRegistrationId.Equals(acceptAt.Configuration.LocalPeerRegistrationId)) { throw new InvalidOperationException(); } // check signature of requester (A) if (!req.RequesterSignature.Verify(_cryptoLibrary, w => req.GetSharedSignedFields(w, false), req.RequesterRegistrationId ) ) { throw new BadSignatureException("invalid REGISTER REQ RequesterSignature 2396"); } if (routedRequest.ReceivedFromNeighborNullable == null) { // A-EP mode if (req.EpEndpoint.Address.Equals(acceptAt.PublicIpApiProviderResponse) == false) { throw new PossibleAttackException(); } } if (PendingRegisterRequestExists(req.RequesterRegistrationId)) { // received duplicate REGISTER REQ packet logger.WriteToLog_needsAttention($"ignoring duplicate registration request {req.RequesterRegistrationId} from {routedRequest.ReceivedFromEndpoint}"); return; } if (!RecentUniqueAcceptedRegistrationRequests.Filter(req.GetUniqueRequestIdFields)) { logger.WriteToLog_needsAttention($"ignoring registration request {req.RequesterRegistrationId} ts={req.ReqTimestamp64} from {routedRequest.ReceivedFromEndpoint} with non-unique request ID fields"); return; } logger.WriteToLog_higherLevelDetail($"accepting registration from {routedRequest.ReceivedFromEndpoint}: ReqP2pSeq16={req.ReqP2pSeq16}, NumberOfHopsRemaining={req.NumberOfHopsRemaining}, epEndpoint={req.EpEndpoint}, sourcePeer={routedRequest.ReceivedFromNeighborNullable}, ts={req.ReqTimestamp64}"); if (!RecentUniquePublicEcdhKeys.Filter(req.RequesterEcdhePublicKey.Ecdh25519PublicKey)) { logger.WriteToLog_needsAttention($"ignoring registration request {req.RequesterRegistrationId} from {routedRequest.ReceivedFromEndpoint} with non-unique RequesterEcdhePublicKey"); return; } _pendingRegisterRequests.Add(req.RequesterRegistrationId); try { if (logger.WriteToLog_detail_enabled) { logger.WriteToLog_detail($"sending NPACK to REQ to {routedRequest.ReceivedFromEndpoint} (delay={routedRequest.ReqReceivedSw_ms}ms)"); } routedRequest.SendNeighborPeerAck_accepted_IfNotAlreadyReplied(); var newConnectionToNeighbor = new ConnectionToNeighbor(this, acceptAt, ConnectedDrpPeerInitiatedBy.remotePeer, req.RequesterRegistrationId) { LocalEndpoint = routedRequest.ReceivedFromNeighborNullable?.LocalEndpoint ?? req.EpEndpoint, }; byte[] ack1UdpData; try { var ack1 = new RegisterAck1Packet { RequesterRegistrationId = req.RequesterRegistrationId, ReqTimestamp64 = req.ReqTimestamp64, ResponderEcdhePublicKey = new EcdhPublicKey(newConnectionToNeighbor.LocalEcdhe25519PublicKey), ResponderRegistrationId = acceptAt.Configuration.LocalPeerRegistrationId, ReqP2pSeq16 = GetNewNpaSeq16_AtoEP(), }; RecentUniquePublicEcdhKeys.AssertIsUnique(ack1.ResponderEcdhePublicKey.Ecdh25519PublicKey, $"ack1.ResponderEcdhePublicKey"); ack1.ToResponderTxParametersEncrypted = newConnectionToNeighbor.Encrypt_ack1_ToResponderTxParametersEncrypted_AtResponder_DeriveSharedDhSecret(logger, req, ack1, routedRequest.ReceivedFromNeighborNullable); ack1.ResponderSignature = RegistrationSignature.Sign(_cryptoLibrary, (w2) => { req.GetSharedSignedFields(w2, true); ack1.GetSharedSignedFields(w2, false, true); }, acceptAt.Configuration.LocalPeerRegistrationPrivateKey); if (routedRequest.ReceivedFromNeighborNullable == null) { ack1.RequesterEndpoint = routedRequest.ReceivedFromEndpoint; } ack1UdpData = ack1.Encode_OpionallySignNeighborHMAC(routedRequest.ReceivedFromNeighborNullable); var ack2Scanner = RegisterAck2Packet.GetScanner(logger, routedRequest.ReceivedFromNeighborNullable, req); var requesterVisibleDescription = routedRequest.ReceivedFromNeighborNullable?.ToString() ?? routedRequest.ReceivedFromEndpoint.ToString(); byte[] ack2UdpData; if (routedRequest.ReceivedFromNeighborNullable == null) { // wait for ACK2, retransmitting ACK1 if (logger.WriteToLog_detail_enabled) { logger.WriteToLog_detail($"sending ACK1, waiting for ACK2"); } ack2UdpData = await OptionallySendUdpRequestAsync_Retransmit_WaitForResponse("ack2 33469", requesterVisibleDescription, ack1UdpData, routedRequest.ReceivedFromEndpoint, ack2Scanner); } else { // retransmit ACK1 until NPACK (via P2P); at same time wait for ACK if (logger.WriteToLog_detail_enabled) { logger.WriteToLog_detail($"sending ACK1, awaiting for NPACK"); } _ = OptionallySendUdpRequestAsync_Retransmit_WaitForNeighborPeerAck("ack1 423087", ack1UdpData, routedRequest.ReceivedFromEndpoint, ack1.ReqP2pSeq16, routedRequest.ReceivedFromNeighborNullable, ack1.GetSignedFieldsForNeighborHMAC); // not waiting for NPACK, wait for ACK if (logger.WriteToLog_detail_enabled) { logger.WriteToLog_detail($"waiting for ACK2"); } ack2UdpData = await OptionallySendUdpRequestAsync_Retransmit_WaitForResponse("ack2 46051", requesterVisibleDescription, null, routedRequest.ReceivedFromEndpoint, ack2Scanner); } if (logger.WriteToLog_detail_enabled) { logger.WriteToLog_detail($"received ACK2"); } var ack2 = RegisterAck2Packet.Decode_OptionallyVerify_InitializeP2pStreamAtResponder(logger, ack2UdpData, req, ack1, newConnectionToNeighbor); if (logger.WriteToLog_detail_enabled) { logger.WriteToLog_detail($"verified ACK2"); } acceptAt.AddToConnectedNeighbors(newConnectionToNeighbor, req); // added to list here in order to respond to ping requests from A SendNeighborPeerAckResponseToRegisterAck2(ack2, routedRequest.ReceivedFromEndpoint, routedRequest.ReceivedFromNeighborNullable); // send NPACK to ACK _ = WaitForRegistrationConfirmationRequestAsync(requesterVisibleDescription, logger, routedRequest.ReceivedFromEndpoint, req, newConnectionToNeighbor, routedRequest.ReceivedFromNeighborNullable); #region send ping, verify pong var ping = newConnectionToNeighbor.CreatePing(true, false, acceptAt.ConnectedNeighborsBusySectorIds, acceptAt.AnotherNeighborToSameSectorExists(newConnectionToNeighbor)); var pendingPingRequest = new PendingLowLevelUdpRequest("pendingPingRequest 693", newConnectionToNeighbor.RemoteEndpoint, PongPacket.GetScanner(newConnectionToNeighbor.LocalNeighborToken32, ping.PingRequestId32), DateTimeNowUtc, Configuration.InitialPingRequests_ExpirationTimeoutS, ping.Encode(), Configuration.InitialPingRequests_InitialRetransmissionTimeoutS, Configuration.InitialPingRequests_RetransmissionTimeoutIncrement ); if (logger.WriteToLog_detail_enabled) { logger.WriteToLog_detail($"sent PING"); } var pongPacketData = await SendUdpRequestAsync_Retransmit(pendingPingRequest); // wait for pong from A if (pongPacketData == null) { throw new DrpTimeoutException($"reg. responder initial PING request to {newConnectionToNeighbor} (timeout={Configuration.InitialPingRequests_ExpirationTimeoutS}s)"); } var pong = PongPacket.DecodeAndVerify(_cryptoLibrary, pongPacketData, ping, newConnectionToNeighbor, true); if (logger.WriteToLog_detail_enabled) { logger.WriteToLog_detail($"verified PONG"); } newConnectionToNeighbor.OnReceivedVerifiedPong(pong, pendingPingRequest.ResponseReceivedAtUtc.Value, pendingPingRequest.ResponseReceivedAtUtc.Value - pendingPingRequest.InitialTxTimeUTC.Value); #endregion } catch (Exception exc) { newConnectionToNeighbor.Dispose(); throw exc; } } catch (DrpTimeoutException exc) { logger.WriteToLog_needsAttention($"could not accept REGISTER request: {exc}"); } catch (Exception exc) { logger.WriteToLog_mediumPain($"could not accept REGISTER request: {exc}"); } finally { _pendingRegisterRequests.Remove(req.RequesterRegistrationId); } }
internal async Task ProcessRegisterRequestAsync(LocalDrpPeer receivedAtLocalDrpPeerNullable, RoutedRequest routedRequest) { var req = routedRequest.RegisterReq; if (!ValidateReceivedReqTimestamp64(req.ReqTimestamp64)) { throw new BadSignatureException($"invalid REGISTER REQ 265 ReqTimestamp64={MiscProcedures.Int64ticksToDateTime(req.ReqTimestamp64)}"); } if (PendingRegisterRequestExists(req.RequesterRegistrationId)) { routedRequest.Logger.WriteToLog_higherLevelDetail($"rejecting {req}: another request is already pending"); await routedRequest.SendErrorResponse(ResponseOrFailureCode.failure_routeIsUnavailable); return; } _retry: routedRequest.ReceivedFromNeighborNullable?.AssertIsNotDisposed(); if (!RouteRegistrationRequest(receivedAtLocalDrpPeerNullable, routedRequest, out var proxyToDestinationPeer, out var acceptAt)) // routing { // no route found await routedRequest.SendErrorResponse(ResponseOrFailureCode.failure_routeIsUnavailable); return; } if (acceptAt != null) { // accept the registration request here at this.LocalDrpPeer routedRequest.ReceivedFromNeighborNullable?.AssertIsNotDisposed(); _ = AcceptRegisterRequestAsync(acceptAt, routedRequest); } else if (proxyToDestinationPeer != null) { // proxy the registration request to another peer routedRequest.ReceivedFromNeighborNullable?.AssertIsNotDisposed(); var needToRerouteToAnotherNeighbor = await ProxyRegisterRequestAsync(routedRequest, proxyToDestinationPeer); if (needToRerouteToAnotherNeighbor && routedRequest.ReceivedFromNeighborNullable?.IsDisposed != true) { routedRequest.TriedNeighbors.Add(proxyToDestinationPeer); routedRequest.Logger.WriteToLog_detail($"retrying to proxy registration to another neighbor on error. already tried {routedRequest.TriedNeighbors.Count}"); routedRequest.ReceivedFromNeighborNullable?.AssertIsNotDisposed(); goto _retry; } } else { routedRequest.Logger.WriteToLog_detail($"rejecting request: routing failed"); await routedRequest.SendErrorResponse(ResponseOrFailureCode.failure_routeIsUnavailable); } }
/// <summary> /// Timestamp32S, NeighborToken32 and NeighborHMAC are verified at this time /// </summary> internal async Task AcceptInviteRequestAsync(RoutedRequest routedRequest) { if (routedRequest.ReceivedFromNeighborNullable == null) { throw new ArgumentException(); } var req = routedRequest.InviteReq; if (!req.ResponderRegistrationId.Equals(this.Configuration.LocalPeerRegistrationId)) { throw new ArgumentException(); } var logger = routedRequest.Logger; logger.ModuleName = DrpPeerEngine.VisionChannelModuleName_inv_responderSide; if (logger.WriteToLog_detail_enabled) { logger.WriteToLog_detail($"accepting {req} from sourcePeer={routedRequest.ReceivedFromNeighborNullable}"); } // check if regID exists in contact book, get userID from the local contact book // ignore the REQ packet if no such user in contacts this._drpPeerApp.OnReceivedInvite(req.RequesterRegistrationId, req.ContactInvitationTokenNullable, out var remoteRequesterUserIdFromLocalContactBookNullable, out var localUserCertificateWithPrivateKey, out var autoReply); if (autoReply == false) { if (logger.WriteToLog_detail_enabled) { logger.WriteToLog_needsAttention($"ignored invite: autoReply = false"); } return; } localUserCertificateWithPrivateKey?.AssertHasPrivateKey(); if (remoteRequesterUserIdFromLocalContactBookNullable != null) { if (logger.WriteToLog_detail_enabled) { logger.WriteToLog_detail($"resolved user {remoteRequesterUserIdFromLocalContactBookNullable} by requester regID={req.RequesterRegistrationId}"); } } if (!Engine.RecentUniquePublicEcdhKeys.Filter(req.RequesterEcdhePublicKey.Ecdh25519PublicKey)) { logger.WriteToLog_mediumPain($"RequesterEcdhePublicKey {req.RequesterEcdhePublicKey} is not unique, it has been recently processed"); return; } if (!Engine.RecentUniqueInviteRequests.Filter(req.GetUniqueRequestIdFields)) { logger.WriteToLog_mediumPain($"{req} fields are not unique, the request has been recently processed"); return; } // verify requester reg. signature if (!req.RequesterRegistrationSignature.Verify(Engine.CryptoLibrary, req.GetSharedSignedFields, req.RequesterRegistrationId)) { throw new BadSignatureException("invalid INVITE REQ RequesterRegistrationSignature 2349"); } _pendingInviteRequests.Add(req.RequesterRegistrationId); try { // send NPACK to REQ if (logger.WriteToLog_detail_enabled) { logger.WriteToLog_detail($"sending NPACK to REQ source peer"); } routedRequest.SendNeighborPeerAck_accepted_IfNotAlreadyReplied(); var session = new InviteSession(this) { Logger = logger }; try { session.DeriveSharedInviteAckDhSecret(Engine.CryptoLibrary, req.RequesterEcdhePublicKey.Ecdh25519PublicKey); session.LocalSessionDescription = new InviteSessionDescription { DirectChannelEndPoint = routedRequest.ReceivedFromNeighborNullable.LocalEndpoint, NatBehaviour = Engine.LocalNatBehaviour, DirectChannelToken32 = session.LocalDirectChannelToken32 }; if (logger.WriteToLog_detail_enabled) { logger.WriteToLog_detail($"responding with local session {session.LocalSessionDescription}"); } session.LocalSessionDescription.UserCertificate = localUserCertificateWithPrivateKey; #region send ACK1. sign local SD by local user var ack1 = new InviteAck1Packet { ReqTimestamp32S = req.ReqTimestamp32S, RequesterRegistrationId = req.RequesterRegistrationId, ResponderRegistrationId = req.ResponderRegistrationId, ResponderEcdhePublicKey = new EcdhPublicKey(session.LocalInviteAckEcdhePublicKey), }; session.LocalSessionDescription.UserCertificateSignature = UserCertificateSignature.Sign(Engine.CryptoLibrary, w => { req.GetSharedSignedFields(w); ack1.GetSharedSignedFields(w, false); session.LocalSessionDescription.WriteSignedFields(w); }, localUserCertificateWithPrivateKey); ack1.ToResponderSessionDescriptionEncrypted = session.LocalSessionDescription.Encrypt(Engine.CryptoLibrary, req, ack1, session, false); ack1.ResponderRegistrationSignature = RegistrationSignature.Sign(Engine.CryptoLibrary, w => { req.GetSharedSignedFields(w); ack1.GetSharedSignedFields(w, true); }, this.Configuration.LocalPeerRegistrationPrivateKey ); var ack1UdpData = ack1.Encode_SetP2pFields(routedRequest.ReceivedFromNeighborNullable); if (logger.WriteToLog_detail_enabled) { logger.WriteToLog_detail($"sending ACK1 to source peer, awaiting for NPACK"); } _ = routedRequest.ReceivedFromNeighborNullable.SendUdpRequestAsync_Retransmit_WaitForNPACK("ack1 1450", ack1UdpData, ack1.ReqP2pSeq16, ack1.GetSignedFieldsForNeighborHMAC); // not waiting for NPACK, wait for ACK2 #endregion // wait for ACK2 if (logger.WriteToLog_detail_enabled) { logger.WriteToLog_detail($"waiting for ACK2"); } var ack2UdpData = await Engine.OptionallySendUdpRequestAsync_Retransmit_WaitForResponse("ack2 23467789", routedRequest.ReceivedFromNeighborNullable.ToString(), null, routedRequest.ReceivedFromNeighborNullable.RemoteEndpoint, InviteAck2Packet.GetScanner(logger, req, routedRequest.ReceivedFromNeighborNullable)); if (logger.WriteToLog_detail_enabled) { logger.WriteToLog_detail($"received ACK2"); } var ack2 = InviteAck2Packet.Decode(ack2UdpData); if (!ack2.RequesterRegistrationSignature.Verify(Engine.CryptoLibrary, w => { req.GetSharedSignedFields(w); ack1.GetSharedSignedFields(w, true); ack2.GetSharedSignedFields(w); }, req.RequesterRegistrationId)) { throw new BadSignatureException("invalid INVITE ACK2 RequesterRegistrationSignature 2348"); } // decrypt, verify SD remote user's certificate and signature session.RemoteSessionDescription = InviteSessionDescription.Decrypt_Verify(Engine.CryptoLibrary, ack2.ToRequesterSessionDescriptionEncrypted, req, ack1, true, session, remoteRequesterUserIdFromLocalContactBookNullable, Engine.DateTimeNowUtc); session.LocalSessionDescription.SessionType = session.RemoteSessionDescription.SessionType; switch (session.RemoteSessionDescription.SessionType) { case SessionType.asyncShortSingleMessage: break; case SessionType.ike1: if (_drpPeerApp.OnReceivedInvite_GetLocalIke1Data(req.ContactInvitationTokenNullable) == null) { throw new BadSignatureException("bad ContactInvitationToken 21379"); } break; default: throw new NotImplementedException(); } // send NPACK to ACK2 if (logger.WriteToLog_detail_enabled) { logger.WriteToLog_detail($"sending NPACK to ACK2 to source peer"); } SendNeighborPeerAckResponseToAck2(ack2, routedRequest.ReceivedFromNeighborNullable); // send CFM with signature var cfm = new InviteConfirmationPacket { ReqTimestamp32S = req.ReqTimestamp32S, RequesterRegistrationId = req.RequesterRegistrationId, ResponderRegistrationId = req.ResponderRegistrationId, }; cfm.ResponderRegistrationSignature = RegistrationSignature.Sign(Engine.CryptoLibrary, w => { req.GetSharedSignedFields(w); ack1.GetSharedSignedFields(w, true); ack2.GetSharedSignedFields(w); }, this.Configuration.LocalPeerRegistrationPrivateKey); var cfmUdpData = cfm.Encode_SetP2pFields(routedRequest.ReceivedFromNeighborNullable); if (logger.WriteToLog_detail_enabled) { logger.WriteToLog_detail($"sending CFM to source peer, waiting for NPACK"); } await routedRequest.ReceivedFromNeighborNullable.SendUdpRequestAsync_Retransmit_WaitForNPACK("cfm 49146", cfmUdpData, cfm.ReqP2pSeq16, cfm.GetSignedFieldsForNeighborHMAC); if (logger.WriteToLog_detail_enabled) { logger.WriteToLog_detail($"received NPACK to CFM"); } session.DeriveSharedPingPongHmacKey(req, ack1, ack2, cfm); await session.SetupAEkeysAsync(); } catch { session.Dispose(); throw; } if (autoReply) { switch (session.RemoteSessionDescription.SessionType) { case SessionType.asyncShortSingleMessage: if (logger.WriteToLog_detail_enabled) { logger.WriteToLog_detail($"autoReply=true: receiving message"); } _ = ReceiveShortSingleMessageAsync(session, req); break; case SessionType.ike1: if (logger.WriteToLog_detail_enabled) { logger.WriteToLog_detail($"autoReply=true: starting IKE1"); } _ = Ike1Async_AtInviteResponder(session, req); break; default: throw new NotImplementedException(); } } else { session.Dispose(); // todo implement other cases } } catch (DrpTimeoutException exc) { logger.WriteToLog_lightPain($"could not accept INVITE request: {exc}"); } catch (Exception exc) { logger.WriteToLog_mediumPain($"could not accept INVITE request: {exc}"); } finally { _pendingInviteRequests.Remove(req.RequesterRegistrationId); } }
/// <summary> /// Timestamp32S, NeighborToken32 and NeighborHMAC are verified at this time /// </summary> /// <returns> /// true to retry the request with another neighbor (if the request needs to be "rerouted") /// </returns> internal async Task <bool> ProxyInviteRequestAsync(RoutedRequest routedRequest, ConnectionToNeighbor destinationPeer) { var req = routedRequest.InviteReq; var logger = routedRequest.Logger; logger.ModuleName = DrpPeerEngine.VisionChannelModuleName_inv_proxySide; if (logger.WriteToLog_detail_enabled) { logger.WriteToLog_detail($"proxying {req} to {destinationPeer}"); } if (!routedRequest.CheckedRecentUniqueProxiedRequests) { if (!Engine.RecentUniqueInviteRequests.Filter(req.GetUniqueRequestIdFields)) { logger.WriteToLog_higherLevelDetail($"rejecting non-unique {req}: requesterEndpoint={routedRequest.ReceivedFromEndpoint}"); await routedRequest.SendErrorResponse(ResponseOrFailureCode.failure_routeIsUnavailable); return(false); } routedRequest.CheckedRecentUniqueProxiedRequests = true; } if (req.NumberOfHopsRemaining > InviteRequestPacket.MaxNumberOfHopsRemaining) { await routedRequest.SendErrorResponse(ResponseOrFailureCode.failure_routeIsUnavailable); return(false); } if (req.NumberOfHopsRemaining <= 1) { await routedRequest.SendErrorResponse(ResponseOrFailureCode.failure_numberOfHopsRemainingReachedZero); return(false); } _pendingInviteRequests.Add(req.RequesterRegistrationId); try { // send NPACK to REQ if (logger.WriteToLog_detail_enabled) { logger.WriteToLog_detail($"sending NPACK to REQ source peer"); } routedRequest.SendNeighborPeerAck_accepted_IfNotAlreadyReplied(); req.NumberOfHopsRemaining--; if (logger.WriteToLog_detail_enabled) { logger.WriteToLog_detail($"decremented number of hops in {req}: NumberOfHopsRemaining={req.NumberOfHopsRemaining}"); } // send (proxy) REQ to responder. wait for NPACK, verify NPACK.senderHMAC, retransmit REQ var reqUdpData = req.Encode_SetP2pFields(destinationPeer); #region wait for ACK1 from responder verify NeighborHMAC byte[] ack1UdpData; try { var sentRequest = new SentRequest(Engine, logger, destinationPeer.RemoteEndpoint, destinationPeer, reqUdpData, req.ReqP2pSeq16, InviteAck1Packet.GetScanner(logger, req, destinationPeer)); ack1UdpData = await sentRequest.SendRequestAsync("inv proxy ack1 3457"); } catch (RequestRejectedException reqExc) { logger.WriteToLog_higherLevelDetail($"got response={reqExc.ResponseCode} from destination {destinationPeer}. will try another neighbor.."); if (reqExc.ResponseCode == ResponseOrFailureCode.failure_numberOfHopsRemainingReachedZero) { await routedRequest.SendErrorResponse(ResponseOrFailureCode.failure_numberOfHopsRemainingReachedZero); return(false); } if (routedRequest.ReceivedFromNeighborNullable?.IsDisposed == true) { logger.WriteToLog_needsAttention($"sourcePeer={routedRequest.ReceivedFromNeighborNullable} is disposed during proxying 75675"); return(false); } if (reqExc.ResponseCode == ResponseOrFailureCode.failure_routeIsUnavailable) { req.NumberOfHopsRemaining++; // roll back previous decrement for a new trial } return(true); // will retry } catch (DrpTimeoutException exc) { logger.WriteToLog_higherLevelDetail($"got timeout error when requesting {destinationPeer}: {exc.Message}"); req.NumberOfHopsRemaining++; // roll back previous decrement for a new trial return(true); // will retry } catch (Exception reqExc) { logger.WriteToLog_mediumPain($"could not proxy INVITE request: {reqExc}"); if (routedRequest.ReceivedFromNeighborNullable?.IsDisposed == true) { logger.WriteToLog_needsAttention($"sourcePeer={routedRequest.ReceivedFromNeighborNullable} is disposed during proxying 76897805"); return(false); } return(true); // will retry } var ack1 = InviteAck1Packet.Decode(ack1UdpData); if (logger.WriteToLog_detail_enabled) { logger.WriteToLog_detail($"verified ACK1 from responder"); } // respond with NPACK to ACk1 SendNeighborPeerAckResponseToAck1(ack1, destinationPeer); #endregion #region send ACK1 to requester, wait for NPACK and ACK2 var ack1UdpDataTx = ack1.Encode_SetP2pFields(routedRequest.ReceivedFromNeighborNullable); if (logger.WriteToLog_detail_enabled) { logger.WriteToLog_detail($"sending ACK1, awaiting for NPACK"); } _ = Engine.OptionallySendUdpRequestAsync_Retransmit_WaitForNeighborPeerAck("ack1 13536", ack1UdpDataTx, routedRequest.ReceivedFromEndpoint, ack1.ReqP2pSeq16, routedRequest.ReceivedFromNeighborNullable, ack1.GetSignedFieldsForNeighborHMAC); // not waiting for NPACK, wait for ACK1 if (logger.WriteToLog_detail_enabled) { logger.WriteToLog_detail($"waiting for ACK2"); } var ack2UdpData = await Engine.OptionallySendUdpRequestAsync_Retransmit_WaitForResponse("ack2 2346892", routedRequest.ReceivedFromNeighborNullable?.ToString() ?? routedRequest.ReceivedFromEndpoint.ToString(), null, routedRequest.ReceivedFromEndpoint, InviteAck2Packet.GetScanner(logger, req, routedRequest.ReceivedFromNeighborNullable)); if (logger.WriteToLog_detail_enabled) { logger.WriteToLog_detail($"received ACK2"); } var ack2 = InviteAck2Packet.Decode(ack2UdpData); #endregion // send NPACK to ACK2 if (logger.WriteToLog_detail_enabled) { logger.WriteToLog_detail($"sending NPACK to ACK2 to source peer"); } SendNeighborPeerAckResponseToAck2(ack2, routedRequest.ReceivedFromNeighborNullable); // send ACK2 to responder // put ACK2.ReqP2pSeq16, sendertoken32, senderHMAC // wait for NPACK var ack2UdpDataTx = ack2.Encode_SetP2pFields(destinationPeer); if (logger.WriteToLog_detail_enabled) { logger.WriteToLog_detail($"sending ACK2 to responder"); } await destinationPeer.SendUdpRequestAsync_Retransmit_WaitForNPACK("ack2 5344530", ack2UdpDataTx, ack2.ReqP2pSeq16, ack2.GetSignedFieldsForNeighborHMAC); if (logger.WriteToLog_detail_enabled) { logger.WriteToLog_detail($"received NPACK to ACK2 from destination peer"); } // wait for CFM from responder if (logger.WriteToLog_detail_enabled) { logger.WriteToLog_detail($"waiting for CFM from responder"); } var cfmUdpData = await Engine.WaitForUdpResponseAsync(new PendingLowLevelUdpRequest("cfm 12358", destinationPeer.RemoteEndpoint, InviteConfirmationPacket.GetScanner(logger, req, destinationPeer), Engine.DateTimeNowUtc, Engine.Configuration.CfmTimoutS )); if (cfmUdpData == null) { throw new DrpTimeoutException($"inv. proxy CFM response from destination peer {destinationPeer} (timeout={Engine.Configuration.CfmTimoutS}s)"); } var cfm = InviteConfirmationPacket.Decode(cfmUdpData); // todo verify signature, update RDRs and QoS if (logger.WriteToLog_detail_enabled) { logger.WriteToLog_detail($"verified CFM from responder"); } // respond NPACK to CFM to destination peer SendNeighborPeerAckResponseToCfm(cfm, destinationPeer); // send CFM to requester var cfmUdpDataTx = cfm.Encode_SetP2pFields(routedRequest.ReceivedFromNeighborNullable); if (logger.WriteToLog_detail_enabled) { logger.WriteToLog_detail($"sending CFM to requester, waiting for NPACK"); } await Engine.OptionallySendUdpRequestAsync_Retransmit_WaitForNeighborPeerAck("cfm 23468", cfmUdpDataTx, routedRequest.ReceivedFromEndpoint, cfm.ReqP2pSeq16, routedRequest.ReceivedFromNeighborNullable, cfm.GetSignedFieldsForNeighborHMAC); if (logger.WriteToLog_detail_enabled) { logger.WriteToLog_detail($"received NPACK to CFM from source peer"); } } catch (DrpTimeoutException exc) { logger.WriteToLog_lightPain($"could not proxy INVITE: {exc.Message}"); } catch (Exception exc) { logger.WriteToLog_mediumPain($"could not proxy INVITE request: {exc}"); } finally { _pendingInviteRequests.Remove(req.RequesterRegistrationId); } return(false); }
/// <summary> /// main register responder proc for both A-EP and P2P modes /// in P2P mode NeighborToken32 and NeighborHMAC are verified at this time /// </summary> /// <param name="receivedFromInP2pMode"> /// is null in A-EP mode /// </param> /// <returns> /// true to retry the request with another neighbor (if the request needs to be "rerouted") /// </returns> internal async Task <bool> ProxyRegisterRequestAsync(RoutedRequest routedRequest, ConnectionToNeighbor destinationPeer) // engine thread { routedRequest.ReceivedFromNeighborNullable?.AssertIsNotDisposed(); var req = routedRequest.RegisterReq; var logger = routedRequest.Logger; logger.ModuleName = VisionChannelModuleName_reg_proxySide; if (!ValidateReceivedReqTimestamp64(req.ReqTimestamp64)) { logger.WriteToLog_needsAttention($"rejecting REGISTER request {req.RequesterRegistrationId}: invalid REGISTER REQ 457 ReqTimestamp64={MiscProcedures.Int64ticksToDateTime(req.ReqTimestamp64)}"); // await routedRequest.SendErrorResponse(ResponseOrFailureCode.failure_numberOfHopsRemainingReachedZero); return(false); } if (PendingRegisterRequestExists(req.RequesterRegistrationId)) { logger.WriteToLog_higherLevelDetail($"rejecting duplicate request {req}: requesterEndpoint={routedRequest.ReceivedFromEndpoint}"); await routedRequest.SendErrorResponse(ResponseOrFailureCode.failure_routeIsUnavailable); return(false); } if (req.NumberOfHopsRemaining > RegisterRequestPacket.MaxNumberOfHopsRemaining || req.NumberOfRandomHopsRemaining > RegisterRequestPacket.MaxNumberOfHopsRemaining) { logger.WriteToLog_needsAttention($"rejecting {req}: invalid number of hops remaining"); await routedRequest.SendErrorResponse(ResponseOrFailureCode.failure_routeIsUnavailable); return(false); } if (!routedRequest.CheckedRecentUniqueProxiedRequests) { var recentUniqueProxiedRegistrationRequests = req.RandomModeAtThisHop ? RecentUniqueProxiedRegistrationRequests_RandomHop : RecentUniqueProxiedRegistrationRequests_NonRandomHop; if (!recentUniqueProxiedRegistrationRequests.Filter(req.GetUniqueRequestIdFields)) { logger.WriteToLog_higherLevelDetail($"rejecting non-unique request {req}: requesterEndpoint={routedRequest.ReceivedFromEndpoint}"); await routedRequest.SendErrorResponse(ResponseOrFailureCode.failure_routeIsUnavailable); return(false); } routedRequest.CheckedRecentUniqueProxiedRequests = true; } logger.WriteToLog_higherLevelDetail($"proxying {req}: requesterEndpoint={routedRequest.ReceivedFromEndpoint}, NumberOfHopsRemaining={req.NumberOfHopsRemaining}, ReqP2pSeq16={req.ReqP2pSeq16}, destinationPeer={destinationPeer}, sourcePeer={routedRequest.ReceivedFromNeighborNullable}"); if (!ValidateReceivedReqTimestamp64(req.ReqTimestamp64)) { throw new BadSignatureException($"invalid REGISTER REQ ReqTimestamp64={MiscProcedures.Int64ticksToDateTime(req.ReqTimestamp64)} 3507"); } req.NumberOfHopsRemaining--; if (req.NumberOfHopsRemaining == 0) { logger.WriteToLog_needsAttention($"rejecting REGISTER request {req.RequesterRegistrationId}: max hops reached"); await routedRequest.SendErrorResponse(ResponseOrFailureCode.failure_numberOfHopsRemainingReachedZero); return(false); } _pendingRegisterRequests.Add(req.RequesterRegistrationId); try { // send NPACK to source peer if (logger.WriteToLog_detail_enabled) { logger.WriteToLog_detail($"sending NPACK to REQ source peer (delay={routedRequest.ReqReceivedSw_ms}ms)"); } routedRequest.SendNeighborPeerAck_accepted_IfNotAlreadyReplied(); if (req.NumberOfRandomHopsRemaining >= 1) { req.NumberOfRandomHopsRemaining--; } if (logger.WriteToLog_detail_enabled) { logger.WriteToLog_detail($"decremented number of hops in {req}: NumberOfHopsRemaining={req.NumberOfHopsRemaining}, NumberOfRandomHopsRemaining={req.NumberOfRandomHopsRemaining}"); } req.ReqP2pSeq16 = destinationPeer.GetNewRequestP2pSeq16_P2P(); byte[] ack1UdpData; try { var sentRequest = new SentRequest(this, logger, destinationPeer.RemoteEndpoint, destinationPeer, req.Encode_OptionallySignNeighborHMAC(destinationPeer), req.ReqP2pSeq16, RegisterAck1Packet.GetScanner(logger, req, destinationPeer)); // send (proxy) REQ to responder. wait for NPACK, verify NPACK.senderHMAC, retransmit REQ // wait for ACK1 from destination peer // verify NeighborHMAC // var ack1R = await sentRequest.SendRequestAsync_NoExc("ack1 34601"); ////////////////////////////////// new method ack1UdpData = await sentRequest.SendRequestAsync("ack1 34601"); // if (logger.WriteToLog_detail2_enabled) logger.WriteToLog_detail("got ACK1 result"); if (routedRequest.ReceivedFromNeighborNullable?.IsDisposed == true) { logger.WriteToLog_needsAttention($"sourcePeer={routedRequest.ReceivedFromNeighborNullable} is disposed during proxying 52460"); return(false); } if (destinationPeer?.IsDisposed == true) { logger.WriteToLog_needsAttention($"destinationPeer={destinationPeer} is disposed during proxying 52460"); return(false); } //if (ack1R.ResponseCode != ResponseOrFailureCode.accepted) //{ // logger.WriteToLog_higherLevelDetail($"got response={ack1R.ResponseCode} from destination {destinationPeer}"); // if (ack1R.ResponseCode == ResponseOrFailureCode.failure_routeIsUnavailable) // req.NumberOfHopsRemaining++; // roll back previous decrement for a new trial // return true; // will retry //} //ack1UdpData = ack1R.UdpData; } catch (RequestRejectedException reqExc) { logger.WriteToLog_higherLevelDetail($"got exception: response={reqExc.ResponseCode} from destination {destinationPeer}"); if (reqExc.ResponseCode == ResponseOrFailureCode.failure_numberOfHopsRemainingReachedZero) { await routedRequest.SendErrorResponse(ResponseOrFailureCode.failure_numberOfHopsRemainingReachedZero); return(false); } if (routedRequest.ReceivedFromNeighborNullable?.IsDisposed == true) { logger.WriteToLog_needsAttention($"sourcePeer={routedRequest.ReceivedFromNeighborNullable} is disposed during proxying 35346232"); return(false); } if (reqExc.ResponseCode == ResponseOrFailureCode.failure_routeIsUnavailable) { req.NumberOfHopsRemaining++; // roll back previous decrement for a new trial } return(true); // will retry } catch (DrpTimeoutException exc) { logger.WriteToLog_higherLevelDetail($"got timeout error when requesting {destinationPeer}: {exc.Message}"); req.NumberOfHopsRemaining++; // roll back previous decrement for a new trial return(true); // will retry } catch (Exception reqExc) { logger.WriteToLog_mediumPain($"could not proxy REGISTER request: {reqExc}"); if (routedRequest.ReceivedFromNeighborNullable?.IsDisposed == true) { logger.WriteToLog_needsAttention($"sourcePeer={routedRequest.ReceivedFromNeighborNullable} is disposed during proxying 76897805"); return(false); } return(true); // will retry } var tr1 = CreateTracker("ack1 34601 2"); var ack1 = RegisterAck1Packet.DecodeAndOptionallyVerify(logger, ack1UdpData, req, null); if (logger.WriteToLog_detail_enabled) { logger.WriteToLog_detail($"verified ACK1 from destination"); } // respond with NPACK SendNeighborPeerAckResponseToRegisterAck1(ack1, destinationPeer); // send ACK1 to source peer // wait for ACK / NPACK if (routedRequest.ReceivedFromNeighborNullable != null) { // P2P mode routedRequest.ReceivedFromNeighborNullable.AssertIsNotDisposed(); ack1.ReqP2pSeq16 = routedRequest.ReceivedFromNeighborNullable.GetNewRequestP2pSeq16_P2P(); } else { // A-EP mode ack1.RequesterEndpoint = routedRequest.ReceivedFromEndpoint; } var ack1UdpDataTx = ack1.Encode_OpionallySignNeighborHMAC(routedRequest.ReceivedFromNeighborNullable); var sourcePeerVisibleDescription = routedRequest.ReceivedFromNeighborNullable?.ToString() ?? routedRequest.ReceivedFromEndpoint.ToString(); var ack2Scanner = RegisterAck2Packet.GetScanner(logger, routedRequest.ReceivedFromNeighborNullable, req); tr1.Dispose(); byte[] ack2UdpData; if (routedRequest.ReceivedFromNeighborNullable == null) { // A-EP mode: wait for ACK2, retransmitting ACK1 if (logger.WriteToLog_detail_enabled) { logger.WriteToLog_detail($"sending ACK1, waiting for ACK2"); } ack2UdpData = await OptionallySendUdpRequestAsync_Retransmit_WaitForResponse("ack2 12368", sourcePeerVisibleDescription, ack1UdpDataTx, routedRequest.ReceivedFromEndpoint, ack2Scanner); } else { // P2P mode: retransmit ACK1 until NPACK (via P2P); at same time wait for ACK2 if (logger.WriteToLog_detail_enabled) { logger.WriteToLog_detail($"sending ACK1, awaiting for NPACK"); } _ = OptionallySendUdpRequestAsync_Retransmit_WaitForNeighborPeerAck("ack1 3686", ack1UdpDataTx, routedRequest.ReceivedFromEndpoint, ack1.ReqP2pSeq16, routedRequest.ReceivedFromNeighborNullable, ack1.GetSignedFieldsForNeighborHMAC); // not waiting for NPACK, wait for ACK2 if (logger.WriteToLog_detail_enabled) { logger.WriteToLog_detail($"waiting for ACK2"); } ack2UdpData = await OptionallySendUdpRequestAsync_Retransmit_WaitForResponse("ack2 345209", sourcePeerVisibleDescription, null, routedRequest.ReceivedFromEndpoint, ack2Scanner); } if (logger.WriteToLog_detail_enabled) { logger.WriteToLog_detail($"received ACK2"); } if (routedRequest.ReceivedFromNeighborNullable?.IsDisposed == true) { logger.WriteToLog_needsAttention($"sourcePeer={routedRequest.ReceivedFromNeighborNullable} is disposed during proxying 2345135"); return(false); } if (destinationPeer?.IsDisposed == true) { logger.WriteToLog_needsAttention($"destinationPeer={destinationPeer} is disposed during proxying 2345135"); return(false); } var ack2 = RegisterAck2Packet.Decode_OptionallyVerify_InitializeP2pStreamAtResponder(logger, ack2UdpData, null, null, null); // send NPACK to source peer if (logger.WriteToLog_detail_enabled) { logger.WriteToLog_detail($"sending NPACK to ACK2 to source peer"); } SendNeighborPeerAckResponseToRegisterAck2(ack2, routedRequest.ReceivedFromEndpoint, routedRequest.ReceivedFromNeighborNullable); // send ACK2 to destination peer // put ACK2.ReqP2pSeq16, sendertoken32, senderHMAC // wait for NPACK if (destinationPeer.IsDisposed == true) { logger.WriteToLog_needsAttention($"destinationPeer={destinationPeer} is disposed during proxying 345784567"); return(false); } if (routedRequest.ReceivedFromNeighborNullable?.IsDisposed == true) { logger.WriteToLog_needsAttention($"sourcePeer={routedRequest.ReceivedFromNeighborNullable} is disposed during proxying 345784567"); return(false); } ack2.ReqP2pSeq16 = destinationPeer.GetNewRequestP2pSeq16_P2P(); await destinationPeer.SendUdpRequestAsync_Retransmit_WaitForNPACK("ack2 41937", ack2.Encode_OptionallySignNeighborHMAC(destinationPeer), ack2.ReqP2pSeq16, ack2.GetSignedFieldsForNeighborHMAC); if (routedRequest.ReceivedFromNeighborNullable?.IsDisposed == true) { logger.WriteToLog_needsAttention($"sourcePeer={routedRequest.ReceivedFromNeighborNullable} is disposed during proxying 234646"); return(false); } if (destinationPeer?.IsDisposed == true) { logger.WriteToLog_needsAttention($"destinationPeer={destinationPeer} is disposed during proxying 234646"); return(false); } // wait for CFM from source peer var cfmUdpData = await OptionallySendUdpRequestAsync_Retransmit_WaitForResponse("cfm 234789", sourcePeerVisibleDescription, null, routedRequest.ReceivedFromEndpoint, RegisterConfirmationPacket.GetScanner(logger, routedRequest.ReceivedFromNeighborNullable, req) ); var cfm = RegisterConfirmationPacket.DecodeAndOptionallyVerify(cfmUdpData, null, null); if (routedRequest.ReceivedFromNeighborNullable?.IsDisposed == true) { logger.WriteToLog_needsAttention($"sourcePeer={routedRequest.ReceivedFromNeighborNullable} is disposed during proxying 3452326"); return(false); } if (destinationPeer?.IsDisposed == true) { logger.WriteToLog_needsAttention($"destinationPeer={destinationPeer} is disposed during proxying 3452326"); return(false); } // TODO verify signatures and update QoS // send NPACK to source peer SendNeighborPeerAckResponseToRegisterCfm(cfm, routedRequest.ReceivedFromEndpoint, routedRequest.ReceivedFromNeighborNullable); // send CFM to responder // wait for NPACK from destination peer, retransmit if (destinationPeer.IsDisposed == true) { logger.WriteToLog_needsAttention($"destinationPeer={destinationPeer} is disposed during proxying 123678"); return(false); } if (routedRequest.ReceivedFromNeighborNullable?.IsDisposed == true) { logger.WriteToLog_needsAttention($"sourcePeer={routedRequest.ReceivedFromNeighborNullable} is disposed during proxying 123678"); return(false); } cfm.ReqP2pSeq16 = destinationPeer.GetNewRequestP2pSeq16_P2P(); await destinationPeer.SendUdpRequestAsync_Retransmit_WaitForNPACK("cfm 1357", cfm.Encode_OptionallySignNeighborHMAC(destinationPeer), cfm.ReqP2pSeq16, cfm.GetSignedFieldsForNeighborHMAC); logger.WriteToLog_higherLevelDetail($"proxying {req} is successfully complete"); } catch (DrpTimeoutException exc) { logger.WriteToLog_lightPain($"could not proxy REGISTER: request timeout: {exc.Message}"); } catch (Exception exc) { logger.WriteToLog_mediumPain($"could not proxy REGISTER request: {exc}"); } finally { _pendingRegisterRequests.Remove(req.RequesterRegistrationId); } return(false); }
/// <summary> /// main routing procedure for register REQ requests /// </summary> /// <param name="receivedAtLocalDrpPeerNullable"> /// is set when routing REQ packets that are received via P2P connection from neighbor to the LocalDrpPeer /// is null in A-EP mode /// </param> internal bool RouteRegistrationRequest(LocalDrpPeer receivedAtLocalDrpPeerNullable, RoutedRequest routedRequest, out ConnectionToNeighbor proxyToDestinationPeer, out LocalDrpPeer acceptAt) { proxyToDestinationPeer = null; acceptAt = null; var req = routedRequest.RegisterReq; var logger = routedRequest.Logger; if (req.NumberOfHopsRemaining <= 1) { var possibleAcceptAt = receivedAtLocalDrpPeerNullable ?? routedRequest.ReceivedFromNeighborNullable?.LocalDrpPeer; if (possibleAcceptAt == null) { possibleAcceptAt = LocalPeers.Values.Where(x => x.Configuration.LocalPeerRegistrationId.Equals(req.RequesterRegistrationId) == false).FirstOrDefault(); } if (possibleAcceptAt != null) { if (!possibleAcceptAt.ConnectedNeighbors.Any(x => x.RemoteRegistrationId.Equals(req.RequesterRegistrationId))) { logger.WriteToLog_routing_higherLevelDetail($"accepting registration request {req.RequesterRegistrationId} at local DRP peer {acceptAt}: low number of hops remaining"); acceptAt = possibleAcceptAt; return(true); } else { logger.WriteToLog_routing_higherLevelDetail($"not accepting registration at local DRP peer" + $" {acceptAt} when low number of hops remaining: already connected"); } } } if (req.RandomModeAtThisHop) { // random mode var itemsForRouting = new List <object>(); if (routedRequest.ReceivedFromNeighborNullable == null) { foreach (var localDrpPeer in LocalPeers.Values) { itemsForRouting.AddRange(localDrpPeer.GetConnectedNeighborsForRouting(routedRequest)); if (!localDrpPeer.Configuration.LocalPeerRegistrationId.Equals(req.RequesterRegistrationId)) { itemsForRouting.Add(localDrpPeer); } } } else { itemsForRouting.AddRange(routedRequest.ReceivedFromNeighborNullable.LocalDrpPeer.GetConnectedNeighborsForRouting(routedRequest)); if (!routedRequest.ReceivedFromNeighborNullable.LocalDrpPeer.Configuration.LocalPeerRegistrationId.Equals(req.RequesterRegistrationId)) { itemsForRouting.Add(routedRequest.ReceivedFromNeighborNullable.LocalDrpPeer); } } if (itemsForRouting.Count != 0) { var itemForRouting = itemsForRouting[_insecureRandom.Next(itemsForRouting.Count - 1)]; logger.WriteToLog_routing_higherLevelDetail($"routing registration in random mode to one of {itemsForRouting.Count} destinations: {proxyToDestinationPeer}"); if (itemForRouting is ConnectionToNeighbor) { proxyToDestinationPeer = (ConnectionToNeighbor)itemForRouting; } else { acceptAt = (LocalDrpPeer)itemForRouting; } } else { logger.WriteToLog_routing_needsAttention($"can not route registration in random mode: no destinations available including local peers"); } } else { double?maxP2pConnectionValue = null; if (receivedAtLocalDrpPeerNullable != null) { RouteRegistrationRequest_LocalDrpPeerIteration(receivedAtLocalDrpPeerNullable, routedRequest, ref maxP2pConnectionValue, ref proxyToDestinationPeer, ref acceptAt); } else { foreach (var localDrpPeer in LocalPeers.Values) { RouteRegistrationRequest_LocalDrpPeerIteration(localDrpPeer, routedRequest, ref maxP2pConnectionValue, ref proxyToDestinationPeer, ref acceptAt); } } } if (proxyToDestinationPeer != null) { logger.WriteToLog_routing_higherLevelDetail($"proxying registration to {proxyToDestinationPeer}"); return(true); } else if (acceptAt != null) { logger.WriteToLog_routing_higherLevelDetail($"accepting registration at local DRP peer {acceptAt}"); return(true); } else { logger.WriteToLog_routing_higherLevelDetail($"no route found for REGISTER request to {req.RequesterRegistrationId}"); return(false); } }
void RouteRegistrationRequest_LocalDrpPeerIteration(LocalDrpPeer localDrpPeer, RoutedRequest routedRequest, ref double?maxP2pConnectionValue, ref ConnectionToNeighbor proxyToDestinationPeer, ref LocalDrpPeer acceptAt) { var req = routedRequest.RegisterReq; var connectedNeighborsForRouting = localDrpPeer.GetConnectedNeighborsForRouting(routedRequest).ToList(); var logger = routedRequest.Logger; var distancesToNeighbors = new List <RegistrationIdDistance>(); var requestedDirectionMulResults = new List <double>(); int connectedNeighborsCountThatMatchReqFilters = 0; foreach (var neighbor in connectedNeighborsForRouting) { if (req.MinimalDistanceToNeighbor != 0) { var distanceToNeighbor = req.RequesterRegistrationId.GetDistanceTo(_cryptoLibrary, neighbor.RemoteRegistrationId, NumberOfDimensions); distancesToNeighbors.Add(distanceToNeighbor); if (logger.WriteToLog_detail_enabled) { logger.WriteToLog_detail($"distanceToNeighbor={distanceToNeighbor} from REGISTER REQ {req.RequesterRegistrationId} to {neighbor} (req.min={req.MinimalDistanceToNeighbor})"); } if (distanceToNeighbor.IsLessThan(req.MinimalDistanceToNeighbor)) { // skip: this is too close than requested if (logger.WriteToLog_detail_enabled) { logger.WriteToLog_detail($"skipping connection to {neighbor}: distance={distanceToNeighbor} is less than requested={req.MinimalDistanceToNeighbor}"); } continue; } } if (req.DirectionVectorNullable != null) { var requestedDirectionVector = req.DirectionVectorNullableD; var vectorFromThisToNeighbor = RegistrationId.GetDifferenceVector(req.RequesterRegistrationId, neighbor.RemoteRegistrationId, CryptoLibrary, Configuration.SandboxModeOnly_NumberOfDimensions); double mulResult = 0; for (int i = 0; i < requestedDirectionVector.Length; i++) { mulResult += requestedDirectionVector[i] * requestedDirectionVector[i]; } requestedDirectionMulResults.Add(mulResult); if (mulResult < 0) { if (logger.WriteToLog_detail_enabled) { logger.WriteToLog_detail($"skipping connection to {neighbor}: direction from requester to neighbor does not match requested vector"); } continue; } } connectedNeighborsCountThatMatchReqFilters++; var p2pConnectionValue_withNeighbor = P2pConnectionValueCalculator.GetMutualP2pConnectionValue(CryptoLibrary, req.RequesterRegistrationId, req.RequesterNeighborsBusySectorIds, neighbor.RemoteRegistrationId, neighbor.RemoteNeighborsBusySectorIds ?? 0, NumberOfDimensions, false, false, false); if (logger.WriteToLog_deepDetail_enabled) { logger.WriteToLog_deepDetail($"p2pConnectionValue_withNeighbor={p2pConnectionValue_withNeighbor} from REGISTER REQ {req.RequesterRegistrationId} to {neighbor}"); } if (maxP2pConnectionValue == null || maxP2pConnectionValue < p2pConnectionValue_withNeighbor) { maxP2pConnectionValue = p2pConnectionValue_withNeighbor; proxyToDestinationPeer = neighbor; acceptAt = null; } } if (connectedNeighborsCountThatMatchReqFilters == 0 && connectedNeighborsForRouting.Count != 0) { if (req.MinimalDistanceToNeighbor != 0) { // special case: we are inside the "minDistance" hypersphere: move away from the requester, proxy to most distant neighbor if (logger.WriteToLog_detail_enabled) { logger.WriteToLog_detail($"special case: move away from the requester, proxy to most distant neighbor"); } RegistrationIdDistance maxDistance = null; for (int i = 0; i < connectedNeighborsForRouting.Count; i++) { var neighbor = connectedNeighborsForRouting[i]; var distanceToNeighbor = distancesToNeighbors[i]; if (logger.WriteToLog_detail_enabled) { logger.WriteToLog_detail($"distanceToConnectedPeer={distanceToNeighbor} from REGISTER REQ {req.RequesterRegistrationId} to {neighbor} (req.min={req.MinimalDistanceToNeighbor})"); } if (maxDistance == null || distanceToNeighbor.IsGreaterThan(maxDistance)) { maxDistance = distanceToNeighbor; proxyToDestinationPeer = neighbor; acceptAt = null; } } } if (req.DirectionVectorNullable != null) { if (logger.WriteToLog_detail_enabled) { logger.WriteToLog_detail($"special case: move towards the specified direction"); } double?maxMulResult = null; for (int i = 0; i < connectedNeighborsForRouting.Count; i++) { var neighbor = connectedNeighborsForRouting[i]; var mulResult = requestedDirectionMulResults[i]; if (logger.WriteToLog_detail_enabled) { logger.WriteToLog_detail($"mulResult={mulResult} for {neighbor}"); } if (maxMulResult == null || mulResult > maxMulResult) { maxMulResult = mulResult; proxyToDestinationPeer = neighbor; acceptAt = null; } } } } // dont connect to local peer if already connected if (localDrpPeer.Configuration.LocalPeerRegistrationId.Equals(req.RequesterRegistrationId) == true) { if (logger.WriteToLog_detail_enabled) { logger.WriteToLog_detail($"not accepting request at local peer: it has same regID {req.RequesterRegistrationId}"); } } else if (localDrpPeer.ConnectedNeighbors.Any(x => x.RemoteRegistrationId.Equals(req.RequesterRegistrationId)) == true) { if (logger.WriteToLog_detail_enabled) { logger.WriteToLog_detail($"not accepting request at local peer: already connected to {req.RequesterRegistrationId}"); } } else if (localDrpPeer.ConnectedNeighbors.Count > localDrpPeer.Configuration.AbsoluteMaxNumberOfNeighbors) { if (logger.WriteToLog_detail_enabled) { logger.WriteToLog_detail($"not accepting request at local peer: already too many neighbors ({localDrpPeer.ConnectedNeighbors.Count})"); } } else { var p2pConnectionValue_withLocalPeer = P2pConnectionValueCalculator.GetMutualP2pConnectionValue(CryptoLibrary, req.RequesterRegistrationId, req.RequesterNeighborsBusySectorIds, localDrpPeer.Configuration.LocalPeerRegistrationId, localDrpPeer.ConnectedNeighborsBusySectorIds, NumberOfDimensions, false, false, false); if (logger.WriteToLog_detail_enabled) { logger.WriteToLog_detail($"p2pConnectionValue_withLocalPeer={p2pConnectionValue_withLocalPeer} from REGISTER REQ {req.RequesterRegistrationId} to {localDrpPeer.Configuration.LocalPeerRegistrationId}"); } if (maxP2pConnectionValue == null || p2pConnectionValue_withLocalPeer > maxP2pConnectionValue) { maxP2pConnectionValue = p2pConnectionValue_withLocalPeer; proxyToDestinationPeer = null; acceptAt = localDrpPeer; } } if (logger.WriteToLog_detail_enabled) { logger.WriteToLog_detail($"<< RouteRegistrationRequest_LocalDrpPeerIteration() proxyTo={proxyToDestinationPeer}, acceptAt={acceptAt}"); } }
/// <summary> /// sends INVITE, autenticates users, returns Session to be used to create direct cannel /// </summary> /// <param name="responderUserId"> /// comes from local contact book /// </param> /// <param name="responderRegId"> /// comes from local contact book /// </param> /// <param name="loggerCb">may be invoked more than one time (in case of retrying)</param> public async Task <InviteSession> SendInviteAsync(UserCertificate requesterUserCertificate, RegistrationId responderRegistrationId, UserId responderUserId, SessionType sessionType, Action <Logger> loggerCb = null) { InviteSession session = null; try { var sw = Stopwatch.StartNew(); RoutedRequest routedRequest = null; int trialsCount = 0; Exception latestTriedNeighborException = null; _retry: trialsCount++; session = new InviteSession(this); var req = new InviteRequestPacket { NumberOfHopsRemaining = InviteRequestPacket.MaxNumberOfHopsRemaining, RequesterEcdhePublicKey = new EcdhPublicKey(session.LocalInviteAckEcdhePublicKey), RequesterRegistrationId = this.Configuration.LocalPeerRegistrationId, ResponderRegistrationId = responderRegistrationId, ReqTimestamp32S = Engine.Timestamp32S, }; var logger = new Logger(Engine, this, req, DrpPeerEngine.VisionChannelModuleName_inv_requesterSide); if (!Engine.RecentUniqueInviteRequests.Filter(req.GetUniqueRequestIdFields)) { if (trialsCount > 50) { throw new NonUniquePacketFieldsException($"could not find unique fields to send INVITE request"); } if (logger.WriteToLog_detail_enabled) { logger.WriteToLog_detail($"waiting a second to generate ne wunique INVITE request"); } await Engine.EngineThreadQueue.WaitAsync(TimeSpan.FromSeconds(1), "inv_wait_1236"); goto _retry; } session.Logger = logger; loggerCb?.Invoke(logger); Engine.RecentUniquePublicEcdhKeys.AssertIsUnique(req.RequesterEcdhePublicKey.Ecdh25519PublicKey, "req.RequesterEcdhePublicKey"); if (logger.WriteToLog_detail_enabled) { logger.WriteToLog_detail($"generated unique ECDH key {req.RequesterEcdhePublicKey}"); } req.RequesterRegistrationSignature = RegistrationSignature.Sign(Engine.CryptoLibrary, req.GetSharedSignedFields, this.Configuration.LocalPeerRegistrationPrivateKey); this.TestDirection(logger, req.ResponderRegistrationId); routedRequest = new RoutedRequest(logger, null, null, null, req, null, routedRequest); // find best connected peer to send the request var destinationPeer = Engine.RouteInviteRequest(this, routedRequest); if (destinationPeer == null) { if (latestTriedNeighborException == null) { throw new NoNeighborsToSendInviteException(); } else { throw latestTriedNeighborException; } } InviteAck1Packet ack1; try { var reqUdpData = req.Encode_SetP2pFields(destinationPeer); if (logger.WriteToLog_detail_enabled) { logger.WriteToLog_detail($"sending {req} (ReqTimestamp32S={MiscProcedures.Uint32secondsToDateTime(req.ReqTimestamp32S)}), waiting for NPACK"); } var sentRequest = new SentRequest(Engine, logger, destinationPeer.RemoteEndpoint, destinationPeer, reqUdpData, req.ReqP2pSeq16, InviteAck1Packet.GetScanner(logger, req, destinationPeer)); var ack1UdpData = await sentRequest.SendRequestAsync("ack1 4146"); #region process ACK1 // NeighborHMAC and NeighborToken32 are already verified by scanner ack1 = InviteAck1Packet.Decode(ack1UdpData); Engine.RecentUniquePublicEcdhKeys.AssertIsUnique(ack1.ResponderEcdhePublicKey.Ecdh25519PublicKey, $"ack1.ResponderEcdhePublicKey"); if (!ack1.ResponderRegistrationSignature.Verify(Engine.CryptoLibrary, w => { req.GetSharedSignedFields(w); ack1.GetSharedSignedFields(w, true); }, responderRegistrationId)) { throw new BadSignatureException("invalid REGISTER ACK1 ResponderRegistrationSignature 2349"); } if (logger.WriteToLog_detail_enabled) { logger.WriteToLog_detail($"verified ACK1"); } session.DeriveSharedInviteAckDhSecret(Engine.CryptoLibrary, ack1.ResponderEcdhePublicKey.Ecdh25519PublicKey); // send NPACK to ACK1 SendNeighborPeerAckResponseToAck1(ack1, destinationPeer); #endregion } catch (RequestFailedException exc2) { latestTriedNeighborException = exc2; if (trialsCount > 50) { throw; } logger.WriteToLog_higherLevelDetail($"trying again on error {exc2.Message}... alreadyTriedProxyingToDestinationPeers.Count={routedRequest.TriedNeighbors.Count}"); routedRequest.TriedNeighbors.Add(destinationPeer); goto _retry; } // decode and verify SD session.RemoteSessionDescription = InviteSessionDescription.Decrypt_Verify(Engine.CryptoLibrary, ack1.ToResponderSessionDescriptionEncrypted, req, ack1, false, session, responderUserId, Engine.DateTimeNowUtc); // sign and encode local SD session.LocalSessionDescription = new InviteSessionDescription { DirectChannelEndPoint = destinationPeer.LocalEndpoint, SessionType = sessionType, DirectChannelToken32 = session.LocalDirectChannelToken32 }; session.LocalSessionDescription.UserCertificate = requesterUserCertificate; session.LocalSessionDescription.UserCertificateSignature = UserCertificateSignature.Sign(Engine.CryptoLibrary, w => { req.GetSharedSignedFields(w); ack1.GetSharedSignedFields(w, true); session.LocalSessionDescription.WriteSignedFields(w); }, requesterUserCertificate ); #region send ack2 var ack2 = new InviteAck2Packet { RequesterRegistrationId = req.RequesterRegistrationId, ResponderRegistrationId = req.ResponderRegistrationId, ReqTimestamp32S = req.ReqTimestamp32S, ToRequesterSessionDescriptionEncrypted = session.LocalSessionDescription.Encrypt(Engine.CryptoLibrary, req, ack1, session, true) }; ack2.RequesterRegistrationSignature = RegistrationSignature.Sign(Engine.CryptoLibrary, w => { req.GetSharedSignedFields(w); ack1.GetSharedSignedFields(w, true); ack2.GetSharedSignedFields(w); }, this.Configuration.LocalPeerRegistrationPrivateKey); var ack2UdpData = ack2.Encode_SetP2pFields(destinationPeer); if (logger.WriteToLog_detail_enabled) { logger.WriteToLog_detail($"sending ACK2, waiting for NPACK"); } await destinationPeer.SendUdpRequestAsync_Retransmit_WaitForNPACK("ack2 234575672", ack2UdpData, ack2.ReqP2pSeq16, ack2.GetSignedFieldsForNeighborHMAC); if (logger.WriteToLog_detail_enabled) { logger.WriteToLog_detail($"received NPACK"); } #endregion #region wait for CFM if (logger.WriteToLog_detail_enabled) { logger.WriteToLog_detail($"waiting for CFM"); } var cfmUdpData = await Engine.WaitForUdpResponseAsync(new PendingLowLevelUdpRequest("cfm 1235695", destinationPeer.RemoteEndpoint, InviteConfirmationPacket.GetScanner(logger, req, destinationPeer), Engine.DateTimeNowUtc, Engine.Configuration.CfmTimoutS )); if (cfmUdpData == null) { throw new DrpTimeoutException($"did not get CFM at invite requester from destination peer {destinationPeer} (timeout={Engine.Configuration.CfmTimoutS}s)"); } // NeighborHMAC and NeighborToken32 are already verified by scanner var cfm = InviteConfirmationPacket.Decode(cfmUdpData); if (!cfm.ResponderRegistrationSignature.Verify(Engine.CryptoLibrary, w => { req.GetSharedSignedFields(w); ack1.GetSharedSignedFields(w, true); ack2.GetSharedSignedFields(w); }, responderRegistrationId)) { throw new BadSignatureException("invalid REGISTER CFM ResponderRegistrationSignature 6398"); } if (logger.WriteToLog_detail_enabled) { logger.WriteToLog_detail($"verified CFM"); } // send NPACK to CFM SendNeighborPeerAckResponseToCfm(cfm, destinationPeer); #endregion session.DeriveSharedPingPongHmacKey(req, ack1, ack2, cfm); return(session); } catch { session?.Dispose(); throw; } }