13229Spst/*
23229Spst * dovend.c : Inserts all but the first few vendor options.
318471Swosch *
450476Speter * $FreeBSD$
53229Spst */
63229Spst
73229Spst#include <sys/types.h>
83229Spst
93229Spst#include <netinet/in.h>
103229Spst#include <arpa/inet.h>			/* inet_ntoa */
113229Spst
123229Spst#include <stdlib.h>
133229Spst#include <stdio.h>
143229Spst#include <string.h>
153229Spst#include <errno.h>
163229Spst#include <syslog.h>
173229Spst
183229Spst#ifndef USE_BFUNCS
193229Spst# include <memory.h>
203229Spst/* Yes, memcpy is OK here (no overlapped copies). */
213229Spst# define bcopy(a,b,c)    memcpy(b,a,c)
223229Spst# define bzero(p,l)      memset(p,0,l)
233229Spst# define bcmp(a,b,c)     memcmp(a,b,c)
243229Spst# define index           strchr
253229Spst#endif
263229Spst
273229Spst#include "bootp.h"
283229Spst#include "bootpd.h"
293229Spst#include "report.h"
303229Spst#include "dovend.h"
313229Spst
3297417SalfredPRIVATE int insert_generic(struct shared_bindata *, byte **, int *);
333229Spst
343229Spst/*
353229Spst * Insert the 2nd part of the options into an option buffer.
363229Spst * Return amount of space used.
373229Spst *
383229Spst * This inserts everything EXCEPT:
393229Spst *   magic cookie, subnet mask, gateway, bootsize, extension file
403229Spst * Those are handled separately (in bootpd.c) to allow this function
413229Spst * to be shared between bootpd and bootpef.
423229Spst *
433229Spst * When an "extension file" is in use, the options inserted by
443229Spst * this function go into the exten_file, not the bootp response.
453229Spst */
463229Spst
473229Spstint
483229Spstdovend_rfc1497(hp, buf, len)
493229Spst	struct host *hp;
503229Spst	byte *buf;
513229Spst	int len;
523229Spst{
533229Spst	int bytesleft = len;
543229Spst	byte *vp = buf;
553229Spst
5690159Skris	static const char noroom[] = "%s: No room for \"%s\" option";
573229Spst#define	NEED(LEN, MSG) do                       \
583229Spst		if (bytesleft < (LEN)) {         	    \
593229Spst			report(LOG_NOTICE, noroom,          \
603229Spst				   hp->hostname->string, MSG);  \
613229Spst			return (vp - buf);                  \
623229Spst		} while (0)
633229Spst
643229Spst	/*
653229Spst	 * Note that the following have already been inserted:
663229Spst	 *   magic_cookie, subnet_mask, gateway, bootsize
673229Spst	 *
683229Spst	 * The remaining options are inserted in order of importance.
693229Spst	 * (Of course the importance of each is a matter of opinion.)
703229Spst	 * The option insertion order should probably be configurable.
713229Spst	 *
723229Spst	 * This is the order used in the NetBSD version.  Can anyone
733229Spst	 * explain why the time_offset and swap_server are first?
743229Spst	 * Also, why is the hostname so far down the list?  -gwr
753229Spst	 */
763229Spst
773229Spst	if (hp->flags.time_offset) {
783229Spst		NEED(6, "to");
793229Spst		*vp++ = TAG_TIME_OFFSET;/* -1 byte  */
803229Spst		*vp++ = 4;				/* -1 byte  */
813229Spst		insert_u_long(htonl(hp->time_offset), &vp);	/* -4 bytes */
823229Spst		bytesleft -= 6;
833229Spst	}
843229Spst	/*
853229Spst	 * swap server, root path, dump path
863229Spst	 */
873229Spst	if (hp->flags.swap_server) {
883229Spst		NEED(6, "sw");
893229Spst		/* There is just one SWAP_SERVER, so it is not an iplist. */
903229Spst		*vp++ = TAG_SWAP_SERVER;/* -1 byte  */
913229Spst		*vp++ = 4;				/* -1 byte  */
923229Spst		insert_u_long(hp->swap_server.s_addr, &vp);	/* -4 bytes */
933229Spst		bytesleft -= 6;			/* Fix real count */
943229Spst	}
953229Spst	if (hp->flags.root_path) {
963229Spst		/*
973229Spst		 * Check for room for root_path.  Add 2 to account for
983229Spst		 * TAG_ROOT_PATH and length.
993229Spst		 */
1003229Spst		len = strlen(hp->root_path->string);
1013229Spst		NEED((len + 2), "rp");
1023229Spst		*vp++ = TAG_ROOT_PATH;
1033229Spst		*vp++ = (byte) (len & 0xFF);
1043229Spst		bcopy(hp->root_path->string, vp, len);
1053229Spst		vp += len;
1063229Spst		bytesleft -= len + 2;
1073229Spst	}
1083229Spst	if (hp->flags.dump_file) {
1093229Spst		/*
1103229Spst		 * Check for room for dump_file.  Add 2 to account for
1113229Spst		 * TAG_DUMP_FILE and length.
1123229Spst		 */
1133229Spst		len = strlen(hp->dump_file->string);
1143229Spst		NEED((len + 2), "df");
1153229Spst		*vp++ = TAG_DUMP_FILE;
1163229Spst		*vp++ = (byte) (len & 0xFF);
1173229Spst		bcopy(hp->dump_file->string, vp, len);
1183229Spst		vp += len;
1193229Spst		bytesleft -= len + 2;
1203229Spst	}
1213229Spst	/*
1223229Spst	 * DNS server and domain
1233229Spst	 */
1243229Spst	if (hp->flags.domain_server) {
1253229Spst		if (insert_ip(TAG_DOMAIN_SERVER,
1263229Spst					  hp->domain_server,
1273229Spst					  &vp, &bytesleft))
1283229Spst			NEED(8, "ds");
1293229Spst	}
1303229Spst	if (hp->flags.domain_name) {
1313229Spst		/*
1323229Spst		 * Check for room for domain_name.  Add 2 to account for
1333229Spst		 * TAG_DOMAIN_NAME and length.
1343229Spst		 */
1353229Spst		len = strlen(hp->domain_name->string);
1363229Spst		NEED((len + 2), "dn");
1373229Spst		*vp++ = TAG_DOMAIN_NAME;
1383229Spst		*vp++ = (byte) (len & 0xFF);
1393229Spst		bcopy(hp->domain_name->string, vp, len);
1403229Spst		vp += len;
1413229Spst		bytesleft -= len + 2;
1423229Spst	}
1433229Spst	/*
1443229Spst	 * NIS (YP) server and domain
1453229Spst	 */
1463229Spst	if (hp->flags.nis_server) {
1473229Spst		if (insert_ip(TAG_NIS_SERVER,
1483229Spst					  hp->nis_server,
1493229Spst					  &vp, &bytesleft))
1503229Spst			NEED(8, "ds");
1513229Spst	}
1523229Spst	if (hp->flags.nis_domain) {
1533229Spst		/*
1543229Spst		 * Check for room for nis_domain.  Add 2 to account for
1553229Spst		 * TAG_NIS_DOMAIN and length.
1563229Spst		 */
1573229Spst		len = strlen(hp->nis_domain->string);
1583229Spst		NEED((len + 2), "dn");
1593229Spst		*vp++ = TAG_NIS_DOMAIN;
1603229Spst		*vp++ = (byte) (len & 0xFF);
1613229Spst		bcopy(hp->nis_domain->string, vp, len);
1623229Spst		vp += len;
1633229Spst		bytesleft -= len + 2;
1643229Spst	}
1653229Spst	/* IEN 116 name server */
1663229Spst	if (hp->flags.name_server) {
1673229Spst		if (insert_ip(TAG_NAME_SERVER,
1683229Spst					  hp->name_server,
1693229Spst					  &vp, &bytesleft))
1703229Spst			NEED(8, "ns");
1713229Spst	}
1723229Spst	if (hp->flags.rlp_server) {
1733229Spst		if (insert_ip(TAG_RLP_SERVER,
1743229Spst					  hp->rlp_server,
1753229Spst					  &vp, &bytesleft))
1763229Spst			NEED(8, "rl");
1773229Spst	}
1783229Spst	/* Time server (RFC 868) */
1793229Spst	if (hp->flags.time_server) {
1803229Spst		if (insert_ip(TAG_TIME_SERVER,
1813229Spst					  hp->time_server,
1823229Spst					  &vp, &bytesleft))
1833229Spst			NEED(8, "ts");
1843229Spst	}
1853229Spst	/* NTP (time) Server (RFC 1129) */
1863229Spst	if (hp->flags.ntp_server) {
1873229Spst		if (insert_ip(TAG_NTP_SERVER,
1883229Spst					  hp->ntp_server,
1893229Spst					  &vp, &bytesleft))
1903229Spst			NEED(8, "ts");
1913229Spst	}
1923229Spst	/*
1933229Spst	 * I wonder:  If the hostname were "promoted" into the BOOTP
1943229Spst	 * response part, might these "extension" files possibly be
1953229Spst	 * shared between several clients?
1963229Spst	 *
1973229Spst	 * Also, why not just use longer BOOTP packets with all the
1983229Spst	 * additional length used as option data.  This bootpd version
1993229Spst	 * already supports that feature by replying with the same
2003229Spst	 * packet length as the client request packet. -gwr
2013229Spst	 */
2023229Spst	if (hp->flags.name_switch && hp->flags.send_name) {
2033229Spst		/*
2043229Spst		 * Check for room for hostname.  Add 2 to account for
2053229Spst		 * TAG_HOST_NAME and length.
2063229Spst		 */
2073229Spst		len = strlen(hp->hostname->string);
2083229Spst#if 0
2093229Spst		/*
2103229Spst		 * XXX - Too much magic.  The user can always set the hostname
2113229Spst		 * to the short version in the bootptab file. -gwr
2123229Spst		 */
2133229Spst		if ((len + 2) > bytesleft) {
2143229Spst			/*
2153229Spst			 * Not enough room for full (domain-qualified) hostname, try
2163229Spst			 * stripping it down to just the first field (host).
2173229Spst			 */
21813572Spst			char *tmpstr = hp->hostname->string;
2193229Spst			len = 0;
2203229Spst			while (*tmpstr && (*tmpstr != '.')) {
2213229Spst				tmpstr++;
2223229Spst				len++;
2233229Spst			}
2243229Spst		}
2253229Spst#endif
2263229Spst		NEED((len + 2), "hn");
2273229Spst		*vp++ = TAG_HOST_NAME;
2283229Spst		*vp++ = (byte) (len & 0xFF);
2293229Spst		bcopy(hp->hostname->string, vp, len);
2303229Spst		vp += len;
2313229Spst		bytesleft -= len + 2;
2323229Spst	}
2333229Spst	/*
2343229Spst	 * The rest of these are less important, so they go last.
2353229Spst	 */
2363229Spst	if (hp->flags.lpr_server) {
2373229Spst		if (insert_ip(TAG_LPR_SERVER,
2383229Spst					  hp->lpr_server,
2393229Spst					  &vp, &bytesleft))
2403229Spst			NEED(8, "lp");
2413229Spst	}
2423229Spst	if (hp->flags.cookie_server) {
2433229Spst		if (insert_ip(TAG_COOKIE_SERVER,
2443229Spst					  hp->cookie_server,
2453229Spst					  &vp, &bytesleft))
2463229Spst			NEED(8, "cs");
2473229Spst	}
2483229Spst	if (hp->flags.log_server) {
2493229Spst		if (insert_ip(TAG_LOG_SERVER,
2503229Spst					  hp->log_server,
2513229Spst					  &vp, &bytesleft))
2523229Spst			NEED(8, "lg");
2533229Spst	}
2543229Spst	/*
2553229Spst	 * XXX - Add new tags here (to insert options)
2563229Spst	 */
2573229Spst	if (hp->flags.generic) {
2583229Spst		if (insert_generic(hp->generic, &vp, &bytesleft))
2593229Spst			NEED(64, "(generic)");
2603229Spst	}
2613229Spst	/*
2623229Spst	 * The end marker is inserted by the caller.
2633229Spst	 */
2643229Spst	return (vp - buf);
2653229Spst#undef	NEED
2663229Spst}								/* dovend_rfc1497 */
2673229Spst
2683229Spst
2693229Spst
2703229Spst/*
2713229Spst * Insert a tag value, a length value, and a list of IP addresses into the
2723229Spst * memory buffer indirectly pointed to by "dest".  "tag" is the RFC1048 tag
2733229Spst * number to use, "iplist" is a pointer to a list of IP addresses
2743229Spst * (struct in_addr_list), and "bytesleft" points to an integer which
2753229Spst * indicates the size of the "dest" buffer.
2763229Spst *
2773229Spst * Return zero if everything fits.
2783229Spst *
2793229Spst * This is used to fill the vendor-specific area of a bootp packet in
2803229Spst * conformance to RFC1048.
2813229Spst */
2823229Spst
2833229Spstint
2843229Spstinsert_ip(tag, iplist, dest, bytesleft)
2853229Spst	byte tag;
2863229Spst	struct in_addr_list *iplist;
2873229Spst	byte **dest;
2883229Spst	int *bytesleft;
2893229Spst{
2903229Spst	struct in_addr *addrptr;
2913229Spst	unsigned addrcount = 1;
2923229Spst	byte *d;
2933229Spst
2943229Spst	if (iplist == NULL)
2953229Spst		return (0);
2963229Spst
2973229Spst	if (*bytesleft >= 6) {
2983229Spst		d = *dest;				/* Save pointer for later */
2993229Spst		**dest = tag;
3003229Spst		(*dest) += 2;
3013229Spst		(*bytesleft) -= 2;		/* Account for tag and length */
3023229Spst		addrptr = iplist->addr;
3033229Spst		addrcount = iplist->addrcount;
3043229Spst		while ((*bytesleft >= 4) && (addrcount > 0)) {
3053229Spst			insert_u_long(addrptr->s_addr, dest);
3063229Spst			addrptr++;
3073229Spst			addrcount--;
3083229Spst			(*bytesleft) -= 4;	/* Four bytes per address */
3093229Spst		}
3103229Spst		d[1] = (byte) ((*dest - d - 2) & 0xFF);
3113229Spst	}
3123229Spst	return (addrcount);
3133229Spst}
3143229Spst
3153229Spst
3163229Spst
3173229Spst/*
3183229Spst * Insert generic data into a bootp packet.  The data is assumed to already
3193229Spst * be in RFC1048 format.  It is inserted using a first-fit algorithm which
3203229Spst * attempts to insert as many tags as possible.  Tags and data which are
3213229Spst * too large to fit are skipped; any remaining tags are tried until they
3223229Spst * have all been exhausted.
3233229Spst * Return zero if everything fits.
3243229Spst */
3253229Spst
3263229Spststatic int
3273229Spstinsert_generic(gendata, buff, bytesleft)
3283229Spst	struct shared_bindata *gendata;
3293229Spst	byte **buff;
3303229Spst	int *bytesleft;
3313229Spst{
3323229Spst	byte *srcptr;
3333229Spst	int length, numbytes;
3343229Spst	int skipped = 0;
3353229Spst
3363229Spst	if (gendata == NULL)
3373229Spst		return (0);
3383229Spst
3393229Spst	srcptr = gendata->data;
3403229Spst	length = gendata->length;
3413229Spst	while ((length > 0) && (*bytesleft > 0)) {
3423229Spst		switch (*srcptr) {
3433229Spst		case TAG_END:
3443229Spst			length = 0;			/* Force an exit on next iteration */
3453229Spst			break;
3463229Spst		case TAG_PAD:
3473229Spst			*(*buff)++ = *srcptr++;
3483229Spst			(*bytesleft)--;
3493229Spst			length--;
3503229Spst			break;
3513229Spst		default:
3523229Spst			numbytes = srcptr[1] + 2;
3533229Spst			if (*bytesleft < numbytes)
3543229Spst				skipped += numbytes;
3553229Spst			else {
3563229Spst				bcopy(srcptr, *buff, numbytes);
3573229Spst				(*buff) += numbytes;
3583229Spst				(*bytesleft) -= numbytes;
3593229Spst			}
3603229Spst			srcptr += numbytes;
3613229Spst			length -= numbytes;
3623229Spst			break;
3633229Spst		}
3643229Spst	} /* while */
3653229Spst	return (skipped);
3663229Spst}
3673229Spst
3683229Spst/*
3693229Spst * Insert the unsigned long "value" into memory starting at the byte
3703229Spst * pointed to by the byte pointer (*dest).  (*dest) is updated to
3713229Spst * point to the next available byte.
3723229Spst *
3733229Spst * Since it is desirable to internally store network addresses in network
3743229Spst * byte order (in struct in_addr's), this routine expects longs to be
3753229Spst * passed in network byte order.
3763229Spst *
3773229Spst * However, due to the nature of the main algorithm, the long must be in
3783229Spst * host byte order, thus necessitating the use of ntohl() first.
3793229Spst */
3803229Spst
3813229Spstvoid
3823229Spstinsert_u_long(value, dest)
3833229Spst	u_int32 value;
3843229Spst	byte **dest;
3853229Spst{
3863229Spst	byte *temp;
3873229Spst	int n;
3883229Spst
3893229Spst	value = ntohl(value);		/* Must use host byte order here */
3903229Spst	temp = (*dest += 4);
3913229Spst	for (n = 4; n > 0; n--) {
3923229Spst		*--temp = (byte) (value & 0xFF);
3933229Spst		value >>= 8;
3943229Spst	}
3953229Spst	/* Final result is network byte order */
3963229Spst}
3973229Spst
3983229Spst/*
3993229Spst * Local Variables:
4003229Spst * tab-width: 4
4013229Spst * c-indent-level: 4
4023229Spst * c-argdecl-indent: 4
4033229Spst * c-continued-statement-offset: 4
4043229Spst * c-continued-brace-offset: -4
4053229Spst * c-label-offset: -4
4063229Spst * c-brace-offset: 0
4073229Spst * End:
4083229Spst */
409