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