]> CyberLeo.Net >> Repos - FreeBSD/stable/10.git/blob - lib/libstand/bootp.c
MFC r335669:
[FreeBSD/stable/10.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 #endif
415                 cp += size;
416         }
417         return(0);
418 }
419
420 #ifdef BOOTP_VEND_CMU
421 static void
422 vend_cmu(cp)
423         u_char *cp;
424 {
425         struct cmu_vend *vp;
426
427 #ifdef BOOTP_DEBUG
428         if (debug)
429                 printf("vend_cmu bootp info.\n");
430 #endif
431         vp = (struct cmu_vend *)cp;
432
433         if (vp->v_smask.s_addr != 0) {
434                 smask = vp->v_smask.s_addr;
435         }
436         if (vp->v_dgate.s_addr != 0) {
437                 gateip = vp->v_dgate;
438         }
439 }
440 #endif
441
442 #ifdef DHCP_ENV
443 /*
444  * Parse DHCP options and store them into kenv variables.
445  * Original code from Danny Braniss, modifications by Luigi Rizzo.
446  *
447  * The parser is driven by tables which specify the type and name of
448  * each dhcp option and how it appears in kenv.
449  * The first entry in the list contains the prefix used to set the kenv
450  * name (including the . if needed), the last entry must have a 0 tag.
451  * Entries do not need to be sorted though it helps for readability.
452  *
453  * Certain vendor-specific tables can be enabled according to DHCP_ENV.
454  * Set it to 0 if you don't want any.
455  */
456 enum opt_fmt { __NONE = 0,
457         __8 = 1, __16 = 2, __32 = 4,    /* Unsigned fields, value=size  */
458         __IP,                           /* IPv4 address                 */
459         __TXT,                          /* C string                     */
460         __BYTES,                        /* byte sequence, printed %02x  */
461         __INDIR,                        /* name=value                   */
462         __ILIST,                        /* name=value;name=value ... */
463         __VE,                           /* vendor specific, recurse     */
464 };
465
466 struct dhcp_opt {
467         uint8_t tag;
468         uint8_t fmt;
469         const char      *desc;
470 };
471
472 static struct dhcp_opt vndr_opt[] = { /* Vendor Specific Options */
473 #if DHCP_ENV == DHCP_ENV_FREEBSD /* FreeBSD table in the original code */
474         {0,     0,      "FreeBSD"},             /* prefix */
475         {1,     __TXT,  "kernel"},
476         {2,     __TXT,  "kernelname"},
477         {3,     __TXT,  "kernel_options"},
478         {4,     __IP,   "usr-ip"},
479         {5,     __TXT,  "conf-path"},
480         {6,     __TXT,  "rc.conf0"},
481         {7,     __TXT,  "rc.conf1"},
482         {8,     __TXT,  "rc.conf2"},
483         {9,     __TXT,  "rc.conf3"},
484         {10,    __TXT,  "rc.conf4"},
485         {11,    __TXT,  "rc.conf5"},
486         {12,    __TXT,  "rc.conf6"},
487         {13,    __TXT,  "rc.conf7"},
488         {14,    __TXT,  "rc.conf8"},
489         {15,    __TXT,  "rc.conf9"},
490
491         {20,    __TXT,  "boot.nfsroot.options"},
492
493         {245,   __INDIR, ""},
494         {246,   __INDIR, ""},
495         {247,   __INDIR, ""},
496         {248,   __INDIR, ""},
497         {249,   __INDIR, ""},
498         {250,   __INDIR, ""},
499         {251,   __INDIR, ""},
500         {252,   __INDIR, ""},
501         {253,   __INDIR, ""},
502         {254,   __INDIR, ""},
503
504 #elif DHCP_ENV == DHCP_ENV_PXE          /* some pxe options, RFC4578 */
505         {0,     0,      "pxe"},         /* prefix */
506         {93,    __16,   "system-architecture"},
507         {94,    __BYTES,        "network-interface"},
508         {97,    __BYTES,        "machine-identifier"},
509 #else                                   /* default (empty) table */
510         {0,     0,      "dhcp.vendor."},                /* prefix */
511 #endif
512         {0,     __TXT,  "%soption-%d"}
513 };
514
515 static struct dhcp_opt dhcp_opt[] = {
516         /* DHCP Option names, formats and codes, from RFC2132. */
517         {0,     0,      "dhcp."},       // prefix
518         {1,     __IP,   "subnet-mask"},
519         {2,     __32,   "time-offset"}, /* this is signed */
520         {3,     __IP,   "routers"},
521         {4,     __IP,   "time-servers"},
522         {5,     __IP,   "ien116-name-servers"},
523         {6,     __IP,   "domain-name-servers"},
524         {7,     __IP,   "log-servers"},
525         {8,     __IP,   "cookie-servers"},
526         {9,     __IP,   "lpr-servers"},
527         {10,    __IP,   "impress-servers"},
528         {11,    __IP,   "resource-location-servers"},
529         {12,    __TXT,  "host-name"},
530         {13,    __16,   "boot-size"},
531         {14,    __TXT,  "merit-dump"},
532         {15,    __TXT,  "domain-name"},
533         {16,    __IP,   "swap-server"},
534         {17,    __TXT,  "root-path"},
535         {18,    __TXT,  "extensions-path"},
536         {19,    __8,    "ip-forwarding"},
537         {20,    __8,    "non-local-source-routing"},
538         {21,    __IP,   "policy-filter"},
539         {22,    __16,   "max-dgram-reassembly"},
540         {23,    __8,    "default-ip-ttl"},
541         {24,    __32,   "path-mtu-aging-timeout"},
542         {25,    __16,   "path-mtu-plateau-table"},
543         {26,    __16,   "interface-mtu"},
544         {27,    __8,    "all-subnets-local"},
545         {28,    __IP,   "broadcast-address"},
546         {29,    __8,    "perform-mask-discovery"},
547         {30,    __8,    "mask-supplier"},
548         {31,    __8,    "perform-router-discovery"},
549         {32,    __IP,   "router-solicitation-address"},
550         {33,    __IP,   "static-routes"},
551         {34,    __8,    "trailer-encapsulation"},
552         {35,    __32,   "arp-cache-timeout"},
553         {36,    __8,    "ieee802-3-encapsulation"},
554         {37,    __8,    "default-tcp-ttl"},
555         {38,    __32,   "tcp-keepalive-interval"},
556         {39,    __8,    "tcp-keepalive-garbage"},
557         {40,    __TXT,  "nis-domain"},
558         {41,    __IP,   "nis-servers"},
559         {42,    __IP,   "ntp-servers"},
560         {43,    __VE,   "vendor-encapsulated-options"},
561         {44,    __IP,   "netbios-name-servers"},
562         {45,    __IP,   "netbios-dd-server"},
563         {46,    __8,    "netbios-node-type"},
564         {47,    __TXT,  "netbios-scope"},
565         {48,    __IP,   "x-font-servers"},
566         {49,    __IP,   "x-display-managers"},
567         {50,    __IP,   "dhcp-requested-address"},
568         {51,    __32,   "dhcp-lease-time"},
569         {52,    __8,    "dhcp-option-overload"},
570         {53,    __8,    "dhcp-message-type"},
571         {54,    __IP,   "dhcp-server-identifier"},
572         {55,    __8,    "dhcp-parameter-request-list"},
573         {56,    __TXT,  "dhcp-message"},
574         {57,    __16,   "dhcp-max-message-size"},
575         {58,    __32,   "dhcp-renewal-time"},
576         {59,    __32,   "dhcp-rebinding-time"},
577         {60,    __TXT,  "vendor-class-identifier"},
578         {61,    __TXT,  "dhcp-client-identifier"},
579         {64,    __TXT,  "nisplus-domain"},
580         {65,    __IP,   "nisplus-servers"},
581         {66,    __TXT,  "tftp-server-name"},
582         {67,    __TXT,  "bootfile-name"},
583         {68,    __IP,   "mobile-ip-home-agent"},
584         {69,    __IP,   "smtp-server"},
585         {70,    __IP,   "pop-server"},
586         {71,    __IP,   "nntp-server"},
587         {72,    __IP,   "www-server"},
588         {73,    __IP,   "finger-server"},
589         {74,    __IP,   "irc-server"},
590         {75,    __IP,   "streettalk-server"},
591         {76,    __IP,   "streettalk-directory-assistance-server"},
592         {77,    __TXT,  "user-class"},
593         {85,    __IP,   "nds-servers"},
594         {86,    __TXT,  "nds-tree-name"},
595         {87,    __TXT,  "nds-context"},
596         {210,   __TXT,  "authenticate"},
597
598         /* use the following entries for arbitrary variables */
599         {246,   __ILIST, ""},
600         {247,   __ILIST, ""},
601         {248,   __ILIST, ""},
602         {249,   __ILIST, ""},
603         {250,   __INDIR, ""},
604         {251,   __INDIR, ""},
605         {252,   __INDIR, ""},
606         {253,   __INDIR, ""},
607         {254,   __INDIR, ""},
608         {0,     __TXT,  "%soption-%d"}
609 };
610
611 /*
612  * parse a dhcp response, set environment variables translating options
613  * names and values according to the tables above. Also set dhcp.tags
614  * to the list of selected tags.
615  */
616 static void
617 setenv_(u_char *cp,  u_char *ep, struct dhcp_opt *opts)
618 {
619     u_char      *ncp;
620     u_char      tag;
621     char        tags[512], *tp; /* the list of tags */
622
623 #define FLD_SEP ','     /* separator in list of elements */
624     ncp = cp;
625     tp = tags;
626     if (opts == NULL)
627         opts = dhcp_opt;
628
629     while (ncp < ep) {
630         unsigned int    size;           /* option size */
631         char *vp, *endv, buf[256];      /* the value buffer */
632         struct dhcp_opt *op;
633
634         tag = *ncp++;                   /* extract tag and size */
635         size = *ncp++;
636         cp = ncp;                       /* current payload */
637         ncp += size;                    /* point to the next option */
638
639         if (tag == TAG_END)
640             break;
641         if (tag == 0)
642             continue;
643
644         for (op = opts+1; op->tag && op->tag != tag; op++)
645                 ;
646         /* if not found we end up on the default entry */
647
648         /*
649          * Copy data into the buffer. libstand does not have snprintf so we
650          * need to be careful with sprintf(). With strings, the source is
651          * always <256 char so shorter than the buffer so we are safe; with
652          * other arguments, the longest string is inet_ntoa which is 16 bytes
653          * so we make sure to have always enough room in the string before
654          * trying an sprint.
655          */
656         vp = buf;
657         *vp = '\0';
658         endv = buf + sizeof(buf) - 1 - 16;      /* last valid write position */
659
660         switch(op->fmt) {
661         case __NONE:
662             break;      /* should not happen */
663
664         case __VE: /* recurse, vendor specific */
665             setenv_(cp, cp+size, vndr_opt);
666             break;
667
668         case __IP:      /* ip address */
669             for (; size > 0 && vp < endv; size -= 4, cp += 4) {
670                 struct  in_addr in_ip;          /* ip addresses */
671                 if (vp != buf)
672                     *vp++ = FLD_SEP;
673                 bcopy(cp, &in_ip.s_addr, sizeof(in_ip.s_addr));
674                 sprintf(vp, "%s", inet_ntoa(in_ip));
675                 vp += strlen(vp);
676             }
677             break;
678
679         case __BYTES:   /* opaque byte string */
680             for (; size > 0 && vp < endv; size -= 1, cp += 1) {
681                 sprintf(vp, "%02x", *cp);
682                 vp += strlen(vp);
683             }
684             break;
685
686         case __TXT:
687             bcopy(cp, buf, size);       /* cannot overflow */
688             buf[size] = 0;
689             break;
690
691         case __32:
692         case __16:
693         case __8:       /* op->fmt is also the length of each field */
694             for (; size > 0 && vp < endv; size -= op->fmt, cp += op->fmt) {
695                 uint32_t v;
696                 if (op->fmt == __32)
697                         v = (cp[0]<<24) + (cp[1]<<16) + (cp[2]<<8) + cp[3];
698                 else if (op->fmt == __16)
699                         v = (cp[0]<<8) + cp[1];
700                 else
701                         v = cp[0];
702                 if (vp != buf)
703                     *vp++ = FLD_SEP;
704                 sprintf(vp, "%u", v);
705                 vp += strlen(vp);
706             }
707             break;
708
709         case __INDIR:   /* name=value */
710         case __ILIST:   /* name=value;name=value... */
711             bcopy(cp, buf, size);       /* cannot overflow */
712             buf[size] = '\0';
713             for (endv = buf; endv; endv = vp) {
714                 u_char *s = NULL;       /* semicolon ? */
715
716                 /* skip leading whitespace */
717                 while (*endv && strchr(" \t\n\r", *endv))
718                     endv++;
719                 vp = strchr(endv, '='); /* find name=value separator */
720                 if (!vp)
721                     break;
722                 *vp++ = 0;
723                 if (op->fmt == __ILIST && (s = strchr(vp, ';')))
724                     *s++ = '\0';
725                 setenv(endv, vp, 1);
726                 vp = s; /* prepare for next round */
727             }
728             buf[0] = '\0';      /* option already done */
729         }
730
731         if (tp - tags < sizeof(tags) - 5) {     /* add tag to the list */
732             if (tp != tags)
733                 *tp++ = FLD_SEP;
734             sprintf(tp, "%d", tag);
735             tp += strlen(tp);
736         }
737         if (buf[0]) {
738             char        env[128];       /* the string name */
739
740             if (op->tag == 0)
741                 sprintf(env, op->desc, opts[0].desc, tag);
742             else
743                 sprintf(env, "%s%s", opts[0].desc, op->desc);
744             /*
745              * Do not replace existing values in the environment, so that
746              * locally-obtained values can override server-provided values.
747              */
748             setenv(env, buf, 0);
749         }
750     }
751     if (tp != tags) {
752         char    env[128];       /* the string name */
753         sprintf(env, "%stags", opts[0].desc);
754         setenv(env, tags, 1);
755     }
756 }
757 #endif /* additional dhcp */