#include <stdbool.h>
|
#include <stdint.h>
|
|
#include "internal/internal.h"
|
|
#define MAX_BITS 1024
|
|
#define CONNLABEL_CFG "/etc/xtables/connlabel.conf"
|
#define HASH_SIZE 64
|
|
struct labelmap_bucket {
|
char *name;
|
unsigned int bit;
|
struct labelmap_bucket *next;
|
};
|
|
struct nfct_labelmap {
|
struct labelmap_bucket *map_name[HASH_SIZE];
|
unsigned int namecount;
|
char **bit_to_name;
|
};
|
|
static struct labelmap_bucket* label_map_bucket_alloc(const char *n, unsigned int b)
|
{
|
struct labelmap_bucket *bucket;
|
char *name = strdup(n);
|
|
if (!name)
|
return NULL;
|
|
bucket = malloc(sizeof(*bucket));
|
if (!bucket) {
|
free(name);
|
return NULL;
|
}
|
bucket->name = name;
|
bucket->bit = b;
|
return bucket;
|
}
|
|
static unsigned int hash_name(const char *name)
|
{
|
unsigned int hash = 0;
|
|
while (*name) {
|
hash = (hash << 5) - hash + *name;
|
name++;
|
}
|
return hash & (HASH_SIZE - 1);
|
}
|
|
int __labelmap_get_bit(struct nfct_labelmap *m, const char *name)
|
{
|
unsigned int i = hash_name(name);
|
struct labelmap_bucket *list = m->map_name[i];
|
|
while (list) {
|
if (strcmp(name, list->name) == 0)
|
return list->bit;
|
list = list->next;
|
}
|
return -1;
|
}
|
|
const char *__labelmap_get_name(struct nfct_labelmap *m, unsigned int bit)
|
{
|
if (bit < m->namecount)
|
return m->bit_to_name[bit] ? m->bit_to_name[bit] : "";
|
return NULL;
|
}
|
|
static int map_insert(struct nfct_labelmap *m, const char *n, unsigned int b)
|
{
|
unsigned int i = hash_name(n);
|
struct labelmap_bucket *list = m->map_name[i];
|
|
while (list) {
|
if (strcmp(list->name, n) == 0)
|
return -1;
|
list = list->next;
|
}
|
|
list = label_map_bucket_alloc(n, b);
|
if (!list)
|
return -1;
|
|
if (m->map_name[i])
|
list->next = m->map_name[i];
|
else
|
list->next = NULL;
|
m->map_name[i] = list;
|
return 0;
|
}
|
|
static int is_space_posix(int c)
|
{
|
return c == ' ' || c == '\f' || c == '\r' || c == '\t' || c == '\v';
|
}
|
|
static char *trim_label(char *label)
|
{
|
char *end;
|
|
while (is_space_posix(*label))
|
label++;
|
end = strchr(label, '\n');
|
if (end)
|
*end = 0;
|
else
|
end = strchr(label, '\0');
|
end--;
|
|
while (end > label && is_space_posix(*end)) {
|
*end = 0;
|
end--;
|
}
|
|
return *label ? label : NULL;
|
}
|
|
static int
|
xtables_parse_connlabel_numerical(const char *s, char **end)
|
{
|
unsigned long value;
|
|
value = strtoul(s, end, 0);
|
if (value == 0 && s == *end)
|
return -1;
|
if (value >= MAX_BITS)
|
return -1;
|
return value;
|
}
|
|
static void free_list(struct labelmap_bucket *b)
|
{
|
struct labelmap_bucket *tmp;
|
|
while (b) {
|
free(b->name);
|
|
tmp = b;
|
b = b->next;
|
|
free(tmp);
|
}
|
}
|
|
void __labelmap_destroy(struct nfct_labelmap *map)
|
{
|
unsigned int i;
|
struct labelmap_bucket *b;
|
|
for (i = 0; i < HASH_SIZE; i++) {
|
b = map->map_name[i];
|
free_list(b);
|
}
|
|
free(map->bit_to_name);
|
free(map);
|
}
|
|
static void make_name_table(struct nfct_labelmap *m)
|
{
|
struct labelmap_bucket *b;
|
unsigned int i;
|
|
for (i = 0; i < HASH_SIZE; i++) {
|
b = m->map_name[i];
|
while (b) {
|
m->bit_to_name[b->bit] = b->name;
|
b = b->next;
|
}
|
}
|
}
|
|
static struct nfct_labelmap *map_alloc(void)
|
{
|
struct nfct_labelmap *map = malloc(sizeof(*map));
|
if (map) {
|
unsigned int i;
|
for (i = 0; i < HASH_SIZE; i++)
|
map->map_name[i] = NULL;
|
map->bit_to_name = NULL;
|
}
|
return map;
|
}
|
|
/*
|
* We will only accept alpha numerical labels; else
|
* parses might choke on output when label named
|
* "foo;<&bar" exists. ASCII machines only.
|
*
|
* Avoids libc isalnum() etc. to avoid issues with locale
|
* settings.
|
*/
|
static bool label_is_sane(const char *label)
|
{
|
for (;*label; label++) {
|
if (*label >= 'a' && *label <= 'z')
|
continue;
|
if (*label >= 'A' && *label <= 'Z')
|
continue;
|
if (*label >= '0' && *label <= '9')
|
continue;
|
if (*label == ' ' || *label == '-')
|
continue;
|
return false;
|
}
|
return true;
|
}
|
|
const char *__labels_get_path(void)
|
{
|
return CONNLABEL_CFG;
|
}
|
|
struct nfct_labelmap *__labelmap_new(const char *name)
|
{
|
struct nfct_labelmap *map;
|
char label[1024];
|
char *end;
|
FILE *fp;
|
int added = 0;
|
unsigned int maxbit = 0;
|
uint32_t bits_seen[MAX_BITS/32];
|
|
fp = fopen(name ? name : CONNLABEL_CFG, "re");
|
if (!fp)
|
return NULL;
|
|
memset(bits_seen, 0, sizeof(bits_seen));
|
|
map = map_alloc();
|
if (!map) {
|
fclose(fp);
|
return NULL;
|
}
|
|
while (fgets(label, sizeof(label), fp)) {
|
int bit;
|
|
if (label[0] == '#')
|
continue;
|
|
bit = xtables_parse_connlabel_numerical(label, &end);
|
if (bit < 0 || test_bit(bit, bits_seen))
|
continue;
|
|
end = trim_label(end);
|
if (!end)
|
continue;
|
|
if (label_is_sane(end) && map_insert(map, end, bit) == 0) {
|
added++;
|
if (maxbit < bit)
|
maxbit = bit;
|
set_bit(bit, bits_seen);
|
}
|
}
|
|
fclose(fp);
|
|
if (added) {
|
map->namecount = maxbit + 1;
|
map->bit_to_name = calloc(sizeof(char *), map->namecount);
|
if (!map->bit_to_name)
|
goto err;
|
make_name_table(map);
|
return map;
|
} else {
|
errno = 0;
|
}
|
err:
|
__labelmap_destroy(map);
|
return NULL;
|
}
|