1/*
2 * dovend.c : Inserts all but the first few vendor options.
3 */
4
5#include <sys/types.h>
6
7#include <netinet/in.h>
8#include <arpa/inet.h>			/* inet_ntoa */
9
10#include <stdlib.h>
11#include <stdio.h>
12#include <string.h>
13#include <errno.h>
14#include <syslog.h>
15
16#include "bootp.h"
17#include "bootpd.h"
18#include "report.h"
19#include "dovend.h"
20
21PRIVATE int insert_generic(struct shared_bindata *, byte **, int *);
22
23/*
24 * Insert the 2nd part of the options into an option buffer.
25 * Return amount of space used.
26 *
27 * This inserts everything EXCEPT:
28 *   magic cookie, subnet mask, gateway, bootsize, extension file
29 * Those are handled separately (in bootpd.c) to allow this function
30 * to be shared between bootpd and bootpef.
31 *
32 * When an "extension file" is in use, the options inserted by
33 * this function go into the exten_file, not the bootp response.
34 */
35
36int
37dovend_rfc1497(struct host *hp, byte *buf, int len)
38{
39	int bytesleft = len;
40	byte *vp = buf;
41
42	static const char noroom[] = "%s: No room for \"%s\" option";
43#define	NEED(LEN, MSG) do                       \
44		if (bytesleft < (LEN)) {         	    \
45			report(LOG_NOTICE, noroom,          \
46				   hp->hostname->string, MSG);  \
47			return (vp - buf);                  \
48		} while (0)
49
50	/*
51	 * Note that the following have already been inserted:
52	 *   magic_cookie, subnet_mask, gateway, bootsize
53	 *
54	 * The remaining options are inserted in order of importance.
55	 * (Of course the importance of each is a matter of opinion.)
56	 * The option insertion order should probably be configurable.
57	 *
58	 * This is the order used in the NetBSD version.  Can anyone
59	 * explain why the time_offset and swap_server are first?
60	 * Also, why is the hostname so far down the list?  -gwr
61	 */
62
63	if (hp->flags.time_offset) {
64		NEED(6, "to");
65		*vp++ = TAG_TIME_OFFSET;/* -1 byte  */
66		*vp++ = 4;				/* -1 byte  */
67		insert_u_long(htonl(hp->time_offset), &vp);	/* -4 bytes */
68		bytesleft -= 6;
69	}
70	/*
71	 * swap server, root path, dump path
72	 */
73	if (hp->flags.swap_server) {
74		NEED(6, "sw");
75		/* There is just one SWAP_SERVER, so it is not an iplist. */
76		*vp++ = TAG_SWAP_SERVER;/* -1 byte  */
77		*vp++ = 4;				/* -1 byte  */
78		insert_u_long(hp->swap_server.s_addr, &vp);	/* -4 bytes */
79		bytesleft -= 6;			/* Fix real count */
80	}
81	if (hp->flags.root_path) {
82		/*
83		 * Check for room for root_path.  Add 2 to account for
84		 * TAG_ROOT_PATH and length.
85		 */
86		len = strlen(hp->root_path->string);
87		NEED((len + 2), "rp");
88		*vp++ = TAG_ROOT_PATH;
89		*vp++ = (byte) (len & 0xFF);
90		bcopy(hp->root_path->string, vp, len);
91		vp += len;
92		bytesleft -= len + 2;
93	}
94	if (hp->flags.dump_file) {
95		/*
96		 * Check for room for dump_file.  Add 2 to account for
97		 * TAG_DUMP_FILE and length.
98		 */
99		len = strlen(hp->dump_file->string);
100		NEED((len + 2), "df");
101		*vp++ = TAG_DUMP_FILE;
102		*vp++ = (byte) (len & 0xFF);
103		bcopy(hp->dump_file->string, vp, len);
104		vp += len;
105		bytesleft -= len + 2;
106	}
107	/*
108	 * DNS server and domain
109	 */
110	if (hp->flags.domain_server) {
111		if (insert_ip(TAG_DOMAIN_SERVER,
112					  hp->domain_server,
113					  &vp, &bytesleft))
114			NEED(8, "ds");
115	}
116	if (hp->flags.domain_name) {
117		/*
118		 * Check for room for domain_name.  Add 2 to account for
119		 * TAG_DOMAIN_NAME and length.
120		 */
121		len = strlen(hp->domain_name->string);
122		NEED((len + 2), "dn");
123		*vp++ = TAG_DOMAIN_NAME;
124		*vp++ = (byte) (len & 0xFF);
125		bcopy(hp->domain_name->string, vp, len);
126		vp += len;
127		bytesleft -= len + 2;
128	}
129	/*
130	 * NIS (YP) server and domain
131	 */
132	if (hp->flags.nis_server) {
133		if (insert_ip(TAG_NIS_SERVER,
134					  hp->nis_server,
135					  &vp, &bytesleft))
136			NEED(8, "ys");
137	}
138	if (hp->flags.nis_domain) {
139		/*
140		 * Check for room for nis_domain.  Add 2 to account for
141		 * TAG_NIS_DOMAIN and length.
142		 */
143		len = strlen(hp->nis_domain->string);
144		NEED((len + 2), "yn");
145		*vp++ = TAG_NIS_DOMAIN;
146		*vp++ = (byte) (len & 0xFF);
147		bcopy(hp->nis_domain->string, vp, len);
148		vp += len;
149		bytesleft -= len + 2;
150	}
151	/* IEN 116 name server */
152	if (hp->flags.name_server) {
153		if (insert_ip(TAG_NAME_SERVER,
154					  hp->name_server,
155					  &vp, &bytesleft))
156			NEED(8, "ns");
157	}
158	if (hp->flags.rlp_server) {
159		if (insert_ip(TAG_RLP_SERVER,
160					  hp->rlp_server,
161					  &vp, &bytesleft))
162			NEED(8, "rl");
163	}
164	/* Time server (RFC 868) */
165	if (hp->flags.time_server) {
166		if (insert_ip(TAG_TIME_SERVER,
167					  hp->time_server,
168					  &vp, &bytesleft))
169			NEED(8, "ts");
170	}
171	/* NTP (time) Server (RFC 1129) */
172	if (hp->flags.ntp_server) {
173		if (insert_ip(TAG_NTP_SERVER,
174					  hp->ntp_server,
175					  &vp, &bytesleft))
176			NEED(8, "nt");
177	}
178	/*
179	 * I wonder:  If the hostname were "promoted" into the BOOTP
180	 * response part, might these "extension" files possibly be
181	 * shared between several clients?
182	 *
183	 * Also, why not just use longer BOOTP packets with all the
184	 * additional length used as option data.  This bootpd version
185	 * already supports that feature by replying with the same
186	 * packet length as the client request packet. -gwr
187	 */
188	if (hp->flags.name_switch && hp->flags.send_name) {
189		/*
190		 * Check for room for hostname.  Add 2 to account for
191		 * TAG_HOST_NAME and length.
192		 */
193		len = strlen(hp->hostname->string);
194#if 0
195		/*
196		 * XXX - Too much magic.  The user can always set the hostname
197		 * to the short version in the bootptab file. -gwr
198		 */
199		if ((len + 2) > bytesleft) {
200			/*
201			 * Not enough room for full (domain-qualified) hostname, try
202			 * stripping it down to just the first field (host).
203			 */
204			char *tmpstr = hp->hostname->string;
205			len = 0;
206			while (*tmpstr && (*tmpstr != '.')) {
207				tmpstr++;
208				len++;
209			}
210		}
211#endif
212		NEED((len + 2), "hn");
213		*vp++ = TAG_HOST_NAME;
214		*vp++ = (byte) (len & 0xFF);
215		bcopy(hp->hostname->string, vp, len);
216		vp += len;
217		bytesleft -= len + 2;
218	}
219	/*
220	 * The rest of these are less important, so they go last.
221	 */
222	if (hp->flags.lpr_server) {
223		if (insert_ip(TAG_LPR_SERVER,
224					  hp->lpr_server,
225					  &vp, &bytesleft))
226			NEED(8, "lp");
227	}
228	if (hp->flags.cookie_server) {
229		if (insert_ip(TAG_COOKIE_SERVER,
230					  hp->cookie_server,
231					  &vp, &bytesleft))
232			NEED(8, "cs");
233	}
234	if (hp->flags.log_server) {
235		if (insert_ip(TAG_LOG_SERVER,
236					  hp->log_server,
237					  &vp, &bytesleft))
238			NEED(8, "lg");
239	}
240	/*
241	 * XXX - Add new tags here (to insert options)
242	 */
243	if (hp->flags.generic) {
244		if (insert_generic(hp->generic, &vp, &bytesleft))
245			NEED(64, "(generic)");
246	}
247	/*
248	 * The end marker is inserted by the caller.
249	 */
250	return (vp - buf);
251#undef	NEED
252}								/* dovend_rfc1497 */
253
254
255
256/*
257 * Insert a tag value, a length value, and a list of IP addresses into the
258 * memory buffer indirectly pointed to by "dest".  "tag" is the RFC1048 tag
259 * number to use, "iplist" is a pointer to a list of IP addresses
260 * (struct in_addr_list), and "bytesleft" points to an integer which
261 * indicates the size of the "dest" buffer.
262 *
263 * Return zero if everything fits.
264 *
265 * This is used to fill the vendor-specific area of a bootp packet in
266 * conformance to RFC1048.
267 */
268
269int
270insert_ip(byte tag, struct in_addr_list *iplist, byte **dest, int *bytesleft)
271{
272	struct in_addr *addrptr;
273	unsigned addrcount = 1;
274	byte *d;
275
276	if (iplist == NULL)
277		return (0);
278
279	if (*bytesleft >= 6) {
280		d = *dest;				/* Save pointer for later */
281		**dest = tag;
282		(*dest) += 2;
283		(*bytesleft) -= 2;		/* Account for tag and length */
284		addrptr = iplist->addr;
285		addrcount = iplist->addrcount;
286		while ((*bytesleft >= 4) && (addrcount > 0)) {
287			insert_u_long(addrptr->s_addr, dest);
288			addrptr++;
289			addrcount--;
290			(*bytesleft) -= 4;	/* Four bytes per address */
291		}
292		d[1] = (byte) ((*dest - d - 2) & 0xFF);
293	}
294	return (addrcount);
295}
296
297
298
299/*
300 * Insert generic data into a bootp packet.  The data is assumed to already
301 * be in RFC1048 format.  It is inserted using a first-fit algorithm which
302 * attempts to insert as many tags as possible.  Tags and data which are
303 * too large to fit are skipped; any remaining tags are tried until they
304 * have all been exhausted.
305 * Return zero if everything fits.
306 */
307
308static int
309insert_generic(struct shared_bindata *gendata, byte **buff, int *bytesleft)
310{
311	byte *srcptr;
312	int length, numbytes;
313	int skipped = 0;
314
315	if (gendata == NULL)
316		return (0);
317
318	srcptr = gendata->data;
319	length = gendata->length;
320	while ((length > 0) && (*bytesleft > 0)) {
321		switch (*srcptr) {
322		case TAG_END:
323			length = 0;			/* Force an exit on next iteration */
324			break;
325		case TAG_PAD:
326			*(*buff)++ = *srcptr++;
327			(*bytesleft)--;
328			length--;
329			break;
330		default:
331			numbytes = srcptr[1] + 2;
332			if (*bytesleft < numbytes)
333				skipped += numbytes;
334			else {
335				bcopy(srcptr, *buff, numbytes);
336				(*buff) += numbytes;
337				(*bytesleft) -= numbytes;
338			}
339			srcptr += numbytes;
340			length -= numbytes;
341			break;
342		}
343	} /* while */
344	return (skipped);
345}
346
347/*
348 * Insert the unsigned long "value" into memory starting at the byte
349 * pointed to by the byte pointer (*dest).  (*dest) is updated to
350 * point to the next available byte.
351 *
352 * Since it is desirable to internally store network addresses in network
353 * byte order (in struct in_addr's), this routine expects longs to be
354 * passed in network byte order.
355 *
356 * However, due to the nature of the main algorithm, the long must be in
357 * host byte order, thus necessitating the use of ntohl() first.
358 */
359
360void
361insert_u_long(u_int32 value, byte **dest)
362{
363	byte *temp;
364	int n;
365
366	value = ntohl(value);		/* Must use host byte order here */
367	temp = (*dest += 4);
368	for (n = 4; n > 0; n--) {
369		*--temp = (byte) (value & 0xFF);
370		value >>= 8;
371	}
372	/* Final result is network byte order */
373}
374
375/*
376 * Local Variables:
377 * tab-width: 4
378 * c-indent-level: 4
379 * c-argdecl-indent: 4
380 * c-continued-statement-offset: 4
381 * c-continued-brace-offset: -4
382 * c-label-offset: -4
383 * c-brace-offset: 0
384 * End:
385 */
386