Atrinik Server  4.0
metaserver.c
Go to the documentation of this file.
1 /*************************************************************************
2  * Atrinik, a Multiplayer Online Role Playing Game *
3  * *
4  * Copyright (C) 2009-2014 Alex Tokar and Atrinik Development Team *
5  * *
6  * Fork from Crossfire (Multiplayer game for X-windows). *
7  * *
8  * This program is free software; you can redistribute it and/or modify *
9  * it under the terms of the GNU General Public License as published by *
10  * the Free Software Foundation; either version 2 of the License, or *
11  * (at your option) any later version. *
12  * *
13  * This program is distributed in the hope that it will be useful, *
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of *
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
16  * GNU General Public License for more details. *
17  * *
18  * You should have received a copy of the GNU General Public License *
19  * along with this program; if not, write to the Free Software *
20  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. *
21  * *
22  * The author can be reached at admin@atrinik.org *
23  ************************************************************************/
24 
30 #include <global.h>
31 #include <toolkit/string.h>
32 #include <toolkit/curl.h>
33 #include <toolkit/socket_crypto.h>
34 #include <player.h>
35 #include <openssl/rand.h>
36 #include <openssl/sha.h>
37 #include <openssl/err.h>
38 
42 static struct {
43  uint64_t num;
44 
45  uint64_t num_failed;
46 
47  time_t last;
48 
49  time_t last_failed;
50 } stats;
51 
55 #define METASERVER_KEY_FILE "metaserver_key"
56 
60 static pthread_mutex_t stats_lock;
61 
65 static curl_request_t *current_request = NULL;
69 static pthread_mutex_t request_lock;
73 static char *request_players = NULL;
77 static uint32_t request_num_players = 0;
81 static bool key_is_new = false;
82 
89 static bool
91 {
92  if (*settings.server_host == '\0') {
93  return false;
94  }
95 
96  if (settings.unit_tests) {
97  return false;
98  }
99 
100  return true;
101 }
102 
106 void
108 {
109  if (!metaserver_enabled()) {
110  return;
111  }
112 
114  pthread_mutex_init(&stats_lock, NULL);
115  pthread_mutex_init(&request_lock, NULL);
116 }
117 
121 void
123 {
124  if (!metaserver_enabled()) {
125  return;
126  }
127 
128  pthread_mutex_lock(&request_lock);
129  if (current_request != NULL) {
130  pthread_mutex_unlock(&request_lock);
131  curl_state_t state;
132  do {
133  pthread_mutex_lock(&request_lock);
134  if (current_request == NULL) {
135  pthread_mutex_unlock(&request_lock);
136  break;
137  }
138  state = curl_request_get_state(current_request);
139  pthread_mutex_unlock(&request_lock);
140  sleep(1);
141  } while (state == CURL_STATE_INPROGRESS);
142 
143  /* No other thread is working with the current request at this
144  * point. */
145  if (current_request != NULL) {
146  curl_request_free(current_request);
147  current_request = NULL;
148  }
149  } else {
150  pthread_mutex_unlock(&request_lock);
151  }
152 
153  if (request_players != NULL) {
154  efree(request_players);
155  request_players = NULL;
156  }
157 
158  pthread_mutex_destroy(&stats_lock);
159  pthread_mutex_destroy(&request_lock);
160 }
161 
170 static bool
171 metaserver_request_process_error (curl_request_t *request)
172 {
173  HARD_ASSERT(request != NULL);
174 
175  curl_state_t state = curl_request_get_state(request);
176  int http_code = curl_request_get_http_code(request);
177  if (state == CURL_STATE_OK && http_code == 200) {
178  return false;
179  }
180 
181  char *body = curl_request_get_body(request, NULL);
182  LOG(SYSTEM,
183  "Failed to update metaserver information "
184  "(HTTP code: %d), response: %s",
185  http_code,
186  body != NULL ? body : "<empty>");
187 
188  pthread_mutex_lock(&stats_lock);
189  stats.last_failed = time(NULL);
190  stats.num_failed++;
191  pthread_mutex_unlock(&stats_lock);
192  return true;
193 }
194 
203 static void
204 metaserver_update_request (curl_request_t *request, void *user_data)
205 {
206  pthread_mutex_lock(&request_lock);
207  current_request = NULL;
208 
209  if (metaserver_request_process_error(request)) {
210  /* If we had a new key generated, remove it so that it will be
211  * re-created, since it was rejected. */
212  if (key_is_new) {
213  char path[HUGE_BUF];
214  snprintf(VS(path), "%s/" METASERVER_KEY_FILE, settings.datapath);
215 
216  if (unlink(path) != 0) {
217  LOG(ERROR, "Failed to unlink %s: %s (%d)",
218  path, strerror(errno), errno);
219  }
220 
221  key_is_new = false;
222  }
223 
224  goto out;
225  }
226 
227  pthread_mutex_lock(&stats_lock);
228  stats.last = time(NULL);
229  stats.num++;
230  pthread_mutex_unlock(&stats_lock);
231 
232 out:
233  curl_request_free(request);
234  pthread_mutex_unlock(&request_lock);
235 }
236 
251 static bool
253  size_t key_size,
254  const char *otp,
255  const char *cotp)
256 {
257  HARD_ASSERT(key != NULL);
258  HARD_ASSERT(key_size == SHA512_DIGEST_LENGTH * 2 + 1);
259 
260  unsigned char tmp_key[SHA512_DIGEST_LENGTH];
261 
262  char path[HUGE_BUF];
263  snprintf(VS(path), "%s/" METASERVER_KEY_FILE, settings.datapath);
264  FILE *fp = fopen(path, "rb");
265  if (fp == NULL && errno == ENOENT) {
266  fp = fopen(path, "wb");
267  if (fp == NULL) {
268  LOG(ERROR, "Failed to open %s for writing: %s (%d)",
269  path, strerror(errno), errno);
270  return false;
271  }
272 
273  unsigned char bytes[64];
274 
275  if (chmod(path, S_IRUSR | S_IWUSR) != 0) {
276  LOG(ERROR, "Failed to chmod %s: %s (%d)",
277  path, strerror(errno), errno);
278  goto error_creating;
279  }
280 
281  if (RAND_bytes(VS(bytes)) != 1) {
282  LOG(ERROR, "RAND_bytes() failed: %s",
283  ERR_error_string(ERR_get_error(), NULL));
284  goto error_creating;
285  }
286 
287  if (SHA512(VS(bytes), tmp_key) == NULL) {
288  LOG(ERROR, "SHA512() failed: %s",
289  ERR_error_string(ERR_get_error(), NULL));
290  goto error_creating;
291  }
292 
293  memset(&bytes, 0, sizeof(bytes));
294  key[SHA512_DIGEST_LENGTH] = '\0';
295 
296  if (fwrite(VS(tmp_key), 1, fp) != 1) {
297  LOG(ERROR, "Failed to write to %s: %s (%d)",
298  path, strerror(errno), errno);
299  goto error_creating;
300  }
301 
302  if (fclose(fp) != 0) {
303  LOG(ERROR, "Failed to close %s: %s (%d)",
304  path, strerror(errno), errno);
305  goto error_creating;
306  }
307 
308  fp = NULL;
309 
310  SOFT_ASSERT_LABEL(string_tohex(VS(tmp_key),
311  key,
312  key_size,
313  false) == key_size - 1,
314  error_creating,
315  "string_tohex failed");
316  string_tolower(key);
317  key_is_new = true;
318 
319  return true;
320 
321 error_creating:
322  if (unlink(path) != 0) {
323  LOG(ERROR, "Failed to unlink %s: %s (%d)",
324  path, strerror(errno), errno);
325  }
326 
327  if (fp != NULL) {
328  fclose(fp);
329  }
330 
331  memset(&bytes, 0, sizeof(bytes));
332  memset(&tmp_key, 0, sizeof(tmp_key));
333  memset(key, 0, key_size);
334  return false;
335  } else if (fp == NULL) {
336  LOG(ERROR, "Failed to open %s for reading: %s (%d)",
337  path, strerror(errno), errno);
338  return false;
339  }
340 
341  key_is_new = false;
342 
343  if (fread(VS(tmp_key), 1, fp) != 1) {
344  LOG(ERROR, "Failed to read from %s: %s (%d)",
345  path, strerror(errno), errno);
346  goto error_reading;
347  }
348 
349  SOFT_ASSERT_LABEL(string_tohex(VS(tmp_key),
350  key,
351  key_size,
352  false) == key_size - 1,
353  error_reading,
354  "string_tohex failed");
355  string_tolower(key);
356 
357  SHA512_CTX ctx;
358  if (SHA512_Init(&ctx) != 1) {
359  LOG(ERROR, "SHA512_Init() failed: %s",
360  ERR_error_string(ERR_get_error(), NULL));
361  goto error_reading;
362  }
363 
364  if (SHA512_Update(&ctx, key, key_size - 1) != 1) {
365  LOG(ERROR, "SHA512_Update() failed: %s",
366  ERR_error_string(ERR_get_error(), NULL));
367  goto error_reading;
368  }
369 
370  if (SHA512_Update(&ctx,
372  strlen(settings.server_host)) != 1) {
373  LOG(ERROR, "SHA512_Update() failed: %s",
374  ERR_error_string(ERR_get_error(), NULL));
375  goto error_reading;
376  }
377 
378  if (SHA512_Final(tmp_key, &ctx) != 1) {
379  LOG(ERROR, "SHA512_Final() failed: %s",
380  ERR_error_string(ERR_get_error(), NULL));
381  goto error_reading;
382  }
383 
384  SOFT_ASSERT_LABEL(string_tohex(VS(tmp_key),
385  key,
386  key_size,
387  false) == key_size - 1,
388  error_reading,
389  "string_tohex failed");
390  string_tolower(key);
391 
392  if (fclose(fp) != 0) {
393  LOG(ERROR, "Failed to close %s: %s (%d)",
394  path, strerror(errno), errno);
395  goto error_reading;
396  }
397 
398  if (SHA512_Init(&ctx) != 1) {
399  LOG(ERROR, "SHA512_Init() failed: %s",
400  ERR_error_string(ERR_get_error(), NULL));
401  goto error_reading;
402  }
403 
404  if (SHA512_Update(&ctx, otp, strlen(otp)) != 1) {
405  LOG(ERROR, "SHA512_Update() failed: %s",
406  ERR_error_string(ERR_get_error(), NULL));
407  goto error_reading;
408  }
409 
410  if (SHA512_Update(&ctx, key, key_size - 1) != 1) {
411  LOG(ERROR, "SHA512_Update() failed: %s",
412  ERR_error_string(ERR_get_error(), NULL));
413  goto error_reading;
414  }
415 
416  if (SHA512_Update(&ctx, cotp, strlen(cotp)) != 1) {
417  LOG(ERROR, "SHA512_Update() failed: %s",
418  ERR_error_string(ERR_get_error(), NULL));
419  goto error_reading;
420  }
421 
422  if (SHA512_Final(tmp_key, &ctx) != 1) {
423  LOG(ERROR, "SHA512_Final() failed: %s",
424  ERR_error_string(ERR_get_error(), NULL));
425  goto error_reading;
426  }
427 
428  SOFT_ASSERT_LABEL(string_tohex(VS(tmp_key),
429  key,
430  key_size,
431  false) == key_size - 1,
432  error_reading,
433  "string_tohex failed");
434  string_tolower(key);
435 
436  return true;
437 
438 error_reading:
439  memset(key, 0, key_size);
440  memset(&tmp_key, 0, sizeof(tmp_key));
441  memset(&ctx, 0, sizeof(ctx));
442 
443  return false;
444 }
445 
454 static void
455 metaserver_otp_request (curl_request_t *request, void *user_data)
456 {
457  pthread_mutex_lock(&request_lock);
458  current_request = NULL;
459 
460  if (metaserver_request_process_error(request)) {
461  goto out;
462  }
463 
464  char *body = curl_request_get_body(request, NULL);
465  if (body == NULL) {
466  LOG(ERROR, "Failed to receive an OTP from metaserver");
467  goto out;
468  }
469 
470  const char *otp_identifier = "\"otp\": \"";
471  const char *otp_pos = strstr(body, otp_identifier);
472  if (otp_pos == NULL) {
473  LOG(ERROR, "Malformed OTP response");
474  goto out;
475  }
476 
477  /* Jump over the OTP identifier */
478  otp_pos += strlen(otp_identifier);
479 
480  const char *otp_end_pos = strstr(otp_pos, "\"");
481  if (otp_end_pos == NULL) {
482  LOG(ERROR, "Malformed OTP response");
483  goto out;
484  }
485 
486  size_t otp_length = otp_end_pos - otp_pos;
487  if (otp_length == 0) {
488  LOG(ERROR, "Malformed OTP response");
489  goto out;
490  }
491 
492  unsigned char cotp[32];
493  if (RAND_bytes(VS(cotp)) != 1) {
494  LOG(ERROR, "RAND_bytes() failed: %s",
495  ERR_error_string(ERR_get_error(), NULL));
496  goto out;
497  }
498 
499  unsigned char cotp_digest[SHA512_DIGEST_LENGTH];
500  if (SHA512(VS(cotp), cotp_digest) == NULL) {
501  LOG(ERROR, "SHA512() failed: %s",
502  ERR_error_string(ERR_get_error(), NULL));
503  goto out;
504  }
505 
506  char cotp_hash[SHA512_DIGEST_LENGTH * 2 + 1];
507  SOFT_ASSERT_LABEL(string_tohex(VS(cotp_digest),
508  VS(cotp_hash),
509  false) == sizeof(cotp_hash) - 1,
510  out,
511  "string_tohex failed");
512  string_tolower(cotp_hash);
513 
514  char *otp = estrndup(body + (otp_pos - body), otp_length);
515 
516  char key[SHA512_DIGEST_LENGTH * 2 + 1];
517  if (!metaserver_get_key(VS(key), otp, cotp_hash)) {
518  efree(otp);
519  goto out;
520  }
521 
522  char url[MAX_BUF];
523  snprintf(VS(url), "%s/update", settings.metaserver_url);
524  current_request = curl_request_create(url, CURL_PKEY_TRUST_ULTIMATE);
525  curl_request_set_cb(current_request, metaserver_update_request, NULL);
526 
527  curl_request_form_add(current_request, "hostname",
529  curl_request_form_add(current_request, "version",
530  PACKAGE_VERSION);
531  curl_request_form_add(current_request, "text_comment",
533  curl_request_form_add(current_request, "name",
535  curl_request_form_add(current_request, "otp",
536  otp);
537  curl_request_form_add(current_request, "cotp",
538  cotp_hash);
539  curl_request_form_add(current_request, "key",
540  key);
541  curl_request_form_add(current_request, "ptr_check",
542  "");
543  curl_request_form_add(current_request, "players",
545 
546  char buf[32];
547  snprintf(VS(buf), "%" PRIu32, request_num_players);
548  curl_request_form_add(current_request, "num_players", buf);
549 
550  snprintf(VS(buf), "%" PRIu16, settings.port);
551  curl_request_form_add(current_request, "port", buf);
552 
553  if (socket_crypto_enabled()) {
554  snprintf(VS(buf), "%" PRIu16, settings.port_crypto);
555  curl_request_form_add(current_request, "port_crypto", buf);
556 
557  const char *cert_pubkey = socket_crypto_get_cert_pubkey();
558  if (cert_pubkey != NULL) {
559  curl_request_form_add(current_request, "cert_pubkey", cert_pubkey);
560  }
561 
562  /* Add the server certificate and its signature, if configured. */
563  if (settings.server_cert != NULL &&
564  settings.server_cert_sig != NULL) {
565  curl_request_form_add(current_request,
566  "server_cert",
568  curl_request_form_add(current_request,
569  "server_cert_sig",
571  }
572  }
573 
574  /* Send off the POST request */
575  curl_request_start_post(current_request);
576 
577  efree(otp);
578 
579 out:
580  curl_request_free(request);
581  pthread_mutex_unlock(&request_lock);
582 }
583 
587 void
589 {
590  if (!metaserver_enabled()) {
591  return;
592  }
593 
594  pthread_mutex_lock(&request_lock);
595 
596  if (current_request != NULL) {
597  curl_state_t state = curl_request_get_state(current_request);
598  if (state == CURL_STATE_INPROGRESS) {
599  pthread_mutex_unlock(&request_lock);
600  return;
601  }
602 
603  curl_request_free(current_request);
604  }
605 
606  pthread_mutex_unlock(&request_lock);
607 
609  StringBuffer *sb = stringbuffer_new();
610  for (player *pl = first_player; pl != NULL; pl = pl->next) {
611  if (stringbuffer_length(sb) != 0) {
612  stringbuffer_append_string(sb, ":");
613  }
614 
615  stringbuffer_append_string(sb, pl->quick_name);
617  }
618 
619  if (request_players != NULL) {
620  efree(request_players);
621  }
622  request_players = stringbuffer_finish(sb);
623 
624  char url[MAX_BUF];
625  snprintf(VS(url), "%s/otp", settings.metaserver_url);
626  /* If we're at this point, no other thread is currently working with
627  * the current request and thus a lock is not necessary. */
628  /* coverity[missing_lock] */
629  current_request = curl_request_create(url, CURL_PKEY_TRUST_ULTIMATE);
630  curl_request_set_cb(current_request, metaserver_otp_request, NULL);
631  curl_request_start_get(current_request);
632 }
633 
642 void
643 metaserver_stats (char *buf, size_t size)
644 {
645  pthread_mutex_lock(&stats_lock);
646  snprintfcat(buf, size, "\n=== METASERVER ===\n");
647  snprintfcat(buf, size, "\nUpdates: %" PRIu64, stats.num);
648  snprintfcat(buf, size, "\nFailed: %" PRIu64, stats.num_failed);
649 
650  if (stats.last != 0) {
651  snprintfcat(buf, size, "\nLast update: %.19s", ctime(&stats.last));
652  }
653 
654  if (stats.last_failed != 0) {
655  snprintfcat(buf, size,
656  "\nLast failure: %.19s",
657  ctime(&stats.last_failed));
658  }
659 
660  snprintfcat(buf, size, "\n");
661  pthread_mutex_unlock(&stats_lock);
662 }
static void metaserver_otp_request(curl_request_t *request, void *user_data)
Definition: metaserver.c:455
static bool metaserver_request_process_error(curl_request_t *request)
Definition: metaserver.c:171
static void metaserver_update_request(curl_request_t *request, void *user_data)
Definition: metaserver.c:204
#define METASERVER_KEY_FILE
Definition: metaserver.c:55
static char * request_players
Definition: metaserver.c:73
char datapath[MAX_BUF]
Definition: global.h:343
char metaserver_url[MAX_BUF]
Definition: global.h:363
bool unit_tests
Definition: global.h:388
static pthread_mutex_t stats_lock
Definition: metaserver.c:60
char * server_cert_sig
Definition: global.h:474
void metaserver_init(void)
Definition: metaserver.c:107
uint64_t num_failed
Number of failed updates.
Definition: metaserver.c:45
void metaserver_info_update(void)
Definition: metaserver.c:588
uint16_t port_crypto
Definition: global.h:333
uint16_t port
Definition: global.h:328
static curl_request_t * current_request
Definition: metaserver.c:65
char server_host[MAX_BUF]
Definition: global.h:368
static bool metaserver_enabled(void)
Definition: metaserver.c:90
static pthread_mutex_t request_lock
Definition: metaserver.c:69
char * server_cert
Definition: global.h:469
uint64_t num
Number of successful updates.
Definition: metaserver.c:43
char server_desc[MAX_BUF]
Definition: global.h:378
time_t last_failed
Last failed update.
Definition: metaserver.c:49
struct settings_struct settings
Definition: init.c:55
void metaserver_stats(char *buf, size_t size)
Definition: metaserver.c:643
char server_name[MAX_BUF]
Definition: global.h:373
void metaserver_deinit(void)
Definition: metaserver.c:122
static bool key_is_new
Definition: metaserver.c:81
player * first_player
Definition: main.c:57
static uint32_t request_num_players
Definition: metaserver.c:77
static struct @14 stats
struct pl_player * next
Definition: player.h:145
time_t last
Last successful update.
Definition: metaserver.c:47
static bool metaserver_get_key(char *key, size_t key_size, const char *otp, const char *cotp)
Definition: metaserver.c:252