#include <sys/types.h>
#include <sys/param.h>
#include <sys/socket.h>
#include <sys/ioctl.h>
#include <sys/file.h>
#include <sys/time.h>
#include <sys/stat.h>
#include <sys/utsname.h>
#include <net/if.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#ifndef NO_UNISTD
#include <unistd.h>
#endif
#include <stdlib.h>
#include <signal.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <ctype.h>
#include <netdb.h>
#include <paths.h>
#include <syslog.h>
#include <assert.h>
#include <inttypes.h>
#ifdef NO_SETSID
# include <fcntl.h>
#endif
#include "bootp.h"
#include "hash.h"
#include "hwaddr.h"
#include "bootpd.h"
#include "dovend.h"
#include "getif.h"
#include "readfile.h"
#include "report.h"
#include "tzone.h"
#include "patchlevel.h"
#ifndef CONFIG_FILE
#define CONFIG_FILE "/etc/bootptab"
#endif
#ifndef DUMPTAB_FILE
#define DUMPTAB_FILE "/tmp/bootpd.dump"
#endif
extern void dumptab(char *);
PRIVATE void catcher(int);
PRIVATE int chk_access(char *, int32 *);
#ifdef VEND_CMU
PRIVATE void dovend_cmu(struct bootp *, struct host *);
#endif
PRIVATE void dovend_rfc1048(struct bootp *, struct host *, int32);
PRIVATE void handle_reply(void);
PRIVATE void handle_request(void);
PRIVATE void sendreply(int forward, int32 dest_override);
PRIVATE void usage(void);
u_short bootps_port, bootpc_port;
struct sockaddr_in bind_addr;
struct sockaddr_in recv_addr;
struct sockaddr_in send_addr;
int debug = 0;
struct timeval actualtimeout =
{
15 * 60L,
0
};
int arpmod = TRUE;
int s;
char *pktbuf;
int pktlen;
char *progname;
char *chdir_path;
struct in_addr my_ip_addr;
static const char *hostname;
static char default_hostname[MAXHOSTNAMELEN];
PRIVATE int do_readtab = 0;
PRIVATE int do_dumptab = 0;
char *bootptab = CONFIG_FILE;
char *bootpd_dump = DUMPTAB_FILE;
int
main(int argc, char **argv)
{
struct timeval *timeout;
struct bootp *bp;
struct servent *servp;
struct hostent *hep;
char *stmp;
socklen_t ba_len, ra_len;
int n;
int nfound;
fd_set readfds;
int standalone;
#ifdef SA_NOCLDSTOP
struct sigaction sa;
#endif
progname = strrchr(argv[0], '/');
if (progname) progname++;
else progname = argv[0];
report_init(0);
report(LOG_INFO, "version %s.%d", VERSION, PATCHLEVEL);
assert(sizeof(struct bootp) == BP_MINPKTSZ);
pktbuf = malloc(MAX_MSG_SIZE);
if (!pktbuf) {
report(LOG_ERR, "malloc failed");
exit(1);
}
bp = (struct bootp *) pktbuf;
s = 0;
ba_len = sizeof(bind_addr);
bzero((char *) &bind_addr, ba_len);
errno = 0;
standalone = TRUE;
if (getsockname(s, (struct sockaddr *) &bind_addr, &ba_len) == 0) {
if (bind_addr.sin_family == AF_INET) {
standalone = FALSE;
bootps_port = ntohs(bind_addr.sin_port);
} else {
report(LOG_ERR, "getsockname: not an INET socket");
}
}
stmp = NULL;
timeout = &actualtimeout;
if (gethostname(default_hostname, sizeof(default_hostname) - 1) < 0) {
report(LOG_ERR, "bootpd: can't get hostname\n");
exit(1);
}
default_hostname[sizeof(default_hostname) - 1] = '\0';
hostname = default_hostname;
for (argc--, argv++; argc > 0; argc--, argv++) {
if (argv[0][0] != '-')
break;
switch (argv[0][1]) {
case 'a':
arpmod = FALSE;
break;
case 'c':
if (argv[0][2]) {
stmp = &(argv[0][2]);
} else {
argc--;
argv++;
stmp = argv[0];
}
if (!stmp || (stmp[0] != '/')) {
report(LOG_ERR,
"bootpd: invalid chdir specification\n");
break;
}
chdir_path = stmp;
break;
case 'd':
if (argv[0][2]) {
stmp = &(argv[0][2]);
} else if (argv[1] && argv[1][0] == '-') {
debug++;
break;
} else {
argc--;
argv++;
stmp = argv[0];
}
if (!stmp || (sscanf(stmp, "%d", &n) != 1) || (n < 0)) {
report(LOG_ERR,
"%s: invalid debug level\n", progname);
break;
}
debug = n;
break;
case 'h':
if (argv[0][2]) {
stmp = &(argv[0][2]);
} else {
argc--;
argv++;
stmp = argv[0];
}
if (!stmp) {
report(LOG_ERR,
"bootpd: missing hostname\n");
break;
}
hostname = stmp;
break;
case 'i':
standalone = FALSE;
break;
case 's':
standalone = TRUE;
break;
case 't':
if (argv[0][2]) {
stmp = &(argv[0][2]);
} else {
argc--;
argv++;
stmp = argv[0];
}
if (!stmp || (sscanf(stmp, "%d", &n) != 1) || (n < 0)) {
report(LOG_ERR,
"%s: invalid timeout specification\n", progname);
break;
}
actualtimeout.tv_sec = (int32) (60 * n);
timeout = (n > 0) ? &actualtimeout : NULL;
break;
default:
report(LOG_ERR, "%s: unknown switch: -%c\n",
progname, argv[0][1]);
usage();
break;
}
}
if (argc > 0)
bootptab = argv[0];
if (argc > 1)
bootpd_dump = argv[1];
hep = gethostbyname(hostname);
if (!hep) {
report(LOG_ERR, "Can not get my IP address\n");
exit(1);
}
bcopy(hep->h_addr, (char *)&my_ip_addr, sizeof(my_ip_addr));
if (standalone) {
if (debug < 3) {
if (fork())
exit(0);
#ifdef NO_SETSID
setpgrp(0,0);
#ifdef TIOCNOTTY
n = open(_PATH_TTY, O_RDWR);
if (n >= 0) {
ioctl(n, TIOCNOTTY, (char *) 0);
(void) close(n);
}
#endif
#else
if (setsid() < 0)
perror("setsid");
#endif
}
timeout = NULL;
}
if (chdir_path) {
if (chdir(chdir_path) < 0)
report(LOG_ERR, "%s: chdir failed", chdir_path);
}
tzone_init();
rdtab_init();
readtab(1);
if (standalone) {
if ((s = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
report(LOG_ERR, "socket: %s", get_network_errmsg());
exit(1);
}
servp = getservbyname("bootps", "udp");
if (servp) {
bootps_port = ntohs((u_short) servp->s_port);
} else {
bootps_port = (u_short) IPPORT_BOOTPS;
report(LOG_ERR,
"bootps/udp: unknown service -- using port %d",
bootps_port);
}
bind_addr.sin_family = AF_INET;
bind_addr.sin_addr.s_addr = INADDR_ANY;
bind_addr.sin_port = htons(bootps_port);
if (bind(s, (struct sockaddr *) &bind_addr,
sizeof(bind_addr)) < 0)
{
report(LOG_ERR, "bind: %s", get_network_errmsg());
exit(1);
}
}
servp = getservbyname("bootpc", "udp");
if (servp) {
bootpc_port = ntohs(servp->s_port);
} else {
report(LOG_ERR,
"bootpc/udp: unknown service -- using port %d",
IPPORT_BOOTPC);
bootpc_port = (u_short) IPPORT_BOOTPC;
}
#ifdef SA_NOCLDSTOP
sa.sa_handler = catcher;
sigemptyset(&sa.sa_mask);
sa.sa_flags = 0;
if (sigaction(SIGHUP, &sa, NULL) < 0) {
report(LOG_ERR, "sigaction: %s", get_errmsg());
exit(1);
}
if (sigaction(SIGUSR1, &sa, NULL) < 0) {
report(LOG_ERR, "sigaction: %s", get_errmsg());
exit(1);
}
#else
if ((int) signal(SIGHUP, catcher) < 0) {
report(LOG_ERR, "signal: %s", get_errmsg());
exit(1);
}
if ((int) signal(SIGUSR1, catcher) < 0) {
report(LOG_ERR, "signal: %s", get_errmsg());
exit(1);
}
#endif
FD_ZERO(&readfds);
for (;;) {
struct timeval tv;
FD_SET(s, &readfds);
if (timeout)
tv = *timeout;
nfound = select(s + 1, &readfds, NULL, NULL,
(timeout) ? &tv : NULL);
if (nfound < 0) {
if (errno != EINTR) {
report(LOG_ERR, "select: %s", get_errmsg());
}
if (do_readtab) {
do_readtab = 0;
readtab(1);
}
if (do_dumptab) {
do_dumptab = 0;
dumptab(bootpd_dump);
}
continue;
}
if (!FD_ISSET(s, &readfds)) {
if (debug > 1)
report(LOG_INFO, "exiting after %jd minutes of inactivity",
(intmax_t)actualtimeout.tv_sec / 60);
exit(0);
}
ra_len = sizeof(recv_addr);
n = recvfrom(s, pktbuf, MAX_MSG_SIZE, 0,
(struct sockaddr *) &recv_addr, &ra_len);
if (n <= 0) {
continue;
}
if (debug > 1) {
report(LOG_INFO, "recvd pkt from IP addr %s",
inet_ntoa(recv_addr.sin_addr));
}
if (n < sizeof(struct bootp)) {
if (debug) {
report(LOG_NOTICE, "received short packet");
}
continue;
}
pktlen = n;
readtab(0);
switch (bp->bp_op) {
case BOOTREQUEST:
handle_request();
break;
case BOOTREPLY:
handle_reply();
break;
}
}
return 0;
}
PRIVATE void
usage()
{
fprintf(stderr,
"usage: bootpd [-a] [-i | -s] [-c chdir-path] [-d level] [-h hostname]\n"
" [-t timeout] [bootptab [dumpfile]]\n");
fprintf(stderr, " -a\tdon't modify ARP table\n");
fprintf(stderr, " -c n\tset current directory\n");
fprintf(stderr, " -d n\tset debug level\n");
fprintf(stderr, " -h n\tset the hostname to listen on\n");
fprintf(stderr, " -i\tforce inetd mode (run as child of inetd)\n");
fprintf(stderr, " -s\tforce standalone mode (run without inetd)\n");
fprintf(stderr, " -t n\tset inetd exit timeout to n minutes\n");
exit(1);
}
PRIVATE void
catcher(int sig)
{
if (sig == SIGHUP)
do_readtab = 1;
if (sig == SIGUSR1)
do_dumptab = 1;
#if !defined(SA_NOCLDSTOP) && defined(SYSV)
signal(sig, catcher);
#endif
}
PRIVATE void
handle_request(void)
{
struct bootp *bp = (struct bootp *) pktbuf;
struct host *hp = NULL;
struct host dummyhost;
int32 bootsize = 0;
unsigned hlen, hashcode;
int32 dest;
char realpath[1024];
char *clntpath;
char *homedir, *bootfile;
int n;
if (bp->bp_htype >= hwinfocnt) {
report(LOG_NOTICE, "bad hw addr type %u", bp->bp_htype);
return;
}
bp->bp_file[sizeof(bp->bp_file)-1] = '\0';
if (strlen(bp->bp_sname)) {
if (strcmp(bp->bp_sname, hostname)) {
if (debug)
report(LOG_INFO, "\
ignoring request for server %s from client at %s address %s",
bp->bp_sname, netname(bp->bp_htype),
haddrtoa(bp->bp_chaddr, bp->bp_hlen));
return;
}
} else {
strcpy(bp->bp_sname, hostname);
}
bp->bp_op = BOOTREPLY;
if (bp->bp_ciaddr.s_addr == 0) {
if (debug > 1) {
report(LOG_INFO, "request from %s address %s",
netname(bp->bp_htype),
haddrtoa(bp->bp_chaddr, bp->bp_hlen));
}
hlen = haddrlength(bp->bp_htype);
if (hlen != bp->bp_hlen) {
report(LOG_NOTICE, "bad addr len from %s address %s",
netname(bp->bp_htype),
haddrtoa(bp->bp_chaddr, hlen));
}
dummyhost.htype = bp->bp_htype;
bcopy(bp->bp_chaddr, dummyhost.haddr, hlen);
hashcode = hash_HashFunction(bp->bp_chaddr, hlen);
hp = (struct host *) hash_Lookup(hwhashtable, hashcode, hwlookcmp,
&dummyhost);
if (hp == NULL &&
bp->bp_htype == HTYPE_IEEE802)
{
haddr_conv802(bp->bp_chaddr, dummyhost.haddr, hlen);
if (debug > 1) {
report(LOG_INFO, "\
HW addr type is IEEE 802. convert to %s and check again\n",
haddrtoa(dummyhost.haddr, bp->bp_hlen));
}
hashcode = hash_HashFunction(dummyhost.haddr, hlen);
hp = (struct host *) hash_Lookup(hwhashtable, hashcode,
hwlookcmp, &dummyhost);
}
if (hp == NULL) {
if (debug)
report(LOG_NOTICE, "unknown client %s address %s",
netname(bp->bp_htype),
haddrtoa(bp->bp_chaddr, bp->bp_hlen));
return;
}
(bp->bp_yiaddr).s_addr = hp->iaddr.s_addr;
} else {
if (debug > 1) {
report(LOG_INFO, "request from IP addr %s",
inet_ntoa(bp->bp_ciaddr));
}
dummyhost.iaddr.s_addr = bp->bp_ciaddr.s_addr;
hashcode = hash_HashFunction((u_char *) &(bp->bp_ciaddr.s_addr), 4);
hp = (struct host *) hash_Lookup(iphashtable, hashcode, iplookcmp,
&dummyhost);
if (hp == NULL) {
if (debug) {
report(LOG_NOTICE, "IP address not found: %s",
inet_ntoa(bp->bp_ciaddr));
}
return;
}
}
if (debug) {
report(LOG_INFO, "found %s (%s)", inet_ntoa(hp->iaddr),
hp->hostname->string);
}
if (hp->flags.min_wait) {
u_int32 t = (u_int32) ntohs(bp->bp_secs);
if (t < hp->min_wait) {
if (debug > 1)
report(LOG_INFO,
"ignoring request due to timestamp (%d < %d)",
t, hp->min_wait);
return;
}
}
#ifdef YORK_EX_OPTION
if (hp->flags.exec_file) {
char tst[100];
strcpy (tst, hp->exec_file->string);
strcat (tst, " ");
strcat (tst, hp->hostname->string);
strcat (tst, " &");
if (debug)
report(LOG_INFO, "executing %s", tst);
system(tst);
}
#endif
(bp->bp_siaddr).s_addr = (hp->flags.bootserver) ?
hp->bootserver.s_addr : 0L;
#ifdef STANFORD_PROM_COMPAT
if (strcmp(bp->bp_file, "sunboot14") == 0)
bp->bp_file[0] = '\0';
#endif
if (hp->flags.tftpdir) {
snprintf(realpath, sizeof(realpath), "%s", hp->tftpdir->string);
clntpath = &realpath[strlen(realpath)];
} else {
realpath[0] = '\0';
clntpath = realpath;
}
homedir = NULL;
bootfile = NULL;
if (bp->bp_file[0]) {
homedir = bp->bp_file;
bootfile = strrchr(homedir, '/');
if (bootfile) {
if (homedir == bootfile)
homedir = NULL;
*bootfile++ = '\0';
} else {
bootfile = homedir;
homedir = NULL;
}
if (debug > 2) {
report(LOG_INFO, "requested path=\"%s\" file=\"%s\"",
(homedir) ? homedir : "",
(bootfile) ? bootfile : "");
}
}
if (hp->flags.homedir)
homedir = hp->homedir->string;
if (hp->flags.bootfile)
bootfile = hp->bootfile->string;
if (homedir) {
if (homedir[0] != '/')
strcat(clntpath, "/");
strcat(clntpath, homedir);
homedir = NULL;
}
if (bootfile) {
if (bootfile[0] != '/')
strcat(clntpath, "/");
strcat(clntpath, bootfile);
bootfile = NULL;
}
n = strlen(clntpath);
strcat(clntpath, ".");
strcat(clntpath, hp->hostname->string);
if (chk_access(realpath, &bootsize) < 0) {
clntpath[n] = 0;
if (chk_access(realpath, &bootsize) < 0) {
#ifdef CHECK_FILE_ACCESS
if (bp->bp_file[0]) {
report(LOG_NOTICE,
"requested file not found: \"%s\"", clntpath);
return;
}
bzero(bp->bp_file, sizeof(bp->bp_file));
goto null_file_name;
#else
if (hp->flags.bootsize_auto) {
report(LOG_ERR, "can not determine size of file \"%s\"",
clntpath);
}
#endif
}
}
strncpy(bp->bp_file, clntpath, BP_FILE_LEN);
if (debug > 2)
report(LOG_INFO, "bootfile=\"%s\"", clntpath);
#ifdef CHECK_FILE_ACCESS
null_file_name:
#endif
if (debug > 1) {
report(LOG_INFO, "vendor magic field is %d.%d.%d.%d",
(int) ((bp->bp_vend)[0]),
(int) ((bp->bp_vend)[1]),
(int) ((bp->bp_vend)[2]),
(int) ((bp->bp_vend)[3]));
}
if (hp->flags.vm_cookie) {
bcopy(hp->vm_cookie, bp->bp_vend, 4);
}
if (!bcmp(bp->bp_vend, vm_rfc1048, 4)) {
dovend_rfc1048(bp, hp, bootsize);
if (debug > 1) {
report(LOG_INFO, "sending reply (with RFC1048 options)");
}
}
#ifdef VEND_CMU
else if (!bcmp(bp->bp_vend, vm_cmu, 4)) {
dovend_cmu(bp, hp);
if (debug > 1) {
report(LOG_INFO, "sending reply (with CMU options)");
}
}
#endif
else {
if (debug > 1) {
report(LOG_INFO, "sending reply (with no options)");
}
}
dest = (hp->flags.reply_addr) ?
hp->reply_addr.s_addr : 0L;
sendreply(0, dest);
}
PRIVATE void
handle_reply(void)
{
if (debug) {
report(LOG_INFO, "processing boot reply");
}
sendreply(1, 0);
}
PRIVATE void
sendreply(int forward, int32 dst_override)
{
struct bootp *bp = (struct bootp *) pktbuf;
struct in_addr dst;
u_short port = bootpc_port;
unsigned char *ha;
int len, haf;
if (dst_override) {
dst.s_addr = dst_override;
if (debug > 1) {
report(LOG_INFO, "reply address override: %s",
inet_ntoa(dst));
}
} else if (bp->bp_ciaddr.s_addr) {
dst = bp->bp_ciaddr;
} else if (bp->bp_giaddr.s_addr && forward == 0) {
dst = bp->bp_giaddr;
port = bootps_port;
if (debug > 1) {
report(LOG_INFO, "sending reply to gateway %s",
inet_ntoa(dst));
}
} else {
dst = bp->bp_yiaddr;
ha = bp->bp_chaddr;
len = bp->bp_hlen;
if (len > MAXHADDRLEN)
len = MAXHADDRLEN;
haf = (int) bp->bp_htype;
if (haf == 0)
haf = HTYPE_ETHERNET;
if (arpmod) {
if (debug > 1)
report(LOG_INFO, "setarp %s - %s",
inet_ntoa(dst), haddrtoa(ha, len));
setarp(s, &dst, haf, ha, len);
}
}
if ((forward == 0) &&
(bp->bp_siaddr.s_addr == 0))
{
struct ifreq *ifr;
struct in_addr siaddr;
ifr = getif(s, &dst);
if (ifr) {
struct sockaddr_in *sip;
sip = (struct sockaddr_in *) &(ifr->ifr_addr);
siaddr = sip->sin_addr;
} else {
siaddr = my_ip_addr;
}
bp->bp_siaddr = siaddr;
}
send_addr.sin_family = AF_INET;
send_addr.sin_port = htons(port);
send_addr.sin_addr = dst;
if (sendto(s, pktbuf, pktlen, 0,
(struct sockaddr *) &send_addr,
sizeof(send_addr)) < 0)
{
report(LOG_ERR, "sendto: %s", get_network_errmsg());
}
}
PRIVATE int
chk_access(char *path, int32 *filesize)
{
struct stat st;
if ((stat(path, &st) == 0) && (st.st_mode & (S_IREAD >> 6))) {
*filesize = (int32) st.st_size;
return 0;
} else {
return -1;
}
}
#ifdef VEND_CMU
PRIVATE void
dovend_cmu(struct bootp *bp, struct host *hp)
{
struct cmu_vend *vendp;
struct in_addr_list *taddr;
bzero(bp->bp_vend, sizeof(bp->bp_vend));
vendp = (struct cmu_vend *) bp->bp_vend;
strcpy(vendp->v_magic, (char *)vm_cmu);
if (hp->flags.subnet_mask) {
(vendp->v_smask).s_addr = hp->subnet_mask.s_addr;
(vendp->v_flags) |= VF_SMASK;
if (hp->flags.gateway) {
(vendp->v_dgate).s_addr = hp->gateway->addr->s_addr;
}
}
if (hp->flags.domain_server) {
taddr = hp->domain_server;
if (taddr->addrcount > 0) {
(vendp->v_dns1).s_addr = (taddr->addr)[0].s_addr;
if (taddr->addrcount > 1) {
(vendp->v_dns2).s_addr = (taddr->addr)[1].s_addr;
}
}
}
if (hp->flags.name_server) {
taddr = hp->name_server;
if (taddr->addrcount > 0) {
(vendp->v_ins1).s_addr = (taddr->addr)[0].s_addr;
if (taddr->addrcount > 1) {
(vendp->v_ins2).s_addr = (taddr->addr)[1].s_addr;
}
}
}
if (hp->flags.time_server) {
taddr = hp->time_server;
if (taddr->addrcount > 0) {
(vendp->v_ts1).s_addr = (taddr->addr)[0].s_addr;
if (taddr->addrcount > 1) {
(vendp->v_ts2).s_addr = (taddr->addr)[1].s_addr;
}
}
}
}
#endif
#define NEED(LEN, MSG) do \
if (bytesleft < (LEN)) { \
report(LOG_NOTICE, noroom, \
hp->hostname->string, MSG); \
return; \
} while (0)
PRIVATE void
dovend_rfc1048(struct bootp *bp, struct host *hp, int32 bootsize)
{
int bytesleft, len;
byte *vp;
static const char noroom[] = "%s: No room for \"%s\" option";
vp = bp->bp_vend;
if (hp->flags.msg_size) {
pktlen = hp->msg_size;
} else {
if (pktlen > sizeof(*bp)) {
if (debug > 1)
report(LOG_INFO, "request message length=%d", pktlen);
}
{
byte *p, *ep;
byte tag, len;
short msgsz = 0;
p = vp + 4;
ep = p + BP_VEND_LEN - 4;
while (p < ep) {
tag = *p++;
if (tag == TAG_PAD)
continue;
if (tag == TAG_END)
break;
len = *p++;
switch (tag) {
case TAG_MAX_MSGSZ:
if (len == 2) {
bcopy(p, (char*)&msgsz, 2);
msgsz = ntohs(msgsz);
}
break;
case TAG_SUBNET_MASK:
break;
}
p += len;
}
if (msgsz > sizeof(*bp) + BP_MSG_OVERHEAD) {
if (debug > 1)
report(LOG_INFO, "request has DHCP msglen=%d", msgsz);
pktlen = msgsz - BP_MSG_OVERHEAD;
}
}
}
if (pktlen < sizeof(*bp)) {
report(LOG_ERR, "invalid response length=%d", pktlen);
pktlen = sizeof(*bp);
}
bytesleft = ((byte*)bp + pktlen) - vp;
if (pktlen > sizeof(*bp)) {
if (debug > 1)
report(LOG_INFO, "extended reply, length=%d, options=%d",
pktlen, bytesleft);
}
bcopy(vm_rfc1048, vp, 4);
vp += 4;
bytesleft -= 4;
if (hp->flags.subnet_mask) {
*vp++ = TAG_SUBNET_MASK;
*vp++ = 4;
insert_u_long(hp->subnet_mask.s_addr, &vp);
bytesleft -= 6;
if (hp->flags.gateway) {
(void) insert_ip(TAG_GATEWAY,
hp->gateway,
&vp, &bytesleft);
}
}
if (hp->flags.bootsize) {
bootsize = (hp->flags.bootsize_auto) ?
((bootsize + 511) / 512) : (hp->bootsize);
*vp++ = TAG_BOOT_SIZE;
*vp++ = 2;
*vp++ = (byte) ((bootsize >> 8) & 0xFF);
*vp++ = (byte) (bootsize & 0xFF);
bytesleft -= 4;
}
if (hp->flags.exten_file) {
len = strlen(hp->exten_file->string);
NEED((len + 3), "ef");
*vp++ = TAG_EXTEN_FILE;
*vp++ = (byte) (len & 0xFF);
bcopy(hp->exten_file->string, vp, len);
vp += len;
*vp++ = TAG_END;
bytesleft -= len + 3;
return;
}
len = dovend_rfc1497(hp, vp, bytesleft - 1);
vp += len;
bytesleft -= len;
NEED(1, "(end)");
*vp++ = TAG_END;
bytesleft--;
if (bytesleft > 0) {
bzero(vp, bytesleft);
}
}
#undef NEED