root/trunk/wifidog/src/centralserver.c

Revision 1377, 11.0 kB (checked in by wichert, 3 months ago)

Include the gw_id in auth server updates.

This makes it possible to simplify auth servers handling multiple gateways a
lot: they need to maintain a lot less state between capture and final login.
Note that this is not part of the official protocol spec.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
Line 
1 /********************************************************************\
2  * This program is free software; you can redistribute it and/or    *
3  * modify it under the terms of the GNU General Public License as   *
4  * published by the Free Software Foundation; either version 2 of   *
5  * the License, or (at your option) any later version.              *
6  *                                                                  *
7  * This program is distributed in the hope that it will be useful,  *
8  * but WITHOUT ANY WARRANTY; without even the implied warranty of   *
9  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the    *
10  * GNU General Public License for more details.                     *
11  *                                                                  *
12  * You should have received a copy of the GNU General Public License*
13  * along with this program; if not, contact:                        *
14  *                                                                  *
15  * Free Software Foundation           Voice:  +1-617-542-5942       *
16  * 59 Temple Place - Suite 330        Fax:    +1-617-542-2652       *
17  * Boston, MA  02111-1307,  USA       gnu@gnu.org                   *
18  *                                                                  *
19  \********************************************************************/
20
21 /* $Id$ */
22 /** @file centralserver.c
23   @brief Functions to talk to the central server (auth/send stats/get rules/etc...)
24   @author Copyright (C) 2004 Philippe April <papril777@yahoo.com>
25  */
26
27 #include <pthread.h>
28 #include <stdio.h>
29 #include <stdlib.h>
30 #include <sys/types.h>
31 #include <sys/socket.h>
32 #include <sys/stat.h>
33 #include <netinet/in.h>
34 #include <arpa/inet.h>
35 #include <errno.h>
36 #include <unistd.h>
37 #include <string.h>
38 #include <syslog.h>
39
40 #include "httpd.h"
41
42 #include "common.h"
43 #include "safe.h"
44 #include "util.h"
45 #include "auth.h"
46 #include "conf.h"
47 #include "debug.h"
48 #include "centralserver.h"
49 #include "firewall.h"
50 #include "../config.h"
51
52 extern pthread_mutex_t  config_mutex;
53
54 /** Initiates a transaction with the auth server, either to authenticate or to
55  * update the traffic counters at the server
56 @param authresponse Returns the information given by the central server
57 @param request_type Use the REQUEST_TYPE_* defines in centralserver.h
58 @param ip IP adress of the client this request is related to
59 @param mac MAC adress of the client this request is related to
60 @param token Authentification token of the client
61 @param incoming Current counter of the client's total incoming traffic, in bytes
62 @param outgoing Current counter of the client's total outgoing traffic, in bytes
63 */
64 t_authcode
65 auth_server_request(t_authresponse *authresponse, const char *request_type, const char *ip, const char *mac, const char *token, unsigned long long int incoming, unsigned long long int outgoing)
66 {
67         int sockfd;
68         ssize_t numbytes;
69         size_t totalbytes;
70         char buf[MAX_BUF];
71         char *tmp;
72         char *safe_token;
73         int done, nfds;
74         fd_set                  readfds;
75         struct timeval          timeout;
76         t_auth_serv     *auth_server = NULL;
77         auth_server = get_auth_server();
78        
79         /* Blanket default is error. */
80         authresponse->authcode = AUTH_ERROR;
81        
82         sockfd = connect_auth_server();
83         if (sockfd == -1) {
84                 /* Could not connect to any auth server */
85                 return (AUTH_ERROR);
86         }
87
88         /**
89          * TODO: XXX change the PHP so we can harmonize stage as request_type
90          * everywhere.
91          */
92         memset(buf, 0, sizeof(buf));
93         safe_token=httpdUrlEncode(token);
94         snprintf(buf, (sizeof(buf) - 1),
95                 "GET %s%sstage=%s&ip=%s&mac=%s&token=%s&incoming=%llu&outgoing=%llu&gw_id=%s HTTP/1.0\r\n"
96                 "User-Agent: WiFiDog %s\r\n"
97                 "Host: %s\r\n"
98                 "\r\n",
99                 auth_server->authserv_path,
100                 auth_server->authserv_auth_script_path_fragment,
101                 request_type,
102                 ip,
103                 mac,
104                 safe_token,
105                 incoming,
106                 outgoing,
107                 config_get_config()->gw_id,
108                 VERSION,
109                 auth_server->authserv_hostname
110         );
111
112         free(safe_token);
113
114         debug(LOG_DEBUG, "Sending HTTP request to auth server: [%s]\n", buf);
115         send(sockfd, buf, strlen(buf), 0);
116
117         debug(LOG_DEBUG, "Reading response");
118         numbytes = totalbytes = 0;
119         done = 0;
120         do {
121                 FD_ZERO(&readfds);
122                 FD_SET(sockfd, &readfds);
123                 timeout.tv_sec = 30; /* XXX magic... 30 second is as good a timeout as any */
124                 timeout.tv_usec = 0;
125                 nfds = sockfd + 1;
126
127                 nfds = select(nfds, &readfds, NULL, NULL, &timeout);
128
129                 if (nfds > 0) {
130                         /** We don't have to use FD_ISSET() because there
131                          *  was only one fd. */
132                         numbytes = read(sockfd, buf + totalbytes, MAX_BUF - (totalbytes + 1));
133                         if (numbytes < 0) {
134                                 debug(LOG_ERR, "An error occurred while reading from auth server: %s", strerror(errno));
135                                 /* FIXME */
136                                 close(sockfd);
137                                 return (AUTH_ERROR);
138                         }
139                         else if (numbytes == 0) {
140                                 done = 1;
141                         }
142                         else {
143                                 totalbytes += numbytes;
144                                 debug(LOG_DEBUG, "Read %d bytes, total now %d", numbytes, totalbytes);
145                         }
146                 }
147                 else if (nfds == 0) {
148                         debug(LOG_ERR, "Timed out reading data via select() from auth server");
149                         /* FIXME */
150                         close(sockfd);
151                         return (AUTH_ERROR);
152                 }
153                 else if (nfds < 0) {
154                         debug(LOG_ERR, "Error reading data via select() from auth server: %s", strerror(errno));
155                         /* FIXME */
156                         close(sockfd);
157                         return (AUTH_ERROR);
158                 }
159         } while (!done);
160
161         close(sockfd);
162
163         buf[totalbytes] = '\0';
164         debug(LOG_DEBUG, "HTTP Response from Server: [%s]", buf);
165        
166         if ((tmp = strstr(buf, "Auth: "))) {
167                 if (sscanf(tmp, "Auth: %d", (int *)&authresponse->authcode) == 1) {
168                         debug(LOG_INFO, "Auth server returned authentication code %d", authresponse->authcode);
169                         return(authresponse->authcode);
170                 } else {
171                         debug(LOG_WARNING, "Auth server did not return expected authentication code");
172                         return(AUTH_ERROR);
173                 }
174         }
175         else {
176                 return(AUTH_ERROR);
177         }
178
179         /* XXX Never reached because of the above if()/else pair. */
180         return(AUTH_ERROR);
181 }
182
183 /* Tries really hard to connect to an auth server. Returns a file descriptor, -1 on error
184  */
185 int connect_auth_server() {
186         int sockfd;
187
188         LOCK_CONFIG();
189         sockfd = _connect_auth_server(0);
190         UNLOCK_CONFIG();
191
192         if (sockfd == -1) {
193                 debug(LOG_ERR, "Failed to connect to any of the auth servers");
194                 mark_auth_offline();
195         }
196         else {
197                 debug(LOG_DEBUG, "Connected to auth server");
198                 mark_auth_online();
199         }
200         return (sockfd);
201 }
202
203 /* Helper function called by connect_auth_server() to do the actual work including recursion
204  * DO NOT CALL DIRECTLY
205  @param level recursion level indicator must be 0 when not called by _connect_auth_server()
206  */
207 int _connect_auth_server(int level) {
208         s_config *config = config_get_config();
209         t_auth_serv *auth_server = NULL;
210         struct in_addr *h_addr;
211         int num_servers = 0;
212         char * hostname = NULL;
213         char * popular_servers[] = {
214                   "www.google.com",
215                   "www.yahoo.com",
216                   NULL
217         };
218         char ** popularserver;
219         char * ip;
220         struct sockaddr_in their_addr;
221         int sockfd;
222
223         /* XXX level starts out at 0 and gets incremented by every iterations. */
224         level++;
225
226         /*
227          * Let's calculate the number of servers we have
228          */
229         for (auth_server = config->auth_servers; auth_server; auth_server = auth_server->next) {
230                 num_servers++;
231         }
232         debug(LOG_DEBUG, "Level %d: Calculated %d auth servers in list", level, num_servers);
233
234         if (level > num_servers) {
235                 /*
236                  * We've called ourselves too many times
237                  * This means we've cycled through all the servers in the server list
238                  * at least once and none are accessible
239                  */
240                 return (-1);
241         }
242
243         /*
244          * Let's resolve the hostname of the top server to an IP address
245          */
246         auth_server = config->auth_servers;
247         hostname = auth_server->authserv_hostname;
248         debug(LOG_DEBUG, "Level %d: Resolving auth server [%s]", level, hostname);
249         h_addr = wd_gethostbyname(hostname);
250         if (!h_addr) {
251                 /*
252                  * DNS resolving it failed
253                  *
254                  * Can we resolve any of the popular servers ?
255                  */
256                 debug(LOG_DEBUG, "Level %d: Resolving auth server [%s] failed", level, hostname);
257
258                 for (popularserver = popular_servers; *popularserver; popularserver++) {
259                         debug(LOG_DEBUG, "Level %d: Resolving popular server [%s]", level, *popularserver);
260                         h_addr = wd_gethostbyname(*popularserver);
261                         if (h_addr) {
262                                 debug(LOG_DEBUG, "Level %d: Resolving popular server [%s] succeeded = [%s]", level, *popularserver, inet_ntoa(*h_addr));
263                                 break;
264                         }
265                         else {
266                                 debug(LOG_DEBUG, "Level %d: Resolving popular server [%s] failed", level, *popularserver);
267                         }
268                 }
269
270                 /*
271                  * If we got any h_addr buffer for one of the popular servers, in other
272                  * words, if one of the popular servers resolved, we'll assume the DNS
273                  * works, otherwise we'll deal with net connection or DNS failure.
274                  */
275                 if (h_addr) {
276                         free (h_addr);
277                         /*
278                          * Yes
279                          *
280                          * The auth server's DNS server is probably dead. Try the next auth server
281                          */
282                         debug(LOG_DEBUG, "Level %d: Marking auth server [%s] as bad and trying next if possible", level, hostname);
283                         if (auth_server->last_ip) {
284                                 free(auth_server->last_ip);
285                                 auth_server->last_ip = NULL;
286                         }
287                         mark_auth_server_bad(auth_server);
288                         return _connect_auth_server(level);
289                 }
290                 else {
291                         /*
292                          * No
293                          *
294                          * It's probably safe to assume that the internet connection is malfunctioning
295                          * and nothing we can do will make it work
296                          */
297                         mark_offline();
298                         debug(LOG_DEBUG, "Level %d: Failed to resolve auth server and all popular servers. "
299                                         "The internet connection is probably down", level);
300                         return(-1);
301                 }
302         }
303         else {
304                 /*
305                  * DNS resolving was successful
306                  */
307                 ip = safe_strdup(inet_ntoa(*h_addr));
308                 debug(LOG_DEBUG, "Level %d: Resolving auth server [%s] succeeded = [%s]", level, hostname, ip);
309
310                 if (!auth_server->last_ip || strcmp(auth_server->last_ip, ip) != 0) {
311                         /*
312                          * But the IP address is different from the last one we knew
313                          * Update it
314                          */
315                         debug(LOG_DEBUG, "Level %d: Updating last_ip IP of server [%s] to [%s]", level, hostname, ip);
316                         if (auth_server->last_ip) free(auth_server->last_ip);
317                         auth_server->last_ip = ip;
318
319                         /* Update firewall rules */
320                         fw_clear_authservers();
321                         fw_set_authservers();
322                 }
323                 else {
324                         /*
325                          * IP is the same as last time
326                          */
327                         free(ip);
328                 }
329
330                 /*
331                  * Connect to it
332                  */
333                 debug(LOG_DEBUG, "Level %d: Connecting to auth server %s:%d", level, hostname, auth_server->authserv_http_port);
334                 their_addr.sin_family = AF_INET;
335                 their_addr.sin_port = htons(auth_server->authserv_http_port);
336                 their_addr.sin_addr = *h_addr;
337                 memset(&(their_addr.sin_zero), '\0', sizeof(their_addr.sin_zero));
338                 free (h_addr);
339
340                 if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) == -1) {
341                         debug(LOG_ERR, "Level %d: Failed to create a new SOCK_STREAM socket: %s", strerror(errno));
342                         return(-1);
343                 }
344
345                 if (connect(sockfd, (struct sockaddr *)&their_addr, sizeof(struct sockaddr)) == -1) {
346                         /*
347                          * Failed to connect
348                          * Mark the server as bad and try the next one
349                          */
350                         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));
351                         close(sockfd);
352                         mark_auth_server_bad(auth_server);
353                         return _connect_auth_server(level); /* Yay recursion! */
354                 }
355                 else {
356                         /*
357                          * We have successfully connected
358                          */
359                         debug(LOG_DEBUG, "Level %d: Successfully connected to auth server %s:%d", level, hostname, auth_server->authserv_http_port);
360                         return sockfd;
361                 }
362         }
363 }
Note: See TracBrowser for help on using the browser.