diff options
Diffstat (limited to 'networking/udhcp/files.c')
-rw-r--r-- | networking/udhcp/files.c | 395 |
1 files changed, 395 insertions, 0 deletions
diff --git a/networking/udhcp/files.c b/networking/udhcp/files.c new file mode 100644 index 000000000..5e399e1f8 --- /dev/null +++ b/networking/udhcp/files.c | |||
@@ -0,0 +1,395 @@ | |||
1 | /* 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 | /* | ||
15 | * Domain names may have 254 chars, and string options can be 254 | ||
16 | * chars long. However, 80 bytes will be enough for most, and won't | ||
17 | * hog up memory. If you have a special application, change it | ||
18 | */ | ||
19 | #define READ_CONFIG_BUF_SIZE 80 | ||
20 | |||
21 | /* on these functions, make sure you datatype matches */ | ||
22 | static int read_ip(const char *line, void *arg) | ||
23 | { | ||
24 | struct in_addr *addr = arg; | ||
25 | struct hostent *host; | ||
26 | int retval = 1; | ||
27 | |||
28 | if (!inet_aton(line, addr)) { | ||
29 | host = gethostbyname(line); | ||
30 | if (host) | ||
31 | addr->s_addr = *((unsigned long *) host->h_addr_list[0]); | ||
32 | else retval = 0; | ||
33 | } | ||
34 | return retval; | ||
35 | } | ||
36 | |||
37 | static int read_mac(const char *line, void *arg) | ||
38 | { | ||
39 | uint8_t *mac_bytes = arg; | ||
40 | struct ether_addr *temp_ether_addr; | ||
41 | int retval = 1; | ||
42 | |||
43 | temp_ether_addr = ether_aton(line); | ||
44 | |||
45 | if (temp_ether_addr == NULL) | ||
46 | retval = 0; | ||
47 | else | ||
48 | memcpy(mac_bytes, temp_ether_addr, 6); | ||
49 | |||
50 | return retval; | ||
51 | } | ||
52 | |||
53 | |||
54 | static int read_str(const char *line, void *arg) | ||
55 | { | ||
56 | char **dest = arg; | ||
57 | |||
58 | free(*dest); | ||
59 | *dest = strdup(line); | ||
60 | |||
61 | return 1; | ||
62 | } | ||
63 | |||
64 | |||
65 | static int read_u32(const char *line, void *arg) | ||
66 | { | ||
67 | *((uint32_t*)arg) = bb_strtou32(line, NULL, 10); | ||
68 | return errno == 0; | ||
69 | } | ||
70 | |||
71 | |||
72 | static int read_yn(const char *line, void *arg) | ||
73 | { | ||
74 | char *dest = arg; | ||
75 | int retval = 1; | ||
76 | |||
77 | if (!strcasecmp("yes", line)) | ||
78 | *dest = 1; | ||
79 | else if (!strcasecmp("no", line)) | ||
80 | *dest = 0; | ||
81 | else retval = 0; | ||
82 | |||
83 | return retval; | ||
84 | } | ||
85 | |||
86 | |||
87 | /* find option 'code' in opt_list */ | ||
88 | struct option_set *find_option(struct option_set *opt_list, char code) | ||
89 | { | ||
90 | while (opt_list && opt_list->data[OPT_CODE] < code) | ||
91 | opt_list = opt_list->next; | ||
92 | |||
93 | if (opt_list && opt_list->data[OPT_CODE] == code) return opt_list; | ||
94 | else return NULL; | ||
95 | } | ||
96 | |||
97 | |||
98 | /* add an option to the opt_list */ | ||
99 | static void attach_option(struct option_set **opt_list, | ||
100 | const struct dhcp_option *option, char *buffer, int length) | ||
101 | { | ||
102 | struct option_set *existing, *new, **curr; | ||
103 | |||
104 | /* add it to an existing option */ | ||
105 | existing = find_option(*opt_list, option->code); | ||
106 | if (existing) { | ||
107 | DEBUG("Attaching option %s to existing member of list", option->name); | ||
108 | if (option->flags & OPTION_LIST) { | ||
109 | if (existing->data[OPT_LEN] + length <= 255) { | ||
110 | existing->data = realloc(existing->data, | ||
111 | existing->data[OPT_LEN] + length + 2); | ||
112 | memcpy(existing->data + existing->data[OPT_LEN] + 2, buffer, length); | ||
113 | existing->data[OPT_LEN] += length; | ||
114 | } /* else, ignore the data, we could put this in a second option in the future */ | ||
115 | } /* else, ignore the new data */ | ||
116 | } else { | ||
117 | DEBUG("Attaching option %s to list", option->name); | ||
118 | |||
119 | /* make a new option */ | ||
120 | new = xmalloc(sizeof(struct option_set)); | ||
121 | new->data = xmalloc(length + 2); | ||
122 | new->data[OPT_CODE] = option->code; | ||
123 | new->data[OPT_LEN] = length; | ||
124 | memcpy(new->data + 2, buffer, length); | ||
125 | |||
126 | curr = opt_list; | ||
127 | while (*curr && (*curr)->data[OPT_CODE] < option->code) | ||
128 | curr = &(*curr)->next; | ||
129 | |||
130 | new->next = *curr; | ||
131 | *curr = new; | ||
132 | } | ||
133 | } | ||
134 | |||
135 | |||
136 | /* read a dhcp option and add it to opt_list */ | ||
137 | static int read_opt(const char *const_line, void *arg) | ||
138 | { | ||
139 | struct option_set **opt_list = arg; | ||
140 | char *opt, *val, *endptr; | ||
141 | const struct dhcp_option *option; | ||
142 | int retval = 0, length; | ||
143 | char buffer[8]; | ||
144 | char *line; | ||
145 | uint16_t *result_u16 = (uint16_t *) buffer; | ||
146 | uint32_t *result_u32 = (uint32_t *) buffer; | ||
147 | |||
148 | /* Cheat, the only const line we'll actually get is "" */ | ||
149 | line = (char *) const_line; | ||
150 | opt = strtok(line, " \t="); | ||
151 | if (!opt) return 0; | ||
152 | |||
153 | option = dhcp_options; | ||
154 | while (1) { | ||
155 | if (!option->code) | ||
156 | return 0; | ||
157 | if (!strcasecmp(option->name, opt)) | ||
158 | break; | ||
159 | option++; | ||
160 | } | ||
161 | |||
162 | do { | ||
163 | val = strtok(NULL, ", \t"); | ||
164 | if (!val) break; | ||
165 | length = option_lengths[option->flags & TYPE_MASK]; | ||
166 | retval = 0; | ||
167 | opt = buffer; /* new meaning for variable opt */ | ||
168 | switch (option->flags & TYPE_MASK) { | ||
169 | case OPTION_IP: | ||
170 | retval = read_ip(val, buffer); | ||
171 | break; | ||
172 | case OPTION_IP_PAIR: | ||
173 | retval = read_ip(val, buffer); | ||
174 | if (!(val = strtok(NULL, ", \t/-"))) retval = 0; | ||
175 | if (retval) retval = read_ip(val, buffer + 4); | ||
176 | break; | ||
177 | case OPTION_STRING: | ||
178 | length = strlen(val); | ||
179 | if (length > 0) { | ||
180 | if (length > 254) length = 254; | ||
181 | opt = val; | ||
182 | retval = 1; | ||
183 | } | ||
184 | break; | ||
185 | case OPTION_BOOLEAN: | ||
186 | retval = read_yn(val, buffer); | ||
187 | break; | ||
188 | case OPTION_U8: | ||
189 | buffer[0] = strtoul(val, &endptr, 0); | ||
190 | retval = (endptr[0] == '\0'); | ||
191 | break; | ||
192 | case OPTION_U16: | ||
193 | *result_u16 = htons(strtoul(val, &endptr, 0)); | ||
194 | retval = (endptr[0] == '\0'); | ||
195 | break; | ||
196 | case OPTION_S16: | ||
197 | *result_u16 = htons(strtol(val, &endptr, 0)); | ||
198 | retval = (endptr[0] == '\0'); | ||
199 | break; | ||
200 | case OPTION_U32: | ||
201 | *result_u32 = htonl(strtoul(val, &endptr, 0)); | ||
202 | retval = (endptr[0] == '\0'); | ||
203 | break; | ||
204 | case OPTION_S32: | ||
205 | *result_u32 = htonl(strtol(val, &endptr, 0)); | ||
206 | retval = (endptr[0] == '\0'); | ||
207 | break; | ||
208 | default: | ||
209 | break; | ||
210 | } | ||
211 | if (retval) | ||
212 | attach_option(opt_list, option, opt, length); | ||
213 | } while (retval && option->flags & OPTION_LIST); | ||
214 | return retval; | ||
215 | } | ||
216 | |||
217 | static int read_staticlease(const char *const_line, void *arg) | ||
218 | { | ||
219 | char *line; | ||
220 | char *mac_string; | ||
221 | char *ip_string; | ||
222 | uint8_t *mac_bytes; | ||
223 | uint32_t *ip; | ||
224 | |||
225 | |||
226 | /* Allocate memory for addresses */ | ||
227 | mac_bytes = xmalloc(sizeof(unsigned char) * 8); | ||
228 | ip = xmalloc(sizeof(uint32_t)); | ||
229 | |||
230 | /* Read mac */ | ||
231 | line = (char *) const_line; | ||
232 | mac_string = strtok(line, " \t"); | ||
233 | read_mac(mac_string, mac_bytes); | ||
234 | |||
235 | /* Read ip */ | ||
236 | ip_string = strtok(NULL, " \t"); | ||
237 | read_ip(ip_string, ip); | ||
238 | |||
239 | addStaticLease(arg, mac_bytes, ip); | ||
240 | |||
241 | if (ENABLE_FEATURE_UDHCP_DEBUG) printStaticLeases(arg); | ||
242 | |||
243 | return 1; | ||
244 | } | ||
245 | |||
246 | |||
247 | static const struct config_keyword keywords[] = { | ||
248 | /* keyword handler variable address default */ | ||
249 | {"start", read_ip, &(server_config.start), "192.168.0.20"}, | ||
250 | {"end", read_ip, &(server_config.end), "192.168.0.254"}, | ||
251 | {"interface", read_str, &(server_config.interface), "eth0"}, | ||
252 | {"option", read_opt, &(server_config.options), ""}, | ||
253 | {"opt", read_opt, &(server_config.options), ""}, | ||
254 | {"max_leases", read_u32, &(server_config.max_leases), "254"}, | ||
255 | {"remaining", read_yn, &(server_config.remaining), "yes"}, | ||
256 | {"auto_time", read_u32, &(server_config.auto_time), "7200"}, | ||
257 | {"decline_time",read_u32, &(server_config.decline_time),"3600"}, | ||
258 | {"conflict_time",read_u32,&(server_config.conflict_time),"3600"}, | ||
259 | {"offer_time", read_u32, &(server_config.offer_time), "60"}, | ||
260 | {"min_lease", read_u32, &(server_config.min_lease), "60"}, | ||
261 | {"lease_file", read_str, &(server_config.lease_file), LEASES_FILE}, | ||
262 | {"pidfile", read_str, &(server_config.pidfile), "/var/run/udhcpd.pid"}, | ||
263 | {"notify_file", read_str, &(server_config.notify_file), ""}, | ||
264 | {"siaddr", read_ip, &(server_config.siaddr), "0.0.0.0"}, | ||
265 | {"sname", read_str, &(server_config.sname), ""}, | ||
266 | {"boot_file", read_str, &(server_config.boot_file), ""}, | ||
267 | {"static_lease",read_staticlease, &(server_config.static_leases), ""}, | ||
268 | /*ADDME: static lease */ | ||
269 | {"", NULL, NULL, ""} | ||
270 | }; | ||
271 | |||
272 | |||
273 | int read_config(const char *file) | ||
274 | { | ||
275 | FILE *in; | ||
276 | char buffer[READ_CONFIG_BUF_SIZE], *token, *line; | ||
277 | int i, lm = 0; | ||
278 | |||
279 | for (i = 0; keywords[i].keyword[0]; i++) | ||
280 | if (keywords[i].def[0]) | ||
281 | keywords[i].handler(keywords[i].def, keywords[i].var); | ||
282 | |||
283 | in = fopen(file, "r"); | ||
284 | if (!in) { | ||
285 | bb_error_msg("cannot open config file: %s", file); | ||
286 | return 0; | ||
287 | } | ||
288 | |||
289 | while (fgets(buffer, READ_CONFIG_BUF_SIZE, in)) { | ||
290 | char debug_orig[READ_CONFIG_BUF_SIZE]; | ||
291 | char *p; | ||
292 | |||
293 | lm++; | ||
294 | p = strchr(buffer, '\n'); | ||
295 | if (p) *p = '\0'; | ||
296 | if (ENABLE_FEATURE_UDHCP_DEBUG) strcpy(debug_orig, buffer); | ||
297 | p = strchr(buffer, '#'); | ||
298 | if (p) *p = '\0'; | ||
299 | |||
300 | if (!(token = strtok(buffer, " \t"))) continue; | ||
301 | if (!(line = strtok(NULL, ""))) continue; | ||
302 | |||
303 | /* eat leading whitespace */ | ||
304 | line = skip_whitespace(line); | ||
305 | /* eat trailing whitespace */ | ||
306 | i = strlen(line) - 1; | ||
307 | while (i >= 0 && isspace(line[i])) | ||
308 | line[i--] = '\0'; | ||
309 | |||
310 | for (i = 0; keywords[i].keyword[0]; i++) | ||
311 | if (!strcasecmp(token, keywords[i].keyword)) | ||
312 | if (!keywords[i].handler(line, keywords[i].var)) { | ||
313 | bb_error_msg("cannot parse line %d of %s", lm, file); | ||
314 | if (ENABLE_FEATURE_UDHCP_DEBUG) | ||
315 | bb_error_msg("cannot parse '%s'", debug_orig); | ||
316 | /* reset back to the default value */ | ||
317 | keywords[i].handler(keywords[i].def, keywords[i].var); | ||
318 | } | ||
319 | } | ||
320 | fclose(in); | ||
321 | return 1; | ||
322 | } | ||
323 | |||
324 | |||
325 | void write_leases(void) | ||
326 | { | ||
327 | int fp; | ||
328 | unsigned i; | ||
329 | time_t curr = time(0); | ||
330 | unsigned long tmp_time; | ||
331 | |||
332 | fp = open(server_config.lease_file, O_WRONLY|O_CREAT|O_TRUNC, 0666); | ||
333 | if (fp < 0) { | ||
334 | bb_error_msg("cannot open %s for writing", server_config.lease_file); | ||
335 | return; | ||
336 | } | ||
337 | |||
338 | for (i = 0; i < server_config.max_leases; i++) { | ||
339 | if (leases[i].yiaddr != 0) { | ||
340 | |||
341 | /* screw with the time in the struct, for easier writing */ | ||
342 | tmp_time = leases[i].expires; | ||
343 | |||
344 | if (server_config.remaining) { | ||
345 | if (lease_expired(&(leases[i]))) | ||
346 | leases[i].expires = 0; | ||
347 | else leases[i].expires -= curr; | ||
348 | } /* else stick with the time we got */ | ||
349 | leases[i].expires = htonl(leases[i].expires); | ||
350 | // FIXME: error check?? | ||
351 | full_write(fp, &leases[i], sizeof(leases[i])); | ||
352 | |||
353 | /* then restore it when done */ | ||
354 | leases[i].expires = tmp_time; | ||
355 | } | ||
356 | } | ||
357 | close(fp); | ||
358 | |||
359 | if (server_config.notify_file) { | ||
360 | char *cmd = xasprintf("%s %s", server_config.notify_file, server_config.lease_file); | ||
361 | system(cmd); | ||
362 | free(cmd); | ||
363 | } | ||
364 | } | ||
365 | |||
366 | |||
367 | void read_leases(const char *file) | ||
368 | { | ||
369 | int fp; | ||
370 | unsigned int i = 0; | ||
371 | struct dhcpOfferedAddr lease; | ||
372 | |||
373 | fp = open(file, O_RDONLY); | ||
374 | if (fp < 0) { | ||
375 | bb_error_msg("cannot open %s for reading", file); | ||
376 | return; | ||
377 | } | ||
378 | |||
379 | while (i < server_config.max_leases | ||
380 | && full_read(fp, &lease, sizeof(lease)) == sizeof(lease) | ||
381 | ) { | ||
382 | /* ADDME: is it a static lease */ | ||
383 | if (lease.yiaddr >= server_config.start && lease.yiaddr <= server_config.end) { | ||
384 | lease.expires = ntohl(lease.expires); | ||
385 | if (!server_config.remaining) lease.expires -= time(0); | ||
386 | if (!(add_lease(lease.chaddr, lease.yiaddr, lease.expires))) { | ||
387 | bb_error_msg("too many leases while loading %s", file); | ||
388 | break; | ||
389 | } | ||
390 | i++; | ||
391 | } | ||
392 | } | ||
393 | DEBUG("Read %d leases", i); | ||
394 | close(fp); | ||
395 | } | ||