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