Annotation of /trunk/mkinitrd-magellan/busybox/networking/udhcp/files.c
Parent Directory | Revision Log
Revision 816 -
(hide annotations)
(download)
Fri Apr 24 18:33:46 2009 UTC (15 years, 1 month ago) by niro
File MIME type: text/plain
File size: 10902 byte(s)
Fri Apr 24 18:33:46 2009 UTC (15 years, 1 month ago) by niro
File MIME type: text/plain
File size: 10902 byte(s)
-updated to busybox-1.13.4
1 | niro | 532 | /* vi: set sw=4 ts=4: */ |
2 | /* | ||
3 | * files.c -- DHCP server file manipulation * | ||
4 | * Rewrite by Russ Dill <Russ.Dill@asu.edu> July 2001 | ||
5 | */ | ||
6 | |||
7 | #include <netinet/ether.h> | ||
8 | |||
9 | #include "common.h" | ||
10 | #include "dhcpd.h" | ||
11 | #include "options.h" | ||
12 | |||
13 | |||
14 | niro | 816 | /* on these functions, make sure your datatype matches */ |
15 | niro | 532 | static int read_ip(const char *line, void *arg) |
16 | { | ||
17 | niro | 816 | len_and_sockaddr *lsa; |
18 | niro | 532 | |
19 | niro | 816 | lsa = host_and_af2sockaddr(line, 0, AF_INET); |
20 | if (!lsa) | ||
21 | return 0; | ||
22 | *(uint32_t*)arg = lsa->u.sin.sin_addr.s_addr; | ||
23 | free(lsa); | ||
24 | return 1; | ||
25 | niro | 532 | } |
26 | |||
27 | static int read_mac(const char *line, void *arg) | ||
28 | { | ||
29 | struct ether_addr *temp_ether_addr; | ||
30 | |||
31 | niro | 816 | temp_ether_addr = ether_aton_r(line, (struct ether_addr *)arg); |
32 | niro | 532 | if (temp_ether_addr == NULL) |
33 | niro | 816 | return 0; |
34 | return 1; | ||
35 | niro | 532 | } |
36 | |||
37 | |||
38 | static int read_str(const char *line, void *arg) | ||
39 | { | ||
40 | char **dest = arg; | ||
41 | |||
42 | free(*dest); | ||
43 | *dest = xstrdup(line); | ||
44 | return 1; | ||
45 | } | ||
46 | |||
47 | |||
48 | static int read_u32(const char *line, void *arg) | ||
49 | { | ||
50 | niro | 816 | *(uint32_t*)arg = bb_strtou32(line, NULL, 10); |
51 | niro | 532 | return errno == 0; |
52 | } | ||
53 | |||
54 | |||
55 | static int read_yn(const char *line, void *arg) | ||
56 | { | ||
57 | char *dest = arg; | ||
58 | |||
59 | niro | 816 | if (!strcasecmp("yes", line)) { |
60 | niro | 532 | *dest = 1; |
61 | niro | 816 | return 1; |
62 | } | ||
63 | if (!strcasecmp("no", line)) { | ||
64 | niro | 532 | *dest = 0; |
65 | niro | 816 | return 1; |
66 | } | ||
67 | return 0; | ||
68 | niro | 532 | } |
69 | |||
70 | |||
71 | /* find option 'code' in opt_list */ | ||
72 | niro | 816 | struct option_set* FAST_FUNC find_option(struct option_set *opt_list, uint8_t code) |
73 | niro | 532 | { |
74 | while (opt_list && opt_list->data[OPT_CODE] < code) | ||
75 | opt_list = opt_list->next; | ||
76 | |||
77 | niro | 816 | if (opt_list && opt_list->data[OPT_CODE] == code) |
78 | return opt_list; | ||
79 | return NULL; | ||
80 | niro | 532 | } |
81 | |||
82 | |||
83 | /* add an option to the opt_list */ | ||
84 | static void attach_option(struct option_set **opt_list, | ||
85 | const struct dhcp_option *option, char *buffer, int length) | ||
86 | { | ||
87 | struct option_set *existing, *new, **curr; | ||
88 | |||
89 | existing = find_option(*opt_list, option->code); | ||
90 | niro | 816 | if (!existing) { |
91 | DEBUG("Attaching option %02x to list", option->code); | ||
92 | niro | 532 | |
93 | niro | 816 | #if ENABLE_FEATURE_UDHCP_RFC3397 |
94 | if ((option->flags & TYPE_MASK) == OPTION_STR1035) | ||
95 | /* reuse buffer and length for RFC1035-formatted string */ | ||
96 | buffer = (char *)dname_enc(NULL, 0, buffer, &length); | ||
97 | #endif | ||
98 | |||
99 | niro | 532 | /* make a new option */ |
100 | niro | 816 | new = xmalloc(sizeof(*new)); |
101 | niro | 532 | new->data = xmalloc(length + 2); |
102 | new->data[OPT_CODE] = option->code; | ||
103 | new->data[OPT_LEN] = length; | ||
104 | memcpy(new->data + 2, buffer, length); | ||
105 | |||
106 | curr = opt_list; | ||
107 | while (*curr && (*curr)->data[OPT_CODE] < option->code) | ||
108 | curr = &(*curr)->next; | ||
109 | |||
110 | new->next = *curr; | ||
111 | *curr = new; | ||
112 | niro | 816 | #if ENABLE_FEATURE_UDHCP_RFC3397 |
113 | if ((option->flags & TYPE_MASK) == OPTION_STR1035 && buffer != NULL) | ||
114 | free(buffer); | ||
115 | #endif | ||
116 | return; | ||
117 | niro | 532 | } |
118 | niro | 816 | |
119 | /* add it to an existing option */ | ||
120 | DEBUG("Attaching option %02x to existing member of list", option->code); | ||
121 | if (option->flags & OPTION_LIST) { | ||
122 | #if ENABLE_FEATURE_UDHCP_RFC3397 | ||
123 | if ((option->flags & TYPE_MASK) == OPTION_STR1035) | ||
124 | /* reuse buffer and length for RFC1035-formatted string */ | ||
125 | buffer = (char *)dname_enc(existing->data + 2, | ||
126 | existing->data[OPT_LEN], buffer, &length); | ||
127 | #endif | ||
128 | if (existing->data[OPT_LEN] + length <= 255) { | ||
129 | existing->data = xrealloc(existing->data, | ||
130 | existing->data[OPT_LEN] + length + 3); | ||
131 | if ((option->flags & TYPE_MASK) == OPTION_STRING) { | ||
132 | /* ' ' can bring us to 256 - bad */ | ||
133 | if (existing->data[OPT_LEN] + length >= 255) | ||
134 | return; | ||
135 | /* add space separator between STRING options in a list */ | ||
136 | existing->data[existing->data[OPT_LEN] + 2] = ' '; | ||
137 | existing->data[OPT_LEN]++; | ||
138 | } | ||
139 | memcpy(existing->data + existing->data[OPT_LEN] + 2, buffer, length); | ||
140 | existing->data[OPT_LEN] += length; | ||
141 | } /* else, ignore the data, we could put this in a second option in the future */ | ||
142 | #if ENABLE_FEATURE_UDHCP_RFC3397 | ||
143 | if ((option->flags & TYPE_MASK) == OPTION_STR1035 && buffer != NULL) | ||
144 | free(buffer); | ||
145 | #endif | ||
146 | } /* else, ignore the new data */ | ||
147 | niro | 532 | } |
148 | |||
149 | |||
150 | /* read a dhcp option and add it to opt_list */ | ||
151 | static int read_opt(const char *const_line, void *arg) | ||
152 | { | ||
153 | struct option_set **opt_list = arg; | ||
154 | char *opt, *val, *endptr; | ||
155 | niro | 816 | char *line; |
156 | niro | 532 | const struct dhcp_option *option; |
157 | niro | 816 | int retval, length, idx; |
158 | char buffer[8] ALIGNED(4); | ||
159 | niro | 532 | uint16_t *result_u16 = (uint16_t *) buffer; |
160 | uint32_t *result_u32 = (uint32_t *) buffer; | ||
161 | |||
162 | /* Cheat, the only const line we'll actually get is "" */ | ||
163 | line = (char *) const_line; | ||
164 | opt = strtok(line, " \t="); | ||
165 | niro | 816 | if (!opt) |
166 | return 0; | ||
167 | niro | 532 | |
168 | niro | 816 | idx = index_in_strings(dhcp_option_strings, opt); /* NB: was strcasecmp! */ |
169 | if (idx < 0) | ||
170 | return 0; | ||
171 | option = &dhcp_options[idx]; | ||
172 | niro | 532 | |
173 | niro | 816 | retval = 0; |
174 | niro | 532 | do { |
175 | val = strtok(NULL, ", \t"); | ||
176 | if (!val) break; | ||
177 | niro | 816 | length = dhcp_option_lengths[option->flags & TYPE_MASK]; |
178 | niro | 532 | retval = 0; |
179 | opt = buffer; /* new meaning for variable opt */ | ||
180 | switch (option->flags & TYPE_MASK) { | ||
181 | case OPTION_IP: | ||
182 | retval = read_ip(val, buffer); | ||
183 | break; | ||
184 | case OPTION_IP_PAIR: | ||
185 | retval = read_ip(val, buffer); | ||
186 | niro | 816 | val = strtok(NULL, ", \t/-"); |
187 | if (!val) | ||
188 | retval = 0; | ||
189 | if (retval) | ||
190 | retval = read_ip(val, buffer + 4); | ||
191 | niro | 532 | break; |
192 | case OPTION_STRING: | ||
193 | niro | 816 | #if ENABLE_FEATURE_UDHCP_RFC3397 |
194 | case OPTION_STR1035: | ||
195 | #endif | ||
196 | niro | 532 | length = strlen(val); |
197 | if (length > 0) { | ||
198 | if (length > 254) length = 254; | ||
199 | opt = val; | ||
200 | retval = 1; | ||
201 | } | ||
202 | break; | ||
203 | case OPTION_BOOLEAN: | ||
204 | retval = read_yn(val, buffer); | ||
205 | break; | ||
206 | case OPTION_U8: | ||
207 | buffer[0] = strtoul(val, &endptr, 0); | ||
208 | retval = (endptr[0] == '\0'); | ||
209 | break; | ||
210 | niro | 816 | /* htonX are macros in older libc's, using temp var |
211 | * in code below for safety */ | ||
212 | /* TODO: use bb_strtoX? */ | ||
213 | case OPTION_U16: { | ||
214 | unsigned long tmp = strtoul(val, &endptr, 0); | ||
215 | *result_u16 = htons(tmp); | ||
216 | retval = (endptr[0] == '\0' /*&& tmp < 0x10000*/); | ||
217 | niro | 532 | break; |
218 | niro | 816 | } |
219 | case OPTION_S16: { | ||
220 | long tmp = strtol(val, &endptr, 0); | ||
221 | *result_u16 = htons(tmp); | ||
222 | niro | 532 | retval = (endptr[0] == '\0'); |
223 | break; | ||
224 | niro | 816 | } |
225 | case OPTION_U32: { | ||
226 | unsigned long tmp = strtoul(val, &endptr, 0); | ||
227 | *result_u32 = htonl(tmp); | ||
228 | niro | 532 | retval = (endptr[0] == '\0'); |
229 | break; | ||
230 | niro | 816 | } |
231 | case OPTION_S32: { | ||
232 | long tmp = strtol(val, &endptr, 0); | ||
233 | *result_u32 = htonl(tmp); | ||
234 | niro | 532 | retval = (endptr[0] == '\0'); |
235 | break; | ||
236 | niro | 816 | } |
237 | niro | 532 | default: |
238 | break; | ||
239 | } | ||
240 | if (retval) | ||
241 | attach_option(opt_list, option, opt, length); | ||
242 | } while (retval && option->flags & OPTION_LIST); | ||
243 | return retval; | ||
244 | } | ||
245 | |||
246 | static int read_staticlease(const char *const_line, void *arg) | ||
247 | { | ||
248 | char *line; | ||
249 | char *mac_string; | ||
250 | char *ip_string; | ||
251 | uint8_t *mac_bytes; | ||
252 | uint32_t *ip; | ||
253 | |||
254 | /* Allocate memory for addresses */ | ||
255 | mac_bytes = xmalloc(sizeof(unsigned char) * 8); | ||
256 | ip = xmalloc(sizeof(uint32_t)); | ||
257 | |||
258 | /* Read mac */ | ||
259 | line = (char *) const_line; | ||
260 | mac_string = strtok(line, " \t"); | ||
261 | read_mac(mac_string, mac_bytes); | ||
262 | |||
263 | /* Read ip */ | ||
264 | ip_string = strtok(NULL, " \t"); | ||
265 | read_ip(ip_string, ip); | ||
266 | |||
267 | addStaticLease(arg, mac_bytes, ip); | ||
268 | |||
269 | niro | 816 | if (ENABLE_UDHCP_DEBUG) printStaticLeases(arg); |
270 | niro | 532 | |
271 | return 1; | ||
272 | } | ||
273 | |||
274 | |||
275 | niro | 816 | struct config_keyword { |
276 | const char *keyword; | ||
277 | int (*handler)(const char *line, void *var); | ||
278 | void *var; | ||
279 | const char *def; | ||
280 | }; | ||
281 | |||
282 | niro | 532 | static const struct config_keyword keywords[] = { |
283 | niro | 816 | /* keyword handler variable address default */ |
284 | {"start", read_ip, &(server_config.start_ip), "192.168.0.20"}, | ||
285 | {"end", read_ip, &(server_config.end_ip), "192.168.0.254"}, | ||
286 | {"interface", read_str, &(server_config.interface), "eth0"}, | ||
287 | /* Avoid "max_leases value not sane" warning by setting default | ||
288 | * to default_end_ip - default_start_ip + 1: */ | ||
289 | {"max_leases", read_u32, &(server_config.max_leases), "235"}, | ||
290 | {"remaining", read_yn, &(server_config.remaining), "yes"}, | ||
291 | {"auto_time", read_u32, &(server_config.auto_time), "7200"}, | ||
292 | {"decline_time", read_u32, &(server_config.decline_time), "3600"}, | ||
293 | {"conflict_time",read_u32, &(server_config.conflict_time),"3600"}, | ||
294 | {"offer_time", read_u32, &(server_config.offer_time), "60"}, | ||
295 | {"min_lease", read_u32, &(server_config.min_lease), "60"}, | ||
296 | {"lease_file", read_str, &(server_config.lease_file), LEASES_FILE}, | ||
297 | {"pidfile", read_str, &(server_config.pidfile), "/var/run/udhcpd.pid"}, | ||
298 | {"siaddr", read_ip, &(server_config.siaddr), "0.0.0.0"}, | ||
299 | /* keywords with no defaults must be last! */ | ||
300 | {"option", read_opt, &(server_config.options), ""}, | ||
301 | {"opt", read_opt, &(server_config.options), ""}, | ||
302 | {"notify_file", read_str, &(server_config.notify_file), ""}, | ||
303 | {"sname", read_str, &(server_config.sname), ""}, | ||
304 | {"boot_file", read_str, &(server_config.boot_file), ""}, | ||
305 | {"static_lease", read_staticlease, &(server_config.static_leases), ""}, | ||
306 | /* ADDME: static lease */ | ||
307 | niro | 532 | }; |
308 | niro | 816 | enum { KWS_WITH_DEFAULTS = ARRAY_SIZE(keywords) - 6 }; |
309 | niro | 532 | |
310 | niro | 816 | void FAST_FUNC read_config(const char *file) |
311 | niro | 532 | { |
312 | niro | 816 | parser_t *parser; |
313 | const struct config_keyword *k; | ||
314 | unsigned i; | ||
315 | char *token[2]; | ||
316 | niro | 532 | |
317 | niro | 816 | for (i = 0; i < KWS_WITH_DEFAULTS; i++) |
318 | keywords[i].handler(keywords[i].def, keywords[i].var); | ||
319 | niro | 532 | |
320 | niro | 816 | parser = config_open(file); |
321 | while (config_read(parser, token, 2, 2, "# \t", PARSE_NORMAL)) { | ||
322 | for (k = keywords, i = 0; i < ARRAY_SIZE(keywords); k++, i++) { | ||
323 | if (!strcasecmp(token[0], k->keyword)) { | ||
324 | if (!k->handler(token[1], k->var)) { | ||
325 | bb_error_msg("can't parse line %u in %s", | ||
326 | parser->lineno, file); | ||
327 | niro | 532 | /* reset back to the default value */ |
328 | niro | 816 | k->handler(k->def, k->var); |
329 | niro | 532 | } |
330 | niro | 816 | break; |
331 | } | ||
332 | } | ||
333 | niro | 532 | } |
334 | niro | 816 | config_close(parser); |
335 | |||
336 | server_config.start_ip = ntohl(server_config.start_ip); | ||
337 | server_config.end_ip = ntohl(server_config.end_ip); | ||
338 | niro | 532 | } |
339 | |||
340 | |||
341 | niro | 816 | void FAST_FUNC write_leases(void) |
342 | niro | 532 | { |
343 | int fp; | ||
344 | unsigned i; | ||
345 | time_t curr = time(0); | ||
346 | unsigned long tmp_time; | ||
347 | |||
348 | niro | 816 | fp = open_or_warn(server_config.lease_file, O_WRONLY|O_CREAT|O_TRUNC); |
349 | niro | 532 | if (fp < 0) { |
350 | return; | ||
351 | } | ||
352 | |||
353 | for (i = 0; i < server_config.max_leases; i++) { | ||
354 | if (leases[i].yiaddr != 0) { | ||
355 | |||
356 | /* screw with the time in the struct, for easier writing */ | ||
357 | tmp_time = leases[i].expires; | ||
358 | |||
359 | if (server_config.remaining) { | ||
360 | if (lease_expired(&(leases[i]))) | ||
361 | leases[i].expires = 0; | ||
362 | else leases[i].expires -= curr; | ||
363 | } /* else stick with the time we got */ | ||
364 | leases[i].expires = htonl(leases[i].expires); | ||
365 | // FIXME: error check?? | ||
366 | full_write(fp, &leases[i], sizeof(leases[i])); | ||
367 | |||
368 | /* then restore it when done */ | ||
369 | leases[i].expires = tmp_time; | ||
370 | } | ||
371 | } | ||
372 | close(fp); | ||
373 | |||
374 | if (server_config.notify_file) { | ||
375 | niro | 816 | // TODO: vfork-based child creation |
376 | niro | 532 | char *cmd = xasprintf("%s %s", server_config.notify_file, server_config.lease_file); |
377 | system(cmd); | ||
378 | free(cmd); | ||
379 | } | ||
380 | } | ||
381 | |||
382 | |||
383 | niro | 816 | void FAST_FUNC read_leases(const char *file) |
384 | niro | 532 | { |
385 | int fp; | ||
386 | niro | 816 | unsigned i; |
387 | niro | 532 | struct dhcpOfferedAddr lease; |
388 | |||
389 | niro | 816 | fp = open_or_warn(file, O_RDONLY); |
390 | niro | 532 | if (fp < 0) { |
391 | return; | ||
392 | } | ||
393 | |||
394 | niro | 816 | i = 0; |
395 | niro | 532 | while (i < server_config.max_leases |
396 | && full_read(fp, &lease, sizeof(lease)) == sizeof(lease) | ||
397 | ) { | ||
398 | /* ADDME: is it a static lease */ | ||
399 | niro | 816 | uint32_t y = ntohl(lease.yiaddr); |
400 | if (y >= server_config.start_ip && y <= server_config.end_ip) { | ||
401 | niro | 532 | lease.expires = ntohl(lease.expires); |
402 | niro | 816 | if (!server_config.remaining) |
403 | lease.expires -= time(NULL); | ||
404 | niro | 532 | if (!(add_lease(lease.chaddr, lease.yiaddr, lease.expires))) { |
405 | bb_error_msg("too many leases while loading %s", file); | ||
406 | break; | ||
407 | } | ||
408 | i++; | ||
409 | } | ||
410 | } | ||
411 | DEBUG("Read %d leases", i); | ||
412 | close(fp); | ||
413 | } |