]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - stand/libsa/bootp.c
meta.autodep.mk do not override start_utc
[FreeBSD/FreeBSD.git] / stand / libsa / bootp.c
1 /*      $NetBSD: bootp.c,v 1.14 1998/02/16 11:10:54 drochner Exp $      */
2
3 /*
4  * Copyright (c) 1992 Regents of the University of California.
5  * All rights reserved.
6  *
7  * This software was developed by the Computer Systems Engineering group
8  * at Lawrence Berkeley Laboratory under DARPA contract BG 91-66 and
9  * contributed to Berkeley.
10  *
11  * Redistribution and use in source and binary forms, with or without
12  * modification, are permitted provided that the following conditions
13  * are met:
14  * 1. Redistributions of source code must retain the above copyright
15  *    notice, this list of conditions and the following disclaimer.
16  * 2. Redistributions in binary form must reproduce the above copyright
17  *    notice, this list of conditions and the following disclaimer in the
18  *    documentation and/or other materials provided with the distribution.
19  * 3. Neither the name of the University nor the names of its contributors
20  *    may be used to endorse or promote products derived from this software
21  *    without specific prior written permission.
22  *
23  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
24  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
27  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
28  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
29  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
30  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
32  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
33  * SUCH DAMAGE.
34  */
35
36 #include <stddef.h>
37 #include <sys/types.h>
38 #include <sys/limits.h>
39 #include <sys/endian.h>
40 #include <netinet/in.h>
41 #include <netinet/in_systm.h>
42
43 #include <string.h>
44
45 #define BOOTP_DEBUGxx
46 #define SUPPORT_DHCP
47
48 #define DHCP_ENV_NOVENDOR       1       /* do not parse vendor options */
49 #define DHCP_ENV_PXE            10      /* assume pxe vendor options */
50 #define DHCP_ENV_FREEBSD        11      /* assume freebsd vendor options */
51 /* set DHCP_ENV to one of the values above to export dhcp options to kenv */
52 #define DHCP_ENV                DHCP_ENV_NO_VENDOR
53
54 #include "stand.h"
55 #include "net.h"
56 #include "netif.h"
57 #include "bootp.h"
58
59
60 struct in_addr servip;
61
62 static time_t   bot;
63
64 static  char vm_rfc1048[4] = VM_RFC1048;
65 #ifdef BOOTP_VEND_CMU
66 static  char vm_cmu[4] = VM_CMU;
67 #endif
68
69 /* Local forwards */
70 static  ssize_t bootpsend(struct iodesc *, void *, size_t);
71 static  ssize_t bootprecv(struct iodesc *, void **, void **, time_t, void *);
72 static  int vend_rfc1048(u_char *, u_int);
73 #ifdef BOOTP_VEND_CMU
74 static  void vend_cmu(u_char *);
75 #endif
76
77 #ifdef DHCP_ENV         /* export the dhcp response to kenv */
78 struct dhcp_opt;
79 static void setenv_(u_char *cp,  u_char *ep, struct dhcp_opt *opts);
80 #else
81 #define setenv_(a, b, c)
82 #endif
83
84 #ifdef SUPPORT_DHCP
85 static char expected_dhcpmsgtype = -1, dhcp_ok;
86 struct in_addr dhcp_serverip;
87 #endif
88 struct bootp *bootp_response;
89 size_t bootp_response_size;
90
91 static void
92 bootp_fill_request(unsigned char *bp_vend)
93 {
94         /*
95          * We are booting from PXE, we want to send the string
96          * 'PXEClient' to the DHCP server so you have the option of
97          * only responding to PXE aware dhcp requests.
98          */
99         bp_vend[0] = TAG_CLASSID;
100         bp_vend[1] = 9;
101         bcopy("PXEClient", &bp_vend[2], 9);
102         bp_vend[11] = TAG_USER_CLASS;
103         /* len of each user class + number of user class */
104         bp_vend[12] = 8;
105         /* len of the first user class */
106         bp_vend[13] = 7;
107         bcopy("FreeBSD", &bp_vend[14], 7);
108         bp_vend[21] = TAG_PARAM_REQ;
109         bp_vend[22] = 7;
110         bp_vend[23] = TAG_ROOTPATH;
111         bp_vend[24] = TAG_HOSTNAME;
112         bp_vend[25] = TAG_SWAPSERVER;
113         bp_vend[26] = TAG_GATEWAY;
114         bp_vend[27] = TAG_SUBNET_MASK;
115         bp_vend[28] = TAG_INTF_MTU;
116         bp_vend[29] = TAG_SERVERID;
117         bp_vend[30] = TAG_END;
118 }
119
120 /* Fetch required bootp infomation */
121 void
122 bootp(int sock)
123 {
124         void *pkt;
125         struct iodesc *d;
126         struct bootp *bp;
127         struct {
128                 u_char header[HEADER_SIZE];
129                 struct bootp wbootp;
130         } wbuf;
131         struct bootp *rbootp;
132
133 #ifdef BOOTP_DEBUG
134         if (debug)
135                 printf("bootp: socket=%d\n", sock);
136 #endif
137         if (!bot)
138                 bot = getsecs();
139         
140         if (!(d = socktodesc(sock))) {
141                 printf("bootp: bad socket. %d\n", sock);
142                 return;
143         }
144 #ifdef BOOTP_DEBUG
145         if (debug)
146                 printf("bootp: d=%lx\n", (long)d);
147 #endif
148
149         bp = &wbuf.wbootp;
150         bzero(bp, sizeof(*bp));
151
152         bp->bp_op = BOOTREQUEST;
153         bp->bp_htype = 1;               /* 10Mb Ethernet (48 bits) */
154         bp->bp_hlen = 6;
155         bp->bp_xid = htonl(d->xid);
156         MACPY(d->myea, bp->bp_chaddr);
157         strncpy(bp->bp_file, bootfile, sizeof(bp->bp_file));
158         bcopy(vm_rfc1048, bp->bp_vend, sizeof(vm_rfc1048));
159 #ifdef SUPPORT_DHCP
160         bp->bp_vend[4] = TAG_DHCP_MSGTYPE;
161         bp->bp_vend[5] = 1;
162         bp->bp_vend[6] = DHCPDISCOVER;
163         bootp_fill_request(&bp->bp_vend[7]);
164
165 #else
166         bp->bp_vend[4] = TAG_END;
167 #endif
168
169         d->myip.s_addr = INADDR_ANY;
170         d->myport = htons(IPPORT_BOOTPC);
171         d->destip.s_addr = INADDR_BROADCAST;
172         d->destport = htons(IPPORT_BOOTPS);
173
174 #ifdef SUPPORT_DHCP
175         expected_dhcpmsgtype = DHCPOFFER;
176         dhcp_ok = 0;
177 #endif
178
179         if(sendrecv(d,
180                     bootpsend, bp, sizeof(*bp),
181                     bootprecv, &pkt, (void **)&rbootp, NULL) == -1) {
182             printf("bootp: no reply\n");
183             return;
184         }
185
186 #ifdef SUPPORT_DHCP
187         if(dhcp_ok) {
188                 uint32_t leasetime;
189                 bp->bp_vend[6] = DHCPREQUEST;
190                 bp->bp_vend[7] = TAG_REQ_ADDR;
191                 bp->bp_vend[8] = 4;
192                 bcopy(&rbootp->bp_yiaddr, &bp->bp_vend[9], 4);
193                 bp->bp_vend[13] = TAG_SERVERID;
194                 bp->bp_vend[14] = 4;
195                 bcopy(&dhcp_serverip.s_addr, &bp->bp_vend[15], 4);
196                 bp->bp_vend[19] = TAG_LEASETIME;
197                 bp->bp_vend[20] = 4;
198                 leasetime = htonl(300);
199                 bcopy(&leasetime, &bp->bp_vend[21], 4);
200                 bootp_fill_request(&bp->bp_vend[25]);
201
202                 expected_dhcpmsgtype = DHCPACK;
203
204                 free(pkt);
205                 if(sendrecv(d,
206                             bootpsend, bp, sizeof(*bp),
207                             bootprecv, &pkt, (void **)&rbootp, NULL) == -1) {
208                         printf("DHCPREQUEST failed\n");
209                         return;
210                 }
211         }
212 #endif
213
214         myip = d->myip = rbootp->bp_yiaddr;
215         servip = rbootp->bp_siaddr;
216         if (rootip.s_addr == INADDR_ANY)
217                 rootip = servip;
218         bcopy(rbootp->bp_file, bootfile, sizeof(bootfile));
219         bootfile[sizeof(bootfile) - 1] = '\0';
220
221         if (!netmask) {
222                 if (IN_CLASSA(ntohl(myip.s_addr)))
223                         netmask = htonl(IN_CLASSA_NET);
224                 else if (IN_CLASSB(ntohl(myip.s_addr)))
225                         netmask = htonl(IN_CLASSB_NET);
226                 else
227                         netmask = htonl(IN_CLASSC_NET);
228 #ifdef BOOTP_DEBUG
229                 if (debug)
230                         printf("'native netmask' is %s\n", intoa(netmask));
231 #endif
232         }
233
234 #ifdef BOOTP_DEBUG
235         if (debug)
236                 printf("mask: %s\n", intoa(netmask));
237 #endif
238
239         /* We need a gateway if root is on a different net */
240         if (!SAMENET(myip, rootip, netmask)) {
241 #ifdef BOOTP_DEBUG
242                 if (debug)
243                         printf("need gateway for root ip\n");
244 #endif
245         }
246
247         /* Toss gateway if on a different net */
248         if (!SAMENET(myip, gateip, netmask)) {
249 #ifdef BOOTP_DEBUG
250                 if (debug)
251                         printf("gateway ip (%s) bad\n", inet_ntoa(gateip));
252 #endif
253                 gateip.s_addr = 0;
254         }
255
256         /* Bump xid so next request will be unique. */
257         ++d->xid;
258         free(pkt);
259 }
260
261 /* Transmit a bootp request */
262 static ssize_t
263 bootpsend(struct iodesc *d, void *pkt, size_t len)
264 {
265         struct bootp *bp;
266
267 #ifdef BOOTP_DEBUG
268         if (debug)
269                 printf("bootpsend: d=%lx called.\n", (long)d);
270 #endif
271
272         bp = pkt;
273         bp->bp_secs = htons((u_short)(getsecs() - bot));
274
275 #ifdef BOOTP_DEBUG
276         if (debug)
277                 printf("bootpsend: calling sendudp\n");
278 #endif
279
280         return (sendudp(d, pkt, len));
281 }
282
283 static ssize_t
284 bootprecv(struct iodesc *d, void **pkt, void **payload, time_t tleft,
285     void *extra)
286 {
287         ssize_t n;
288         struct bootp *bp;
289         void *ptr;
290
291 #ifdef BOOTP_DEBUG
292         if (debug)
293                 printf("bootp_recvoffer: called\n");
294 #endif
295
296         ptr = NULL;
297         n = readudp(d, &ptr, (void **)&bp, tleft);
298         if (n == -1 || n < sizeof(struct bootp) - BOOTP_VENDSIZE)
299                 goto bad;
300
301 #ifdef BOOTP_DEBUG
302         if (debug)
303                 printf("bootprecv: checked.  bp = %p, n = %zd\n", bp, n);
304 #endif
305         if (bp->bp_xid != htonl(d->xid)) {
306 #ifdef BOOTP_DEBUG
307                 if (debug) {
308                         printf("bootprecv: expected xid 0x%lx, got 0x%x\n",
309                             d->xid, ntohl(bp->bp_xid));
310                 }
311 #endif
312                 goto bad;
313         }
314
315 #ifdef BOOTP_DEBUG
316         if (debug)
317                 printf("bootprecv: got one!\n");
318 #endif
319
320         /* Suck out vendor info */
321         if (bcmp(vm_rfc1048, bp->bp_vend, sizeof(vm_rfc1048)) == 0) {
322                 int vsize = n - offsetof(struct bootp, bp_vend);
323                 if (vend_rfc1048(bp->bp_vend, vsize) != 0)
324                     goto bad;
325
326                 /* Save copy of bootp reply or DHCP ACK message */
327                 if (bp->bp_op == BOOTREPLY &&
328                     ((dhcp_ok == 1 && expected_dhcpmsgtype == DHCPACK) ||
329                     dhcp_ok == 0)) {
330                         free(bootp_response);
331                         bootp_response = malloc(n);
332                         if (bootp_response != NULL) {
333                                 bootp_response_size = n;
334                                 bcopy(bp, bootp_response, bootp_response_size);
335                         }
336                 }
337         }
338 #ifdef BOOTP_VEND_CMU
339         else if (bcmp(vm_cmu, bp->bp_vend, sizeof(vm_cmu)) == 0)
340                 vend_cmu(bp->bp_vend);
341 #endif
342         else
343                 printf("bootprecv: unknown vendor 0x%lx\n", (long)bp->bp_vend);
344
345         *pkt = ptr;
346         *payload = bp;
347         return (n);
348 bad:
349         free(ptr);
350         errno = 0;
351         return (-1);
352 }
353
354 static int
355 vend_rfc1048(u_char *cp, u_int len)
356 {
357         u_char *ep;
358         int size;
359         u_char tag;
360         const char *val;
361
362 #ifdef BOOTP_DEBUG
363         if (debug)
364                 printf("vend_rfc1048 bootp info. len=%d\n", len);
365 #endif
366         ep = cp + len;
367
368         /* Step over magic cookie */
369         cp += sizeof(int);
370
371         setenv_(cp, ep, NULL);
372
373         while (cp < ep) {
374                 tag = *cp++;
375                 size = *cp++;
376                 if (tag == TAG_END)
377                         break;
378
379                 if (tag == TAG_SUBNET_MASK) {
380                         bcopy(cp, &netmask, sizeof(netmask));
381                 }
382                 if (tag == TAG_GATEWAY) {
383                         bcopy(cp, &gateip.s_addr, sizeof(gateip.s_addr));
384                 }
385                 if (tag == TAG_SWAPSERVER) {
386                         /* let it override bp_siaddr */
387                         bcopy(cp, &rootip.s_addr, sizeof(rootip.s_addr));
388                 }
389                 if (tag == TAG_ROOTPATH) {
390                         if ((val = getenv("dhcp.root-path")) == NULL)
391                                 val = (const char *)cp;
392                         strlcpy(rootpath, val, sizeof(rootpath));
393                 }
394                 if (tag == TAG_HOSTNAME) {
395                         if ((val = getenv("dhcp.host-name")) == NULL)
396                                 val = (const char *)cp;
397                         strlcpy(hostname, val, sizeof(hostname));
398                 }
399                 if (tag == TAG_INTF_MTU) {
400                         intf_mtu = 0;
401                         if ((val = getenv("dhcp.interface-mtu")) != NULL) {
402                                 unsigned long tmp;
403                                 char *end;
404
405                                 errno = 0;
406                                 /*
407                                  * Do not allow MTU to exceed max IPv4 packet
408                                  * size, max value of 16-bit word.
409                                  */
410                                 tmp = strtoul(val, &end, 0);
411                                 if (errno != 0 ||
412                                     *val == '\0' || *end != '\0' ||
413                                     tmp > USHRT_MAX) {
414                                         printf("%s: bad value: \"%s\", "
415                                             "ignoring\n",
416                                             "dhcp.interface-mtu", val);
417                                 } else {
418                                         intf_mtu = (u_int)tmp;
419                                 }
420                         }
421                         if (intf_mtu <= 0)
422                                 intf_mtu = be16dec(cp);
423                 }
424 #ifdef SUPPORT_DHCP
425                 if (tag == TAG_DHCP_MSGTYPE) {
426                         if(*cp != expected_dhcpmsgtype)
427                             return(-1);
428                         dhcp_ok = 1;
429                 }
430                 if (tag == TAG_SERVERID) {
431                         bcopy(cp, &dhcp_serverip.s_addr,
432                               sizeof(dhcp_serverip.s_addr));
433                 }
434 #endif
435                 cp += size;
436         }
437         return(0);
438 }
439
440 #ifdef BOOTP_VEND_CMU
441 static void
442 vend_cmu(u_char *cp)
443 {
444         struct cmu_vend *vp;
445
446 #ifdef BOOTP_DEBUG
447         if (debug)
448                 printf("vend_cmu bootp info.\n");
449 #endif
450         vp = (struct cmu_vend *)cp;
451
452         if (vp->v_smask.s_addr != 0) {
453                 netmask = vp->v_smask.s_addr;
454         }
455         if (vp->v_dgate.s_addr != 0) {
456                 gateip = vp->v_dgate;
457         }
458 }
459 #endif
460
461 #ifdef DHCP_ENV
462 /*
463  * Parse DHCP options and store them into kenv variables.
464  * Original code from Danny Braniss, modifications by Luigi Rizzo.
465  *
466  * The parser is driven by tables which specify the type and name of
467  * each dhcp option and how it appears in kenv.
468  * The first entry in the list contains the prefix used to set the kenv
469  * name (including the . if needed), the last entry must have a 0 tag.
470  * Entries do not need to be sorted though it helps for readability.
471  *
472  * Certain vendor-specific tables can be enabled according to DHCP_ENV.
473  * Set it to 0 if you don't want any.
474  */
475 enum opt_fmt { __NONE = 0,
476         __8 = 1, __16 = 2, __32 = 4,    /* Unsigned fields, value=size  */
477         __IP,                           /* IPv4 address                 */
478         __TXT,                          /* C string                     */
479         __BYTES,                        /* byte sequence, printed %02x  */
480         __INDIR,                        /* name=value                   */
481         __ILIST,                        /* name=value;name=value ... */
482         __VE,                           /* vendor specific, recurse     */
483 };
484
485 struct dhcp_opt {
486         uint8_t tag;
487         uint8_t fmt;
488         const char      *desc;
489 };
490
491 static struct dhcp_opt vndr_opt[] = { /* Vendor Specific Options */
492 #if DHCP_ENV == DHCP_ENV_FREEBSD /* FreeBSD table in the original code */
493         {0,     0,      "FreeBSD"},             /* prefix */
494         {1,     __TXT,  "kernel"},
495         {2,     __TXT,  "kernelname"},
496         {3,     __TXT,  "kernel_options"},
497         {4,     __IP,   "usr-ip"},
498         {5,     __TXT,  "conf-path"},
499         {6,     __TXT,  "rc.conf0"},
500         {7,     __TXT,  "rc.conf1"},
501         {8,     __TXT,  "rc.conf2"},
502         {9,     __TXT,  "rc.conf3"},
503         {10,    __TXT,  "rc.conf4"},
504         {11,    __TXT,  "rc.conf5"},
505         {12,    __TXT,  "rc.conf6"},
506         {13,    __TXT,  "rc.conf7"},
507         {14,    __TXT,  "rc.conf8"},
508         {15,    __TXT,  "rc.conf9"},
509
510         {20,    __TXT,  "boot.nfsroot.options"},
511
512         {245,   __INDIR, ""},
513         {246,   __INDIR, ""},
514         {247,   __INDIR, ""},
515         {248,   __INDIR, ""},
516         {249,   __INDIR, ""},
517         {250,   __INDIR, ""},
518         {251,   __INDIR, ""},
519         {252,   __INDIR, ""},
520         {253,   __INDIR, ""},
521         {254,   __INDIR, ""},
522
523 #elif DHCP_ENV == DHCP_ENV_PXE          /* some pxe options, RFC4578 */
524         {0,     0,      "pxe"},         /* prefix */
525         {93,    __16,   "system-architecture"},
526         {94,    __BYTES,        "network-interface"},
527         {97,    __BYTES,        "machine-identifier"},
528 #else                                   /* default (empty) table */
529         {0,     0,      "dhcp.vendor."},                /* prefix */
530 #endif
531         {0,     __TXT,  "%soption-%d"}
532 };
533
534 static struct dhcp_opt dhcp_opt[] = {
535         /* DHCP Option names, formats and codes, from RFC2132. */
536         {0,     0,      "dhcp."},       // prefix
537         {1,     __IP,   "subnet-mask"},
538         {2,     __32,   "time-offset"}, /* this is signed */
539         {3,     __IP,   "routers"},
540         {4,     __IP,   "time-servers"},
541         {5,     __IP,   "ien116-name-servers"},
542         {6,     __IP,   "domain-name-servers"},
543         {7,     __IP,   "log-servers"},
544         {8,     __IP,   "cookie-servers"},
545         {9,     __IP,   "lpr-servers"},
546         {10,    __IP,   "impress-servers"},
547         {11,    __IP,   "resource-location-servers"},
548         {12,    __TXT,  "host-name"},
549         {13,    __16,   "boot-size"},
550         {14,    __TXT,  "merit-dump"},
551         {15,    __TXT,  "domain-name"},
552         {16,    __IP,   "swap-server"},
553         {17,    __TXT,  "root-path"},
554         {18,    __TXT,  "extensions-path"},
555         {19,    __8,    "ip-forwarding"},
556         {20,    __8,    "non-local-source-routing"},
557         {21,    __IP,   "policy-filter"},
558         {22,    __16,   "max-dgram-reassembly"},
559         {23,    __8,    "default-ip-ttl"},
560         {24,    __32,   "path-mtu-aging-timeout"},
561         {25,    __16,   "path-mtu-plateau-table"},
562         {26,    __16,   "interface-mtu"},
563         {27,    __8,    "all-subnets-local"},
564         {28,    __IP,   "broadcast-address"},
565         {29,    __8,    "perform-mask-discovery"},
566         {30,    __8,    "mask-supplier"},
567         {31,    __8,    "perform-router-discovery"},
568         {32,    __IP,   "router-solicitation-address"},
569         {33,    __IP,   "static-routes"},
570         {34,    __8,    "trailer-encapsulation"},
571         {35,    __32,   "arp-cache-timeout"},
572         {36,    __8,    "ieee802-3-encapsulation"},
573         {37,    __8,    "default-tcp-ttl"},
574         {38,    __32,   "tcp-keepalive-interval"},
575         {39,    __8,    "tcp-keepalive-garbage"},
576         {40,    __TXT,  "nis-domain"},
577         {41,    __IP,   "nis-servers"},
578         {42,    __IP,   "ntp-servers"},
579         {43,    __VE,   "vendor-encapsulated-options"},
580         {44,    __IP,   "netbios-name-servers"},
581         {45,    __IP,   "netbios-dd-server"},
582         {46,    __8,    "netbios-node-type"},
583         {47,    __TXT,  "netbios-scope"},
584         {48,    __IP,   "x-font-servers"},
585         {49,    __IP,   "x-display-managers"},
586         {50,    __IP,   "dhcp-requested-address"},
587         {51,    __32,   "dhcp-lease-time"},
588         {52,    __8,    "dhcp-option-overload"},
589         {53,    __8,    "dhcp-message-type"},
590         {54,    __IP,   "dhcp-server-identifier"},
591         {55,    __8,    "dhcp-parameter-request-list"},
592         {56,    __TXT,  "dhcp-message"},
593         {57,    __16,   "dhcp-max-message-size"},
594         {58,    __32,   "dhcp-renewal-time"},
595         {59,    __32,   "dhcp-rebinding-time"},
596         {60,    __TXT,  "vendor-class-identifier"},
597         {61,    __TXT,  "dhcp-client-identifier"},
598         {64,    __TXT,  "nisplus-domain"},
599         {65,    __IP,   "nisplus-servers"},
600         {66,    __TXT,  "tftp-server-name"},
601         {67,    __TXT,  "bootfile-name"},
602         {68,    __IP,   "mobile-ip-home-agent"},
603         {69,    __IP,   "smtp-server"},
604         {70,    __IP,   "pop-server"},
605         {71,    __IP,   "nntp-server"},
606         {72,    __IP,   "www-server"},
607         {73,    __IP,   "finger-server"},
608         {74,    __IP,   "irc-server"},
609         {75,    __IP,   "streettalk-server"},
610         {76,    __IP,   "streettalk-directory-assistance-server"},
611         {77,    __TXT,  "user-class"},
612         {85,    __IP,   "nds-servers"},
613         {86,    __TXT,  "nds-tree-name"},
614         {87,    __TXT,  "nds-context"},
615         {210,   __TXT,  "authenticate"},
616
617         /* use the following entries for arbitrary variables */
618         {246,   __ILIST, ""},
619         {247,   __ILIST, ""},
620         {248,   __ILIST, ""},
621         {249,   __ILIST, ""},
622         {250,   __INDIR, ""},
623         {251,   __INDIR, ""},
624         {252,   __INDIR, ""},
625         {253,   __INDIR, ""},
626         {254,   __INDIR, ""},
627         {0,     __TXT,  "%soption-%d"}
628 };
629
630 /*
631  * parse a dhcp response, set environment variables translating options
632  * names and values according to the tables above. Also set dhcp.tags
633  * to the list of selected tags.
634  */
635 static void
636 setenv_(u_char *cp,  u_char *ep, struct dhcp_opt *opts)
637 {
638     u_char      *ncp;
639     u_char      tag;
640     char        tags[512], *tp; /* the list of tags */
641
642 #define FLD_SEP ','     /* separator in list of elements */
643     ncp = cp;
644     tp = tags;
645     if (opts == NULL)
646         opts = dhcp_opt;
647
648     while (ncp < ep) {
649         unsigned int    size;           /* option size */
650         char *vp, *endv, buf[256];      /* the value buffer */
651         struct dhcp_opt *op;
652
653         tag = *ncp++;                   /* extract tag and size */
654         size = *ncp++;
655         cp = ncp;                       /* current payload */
656         ncp += size;                    /* point to the next option */
657
658         if (tag == TAG_END)
659             break;
660         if (tag == 0)
661             continue;
662
663         for (op = opts+1; op->tag && op->tag != tag; op++)
664                 ;
665         /* if not found we end up on the default entry */
666
667         /*
668          * Copy data into the buffer. While the code uses snprintf, it's also
669          * careful never to insert strings that would be truncated. inet_ntoa is
670          * tricky to know the size, so it assumes we can always insert it
671          * because we reserve 16 bytes at the end of the string for its worst
672          * case. Other cases are covered because they will write fewer than
673          * these reserved bytes at the end. Source strings can't overflow (as
674          * noted below) because buf is 256 bytes and all strings are limited by
675          * the protocol to be 256 bytes or smaller.
676          */
677         vp = buf;
678         *vp = '\0';
679         endv = buf + sizeof(buf) - 1 - 16;      /* last valid write position */
680
681         switch(op->fmt) {
682         case __NONE:
683             break;      /* should not happen */
684
685         case __VE: /* recurse, vendor specific */
686             setenv_(cp, cp+size, vndr_opt);
687             break;
688
689         case __IP:      /* ip address */
690             for (; size > 0 && vp < endv; size -= 4, cp += 4) {
691                 struct  in_addr in_ip;          /* ip addresses */
692                 if (vp != buf)
693                     *vp++ = FLD_SEP;
694                 bcopy(cp, &in_ip.s_addr, sizeof(in_ip.s_addr));
695                 snprintf(vp, endv - vp, "%s", inet_ntoa(in_ip));
696                 vp += strlen(vp);
697             }
698             break;
699
700         case __BYTES:   /* opaque byte string */
701             for (; size > 0 && vp < endv; size -= 1, cp += 1) {
702                 snprintf(vp, endv - vp, "%02x", *cp);
703                 vp += strlen(vp);
704             }
705             break;
706
707         case __TXT:
708             bcopy(cp, buf, size);       /* cannot overflow */
709             buf[size] = 0;
710             break;
711
712         case __32:
713         case __16:
714         case __8:       /* op->fmt is also the length of each field */
715             for (; size > 0 && vp < endv; size -= op->fmt, cp += op->fmt) {
716                 uint32_t v;
717                 if (op->fmt == __32)
718                         v = (cp[0]<<24) + (cp[1]<<16) + (cp[2]<<8) + cp[3];
719                 else if (op->fmt == __16)
720                         v = (cp[0]<<8) + cp[1];
721                 else
722                         v = cp[0];
723                 if (vp != buf)
724                     *vp++ = FLD_SEP;
725                 snprintf(vp, endv - vp, "%u", v);
726                 vp += strlen(vp);
727             }
728             break;
729
730         case __INDIR:   /* name=value */
731         case __ILIST:   /* name=value;name=value... */
732             bcopy(cp, buf, size);       /* cannot overflow */
733             buf[size] = '\0';
734             for (endv = buf; endv; endv = vp) {
735                 char *s = NULL; /* semicolon ? */
736
737                 /* skip leading whitespace */
738                 while (*endv && strchr(" \t\n\r", *endv))
739                     endv++;
740                 vp = strchr(endv, '='); /* find name=value separator */
741                 if (!vp)
742                     break;
743                 *vp++ = 0;
744                 if (op->fmt == __ILIST && (s = strchr(vp, ';')))
745                     *s++ = '\0';
746                 setenv(endv, vp, 1);
747                 vp = s; /* prepare for next round */
748             }
749             buf[0] = '\0';      /* option already done */
750             break;
751         }
752
753         if (tp - tags < sizeof(tags) - 5) {     /* add tag to the list */
754             if (tp != tags)
755                 *tp++ = FLD_SEP;
756             snprintf(tp, sizeof(tags) - (tp - tags), "%d", tag);
757             tp += strlen(tp);
758         }
759         if (buf[0]) {
760             char        env[128];       /* the string name */
761
762             if (op->tag == 0)
763                 snprintf(env, sizeof(env), op->desc, opts[0].desc, tag);
764             else
765                 snprintf(env, sizeof(env), "%s%s", opts[0].desc, op->desc);
766             /*
767              * Do not replace existing values in the environment, so that
768              * locally-obtained values can override server-provided values.
769              */
770             setenv(env, buf, 0);
771         }
772     }
773     if (tp != tags) {
774         char    env[128];       /* the string name */
775         snprintf(env, sizeof(env), "%stags", opts[0].desc);
776         setenv(env, tags, 1);
777     }
778 }
779 #endif /* additional dhcp */