public override string ToString()
        {
            var r = $"[responderEP={ResponderEndpoint}";

            if (RequestPacketDataNullable != null)
            {
                r += $", req={(PacketTypes)RequestPacketDataNullable[0]} (hash={MiscProcedures.GetArrayHashCodeString(RequestPacketDataNullable)})";
            }
            if (ResponseScanner != null && ResponseScanner.ResponseFirstBytes != null)
            {
                r += $", resp={(PacketTypes)ResponseScanner.ResponseFirstBytes[0]}";
            }
            r += $", timeout={_expirationTimeoutS}s, compl.Action={CompletionActionVisibleId}]";
            return(r);
        }
Ejemplo n.º 2
0
        /// <summary>
        /// creates a scanner that finds ACK1 that matches to REQ
        /// </summary>
        /// <param name="connectionToNeighborNullable">
        /// peer that responds to REQ with ACK1
        /// if not null - the scanner will verify ACK1.NeighborHMAC
        /// </param>
        public static LowLevelUdpResponseScanner GetScanner(Logger logger, RequestP2pSequenceNumber16 reqP2pSeq16, ConnectionToNeighbor connectionToNeighborNullable = null)
        {
            if (logger.WriteToLog_detail_enabled)
            {
                logger.WriteToLog_detail($">> FailurePacket.GetScanner() reqP2pSeq16={reqP2pSeq16}");
            }
            BinaryProcedures.CreateBinaryWriter(out var ms, out var w);
            w.Write((byte)PacketTypes.Failure);
            w.Write((byte)0);
            if (connectionToNeighborNullable != null)
            {
                connectionToNeighborNullable.LocalNeighborToken32.Encode(w);
            }

            reqP2pSeq16.Encode(w);

            var r = new LowLevelUdpResponseScanner
            {
                ResponseFirstBytes   = ms.ToArray(),
                IgnoredByteAtOffset1 = 1 // ignore flags
            };

            if (connectionToNeighborNullable != null)
            {
                r.OptionalFilter = (responseData) =>
                {
                    if (logger.WriteToLog_detail_enabled)
                    {
                        logger.WriteToLog_detail($"filtering FAILURE @scanner: hash={MiscProcedures.GetArrayHashCodeString(responseData)}");
                    }
                    if (connectionToNeighborNullable.IsDisposed)
                    {
                        logger.WriteToLog_needsAttention("ignoring FAILURE: connection is disposed");
                        return(false);
                    }
                    var failure = DecodeAndOptionallyVerify(responseData, reqP2pSeq16);
                    if (failure.NeighborHMAC.Equals(connectionToNeighborNullable.GetNeighborHMAC(failure.GetSignedFieldsForNeighborHMAC)) == false)
                    {
                        logger.WriteToLog_attacks("ignoring FAILURE: received HMAC is invalid");
                        return(false);
                    }
                    return(true);
                };
            }
            return(r);
        }
        /// <summary>
        /// is executed by engine thread
        /// </summary>
        /// <returns>
        /// true if the response is linked to request, and the packet is processed
        /// </returns>
        bool PendingUdpRequests_ProcessPacket(IPEndPoint responderEndpoint, byte[] udpData, DateTime receivedAtUtc)
        {
            using var tracker = CreateTracker("PendingUdpRequests_ProcessPacket");
            tracker.Details   = $"count={_pendingLowLevelUdpRequests.Count}";

            // todo optimize this by storing pending requests indexed
            for (var item = _pendingLowLevelUdpRequests.First; item != null; item = item.Next)
            {
                var request = item.Value;

                if (WriteToLog_udp_deepDetail_enabled)
                {
                    WriteToLog_udp_deepDetail($"matching to pending request... responderEndpoint={responderEndpoint}, " +
                                              $"udpData={MiscProcedures.ByteArrayToString(udpData)} ({(PacketTypes)udpData[0]}) " +
                                              $"hash={MiscProcedures.GetArrayHashCodeString(udpData)}, " +
                                              $"request={request}"
                                              // + $" ResponseScanner.ResponseFirstBytes={MiscProcedures.ByteArrayToString(request.ResponseScanner.ResponseFirstBytes)}"
                                              );
                }

                try
                {
                    if (request.ResponderEndpoint.Equals(responderEndpoint) && request.ResponseScanner.Scan(this, udpData))
                    {
                        tracker.Details += $"; completed {request.CompletionActionVisibleId}";
                        _pendingLowLevelUdpRequests.Remove(item);
                        request.ResponseReceivedAtUtc = receivedAtUtc;
                        tracker.Dispose();
                        using (var tr2 = CreateTracker(request.CompletionActionVisibleId))
                            request.TaskCompletionSource.SetResult(udpData);
                        return(true);
                    }
                }
                catch (Exception exc)
                {
                    HandleExceptionInEngineThread(exc);
                }
            }

            //  WriteToLog_udp_detail($"match to pending request was not found for packet from {responderEndpoint}, udpData={MiscProcedures.ByteArrayToString(udpData)}");

            return(false);
        }
        bool RespondersToRetransmittedRequests_ProcessPacket(IPEndPoint requesterEndpoint, byte[] udpData)
        {
            var key = new RequestKey(udpData, requesterEndpoint);

            if (_respondersToRetransmittedRequests.TryGetValue(key, out var responder))
            {
                if (WriteToLog_udp_deepDetail_enabled)
                {
                    WriteToLog_udp_deepDetail($"responding {(PacketTypes)responder.ResponseUdpPayloadData[0]} to retransmitted request {(PacketTypes)udpData[0]} (hash={MiscProcedures.GetArrayHashCodeString(udpData)})");
                }
                SendPacket(responder.ResponseUdpPayloadData, requesterEndpoint);
                return(true);
            }
            else
            {
                return(false);
            }
        }
 internal void SendPacket(byte[] udpPayload, IPEndPoint remoteEndpoint)
 {
     if (udpPayload.Length > 548)
     {
         throw new ArgumentException("Transmitted UDP packet size is too big to bypass internet safely without fragmentation");
     }
     if (WriteToLog_udp_deepDetail_enabled)
     {
         WriteToLog_udp_deepDetail($"sending packet {(PacketTypes)udpPayload[0]} to {remoteEndpoint} ({udpPayload.Length} bytes, hash={MiscProcedures.GetArrayHashCodeString(udpPayload)})");
     }
     _socket.Send(udpPayload, udpPayload.Length, remoteEndpoint);
 }
Ejemplo n.º 6
0
        public async Task <byte[]> SendRequestAsync(string completionActionVisibleId)
        {
            // wait for NPACK (-accepted or -failure)
            _logger.WriteToLog_detail($"[{completionActionVisibleId}] >> SendRequestAsync() _requestUdpData={MiscProcedures.GetArrayHashCodeString(_requestUdpData)}");
            await _engine.OptionallySendUdpRequestAsync_Retransmit_WaitForNeighborPeerAck(completionActionVisibleId + "_first_npack", _requestUdpData, _destinationEndpoint, _sentReqP2pSeq16);

            // wait for ACK1 OR FAILURE
            await Task.WhenAny(
                WaitForAck1Async(completionActionVisibleId + "_ack1"),
                WaitForFailureAsync(completionActionVisibleId + "_failure")
                );

            if (_pendingAck1Request != null)
            {
                _engine.CancelPendingRequest(_pendingAck1Request);
                _pendingAck1Request = null;
            }
            if (_pendingFailureRequest != null)
            {
                _engine.CancelPendingRequest(_pendingFailureRequest);
                _pendingFailureRequest = null;
            }


            if (_waitForAck1Completed)
            {
                if (Ack1UdpData == null)
                {
                    ThrowTimeoutException(completionActionVisibleId);
                }
                _logger.WriteToLog_detail($"received ACK1");
                return(Ack1UdpData);
            }
            else if (_waitForFailureCompleted)
            {
                if (_failureUdpData == null)
                {
                    ThrowTimeoutException(completionActionVisibleId);
                }
                _logger.WriteToLog_detail($"received FAILURE");
                var failure = FailurePacket.DecodeAndOptionallyVerify(_failureUdpData, _sentReqP2pSeq16);

                if (_failureUdpData != null)
                {
                    // send NPACK to FAILURE
                    var npAckToFailure = new NeighborPeerAckPacket
                    {
                        ReqP2pSeq16  = _sentReqP2pSeq16,
                        ResponseCode = ResponseOrFailureCode.accepted
                    };
                    if (_destinationNeighborNullable != null)
                    {
                        npAckToFailure.NeighborToken32 = _destinationNeighborNullable.RemoteNeighborToken32;
                        npAckToFailure.NeighborHMAC    = _destinationNeighborNullable.GetNeighborHMAC(w => npAckToFailure.GetSignedFieldsForNeighborHMAC(w, failure.GetSignedFieldsForNeighborHMAC));
                    }

                    var npAckToFailureUdpData = npAckToFailure.Encode(_destinationNeighborNullable == null);

                    _engine.RespondToRequestAndRetransmissions(_failureUdpData, npAckToFailureUdpData, _destinationEndpoint);
                }

                throw new RequestRejectedException(failure.ResponseCode);
            }
            else
            {
                throw new InvalidOperationException();
            }
        }