Author SHA1 Message Date
Stone_Red 25b95d7605 Merge pull request #10 from Stone-Red-Code/develop
Develop
2025-06-27 13:06:48 +02:00
Stone_Red e5f69fd256 Update version 2025-06-27 13:06:17 +02:00
Stone_Red a99903eec6 Update nuget packages 2025-06-27 13:03:38 +02:00
Stone_Red e6943924cc Allow files paths without directory as output paths 2025-06-27 13:01:16 +02:00
Stone_Red cb5a4f2fe3 Merge pull request #9 from Stone-Red-Code/develop
Develop
2024-10-30 20:51:05 +01:00
Stone_Red edc7ea95dd Update package manifest files 2024-10-30 20:49:50 +01:00
Stone_Red 4c38d52709 Add missing license in readme and small fixes 2024-10-30 19:00:44 +01:00
Stone_Red c094f0400e Update readme to new version and include banner 2024-10-29 12:30:23 +01:00
Stone_Red 975e7b7d59 Merge pull request #8 from Stone-Red-Code/main
Sync
2024-10-29 12:21:38 +01:00
Stone_Red 7e043c2fe2 Merge branch 'develop' into main 2024-10-29 12:21:27 +01:00
Stone_Red 6e86058862 Set "scan" as default 2024-10-29 12:17:14 +01:00
Stone_Red 1b5278bf09 Add monitoring mode and improve json output format 2024-10-27 01:17:10 +02:00
Stone_Red 701cd8e3fc Add command line arguments
- Save to JSON
- Save to CSV
- Retry
- Concurrency
- Silent
- Help
- Version
2024-10-25 14:45:26 +02:00
Stone_Red ae8055fac8 Add humanizer library 2024-10-25 00:18:05 +02:00
Stone_Red 11a45e1cc6 Add summary to package description 2024-06-21 15:29:57 +02:00
Stone_Red 08b84f3dad Update .nuspec 2024-06-04 20:24:21 +02:00
Stone_Red a06dfe82a8 Update readme 2024-06-04 17:56:02 +02:00
Stone_Red 3c5d165ebb Fix pluralization & alignment of output 2024-05-01 14:58:57 +02:00
Stone_Red 553fd97e2a Update arp-scanner.nuspec 2024-05-01 14:48:11 +02:00
Stone_Red 5ceee1092f Set Private to null instead of false 2024-05-01 02:00:12 +02:00
Stone_Red 9f49f2d595 Merge pull request #7 from Stone-Red-Code/develop
Fix snapcraft.yaml?
2024-05-01 01:51:24 +02:00
Stone_Red 92b1fd0598 Fix snapcraft.yaml? 2024-05-01 01:51:02 +02:00
Stone_Red 6f6a2c2ee5 Merge pull request #6 from Stone-Red-Code/develop
Update snapcraft architecture section
2024-05-01 01:46:29 +02:00
Stone_Red 687b882d5e Update snapcraft architecture section 2024-05-01 01:45:59 +02:00
Stone_Red 95774eaee8 Merge pull request #5 from Stone-Red-Code/develop
Update project to .NET 8 and auto update mac db
2024-05-01 01:37:05 +02:00
Stone_Red 6de6edbf48 Update project to .NET 8 and auto update mac db 2024-05-01 01:36:31 +02:00
Stone_Red dabc878624 Update choco VERIFICATION.txt 2024-02-08 07:58:49 +01:00
Stone_Red 1420655b8d Update snapcraft.yaml 2024-01-09 13:35:34 +01:00
Stone_Red 96b261238e Update choco package nuspec 2024-01-09 13:19:23 +01:00
Stone_Red 89f1401de9 Update README.md
Remove snap connect instructions because it now auto connects.
2024-01-05 14:28:50 +01:00
Stone_Red 924e0016cb Update twitter link in README.md to always show the latest updates 2024-01-03 12:23:41 +01:00
Stone_Red 40cb53d037 Update README.md 2024-01-03 12:22:28 +01:00
Stone_Red a0c2811fae Merge pull request #4 from Stone-Red-Code/develop
Develop
2024-01-03 11:58:46 +01:00
Stone_Red a4536f05d8 Debug build 2024-01-03 11:58:25 +01:00
Stone_Red 38ea2e99c7 Merge branch 'main' into develop 2024-01-03 00:43:04 +01:00
14 changed files with 559 additions and 44676 deletions
+16 -10
View File
@@ -1,26 +1,32 @@
# ARP-Scanner ![Arp-Scanner](https://socialify.git.ci/Stone-Red-Code/Arp-Scanner/image?description=1&forks=1&issues=1&logo=data%3Aimage%2Fsvg%2Bxml%3Bbase64%2CPD94bWwgdmVyc2lvbj0iMS4wIiBzdGFuZGFsb25lPSJubyI%2FPgo8IURPQ1RZUEUgc3ZnIFBVQkxJQyAiLS8vVzNDLy9EVEQgU1ZHIDIwMDEwOTA0Ly9FTiIKICJodHRwOi8vd3d3LnczLm9yZy9UUi8yMDAxL1JFQy1TVkctMjAwMTA5MDQvRFREL3N2ZzEwLmR0ZCI%2BCjxzdmcgdmVyc2lvbj0iMS4wIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciCiB3aWR0aD0iMTAwLjAwMDAwMHB0IiBoZWlnaHQ9IjEwMC4wMDAwMDBwdCIgdmlld0JveD0iMCAwIDEwMC4wMDAwMDAgMTAwLjAwMDAwMCIKIHByZXNlcnZlQXNwZWN0UmF0aW89InhNaWRZTWlkIG1lZXQiPgo8bWV0YWRhdGE%2BCkNyZWF0ZWQgYnkgcG90cmFjZSAxLjEwLCB3cml0dGVuIGJ5IFBldGVyIFNlbGluZ2VyIDIwMDEtMjAxMQo8L21ldGFkYXRhPgogICA8cmVjdCB4PSIwIiB3aWR0aD0iMTAwIiBoZWlnaHQ9IjEwMCIgcng9IjE1IiBmaWxsPSIjNWE1YTVhIiAvPgo8ZyB0cmFuc2Zvcm09InRyYW5zbGF0ZSgwLjAwMDAwMCw5NS4wMDAwMDApIHNjYWxlKDAuMTAwMDAwLC0wLjEwMDAwMCkiCmZpbGw9IiMwMDAwMDAiIHN0cm9rZT0ibm9uZSI%2BCjxwYXRoIGQ9Ik02NDAgOTIwIGMtOSAtMTcgLTMgLTIzIDQ3IC00OSBsNTggLTMwIC0xMDIgLTEgLTEwMyAwIDAgLTMwIGMwIC0yNQotNCAtMzAgLTI0IC0zMCAtMTQgMCAtMjggLTQgLTMxIC0xMCAtNCAtNiAtNTYgLTEwIC0xMjIgLTEwIC0xNDggMCAtMTg1IC0xMQotMjQ4IC03NSAtNzMgLTcyIC04OSAtMTM1IC02MCAtMjMzIDIwIC02NiA2NiAtMTE5IDEzMCAtMTQ4IDQ4IC0yMyA1OCAtMjQKMzE2IC0yNCAyNTkgMCAyNjcgLTEgMzEwIC0yMyAyNCAtMTMgNTcgLTQzIDc0IC02NiAyNSAtMzYgMzAgLTU0IDMzIC0xMTcgNAotNjUgNyAtNzQgMjMgLTc0IDE2IDAgMTkgOCAxOSA1NiAwIDMxIC01IDc1IC0xMiA5OCAtMTYgNTUgLTk5IDEzOCAtMTU0IDE1NAotMjggOCAtMTI2IDEyIC0zMDEgMTIgLTI1MyAwIC0yNjEgMSAtMzA0IDIzIC0yNCAxMyAtNTcgNDMgLTc0IDY2IC0yNiAzNyAtMzAKNTEgLTMwIDExMSAwIDYwIDQgNzQgMzAgMTExIDE3IDIzIDUwIDUzIDc0IDY2IDM5IDIwIDU4IDIzIDE2NyAyMyA3MSAwIDEyNQotNCAxMjkgLTEwIDMgLTUgMTcgLTEwIDMwIC0xMCAxOCAwIDI0IC02IDI3IC0yNyBsMyAtMjggMTk4IC0zIDE5NyAtMiAwIDEwMAowIDEwMCAtNTkgMCBjLTUzIDAgLTY5IDUgLTE0NSA0OSAtODEgNDcgLTg2IDQ5IC05NiAzMXogbTIxMCAtMTA5IGMzOSAtNSA0NQotMTAgNTMgLTM4IDUgLTE3IDYgLTQ4IDMgLTY4IGwtNyAtMzYgLTE1NiAyIC0xNTYgMSAtMzIgMzQgLTMyIDM0IDMzIDM1IGMzMQozMyAzNyAzNSA5NiAzNSAzNSAwIDcwIDUgNzggMTAgOCA2IDI5IDggNDUgNCAxNyAtNCA1MCAtOSA3NSAtMTN6Ii8%2BCjxwYXRoIGQ9Ik0zMjAgNjE5IGMtNTYgLTI1IC0xNTAgLTk3IC0xNTAgLTExNSAwIC0xNyA5MiAtODcgMTUwIC0xMTMgNjAgLTI3CjE1MCAtMjcgMjEwIDAgNTUgMjUgMTUwIDk3IDE1MCAxMTQgMCAxNyAtOTUgODkgLTE1MCAxMTQgLTYwIDI3IC0xNTAgMjcgLTIxMAoweiBtMTkyIC0yOSBjMjggLTM0IDIzIC04NCAtMTEgLTEyMiAtNDMgLTQ4IC0xMDkgLTQ4IC0xNTIgMCAtNDQgNDkgLTM3IDEyMQoxNCAxNDIgMTIgNiA0NiA4IDc2IDYgNDIgLTIgNTkgLTggNzMgLTI2eiBtLTIxMCAtNDkgYy00IC00NSAtMiAtNTIgMzEgLTg3CjMzIC0zNiAzOSAtMzkgOTIgLTM5IDUzIDAgNTkgMyA5MiAzOSAzMyAzNSAzNSA0MiAzMSA4NyBsLTUgNDkgMjYgLTE2IGMxNCAtOQozOCAtMjggNTQgLTQyIGwyNyAtMjYgLTI3IC0yOCBjLTE1IC0xNiAtNTQgLTQyIC04NyAtNTggLTgwIC00MSAtMTQyIC00MQotMjIyIDAgLTU0IDI3IC0xMjQgODQgLTExMyA5MyAxMyAxMiAxMDEgNzcgMTAzIDc3IDIgMCAxIC0yMiAtMiAtNDl6Ii8%2BCjxwYXRoIGQ9Ik0zODQgNTY1IGMtMTkgLTI5IC0xMiAtNTcgMjAgLTc0IDI4IC0xNiA2MSAtMiA3MSAyOSA5IDMwIC0yMCA3MAotNTAgNzAgLTE1IDAgLTMxIC0xMCAtNDEgLTI1eiIvPgo8cGF0aCBkPSJNMjc2IDIyMiBjLTMgLTUgLTE4IC00NSAtMzUgLTkwIC0yMSAtNTcgLTI2IC04MiAtMTcgLTgyIDcgMCAxOCAxMQoyNCAyNCAxNSAzNCA2OSAzMyA4NSAtMSA5IC0yMCA0NCAtMzUgMzUgLTE1IC0xIDQgLTE2IDQ1IC0zMyA5MCAtMjcgNzQgLTQ1Cjk3IC01OSA3NHogbTI4IC01NiBjMyAtMTMgOCAtMzEgMTEgLTM4IDUgLTExIC0xIC0xNCAtMjMgLTEwIC0yOSA0IC0yOSA1IC0xOQozOCAxMiA0MCAyMiA0MyAzMSAxMHoiLz4KPHBhdGggZD0iTTM5OSAyMjggYy0xIC0yIC0zIC00MiAtNCAtOTAgLTIgLTY5IDAgLTg4IDExIC04OCAxMCAwIDE0IDEzIDE0IDQxCjAgMzMgMyA0MCAxOCAzNyA5IC0yIDI1IC0yMCAzNSAtNDAgMTAgLTIxIDI1IC0zOCAzNCAtMzggMTIgMCA5IDEwIC0xMSA0NQotMjEgMzggLTIzIDQ3IC0xMSA1MiAyMiA4IDE5IDYwIC00IDczIC0xNyA5IC03OSAxNSAtODIgOHogbTcxIC0zMyBjMTggLTIxIDUKLTQ1IC0yNSAtNDUgLTIxIDAgLTI1IDUgLTI1IDMwIDAgMjMgNSAzMCAxOSAzMCAxMCAwIDI0IC03IDMxIC0xNXoiLz4KPHBhdGggZD0iTTU1MCAxNDAgYzAgLTUzIDQgLTkwIDEwIC05MCA2IDAgMTAgMTYgMTAgMzUgMCAzMiAyIDM1IDI5IDM1IDMwIDAKNjEgMzAgNjEgNTcgMCAyNSAtMzkgNTMgLTc1IDUzIGwtMzUgMCAwIC05MHogbTY0IDU3IGMyNiAtMTkgMTcgLTUxIC0xNyAtNTUKLTI1IC0zIC0yNyAtMSAtMjcgMzIgMCAzOSAxMyA0NSA0NCAyM3oiLz4KPC9nPgo8L3N2Zz4K&name=1&pattern=Signal&pulls=1&stargazers=1&theme=Auto)
> A lightweight cross-platform IP scanner
## Usage ## Usage
1. Download the latest release 1. Download & install the latest release
- Chocolatey (Windows): `choco install arp-scanner` - Chocolatey (Windows): `choco install arp-scanner`
- Snapcraft (Linux): `snap install arp-scanner`\ - Snapcraft (Linux): `snap install arp-scanner`
and `snap connect arp-scanner:network-control` to give it permission to send arp requests - GitHub (Windows/Linux): [releases](https://github.com/Stone-Red-Code/ARP-Scanner/releases)
- GitHub (Windows/Linux): https://github.com/Stone-Red-Code/ARP-Scanner/releases 1. Use the `arp-scanner` command
1. Start `arp-scanner` with the IP range it should scan as parameter (e.g. `192.168.1.0 - 192.168.1.255`) - `arp-scanner help` lists all available commands
- `arp-scanner scan` scans the network for devices
- `arp-scanner scan --help` lists all available options for the scan command
- `arp-scanner monitor` continuously scans the network for devices
- `arp-scanner monitor --help` lists all available options for the monitor command
> [!Note] > [!Note]
> This programm may not work on all linux distributions because the [ArpLookup](https://github.com/georg-jung/ArpLookup) library has some [limitations](https://github.com/georg-jung/ArpLookup#supported-platforms) > This programm may not work on all linux distributions because the [ArpLookup](https://github.com/georg-jung/ArpLookup) library has some [limitations](https://github.com/georg-jung/ArpLookup#supported-platforms)
## Follow the development ## Follow the development
[![Twitter(X)](http://img.shields.io/badge/Twitter-black.svg?&logo=x&style=for-the-badge&logoColor=white)](https://twitter.com/search?q=%23ArpScanner%20%40StoneRedCode)
[![Twitter(X)](http://img.shields.io/badge/Twitter-black.svg?&logo=x&style=for-the-badge&logoColor=white)](https://twitter.com/search?q=%23ArpScanner%20%40StoneRedCode&f=live)
## Example ## Example
Start `arp-scanner` with the IP range it should scan as parameter (e.g. `192.168.1.0-192.168.1.255`)
![ARP-Scanner-Example](https://user-images.githubusercontent.com/56473591/236293969-4e8a65d2-86a3-4f10-8837-2b1aa0490252.png) ![ARP-Scanner-Example](https://user-images.githubusercontent.com/56473591/236293969-4e8a65d2-86a3-4f10-8837-2b1aa0490252.png)
## Third party licenses ## Third party licenses
- [ArpLookup](https://github.com/georg-jung/ArpLookup) - [MIT](https://github.com/georg-jung/ArpLookup/blob/master/LICENSE.txt) - [ArpLookup](https://github.com/georg-jung/ArpLookup) - [MIT](https://github.com/georg-jung/ArpLookup/blob/master/LICENSE.txt)
- [IPAddressRange](https://github.com/jsakamoto/ipaddressrange) - [MPL-2.0](https://github.com/jsakamoto/ipaddressrange/blob/master/LICENSE) - [IPAddressRange](https://github.com/jsakamoto/ipaddressrange) - [MPL-2.0](https://github.com/jsakamoto/ipaddressrange/blob/master/LICENSE)
- [Humanizer](https://github.com/Humanizr/Humanizer) - [MIT](https://github.com/Humanizr/Humanizer/blob/main/license.txt)
+17 -12
View File
@@ -40,7 +40,7 @@ enclosed in quotation marks, you should use an editor that supports UTF-8, not t
<!-- Note that unstable versions like 0.0.1 can be considered a released version, but it's <!-- Note that unstable versions like 0.0.1 can be considered a released version, but it's
possible that one can release a 0.0.1-beta before you release a 0.0.1 version. If the version possible that one can release a 0.0.1-beta before you release a 0.0.1 version. If the version
number is final, that is considered a released version and not a prerelease. --> number is final, that is considered a released version and not a prerelease. -->
<version>0.2.0.20240103</version> <version>1.1.0.20250627</version>
<!-- <packageSourceUrl>Where is this Chocolatey package located (think GitHub)? packageSourceUrl <!-- <packageSourceUrl>Where is this Chocolatey package located (think GitHub)? packageSourceUrl
is highly recommended for the community feed</packageSourceUrl>--> is highly recommended for the community feed</packageSourceUrl>-->
<!-- owners is a poor name for maintainers of the package. It sticks around by this name for <!-- owners is a poor name for maintainers of the package. It sticks around by this name for
@@ -67,26 +67,31 @@ enclosed in quotation marks, you should use an editor that supports UTF-8, not t
<!--<docsUrl>At <!--<docsUrl>At
what url are the software docs located?</docsUrl>--> what url are the software docs located?</docsUrl>-->
<!--<mailingListUrl></mailingListUrl>--> <!--<mailingListUrl></mailingListUrl>-->
<!--<bugTrackerUrl></bugTrackerUrl>--> <bugTrackerUrl>https://github.com/Stone-Red-Code/ARP-Scanner/issues</bugTrackerUrl>
<tags>arp-scanner arp p2p fiel-transfer</tags> <tags>arp-scanner arp</tags>
<summary>A lightweight cross-platform IP scanner</summary> <summary>A lightweight cross-platform IP scanner</summary>
<description> <description>
**Usage** **Usage**
1. Download the latest release 1. Download &amp; install the latest release
- Chocolatey (Windows): `choco install arp-scanner --version=0.2.0` - Chocolatey (Windows): `choco install arp-scanner`
- Snapcraft (Linux): `snap install arp-scanner`\ - Snapcraft (Linux): `snap install arp-scanner`
and `snap connect arp-scanner:network-control` to give it permission to send arp requests - GitHub (Windows/Linux): releases (https://github.com/Stone-Red-Code/ARP-Scanner/releases)
- GitHub (Windows/Linux): https://github.com/Stone-Red-Code/ARP-Scanner/releases 2. Use the `arp-scanner` command
1. Start `arp-scanner` with the IP range it should scan as parameter (e.g. `192.168.1.0 - 192.168.1.255`) - `arp-scanner help` lists all available commands
- `arp-scanner scan` scans the network for devices
- `arp-scanner scan --help` lists all available options for the scan command
- `arp-scanner monitor` continuously scans the network for devices
- `arp-scanner monitor --help` lists all available options for the monitor command
&gt; Note: This programm may not work on all linux distributions because the [ArpLookup](https://github.com/georg-jung/ArpLookup) library has some [limitations](https://github.com/georg-jung/ArpLookup#supported-platforms) Note: This programm may not work on all linux distributions because the ArpLookup (https://github.com/georg-jung/ArpLookup) library has some limitations (https://github.com/georg-jung/ArpLookup#supported-platforms)
**Third party licenses** **Third party licenses**
- ArpLookup (https://github.com/georg-jung/ArpLookup) - MIT (https://github.com/georg-jung/ArpLookup/blob/master/LICENSE.txt) - ArpLookup (https://github.com/georg-jung/ArpLookup) - MIT (https://github.com/georg-jung/ArpLookup/blob/master/LICENSE.txt)
- IPAddressRange (https://github.com/jsakamoto/ipaddressrange) - MPL-2.0 (https://github.com/jsakamoto/ipaddressrange/blob/master/LICENSE) - IPAddressRange (https://github.com/jsakamoto/ipaddressrange) - MPL-2.0 (https://github.com/jsakamoto/ipaddressrange/blob/master/LICENSE)
- Humanizer (https://github.com/Humanizr/Humanizer) - MIT (https://github.com/Humanizr/Humanizer/blob/main/license.txt)
</description> </description>
<!-- <releaseNotes>__REPLACE_OR_REMOVE__MarkDown_Okay</releaseNotes> --> <releaseNotes>https://github.com/Stone-Red-Code/ARP-Scanner/releases/latest</releaseNotes>
<!-- =============================== --> <!-- =============================== -->
<!-- Specifying dependencies and version ranges? <!-- Specifying dependencies and version ranges?
@@ -112,6 +117,6 @@ enclosed in quotation marks, you should use an editor that supports UTF-8, not t
<files> <files>
<!-- this section controls what actually gets packaged into the Chocolatey package --> <!-- this section controls what actually gets packaged into the Chocolatey package -->
<file src="tools\**" target="tools" /> <file src="tools\**" target="tools" />
<file src="..\src\ARP-Scanner\bin\Release\net6.0\**" target="tools" /> <file src="..\src\ARP-Scanner\bin\Release\net8.0\**" target="tools" />
</files> </files>
</package> </package>
+4 -2
View File
@@ -2,5 +2,7 @@
Verification is intended to assist the Chocolatey moderators and community Verification is intended to assist the Chocolatey moderators and community
in verifying that this package's contents are trustworthy. in verifying that this package's contents are trustworthy.
Checksum: c37a793bae228da768c9d4223d12608bfa28decbe7142e9ac19a49795cf23b82b2f0434032b98883343c658276bf2e4423926fbf79c65c48ae6cf11ad48d37ad Source Code: https://github.com/Stone-Red-Code/Arp-Scanner
Veify using: certutil -hashfile ARP-Scanner.exe sha512 Releases: https://github.com/Stone-Red-Code/ARP-Scanner/releases
Checksum: 551d33306839687dcb226e5290e4bb4d3d5786a9231799e0dba9f9c2a3b4711c346bf2aeb29f4fab54008b6b19c88ebb2649c6545343d41b11bed6b034407374
Verify using: certutil -hashfile C:\ProgramData\Chocolatey\lib\arp-scanner\ARP-Scanner.exe sha512
+16 -12
View File
@@ -1,25 +1,28 @@
name: arp-scanner # you probably want to 'snapcraft register <name>' name: arp-scanner # you probably want to 'snapcraft register <name>'
version: "0.2.0.20240103" # just for humans, typically '1.2+git' or '1.3.2' version: "1.1.0.20250627" # just for humans, typically '1.2+git' or '1.3.2'
grade: stable # must be 'stable' to release into candidate/stable channels grade: stable # must be 'stable' to release into candidate/stable channels
summary: A lightweight cross-platform IP scanner # 79 char long summary summary: A lightweight cross-platform IP scanner # 79 char long summary
description: | description: |
**Usage** **Usage**
1. Download the latest release 1. Download & install the latest release
- Chocolatey (Windows): `choco install arp-scanner --version=0.2.0` - Chocolatey (Windows): `choco install arp-scanner`
- Snapcraft (Linux): `snap install arp-scanner`\ - Snapcraft (Linux): `snap install arp-scanner`
and `snap connect arp-scanner:network-control` to give it permission to send arp requests - GitHub (Windows/Linux): releases (https://github.com/Stone-Red-Code/ARP-Scanner/releases)
- GitHub (Windows/Linux): https://github.com/Stone-Red-Code/ARP-Scanner/releases 2. Use the `arp-scanner` command
1. Start `arp-scanner` with the IP range it should scan as parameter (e.g. `192.168.1.0 - 192.168.1.255`) - `arp-scanner help` lists all available commands
- `arp-scanner scan` scans the network for devices
- `arp-scanner scan --help` lists all available options for the scan command
- `arp-scanner monitor` continuously scans the network for devices
- `arp-scanner monitor --help` lists all available options for the monitor command
> Note: This programm may not work on all linux distributions because the [ArpLookup](https://github.com/georg-jung/ArpLookup) library has some [limitations](https://github.com/georg-jung/ArpLookup#supported-platforms) > Note: This programm may not work on all linux distributions because the ArpLookup (https://github.com/georg-jung/ArpLookup) library has some limitations (https://github.com/georg-jung/ArpLookup#supported-platforms)
**Third party licenses** **Third party licenses**
- ArpLookup (https://github.com/georg-jung/ArpLookup) - MIT (https://github.com/georg-jung/ArpLookup/blob/master/LICENSE.txt) - ArpLookup (https://github.com/georg-jung/ArpLookup) - MIT (https://github.com/georg-jung/ArpLookup/blob/master/LICENSE.txt)
- IPAddressRange (https://github.com/jsakamoto/ipaddressrange) - MPL-2.0 (https://github.com/jsakamoto/ipaddressrange/blob/master/LICENSE) - IPAddressRange (https://github.com/jsakamoto/ipaddressrange) - MPL-2.0 (https://github.com/jsakamoto/ipaddressrange/blob/master/LICENSE)
- Humanizer (https://github.com/Humanizr/Humanizer) - MIT (https://github.com/Humanizr/Humanizer/blob/main/license.txt)
base: core22 # the base snap is the execution environment for this snap base: core22 # the base snap is the execution environment for this snap
architectures: architectures:
- build-on: amd64 - build-on: amd64
- build-on: arm64 - build-on: arm64
@@ -34,7 +37,7 @@ parts:
dotnet-self-contained-runtime-identifier: linux-x64 dotnet-self-contained-runtime-identifier: linux-x64
source: ./src/ARP-Scanner source: ./src/ARP-Scanner
build-packages: build-packages:
- dotnet-sdk-6.0 - dotnet-sdk-8.0
stage-packages: stage-packages:
- libicu70 - libicu70
@@ -42,4 +45,5 @@ apps:
arp-scanner: arp-scanner:
command: ARP-Scanner command: ARP-Scanner
plugs: plugs:
- network-control - network-observe
- home
+7 -9
View File
@@ -2,22 +2,20 @@
<PropertyGroup> <PropertyGroup>
<OutputType>Exe</OutputType> <OutputType>Exe</OutputType>
<TargetFramework>net6.0</TargetFramework> <TargetFramework>net8.0</TargetFramework>
<RootNamespace>ARP_Scanner</RootNamespace> <RootNamespace>ARP_Scanner</RootNamespace>
<ImplicitUsings>enable</ImplicitUsings> <ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable> <Nullable>enable</Nullable>
<IncludeSourceRevisionInInformationalVersion>false</IncludeSourceRevisionInInformationalVersion>
<Version>1.1.0.20250627</Version>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="ArpLookup" Version="2.0.3" /> <PackageReference Include="ArpLookup" Version="2.0.3" />
<PackageReference Include="IPAddressRange" Version="6.0.0" /> <PackageReference Include="CommandLineParser" Version="2.9.1" />
<PackageReference Include="Stone_Red-C-Sharp-Utilities" Version="1.0.3.1" /> <PackageReference Include="CuteUtils" Version="1.1.1" />
</ItemGroup> <PackageReference Include="Humanizer" Version="2.14.1" />
<PackageReference Include="IPAddressRange" Version="6.2.0" />
<ItemGroup>
<None Update="mac-vendors.csv">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
</ItemGroup> </ItemGroup>
</Project> </Project>
+53
View File
@@ -0,0 +1,53 @@
namespace ARP_Scanner;
internal class JsonResult
{
public IEnumerable<HostInformation> Hosts { get; set; } = [];
public IEnumerable<HostInformation> NewHosts { get; set; } = [];
public IEnumerable<HostInformation> RemovedHosts { get; set; } = [];
public static JsonResult Parse(IEnumerable<string[]> activeHosts, List<string[]> previousHosts)
{
JsonResult result = new JsonResult
{
Hosts = activeHosts.Select(HostInformation.Parse)
};
if (previousHosts.Count == 0)
{
return result;
}
result.NewHosts = result.Hosts.Where(host => !previousHosts.Exists(previousHost => previousHost[1] == host.Mac));
result.RemovedHosts = previousHosts.Where(previousHost => !result.Hosts.Any(host => host.Mac == previousHost[1])).Select(HostInformation.Parse);
return result;
}
public class HostInformation
{
public required string Ip { get; set; }
public required string Mac { get; set; }
public required string VendorName { get; set; }
public required string BlockType { get; set; }
public bool? Private { get; set; }
public required string LastUpdate { get; set; }
public static HostInformation Parse(string[] row)
{
bool success = bool.TryParse(row[4], out bool isPrivate);
return new HostInformation
{
Ip = row[0],
Mac = row[1],
VendorName = row[2],
BlockType = row[3],
Private = success ? isPrivate : null,
LastUpdate = row[5]
};
}
}
}
+7
View File
@@ -0,0 +1,7 @@
namespace ARP_Scanner;
internal class MacDatabase
{
public DateTime LastUpdate { get; set; }
public List<MacInformation> MacInformations { get; set; } = [];
}
+21
View File
@@ -0,0 +1,21 @@
using System.Text.Json.Serialization;
namespace ARP_Scanner;
internal class MacInformation
{
[JsonPropertyName("macPrefix")]
public required string MacPrefix { get; set; }
[JsonPropertyName("vendorName")]
public required string VendorName { get; set; }
[JsonPropertyName("private")]
public bool? Private { get; set; }
[JsonPropertyName("blockType")]
public required string BlockType { get; set; }
[JsonPropertyName("lastUpdate")]
public required string LastUpdate { get; set; }
}
+93 -29
View File
@@ -1,57 +1,121 @@
using System.Text.RegularExpressions; using CuteUtils.Misc;
using System.Net.Http.Json;
using System.Text.Json;
namespace ARP_Scanner; namespace ARP_Scanner;
internal class MacVendorLookup internal partial class MacVendorLookup
{ {
public readonly string[] header; private const string macLookupUrl = "https://maclookup.app/downloads/json-database/get-db";
private readonly string[][] fields; private readonly HttpClient httpClient = new HttpClient() { Timeout = TimeSpan.FromSeconds(20) };
public MacVendorLookup(string csvPath) private MacDatabase macDatabase = new MacDatabase();
public async Task Initialize(bool silent)
{ {
if (!File.Exists(csvPath)) if (macDatabase.MacInformations.Count != 0)
{ {
header = Array.Empty<string>(); // Already initialized
fields = Array.Empty<string[]>();
return; return;
} }
string[] lines = File.ReadAllLines(csvPath); string cachePath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), "macDatabase.json");
header = lines[0].Split(','); // Snap support
fields = lines.Skip(1).Select(l => Regex.Split(l, ",(?=(?:[^\"]*\"[^\"]*\")*(?![^\"]*\"))")).ToArray(); string? snapUserCommon = Environment.GetEnvironmentVariable("SNAP_USER_COMMON");
}
public string[] GetInformation(string macAdress) if (snapUserCommon is not null)
{
string[]? data = Array.Find(fields, f => macAdress.StartsWith(f[0]));
if (fields.Length == 0 || header.Length == 0)
{ {
return Array.Empty<string>(); cachePath = Path.Combine(snapUserCommon, "macDatabase.json");
} }
else if (data is null)
if (File.Exists(cachePath))
{ {
string[] result = new string[header.Length - 1]; try
for (int i = 0; i < result.Length; i++)
{ {
result[i] = "Unknown"; macDatabase = JsonSerializer.Deserialize<MacDatabase>(File.ReadAllText(cachePath)) ?? new MacDatabase();
}
catch (Exception ex)
{
if (!silent)
{
ConsoleExt.WriteLine($"Failed to read MAC database cache: {ex.Message}", ConsoleColor.Red);
}
}
}
// Update MAC database if it's older than a week
if (macDatabase.LastUpdate > DateTime.Now.AddDays(-7))
{
if (!silent)
{
ConsoleExt.WriteLine("Using cached MAC database...", ConsoleColor.DarkYellow);
}
return;
}
if (!silent)
{
ConsoleExt.WriteLine("Downloading MAC database from maclookup.app...", ConsoleColor.DarkYellow);
}
List<MacInformation>? newMacInformation = null;
try
{
newMacInformation = await httpClient.GetFromJsonAsync<List<MacInformation>>(macLookupUrl);
}
catch (Exception ex)
{
if (!silent)
{
ConsoleExt.WriteLine($"Failed to download MAC database: {ex.Message}", ConsoleColor.Red);
}
}
if (newMacInformation is null)
{
if (silent)
{
return;
}
if (macDatabase.MacInformations.Count != 0)
{
ConsoleExt.WriteLine("Failed to download MAC database, using cache...", ConsoleColor.DarkYellow);
}
else
{
ConsoleExt.WriteLine("Failed to download MAC database and no cache found, using empty database...", ConsoleColor.Red);
} }
return result;
} }
else else
{ {
List<string> result = new List<string>(); if (!silent)
for (int i = 1; i < header.Length; i++)
{ {
result.Add($"{data[i]}"); ConsoleExt.WriteLine("MAC database downloaded successfully!", ConsoleColor.Green);
} }
return result.ToArray();
_ = Directory.CreateDirectory(Path.GetDirectoryName(cachePath)!);
macDatabase.MacInformations = newMacInformation;
macDatabase.LastUpdate = DateTime.Now;
File.WriteAllText(cachePath, JsonSerializer.Serialize(macDatabase));
} }
} }
public string[] GetHeader() public MacInformation GetInformation(string macAddress)
{ {
return header!.Skip(1).ToArray(); macAddress = macAddress.Replace("-", ":")[..8].ToUpper();
return macDatabase.MacInformations.Find(m => m.MacPrefix == macAddress) ?? new MacInformation()
{
MacPrefix = macAddress,
VendorName = "Unknown",
BlockType = "Unknown",
Private = null,
LastUpdate = "Unknown"
};
} }
} }
+10
View File
@@ -0,0 +1,10 @@
using CommandLine;
namespace ARP_Scanner;
[Verb("monitor", HelpText = "Continuously monitor the specified IP range.")]
internal class MonitorOptions : ScanOptions
{
[Option('d', "delay", Required = false, Default = 60, HelpText = "The delay between each scan in seconds.")]
public int Delay { get; set; }
}
+289 -34
View File
@@ -1,94 +1,349 @@
using ArpLookup; using ArpLookup;
using CommandLine;
using CuteUtils.Misc;
using Humanizer;
using Humanizer.Localisation;
using NetTools; using NetTools;
using Stone_Red_Utilities.CollectionExtentions;
using Stone_Red_Utilities.ConsoleExtentions;
using System.Collections.Concurrent; using System.Collections.Concurrent;
using System.Diagnostics;
using System.Net; using System.Net;
using System.Net.NetworkInformation; using System.Net.NetworkInformation;
using System.Text.Json;
using System.Text.Json.Serialization;
namespace ARP_Scanner; namespace ARP_Scanner;
internal static class Program internal static class Program
{ {
private static async Task Main(string[] args) private static readonly MacVendorLookup macVendorLookup = new();
{ private static readonly List<string[]> previouslyActiveHosts = [];
MacVendorLookup macVendorLookup = new MacVendorLookup("mac-vendors.csv");
private static readonly JsonSerializerOptions jsonSerializerOptions = new()
{
WriteIndented = true,
DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull
};
private static async Task<int> Main(string[] args)
{
if (!Arp.IsSupported) if (!Arp.IsSupported)
{ {
ConsoleExt.WriteLine("ARP is not supported on this platform!", ConsoleColor.Red); ConsoleExt.WriteLine("ARP is not supported on this platform!", ConsoleColor.Red);
return; return 1;
} }
if (!IPAddressRange.TryParse(string.Join("", args), out IPAddressRange ipAddressRange)) return await Parser.Default.ParseArguments<ScanOptions, MonitorOptions>(args)
.MapResult(
(MonitorOptions monitorOptions) => StartMonitor(monitorOptions),
(ScanOptions scanOptions) => StartScan(scanOptions),
HandleParseError);
}
private static async Task<int> StartMonitor(MonitorOptions options)
{
ConsoleExt.WriteLine($"Monitoring IP range '{options.IpRange}' every {options.Delay} seconds...", ConsoleColor.DarkYellow);
while (true)
{
int exitCode = await StartScan(options);
if (exitCode != 0)
{
return exitCode;
}
Console.WriteLine();
Stopwatch stopwatch = Stopwatch.StartNew();
bool printed = false;
while (stopwatch.Elapsed < TimeSpan.FromSeconds(options.Delay))
{
TimeSpan remainingTime = TimeSpan.FromSeconds(options.Delay) - stopwatch.Elapsed;
Console.Write($"\rNext scan in: {remainingTime.Humanize(3, minUnit: TimeUnit.Second)} ");
await Task.Delay(1000);
printed = true;
}
if (printed)
{
Console.Write($"\r{new string(' ', Console.WindowWidth)}\r");
}
}
}
private static async Task<int> StartScan(ScanOptions options)
{
if (!IPAddressRange.TryParse(options.IpRange, out IPAddressRange ipAddressRange))
{ {
ConsoleExt.WriteLine("Invalid IP range!", ConsoleColor.Red); ConsoleExt.WriteLine("Invalid IP range!", ConsoleColor.Red);
return; return 2;
} }
await macVendorLookup.Initialize(options.Silent);
long ipAddressesCount = ipAddressRange.Count(); long ipAddressesCount = ipAddressRange.Count();
long processedIpAddressesCount = 0; long processedIpAddressesCount = 0;
int numberOfDigits = ipAddressesCount.ToString().Length; int numberOfDigits = ipAddressesCount.ToString().Length;
int exitCode = 0;
List<string> header = new List<string>() { "IP", "MAC" }; ConcurrentBag<string[]> activeHosts = [];
ConcurrentBag<string[]> activeHosts = new ConcurrentBag<string[]>();
header.AddRange(macVendorLookup.GetHeader()); // If you want to change the header, you also need to change the JsonResult class
List<string> header = [
"IP",
"MAC",
nameof(MacInformation.VendorName).Humanize(LetterCasing.Title),
nameof(MacInformation.BlockType).Humanize(LetterCasing.Title),
nameof(MacInformation.Private).Humanize(LetterCasing.Title),
nameof(MacInformation.LastUpdate).Humanize(LetterCasing.Title)];
ConsoleExt.WriteLine("Starting scan...", ConsoleColor.DarkYellow); if (!options.Silent)
{
ConsoleExt.WriteLine("Starting scan...", ConsoleColor.DarkYellow);
}
await Parallel.ForEachAsync(ipAddressRange, async (IPAddress ipAddress, CancellationToken _) => ParallelOptions parallelOptions = new()
{
MaxDegreeOfParallelism = options.Concurrency
};
await Parallel.ForEachAsync(ipAddressRange, parallelOptions, async (IPAddress ipAddress, CancellationToken _) =>
{ {
PhysicalAddress? mac = null; PhysicalAddress? mac = null;
bool fail = false; bool fail = false;
int retry = options.Retry;
try do
{ {
mac = await Arp.LookupAsync(ipAddress); try
} {
catch mac = await Arp.LookupAsync(ipAddress);
{ fail = false;
fail = true; }
catch (Exception ex)
{
if (!options.Silent)
{
ConsoleExt.WriteLine($"Failed to lookup MAC address for {ipAddress}: {ex.Message}", ConsoleColor.Red);
}
fail = true;
}
} }
while (retry-- > 0 && (mac is null || fail));
long localProcessedIpAddressesCount = Interlocked.Increment(ref processedIpAddressesCount); long localProcessedIpAddressesCount = Interlocked.Increment(ref processedIpAddressesCount);
if (mac is not null && Array.Exists(mac.GetAddressBytes(), b => b != 0)) if (mac is not null && Array.Exists(mac.GetAddressBytes(), b => b != 0))
{ {
string formattedMac = BitConverter.ToString(mac.GetAddressBytes()); string formattedMac = BitConverter.ToString(mac.GetAddressBytes());
List<string> info = new List<string> { ipAddress.ToString(), formattedMac }; MacInformation macInformation = macVendorLookup.GetInformation(formattedMac);
info.AddRange(macVendorLookup.GetInformation(formattedMac));
ConsoleExt.WriteLine($"Progress: {localProcessedIpAddressesCount.ToString().PadLeft(numberOfDigits)}/{ipAddressesCount} [{100d / ipAddressesCount * localProcessedIpAddressesCount,6:##0.00}%] | Active: {ipAddress}", ConsoleColor.Green); List<string> info = [ipAddress.ToString(), formattedMac, macInformation.VendorName, macInformation.BlockType, macInformation.Private?.ToString() ?? "Unknown", macInformation.LastUpdate];
activeHosts.Add(info.ToArray()); if (!options.Silent)
{
ConsoleExt.WriteLine($"Progress: {localProcessedIpAddressesCount.ToString().PadLeft(numberOfDigits)}/{ipAddressesCount} [{100d / ipAddressesCount * localProcessedIpAddressesCount,6:##0.00}%] | Active: {ipAddress}", ConsoleColor.Green);
}
activeHosts.Add([.. info]);
} }
else if (fail) else if (fail)
{ {
ConsoleExt.WriteLine($"Progress: {localProcessedIpAddressesCount.ToString().PadLeft(numberOfDigits)}/{ipAddressesCount} [{100d / ipAddressesCount * localProcessedIpAddressesCount,6:##0.00}%] | Failed: {ipAddress}", ConsoleColor.Red); if (!options.Silent)
{
ConsoleExt.WriteLine($"Progress: {localProcessedIpAddressesCount.ToString().PadLeft(numberOfDigits)}/{ipAddressesCount} [{100d / ipAddressesCount * localProcessedIpAddressesCount,6:##0.00}%] | Failed: {ipAddress}", ConsoleColor.Red);
}
} }
else else
{ {
ConsoleExt.WriteLine($"Progress: {localProcessedIpAddressesCount.ToString().PadLeft(numberOfDigits)}/{ipAddressesCount} [{100d / ipAddressesCount * localProcessedIpAddressesCount,6:##0.00}%] | Inactive: {ipAddress}", ConsoleColor.Red); if (!options.Silent)
{
ConsoleExt.WriteLine($"Progress: {localProcessedIpAddressesCount.ToString().PadLeft(numberOfDigits)}/{ipAddressesCount} [{100d / ipAddressesCount * localProcessedIpAddressesCount,6:##0.00}%] | Inactive: {ipAddress}", ConsoleColor.Red);
}
} }
}); });
if (!activeHosts.IsEmpty) if (previouslyActiveHosts.Count > 0 && !options.Silent)
{ {
Console.WriteLine(Environment.NewLine + "Active hosts:"); PrintDifference(activeHosts, header);
}
else if (!options.Silent)
{
PrintActiveHosts(activeHosts, header);
}
List<string[]>? activeHostsTable = activeHosts.ToList(); if (!options.Silent && (options.JsonPath is not null || options.CsvPath is not null))
{
Console.WriteLine();
}
activeHostsTable.Insert(0, header.ToArray()); if (options.JsonPath is not null)
{
string jsonPath = GetOutputPath(options.JsonPath, ".json");
activeHostsTable.ToArray().To2D().PrintTable(TableStyle.List); try
{
if (!string.IsNullOrWhiteSpace(Path.GetDirectoryName(jsonPath)))
{
_ = Directory.CreateDirectory(Path.GetDirectoryName(jsonPath)!);
}
ConsoleExt.WriteLine($"{Environment.NewLine}Found {activeHosts.Count} active hosts", ConsoleColor.Green); string json = JsonSerializer.Serialize(JsonResult.Parse([.. activeHosts], previouslyActiveHosts), jsonSerializerOptions);
File.WriteAllText(jsonPath, json);
if (!options.Silent)
{
ConsoleExt.WriteLine($"Saved JSON to '{jsonPath}'", ConsoleColor.Green);
}
}
catch (Exception ex)
{
if (!options.Silent)
{
ConsoleExt.WriteLine($"Failed to save JSON to '{jsonPath}': {ex.Message}", ConsoleColor.Red);
}
exitCode = 3;
}
}
if (options.CsvPath is not null)
{
string csvPath = GetOutputPath(options.CsvPath, ".csv");
List<string[]>? activeHostsTable = [.. activeHosts];
activeHostsTable.Insert(0, [.. header]);
try
{
if (!string.IsNullOrWhiteSpace(Path.GetDirectoryName(csvPath)))
{
_ = Directory.CreateDirectory(Path.GetDirectoryName(csvPath)!);
}
File.WriteAllLines(csvPath, activeHostsTable.Select(row => string.Join(",", row)));
if (!options.Silent)
{
ConsoleExt.WriteLine($"Saved CSV to '{csvPath}'", ConsoleColor.Green);
}
}
catch (Exception ex)
{
if (!options.Silent)
{
ConsoleExt.WriteLine($"Failed to save CSV to '{csvPath}': {ex.Message}", ConsoleColor.Red);
}
exitCode = 3;
}
}
previouslyActiveHosts.Clear();
foreach (string[] activeHost in activeHosts)
{
previouslyActiveHosts.Add(activeHost);
}
return exitCode;
}
private static Task<int> HandleParseError(IEnumerable<Error> errors)
{
if (errors.IsHelp() || errors.IsVersion())
{
return Task.FromResult(0);
} }
else else
{ {
ConsoleExt.WriteLine($"{Environment.NewLine}No active hosts found", ConsoleColor.Red); return Task.FromResult(1);
}
}
private static string GetOutputPath(string path, string extension)
{
string directory = Path.GetDirectoryName(path) ?? string.Empty;
string fileName = Path.GetFileNameWithoutExtension(path);
string fileExtension = Path.GetExtension(path);
fileExtension = string.IsNullOrEmpty(fileExtension) ? extension : fileExtension;
string dateTime = DateTime.Now.ToString("yyyy-MM-dd_HH-mm-ss");
string newPath = Path.Combine(directory, $"{fileName}_{dateTime}{fileExtension}");
int counter = 0;
while (File.Exists(newPath))
{
newPath = Path.Combine(directory, $"{fileName}_{dateTime}_{++counter}{fileExtension}");
}
return newPath;
}
private static void PrintActiveHosts(ConcurrentBag<string[]> activeHosts, List<string> header)
{
List<string[]>? activeHostsTable = [.. activeHosts];
activeHostsTable.Insert(0, [.. header]);
Console.WriteLine();
if (!activeHosts.IsEmpty)
{
Console.WriteLine($"Active hosts:");
activeHostsTable.ToArray().To2D().PrintTable(TableStyle.List);
ConsoleExt.WriteLine($"{Environment.NewLine}Found {"active host".ToQuantity(activeHosts.Count)}", ConsoleColor.Green);
}
else
{
ConsoleExt.WriteLine($"No active hosts found", ConsoleColor.Red);
}
}
private static void PrintDifference(ConcurrentBag<string[]> activeHosts, List<string> header)
{
List<string[]> newHosts = activeHosts.Where(activeHost => !previouslyActiveHosts.Exists(previousHost => previousHost[1] == activeHost[1])).ToList();
List<string[]> removedHosts = previouslyActiveHosts.Where(previousHost => !activeHosts.Any(activeHost => activeHost[1] == previousHost[1])).ToList();
List<string[]>? newHostsTable = [.. newHosts];
newHostsTable.Insert(0, [.. header]);
List<string[]>? removedHostsTable = [.. removedHosts];
removedHostsTable.Insert(0, [.. header]);
Console.WriteLine();
if (newHosts.Count > 0)
{
Console.WriteLine($"New hosts:");
newHostsTable.ToArray().To2D().PrintTable(TableStyle.List);
ConsoleExt.WriteLine($"{Environment.NewLine}Found {"active host".ToQuantity(activeHosts.Count)}", ConsoleColor.Green);
}
else
{
ConsoleExt.WriteLine($"No new hosts found", ConsoleColor.Blue);
}
Console.WriteLine();
if (removedHosts.Count > 0)
{
// Better term for removed host?
Console.WriteLine($"Previously active hosts:");
removedHostsTable.ToArray().To2D().PrintTable(TableStyle.List);
ConsoleExt.WriteLine($"{Environment.NewLine}Found {"previously active host".ToQuantity(removedHosts.Count)}", ConsoleColor.Red);
}
else
{
ConsoleExt.WriteLine($"No previously active hosts are currently inactive", ConsoleColor.Blue);
} }
} }
} }
@@ -2,7 +2,7 @@
"profiles": { "profiles": {
"ARP-Scanner": { "ARP-Scanner": {
"commandName": "Project", "commandName": "Project",
"commandLineArgs": "192.168.1.0 - 192.168.1.255" "commandLineArgs": "scan 192.168.1.239"
} }
} }
} }
+25
View File
@@ -0,0 +1,25 @@
using CommandLine;
namespace ARP_Scanner;
[Verb("scan", true, HelpText = "Scan the specified IP range.")]
internal class ScanOptions
{
[Value(0, Required = true, MetaName = "IP range", HelpText = "The IP range to scan.")]
public required string IpRange { get; set; }
[Option('s', "silent", Required = false, HelpText = "Don't print anything to the console.")]
public bool Silent { get; set; }
[Option('r', "retry", Required = false, Default = 0, HelpText = "The number of retries for each ARP request.")]
public int Retry { get; set; }
[Option('c', "concurrency", Required = false, Default = -1, HelpText = "The number of concurrent ARP requests.")]
public int Concurrency { get; set; }
[Option("json", Required = false, HelpText = "The path to the JSON file to save the results.")]
public string? JsonPath { get; set; }
[Option("csv", Required = false, HelpText = "The path to the CSV file to save the results.")]
public string? CsvPath { get; set; }
}
File diff suppressed because it is too large Load Diff