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