libnetfilter_conntrack  1.0.6
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  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 
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 
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 
95 static int is_space_posix(int c)
96 {
97  return c == ' ' || c == '\f' || c == '\r' || c == '\t' || c == '\v';
98 }
99 
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
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 
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 
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 
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 
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  */
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 
212 const char *__labels_get_path(void)
213 {
214  return CONNLABEL_CFG;
215 }
216 
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 }