Created
January 15, 2023 21:53
-
-
Save moonexpr/c9b98f375689f1801496ac55faf214d5 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// ---------- 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; | |
} |
Author
moonexpr
commented
Jan 15, 2023
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment