using System.Buffers.Binary; using System.Collections.Concurrent; using System.Net; using System.Net.NetworkInformation; using System.Numerics; using System.Runtime.InteropServices; using Backend.Helper; using Models.Experimental; using Models.Handler; using Models.Model.Backend; namespace Backend.Handler; public class ScanSettings { public int Start; public int End; public int ThreadNumber; public EventWaitHandle? Handle; } public class IpScanner { private readonly ConcurrentQueue _discardedQueue; private readonly ConcurrentQueue _preFilteredQueue; private readonly ConcurrentQueue _resumeQueue; private readonly DbHandler _dbHandler; private bool _stop; private readonly int _timeout; public IpScanner(ConcurrentQueue discardedQueue, ConcurrentQueue resumeQueue, DbHandler dbHandler, ConcurrentQueue preFilteredQueue) { _dbHandler = dbHandler; _preFilteredQueue = preFilteredQueue; _discardedQueue = discardedQueue; _resumeQueue = resumeQueue; _timeout = 32; } public List Start(int threads) { int threadsAmount = 0; if (threads % 2 == 0) { threadsAmount = 256 / threads; } WaitHandle[] waitHandle = new WaitHandle[64]; int counter = 0; List waitHandles = []; for (int i = 0; i < threads; i++) { EventWaitHandle handle = new(false, EventResetMode.ManualReset); ScanSettings scanSettings = new() { Start = threadsAmount * i, End = threadsAmount * i + threadsAmount, ThreadNumber = i, Handle = handle }; if (counter < 64) { waitHandle[counter] = handle; counter++; Thread f = new (Scan!); f.Start(scanSettings); Console.WriteLine($"Scanner thread ({i}) started"); Thread.Sleep(128); continue; } counter = 0; waitHandles.Add(waitHandle); waitHandle = new WaitHandle[64]; } return waitHandles; } private void Scan(object obj) { ScanSettings scanSettings = (ScanSettings)obj; ScannerResumeObject resumeObject = new(); resumeObject.ThreadNumber = scanSettings.ThreadNumber; resumeObject.StartRange = scanSettings.Start; resumeObject.EndRange = scanSettings.End; ScannerResumeObject? resumeNow = _dbHandler.GetResumeObject(scanSettings.ThreadNumber); int secondByte = 0; int thirdByte = 0; int fourthByte = 0; if (resumeNow is not null) { if (resumeNow.Completed) { return; } scanSettings.Start = resumeNow.FirstByte; scanSettings.End = resumeNow.EndRange; secondByte = resumeNow.SecondByte; thirdByte = resumeNow.ThirdByte; fourthByte = resumeNow.FourthByte; } else { CreateResumeObject(scanSettings.ThreadNumber, scanSettings.Start, scanSettings.End, scanSettings.Start, secondByte, thirdByte, fourthByte, false, false, Operations.Insert); } // Empty buffer so we use the lowest abstracted ping.Send() method. byte[] buf = []; using Ping ping = new(); int x = 0; for (int i = scanSettings.Start; i < scanSettings.End; i++) { if (i is 0 or 10 or 127 or 256) continue; if (i is >= 224 and <= 239) continue; for (int j = secondByte; j < 256; j++) { if (i == 169 && j == 254) continue; if (i == 192 && j == 168) continue; if (i == 198 && j == 18 || j == 19) continue; if (i == 172 && j is >= 16 and <= 31) continue; for (int k = thirdByte; k < 256; k++) { if (i == 192 && k == 2) continue; if (i == 192 && j == 88 && k == 99) continue; if (_discardedQueue.Count >= 20_000) { Thread.Sleep(1000); } if (_preFilteredQueue.Count >= 20_000) { Thread.Sleep(1000); } for (int l = fourthByte; l < 256; l++) { if (x == 75_000) { CreateResumeObject(scanSettings.ThreadNumber, scanSettings.Start, scanSettings.End, i, j, k, l, false, false, Operations.Update); x = 0; } x++; if (_stop) { resumeObject.FourthByte = l; break; } Ip ip = new() { Ip1 = i, Ip2 = j, Ip3 = k, Ip4 = l }; IPStatus responseCode = IPStatus.Unknown; try { // Sometimes, if the pinger gets a Destination Unreachable Communication administratively prohibited response, the pinger will throw an exception. // https://en.wikipedia.org/wiki/Internet_Control_Message_Protocol?useskin=vector#Control_messages //_ = IPAddress.TryParse(ip.ToString(), out IPAddress? address); responseCode = CustomPing.SendIcmpEchoRequestOverRawSocket(Parse(ip.ToString()), _timeout); /*if (l % 2 == 0) { responseCode = IPStatus.Success; } else { responseCode = IPStatus.TimedOut; }*/ //responseCode = IPStatus.TimedOut; //Thread.Sleep(0); } catch { // } if (responseCode != IPStatus.Success) { _discardedQueue.Enqueue(CreateDiscardedQueueItem(ip, (int)responseCode)); continue; } _preFilteredQueue.Enqueue(CreateUnfilteredQueueItem(ip, (int)responseCode)); } if (_stop) { resumeObject.ThirdByte = k; break; } } if (_stop) { resumeObject.SecondByte = j; break; } } if (_stop) { resumeObject.FirstByte = i; break; } } Console.WriteLine($"Thread ({scanSettings.ThreadNumber}) stopped."); if (_stop) { resumeObject.Paused = true; } else { resumeObject.Completed = true; } resumeObject.Operation = Operations.Update; _resumeQueue.Enqueue(resumeObject); scanSettings.Handle!.Set(); } private void CreateResumeObject(int threadNumber, int startRange, int endRange, int firstByte, int secondByte, int thirdByte, int fourthByte, bool paused, bool completed, Operations operation) { ScannerResumeObject resumeObject = new(); resumeObject.ThreadNumber = threadNumber; resumeObject.StartRange = startRange; resumeObject.EndRange = endRange; resumeObject.FirstByte = firstByte; resumeObject.SecondByte = secondByte; resumeObject.ThirdByte = thirdByte; resumeObject.FourthByte = fourthByte; resumeObject.Paused = paused; resumeObject.Completed = completed; resumeObject.Operation = operation; _resumeQueue.Enqueue(resumeObject); } private static Discarded CreateDiscardedQueueItem(Ip ip, int responseCode) { return new() { Ip = ip, ResponseCode = responseCode }; } private static Ip CreateUnfilteredQueueItem(Ip ip, int responseCode) { ip.ResponseCode = responseCode; return ip; } public void Stop() { _stop = true; } private static unsafe IPAddress Parse(ReadOnlySpan ipSpan) { int length = ipSpan.Length; long nonCanonical; fixed (char* name = &MemoryMarshal.GetReference(ipSpan)) nonCanonical = ParseNonCanonical(name, 0, ref length, true); return new IPAddress(BitOperations.RotateRight((uint)nonCanonical & 16711935U, 8) + BitOperations.RotateLeft((uint)nonCanonical & 4278255360U, 8)); } private static unsafe long ParseNonCanonical(char* name, int start, ref int end, bool notImplicitFile) { long* numPtr = stackalloc long[4]; long num1 = 0; bool flag = false; int index1 = 0; int index2; for (index2 = start; index2 < end; ++index2) { char ch = name[index2]; num1 = 0L; int num2 = 10; if (ch == '0') { num2 = 8; ++index2; flag = true; if (index2 < end) { switch (name[index2]) { case 'X': case 'x': num2 = 16; ++index2; flag = false; break; } } } for (; index2 < end; ++index2) { char c = name[index2]; int num3; if ((num2 == 10 || num2 == 16) && char.IsAsciiDigit(c)) num3 = (int) c - 48; else if (num2 == 8 && '0' <= c && c <= '7') num3 = (int) c - 48; else if (num2 == 16 && 'a' <= c && c <= 'f') num3 = (int) c + 10 - 97; else if (num2 == 16 && 'A' <= c && c <= 'F') num3 = (int) c + 10 - 65; else break; num1 = num1 * (long) num2 + (long) num3; if (num1 > (long) uint.MaxValue) return -1; flag = true; } if (index2 < end && name[index2] == '.') { if (index1 >= 3 || !flag || num1 > (long) byte.MaxValue) return -1; numPtr[index1] = num1; ++index1; flag = false; } else break; } if (!flag) return -1; if (index2 < end) { char ch; if ((ch = name[index2]) != '/' && ch != '\\' && (!notImplicitFile || ch != ':' && ch != '?' && ch != '#')) return -1; end = index2; } numPtr[index1] = num1; switch (index1) { case 0: return numPtr[0] > (long) uint.MaxValue ? -1L : numPtr[0]; case 1: return numPtr[1] > 16777215L ? -1L : numPtr[0] << 24 | numPtr[1] & 16777215L; case 2: return numPtr[2] > (long) ushort.MaxValue ? -1L : numPtr[0] << 24 | (numPtr[1] & (long) byte.MaxValue) << 16 | numPtr[2] & (long) ushort.MaxValue; case 3: return numPtr[3] > (long) byte.MaxValue ? -1L : numPtr[0] << 24 | (numPtr[1] & (long) byte.MaxValue) << 16 | (numPtr[2] & (long) byte.MaxValue) << 8 | numPtr[3] & (long) byte.MaxValue; default: return -1; } } }