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