/********************************************************************\ * 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 of * * the License, 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. * * * * You should have received a copy of the GNU General Public License* * along with this program; if not, contact: * * * * Free Software Foundation Voice: +1-617-542-5942 * * 59 Temple Place - Suite 330 Fax: +1-617-542-2652 * * Boston, MA 02111-1307, USA gnu@gnu.org * * * \********************************************************************/ /* * $Id: util.c 1162 2007-01-06 23:51:02Z benoitg $ */ /** @file util.c @brief Misc utility functions @author Copyright (C) 2004 Philippe April @author Copyright (C) 2006 Benoit Grégoire */ #define _GNU_SOURCE #include #include #include #include #include #include #include #include #include #include #ifdef __linux__ #include #endif #include #include #include #include "common.h" #include "client_list.h" #include "safe.h" #include "util.h" #include "conf.h" #include "debug.h" #include "firewall.h" #include "../config.h" static pthread_mutex_t ghbn_mutex = PTHREAD_MUTEX_INITIALIZER; /* Defined in gateway.c */ extern time_t started_time; /* Defined in clientlist.c */ extern pthread_mutex_t client_list_mutex; extern pthread_mutex_t config_mutex; /* Defined in commandline.c */ extern pid_t restart_orig_pid; /* XXX Do these need to be locked ? */ static time_t last_online_time = 0; static time_t last_offline_time = 0; static time_t last_auth_online_time = 0; static time_t last_auth_offline_time = 0; long authenticated_this_session = 0; /** Fork a child and execute a shell command, the parent * process waits for the child to return and returns the child's exit() * value. * @return Return code of the command */ int execute(char *cmd_line, int quiet) { int pid, status, rc; const char *new_argv[4]; new_argv[0] = "/bin/sh"; new_argv[1] = "-c"; new_argv[2] = cmd_line; new_argv[3] = NULL; pid = safe_fork(); if (pid == 0) { /* for the child process: */ /* We don't want to see any errors if quiet flag is on */ if (quiet) close(2); if (execvp("/bin/sh", (char *const *)new_argv) < 0) { /* execute the command */ debug(LOG_ERR, "execvp(): %s", strerror(errno)); exit(1); } } else { /* for the parent: */ debug(LOG_DEBUG, "Waiting for PID %d to exit", pid); rc = waitpid(pid, &status, 0); debug(LOG_DEBUG, "Process PID %d exited", rc); } return (WEXITSTATUS(status)); } struct in_addr * wd_gethostbyname(const char *name) { struct hostent *he; struct in_addr *h_addr, *in_addr_temp; /* XXX Calling function is reponsible for free() */ h_addr = safe_malloc(sizeof(struct in_addr)); LOCK_GHBN(); he = gethostbyname(name); if (he == NULL) { free(h_addr); UNLOCK_GHBN(); return NULL; } in_addr_temp = (struct in_addr *)he->h_addr_list[0]; h_addr->s_addr = in_addr_temp->s_addr; UNLOCK_GHBN(); return h_addr; } char *get_iface_ip(char *ifname) { #ifdef __linux__ struct ifreq if_data; #endif struct in_addr in; char *ip_str; int sockd; u_int32_t ip; #ifdef __linux__ /* Create a socket. SOCK_PACKET is obsolete */ if ((sockd = socket (PF_INET, SOCK_RAW, htons(0x8086))) < 0) { debug(LOG_ERR, "socket(): %s", strerror(errno)); return NULL; } /* Get IP of internal interface */ strcpy (if_data.ifr_name, ifname); /* Get the IP address */ if (ioctl (sockd, SIOCGIFADDR, &if_data) < 0) { debug(LOG_ERR, "Finding IP for %s: ioctl(): SIOCGIFADDR %s", ifname,strerror(errno)); return NULL; } memcpy ((void *) &ip, (void *) &if_data.ifr_addr.sa_data + 2, 4); in.s_addr = ip; ip_str = (char *)inet_ntoa(in); return safe_strdup(ip_str); #else return safe_strdup("0.0.0.0"); #endif } char *get_iface_mac (char *ifname) { #ifdef __linux__ int r, s; struct ifreq ifr; char *hwaddr, mac[13]; strcpy(ifr.ifr_name, ifname); s = socket(PF_INET, SOCK_RAW, htons(0x8086)); if (-1 == s) { debug(LOG_ERR, "get_iface_mac socket: %s", strerror(errno)); return NULL; } r = ioctl(s, SIOCGIFHWADDR, &ifr); if (r == -1) { debug(LOG_ERR, "get_iface_mac ioctl(SIOCGIFHWADDR): %s", strerror(errno)); close(s); return NULL; } hwaddr = ifr.ifr_hwaddr.sa_data; snprintf(mac, 13, "%02X%02X%02X%02X%02X%02X", hwaddr[0] & 0xFF, hwaddr[1] & 0xFF, hwaddr[2] & 0xFF, hwaddr[3] & 0xFF, hwaddr[4] & 0xFF, hwaddr[5] & 0xFF ); close(s); return safe_strdup(mac); #else return NULL; #endif } /** Get name of external interface (the one with default route to the net). * Caller must free. */ char *get_ext_iface (void) { #ifdef __linux__ FILE *input; char *device, *gw; int i; pthread_cond_t cond = PTHREAD_COND_INITIALIZER; pthread_mutex_t cond_mutex = PTHREAD_MUTEX_INITIALIZER; struct timespec timeout; device = (char *)malloc(16); gw = (char *)malloc(16); debug(LOG_DEBUG, "get_ext_iface(): Autodectecting the external interface from routing table"); for (i=1; i<=NUM_EXT_INTERFACE_DETECT_RETRY; i++) { input = fopen("/proc/net/route", "r"); while (!feof(input)) { fscanf(input, "%s %s %*s %*s %*s %*s %*s %*s %*s %*s %*s\n", device, gw); if (strcmp(gw, "00000000") == 0) { free(gw); debug(LOG_INFO, "get_ext_iface(): Detected %s as the default interface after try %d", device, i); return device; } } fclose(input); debug(LOG_ERR, "get_ext_iface(): Failed to detect the external interface after try %d of %d (maybe the interface is not up yet?)", i, NUM_EXT_INTERFACE_DETECT_RETRY); /* Sleep for EXT_INTERFACE_DETECT_RETRY_INTERVAL seconds */ timeout.tv_sec = time(NULL) + EXT_INTERFACE_DETECT_RETRY_INTERVAL; timeout.tv_nsec = 0; /* Mutex must be locked for pthread_cond_timedwait... */ pthread_mutex_lock(&cond_mutex); /* Thread safe "sleep" */ pthread_cond_timedwait(&cond, &cond_mutex, &timeout); /* No longer needs to be locked */ pthread_mutex_unlock(&cond_mutex); } debug(LOG_ERR, "get_ext_iface(): Failed to detect the external interface after %d tries, aborting", NUM_EXT_INTERFACE_DETECT_RETRY); exit(1); free(device); free(gw); #endif return NULL; } /* * @return A string containing human-readable status text. * MUST BE free()d by caller */ char * get_status_text() { char buffer[STATUS_BUF_SIZ]; char timebuf[32]; ssize_t len; s_config *config; t_client *first; int indx; unsigned long int uptime = 0, uptimesecs = 0; unsigned int days = 0, hours = 0, minutes = 0, seconds = 0; unsigned long long int download_bytes, upload_bytes; t_MAC *trust_mac; t_MAC *block_mac; config = config_get_config(); len = 0; snprintf(buffer, (sizeof(buffer) - len), "==================\nNoDogSplash Status\n====\n"); len = strlen(buffer); uptimesecs = uptime = time(NULL) - started_time; days = uptime / (24 * 60 * 60); uptime -= days * (24 * 60 * 60); hours = uptime / (60 * 60); uptime -= hours * (60 * 60); minutes = uptime / 60; uptime -= minutes * 60; seconds = uptime; snprintf((buffer + len), (sizeof(buffer) - len), "Version: " VERSION "\n"); len = strlen(buffer); snprintf((buffer + len), (sizeof(buffer) - len), "Uptime: %ud %uh %um %us\n", days, hours, minutes, seconds); len = strlen(buffer); snprintf((buffer + len), (sizeof(buffer) - len), "Gateway Name: %s\n", config->gw_name); len = strlen(buffer); snprintf((buffer + len), (sizeof(buffer) - len), "Managed interface: %s\n", config->gw_interface); len = strlen(buffer); snprintf((buffer + len), (sizeof(buffer) - len), "Server listening: %s:%d\n", config->gw_address, config->gw_port); len = strlen(buffer); if(config->authenticate_immediately) { snprintf((buffer + len), (sizeof(buffer) - len), "Authenticate immediately: yes\n"); len = strlen(buffer); } else { snprintf((buffer + len), (sizeof(buffer) - len), "Splashpage: %s/%s\n", config->webroot, config->splashpage); len = strlen(buffer); } if(config->redirectURL) { snprintf((buffer + len), (sizeof(buffer) - len), "Redirect URL: %s\n", config->redirectURL); len = strlen(buffer); } snprintf((buffer + len), (sizeof(buffer) - len), "Traffic control: %s\n", config->traffic_control ? "yes" : "no"); len = strlen(buffer); if(config->traffic_control) { if(config->download_limit > 0) { snprintf((buffer + len), (sizeof(buffer) - len), "Download rate limit: %d kbit/s\n", config->download_limit); len = strlen(buffer); } else { snprintf((buffer + len), (sizeof(buffer) - len), "Download rate limit: none\n"); len = strlen(buffer); } if(config->upload_limit > 0) { snprintf((buffer + len), (sizeof(buffer) - len), "Upload rate limit: %d kbit/s\n", config->upload_limit); len = strlen(buffer); } else { snprintf((buffer + len), (sizeof(buffer) - len), "Upload rate limit: none\n"); len = strlen(buffer); } } download_bytes = iptables_fw_total_download(); snprintf((buffer + len), (sizeof(buffer) - len), "Total download: %llu kbytes", download_bytes/1000); len = strlen(buffer); snprintf((buffer + len), (sizeof(buffer) - len), "; avg: %.6g kbit/s\n", ((double) download_bytes) / 125 / uptimesecs); len = strlen(buffer); upload_bytes = iptables_fw_total_upload(); snprintf((buffer + len), (sizeof(buffer) - len), "Total upload: %llu kbytes", upload_bytes/1000); len = strlen(buffer); snprintf((buffer + len), (sizeof(buffer) - len), "; avg: %.6g kbit/s\n", ((double) upload_bytes) / 125 / uptimesecs); len = strlen(buffer); /** not needed in nodogsplash, since we don't permit ndsctl restart snprintf((buffer + len), (sizeof(buffer) - len), "Has been restarted: "); len = strlen(buffer); if (restart_orig_pid) { snprintf((buffer + len), (sizeof(buffer) - len), "yes (from PID %d)\n", restart_orig_pid); len = strlen(buffer); } else { snprintf((buffer + len), (sizeof(buffer) - len), "no\n"); len = strlen(buffer); } */ snprintf((buffer + len), (sizeof(buffer) - len), "====\n"); len = strlen(buffer); snprintf((buffer + len), (sizeof(buffer) - len), "Clients authenticated this session: %lu\n", authenticated_this_session); len = strlen(buffer); /* Update the client's counters so info is current */ iptables_fw_counters_update(); LOCK_CLIENT_LIST(); snprintf((buffer + len), (sizeof(buffer) - len), "Current clients: %d\n", get_client_list_length()); len = strlen(buffer); first = client_get_first_client(); if(first) { snprintf((buffer + len), (sizeof(buffer) - len), "\n"); len = strlen(buffer); } indx = 0; while (first != NULL) { snprintf((buffer + len), (sizeof(buffer) - len), "Client %d\n", indx); len = strlen(buffer); snprintf((buffer + len), (sizeof(buffer) - len), " IP: %s MAC: %s\n", first->ip, first->mac); len = strlen(buffer); ctime_r(&(first->added_time),timebuf); snprintf((buffer + len), (sizeof(buffer) - len), " Added: %s", timebuf); len = strlen(buffer); ctime_r(&(first->counters.last_updated),timebuf); snprintf((buffer + len), (sizeof(buffer) - len), " Active: %s", timebuf); len = strlen(buffer); uptimesecs = uptime = first->counters.last_updated - first->added_time; days = uptime / (24 * 60 * 60); uptime -= days * (24 * 60 * 60); hours = uptime / (60 * 60); uptime -= hours * (60 * 60); minutes = uptime / 60; uptime -= minutes * 60; seconds = uptime; snprintf((buffer + len), (sizeof(buffer) - len), " Active time: %ud %uh %um %us\n", days, hours, minutes, seconds); len = strlen(buffer); snprintf((buffer + len), (sizeof(buffer) - len), " Token: %s\n", first->token ? first->token : "none"); len = strlen(buffer); snprintf((buffer + len), (sizeof(buffer) - len), " State: %s\n", fw_connection_state_as_string(first->fw_connection_state)); len = strlen(buffer); download_bytes = first->counters.incoming; upload_bytes = first->counters.outgoing; snprintf((buffer + len), (sizeof(buffer) - len), " Download: %llu kbytes; avg: %.6g kbit/s\n Upload: %llu kbytes; avg: %.6g kbit/s\n\n", download_bytes/1000, ((double)download_bytes)/125/uptimesecs, upload_bytes/1000, ((double)upload_bytes)/125/uptimesecs); len = strlen(buffer); indx++; first = first->next; } UNLOCK_CLIENT_LIST(); snprintf((buffer + len), (sizeof(buffer) - len), "====\n"); len = strlen(buffer); snprintf((buffer + len), (sizeof(buffer) - len), "Blocked MAC addresses:\n"); len = strlen(buffer); if (config->blockedmaclist != NULL) { for (block_mac = config->blockedmaclist; block_mac != NULL; block_mac = block_mac->next) { snprintf((buffer + len), (sizeof(buffer) - len), " %s\n", block_mac->mac); len = strlen(buffer); } } else { snprintf((buffer + len), (sizeof(buffer) - len), " none\n"); len = strlen(buffer); } snprintf((buffer + len), (sizeof(buffer) - len), "Trusted MAC addresses:\n"); len = strlen(buffer); if (config->trustedmaclist != NULL) { for (trust_mac = config->trustedmaclist; trust_mac != NULL; trust_mac = trust_mac->next) { snprintf((buffer + len), (sizeof(buffer) - len), " %s\n", trust_mac->mac); len = strlen(buffer); } } else { snprintf((buffer + len), (sizeof(buffer) - len), " none\n"); len = strlen(buffer); } snprintf((buffer + len), (sizeof(buffer) - len), "========\n"); len = strlen(buffer); return safe_strdup(buffer); }