Skip to content

Instantly share code, notes, and snippets.

@layou233
Last active February 24, 2023 02:24
Show Gist options
  • Save layou233/0a92b9df965ee97424ae65ba0cea74d6 to your computer and use it in GitHub Desktop.
Save layou233/0a92b9df965ee97424ae65ba0cea74d6 to your computer and use it in GitHub Desktop.
MCPing for Windows
/*
This code was written in pure C.
Build it with: (Windows only)
gcc ./mcping.c -o mcping.exe -lws2_32 -O2
MCPing v0.1.1
By Launium 2023. GitHub @layou233.
License: CC-BY-SA 3.0
*/
#include<stdio.h>
#include<stdlib.h>
#include<sys/timeb.h>
#include<winsock2.h>
typedef struct {
short sin_family;
u_short sin_port;
struct in_addr sin_addr;
char sin_zero[8];
} sockaddr_in;
boolean process_ip4(const char *ip, struct in_addr* addr)
{
int dots = 0;
int setions = 0;
if (NULL == ip || *ip == '.') {
return 0;
}
while (*ip) {
if (*ip == '.') {
if (setions >= 0 && setions <= 255) {
*((&addr->S_un.S_un_b.s_b1)+dots) = setions;
setions = 0;
ip++;
continue;
}
dots ++;
return 0;
}
else if (*ip >= '0' && *ip <= '9') { // check if not a number
setions = setions * 10 + (*ip - '0');
} else
return 0;
ip++;
}
// check the last pattern
if (setions >= 0 && setions <= 255) {
if (dots == 3) {
addr->S_un.S_un_b.s_b4 = setions;
return 1;
}
}
return 0;
}
int read_varint(const char* buf, int* numread) {
int result = 0;
int value;
char byte;
do {
byte = *(buf++);
value = byte & 0x7F;
result |= value << (7 * *numread);
(*numread)++;
if (*numread > 5) {
fprintf(stderr, "Error reading varint: varint too big\n");
return -1;
}
} while ((byte & 0x80) != 0);
return result;
}
void test_ping(sockaddr_in* addr, char* hostname, u_short le_port)
{
SOCKET conn = socket(AF_INET, SOCK_STREAM, 0);
if (INVALID_SOCKET == conn)
{
fprintf(stderr, " Fail to initialize socket. Error: %d\n", WSAGetLastError());
return;
}
int err = connect(conn, (struct sockaddr*)addr, sizeof(*addr));
if (SOCKET_ERROR == err)
{
fprintf(stderr, " Connection failed. Error: %d\n", WSAGetLastError());
closesocket(conn);
return;
}
char buf[1024];
// generate status packet
char hostname_len = strlen(hostname);
char packet_len = 1; // packet ID
packet_len += 1; // protocol version 47 (1.8.9)
packet_len += 1; // hostname length
packet_len += hostname_len; // hostname
packet_len += 2; // little-endian port
packet_len += 1; // next state
char i = 0;
buf[i++] = packet_len; // no need to encode varint
buf[i++] = 0; // packet ID
buf[i++] = 47;
buf[i++] = hostname_len;
memcpy(&buf[i], hostname, hostname_len);
i += hostname_len;
memcpy(&buf[i], &addr->sin_port, 2);
i += 2;
buf[i++] = 1;
// append a empty packet for Status state
buf[i++] = 1;
buf[i++] = 0;
err = send(conn, &buf[0], packet_len + 3, 0);
if (err != packet_len + 3)
{
fprintf(stderr, " Fail to write handshake packet. Error: %d\n", WSAGetLastError());
closesocket(conn);
return;
}
for (int read=0, to_recv; read < 5; read += err)
{
//printf("%d",to_recv);
int to_recv = 5 - read;
err = recv(conn, &buf[read], to_recv, 0);
if (SOCKET_ERROR == err)
{
fprintf(stderr, " Fail to read MOTD packet length. Error: %d, Read %d\n", WSAGetLastError(), read);
closesocket(conn);
return;
}
}
int num_read = 0;
int motd_len = read_varint(&buf[0], &num_read);
if (motd_len == -1)
{
fprintf(stderr, " Fail to read handshake response: got a strange VarInt number.\n");
closesocket(conn);
return;
}
motd_len -= 5 - num_read; // part of motd might be read already in this recv call
// skip MOTD
for (int read=0, to_recv; read < motd_len; read += err)
{
//printf("%d",to_recv);
to_recv = motd_len - read;
if (to_recv > 1024) to_recv = 1024;
err = recv(conn, &buf[0], to_recv, 0);
if (SOCKET_ERROR == err)
{
fprintf(stderr, " Fail to read MOTD. Error: %d\n", WSAGetLastError());
closesocket(conn);
return;
}
}
// send ping packet
i = 0; // reset
buf[i++] = 9; // packet length
buf[i++] = 1; // packet ID
struct timeb ping_time, pong_time;
ftime(&ping_time);
memcpy(&buf[i], &ping_time, sizeof(ping_time));
err = send(conn, &buf[0], 10, 0);
if (err != 10)
{
fprintf(stderr, " Fail to write ping packet. Error: %d\n", WSAGetLastError());
closesocket(conn);
return;
}
// receive the first byte of pong packet
err = recv(conn, &buf[0], 1, 0);
ftime(&pong_time);
closesocket(conn);
if (err != 1)
{
fprintf(stderr, " Fail to read pong packet. Error: %d\n", WSAGetLastError());
return;
}
if (buf[0] != 9)
{
fprintf(stderr, " Fail to read pong packet: unexpected pong packet length: %d\n", buf[0]);
return;
}
u_int64 diff = pong_time.time * 1000 + pong_time.millitm - ping_time.time * 1000 - ping_time.millitm;
printf(" Succeed. RTT=%llums\n", diff);
}
int main(int argc, char *argv[])
{
printf("MCPing v0.1.1 Started...\n");
if (argc < 2)
{
fprintf(stderr, "Bad usage. Example: ./mcping.exe mc.hypixel.net 25565 [-t]\n");
return 1;
}
char *addr = argv[1];
u_short port;
if (argc == 3) port = 25565;
else port = atoi(argv[2]);
boolean t_mode = 0;
if (argc > 3)
{
if (!strcmp(argv[3], "-t")) t_mode = 1;
}
WSADATA wsa;
int err = WSAStartup(MAKEWORD(2, 2), &wsa);
if (err != 0)
{
fprintf(stderr, "Fail to start up Winsock2! Error code: %d\n", err);
return 1;
}
sockaddr_in s;
s.sin_family = AF_INET;
s.sin_port = htons(port);
boolean is_domain = !process_ip4(addr, &s.sin_addr);
if (is_domain)
{
HOSTENT *host_entry = gethostbyname(addr);
if(NULL != host_entry)
{
s.sin_addr.S_un.S_un_b.s_b1 = host_entry->h_addr_list[0][0];
s.sin_addr.S_un.S_un_b.s_b2 = host_entry->h_addr_list[0][1];
s.sin_addr.S_un.S_un_b.s_b3 = host_entry->h_addr_list[0][2];
s.sin_addr.S_un.S_un_b.s_b4 = host_entry->h_addr_list[0][3];
} else {
fprintf(stderr, "\nFail to resolve the hostname: %s\n", addr);
return 1;
}
}
printf("\nConnecting to tcp://%d.%d.%d.%d:%d using Minecraft JE Protocol Status...\n",
s.sin_addr.S_un.S_un_b.s_b1,
s.sin_addr.S_un.S_un_b.s_b2,
s.sin_addr.S_un.S_un_b.s_b3,
s.sin_addr.S_un.S_un_b.s_b4, port);
if (t_mode) for (;;) test_ping(&s, addr, port);
for (int n=0; n < 4; n++)
{
test_ping(&s, addr, port);
}
return 0;
}
@layou233
Copy link
Author

layou233 commented Jan 13, 2023

Pre-built download:

https://anonfiles.com/x8A842R5y0/mcping_exe

Built with gcc ./mcping.c -o mcping.exe -lws2_32 -O2

gcc -v

C:\Users\Administrator>gcc -v
Using built-in specs.
COLLECT_GCC=gcc
COLLECT_LTO_WRAPPER=C:/mingw32/bin/../libexec/gcc/i686-w64-mingw32/8.1.0/lto-wrapper.exe
Target: i686-w64-mingw32
Configured with: ../../../src/gcc-8.1.0/configure --host=i686-w64-mingw32 --build=i686-w64-mingw32 --target=i686-w64-mingw32 --prefix=/mingw32 --with-sysroot=/c/mingw810/i686-810-posix-dwarf-rt_v6-rev0/mingw32 --enable-shared --enable-static --disable-multilib --enable-languages=c,c++,fortran,lto --enable-libstdcxx-time=yes --enable-threads=posix --enable-libgomp --enable-libatomic --enable-lto --enable-graphite --enable-checking=release --enable-fully-dynamic-string --enable-version-specific-runtime-libs --disable-sjlj-exceptions --with-dwarf2 --disable-libstdcxx-pch --disable-libstdcxx-debug --enable-bootstrap --disable-rpath --disable-win32-registry --disable-nls --disable-werror --disable-symvers --with-gnu-as --with-gnu-ld --with-arch=i686 --with-tune=generic --with-libiconv --with-system-zlib --with-gmp=/c/mingw810/prerequisites/i686-w64-mingw32-static --with-mpfr=/c/mingw810/prerequisites/i686-w64-mingw32-static --with-mpc=/c/mingw810/prerequisites/i686-w64-mingw32-static --with-isl=/c/mingw810/prerequisites/i686-w64-mingw32-static --with-pkgversion='i686-posix-dwarf-rev0, Built by MinGW-W64 project' --with-bugurl=https://sourceforge.net/projects/mingw-w64 CFLAGS='-O2 -pipe -fno-ident -I/c/mingw810/i686-810-posix-dwarf-rt_v6-rev0/mingw32/opt/include -I/c/mingw810/prerequisites/i686-zlib-static/include -I/c/mingw810/prerequisites/i686-w64-mingw32-static/include' CXXFLAGS='-O2 -pipe -fno-ident -I/c/mingw810/i686-810-posix-dwarf-rt_v6-rev0/mingw32/opt/include -I/c/mingw810/prerequisites/i686-zlib-static/include -I/c/mingw810/prerequisites/i686-w64-mingw32-static/include' CPPFLAGS=' -I/c/mingw810/i686-810-posix-dwarf-rt_v6-rev0/mingw32/opt/include -I/c/mingw810/prerequisites/i686-zlib-static/include -I/c/mingw810/prerequisites/i686-w64-mingw32-static/include' LDFLAGS='-pipe -fno-ident -L/c/mingw810/i686-810-posix-dwarf-rt_v6-rev0/mingw32/opt/lib -L/c/mingw810/prerequisites/i686-zlib-static/lib -L/c/mingw810/prerequisites/i686-w64-mingw32-static/lib -Wl,--large-address-aware'
Thread model: posix
gcc version 8.1.0 (i686-posix-dwarf-rev0, Built by MinGW-W64 project)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment