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 
11 struct labelmap_bucket {
12 	char *name;
13 	unsigned int bit;
14 	struct labelmap_bucket *next;
15 };
16 
17 struct nfct_labelmap {
18 	struct labelmap_bucket *map_name[HASH_SIZE];
19 	unsigned int namecount;
20 	char **bit_to_name;
21 };
22 
label_map_bucket_alloc(const char * n,unsigned int b)23 static 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 
hash_name(const char * name)41 static 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 
__labelmap_get_bit(struct nfct_labelmap * m,const char * name)52 int __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 
__labelmap_get_name(struct nfct_labelmap * m,unsigned int bit)65 const 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 
map_insert(struct nfct_labelmap * m,const char * n,unsigned int b)72 static 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 
is_space_posix(int c)95 static int is_space_posix(int c)
96 {
97 	return c == ' ' || c == '\f' || c == '\r' || c == '\t' || c == '\v';
98 }
99 
trim_label(char * label)100 static 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 
121 static int
xtables_parse_connlabel_numerical(const char * s,char ** end)122 xtables_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 
free_list(struct labelmap_bucket * b)134 static 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 
__labelmap_destroy(struct nfct_labelmap * map)148 void __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 
make_name_table(struct nfct_labelmap * m)162 static 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 
map_alloc(void)176 static 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  */
label_is_sane(const char * label)196 static 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 
__labels_get_path(void)212 const char *__labels_get_path(void)
213 {
214 	return CONNLABEL_CFG;
215 }
216 
__labelmap_new(const char * name)217 struct 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