/* Copyright 2004-2011 Russell Miller
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include "packetbl.h"
/*
* SYNOPSIS:
* void daemonize(void);
*
* NOTES:
* This function accomplishes everything needed to become a daemon.
* Including closing standard in/out/err and forking.
* It returns nothing, on failure the program must abort.
*
*/
void daemonize(void) {
pid_t pid;
chdir("/");
close(STDIN_FILENO);
close(STDOUT_FILENO);
close(STDERR_FILENO);
setsid();
pid = fork();
if (pid > 0) {
exit(EXIT_SUCCESS);
}
if (pid < 0) {
DPRINT("Fork failed while damonizing: %s", strerror(errno));
exit(EXIT_FAILURE);
}
}
#ifdef USE_CACHE
/*
* SYNOPSIS:
* static uint32_t packet_cache_hash(
* const struct packet_info ip
* );
*
* ARGUMENTS:
* struct packet_info ip Structure containing information about the
* IP address to create the hash.
*
* RETURN VALUE:
* An integer representing the hash value is returned. This value *MAY BE*
* greater than the size of the hash table, so it should be checked before
* use.
*
* NOTES:
*
* CURRENT IMPLEMENTATION NOTES (do not rely on this for design):
* Currently, only the IP portion of the structure is used for computing the
* hash.
* The current implementation will never return a value greater than 21675
* so having a hash table larger than that would be wasteful.
*
*/
static uint32_t packet_cache_hash(const struct packet_info ip) {
uint32_t hash = 0;
hash = ip.b1 << 6;
hash += ip.b2 << 4;
hash += ip.b3 << 2;
hash += ip.b4;
return hash;
}
/*
* SYNOPSIS:
* void packet_cache_clear(void);
*
* ARGUMENTS:
* (none)
*
* RETURN VALUE:
* (none)
*
* NOTES:
* This function must succeed even if "packet_cache" is NULL.
* This function initializes the values inside the previously allocated
* "packet_cache" array to safe values so that we may check entries
* safely.
*
*/
void packet_cache_clear(void) {
uint32_t i;
if (packet_cache==NULL) return;
for (i=0; i<packet_cache_len; i++) {
packet_cache[i].ipaddr = 0;
packet_cache[i].action = NF_ACCEPT;
packet_cache[i].expires = 0;
}
return;
}
#endif
/*
* SYNOPSIS:
* static uint32_t packet_info_to_ip(
* const struct packet_info ip
* );
*
* ARGUMENTS:
* struct packet_info ip Structure containing IP fields to convert to
* a 32bit unsigned integer.
*
* RETURN VALUE:
* This function returns a 32bit unsigned integer that represents a
* "one-to-one" mapping of IP octets and integer addresses, it will not
* overlap, therefore.
*
* NOTES:
*
*/
static uint32_t packet_info_to_ip(const struct packet_info ip) {
return ((ip.b1 & 0xff) << 24) |
((ip.b2 & 0xff) << 16) |
((ip.b3 & 0xff) << 8) |
(ip.b4 & 0xff);
}
/*
* SYNOPSIS:
* int packet_check_ip(
* const struct packet_info ip
* );
*
* ARGUMENTS:
* struct packet_info ip Structure containing information about
* packet to check.
*
* RETURN VALUE:
* "packet_check_ip" returns an action to supply to "pbl_set_verdict".
* Currently, it will be one of NF_DROP or NF_ACCEPT but other values should
* be accounted for. The supplied information is checked against the
* configued DNS RBLs and Whitelists to determine the appropriate action.
*
* NOTES:
* This function may return stale entries due to caching.
* This function MUST continue to work if "packet_cache" is NULL.
*
*/
int packet_check_ip(const struct packet_info ip) {
int retval;
#ifdef USE_CACHE
uint32_t ipaddr_check;
uint32_t cache_hash = 0;
time_t currtime;
char *actionstr = NULL, *buf = NULL;
currtime = time(NULL);
ipaddr_check = packet_info_to_ip(ip);
if (packet_cache_len > 0) {
cache_hash = packet_cache_hash(ip) % packet_cache_len;
}
buf = get_ip_string(&ip);
if (cache_hash>0 && cache_hash<packet_cache_len && packet_cache != NULL) {
if (packet_cache[cache_hash].ipaddr==ipaddr_check
&& packet_cache[cache_hash].expires>currtime) {
retval = packet_cache[cache_hash].action;
switch (retval) {
case NF_DROP:
actionstr="reject";
statistics.cachereject++;
break;
case NF_ACCEPT:
actionstr="accept";
statistics.cacheaccept++;
break;
default:
actionstr="???";
break;
}
DPRINTQ("[Found in cache (%s)] [%s]",
actionstr, buf);
free(buf);
return retval;
}
}
#else
int rv;
char *buf;
buf = get_ip_string(&ip);
#endif
if (retval = check_packet_list(&ip, conf.whitelist) > 0) {
DPRINTQ("[accept whitelist] [%s] [%d]\n", buf, retval);
statistics.whitelisthits++;
retval=NF_ACCEPT;
} else if (retval = check_packet_list(&ip, conf.blacklist) > 0) {
DPRINTQ("[reject blacklist] [%s] [%d]\n", buf, retval);
statistics.blacklisthits++;
retval=NF_DROP;
} else if (retval = check_packet_dnsbl(&ip, conf.whitelistbl) > 0) {
DPRINTQ("[accept dnsbl] [%s] [%d]", buf, retval);
statistics.whitelistblhits++;
retval=NF_ACCEPT;
} else if (retval = check_packet_dnsbl(&ip, conf.blacklistbl) > 0) {
DPRINTQ("[reject dnsbl] [%s] [%d]", buf, retval);
statistics.blacklistblhits++;
retval=NF_DROP;
} else {
if (conf.default_accept == 1) {
DPRINTQ("[accept fallthrough] [%s]", buf);
retval=NF_ACCEPT;
} else {
DPRINTQ("[reject fallthrough] [%s]", buf);
retval=NF_DROP;
}
statistics.fallthroughhits++;
}
#ifdef USE_CACHE
/* Put current action into the cache. */
if (packet_cache != NULL) {
packet_cache[cache_hash].ipaddr = ipaddr_check;
packet_cache[cache_hash].action = retval;
packet_cache[cache_hash].expires = currtime + packet_cache_ttl;
}
#endif
free(buf);
return retval;
}
static int pbl_callback(struct nfq_q_handle *qh, struct nfgenmsg *nfmsg,
struct nfq_data *nfa, void *data) {
int ret;
int id;
struct nfqnl_msg_packet_hdr *ph;
unsigned char *nfdata;
struct packet_info ip;
DEBUG(2, "Entering callback");
if (ph = nfq_get_msg_packet_hdr(nfa)) {
id = ntohl(ph->packet_id);
}
ret = nfq_get_payload(nfa, &nfdata);
/* what return codes here? */
ret = get_packet_info(nfdata, &ip);
if (ret == -1) {
SET_VERDICT(qh, id, NF_ACCEPT, 0, NULL);
return;
}
ret = packet_check_ip(ip);
if (conf.debug >= 2) {
printf ("Got packet from %hhu.%hhu.%hhu.%hhu: %d\n", ip.b1, ip.b2, ip.b3, ip.b4, ret);
}
SET_VERDICT(qh, id, conf.dryrun ? NF_ACCEPT : ret, 0, NULL);
}
/*
* SYNOPSIS:
* int main(
* int argc,
* char **argv
* );
*
* ARGUMENTS:
* int argc "Argument Count," number of valid elements
* in the "argv" array too.
* char **argv "Argument Vector," array of pointers to the
* arguments passed to this process.
*
* RETURN VALUE:
* This function should never return, since we are a daemon. The parent
* process exits with success (EXIT_SUCCESS) from daemonize();
*
* NOTES:
* This is the function that should be called before any others, it does
* many important initialization routines (reading configuration file,
* setting up the IP Queue routines, system logging, etc) and provides
* the main loop where packets are read and processed.
*
*/
int main(int argc, char **argv) {
struct PBL_HANDLE *handle;
char buf[BUFFERSIZE];
struct nfq_handle *h;
struct nfnl_handle *nh;
int fd;
struct packet_info ip;
struct stat fbuf;
int action;
int rv;
conf.debug = 0;
if (stat("/proc/net/netfilter/nfnetlink_queue", &fbuf) == ENOENT) {
fprintf(stderr, "Please make sure you have\ncompiled a kernel with the Netfilter QUEUE target built in, or loaded the appropriate module.\n");
exit(EXIT_FAILURE);
}
/* Parse our configuration data. */
parse_config();
/* We parse arguments after parsing the config file so we can override the
config file. */
parse_arguments(argc, argv);
if (conf.debug > 0) {
fprintf(stderr, "Debug level %d\n", conf.debug);
}
if (conf.debug > 0) {
fprintf(stderr, "Linking to queue %d\n", conf.queueno);
}
openlog("packetbl", LOG_PID, conf.log_facility);
if (conf.debug == 0) {
daemonize();
}
#ifdef USE_SOCKSTAT
pbl_init_sockstat();
#endif
#ifdef USE_CACHE
if (packet_cache_len > 0) {
/* Allocate space for the packet cache if a positive number of
elements is requested. */
packet_cache = malloc(sizeof(*packet_cache) * packet_cache_len);
} else {
packet_cache = NULL;
}
packet_cache_clear();
#endif
DEBUG(2, "Creating nfq handle...");
if ((h = nfq_open()) == NULL) {
syslog(LOG_ERR, "Couldn't create nfq handle: %s", strerror(errno));
DEBUG(1, "Couldn't create nfq handle");
exit(EXIT_FAILURE);
}
DEBUG(2, "unbinding nfq handle...");
if (nfq_unbind_pf(h, AF_INET) < 0) {
syslog(LOG_ERR, "Couldn't unbind nf_queue handler for AF_INET");
DEBUG(1, "Couldn't unbind nf_queue handler for AF_INET");
exit(EXIT_FAILURE);
}
DEBUG(2, "binding nfq handle...");
if (nfq_bind_pf(h, AF_INET) < 0) {
syslog(LOG_ERR, "Couldn't bind ns_queue handler for AF_INET");
DEBUG(1, "Couldn't bind ns_queue handler for AF_INET");
exit(EXIT_FAILURE);
}
DEBUG(2, "creating queue...");
if ((handle = nfq_create_queue(h, conf.queueno, &pbl_callback, NULL)) == NULL) {
syslog(LOG_ERR, "nfq_create_queue failed");
DEBUG(1, "nfq_create_queue failed");
exit(EXIT_FAILURE);
}
if ((PBL_SET_MODE(handle, PBL_COPY_PACKET, BUFFERSIZE)) == -1) {
syslog(LOG_ERR, "ipq_set_mode error: %s",
PBL_ERRSTR);
DEBUG(1, "ipq_set_mode error");
if (errno == 111) {
syslog(LOG_ERR, "try loading the ip_queue module");
}
exit(EXIT_FAILURE);
}
syslog(LOG_INFO, "packetbl started successfully");
DEBUG(1, "packetbl started successfully");
/* main packet processing loop. This loop should never terminate
* unless a signal is received or some other unforeseen thing
* happens.
*/
while (1) {
nh = nfq_nfnlh(h);
fd = nfnl_fd(nh);
DEBUG(2, "Entering main loop.");
DEBUG(2, "waiting for a packet...");
while ((rv = recv(fd, buf, sizeof(buf), 0)) > 0) {
DEBUG(2, "Handling a packet");
nfq_handle_packet(h, buf, rv);
}
DEBUG(2, "Packet got.");
statistics.totalpackets++;
}
}
/*
* SYNOPSIS:
* int get_packet_info(
* ipq_packet_msg_t *packet,
* struct packet_info *ip
* );
*
* ARGUMENTS:
* ipq_packet_msg_t *packet IP Queue supplied packet headers
* struct packet_info *ip Structure to be filled in.
*
* RETURN VALUE:
* 0 is returned on success, non-zero indicates that the packet could not
* be properly processed (i.e., it's off the wrong protocol or version).
*
* NOTES:
* This function fills in the previously allocated "ip" parameter with
* data from the "packet" parameter.
*
*/
int get_packet_info(char *payload, struct packet_info *ip) {
int version;
int ip_header_length, header_size;;
if (ip == NULL || payload == NULL) {
return -1;
}
ip->s_port = 0;
ip->d_port = 0;
/* Get IP Version Byte 1 of IP Header */
version = payload[0] & 0xF0;
version >>= 4;
/* Get IP Header length Byte 2 of IP Header
* Header length is usually 20, or 5 32-bit words */
ip_header_length = payload[0] & 0x0F;
header_size = ip_header_length * 4;
/* We're not handling IPV6 packets yet. I'll probably rewrite
* this whole damned thing in C++ first. */
if (version != 4) {
return -1;
}
/* IP Address Bytes 13 - 16 of IP header */
ip->b1 = payload[12];
ip->b2 = payload[13];
ip->b3 = payload[14];
ip->b4 = payload[15];
/* Source Port Bytes 21 - 22 of IP Header
* Bytes 1 - 2 of TCP Header */
ip->s_port = payload[header_size] * 256;
ip->s_port += payload[header_size + 1];
/* Destination Port Bytes 23 - 24 of IP Header
* Bytes 3 - 4 of TCP Header */
ip->d_port = payload[header_size + 2] * 256;
ip->d_port += payload[header_size + 3];
/* TCP Flags Byte 14 of TCP header
* Last six bits
* We're only interested at present in the SYN Flag.
* But there's no reason not to copy all of them, the operation
* would take pretty much the same time anyway. */
ip->flags = payload[header_size + 13] & 0x3F;
/* Returning -1, at present accepts the packet unconditionally. */
if (conf.allow_non25 == 0 && ip->d_port != 25) {
return -1;
}
if ((conf.allow_nonsyn == 0) && ((ip->flags & TH_SYN) == 0)) {
return -1;
}
/* Return success */
return 0;
}
/*
* SYNOPSIS:
* void parse_config(void);
*
* ARGUMENTS:
* (none)
*
* RETURN VALUE:
* (none)
*
* NOTES:
* This function parses the configuration file and sets the appropriate
* global variables. It may cause the program to abort with a failure
* if the configuration is unreadable or unparsable. Due to this fact,
* it should only be called during start-up and not from the main loop.
*
*/
void parse_config(void) {
config_t cf, *config;
config_setting_t *config_setting = NULL;
struct ce *config_entry = NULL;
int result = 0;
int i = 0;
const char *facstr = NULL;
config = &cf;
config_init(config);
result = config_read_file(config, "CONFIGFILE");
if (result == CONFIG_FALSE) {
if (config_error_type(config) == CONFIG_ERR_PARSE) {
fprintf (stderr, "Error parsing config file %s, line %d: %s\n",
config_error_file(config),
config_error_line(config),
config_error_text(config));
}
if (config_error_type(config) == CONFIG_ERR_FILE_IO) {
fprintf (stderr, "Error reading config file: %s\n",
config_error_text(config));
}
exit(EXIT_FAILURE);
}
/* there are default, so I'm not checking return values. If it fails,
* then we'll just stay with the default, whatever that might be. */
config_lookup_bool(config, "options.fallthroughaccept", &conf.default_accept);
config_lookup_bool(config, "options.allownonport25", &conf.allow_non25);
config_lookup_bool(config, "options.dryrun", &conf.dryrun);
config_lookup_bool(config, "options.allownonsyn", &conf.allow_nonsyn);
config_lookup_bool(config, "options.quiet", &conf.quiet);
config_lookup_int(config, "options.debug", &conf.debug);
#ifdef USE_CACHE
config_lookup_int(config, "cache.ttl", &packet_cache_ttl);
if (packet_cache_ttl < 0) {
packet_cache_ttl = USE_CACHE_DEF_TTL;
fprintf(stderr, "config cache TTL negative - using default");
}
config_lookup_int(config, "cache.len", &packet_cache_len);
if (packet_cache_len < 0) {
packet_cache_len = USE_CACHE_DEF_LEN;
fprintf(stderr, "config size TTL negative - using default");
}
#endif
result = config_lookup_string(config, "log.facility", &facstr);
i = 0;
if (result == CONFIG_TRUE) {
while (&facenum[i] != NULL) {
if (strcasecmp(facenum[i].string, facstr) == 0) {
conf.log_facility = facenum[i].num;
break;
} else {
i++;
}
}
}
config_lookup_int(config, "options.queueno", &conf.queueno);
if (conf.queueno < 0) {
conf.queueno = 1;
fprintf(stderr, "queueno negative - using default");
}
config_setting = config_lookup(config, "blacklistbl");
if (config_setting != NULL) {
parse_config_bl_list(config_setting, 1);
}
config_setting = config_lookup(config, "whitelistbl");
if (config_setting != NULL) {
parse_config_bl_list(config_setting, 2);
}
config_setting = config_lookup(config, "blacklist");
if (config_setting != NULL) {
parse_config_bl_list(config_setting, 3);
}
config_setting = config_lookup(config, "whitelist");
if (config_setting != NULL) {
parse_config_bl_list(config_setting, 4);
}
}
parse_config_bl_list(config_setting_t *c, int type) {
struct config_entry *ce, *tmp;
int i = 0, len = 0;
char *setting;
#ifdef HAVE_FIREDNS
size_t blacklistlen = 0;
#endif
len = config_setting_length(c);
while (i < len) {
setting = config_setting_get_string_elem(c, i);
if (setting == NULL) {
break;
}
ce = malloc(sizeof(struct config_entry));
if (ce == NULL) {
/* shouldn't happen... */
fprintf(stderr, "Failed to allocate memory for ce struct\n");
exit(EXIT_FAILURE);
}
ce->index = i;
ce->string = (char *)strdup(setting);
ce->next = NULL;
#ifdef HAVE_FIREDNS
blacklistlen = strlen(ce->string);
if (ce->string[blacklistlen - 1] == '.') {
ce->string[blacklistlen - 1] = '\0';
}
#endif
i++;
switch (type) {
case 1:
if (conf.blacklistbl == NULL) {
conf.blacklistbl = ce;
continue;
} else {
tmp = conf.blacklistbl;
}
break;
case 2:
if (conf.whitelistbl == NULL) {
conf.whitelistbl = ce;
continue;
} else {
tmp = conf.whitelistbl;
}
case 3:
fprintf(stderr, "got %s for blacklist\n", ce->string);
if (parse_cidr(ce) == -1) {
fprintf(stderr, "Error parsing CIDR in %s, ignoring\n", ce->string);
free(ce->string);
free(ce);
continue;
}
if (conf.blacklist == NULL) {
conf.blacklist = ce;
continue;
} else {
tmp = conf.blacklist;
}
break;
case 4:
if (parse_cidr(ce) == -1) {
fprintf(stderr, "Error parsing CIDR in %s, ignoring\n", ce->string);
free(ce->string);
free(ce);
continue;
}
if (conf.whitelist == NULL) {
conf.whitelist = ce;
continue;
} else {
tmp = conf.whitelist;
}
break;
}
while (tmp->next != NULL) {
tmp = tmp->next;
}
tmp->next = ce;
}
}
/*
* SYNOPSIS:
* void parse_arguments(
* int argc,
* char **argv
* );
*
* ARGUMENTS:
* int argc "Argument Count," number of valid elements
* in the "argv" array too.
* char **argv "Argument Vector," array of pointers to the
* arguments to be considered for processing.
*
* RETURN VALUE:
* (none)
*
* NOTES:
* Use getopt() to parse passed short arguments. This should be done after
* parsing the config file, because we might need to override some of its
* settings. We cannot return sucess or failure, so upon failure we should
* abort the program.
*
*/
void parse_arguments(int argc, char **argv) {
int ch;
while ((ch = getopt(argc, argv, "qVd")) != -1) {
switch (ch) {
case 'q':
conf.quiet = 1;
break;
case 'V':
printf("PacketBL version %s\n", PACKAGE_VERSION);
exit(EXIT_SUCCESS);
break;
case 'd':
conf.debug++;
break;
case '?':
case ':':
default:
exit(EXIT_FAILURE);
break;
}
}
return;
}
/*
* SYNOPSIS:
* int parse_cidr(
* struct config_entry *ce
* );
*
* ARGUMENTS:
* struct config_entry *ce Structure to be filled in, ->string must
* be supplied.
*
* RETURN VALUE:
* On success 0 is returned, non-zero is returned on error.
*
* NOTES:
* This routine is rather tortured, but it works and is believed
* correct. Please don't mess with it without a good reason.
*
*/
int parse_cidr(struct config_entry *ce) {
int sep = 0; // which separator we're on.
int i = 0;
char *counter, *c1, *numptr;
char number[BUFFERSIZE];
if (ce == NULL) {
return -1;
}
c1 = ce->string; // initialize state counter
for (counter = ce->string;
(counter - ce->string) < strlen(ce->string);
counter++) {
switch (*counter) {
case '.':
case '/':
// separator
strncpy(number, c1, (int)(counter - c1));
number[(int)(counter - c1)] = '\0';
i = atoi(number);
switch(sep) {
case 0:
numptr = &ce->ip.b1;
break;
case 1:
numptr = &ce->ip.b2;
break;
case 2:
numptr = &ce->ip.b3;
break;
case 3:
numptr = &ce->ip.b4;
break;
default:
/* shouldn't happen.
FIXME: add error */
;
}
ce->ip.b1 = i;
if (INVALID_OCTET(*numptr)) {
return -1;
}
sep++;
c1 = counter + 1;
break;
case '0':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
continue;
default:
// this character doesn't belong here.
return -1;
break;
}
}
strncpy (number, c1, (int)(counter - c1));
number[(int)(counter - c1)] = '\0';
ce->cidr.network = atoi(number);
ce->cidr.processed = 0;
ce->cidr.processed = 0xffffffff << (32 - ce->cidr.network);
ce->cidr.ip = 0;
ce->cidr.ip = ce->ip.b1 << 24;
ce->cidr.ip |= ce->ip.b2 << 16;
ce->cidr.ip |= ce->ip.b3 << 8;
ce->cidr.ip |= ce->ip.b4;
/* Mask out the bits that aren't in the network in cidr.ip.
* We don't care about them and they'll just confuse the issue. */
ce->cidr.ip &= ce->cidr.processed;
return 0;
}
/*
* this routine isn't necessary right now.
int validate_blacklist(char *str) {
struct hostent *host;
assert(str != NULL);
host = gethostbyname(str);
if (host == NULL && h_errno != NETDB_SUCCESS) {
return -1;
}
return 0;
}
*/
/*
* SYNOPSIS:
* int check_packet_dnsbl(
* const struct packet_info
* struct config_entry *list
* );
*
* ARGUMENTS:
* struct packet_info *ip IP address data to check in DNS RBL.
* struct config_entry *list Configured DNS RBL to check.
*
* RETURN VALUE:
* 0 is returned if the "ip" cannot be found in the given "list". 1 is
* returned on a successful match.
*
* NOTES:
* "check_packet_dnsbl" searches the given list parameter (which is a list
* of configured DNS RBLs in ->string) to determine if the data passed in
* "ip" should be blocked.
* This function must be able to cope with NULL "ip" and "list" paramters
* without aborting.
*
*/
int check_packet_dnsbl(const struct packet_info *ip, struct config_entry *list) {
struct config_entry *wltmp = NULL;
#ifndef HAVE_FIREDNS
struct hostent *host;
#else
struct in_addr *host;
#endif
if (ip == NULL || list == NULL) {
return 0;
}
wltmp = list;
while (1) {
char lookupbuf[BUFFERSIZE];
snprintf(lookupbuf, sizeof(lookupbuf), "%hhu.%hhu.%hhu.%hhu.%s", ip->b4, ip->b3, ip->b2, ip->b1,
wltmp->string);
#ifndef HAVE_FIREDNS
host = gethostbyname(lookupbuf);
#else
host = firedns_resolveip4(lookupbuf);
#endif
if (host == NULL) {
#ifndef HAVE_FIREDNS
if (h_errno != HOST_NOT_FOUND) {
syslog(LOG_ERR, "Error looking up host %s",
lookupbuf
);
}
#else
;
#endif
} else {
// found.
return 1;
}
if (wltmp->next == NULL) {
/* Termination case */
return 0;
}
wltmp = wltmp->next;
}
return 0;
}
/*
* SYNOPSIS:
* int check_packet_list(
* const struct packet_info *ip
* struct config_entry *list
* );
*
* ARGUMENTS:
* struct packet_info *ip IP address data to check in supplied list.
* struct config_entry *list List that contains data to check in against,
* whitelist for example.
*
* RETURN VALUE:
* 0 is returned if the "ip" cannot be found in the given "list". The index
* of the matched list is returned on a successful match.
*
* NOTES:
* "check_packet_list" searches the given list parameter (which is a list
* CIDRs) to determine if the data passed in "ip" matches (whitelist, for
* for example).
* This function must be able to cope with NULL "ip" and "list" paramters
* without aborting.
*
*/
int check_packet_list(const struct packet_info *ip, struct config_entry *list) {
struct config_entry *wltmp = NULL;
unsigned int ip_proc;
int rv;
if (ip == NULL || list == NULL) {
return 0;
}
ip_proc = ip->b1 << 24;
ip_proc |= ip->b2 << 16;
ip_proc |= ip->b3 << 8;
ip_proc |= ip->b4;
wltmp = list;
while (1) {
uint32_t p = 0;
p = ip_proc;
p &= wltmp->cidr.processed;
if (p == wltmp->cidr.ip) {
return wltmp->index;
return 1;
}
if (wltmp->next == NULL) {
break;
}
wltmp = wltmp->next;
}
return 0;
}
/*
* SYNOPSIS:
* char * get_ip_string(
* const struct packet_info *ip
* );
*
* ARGUMENTS:
* struct packet_info *ip Structure containing IP parts to construct
* the ASCII representation from.
*
* RETURN VALUE:
* (none)
*
* NOTES:
* This function takes the data in the parameter "ip" and returns a buffer
* containing a string representation. This buffer must be freed.
* It must be able to cope with "ip" being NULL.
*
*/
char *get_ip_string(const struct packet_info *ip) {
int rv;
char *buf = NULL;
buf = malloc(BUFFERSIZE);
if (buf == NULL) {
syslog(LOG_ERR, "could not malloc buf in get_ip_string: %s\n",
strerror(errno));
exit(EXIT_FAILURE);
}
if (ip == NULL) {
rv = sprintf(buf, "-");
if (rv < 0) {
syslog(LOG_ERR, "sprintf failed in line %d: %s",
__LINE__, strerror(errno));
exit(EXIT_FAILURE);
}
return buf;
}
rv = snprintf(buf, BUFFERSIZE, "%hhu.%hhu.%hhu.%hhu:%hu.%hu",
ip->b1, ip->b2, ip->b3, ip->b4,
ip->s_port,ip->d_port);
if (rv < 0) {
syslog(LOG_ERR, "snprintf failed in line %d: %s",
__LINE__, strerror(errno));
exit(1);
}
return buf;
}
#ifdef USE_SOCKSTAT
/*
* SYNOPSIS:
* void *pbl_sockstat_thread(
* void *tdata
* );
*
* ARGUMENTS:
* void *tdata Data to pass into the thread. This is unused
* currently.
*
* RETURN VALUE:
* This function always returns NULL.
*
* NOTES:
*/
void *pbl_sockstat_thread(void *tdata) {
struct sockaddr_un sockinfo;
FILE *sockfp = NULL;
char buf[1024]={0};
time_t current_time;
int master_sockfd, sockfd;
int bindret, listenret, snprintfret;
int sockinfolen;
/* Delete any stray sockets left lying around. */
unlink(SOCKSTAT_PATH);
/* Create a UNIX domain socket. */
master_sockfd = socket(PF_UNIX, SOCK_STREAM, 0);
if (master_sockfd < 0) {
syslog(LOG_ERR, "Error creating socket: %s",
strerror(errno));
pthread_exit(NULL);
}
/* Bind our socket to the pathname. */
sockinfo.sun_family = AF_UNIX;
strncpy(sockinfo.sun_path, SOCKSTAT_PATH, sizeof(sockinfo.sun_path));
bindret = bind(master_sockfd,
(struct sockaddr *) &sockinfo, sizeof(sockinfo));
if (bindret < 0) {
syslog(LOG_ERR, "Error binding to socket: %s",
strerror(errno));
if (close(master_sockfd) < 0) {
syslog(LOG_ERR, "%s:%d close() failed: %s",
__FILE__,__LINE__, strerror(errno));
}
pthread_exit(NULL);
}
/* Start listening for connections. */
listenret = listen(master_sockfd, 3);
if (listenret < 0) {
syslog(LOG_ERR, "Error listening on socket: %s",
strerror(errno));
if (close(master_sockfd) < 0) {
syslog(LOG_ERR, "%s:%d close() failed: %s",
__FILE__,__LINE__, strerror(errno));
}
if (unlink(SOCKSTAT_PATH) < 0) {
syslog(LOG_ERR, "%s:%d removing socket failed: %s",
__FILE__,__LINE__, strerror(errno));
}
pthread_exit(NULL);
}
current_time = time(NULL);
ctime_r(¤t_time, buf);
while (1) {
sockinfolen = sizeof(sockinfo);
sockfd = accept(master_sockfd,
(struct sockaddr *) &sockinfo, &sockinfolen);
if (sockfd < 0) continue;
sockfp = fdopen(sockfd, "w");
if (sockfp == NULL) {
if (close(sockfd) < 0) {
syslog(LOG_ERR, "%s:%d close() failed: %s",
__FILE__,__LINE__, strerror(errno));
}
continue;
}
fprintf(sockfp, "Running since: %s", buf);
fprintf(sockfp, "Statistics:\n");
fprintf(sockfp, " Cache hits (accept): %d\n",
statistics.cacheaccept);
fprintf(sockfp, " Cache hits (reject): %d\n",
statistics.cachereject);
fprintf(sockfp, " DNS Whitelist hits: %d\n",
statistics.whitelistblhits);
fprintf(sockfp, " DNS Blacklist hits: %d\n",
statistics.blacklistblhits);
fprintf(sockfp, " Whitelist hits: %d\n",
statistics.whitelisthits);
fprintf(sockfp, " Blacklist hits: %d\n",
statistics.blacklisthits);
fprintf(sockfp, " Fall through hits: %d\n",
statistics.fallthroughhits);
fprintf(sockfp, " Total packets: %d\n",
statistics.totalpackets);
fclose(sockfp);
}
close(master_sockfd);
if (close(master_sockfd) < 0) {
syslog(LOG_ERR, "%s:%d close() failed: %s",
__FILE__,__LINE__, strerror(errno));
}
/* Cleanup sockets. */
if (unlink(SOCKSTAT_PATH) < 0) {
syslog(LOG_ERR, "%s:%d removing socket failed: %s",
__FILE__,__LINE__, strerror(errno));
}
/* Terminate our thread without taking down the entire process. */
pthread_exit(NULL);
/* This should never be reached. */
return(NULL);
}
/*
* SYNOPSIS:
* void pbl_init_sockstat(void);
*
* ARGUMENTS:
* (none)
*
* RETURN VALUE:
* (none)
*
* NOTES:
*/
void pbl_init_sockstat(void) {
pthread_t pthread_data;
int pthread_ret = 0;
/* Create the thread to handle socket requests. */
pthread_ret = pthread_create(
&pthread_data, NULL, pbl_sockstat_thread, NULL);
if (pthread_ret < 0) {
syslog(LOG_ERR, "pthread_create failed: %s",
strerror(errno));
}
return;
}
#endif