Browse Source

Make snetaid reactive to netlink events

master
Aitor 11 months ago
parent
commit
3ce7c57698
  1. 43
      src/config.c
  2. 9
      src/config.h
  3. 2
      src/def.h
  4. 13
      src/ini.c
  5. 259
      src/main.c
  6. 146
      src/sigact.c
  7. 3
      src/sigact.h

43
src/config.c

@ -94,13 +94,6 @@ static int config_ini_parser(void* userdata, char const* section, char const* na
return 1;
}
if(strcmp(name, "polltime") == 0) {
// The flag --polltime doesn't exist
conf->polltime = atoi(value);
return 1;
}
if(strcmp(name, "automatically_connect") == 0) {
// The flag --automatically connect doesn't exist
@ -126,6 +119,15 @@ static int config_ini_parser(void* userdata, char const* section, char const* na
return 1;
}
if(strcmp(name, CONFIG_WHITELIST) == 0) {
if(conf->whitelist == NULL) {
conf->whitelist = strdup_or_null(value);
}
return 1;
}
if(strcmp(name, CONFIG_LOG_LEVEL) == 0) {
if(strcasecmp(value, "debug") == 0) {
@ -247,6 +249,12 @@ int config_free(config_t* conf)
conf->pidfile_path = NULL;
}
if(conf->whitelist != NULL) {
free(conf->whitelist);
conf->whitelist = NULL;
}
return 0;
}
@ -260,6 +268,7 @@ int config_fullpaths(config_t* conf)
&conf->config_path,
&conf->pidfile_path,
&conf->logfile_path,
&conf->whitelist,
NULL
};
@ -304,10 +313,10 @@ int config_usage(char const* progname)
"Options:\n"
" -a --conf_file filename Read configuration from the file\n\n"
" -l --log_file filename Write logs to the file\n\n"
" -v --verbose-level VERBOSITY\n\
Set the level of verbose output. Valid values are\n\
positive integers. Larger integers lead to more\n\
debugging information.\n\n"
" -v --verbose-level VERBOSITY\n\
Set the level of verbose output. Valid values are\n\
positive integers. Larger integers lead to more\n\
debugging information.\n\n"
" -p --pid_file filename PID file used by daemonized app\n\n"
" -b --background Run in the background\n\n"
" -f --foreground Run in the foreground (Default)\n\n"
@ -339,6 +348,7 @@ int config_load_from_args(config_t* config, int argc, char** argv)
{"config_path", required_argument, 0, 'a'},
{"logfile_path", required_argument, 0, 'l'},
{"pidfile_path", required_argument, 0, 'p'},
{"whitelist", required_argument, 0, 'w'},
{"check-running", no_argument, 0, 'c'},
{0, 0, 0, 0}
};
@ -346,11 +356,11 @@ int config_load_from_args(config_t* config, int argc, char** argv)
/* Try to process all command line arguments */
// Si la letra del argumento va seguida de : entonces es que requiere argumento,
// si va seguida de :: entonces el argumento es opcional. El orden es irrelevante.
// Podría ser, por ejemplo: "a:bfkl:v:cp:"
// Podría ser, por ejemplo: "a:bfkl:v:cp:w:"
int rc = 0;
int value, option_index = 0;
int _kill = 0, _check = 0;
while ((value = getopt_long(argc, argv, "fbka:l:v:p:c", long_options, &option_index)) != -1) {
while ((value = getopt_long(argc, argv, "fbka:l:v:p:cw:", long_options, &option_index)) != -1) {
switch (value) {
case 0:
// If this option sets a flag, do nothing else now.
@ -405,6 +415,13 @@ int config_load_from_args(config_t* config, int argc, char** argv)
m_config->pidfile_path = strdup_or_null(optarg);
break;
case 'w':
if(m_config->whitelist != NULL) {
free(m_config->whitelist);
}
m_config->whitelist = strdup_or_null(optarg);
break;
case 'k':
_kill = 1;
break;

9
src/config.h

@ -29,6 +29,7 @@
#define CONFIG_PIDFILE_PATH "pidfile"
#define CONFIG_LOGFILE_PATH "logfile"
#define CONFIG_LOG_LEVEL "loglevel"
#define CONFIG_WHITELIST "whitelist"
#define INI_MAX_LINE 4096
#define INI_STOP_ON_FIRST_ERROR 1
@ -44,12 +45,12 @@
// structure for both file configuration and command-line options
typedef struct {
char* config_path;
char* pidfile_path;
char* logfile_path;
char *config_path;
char *pidfile_path;
char *logfile_path;
char *whitelist;
int debug_level;
int error_level;
int polltime;
bool foreground;
bool automatically_connect;
} config_t;

2
src/def.h

@ -114,6 +114,8 @@ extern const char *progname;
extern int pid_fd;
extern pid_t m_pid;
extern void (*f)(void);
#define SOCKET_NAME "/tmp/snetaid.socket"
#define DEFAULT_CONFIG_FILE "/etc/simple-netaid/snetaid.conf"
#define WIFI_DIR "/etc/network/wifi"

13
src/ini.c

@ -15,23 +15,12 @@ Copyright (C) 2021 Aitor Cuadrado Zubizarreta <aitor_czr@gnuinos.org>
#include <ctype.h>
#include <string.h>
#include <assert.h>
#include <simple-netaid/util.h>
#include "ini.h"
#define MAX_SECTION 50
#define MAX_NAME 50
// Copy string "in" with at most "insz" chars to buffer "out", which
// is "outsz" bytes long. The output is always 0-terminated. Unlike
// strncpy(), strncpy_t() does not zero fill remaining space in the
// output buffer:
static inline char* strncpy_t(char* out, size_t outsz, const char* in, size_t insz)
{
assert(outsz > 0);
while(--outsz > 0 && insz > 0 && *in) { *out++ = *in++; insz--; }
*out = 0;
return out;
}
/* Strip whitespace chars off end of given string, in place. Return s. */
static char* rstrip(char* s)

259
src/main.c

@ -24,11 +24,13 @@
#include <string.h>
#include <errno.h>
#include <libgen.h> // dirname()
#include <time.h>
#include <simple-netaid/nlsock.h>
#include <simple-netaid/sbuf.h>
#include <simple-netaid/iproute.h>
#include <simple-netaid/interfaces.h>
#include <simple-netaid/helpers.h>
#include <simple-netaid/nl_monitor.h>
#include "ini.h"
#include "def.h"
@ -36,6 +38,13 @@
#include "util.h"
#include "sigact.h"
int DEBUG = 1;
int _DEBUG_MESSAGES = 0;
int _INFO_MESSAGES = 0;
int _WARN_MESSAGES = 1;
int _ERROR_MESSAGES = 1;
#define DEFAULT_POLLTIME 100
config_t *m_config = NULL;
@ -43,12 +52,17 @@ const char *progname, *libpath;
int pid_fd = -1;
pid_t m_pid = 0;
int DEBUG = 1;
void (*f)(void) = NULL;
int _DEBUG_MESSAGES = 0;
int _INFO_MESSAGES = 0;
int _WARN_MESSAGES = 1;
int _ERROR_MESSAGES = 1;
void skip() { };
void emit_sigusr2() {
nl_monitor(200, false);
};
char* replace(char const * const, char const * const, char const * const);
void get_fullpath(struct sbuf*, const char*);
int is_running();
int get_status(const char*, const char*);
// set debug level, by setting for info and debug messages
static void set_debug_level(int d)
@ -72,45 +86,6 @@ static void set_error_level(int e)
}
}
static
int get_status(const char *wired_device, const char *wireless_device)
{
int a=1; /* disconnected */
int b=get_interface_status(wired_device);
int c=get_interface_status(wireless_device);
const char *ifname = iproute();
if(strcmp(ifname, "")) {
if(!strcmp(ifname, wired_device)) a=2;
else if(!strcmp(ifname, wireless_device)) a=3;
}
return (a*100 + b*10 +c);
}
static void get_fullpath(struct sbuf *s, const char *name)
{
char *token;
char delim[4] = "=> ";
char output[1024] = {0};
char cmd[64] = {0};
sprintf(cmd, "ldd ");
strcat(cmd, name);
strcat(cmd, " | grep libnetaid.so");
FILE *pf = popen(cmd, "r");
if(!pf) {
printf("Sorry, there was an error opening the pipe\n");
exit(EXIT_FAILURE);
}
fgets(output, 1024, pf);
token = strtok((char*)output, delim);
token = strtok(NULL, delim);
sbuf_addstr(s, token);
pclose(pf);
}
int main (int argc, char **argv)
{
int fd = -1, rc = 0;
@ -204,25 +179,21 @@ int main (int argc, char **argv)
if(!ifquery(wireless_device.buf)) ifup(wireless_device.buf);
}
if(m_config->polltime == 0) m_config->polltime = DEFAULT_POLLTIME;
sigaction_init();
memset(&addr, 0, sizeof(addr));
addr.nl_family = AF_NETLINK;
addr.nl_groups = RTMGRP_LINK;
if ((fd = socket(AF_LOCAL, SOCK_DGRAM, 0)) < 0) {
log_error("1) bind(): '%s' errno = %d\n", strerror(errno), -errno);
goto finish;
}
status = new_status = get_status(wired_device.buf, wireless_device.buf);
addr.nl_groups = RTMGRP_LINK | RTMGRP_IPV4_IFADDR | RTMGRP_IPV4_ROUTE;
if ((fd = socket(AF_LOCAL, SOCK_DGRAM, 0)) < 0) {
log_error("socket(PF_NETLINK): '%s' errno = %d\n", strerror(errno), -errno);
return -errno;
}
for( ;; ) {
fd_set fds;
int nlsock_fd = -1;
bool shall_notify;
clock_t start;
if ((nlsock_fd = socket(PF_NETLINK, SOCK_DGRAM, NETLINK_ROUTE)) < 0) {
log_error("socket(PF_NETLINK): '%s' errno = %d\n", strerror(errno), -errno);
@ -240,49 +211,155 @@ int main (int argc, char **argv)
if(*iproute() == 0) {
/* Connection attempt */
if(m_config->automatically_connect) work(wired_device.buf, wireless_device.buf);
if(m_config->automatically_connect) {
work(wired_device.buf, wireless_device.buf);
}
}
shall_notify = true;
if (select(FD_SETSIZE, &fds, NULL, NULL, NULL) < 0) {
if (errno == EINTR) {
/*
select() will report the EINTR error code if a signal occurred
while the system call was in progress. No error actually occurred,
it's just reported this way because the system is unable to resume
the system call automatically. This coding pattern simply retries
the system call when this happens, to ignore the interrupt.
For instance, this might happen if the program makes use of alarm()
to run some code asynchronously when a timer runs out. If the timeout
occurs while the program is calling select(), we just want to retry the
system call.
*/
log_info("select(): '%s'\n", strerror(errno));
}
//if(!access(SOCKET_NAME, F_OK))
shall_notify = false;
}
if(m_pid && shall_notify) {
new_status = get_status(wired_device.buf, wireless_device.buf);
if(status != new_status) {
kill(m_pid, SIGUSR2);
status = new_status;
status = get_status(wired_device.buf, wireless_device.buf);
Select:
time(&start);
if (select(FD_SETSIZE, &fds, NULL, NULL, NULL) < 0) {
if (errno == EINTR) {
// We are here due to the SIGUSR1 signal emmited by libnetaid.so (with CAPKILL capabilities)
clock_t end;
double diff;
time (&end);
diff = difftime (end,start);
log_info("select(): %.2lf seconds '%s'\n", diff, strerror(errno));
if(!f || diff > 2) f = skip;
else f = emit_sigusr2;
goto Select;
}
}
// Give time to f() to change from emit_sigusr2 to skip if necessary
sleep(1);
if(m_pid != 0) f();
new_status = get_status(wired_device.buf, wireless_device.buf);
if(f == emit_sigusr2 && status != new_status) {
kill(m_pid, SIGUSR2);
}
if(f) f = emit_sigusr2;
if (nlsock_fd >= 0) {
close(nlsock_fd);
}
}
if (nlsock_fd >= 0)
close(nlsock_fd);
nlsock_fd = -1;
nlsock_fd = -1;
} // for
finish:
if (fd >= 0)
close(fd);
fd = -1;
return 0;
}
// Usage:
// char * const newstr = replace(original, pattern, replacement);
char* replace(char const * const original, char const * const pattern, char const * const replacement)
{
size_t const replen = strlen(replacement);
size_t const patlen = strlen(pattern);
size_t const orilen = strlen(original);
size_t patcnt = 0;
const char * oriptr;
const char * patloc;
// find how many times the pattern occurs in the original string
for (oriptr = original; patloc = strstr(oriptr, pattern); oriptr = patloc + patlen)
{
patcnt++;
}
// allocate memory for the new string
size_t const retlen = orilen + patcnt * (replen - patlen);
char * const returned = (char *) malloc( sizeof(char) * (retlen + 1) );
if (returned != NULL) {
// copy the original string replacing all the instances of the pattern
char * retptr = returned;
for (oriptr = original; patloc = strstr(oriptr, pattern); oriptr = patloc + patlen) {
size_t const skplen = patloc - oriptr;
// copy the section until the occurence of the pattern
strncpy(retptr, oriptr, skplen);
retptr += skplen;
// copy the replacement
strncpy(retptr, replacement, replen);
retptr += replen;
}
// copy the rest of the string.
strcpy(retptr, oriptr);
}
return returned;
}
void get_fullpath(struct sbuf *s, const char *name)
{
char *token;
char delim[4] = "=> ";
char output[1024] = {0};
char cmd[64] = {0};
sprintf(cmd, "ldd ");
strcat(cmd, name);
strcat(cmd, " | grep libnetaid.so");
FILE *pf = popen(cmd, "r");
if(!pf) {
printf("Sorry, there was an error opening the pipe\n");
exit(EXIT_FAILURE);
}
fgets(output, 1024, pf);
char * const newstr = replace(output, "//", "/");
token = strtok((char*)newstr, delim);
token = strtok(NULL, delim);
sbuf_addstr(s, token);
pclose(pf);
}
int is_running()
{
FILE *fp = NULL;
char *token = NULL;
struct sbuf cmd;
int count=0;
sbuf_init(&cmd);
sbuf_addstr(&cmd, "ps -A | grep \"libnetaid.so\"");
fp=popen(cmd.buf, "r");
if(!fp) {
printf("Sorry, there was an error opening the pipe\n");
free(cmd.buf);
exit(EXIT_FAILURE);
}
sbuf_free(&cmd);
char line[1024]={0};
while (fgets(line, 1024, fp)) {
const char delim[2] = " ";
token = strtok( (char*)line, delim );
token[strcspn(token, "\n")] = 0;
count++;
}
pclose(fp);
return count;
}
int get_status(const char *wired_device, const char *wireless_device)
{
int a=1; /* disconnected */
int b=get_interface_status(wired_device);
int c=get_interface_status(wireless_device);
const char *ifname = iproute();
if(strcmp(ifname, "")) {
if(!strcmp(ifname, wired_device)) a=2;
else if(!strcmp(ifname, wireless_device)) a=3;
}
return (a*100 + b*10 +c);
}

146
src/sigact.c

@ -35,28 +35,18 @@
#include <simple-netaid/interfaces.h>
#include <simple-netaid/ipaddr.h>
#include <simple-netaid/wireless.h>
#include <simple-netaid/nl_monitor.h>
#include "sigact.h"
#include "def.h"
#include "config.h"
#define BUFFER_SIZE 128
#define MAX_SIZE 1024
#define PROTOCOL 0
#define ACTIVE_WIFIS_SOCKET "/tmp/snetaid_active_wifis.socket"
// Copy string "in" with at most "insz" chars to buffer "out", which
// is "outsz" bytes long. The output is always 0-terminated. Unlike
// strncpy(), strncpy_t() does not zero fill remaining space in the
// output buffer:
static inline char* strncpy_t(char* out, size_t outsz, const char* in, size_t insz)
{
assert(outsz > 0);
while(--outsz > 0 && insz > 0 && *in) { *out++ = *in++; insz--; }
*out = 0;
return out;
}
void sigaction_init(void)
{
struct sigaction sa;
@ -81,19 +71,17 @@ void sig_handler(int signum)
switch(signum) {
case SIGALRM:
break;
break;
case SIGCHLD:
break;
break;
case SIGHUP:
break;
break;
case SIGUSR1:
{
sleep(1);
read_arguments(true);
}
if(m_pid) sleep(1);
read_arguments();
break;
case SIGUSR2:
@ -156,11 +144,12 @@ const char* read_buffer(int df, char *buf)
void read_arguments()
{
int num_messages __attribute__((unused));
int count = 0;
int dfData = -1;
int num_messages __attribute__((unused));
struct sockaddr_un dirUNIXServer;
socklen_t pid_size;
int sz, rc = 0;
int sz, rc = -1;
char *arr_pid = NULL;
char buffer[BUFFER_SIZE] = {0};
const char *device;
@ -182,12 +171,12 @@ void read_arguments()
dirUNIXServer.sun_family = AF_UNIX;
sz = sizeof(dirUNIXServer.sun_path) - 1;
strncpy_t(dirUNIXServer.sun_path, sz, SOCKET_NAME, sz);
rc = connect(dfData, (const struct sockaddr*) &dirUNIXServer, sizeof(struct sockaddr_un));
if(rc == -1) {
fprintf(stderr, "The server is down.\n");
close(dfData);
return;
while(rc == -1 && count < 10) {
sleep(1);
rc = connect(dfData, (const struct sockaddr*) &dirUNIXServer, sizeof(struct sockaddr_un));
count++;
}
pid_size = sizeof(m_pid);
@ -196,34 +185,53 @@ void read_arguments()
perror("getsockopt");
exit(EXIT_FAILURE);
}
if (asprintf(&arr_pid, "%jd", (intmax_t) m_pid) != -1) {
printf("PID=%s\n", arr_pid);
free(arr_pid); // cleanup when done.
} else {
printf("Unable to get the pid\n");
bool whitelisted;
struct sbuf pstat_binary;
sbuf_init(&pstat_binary);
get_pstat_binary(arr_pid, &pstat_binary);
free(arr_pid);
whitelisted = query_whitelist(pstat_binary.buf);
sbuf_free(&pstat_binary);
if(!whitelisted) {
log_error("This application is not included in the white list %s\n", m_config->whitelist);
return;
}
}
else {
log_error("%s: Unable to get the pid\n", progname);
}
option = atoi(read_buffer(dfData, buffer));
num_messages = atoi(read_buffer(dfData, buffer));
device = read_buffer(dfData, buffer);
switch(option) {
case 0:
m_config->automatically_connect= 0;
disconnect(device);
kill(m_pid, SIGUSR1);
nl_monitor(10, false);
break;
case 1:
wired_connection(device);
kill(m_pid, SIGUSR1);
nl_monitor(10, false);
break;
case 2:
interface_up(device);
kill(m_pid, SIGUSR1);
nl_monitor(10, false);
break;
case 3:
m_config->automatically_connect = 0;
interface_down(device);
kill(m_pid, SIGUSR1);
nl_monitor(10, false);
break;
case 4:
@ -231,6 +239,8 @@ void read_arguments()
const char *essid = read_buffer(dfData, buffer);
const char *passwd = read_buffer(dfData, buffer);
wireless_connection(device, essid, passwd, NULL);
kill(m_pid, SIGUSR1);
nl_monitor(10, false);
}
break;
@ -240,6 +250,8 @@ void read_arguments()
const char *passwd = read_buffer(dfData, buffer);
const char *filename = read_buffer(dfData, buffer);
wireless_connection(device, essid, passwd, filename);
kill(m_pid, SIGUSR1);
nl_monitor(10, false);
}
break;
@ -258,6 +270,8 @@ void read_arguments()
free(cmd.buf);
sleep(1);
ifup(device);
kill(m_pid, SIGUSR1);
nl_monitor(10, false);
}
break;
@ -278,10 +292,7 @@ void read_arguments()
break;
}
kill(m_pid, SIGUSR1);
close(dfData);
unlink(SOCKET_NAME);
}
/**
@ -350,3 +361,66 @@ void send_active_wifis(struct sbuf *s)
close(dfData);
unlink(ACTIVE_WIFIS_SOCKET);
}
void get_pstat_binary(char *pid, struct sbuf *s)
{
char output[MAX_SIZE];
struct sbuf cmd;
FILE *fp = NULL;
// Get the output of "pstat PID" and inspect the PSTAT_BINARY="value" key pair
sbuf_init(&cmd);
sbuf_concat(&cmd, 2, "/usr/bin/pstat ", pid);
fp = popen(cmd.buf, "r");
if(!fp) {
printf("Pipe error\n");
free(cmd.buf);
exit(EXIT_FAILURE);
}
sbuf_free(&cmd);
while(fgets(output, MAX_SIZE, fp)) {
output[strcspn(output, "\n")] = 0;
const char delim1[2] = "=";
const char delim2[2] = "\"";
char *token = strtok((char*)output, delim1);
if(!strcmp(token, "PSTAT_BINARY")) {
token = strtok(NULL, delim1);
token = strtok((char*)token, delim2);
token = strtok(NULL, delim2);
sbuf_addstr(s, token);
}
}
pclose(fp);
}
bool query_whitelist(const char *pstat_binary)
{
FILE *wl;
char line[MAX_SIZE] = {0};
bool res = false;
wl = fopen(m_config->whitelist, "r");
if(!wl) {
log_error("Unable to open file: %s\n", m_config->whitelist);
exit(EXIT_FAILURE);
}
while(fgets(line, MAX_SIZE, wl)) {
line[strcspn(line, "\n")] = 0;
char str[MAX_SIZE] = {0};
sprintf(str, "\"");
strcat(str, line);
strcat(str, "\"");
// Do they match?
if(!strcmp(line, pstat_binary)) {
res = true;
break;
}
}
fclose(wl);
return res;
}

3
src/sigact.h

@ -26,5 +26,8 @@ void sigaction_init(void);
void sig_handler(int);
void read_arguments();
void send_active_wifis(struct sbuf*);
void get_pstat_binary(char*, struct sbuf*);
bool query_whitelist(const char*);
void emit_sigusr2();
#endif // __SIGACT_H__

Loading…
Cancel
Save