blob: 57043acc9615cc659bfc9258401eac548ef1f059 [file] [log] [blame]
Holger Hans Peter Freyther85d3b342013-06-14 19:10:28 +02001/* Handling for loading a re-write file/database */
2/*
3 * (C) 2013 by On-Waves
4 * (C) 2013 by Holger Hans Peter Freyther <zecke@selfish.org>
5 * All Rights Reserved
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU Affero General Public License as published by
9 * the Free Software Foundation; either version 3 of the License, or
10 * (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU Affero General Public License for more details.
16 *
17 * You should have received a copy of the GNU Affero General Public License
18 * along with this program. If not, see <http://www.gnu.org/licenses/>.
19 *
20 */
21
22#include <openbsc/nat_rewrite_trie.h>
23#include <openbsc/debug.h>
24
25#include <osmocom/core/talloc.h>
26#include <osmocom/core/utils.h>
27
28#include <assert.h>
29#include <stdio.h>
30#include <string.h>
31#include <ctype.h>
32
33#define CHECK_IS_DIGIT_OR_FAIL(prefix, pos) \
34 if (!isdigit(prefix[pos]) && prefix[pos] != '+') { \
35 LOGP(DNAT, LOGL_ERROR, \
36 "Prefix(%s) contains non ascii text at(%d=%c)\n", \
37 prefix, pos, prefix[pos]); \
38 goto fail; \
39 }
40#define TO_INT(c) \
41 ((c) == '+' ? 10 : ((c - '0') % 10))
42
43static void insert_rewrite_node(struct nat_rewrite_rule *rule, struct nat_rewrite *root)
44{
45 struct nat_rewrite_rule *new = &root->rule;
46
47 const size_t len = strlen(rule->prefix);
48 int i;
49
50 if (len == 0) {
51 LOGP(DNAT, LOGL_ERROR, "An empty prefix does not make sense.\n");
52 goto fail;
53 }
54
55 for (i = 0; i < len - 1; ++i) {
56 int pos;
57
58 /* check if the input is valid */
59 CHECK_IS_DIGIT_OR_FAIL(rule->prefix, i);
60
61 /* check if the next node is already valid */
62 pos = TO_INT(rule->prefix[i]);
63 if (!new->rules[pos]) {
64 new->rules[pos] = talloc_zero(root, struct nat_rewrite_rule);
65 if (!new->rules[pos]) {
66 LOGP(DNAT, LOGL_ERROR,
67 "Failed to allocate memory.\n");
68 goto fail;
69 }
70
71 new->rules[pos]->empty = 1;
72 }
73
74 /* we continue here */
75 new = new->rules[pos];
76 }
77
78 /* new now points to the place where we want to add it */
79 int pos;
80
81 /* check if the input is valid */
82 CHECK_IS_DIGIT_OR_FAIL(rule->prefix, (len - 1));
83
84 /* check if the next node is already valid */
85 pos = TO_INT(rule->prefix[len - 1]);
86 if (!new->rules[pos])
87 new->rules[pos] = rule;
88 else if (new->rules[pos]->empty) {
89 /* copy over entries */
90 new->rules[pos]->empty = 0;
91 memcpy(new->rules[pos]->prefix, rule->prefix, sizeof(rule->prefix));
92 memcpy(new->rules[pos]->rewrite, rule->rewrite, sizeof(rule->rewrite));
93 talloc_free(rule);
94 } else {
95 LOGP(DNAT, LOGL_ERROR,
96 "Prefix(%s) is already installed\n", rule->prefix);
97 goto fail;
98 }
99
100 root->prefixes += 1;
101 return;
102
103fail:
104 talloc_free(rule);
105 return;
106}
107
108static void handle_line(struct nat_rewrite *rewrite, char *line)
109{
110 char *split;
111 struct nat_rewrite_rule *rule;
112 size_t size_prefix, size_end, len;
113
114
115 /* Find the ',' in the line */
116 len = strlen(line);
117 split = strstr(line, ",");
118 if (!split) {
119 LOGP(DNAT, LOGL_ERROR, "Line doesn't contain ','\n");
120 return;
121 }
122
123 /* Check if there is space for the rewrite rule */
124 size_prefix = split - line;
125 if (len - size_prefix <= 2) {
126 LOGP(DNAT, LOGL_ERROR, "No rewrite available.\n");
127 return;
128 }
129
130 /* Continue after the ',' to the end */
131 split = &line[size_prefix + 1];
132 size_end = strlen(split) - 1;
133
134 /* Check if both strings can fit into the static array */
135 if (size_prefix > sizeof(rule->prefix) - 1) {
136 LOGP(DNAT, LOGL_ERROR,
137 "Prefix is too long with %zu\n", size_prefix);
138 return;
139 }
140
141 if (size_end > sizeof(rule->rewrite) - 1) {
142 LOGP(DNAT, LOGL_ERROR,
143 "Rewrite is too long with %zu on %s\n",
144 size_end, &line[size_prefix + 1]);
145 return;
146 }
147
148 /* Now create the entry and insert it into the trie */
149 rule = talloc_zero(rewrite, struct nat_rewrite_rule);
150 if (!rule) {
151 LOGP(DNAT, LOGL_ERROR, "Can not allocate memory\n");
152 return;
153 }
154
155 memcpy(rule->prefix, line, size_prefix);
156 assert(size_prefix < sizeof(rule->prefix));
157 rule->prefix[size_prefix] = '\0';
158
159 memcpy(rule->rewrite, split, size_end);
160 assert(size_end < sizeof(rule->rewrite));
161 rule->rewrite[size_end] = '\0';
162
163 /* now insert and balance the tree */
164 insert_rewrite_node(rule, rewrite);
165}
166
167struct nat_rewrite *nat_rewrite_parse(void *ctx, const char *filename)
168{
169 FILE *file;
170 char *line = NULL;
171 size_t n = 2342;
172 struct nat_rewrite *res;
173
174 file = fopen(filename, "r");
175 if (!file)
176 return NULL;
177
178 res = talloc_zero(ctx, struct nat_rewrite);
179 if (!res) {
180 fclose(file);
181 return NULL;
182 }
183
184 /* mark the root as empty */
185 res->rule.empty = 1;
186
187 while (getline(&line, &n, file) != -1) {
188 handle_line(res, line);
189 }
190
191 free(line);
192 fclose(file);
193 return res;
194}
195
196/**
197 * Simple find that tries to do a longest match...
198 */
199struct nat_rewrite_rule *nat_rewrite_lookup(struct nat_rewrite *rewrite,
200 const char *prefix)
201{
202 struct nat_rewrite_rule *rule = &rewrite->rule;
203 struct nat_rewrite_rule *last = NULL;
204 const int len = OSMO_MIN(strlen(prefix), (sizeof(rule->prefix) - 1));
205 int i;
206
207 for (i = 0; rule && i < len; ++i) {
208 int pos;
209
210 CHECK_IS_DIGIT_OR_FAIL(prefix, i);
211 pos = TO_INT(prefix[i]);
212
213 rule = rule->rules[pos];
214 if (rule && !rule->empty)
215 last = rule;
216 }
217
218 return last;
219
220fail:
221 return NULL;
222}
223
224static void nat_rewrite_dump_rec(struct nat_rewrite_rule *rule)
225{
226 int i;
227 if (!rule->empty)
228 printf("%s,%s\n", rule->prefix, rule->rewrite);
229
230 for (i = 0; i < ARRAY_SIZE(rule->rules); ++i) {
231 if (!rule->rules[i])
232 continue;
233 nat_rewrite_dump_rec(rule->rules[i]);
234 }
235}
236
237void nat_rewrite_dump(struct nat_rewrite *rewrite)
238{
239 nat_rewrite_dump_rec(&rewrite->rule);
240}