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); }
/// <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); }
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(); } }