root/trunk/wifidog/src/util.c

Revision 1429, 13.9 KB (checked in by gbastien, 4 years ago)
  • Fix #625, does not display failure notice when quiet is set to true
  • Fix #587, change index and rindex to strchr and strrchr
  • Fix #548, trim leading spaces of the config file's options
  • 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/*
22 * $Id$
23 */
24/**
25  @file util.c
26  @brief Misc utility functions
27  @author Copyright (C) 2004 Philippe April <papril777@yahoo.com>
28  @author Copyright (C) 2006 Benoit Grégoire <bock@step.polymtl.ca>
29 */
30
31#define _GNU_SOURCE
32
33#include <stdio.h>
34#include <stdlib.h>
35#include <syslog.h>
36#include <errno.h>
37#include <pthread.h>
38#include <sys/wait.h>
39#include <sys/types.h>
40#include <sys/unistd.h>
41#include <netinet/in.h>
42#include <sys/ioctl.h>
43#include <arpa/inet.h>
44
45#if defined(__NetBSD__)
46#include <sys/socket.h>
47#include <ifaddrs.h>
48#include <net/if.h>
49#include <net/if_dl.h>
50#include <util.h>
51#endif
52
53#ifdef __linux__
54#include <netinet/in.h>
55#include <net/if.h>
56#endif
57
58#include <string.h>
59#include <pthread.h>
60#include <netdb.h>
61
62#include "common.h"
63#include "client_list.h"
64#include "safe.h"
65#include "util.h"
66#include "conf.h"
67#include "debug.h"
68
69#include "../config.h"
70
71static pthread_mutex_t ghbn_mutex = PTHREAD_MUTEX_INITIALIZER;
72
73/* Defined in ping_thread.c */
74extern time_t started_time;
75
76/* Defined in clientlist.c */
77extern  pthread_mutex_t client_list_mutex;
78extern  pthread_mutex_t config_mutex;
79
80/* Defined in commandline.c */
81extern pid_t restart_orig_pid;
82
83/* XXX Do these need to be locked ? */
84static time_t last_online_time = 0;
85static time_t last_offline_time = 0;
86static time_t last_auth_online_time = 0;
87static time_t last_auth_offline_time = 0;
88
89long served_this_session = 0;
90
91/** Fork a child and execute a shell command, the parent
92 * process waits for the child to return and returns the child's exit()
93 * value.
94 * @return Return code of the command
95 */
96int
97execute(char *cmd_line, int quiet)
98{
99        int pid,
100            status,
101            rc;
102
103        const char *new_argv[4];
104        new_argv[0] = "/bin/sh";
105        new_argv[1] = "-c";
106        new_argv[2] = cmd_line;
107        new_argv[3] = NULL;
108
109        pid = safe_fork();
110        if (pid == 0) {    /* for the child process:         */
111                /* We don't want to see any errors if quiet flag is on */
112                if (quiet) close(2);
113                if (execvp("/bin/sh", (char *const *)new_argv) == -1) {    /* execute the command  */
114                        debug(LOG_ERR, "execvp(): %s", strerror(errno));
115                } else {
116                        debug(LOG_ERR, "execvp() failed");
117                }
118                exit(1);
119        }
120
121        /* for the parent:      */
122        debug(LOG_DEBUG, "Waiting for PID %d to exit", pid);
123        rc = waitpid(pid, &status, 0);
124        debug(LOG_DEBUG, "Process PID %d exited", rc);
125
126        return (WEXITSTATUS(status));
127}
128
129        struct in_addr *
130wd_gethostbyname(const char *name)
131{
132        struct hostent *he;
133        struct in_addr *h_addr, *in_addr_temp;
134
135        /* XXX Calling function is reponsible for free() */
136
137        h_addr = safe_malloc(sizeof(struct in_addr));
138
139        LOCK_GHBN();
140
141        he = gethostbyname(name);
142
143        if (he == NULL) {
144                free(h_addr);
145                UNLOCK_GHBN();
146                return NULL;
147        }
148
149        mark_online();
150
151        in_addr_temp = (struct in_addr *)he->h_addr_list[0];
152        h_addr->s_addr = in_addr_temp->s_addr;
153
154        UNLOCK_GHBN();
155
156        return h_addr;
157}
158
159        char *
160get_iface_ip(const char *ifname)
161{
162#if defined(__linux__)
163        struct ifreq if_data;
164        struct in_addr in;
165        char *ip_str;
166        int sockd;
167        u_int32_t ip;
168
169        /* Create a socket */
170        if ((sockd = socket (AF_INET, SOCK_PACKET, htons(0x8086))) < 0) {
171                debug(LOG_ERR, "socket(): %s", strerror(errno));
172                return NULL;
173        }
174
175        /* Get IP of internal interface */
176        strcpy (if_data.ifr_name, ifname);
177
178        /* Get the IP address */
179        if (ioctl (sockd, SIOCGIFADDR, &if_data) < 0) {
180                debug(LOG_ERR, "ioctl(): SIOCGIFADDR %s", strerror(errno));
181                return NULL;
182        }
183        memcpy ((void *) &ip, (void *) &if_data.ifr_addr.sa_data + 2, 4);
184        in.s_addr = ip;
185
186        ip_str = inet_ntoa(in);
187        close(sockd);
188        return safe_strdup(ip_str);
189#elif defined(__NetBSD__)
190        struct ifaddrs *ifa, *ifap;
191        char *str = NULL;
192
193        if (getifaddrs(&ifap) == -1) {
194                debug(LOG_ERR, "getifaddrs(): %s", strerror(errno));
195                return NULL;
196        }
197        /* XXX arbitrarily pick the first IPv4 address */
198        for (ifa = ifap; ifa != NULL; ifa = ifa->ifa_next) {
199                if (strcmp(ifa->ifa_name, ifname) == 0 &&
200                                ifa->ifa_addr->sa_family == AF_INET)
201                        break;
202        }
203        if (ifa == NULL) {
204                debug(LOG_ERR, "%s: no IPv4 address assigned");
205                goto out;
206        }
207        str = safe_strdup(inet_ntoa(
208                                ((struct sockaddr_in *)ifa->ifa_addr)->sin_addr));
209out:
210        freeifaddrs(ifap);
211        return str;
212#else
213        return safe_strdup("0.0.0.0");
214#endif
215}
216
217        char *
218get_iface_mac(const char *ifname)
219{
220#if defined(__linux__)
221        int r, s;
222        struct ifreq ifr;
223        char *hwaddr, mac[13];
224
225        strcpy(ifr.ifr_name, ifname);
226
227        s = socket(PF_INET, SOCK_DGRAM, 0);
228        if (-1 == s) {
229                debug(LOG_ERR, "get_iface_mac socket: %s", strerror(errno));
230                return NULL;
231        }
232
233        r = ioctl(s, SIOCGIFHWADDR, &ifr);
234        if (r == -1) {
235                debug(LOG_ERR, "get_iface_mac ioctl(SIOCGIFHWADDR): %s", strerror(errno));
236                close(s);
237                return NULL;
238        }
239
240        hwaddr = ifr.ifr_hwaddr.sa_data;
241        close(s);
242        snprintf(mac, sizeof(mac), "%02X%02X%02X%02X%02X%02X", 
243                        hwaddr[0] & 0xFF,
244                        hwaddr[1] & 0xFF,
245                        hwaddr[2] & 0xFF,
246                        hwaddr[3] & 0xFF,
247                        hwaddr[4] & 0xFF,
248                        hwaddr[5] & 0xFF
249                );
250
251        return safe_strdup(mac);
252#elif defined(__NetBSD__)
253        struct ifaddrs *ifa, *ifap;
254        const char *hwaddr;
255        char mac[13], *str = NULL;
256        struct sockaddr_dl *sdl;
257
258        if (getifaddrs(&ifap) == -1) {
259                debug(LOG_ERR, "getifaddrs(): %s", strerror(errno));
260                return NULL;
261        }
262        for (ifa = ifap; ifa != NULL; ifa = ifa->ifa_next) {
263                if (strcmp(ifa->ifa_name, ifname) == 0 &&
264                                ifa->ifa_addr->sa_family == AF_LINK)
265                        break;
266        }
267        if (ifa == NULL) {
268                debug(LOG_ERR, "%s: no link-layer address assigned");
269                goto out;
270        }
271        sdl = (struct sockaddr_dl *)ifa->ifa_addr;
272        hwaddr = LLADDR(sdl);
273        snprintf(mac, sizeof(mac), "%02X%02X%02X%02X%02X%02X",
274                        hwaddr[0] & 0xFF, hwaddr[1] & 0xFF,
275                        hwaddr[2] & 0xFF, hwaddr[3] & 0xFF,
276                        hwaddr[4] & 0xFF, hwaddr[5] & 0xFF);
277
278        str = safe_strdup(mac);
279out:
280        freeifaddrs(ifap);
281        return str;
282#else
283        return NULL;
284#endif
285}
286
287        char *
288get_ext_iface(void)
289{
290#ifdef __linux__
291        FILE *input;
292        char *device, *gw;
293        int i = 1;
294        int keep_detecting = 1;
295        pthread_cond_t          cond = PTHREAD_COND_INITIALIZER;
296        pthread_mutex_t         cond_mutex = PTHREAD_MUTEX_INITIALIZER;
297        struct  timespec        timeout;
298        device = (char *)malloc(16);
299        gw = (char *)malloc(16);
300        debug(LOG_DEBUG, "get_ext_iface(): Autodectecting the external interface from routing table");
301        while(keep_detecting) {
302                input = fopen("/proc/net/route", "r");
303                while (!feof(input)) {
304                        /* XXX scanf(3) is unsafe, risks overrun */ 
305                        fscanf(input, "%s %s %*s %*s %*s %*s %*s %*s %*s %*s %*s\n", device, gw);
306                        if (strcmp(gw, "00000000") == 0) {
307                                free(gw);
308                                debug(LOG_INFO, "get_ext_iface(): Detected %s as the default interface after try %d", device, i);
309                                return device;
310                        }
311                }
312                fclose(input);
313                debug(LOG_ERR, "get_ext_iface(): Failed to detect the external interface after try %d (maybe the interface is not up yet?).  Retry limit: %d", i, NUM_EXT_INTERFACE_DETECT_RETRY);
314                /* Sleep for EXT_INTERFACE_DETECT_RETRY_INTERVAL seconds */
315                timeout.tv_sec = time(NULL) + EXT_INTERFACE_DETECT_RETRY_INTERVAL;
316                timeout.tv_nsec = 0;
317                /* Mutex must be locked for pthread_cond_timedwait... */
318                pthread_mutex_lock(&cond_mutex);       
319                /* Thread safe "sleep" */
320                pthread_cond_timedwait(&cond, &cond_mutex, &timeout);
321                /* No longer needs to be locked */
322                pthread_mutex_unlock(&cond_mutex);
323                //for (i=1; i<=NUM_EXT_INTERFACE_DETECT_RETRY; i++) {
324                if (NUM_EXT_INTERFACE_DETECT_RETRY != 0 && i>NUM_EXT_INTERFACE_DETECT_RETRY) {
325                        keep_detecting = 0;
326                }
327                i++;
328        }
329        debug(LOG_ERR, "get_ext_iface(): Failed to detect the external interface after %d tries, aborting", i);
330        exit(1);
331        free(device);
332        free(gw);
333#endif
334        return NULL;
335        }
336
337        void mark_online() {
338                int before;
339                int after;
340
341                before = is_online();
342                time(&last_online_time);
343                after = is_online();
344
345                if (before != after) {
346                        debug(LOG_INFO, "ONLINE status became %s", (after ? "ON" : "OFF"));
347                }
348
349        }
350
351        void mark_offline() {
352                int before;
353                int after;
354
355                before = is_online();
356                time(&last_offline_time);
357                after = is_online();
358
359                if (before != after) {
360                        debug(LOG_INFO, "ONLINE status became %s", (after ? "ON" : "OFF"));
361                }
362
363                /* If we're offline it definately means the auth server is offline */
364                mark_auth_offline();
365
366        }
367
368        int is_online() {
369                if (last_online_time == 0 || (last_offline_time - last_online_time) >= (config_get_config()->checkinterval * 2) ) {
370                        /* We're probably offline */
371                        return (0);
372                }
373                else {
374                        /* We're probably online */
375                        return (1);
376                }
377        }
378
379        void mark_auth_online() {
380                int before;
381                int after;
382
383                before = is_auth_online();
384                time(&last_auth_online_time);
385                after = is_auth_online();
386
387                if (before != after) {
388                        debug(LOG_INFO, "AUTH_ONLINE status became %s", (after ? "ON" : "OFF"));
389                }
390
391                /* If auth server is online it means we're definately online */
392                mark_online();
393
394        }
395
396        void mark_auth_offline() {
397                int before;
398                int after;
399
400                before = is_auth_online();
401                time(&last_auth_offline_time);
402                after = is_auth_online();
403
404                if (before != after) {
405                        debug(LOG_INFO, "AUTH_ONLINE status became %s", (after ? "ON" : "OFF"));
406                }
407
408        }
409
410        int is_auth_online() {
411                if (!is_online()) {
412                        /* If we're not online auth is definately not online :) */
413                        return (0);
414                }
415                else if (last_auth_online_time == 0 || (last_auth_offline_time - last_auth_online_time) >= (config_get_config()->checkinterval * 2) ) {
416                        /* Auth is  probably offline */
417                        return (0);
418                }
419                else {
420                        /* Auth is probably online */
421                        return (1);
422                }
423        }
424
425        /*
426         * @return A string containing human-readable status text. MUST BE free()d by caller
427         */
428        char * get_status_text() {
429                char buffer[STATUS_BUF_SIZ];
430                ssize_t len;
431                s_config *config;
432                t_auth_serv *auth_server;
433                t_client        *first;
434                int             count;
435                unsigned long int uptime = 0;
436                unsigned int days = 0, hours = 0, minutes = 0, seconds = 0;
437                t_trusted_mac *p;
438
439                len = 0;
440                snprintf(buffer, (sizeof(buffer) - len), "WiFiDog status\n\n");
441                len = strlen(buffer);
442
443                uptime = time(NULL) - started_time;
444                days    = uptime / (24 * 60 * 60);
445                uptime -= days * (24 * 60 * 60);
446                hours   = uptime / (60 * 60);
447                uptime -= hours * (60 * 60);
448                minutes = uptime / 60;
449                uptime -= minutes * 60;
450                seconds = uptime;
451
452                snprintf((buffer + len), (sizeof(buffer) - len), "Version: " VERSION "\n");
453                len = strlen(buffer);
454
455                snprintf((buffer + len), (sizeof(buffer) - len), "Uptime: %ud %uh %um %us\n", days, hours, minutes, seconds);
456                len = strlen(buffer);
457
458                snprintf((buffer + len), (sizeof(buffer) - len), "Has been restarted: ");
459                len = strlen(buffer);
460                if (restart_orig_pid) {
461                        snprintf((buffer + len), (sizeof(buffer) - len), "yes (from PID %d)\n", restart_orig_pid);
462                        len = strlen(buffer);
463                }
464                else {
465                        snprintf((buffer + len), (sizeof(buffer) - len), "no\n");
466                        len = strlen(buffer);
467                }
468
469                snprintf((buffer + len), (sizeof(buffer) - len), "Internet Connectivity: %s\n", (is_online() ? "yes" : "no"));
470                len = strlen(buffer);
471
472                snprintf((buffer + len), (sizeof(buffer) - len), "Auth server reachable: %s\n", (is_auth_online() ? "yes" : "no"));
473                len = strlen(buffer);
474
475                snprintf((buffer + len), (sizeof(buffer) - len), "Clients served this session: %lu\n\n", served_this_session);
476                len = strlen(buffer);
477
478                LOCK_CLIENT_LIST();
479
480                first = client_get_first_client();
481
482                if (first == NULL) {
483                        count = 0;
484                } else {
485                        count = 1;
486                        while (first->next != NULL) {
487                                first = first->next;
488                                count++;
489                        }
490                }
491
492                snprintf((buffer + len), (sizeof(buffer) - len), "%d clients "
493                                "connected.\n", count);
494                len = strlen(buffer);
495
496                first = client_get_first_client();
497
498                count = 0;
499                while (first != NULL) {
500                        snprintf((buffer + len), (sizeof(buffer) - len), "\nClient %d\n", count);
501                        len = strlen(buffer);
502
503                        snprintf((buffer + len), (sizeof(buffer) - len), "  IP: %s MAC: %s\n", first->ip, first->mac);
504                        len = strlen(buffer);
505
506                        snprintf((buffer + len), (sizeof(buffer) - len), "  Token: %s\n", first->token);
507                        len = strlen(buffer);
508
509                        snprintf((buffer + len), (sizeof(buffer) - len), "  Downloaded: %llu\n  Uploaded: %llu\n" , first->counters.incoming, first->counters.outgoing);
510                        len = strlen(buffer);
511
512                        count++;
513                        first = first->next;
514                }
515
516                UNLOCK_CLIENT_LIST();
517
518                config = config_get_config();
519
520                if (config->trustedmaclist != NULL) {
521                        snprintf((buffer + len), (sizeof(buffer) - len), "\nTrusted MAC addresses:\n");
522                        len = strlen(buffer);
523
524                        for (p = config->trustedmaclist; p != NULL; p = p->next) {
525                                snprintf((buffer + len), (sizeof(buffer) - len), "  %s\n", p->mac);
526                                len = strlen(buffer);
527                        }
528                }
529
530                snprintf((buffer + len), (sizeof(buffer) - len), "\nAuthentication servers:\n");
531                len = strlen(buffer);
532
533                LOCK_CONFIG();
534
535                for (auth_server = config->auth_servers; auth_server != NULL; auth_server = auth_server->next) {
536                        snprintf((buffer + len), (sizeof(buffer) - len), "  Host: %s (%s)\n", auth_server->authserv_hostname, auth_server->last_ip);
537                        len = strlen(buffer);
538                }
539
540                UNLOCK_CONFIG();
541
542                return safe_strdup(buffer);
543        }
Note: See TracBrowser for help on using the browser.