392 lines
13 KiB
C#
392 lines
13 KiB
C#
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<Discarded> _discardedQueue;
|
|
private readonly ConcurrentQueue<FilterQueueItem> _preFilteredQueue;
|
|
private readonly ConcurrentQueue<ScannerResumeObject> _resumeQueue;
|
|
private readonly DbHandler _dbHandler;
|
|
private bool _stop;
|
|
private readonly int _timeout;
|
|
|
|
public IpScanner(ConcurrentQueue<Discarded> discardedQueue,
|
|
ConcurrentQueue<ScannerResumeObject> resumeQueue, DbHandler dbHandler,
|
|
ConcurrentQueue<FilterQueueItem> preFilteredQueue)
|
|
{
|
|
_dbHandler = dbHandler;
|
|
_preFilteredQueue = preFilteredQueue;
|
|
_discardedQueue = discardedQueue;
|
|
_resumeQueue = resumeQueue;
|
|
|
|
_timeout = 32;
|
|
}
|
|
|
|
public List<WaitHandle[]> Start(int threads)
|
|
{
|
|
int threadsAmount = 0;
|
|
if (threads % 2 == 0)
|
|
{
|
|
threadsAmount = 256 / threads;
|
|
}
|
|
|
|
WaitHandle[] waitHandle = new WaitHandle[64];
|
|
|
|
int counter = 0;
|
|
|
|
List<WaitHandle[]> 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 >= 2000)
|
|
{
|
|
Thread.Sleep(1000);
|
|
}
|
|
|
|
if (_preFilteredQueue.Count >= 2000)
|
|
{
|
|
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);
|
|
|
|
if (i % 2 == 0)
|
|
{
|
|
responseCode = IPStatus.Success;
|
|
}
|
|
else
|
|
{
|
|
responseCode = IPStatus.TimedOut;
|
|
}
|
|
|
|
//CustomPing.SendIcmpEchoRequestOverRawSocket(Parse(ip.ToString()), _timeout);
|
|
Thread.Sleep(16);
|
|
|
|
}
|
|
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}) is at index ({i}) out of ({scanSettings.End}). Remaining ({scanSettings.End - i})");
|
|
}
|
|
|
|
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 FilterQueueItem CreateUnfilteredQueueItem(Ip ip, int responseCode)
|
|
{
|
|
FilterQueueItem filterQueueItem = new()
|
|
{
|
|
Ip = ip,
|
|
ResponseCode = responseCode
|
|
};
|
|
|
|
return filterQueueItem;
|
|
}
|
|
|
|
public void Stop()
|
|
{
|
|
_stop = true;
|
|
}
|
|
|
|
private static unsafe IPAddress Parse(ReadOnlySpan<char> ipSpan)
|
|
{
|
|
int length = ipSpan.Length;
|
|
long nonCanonical;
|
|
fixed (char* name = &MemoryMarshal.GetReference<char>(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;
|
|
}
|
|
}
|
|
} |