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