labels.c revision 8a44513648da0c5f5551f96b329cf56b66f5b303
1#include <stdbool.h>
2#include <stdint.h>
3
4#include "internal/internal.h"
5
6#define MAX_BITS 1024
7
8#define CONNLABEL_CFG "/etc/xtables/connlabel.conf"
9#define HASH_SIZE 64
10
11struct labelmap_bucket {
12	char *name;
13	unsigned int bit;
14	struct labelmap_bucket *next;
15};
16
17struct nfct_labelmap {
18	struct labelmap_bucket *map_name[HASH_SIZE];
19	unsigned int namecount;
20	char **bit_to_name;
21};
22
23static struct labelmap_bucket* label_map_bucket_alloc(const char *n, unsigned int b)
24{
25	struct labelmap_bucket *bucket;
26	char *name = strdup(n);
27
28	if (!name)
29		return NULL;
30
31	bucket = malloc(sizeof(*bucket));
32	if (!bucket) {
33		free(name);
34		return NULL;
35	}
36	bucket->name = name;
37	bucket->bit = b;
38	return bucket;
39}
40
41static unsigned int hash_name(const char *name)
42{
43	unsigned int hash = 0;
44
45	while (*name) {
46		hash = (hash << 5) - hash + *name;
47		name++;
48	}
49	return hash & (HASH_SIZE - 1);
50}
51
52int __labelmap_get_bit(struct nfct_labelmap *m, const char *name)
53{
54	unsigned int i = hash_name(name);
55	struct labelmap_bucket *list = m->map_name[i];
56
57	while (list) {
58		if (strcmp(name, list->name) == 0)
59			return list->bit;
60		list = list->next;
61	}
62	return -1;
63}
64
65const char *__labelmap_get_name(struct nfct_labelmap *m, unsigned int bit)
66{
67	if (bit < m->namecount)
68		return m->bit_to_name[bit] ? m->bit_to_name[bit] : "";
69	return NULL;
70}
71
72static int map_insert(struct nfct_labelmap *m, const char *n, unsigned int b)
73{
74	unsigned int i = hash_name(n);
75	struct labelmap_bucket *list = m->map_name[i];
76
77	while (list) {
78		if (strcmp(list->name, n) == 0)
79			return -1;
80		list = list->next;
81	}
82
83	list = label_map_bucket_alloc(n, b);
84	if (!list)
85		return -1;
86
87	if (m->map_name[i])
88		list->next = m->map_name[i];
89	else
90		list->next = NULL;
91	m->map_name[i] = list;
92	return 0;
93}
94
95static int is_space_posix(int c)
96{
97	return c == ' ' || c == '\f' || c == '\r' || c == '\t' || c == '\v';
98}
99
100static char *trim_label(char *label)
101{
102	char *end;
103
104	while (is_space_posix(*label))
105		label++;
106	end = strchr(label, '\n');
107	if (end)
108		*end = 0;
109	else
110		end = strchr(label, '\0');
111	end--;
112
113	while (end > label && is_space_posix(*end)) {
114		*end = 0;
115		end--;
116	}
117
118	return *label ? label : NULL;
119}
120
121static int
122xtables_parse_connlabel_numerical(const char *s, char **end)
123{
124	unsigned long value;
125
126	value = strtoul(s, end, 0);
127	if (value == 0 && s == *end)
128		return -1;
129	if (value >= MAX_BITS)
130		return -1;
131	return value;
132}
133
134static void free_list(struct labelmap_bucket *b)
135{
136	struct labelmap_bucket *tmp;
137
138	while (b) {
139		free(b->name);
140
141		tmp = b;
142		b = b->next;
143
144		free(tmp);
145	}
146}
147
148void __labelmap_destroy(struct nfct_labelmap *map)
149{
150	unsigned int i;
151	struct labelmap_bucket *b;
152
153	for (i = 0; i < HASH_SIZE; i++) {
154		b = map->map_name[i];
155		free_list(b);
156	}
157
158	free(map->bit_to_name);
159	free(map);
160}
161
162static void make_name_table(struct nfct_labelmap *m)
163{
164	struct labelmap_bucket *b;
165	unsigned int i;
166
167	for (i = 0; i < HASH_SIZE; i++) {
168		b = m->map_name[i];
169		while (b) {
170			m->bit_to_name[b->bit] = b->name;
171			b = b->next;
172		}
173	}
174}
175
176static struct nfct_labelmap *map_alloc(void)
177{
178	struct nfct_labelmap *map = malloc(sizeof(*map));
179	if (map) {
180		unsigned int i;
181		for (i = 0; i < HASH_SIZE; i++)
182			map->map_name[i] = NULL;
183		map->bit_to_name = NULL;
184	}
185	return map;
186}
187
188/*
189 * We will only accept alpha numerical labels; else
190 * parses might choke on output when label named
191 * "foo;<&bar" exists.  ASCII machines only.
192 *
193 * Avoids libc isalnum() etc. to avoid issues with locale
194 * settings.
195 */
196static bool label_is_sane(const char *label)
197{
198	for (;*label; label++) {
199		if (*label >= 'a' && *label <= 'z')
200			continue;
201		if (*label >= 'A' && *label <= 'Z')
202			continue;
203		if (*label >= '0' && *label <= '9')
204			continue;
205		if (*label == ' ' || *label == '-')
206			continue;
207		return false;
208	}
209	return true;
210}
211
212const char *__labels_get_path(void)
213{
214	return CONNLABEL_CFG;
215}
216
217struct nfct_labelmap *__labelmap_new(const char *name)
218{
219	struct nfct_labelmap *map;
220	char label[1024];
221	char *end;
222	FILE *fp;
223	int added = 0;
224	unsigned int maxbit = 0;
225	uint32_t bits_seen[MAX_BITS/32];
226
227	fp = fopen(name ? name : CONNLABEL_CFG, "re");
228	if (!fp)
229		return NULL;
230
231	memset(bits_seen, 0, sizeof(bits_seen));
232
233	map = map_alloc();
234	if (!map) {
235		fclose(fp);
236		return NULL;
237	}
238
239	while (fgets(label, sizeof(label), fp)) {
240		int bit;
241
242		if (label[0] == '#')
243			continue;
244
245		bit = xtables_parse_connlabel_numerical(label, &end);
246		if (bit < 0 || test_bit(bit, bits_seen))
247			continue;
248
249		end = trim_label(end);
250		if (!end)
251			continue;
252
253		if (label_is_sane(end) && map_insert(map, end, bit) == 0) {
254			added++;
255			if (maxbit < bit)
256				maxbit = bit;
257			set_bit(bit, bits_seen);
258		}
259	}
260
261	fclose(fp);
262
263	if (added) {
264		map->namecount = maxbit + 1;
265		map->bit_to_name = calloc(sizeof(char *), map->namecount);
266		if (!map->bit_to_name)
267			goto err;
268		make_name_table(map);
269		return map;
270	} else {
271		errno = 0;
272	}
273 err:
274	__labelmap_destroy(map);
275	return NULL;
276}
277