/*
* 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)
};