libq3.c 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734
  1. /* libq3.c: masterserver plugin for Quake3 servers. */
  2. /* Copyright (C) 2003 Andre' Schulz
  3. * This file is part of masterserver.
  4. *
  5. * masterserver is free software; you can redistribute it and/or modify
  6. * it under the terms of the GNU General Public License as published by
  7. * the Free Software Foundation; either version 2 of the License, or
  8. * (at your option) any later version.
  9. *
  10. * masterserver is distributed in the hope that it will be useful,
  11. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. * GNU General Public License for more details.
  14. *
  15. * You should have received a copy of the GNU General Public License
  16. * along with masterserver; if not, write to the Free Software
  17. * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
  18. *
  19. * The author can be contacted at chickenman@exhale.de
  20. */
  21. /*
  22. * vim:sw=4:ts=4
  23. */
  24. #include <stdio.h>
  25. #include <stdlib.h>
  26. #include <string.h>
  27. #include <time.h>
  28. #include <pthread.h>
  29. #include <sys/socket.h> // for socket() etc.
  30. #include <netinet/in.h>
  31. #include <arpa/inet.h>
  32. #include <math.h>
  33. #include "../masterserver.h"
  34. #define HEARTBEAT_TIMEOUT 300
  35. // message of the day
  36. #define Q3M_MOTD "Insert MOTD here."
  37. // for logging stuff
  38. #undef LOG_SUBNAME
  39. #define LOG_SUBNAME "libq3" // logging subcategory description
  40. // q3 packet stuff
  41. const char q3_pkt_header[] = "\xff\xff\xff\xff";
  42. const int q3_pkt_header_len = 4;
  43. const char q3_pkt_heartbeat[] = "heartbeat QuakeArena-1\n";
  44. const int q3_pkt_heartbeat_len= 23;
  45. const char q3_pkt_getinfo[] = "getinfo\n";
  46. const int q3_pkt_getinfo_len = 8;
  47. const char q3_pkt_inforsp[] = "infoResponse\n";
  48. const int q3_pkt_inforsp_len = 13;
  49. const char q3_pkt_getstatus[] = "getstatus ";
  50. const int q3_pkt_getstatus_len= 10;
  51. const char q3_pkt_statusrsp[] = "statusResponse\n";
  52. const int q3_pkt_statusrsp_len= 15;
  53. const char q3_pkt_getsrv[] = "getservers";
  54. const int q3_pkt_getsrv_len = 10;
  55. const char q3_pkt_getsrvrsp[] = "getserversResponse";
  56. const int q3_pkt_getsrvrsp_len= 18;
  57. const char q3_pkt_getmotd[] = "getmotd";
  58. const int q3_pkt_getmotd_len = 7;
  59. const char q3_pkt_motd[] = "\xff\xff\xff\xffmotd \"challenge\\%d\\motd\\%s\\\"";
  60. const int q3_pkt_motd_len = 28;
  61. const char q3_pkt_footer[] = "\\EOT";
  62. const int q3_pkt_footer_len = 4;
  63. const char q3m_plugin_version[] = "0.8";
  64. static port_t q3m_ports[] = { { IPPROTO_UDP, 27950 }, // master
  65. { IPPROTO_UDP, 27951 }, // motd
  66. // { IPPROTO_UDP, 27952 }, // auth
  67. };
  68. // player info
  69. typedef struct {
  70. int score;
  71. int ping;
  72. char *name;
  73. } q3m_player_data_t;
  74. // q3 plugin private data
  75. typedef struct {
  76. // statusResponse vars
  77. int challenge;
  78. int sv_punkbuster; // 0 | 1
  79. int g_maxGameClients;
  80. int capturelimit;
  81. int sv_maxclients; // max num of clients
  82. int timelimit;
  83. int fraglimit;
  84. int dmflags; // bit field
  85. int sv_maxPing;
  86. int sv_minPing;
  87. char *sv_hostname; // server name
  88. int sv_maxRate;
  89. int sv_floodProtect; // 0 | 1
  90. char *version; // self explanatory
  91. int g_gametype; // 0 - FFA | 1 - Tournament | 2 - Single Player | 3 - TDM | 4 - CTF
  92. int protocol; // q3 network protocol version
  93. char *mapname; // self explanatory
  94. int sv_privateClients; // # of passworded player slots
  95. int sv_allowDownload; // 0 | 1
  96. int bot_minplayers;
  97. char *gamename; // which mod
  98. int g_needpass; // 0 | 1
  99. q3m_player_data_t *_player; // player info
  100. // following is information not in packet
  101. int _players; // # of players
  102. int _challenge; // our challenge #
  103. } q3m_private_data_t;
  104. static void info(void); // print information about plugin
  105. static void free_privdata(void *);
  106. static int process(char *, int); // process packet and return a value
  107. static int process_getmotd(char *, int);
  108. static int process_getservers(char *);
  109. static int process_heartbeat(char *);
  110. static int send_getstatus();
  111. static int process_statusResponse(char *, int);
  112. static void cleanup(void);
  113. void init_plugin(void) __attribute__ ((constructor));
  114. static
  115. struct masterserver_plugin q3m
  116. = { "q3m",
  117. q3m_plugin_version,
  118. masterserver_version,
  119. q3m_ports,
  120. 2,
  121. HEARTBEAT_TIMEOUT,
  122. &info,
  123. &process,
  124. &free_privdata,
  125. &cleanup
  126. };
  127. static void
  128. info(void)
  129. {
  130. INFO("quake3 masterserver plugin v%s\n", q3m_plugin_version);
  131. INFO(" compiled for masterserver v%s\n", masterserver_version);
  132. }
  133. static void
  134. free_privdata(void *data)
  135. {
  136. int i;
  137. q3m_private_data_t *privdata = (q3m_private_data_t *) data;
  138. if (data == NULL) return;
  139. free(privdata->sv_hostname);
  140. free(privdata->version);
  141. free(privdata->mapname);
  142. free(privdata->gamename);
  143. for (i = 0; i < privdata->_players; i++)
  144. free(privdata->_player[i].name);
  145. free(privdata->_player);
  146. free(privdata);
  147. }
  148. static int
  149. process_heartbeat(char *packet)
  150. {
  151. int server_dup = 0;
  152. int time_diff, i;
  153. serverlist_t *backup_ptr;
  154. // first, check if server is already in our list
  155. for (i = 0; i < q3m.num_servers; i++) {
  156. if ((q3m.list[i].ip.s_addr == q3m.client.sin_addr.s_addr)
  157. && (q3m.list[i].port == q3m.client.sin_port)) {
  158. DEBUG("duplicate server detected! (%s:%d)\n",
  159. inet_ntoa(q3m.client.sin_addr), ntohs(q3m.client.sin_port));
  160. server_dup = 1;
  161. break;
  162. }
  163. }
  164. INFO("heartbeat from %s:%d\n",
  165. inet_ntoa(q3m.client.sin_addr), ntohs(q3m.client.sin_port));
  166. // if not, then add it to the list
  167. if (server_dup == 0) {
  168. // server is not in our list so add its ip, port and a timestamp
  169. q3m.list[q3m.num_servers].ip = q3m.client.sin_addr;
  170. q3m.list[q3m.num_servers].port = q3m.client.sin_port;
  171. q3m.list[q3m.num_servers].lastheartbeat = time(NULL);
  172. DEBUG("this is server no.: %d | lastheartbeat: %d\n",
  173. q3m.num_servers, q3m.list[q3m.num_servers].lastheartbeat);
  174. // allocate memory for private data
  175. q3m.list[q3m.num_servers].private_data = calloc(1, sizeof(q3m_private_data_t));
  176. q3m.num_servers++;
  177. DEBUG("reallocating server list (old size: %d -> new size: %d)\n",
  178. q3m.num_servers * sizeof(serverlist_t),
  179. (q3m.num_servers+1) * sizeof(serverlist_t));
  180. // back up the current list pointer in case realloc() fails
  181. backup_ptr = q3m.list;
  182. q3m.list = (serverlist_t *) realloc(q3m.list, ((q3m.num_servers+1)*sizeof(serverlist_t)));
  183. if (q3m.list == NULL) {
  184. WARNING("realloc() failed trying to get %d bytes!\n",
  185. (q3m.num_servers+1)*sizeof(serverlist_t));
  186. // since the pointer is overwritten with NULL
  187. // we'll recover by using the backup pointer
  188. q3m.list = backup_ptr;
  189. return -2;
  190. } else DEBUG("reallocation successful\n");
  191. } else {
  192. time_diff = time(NULL) - q3m.list[i].lastheartbeat;
  193. // if time_diff is 0 the server has shutdown (most likely)
  194. if (time_diff == 0) {
  195. INFO("server %s:%u is shutting down (time_diff %d)\n",
  196. inet_ntoa(q3m.list[i].ip), ntohs(q3m.list[i].port),
  197. time_diff);
  198. delete_server(&q3m, i);
  199. server_dup = 0;
  200. return 2; // return "server-shutdown" code
  201. } else {
  202. // server is in already in our list so we just update the timestamp
  203. q3m.list[i].lastheartbeat = time(NULL);
  204. server_dup = 0;
  205. }
  206. }
  207. // server added/updated
  208. return 1;
  209. }
  210. static int
  211. send_getstatus()
  212. {
  213. int challenge, i;
  214. // create challenge number
  215. challenge = rand();
  216. DEBUG("challenge: %d\n", challenge);
  217. // prepare q3m.msg_out
  218. q3m.num_msgs = 1;
  219. q3m.msg_out_length = calloc(1, sizeof(int));
  220. if (q3m.msg_out_length == NULL) {
  221. ERRORV("calloc() failed trying to get %d bytes!\n", sizeof(int));
  222. return -2; // TODO: define retval for errors
  223. }
  224. DEBUG("allocated %d bytes for msg_out_length[]\n", sizeof(int));
  225. // q3m.msg_out_length[0] = q3_pkt_header_len + q3_pkt_getstatus_len;
  226. q3m.msg_out_length[0] = q3_pkt_header_len
  227. + q3_pkt_getstatus_len + (int)(sizeof(int)*2.5);
  228. // allocate the memory for the outgoing packet
  229. q3m.msg_out = calloc(1, sizeof(char *));
  230. if (q3m.msg_out == NULL) {
  231. ERRORV("calloc() failed trying to get %d bytes!\n", sizeof(char *));
  232. return -2; // TODO: define retval for errors
  233. }
  234. q3m.msg_out[0] = calloc(q3m.msg_out_length[0]+1, 1);
  235. if (q3m.msg_out[0] == NULL) {
  236. ERRORV("calloc() failed trying to get %d bytes!\n",
  237. q3m.msg_out_length[0]);
  238. return -2; // TODO: define retval for errors
  239. }
  240. DEBUG("allocated %d bytes for msg_out[0]\n", q3m.msg_out_length[0]+1);
  241. memcpy(q3m.msg_out[0], q3_pkt_header, q3_pkt_header_len);
  242. memcpy(q3m.msg_out[0]+q3_pkt_header_len, q3_pkt_getstatus, q3_pkt_getstatus_len);
  243. sprintf(q3m.msg_out[0]+q3_pkt_header_len+q3_pkt_getstatus_len, "%d", challenge);
  244. // write challenge into serverlist
  245. for (i = 0; i < q3m.num_servers; i++) {
  246. if ((q3m.client.sin_addr.s_addr == q3m.list[i].ip.s_addr)
  247. && (q3m.client.sin_port == q3m.list[i].port)) {
  248. ((q3m_private_data_t *) q3m.list[i].private_data)->_challenge = challenge;
  249. break;
  250. }
  251. }
  252. return 1; // send "getstatus" packet
  253. }
  254. static int
  255. process_getservers(char *packet)
  256. {
  257. int i, j, pkt_offset; // temp vars
  258. int getsrv_protocol;
  259. char *temp;
  260. q3m_private_data_t *temp_priv_data;
  261. INFO("getservers from %s:%u\n",
  262. inet_ntoa(q3m.client.sin_addr), ntohs(q3m.client.sin_port));
  263. // we need the protocol version from the packet so we parse it
  264. temp = packet+q3_pkt_header_len+q3_pkt_getsrv_len+1;
  265. getsrv_protocol = atoi(temp);
  266. DEBUG("requested protocol is %d\n", getsrv_protocol);
  267. // got the protocol version now we can assemble the outgoing packet(s)
  268. DEBUG("assembling server list packet\n");
  269. /*
  270. * packet assembler follows
  271. */
  272. // walk the server list
  273. for (i = j = 0; (j < q3m.num_servers) || (q3m.num_msgs == 0); i++) {
  274. q3m.num_msgs++;
  275. // allocate memory for the packets
  276. q3m.msg_out = realloc(q3m.msg_out, q3m.num_msgs*sizeof(char *));
  277. if (q3m.msg_out == NULL) {
  278. ERRORV("malloc() failed to get %d bytes!\n", q3m.num_msgs*sizeof(char *));
  279. return -2;
  280. }
  281. q3m.msg_out_length = realloc(q3m.msg_out_length, q3m.num_msgs*sizeof(int));
  282. if (q3m.msg_out_length == NULL) {
  283. ERRORV("malloc() failed to get %d bytes!\n", q3m.num_msgs*sizeof(int));
  284. return -2;
  285. }
  286. // get memory for header and command
  287. q3m.msg_out[i] = malloc(811);
  288. if (q3m.msg_out[i] == NULL) {
  289. ERROR("malloc() failed to get 811 bytes!\n");
  290. return -2;
  291. }
  292. // write header and command into packet
  293. memcpy(q3m.msg_out[i], q3_pkt_header, q3_pkt_header_len);
  294. pkt_offset = q3_pkt_header_len;
  295. memcpy(q3m.msg_out[i]+pkt_offset, q3_pkt_getsrvrsp, q3_pkt_getsrvrsp_len);
  296. pkt_offset += q3_pkt_getsrvrsp_len;
  297. for (; (j < q3m.num_servers) && (pkt_offset < 806); j++) {
  298. temp_priv_data = (q3m_private_data_t *) q3m.list[j].private_data;
  299. // if the protocol matches, write ip/port into the packet
  300. if (temp_priv_data->protocol == getsrv_protocol) {
  301. // copy data from server list into packet
  302. memcpy(q3m.msg_out[i]+pkt_offset, "\\", 1);
  303. pkt_offset++;
  304. memcpy(q3m.msg_out[i]+pkt_offset, &q3m.list[j].ip, 4);
  305. pkt_offset += 4;
  306. memcpy(q3m.msg_out[i]+pkt_offset, &q3m.list[j].port, 2);
  307. pkt_offset += 2;
  308. }
  309. } // for j < 112
  310. // write footer
  311. memcpy(q3m.msg_out[i]+pkt_offset, q3_pkt_footer, q3_pkt_footer_len);
  312. pkt_offset += q3_pkt_footer_len;
  313. q3m.msg_out[i][pkt_offset] = '\0';
  314. q3m.msg_out_length[i] = pkt_offset;
  315. DEBUG("q3m.msg_out_length[%d] = %d\n", i, pkt_offset);
  316. }
  317. // packet with server list is ready
  318. return 1;
  319. }
  320. static int
  321. process_statusResponse(char *packet, int packetlen)
  322. {
  323. char *varname = NULL, *value = NULL;
  324. char *score = NULL, *ping = NULL, *name = NULL;
  325. int i;
  326. int server_dup = 0, done = 0;
  327. char *packetend = packet+packetlen;
  328. q3m_private_data_t *private_data = calloc(1, sizeof(q3m_private_data_t));
  329. q3m_private_data_t *oldprivdata;
  330. // check if source address is known
  331. for (i = 0; i < q3m.num_servers; i++) {
  332. if ((q3m.client.sin_addr.s_addr == q3m.list[i].ip.s_addr)
  333. && (q3m.client.sin_port == q3m.list[i].port)) {
  334. server_dup = 1;
  335. break;
  336. }
  337. }
  338. // source address not known
  339. if (server_dup == 0) {
  340. WARNING("unexpected \"statusResponse\" from %s:%d ignored\n",
  341. inet_ntoa(q3m.client.sin_addr), ntohs(q3m.client.sin_port));
  342. return -1;
  343. }
  344. oldprivdata = (q3m_private_data_t *)q3m.list[i].private_data;
  345. // go to 1st "\" which is after the command string
  346. packet = strpbrk(packet, "\\");
  347. if (packet == NULL) {
  348. WARNING("malformed statusResponse packet received from %s:%d!\n",
  349. inet_ntoa(q3m.client.sin_addr), ntohs(q3m.client.sin_port));
  350. return -1;
  351. }
  352. DEBUG("begin parsing server info\n");
  353. while ((++packet < packetend) && !done)
  354. {
  355. // get variable name
  356. varname = packet;
  357. // go to next delimiter
  358. packet = strpbrk(packet, "\\");
  359. if (packet == NULL) {
  360. ERRORV("malformed statusResponse packet received from %s:%d!\n",
  361. inet_ntoa(q3m.client.sin_addr), ntohs(q3m.client.sin_port));
  362. return -2;
  363. }
  364. // overwrite delimiter with \0
  365. *packet = '\0';
  366. // get value
  367. value = ++packet;
  368. // go to next delimiter
  369. packet = strpbrk(packet, "\\\n");
  370. if (packet == NULL) {
  371. ERRORV("malformed statusResponse packet received from %s:%d!\n",
  372. inet_ntoa(q3m.client.sin_addr), ntohs(q3m.client.sin_port));
  373. return -2;
  374. }
  375. // check if we're at the end of the server info section
  376. if (*packet == '\n') done = 1;
  377. // overwrite delimiter with \0
  378. *packet = '\0';
  379. DEBUG("varname = \"%s\", value = \"%s\"\n", varname, value);
  380. // parse varname and assign the value to the struct
  381. if (strcmp(varname, "challenge") == 0) {
  382. private_data->challenge = atoi(value);
  383. } else if (strcmp(varname, "sv_punkbuster") == 0) {
  384. private_data->sv_punkbuster = atoi(value);
  385. } else if (strcmp(varname, "g_maxGameClients") == 0) {
  386. private_data->g_maxGameClients = atoi(value);
  387. } else if (strcmp(varname, "capturelimit") == 0) {
  388. private_data->capturelimit = atoi(value);
  389. } else if (strcmp(varname, "sv_maxclients") == 0) {
  390. private_data->sv_maxclients = atoi(value);
  391. } else if (strcmp(varname, "timelimit") == 0) {
  392. private_data->timelimit = atoi(value);
  393. } else if (strcmp(varname, "fraglimit") == 0) {
  394. private_data->fraglimit = atoi(value);
  395. } else if (strcmp(varname, "dmflags") == 0) {
  396. private_data->dmflags = atoi(value);
  397. } else if (strcmp(varname, "sv_maxPing") == 0) {
  398. private_data->sv_maxPing = atoi(value);
  399. } else if (strcmp(varname, "sv_minPing") == 0) {
  400. private_data->sv_minPing = atoi(value);
  401. } else if (strcmp(varname, "sv_hostname") == 0) {
  402. private_data->sv_hostname = strdup(value);
  403. if (private_data->sv_hostname == NULL) {
  404. ERRORV("strdup() failed to get %d bytes!\n", strlen(value)+1);
  405. return -2;
  406. }
  407. } else if (strcmp(varname, "sv_maxRate") == 0) {
  408. private_data->sv_maxRate = atoi(value);
  409. } else if (strcmp(varname, "sv_floodProtect") == 0) {
  410. private_data->sv_floodProtect = atoi(value);
  411. } else if (strcmp(varname, "version") == 0) {
  412. private_data->version = strdup(value);
  413. if (private_data->version == NULL) {
  414. ERRORV("strdup() failed to get %d bytes!\n", strlen(value)+1);
  415. return -2;
  416. }
  417. } else if (strcmp(varname, "g_gametype") == 0) {
  418. private_data->g_gametype = atoi(value);
  419. } else if (strcmp(varname, "protocol") == 0) {
  420. private_data->protocol = atoi(value);
  421. } else if (strcmp(varname, "mapname") == 0) {
  422. private_data->mapname = strdup(value);
  423. if (private_data->mapname == NULL) {
  424. ERRORV("strdup() failed to get %d bytes!\n", strlen(value)+1);
  425. return -2;
  426. }
  427. } else if (strcmp(varname, "sv_privateClients") == 0) {
  428. private_data->sv_privateClients = atoi(value);
  429. } else if (strcmp(varname, "sv_allowDownload") == 0) {
  430. private_data->sv_allowDownload = atoi(value);
  431. } else if (strcmp(varname, "bot_minplayers") == 0) {
  432. private_data->bot_minplayers = atoi(value);
  433. } else if (strcmp(varname, "gamename") == 0) {
  434. private_data->gamename = strdup(value);
  435. if (private_data->gamename == NULL) {
  436. ERRORV("calloc() failed to get %d bytes!\n", strlen(value)+1);
  437. return -2;
  438. }
  439. } else if (strcmp(varname, "g_needpass") == 0) {
  440. private_data->g_needpass = atoi(value);
  441. } //else {
  442. // WARNING("unknown option \"%s\" in statusResponse ignored\n", varname);
  443. //}
  444. }
  445. DEBUG("end parsing server info\n");
  446. // parse player info
  447. private_data->_players = 0;
  448. while (++packet < packetend) {
  449. // FIXME: recover from realloc() failure
  450. private_data->_player = (q3m_player_data_t *) realloc(private_data->_player,
  451. (private_data->_players+1)*sizeof(q3m_player_data_t));
  452. if (private_data->_player == NULL) {
  453. ERRORV("realloc() failed trying to get %d bytes!\n",
  454. private_data->_players*sizeof(q3m_player_data_t));
  455. return -2;
  456. }
  457. // get player score
  458. score = packet;
  459. // go to next delimiter
  460. if ((packet = strpbrk(packet, " ")) == NULL) {
  461. ERRORV("malformed statusResponse packet received from %s:%d!\n",
  462. inet_ntoa(q3m.client.sin_addr), ntohs(q3m.client.sin_port));
  463. return -2;
  464. }
  465. // overwrite delimiter
  466. *packet = '\0';
  467. // parse player score
  468. private_data->_player[private_data->_players].score = atoi(score);
  469. // get player ping
  470. ping = ++packet;
  471. // go to next delimiter
  472. if ((packet = strpbrk(packet, " ")) == NULL) {
  473. ERRORV("malformed statusResponse packet received from %s:%d!\n",
  474. inet_ntoa(q3m.client.sin_addr), ntohs(q3m.client.sin_port));
  475. return -2;
  476. }
  477. // overwrite delimiter
  478. *packet = '\0';
  479. // parse player ping
  480. private_data->_player[private_data->_players].ping = atoi(ping);
  481. // get player name
  482. name = ++packet;
  483. // go to next delimiter
  484. if ((packet = strpbrk(packet, "\n")) == NULL) {
  485. ERRORV("malformed statusResponse packet received from %s:%d!\n",
  486. inet_ntoa(q3m.client.sin_addr), ntohs(q3m.client.sin_port));
  487. return -2;
  488. }
  489. // overwrite delimiter
  490. *packet = '\0';
  491. // parse player name
  492. private_data->_player[private_data->_players].name = strdup(name);
  493. if (private_data->_player[private_data->_players].name == NULL) {
  494. ERRORV("strdup() failed to get %d bytes!\n",
  495. strlen(name)+1);
  496. return -2;
  497. }
  498. DEBUG("player #%d name: \"%s\", ping: %d, score: %d\n",
  499. private_data->_players, private_data->_player[private_data->_players].name,
  500. private_data->_player[private_data->_players].ping,
  501. private_data->_player[private_data->_players].score);
  502. private_data->_players++;
  503. }
  504. // compare challenge to ours
  505. if (private_data->challenge != oldprivdata->_challenge) {
  506. WARNING("statusResponse challenge mismatch (%d != %d)\n",
  507. private_data->challenge, oldprivdata->_challenge);
  508. free_privdata(private_data);
  509. return -1;
  510. }
  511. // if we already have parsed server/player info we have to free it first
  512. if (q3m.list[i].private_data != NULL) free_privdata(q3m.list[i].private_data);
  513. q3m.list[i].private_data = private_data;
  514. return 0;
  515. }
  516. static int
  517. process_getmotd(char *packet, int packetlen)
  518. {
  519. char *version, *renderer, *challenge;
  520. char *varname = NULL, *value = NULL;
  521. char *packetend = packet+packetlen;
  522. packet += q3_pkt_header_len+q3_pkt_getmotd_len+2;
  523. while ((++packet < packetend) && (*packet != '\x0a')) {
  524. // save position as variable name
  525. varname = packet;
  526. // go to next delimiter
  527. packet = strpbrk(packet, "\\");
  528. if (packet == NULL) {
  529. WARNING("invalid \"getmotd\" from %s:%d received; ignored\n",
  530. inet_ntoa(q3m.client.sin_addr),
  531. ntohs(q3m.client.sin_port));
  532. return -1;
  533. }
  534. // overwrite delimiter with \0
  535. *packet = '\0';
  536. // save next position as value
  537. value = ++packet;
  538. // go to next delimiter
  539. packet = strpbrk(packet, "\\\"");
  540. if (packet == NULL) {
  541. WARNING("invalid \"getmotd\" from %s:%d received; ignored\n",
  542. inet_ntoa(q3m.client.sin_addr),
  543. ntohs(q3m.client.sin_port));
  544. return -1;
  545. }
  546. // overwrite delimiter with \0
  547. *packet = '\0';
  548. // parse
  549. if (strcmp(varname, "version") == 0) {
  550. version = strdup(value);
  551. if (version == NULL) {
  552. ERRORV("strdup() failed to get %d bytes!\n", strlen(value)+1);
  553. return -2;
  554. }
  555. } else if (strcmp(varname, "renderer") == 0) {
  556. renderer = strdup(value);
  557. if (renderer == NULL) {
  558. ERRORV("strdup() failed to get %d bytes!\n", strlen(value)+1);
  559. return -2;
  560. }
  561. } else if (strcmp(varname, "challenge") == 0) {
  562. challenge = strdup(value);
  563. if (challenge == NULL) {
  564. ERRORV("strdup() failed to get %d bytes!\n", strlen(value)+1);
  565. return -2;
  566. }
  567. } else {
  568. WARNING("unknown variable \"%s\" in \"getmotd\" packet ignored\n",
  569. varname);
  570. }
  571. }
  572. INFO("getmotd from %s:%d running \"%s\" with a \"%s\"\n",
  573. inet_ntoa(q3m.client.sin_addr), ntohs(q3m.client.sin_port),
  574. version, renderer);
  575. // we got all we need to assemble the motd packet
  576. q3m.msg_out = calloc(1, sizeof(char *));
  577. if (q3m.msg_out == NULL) {
  578. ERRORV("calloc() failed trying to get %d bytes!\n",
  579. sizeof(char *));
  580. return -2;
  581. }
  582. q3m.msg_out_length = calloc(1, sizeof(int));
  583. if (q3m.msg_out_length == NULL) {
  584. ERRORV("calloc() failed trying to get %d bytes!\n",
  585. sizeof(int));
  586. return -2;
  587. }
  588. q3m.msg_out_length[0] = q3_pkt_header_len+q3_pkt_motd_len
  589. + strlen(Q3M_MOTD)
  590. + (int)(sizeof(int)*2.5);
  591. q3m.msg_out[0] = calloc(q3m.msg_out_length[0]+1, 1);
  592. if (q3m.msg_out[0] == NULL) {
  593. ERRORV("calloc() failed trying to get %d bytes!\n",
  594. q3m.msg_out_length[0]);
  595. return -2;
  596. }
  597. q3m.num_msgs = 1;
  598. sprintf(q3m.msg_out[0], q3_pkt_motd, atoi(challenge), Q3M_MOTD);
  599. // clean up
  600. free(version);
  601. free(renderer);
  602. free(challenge);
  603. return 1;
  604. }
  605. static int
  606. process(char *packet, int packetlen)
  607. {
  608. int retval;
  609. // check if packet is q3a related
  610. if (strncmp(packet, q3_pkt_header, q3_pkt_header_len) == 0) {
  611. DEBUG("Q3A protocol marker detected!\n");
  612. // which packet did we receive?
  613. if (strcmp(packet+q3_pkt_header_len, q3_pkt_heartbeat) == 0) {
  614. retval = process_heartbeat(packet);
  615. if (retval == 1) {
  616. send_getstatus();
  617. return 1; // "send packet" code
  618. }
  619. return retval;
  620. } else if (strncmp(packet+q3_pkt_header_len, q3_pkt_getsrv, q3_pkt_getsrv_len) == 0) {
  621. return process_getservers(packet);
  622. } else if (strncmp(packet+q3_pkt_header_len, q3_pkt_statusrsp, q3_pkt_statusrsp_len) == 0) {
  623. return process_statusResponse(packet, packetlen);
  624. } else if (strncmp(packet+q3_pkt_header_len, q3_pkt_getmotd, q3_pkt_getmotd_len) == 0) {
  625. return process_getmotd(packet, packetlen);
  626. }
  627. WARNING("unknown packet received!\n");
  628. return -1;
  629. } // end if for 0xff 0xff 0xff 0xff marker
  630. WARNING("invalid packet received: Q3A protocol marker missing!\n");
  631. return -1; // invalid packet
  632. }
  633. static void
  634. cleanup(void)
  635. {
  636. int i;
  637. if (q3m.num_servers > 0) {
  638. for (i = 0; i < q3m.num_servers; i++) {
  639. free_privdata(q3m.list[i].private_data);
  640. }
  641. }
  642. }
  643. void
  644. init_plugin(void)
  645. {
  646. register_plugin(&q3m);
  647. }