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