private void SendPacket() { byte[] receiveBuffer = new byte[m_PingAttributes.ReceiveBufferSize]; // Ipv4Header.length + IcmpHeader.length + attrs.recievebuffersize int bytesRead = 0; // Sending loop for (int index = 1; m_PingAttributes.Continous || index <= m_PingAttributes.Count; index++) { if (index != 1) { // Wait for set interval before sending again or cancel if requested if (m_CancellationToken.WaitHandle.WaitOne(m_PingAttributes.Interval)) { break; } // Generate random interval when RandomTiming flag is set if (m_PingAttributes.RandomTiming) { m_PingAttributes.Interval = Helper.RandomInt(5000, 100000); } } // Update packet before sending UpdatePacketSequenceNumber(index); if (m_PingAttributes.RandomMessage) { UpdatePacketMessage(Encoding.ASCII.GetBytes(Helper.RandomString())); } UpdatePacketChecksum(); try { // Show request packet if (Display.ShowRequests) { Display.RequestPacket(m_Packet, Display.UseInputtedAddress | Display.UseResolvedAddress ? m_PingAttributes.InputtedAddress : m_PingAttributes.ResolvedAddress, index); } // If there were extra responses from a prior request, ignore them while (m_Socket.Available != 0) { bytesRead = m_Socket.Receive(receiveBuffer); } // Send ping request m_Socket.SendTo(m_Packet.GetBytes(), m_PacketSize, SocketFlags.None, m_RemoteEndpoint); // Packet size = message field + 4 header bytes long requestTimestamp = Stopwatch.GetTimestamp(); m_PingResults.IncrementSentPackets(); // Just for artifically testing higher ping response times if (m_Debug) { Random rnd = new Random(); Thread.Sleep(rnd.Next(10, 400)); if (rnd.Next(3) == 1) { throw new SocketException(); } } // Try and recieve a packet ICMP response = null; EndPoint responseEP = m_RemoteEndpoint; TimeSpan replyTime = TimeSpan.Zero; ReceivePacket(ref response, ref responseEP, ref replyTime, ref bytesRead, requestTimestamp); if (Display.ShowReplies) { // Determine what form the response address is going to be displayed in string responseAddress = responseEP.ToString(); if (Display.UseResolvedAddress) { // Returned address normally have port at the end (eg 8.8.8.8:0) so we need to remove that before trying to query the DNS string responseIP = responseEP.ToString().Split(':')[0]; // Resolve the ip and store as the response address responseAddress = Helper.RunWithCancellationToken(() => Lookup.QueryHost(responseIP), m_CancellationToken); } else if (Display.UseInputtedAddress) { responseAddress = m_PingAttributes.InputtedAddress; } Display.ReplyPacket(response, responseAddress, index, replyTime, bytesRead); } // Store response info m_PingResults.IncrementReceivedPackets(); m_PingResults.CountPacketType(response.Type); m_PingResults.SaveResponseTime(replyTime.TotalMilliseconds); if (m_PingAttributes.BeepMode == 2) { try { Console.Beep(); } catch (Exception) { } // Silently continue if Console.Beep errors } } catch (IOException) { if (Display.ShowOutput) { Display.Error("General transmit error"); } m_PingResults.SaveResponseTime(-1); m_PingResults.IncrementLostPackets(); } catch (SocketException) { Display.Timeout(index); if (m_PingAttributes.BeepMode == 1) { try { Console.Beep(); } catch (Exception) { } } m_PingResults.SaveResponseTime(-1); m_PingResults.IncrementLostPackets(); } catch (OperationCanceledException) { m_PingResults.ScanWasCanceled = true; break; } catch (Exception) { if (Display.ShowOutput) { Display.Error("General error occured"); } m_PingResults.SaveResponseTime(-1); m_PingResults.IncrementLostPackets(); } // Run callback (if provided) to notify of updated results OnPingResultsUpdateCallback?.Invoke(m_PingResults); } }
private PingResults SendICMP(PingAttributes attrs, Action <PingResults> resultsUpdateCallback = null) { PingResults results = new PingResults(); ICMP packet = new ICMP(); byte[] receiveBuffer = new byte[attrs.RecieveBufferSize]; // Ipv4Header.length + IcmpHeader.length + attrs.recievebuffersize int bytesRead, packetSize; // Convert to IPAddress IPAddress ipAddr = IPAddress.Parse(attrs.Address); // Setup endpoint IPEndPoint iep = new IPEndPoint(ipAddr, 0); // Setup raw socket Socket sock = CreateRawSocket(ipAddr.AddressFamily); // Helper function to set receive timeout (only if it's changing) int appliedReceiveTimeout = 0; void SetReceiveTimeout(int receiveTimeout) { if (receiveTimeout != appliedReceiveTimeout) { sock.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReceiveTimeout, receiveTimeout); appliedReceiveTimeout = receiveTimeout; } } // Set socket options sock.SetSocketOption(SocketOptionLevel.IP, SocketOptionName.IpTimeToLive, attrs.Ttl); sock.DontFragment = attrs.DontFragment; sock.ReceiveBufferSize = attrs.RecieveBufferSize; // Create packet message payload byte[] payload; if (attrs.Size != -1) { payload = Helper.GenerateByteArray(attrs.Size); } else { payload = Encoding.ASCII.GetBytes(attrs.Message); } // Construct our ICMP packet packet.Type = attrs.Type; packet.Code = attrs.Code; Buffer.BlockCopy(BitConverter.GetBytes(m_SessionId), 0, packet.Message, 0, 2); // Add identifier to ICMP message Buffer.BlockCopy(payload, 0, packet.Message, 4, payload.Length); // Add text into ICMP message packet.MessageSize = payload.Length + 4; packetSize = packet.MessageSize + 4; // Sending loop for (int index = 1; attrs.Continous || index <= attrs.Count; index++) { if (index != 1) { // Wait for set interval before sending again or cancel if requested if (m_CancellationToken.WaitHandle.WaitOne(attrs.Interval)) { break; } // Generate random interval when RandomTimings flag is set if (attrs.RandomTiming) { attrs.Interval = Helper.RandomInt(5000, 100000); } } // Include sequence number in ping message ushort sequenceNum = (ushort)index; Buffer.BlockCopy(BitConverter.GetBytes(sequenceNum), 0, packet.Message, 2, 2); // Fill ICMP message field if (attrs.RandomMsg) { payload = Encoding.ASCII.GetBytes(Helper.RandomString()); Buffer.BlockCopy(payload, 0, packet.Message, 4, payload.Length); } // Update packet checksum packet.Checksum = 0; UInt16 chksm = packet.GetChecksum(); packet.Checksum = chksm; try { // Show request packet if (Display.ShowRequests) { Display.RequestPacket(packet, Display.UseInputtedAddress | Display.UseResolvedAddress ? attrs.InputtedAddress : attrs.Address, index); } // If there were extra responses from a prior request, ignore them while (sock.Available != 0) { bytesRead = sock.Receive(receiveBuffer); } // Send ping request sock.SendTo(packet.GetBytes(), packetSize, SocketFlags.None, iep); // Packet size = message field + 4 header bytes long requestTimestamp = Stopwatch.GetTimestamp(); try { results.Sent++; } catch (OverflowException) { results.HasOverflowed = true; } if (m_Debug) { // Induce random wait for debugging Random rnd = new Random(); Thread.Sleep(scale);//rnd.Next(scale));//1500)); //Thread.Sleep(rnd.Next(100)); if (inverting) { scale -= 5; } else { scale += 5; } if (scale > 1100) { inverting = true; } else if (scale == 10) { inverting = false; } //if (rnd.Next(20) == 1) { throw new SocketException(); } } ICMP response; EndPoint responseEP = iep; TimeSpan replyTime = TimeSpan.Zero; do { // Cancel if requested m_CancellationToken.ThrowIfCancellationRequested(); // Set receive timeout, limited to 250ms so we don't block very long without checking for // cancellation. If the requested ping timeout is longer, we will wait some more in subsequent // loop iterations until the requested ping timeout is reached. int remainingTimeout = (int)Math.Ceiling(attrs.Timeout - replyTime.TotalMilliseconds); if (remainingTimeout <= 0) { throw new SocketException(); } SetReceiveTimeout(Math.Min(remainingTimeout, 250)); // Wait for response try { bytesRead = sock.ReceiveFrom(receiveBuffer, ref responseEP); } catch (SocketException) { bytesRead = 0; } replyTime = new TimeSpan(Helper.StopwatchToTimeSpanTicks(Stopwatch.GetTimestamp() - requestTimestamp)); if (bytesRead == 0) { response = null; } else { // Store reply packet response = new ICMP(receiveBuffer, bytesRead); // If we sent an echo and receive a response with a different identifier or sequence // number, ignore it (it could correspond to an older request that timed out) if (packet.Type == 8 && response.Type == 0) { ushort responseSessionId = BitConverter.ToUInt16(response.Message, 0); ushort responseSequenceNum = BitConverter.ToUInt16(response.Message, 2); if (responseSessionId != m_SessionId || responseSequenceNum != sequenceNum) { response = null; } } } } while (response == null); // Display reply packet if (Display.ShowReplies) { Display.ReplyPacket(response, Display.UseInputtedAddress | Display.UseResolvedAddress ? attrs.InputtedAddress : responseEP.ToString(), index, replyTime, bytesRead); } // Store response info try { results.Received++; } catch (OverflowException) { results.HasOverflowed = true; } results.CountPacketType(response.Type); results.SaveResponseTime(replyTime.TotalMilliseconds); if (attrs.BeepLevel == 2) { try { Console.Beep(); } catch (Exception) { } // Silently continue if Console.Beep errors } } catch (IOException) { if (Display.ShowOutput) { Display.Error("General transmit error"); } results.SaveResponseTime(-1); try { results.Lost++; } catch (OverflowException) { results.HasOverflowed = true; } } catch (SocketException) { Display.Timeout(index); if (attrs.BeepLevel == 1) { try { Console.Beep(); } catch (Exception) { results.HasOverflowed = true; } } results.SaveResponseTime(-1); try { results.Lost++; } catch (OverflowException) { results.HasOverflowed = true; } } catch (OperationCanceledException) { results.ScanWasCanceled = true; break; } catch (Exception) { if (Display.ShowOutput) { Display.Error("General error occured"); } results.SaveResponseTime(-1); try { results.Lost++; } catch (OverflowException) { results.HasOverflowed = true; } } // Run callback (if provided) to notify of updated results resultsUpdateCallback?.Invoke(results); } // Clean up sock.Close(); return(results); }