LICENSE: The Artistic License 2.0 /* * qstat.h * by Steve Jankowski * steve@qstat.org * http://www.qstat.org * * Copyright 1996,1997,1998,1999,2000,2001,2002 by Steve Jankowski */ char ottd_serverinfo[] = { 0x03, 0x00, // packet length 0x00, // packet type }; char ottd_serverdetails[] = { 0x03, 0x00, // packet length 0x02, // packet type }; { /* openTTD */ OTTD_SERVER, /* id */ "OTTDS", /* type_prefix */ "ottds", /* type_string */ "-ottds", /* type_option */ "OpenTTD", /* game_name */ 0, /* master */ 3979, /* default_port */ 0, /* port_offset */ 0, /* flags */ "", /* game_rule */ "OPENTTD", /* template_var */ (char*) &ottd_serverinfo, /* status_packet */ sizeof( ottd_serverinfo), /* status_len */ NULL, /* player_packet */ 0, /* player_len */ (char*) &ottd_serverdetails,/* rule_packet */ sizeof( ottd_serverdetails), /* rule_len */ NULL, /* master_packet */ 0, /* master_len */ NULL, /* master_protocol */ NULL, /* master_query */ display_q2_player_info, /* display_player_func */ display_server_rules, /* display_rule_func */ raw_display_q2_player_info, /* display_raw_player_func */ raw_display_server_rules, /* display_raw_rule_func */ xml_display_player_info,/* display_xml_player_func */ xml_display_server_rules, /* display_xml_rule_func */ send_ottd_request_packet, /* status_query_func */ NULL, /* rule_query_func */ NULL, /* player_query_func */ deal_with_ottd_packet, /* packet_func */ }, /* * qstat 2.11 * * opentTTD protocol * Copyright 2007 Ludwig Nussel * * Licensed under the Artistic License, see LICENSE.txt for license terms */ #include #ifndef _WIN32 #include #endif #include #include #include "qstat.h" #include "qserver.h" #include "debug.h" enum { MAX_VEHICLE_TYPES = 5, MAX_STATION_TYPES = 5 }; static const char* vehicle_types[] = { "num_trains", "num_trucks", "num_busses", "num_aircrafts", "num_ships", }; static const char* station_types[] = { "num_stations", "num_truckbays", "num_busstations", "num_airports", "num_docks", }; query_status_t deal_with_ottdmaster_packet(struct qserver *server, char *rawpkt, int pktlen) { unsigned num; server->ping_total += time_delta(&packet_recv_time, &server->packet_time1); server->server_name = MASTER; if(swap_short_from_little(rawpkt) != pktlen) { malformed_packet( server, "invalid packet length" ); return PKT_ERROR; } if(rawpkt[2] != 7) { malformed_packet( server, "invalid packet type" ); return PKT_ERROR; } if(rawpkt[3] != 1) { malformed_packet( server, "invalid packet version" ); return PKT_ERROR; } num = swap_short_from_little(&rawpkt[4]); rawpkt += 6; pktlen -= 6; if( num && num*6 <= pktlen ) { unsigned i; server->master_pkt = (char*)realloc(server->master_pkt, server->master_pkt_len + pktlen ); memset(server->master_pkt + server->master_pkt_len, 0, pktlen ); server->master_pkt_len += pktlen; for( i = 0; i < num * 6; i += 6 ) { memcpy(&server->master_pkt[i], &rawpkt[i], 4); server->master_pkt[i+4] = rawpkt[i+5]; server->master_pkt[i+5] = rawpkt[i+4]; } server->n_servers += num; } else { malformed_packet( server, "invalid packet" ); return PKT_ERROR; } bind_sockets(); return DONE_AUTO; } #define xstr(s) str(s) #define str(s) #s #define GET_STRING do { \ str = (char*)ptr; \ ptr = memchr(ptr, '\0', end-ptr); \ if ( !ptr ) \ { \ malformed_packet( server, "%s:%s invalid packet", __FILE__, xstr(__LINE__) ); \ return PKT_ERROR; \ } \ ++ptr; \ } while(0) #define FAIL_IF(cond, msg) \ if((cond)) { \ malformed_packet( server, "%s:%s %s", __FILE__, xstr(__LINE__), msg ); \ return PKT_ERROR; \ } #define INVALID_IF(cond) \ FAIL_IF(cond, "invalid packet") query_status_t deal_with_ottd_packet(struct qserver *server, char *rawpkt, int pktlen) { unsigned char *ptr = (unsigned char*)rawpkt; unsigned char *end = (unsigned char*)(rawpkt + pktlen); unsigned char type; char* str; char buf[32]; unsigned ver; server->n_servers++; if ( server->server_name == NULL) { server->ping_total += time_delta( &packet_recv_time, &server->packet_time1); server->n_requests++; } else { gettimeofday( &server->packet_time1, NULL); } FAIL_IF(pktlen < 4 || swap_short_from_little(rawpkt) > pktlen, "invalid packet"); type = ptr[2]; ver = ptr[3]; ptr += 4; debug(3, "len %hu type %hhu ver %hhu", swap_short_from_little(rawpkt), type, ver); FAIL_IF(ver != 4 && ver != 5, "only version 4 and 5 servers are supported"); if(type == 1) // info packet { unsigned numgrf = *ptr; FAIL_IF(ptr + numgrf * 20 + 1 > end, "invalid newgrf number"); ptr += numgrf * 20 + 1; snprintf(buf, sizeof(buf), "%u", swap_long_from_little(ptr)); add_rule(server, "date_days", buf, NO_FLAGS); ptr += 4; snprintf(buf, sizeof(buf), "%u", swap_long_from_little(ptr)); add_rule(server, "startdate_days", buf, NO_FLAGS); ptr += 4; FAIL_IF(ptr + 3 > end, "invalid packet"); snprintf(buf, sizeof(buf), "%hhu", ptr[0]); add_rule(server, "maxcompanies", buf, NO_FLAGS); snprintf(buf, sizeof(buf), "%hhu", ptr[1]); add_rule(server, "numcompanies", buf, NO_FLAGS); server->max_spectators = ptr[2]; ptr += 3; GET_STRING; server->server_name = strdup(str); GET_STRING; add_rule(server, "version", str, NO_FLAGS); FAIL_IF(ptr + 7 > end, "invalid packet"); { static const char* langs[] = { "any", "English", "German", "French" }; unsigned i = *ptr++; if(i > 3) i = 0; add_rule(server, "language", (char*)langs[i], NO_FLAGS); } add_rule(server, "password", *ptr++ ? "1" : "0", NO_FLAGS); server->max_players = *ptr++; server->num_players = *ptr++; server->num_spectators = *ptr++; GET_STRING; server->map_name = strdup(str); snprintf(buf, sizeof(buf), "%hu", swap_short_from_little(ptr)); add_rule(server, "map_width", buf, NO_FLAGS); snprintf(buf, sizeof(buf), "%hu", swap_short_from_little(ptr)); add_rule(server, "map_height", buf, NO_FLAGS); { static const char* sets[] = { "temperate", "arctic", "desert", "toyland" }; unsigned i = *ptr++; if(i > 3) i = 0; add_rule(server, "map_set", (char*)sets[i], NO_FLAGS); } add_rule(server, "dedicated", *ptr++ ? "1" : "0", NO_FLAGS); } else if(type == 3) // player packet { unsigned i, j; INVALID_IF(ptr + 2 > end); server->num_players = *ptr++; for(i = 0; i < server->num_players; ++i) { unsigned long long lli; struct player* player; unsigned char nr; nr = *ptr++; debug(3, "player number %d", nr); player = add_player(server, i); FAIL_IF(!player, "can't allocate player"); GET_STRING; player->name = strdup(str); debug(3, "name %s", str); player->frags = 0; INVALID_IF(ptr + 4 + 3*8 + 2 + 1 + 2*MAX_VEHICLE_TYPES + 2*MAX_STATION_TYPES > end); snprintf(buf, sizeof(buf), "%u", swap_long_from_little(ptr)); player_add_info(player, "startdate", buf, 0); ptr += 4; lli = swap_long_from_little(ptr+4); lli <<= 32; lli += swap_long_from_little(ptr); snprintf(buf, sizeof(buf), "%lld", lli); player_add_info(player, "value", buf, 0); ptr += 8; lli = swap_long_from_little(ptr+4); lli <<= 32; lli = swap_long_from_little(ptr); snprintf(buf, sizeof(buf), "%lld", lli); player_add_info(player, "money", buf, 0); ptr += 8; lli = swap_long_from_little(ptr+4); lli <<= 32; lli += swap_long_from_little(ptr); snprintf(buf, sizeof(buf), "%lld", lli); player_add_info(player, "income", buf, 0); ptr += 8; snprintf(buf, sizeof(buf), "%hu", swap_short_from_little(ptr)); player_add_info(player, "performance", buf, 0); ptr += 2; player_add_info(player, "password", *ptr?"1":"0", 0); ++ptr; for (j = 0; j < MAX_VEHICLE_TYPES; ++j) { snprintf(buf, sizeof(buf), "%hu", swap_short_from_little(ptr)); player_add_info(player, (char*)vehicle_types[j], buf, 0); ptr += 2; } for (j = 0; j < MAX_STATION_TYPES; ++j) { snprintf(buf, sizeof(buf), "%hu", swap_short_from_little(ptr)); player_add_info(player, (char*)station_types[j], buf, 0); ptr += 2; } if (ver != 5) { // connections while(ptr + 1 < end && *ptr) { ++ptr; GET_STRING; // client name debug(3, "%s played by %s", str, player->name); GET_STRING; // id INVALID_IF(ptr + 4 > end); ptr += 4; } ++ptr; // record terminated by zero byte } } // spectators while(ptr + 1 < end && *ptr) { ++ptr; GET_STRING; // client name debug(3, "spectator %s", str); GET_STRING; // id INVALID_IF(ptr + 4 > end); ptr += 4; } ++ptr; // record terminated by zero byte server->next_rule = NO_SERVER_RULES; // we're done server->next_player_info = server->num_players; // we're done } else { malformed_packet( server, "invalid type" ); return PKT_ERROR; } server->retry1 = n_retries; // we're done with this packet, reset retry counter return DONE_AUTO; } query_status_t send_ottdmaster_request_packet(struct qserver *server) { return qserver_send_initial(server, server->type->master_packet, server->type->master_len); } query_status_t send_ottd_request_packet(struct qserver *server) { qserver_send_initial(server, server->type->status_packet, server->type->status_len); if(get_server_rules || get_player_info) { server->next_rule = ""; // trigger calling send_a2s_rule_request_packet } return INPROGRESS; }