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