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