Switch iptables invocation to execve() instead of popen().

This commit is contained in:
john 2020-09-15 22:26:09 -04:00
parent ede7ae5afb
commit af860167ac
4 changed files with 223 additions and 79 deletions

View File

@ -92,7 +92,7 @@ struct Global G= {
.version= {
.major= 0,
.minor= 14,
.patch= 0
.patch= 1
},
.bitTuples.flags= GlobalFlagBitTuples
@ -143,7 +143,6 @@ static struct {
/*==================================================================*/
/*======================== main() ==================================*/
/*==================================================================*/
/* Enums for long options */
enum {
VERSION_OPT_ENUM=128, /* Larger than any printable character */
@ -614,7 +613,7 @@ main(int argc, char **argv)
if(n2Block) {
if(IPTABLES_block_addresses(&S.toBlock_vec, 10)) {
if(IPTABLES_block_addresses(&S.toBlock_vec)) {
eprintf("ERROR: cannot block addresses!");
goto abort;
}
@ -623,7 +622,7 @@ main(int argc, char **argv)
if(n2Unblock) {
if(IPTABLES_unblock_addresses(&S.toUnblock_vec, 10)) {
if(IPTABLES_unblock_addresses(&S.toUnblock_vec)) {
eprintf("ERROR: cannot unblock addresses!");
goto abort;
}

View File

@ -36,7 +36,7 @@
* work with a much larger number. The command line maximum
* is something like 200K characters.
*/
#define IPTABLES_BATCH_SZ 100
//#define IPTABLES_BATCH_SZ 100
/* For sizing maps and vectors, this a starting point */
#define N_ADDRESSES_HINT 10000

View File

@ -21,6 +21,7 @@
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#include "ban2fail.h"
@ -145,10 +146,160 @@ addrCmp_pvsort(const void *const* pp1, const void *const* pp2)
return 0;
}
#if 0
static int
_control_addresses(int cmdFlag, PTRVEC *h_vec, unsigned batch_sz)
run_command(const char *argv[])
/**************************************************************
* (Un)block addresses in batches of batch_sz.
* Run a command given argv using fork() and execve(). Wait
* for command to finish.
*/
{
int out[2];
/* Create a connected pipe for output from command */
ez_pipe(out);
// Parent will read from out[0];
// Create child process
pid_t child_pid= ez_fork();
if(!child_pid) { // Child process
// Close useless end of pipe
ez_close(out[0]);
// Attach standard outputs to our pipe
ez_dup2(out[1], STDOUT_FILENO);
ez_dup2(out[1], STDERR_FILENO);
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wincompatible-pointer-types"
// Execute command
ez_execve(argv[0], argv, environ);
// We will never get to here
#pragma GCC diagnostic pop
}
#define BUF_SZ 1024
// Read buffer
static char buf[BUF_SZ];
// Loop reading data from child's output
ssize_t nRead;
while(0 < (nRead= read(out[0], buf, BUF_SZ-1))) {
// read() error
if(-1 == nRead) {
sys_eprintf("ERROR: read()");
break;
}
// pipe closed
if(!nRead)
break;
// Relay to our stderr
ez_write(STDERR_FILENO, buf, nRead);
}
#undef BUF_SZ
if(-1 == nRead)
sys_eprintf("ERROR: read()");
/* Wait indefinitely for child to finish */
int wstatus;
pid_t rc= waitpid(child_pid, &wstatus, 0);
// Proper exit
if(WIFEXITED(wstatus))
return WEXITSTATUS(wstatus);
// Killed with signal
if(WIFSIGNALED(wstatus)) {
eprintf("ERROR: %s killed by signal: %s", argv[0], strsignal(WTERMSIG(wstatus)));
return -1;
}
// Shouldn't ever get here
assert(0);
}
#endif
static int
run_command(const char *argv[])
/**************************************************************
* Run a command given argv using fork() and execve(). Wait
* for command to finish.
*/
{
int out[2];
/* Create a connected pipe for output from command */
ez_pipe(out);
// Parent will read from out[0];
// Create child process
pid_t child_pid= ez_fork();
if(!child_pid) { // Child process
// Close useless end of pipe
ez_close(out[0]);
// Attach standard outputs to our pipe
ez_dup2(out[1], STDOUT_FILENO);
ez_dup2(out[1], STDERR_FILENO);
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wincompatible-pointer-types"
// Execute command
ez_execve(argv[0], argv, environ);
// We will never get to here
#pragma GCC diagnostic pop
}
// Close useless end of pipe
ez_close(out[1]);
#define BUF_SZ 1024
// Read buffer
static char buf[BUF_SZ];
// Loop reading data from child's output
ssize_t nRead;
while(0 < (nRead= read(out[0], buf, BUF_SZ-1))) {
// Relay to our stderr
ez_write(STDERR_FILENO, buf, nRead);
}
#undef BUF_SZ
if(-1 == nRead)
sys_eprintf("ERROR: read()");
/* Wait indefinitely for child to finish */
int wstatus;
pid_t rc= waitpid(child_pid, &wstatus, 0);
// Proper exit
if(WIFEXITED(wstatus))
return WEXITSTATUS(wstatus);
// Killed with signal
if(WIFSIGNALED(wstatus)) {
eprintf("ERROR: %s killed by signal: %s", argv[0], strsignal(WTERMSIG(wstatus)));
return -1;
}
// Shouldn't ever get here
assert(0);
}
static int
_control_addresses(const char *cmdFlag, PTRVEC *h_vec)
/**************************************************************
* (Un)block addresses.
*/
{
if(!S.is_init)
@ -156,86 +307,80 @@ _control_addresses(int cmdFlag, PTRVEC *h_vec, unsigned batch_sz)
int rtn= -1;
/* Sanity check for debugging */
assert(batch_sz > 0 && batch_sz <= 100);
// Need a large string buffer for comma separated address list
static STR addr_sb;
STR_sinit(&addr_sb, N_ADDRESSES_HINT*20);
/* Use string buffer to form command */
static STR cmd_sb;
// argv always same length, with NULL at the end
static const char *argv[8];
/**************************************************************************/
/**************** ip4 addresses *******************************************/
/**************************************************************************/
/* Name of executable file */
argv[0]= IPTABLES;
/* iptables command string begins like this */
argv[1]= cmdFlag;
argv[2]= "INPUT";
argv[3]= "-s";
const char *addr;
/* Put any ipv6 addresses at end */
/* Move any ipv6 addresses to the end */
PTRVEC_sort(h_vec, addrCmp_pvsort);
/* Work through ipv4 addresses in the vector */
while((addr= PTRVEC_remHead(h_vec)) &&
!strchr(addr, ':'))
/* Place comma separated address list into single string buffer */
for(unsigned i= 0;
(addr= PTRVEC_remHead(h_vec)) && !strchr(addr, ':');
++i)
{
/* Initialize / reset string buffer */
STR_sinit(&cmd_sb, 256+batch_sz*42);
/* Need comma after 1st address */
if(i)
STR_append(&addr_sb, ",", 1);
/* Beginning of command string, with first source address */
STR_sprintf(&cmd_sb, IPTABLES " 2>&1 -%c INPUT -s %s", cmdFlag, addr);
/* Put address in place */
STR_append(&addr_sb, addr, -1);
/* Append additional source addresses */
unsigned i= 1;
while(i < batch_sz &&
(addr= PTRVEC_remHead(h_vec)) &&
!strchr(addr, ':'))
{
/* employ multiple source addresses for batching */
STR_sprintf(&cmd_sb, ",%s", addr);
++i;
}
}
// Place string buffer in argv
argv[4]= STR_str(&addr_sb);
argv[5]= "-j";
argv[6]= "DROP";
/* Put the end of the command in place */
STR_sprintf(&cmd_sb, " -j DROP");
/* Run iptables */
FILE *fh= ez_popen(STR_str(&cmd_sb), "r");
/* Display any output from iptables */
static char lbuf[1024];
while(ez_fgets(lbuf, sizeof(lbuf), fh))
ez_fprintf(stderr, "NOTE: iptables output: %s", lbuf);
/* All done */
ez_pclose(fh);
/* If the last address pulled was ipv6, move on */
if(addr && strchr(addr, ':')) break;
// Run iptables
if(run_command(argv)) {
eprintf("ERROR: run_command() failed.");
goto abort;
}
/**************************************************************************/
/**************** ip6 addresses *******************************************/
/**************************************************************************/
argv[0]= IP6TABLES;
// Load up ipv6 addresses in string buffer
STR_reset(&addr_sb);
/* Work through ipv6 addresses in the vector */
for( ; addr; (addr= PTRVEC_remHead(h_vec))) {
for(unsigned i= 0 ; addr; (addr= PTRVEC_remHead(h_vec)), ++i) {
/* Initialize / reset string buffer */
STR_sinit(&cmd_sb, 256+batch_sz*42);
/* Need comma after 1st address */
if(i)
STR_append(&addr_sb, ",", 1);
/* Beginning of command string, with first source address */
STR_sprintf(&cmd_sb, IP6TABLES " 2>&1 -%c INPUT -s %s", cmdFlag, addr);
/* Put address in place */
STR_append(&addr_sb, addr, -1);
/* Append additional source addresses */
unsigned i= 1;
while(i < batch_sz && (addr= PTRVEC_remHead(h_vec))) {
}
/* employ multiple source addresses for batching */
STR_sprintf(&cmd_sb, ",%s", addr);
++i;
}
/* Put the end of the command in place */
STR_sprintf(&cmd_sb, " -j DROP");
/* Run iptables */
FILE *fh= ez_popen(STR_str(&cmd_sb), "r");
/* Display any output from iptables */
static char lbuf[1024];
while(ez_fgets(lbuf, sizeof(lbuf), fh))
ez_fprintf(stderr, "NOTE: ip6tables output: %s", lbuf);
/* All done */
ez_pclose(fh);
// Address list is the only thing that changed
argv[4]= STR_str(&addr_sb);
// Run iptables
if(run_command(argv)) {
eprintf("ERROR: run_command() failed.");
goto abort;
}
rtn= 0;
@ -244,28 +389,28 @@ abort:
}
int
IPTABLES_block_addresses(PTRVEC *h_vec, unsigned batch_sz)
IPTABLES_block_addresses(PTRVEC *h_vec)
/**************************************************************
* Block addresses in batches of batch_sz.
* Block addresses.
*/
{
if(!S.is_init)
initialize();
return _control_addresses('A', h_vec, batch_sz);
return _control_addresses("-A", h_vec);
}
int
IPTABLES_unblock_addresses(PTRVEC *h_vec, unsigned batch_sz)
IPTABLES_unblock_addresses(PTRVEC *h_vec)
/**************************************************************
* Block addresses in batches of batch_sz.
* Unblock addresses.
*/
{
if(!S.is_init)
initialize();
return _control_addresses('D', h_vec, batch_sz);
return _control_addresses("-D", h_vec);
}

View File

@ -37,15 +37,15 @@ IPTABLES_is_currently_blocked(const char *addr);
*/
int
IPTABLES_block_addresses(PTRVEC *h_vec, unsigned batch_sz);
IPTABLES_block_addresses(PTRVEC *h_vec);
/**************************************************************
* Block addresses in batches of batch_sz.
* Block addresses.
*/
int
IPTABLES_unblock_addresses(PTRVEC *h_vec, unsigned batch_sz);
IPTABLES_unblock_addresses(PTRVEC *h_vec);
/**************************************************************
* Unblock addresses in batches of batch_sz.
* Unblock addresses.
*/
int