ntopng/src/Utils.cpp

3320 lines
84 KiB
C++
Executable file

/*
*
* (C) 2013-18 - ntop.org
*
*
* 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 3 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, write to the Free Software Foundation,
* Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*
*/
#include "ntop_includes.h"
#if defined(__OpenBSD__) || defined(__APPLE__)
#include <net/if_dl.h>
#include <ifaddrs.h>
#endif
// A simple struct for strings.
typedef struct {
char *s;
size_t l;
} String;
typedef struct {
u_int8_t header_over;
char outbuf[2*65536];
u_int num_bytes;
lua_State* vm;
} DownloadState;
#ifdef HAVE_LIBCAP
/*
The include below can be found in libcap-dev
sudo apt-get install libcap-dev
*/
#include <sys/capability.h>
#include <sys/prctl.h>
static cap_value_t cap_values[] = {
CAP_DAC_OVERRIDE, /* Bypass file read, write, and execute permission checks */
CAP_NET_RAW, /* Use RAW and PACKET sockets */
CAP_NET_ADMIN /* Perform various network-related operations */
};
int num_cap = sizeof(cap_values)/sizeof(cap_value_t);
#endif
/* ****************************************************** */
char* Utils::jsonLabel(int label, const char *label_str,char *buf, u_int buf_len){
if(ntop->getPrefs()->json_labels_as_strings()) {
snprintf(buf, buf_len, "%s", label_str);
} else
snprintf(buf, buf_len, "%d", label);
return(buf);
}
/* ****************************************************** */
char* Utils::formatTraffic(float numBits, bool bits, char *buf, u_int buf_len) {
char unit;
if(bits)
unit = 'b';
else
unit = 'B';
if(numBits < 1024) {
snprintf(buf, buf_len, "%lu %c", (unsigned long)numBits, unit);
} else if(numBits < 1048576) {
snprintf(buf, buf_len, "%.2f K%c", (float)(numBits)/1024, unit);
} else {
float tmpMBits = ((float)numBits)/1048576;
if(tmpMBits < 1024) {
snprintf(buf, buf_len, "%.2f M%c", tmpMBits, unit);
} else {
tmpMBits /= 1024;
if(tmpMBits < 1024) {
snprintf(buf, buf_len, "%.2f G%c", tmpMBits, unit);
} else {
snprintf(buf, buf_len, "%.2f T%c", (float)(tmpMBits)/1024, unit);
}
}
}
return(buf);
}
/* ****************************************************** */
char* Utils::formatPackets(float numPkts, char *buf, u_int buf_len) {
if(numPkts < 1000) {
snprintf(buf, buf_len, "%.2f", numPkts);
} else if(numPkts < 1000000) {
snprintf(buf, buf_len, "%.2f K", numPkts/(float)1000);
} else {
numPkts /= 1000000;
snprintf(buf, buf_len, "%.2f M", numPkts);
}
return(buf);
}
/* ****************************************************** */
char* Utils::l4proto2name(u_int8_t proto) {
static char proto_string[8];
/* NOTE: keep in sync with /lua/pro/db_explorer_data.lua */
switch(proto) {
case 0: return((char*)"IP");
case 1: return((char*)"ICMP");
case 2: return((char*)"IGMP");
case 6: return((char*)"TCP");
case 17: return((char*)"UDP");
case 41: return((char*)"IPv6");
case 46: return((char*)"RSVP");
case 47: return((char*)"GRE");
case 50: return((char*)"ESP");
case 51: return((char*)"AH");
case 58: return((char*)"IPv6-ICMP");
case 89: return((char*)"OSPF");
case 103: return((char*)"PIM");
case 112: return((char*)"VRRP");
case 139: return((char*)"HIP");
default:
snprintf(proto_string, sizeof(proto_string), "%u", proto);
return(proto_string);
}
}
/* ****************************************************** */
#ifdef NOTUSED
bool Utils::isIPAddress(char *ip) {
struct in_addr addr4;
struct in6_addr addr6;
if((ip == NULL) || (ip[0] == '\0'))
return(false);
if(strchr(ip, ':') != NULL) { /* IPv6 */
if(inet_pton(AF_INET6, ip, &addr6) == 1)
return(true);
} else {
if(inet_pton(AF_INET, ip, &addr4) == 1)
return(true);
}
return(false);
}
#endif
/* ****************************************************** */
int Utils::setThreadAffinity(pthread_t thread, int core_id) {
if(core_id < 0)
return(0);
else {
int ret = -1;
#ifdef HAVE_LIBCAP
u_int num_cores = ntop->getNumCPUs();
u_long core = core_id % num_cores;
cpu_set_t cpu_set;
if(num_cores > 1) {
CPU_ZERO(&cpu_set);
CPU_SET(core, &cpu_set);
ret = pthread_setaffinity_np(thread, sizeof(cpu_set_t), &cpu_set);
}
#endif
return ret;
}
}
/* ****************************************************** */
char *Utils::trim(char *s) {
char *end;
while(isspace(s[0]) || (s[0] == '"') || (s[0] == '\'')) s++;
if(s[0] == 0) return s;
end = &s[strlen(s) - 1];
while(end > s
&& (isspace(end[0])|| (end[0] == '"') || (end[0] == '\'')))
end--;
end[1] = 0;
return s;
}
/* ****************************************************** */
u_int32_t Utils::hashString(char *key) {
u_int32_t hash = 0, len = (u_int32_t)strlen(key);
for(u_int32_t i=0; i<len; i++)
hash += ((u_int32_t)key[i])*i;
return(hash);
}
/* ****************************************************** */
float Utils::timeval2ms(struct timeval *tv) {
return((float)tv->tv_sec*1000+(float)tv->tv_usec/1000);
}
/* ****************************************************** */
u_int32_t Utils::timeval2usec(const struct timeval *tv) {
return(tv->tv_sec*1000000+tv->tv_usec);
}
/* ****************************************************** */
float Utils::msTimevalDiff(const struct timeval *end, const struct timeval *begin) {
if((end->tv_sec == 0) && (end->tv_usec == 0))
return(0);
else {
float f = (end->tv_sec-begin->tv_sec)*1000+((float)(end->tv_usec-begin->tv_usec))/(float)1000;
return((f < 0) ? 0 : f);
}
}
/* ****************************************************** */
bool Utils::file_exists(const char *path) {
std::ifstream infile(path);
/* ntop->getTrace()->traceEvent(TRACE_WARNING, "%s(): %s", __FUNCTION__, path); */
bool ret = infile.good();
infile.close();
return ret;
}
/* ****************************************************** */
bool Utils::dir_exists(const char * const path) {
struct stat buf;
return !((stat(path, &buf) != 0) || (!S_ISDIR(buf.st_mode)));
}
/* ****************************************************** */
size_t Utils::file_write(const char *path, const char *content, size_t content_len) {
size_t ret = 0;
FILE *fd = fopen(path, "wb");
if(fd == NULL) {
ntop->getTrace()->traceEvent(TRACE_WARNING, "Unable to write file %s", path);
} else {
chmod(path, CONST_DEFAULT_FILE_MODE);
ret = fwrite(content, content_len, 1, fd);
fclose(fd);
}
return ret;
}
/* ****************************************************** */
size_t Utils::file_read(const char *path, char **content) {
size_t ret = 0;
char *buffer = NULL;
u_int64_t length;
FILE *f = fopen(path, "rb");
if(f) {
fseek (f, 0, SEEK_END);
length = ftell(f);
fseek (f, 0, SEEK_SET);
buffer = (char*)malloc(length);
if(buffer)
ret = fread(buffer, 1, length, f);
fclose(f);
}
if(buffer) {
if(content && ret)
*content = buffer;
else
free(buffer);
}
return ret;
}
/* ****************************************************** */
int Utils::remove_recursively(const char * const path) {
DIR *d = opendir(path);
size_t path_len = strlen(path);
int r = -1;
size_t len;
char *buf;
if(d) {
struct dirent *p;
r = 0;
while ((r==0) && (p=readdir(d))) {
/* Skip the names "." and ".." as we don't want to recurse on them. */
if(!strcmp(p->d_name, ".") || !strcmp(p->d_name, ".."))
continue;
len = path_len + strlen(p->d_name) + 2;
buf = (char *) malloc(len);
if(buf) {
struct stat statbuf;
snprintf(buf, len, "%s/%s", path, p->d_name);
if(stat(buf, &statbuf) == 0) {
if(S_ISDIR(statbuf.st_mode))
r = remove_recursively(buf);
else
r = unlink(buf);
}
free(buf);
}
}
closedir(d);
}
if(r == 0)
r = rmdir(path);
return r;
}
/* ****************************************************** */
bool Utils::mkdir_tree(char *path) {
int rc;
struct stat s;
ntop->fixPath(path);
if(stat(path, &s) != 0) {
/* Start at 1 to skip the root */
for(int i=1; path[i] != '\0'; i++)
if(path[i] == CONST_PATH_SEP) {
#ifdef WIN32
/* Do not create devices directory */
if((i > 1) && (path[i-1] == ':')) continue;
#endif
/*
* If we are already handling the final portion
* of a path, e.g. because the path has a trailing
* CONST_PATH_SEP, do not create the final
* directory: it will be created later.
*/
if(path[i+1] == '\0')
break;
path[i] = '\0';
rc = Utils::mkdir(path, CONST_DEFAULT_DIR_MODE);
path[i] = CONST_PATH_SEP;
}
rc = Utils::mkdir(path, CONST_DEFAULT_DIR_MODE);
return(((rc == 0) || (errno == EEXIST/* Already existing */)) ? true : false);
} else
return(true); /* Already existing */
}
/* **************************************************** */
int Utils::mkdir(const char *path, mode_t mode) {
#ifdef WIN32
return(_mkdir(path));
#else
int rc = ::mkdir(path, mode);
if(rc == -1) {
if(errno != EEXIST)
ntop->getTrace()->traceEvent(TRACE_WARNING, "mkdir(%s) failed [%d/%s]",
path, errno, strerror(errno));
} else {
if(chmod(path, CONST_DEFAULT_DIR_MODE) == -1) /* Ubuntu 18 */
ntop->getTrace()->traceEvent(TRACE_WARNING, "chmod(%s) failed [%d/%s]",
path, errno, strerror(errno));
}
return(rc);
#endif
}
/* **************************************************** */
const char* Utils::flowStatus2str(FlowStatus s, AlertType *aType, AlertLevel *aLevel) {
*aType = alert_flow_misbehaviour; /* Default */
*aLevel = alert_level_warning;
switch(s) {
case status_normal:
*aType = alert_none;
*aLevel = alert_level_none;
return("Normal");
break;
case status_slow_tcp_connection:
return("Slow TCP Connection");
break;
case status_slow_application_header:
return("Slow Application Header");
break;
case status_slow_data_exchange:
return("Slow Data Exchange (Slowloris?)");
break;
case status_low_goodput:
return("Low Goodput");
break;
case status_suspicious_tcp_syn_probing:
*aType = alert_suspicious_activity;
return("Suspicious TCP SYN Probing (or server port down)");
break;
case status_tcp_connection_issues:
return("TCP Connection Issues");
break;
case status_suspicious_tcp_probing:
*aType = alert_suspicious_activity;
return("Suspicious TCP Probing");
case status_flow_when_interface_alerted:
*aType = alert_interface_alerted;
return("Flow emitted during alerted interface");
case status_tcp_connection_refused:
*aType = alert_suspicious_activity;
return("TCP connection refused");
case status_ssl_certificate_mismatch:
*aType = alert_suspicious_activity;
return("SSL certificate mismatch");
case status_dns_invalid_query:
*aType = alert_suspicious_activity;
return("Invalid DNS query");
case status_remote_to_remote:
*aType = alert_flow_remote_to_remote;
return("Remote client and remote server");
case status_web_mining_detected:
*aType = alert_flow_web_mining;
*aLevel = alert_level_warning;
return("Web miner detected");
case status_blacklisted:
*aType = alert_flow_blacklisted;
*aLevel = alert_level_error;
return("Client or server blacklisted (or both)");
case status_blocked:
*aLevel = alert_level_info;
*aType = alert_flow_blocked;
return("Flow blocked");
case status_device_protocol_not_allowed:
*aType = alert_device_protocol_not_allowed;
*aLevel = alert_level_warning;
return("Protocol not allowed for this device type");
default:
return("Unknown status");
break;
}
}
/* **************************************************** */
const char* Utils::trend2str(ValueTrend t) {
switch(t) {
case trend_up:
return("Up");
break;
case trend_down:
return("Down");
break;
case trend_stable:
return("Stable");
break;
default:
case trend_unknown:
return("Unknown");
break;
}
}
/* **************************************************** */
int Utils::dropPrivileges() {
#ifndef WIN32
struct passwd *pw = NULL;
const char *username;
int rv;
if(getgid() && getuid()) {
ntop->getTrace()->traceEvent(TRACE_NORMAL,
"Privileges are not dropped as we're not superuser");
return -1;
}
if(Utils::retainWriteCapabilities() != 0) {
#ifdef HAVE_LIBCAP
ntop->getTrace()->traceEvent(TRACE_WARNING,
"Unable to retain privileges for privileged file writing");
#endif
}
username = ntop->getPrefs()->get_user();
pw = getpwnam(username);
if(pw == NULL) {
/* if the user (e.g. 'ntopng') does not exists, falls back to 'nobody' */
username = CONST_OLD_DEFAULT_NTOP_USER;
pw = getpwnam(username);
}
if(pw != NULL) {
if(ntop->getPrefs()->get_pid_path() != NULL) {
/* Change PID file ownership to be able to delete it on shutdown */
rv = chown(ntop->getPrefs()->get_pid_path(), pw->pw_uid, pw->pw_gid);
if(rv != 0)
ntop->getTrace()->traceEvent(TRACE_ERROR, "Unable to change owner to PID in file %s", ntop->getPrefs()->get_pid_path());
}
/* Drop privileges */
if((setgid(pw->pw_gid) != 0) || (setuid(pw->pw_uid) != 0)) {
ntop->getTrace()->traceEvent(TRACE_WARNING, "Unable to drop privileges [%s]",
strerror(errno));
return -1;
}
ntop->getTrace()->traceEvent(TRACE_NORMAL, "User changed to %s", username);
#ifndef WIN32
ntop->getTrace()->traceEvent(TRACE_INFO, "Umask: %#o", umask(0077));
#endif
} else {
ntop->getTrace()->traceEvent(TRACE_WARNING, "Unable to locate user %s", username);
return -1;
}
// umask(0);
#endif
return 0;
}
/* **************************************************** */
/* http://www.adp-gmbh.ch/cpp/common/base64.html */
static const std::string base64_chars =
"ABCDEFGHIJKLMNOPQRSTUVWXYZ"
"abcdefghijklmnopqrstuvwxyz"
"0123456789+/";
static inline bool is_base64(unsigned char c) {
return (isalnum(c) || (c == '+') || (c == '/'));
}
/* **************************************************** */
#ifdef NOTUSED
std::string Utils::base64_encode(unsigned char const* bytes_to_encode, unsigned int in_len) {
std::string ret;
int i = 0;
unsigned char char_array_3[3];
unsigned char char_array_4[4];
while (in_len--) {
char_array_3[i++] = *(bytes_to_encode++);
if(i == 3) {
char_array_4[0] = (char_array_3[0] & 0xfc) >> 2;
char_array_4[1] = ((char_array_3[0] & 0x03) << 4) + ((char_array_3[1] & 0xf0) >> 4);
char_array_4[2] = ((char_array_3[1] & 0x0f) << 2) + ((char_array_3[2] & 0xc0) >> 6);
char_array_4[3] = char_array_3[2] & 0x3f;
for(i = 0; (i <4) ; i++)
ret += base64_chars[char_array_4[i]];
i = 0;
}
}
if(i) {
for(int j = i; j < 3; j++)
char_array_3[j] = '\0';
char_array_4[0] = (char_array_3[0] & 0xfc) >> 2;
char_array_4[1] = ((char_array_3[0] & 0x03) << 4) + ((char_array_3[1] & 0xf0) >> 4);
char_array_4[2] = ((char_array_3[1] & 0x0f) << 2) + ((char_array_3[2] & 0xc0) >> 6);
char_array_4[3] = char_array_3[2] & 0x3f;
for(int j = 0; (j < i + 1); j++)
ret += base64_chars[char_array_4[j]];
while((i++ < 3))
ret += '=';
}
return ret;
}
#endif
/* **************************************************** */
std::string Utils::base64_decode(std::string const& encoded_string) {
int in_len = encoded_string.size();
int i = 0, in_ = 0;
unsigned char char_array_4[4], char_array_3[3];
std::string ret;
while (in_len-- && ( encoded_string[in_] != '=') && is_base64(encoded_string[in_])) {
char_array_4[i++] = encoded_string[in_]; in_++;
if(i == 4) {
for(i = 0; i <4; i++)
char_array_4[i] = base64_chars.find(char_array_4[i]);
char_array_3[0] = (char_array_4[0] << 2) + ((char_array_4[1] & 0x30) >> 4);
char_array_3[1] = ((char_array_4[1] & 0xf) << 4) + ((char_array_4[2] & 0x3c) >> 2);
char_array_3[2] = ((char_array_4[2] & 0x3) << 6) + char_array_4[3];
for(i = 0; (i < 3); i++)
ret += char_array_3[i];
i = 0;
}
}
if(i) {
int j;
for(j = i; j <4; j++)
char_array_4[j] = 0;
for(j = 0; j <4; j++)
char_array_4[j] = base64_chars.find(char_array_4[j]);
char_array_3[0] = (char_array_4[0] << 2) + ((char_array_4[1] & 0x30) >> 4);
char_array_3[1] = ((char_array_4[1] & 0xf) << 4) + ((char_array_4[2] & 0x3c) >> 2);
char_array_3[2] = ((char_array_4[2] & 0x3) << 6) + char_array_4[3];
for(j = 0; (j < i - 1); j++) ret += char_array_3[j];
}
return ret;
}
/* **************************************************** */
bool Utils::dumpHostToDB(IpAddress *host, LocationPolicy policy) {
bool do_dump = false;
int16_t network_id;
switch(policy) {
case location_local_only:
if(host->isLocalHost(&network_id)) do_dump = true;
break;
case location_remote_only:
if(!host->isLocalHost(&network_id)) do_dump = true;
break;
case location_all:
do_dump = true;
break;
case location_none:
do_dump = false;
break;
}
return(do_dump);
}
/* *************************************** */
double Utils::pearsonValueCorrelation(activity_bitmap *x, activity_bitmap *y) {
double ex = 0, ey = 0, sxx = 0, syy = 0, sxy = 0, tiny_value = 1e-2;
for(size_t i = 0; i < NUM_MINUTES_PER_DAY; i++) {
/* Find the means */
ex += x->counter[i], ey += y->counter[i];
}
ex /= NUM_MINUTES_PER_DAY, ey /= NUM_MINUTES_PER_DAY;
for(size_t i = 0; i < NUM_MINUTES_PER_DAY; i++) {
/* Compute the correlation coefficient */
double xt = x->counter[i] - ex, yt = y->counter[i] - ey;
sxx += xt * xt, syy += yt * yt, sxy += xt * yt;
}
return (sxy/(sqrt(sxx*syy)+tiny_value));
}
/* *************************************** */
/* XXX: it assumes that the vectors are bitmaps */
double Utils::JaccardSimilarity(activity_bitmap *x, activity_bitmap *y) {
size_t inter_card = 0, union_card = 0;
for(size_t i = 0; i < NUM_MINUTES_PER_DAY; i++) {
union_card += x->counter[i] | y->counter[i];
inter_card += x->counter[i] & y->counter[i];
}
if(union_card == 0)
return(1e-2);
return ((double)inter_card/union_card);
}
/* *************************************** */
#ifdef WIN32
extern "C" {
const char *strcasestr(const char *haystack, const char *needle) {
int i = -1;
while (haystack[++i] != '\0') {
if(tolower(haystack[i]) == tolower(needle[0])) {
int j = i, k = 0, match = 0;
while (tolower(haystack[++j]) == tolower(needle[++k])) {
match = 1;
// Catch case when they match at the end
//printf("j:%d, k:%d\n",j,k);
if(haystack[j] == '\0' && needle[k] == '\0') {
//printf("Mj:%d, k:%d\n",j,k);
return &haystack[i];
}
}
// Catch normal case
if(match && needle[k] == '\0'){
// printf("Norm j:%d, k:%d\n",j,k);
return &haystack[i];
}
}
}
return NULL;
}
};
#endif
/* **************************************************** */
int Utils::ifname2id(const char *name) {
char rsp[MAX_INTERFACE_NAME_LEN], ifidx[8];
if(name == NULL) return(-1);
else if(!strncmp(name, "-", 1)) name = (char*) "stdin";
if(ntop->getRedis()->hashGet((char*)CONST_IFACE_ID_PREFS, (char*)name, rsp, sizeof(rsp)) == 0) {
/* Found */
return(atoi(rsp));
} else {
for(int i = 0; i < MAX_NUM_INTERFACE_IDS; i++) {
snprintf(ifidx, sizeof(ifidx), "%d", i);
if(ntop->getRedis()->hashGet((char*)CONST_IFACE_ID_PREFS, ifidx, rsp, sizeof(rsp)) < 0) {
snprintf(rsp, sizeof(rsp), "%s", name);
ntop->getRedis()->hashSet((char*)CONST_IFACE_ID_PREFS, rsp, ifidx);
ntop->getRedis()->hashSet((char*)CONST_IFACE_ID_PREFS, ifidx, rsp);
return(i);
}
}
ntop->getTrace()->traceEvent(TRACE_ERROR, "Interface ids exhausted. Flush redis to create new interfaces.");
}
return(-1); /* This can't happen, hopefully */
}
/* **************************************************** */
/* http://en.wikipedia.org/wiki/Hostname */
char* Utils::sanitizeHostName(char *str) {
int i;
for(i=0; str[i] != '\0'; i++) {
if(((str[i] >= 'a') && (str[i] <= 'z'))
|| ((str[i] >= 'A') && (str[i] <= 'Z'))
|| ((str[i] >= '0') && (str[i] <= '9'))
|| (str[i] == '-')
|| (str[i] == '_')
|| (str[i] == '.')
|| (str[i] == ':') /* Used in HTTP host:port */
|| (str[i] == '@') /* Used by DNS but not a valid char */)
;
else if(str[i] == '_') {
str[i] = '\0';
break;
} else
str[i] = '_';
}
return(str);
}
/* **************************************************** */
char* Utils::stripHTML(const char * const str) {
if(!str) return NULL;
int len = strlen(str), j = 0;
char *stripped_str = NULL;
try {
stripped_str = new char[len + 1];
} catch(std::bad_alloc& ba) {
static bool oom_warning_sent = false;
if(!oom_warning_sent) {
ntop->getTrace()->traceEvent(TRACE_WARNING, "Not enough memory");
oom_warning_sent = true;
}
return NULL;
}
// scan string
for (int i = 0; i < len; i++) {
// found an open '<', scan for its close
if(str[i] == '<') {
// charge ahead in the string until it runs out or we find what we're looking for
for (; i < len && str[i] != '>'; i++);
} else {
stripped_str[j] = str[i];
j++;
}
}
stripped_str[j] = 0;
return stripped_str;
}
/* **************************************************** */
char* Utils::urlDecode(const char *src, char *dst, u_int dst_len) {
char *ret = dst;
u_int i = 0;
dst_len--; /* Leave room for \0 */
dst[dst_len] = 0;
while((*src) && (i < dst_len)) {
char a, b;
if((*src == '%') &&
((a = src[1]) && (b = src[2]))
&& (isxdigit(a) && isxdigit(b))) {
char h[3] = { a, b, 0 };
char hexval = (char)strtol(h, (char **)NULL, 16);
// if(iswprint(hexval))
*dst++ = hexval;
src += 3;
} else if(*src == '+') {
*dst++ = ' '; src++;
} else
*dst++ = *src++;
i++;
}
*dst++ = '\0';
return(ret);
}
/* **************************************************** */
/**
* @brief Check if the current user is an administrator
*
* @param vm The lua state.
* @return true if the current user is an administrator, false otherwise.
*/
bool Utils::isUserAdministrator(lua_State* vm) {
struct mg_connection *conn;
char *username;
char key[64], val[64];
#ifdef DONT_USE_LUAJIT
lua_getglobal(vm, "userdata");
#endif
if(!ntop->getPrefs()->is_users_login_enabled())
return(true); /* login disabled for all users, everyone's an admin */
if((conn = getLuaVMUservalue(vm,conn)) == NULL) {
/* this is an internal script (e.g. periodic script), admin check should pass */
return(true);
} else if(HTTPserver::authorized_localhost_user_login(conn))
return(true); /* login disabled from localhost, everyone's connecting from localhost is an admin */
if((username = getLuaVMUserdata(vm,user)) == NULL) {
// ntop->getTrace()->traceEvent(TRACE_WARNING, "%s(%s): NO", __FUNCTION__, "???");
return(false); /* Unknown */
}
if(!strncmp(username, NTOP_NOLOGIN_USER, strlen(username)))
return(true);
snprintf(key, sizeof(key), CONST_STR_USER_GROUP, username);
if(ntop->getRedis()->get(key, val, sizeof(val)) >= 0) {
return(!strcmp(val, NTOP_NOLOGIN_USER) ||
!strcmp(val, CONST_ADMINISTRATOR_USER));
} else {
// ntop->getTrace()->traceEvent(TRACE_WARNING, "%s(%s): NO", __FUNCTION__, username);
return(false); /* Unknown */
}
}
/* **************************************************** */
/**
* @brief Purify the HTTP parameter
*
* @param param The parameter to purify (remove unliked chars with _)
*/
static const char* xssAttempts[] = {
"<?import",
"<applet",
"<base",
"<embed",
"<frame",
"<iframe",
"<implementation",
"<import",
"<link",
"<meta",
"<object",
"<script",
"<style",
"charset",
"classid",
"code",
"codetype",
/* "data", */
"href",
"http-equiv",
"javascript:",
"vbscript:",
"vmlframe",
"xlink:href",
"=",
NULL
};
/* ************************************************************ */
/* http://www.ascii-code.com */
bool Utils::isPrintableChar(u_char c) {
if(isprint(c)) return(true);
if((c >= 192) && (c <= 255))
return(true);
return(false);
}
/* ************************************************************ */
bool Utils::purifyHTTPparam(char * const param, bool strict, bool allowURL, bool allowDots) {
if(strict) {
for(int i=0; xssAttempts[i] != NULL; i++) {
if(strstr(param, xssAttempts[i])) {
ntop->getTrace()->traceEvent(TRACE_WARNING, "Found possible XSS attempt: %s [%s]", param, xssAttempts[i]);
param[0] = '\0';
return(true);
}
}
}
for(int i=0; param[i] != '\0'; i++) {
bool is_good;
if(strict) {
is_good =
((param[i] >= 'a') && (param[i] <= 'z'))
|| ((param[i] >= 'A') && (param[i] <= 'Z'))
|| ((param[i] >= '0') && (param[i] <= '9'))
// || (param[i] == ':')
// || (param[i] == '-')
|| (param[i] == '_')
// || (param[i] == '/')
|| (param[i] == '@')
// || (param[i] == ',')
// || (param[i] == '.')
;
} else {
char c;
int new_i;
if((u_char)param[i] == 0xC3) {
/* Latin-1 within UTF-8 - Align to ASCII encoding */
c = param[i+1] | 0x40;
new_i = i+1; /* We are actually validating two bytes */
} else {
c = param[i];
new_i = i;
}
is_good = Utils::isPrintableChar(c)
&& (c != '<')
&& (c != '>')
&& (c != '"'); /* Prevents injections - single quotes are allowed and will be validated in http_lint.lua */
if(is_good)
i = new_i;
}
if(is_good)
; /* Good: we're on the whitelist */
else
param[i] = '_'; /* Invalid char: we discard it */
if((i > 0)
&& (((!allowDots) && (param[i] == '.') && (param[i-1] == '.'))
|| ((!allowURL) && ((param[i] == '/') && (param[i-1] == '/')))
|| ((param[i] == '\\') && (param[i-1] == '\\'))
)) {
/* Make sure we do not have .. in the variable that can be used for future hacking */
param[i-1] = '_', param[i] = '_'; /* Invalidate the path */
}
}
return(false);
}
/* **************************************************** */
/* holder for curl fetch */
struct curl_fetcher_t {
char * const payload;
size_t cur_size;
const size_t max_size;
};
static size_t curl_get_writefunc(void *contents, size_t size, size_t nmemb, void *userp) {
size_t realsize = size * nmemb;
struct curl_fetcher_t *p = (struct curl_fetcher_t*)userp;
if(!p->max_size)
return realsize;
/* Leave the last position for a '\0' */
if(p->cur_size + realsize > p->max_size - 1)
realsize = p->max_size - p->cur_size - 1;
if(realsize) {
memcpy(&(p->payload[p->cur_size]), contents, realsize);
p->cur_size += realsize;
p->payload[p->cur_size] = 0;
}
return realsize;
}
/* **************************************************** */
/**
* @brief Implement HTTP POST of JSON data
*
* @param username Username to be used on post or NULL if missing
* @param password Password to be used on post or NULL if missing
* @param url URL where to post data to
* @param json The content of the POST
* @return true if post was successful, false otherwise.
*/
static int curl_post_writefunc(void *ptr, size_t size, size_t nmemb, void *stream) {
char *str = (char*)ptr;
ntop->getTrace()->traceEvent(TRACE_INFO, "[JSON] %s", str);
return(size*nmemb);
}
/* **************************************** */
#ifdef HAVE_CURL_SMTP
struct snmp_upload_status {
char *lines;
char msg_log[1024];
};
static int curl_debugfunc(CURL *handle, curl_infotype type, char *data,
size_t size, void *userptr) {
char dir = '\0';
switch(type) {
case CURLINFO_HEADER_IN:
case CURLINFO_DATA_IN:
dir = '<';
break;
case CURLINFO_DATA_OUT:
case CURLINFO_HEADER_OUT:
dir = '>';
break;
default:
break;
}
if(dir) {
char *msg = data;
while(*msg) {
char *end = strchr(msg, '\n');
if(!end) break;
*end = '\0';
ntop->getTrace()->traceEvent(TRACE_DEBUG, "[CURL] %c %s", dir, msg);
*end = '\n';
msg = end+1;
}
}
return(size);
}
/* **************************************** */
static size_t curl_smtp_payload_source(void *ptr, size_t size, size_t nmemb, void *userp) {
struct snmp_upload_status *upload_ctx = (struct snmp_upload_status *)userp;
if((size == 0) || (nmemb == 0) || ((size*nmemb) < 1)) {
return 0;
}
char *eol = strstr(upload_ctx->lines, "\r\n");
if(eol) {
size_t len = min(size, (size_t)(eol - upload_ctx->lines + 2));
memcpy(ptr, upload_ctx->lines, len);
upload_ctx->lines += len;
return len;
}
return 0;
}
#endif
/* **************************************** */
static void readCurlStats(CURL *curl, HTTPTranferStats *stats, lua_State* vm) {
curl_easy_getinfo(curl, CURLINFO_NAMELOOKUP_TIME, &stats->namelookup);
curl_easy_getinfo(curl, CURLINFO_CONNECT_TIME, &stats->connect);
curl_easy_getinfo(curl, CURLINFO_APPCONNECT_TIME, &stats->appconnect);
curl_easy_getinfo(curl, CURLINFO_PRETRANSFER_TIME, &stats->pretransfer);
curl_easy_getinfo(curl, CURLINFO_REDIRECT_TIME, &stats->redirect);
curl_easy_getinfo(curl, CURLINFO_STARTTRANSFER_TIME, &stats->start);
curl_easy_getinfo(curl, CURLINFO_TOTAL_TIME, &stats->total);
if(vm) {
lua_newtable(vm);
lua_push_float_table_entry(vm, "NAMELOOKUP_TIME", stats->namelookup);
lua_push_float_table_entry(vm, "CONNECT_TIME", stats->connect);
lua_push_float_table_entry(vm, "APPCONNECT_TIME", stats->appconnect);
lua_push_float_table_entry(vm, "PRETRANSFER_TIME", stats->pretransfer);
lua_push_float_table_entry(vm, "REDIRECT_TIME", stats->redirect);
lua_push_float_table_entry(vm, "STARTTRANSFER_TIME", stats->start);
lua_push_float_table_entry(vm, "TOTAL_TIME", stats->total);
lua_pushstring(vm, "HTTP_STATS");
lua_insert(vm, -2);
lua_settable(vm, -3);
}
ntop->getTrace()->traceEvent(TRACE_INFO,
"[NAMELOOKUP_TIME %.02f][CONNECT_TIME %.02f][APPCONNECT_TIME %.02f][PRETRANSFER_TIME %.02f]"
"[REDIRECT_TIME %.02f][STARTTRANSFER_TIME %.02f][TOTAL_TIME %.02f]",
stats->namelookup, stats->connect, stats->appconnect,
stats->pretransfer,stats->redirect, stats->start,
stats->total);
}
/* **************************************** */
bool Utils::postHTTPJsonData(char *username, char *password, char *url,
char *json, HTTPTranferStats *stats) {
CURL *curl;
bool ret = true;
curl = curl_easy_init();
if(curl) {
CURLcode res;
struct curl_slist* headers = NULL;
memset(stats, 0, sizeof(HTTPTranferStats));
curl_easy_setopt(curl, CURLOPT_URL, url);
if((username && (username[0] != '\0'))
|| (password && (password[0] != '\0'))) {
char auth[64];
snprintf(auth, sizeof(auth), "%s:%s",
username ? username : "",
password ? password : "");
curl_easy_setopt(curl, CURLOPT_USERPWD, auth);
curl_easy_setopt(curl, CURLOPT_HTTPAUTH, (long)CURLAUTH_BASIC);
}
if(!strncmp(url, "https", 5)) {
curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0L);
curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 0L);
}
curl_easy_setopt(curl, CURLOPT_POST, 1L);
headers = curl_slist_append(headers, "Content-Type: application/json");
curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers);
curl_easy_setopt(curl, CURLOPT_POSTFIELDS, json);
curl_easy_setopt(curl, CURLOPT_POSTFIELDSIZE, (long)strlen(json));
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, curl_post_writefunc);
res = curl_easy_perform(curl);
if(res != CURLE_OK) {
ntop->getTrace()->traceEvent(TRACE_WARNING,
"Unable to post data to (%s): %s",
url, curl_easy_strerror(res));
ret = false;
} else {
ntop->getTrace()->traceEvent(TRACE_INFO, "Posted JSON to %s", url);
readCurlStats(curl, stats, NULL);
}
/* always cleanup */
curl_slist_free_all(headers);
curl_easy_cleanup(curl);
}
return(ret);
}
/* **************************************** */
static size_t read_callback(void *ptr, size_t size, size_t nmemb, void *stream) {
return(fread(ptr, size, nmemb, (FILE*)stream));
}
bool Utils::postHTTPTextFile(char *username, char *password, char *url,
char *path, int timeout, HTTPTranferStats *stats) {
CURL *curl;
bool ret = true;
struct stat file_info;
size_t file_len;
FILE *fd = fopen(path, "r");
if((fd == NULL) || (stat(path, &file_info) != 0))
return(false);
else
file_len = (size_t)file_info.st_size;
curl = curl_easy_init();
if(curl) {
CURLcode res;
struct curl_slist* headers = NULL;
memset(stats, 0, sizeof(HTTPTranferStats));
curl_easy_setopt(curl, CURLOPT_URL, url);
if((username && (username[0] != '\0'))
|| (password && (password[0] != '\0'))) {
char auth[64];
snprintf(auth, sizeof(auth), "%s:%s",
username ? username : "",
password ? password : "");
curl_easy_setopt(curl, CURLOPT_USERPWD, auth);
curl_easy_setopt(curl, CURLOPT_HTTPAUTH, (long)CURLAUTH_BASIC);
}
if(!strncmp(url, "https", 5)) {
curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0L);
curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 0L);
}
curl_easy_setopt(curl, CURLOPT_POST, 1L);
headers = curl_slist_append(headers, "Content-Type: text/plain; charset=utf-8");
curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers);
curl_easy_setopt(curl, CURLOPT_READDATA, fd);
curl_easy_setopt(curl, CURLOPT_READFUNCTION, read_callback);
curl_easy_setopt(curl, CURLOPT_POSTFIELDSIZE, (curl_off_t)file_len);
curl_easy_setopt(curl, CURLOPT_TIMEOUT, timeout);
curl_easy_setopt(curl, CURLOPT_CONNECTTIMEOUT, timeout);
#ifdef CURLOPT_CONNECTTIMEOUT_MS
curl_easy_setopt(curl, CURLOPT_CONNECTTIMEOUT_MS, timeout*1000);
#endif
res = curl_easy_perform(curl);
if(res != CURLE_OK) {
ntop->getTrace()->traceEvent(TRACE_WARNING,
"Unable to post data to (%s): %s",
url, curl_easy_strerror(res));
ret = false;
} else {
ntop->getTrace()->traceEvent(TRACE_INFO, "Posted JSON to %s", url);
readCurlStats(curl, stats, NULL);
}
fclose(fd);
/* always cleanup */
curl_slist_free_all(headers);
curl_easy_cleanup(curl);
}
return(ret);
}
/* **************************************** */
bool Utils::sendMail(char *from, char *to, char *message, char *smtp_server) {
#ifdef HAVE_CURL_SMTP
CURL *curl;
CURLcode res;
bool ret = true;
struct curl_slist *recipients = NULL;
struct snmp_upload_status *upload_ctx = (struct snmp_upload_status*) calloc(1, sizeof(struct snmp_upload_status));
if(!upload_ctx) return false;
upload_ctx->lines = message;
curl = curl_easy_init();
if(curl) {
recipients = curl_slist_append(recipients, to);
curl_easy_setopt(curl, CURLOPT_URL, smtp_server);
curl_easy_setopt(curl, CURLOPT_MAIL_FROM, from);
curl_easy_setopt(curl, CURLOPT_MAIL_RCPT, recipients);
/* Try using SSL */
curl_easy_setopt(curl, CURLOPT_USE_SSL, CURLUSESSL_TRY);
if(ntop->getTrace()->get_trace_level() >= TRACE_LEVEL_DEBUG) {
/* Show verbose message trace */
curl_easy_setopt(curl, CURLOPT_VERBOSE, 1L);
curl_easy_setopt(curl, CURLOPT_DEBUGFUNCTION, curl_debugfunc);
curl_easy_setopt(curl, CURLOPT_DEBUGDATA, upload_ctx);
}
curl_easy_setopt(curl, CURLOPT_READFUNCTION, curl_smtp_payload_source);
curl_easy_setopt(curl, CURLOPT_READDATA, upload_ctx);
curl_easy_setopt(curl, CURLOPT_UPLOAD, 1L);
res = curl_easy_perform(curl);
if(res != CURLE_OK) {
ntop->getTrace()->traceEvent(TRACE_WARNING,
"Unable to send email to (%s): %s. Run ntopng with -v6 for more details.",
smtp_server, curl_easy_strerror(res));
ret = false;
}
curl_slist_free_all(recipients);
/* NOTE: connection could be reused */
curl_easy_cleanup(curl);
}
free(upload_ctx);
return ret;
#else
ntop->getTrace()->traceEvent(TRACE_ERROR, "SMTP support is not available");
return(false);
#endif
}
/* **************************************** */
/* curl calls this routine to get more data */
static size_t curl_writefunc_to_lua(char *buffer, size_t size,
size_t nitems, void *userp) {
DownloadState *state = (DownloadState*)userp;
int len = size*nitems, diff;
if(state->header_over == 0) {
/* We need to parse the header as this is the first call for the body */
char *tmp, *element;
state->outbuf[state->num_bytes] = 0;
element = strtok_r(state->outbuf, "\r\n", &tmp);
if(element) element = strtok_r(NULL, "\r\n", &tmp);
lua_newtable(state->vm);
while(element) {
char *column = strchr(element, ':');
if(!column) break;
column[0] = '\0';
/* Put everything in lowercase */
for(int i=0; element[i] != '\0'; i++) element[i] = tolower(element[i]);
lua_push_str_table_entry(state->vm, element, &column[1]);
element = strtok_r(NULL, "\r\n", &tmp);
}
lua_pushstring(state->vm, "HTTP_HEADER");
lua_insert(state->vm, -2);
lua_settable(state->vm, -3);
state->num_bytes = 0, state->header_over = 1;
}
diff = sizeof(state->outbuf) - state->num_bytes - 1;
if(diff > 0) {
int buff_diff = min(diff, len);
if(buff_diff > 0) {
strncpy(&state->outbuf[state->num_bytes], buffer, buff_diff);
state->num_bytes += buff_diff;
state->outbuf[state->num_bytes] = '\0';
}
}
return(len);
}
/* **************************************** */
/* Same as the above function but only for header */
static size_t curl_hdf(char *buffer, size_t size, size_t nitems, void *userp) {
DownloadState *state = (DownloadState*)userp;
int len = size*nitems;
int diff = sizeof(state->outbuf) - state->num_bytes - 1;
if(diff > 0) {
int buff_diff = min(diff, len);
if(buff_diff > 0) {
strncpy(&state->outbuf[state->num_bytes], buffer, buff_diff);
state->num_bytes += buff_diff;
state->outbuf[state->num_bytes] = '\0';
}
}
return(len);
}
/* **************************************** */
/* form_data is in format param=value&param1=&value1... */
bool Utils::httpGetPost(lua_State* vm, char *url, char *username,
char *password, int timeout,
bool return_content,
bool use_cookie_authentication,
HTTPTranferStats *stats, const char *form_data) {
CURL *curl;
bool ret = true;
curl = curl_easy_init();
if(curl) {
DownloadState *state = NULL;
long response_code;
char *content_type, *redirection;
char ua[64];
memset(stats, 0, sizeof(HTTPTranferStats));
curl_easy_setopt(curl, CURLOPT_URL, url);
if(username || password) {
char auth[64];
if(use_cookie_authentication) {
snprintf(auth, sizeof(auth),
"user=%s; password=%s",
username ? username : "",
password ? password : "");
curl_easy_setopt(curl, CURLOPT_COOKIE, auth);
} else {
snprintf(auth, sizeof(auth), "%s:%s",
username ? username : "",
password ? password : "");
curl_easy_setopt(curl, CURLOPT_USERPWD, auth);
curl_easy_setopt(curl, CURLOPT_HTTPAUTH, (long)CURLAUTH_BASIC);
}
}
if(!strncmp(url, "https", 5)) {
curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0L);
curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 0L);
}
if(form_data) {
/* This is a POST request */
curl_easy_setopt(curl, CURLOPT_POST, 1L);
curl_easy_setopt(curl, CURLOPT_POSTFIELDS, form_data);
curl_easy_setopt(curl, CURLOPT_POSTFIELDSIZE, (long)strlen(form_data));
}
if(return_content) {
state = (DownloadState*)malloc(sizeof(DownloadState));
if(state != NULL) {
memset(state, 0, sizeof(DownloadState));
curl_easy_setopt(curl, CURLOPT_WRITEDATA, state);
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, curl_writefunc_to_lua);
curl_easy_setopt(curl, CURLOPT_HEADERDATA, state);
curl_easy_setopt(curl, CURLOPT_HEADERFUNCTION, curl_hdf);
state->vm = vm, state->header_over = 0;
} else {
ntop->getTrace()->traceEvent(TRACE_WARNING, "Out of memory");
curl_easy_cleanup(curl);
if(vm) lua_pushnil(vm);
return(false);
}
}
curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1);
curl_easy_setopt(curl, CURLOPT_MAXREDIRS, 5);
curl_easy_setopt(curl, CURLOPT_NOSIGNAL, 1);
curl_easy_setopt(curl, CURLOPT_TIMEOUT, timeout);
curl_easy_setopt(curl, CURLOPT_CONNECTTIMEOUT, timeout);
#ifdef CURLOPT_CONNECTTIMEOUT_MS
curl_easy_setopt(curl, CURLOPT_CONNECTTIMEOUT_MS, timeout*1000);
#endif
snprintf(ua, sizeof(ua), "%s [%s][%s]",
PACKAGE_STRING, PACKAGE_MACHINE, PACKAGE_OS);
curl_easy_setopt(curl, CURLOPT_USERAGENT, ua);
if(vm) lua_newtable(vm);
if(curl_easy_perform(curl) == CURLE_OK) {
readCurlStats(curl, stats, vm);
if(return_content && vm) {
lua_push_str_table_entry(vm, "CONTENT", state->outbuf);
lua_push_int_table_entry(vm, "CONTENT_LEN", state->num_bytes);
}
ret = true;
} else
ret = false;
if(vm) {
if(curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &response_code) == CURLE_OK)
lua_push_int_table_entry(vm, "RESPONSE_CODE", response_code);
if((curl_easy_getinfo(curl, CURLINFO_CONTENT_TYPE, &content_type) == CURLE_OK) && content_type)
lua_push_str_table_entry(vm, "CONTENT_TYPE", content_type);
if(curl_easy_getinfo(curl, CURLINFO_EFFECTIVE_URL, &redirection) == CURLE_OK)
lua_push_str_table_entry(vm, "EFFECTIVE_URL", redirection);
}
if(return_content && state)
free(state);
/* always cleanup */
curl_easy_cleanup(curl);
}
return(ret);
}
/* **************************************** */
long Utils::httpGet(const char * const url,
const char * const username, const char * const password,
int timeout,
char * const resp, const u_int resp_len) {
CURL *curl;
long response_code = 0;
curl = curl_easy_init();
if(curl) {
char *content_type;
char ua[64];
curl_fetcher_t fetcher = {
/* .payload = */ resp,
/* .cur_size = */ 0,
/* .max_size = */ resp_len};
curl_easy_setopt(curl, CURLOPT_URL, url);
if(username || password) {
char auth[64];
snprintf(auth, sizeof(auth), "%s:%s",
username ? username : "",
password ? password : "");
curl_easy_setopt(curl, CURLOPT_USERPWD, auth);
curl_easy_setopt(curl, CURLOPT_HTTPAUTH, (long)CURLAUTH_BASIC);
}
if(!strncmp(url, "https", 5)) {
curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0L);
curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 0L);
}
if(resp && resp_len) {
curl_easy_setopt(curl, CURLOPT_WRITEDATA, &fetcher);
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, curl_get_writefunc);
}
curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1);
curl_easy_setopt(curl, CURLOPT_MAXREDIRS, 5);
curl_easy_setopt(curl, CURLOPT_NOSIGNAL, 1);
curl_easy_setopt(curl, CURLOPT_TIMEOUT, timeout);
curl_easy_setopt(curl, CURLOPT_CONNECTTIMEOUT, timeout);
#ifdef CURLOPT_CONNECTTIMEOUT_MS
curl_easy_setopt(curl, CURLOPT_CONNECTTIMEOUT_MS, timeout*1000);
#endif
snprintf(ua, sizeof(ua), "%s [%s][%s]",
PACKAGE_STRING, PACKAGE_MACHINE, PACKAGE_OS);
curl_easy_setopt(curl, CURLOPT_USERAGENT, ua);
if(curl_easy_perform(curl) == CURLE_OK) {
if((curl_easy_getinfo(curl, CURLINFO_CONTENT_TYPE, &content_type) != CURLE_OK)
|| (curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &response_code) != CURLE_OK))
response_code = 0;
}
/* always cleanup */
curl_easy_cleanup(curl);
}
return response_code;
}
/* **************************************** */
char* Utils::getURL(char *url, char *buf, u_int buf_len) {
struct stat s;
if(!ntop->getPrefs()->is_pro_edition())
return(url);
snprintf(buf, buf_len, "%s/lua/pro%s",
ntop->get_HTTPserver()->get_scripts_dir(),
&url[4]);
ntop->fixPath(buf);
if((stat(buf, &s) == 0) && (S_ISREG(s.st_mode))) {
u_int l = strlen(ntop->get_HTTPserver()->get_scripts_dir());
char *new_url = &buf[l];
// ntop->getTrace()->traceEvent(TRACE_NORMAL, "===>>> %s", new_url);
return(new_url);
} else
return(url);
}
/* **************************************** */
// Support functions for 'urlEncode'.
#ifdef NOTUSED
static char to_hex(char code) {
static char hex[] = "0123456789ABCDEF";
return hex[code & 15];
}
#endif
/* **************************************************** */
#ifdef NOTUSED
static int alphanum(char code) {
int i;
static char alnum[] = "0123456789abcdefghijklmnopqrstuvwxyz";
for (i = 0; i < 36; i++) {
if(code == alnum[i]) return 1;
}
return 0;
}
#endif
/* **************************************************** */
// Encodes a URL to hexadecimal format.
#ifdef NOTUSED
char* Utils::urlEncode(char *url) {
char *pstr = url;
char *buf = (char *) malloc(strlen(url) * 3 + 1);
char *pbuf = buf;
while (*pstr) {
if(alphanum(*pstr) || *pstr == '-' || *pstr == '_' || *pstr == '.' || *pstr == '~') {
*pbuf++ = *pstr;
}
else {
if(*pstr == ' ') *pbuf++ = '+';
else {
*pbuf++ = '%';
*pbuf++ = to_hex(*pstr >> 4);
*pbuf++ = to_hex(*pstr & 15);
}
}
pstr++;
}
*pbuf = '\0';
return buf;
}
#endif
/* **************************************** */
// The following one initializes a new string.
#ifdef NOTUSED
static void newString(String *str) {
str->l = 0;
str->s = (char *) malloc((str->l) + 1);
if(str->s == NULL) {
fprintf(stderr, "ERROR: malloc() failed!\n");
exit(EXIT_FAILURE);
}
else {
str->s[0] = '\0';
}
return;
}
#endif
/* **************************************** */
#ifdef WIN32
ticks Utils::getticks() {
struct timeval tv;
gettimeofday (&tv, 0);
return (((ticks)tv.tv_usec) + (((ticks)tv.tv_sec) * 1000000LL));
}
#else
ticks Utils::getticks() {
#if defined(__i386__)
ticks x;
__asm__ volatile (".byte 0x0f, 0x31" : "=A" (x));
return x;
#elif defined(__x86_64__)
u_int32_t a, d;
asm volatile("rdtsc" : "=a" (a), "=d" (d));
return (((ticks)a) | (((ticks)d) << 32));
/*
__asm __volatile("rdtsc" : "=A" (x));
return (x);
*/
#else /* ARM, MIPS.... (not very fast) */
struct timeval tv;
gettimeofday (&tv, 0);
return (((ticks)tv.tv_usec) + (((ticks)tv.tv_sec) * 1000000LL));
#endif
}
#endif
/* **************************************** */
static bool scan_dir(const char * dir_name,
list<pair<struct dirent *, char * > > *dirlist,
unsigned long *total) {
int path_length;
char path[MAX_PATH+2];
DIR *d;
struct stat file_stats;
d = opendir(dir_name);
if(!d) return false;
while (1) {
struct dirent *entry;
const char *d_name;
entry = readdir(d);
if(!entry) break;
d_name = entry->d_name;
if(entry->d_type & DT_REG) {
snprintf(path, sizeof(path), "%s/%s", dir_name, entry->d_name);
if(!stat(path, &file_stats)) {
struct dirent *temp = (struct dirent *)malloc(sizeof(struct dirent));
memcpy(temp, entry, sizeof(struct dirent));
dirlist->push_back(make_pair(temp, strndup(path, MAX_PATH)));
if(total)
*total += file_stats.st_size;
}
} else if(entry->d_type & DT_DIR) {
if(strncmp (d_name, "..", 2) != 0 &&
strncmp (d_name, ".", 1) != 0) {
path_length = snprintf (path, MAX_PATH,
"%s/%s", dir_name, d_name);
if(path_length >= MAX_PATH)
return false;
scan_dir(path, dirlist, total);
}
}
}
if(closedir(d)) return false;
return true;
}
/* **************************************** */
bool file_mtime_compare(const pair<struct dirent *, char * > &d1, const pair<struct dirent *, char * > &d2) {
struct stat sa, sb;
if(!d1.second || !d2.second)
return false;
if(stat(d1.second, &sa) || stat(d2.second, &sb))
return false;
return difftime(sa.st_mtime, sb.st_mtime) <= 0;
}
/* **************************************** */
bool Utils::discardOldFilesExceeding(const char *path, const unsigned long max_size) {
unsigned long total = 0;
list<pair<struct dirent *, char * > > fileslist;
list<pair<struct dirent *, char * > >::iterator it;
struct stat st;
if(path == NULL || !strncmp(path, "", MAX_PATH))
return false;
/* First, get a list of all non-dir dirents and compute total size */
if(!scan_dir(path, &fileslist, &total)) return false;
//printf("path: %s, total: %u, max_size: %u\n", path, total, max_size);
if(total < max_size) return true;
fileslist.sort(file_mtime_compare);
/* Third, traverse list and delete until we go below quota */
for (it = fileslist.begin(); it != fileslist.end(); ++it) {
//printf("[file: %s][path: %s]\n", it->first->d_name, it->second);
if(!it->second) continue;
stat(it->second, &st);
unlink(it->second);
total -= st.st_size;
if(total < max_size)
break;
}
for (it = fileslist.begin(); it != fileslist.end(); ++it) {
if(it->first)
free(it->first);
if(it->second)
free(it->second);
}
return true;
}
/* **************************************** */
bool ntop_delete_old_files(const char *dir_name, time_t now, int older_than_seconds) {
struct dirent *result;
int path_length;
char path[MAX_PATH];
DIR *d;
struct stat file_stats;
if(!dir_name || strlen(dir_name) > MAX_PATH)
return false;
d = opendir(dir_name);
if(!d) return false;
while((result = readdir(d)) != NULL) {
if(result->d_type & DT_REG) {
if((path_length = snprintf(path, MAX_PATH, "%s/%s", dir_name, result->d_name)) <= MAX_PATH) {
ntop->fixPath(path);
if(!stat(path, &file_stats)) {
if(file_stats.st_mtime <= now - older_than_seconds)
unlink(path);
}
}
} else if(result->d_type & DT_DIR) {
if(strncmp(result->d_name, "..", 2) && strncmp(result->d_name, ".", 1)) {
if((path_length = snprintf(path, MAX_PATH, "%s/%s", dir_name, result->d_name)) <= MAX_PATH) {
ntop->fixPath(path);
ntop_delete_old_files(path, now, older_than_seconds);
}
}
}
}
rmdir(dir_name); /* Remove the directory, if empty */
closedir(d);
return true;
}
/* **************************************** */
bool Utils::discardOldFiles(char *path, int older_than_seconds) {
time_t now = time(NULL);
if(!path || strlen(path) > MAX_PATH)
return false;
ntop->fixPath(path);
return ntop_delete_old_files(path, now, older_than_seconds);
}
/* **************************************** */
char* Utils::formatMac(u_int8_t *mac, char *buf, u_int buf_len) {
if((mac == NULL) || (ntop->getPrefs()->getHostMask() != no_host_mask))
snprintf(buf, buf_len, "00:00:00:00:00:00");
else
snprintf(buf, buf_len, "%02X:%02X:%02X:%02X:%02X:%02X",
mac[0] & 0xFF, mac[1] & 0xFF,
mac[2] & 0xFF, mac[3] & 0xFF,
mac[4] & 0xFF, mac[5] & 0xFF);
return(buf);
}
/* **************************************** */
u_int64_t Utils::macaddr_int(const u_int8_t *mac) {
if(mac == NULL)
return(0);
else {
u_int64_t mac_int = 0;
for(u_int8_t i=0; i<6; i++){
mac_int |= (mac[i] & 0xFF) << (5-i)*8;
}
return mac_int;
}
}
/* **************************************** */
#if defined(linux) || defined(__FreeBSD__) || defined(__APPLE__)
void Utils::readMac(char *_ifname, dump_mac_t mac_addr) {
char ifname[32];
char *colon, *at;
macstr_t mac_addr_buf;
int res;
/* Handle PF_RING interfaces zc:ens2f1@3 */
colon = strchr(_ifname, ':');
if(colon != NULL) /* removing pf_ring module prefix (e.g. zc:ethX) */
_ifname = colon+1;
snprintf(ifname, sizeof(ifname), "%s", _ifname);
at = strchr(ifname, '@');
if(at != NULL)
at[0] = '\0';
#if defined(__FreeBSD__) || defined(__APPLE__)
struct ifaddrs *ifap, *ifaptr;
unsigned char *ptr;
if((res = getifaddrs(&ifap)) == 0) {
for(ifaptr = ifap; ifaptr != NULL; ifaptr = ifaptr->ifa_next) {
if(!strcmp(ifaptr->ifa_name, ifname) && (ifaptr->ifa_addr->sa_family == AF_LINK)) {
ptr = (unsigned char *)LLADDR((struct sockaddr_dl *)ifaptr->ifa_addr);
memcpy(mac_addr, ptr, 6);
break;
}
}
freeifaddrs(ifap);
}
#else
int _sock;
struct ifreq ifr;
memset (&ifr, 0, sizeof(struct ifreq));
/* Dummy socket, just to make ioctls with */
_sock = socket(PF_INET, SOCK_DGRAM, 0);
strncpy(ifr.ifr_name, ifname, IFNAMSIZ-1);
if((res = ioctl(_sock, SIOCGIFHWADDR, &ifr)) >= 0)
memcpy(mac_addr, ifr.ifr_ifru.ifru_hwaddr.sa_data, 6);
close(_sock);
#endif
if(res < 0)
ntop->getTrace()->traceEvent(TRACE_ERROR, "Cannot get hw addr for %s", ifname);
else
ntop->getTrace()->traceEvent(TRACE_INFO, "Interface %s has MAC %s",
ifname,
formatMac((u_int8_t *)mac_addr, mac_addr_buf, sizeof(mac_addr_buf)));
}
#else
void Utils::readMac(char *ifname, dump_mac_t mac_addr) {
memset(mac_addr, 0, 6);
}
#endif
/* **************************************** */
u_int32_t Utils::readIPv4(char *ifname) {
u_int32_t ret_ip = 0;
#ifndef WIN32
struct ifreq ifr;
int fd;
memset(&ifr, 0, sizeof(ifr));
strncpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name));
ifr.ifr_addr.sa_family = AF_INET;
if((fd = socket(AF_INET, SOCK_DGRAM, IPPROTO_IP)) < 0) {
ntop->getTrace()->traceEvent(TRACE_WARNING, "Unable to create socket");
} else {
if(ioctl(fd, SIOCGIFADDR, &ifr) == -1)
ntop->getTrace()->traceEvent(TRACE_INFO, "Unable to read IPv4 for device %s", ifname);
else
ret_ip = (((struct sockaddr_in*)&ifr.ifr_addr)->sin_addr).s_addr;
closesocket(fd);
}
#endif
return(ret_ip);
}
/* **************************************** */
u_int16_t Utils::getIfMTU(const char *ifname) {
#ifdef WIN32
return(CONST_DEFAULT_MAX_PACKET_SIZE);
#else
struct ifreq ifr;
u_int32_t max_packet_size = CONST_DEFAULT_MAX_PACKET_SIZE; /* default */
int fd;
memset(&ifr, 0, sizeof(ifr));
strncpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name));
ifr.ifr_addr.sa_family = AF_INET;
if((fd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
ntop->getTrace()->traceEvent(TRACE_WARNING, "Unable to create socket");
} else {
if(ioctl(fd, SIOCGIFMTU, &ifr) == -1)
ntop->getTrace()->traceEvent(TRACE_INFO, "Unable to read MTU for device %s", ifname);
else {
max_packet_size = ifr.ifr_mtu + sizeof(struct ndpi_ethhdr) + sizeof(Ether80211q);
if(max_packet_size > ((u_int16_t)-1))
max_packet_size = ((u_int16_t)-1);
}
closesocket(fd);
}
return((u_int16_t) max_packet_size);
#endif
}
/* **************************************** */
u_int32_t Utils::getMaxIfSpeed(const char *ifname) {
#if defined(linux) && (!defined(__GNUC_RH_RELEASE__) || (__GNUC_RH_RELEASE__ != 44))
int sock, rc;
struct ifreq ifr;
struct ethtool_cmd edata;
u_int32_t ifSpeed = 1000;
if(strncmp(ifname, "zc:", 3) == 0) ifname = &ifname[3];
if(strchr(ifname, ',')) {
/* These are interfaces with , (e.g. eth0,eth1) */
char ifaces[128], *iface, *tmp;
u_int32_t speed = 0;
snprintf(ifaces, sizeof(ifaces), "%s", ifname);
iface = strtok_r(ifaces, ",", &tmp);
while(iface) {
u_int32_t thisSpeed = getMaxIfSpeed(iface);
if(thisSpeed > speed) speed = thisSpeed;
iface = strtok_r(NULL, ",", &tmp);
}
return(speed);
}
memset(&ifr, 0, sizeof(struct ifreq));
sock = socket(PF_INET, SOCK_DGRAM, 0);
if(sock < 0) {
// ntop->getTrace()->traceEvent(TRACE_ERROR, "Socket error [%s]", ifname);
return(ifSpeed);
}
strncpy(ifr.ifr_name, ifname, IFNAMSIZ-1);
ifr.ifr_data = (char *) &edata;
// Do the work
edata.cmd = ETHTOOL_GSET;
rc = ioctl(sock, SIOCETHTOOL, &ifr);
if(rc < 0) {
// ntop->getTrace()->traceEvent(TRACE_ERROR, "I/O Control error [%s]", ifname);
return(ifSpeed);
}
if((int32_t)ethtool_cmd_speed(&edata) != SPEED_UNKNOWN)
ifSpeed = ethtool_cmd_speed(&edata);
ntop->getTrace()->traceEvent(TRACE_INFO, "Interface %s has MAC Speed = %u",
ifname, edata.speed);
closesocket(sock);
return(ifSpeed);
#else
return(1000);
#endif
}
/* **************************************** */
bool Utils::isGoodNameToCategorize(char *name) {
if((name[0] == '\0')
|| (strchr(name, '.') == NULL) /* Missing domain */
|| (!strcmp(name, "Broadcast"))
|| (!strcmp(name, "localhost"))
|| strchr((const char*)name, ':') /* IPv6 */
|| (strstr(name, "in-addr.arpa"))
|| (strstr(name, "ip6.arpa"))
|| (strstr(name, "_dns-sd._udp"))
)
return(false);
else
return(true);
}
/* **************************************** */
char* Utils::get2ndLevelDomain(char *_domainname) {
int i, found = 0;
for(i=(int)strlen(_domainname)-1, found = 0; (found != 2) && (i > 0); i--) {
if(_domainname[i] == '.') {
found++;
if(found == 2) {
return(&_domainname[i+1]);
}
}
}
return(_domainname);
}
/* ****************************************************** */
char* Utils::tokenizer(char *arg, int c, char **data) {
char *p = NULL;
if((p = strchr(arg, c)) != NULL) {
*p = '\0';
if(data) {
if(strlen(arg))
*data = strdup(arg);
else
*data = strdup("");
}
arg = &(p[1]);
} else if(data) {
if(arg)
*data = strdup(arg);
else
*data = NULL;
}
return (arg);
}
/* ****************************************************** */
in_addr_t Utils::inet_addr(const char *cp) {
if((cp == NULL) || (cp[0] == '\0'))
return(0);
else
return(::inet_addr(cp));
}
/* ****************************************************** */
char* Utils::intoaV4(unsigned int addr, char* buf, u_short bufLen) {
char *cp, *retStr;
int n;
cp = &buf[bufLen];
*--cp = '\0';
n = 4;
do {
u_int byte = addr & 0xff;
*--cp = byte % 10 + '0';
byte /= 10;
if(byte > 0) {
*--cp = byte % 10 + '0';
byte /= 10;
if(byte > 0)
*--cp = byte + '0';
}
*--cp = '.';
addr >>= 8;
} while (--n > 0);
/* Convert the string to lowercase */
retStr = (char*)(cp+1);
return(retStr);
}
/* ****************************************************** */
char* Utils::intoaV6(struct ndpi_in6_addr ipv6, u_int8_t bitmask, char* buf, u_short bufLen) {
char *ret;
for(int32_t i = bitmask, j = 0; i > 0; i -= 8, ++j)
ipv6.u6_addr.u6_addr8[j] &= i >= 8 ? 0xff : (u_int32_t)(( 0xffU << ( 8 - i ) ) & 0xffU );
ret = (char*)inet_ntop(AF_INET6, &ipv6, buf, bufLen);
if(ret == NULL) {
/* Internal error (buffer too short) */
buf[0] = '\0';
return(buf);
} else
return(ret);
}
/* ****************************************************** */
void Utils::xor_encdec(u_char *data, int data_len, u_char *key) {
int i, y;
for(i = 0, y = 0; i < data_len; i++) {
data[i] ^= key[y++];
if(key[y] == 0) y = 0;
}
}
/* ****************************************************** */
u_int32_t Utils::macHash(u_int8_t *mac) {
if(mac == NULL)
return(0);
else {
u_int32_t hash = 0;
for(int i=0; i<6; i++)
hash += mac[i] << (i+1);
return(hash);
}
}
/* ****************************************************** */
/* https://en.wikipedia.org/wiki/Multicast_address */
/* https://hwaddress.com/company/private */
bool Utils::isSpecialMac(u_int8_t *mac) {
u_int8_t zero[6] = { 0, 0, 0, 0, 0, 0 };
if(memcmp(mac, zero, 6) == 0)
return(true);
else {
u_int16_t v2 = (mac[0] << 8) + mac[1];
u_int32_t v3 = (mac[0] << 16) + (mac[1] << 8) + mac[2];
switch(v3) {
case 0x01000C:
case 0x0180C2:
case 0x01005E:
case 0x010CCD:
case 0x011B19:
case 0x00006C:
case 0x000101:
case 0x000578:
case 0x000B18:
case 0x000BF4:
case 0x000C53:
case 0x000D58:
case 0x000DA7:
case 0x000DC2:
case 0x000DF2:
case 0x000E17:
case 0x000E22:
case 0x000E2A:
case 0x000EEF:
case 0x000F09:
case 0x0016B4:
case 0x001761:
case 0x001825:
case 0x002067:
case 0x00221C:
case 0x0022F1:
case 0x00234A:
case 0x00238C:
case 0x0023F7:
case 0x002419:
case 0x0024FB:
case 0x00259D:
case 0x0025DF:
case 0x00269F:
case 0x005047:
case 0x005079:
case 0x0050C2:
case 0x0050C7:
case 0x0084ED:
case 0x0086A0:
case 0x00A054:
case 0x00A085:
case 0x00CB00:
case 0x0418B6:
case 0x0C8112:
case 0x100000:
case 0x10AE60:
case 0x10B713:
case 0x1100AA:
case 0x111111:
case 0x140708:
case 0x146E0A:
case 0x18421D:
case 0x1CF4CA:
case 0x205B2A:
case 0x20D160:
case 0x24336C:
case 0x24BF74:
case 0x28EF01:
case 0x3CB87A:
case 0x40A93F:
case 0x40D855:
case 0x487604:
case 0x48D35D:
case 0x48F317:
case 0x50E14A:
case 0x544E45:
case 0x580943:
case 0x586ED6:
case 0x604BAA:
case 0x609620:
case 0x68E166:
case 0x706F81:
case 0x78F944:
case 0x7CE4AA:
case 0x8C8401:
case 0x8CE748:
case 0x906F18:
case 0x980EE4:
case 0x9C93E4:
case 0xA0D86F:
case 0xA468BC:
case 0xA4A6A9:
case 0xACDE48:
case 0xACF85C:
case 0xB025AA:
case 0xB0ECE1:
case 0xB0FEBD:
case 0xB4E1EB:
case 0xC02250:
case 0xC8AACC:
case 0xCC3ADF:
case 0xD85DFB:
case 0xDC7014:
case 0xE0CB1D:
case 0xE4F14C:
case 0xE80410:
case 0xE89E0C:
case 0xF04F7C:
case 0xF0A225:
case 0xFCC233:
return(true);
}
switch(v2) {
case 0xFFFF:
case 0x3333:
return(true);
break;
}
return(false);
}
}
/* ****************************************************** */
void Utils::parseMac(u_int8_t *mac, const char *symMac) {
int _mac[6] = { 0 };
sscanf(symMac, "%x:%x:%x:%x:%x:%x",
&_mac[0], &_mac[1], &_mac[2],
&_mac[3], &_mac[4], &_mac[5]);
for(int i = 0; i < 6; i++) mac[i] = (u_int8_t)_mac[i];
}
/* *********************************************** */
static int fill_prefix_v4(prefix_t *p, struct in_addr *a, int b, int mb) {
if(b < 0 || b > mb)
return(-1);
memcpy(&p->add.sin, a, (mb+7)/8);
p->family = AF_INET, p->bitlen = b, p->ref_count = 0;
return(0);
}
/* ******************************************* */
static int fill_prefix_v6(prefix_t *prefix, struct in6_addr *addr, int bits, int maxbits) {
if(bits < 0 || bits > maxbits)
return -1;
memcpy(&prefix->add.sin6, addr, (maxbits + 7) / 8);
prefix->family = AF_INET6, prefix->bitlen = bits, prefix->ref_count = 0;
return 0;
}
/* ******************************************* */
static int fill_prefix_mac(prefix_t *prefix, u_int8_t *mac, int bits, int maxbits) {
if(bits < 0 || bits > maxbits)
return -1;
memcpy(prefix->add.mac, mac, 6);
prefix->family = AF_MAC, prefix->bitlen = bits, prefix->ref_count = 0;
return 0;
}
/* ******************************************* */
static patricia_node_t* add_to_ptree(patricia_tree_t *tree, int family, void *addr, int bits) {
prefix_t prefix;
patricia_node_t *node;
if(family == AF_INET)
fill_prefix_v4(&prefix, (struct in_addr*)addr, bits, tree->maxbits);
else if(family == AF_INET6)
fill_prefix_v6(&prefix, (struct in6_addr*)addr, bits, tree->maxbits);
else
fill_prefix_mac(&prefix, (u_int8_t*)addr, bits, tree->maxbits);
node = patricia_lookup(tree, &prefix);
return(node);
}
/* ******************************************* */
static int remove_from_ptree(patricia_tree_t *tree, int family, void *addr, int bits) {
prefix_t prefix;
patricia_node_t *node;
int rc;
if(family == AF_INET)
fill_prefix_v4(&prefix, (struct in_addr*)addr, bits, tree->maxbits);
else
fill_prefix_v6(&prefix, (struct in6_addr*)addr, bits, tree->maxbits);
node = patricia_lookup(tree, &prefix);
if((patricia_node_t *)0 != node)
rc = 0, free(node);
else
rc = -1;
return(rc);
}
/* ******************************************* */
patricia_node_t* Utils::ptree_match(patricia_tree_t *tree, int family, void *addr, int bits) {
prefix_t prefix;
if(family == AF_INET)
fill_prefix_v4(&prefix, (struct in_addr*)addr, bits, tree->maxbits);
else if(family == AF_INET6)
fill_prefix_v6(&prefix, (struct in6_addr*)addr, bits, tree->maxbits);
else
fill_prefix_mac(&prefix, (u_int8_t*)addr, bits, tree->maxbits);
return(patricia_search_best(tree, &prefix));
}
/* ******************************************* */
patricia_node_t* Utils::ptree_add_rule(patricia_tree_t *ptree, char *line) {
char *ip, *bits, *slash = NULL;
struct in_addr addr4;
struct in6_addr addr6;
u_int8_t mac[6];
u_int32_t _mac[6];
patricia_node_t *node = NULL;
ip = line;
bits = strchr(line, '/');
if(bits == NULL)
bits = (char*)"/32";
else {
slash = bits;
slash[0] = '\0';
}
bits++;
ntop->getTrace()->traceEvent(TRACE_DEBUG, "Rule %s/%s", ip, bits);
if(sscanf(ip, "%02X:%02X:%02X:%02X:%02X:%02X",
&_mac[0], &_mac[1], &_mac[2], &_mac[3], &_mac[4], &_mac[5]) == 6) {
for(int i=0; i<6; i++) mac[i] = _mac[i];
node = add_to_ptree(ptree, AF_MAC, mac, 48);
} else if(strchr(ip, ':') != NULL) { /* IPv6 */
if(inet_pton(AF_INET6, ip, &addr6) == 1)
node = add_to_ptree(ptree, AF_INET6, &addr6, atoi(bits));
else
ntop->getTrace()->traceEvent(TRACE_ERROR, "Error parsing IPv6 %s\n", ip);
} else { /* IPv4 */
/* inet_aton(ip, &addr4) fails parsing subnets */
int num_octets;
u_int ip4_0 = 0, ip4_1 = 0, ip4_2 = 0, ip4_3 = 0;
u_char *ip4 = (u_char *) &addr4;
if((num_octets = sscanf(ip, "%u.%u.%u.%u",
&ip4_0, &ip4_1, &ip4_2, &ip4_3)) >= 1) {
int num_bits = atoi(bits);
ip4[0] = ip4_0, ip4[1] = ip4_1, ip4[2] = ip4_2, ip4[3] = ip4_3;
if(num_bits > 32) num_bits = 32;
if(num_octets * 8 < num_bits)
ntop->getTrace()->traceEvent(TRACE_INFO,
"Found IP smaller than netmask [%s]", line);
//addr4.s_addr = ntohl(addr4.s_addr);
node = add_to_ptree(ptree, AF_INET, &addr4, num_bits);
} else {
ntop->getTrace()->traceEvent(TRACE_ERROR, "Error parsing IPv4 %s\n", ip);
}
}
if(slash) slash[0] = '/';
// ntop->getTrace()->traceEvent(TRACE_NORMAL, "Added IPv%d rule %s/%s [%p]", isV4 ? 4 : 6, ip, bits, node);
return(node);
}
/* ******************************************* */
int Utils::ptree_remove_rule(patricia_tree_t *ptree, char *line) {
char *ip, *bits, *slash = NULL;
struct in_addr addr4;
struct in6_addr addr6;
u_int32_t _mac[6];
u_int8_t mac[6];
int rc = -1;
ip = line;
bits = strchr(line, '/');
if(bits == NULL)
bits = (char*)"/32";
else {
slash = bits;
slash[0] = '\0';
}
bits++;
ntop->getTrace()->traceEvent(TRACE_DEBUG, "Rule %s/%s", ip, bits);
if(sscanf(ip, "%02X:%02X:%02X:%02X:%02X:%02X",
&_mac[0], &_mac[1], &_mac[2], &_mac[3], &_mac[4], &_mac[5]) == 6) {
for(int i=0; i<6; i++) mac[i] = _mac[i];
rc = remove_from_ptree(ptree, AF_MAC, mac, 48);
} else if(strchr(ip, ':') != NULL) { /* IPv6 */
if(inet_pton(AF_INET6, ip, &addr6) == 1)
rc = remove_from_ptree(ptree, AF_INET6, &addr6, atoi(bits));
else
ntop->getTrace()->traceEvent(TRACE_ERROR, "Error parsing IPv6 %s\n", ip);
} else { /* IPv4 */
/* inet_aton(ip, &addr4) fails parsing subnets */
int num_octets;
u_int ip4_0 = 0, ip4_1 = 0, ip4_2 = 0, ip4_3 = 0;
u_char *ip4 = (u_char *) &addr4;
if((num_octets = sscanf(ip, "%u.%u.%u.%u", &ip4_0, &ip4_1, &ip4_2, &ip4_3)) >= 1) {
int num_bits = atoi(bits);
ip4[0] = ip4_0, ip4[1] = ip4_1, ip4[2] = ip4_2, ip4[3] = ip4_3;
if(num_bits > 32) num_bits = 32;
if(num_octets * 8 < num_bits)
ntop->getTrace()->traceEvent(TRACE_INFO, "Found IP smaller than netmask [%s]", line);
//addr4.s_addr = ntohl(addr4.s_addr);
rc = remove_from_ptree(ptree, AF_INET, &addr4, num_bits);
} else {
ntop->getTrace()->traceEvent(TRACE_ERROR, "Error parsing IPv4 %s\n", ip);
}
}
if(slash) slash[0] = '/';
return(rc);
}
/* ******************************************* */
int Utils::numberOfSetBits(u_int32_t i) {
// Java: use >>> instead of >>
// C or C++: use uint32_t
i = i - ((i >> 1) & 0x55555555);
i = (i & 0x33333333) + ((i >> 2) & 0x33333333);
return (((i + (i >> 4)) & 0x0F0F0F0F) * 0x01010101) >> 24;
}
/* ******************************************* */
void Utils::initRedis(Redis **r, const char *redis_host, const char *redis_password, u_int16_t redis_port, u_int8_t _redis_db_id) {
if(r) {
if(*r) delete(*r);
(*r) = new Redis(redis_host, redis_password, redis_port, _redis_db_id);
}
}
/* ******************************************* */
/*
IMPORTANT: line buffer is large enough to contain the replaced string
*/
void Utils::replacestr(char *line, const char *search, const char *replace) {
char *sp;
int search_len, replace_len, tail_len;
if((sp = strstr(line, search)) == NULL) {
return;
}
search_len = strlen(search), replace_len = strlen(replace);
tail_len = strlen(sp+search_len);
memmove(sp+replace_len,sp+search_len,tail_len+1);
memcpy(sp, replace, replace_len);
}
/* ****************************************************** */
u_int32_t Utils::stringHash(const char *s) {
u_int32_t hash = 0;
const char *p = s;
int pos = 0;
while(*p) {
hash += (*p) << pos;
p++;
pos += 8;
if(pos == 32) pos = 0;
}
return hash;
}
/* ****************************************************** */
/* Note: the returned IP address is in network byte order */
u_int32_t Utils::getHostManagementIPv4Address() {
int sock = socket(AF_INET, SOCK_DGRAM, 0);
const char* kGoogleDnsIp = "8.8.8.8";
u_int16_t kDnsPort = 53;
struct sockaddr_in serv;
struct sockaddr_in name;
socklen_t namelen = sizeof(name);
u_int32_t me;
memset(&serv, 0, sizeof(serv));
serv.sin_family = AF_INET;
serv.sin_addr.s_addr = inet_addr(kGoogleDnsIp);
serv.sin_port = htons(kDnsPort);
if((connect(sock, (const struct sockaddr*) &serv, sizeof(serv)) == 0)
&& (getsockname(sock, (struct sockaddr*) &name, &namelen) == 0)) {
me = name.sin_addr.s_addr;
} else
me = inet_addr("127.0.0.1");
closesocket(sock);
return(me);
}
/* ****************************************************** */
bool Utils::isInterfaceUp(char *ifname) {
#ifdef WIN32
return(true);
#else
struct ifreq ifr;
char *colon;
int sock;
sock = socket(PF_INET, SOCK_DGRAM, IPPROTO_IP);
if (sock == -1)
return(false);
/* Handle PF_RING interfaces zc:ens2f1@3 */
colon = strchr(ifname, ':');
if(colon != NULL) /* removing pf_ring module prefix (e.g. zc:ethX) */
ifname = colon+1;
memset(&ifr, 0, sizeof(ifr));
strcpy(ifr.ifr_name, ifname);
if (ioctl(sock, SIOCGIFFLAGS, &ifr) < 0) {
close(sock);
return(false);
}
close(sock);
return(!!(ifr.ifr_flags & IFF_UP) ? true : false);
#endif
}
/* ****************************************************** */
bool Utils::maskHost(bool isLocalIP) {
bool mask_host = false;
switch(ntop->getPrefs()->getHostMask()) {
case mask_local_hosts:
if(isLocalIP) mask_host = true;
break;
case mask_remote_hosts:
if(!isLocalIP) mask_host = true;
break;
default:
break;
}
return(mask_host);
}
/* ****************************************************** */
void Utils::luaCpuLoad(lua_State* vm) {
#if !defined(__FreeBSD__) && !defined(__NetBSD__) & !defined(__OpenBSD__) && !defined(__APPLE__) && !defined(WIN32)
long unsigned int user, nice, system, idle, iowait, irq, softirq;
FILE *fp;
if(vm) {
if((fp = fopen("/proc/stat", "r"))) {
fscanf(fp,"%*s %lu %lu %lu %lu %lu %lu %lu",
&user, &nice, &system, &idle, &iowait, &irq, &softirq);
fclose(fp);
lua_push_int_table_entry(vm, "cpu_load", user + nice + system + iowait + irq + softirq);
lua_push_int_table_entry(vm, "cpu_idle", idle);
}
}
#endif
};
/* ****************************************************** */
void Utils::luaMeminfo(lua_State* vm) {
#if !defined(__FreeBSD__) && !defined(__NetBSD__) & !defined(__OpenBSD__) && !defined(__APPLE__) && !defined(WIN32)
long unsigned int memtotal = 0, memfree = 0, buffers = 0, cached = 0, sreclaimable = 0, shmem = 0;
char *line = NULL;
size_t len;
int read;
FILE *fp;
if(vm) {
if((fp = fopen("/proc/meminfo", "r"))) {
while ((read = getline(&line, &len, fp)) != -1) {
if(!strncmp(line, "MemTotal", strlen("MemTotal")) && sscanf(line, "%*s %lu kB", &memtotal))
lua_push_int_table_entry(vm, "mem_total", memtotal);
else if(!strncmp(line, "MemFree", strlen("MemFree")) && sscanf(line, "%*s %lu kB", &memfree))
lua_push_int_table_entry(vm, "mem_free", memfree);
else if(!strncmp(line, "Buffers", strlen("Buffers")) && sscanf(line, "%*s %lu kB", &buffers))
lua_push_int_table_entry(vm, "mem_buffers", buffers);
else if(!strncmp(line, "Cached", strlen("Cached")) && sscanf(line, "%*s %lu kB", &cached))
lua_push_int_table_entry(vm, "mem_cached", cached);
else if(!strncmp(line, "SReclaimable", strlen("SReclaimable")) && sscanf(line, "%*s %lu kB", &sreclaimable))
lua_push_int_table_entry(vm, "mem_sreclaimable", sreclaimable);
else if(!strncmp(line, "Shmem", strlen("Shmem")) && sscanf(line, "%*s %lu kB", &shmem))
lua_push_int_table_entry(vm, "mem_shmem", shmem);
}
if(line)
free(line);
fclose(fp);
/* Equivalent to top utility mem used */
lua_push_int_table_entry(vm, "mem_used", memtotal - memfree - (buffers + cached + sreclaimable - shmem));
}
}
#endif
};
/* ****************************************************** */
char* Utils::getInterfaceDescription(char *ifname, char *buf, int buf_len) {
char ebuf[PCAP_ERRBUF_SIZE];
pcap_if_t *devs, *devpointer;
snprintf(buf, buf_len, "%s", ifname);
ebuf[0] = '\0';
if(pcap_findalldevs(&devs, ebuf) == 0) {
devpointer = devs;
for(int i = 0; devpointer != NULL; i++) {
if(strcmp(devpointer->name, ifname) == 0) {
if(devpointer->description)
snprintf(buf, buf_len, "%s", devpointer->description);
break;
} else
devpointer = devpointer->next;
}
pcap_freealldevs(devs);
}
return(buf);
}
/* ****************************************************** */
int Utils::bindSockToDevice(int sock, int family, const char* devicename) {
#ifdef WIN32
return(-1);
#else
struct ifaddrs* pList = NULL;
struct ifaddrs* pAdapter = NULL;
struct ifaddrs* pAdapterFound = NULL;
int bindresult = -1;
int result = getifaddrs(&pList);
if(result < 0)
return -1;
pAdapter = pList;
while(pAdapter) {
if((pAdapter->ifa_addr != NULL) && (pAdapter->ifa_name != NULL) && (family == pAdapter->ifa_addr->sa_family)) {
if(strcmp(pAdapter->ifa_name, devicename) == 0) {
pAdapterFound = pAdapter;
break;
}
}
pAdapter = pAdapter->ifa_next;
}
if(pAdapterFound != NULL) {
int addrsize = (family == AF_INET6) ? sizeof(sockaddr_in6) : sizeof(sockaddr_in);
bindresult = ::bind(sock, pAdapterFound->ifa_addr, addrsize);
}
freeifaddrs(pList);
return bindresult;
#endif
}
/* ****************************************************** */
int Utils::retainWriteCapabilities() {
int rc = 0;
#ifdef HAVE_LIBCAP
cap_t caps;
/* Add the capability of interest to the permitted capabilities */
caps = cap_get_proc();
cap_set_flag(caps, CAP_PERMITTED, num_cap, cap_values, CAP_SET);
cap_set_flag(caps, CAP_EFFECTIVE, num_cap, cap_values, CAP_SET);
rc = cap_set_proc(caps);
if(rc == 0) {
/* Tell the kernel to retain permitted capabilities */
if(prctl(PR_SET_KEEPCAPS, 1, 0, 0, 0) != 0) {
ntop->getTrace()->traceEvent(TRACE_WARNING, "Unable to retain permitted capabilities [%s]\n", strerror(errno));
rc = -1;
}
}
cap_free(caps);
#else
#ifndef __APPLE__
rc = -1;
ntop->getTrace()->traceEvent(TRACE_WARNING, "ntopng has not been compiled with libcap-dev");
ntop->getTrace()->traceEvent(TRACE_WARNING, "Network discovery and other privileged activities will fail");
#endif
#endif
return(rc);
}
/* ****************************************************** */
#ifndef __APPLE__
static int _setWriteCapabilities(int enable) {
int rc = 0;
#ifdef HAVE_LIBCAP
cap_t caps;
caps = cap_get_proc();
if(caps) {
cap_set_flag(caps, CAP_EFFECTIVE, num_cap, cap_values, enable ? CAP_SET : CAP_CLEAR);
rc = cap_set_proc(caps);
cap_free(caps);
} else
rc = -1;
#else
rc = -1;
#endif
return(rc);
}
#endif
/* ****************************************************** */
/*
Usage example
local path="/etc/test.lua"
ntop.gainWriteCapabilities()
file = io.open(path, "w")
if(file ~= nil) then
file:write("-- End of the test.lua file")
file:close()
else
print("Unable to create file "..path.."<p>")
end
ntop.dropWriteCapabilities()
*/
int Utils::gainWriteCapabilities() {
#ifndef __APPLE__
return(_setWriteCapabilities(true));
#else
return(0);
#endif
}
/* ****************************************************** */
int Utils::dropWriteCapabilities() {
#ifndef __APPLE__
return(_setWriteCapabilities(false));
#else
return(0);
#endif
}
/* ******************************* */
/* Return IP is network byte order */
u_int32_t Utils::findInterfaceGatewayIPv4(const char* ifname) {
#ifndef WIN32
char cmd[128];
FILE *fp;
sprintf(cmd, "netstat -rn | grep '%s' | grep 'UG' | awk '{print $2}'", ifname);
if((fp = popen(cmd, "r")) != NULL) {
char line[256];
u_int32_t rc = 0;
if(fgets(line, sizeof(line), fp) != NULL)
rc = inet_addr(line);
pclose(fp);
return(rc);
} else
#endif
return(0);
}
/* ******************************* */
void Utils::maximizeSocketBuffer(int sock_fd, bool rx_buffer, u_int max_buf_mb) {
int i, rcv_buffsize_base, rcv_buffsize, max_buf_size = 1024 * max_buf_mb * 1024, debug = 0;
socklen_t len = sizeof(rcv_buffsize_base);
int buf_type = rx_buffer ? SO_RCVBUF /* RX */ : SO_SNDBUF /* TX */;
if(getsockopt(sock_fd, SOL_SOCKET, buf_type, (char*)&rcv_buffsize_base, &len) < 0) {
ntop->getTrace()->traceEvent(TRACE_ERROR, "Unable to read socket receiver buffer size [%s]",
strerror(errno));
return;
} else {
if(debug) ntop->getTrace()->traceEvent(TRACE_INFO, "Default socket %s buffer size is %d",
buf_type == SO_RCVBUF ? "receive" : "send",
rcv_buffsize_base);
}
for(i=2;; i++) {
rcv_buffsize = i * rcv_buffsize_base;
if(rcv_buffsize > max_buf_size) break;
if(setsockopt(sock_fd, SOL_SOCKET, buf_type, (const char*)&rcv_buffsize, sizeof(rcv_buffsize)) < 0) {
if(debug) ntop->getTrace()->traceEvent(TRACE_ERROR, "Unable to set socket %s buffer size [%s]",
buf_type == SO_RCVBUF ? "receive" : "send",
strerror(errno));
break;
} else
if(debug) ntop->getTrace()->traceEvent(TRACE_INFO, "%s socket buffer size set %d",
buf_type == SO_RCVBUF ? "Receive" : "Send",
rcv_buffsize);
}
}
/* ****************************************************** */
char* Utils::formatTraffic(float numBits, bool bits, char *buf) {
char unit;
if(bits)
unit = 'b';
else
unit = 'B';
if(numBits < 1024) {
snprintf(buf, 32, "%lu %c", (unsigned long)numBits, unit);
} else if(numBits < 1048576) {
snprintf(buf, 32, "%.2f K%c", (float)(numBits)/1024, unit);
} else {
float tmpMBits = ((float)numBits)/1048576;
if(tmpMBits < 1024) {
snprintf(buf, 32, "%.2f M%c", tmpMBits, unit);
} else {
tmpMBits /= 1024;
if(tmpMBits < 1024) {
snprintf(buf, 32, "%.2f G%c", tmpMBits, unit);
} else {
snprintf(buf, 32, "%.2f T%c", (float)(tmpMBits)/1024, unit);
}
}
}
return(buf);
}
/* ****************************************************** */
char* Utils::formatPackets(float numPkts, char *buf) {
if(numPkts < 1000) {
snprintf(buf, 32, "%.2f", numPkts);
} else if(numPkts < 1000000) {
snprintf(buf, 32, "%.2f K", numPkts/1000);
} else {
numPkts /= 1000000;
snprintf(buf, 32, "%.2f M", numPkts);
}
return(buf);
}
/* ****************************************************** */
bool Utils::str2DetailsLevel(const char *details, DetailsLevel *out) {
bool rv = false;
if(!strcmp(details, "normal")) {
*out = details_normal;
rv = true;
} else if(!strcmp(details, "high")) {
*out = details_high;
rv = true;
} else if(!strcmp(details, "higher")) {
*out = details_higher;
rv = true;
} else if(!strcmp(details, "max")) {
*out = details_max;
rv = true;
}
return rv;
}
/* ****************************************************** */
bool Utils::isCriticalNetworkProtocol(u_int16_t protocol_id) {
return (protocol_id == NDPI_PROTOCOL_DNS) || (protocol_id == NDPI_PROTOCOL_DHCP);
}
/* ****************************************************** */
u_int32_t Utils::roundTime(u_int32_t now, u_int32_t rounder, int32_t offset_from_utc) {
now -= (now % rounder);
now += rounder; /* Aligned to midnight UTC */
if(offset_from_utc > 0)
now += 86400 - offset_from_utc;
else if(offset_from_utc < 0)
now += -offset_from_utc;
return(now);
}
/* ************************************************* */
/*
now
now+1h (hour)
now+1d (day)
now+1w (week)
now+1m (month)
now+1min (minute)
now+1y (year)
*/
u_int32_t Utils::parsetime(char *str) {
if(!strncmp(str, "now", 3)) {
char op = str[3];
int v;
char what[64];
u_int32_t ret = time(NULL);
if(op == '\0')
return(ret);
else if(sscanf(&str[4], "%d%s", &v, what) == 2) {
if(!strcmp(what, "h")) v *= 3600;
else if(!strcmp(what, "d")) v *= 3600*24;
else if(!strcmp(what, "w")) v *= 3600*24*7;
else if(!strcmp(what, "m")) v *= 3600*24*7*30;
else if(!strcmp(what, "min")) v *= 60;
else if(!strcmp(what, "y")) v *= 3600*24*7*365;
if(op == '-')
ret -= v;
else
ret += v;
return(ret);
} else
return(0);
} else
return(atol(str));
}
/* ************************************************* */
u_int64_t Utils::mac2int(u_int8_t *mac) {
u_int64_t m = 0;
memcpy(&m, mac, 6);
return(m);
}
/* ************************************************* */
u_int8_t* Utils::int2mac(u_int64_t mac, u_int8_t *buf) {
memcpy(buf, &mac, 6);
buf[6] = buf[7] = '\0';
return(buf);
}
/* ************************************************* */
void Utils::init_pcap_header(struct pcap_file_header * const h, NetworkInterface * const iface) {
/*
* [0000000] c3d4 a1b2 0002 0004 0000 0000 0000 0000
* [0000010] 05ea 0000 0001 0000
*/
if(!h || !iface)
return;
memset(h, 0, sizeof(*h));
h->magic = PCAP_MAGIC;
h->version_major = 2;
h->version_minor = 4;
h->thiszone = 0;
h->sigfigs = 0;
h->snaplen = ntop->getGlobals()->getSnaplen();
h->linktype = iface->get_datalink();
}
/* ****************************************************** */
void Utils::listInterfaces(lua_State* vm) {
char ebuf[PCAP_ERRBUF_SIZE];
pcap_if_t *devs, *devpointer;
if (pcap_findalldevs(&devs, ebuf) != 0)
return;
devpointer = devs;
while (devpointer != NULL) {
if (Utils::validInterface(devpointer->description) &&
Utils::isInterfaceUp(devpointer->name)) {
lua_newtable(vm);
lua_push_str_table_entry(vm, "description", devpointer->description ? devpointer->description : (char *) "");
// add more info here..
lua_pushstring(vm, devpointer->name);
lua_insert(vm, -2);
lua_settable(vm, -3);
}
devpointer = devpointer->next;
}
pcap_freealldevs(devs);
}
/* ****************************************************** */
bool Utils::validInterface(char *name) {
#ifdef HAVE_NEDGE
return((name && (strncmp(name, "nf:", 3) == 0)) ? true : false);
#else
if(name &&
(strstr(name, "PPP") /* Avoid to use the PPP interface */
|| strstr(name, "dialup") /* Avoid to use the dialup interface */
|| strstr(name, "ICSHARE") /* Avoid to use the internet sharing interface */
|| strstr(name, "NdisWan"))) { /* Avoid to use the internet sharing interface */
return(false);
}
return(true);
#endif
}
/* ****************************************************** */
const char* Utils::policySource2Str(L7PolicySource_t policy_source) {
switch(policy_source) {
case policy_source_pool:
return "policy_source_pool";
case policy_source_protocol:
return "policy_source_protocol";
case policy_source_category:
return "policy_source_category";
case policy_source_device_protocol:
return "policy_source_device_protocol";
case policy_source_schedule:
return "policy_source_schedule";
default:
return "policy_source_default";
}
}