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