/* * dnsutl - utilities to make DNS easier to configure * Copyright (C) 1996, 1999, 2003, 2006, 2007 Peter Miller * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see * . */ #include #include #include #include #include #include #include #include #include #include static void check_list_of_one_or_more_machines(srrf_t *rp) { string_ty *tmp; size_t j; if (rp->arg.nstrings < 1) srrf_lex_error("at least one machine must be named"); for (j = 0; j < rp->arg.nstrings; ++j) { tmp = rp->arg.string[j]; rp->arg.string[j] = srrf_relative_to_absolute(tmp); str_free(tmp); } } /* ---------- bf ---------- boot file -------------------------*/ static void bf_check_arguments(srrf_t *rp) { if (rp->arg.string[0]->str_text[0] != '/') srrf_lex_error("the boot file must be an absolute path"); } /* ---------- bs ---------- boot file size/512 ----------------*/ /* ---------- cs ---------- cookie servers --------------------*/ /* ---------- ds ---------- DNS servers -----------------------*/ static void ds_check_arguments(srrf_t *rp) { check_list_of_one_or_more_machines(rp); srrf_lex_error ( "you may not specify \"bootp ds\" explicitly, it is generated " "from the \"in ns\" fields" ); } /* ---------- gw ---------- gateways (routers) ----------------*/ /* ---------- ha ---------- hardware (ether) address ----------*/ static void ha_check_arguments(srrf_t *rp) { check_list_of_one_or_more_machines(rp); srrf_lex_error ( "you may not specify \"bootp ha\" explicitly, it is generated " "from the \"ether a\" fields" ); } static int hex(int c) { static char digit[] = "0123456789ABCDEFabcdef"; static int value[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 10, 11, 12, 13, 14, 15, }; char *cp; cp = strchr(digit, c); if (!cp) { /* should not happen */ return 0; } return value[cp - digit]; } static string_ty * is_legal_ether_address(string_ty * s) { size_t nbytes; unsigned char buffer[6 + 1]; /* must be more than 6 */ const char *cp; cp = s->str_text; nbytes = 0; while (nbytes < SIZEOF(buffer)) { if (!isxdigit(cp[0])) return 0; if (isxdigit(cp[1])) { buffer[nbytes++] = (hex(cp[0]) << 4) | hex(cp[1]); cp += 2; } else buffer[nbytes++] = hex(*cp++); if (*cp != ':') break; ++cp; } if (*cp != 0 || nbytes != 6) return 0; return str_format ( "%2.2x%2.2x%2.2x%2.2x%2.2x%2.2x", buffer[0], buffer[1], buffer[2], buffer[3], buffer[4], buffer[5] ); } static void ha_bootp_print(srrf_t *rp, void *arg) { bootp_aux_ty *a; string_ty *s; srrf_t *rp2; a = arg; s = rp->arg.string[0]; rp2 = symtab_query(a->ether_a_stp, s); if (!rp2) { output_error_locn ( rp->file_name->str_text, rp->line_number, "host \"%s\" has no Ethernet address", s->str_text ); return; } s = is_legal_ether_address(rp2->arg.string[0]); if (!s) { output_error_locn ( rp2->file_name->str_text, rp2->line_number, "the string \"%s\" is not a legal Ethernet address", rp2->arg.string[0]->str_text ); return; } output_printf("%s=%s:", rp->type->name, s->str_text); str_free(s); } /* ---------- hd ---------- boot file home directory ----------*/ static void hd_check_arguments(srrf_t *rp) { if (rp->arg.string[0]->str_text[0] != '/') srrf_lex_error("the home directory must be an absolute path"); } /* ---------- hn ---------- send host name --------------------*/ static void hn_check_arguments(srrf_t *rp) { typedef struct table_ty table_ty; struct table_ty { const char *name; const char *prefer; }; static table_ty table[] = { { "false", "no" }, { "no", "no" }, { "true", "yes" }, { "yes", "yes" }, }; table_ty *tp; /* * find the name in the table */ for (tp = table; tp < ENDOF(table); ++tp) if (!strcasecmp(tp->name, rp->arg.string[0]->str_text)) break; if (tp >= ENDOF(table)) { srrf_lex_error ( "argument \"%s\" unrecognised, please use \"yes\" or \"no\"", rp->arg.string[0]->str_text ); return; } /* * replace the argument given with the preferred form from the table */ str_free(rp->arg.string[0]); rp->arg.string[0] = str_from_c(tp->prefer); } static void hn_bootp_print(srrf_t *rp, void *arg) { static string_ty *yes; (void)arg; if (!yes) yes = str_from_c("yes"); if (str_equal(rp->arg.string[0], yes)) output_printf("hn:"); else output_printf("hn@:"); } /* ---------- ht ---------- host hardware type ----------------*/ /* * The ht tag specifies the hardware type code as either an unsigned * decimal, octal, or hexadecimal integer or one of the following * symbolic names: * ethernet for 10Mb Ethernet * ether for 10Mb Ethernet * ethernet3 for 3Mb experimental Ethernet * ether3 for 3Mb experimental Ethernet * ieee802 for IEEE 802 networks * tr for IEEE 802 networks * token-ring for IEEE 802 networks * pronet for Proteon ProNET Token Ring * chaos for Chaos networks * arcnet for ARCNET networks * ax.25 for AX.25 Amateur Radio networks * * Note: don't allow the numbers - we don't know what the values mean. */ static void ht_check_arguments(srrf_t *rp) { typedef struct table_ty table_ty; struct table_ty { const char *name; const char *prefer; }; static table_ty table[] = { { "arcnet", "arcnet" }, { "ax.25", "ax.25" }, { "chaos", "chaos" }, { "ether", "ether" }, { "ether3", "ether3" }, { "ethernet", "ether" }, { "ethernet3", "ether3" }, { "ieee802", "ieee802" }, { "pronet", "pronet" }, { "token-ring", "ieee802" }, { "tr", "ieee802" }, }; table_ty *tp; /* * find the name in the table */ for (tp = table; tp < ENDOF(table); ++tp) if (!strcasecmp(tp->name, rp->arg.string[0]->str_text)) break; if (tp >= ENDOF(table)) { strlist_ty sl; string_ty *s; strlist_zero(&sl); for (tp = table; tp < ENDOF(table); ++tp) { s = str_from_c(tp->prefer); strlist_append_unique(&sl, s); str_free(s); } s = wl2str(&sl, 0, sl.nstrings, ", "); strlist_free(&sl); srrf_lex_error ( "argument \"%s\" unrecognised, legal values are: %s", rp->arg.string[0]->str_text, s->str_text ); str_free(s); return; } /* * replace the argument given with the preferred form from the table */ str_free(rp->arg.string[0]); rp->arg.string[0] = str_from_c(tp->prefer); } /* ---------- im ---------- Impress servers -------------------*/ /* probably obsolete */ /* ---------- ip ---------- host IP address -------------------*/ static void ip_check_arguments(srrf_t *rp) { check_list_of_one_or_more_machines(rp); srrf_lex_error ( "you may not specify \"ip\" explicitly, it is generated from " "the \"in a\" fields" ); } /* ---------- lg ---------- log servers -----------------------*/ /* ---------- lp ---------- lpr(1) servers --------------------*/ /* ---------- ns ---------- IEN-116 name servers --------------*/ /* probably obsolete */ /* ---------- rl ---------- resource location protocol servers -*/ /* probably obsolete */ /* ---------- sm ---------- host subnet mask ------------------*/ static void sm_check_arguments(srrf_t *rp) { long n; n = srrf_address(rp->arg.string[0]->str_text); #if (-1L != 0xFFFFFFFFL) /* sign extend */ n |= -(n & 0x80000000); #endif /* * check that the high bits are on, and that they are contiguous * * If all the high bits are all on, and the low bits are all off, * then (~n + 1) will only have a single bit on (-n == ~n + 1). * To find the least significant on bit of a number, use (n&-n). * To test if a number is a single bit number, use (n==(n&-n)). */ if (!n || -n != (n & -n)) { srrf_lex_error ( "the value \"%s\" is not a valid subnet mask", rp->arg.string[0]->str_text ); } } /* ---------- sr ---------- boot server -----------------------*/ /* ---------- tc ---------- table continuation ----------------*/ static void tc_check_arguments(srrf_t *rp) { char *cp; if (rp->arg.string[0]->str_length == 0) { yuck: srrf_lex_error("the \"tc\" name must be alphanumeric"); return; } cp = rp->arg.string[0]->str_text; if (!isalpha(*cp)) goto yuck; for (;;) { ++cp; if (!*cp) break; if (!isalnum(*cp) && *cp != '-') goto yuck; } } /* ---------- to ---------- time offset -----------------------*/ /* ---------- ts ---------- time servers ----------------------*/ /* ---------- vm ---------- vendor magic ----------------------*/ /* ----------------------------------------------------------------*/ static void check_number(srrf_t *rp) { size_t j; if (rp->arg.nstrings < 1) srrf_lex_error("at least one number must be given"); for (j = 0; j < rp->arg.nstrings; ++j) { string_ty *tmp; char *s; tmp = rp->arg.string[j]; if (!strcasecmp(tmp->str_text, "auto")) continue; if (!tmp->str_length) { bad_num: srrf_lex_error ( "argument \"%s\" is not a decimal number", tmp->str_text ); continue; } if (tmp->str_length >= 2 && tmp->str_text[0] == '0') goto bad_num; for (s = tmp->str_text; *s; ++s) if (!isdigit(*s)) goto bad_num; } } static int local_test(srrf_t *rp) { size_t j; for (j = 0; j < rp->arg.nstrings; ++j) { if (!srrf_private_domain_member(rp->arg.string[j])) return 0; } return 1; } static void abs_to_rel(srrf_t *rp) { size_t j; for (j = 0; j < rp->arg.nstrings; ++j) { string_ty *s; s = rp->arg.string[j]; rp->arg.string[j] = srrf_absolute_to_relative(s); str_free(s); } } static void bootp_print(srrf_t *rp, void *arg) { bootp_aux_ty *a; size_t j; a = arg; if (rp->arg.nstrings == 0) { output_printf("%s:", rp->type->name); return; } output_printf("%s=%s", rp->type->name, rp->arg.string[0]->str_text); for (j = 1; j < rp->arg.nstrings; ++j) output_printf(" %s", rp->arg.string[j]->str_text); output_printf(":"); } static void bootp_print_number(srrf_t *rp, void *arg) { (void)arg; output_printf("%s=%s:", rp->type->name, rp->arg.string[0]->str_text); } static void bootp_print_machines(srrf_t *rp, void *arg) { bootp_aux_ty *a; size_t j; string_ty *s; srrf_t *rp2; static srrf_type_ty *in_cname_type; if (!in_cname_type) { srrf_class_ty *in_class; in_class = srrf_class_by_name("in"); assert(in_class); in_cname_type = srrf_type_by_name(in_class, "cname"); assert(in_cname_type); } a = arg; output_printf("%s", rp->type->name); for (j = 0; j < rp->arg.nstrings; ++j) { s = rp->arg.string[j]; rp2 = symtab_query(a->in_a_stp, s); if (rp2 && rp2->type == in_cname_type) { s = rp2->arg.string[0]; rp2 = symtab_query(a->in_a_stp, s); if (rp2 && rp2->type == in_cname_type) { output_error_locn ( rp2->file_name->str_text, rp2->line_number, "host \"%s\" has no IP address", s->str_text ); return; } } if (rp2) s = rp2->arg.string[0]; else { output_error_locn ( rp->file_name->str_text, rp->line_number, "host \"%s\" has no IP address", s->str_text ); } output_printf("%s%s", (j ? " " : "="), s->str_text); } output_printf(":"); } static srrf_type_ty type[] = { { /* Bootfile */ "bf", 1, bf_check_arguments, 0, /* local_test */ 0, /* print */ 0, /* abs_to_rel */ bootp_print, /* aux1 */ 0, }, { /* Bootfile size in 512-octet blocks */ "bs", 1, check_number, 0, /* local_test */ 0, /* print */ 0, /* abs_to_rel */ bootp_print_number, /* aux1 */ 0, }, { /* Cookie server address list */ "cs", 0, /* number of arguments */ check_list_of_one_or_more_machines, local_test, 0, /* print */ abs_to_rel, bootp_print_machines, /* aux1 */ 0, }, { /* Domain name server address list */ "ds", 0, /* number of arguments */ ds_check_arguments, local_test, 0, /* print */ abs_to_rel, bootp_print_machines, /* aux1 */ 0, }, { /* Gateway address list */ "gw", 0, /* number of arguments */ check_list_of_one_or_more_machines, local_test, 0, /* print */ abs_to_rel, bootp_print_machines, /* aux1 */ 0, }, { /* Host hardware address */ "ha", 1, /* number_of_arguments */ ha_check_arguments, 0, /* local_test */ 0, /* print */ abs_to_rel, ha_bootp_print, /* aux1 */ 0, }, { /* Bootfile home directory */ "hd", 1, hd_check_arguments, 0, /* local_test */ 0, /* print */ 0, /* abs_to_rel */ bootp_print, /* aux1 */ 0, }, { /* Send hostname */ "hn", 1, hn_check_arguments, 0, /* local_test */ 0, /* print */ 0, /* abs_to_rel */ hn_bootp_print, /* aux1 */ 0, }, { /* Host hardware type (see Assigned Numbers RFC) */ "ht", 1, ht_check_arguments, 0, /* local_test */ 0, /* print */ 0, /* abs_to_rel */ bootp_print, /* aux1 */ 0, }, { /* Impress server address list */ "im", 0, /* number of arguments */ check_list_of_one_or_more_machines, local_test, 0, /* print */ abs_to_rel, bootp_print_machines, /* aux1 */ 0, }, { /* Host IP address */ "ip", 1, /* number_of_arguments */ ip_check_arguments, local_test, 0, /* print */ abs_to_rel, bootp_print_machines, /* aux1 */ 0, }, { /* Log server address list */ "lg", 0, /* number of arguments */ check_list_of_one_or_more_machines, local_test, 0, /* print */ abs_to_rel, bootp_print_machines, /* aux1 */ 0, }, { /* LPR server address list */ "lp", 1, 0, /* check_arguments */ 0, /* local_test */ 0, /* print */ 0, /* abs_to_rel */ bootp_print, /* aux1 */ 0, }, { /* IEN-116 name server address list */ "ns", 0, /* number of arguments */ check_list_of_one_or_more_machines, local_test, 0, /* print */ abs_to_rel, bootp_print_machines, /* aux1 */ 0, }, { /* Resource location protocol server address list */ "rl", 0, /* number of arguments */ check_list_of_one_or_more_machines, local_test, 0, /* print */ abs_to_rel, bootp_print_machines, /* aux1 */ 0, }, { /* Host subnet mask */ "sm", 1, sm_check_arguments, 0, /* local_test */ 0, /* print */ 0, /* abs_to_rel */ bootp_print, /* aux1 */ 0, }, { /* Server to boot from (IP address) */ "sr", 1, check_list_of_one_or_more_machines, local_test, 0, /* print */ abs_to_rel, bootp_print_machines, /* aux1 */ 0, }, { /* * Table continuation * (points to similar "template" host entry) */ "tc", 1, tc_check_arguments, 0, /* local_test */ 0, /* print */ 0, /* abs_to_rel */ bootp_print, /* aux1 */ 0, }, { /* Time offset in seconds from UTC */ "to", 1, check_number, 0, /* local_test */ 0, /* print */ 0, /* abs_to_rel */ bootp_print_number, /* aux1 */ 0, }, { /* Time server address list */ "ts", 0, /* number of arguments */ check_list_of_one_or_more_machines, local_test, 0, /* print */ abs_to_rel, bootp_print_machines, /* aux1 */ 0, }, { /* Vendor magic cookie selector */ "vm", 1, 0, /* check_arguments */ 0, /* local_test */ 0, /* print */ 0, /* abs_to_rel */ bootp_print, /* aux1 */ 0, }, { /* extensions */ "T*", 0, 0, /* check_arguments */ 0, /* local_test */ 0, /* print */ 0, /* abs_to_rel */ 0, /* aux1 */ 0, } }; /* * This symbol describes the class. * It should be the only symbol exported from this file. */ srrf_class_ty srrf_class_bootp = { "bootp", type, SIZEOF(type) };