Atrinik Client  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 
32 #include <global.h>
33 #include <toolkit/string.h>
34 #include <toolkit/curl.h>
35 
36 #include <libxml/parser.h>
37 #include <libxml/tree.h>
38 #include <libxml/valid.h>
39 #include <libxml/xmlschemas.h>
40 
41 #include <openssl/sha.h>
42 #include <openssl/err.h>
43 
55 #define XML_STR_EQUAL(s1, s2) \
56  xmlStrEqual((const xmlChar *) s1, (const xmlChar *) s2)
57 
58 #ifdef __MINGW32__
59 # define xmlFree free
60 #endif
61 
63 static int metaserver_connecting = 1;
65 static SDL_mutex *metaserver_connecting_mutex;
69 static size_t server_count;
71 static SDL_mutex *server_head_mutex;
73 static uint8_t enabled = 1;
75 static const char *cert_begin_str = "=========================="
76  " BEGIN INFORMATION "
77  "==========================";
79 static const char *cert_end_str = "=========================="
80  " END INFORMATION "
81  "==========================";
82 
86 void metaserver_init(void)
87 {
88  /* Initialize the data. */
89  server_head = NULL;
90  server_count = 0;
91 
92  /* Initialize mutexes. */
93  metaserver_connecting_mutex = SDL_CreateMutex();
94  server_head_mutex = SDL_CreateMutex();
95 
96  /* Initialize libxml2 */
97  LIBXML_TEST_VERSION
98 }
99 
104 {
105  enabled = 0;
107 }
108 
115 static void
117 {
118  HARD_ASSERT(info != NULL);
119 
120  if (info->name != NULL) {
121  efree(info->name);
122  }
123 
124  if (info->hostname != NULL) {
125  efree(info->hostname);
126  }
127 
128  if (info->ipv4_address != NULL) {
129  efree(info->ipv4_address);
130  }
131 
132  if (info->ipv6_address != NULL) {
133  efree(info->ipv6_address);
134  }
135 
136  if (info->pubkey != NULL) {
137  efree(info->pubkey);
138  }
139 
140  efree(info);
141 }
142 
149 static void
151 {
152  HARD_ASSERT(server != NULL);
153 
154  if (server->hostname != NULL) {
155  efree(server->hostname);
156  }
157 
158  if (server->name != NULL) {
159  efree(server->name);
160  }
161 
162  if (server->version != NULL) {
163  efree(server->version);
164  }
165 
166  if (server->desc != NULL) {
167  efree(server->desc);
168  }
169 
170  if (server->cert_pubkey != NULL) {
171  efree(server->cert_pubkey);
172  }
173 
174  if (server->cert != NULL) {
175  efree(server->cert);
176  }
177 
178  if (server->cert_sig != NULL) {
179  efree(server->cert_sig);
180  }
181 
182  if (server->cert_info != NULL) {
184  }
185 
186  efree(server);
187 }
188 
197 static bool
199 {
200  HARD_ASSERT(server != NULL);
201 
202  if (server->cert == NULL || server->cert_sig == NULL) {
203  /* No certificate. */
204  return true;
205  }
206 
207  /* Generate a SHA512 hash of the certificate's contents. */
208  unsigned char cert_digest[SHA512_DIGEST_LENGTH];
209  if (SHA512((unsigned char *) server->cert,
210  strlen(server->cert),
211  cert_digest) == NULL) {
212  LOG(ERROR, "SHA512() failed: %s",
213  ERR_error_string(ERR_get_error(), NULL));
214  return false;
215  }
216 
217  char cert_hash[SHA512_DIGEST_LENGTH * 2 + 1];
218  SOFT_ASSERT_RC(string_tohex(VS(cert_digest),
219  VS(cert_hash),
220  false) == sizeof(cert_hash) - 1,
221  false,
222  "string_tohex failed");
223  string_tolower(cert_hash);
224 
225  /* Verify the signature. */
226  if (!curl_verify(CURL_PKEY_TRUST_ULTIMATE,
227  cert_hash,
228  strlen(cert_hash),
229  server->cert_sig,
230  server->cert_sig_len)) {
231  LOG(ERROR, "Failed to verify signature");
232  return false;
233  }
234 
235  server_cert_info_t *info = ecalloc(1, sizeof(*info));
236 
237  char buf[MAX_BUF];
238  size_t pos = 0;
239  bool in_info = false;
240  while (string_get_word(server->cert, &pos, '\n', VS(buf), 0)) {
241  char *cp = buf;
242  string_skip_whitespace(cp);
243  string_strip_newline(cp);
244 
245  if (*cp == '\0') {
246  continue;
247  }
248 
249  if (strcmp(cp, cert_begin_str) == 0) {
250  in_info = true;
251  continue;
252  } else if (!in_info) {
253  continue;
254  } else if (strcmp(cp, cert_end_str) == 0) {
255  break;
256  }
257 
258  char *cps[2];
259  if (string_split(cp, cps, arraysize(cps), ':') != arraysize(cps)) {
260  LOG(ERROR, "Parsing error");
261  continue;
262  }
263 
264  string_tolower(cps[0]);
265  string_skip_whitespace(cps[1]);
266  const char *key = cps[0];
267  const char *value = cps[1];
268  char **content = NULL;
269  if (strcmp(key, "name") == 0) {
270  content = &info->name;
271  } else if (strcmp(key, "hostname") == 0) {
272  content = &info->hostname;
273  } else if (strcmp(key, "ipv4 address") == 0) {
274  content = &info->ipv4_address;
275  } else if (strcmp(key, "ipv6 address") == 0) {
276  content = &info->ipv6_address;
277  } else if (strcmp(key, "public key") == 0) {
278  content = &info->pubkey;
279  } else if (strcmp(key, "port") == 0) {
280  info->port = atoi(value);
281  } else if (strcmp(key, "crypto port") == 0) {
282  info->port_crypto = atoi(value);
283  } else {
284  LOG(DEVEL, "Unrecognized key: %s", key);
285  continue;
286  }
287 
288  if (content != NULL) {
289  StringBuffer *sb = stringbuffer_new();
290 
291  if (*content != NULL) {
292  stringbuffer_append_string(sb, *content);
293  stringbuffer_append_char(sb, '\n');
294  efree(*content);
295  }
296 
297  stringbuffer_append_string(sb, value);
298  *content = stringbuffer_finish(sb);
299  }
300  }
301 
302  /* Ensure we got the data we need. */
303  if (info->name == NULL ||
304  info->hostname == NULL ||
305  info->pubkey == NULL ||
306  info->port_crypto <= 0 ||
307  (info->ipv4_address == NULL) != (info->ipv6_address == NULL)) {
308  LOG(ERROR,
309  "Certificate is missing required data.");
310  goto error;
311  }
312 
313  /* Ensure certificate attributes match the advertised ones. */
314  if (strcmp(info->hostname, server->hostname) != 0) {
315  LOG(ERROR,
316  "Certificate hostname does not match advertised hostname.");
317  goto error;
318  }
319 
320  if (strcmp(info->name, server->name) != 0) {
321  LOG(ERROR,
322  "Certificate name does not match advertised name.");
323  goto error;
324  }
325 
326  if (info->port != server->port) {
327  LOG(ERROR,
328  "Certificate port does not match advertised port.");
329  goto error;
330  }
331 
332  if (info->port_crypto != server->port_crypto) {
333  LOG(ERROR,
334  "Certificate crypto port does not match advertised crypto port.");
335  goto error;
336  }
337 
338  server->cert_info = info;
339  return true;
340 
341 error:
342  metaserver_cert_free(info);
343  return false;
344 }
345 
356 static bool
357 parse_metaserver_data_node (xmlNodePtr node, server_struct *server)
358 {
359  HARD_ASSERT(node != NULL);
360  HARD_ASSERT(server != NULL);
361 
362  xmlChar *content = xmlNodeGetContent(node);
363  SOFT_ASSERT_LABEL(content != NULL && *content != '\0',
364  error,
365  "Parsing error");
366 
367  if (XML_STR_EQUAL(node->name, "Hostname")) {
368  SOFT_ASSERT_LABEL(server->hostname == NULL, error, "Parsing error");
369  server->hostname = estrdup((const char *) content);
370  } else if (XML_STR_EQUAL(node->name, "Port")) {
371  SOFT_ASSERT_LABEL(server->port == 0, error, "Parsing error");
372  server->port = atoi((const char *) content);
373  } else if (XML_STR_EQUAL(node->name, "PortCrypto")) {
374  SOFT_ASSERT_LABEL(server->port_crypto == -1, error, "Parsing error");
375  server->port_crypto = atoi((const char *) content);
376  } else if (XML_STR_EQUAL(node->name, "Name")) {
377  SOFT_ASSERT_LABEL(server->name == NULL, error, "Parsing error");
378  server->name = estrdup((const char *) content);
379  } else if (XML_STR_EQUAL(node->name, "PlayersCount")) {
380  SOFT_ASSERT_LABEL(server->player == 0, error, "Parsing error");
381  server->player = atoi((const char *) content);
382  } else if (XML_STR_EQUAL(node->name, "Version")) {
383  SOFT_ASSERT_LABEL(server->version == NULL, error, "Parsing error");
384  server->version = estrdup((const char *) content);
385  } else if (XML_STR_EQUAL(node->name, "TextComment")) {
386  SOFT_ASSERT_LABEL(server->desc == NULL, error, "Parsing error");
387  server->desc = estrdup((const char *) content);
388  } else if (XML_STR_EQUAL(node->name, "CertificatePublicKey")) {
389  SOFT_ASSERT_LABEL(server->cert_pubkey == NULL, error, "Parsing error");
390  server->cert_pubkey = estrdup((const char *) content);
391  } else if (XML_STR_EQUAL(node->name, "Certificate")) {
392  SOFT_ASSERT_LABEL(server->cert == NULL, error, "Parsing error");
393  server->cert = estrdup((const char *) content);
394  } else if (XML_STR_EQUAL(node->name, "CertificateSignature")) {
395  SOFT_ASSERT_LABEL(server->cert_sig == NULL, error, "Parsing error");
396  unsigned char *sig;
397  size_t sig_len;
398  if (!math_base64_decode((const char *) content, &sig, &sig_len)) {
399  LOG(ERROR, "Error decoding BASE64 certificate signature");
400  goto error;
401  }
402 
403  server->cert_sig = sig;
404  server->cert_sig_len = sig_len;
405  } else {
406  LOG(DEVEL, "Unrecognized node: %s", (const char *) node->name);
407  }
408 
409  bool ret = true;
410  goto out;
411 
412 error:
413  ret = false;
414 
415 out:
416  if (content != NULL) {
417  xmlFree(content);
418  }
419 
420  return ret;
421 }
422 
429 static void
430 parse_metaserver_node (xmlNodePtr node)
431 {
432  HARD_ASSERT(node != NULL);
433 
434  server_struct *server = ecalloc(1, sizeof(*server));
435  server->port_crypto = -1;
436  server->is_meta = true;
437 
438  for (xmlNodePtr tmp = node->children; tmp != NULL; tmp = tmp->next) {
439  if (!parse_metaserver_data_node(tmp, server)) {
440  goto error;
441  }
442  }
443 
444  if (server->hostname == NULL ||
445  server->port == 0 ||
446  server->name == NULL ||
447  server->version == NULL ||
448  server->desc == NULL) {
449  LOG(ERROR, "Incomplete data from metaserver");
450  goto error;
451  }
452 
453  if (!parse_metaserver_cert(server)) {
454  /* Logging already done */
455  goto error;
456  }
457 
458  SDL_LockMutex(server_head_mutex);
459  DL_PREPEND(server_head, server);
460  server_count++;
461  SDL_UnlockMutex(server_head_mutex);
462  return;
463 
464 error:
465  metaserver_free(server);
466 }
467 
478 static void
479 parse_metaserver_data_error (void *ctx, const char *format, ...)
480 {
481  va_list args;
482  va_start(args, format);
483  char formatted[HUGE_BUF * 32];
484  vsnprintf(VS(formatted), format, args);
485  string_strip_newline(formatted);
486  va_end(args);
487  LOG(ERROR, "%s", formatted);
488 }
489 
498 static void
499 parse_metaserver_data (const char *body, size_t body_size)
500 {
501  HARD_ASSERT(body != NULL);
502 
503  xmlSchemaParserCtxtPtr parser_ctx = NULL;
504  xmlSchemaPtr schema = NULL;
505  xmlSchemaValidCtxtPtr valid_ctx = NULL;
506 
507  xmlDocPtr doc = xmlReadMemory(body, body_size, "noname.xml", NULL, 0);
508  if (doc == NULL) {
509  LOG(ERROR, "Failed to parse data from metaserver");
510  goto out;
511  }
512 
513  parser_ctx = xmlSchemaNewParserCtxt("schemas/Atrinik-ADS-7.xsd");
514  if (parser_ctx == NULL) {
515  LOG(ERROR, "Failed to create a schema parser context");
516  goto out;
517  }
518 
519  schema = xmlSchemaParse(parser_ctx);
520  if (schema == NULL) {
521  LOG(ERROR, "Failed to parse schema file");
522  goto out;
523  }
524 
525  valid_ctx = xmlSchemaNewValidCtxt(schema);
526  if (valid_ctx == NULL) {
527  LOG(ERROR, "Failed to create a validation context");
528  goto out;
529  }
530 
531  xmlSetStructuredErrorFunc(NULL, NULL);
532  xmlSetGenericErrorFunc(NULL, parse_metaserver_data_error);
533  xmlThrDefSetStructuredErrorFunc(NULL, NULL);
534  xmlThrDefSetGenericErrorFunc(NULL, parse_metaserver_data_error);
535 
536  if (xmlSchemaValidateDoc(valid_ctx, doc) != 0) {
537  LOG(ERROR, "XML verification failed.");
538  goto out;
539  }
540 
541  xmlNodePtr root = xmlDocGetRootElement(doc);
542  if (root == NULL || !XML_STR_EQUAL(root->name, "Servers")) {
543  LOG(ERROR, "No servers element found in metaserver XML");
544  goto out;
545  }
546 
547  xmlNodePtr last = NULL;
548  for (xmlNodePtr node = root->children; node != NULL; node = node->next) {
549  last = node;
550  }
551 
552  for (xmlNodePtr node = last; node != NULL; node = node->prev) {
553  if (!XML_STR_EQUAL(node->name, "Server")) {
554  continue;
555  }
556 
557  parse_metaserver_node(node);
558  }
559 
560 out:
561  if (doc != NULL) {
562  xmlFreeDoc(doc);
563  }
564 
565  if (parser_ctx != NULL) {
566  xmlSchemaFreeParserCtxt(parser_ctx);
567  }
568 
569  if (schema != NULL) {
570  xmlSchemaFree(schema);
571  }
572 
573  if (valid_ctx != NULL) {
574  xmlSchemaFreeValidCtxt(valid_ctx);
575  }
576 }
577 
590 bool
591 metaserver_cert_verify_host (server_struct *server, const char *host)
592 {
593  HARD_ASSERT(server != NULL);
594  HARD_ASSERT(host != NULL);
595 
596  /* No certificate, nothing to verify. */
597  if (server->cert_info == NULL) {
598  return true;
599  }
600 
601  struct sockaddr_storage addr;
602  SOFT_ASSERT_RC(socket_host2addr(host, &addr), false,
603  "Failed to convert host to IP address");
604 
605  switch (addr.ss_family) {
606  case AF_INET:
607  if (strcmp(host, server->cert_info->ipv4_address) != 0) {
608  LOG(ERROR, "!!! Certificate IPv4 address error: %s != %s !!!",
609  host, server->cert_info->ipv4_address);
610  return false;
611  }
612 
613  break;
614 
615  case AF_INET6:
616  if (strcmp(host, server->cert_info->ipv6_address) != 0) {
617  LOG(ERROR, "!!! Certificate IPv6 address error: %s != %s !!!",
618  host, server->cert_info->ipv6_address);
619  return false;
620  }
621 
622  break;
623 
624  default:
625  LOG(ERROR, "!!! Unknown address family %u !!!", addr.ss_family);
626  return false;
627  }
628 
629  return true;
630 }
631 
640 {
641  server_struct *node;
642  size_t i;
643 
644  SDL_LockMutex(server_head_mutex);
645 
646  for (node = server_head, i = 0; node; node = node->next, i++) {
647  if (i == num) {
648  break;
649  }
650  }
651 
652  SDL_UnlockMutex(server_head_mutex);
653  return node;
654 }
655 
661 size_t server_get_count(void)
662 {
663  size_t count;
664 
665  SDL_LockMutex(server_head_mutex);
666  count = server_count;
667  SDL_UnlockMutex(server_head_mutex);
668  return count;
669 }
670 
678 int ms_connecting(int val)
679 {
680  int connecting;
681 
682  SDL_LockMutex(metaserver_connecting_mutex);
683  connecting = metaserver_connecting;
684 
685  /* More useful to return the old value than the one we're setting. */
686  if (val != -1) {
687  metaserver_connecting = val;
688  }
689 
690  SDL_UnlockMutex(metaserver_connecting_mutex);
691  return connecting;
692 }
693 
698 {
699  server_struct *node, *tmp;
700 
701  SDL_LockMutex(server_head_mutex);
702 
703  DL_FOREACH_SAFE(server_head, node, tmp)
704  {
705  DL_DELETE(server_head, node);
706  metaserver_free(node);
707  }
708 
709  server_count = 0;
710  SDL_UnlockMutex(server_head_mutex);
711 }
712 
731 metaserver_add (const char *hostname,
732  int port,
733  int port_crypto,
734  const char *name,
735  const char *version,
736  const char *desc)
737 {
738  server_struct *node = ecalloc(1, sizeof(*node));
739  node->player = -1;
740  node->port = port;
741  node->port_crypto = port_crypto;
742  node->hostname = estrdup(hostname);
743  node->name = estrdup(name);
744  node->version = estrdup(version);
745  node->desc = estrdup(desc);
746 
747  SDL_LockMutex(server_head_mutex);
748  DL_PREPEND(server_head, node);
749  server_count++;
750  SDL_UnlockMutex(server_head_mutex);
751 
752  return node;
753 }
754 
766 int metaserver_thread(void *dummy)
767 {
768  /* Go through all the metaservers in the list */
769  for (size_t i = clioption_settings.metaservers_num; i > 0; i--) {
770  /* Send a GET request to the metaserver */
771  curl_request_t *request =
772  curl_request_create(clioption_settings.metaservers[i - 1],
773  CURL_PKEY_TRUST_ULTIMATE);
774  curl_request_do_get(request);
775 
776  /* If the request succeeded, parse the metaserver data and break out. */
777  int http_code = curl_request_get_http_code(request);
778  size_t body_size;
779  char *body = curl_request_get_body(request, &body_size);
780  if (http_code == 200 && body != NULL) {
781  parse_metaserver_data(body, body_size);
782  curl_request_free(request);
783  break;
784  }
785 
786  curl_request_free(request);
787  }
788 
789  SDL_LockMutex(metaserver_connecting_mutex);
790  /* We're not connecting anymore. */
792  SDL_UnlockMutex(metaserver_connecting_mutex);
793  return 0;
794 }
795 
802 {
803  SDL_Thread *thread;
804 
805  if (!enabled) {
806  return;
807  }
808 
809  SDL_LockMutex(metaserver_connecting_mutex);
811  SDL_UnlockMutex(metaserver_connecting_mutex);
812 
813  thread = SDL_CreateThread(metaserver_thread, NULL);
814 
815  if (!thread) {
816  LOG(ERROR, "Thread creation failed.");
817  exit(1);
818  }
819 }
struct server_struct * next
Definition: main.h:59
unsigned char * cert_sig
Definition: main.h:95
char * hostname
Server hostname.
Definition: main.h:45
size_t cert_sig_len
Definition: main.h:100
static SDL_mutex * metaserver_connecting_mutex
Definition: metaserver.c:65
static void metaserver_free(server_struct *server)
Definition: metaserver.c:150
void metaserver_get_servers(void)
Definition: metaserver.c:801
char * ipv4_address
IPv4 address. Can be NULL.
Definition: main.h:50
bool metaserver_cert_verify_host(server_struct *server, const char *host)
Definition: metaserver.c:591
void metaserver_init(void)
Definition: metaserver.c:86
static void parse_metaserver_data(const char *body, size_t body_size)
Definition: metaserver.c:499
char * pubkey
Public key.
Definition: main.h:53
char * cert_pubkey
Definition: main.h:86
static int metaserver_connecting
Definition: metaserver.c:63
int player
Definition: main.h:77
char * name
Definition: main.h:65
server_struct * metaserver_add(const char *hostname, int port, int port_crypto, const char *name, const char *version, const char *desc)
Definition: metaserver.c:731
char * version
Definition: main.h:71
static bool parse_metaserver_cert(server_struct *server)
Definition: metaserver.c:198
static SDL_mutex * server_head_mutex
Definition: metaserver.c:71
char * desc
Definition: main.h:74
char * hostname
Definition: main.h:68
int metaserver_thread(void *dummy)
Definition: metaserver.c:766
int ms_connecting(int val)
Definition: metaserver.c:678
static void parse_metaserver_node(xmlNodePtr node)
Definition: metaserver.c:430
char * cert
Definition: main.h:89
server_struct * server_get_id(size_t num)
Definition: metaserver.c:639
int port
Regular port.
Definition: main.h:47
size_t server_get_count(void)
Definition: metaserver.c:661
server_cert_info_t * cert_info
Definition: main.h:103
char * ipv6_address
IPv6 address. Can be NULL.
Definition: main.h:51
void metaserver_clear_data(void)
Definition: metaserver.c:697
clioption_settings_struct clioption_settings
Definition: main.c:87
static curl_request_t * request
Definition: updater.c:95
static server_struct * server_head
Definition: metaserver.c:67
int port_crypto
Crypto port.
Definition: main.h:48
void metaserver_disable(void)
Definition: metaserver.c:103
int port
Definition: main.h:80
#define XML_STR_EQUAL(s1, s2)
Definition: metaserver.c:55
static bool parse_metaserver_data_node(xmlNodePtr node, server_struct *server)
Definition: metaserver.c:357
static const char * cert_begin_str
Definition: metaserver.c:75
static void metaserver_cert_free(server_cert_info_t *info)
Definition: metaserver.c:116
int port_crypto
Definition: main.h:83
static const char * cert_end_str
Definition: metaserver.c:79
static size_t server_count
Definition: metaserver.c:69
char * name
Server name.
Definition: main.h:44
bool is_meta
Definition: main.h:106
static uint8_t enabled
Definition: metaserver.c:73
static void parse_metaserver_data_error(void *ctx, const char *format,...)
Definition: metaserver.c:479