Init
This commit is contained in:
commit
1b8e531f29
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
cmake-build-debug
|
9
.idea/.gitignore
vendored
Normal file
9
.idea/.gitignore
vendored
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
# Default ignored files
|
||||||
|
/shelf/
|
||||||
|
/workspace.xml
|
||||||
|
# Editor-based HTTP Client requests
|
||||||
|
/httpRequests/
|
||||||
|
# Datasource local storage ignored files
|
||||||
|
/dataSources/
|
||||||
|
/dataSources.local.xml
|
||||||
|
cmake-build-debug
|
7
.idea/discord.xml
Normal file
7
.idea/discord.xml
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="DiscordProjectSettings">
|
||||||
|
<option name="show" value="PROJECT_FILES" />
|
||||||
|
<option name="description" value="" />
|
||||||
|
</component>
|
||||||
|
</project>
|
7
.idea/inspectionProfiles/Project_Default.xml
Normal file
7
.idea/inspectionProfiles/Project_Default.xml
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
<component name="InspectionProjectProfileManager">
|
||||||
|
<profile version="1.0">
|
||||||
|
<option name="myName" value="Project Default" />
|
||||||
|
<inspection_tool class="Misra" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||||
|
<inspection_tool class="OCInconsistentNaming" enabled="true" level="WEAK WARNING" enabled_by_default="true" />
|
||||||
|
</profile>
|
||||||
|
</component>
|
2
.idea/mcpinger.iml
Normal file
2
.idea/mcpinger.iml
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<module classpath="CMake" type="CPP_MODULE" version="4" />
|
7
.idea/misc.xml
Normal file
7
.idea/misc.xml
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="CMakePythonSetting">
|
||||||
|
<option name="pythonIntegrationState" value="YES" />
|
||||||
|
</component>
|
||||||
|
<component name="CMakeWorkspace" PROJECT_DIR="$PROJECT_DIR$" />
|
||||||
|
</project>
|
8
.idea/modules.xml
Normal file
8
.idea/modules.xml
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="ProjectModuleManager">
|
||||||
|
<modules>
|
||||||
|
<module fileurl="file://$PROJECT_DIR$/.idea/mcpinger.iml" filepath="$PROJECT_DIR$/.idea/mcpinger.iml" />
|
||||||
|
</modules>
|
||||||
|
</component>
|
||||||
|
</project>
|
6
.idea/vcs.xml
Normal file
6
.idea/vcs.xml
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="VcsDirectoryMappings">
|
||||||
|
<mapping directory="$PROJECT_DIR$" vcs="Git" />
|
||||||
|
</component>
|
||||||
|
</project>
|
13
CMakeLists.txt
Normal file
13
CMakeLists.txt
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
cmake_minimum_required(VERSION 3.29)
|
||||||
|
project(mcpinger)
|
||||||
|
|
||||||
|
set(CMAKE_CXX_STANDARD 26)
|
||||||
|
|
||||||
|
add_executable(mcpinger main.cpp
|
||||||
|
main.cpp
|
||||||
|
cli_parser.cpp
|
||||||
|
cli_parser.h
|
||||||
|
protocol_utils.cpp
|
||||||
|
protocol_utils.h
|
||||||
|
network_utils.cpp
|
||||||
|
network_utils.h)
|
68
cli_parser.cpp
Normal file
68
cli_parser.cpp
Normal file
@ -0,0 +1,68 @@
|
|||||||
|
#include "cli_parser.h"
|
||||||
|
#include <getopt.h>
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
|
void config::parseCommandLine(int argc, char* argv[]) {
|
||||||
|
const struct option longOptions[] = {
|
||||||
|
{"host", required_argument, nullptr, 'h'},
|
||||||
|
{"port", required_argument, nullptr, 'p'},
|
||||||
|
{"vhost", required_argument, nullptr, 'v'},
|
||||||
|
{"vport", required_argument, nullptr, 'b'},
|
||||||
|
{"version", required_argument, nullptr, 'r'},
|
||||||
|
{"state", required_argument, nullptr, 's'},
|
||||||
|
{"help", no_argument, nullptr, 0},
|
||||||
|
{nullptr, 0, nullptr, 0}
|
||||||
|
};
|
||||||
|
|
||||||
|
int opt;
|
||||||
|
while ((opt = getopt_long(argc, argv, "h:p:v:b:r:s:", longOptions, nullptr)) != -1) {
|
||||||
|
switch (opt) {
|
||||||
|
case 'h':
|
||||||
|
hostname = optarg;
|
||||||
|
break;
|
||||||
|
case 'p':
|
||||||
|
port = std::stoi(optarg);
|
||||||
|
break;
|
||||||
|
case 'v':
|
||||||
|
virtualHostname = optarg;
|
||||||
|
break;
|
||||||
|
case 'b':
|
||||||
|
virtualPort = std::stoi(optarg);
|
||||||
|
break;
|
||||||
|
case 'r':
|
||||||
|
protocolVersion = std::stoi(optarg);
|
||||||
|
break;
|
||||||
|
case 's':
|
||||||
|
nextState = std::stoi(optarg);
|
||||||
|
break;
|
||||||
|
case 0: // --help
|
||||||
|
printHelp();
|
||||||
|
exit(0);
|
||||||
|
default: // '?'
|
||||||
|
printHelp();
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (hostname.empty()) {
|
||||||
|
std::cerr << "Error: Hostname is required.\n";
|
||||||
|
printHelp();
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (virtualHostname.empty()) {
|
||||||
|
virtualHostname = hostname; // Default to real hostname
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void config::printHelp() {
|
||||||
|
std::cout << "Usage: mcpinger [options]\n"
|
||||||
|
<< "Options:\n"
|
||||||
|
<< " --host <hostname> Real server hostname (required)\n"
|
||||||
|
<< " --port <port> Real server port (default 25565)\n"
|
||||||
|
<< " --vhost <virtual host> Virtual hostname (default is same as host)\n"
|
||||||
|
<< " --vport <virtual port> Virtual port (default is same as port)\n"
|
||||||
|
<< " --version <protocol> Protocol version (default -1)\n"
|
||||||
|
<< " --state <next state> Next state (default 1, status request)\n"
|
||||||
|
<< " --help Show this help message\n";
|
||||||
|
}
|
18
cli_parser.h
Normal file
18
cli_parser.h
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
#ifndef MCPINGER_CLI_PARSER_H
|
||||||
|
#define MCPINGER_CLI_PARSER_H
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
struct config {
|
||||||
|
std::string hostname;
|
||||||
|
std::string virtualHostname;
|
||||||
|
int port = 25565;
|
||||||
|
int virtualPort = 25565;
|
||||||
|
int protocolVersion = -1;
|
||||||
|
int nextState = 1;
|
||||||
|
|
||||||
|
void parseCommandLine(int argc, char* argv[]);
|
||||||
|
static void printHelp() ;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // MCPINGER_CLI_PARSER_H
|
8
main.cpp
Normal file
8
main.cpp
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
#include "cli_parser.h"
|
||||||
|
#include "network_utils.h"
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
107
network_utils.cpp
Normal file
107
network_utils.cpp
Normal file
@ -0,0 +1,107 @@
|
|||||||
|
#include "network_utils.h"
|
||||||
|
#include <sys/socket.h>
|
||||||
|
#include <netdb.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <iostream>
|
||||||
|
#include <cstring>
|
||||||
|
#include <algorithm>
|
||||||
|
#include "protocol_utils.h"
|
||||||
|
|
||||||
|
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");
|
||||||
|
return ERROR_SENDING_DATA;
|
||||||
|
}
|
||||||
|
return SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
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) {
|
||||||
|
perror("Error receiving data");
|
||||||
|
return ERROR_RECEIVING_DATA;
|
||||||
|
}
|
||||||
|
buffer.resize(bytesReceived);
|
||||||
|
return SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
int 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));
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (res = result; res != nullptr; res = res->ai_next) {
|
||||||
|
sockfd = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
|
||||||
|
if (sockfd == -1) continue;
|
||||||
|
|
||||||
|
if (connect(sockfd, res->ai_addr, res->ai_addrlen) != -1) break;
|
||||||
|
|
||||||
|
(void)close(sockfd);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (res == nullptr) {
|
||||||
|
std::cerr << "Could not connect to any address" << std::endl;
|
||||||
|
freeaddrinfo(result);
|
||||||
|
return ERROR_CONNECTION_FAILED;
|
||||||
|
}
|
||||||
|
|
||||||
|
freeaddrinfo(result);
|
||||||
|
|
||||||
|
// Handshake
|
||||||
|
std::vector<unsigned char> packet;
|
||||||
|
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> 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());
|
||||||
|
|
||||||
|
std::vector<unsigned char> handshakeLength = encodeVarint(packet.size());
|
||||||
|
(void)packet.insert(packet.begin(), handshakeLength.begin(), handshakeLength.end());
|
||||||
|
|
||||||
|
if (sendData(sockfd, packet) != SUCCESS) {
|
||||||
|
(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;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read status response
|
||||||
|
std::vector<unsigned char> response;
|
||||||
|
if (receiveData(sockfd, response) != SUCCESS) {
|
||||||
|
(void)close(sockfd);
|
||||||
|
return ERROR_RECEIVING_DATA;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Extract JSON string (assuming it starts from the position of the first '{' found in the response)
|
||||||
|
auto pos = std::find(response.begin(), response.end(), '{');
|
||||||
|
if (pos != response.end()) {
|
||||||
|
std::string json(pos, response.end());
|
||||||
|
std::cout << json << std::endl;
|
||||||
|
} else {
|
||||||
|
std::cerr << "Failed to find JSON response in server data." << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
(void)close(sockfd);
|
||||||
|
return SUCCESS;
|
||||||
|
}
|
17
network_utils.h
Normal file
17
network_utils.h
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
#ifndef MCPINGER_NETWORK_UTILS_H
|
||||||
|
#define MCPINGER_NETWORK_UTILS_H
|
||||||
|
|
||||||
|
#include <vector>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
#define SUCCESS 0
|
||||||
|
#define ERROR_RESOLVING_HOSTNAME (-2)
|
||||||
|
#define ERROR_CONNECTION_FAILED (-3)
|
||||||
|
#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);
|
||||||
|
|
||||||
|
#endif // MCPINGER_NETWORK_UTILS_H
|
21
protocol_utils.cpp
Normal file
21
protocol_utils.cpp
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
#include "protocol_utils.h"
|
||||||
|
|
||||||
|
std::vector<unsigned char> encodeVarint(int value) {
|
||||||
|
std::vector<unsigned char> varint;
|
||||||
|
auto uValue = static_cast<unsigned int>(value);
|
||||||
|
do {
|
||||||
|
unsigned char temp = uValue & 0x7FU;
|
||||||
|
uValue >>= 7;
|
||||||
|
if (uValue != 0) {
|
||||||
|
temp |= 0x80;
|
||||||
|
}
|
||||||
|
varint.push_back(temp);
|
||||||
|
} while (uValue != 0);
|
||||||
|
return varint;
|
||||||
|
}
|
||||||
|
|
||||||
|
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());
|
||||||
|
return data;
|
||||||
|
}
|
10
protocol_utils.h
Normal file
10
protocol_utils.h
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
#ifndef MCPINGER_PROTOCOL_UTILS_H
|
||||||
|
#define MCPINGER_PROTOCOL_UTILS_H
|
||||||
|
|
||||||
|
#include <vector>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
std::vector<unsigned char> encodeVarint(int value);
|
||||||
|
std::vector<unsigned char> encodeString(const std::string& str);
|
||||||
|
|
||||||
|
#endif // MCPINGER_PROTOCOL_UTILS_H
|
Loading…
Reference in New Issue
Block a user