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