Add scanning
This commit is contained in:
parent
1b8e531f29
commit
bd81347c83
@ -10,4 +10,6 @@ add_executable(mcpinger main.cpp
|
||||
protocol_utils.cpp
|
||||
protocol_utils.h
|
||||
network_utils.cpp
|
||||
network_utils.h)
|
||||
network_utils.h
|
||||
utils.cpp
|
||||
utils.h)
|
||||
|
@ -1,8 +1,9 @@
|
||||
//cli_parser.cpp
|
||||
#include "cli_parser.h"
|
||||
#include <getopt.h>
|
||||
#include <iostream>
|
||||
|
||||
void config::parseCommandLine(int argc, char* argv[]) {
|
||||
void config::parseCommandLine(int argc, char *argv[]) {
|
||||
const struct option longOptions[] = {
|
||||
{"host", required_argument, nullptr, 'h'},
|
||||
{"port", required_argument, nullptr, 'p'},
|
||||
|
@ -1,3 +1,4 @@
|
||||
//cli_parser.h
|
||||
#ifndef MCPINGER_CLI_PARSER_H
|
||||
#define MCPINGER_CLI_PARSER_H
|
||||
|
||||
@ -11,8 +12,9 @@ struct config {
|
||||
int protocolVersion = -1;
|
||||
int nextState = 1;
|
||||
|
||||
void parseCommandLine(int argc, char* argv[]);
|
||||
static void printHelp() ;
|
||||
void parseCommandLine(int argc, char *argv[]);
|
||||
|
||||
static void printHelp();
|
||||
};
|
||||
|
||||
#endif // MCPINGER_CLI_PARSER_H
|
||||
|
59
main.cpp
59
main.cpp
@ -1,8 +1,59 @@
|
||||
#include "cli_parser.h"
|
||||
#include "network_utils.h"
|
||||
#include "utils.h"
|
||||
#include <thread>
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
#include <atomic>
|
||||
|
||||
int main(int argc, char* argv[]) {
|
||||
config config;
|
||||
config.parseCommandLine(argc, argv);
|
||||
return pingMinecraftServer(config.hostname, config.port, config.virtualHostname, config.virtualPort, config.protocolVersion, config.nextState);
|
||||
std::mutex fileMutex;
|
||||
|
||||
void scanIp(const std::string &ip, int port, const config &cfg, const std::string &filename) {
|
||||
std::string result = pingMinecraftServer(ip, port, cfg.virtualHostname, cfg.virtualPort, cfg.protocolVersion,
|
||||
cfg.nextState);
|
||||
if (!result.starts_with("ERROR")) {
|
||||
std::lock_guard<std::mutex> lock(fileMutex); // Locking here to manage concurrent file access
|
||||
std::ofstream file(filename, std::ios::app | std::ios::out);
|
||||
if (!file.is_open()) {
|
||||
std::cerr << "Failed to open file for writing: " << filename << std::endl;
|
||||
return;
|
||||
}
|
||||
// Write the scan result as a tab-separated line
|
||||
file << ip << '\t'
|
||||
<< port << '\t'
|
||||
<< cfg.virtualHostname << '\t'
|
||||
<< cfg.virtualPort << '\t'
|
||||
<< cfg.protocolVersion << '\t'
|
||||
<< cfg.nextState << '\t'
|
||||
<< result << '\n';
|
||||
file.close();
|
||||
}
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
config cfg;
|
||||
cfg.parseCommandLine(argc, argv);
|
||||
std::vector<std::string> ips = parseIps(cfg.hostname); // handles both CIDR and single IPs
|
||||
std::string outputFilename = "responsive_" + makePathSafe(cfg.hostname) + ".tsv";
|
||||
|
||||
// Create and write headers to the output file
|
||||
std::ofstream outfile(outputFilename, std::ios::out);
|
||||
if (!outfile.is_open()) {
|
||||
std::cerr << "Failed to open file for writing: " << outputFilename << std::endl;
|
||||
return 1;
|
||||
}
|
||||
outfile << "IP Address\tPort\tVirtual Hostname\tVirtual Port\tProtocol Version\tNext State\tStatus\n";
|
||||
outfile.close();
|
||||
|
||||
// Multithreaded scanning
|
||||
std::vector<std::thread> threads;
|
||||
for (const auto &ip: ips) {
|
||||
(void) threads.emplace_back(scanIp, ip, cfg.port, std::ref(cfg), outputFilename);
|
||||
usleep(10000);
|
||||
}
|
||||
for (auto &thread: threads) {
|
||||
thread.join();
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
@ -1,3 +1,5 @@
|
||||
//network_utils.cpp
|
||||
|
||||
#include "network_utils.h"
|
||||
#include <sys/socket.h>
|
||||
#include <netdb.h>
|
||||
@ -7,7 +9,7 @@
|
||||
#include <algorithm>
|
||||
#include "protocol_utils.h"
|
||||
|
||||
int sendData(int sockfd, const std::vector<unsigned char>& data) {
|
||||
int sendData(int sockfd, const std::vector<unsigned char> &data) {
|
||||
ssize_t sent = send(sockfd, data.data(), data.size(), 0);
|
||||
if (sent < 0) {
|
||||
perror("Error sending data");
|
||||
@ -16,7 +18,7 @@ int sendData(int sockfd, const std::vector<unsigned char>& data) {
|
||||
return SUCCESS;
|
||||
}
|
||||
|
||||
int receiveData(int sockfd, std::vector<unsigned char>& buffer) {
|
||||
int receiveData(int sockfd, std::vector<unsigned char> &buffer) {
|
||||
buffer.resize(1024);
|
||||
ssize_t bytesReceived = recv(sockfd, buffer.data(), buffer.size(), 0);
|
||||
if (bytesReceived < 0) {
|
||||
@ -27,33 +29,41 @@ int receiveData(int sockfd, std::vector<unsigned char>& buffer) {
|
||||
return SUCCESS;
|
||||
}
|
||||
|
||||
int pingMinecraftServer(const std::string& hostname, int port, const std::string& virtualHostname, int virtualPort, int protocolVersionIn, int nextStateIn) {
|
||||
std::string
|
||||
pingMinecraftServer(const std::string &hostname, int port, const std::string &virtualHostname, int virtualPort,
|
||||
int protocolVersionIn, int nextStateIn) {
|
||||
struct addrinfo hints{}, *res, *result;
|
||||
int sockfd, err;
|
||||
|
||||
(void)memset(&hints, 0, sizeof(hints));
|
||||
(void) memset(&hints, 0, sizeof(hints));
|
||||
hints.ai_family = AF_UNSPEC;
|
||||
hints.ai_socktype = SOCK_STREAM;
|
||||
|
||||
err = getaddrinfo(hostname.c_str(), std::to_string(port).c_str(), &hints, &result);
|
||||
if (err != 0) {
|
||||
std::cerr << "Error resolving hostname: " << gai_strerror(err) << std::endl;
|
||||
return ERROR_RESOLVING_HOSTNAME;
|
||||
return "ERROR_RESOLVING_HOSTNAME";
|
||||
}
|
||||
|
||||
for (res = result; res != nullptr; res = res->ai_next) {
|
||||
sockfd = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
|
||||
if (sockfd == -1) continue;
|
||||
|
||||
// Set timeouts
|
||||
struct timeval timeout;
|
||||
timeout.tv_sec = 5; // 5 seconds timeout
|
||||
timeout.tv_usec = 0;
|
||||
setsockopt(sockfd, SOL_SOCKET, SO_RCVTIMEO, (const char *) &timeout, sizeof(timeout));
|
||||
setsockopt(sockfd, SOL_SOCKET, SO_SNDTIMEO, (const char *) &timeout, sizeof(timeout));
|
||||
|
||||
if (connect(sockfd, res->ai_addr, res->ai_addrlen) != -1) break;
|
||||
|
||||
(void)close(sockfd);
|
||||
(void) close(sockfd);
|
||||
}
|
||||
|
||||
if (res == nullptr) {
|
||||
std::cerr << "Could not connect to any address" << std::endl;
|
||||
freeaddrinfo(result);
|
||||
return ERROR_CONNECTION_FAILED;
|
||||
return "ERROR_CONNECTION_FAILED";
|
||||
}
|
||||
|
||||
freeaddrinfo(result);
|
||||
@ -63,34 +73,35 @@ int pingMinecraftServer(const std::string& hostname, int port, const std::string
|
||||
packet.push_back(0x00U); // Packet ID for handshake
|
||||
std::vector<unsigned char> protocolVersion = encodeVarint(protocolVersionIn);
|
||||
std::vector<unsigned char> serverAddress = encodeString(virtualHostname);
|
||||
std::vector<unsigned char> serverPort = {static_cast<unsigned char>((virtualPort >> 8) & 0xFFU), static_cast<unsigned char>(virtualPort & 0xFFU) };
|
||||
std::vector<unsigned char> serverPort = {static_cast<unsigned char>((virtualPort >> 8) & 0xFF),
|
||||
static_cast<unsigned char>(virtualPort & 0xFFU)};
|
||||
std::vector<unsigned char> nextState = encodeVarint(nextStateIn);
|
||||
|
||||
(void)packet.insert(packet.end(), protocolVersion.begin(), protocolVersion.end());
|
||||
(void)packet.insert(packet.end(), serverAddress.begin(), serverAddress.end());
|
||||
(void)packet.insert(packet.end(), serverPort.begin(), serverPort.end());
|
||||
(void)packet.insert(packet.end(), nextState.begin(), nextState.end());
|
||||
(void) packet.insert(packet.end(), protocolVersion.begin(), protocolVersion.end());
|
||||
(void) packet.insert(packet.end(), serverAddress.begin(), serverAddress.end());
|
||||
(void) packet.insert(packet.end(), serverPort.begin(), serverPort.end());
|
||||
(void) packet.insert(packet.end(), nextState.begin(), nextState.end());
|
||||
|
||||
std::vector<unsigned char> handshakeLength = encodeVarint(packet.size());
|
||||
(void)packet.insert(packet.begin(), handshakeLength.begin(), handshakeLength.end());
|
||||
(void) packet.insert(packet.begin(), handshakeLength.begin(), handshakeLength.end());
|
||||
|
||||
if (sendData(sockfd, packet) != SUCCESS) {
|
||||
(void)close(sockfd);
|
||||
return ERROR_SENDING_DATA;
|
||||
(void) close(sockfd);
|
||||
return "ERROR_SENDING_DATA";
|
||||
}
|
||||
|
||||
// Status request
|
||||
std::vector<unsigned char> statusRequest = {0x01U, 0x00U}; // Packet length and Packet ID for status request
|
||||
if (sendData(sockfd, statusRequest) != SUCCESS) {
|
||||
(void)close(sockfd);
|
||||
return ERROR_SENDING_DATA;
|
||||
(void) close(sockfd);
|
||||
return "ERROR_SENDING_DATA";
|
||||
}
|
||||
|
||||
// Read status response
|
||||
std::vector<unsigned char> response;
|
||||
if (receiveData(sockfd, response) != SUCCESS) {
|
||||
(void)close(sockfd);
|
||||
return ERROR_RECEIVING_DATA;
|
||||
(void) close(sockfd);
|
||||
return "ERROR_RECEIVING_DATA";
|
||||
}
|
||||
|
||||
// Extract JSON string (assuming it starts from the position of the first '{' found in the response)
|
||||
@ -98,10 +109,10 @@ int pingMinecraftServer(const std::string& hostname, int port, const std::string
|
||||
if (pos != response.end()) {
|
||||
std::string json(pos, response.end());
|
||||
std::cout << json << std::endl;
|
||||
(void) close(sockfd);
|
||||
return json;
|
||||
} else {
|
||||
std::cerr << "Failed to find JSON response in server data." << std::endl;
|
||||
(void) close(sockfd);
|
||||
return "ERROR_JSON";
|
||||
}
|
||||
|
||||
(void)close(sockfd);
|
||||
return SUCCESS;
|
||||
}
|
||||
|
@ -1,3 +1,4 @@
|
||||
//network_utils.h
|
||||
#ifndef MCPINGER_NETWORK_UTILS_H
|
||||
#define MCPINGER_NETWORK_UTILS_H
|
||||
|
||||
@ -10,8 +11,12 @@
|
||||
#define ERROR_SENDING_DATA (-4)
|
||||
#define ERROR_RECEIVING_DATA (-5)
|
||||
|
||||
int sendData(int sockfd, const std::vector<unsigned char>& data);
|
||||
int receiveData(int sockfd, std::vector<unsigned char>& buffer);
|
||||
int pingMinecraftServer(const std::string& hostname, int port, const std::string& virtualHostname, int virtualPort, int protocolVersion, int nextState);
|
||||
int sendData(int sockfd, const std::vector<unsigned char> &data);
|
||||
|
||||
int receiveData(int sockfd, std::vector<unsigned char> &buffer);
|
||||
|
||||
std::string
|
||||
pingMinecraftServer(const std::string &hostname, int port, const std::string &virtualHostname, int virtualPort,
|
||||
int protocolVersionIn, int nextStateIn);
|
||||
|
||||
#endif // MCPINGER_NETWORK_UTILS_H
|
||||
|
@ -1,3 +1,4 @@
|
||||
//protocol_utils.cpp
|
||||
#include "protocol_utils.h"
|
||||
|
||||
std::vector<unsigned char> encodeVarint(int value) {
|
||||
@ -14,8 +15,8 @@ std::vector<unsigned char> encodeVarint(int value) {
|
||||
return varint;
|
||||
}
|
||||
|
||||
std::vector<unsigned char> encodeString(const std::string& str) {
|
||||
std::vector<unsigned char> encodeString(const std::string &str) {
|
||||
std::vector<unsigned char> data = encodeVarint(str.length());
|
||||
(void)data.insert(data.end(), str.begin(), str.end());
|
||||
(void) data.insert(data.end(), str.begin(), str.end());
|
||||
return data;
|
||||
}
|
||||
|
@ -1,3 +1,4 @@
|
||||
//protocol_utils.h
|
||||
#ifndef MCPINGER_PROTOCOL_UTILS_H
|
||||
#define MCPINGER_PROTOCOL_UTILS_H
|
||||
|
||||
@ -5,6 +6,7 @@
|
||||
#include <string>
|
||||
|
||||
std::vector<unsigned char> encodeVarint(int value);
|
||||
std::vector<unsigned char> encodeString(const std::string& str);
|
||||
|
||||
std::vector<unsigned char> encodeString(const std::string &str);
|
||||
|
||||
#endif // MCPINGER_PROTOCOL_UTILS_H
|
||||
|
64
utils.cpp
Normal file
64
utils.cpp
Normal file
@ -0,0 +1,64 @@
|
||||
#include "utils.h"
|
||||
#include <arpa/inet.h>
|
||||
#include <regex>
|
||||
#include <unordered_set>
|
||||
|
||||
std::vector<std::string> cidrToIps(const std::string &cidr) {
|
||||
std::vector<std::string> result;
|
||||
// Buffer to hold the IP address in text form
|
||||
int ip[4], bits; // Array to hold each byte of the IP, and the bits for subnet mask
|
||||
|
||||
// Parse CIDR notation
|
||||
(void) sscanf(cidr.c_str(), "%d.%d.%d.%d/%d", &ip[0], &ip[1], &ip[2], &ip[3], &bits);
|
||||
|
||||
// Convert parsed IP to struct in_addr
|
||||
struct in_addr ipAddr{};
|
||||
ipAddr.s_addr = htonl((ip[0] << 24) | (ip[1] << 16) | (ip[2] << 8) | ip[3]);
|
||||
|
||||
// Calculate network address and number of hosts
|
||||
uint32_t ipNum = ntohl(ipAddr.s_addr);
|
||||
uint32_t numHosts = (1 << (32 - bits)) - 2; // Host count excludes network and broadcast addresses
|
||||
uint32_t network = ipNum & (~((1 << (32 - bits)) - 1)); // Network address
|
||||
|
||||
for (uint32_t i = 1; i <= numHosts; i++) {
|
||||
struct in_addr addr{};
|
||||
addr.s_addr = htonl(network + i);
|
||||
char ipStr[INET_ADDRSTRLEN];
|
||||
(void) inet_ntop(AF_INET, &addr, ipStr, INET_ADDRSTRLEN);
|
||||
(void) result.emplace_back(ipStr);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
// Utility function to check and expand CIDR or single IP
|
||||
std::vector<std::string> parseIps(const std::string &input) {
|
||||
std::vector<std::string> ips;
|
||||
std::regex cidrPattern(R"(\b\d{1,3}(?:\.\d{1,3}){3}\/\d{1,2}\b)"); // Simple regex for CIDR validation
|
||||
if (std::regex_match(input, cidrPattern)) {
|
||||
// It's a CIDR
|
||||
ips = cidrToIps(input);
|
||||
} else {
|
||||
// It's a single IP
|
||||
ips.push_back(input);
|
||||
}
|
||||
return ips;
|
||||
}
|
||||
|
||||
std::string makePathSafe(const std::string& input) {
|
||||
// Define a set of characters that are not allowed in file names
|
||||
const std::unordered_set<char> unsafeChars = {
|
||||
'/', '\\', ':', '*', '?', '"', '<', '>', '|', '.'
|
||||
};
|
||||
|
||||
// Start with the original string
|
||||
std::string result = input;
|
||||
|
||||
// Replace each unsafe character with an underscore
|
||||
for (char& c : result) {
|
||||
if (unsafeChars.count(c)) {
|
||||
c = '_';
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
13
utils.h
Normal file
13
utils.h
Normal file
@ -0,0 +1,13 @@
|
||||
#ifndef MCPINGER_UTILS_H
|
||||
#define MCPINGER_UTILS_H
|
||||
|
||||
#include <vector>
|
||||
#include <string>
|
||||
|
||||
std::vector<std::string> cidrToIps(const std::string &cidr);
|
||||
|
||||
std::vector<std::string> parseIps(const std::string &input);
|
||||
|
||||
std::string makePathSafe(const std::string& input);
|
||||
|
||||
#endif // MCPINGER_UTILS_H
|
Loading…
Reference in New Issue
Block a user