1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146
|
/*
* mxallowd
* (c) 2007-2009 Michael Stapelberg
*
* See mxallowd.c for description, website and license information
*
*/
#include <stdbool.h>
#include <time.h>
#include <malloc.h>
#include <string.h>
#include <pthread.h>
#ifdef PF
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/ioctl.h>
#include <net/if.h>
#include <net/if_pflog.h>
#include <netinet/in.h>
#include <netinet/in_systm.h>
#include <netinet/ip.h>
#include <netinet/ip6.h>
#include <arpa/inet.h>
#include <net/pfvar.h>
#include <net/pfvar.h>
#include <fcntl.h>
#include <unistd.h>
#endif
#include "mxallowd.h"
#include "log.h"
#define _WHITELIST_INTERNAL
#include "whitelist.h"
#undef _WHITELIST_INTERNAL
bool is_whitelisted(char *ip_address, bool useIt) {
if (root == NULL)
return false;
struct whitelist_entry *cur = root;
do {
if (cur->ip_address != NULL && strcmp(cur->ip_address, ip_address) == 0) {
if (useIt)
cur->used = true;
return true;
}
} while ((cur = cur->next) != NULL);
return false;
}
/*
* Doesn't free() old entries but invalidates them, they'll get reused
*
* */
void cleanup_whitelist() {
if (root != NULL) {
time_t current_time = time(NULL);
struct whitelist_entry *cur = root;
do {
if (cur->ip_address != NULL && (current_time - cur->added) > allow_time) {
/* If the entry is too old, invalidate it by freeing ip_address */
slog("Cleaning %s (RDNS: %s) from whitelist (timeout)\n",
cur->ip_address, (cur->rdns != NULL ? cur->rdns : "unresolvable"));
if (!cur->is_child) {
if (cur->used)
successful_connects++;
else direct_to_fake++;
}
free(cur->ip_address);
cur->ip_address = NULL;
if (cur->rdns != NULL) {
free(cur->rdns);
cur->rdns = NULL;
}
}
} while ((cur = cur->next) != NULL);
}
}
/*
* Adds the given ip address (with rdns if not NULL) to whitelist
* is_child = flag whether the entry has to be counted as one mailserver
* or whether it's just another IP of this mailserver
*
*/
void add_to_whitelist(char *ip_address, char *rdns, bool is_child, int af, const void *source_addr) {
if (is_whitelisted(ip_address, false))
return;
struct whitelist_entry *new_entry = malloc(sizeof(struct whitelist_entry));
new_entry->next = NULL;
new_entry->ip_address = strdup(ip_address);
new_entry->rdns = (rdns != NULL ? strdup(rdns) : NULL);
new_entry->rdns_tried = (rdns != NULL);
new_entry->added = time(NULL);
new_entry->is_child = is_child;
new_entry->used = false;
if (root == NULL)
root = new_entry;
else {
struct whitelist_entry *cur = root;
while (cur->next != NULL && cur->ip_address != NULL)
cur = cur->next;
if (cur->ip_address == NULL) {
/* This is a cleaned up entry, overwrite values */
cur->ip_address = new_entry->ip_address;
cur->rdns = new_entry->rdns;
cur->rdns_tried = new_entry->rdns_tried;
cur->added = new_entry->added;
cur->is_child = is_child;
cur->used = false;
free(new_entry);
} else cur->next = new_entry;
}
#ifdef PF
struct pfioc_table table;
struct pfr_addr addr;
memset(&table, '\0', sizeof(struct pfioc_table));
memset(&addr, '\0', sizeof(struct pfr_addr));
table.pfrio_buffer = &addr;
table.pfrio_esize = sizeof(struct pfr_addr);
table.pfrio_size = 1;
strcpy(table.pfrio_table.pfrt_name, "mx-white");
addr.pfra_af = af;
addr.pfra_net = (af == AF_INET ? 32 : 128);
if (af == AF_INET)
memcpy(&(addr.pfra_ip4addr), ((struct sockaddr_in*)source_addr)->sin_addr, sizeof(struct in_addr));
else memcpy(&addr.pfra_ip6addr, ((struct sockaddr_in6*)source_addr)->sin6_addr, sizeof(struct in6_addr));
if (ioctl(pffd, DIOCRADDADDRS, &table) == -1)
slogerror("Couldn't ioctl() on /dev/pf\n");
if (table.pfrio_nadd != 1)
slog("Adding IP to table failed, it probably is already whitelisted\n");
#endif
if (rdns_whitelist) {
/* Inform the resolver-thread of the new entry to resolve */
pthread_mutex_lock(&resolv_thread_mutex);
pthread_cond_broadcast(&resolv_new_cond);
pthread_mutex_unlock(&resolv_thread_mutex);
}
}
|