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
21 PRIVATE 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
36 int
dovend_rfc1497(struct host * hp,byte * buf,int len)37 dovend_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
269 int
insert_ip(byte tag,struct in_addr_list * iplist,byte ** dest,int * bytesleft)270 insert_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
308 static int
insert_generic(struct shared_bindata * gendata,byte ** buff,int * bytesleft)309 insert_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
360 void
insert_u_long(u_int32 value,byte ** dest)361 insert_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