Skip to content

Commit 9da6513

Browse files
authored
Merge pull request #2 from aymaneelmaini/tests
Write Unit tests using criterion and introduce some small code changes
2 parents 7e45c15 + 6194210 commit 9da6513

8 files changed

Lines changed: 483 additions & 108 deletions

File tree

CMakeLists.txt

Lines changed: 27 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,38 @@
11
cmake_minimum_required(VERSION 3.10)
22
project(XP-SCAN)
3-
43
set(CMAKE_CXX_STANDARD 17)
54

5+
set(SOURCES src/Scanner.cpp src/Exporter.cpp src/Printer.cpp)
6+
set(TEST_SOURCES tests/ExporterTest.cpp tests/ScannerTest.cpp tests/PrinterTest.cpp)
7+
8+
# Fetch Criterion (Testing library)
9+
include(FetchContent)
10+
FetchContent_Declare(criterion
11+
GIT_REPOSITORY https://github.com/Snaipe/Criterion.git
12+
GIT_TAG v2.4.2
13+
GIT_SHALLOW TRUE
14+
GIT_PROGRESS TRUE
15+
)
16+
set(FETCHCONTENT_QUIET FALSE)
17+
FetchContent_MakeAvailable(criterion)
18+
619
# Look for threads
720
find_package(Threads REQUIRED)
821

922
# Include directories
1023
include_directories(include)
1124

12-
# Source files
13-
add_executable(xpscan src/main.cpp src/Scanner.cpp src/Exporter.cpp src/Printer.cpp)
14-
15-
# Link pthread
25+
# Actual executable
26+
add_executable(xpscan src/main.cpp ${SOURCES})
1627
target_link_libraries(xpscan PRIVATE Threads::Threads)
28+
29+
# Tests runner
30+
add_executable(xpscan_tests EXCLUDE_FROM_ALL ${TEST_SOURCES} ${SOURCES})
31+
target_link_libraries(xpscan_tests PRIVATE Threads::Threads criterion)
32+
33+
# Test target
34+
add_custom_target(test
35+
COMMAND ${CMAKE_MAKE_PROGRAM} xpscan_tests
36+
COMMAND ./xpscan_tests
37+
WORKING_DIRECTORY ${CMAKE_BINARY_DIR}
38+
)

include/Common.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,4 +10,11 @@ struct PortResult
1010
string service;
1111
};
1212

13+
#define START_PORT 1
14+
#define END_PORT 65535
15+
#define RED "\033[31m"
16+
#define BLUE "\033[36m"
17+
#define PINK "\033[33m"
18+
#define NC "\033[0m"
19+
1320
#endif // !COMMON_H

include/Exporter.h

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,14 @@
55

66
#include "Common.h"
77

8-
class Exporter
9-
{
10-
public:
11-
static void saveToJson(const std::string& ip, const std::vector<PortResult>& results);
12-
static void saveToText(const std::string& ip, const std::vector<PortResult>& results);
8+
class Exporter {
9+
public:
10+
Exporter(const std::string& config_path = "");
11+
void saveToJson(const std::string& ip, const std::vector<PortResult>& results);
12+
void saveToText(const std::string& ip, const std::vector<PortResult>& results);
13+
std::string getExportPath();
14+
15+
private:
16+
std::string config_path_;
1317
};
1418
#endif

src/Exporter.cpp

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,13 @@
88
#include <iostream>
99
#include <string>
1010

11-
std::string get_export_path() {
12-
std::string config_file = std::string(getenv("HOME")) + "/.config/.xpscan/path.conf";
11+
Exporter::Exporter(const std::string& config_path) : config_path_(config_path) {}
12+
13+
std::string Exporter::getExportPath() {
14+
std::string config_file = config_path_.empty()
15+
? std::string(getenv("HOME")) + "/.config/.xpscan/path.conf"
16+
: config_path_;
17+
1318
std::ifstream file(config_file);
1419
if (!file.is_open()) {
1520
std::cout << "[x] Error opening config file...";
@@ -39,10 +44,9 @@ std::string get_current_time_str() {
3944

4045
void Exporter::saveToJson(const std::string& ip, const std::vector<PortResult>& results) {
4146
if (results.empty()) {
42-
std::cout << "\033[34m[x] No results found, skipping export." << "\033[0m" << std::endl;
4347
return;
4448
}
45-
std::string dir = get_export_path(); // Uses the wordexp logic from earlier
49+
std::string dir = getExportPath();
4650
std::string full_path = dir + ip + "_" + get_timestamp() + ".json";
4751

4852
std::ofstream file(full_path);
@@ -72,7 +76,7 @@ void Exporter::saveToText(const std::string& ip, const std::vector<PortResult>&
7276
std::cout << "\033[34m[x] No results found, skipping export." << "\033[0m" << std::endl;
7377
return;
7478
}
75-
std::string dir = get_export_path();
79+
std::string dir = getExportPath();
7680
std::string full_path = dir + ip + "_" + get_timestamp() + ".txt";
7781

7882
std::ofstream file(full_path);

src/main.cpp

Lines changed: 79 additions & 93 deletions
Original file line numberDiff line numberDiff line change
@@ -5,107 +5,93 @@
55
#include <regex>
66
#include <string>
77

8+
#include "../include/Common.h"
89
#include "../include/Exporter.h"
910
#include "../include/Printer.h"
1011
#include "../include/Scanner.h"
1112

12-
#define START_PORT 1
13-
#define END_PORT 65535
14-
#define RED "\033[31m"
15-
#define BLUE "\033[36m"
16-
#define PINK "\033[33m"
17-
#define NC "\033[0m"
18-
1913
using namespace std;
2014

2115
#define THREAD_NUM 200
2216

23-
void print_usage()
24-
{
25-
std::cout << PINK << "Usage: xpscan <IP> [options]\n"
26-
<< NC
27-
<< BLUE << "Options:\n"
28-
<< " --help Print this message\n"
29-
<< " --json Export results to [IP].json\n"
30-
<< " --txt Export results to [IP].txt\n"
31-
<< " --full Scan all 65535 ports\n"
32-
<< NC;
17+
void print_usage() {
18+
std::cout << PINK << "Usage: xpscan <IP> [options]\n"
19+
<< NC << BLUE << "Options:\n"
20+
<< " --help Print this message\n"
21+
<< " --json Export results to [IP].json\n"
22+
<< " --txt Export results to [IP].txt\n"
23+
<< " --full Scan all 65535 ports\n"
24+
<< NC;
3325
}
3426

35-
int main(int argc, char* argv[])
36-
{
37-
// check if at least one arg is passed
38-
if (argc < 2 || std::strcmp(argv[1], "--help") == 0)
39-
{
40-
print_usage();
41-
return 1;
42-
}
43-
44-
// Validate the ip
45-
std::regex ip_regex(R"(^(\d{1,3}\.){3}\d{1,3}$)");
46-
if (!std::regex_match(argv[1], ip_regex))
47-
{
48-
std::cerr << RED << "[x] Invalid IP address format." << NC << endl;
49-
return 1;
50-
}
51-
52-
// Instatiate user input struct
53-
struct UserInput input{};
54-
55-
// Parse args
56-
std::vector<std::string> args(argv + 2, argv + argc);
57-
58-
// Set IP attribute to the second arg passed
59-
input.ip = argv[1];
60-
61-
// Parse Flags
62-
bool exportJson = (find(args.begin(), args.end(), "--json") != args.end());
63-
bool exportTxt = (find(args.begin(), args.end(), "--txt") != args.end());
64-
bool fullScan = (find(args.begin(), args.end(), "--full") != args.end());
65-
66-
Printer::printHeader();
67-
68-
// Check for fullscan flag
69-
if (fullScan)
70-
{
71-
input.start_port = START_PORT;
72-
input.end_port = END_PORT;
73-
}
74-
else
75-
{
76-
std::cout << BLUE << "Enter start port:" << NC;
77-
std::cin >> input.start_port;
78-
std::cout << BLUE << "Enter end port:" << NC;
79-
std::cin >> input.end_port;
80-
}
81-
82-
// Instatiate the scanner class with params
83-
Scanner scanner(input.ip, THREAD_NUM);
84-
85-
// Record start time
86-
auto start = std::chrono::steady_clock::now();
87-
88-
// Proceed with the scanning logic
89-
scanner.scanRange(input.start_port, input.end_port);
90-
91-
// Record end time
92-
auto end = std::chrono::steady_clock::now();
93-
94-
// Calculate elapsed time
95-
std::chrono::duration<double> diff = end - start;
96-
std::cout << "\n"
97-
<< BLUE << "[!] Scan finished in " << diff.count() << "s" << NC << std::endl;
98-
99-
// Check for export options and proceed with it
100-
if (exportJson)
101-
Exporter::saveToJson(input.ip, scanner.getResults());
102-
if (exportTxt)
103-
Exporter::saveToText(input.ip, scanner.getResults());
104-
105-
if (!exportJson && !exportTxt)
106-
{
107-
std::cout << PINK << "[!] No export flags set. Use --json or --txt to save results." << NC << std::endl;
108-
}
109-
110-
return 0;
27+
int main(int argc, char *argv[]) {
28+
// check if at least one arg is passed
29+
if (argc < 2 || std::strcmp(argv[1], "--help") == 0) {
30+
print_usage();
31+
return 1;
32+
}
33+
34+
// Validate the ip
35+
std::regex ip_regex(R"(^(\d{1,3}\.){3}\d{1,3}$)");
36+
if (!std::regex_match(argv[1], ip_regex)) {
37+
std::cerr << RED << "[x] Invalid IP address format." << NC << endl;
38+
return 1;
39+
}
40+
41+
// Instatiate user input struct
42+
struct UserInput input{};
43+
44+
// Parse args
45+
std::vector<std::string> args(argv + 2, argv + argc);
46+
47+
// Set IP attribute to the second arg passed
48+
input.ip = argv[1];
49+
50+
// Parse Flags
51+
bool exportJson = (find(args.begin(), args.end(), "--json") != args.end());
52+
bool exportTxt = (find(args.begin(), args.end(), "--txt") != args.end());
53+
bool fullScan = (find(args.begin(), args.end(), "--full") != args.end());
54+
55+
Printer::printHeader();
56+
57+
// Check for fullscan flag
58+
if (fullScan) {
59+
input.start_port = START_PORT;
60+
input.end_port = END_PORT;
61+
} else {
62+
std::cout << BLUE << "Enter start port:" << NC;
63+
std::cin >> input.start_port;
64+
std::cout << BLUE << "Enter end port:" << NC;
65+
std::cin >> input.end_port;
66+
}
67+
68+
// Instatiate the scanner class with params
69+
Scanner scanner(input.ip, THREAD_NUM);
70+
71+
// Record start time
72+
auto start = std::chrono::steady_clock::now();
73+
74+
// Proceed with the scanning logic
75+
scanner.scanRange(input.start_port, input.end_port);
76+
77+
// Record end time
78+
auto end = std::chrono::steady_clock::now();
79+
80+
// Calculate elapsed time
81+
std::chrono::duration<double> diff = end - start;
82+
std::cout << "\n"
83+
<< BLUE << "[!] Scan finished in " << diff.count() << "s" << NC
84+
<< std::endl;
85+
86+
Exporter exporter; // Uses default config path because i extracted the config file path as a parameter
87+
if (exportJson) exporter.saveToJson(input.ip, scanner.getResults());
88+
if (exportTxt) exporter.saveToText(input.ip, scanner.getResults());
89+
90+
if (!exportJson && !exportTxt) {
91+
std::cout << PINK
92+
<< "[!] No export flags set. Use --json or --txt to save results."
93+
<< NC << std::endl;
94+
}
95+
96+
return 0;
11197
}

0 commit comments

Comments
 (0)