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