Crossfire Client, Trunk  R19545
metaserver.c
Go to the documentation of this file.
1 /*
2  * Crossfire -- cooperative multi-player graphical RPG and adventure game
3  *
4  * Copyright (c) 1999-2013 Mark Wedel and the Crossfire Development Team
5  * Copyright (c) 1992 Frank Tore Johansen
6  *
7  * Crossfire is free software and comes with ABSOLUTELY NO WARRANTY. You are
8  * welcome to redistribute it under certain conditions. For details, please
9  * see COPYING and LICENSE.
10  *
11  * The authors can be reached via e-mail at <crossfire@metalforge.org>.
12  */
13 
21 #ifndef WIN32
22 #include <netdb.h>
23 #include <sys/types.h>
24 #include <sys/socket.h>
25 #include <netinet/in.h>
26 #include <arpa/inet.h>
27 #endif /* WIN32 */
28 
29 #include <ctype.h>
30 #include <stdio.h>
31 #include <stdlib.h>
32 
33 #include <client.h>
34 #include <external.h>
35 
36 #include <metaserver.h>
37 
38 #ifdef HAVE_CURL_CURL_H
39 #include <curl/curl.h>
40 #include <curl/easy.h>
41 #endif
42 
44 
46 
48 {
49  return g_ascii_strcasecmp(m1->hostname, m2->hostname);
50 }
51 
63 int check_server_version(int entry)
64 {
65 
66  /* No version information - nothing to do. */
67  if (!meta_servers[entry].sc_version || !meta_servers[entry].cs_version) {
68  return 1;
69  }
70 
71  if (meta_servers[entry].sc_version != VERSION_SC) {
72  /* 1027->1028 removed a bunch of old commands, so a 1028
73  * version client can still play on a 1027 server, so
74  * special hard code that.
75  *
76  * Likewise, 1028->1029 just changed how weapon_speed
77  * should be interperted on the client - the client
78  * does the right thing, so not problem with a 1029
79  * client playing on 1028 or 1027 server.
80  *
81  * A 1028 client could in practice play on a 1029
82  * server, since at the protocol level, data is the same -
83  * the client would just have screwed up weapon_sp values.
84  */
85  if ((VERSION_SC == 1028 || VERSION_SC==1029) &&
86  (meta_servers[entry].sc_version==1027 ||
87  meta_servers[entry].sc_version==1028)) {
88  return 1;
89  }
90  }
91  if (meta_servers[entry].cs_version != VERSION_CS) {
92  return 0;
93  }
94 
95  return 1;
96 }
97 
98 /*****************************************************************************
99  * Start of cache related functions.
100  *****************************************************************************/
104 static int cached_servers_loaded = 0;
105 const char *cached_server_file = NULL;
106 
121 static void metaserver_load_cache(void)
122 {
123  char name[MS_LARGE_BUF], ip[MS_LARGE_BUF];
124  FILE *cache;
125 
127  return;
128  }
129 
130  /* If failure, we don't want to load again */
132  cached_servers_num = 0;
133 
134  cache = fopen(cached_server_file, "r");
135  if (!cache) {
136  return;
137  }
138 
140  && fgets(name, MS_LARGE_BUF, cache) != NULL
141  && fgets(ip , MS_LARGE_BUF, cache) != NULL) {
142  ip[strlen(ip)-1] = 0;
143  name[strlen(name)-1] = 0;
146  }
147  fclose(cache);
148 }
149 
153 static void metaserver_save_cache(void)
154 {
155  FILE *cache;
156  int server;
157 
158  if (!cached_server_file) {
159  return;
160  }
161 
162  cache = fopen(cached_server_file, "w");
163  if (!cache) {
164  return;
165  }
166 
167  for (server = 0; server < cached_servers_num; server++) {
168  fprintf(cache, "%s\n", cached_servers_name[server]);
169  fprintf(cache, "%s\n", cached_servers_ip[server]);
170  }
171  fclose(cache);
172 }
173 
179 void metaserver_update_cache(const char *server_name, const char *server_ip)
180 {
181  int index;
182 
183  /*
184  * Try to find the given server name in the existing server cache. If the
185  * zero-based index ends up equal to the one-based number of cached
186  * servers, it was not found.
187  */
188  for (index = 0; index < cached_servers_num; index++) {
189  if (strcmp(server_name, cached_servers_name[index]) == 0) {
190  break;
191  }
192  }
193 
194  /*
195  * If server is already first in the cache list, nothing else needs to be
196  * done, otherwise, the server needs to be cached.
197  */
198  if (index != 0 || !cached_servers_num) {
199  char *name;
200  char *ip;
201  int copy;
202 
203  if (index == cached_servers_num) {
204  /*
205  * If the server was not found in the cache, expand the cache size
206  * by one unless that creates too many entries.
207  */
208  name = g_strdup(server_name);
209  ip = g_strdup(server_ip);
210  cached_servers_num++;
211  if (cached_servers_num > CACHED_SERVERS_MAX) {
212  cached_servers_num--;
213  free(cached_servers_name[cached_servers_num-1]);
214  free(cached_servers_ip[cached_servers_num-1]);
215  }
216  } else {
217  /*
218  * If the server was already listed in the cache, grab a copy of
219  * the prior listing.
220  */
221  name = cached_servers_name[index];
222  ip = cached_servers_ip[index];
223  }
224 
225  /*
226  * If the server as already listed, move all the cached items above
227  * the listing down a slot, otherwise, move the whole list down a
228  * notch. This "empties" the top slot.
229  */
230  for (copy = MIN(index, CACHED_SERVERS_MAX-1); copy > 0; copy--) {
232  cached_servers_ip[copy] = cached_servers_ip[copy-1];
233  }
234 
235  /*
236  * Put the added server information at the top of the cache list, and
237  * save the changes.
238  */
240  cached_servers_ip[0] = ip;
242  }
243 }
244 
245 /*****************************************************************************
246  * End of cache related functions.
247  *****************************************************************************/
248 
249 /******************************************************************************
250  * Metaserver2 support starts here.
251  *
252  ******************************************************************************/
253 
254 pthread_mutex_t ms2_info_mutex;
255 
256 /* we use threads so that the GUI keeps responding while we wait for
257  * data. But we need to note if the thread is running or not,
258  * so we store it here. This, like the other metaserver2 data,
259  * should be protected by using the ms2_info_mutext.
260  */
261 static int ms2_is_running=0;
262 
263 /* list of metaserver URL to get information from - this should generally
264  * correspond to the value in the metaserver2 server file, but instead
265  * of meta_update.php, use meta_client.php.
266  *
267  * These could perhaps be in some other file (config.h or the like), but
268  * it seems unlikely that these will change very often, and certainly not
269  * at a level where we would expect users to go about changing the values.
270  */
271 static char *metaservers[] = {"http://crossfire.real-time.com/metaserver2/meta_client.php"};
272 
297 size_t metaserver2_writer(void *ptr, size_t size, size_t nmemb, void *data)
298 {
299 #ifdef HAVE_CURL_CURL_H
300  size_t realsize = size * nmemb;
301  char *cp, *newline, *eq, inbuf[CURL_MAX_WRITE_SIZE*2+1], *leftover;
302 
303  leftover = (char*) data;
304 
305  if (realsize > CURL_MAX_WRITE_SIZE) {
306  LOG(LOG_CRITICAL, "common::metaserver2_writer", "Function called with more data than allowed!");
307  }
308 
309  /* This memcpy here is to just give us a null terminated character
310  * array - easier to do with than having to check lengths as well as other
311  * values. Also, it makes it easier to deal with unprocessed data from
312  * the last call.
313  */
314  memcpy(inbuf, leftover, strlen(leftover));
315  memcpy(inbuf+strlen(leftover), ptr, realsize);
316  inbuf[strlen(leftover)+realsize] = 0;
317  leftover[0] =0;
318 
319  /* Processing this block of data shouldn't take very long, even on
320  * slow machines, so putting the lock here, instead of each time
321  * we update a variable is cleaner
322  */
323  pthread_mutex_lock(&ms2_info_mutex);
324 
325  for (cp = inbuf; cp != NULL && *cp!=0; cp=newline) {
326  newline=strchr(cp, '\n');
327  if (newline) {
328  *newline = 0;
329  newline++;
330  } else {
331  /* If we didn't get a newline, then this is the
332  * end of the block of data for this call - store
333  * away the extra for the next call.
334  */
335  strncpy(leftover, cp, CURL_MAX_WRITE_SIZE-1);
336  leftover[CURL_MAX_WRITE_SIZE-1] = 0;
337  break;
338  }
339 
340  eq = strchr(cp,'=');
341  if (eq) {
342  *eq = 0;
343  eq++;
344  }
345 
346  if (!strcmp(cp, "START_SERVER_DATA")) {
347  /* Clear out all data - MS2 doesn't necessarily use all the
348  * fields, so blank out any that we are not using.
349  */
350  memset(&meta_servers[meta_numservers], 0, sizeof(Meta_Info));
351  } else if (!strcmp(cp, "END_SERVER_DATA")) {
352  int i;
353 
354  /* we can get data from both metaserver1 & 2 - no reason to keep
355  * both. So check for duplicates, and consider metaserver2
356  * data 'better'.
357  */
358  for (i=0; i<meta_numservers; i++) {
359  if (!g_ascii_strcasecmp(meta_servers[i].hostname, meta_servers[meta_numservers].hostname)) {
360  memcpy(&meta_servers[i], &meta_servers[meta_numservers], sizeof(Meta_Info));
361  break;
362  }
363  }
364  if (i>=meta_numservers) {
365  meta_numservers++;
366  }
367  } else {
368  /* If we get here, these should be variable=value pairs.
369  * if we don't have a value, can't do anything, and
370  * report an error. This would normally be incorrect
371  * data from the server.
372  */
373  if (!eq) {
374  LOG(LOG_ERROR, "common::metaserver2_writer", "Unknown line: %s",cp);
375  continue;
376  }
377  if (!strcmp(cp,"hostname")) {
378  strncpy(meta_servers[meta_numservers].hostname, eq, sizeof(meta_servers[meta_numservers].hostname));
379  } else if (!strcmp(cp,"port")) {
380  meta_servers[meta_numservers].port = atoi(eq);
381  } else if (!strcmp(cp,"html_comment")) {
382  strncpy(meta_servers[meta_numservers].html_comment, eq, sizeof(meta_servers[meta_numservers].html_comment));
383  } else if (!strcmp(cp,"text_comment")) {
384  strncpy(meta_servers[meta_numservers].text_comment, eq, sizeof(meta_servers[meta_numservers].text_comment));
385  } else if (!strcmp(cp,"archbase")) {
386  strncpy(meta_servers[meta_numservers].archbase, eq, sizeof(meta_servers[meta_numservers].archbase));
387  } else if (!strcmp(cp,"mapbase")) {
388  strncpy(meta_servers[meta_numservers].mapbase, eq, sizeof(meta_servers[meta_numservers].mapbase));
389  } else if (!strcmp(cp,"codebase")) {
390  strncpy(meta_servers[meta_numservers].codebase, eq, sizeof(meta_servers[meta_numservers].codebase));
391  } else if (!strcmp(cp,"flags")) {
392  strncpy(meta_servers[meta_numservers].flags, eq, sizeof(meta_servers[meta_numservers].flags));
393  } else if (!strcmp(cp,"version")) {
394  strncpy(meta_servers[meta_numservers].version, eq, sizeof(meta_servers[meta_numservers].version));
395  } else if (!strcmp(cp,"num_players")) {
396  meta_servers[meta_numservers].num_players = atoi(eq);
397  } else if (!strcmp(cp,"in_bytes")) {
398  meta_servers[meta_numservers].in_bytes = atoi(eq);
399  } else if (!strcmp(cp,"out_bytes")) {
400  meta_servers[meta_numservers].out_bytes = atoi(eq);
401  } else if (!strcmp(cp,"uptime")) {
402  meta_servers[meta_numservers].uptime = atoi(eq);
403  } else if (!strcmp(cp,"sc_version")) {
404  meta_servers[meta_numservers].sc_version = atoi(eq);
405  } else if (!strcmp(cp,"cs_version")) {
406  meta_servers[meta_numservers].cs_version = atoi(eq);
407  } else if (!strcmp(cp,"last_update")) {
408  /* MS2 reports update time as when it last got an update,
409  * where as we want actual elapsed time since last update.
410  * So do the conversion. Second check is because of clock
411  * skew - my clock may be fast, and we don't want negative times.
412  */
413  meta_servers[meta_numservers].idle_time = time(NULL) - atoi(eq);
414  if (meta_servers[meta_numservers].idle_time < 0) {
415  meta_servers[meta_numservers].idle_time = 0;
416  }
417  } else {
418  LOG(LOG_ERROR, "common::metaserver2_writer", "Unknown line: %s=%s",cp,eq);
419  }
420  }
421  }
422  pthread_mutex_unlock(&ms2_info_mutex);
423  return realsize;
424 #else
425  return 0;
426 #endif
427 }
428 
437 static int get_metaserver2_data(char *metaserver2)
438 {
439 #ifdef HAVE_CURL_CURL_H
440  CURL *curl;
441  CURLcode res;
442  char leftover[CURL_MAX_WRITE_SIZE];
443 
444  curl = curl_easy_init();
445  if (!curl) {
446  return 0;
447  }
448  leftover[0] =0;
449  curl_easy_setopt(curl, CURLOPT_URL, metaserver2);
450  curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, metaserver2_writer);
451  curl_easy_setopt(curl, CURLOPT_WRITEDATA, leftover);
452  res = curl_easy_perform(curl);
453  curl_easy_cleanup(curl);
454 
455  if (res) {
456  return 0;
457  } else {
458  return 1;
459  }
460 #else
461  return 1;
462 #endif
463 }
464 
471 void *metaserver2_thread(void *junk)
472 {
473  int metaserver_choice, tries=0;
474 
475  do {
476  metaserver_choice = g_random_int() % (sizeof(metaservers) / sizeof(char*));
477  tries++;
478  if (tries>5) {
479  break;
480  }
481  } while (!get_metaserver2_data(metaservers[metaserver_choice]));
482 
483  pthread_mutex_lock(&ms2_info_mutex);
484  qsort(meta_servers, meta_numservers, sizeof(Meta_Info), (int (*)(const void *, const void *))meta_sort);
485  ms2_is_running=0;
486  pthread_mutex_unlock(&ms2_info_mutex);
487  pthread_exit(NULL);
488  // never reached, just to make the compiler happy.
489  return NULL;
490 }
491 
492 
502 {
503  pthread_t thread_id;
504  int ret;
505 
506  if (!metaserver2_on) {
507  return 0;
508  }
509 #ifndef HAVE_CURL_CURL_H
510  return 0;
511 #endif
512 
514 
515  pthread_mutex_lock(&ms2_info_mutex);
516  if (!meta_servers) {
517  meta_servers = calloc(MAX_METASERVER, sizeof(Meta_Info));
518  }
519 
520  ms2_is_running=1;
521  pthread_mutex_unlock(&ms2_info_mutex);
522 
523  ret=pthread_create(&thread_id, NULL, metaserver2_thread, NULL);
524  if (ret) {
525  LOG(LOG_ERROR, "common::metaserver2_get_info", "Thread creation failed.");
526  pthread_mutex_lock(&ms2_info_mutex);
527  ms2_is_running=0;
528  pthread_mutex_unlock(&ms2_info_mutex);
529  }
530 
531  return 0;
532 }
533 
537 void init_metaserver(void)
538 {
539  pthread_mutex_init(&ms2_info_mutex, NULL);
540 #ifdef HAVE_CURL_CURL_H
541  curl_global_init(CURL_GLOBAL_ALL);
542 #endif
543 }
544 
545 /******************************************************************************
546  * End of Metasever2 functions.
547  ******************************************************************************/
548 
549 /******************************************************************************
550  * Start of metaserver1 logic
551  *
552  * Note that this shares the same mutex as metaserver2, since it is updating
553  * most of the same structures.
554  *******************************************************************************/
555 
556 static int ms1_is_running=0;
557 
558 
559 #ifdef WIN32
560 /* Need script.h for script_killall */
561 #include <script.h>
562 
567 char *get_line_from_sock(char *s, size_t n, int fd)
568 {
569  static long charsleft = 0;
570  static char inbuf[MS_LARGE_BUF*4];
571  char *cp;
572  int ct;
573 
574  if (!s) {
575  return s;
576  }
577  if (n != MS_LARGE_BUF*4-1) {
578  LOG(LOG_CRITICAL, "common::get_line_from_sock", "Serious program logic error in get_line_from_sock().");
579  exit(-1);
580  }
581 
582  if (charsleft > MS_LARGE_BUF*4-3 && strchr(inbuf, '\n') == NULL) {
584  "Metaserver returned an overly long line.");
585  return NULL;
586  }
587 
588  /* If there is no line in the buffer */
589  while (charsleft == 0 || (cp = strchr(inbuf, '\n')) == NULL) {
590  FD_SET fdset;
591  TIMEVAL tv = {3, 0}; /* 3 second timeout on reads */
592  int nlen;
593  FD_ZERO(&fdset);
594  FD_SET(fd, &fdset);
595  if (select(0, &fdset, NULL, NULL, &tv) == 0) {
597  "Metaserver timed out.");
598  return NULL;
599  }
600 
601  nlen = recv(fd, inbuf+charsleft-1, MS_LARGE_BUF*4-1-charsleft, 0);
602  if (nlen == SOCKET_ERROR || nlen <= 0) { /* Probably EOF */
603  return NULL;
604  }
605 
606  charsleft += nlen;
607  }
608 
609  /* OK, inbuf contains a null terminated string with at least one \n
610  * Copy the string up to the \n to s, and then move the rest of the
611  * inbuf string to the beginning of the buffer. And finally, set
612  * charsleft to the number of characters left in inbuf, or 0.
613  * Oh, and cp contains the location of the \n.
614  */
615 
616  memcpy(s, inbuf, cp-inbuf+1); /* Extract the line, including the \n. */
617  s[cp-inbuf+1] = 0; /* null terminate it */
618 
619  /* Copy cp to inbuf up to the \0, (skipping the \n) */
620  ct = 0;
621  while (cp[++ct] != 0) {
622  inbuf[ct-1] = cp[ct];
623  }
624  inbuf[ct-1] = 0;
625  charsleft = ct; /* And keep track of how many characters are left. */
626 
627  return s;
628 }
629 
630 #endif /* Win32 */
631 
635 void *metaserver1_thread(void *junk)
636 {
637  struct protoent *protox;
638  int fd;
639  struct sockaddr_in insock;
640 #ifndef WIN32
641  FILE *fp;
642 #endif
643  char inbuf[MS_LARGE_BUF*4];
644  Meta_Info *current;
645 
646  protox = getprotobyname("tcp");
647  if (protox == NULL) {
648  LOG(LOG_WARNING, "common::metaserver_get_info", "Error getting protobyname (tcp)");
649  pthread_mutex_lock(&ms2_info_mutex);
650  ms1_is_running=0;
651  pthread_mutex_unlock(&ms2_info_mutex);
652  pthread_exit(NULL);
653  }
654 
655  fd = socket(PF_INET, SOCK_STREAM, protox->p_proto);
656  if (fd == -1) {
657  perror("get_metaserver_info: Error on socket command.\n");
658  pthread_mutex_lock(&ms2_info_mutex);
659  ms1_is_running=0;
660  pthread_mutex_unlock(&ms2_info_mutex);
661  pthread_exit(NULL);
662  }
663  insock.sin_family = AF_INET;
664  insock.sin_port = htons((unsigned short)meta_port);
665  if (isdigit(*meta_server)) {
666  insock.sin_addr.s_addr = inet_addr(meta_server);
667  } else {
668  struct hostent *hostbn = gethostbyname(meta_server);
669  if (hostbn == NULL) {
670  LOG(LOG_WARNING, "common::metaserver_get_info", "Unknown metaserver hostname: %s", meta_server);
671  pthread_mutex_lock(&ms2_info_mutex);
672  ms1_is_running=0;
673  pthread_mutex_unlock(&ms2_info_mutex);
674  pthread_exit(NULL);
675  }
676  memcpy(&insock.sin_addr, hostbn->h_addr, hostbn->h_length);
677  }
678  if (connect(fd, (struct sockaddr *)&insock, sizeof(insock)) == -1) {
679  perror("Can't connect to metaserver");
681  "\nCan't connect to metaserver.");
682  pthread_mutex_lock(&ms2_info_mutex);
683  ms1_is_running=0;
684  pthread_mutex_unlock(&ms2_info_mutex);
685  pthread_exit(NULL);
686  }
687 
688 #ifndef WIN32 /* Windows doesn't support this */
689  /* Turn this into a file handle - this will break it on newlines
690  * for us, which makes our processing much easier - it basically
691  * means one line/server
692  */
693  if ((fp = fdopen(fd, "r")) == NULL) {
694  perror("fdopen failed.");
695  pthread_mutex_lock(&ms2_info_mutex);
696  ms1_is_running=0;
697  pthread_mutex_unlock(&ms2_info_mutex);
698  pthread_exit(NULL);
699  }
700 #endif
701 
702  pthread_mutex_lock(&ms2_info_mutex);
703  if (!meta_servers) {
704  meta_servers = calloc(MAX_METASERVER, sizeof(Meta_Info));
705  }
706 
707 
708  /* The loop goes through and unpacks the data from the metaserver
709  * into its individual components. We do a little extra work and
710  * put the |'s back in the string after we are done with that section -
711  * this is so if there is a corrupt entry, it gets displayed as
712  * originally received from the server.
713  */
714 #ifndef WIN32 /* Windows doesn't support this */
715  while (fgets(inbuf, MS_LARGE_BUF*4-1, fp) != NULL) {
716 #else
717  while (get_line_from_sock(inbuf, MS_LARGE_BUF*4-1, fd) != NULL) {
718 #endif
719  char *cp, *cp1;
720 
721  cp = strchr(inbuf, '|');
722  if (cp == NULL) {
723  LOG(LOG_WARNING, "common::metaserver_get_info", "Corrupt line from server: %s", inbuf);
724  break;
725  }
726  *cp = 0;
727 
728  current = &meta_servers[meta_numservers];
729 
730  strncpy(current->ip_addr, inbuf, sizeof(current->ip_addr)-1);
731  current->ip_addr[sizeof(current->ip_addr)-1] = '\0';
732  *cp++ = '|';
733 
734  current->idle_time = atoi(cp);
735 
736  cp1 = strchr(cp, '|');
737  if (cp1 == NULL) {
738  LOG(LOG_WARNING, "common::metaserver_get_info", "Corrupt line from server: %s", inbuf);
739  break;
740  }
741  *cp1 = 0;
742 
743  cp = strchr(cp1+1, '|');
744  if (cp == NULL) {
745  LOG(LOG_WARNING, "common::metaserver_get_info", "Corrupt line from server: %s", inbuf);
746  break;
747  }
748  *cp = 0;
749  /* cp1 points at start of comment, cp points at end */
750  strncpy(current->hostname, cp1+1, sizeof(current->hostname)-1);
751  current->hostname[sizeof(current->hostname)-1] = '\0';
752 
753  *cp1++ = '|';
754  *cp++ = '|'; /* cp now points to num players */
755 
756  current->num_players = atoi(cp);
757 
758  cp1 = strchr(cp, '|');
759  if (cp1 == NULL) {
760  LOG(LOG_WARNING, "common::metaserver_get_info", "Corrupt line from server: %s", inbuf);
761  break;
762  }
763  *cp1 = 0;
764 
765  cp = strchr(cp1+1, '|');
766  if (cp == NULL) {
767  LOG(LOG_WARNING, "common::metaserver_get_info", "Corrupt line from server: %s", inbuf);
768  break;
769  }
770  *cp = 0;
771  /* cp1 is start of version, cp is end */
772  strncpy(current->version, cp1+1, sizeof(current->version)-1);
773  current->version[sizeof(current->version)-1] = '\0';
774 
775  *cp1++ = '|';
776  *cp++ = '|'; /* cp now points to comment */
777 
778  cp1 = strchr(cp, '\n');
779  if (cp1 == NULL) {
780  LOG(LOG_WARNING, "common::metaserver_get_info", "Corrupt line from server: %s", inbuf);
781  break;
782  }
783  *cp1 = 0;
784  /* There is extra info included, like the bytes to/from the server
785  * that we dont' care about, so strip them off so they don't show up in
786  * the comment.
787  */
788  cp1 = strchr(cp, '|');
789  if (cp1 != NULL) {
790  *cp1 = 0;
791  }
792 
793  strncpy(current->text_comment, cp, sizeof(current->text_comment)-1);
794  current->text_comment[sizeof(current->text_comment)-1] = '\0';
795 
796  meta_numservers++;
797  /* has to be 1 less than array size, since array starts counting
798  * at 0.
799  */
800  if (meta_numservers >= MAX_METASERVER-1) {
801  LOG(LOG_WARNING, "common:metaserver_get_info", "Have reached maximum metaserver count\n");
802  break;
803  }
804  }
805 #ifdef WIN32
806  closesocket(fd);
807 #else
808  fclose(fp);
809 #endif
810  qsort(meta_servers, meta_numservers, sizeof(Meta_Info), (int (*)(const void *, const void *))meta_sort);
811  ms1_is_running=0;
812  pthread_mutex_unlock(&ms2_info_mutex);
813  pthread_exit(NULL);
814  /* never reached, just to make the compiler happy. */
815  return NULL;
816 }
817 
822 {
823  pthread_t thread_id;
824  int ret;
825 
826  if (!metaserver_on) {
827  return 0;
828  }
830 
831  pthread_mutex_lock(&ms2_info_mutex);
832  if (!meta_servers) {
833  meta_servers = calloc(MAX_METASERVER, sizeof(Meta_Info));
834  }
835 
836  ms1_is_running=1;
837  pthread_mutex_unlock(&ms2_info_mutex);
838 
839  ret=pthread_create(&thread_id, NULL, metaserver1_thread, NULL);
840  if (ret) {
841  LOG(LOG_ERROR, "common::metaserver1_get_info", "Thread creation failed.");
842  pthread_mutex_lock(&ms2_info_mutex);
843  ms1_is_running=0;
844  pthread_mutex_unlock(&ms2_info_mutex);
845  }
846 
847  return 0;
848 }
849 /******************************************************************************
850  * End of metaserver1 logic
851  ******************************************************************************/
852 
853 /******************************************************************************
854  * This is start of common logic - the above sections are actually getting
855  * the data. The code below here is just displaying the data we got
856  */
857 
869 {
870  int status;
871 
872  pthread_mutex_lock(&ms2_info_mutex);
873  status = ms2_is_running | ms1_is_running;
874  pthread_mutex_unlock(&ms2_info_mutex);
875 
876  return status;
877 }
878 
886 int metaserver_get_info(char *metaserver, int meta_port)
887 {
888 
889  meta_numservers = 0;
890 
892 
893  if (metaserver_on) {
895  }
896 
897  return 0;
898 }
899 
904 void metaserver_show(int show_selection)
905 {
906  int i;
907  char buf[256];
908 
909  if (cached_servers_num) {
911  "\nLast servers you connected to:\n");
912  for (i = 0; i < cached_servers_num; i++) {
913  snprintf(buf, sizeof(buf), "%2d) %-20.20s %-20.20s", i+1, cached_servers_name[i], cached_servers_ip[i]);
916  }
918  }
919 
920  while(metaserver_check_status()) {
921  usleep(100);
922  }
923 
925  " #) Server # version idle");
927  " Name players seconds");
928  pthread_mutex_lock(&ms2_info_mutex);
929 
930  /* Re-sort the data - may get different data from ms1 and ms2, so
931  * order of this is somewhat random.
932  */
933  qsort(meta_servers, meta_numservers, sizeof(Meta_Info), (int (*)(const void *, const void *))meta_sort);
934  for (i = 0; i < meta_numservers; i++) {
935  if (check_server_version(i)) {
936  snprintf(buf, sizeof(buf), "%2d) %-15.15s %2d %-12.12s %2d",
937  i+1+cached_servers_num, meta_servers[i].hostname,
938  meta_servers[i].num_players, meta_servers[i].version,
939  meta_servers[i].idle_time);
942  }
943  }
944  if (show_selection) {
945  /* Show default/current server */
946  if (server) {
947  snprintf(buf, sizeof(buf), "%2d) %s (default)", meta_numservers+1+cached_servers_num, server);
950  }
951 
953  "Choose one of the entries above");
955  "or type in a hostname/ip address");
957  "Hit enter to re-update this list");
959  "Enter 0 to exit the program.");
960  }
961  pthread_mutex_unlock(&ms2_info_mutex);
962 }
963 
970 int metaserver_select(char *sel)
971 {
972  int num = atoi(sel);
973  int port=0;
974  char buf[MAX_BUF], buf2[MAX_BUF];
975  char *server_name = NULL, *server_ip;
976 
977  /* User hit return */
978  if (sel[0] == 0) {
979  metaserver_get_info(meta_server, meta_port);
980  metaserver_show(TRUE);
981  return 1;
982  }
983 
984  /* Special case - player really entered a 0, so exit the
985  * program.
986  */
987  if (num == 0 && sel[0] == '0') {
988 #ifdef WIN32
989  script_killall();
990 #endif
991  exit(0);
992  }
993 
994  pthread_mutex_lock(&ms2_info_mutex);
995 
996  /* if the entry is not a number (selection from the list),
997  * or is a selection but also has a dot (suggesting
998  * a.b.c.d selection), just try to connect with given name.
999  */
1000  if (num == 0 || strchr(sel, '.') != NULL) {
1001  server_name = sel;
1002  server_ip = sel;
1003  } else {
1004  if (num <= 0 || num > meta_numservers+cached_servers_num+1) {
1005  draw_ext_info(
1007  "Invalid selection. Try again");
1008  return 1;
1009  }
1010 
1011  if (num == meta_numservers+cached_servers_num+1) {
1012  server_name = server;
1013  server_ip = server;
1014  } else if (num > cached_servers_num) {
1015  server_name = meta_servers[num-cached_servers_num-1 ].hostname;
1016  server_ip = meta_servers[num-cached_servers_num-1 ].ip_addr;
1017  port = meta_servers[num-cached_servers_num-1 ].port;
1018  } else {
1019  server_name = cached_servers_name[num-1];
1020  server_ip = cached_servers_ip[num-1];
1021  }
1022  }
1023  pthread_mutex_unlock(&ms2_info_mutex);
1024  if (!server_name) {
1026  "Bad selection. Try again");
1027  return 1;
1028  }
1029 
1030  /* check for :port suffix, and use it */
1031  if (!port) {
1032  if ((sel = strrchr(server_name, ':')) != NULL && (port = atoi(sel+1)) > 0) {
1033  snprintf(buf2, sizeof(buf2), "%s", server_name);
1034  buf2[sel-server_name] = '\0';
1035  server_name = buf2;
1036  } else {
1037  port = use_config[CONFIG_PORT];
1038  }
1039  }
1040 
1041  snprintf(buf, sizeof(buf), "Trying to connect to %s:%d", server_name, port);
1043  csocket.fd = init_connection(server_name, port);
1044  if (csocket.fd == -1) {
1046  "Unable to connect to server.");
1047  return 1;
1048  }
1049 
1050  /*
1051  * Upon successful connection, add the server to the cache or move it to
1052  * the top of the list.
1053  */
1054  if ((num <= meta_numservers)
1055  && (num != meta_numservers + cached_servers_num + 1)) {
1056  metaserver_update_cache(server_name, server_ip);
1057  }
1058 
1059  return 0;
1060 }
1061 
1062 #ifdef MS_STANDALONE
1063 /* This is here just to verify that the code seems to be working
1064  * properly, this tests both metaserver one and metaserver2
1065  * To use this code, compile as:
1066  * gcc -o metaserver -I. -DMS_STANDALONE metaserver.c misc.o -lcurl -lpthread
1067  * if you only want to have support for one type of server then use either
1068  * -DMS_SA_NOTMS1 or -DMS_SA_NOTMS2 to disable the metaserver you don't want.
1069  * The list of servers goes to stdout, the headers for the tables, status messages etc, go to stderr.
1070  */
1071 
1072 /* Following lines are to cover external symbols not
1073  * defined - trying to bring in the files the are defined
1074  * in just causes more dependencies, etc.
1075  */
1076 void draw_ext_info(int orig_color, int type, int subtype, const char *message) {}
1077 int init_connection(char *host, int port) {}
1078 
1079 int metaserver2_on=1, metaserver_on=1;
1080 char *server=NULL;
1081 gint16 use_config[CONFIG_NUMS];
1083 char *meta_server=META_SERVER;
1084 int meta_port=META_PORT;
1085 
1089 void handle_ms_data(int msservernum)
1090 {
1091  int i;
1092  fprintf(stderr,"Collecting data from metaserver %d.", msservernum);
1093  while (metaserver_check_status()) {
1094  fprintf(stderr,".");
1095  g_usleep(1 * 1e6);
1096  }
1097  fprintf(stderr, "\nIp Address:Idle Time:Hostname:Players:Version:Comment\n");
1098  for (i = 0; i < meta_numservers; i++) {
1099  printf("%s:%d:%s:%d:%s:%s\n",
1100  meta_servers[i].ip_addr,
1101  meta_servers[i].idle_time,
1102  meta_servers[i].hostname,
1103  meta_servers[i].num_players,
1104  meta_servers[i].version,
1105  meta_servers[i].text_comment);
1106  }
1107  fprintf(stderr, "%d servers found\n", meta_numservers);
1108 }
1109 
1113 int main(int argc, char *argv[])
1114 {
1115 
1116 #ifdef MS_SA_NOTMS2
1117  metaserver2_on=0;
1118 #endif
1119 #ifdef MS_SA_NOTMS1
1120  metaserver_on=0;
1121 #endif
1122 
1123  init_metaserver();
1124  if(metaserver2_on) {
1126  handle_ms_data(2);
1127  }
1128  /* both metaservers use the same array to store the servers in, so we'll
1129  * reset it here in order to get the results from the other metaserver. */
1130  free(meta_servers);
1131  meta_servers=NULL;
1132  meta_numservers = 0;
1133  if(metaserver_on) {
1135  handle_ms_data(1);
1136  }
1137 }
1138 
1139 #endif
#define MS_LARGE_BUF
Definition: metaserver.h:28
#define VERSION_CS
Definition: client.h:31
static void metaserver_load_cache(void)
Load server names and addresses or DNS names from a cache file found in the player's client data fold...
Definition: metaserver.c:121
char * cached_servers_ip[CACHED_SERVERS_MAX]
Definition: metaserver.c:103
int sc_version
Definition: metaserver.h:57
gint16 use_config[CONFIG_NUMS]
Definition: init.c:39
static char * metaservers[]
Definition: metaserver.c:271
void init_metaserver(void)
Does single use initalization of metaserver2 variables.
Definition: metaserver.c:537
void metaserver_show(int show_selection)
Show the metaservers to the player.
Definition: metaserver.c:904
int metaserver_select(char *sel)
String contains the selection that the player made for the metaserver.
Definition: metaserver.c:970
guint32 in_bytes
Definition: metaserver.h:51
ClientSocket csocket
Definition: client.c:71
int port
Definition: metaserver.h:42
int cs_version
Definition: metaserver.h:58
#define MSG_TYPE_CLIENT
Client originated Messages.
Definition: newclient.h:420
int meta_sort(Meta_Info *m1, Meta_Info *m2)
Definition: metaserver.c:47
int meta_port
Definition: client.c:58
Contains external calls that the common area makes callbacks to.
int uptime
Definition: metaserver.h:55
#define NDI_BLACK
Definition: newclient.h:249
char hostname[MS_LARGE_BUF]
Definition: metaserver.h:41
#define CACHED_SERVERS_MAX
Definition: metaserver.h:75
char text_comment[MS_LARGE_BUF]
Definition: metaserver.h:44
char * server
Definition: client.c:46
void * metaserver1_thread(void *junk)
Definition: metaserver.c:635
Metaserver settings, structures, and prototypes.
Basic support for socket communications, including the file descriptor, input buffer, server, server, version, etc.
Definition: client.h:90
int metaserver2_on
Definition: client.c:60
int init_connection(char *host, int port)
Attempt to establish a socket connection to a specified server and port.
Definition: client.c:294
Meta_Info * meta_servers
Definition: metaserver.c:43
void LOG(LogLevel level, const char *origin, const char *format,...)
Log messages of a certain importance to stderr.
Definition: misc.c:111
void * metaserver2_thread(void *junk)
Thread function that goes off and collects metaserver data.
Definition: metaserver.c:471
size_t metaserver2_writer(void *ptr, size_t size, size_t nmemb, void *data)
Curl doesn't really have any built in way to get data from the URL into string data - instead...
Definition: metaserver.c:297
Structure that contains data we get from metaservers This is used by both metaserver1 and metaserver2...
Definition: metaserver.h:39
int metaserver1_get_info(void)
Definition: metaserver.c:821
char * meta_server
Definition: client.c:50
void metaserver_update_cache(const char *server_name, const char *server_ip)
Add a server to the players server cache file.
Definition: metaserver.c:179
pthread_mutex_t ms2_info_mutex
Definition: metaserver.c:254
int check_server_version(int entry)
This checks the servers sc_version and cs_version to see if they are compatible.
Definition: metaserver.c:63
#define CONFIG_PORT
Is this useful any more?
Definition: client.h:180
int fd
Definition: client.h:91
static void metaserver_save_cache(void)
Definition: metaserver.c:153
static int ms1_is_running
Definition: metaserver.c:556
char ip_addr[MS_SMALL_BUF]
Definition: metaserver.h:40
#define CONFIG_NUMS
This should always be the last value in the CONFIG_xxx list.
Definition: client.h:191
static int cached_servers_loaded
Definition: metaserver.c:104
int num_players
Definition: metaserver.h:50
#define MAX_BUF
Definition: client-types.h:43
int main(int argc, char *argv[])
Main client entry point.
Definition: main.c:506
int cached_servers_num
Definition: metaserver.c:101
char * cached_servers_name[CACHED_SERVERS_MAX]
Definition: metaserver.c:102
int metaserver2_get_info(void)
this is basically a replacement to the metaserver_get_info - idea being that when metaserver 1 suppor...
Definition: metaserver.c:501
static int get_metaserver2_data(char *metaserver2)
Connects to the URL and gets metaserver data.
Definition: metaserver.c:437
void draw_ext_info(int orig_color, int type, int subtype, const char *message)
A message processor that accepts messages along with meta information color and type.
Definition: info.c:934
int metaserver_on
Definition: client.c:60
#define VERSION_SC
Definition: client.h:32
#define META_PORT
Definition: metaserver.h:18
#define MAX_METASERVER
Definition: metaserver.h:24
int metaserver_get_info(char *metaserver, int meta_port)
This contacts the metaserver and gets the list of servers.
Definition: metaserver.c:886
Includes various dependencies header files needed by most everything.
int idle_time
Definition: metaserver.h:53
char * name
Definition: image.c:56
char version[MS_SMALL_BUF]
Definition: metaserver.h:56
int meta_numservers
Definition: metaserver.c:45
#define META_SERVER
Definition: metaserver.h:17
static int ms2_is_running
Definition: metaserver.c:261
guint32 out_bytes
Definition: metaserver.h:52
#define MIN(X__, Y__)
Definition: client.h:618
const char * cached_server_file
Definition: metaserver.c:105
#define MSG_TYPE_CLIENT_METASERVER
Metaserver messages.
Definition: newclient.h:690
char * g_strdup(const char *str)
int metaserver_check_status(void)
Sees if we are gathering data or not.
Definition: metaserver.c:868