Skip to content

Instantly share code, notes, and snippets.

@moonexpr
Created January 15, 2023 21:53
Show Gist options
  • Save moonexpr/c9b98f375689f1801496ac55faf214d5 to your computer and use it in GitHub Desktop.
Save moonexpr/c9b98f375689f1801496ac55faf214d5 to your computer and use it in GitHub Desktop.
// ---------- Copyright John Chandara, all rights reserved. ------------- //
//
// Purpose: Demonstrate receiving UDP messages from the HL2 master server.
//
// ---------------------------------------------------------------------- //
#include <stdio.h>
#include <stdlib.h>
#include <stdio.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>
#include <string.h>
// https://developer.valvesoftware.com/wiki/Master_Server_Query_Protocol#Region_codes
#define REGION_NA_E 0x00 // US East coast
#define REGION_NA_W 0x01 // US West coast
#define REGION_SA 0x02 // South America
#define REGION_EU 0x03 // Europe
#define REGION_AS 0x04 // Asia
#define REGION_AU 0x05 // Australia
#define REGION_ME 0x06 // Middle East
#define REGION_AF 0x07 // Africa
#define REGION_ALL 0xFF // All
// https://developer.valvesoftware.com/wiki/Master_Server_Query_Protocol#IP:Port
#define ID_INITIAL "0.0.0.0:0"
// https://developer.valvesoftware.com/wiki/Master_Server_Query_Protocol#Filter
#define FILTER_TF2 "\\napp\\440"
#define MAXLEN 1023
int build( char* buf, char region, char *id, char *filters )
{
return snprintf(buf, MAXLEN, "%c%c%s%s", '1', region, id, filters);
}
void hex_dump ( const char * desc, const void * addr, const int len, int perLine)
{
// Silently ignore silly per-line values.
if (perLine < 4 || perLine > 64) perLine = 16;
int i;
unsigned char buff[32];
const unsigned char * pc = (const unsigned char *)addr;
// Output description if given.
if (desc != NULL) printf ("%s:\n", desc);
// Length checks.
if (len == 0) {
printf(" ZERO LENGTH\n");
return;
}
if (len < 0) {
printf(" NEGATIVE LENGTH: %d\n", len);
return;
}
// Process every byte in the data.
for (i = 0; i < len; i++) {
// Multiple of perLine means new or first line (with line offset).
if ((i % perLine) == 0) {
// Only print previous-line ASCII buffer for lines beyond first.
if (i != 0) printf (" %s\n", buff);
// Output the offset of current line.
printf (" %04x ", i);
}
// Now the hex code for the specific character.
printf (" %02x", pc[i]);
// And buffer a printable ASCII character for later.
if ((pc[i] < 0x20) || (pc[i] > 0x7e)) // isprint() may be better.
buff[i % perLine] = '.';
else
buff[i % perLine] = pc[i];
buff[(i % perLine) + 1] = '\0';
}
// Pad out last line if not exactly perLine characters.
while ((i % perLine) != 0) {
printf (" ");
i++;
}
// And print the final ASCII buffer.
printf (" %s\n", buff);
}
int fails_challenge_test (const void * addr)
{
const int challenge[] = { 0xFF, 0xFF, 0xFF, 0xFF, 0x66, 0x0A };
const int offset = 6;
const unsigned char * pc = (const unsigned char *)addr;
// Verify integrity challenge first.
int i;
for (i = 0; i < offset - 1; i++) {
if (pc[i] != challenge[i])
{
return 1;
}
}
return 0;
}
void response_dump (const void * addr)
{
// Silently ignore silly per-line values.
const int len = MAXLEN;
const int perLine = 6;
const int offset = perLine * 1;
int i;
unsigned char buff[perLine+1];
const unsigned char * pc = (const unsigned char *)addr;
if (fails_challenge_test(addr)) {
fprintf(stderr, "E: Response data failed integrity check. Abort processing.");
return;
}
// Process everything after challenge.
for (i = offset; i < len; i++) {
// Multiple of perLine means new or first line (with line offset).
if ((i % perLine) == 0) {
sprintf(&buff[i % perLine], "%d.%d.%d.%d:%hu",
pc[i],
pc[i + 1],
pc[i + 2],
pc[i + 3],
(pc[i + 4] << 8) + pc[i + 5]
);
// Only print previous-line ASCII buffer for lines beyond first.
if (i != offset) printf (" %s\n", buff);
// Output the offset of current line.
printf (" %04x ", i);
}
// Now the hex code for the specific character.
printf (" %02x", pc[i]);
// And buffer a printable ASCII character for later.
if ((pc[i] < 0x20) || (pc[i] > 0x7e)) // isprint() may be better.
buff[i % perLine] = '.';
else
buff[i % perLine] = pc[i];
buff[(i % perLine) + 1] = '\0';
}
// Pad out last line if not exactly perLine characters.
while ((i % perLine) != 0) {
printf (" ");
i++;
}
// And print the final ASCII buffer.
printf (" %s\n", buff);
}
void resolve_hostname (const char *hostname, char *buf)
{
int socket_fd;
struct addrinfo hints, *info;
int result;
int _test;
memset(&hints, 0, sizeof(hints));
hints.ai_family = AF_INET;
hints.ai_socktype = SOCK_DGRAM;
_test = getaddrinfo(hostname, "udp", &hints, &info);
if (_test)
{
fprintf(stderr, "E: %s\n", gai_strerror(_test));
exit(1);
}
struct addrinfo *ptr;
struct sockaddr_in *addr;
for (ptr = info; !ptr; ptr->ai_next)
{
addr = (struct sockaddr_in *) ptr->ai_addr;
printf("%s\n", inet_ntoa(addr->sin_addr));
strncpy(buf, inet_ntoa(addr->sin_addr), 15);
break;
}
freeaddrinfo(info);
}
int main () {
int socket_fd;
struct sockaddr_in server_addr, client_addr;
int _test, _len;
// 1. Create UDP socket
printf("Demo: Creating socket... ");
_test = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
if (_test < 0)
{
fprintf(stderr, "E: Failed to create socket.");
close(_test);
return 1;
} socket_fd = _test;
// 2. Set port and IP
// char host[16]; resolve_hostname("hl2master.steampowered.com", host);
// ;; ANSWER SECTION:
// hl2master.steampowered.com. 2233 IN A 208.64.201.194
// hl2master.steampowered.com. 2233 IN A 208.64.200.65
// hl2master.steampowered.com. 2233 IN A 208.64.201.193
char host[] = "208.64.201.193";
server_addr.sin_family = AF_INET;
server_addr.sin_port = htons(27011);
server_addr.sin_addr.s_addr = inet_addr(host);
printf("contacting %s:%d ... \n", host, 27011);
// 3. Send query to server
socklen_t addr_len = (socklen_t) sizeof(struct sockaddr_in);
char cl_buffer[MAXLEN]; memset(cl_buffer, '\0', MAXLEN);
char sv_buffer[MAXLEN]; memset(sv_buffer, '\0', MAXLEN);
_len = build(cl_buffer, REGION_NA_E, ID_INITIAL, FILTER_TF2);
_test = sendto(socket_fd, cl_buffer, _len + 1, 0,
(struct sockaddr*) &server_addr, addr_len);
if (_test < 0)
{
fprintf(stderr, "E: Failed to send query to master server.\n");
close(socket_fd);
return _test;
}
printf("Demo: Sent search query!\n");
// 4. Receive client messages
_test = recvfrom(socket_fd, sv_buffer, MAXLEN, 0,
(struct sockaddr*) &server_addr, &addr_len);
if (_test < 0)
{
perror("E: ");
close(socket_fd);
return 1;
}
printf("Demo: Response.\n");
response_dump(sv_buffer);
printf("\nDemo: Successfully queried master server.\n");
// 5. Close socket
close(socket_fd);
return 0;
}
@moonexpr
Copy link
Author

image

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