/// <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);
            }
        }
Esempio n. 2
0
        /// <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);
        }