Complete.Org: Mailing Lists: Archives: freeciv-dev: August 2005:
[Freeciv-Dev] (PR#13700) move server-scanning code into its own file
Home

[Freeciv-Dev] (PR#13700) move server-scanning code into its own file

[Top] [All Lists]

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index] [Thread Index]
Subject: [Freeciv-Dev] (PR#13700) move server-scanning code into its own file
From: "Jason Short" <jdorje@xxxxxxxxxxxxxxxxxxxxx>
Date: Thu, 18 Aug 2005 10:27:49 -0700
Reply-to: bugs@xxxxxxxxxxx

<URL: http://bugs.freeciv.org/Ticket/Display.html?id=13700 >

This patch moves the server-scanning client code (lanservers and 
metaserver) into a new file servers.[ch].  This separates it from the 
code for connecting to a server (which is a quite different operation 
although both involve netowork usage).

This is in preparation for a rewrite to make the metaserver connection 
not block.

-jason

Index: client/Makefile.am
===================================================================
RCS file: /home/freeciv/CVS/freeciv/client/Makefile.am,v
retrieving revision 1.66
diff -p -u -r1.66 Makefile.am
--- client/Makefile.am  9 May 2005 18:42:18 -0000       1.66
+++ client/Makefile.am  18 Aug 2005 17:26:04 -0000
@@ -169,6 +169,8 @@ civclient_SOURCES = $(ESD_FILES) $(SDL_F
        repodlgs_common.h \
        reqtree.c \
        reqtree.h \
+       servers.c               \
+       servers.h               \
        text.c  \
        text.h  \
        tilespec.c      \
Index: client/clinet.c
===================================================================
RCS file: /home/freeciv/CVS/freeciv/client/clinet.c,v
retrieving revision 1.117
diff -p -u -r1.117 clinet.c
--- client/clinet.c     1 Aug 2005 06:48:28 -0000       1.117
+++ client/clinet.c     18 Aug 2005 17:26:04 -0000
@@ -91,8 +91,6 @@
 #include "clinet.h"
 
 struct connection aconnection;
-static int socklan;
-static struct server_list *lan_servers;
 static union my_sockaddr server_addr;
 
 /*************************************************************************
@@ -495,421 +493,6 @@ static char *win_uname()
 }
 #endif
 
-/**************************************************************************
- The server sends a stream in a registry 'ini' type format.
- Read it using secfile functions and fill the server_list structs.
-**************************************************************************/
-static struct server_list *parse_metaserver_data(fz_FILE *f)
-{
-  struct server_list *server_list;
-  struct section_file the_file, *file = &the_file;
-  int nservers, i, j;
-
-  server_list = server_list_new();
-
-  /* This call closes f. */
-  if (!section_file_load_from_stream(file, f)) {
-    return server_list;
-  }
-
-  nservers = secfile_lookup_int_default(file, 0, "main.nservers");
-
-  for (i = 0; i < nservers; i++) {
-    char *host, *port, *version, *state, *message, *nplayers;
-    int n;
-    struct server *pserver = (struct server*)fc_malloc(sizeof(struct server));
-
-    host = secfile_lookup_str_default(file, "", "server%d.host", i);
-    pserver->host = mystrdup(host);
-
-    port = secfile_lookup_str_default(file, "", "server%d.port", i);
-    pserver->port = mystrdup(port);
-
-    version = secfile_lookup_str_default(file, "", "server%d.version", i);
-    pserver->version = mystrdup(version);
-
-    state = secfile_lookup_str_default(file, "", "server%d.state", i);
-    pserver->state = mystrdup(state);
-
-    message = secfile_lookup_str_default(file, "", "server%d.message", i);
-    pserver->message = mystrdup(message);
-
-    nplayers = secfile_lookup_str_default(file, "0", "server%d.nplayers", i);
-    pserver->nplayers = mystrdup(nplayers);
-    n = atoi(nplayers);
-
-    if (n > 0) {
-      pserver->players = fc_malloc(n * sizeof(*pserver->players));
-    } else {
-      pserver->players = NULL;
-    }
-      
-    for (j = 0; j < n; j++) {
-      char *name, *nation, *type, *host;
-
-      name = secfile_lookup_str_default(file, "", 
-                                        "server%d.player%d.name", i, j);
-      pserver->players[j].name = mystrdup(name);
-
-      type = secfile_lookup_str_default(file, "",
-                                        "server%d.player%d.type", i, j);
-      pserver->players[j].type = mystrdup(type);
-
-      host = secfile_lookup_str_default(file, "", 
-                                        "server%d.player%d.host", i, j);
-      pserver->players[j].host = mystrdup(host);
-
-      nation = secfile_lookup_str_default(file, "",
-                                          "server%d.player%d.nation", i, j);
-      pserver->players[j].nation = mystrdup(nation);
-    }
-
-    server_list_append(server_list, pserver);
-  }
-
-  section_file_free(file);
-  return server_list;
-}
-
-/**************************************************************************
- Create the list of servers from the metaserver
- The result must be free'd with delete_server_list() when no
- longer used
-**************************************************************************/
-struct server_list *create_server_list(char *errbuf, int n_errbuf)
-{
-  union my_sockaddr addr;
-  int s;
-  fz_FILE *f;
-  const char *urlpath;
-  char metaname[MAX_LEN_ADDR];
-  int metaport;
-  const char *capstr;
-  char str[MAX_LEN_PACKET];
-  char machine_string[128];
-#ifdef HAVE_UNAME
-  struct utsname un;
-#endif 
-
-  urlpath = my_lookup_httpd(metaname, &metaport, METALIST_ADDR);//metaserver);
-  if (!urlpath) {
-    (void) mystrlcpy(errbuf, _("Invalid $http_proxy or metaserver value, must "
-                              "start with 'http://'"), n_errbuf);
-    return NULL;
-  }
-
-  if (!net_lookup_service(metaname, metaport, &addr)) {
-    (void) mystrlcpy(errbuf, _("Failed looking up metaserver's host"), 
n_errbuf);
-    return NULL;
-  }
-  
-  if((s = socket(AF_INET, SOCK_STREAM, 0)) == -1) {
-    (void) mystrlcpy(errbuf, mystrerror(), n_errbuf);
-    return NULL;
-  }
-  
-  if(connect(s, (struct sockaddr *) &addr.sockaddr, sizeof(addr)) == -1) {
-    (void) mystrlcpy(errbuf, mystrerror(), n_errbuf);
-    my_closesocket(s);
-    return NULL;
-  }
-
-#ifdef HAVE_UNAME
-  uname(&un);
-  my_snprintf(machine_string,sizeof(machine_string),
-              "%s %s [%s]",
-              un.sysname,
-              un.release,
-              un.machine);
-#else /* ! HAVE_UNAME */
-  /* Fill in here if you are making a binary without sys/utsname.h and know
-     the OS name, release number, and machine architechture */
-#ifdef WIN32_NATIVE
-  sz_strlcpy(machine_string,win_uname());
-#else
-  my_snprintf(machine_string,sizeof(machine_string),
-              "unknown unknown [unknown]");
-#endif
-#endif /* HAVE_UNAME */
-
-  capstr = my_url_encode(our_capability);
-
-  my_snprintf(str, sizeof(str),
-    "POST %s HTTP/1.1\r\n"
-    "Host: %s:%d\r\n"
-    "User-Agent: Freeciv/%s %s %s\r\n"
-    "Connection: close\r\n"
-    "Content-Type: application/x-www-form-urlencoded; charset=\"utf-8\"\r\n"
-    "Content-Length: %lu\r\n"
-    "\r\n"
-    "client_cap=%s\r\n",
-    urlpath,
-    metaname, metaport,
-    VERSION_STRING, client_string, machine_string,
-    (unsigned long) (strlen("client_cap=") + strlen(capstr)),
-    capstr);
-
-  f = my_querysocket(s, str, strlen(str));
-
-  if (f == NULL) {
-    /* TRANS: This means a network error when trying to connect to
-     * the metaserver.  The message will be shown directly to the user. */
-    (void) mystrlcpy(errbuf, _("Failed querying socket"), n_errbuf);
-    return NULL;
-  }
-
-  /* skip HTTP headers */
-  while (fz_fgets(str, sizeof(str), f) && strcmp(str, "\r\n") != 0) {
-    /* nothing */
-  }
-
-  /* XXX: TODO check for magic Content-Type: text/x-ini -vasc */
-
-  /* parse HTTP message body */
-  return parse_metaserver_data(f);
-}
-
-/**************************************************************************
- Frees everything associated with a server list including
- the server list itself (so the server_list is no longer
- valid after calling this function)
-**************************************************************************/
-void delete_server_list(struct server_list *server_list)
-{
-  server_list_iterate(server_list, ptmp) {
-    int i;
-    int n = atoi(ptmp->nplayers);
-
-    free(ptmp->host);
-    free(ptmp->port);
-    free(ptmp->version);
-    free(ptmp->state);
-    free(ptmp->message);
-
-    if (ptmp->players) {
-      for (i = 0; i < n; i++) {
-        free(ptmp->players[i].name);
-        free(ptmp->players[i].type);
-        free(ptmp->players[i].host);
-        free(ptmp->players[i].nation);
-      }
-      free(ptmp->players);
-    }
-    free(ptmp->nplayers);
-
-    free(ptmp);
-  } server_list_iterate_end;
-
-  server_list_unlink_all(server_list);
-  server_list_free(server_list);
-}
-
-/**************************************************************************
-  Broadcast an UDP package to all servers on LAN, requesting information 
-  about the server. The packet is send to all Freeciv servers in the same
-  multicast group as the client.
-**************************************************************************/
-int begin_lanserver_scan(void)
-{
-  union my_sockaddr addr;
-  struct data_out dout;
-  int sock, opt = 1;
-  unsigned char buffer[MAX_LEN_PACKET];
-  struct ip_mreq mreq;
-  const char *group;
-  unsigned char ttl;
-  size_t size;
-
-  /* Create a socket for broadcasting to servers. */
-  if ((sock = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
-    freelog(LOG_ERROR, "socket failed: %s", mystrerror());
-    return 0;
-  }
-
-  if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR,
-                 (char *)&opt, sizeof(opt)) == -1) {
-    freelog(LOG_ERROR, "SO_REUSEADDR failed: %s", mystrerror());
-  }
-
-  /* Set the UDP Multicast group IP address. */
-  group = get_multicast_group();
-  memset(&addr, 0, sizeof(addr));
-  addr.sockaddr_in.sin_family = AF_INET;
-  addr.sockaddr_in.sin_addr.s_addr = inet_addr(get_multicast_group());
-  addr.sockaddr_in.sin_port = htons(SERVER_LAN_PORT);
-
-  /* Set the Time-to-Live field for the packet  */
-  ttl = SERVER_LAN_TTL;
-  if (setsockopt(sock, IPPROTO_IP, IP_MULTICAST_TTL, (const char*)&ttl, 
-                 sizeof(ttl))) {
-    freelog(LOG_ERROR, "setsockopt failed: %s", mystrerror());
-    return 0;
-  }
-
-  if (setsockopt(sock, SOL_SOCKET, SO_BROADCAST, (const char*)&opt, 
-                 sizeof(opt))) {
-    freelog(LOG_ERROR, "setsockopt failed: %s", mystrerror());
-    return 0;
-  }
-
-  dio_output_init(&dout, buffer, sizeof(buffer));
-  dio_put_uint8(&dout, SERVER_LAN_VERSION);
-  size = dio_output_used(&dout);
- 
-
-  if (sendto(sock, buffer, size, 0, &addr.sockaddr,
-      sizeof(addr)) < 0) {
-    freelog(LOG_ERROR, "sendto failed: %s", mystrerror());
-    return 0;
-  } else {
-    freelog(LOG_DEBUG, ("Sending request for server announcement on LAN."));
-  }
-
-  my_closesocket(sock);
-
-  /* Create a socket for listening for server packets. */
-  if ((socklan = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
-    freelog(LOG_ERROR, "socket failed: %s", mystrerror());
-    return 0;
-  }
-
-  my_nonblock(socklan);
-
-  if (setsockopt(socklan, SOL_SOCKET, SO_REUSEADDR,
-                 (char *)&opt, sizeof(opt)) == -1) {
-    freelog(LOG_ERROR, "SO_REUSEADDR failed: %s", mystrerror());
-  }
-                                                                               
-  memset(&addr, 0, sizeof(addr));
-  addr.sockaddr_in.sin_family = AF_INET;
-  addr.sockaddr_in.sin_addr.s_addr = htonl(INADDR_ANY); 
-  addr.sockaddr_in.sin_port = htons(SERVER_LAN_PORT + 1);
-
-  if (bind(socklan, &addr.sockaddr, sizeof(addr)) < 0) {
-    freelog(LOG_ERROR, "bind failed: %s", mystrerror());
-    return 0;
-  }
-
-  mreq.imr_multiaddr.s_addr = inet_addr(group);
-  mreq.imr_interface.s_addr = htonl(INADDR_ANY);
-  if (setsockopt(socklan, IPPROTO_IP, IP_ADD_MEMBERSHIP, 
-                 (const char*)&mreq, sizeof(mreq)) < 0) {
-    freelog(LOG_ERROR, "setsockopt failed: %s", mystrerror());
-    return 0;
-  }
-
-  lan_servers = server_list_new();
-
-  return 1;
-}
-
-/**************************************************************************
-  Listens for UDP packets broadcasted from a server that responded
-  to the request-packet sent from the client. 
-**************************************************************************/
-struct server_list *get_lan_server_list(void) {
-
-# if defined(__VMS) && !defined(_DECC_V4_SOURCE)
-  size_t fromlen;
-# else
-  unsigned int fromlen;
-# endif
-  union my_sockaddr fromend;
-  struct hostent *from;
-  char msgbuf[128];
-  int type;
-  struct data_in din;
-  char servername[512];
-  char port[256];
-  char version[256];
-  char status[256];
-  char players[256];
-  char message[1024];
-  fd_set readfs, exceptfs;
-  struct timeval tv;
-
-  FD_ZERO(&readfs);
-  FD_ZERO(&exceptfs);
-  FD_SET(socklan, &exceptfs);
-  FD_SET(socklan, &readfs);
-  tv.tv_sec = 0;
-  tv.tv_usec = 0;
-
-  while (select(socklan + 1, &readfs, NULL, &exceptfs, &tv) == -1) {
-    if (errno != EINTR) {
-      freelog(LOG_ERROR, "select failed: %s", mystrerror());
-      return lan_servers;
-    }
-    /* EINTR can happen sometimes, especially when compiling with -pg.
-     * Generally we just want to run select again. */
-  }
-
-  if (!FD_ISSET(socklan, &readfs)) {
-    return lan_servers;
-  }
-
-  dio_input_init(&din, msgbuf, sizeof(msgbuf));
-  fromlen = sizeof(fromend);
-
-  /* Try to receive a packet from a server. */ 
-  if (0 < recvfrom(socklan, msgbuf, sizeof(msgbuf), 0,
-                   &fromend.sockaddr, &fromlen)) {
-    struct server *pserver;
-
-    dio_get_uint8(&din, &type);
-    if (type != SERVER_LAN_VERSION) {
-      return lan_servers;
-    }
-    dio_get_string(&din, servername, sizeof(servername));
-    dio_get_string(&din, port, sizeof(port));
-    dio_get_string(&din, version, sizeof(version));
-    dio_get_string(&din, status, sizeof(status));
-    dio_get_string(&din, players, sizeof(players));
-    dio_get_string(&din, message, sizeof(message));
-
-    if (!mystrcasecmp("none", servername)) {
-      from = gethostbyaddr((char *) &fromend.sockaddr_in.sin_addr,
-                          sizeof(fromend.sockaddr_in.sin_addr), AF_INET);
-      sz_strlcpy(servername, inet_ntoa(fromend.sockaddr_in.sin_addr));
-    }
-
-    /* UDP can send duplicate or delayed packets. */
-    server_list_iterate(lan_servers, aserver) {
-      if (!mystrcasecmp(aserver->host, servername) 
-          && !mystrcasecmp(aserver->port, port)) {
-        return lan_servers;
-      } 
-    } server_list_iterate_end;
-
-    freelog(LOG_DEBUG,
-            ("Received a valid announcement from a server on the LAN."));
-    
-    pserver =  (struct server*)fc_malloc(sizeof(struct server));
-    pserver->host = mystrdup(servername);
-    pserver->port = mystrdup(port);
-    pserver->version = mystrdup(version);
-    pserver->state = mystrdup(status);
-    pserver->nplayers = mystrdup(players);
-    pserver->message = mystrdup(message);
-    pserver->players = NULL;
-
-    server_list_prepend(lan_servers, pserver);
-  } else {
-    return lan_servers;
-  }                                       
-
-  return lan_servers;
-}
-
-/**************************************************************************
-  Closes the socket listening on the lan and frees the list of LAN servers.
-**************************************************************************/
-void finish_lanserver_scan(void) 
-{
-  my_closesocket(socklan);
-  delete_server_list(lan_servers);
-}
-
 static bool autoconnecting = FALSE;
 /**************************************************************************
   Make an attempt to autoconnect to the server.
Index: client/clinet.h
===================================================================
RCS file: /home/freeciv/CVS/freeciv/client/clinet.h,v
retrieving revision 1.21
diff -p -u -r1.21 clinet.h
--- client/clinet.h     1 Aug 2005 06:48:28 -0000       1.21
+++ client/clinet.h     18 Aug 2005 17:26:04 -0000
@@ -14,11 +14,6 @@
 #define FC__CLINET_H
 
 #define DEFAULT_SOCK_PORT 5555
-#define METALIST_ADDR "http://meta.freeciv.org/metaserver.phtml";
-
-#define SERVER_LAN_PORT 4555
-#define SERVER_LAN_TTL 1
-#define SERVER_LAN_VERSION 1
 
 /* In autoconnect mode, try to connect to once a second */
 #define AUTOCONNECT_INTERVAL           500
@@ -41,42 +36,6 @@ void disconnect_from_server(void);
 extern struct connection aconnection;
 /* this is the client's connection to the server */
 
-struct server
-{
-  char *host;
-  char *port;
-  char *capability;
-  char *patches;
-  char *version;
-  char *state;
-  char *topic;
-  char *message;
-
-  struct players
-  {
-    char *name;
-    char *type;
-    char *host;
-    char *nation;
-  } *players;
-
-  char *nplayers;
-};
-
-#define SPECLIST_TAG server
-#define SPECLIST_TYPE struct server
-#include "speclist.h"
-
-#define server_list_iterate(serverlist, pserver) \
-  TYPED_LIST_ITERATE(struct server, serverlist, pserver)
-#define server_list_iterate_end  LIST_ITERATE_END
-
-struct server_list *create_server_list(char *errbuf, int n_errbuf);
-void delete_server_list(struct server_list *server_list);
-int begin_lanserver_scan(void);
-struct server_list *get_lan_server_list(void); 
-void finish_lanserver_scan(void);
-
 double try_to_autoconnect(void);
 void start_autoconnecting_to_server(void);
 #endif  /* FC__CLINET_H */
Index: client/options.c
===================================================================
RCS file: /home/freeciv/CVS/freeciv/client/options.c,v
retrieving revision 1.138
diff -p -u -r1.138 options.c
--- client/options.c    2 Aug 2005 02:40:58 -0000       1.138
+++ client/options.c    18 Aug 2005 17:26:04 -0000
@@ -35,12 +35,12 @@
 #include "clinet.h"
 #include "cma_fec.h"
 #include "mapview_common.h"
+#include "options.h"
 #include "overview_common.h"
 #include "plrdlg_common.h"
+#include "servers.h"
 #include "tilespec.h"
 
-#include "options.h"
- 
 /** Defaults for options normally on command line **/
 
 char default_user_name[512] = "\0";
Index: client/servers.c
===================================================================
RCS file: client/servers.c
diff -N client/servers.c
--- /dev/null   1 Jan 1970 00:00:00 -0000
+++ client/servers.c    18 Aug 2005 17:26:05 -0000
@@ -0,0 +1,490 @@
+/**********************************************************************
+ Freeciv - Copyright (C) 1996-2005 - Freeciv Development Team
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2, or (at your option)
+   any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+***********************************************************************/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <errno.h>
+
+#ifdef HAVE_ARPA_INET_H
+#include <arpa/inet.h>
+#endif
+#ifdef HAVE_NETDB_H
+#include <netdb.h>
+#endif
+#ifdef HAVE_NETINET_IN_H
+#include <netinet/in.h>
+#endif
+#ifdef HAVE_PWD_H
+#include <pwd.h>
+#endif
+#ifdef HAVE_SYS_SELECT_H
+#include <sys/select.h>
+#endif
+#ifdef HAVE_SYS_SOCKET_H
+#include <sys/socket.h>
+#endif
+#ifdef HAVE_SYS_TIME_H
+#include <sys/time.h>
+#endif
+#ifdef HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+#ifdef HAVE_SYS_UIO_H
+#include <sys/uio.h>
+#endif
+#ifdef HAVE_SYS_UTSNAME_H
+#include <sys/utsname.h>
+#endif
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#ifdef HAVE_WINSOCK
+#include <winsock.h>
+#endif
+
+#include "capstr.h"
+#include "dataio.h"
+#include "fcintl.h"
+#include "game.h"
+#include "hash.h"
+#include "log.h"
+#include "mem.h"
+#include "netintf.h"
+#include "packets.h"
+#include "registry.h"
+#include "support.h"
+#include "version.h"
+
+#include "servers.h"
+
+#include "gui_main_g.h"
+
+static int socklan;
+static struct server_list *lan_servers;
+
+/**************************************************************************
+ The server sends a stream in a registry 'ini' type format.
+ Read it using secfile functions and fill the server_list structs.
+**************************************************************************/
+static struct server_list *parse_metaserver_data(fz_FILE *f)
+{
+  struct server_list *server_list;
+  struct section_file the_file, *file = &the_file;
+  int nservers, i, j;
+
+  server_list = server_list_new();
+
+  /* This call closes f. */
+  if (!section_file_load_from_stream(file, f)) {
+    return server_list;
+  }
+
+  nservers = secfile_lookup_int_default(file, 0, "main.nservers");
+
+  for (i = 0; i < nservers; i++) {
+    char *host, *port, *version, *state, *message, *nplayers;
+    int n;
+    struct server *pserver = (struct server*)fc_malloc(sizeof(struct server));
+
+    host = secfile_lookup_str_default(file, "", "server%d.host", i);
+    pserver->host = mystrdup(host);
+
+    port = secfile_lookup_str_default(file, "", "server%d.port", i);
+    pserver->port = mystrdup(port);
+
+    version = secfile_lookup_str_default(file, "", "server%d.version", i);
+    pserver->version = mystrdup(version);
+
+    state = secfile_lookup_str_default(file, "", "server%d.state", i);
+    pserver->state = mystrdup(state);
+
+    message = secfile_lookup_str_default(file, "", "server%d.message", i);
+    pserver->message = mystrdup(message);
+
+    nplayers = secfile_lookup_str_default(file, "0", "server%d.nplayers", i);
+    pserver->nplayers = mystrdup(nplayers);
+    n = atoi(nplayers);
+
+    if (n > 0) {
+      pserver->players = fc_malloc(n * sizeof(*pserver->players));
+    } else {
+      pserver->players = NULL;
+    }
+      
+    for (j = 0; j < n; j++) {
+      char *name, *nation, *type, *host;
+
+      name = secfile_lookup_str_default(file, "", 
+                                        "server%d.player%d.name", i, j);
+      pserver->players[j].name = mystrdup(name);
+
+      type = secfile_lookup_str_default(file, "",
+                                        "server%d.player%d.type", i, j);
+      pserver->players[j].type = mystrdup(type);
+
+      host = secfile_lookup_str_default(file, "", 
+                                        "server%d.player%d.host", i, j);
+      pserver->players[j].host = mystrdup(host);
+
+      nation = secfile_lookup_str_default(file, "",
+                                          "server%d.player%d.nation", i, j);
+      pserver->players[j].nation = mystrdup(nation);
+    }
+
+    server_list_append(server_list, pserver);
+  }
+
+  section_file_free(file);
+  return server_list;
+}
+
+/**************************************************************************
+ Create the list of servers from the metaserver
+ The result must be free'd with delete_server_list() when no
+ longer used
+**************************************************************************/
+struct server_list *create_server_list(char *errbuf, int n_errbuf)
+{
+  union my_sockaddr addr;
+  int s;
+  fz_FILE *f;
+  const char *urlpath;
+  char metaname[MAX_LEN_ADDR];
+  int metaport;
+  const char *capstr;
+  char str[MAX_LEN_PACKET];
+  char machine_string[128];
+#ifdef HAVE_UNAME
+  struct utsname un;
+#endif 
+
+  urlpath = my_lookup_httpd(metaname, &metaport, METALIST_ADDR);//metaserver);
+  if (!urlpath) {
+    (void) mystrlcpy(errbuf, _("Invalid $http_proxy or metaserver value, must "
+                              "start with 'http://'"), n_errbuf);
+    return NULL;
+  }
+
+  if (!net_lookup_service(metaname, metaport, &addr)) {
+    (void) mystrlcpy(errbuf, _("Failed looking up metaserver's host"), 
n_errbuf);
+    return NULL;
+  }
+  
+  if((s = socket(AF_INET, SOCK_STREAM, 0)) == -1) {
+    (void) mystrlcpy(errbuf, mystrerror(), n_errbuf);
+    return NULL;
+  }
+  
+  if(connect(s, (struct sockaddr *) &addr.sockaddr, sizeof(addr)) == -1) {
+    (void) mystrlcpy(errbuf, mystrerror(), n_errbuf);
+    my_closesocket(s);
+    return NULL;
+  }
+
+#ifdef HAVE_UNAME
+  uname(&un);
+  my_snprintf(machine_string,sizeof(machine_string),
+              "%s %s [%s]",
+              un.sysname,
+              un.release,
+              un.machine);
+#else /* ! HAVE_UNAME */
+  /* Fill in here if you are making a binary without sys/utsname.h and know
+     the OS name, release number, and machine architechture */
+#ifdef WIN32_NATIVE
+  sz_strlcpy(machine_string,win_uname());
+#else
+  my_snprintf(machine_string,sizeof(machine_string),
+              "unknown unknown [unknown]");
+#endif
+#endif /* HAVE_UNAME */
+
+  capstr = my_url_encode(our_capability);
+
+  my_snprintf(str, sizeof(str),
+    "POST %s HTTP/1.1\r\n"
+    "Host: %s:%d\r\n"
+    "User-Agent: Freeciv/%s %s %s\r\n"
+    "Connection: close\r\n"
+    "Content-Type: application/x-www-form-urlencoded; charset=\"utf-8\"\r\n"
+    "Content-Length: %lu\r\n"
+    "\r\n"
+    "client_cap=%s\r\n",
+    urlpath,
+    metaname, metaport,
+    VERSION_STRING, client_string, machine_string,
+    (unsigned long) (strlen("client_cap=") + strlen(capstr)),
+    capstr);
+
+  f = my_querysocket(s, str, strlen(str));
+
+  if (f == NULL) {
+    /* TRANS: This means a network error when trying to connect to
+     * the metaserver.  The message will be shown directly to the user. */
+    (void) mystrlcpy(errbuf, _("Failed querying socket"), n_errbuf);
+    return NULL;
+  }
+
+  /* skip HTTP headers */
+  while (fz_fgets(str, sizeof(str), f) && strcmp(str, "\r\n") != 0) {
+    /* nothing */
+  }
+
+  /* XXX: TODO check for magic Content-Type: text/x-ini -vasc */
+
+  /* parse HTTP message body */
+  return parse_metaserver_data(f);
+}
+
+/**************************************************************************
+ Frees everything associated with a server list including
+ the server list itself (so the server_list is no longer
+ valid after calling this function)
+**************************************************************************/
+void delete_server_list(struct server_list *server_list)
+{
+  server_list_iterate(server_list, ptmp) {
+    int i;
+    int n = atoi(ptmp->nplayers);
+
+    free(ptmp->host);
+    free(ptmp->port);
+    free(ptmp->version);
+    free(ptmp->state);
+    free(ptmp->message);
+
+    if (ptmp->players) {
+      for (i = 0; i < n; i++) {
+        free(ptmp->players[i].name);
+        free(ptmp->players[i].type);
+        free(ptmp->players[i].host);
+        free(ptmp->players[i].nation);
+      }
+      free(ptmp->players);
+    }
+    free(ptmp->nplayers);
+
+    free(ptmp);
+  } server_list_iterate_end;
+
+  server_list_unlink_all(server_list);
+  server_list_free(server_list);
+}
+
+/**************************************************************************
+  Broadcast an UDP package to all servers on LAN, requesting information 
+  about the server. The packet is send to all Freeciv servers in the same
+  multicast group as the client.
+**************************************************************************/
+int begin_lanserver_scan(void)
+{
+  union my_sockaddr addr;
+  struct data_out dout;
+  int sock, opt = 1;
+  unsigned char buffer[MAX_LEN_PACKET];
+  struct ip_mreq mreq;
+  const char *group;
+  unsigned char ttl;
+  size_t size;
+
+  /* Create a socket for broadcasting to servers. */
+  if ((sock = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
+    freelog(LOG_ERROR, "socket failed: %s", mystrerror());
+    return 0;
+  }
+
+  if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR,
+                 (char *)&opt, sizeof(opt)) == -1) {
+    freelog(LOG_ERROR, "SO_REUSEADDR failed: %s", mystrerror());
+  }
+
+  /* Set the UDP Multicast group IP address. */
+  group = get_multicast_group();
+  memset(&addr, 0, sizeof(addr));
+  addr.sockaddr_in.sin_family = AF_INET;
+  addr.sockaddr_in.sin_addr.s_addr = inet_addr(get_multicast_group());
+  addr.sockaddr_in.sin_port = htons(SERVER_LAN_PORT);
+
+  /* Set the Time-to-Live field for the packet  */
+  ttl = SERVER_LAN_TTL;
+  if (setsockopt(sock, IPPROTO_IP, IP_MULTICAST_TTL, (const char*)&ttl, 
+                 sizeof(ttl))) {
+    freelog(LOG_ERROR, "setsockopt failed: %s", mystrerror());
+    return 0;
+  }
+
+  if (setsockopt(sock, SOL_SOCKET, SO_BROADCAST, (const char*)&opt, 
+                 sizeof(opt))) {
+    freelog(LOG_ERROR, "setsockopt failed: %s", mystrerror());
+    return 0;
+  }
+
+  dio_output_init(&dout, buffer, sizeof(buffer));
+  dio_put_uint8(&dout, SERVER_LAN_VERSION);
+  size = dio_output_used(&dout);
+ 
+
+  if (sendto(sock, buffer, size, 0, &addr.sockaddr,
+      sizeof(addr)) < 0) {
+    freelog(LOG_ERROR, "sendto failed: %s", mystrerror());
+    return 0;
+  } else {
+    freelog(LOG_DEBUG, ("Sending request for server announcement on LAN."));
+  }
+
+  my_closesocket(sock);
+
+  /* Create a socket for listening for server packets. */
+  if ((socklan = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
+    freelog(LOG_ERROR, "socket failed: %s", mystrerror());
+    return 0;
+  }
+
+  my_nonblock(socklan);
+
+  if (setsockopt(socklan, SOL_SOCKET, SO_REUSEADDR,
+                 (char *)&opt, sizeof(opt)) == -1) {
+    freelog(LOG_ERROR, "SO_REUSEADDR failed: %s", mystrerror());
+  }
+                                                                               
+  memset(&addr, 0, sizeof(addr));
+  addr.sockaddr_in.sin_family = AF_INET;
+  addr.sockaddr_in.sin_addr.s_addr = htonl(INADDR_ANY); 
+  addr.sockaddr_in.sin_port = htons(SERVER_LAN_PORT + 1);
+
+  if (bind(socklan, &addr.sockaddr, sizeof(addr)) < 0) {
+    freelog(LOG_ERROR, "bind failed: %s", mystrerror());
+    return 0;
+  }
+
+  mreq.imr_multiaddr.s_addr = inet_addr(group);
+  mreq.imr_interface.s_addr = htonl(INADDR_ANY);
+  if (setsockopt(socklan, IPPROTO_IP, IP_ADD_MEMBERSHIP, 
+                 (const char*)&mreq, sizeof(mreq)) < 0) {
+    freelog(LOG_ERROR, "setsockopt failed: %s", mystrerror());
+    return 0;
+  }
+
+  lan_servers = server_list_new();
+
+  return 1;
+}
+
+/**************************************************************************
+  Listens for UDP packets broadcasted from a server that responded
+  to the request-packet sent from the client. 
+**************************************************************************/
+struct server_list *get_lan_server_list(void) {
+
+# if defined(__VMS) && !defined(_DECC_V4_SOURCE)
+  size_t fromlen;
+# else
+  unsigned int fromlen;
+# endif
+  union my_sockaddr fromend;
+  struct hostent *from;
+  char msgbuf[128];
+  int type;
+  struct data_in din;
+  char servername[512];
+  char port[256];
+  char version[256];
+  char status[256];
+  char players[256];
+  char message[1024];
+  fd_set readfs, exceptfs;
+  struct timeval tv;
+
+  FD_ZERO(&readfs);
+  FD_ZERO(&exceptfs);
+  FD_SET(socklan, &exceptfs);
+  FD_SET(socklan, &readfs);
+  tv.tv_sec = 0;
+  tv.tv_usec = 0;
+
+  while (select(socklan + 1, &readfs, NULL, &exceptfs, &tv) == -1) {
+    if (errno != EINTR) {
+      freelog(LOG_ERROR, "select failed: %s", mystrerror());
+      return lan_servers;
+    }
+    /* EINTR can happen sometimes, especially when compiling with -pg.
+     * Generally we just want to run select again. */
+  }
+
+  if (!FD_ISSET(socklan, &readfs)) {
+    return lan_servers;
+  }
+
+  dio_input_init(&din, msgbuf, sizeof(msgbuf));
+  fromlen = sizeof(fromend);
+
+  /* Try to receive a packet from a server. */ 
+  if (0 < recvfrom(socklan, msgbuf, sizeof(msgbuf), 0,
+                   &fromend.sockaddr, &fromlen)) {
+    struct server *pserver;
+
+    dio_get_uint8(&din, &type);
+    if (type != SERVER_LAN_VERSION) {
+      return lan_servers;
+    }
+    dio_get_string(&din, servername, sizeof(servername));
+    dio_get_string(&din, port, sizeof(port));
+    dio_get_string(&din, version, sizeof(version));
+    dio_get_string(&din, status, sizeof(status));
+    dio_get_string(&din, players, sizeof(players));
+    dio_get_string(&din, message, sizeof(message));
+
+    if (!mystrcasecmp("none", servername)) {
+      from = gethostbyaddr((char *) &fromend.sockaddr_in.sin_addr,
+                          sizeof(fromend.sockaddr_in.sin_addr), AF_INET);
+      sz_strlcpy(servername, inet_ntoa(fromend.sockaddr_in.sin_addr));
+    }
+
+    /* UDP can send duplicate or delayed packets. */
+    server_list_iterate(lan_servers, aserver) {
+      if (!mystrcasecmp(aserver->host, servername) 
+          && !mystrcasecmp(aserver->port, port)) {
+        return lan_servers;
+      } 
+    } server_list_iterate_end;
+
+    freelog(LOG_DEBUG,
+            ("Received a valid announcement from a server on the LAN."));
+    
+    pserver =  (struct server*)fc_malloc(sizeof(struct server));
+    pserver->host = mystrdup(servername);
+    pserver->port = mystrdup(port);
+    pserver->version = mystrdup(version);
+    pserver->state = mystrdup(status);
+    pserver->nplayers = mystrdup(players);
+    pserver->message = mystrdup(message);
+    pserver->players = NULL;
+
+    server_list_prepend(lan_servers, pserver);
+  } else {
+    return lan_servers;
+  }                                       
+
+  return lan_servers;
+}
+
+/**************************************************************************
+  Closes the socket listening on the lan and frees the list of LAN servers.
+**************************************************************************/
+void finish_lanserver_scan(void) 
+{
+  my_closesocket(socklan);
+  delete_server_list(lan_servers);
+}
Index: client/servers.h
===================================================================
RCS file: client/servers.h
diff -N client/servers.h
--- /dev/null   1 Jan 1970 00:00:00 -0000
+++ client/servers.h    18 Aug 2005 17:26:05 -0000
@@ -0,0 +1,59 @@
+/**********************************************************************
+ Freeciv - Copyright (C) 1996-2005 - Freeciv Development Team
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2, or (at your option)
+   any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+***********************************************************************/
+
+#ifndef FC__SERVERS_H
+#define FC__SERVERS_H
+
+#define METALIST_ADDR "http://meta.freeciv.org/metaserver.phtml";
+
+#define SERVER_LAN_PORT 4555
+#define SERVER_LAN_TTL 1
+#define SERVER_LAN_VERSION 1
+
+struct server
+{
+  char *host;
+  char *port;
+  char *capability;
+  char *patches;
+  char *version;
+  char *state;
+  char *topic;
+  char *message;
+
+  struct players
+  {
+    char *name;
+    char *type;
+    char *host;
+    char *nation;
+  } *players;
+
+  char *nplayers;
+};
+
+#define SPECLIST_TAG server
+#define SPECLIST_TYPE struct server
+#include "speclist.h"
+
+#define server_list_iterate(serverlist, pserver) \
+  TYPED_LIST_ITERATE(struct server, serverlist, pserver)
+#define server_list_iterate_end  LIST_ITERATE_END
+
+struct server_list *create_server_list(char *errbuf, int n_errbuf);
+void delete_server_list(struct server_list *server_list);
+int begin_lanserver_scan(void);
+struct server_list *get_lan_server_list(void); 
+void finish_lanserver_scan(void);
+
+#endif /* FC__SERVERS_H */
Index: client/gui-gtk-2.0/pages.c
===================================================================
RCS file: /home/freeciv/CVS/freeciv/client/gui-gtk-2.0/pages.c,v
retrieving revision 1.38
diff -p -u -r1.38 pages.c
--- client/gui-gtk-2.0/pages.c  18 Aug 2005 06:53:30 -0000      1.38
+++ client/gui-gtk-2.0/pages.c  18 Aug 2005 17:26:05 -0000
@@ -35,6 +35,7 @@
 #include "clinet.h"
 #include "connectdlg_common.h"
 #include "packhand.h"
+#include "servers.h"
 
 #include "dialogs_g.h"
 

[Prev in Thread] Current Thread [Next in Thread]
  • [Freeciv-Dev] (PR#13700) move server-scanning code into its own file, Jason Short <=