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 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 } 277