#include <sys/errno.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/file.h>
#include <sys/time.h>
#include <netinet/in.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#include <assert.h>
#include <syslog.h>
#include "bootp.h"
#include "hash.h"
#include "hwaddr.h"
#include "lookup.h"
#include "readfile.h"
#include "report.h"
#include "tzone.h"
#include "bootpd.h"
#define HASHTABLESIZE 257
#define HTYPE_DIRECT 0
#define SUCCESS 0
#define E_END_OF_ENTRY (-1)
#define E_SYNTAX_ERROR (-2)
#define E_UNKNOWN_SYMBOL (-3)
#define E_BAD_IPADDR (-4)
#define E_BAD_HWADDR (-5)
#define E_BAD_LONGWORD (-6)
#define E_BAD_HWATYPE (-7)
#define E_BAD_PATHNAME (-8)
#define E_BAD_VALUE (-9)
#define SYM_NULL 0
#define SYM_BOOTFILE 1
#define SYM_COOKIE_SERVER 2
#define SYM_DOMAIN_SERVER 3
#define SYM_GATEWAY 4
#define SYM_HWADDR 5
#define SYM_HOMEDIR 6
#define SYM_HTYPE 7
#define SYM_IMPRESS_SERVER 8
#define SYM_IPADDR 9
#define SYM_LOG_SERVER 10
#define SYM_LPR_SERVER 11
#define SYM_NAME_SERVER 12
#define SYM_RLP_SERVER 13
#define SYM_SUBNET_MASK 14
#define SYM_TIME_OFFSET 15
#define SYM_TIME_SERVER 16
#define SYM_VENDOR_MAGIC 17
#define SYM_SIMILAR_ENTRY 18
#define SYM_NAME_SWITCH 19
#define SYM_BOOTSIZE 20
#define SYM_BOOT_SERVER 22
#define SYM_TFTPDIR 23
#define SYM_DUMP_FILE 24
#define SYM_DOMAIN_NAME 25
#define SYM_SWAP_SERVER 26
#define SYM_ROOT_PATH 27
#define SYM_EXTEN_FILE 28
#define SYM_REPLY_ADDR 29
#define SYM_NIS_DOMAIN 30
#define SYM_NIS_SERVER 31
#define SYM_NTP_SERVER 32
#define SYM_EXEC_FILE 33
#define SYM_MSG_SIZE 34
#define SYM_MIN_WAIT 35
#define OP_ADDITION 1
#define OP_DELETION 2
#define OP_BOOLEAN 3
#define MAXINADDRS 16
#define MAXBUFLEN 256
#define MAXENTRYLEN 2048
struct symbolmap {
char *symbol;
int symbolcode;
};
struct htypename {
char *name;
byte htype;
};
PRIVATE int nhosts;
PRIVATE int nentries;
PRIVATE int32 modtime = 0;
PRIVATE char *current_hostname;
PRIVATE char current_tagname[8];
PRIVATE struct symbolmap symbol_list[] = {
{"bf", SYM_BOOTFILE},
{"bs", SYM_BOOTSIZE},
{"cs", SYM_COOKIE_SERVER},
{"df", SYM_DUMP_FILE},
{"dn", SYM_DOMAIN_NAME},
{"ds", SYM_DOMAIN_SERVER},
{"ef", SYM_EXTEN_FILE},
{"ex", SYM_EXEC_FILE},
{"gw", SYM_GATEWAY},
{"ha", SYM_HWADDR},
{"hd", SYM_HOMEDIR},
{"hn", SYM_NAME_SWITCH},
{"ht", SYM_HTYPE},
{"im", SYM_IMPRESS_SERVER},
{"ip", SYM_IPADDR},
{"lg", SYM_LOG_SERVER},
{"lp", SYM_LPR_SERVER},
{"ms", SYM_MSG_SIZE},
{"mw", SYM_MIN_WAIT},
{"ns", SYM_NAME_SERVER},
{"nt", SYM_NTP_SERVER},
{"ra", SYM_REPLY_ADDR},
{"rl", SYM_RLP_SERVER},
{"rp", SYM_ROOT_PATH},
{"sa", SYM_BOOT_SERVER},
{"sm", SYM_SUBNET_MASK},
{"sw", SYM_SWAP_SERVER},
{"tc", SYM_SIMILAR_ENTRY},
{"td", SYM_TFTPDIR},
{"to", SYM_TIME_OFFSET},
{"ts", SYM_TIME_SERVER},
{"vm", SYM_VENDOR_MAGIC},
{"yd", SYM_NIS_DOMAIN},
{"ys", SYM_NIS_SERVER},
};
PRIVATE struct htypename htnamemap[] = {
{"ethernet", HTYPE_ETHERNET},
{"ethernet3", HTYPE_EXP_ETHERNET},
{"ether", HTYPE_ETHERNET},
{"ether3", HTYPE_EXP_ETHERNET},
{"ieee802", HTYPE_IEEE802},
{"tr", HTYPE_IEEE802},
{"token-ring", HTYPE_IEEE802},
{"pronet", HTYPE_PRONET},
{"chaos", HTYPE_CHAOS},
{"arcnet", HTYPE_ARCNET},
{"ax.25", HTYPE_AX25},
{"direct", HTYPE_DIRECT},
{"serial", HTYPE_DIRECT},
{"slip", HTYPE_DIRECT},
{"ppp", HTYPE_DIRECT}
};
boolean nmcmp(hash_datum *, hash_datum *);
PRIVATE void
adjust(char **);
PRIVATE void
del_string(struct shared_string *);
PRIVATE void
del_bindata(struct shared_bindata *);
PRIVATE void
del_iplist(struct in_addr_list *);
PRIVATE void
eat_whitespace(char **);
PRIVATE int
eval_symbol(char **, struct host *);
PRIVATE void
fill_defaults(struct host *, char **);
PRIVATE void
free_host(hash_datum *);
PRIVATE struct in_addr_list *
get_addresses(char **);
PRIVATE struct shared_string *
get_shared_string(char **);
PRIVATE char *
get_string(char **, char *, u_int *);
PRIVATE u_int32
get_u_long(char **);
PRIVATE boolean
goodname(char *);
PRIVATE boolean
hwinscmp(hash_datum *, hash_datum *);
PRIVATE int
interp_byte(char **, byte *);
PRIVATE void
makelower(char *);
PRIVATE boolean
nullcmp(hash_datum *, hash_datum *);
PRIVATE int
process_entry(struct host *, char *);
PRIVATE int
process_generic(char **, struct shared_bindata **, u_int);
PRIVATE byte *
prs_haddr(char **, u_int);
PRIVATE int
prs_inetaddr(char **, u_int32 *);
PRIVATE void
read_entry(FILE *, char *, u_int *);
PRIVATE char *
smalloc(u_int);
u_char vm_cmu[4] = VM_CMU;
u_char vm_rfc1048[4] = VM_RFC1048;
hash_tbl *hwhashtable;
hash_tbl *iphashtable;
hash_tbl *nmhashtable;
void
rdtab_init(void)
{
hwhashtable = hash_Init(HASHTABLESIZE);
iphashtable = hash_Init(HASHTABLESIZE);
nmhashtable = hash_Init(HASHTABLESIZE);
if (!(hwhashtable && iphashtable && nmhashtable)) {
report(LOG_ERR, "Unable to allocate hash tables.");
exit(1);
}
}
void
readtab(int force)
{
struct host *hp;
FILE *fp;
struct stat st;
unsigned hashcode, buflen;
static char buffer[MAXENTRYLEN];
if (stat(bootptab, &st) < 0) {
report(LOG_ERR, "stat on \"%s\": %s",
bootptab, get_errmsg());
return;
}
#ifdef DEBUG
if (debug > 3) {
char timestr[28];
strcpy(timestr, ctime(&(st.st_mtime)));
timestr[24] = '\0';
report(LOG_INFO, "bootptab mtime: %s",
timestr);
}
#endif
if ((force == 0) &&
(st.st_mtime == modtime) &&
st.st_nlink) {
return;
}
if (debug)
report(LOG_INFO, "reading %s\"%s\"",
(modtime != 0L) ? "new " : "",
bootptab);
if ((fp = fopen(bootptab, "r")) == NULL) {
report(LOG_ERR, "error opening \"%s\": %s", bootptab, get_errmsg());
return;
}
if (fstat(fileno(fp), &st) < 0) {
report(LOG_ERR, "fstat: %s", get_errmsg());
fclose(fp);
return;
}
modtime = st.st_mtime;
hash_Reset(hwhashtable, free_host);
hash_Reset(iphashtable, free_host);
hash_Reset(nmhashtable, free_host);
nhosts = 0;
nentries = 0;
while (TRUE) {
buflen = sizeof(buffer);
read_entry(fp, buffer, &buflen);
if (buflen == 0) {
break;
}
hp = (struct host *) smalloc(sizeof(struct host));
bzero((char *) hp, sizeof(*hp));
if (process_entry(hp, buffer) < 0) {
hp->linkcount = 1;
free_host((hash_datum *) hp);
continue;
}
if (goodname(hp->hostname->string)) {
char *hn = hp->hostname->string;
u_int32 value;
if (hp->flags.iaddr == 0) {
if (lookup_ipa(hn, &value)) {
report(LOG_ERR, "can not get IP addr for %s", hn);
report(LOG_ERR, "(dummy names should start with '.')");
} else {
hp->iaddr.s_addr = value;
hp->flags.iaddr = TRUE;
}
}
if (hp->flags.subnet_mask == 0) {
if (lookup_netmask(hp->iaddr.s_addr, &value)) {
report(LOG_ERR, "can not get netmask for %s", hn);
} else {
hp->subnet_mask.s_addr = value;
hp->flags.subnet_mask = TRUE;
}
}
}
if (hp->flags.iaddr) {
nhosts++;
}
if (hp->flags.htype && hp->flags.haddr) {
hp->linkcount++;
hashcode = hash_HashFunction(hp->haddr, haddrlength(hp->htype));
if (hash_Insert(hwhashtable, hashcode, hwinscmp, hp, hp) < 0) {
report(LOG_NOTICE, "duplicate %s address: %s",
netname(hp->htype),
haddrtoa(hp->haddr, haddrlength(hp->htype)));
free_host((hash_datum *) hp);
continue;
}
}
if (hp->flags.iaddr) {
hashcode = hash_HashFunction((u_char *) & (hp->iaddr.s_addr), 4);
if (hash_Insert(iphashtable, hashcode, nullcmp, hp, hp) < 0) {
report(LOG_ERR,
"hash_Insert() failed on IP address insertion");
} else {
hp->linkcount++;
}
}
hashcode = hash_HashFunction((u_char *) hp->hostname->string,
strlen(hp->hostname->string));
if (hash_Insert(nmhashtable, hashcode, nullcmp,
hp->hostname->string, hp) < 0) {
report(LOG_ERR,
"hash_Insert() failed on insertion of hostname: \"%s\"",
hp->hostname->string);
} else {
hp->linkcount++;
}
nentries++;
}
fclose(fp);
if (debug)
report(LOG_INFO, "read %d entries (%d hosts) from \"%s\"",
nentries, nhosts, bootptab);
return;
}
PRIVATE void
read_entry(FILE *fp, char *buffer, unsigned *bufsiz)
{
int c, length;
length = 0;
top:
c = fgetc(fp);
if (c < 0) {
goto done;
}
if (isspace(c)) {
goto top;
}
if (c == '#') {
while (TRUE) {
c = fgetc(fp);
if (c < 0) {
goto done;
}
if (c == '\n') {
goto top;
}
}
}
ungetc(c, fp);
mainloop:
c = fgetc(fp);
switch (c) {
case EOF:
case '\n':
goto done;
case '\\':
c = fgetc(fp);
if (c < 0) {
goto done;
}
*buffer++ = c;
length++;
if (length < *bufsiz - 1) {
goto mainloop;
} else {
goto done;
}
case '"':
*buffer++ = '"';
length++;
if (length >= *bufsiz - 1) {
goto done;
}
while (TRUE) {
c = fgetc(fp);
switch (c) {
case EOF:
goto done;
case '"':
*buffer++ = '"';
length++;
if (length < *bufsiz - 1) {
goto mainloop;
} else {
goto done;
}
case '\\':
if ((c = fgetc(fp)) < 0) {
goto done;
}
default:
*buffer++ = c;
length++;
if (length >= *bufsiz - 1) {
goto done;
}
}
}
case ':':
*buffer++ = c;
length++;
if (length >= *bufsiz - 1) {
goto done;
}
do {
c = fgetc(fp);
if ((c < 0) || (c == '\n')) {
goto done;
}
} while (isspace(c));
if (c == '\\') {
c = fgetc(fp);
if (c < 0) {
goto done;
}
if (c == '\n') {
goto top;
}
}
default:
*buffer++ = c;
length++;
if (length >= *bufsiz - 1) {
goto done;
}
}
goto mainloop;
done:
*buffer = '\0';
*bufsiz = length;
}
PRIVATE int
process_entry(struct host *host, char *src)
{
int retval;
char *msg;
if (!host || *src == '\0') {
return -1;
}
host->hostname = get_shared_string(&src);
#if 0
if (!goodname(host->hostname->string)) {
report(LOG_ERR, "bad hostname: \"%s\"", host->hostname->string);
del_string(host->hostname);
return -1;
}
#endif
current_hostname = host->hostname->string;
adjust(&src);
while (TRUE) {
retval = eval_symbol(&src, host);
if (retval == SUCCESS) {
adjust(&src);
continue;
}
if (retval == E_END_OF_ENTRY) {
return 0;
}
switch (retval) {
case E_SYNTAX_ERROR:
msg = "bad syntax";
break;
case E_UNKNOWN_SYMBOL:
msg = "unknown symbol";
break;
case E_BAD_IPADDR:
msg = "bad INET address";
break;
case E_BAD_HWADDR:
msg = "bad hardware address";
break;
case E_BAD_LONGWORD:
msg = "bad longword value";
break;
case E_BAD_HWATYPE:
msg = "bad HW address type";
break;
case E_BAD_PATHNAME:
msg = "bad pathname (need leading '/')";
break;
case E_BAD_VALUE:
msg = "bad value";
break;
default:
msg = "unknown error";
break;
}
report(LOG_ERR, "in entry named \"%s\", symbol \"%s\": %s",
current_hostname, current_tagname, msg);
return -1;
}
}
#define PARSE_IA1(MEMBER) do \
{ \
if (optype == OP_BOOLEAN) \
return E_SYNTAX_ERROR; \
hp->flags.MEMBER = FALSE; \
if (optype == OP_ADDITION) { \
if (prs_inetaddr(symbol, &value) < 0) \
return E_BAD_IPADDR; \
hp->MEMBER.s_addr = value; \
hp->flags.MEMBER = TRUE; \
} \
} while (0)
#define PARSE_IAL(MEMBER) do \
{ \
if (optype == OP_BOOLEAN) \
return E_SYNTAX_ERROR; \
if (hp->flags.MEMBER) { \
hp->flags.MEMBER = FALSE; \
assert(hp->MEMBER); \
del_iplist(hp->MEMBER); \
hp->MEMBER = NULL; \
} \
if (optype == OP_ADDITION) { \
hp->MEMBER = get_addresses(symbol); \
if (hp->MEMBER == NULL) \
return E_SYNTAX_ERROR; \
hp->flags.MEMBER = TRUE; \
} \
} while (0)
#define PARSE_STR(MEMBER) do \
{ \
if (optype == OP_BOOLEAN) \
return E_SYNTAX_ERROR; \
if (hp->flags.MEMBER) { \
hp->flags.MEMBER = FALSE; \
assert(hp->MEMBER); \
del_string(hp->MEMBER); \
hp->MEMBER = NULL; \
} \
if (optype == OP_ADDITION) { \
hp->MEMBER = get_shared_string(symbol); \
if (hp->MEMBER == NULL) \
return E_SYNTAX_ERROR; \
hp->flags.MEMBER = TRUE; \
} \
} while (0)
#define PARSE_UINT(MEMBER) do \
{ \
if (optype == OP_BOOLEAN) \
return E_SYNTAX_ERROR; \
hp->flags.MEMBER = FALSE; \
if (optype == OP_ADDITION) { \
value = get_u_long(symbol); \
hp->MEMBER = value; \
hp->flags.MEMBER = TRUE; \
} \
} while (0)
PRIVATE int
eval_symbol(char **symbol, struct host *hp)
{
char tmpstr[MAXSTRINGLEN];
byte *tmphaddr;
struct symbolmap *symbolptr;
u_int32 value;
int32 timeoff;
int i, numsymbols;
unsigned len;
int optype;
eat_whitespace(symbol);
current_tagname[0] = (*symbol)[0];
current_tagname[1] = (*symbol)[1];
current_tagname[2] = 0;
if ((*symbol)[0] == '\0') {
return E_END_OF_ENTRY;
}
if ((*symbol)[0] == ':') {
return SUCCESS;
}
if ((*symbol)[0] == 'T') {
(*symbol)++;
value = get_u_long(symbol);
snprintf(current_tagname, sizeof(current_tagname),
"T%d", (int)value);
eat_whitespace(symbol);
if ((*symbol)[0] != '=') {
return E_SYNTAX_ERROR;
}
(*symbol)++;
if (!(hp->generic)) {
hp->generic = (struct shared_bindata *)
smalloc(sizeof(struct shared_bindata));
}
if (process_generic(symbol, &(hp->generic), (byte) (value & 0xFF)))
return E_SYNTAX_ERROR;
hp->flags.generic = TRUE;
return SUCCESS;
}
switch ((*symbol)[2]) {
case '=':
optype = OP_ADDITION;
break;
case '@':
optype = OP_DELETION;
break;
case ':':
case '\0':
optype = OP_BOOLEAN;
break;
default:
return E_SYNTAX_ERROR;
}
symbolptr = symbol_list;
numsymbols = sizeof(symbol_list) / sizeof(struct symbolmap);
for (i = 0; i < numsymbols; i++) {
if (((symbolptr->symbol)[0] == (*symbol)[0]) &&
((symbolptr->symbol)[1] == (*symbol)[1])) {
break;
}
symbolptr++;
}
if (i >= numsymbols) {
return E_UNKNOWN_SYMBOL;
}
(*symbol) += (optype == OP_BOOLEAN) ? 2 : 3;
eat_whitespace(symbol);
switch (symbolptr->symbolcode) {
case SYM_BOOTFILE:
PARSE_STR(bootfile);
break;
case SYM_COOKIE_SERVER:
PARSE_IAL(cookie_server);
break;
case SYM_DOMAIN_SERVER:
PARSE_IAL(domain_server);
break;
case SYM_GATEWAY:
PARSE_IAL(gateway);
break;
case SYM_HWADDR:
if (optype == OP_BOOLEAN)
return E_SYNTAX_ERROR;
hp->flags.haddr = FALSE;
if (optype == OP_ADDITION) {
if (hp->flags.htype == 0) {
hp->flags.htype = TRUE;
hp->htype = HTYPE_ETHERNET;
}
tmphaddr = prs_haddr(symbol, hp->htype);
if (!tmphaddr)
return E_BAD_HWADDR;
bcopy(tmphaddr, hp->haddr, haddrlength(hp->htype));
hp->flags.haddr = TRUE;
}
break;
case SYM_HOMEDIR:
PARSE_STR(homedir);
break;
case SYM_HTYPE:
if (optype == OP_BOOLEAN)
return E_SYNTAX_ERROR;
hp->flags.htype = FALSE;
if (optype == OP_ADDITION) {
value = 0L;
eat_whitespace(symbol);
if (isdigit(**symbol)) {
value = get_u_long(symbol);
} else {
len = sizeof(tmpstr);
(void) get_string(symbol, tmpstr, &len);
makelower(tmpstr);
numsymbols = sizeof(htnamemap) /
sizeof(struct htypename);
for (i = 0; i < numsymbols; i++) {
if (!strcmp(htnamemap[i].name, tmpstr)) {
break;
}
}
if (i < numsymbols) {
value = htnamemap[i].htype;
}
}
if (value >= hwinfocnt) {
return E_BAD_HWATYPE;
}
hp->htype = (byte) (value & 0xFF);
hp->flags.htype = TRUE;
}
break;
case SYM_IMPRESS_SERVER:
PARSE_IAL(impress_server);
break;
case SYM_IPADDR:
PARSE_IA1(iaddr);
break;
case SYM_LOG_SERVER:
PARSE_IAL(log_server);
break;
case SYM_LPR_SERVER:
PARSE_IAL(lpr_server);
break;
case SYM_NAME_SERVER:
PARSE_IAL(name_server);
break;
case SYM_RLP_SERVER:
PARSE_IAL(rlp_server);
break;
case SYM_SUBNET_MASK:
PARSE_IA1(subnet_mask);
break;
case SYM_TIME_OFFSET:
if (optype == OP_BOOLEAN)
return E_SYNTAX_ERROR;
hp->flags.time_offset = FALSE;
if (optype == OP_ADDITION) {
len = sizeof(tmpstr);
(void) get_string(symbol, tmpstr, &len);
if (!strncmp(tmpstr, "auto", 4)) {
hp->time_offset = secondswest;
} else {
if (sscanf(tmpstr, "%d", (int*)&timeoff) != 1)
return E_BAD_LONGWORD;
hp->time_offset = timeoff;
}
hp->flags.time_offset = TRUE;
}
break;
case SYM_TIME_SERVER:
PARSE_IAL(time_server);
break;
case SYM_VENDOR_MAGIC:
if (optype == OP_BOOLEAN)
return E_SYNTAX_ERROR;
hp->flags.vm_cookie = FALSE;
if (optype == OP_ADDITION) {
if (strncmp(*symbol, "auto", 4)) {
if (!strncmp(*symbol, "rfc", 3)) {
bcopy(vm_rfc1048, hp->vm_cookie, 4);
} else if (!strncmp(*symbol, "cmu", 3)) {
bcopy(vm_cmu, hp->vm_cookie, 4);
} else {
if (!isdigit(**symbol))
return E_BAD_IPADDR;
if (prs_inetaddr(symbol, &value) < 0)
return E_BAD_IPADDR;
bcopy(&value, hp->vm_cookie, 4);
}
hp->flags.vm_cookie = TRUE;
}
}
break;
case SYM_SIMILAR_ENTRY:
switch (optype) {
case OP_ADDITION:
fill_defaults(hp, symbol);
break;
default:
return E_SYNTAX_ERROR;
}
break;
case SYM_NAME_SWITCH:
switch (optype) {
case OP_ADDITION:
return E_SYNTAX_ERROR;
case OP_DELETION:
hp->flags.send_name = FALSE;
hp->flags.name_switch = FALSE;
break;
case OP_BOOLEAN:
hp->flags.send_name = TRUE;
hp->flags.name_switch = TRUE;
break;
}
break;
case SYM_BOOTSIZE:
switch (optype) {
case OP_ADDITION:
if (!strncmp(*symbol, "auto", 4)) {
hp->flags.bootsize = TRUE;
hp->flags.bootsize_auto = TRUE;
} else {
hp->bootsize = (unsigned int) get_u_long(symbol);
hp->flags.bootsize = TRUE;
hp->flags.bootsize_auto = FALSE;
}
break;
case OP_DELETION:
hp->flags.bootsize = FALSE;
break;
case OP_BOOLEAN:
hp->flags.bootsize = TRUE;
hp->flags.bootsize_auto = TRUE;
break;
}
break;
case SYM_BOOT_SERVER:
PARSE_IA1(bootserver);
break;
case SYM_TFTPDIR:
PARSE_STR(tftpdir);
if ((hp->tftpdir != NULL) &&
(hp->tftpdir->string[0] != '/'))
return E_BAD_PATHNAME;
break;
case SYM_DUMP_FILE:
PARSE_STR(dump_file);
break;
case SYM_DOMAIN_NAME:
PARSE_STR(domain_name);
break;
case SYM_SWAP_SERVER:
PARSE_IA1(swap_server);
break;
case SYM_ROOT_PATH:
PARSE_STR(root_path);
break;
case SYM_EXTEN_FILE:
PARSE_STR(exten_file);
break;
case SYM_REPLY_ADDR:
PARSE_IA1(reply_addr);
break;
case SYM_NIS_DOMAIN:
PARSE_STR(nis_domain);
break;
case SYM_NIS_SERVER:
PARSE_IAL(nis_server);
break;
case SYM_NTP_SERVER:
PARSE_IAL(ntp_server);
break;
#ifdef YORK_EX_OPTION
case SYM_EXEC_FILE:
PARSE_STR(exec_file);
break;
#endif
case SYM_MSG_SIZE:
PARSE_UINT(msg_size);
if (hp->msg_size < BP_MINPKTSZ ||
hp->msg_size > MAX_MSG_SIZE)
return E_BAD_VALUE;
break;
case SYM_MIN_WAIT:
PARSE_UINT(min_wait);
break;
default:
return E_UNKNOWN_SYMBOL;
}
return SUCCESS;
}
#undef PARSE_IA1
#undef PARSE_IAL
#undef PARSE_STR
PRIVATE char *
get_string(char **src, char *dest, unsigned *length)
{
int n, len, quoteflag;
quoteflag = FALSE;
n = 0;
len = *length - 1;
while ((n < len) && (**src)) {
if (!quoteflag && (**src == ':')) {
break;
}
if (**src == '"') {
(*src)++;
quoteflag = !quoteflag;
continue;
}
if (**src == '\\') {
(*src)++;
if (!**src) {
break;
}
}
*dest++ = *(*src)++;
n++;
}
while ((n > 0) && isspace(dest[-1])) {
dest--;
n--;
}
*dest = '\0';
*length = n;
return dest;
}
PRIVATE struct shared_string *
get_shared_string(char **src)
{
char retstring[MAXSTRINGLEN];
struct shared_string *s;
unsigned length;
length = sizeof(retstring);
(void) get_string(src, retstring, &length);
s = (struct shared_string *) smalloc(sizeof(struct shared_string)
+ length);
s->linkcount = 1;
strcpy(s->string, retstring);
return s;
}
PRIVATE int
process_generic(char **src, struct shared_bindata **dest, u_int tagvalue)
{
byte tmpbuf[MAXBUFLEN];
byte *str;
struct shared_bindata *bdata;
u_int newlength, oldlength;
str = tmpbuf;
*str++ = (tagvalue & 0xFF);
str++;
if ((*src)[0] == '"') {
newlength = sizeof(tmpbuf) - 2;
(void) get_string(src, (char *) str, &newlength);
newlength++;
} else {
newlength = 0;
while (newlength < sizeof(tmpbuf) - 2) {
if (interp_byte(src, str++) < 0)
break;
newlength++;
if (**src == '.') {
(*src)++;
}
}
}
if ((*src)[0] != ':')
return -1;
tmpbuf[1] = (newlength & 0xFF);
oldlength = ((*dest)->length);
bdata = (struct shared_bindata *) smalloc(sizeof(struct shared_bindata)
+ oldlength + newlength + 1);
if (oldlength > 0) {
bcopy((*dest)->data, bdata->data, oldlength);
}
bcopy(tmpbuf, bdata->data + oldlength, newlength + 2);
bdata->length = oldlength + newlength + 2;
bdata->linkcount = 1;
if (*dest) {
del_bindata(*dest);
}
*dest = bdata;
return 0;
}
PRIVATE boolean
goodname(char *hostname)
{
do {
if (!isalpha(*hostname++)) {
return FALSE;
}
while (isalnum(*hostname) ||
(*hostname == '-') ||
(*hostname == '_') )
{
hostname++;
}
if (!isalnum(hostname[-1])) {
return FALSE;
}
if (*hostname == '\0') {
return TRUE;
}
} while (*hostname++ == '.');
return FALSE;
}
PRIVATE boolean
nullcmp(hash_datum *d1, hash_datum *d2)
{
return FALSE;
}
boolean
nmcmp(hash_datum *d1, hash_datum *d2)
{
char *name = (char *) d1;
struct host *hp = (struct host *) d2;
return !strcmp(name, hp->hostname->string);
}
PRIVATE boolean
hwinscmp(hash_datum *d1, hash_datum *d2)
{
struct host *host1 = (struct host *) d1;
struct host *host2 = (struct host *) d2;
if (host1->htype != host2->htype) {
return FALSE;
}
if (bcmp(host1->haddr, host2->haddr, haddrlength(host1->htype))) {
return FALSE;
}
if ((host1->subnet_mask.s_addr) == (host2->subnet_mask.s_addr)) {
if (((host1->iaddr.s_addr) & (host1->subnet_mask.s_addr)) !=
((host2->iaddr.s_addr) & (host2->subnet_mask.s_addr)))
{
return FALSE;
}
}
return TRUE;
}
#define DUP_COPY(MEMBER) do \
{ \
if (!hp->flags.MEMBER) { \
if ((hp->flags.MEMBER = hp2->flags.MEMBER) != 0) { \
hp->MEMBER = hp2->MEMBER; \
} \
} \
} while (0)
#define DUP_LINK(MEMBER) do \
{ \
if (!hp->flags.MEMBER) { \
if ((hp->flags.MEMBER = hp2->flags.MEMBER) != 0) { \
assert(hp2->MEMBER); \
hp->MEMBER = hp2->MEMBER; \
(hp->MEMBER->linkcount)++; \
} \
} \
} while (0)
PRIVATE void
fill_defaults(struct host *hp, char **src)
{
unsigned int tlen, hashcode;
struct host *hp2;
char tstring[MAXSTRINGLEN];
tlen = sizeof(tstring);
(void) get_string(src, tstring, &tlen);
hashcode = hash_HashFunction((u_char *) tstring, tlen);
hp2 = (struct host *) hash_Lookup(nmhashtable, hashcode, nmcmp, tstring);
if (hp2 == NULL) {
report(LOG_ERR, "can't find tc=\"%s\"", tstring);
return;
}
DUP_LINK(bootfile);
DUP_LINK(cookie_server);
DUP_LINK(domain_server);
DUP_LINK(gateway);
DUP_LINK(homedir);
DUP_COPY(htype);
DUP_LINK(impress_server);
DUP_LINK(log_server);
DUP_LINK(lpr_server);
DUP_LINK(name_server);
DUP_LINK(rlp_server);
DUP_COPY(subnet_mask);
DUP_COPY(time_offset);
DUP_LINK(time_server);
if (!hp->flags.vm_cookie) {
if ((hp->flags.vm_cookie = hp2->flags.vm_cookie)) {
bcopy(hp2->vm_cookie, hp->vm_cookie, 4);
}
}
if (!hp->flags.name_switch) {
if ((hp->flags.name_switch = hp2->flags.name_switch)) {
hp->flags.send_name = hp2->flags.send_name;
}
}
if (!hp->flags.bootsize) {
if ((hp->flags.bootsize = hp2->flags.bootsize)) {
hp->flags.bootsize_auto = hp2->flags.bootsize_auto;
hp->bootsize = hp2->bootsize;
}
}
DUP_COPY(bootserver);
DUP_LINK(tftpdir);
DUP_LINK(dump_file);
DUP_LINK(domain_name);
DUP_COPY(swap_server);
DUP_LINK(root_path);
DUP_LINK(exten_file);
DUP_COPY(reply_addr);
DUP_LINK(nis_domain);
DUP_LINK(nis_server);
DUP_LINK(ntp_server);
#ifdef YORK_EX_OPTION
DUP_LINK(exec_file);
#endif
DUP_COPY(msg_size);
DUP_COPY(min_wait);
DUP_LINK(generic);
}
#undef DUP_COPY
#undef DUP_LINK
PRIVATE void
adjust(char **s)
{
char *t;
t = *s;
while (*t && (*t != ':')) {
t++;
}
if (*t) {
t++;
}
*s = t;
}
PRIVATE void
eat_whitespace(char **s)
{
char *t;
t = *s;
while (*t && isspace(*t)) {
t++;
}
*s = t;
}
PRIVATE void
makelower(char *s)
{
while (*s) {
if (isupper(*s)) {
*s = tolower(*s);
}
s++;
}
}
PRIVATE struct in_addr_list *
get_addresses(char **src)
{
struct in_addr tmpaddrlist[MAXINADDRS];
struct in_addr *address1, *address2;
struct in_addr_list *result;
unsigned addrcount, totalsize;
address1 = tmpaddrlist;
for (addrcount = 0; addrcount < MAXINADDRS; addrcount++) {
while (isspace(**src) || (**src == ',')) {
(*src)++;
}
if (!**src) {
break;
}
if (prs_inetaddr(src, &(address1->s_addr)) < 0) {
break;
}
address1++;
}
if (addrcount < 1) {
result = NULL;
} else {
totalsize = sizeof(struct in_addr_list)
+ (addrcount - 1) * sizeof(struct in_addr);
result = (struct in_addr_list *) smalloc(totalsize);
result->linkcount = 1;
result->addrcount = addrcount;
address1 = tmpaddrlist;
address2 = result->addr;
for (; addrcount > 0; addrcount--) {
address2->s_addr = address1->s_addr;
address1++;
address2++;
}
}
return result;
}
PRIVATE int
prs_inetaddr(char **src, u_int32 *result)
{
char tmpstr[MAXSTRINGLEN];
u_int32 value;
u_int32 parts[4], *pp;
int n;
char *s, *t;
if (isalpha(**src)) {
s = *src;
t = tmpstr;
while ((isalnum(*s) || (*s == '.') ||
(*s == '-') || (*s == '_') ) &&
(t < &tmpstr[MAXSTRINGLEN - 1]) )
*t++ = *s++;
*t = '\0';
*src = s;
n = lookup_ipa(tmpstr, result);
if (n < 0)
report(LOG_ERR, "can not get IP addr for %s", tmpstr);
return n;
}
pp = parts;
loop:
if (!isdigit(**src))
return -1;
*pp++ = get_u_long(src);
if (**src == '.') {
if (pp < (parts + 4)) {
(*src)++;
goto loop;
}
return (-1);
}
#if 0
if (**src && !(isspace(**src) || (**src == ':'))) {
return (-1);
}
#endif
n = pp - parts;
switch (n) {
case 1:
value = parts[0];
break;
case 2:
value = (parts[0] << 24) | (parts[1] & 0xFFFFFF);
break;
case 3:
value = (parts[0] << 24) | ((parts[1] & 0xFF) << 16) |
(parts[2] & 0xFFFF);
break;
case 4:
value = (parts[0] << 24) | ((parts[1] & 0xFF) << 16) |
((parts[2] & 0xFF) << 8) | (parts[3] & 0xFF);
break;
default:
return (-1);
}
*result = htonl(value);
return (0);
}
PRIVATE byte *
prs_haddr(char **src, u_int htype)
{
static byte haddr[MAXHADDRLEN];
byte *hap;
char tmpstr[MAXSTRINGLEN];
u_int tmplen;
unsigned hal;
char *p;
hal = haddrlength(htype);
if (hal <= 0) {
report(LOG_ERR, "Invalid addr type for HW addr parse");
return NULL;
}
tmplen = sizeof(tmpstr);
get_string(src, tmpstr, &tmplen);
p = tmpstr;
if (goodname(p)) {
if ((hap = lookup_hwa(p, htype)) != NULL)
return hap;
report(LOG_ERR, "Add 0x prefix if hex value starts with A-F");
}
hap = haddr;
while (hap < haddr + hal) {
if ((*p == '.') || (*p == ':'))
p++;
if (interp_byte(&p, hap++) < 0) {
return NULL;
}
}
return haddr;
}
PRIVATE int
interp_byte(char **src, byte *retbyte)
{
int v;
if ((*src)[0] == '0' &&
((*src)[1] == 'x' ||
(*src)[1] == 'X')) {
(*src) += 2;
}
if (!isxdigit((*src)[0]) || !isxdigit((*src)[1])) {
return -1;
}
if (sscanf(*src, "%2x", &v) != 1) {
return -1;
}
(*src) += 2;
*retbyte = (byte) (v & 0xFF);
return 0;
}
PRIVATE u_int32
get_u_long(char **src)
{
u_int32 value, base;
char c;
value = 0;
base = 10;
if (**src == '0') {
base = 8;
(*src)++;
}
if (**src == 'x' || **src == 'X') {
base = 16;
(*src)++;
}
while ((c = **src)) {
if (isdigit(c)) {
value = (value * base) + (c - '0');
(*src)++;
continue;
}
if (base == 16 && isxdigit(c)) {
value = (value << 4) + ((c & ~32) + 10 - 'A');
(*src)++;
continue;
}
break;
}
return value;
}
PRIVATE void
free_host(hash_datum *hmp)
{
struct host *hostptr = (struct host *) hmp;
if (hostptr == NULL)
return;
assert(hostptr->linkcount > 0);
if (--(hostptr->linkcount))
return;
del_iplist(hostptr->cookie_server);
del_iplist(hostptr->domain_server);
del_iplist(hostptr->gateway);
del_iplist(hostptr->impress_server);
del_iplist(hostptr->log_server);
del_iplist(hostptr->lpr_server);
del_iplist(hostptr->name_server);
del_iplist(hostptr->rlp_server);
del_iplist(hostptr->time_server);
del_iplist(hostptr->nis_server);
del_iplist(hostptr->ntp_server);
del_string(hostptr->hostname);
del_string(hostptr->homedir);
del_string(hostptr->bootfile);
del_string(hostptr->tftpdir);
del_string(hostptr->root_path);
del_string(hostptr->domain_name);
del_string(hostptr->dump_file);
del_string(hostptr->exten_file);
del_string(hostptr->nis_domain);
#ifdef YORK_EX_OPTION
del_string(hostptr->exec_file);
#endif
del_bindata(hostptr->generic);
free((char *) hostptr);
}
PRIVATE void
del_iplist(struct in_addr_list *iplist)
{
if (iplist) {
if (!(--(iplist->linkcount))) {
free((char *) iplist);
}
}
}
PRIVATE void
del_string(struct shared_string *stringptr)
{
if (stringptr) {
if (!(--(stringptr->linkcount))) {
free((char *) stringptr);
}
}
}
PRIVATE void
del_bindata(struct shared_bindata *dataptr)
{
if (dataptr) {
if (!(--(dataptr->linkcount))) {
free((char *) dataptr);
}
}
}
PRIVATE char *
smalloc(unsigned nbytes)
{
char *retvalue;
retvalue = malloc(nbytes);
if (!retvalue) {
report(LOG_ERR, "malloc() failure -- exiting");
exit(1);
}
bzero(retvalue, nbytes);
return retvalue;
}
boolean
hwlookcmp(hash_datum *d1, hash_datum *d2)
{
struct host *host1 = (struct host *) d1;
struct host *host2 = (struct host *) d2;
if (host1->htype != host2->htype) {
return FALSE;
}
if (bcmp(host1->haddr, host2->haddr, haddrlength(host1->htype))) {
return FALSE;
}
return TRUE;
}
boolean
iplookcmp(hash_datum *d1, hash_datum *d2)
{
struct host *host1 = (struct host *) d1;
struct host *host2 = (struct host *) d2;
return (host1->iaddr.s_addr == host2->iaddr.s_addr);
}