centralserver.c

Go to the documentation of this file.
00001 /********************************************************************\
00002  * This program is free software; you can redistribute it and/or    *
00003  * modify it under the terms of the GNU General Public License as   *
00004  * published by the Free Software Foundation; either version 2 of   *
00005  * the License, or (at your option) any later version.              *
00006  *                                                                  *
00007  * This program is distributed in the hope that it will be useful,  *
00008  * but WITHOUT ANY WARRANTY; without even the implied warranty of   *
00009  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the    *
00010  * GNU General Public License for more details.                     *
00011  *                                                                  *
00012  * You should have received a copy of the GNU General Public License*
00013  * along with this program; if not, contact:                        *
00014  *                                                                  *
00015  * Free Software Foundation           Voice:  +1-617-542-5942       *
00016  * 59 Temple Place - Suite 330        Fax:    +1-617-542-2652       *
00017  * Boston, MA  02111-1307,  USA       gnu@gnu.org                   *
00018  *                                                                  *
00019  \********************************************************************/
00020 
00021 /* $Id: centralserver.c 1243 2007-06-28 01:48:01Z benoitg $ */
00027 #include <stdio.h>
00028 #include <stdlib.h>
00029 #include <sys/types.h>
00030 #include <sys/socket.h>
00031 #include <sys/stat.h>
00032 #include <netinet/in.h>
00033 #include <arpa/inet.h>
00034 #include <errno.h>
00035 #include <unistd.h>
00036 #include <string.h>
00037 #include <syslog.h>
00038 
00039 #include "httpd.h"
00040 
00041 #include "common.h"
00042 #include "safe.h"
00043 #include "util.h"
00044 #include "auth.h"
00045 #include "conf.h"
00046 #include "debug.h"
00047 #include "centralserver.h"
00048 #include "../config.h"
00049 
00050 extern pthread_mutex_t  config_mutex;
00051 
00062 t_authcode
00063 auth_server_request(t_authresponse *authresponse, char *request_type, char *ip, char *mac, char *token, unsigned long long int incoming, unsigned long long int outgoing)
00064 {
00065         int sockfd;
00066         size_t  numbytes, totalbytes;
00067         char buf[MAX_BUF];
00068         char *tmp;
00069         int done, nfds;
00070         fd_set                  readfds;
00071         struct timeval          timeout;
00072         t_auth_serv     *auth_server = NULL;
00073         auth_server = get_auth_server();
00074         
00075         /* Blanket default is error. */
00076         authresponse->authcode = AUTH_ERROR;
00077         
00078         sockfd = connect_auth_server();
00079         if (sockfd == -1) {
00080                 /* Could not connect to any auth server */
00081                 return (AUTH_ERROR);
00082         }
00083 
00088         memset(buf, 0, sizeof(buf));
00089         snprintf(buf, (sizeof(buf) - 1),
00090                 "GET %s%sstage=%s&ip=%s&mac=%s&token=%s&incoming=%llu&outgoing=%llu HTTP/1.0\r\n"
00091                 "User-Agent: WiFiDog %s\r\n"
00092                 "Host: %s\r\n"
00093                 "\r\n",
00094                 auth_server->authserv_path,
00095                 auth_server->authserv_auth_script_path_fragment,
00096                 request_type,
00097                 ip,
00098                 mac,
00099                 token,
00100                 incoming,
00101                 outgoing,
00102                 VERSION,
00103                 auth_server->authserv_hostname
00104         );
00105 
00106         debug(LOG_DEBUG, "Sending HTTP request to auth server: [%s]\n", buf);
00107         send(sockfd, buf, strlen(buf), 0);
00108 
00109         debug(LOG_DEBUG, "Reading response");
00110         numbytes = totalbytes = 0;
00111         done = 0;
00112         do {
00113                 FD_ZERO(&readfds);
00114                 FD_SET(sockfd, &readfds);
00115                 timeout.tv_sec = 30; /* XXX magic... 30 second is as good a timeout as any */
00116                 timeout.tv_usec = 0;
00117                 nfds = sockfd + 1;
00118 
00119                 nfds = select(nfds, &readfds, NULL, NULL, &timeout);
00120 
00121                 if (nfds > 0) {
00124                         numbytes = read(sockfd, buf + totalbytes, MAX_BUF - (totalbytes + 1));
00125                         if (numbytes < 0) {
00126                                 debug(LOG_ERR, "An error occurred while reading from auth server: %s", strerror(errno));
00127                                 /* FIXME */
00128                                 close(sockfd);
00129                                 return (AUTH_ERROR);
00130                         }
00131                         else if (numbytes == 0) {
00132                                 done = 1;
00133                         }
00134                         else {
00135                                 totalbytes += numbytes;
00136                                 debug(LOG_DEBUG, "Read %d bytes, total now %d", numbytes, totalbytes);
00137                         }
00138                 }
00139                 else if (nfds == 0) {
00140                         debug(LOG_ERR, "Timed out reading data via select() from auth server");
00141                         /* FIXME */
00142                         close(sockfd);
00143                         return (AUTH_ERROR);
00144                 }
00145                 else if (nfds < 0) {
00146                         debug(LOG_ERR, "Error reading data via select() from auth server: %s", strerror(errno));
00147                         /* FIXME */
00148                         close(sockfd);
00149                         return (AUTH_ERROR);
00150                 }
00151         } while (!done);
00152 
00153         close(sockfd);
00154 
00155         buf[totalbytes] = '\0';
00156         debug(LOG_DEBUG, "HTTP Response from Server: [%s]", buf);
00157         
00158         if ((tmp = strstr(buf, "Auth: "))) {
00159                 if (sscanf(tmp, "Auth: %d", (int *)&authresponse->authcode) == 1) {
00160                         debug(LOG_INFO, "Auth server returned authentication code %d", authresponse->authcode);
00161                         return(authresponse->authcode);
00162                 } else {
00163                         debug(LOG_WARNING, "Auth server did not return expected authentication code");
00164                         return(AUTH_ERROR);
00165                 }
00166         }
00167         else {
00168                 return(AUTH_ERROR);
00169         }
00170 
00171         /* XXX Never reached because of the above if()/else pair. */
00172         return(AUTH_ERROR);
00173 }
00174 
00175 /* Tries really hard to connect to an auth server. Returns a file descriptor, -1 on error
00176  */
00177 int connect_auth_server() {
00178         int sockfd;
00179 
00180         LOCK_CONFIG();
00181         sockfd = _connect_auth_server(0);
00182         UNLOCK_CONFIG();
00183 
00184         if (sockfd == -1) {
00185                 debug(LOG_ERR, "Failed to connect to any of the auth servers");
00186                 mark_auth_offline();
00187         }
00188         else {
00189                 debug(LOG_DEBUG, "Connected to auth server");
00190                 mark_auth_online();
00191         }
00192         return (sockfd);
00193 }
00194 
00195 /* Helper function called by connect_auth_server() to do the actual work including recursion
00196  * DO NOT CALL DIRECTLY
00197  @param level recursion level indicator must be 0 when not called by _connect_auth_server()
00198  */
00199 int _connect_auth_server(int level) {
00200         s_config *config = config_get_config();
00201         t_auth_serv *auth_server = NULL;
00202         struct in_addr *h_addr;
00203         int num_servers = 0;
00204         char * hostname = NULL;
00205         char * popular_servers[] = {
00206                   "www.google.com",
00207                   "www.yahoo.com",
00208                   NULL
00209         };
00210         char ** popularserver;
00211         char * ip;
00212         struct sockaddr_in their_addr;
00213         int sockfd;
00214 
00215         /* XXX level starts out at 0 and gets incremented by every iterations. */
00216         level++;
00217 
00218         /*
00219          * Let's calculate the number of servers we have
00220          */
00221         for (auth_server = config->auth_servers; auth_server; auth_server = auth_server->next) {
00222                 num_servers++;
00223         }
00224         debug(LOG_DEBUG, "Level %d: Calculated %d auth servers in list", level, num_servers);
00225 
00226         if (level > num_servers) {
00227                 /*
00228                  * We've called ourselves too many times
00229                  * This means we've cycled through all the servers in the server list
00230                  * at least once and none are accessible
00231                  */
00232                 return (-1);
00233         }
00234 
00235         /*
00236          * Let's resolve the hostname of the top server to an IP address
00237          */
00238         auth_server = config->auth_servers;
00239         hostname = auth_server->authserv_hostname;
00240         debug(LOG_DEBUG, "Level %d: Resolving auth server [%s]", level, hostname);
00241         h_addr = wd_gethostbyname(hostname);
00242         if (!h_addr) {
00243                 /*
00244                  * DNS resolving it failed
00245                  *
00246                  * Can we resolve any of the popular servers ?
00247                  */
00248                 debug(LOG_DEBUG, "Level %d: Resolving auth server [%s] failed", level, hostname);
00249 
00250                 for (popularserver = popular_servers; *popularserver; popularserver++) {
00251                         debug(LOG_DEBUG, "Level %d: Resolving popular server [%s]", level, *popularserver);
00252                         h_addr = wd_gethostbyname(*popularserver);
00253                         if (h_addr) {
00254                                 debug(LOG_DEBUG, "Level %d: Resolving popular server [%s] succeeded = [%s]", level, *popularserver, inet_ntoa(*h_addr));
00255                                 break;
00256                         }
00257                         else {
00258                                 debug(LOG_DEBUG, "Level %d: Resolving popular server [%s] failed", level, *popularserver);
00259                         }
00260                 }
00261 
00262                 /* 
00263                  * If we got any h_addr buffer for one of the popular servers, in other
00264                  * words, if one of the popular servers resolved, we'll assume the DNS
00265                  * works, otherwise we'll deal with net connection or DNS failure.
00266                  */
00267                 if (h_addr) {
00268                         free (h_addr);
00269                         /*
00270                          * Yes
00271                          *
00272                          * The auth server's DNS server is probably dead. Try the next auth server
00273                          */
00274                         debug(LOG_DEBUG, "Level %d: Marking auth server [%s] as bad and trying next if possible", level, hostname);
00275                         if (auth_server->last_ip) {
00276                                 free(auth_server->last_ip);
00277                                 auth_server->last_ip = NULL;
00278                         }
00279                         mark_auth_server_bad(auth_server);
00280                         return _connect_auth_server(level);
00281                 }
00282                 else {
00283                         /*
00284                          * No
00285                          *
00286                          * It's probably safe to assume that the internet connection is malfunctioning
00287                          * and nothing we can do will make it work
00288                          */
00289                         mark_offline();
00290                         debug(LOG_DEBUG, "Level %d: Failed to resolve auth server and all popular servers. "
00291                                         "The internet connection is probably down", level);
00292                         return(-1);
00293                 }
00294         }
00295         else {
00296                 /*
00297                  * DNS resolving was successful
00298                  */
00299                 ip = safe_strdup(inet_ntoa(*h_addr));
00300                 debug(LOG_DEBUG, "Level %d: Resolving auth server [%s] succeeded = [%s]", level, hostname, ip);
00301 
00302                 if (!auth_server->last_ip || strcmp(auth_server->last_ip, ip) != 0) {
00303                         /*
00304                          * But the IP address is different from the last one we knew
00305                          * Update it
00306                          */
00307                         debug(LOG_DEBUG, "Level %d: Updating last_ip IP of server [%s] to [%s]", level, hostname, ip);
00308                         if (auth_server->last_ip) free(auth_server->last_ip);
00309                         auth_server->last_ip = ip;
00310 
00311                         /* Update firewall rules */
00312                         fw_clear_authservers();
00313                         fw_set_authservers();
00314                 }
00315                 else {
00316                         /*
00317                          * IP is the same as last time
00318                          */
00319                         free(ip);
00320                 }
00321 
00322                 /*
00323                  * Connect to it
00324                  */
00325                 debug(LOG_DEBUG, "Level %d: Connecting to auth server %s:%d", level, hostname, auth_server->authserv_http_port);
00326                 their_addr.sin_family = AF_INET;
00327                 their_addr.sin_port = htons(auth_server->authserv_http_port);
00328                 their_addr.sin_addr = *h_addr;
00329                 memset(&(their_addr.sin_zero), '\0', sizeof(their_addr.sin_zero));
00330                 free (h_addr);
00331 
00332                 if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) == -1) {
00333                         debug(LOG_ERR, "Level %d: Failed to create a new SOCK_STREAM socket: %s", strerror(errno));
00334                         return(-1);
00335                 }
00336 
00337                 if (connect(sockfd, (struct sockaddr *)&their_addr, sizeof(struct sockaddr)) == -1) {
00338                         /*
00339                          * Failed to connect
00340                          * Mark the server as bad and try the next one
00341                          */
00342                         debug(LOG_DEBUG, "Level %d: Failed to connect to auth server %s:%d (%s). Marking it as bad and trying next if possible", level, hostname, auth_server->authserv_http_port, strerror(errno));
00343                         close(sockfd);
00344                         mark_auth_server_bad(auth_server);
00345                         return _connect_auth_server(level); /* Yay recursion! */
00346                 }
00347                 else {
00348                         /*
00349                          * We have successfully connected
00350                          */
00351                         debug(LOG_DEBUG, "Level %d: Successfully connected to auth server %s:%d", level, hostname, auth_server->authserv_http_port);
00352                         return sockfd;
00353                 }
00354         }
00355 }

Generated on Thu Oct 18 17:15:19 2007 for WifiDog by  doxygen 1.5.3