Skip to content

Commit

Permalink
added async calls to the backend to run nmap in parallel.
Browse files Browse the repository at this point in the history
  • Loading branch information
royrusso committed Nov 30, 2024
1 parent 1706eca commit 5fd80f9
Show file tree
Hide file tree
Showing 5 changed files with 154 additions and 11 deletions.
13 changes: 13 additions & 0 deletions backend/api/info.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
from fastapi import APIRouter
import FindMyIP as ip
from loguru import logger
from scan.nmap import NmapScanner

router = APIRouter()

Expand All @@ -14,6 +15,18 @@ def get_ip_info():
return {"internet_connected": ip.internet(), "internal_ip": ip.internal(), "external_ip": ip.external()}


@router.get("/info/nmap_info", tags=["info"])
def which_nmap():
"""
Returns the path to the nmap binary.
"""
scanner = NmapScanner()
nmap_version = scanner.nmap_version()
which_nmap = scanner.which_nmap()

return {"nmap_version": nmap_version, "namp_path": which_nmap}


@router.get("/info/is_root", tags=["info"])
def is_root():
"""
Expand Down
1 change: 1 addition & 0 deletions backend/api/scan.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ async def scan_basic(ip_address: str):
In NMap terms, it runs a basic no-port-scan (nmap -sn --traceroute).
"""
nmap_scanner = NmapScanner()

result = nmap_scanner.scan(ip_address, "ping")
return {"result": result}

Expand Down
61 changes: 54 additions & 7 deletions frontend/src/routes/home.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,8 @@ import {
Row,
Toast,
} from "react-bootstrap";
import { scanPingType } from "../services/Client";
import { fetchIPList, fetchPingData } from "../services/Client";
import { IPListToRange } from "../utils/IPUtils";

const Home = () => {
const [scanFormData, setScanFormData] = useState({
Expand Down Expand Up @@ -49,13 +50,59 @@ const Home = () => {
`Scan started for IP Address(es): ${scanFormData.ipAddress}`
);

// Send the scan request to the backend
// scanListType(scanFormData.ipAddress).then((data) => {
// console.log("Scan Response: ", data);
// });
// const result = doFullScan(scanFormData.ipAddress);

scanPingType(scanFormData.ipAddress).then((data) => {
console.log("Ping Response: ", data);
let ipList = fetchIPList(scanFormData.ipAddress);
ipList.then((data) => {
console.log("IP List: ", data);
if (data.length === 0) {
console.log("No IP Address found in the given range");
return;
}

// get host IP object
const hosts = data.result.nmaprun.host;

console.log("Hosts: ", hosts);

// divide hosts into chunks of 10 for parallel scanning
const chunkSize = 10;
const chunkedHosts = [];
for (let i = 0; i < hosts.length; i += chunkSize) {
chunkedHosts.push(hosts.slice(i, i + chunkSize));
}

console.log("Chunked Hosts: ", chunkedHosts);

// for each chunk, convert the IP list to IP range and scan in parallel
chunkedHosts.forEach((chunk: any) => {
const ipRange = IPListToRange(
chunk.map((host: any) => host.address["@addr"])
);
console.log("IP Range: ", ipRange);

fetchPingData(ipRange)
.then((data) => {
console.log("Ping Data: ", data);

// Sometimes host is missing, if all IPs are down.
if ("host" in data.result.nmaprun) {
const hosts = data.result.nmaprun.host;

// hosts can be an array, when multiple hosts are up. Otherwise, it's an object.
if (hosts.isArray) {
hosts.forEach((host: any) => {
console.log("Host: ", host);
});
} else {
console.log("Host: ", hosts);
}
}
})
.finally(() => {
console.log("Ping scan completed");
});
});
});
};

Expand Down
63 changes: 59 additions & 4 deletions frontend/src/services/Client.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,68 @@
import { IPListToRange } from "../utils/IPUtils";

export const BASE_URI = import.meta.env.VITE_API_URL;

export const scanListType = async (ip_arg: string) => {
const response = await fetch(`${BASE_URI}/scan/list/${ip_arg}`);
export const fetchPingData = async (ip_arg: string) => {
const response = await fetch(`${BASE_URI}/scan/ping/${ip_arg}`);
const data = await response.json();
return data;
};

export const scanPingType = async (ip_arg: string) => {
const response = await fetch(`${BASE_URI}/scan/ping/${ip_arg}`);
export const fetchIPList = async (ip_arg: string) => {
const response = await fetch(`${BASE_URI}/scan/list/${ip_arg}`);
const data = await response.json();
return data;
};

const doFullScan = async (ip_arg: string) => {
let ipList = fetchIPList(ip_arg);
ipList.then((data) => {
console.log("IP List: ", data);
if (data.length === 0) {
console.log("No IP Address found in the given range");
return;
}

// get host IP object
const hosts = data.result.nmaprun.host;

console.log("Hosts: ", hosts);

// divide hosts into chunks of 10 for parallel scanning
const chunkSize = 10;
const chunkedHosts = [];
for (let i = 0; i < hosts.length; i += chunkSize) {
chunkedHosts.push(hosts.slice(i, i + chunkSize));
}

console.log("Chunked Hosts: ", chunkedHosts);

// for each chunk, convert the IP list to IP range and scan in parallel
chunkedHosts.forEach((chunk: any) => {
const ipRange = IPListToRange(
chunk.map((host: any) => host.address["@addr"])
);
console.log("IP Range: ", ipRange);

fetchPingData(ipRange).then((data) => {
console.log("Ping Data: ", data);

// Sometimes host is missing, if all IPs are down.
if ("host" in data.result.nmaprun) {
const hosts = data.result.nmaprun.host;

// hosts can be an array, when multiple hosts are up. Otherwise, it's an object.
if (hosts.isArray) {
hosts.forEach((host: any) => {
console.log("Host: ", host);
});
} else {
console.log("Host: ", hosts);
}
}
});
});
});
};

export default doFullScan;
27 changes: 27 additions & 0 deletions frontend/src/utils/IPUtils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
// Function to convert a range of IP addresses to a list of IP addresses
// Abandon all hope, ye who enter here
export const IPListToRange = (ipList: string[]): string => {
let ipRange: string;

if (ipList.length === 0) {
return "";
}

const sortedIPList = ipList
.map((ip) => {
return ip.split(".").map((octet) => {
return parseInt(octet);
});
})
.sort((a, b) => {
return a[0] - b[0] || a[1] - b[1] || a[2] - b[2] || a[3] - b[3];
});

const baseIP = sortedIPList[0].slice(0, 3).join("."); // get the base IP - first three octets
let startIP = sortedIPList[0][3]; // get the start IP - last octet
let endIP = sortedIPList[sortedIPList.length - 1][3]; // get the end IP - last octet

ipRange = `${baseIP}.${startIP}-${endIP}`;

return ipRange;
};

0 comments on commit 5fd80f9

Please sign in to comment.