/* Handling for loading a re-write file/database */ /* * (C) 2013 by On-Waves * (C) 2013 by Holger Hans Peter Freyther * All Rights Reserved * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as published by * the Free Software Foundation; either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . * */ #include #include #include #include #include #include #include #include #include #define CHECK_IS_DIGIT_OR_FAIL(prefix, pos) \ if (!isdigit(prefix[pos]) && prefix[pos] != '+') { \ LOGP(DNAT, LOGL_ERROR, \ "Prefix(%s) contains non ascii text at(%d=%c)\n", \ prefix, pos, prefix[pos]); \ goto fail; \ } #define TO_INT(c) \ ((c) == '+' ? 10 : ((c - '0') % 10)) static void insert_rewrite_node(struct nat_rewrite_rule *rule, struct nat_rewrite *root) { struct nat_rewrite_rule *new = &root->rule; const int len = strlen(rule->prefix); int i; if (len <= 0) { LOGP(DNAT, LOGL_ERROR, "An empty prefix does not make sense.\n"); goto fail; } for (i = 0; i < len - 1; ++i) { int pos; /* check if the input is valid */ CHECK_IS_DIGIT_OR_FAIL(rule->prefix, i); /* check if the next node is already valid */ pos = TO_INT(rule->prefix[i]); if (!new->rules[pos]) { new->rules[pos] = talloc_zero(root, struct nat_rewrite_rule); if (!new->rules[pos]) { LOGP(DNAT, LOGL_ERROR, "Failed to allocate memory.\n"); goto fail; } new->rules[pos]->empty = 1; } /* we continue here */ new = new->rules[pos]; } /* new now points to the place where we want to add it */ int pos; /* check if the input is valid */ CHECK_IS_DIGIT_OR_FAIL(rule->prefix, (len - 1)); /* check if the next node is already valid */ pos = TO_INT(rule->prefix[len - 1]); if (!new->rules[pos]) new->rules[pos] = rule; else if (new->rules[pos]->empty) { /* copy over entries */ new->rules[pos]->empty = 0; memcpy(new->rules[pos]->prefix, rule->prefix, sizeof(rule->prefix)); memcpy(new->rules[pos]->rewrite, rule->rewrite, sizeof(rule->rewrite)); talloc_free(rule); } else { LOGP(DNAT, LOGL_ERROR, "Prefix(%s) is already installed\n", rule->prefix); goto fail; } root->prefixes += 1; return; fail: talloc_free(rule); return; } static void handle_line(struct nat_rewrite *rewrite, char *line) { char *split; struct nat_rewrite_rule *rule; size_t size_prefix, size_end, len; /* Find the ',' in the line */ len = strlen(line); split = strstr(line, ","); if (!split) { LOGP(DNAT, LOGL_ERROR, "Line doesn't contain ','\n"); return; } /* Check if there is space for the rewrite rule */ size_prefix = split - line; if (len - size_prefix <= 2) { LOGP(DNAT, LOGL_ERROR, "No rewrite available.\n"); return; } /* Continue after the ',' to the end */ split = &line[size_prefix + 1]; size_end = strlen(split) - 1; /* Check if both strings can fit into the static array */ if (size_prefix > sizeof(rule->prefix) - 1) { LOGP(DNAT, LOGL_ERROR, "Prefix is too long with %zu\n", size_prefix); return; } if (size_end > sizeof(rule->rewrite) - 1) { LOGP(DNAT, LOGL_ERROR, "Rewrite is too long with %zu on %s\n", size_end, &line[size_prefix + 1]); return; } /* Now create the entry and insert it into the trie */ rule = talloc_zero(rewrite, struct nat_rewrite_rule); if (!rule) { LOGP(DNAT, LOGL_ERROR, "Can not allocate memory\n"); return; } memcpy(rule->prefix, line, size_prefix); assert(size_prefix < sizeof(rule->prefix)); rule->prefix[size_prefix] = '\0'; memcpy(rule->rewrite, split, size_end); assert(size_end < sizeof(rule->rewrite)); rule->rewrite[size_end] = '\0'; /* now insert and balance the tree */ insert_rewrite_node(rule, rewrite); } struct nat_rewrite *nat_rewrite_parse(void *ctx, const char *filename) { FILE *file; char *line = NULL; size_t n = 2342; struct nat_rewrite *res; file = fopen(filename, "r"); if (!file) return NULL; res = talloc_zero(ctx, struct nat_rewrite); if (!res) { fclose(file); return NULL; } /* mark the root as empty */ res->rule.empty = 1; while (getline(&line, &n, file) != -1) { handle_line(res, line); } free(line); fclose(file); return res; } /** * Simple find that tries to do a longest match... */ struct nat_rewrite_rule *nat_rewrite_lookup(struct nat_rewrite *rewrite, const char *prefix) { struct nat_rewrite_rule *rule = &rewrite->rule; struct nat_rewrite_rule *last = NULL; const int len = OSMO_MIN(strlen(prefix), (sizeof(rule->prefix) - 1)); int i; for (i = 0; rule && i < len; ++i) { int pos; CHECK_IS_DIGIT_OR_FAIL(prefix, i); pos = TO_INT(prefix[i]); rule = rule->rules[pos]; if (rule && !rule->empty) last = rule; } return last; fail: return NULL; } static void nat_rewrite_dump_rec(struct nat_rewrite_rule *rule) { int i; if (!rule->empty) printf("%s,%s\n", rule->prefix, rule->rewrite); for (i = 0; i < ARRAY_SIZE(rule->rules); ++i) { if (!rule->rules[i]) continue; nat_rewrite_dump_rec(rule->rules[i]); } } void nat_rewrite_dump(struct nat_rewrite *rewrite) { nat_rewrite_dump_rec(&rewrite->rule); } static void nat_rewrite_dump_rec_vty(struct vty *vty, struct nat_rewrite_rule *rule) { int i; if (!rule->empty) vty_out(vty, "%s,%s%s", rule->prefix, rule->rewrite, VTY_NEWLINE); for (i = 0; i < ARRAY_SIZE(rule->rules); ++i) { if (!rule->rules[i]) continue; nat_rewrite_dump_rec_vty(vty, rule->rules[i]); } } void nat_rewrite_dump_vty(struct vty *vty, struct nat_rewrite *rewrite) { nat_rewrite_dump_rec_vty(vty, &rewrite->rule); }