source: rtems-libbsd/dhcpcd/dhcp-common.c @ be2e60c

4.1155-freebsd-126-freebsd-12freebsd-9.3
Last change on this file since be2e60c was f2ed769, checked in by Sebastian Huber <sebastian.huber@…>, on 01/30/14 at 12:29:46

DHCPCD(8): Import

Import DHCPCD(8) from:

http://roy.marples.name/projects/dhcpcd/

The upstream sources can be obtained via:

fossil clone http://roy.marples.name/projects/dhcpcd

The imported version is 2014-01-29 19:46:44 [6b209507bb].

  • Property mode set to 100644
File size: 13.8 KB
Line 
1/*
2 * dhcpcd - DHCP client daemon
3 * Copyright (c) 2006-2014 Roy Marples <roy@marples.name>
4 * All rights reserved
5
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 * 1. Redistributions of source code must retain the above copyright
10 *    notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 *    notice, this list of conditions and the following disclaimer in the
13 *    documentation and/or other materials provided with the distribution.
14 *
15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25 * SUCH DAMAGE.
26 */
27
28#include <ctype.h>
29#include <errno.h>
30#include <fcntl.h>
31#include <stdlib.h>
32#include <string.h>
33#include <syslog.h>
34#include <unistd.h>
35
36#include "config.h"
37#include "common.h"
38#include "dhcp-common.h"
39#include "dhcp.h"
40
41/* DHCP Enterprise options, RFC3925 */
42struct dhcp_opt *vivso = NULL;
43size_t vivso_len = 0;
44
45struct dhcp_opt *
46vivso_find(uint16_t iana_en, const void *arg)
47{
48        const struct interface *ifp = arg;
49        size_t i;
50        struct dhcp_opt *opt;
51
52        if (arg) {
53                ifp = arg;
54                for (i = 0, opt = ifp->options->vivso_override;
55                    i < ifp->options->vivso_override_len;
56                    i++, opt++)
57                        if (opt->option == iana_en)
58                                return opt;
59        }
60        for (i = 0, opt = vivso; i < vivso_len; i++, opt++)
61                if (opt->option == iana_en)
62                        return opt;
63        return NULL;
64}
65
66int
67make_option_mask(const struct dhcp_opt *dopts, size_t dopts_len,
68    uint8_t *mask, const char *opts, int add)
69{
70        char *token, *o, *p, *t;
71        const struct dhcp_opt *opt;
72        int match;
73        unsigned int n;
74        size_t i;
75
76        o = p = strdup(opts);
77        if (opts == NULL)
78                return -1;
79        while ((token = strsep(&p, ", "))) {
80                if (*token == '\0')
81                        continue;
82                for (i = 0, opt = dopts; i < dopts_len; i++, opt++) {
83                        match = 0;
84                        if (strcmp(opt->var, token) == 0)
85                                match = 1;
86                        else {
87                                errno = 0;
88                                n = strtol(token, &t, 0);
89                                if (errno == 0 && !*t)
90                                        if (opt->option == n)
91                                                match = 1;
92                        }
93                        if (match) {
94                                if (add == 2 && !(opt->type & ADDRIPV4)) {
95                                        free(o);
96                                        errno = EINVAL;
97                                        return -1;
98                                }
99                                if (add == 1 || add == 2)
100                                        add_option_mask(mask,
101                                            opt->option);
102                                else
103                                        del_option_mask(mask,
104                                            opt->option);
105                                break;
106                        }
107                }
108                if (!opt->option) {
109                        free(o);
110                        errno = ENOENT;
111                        return -1;
112                }
113        }
114        free(o);
115        return 0;
116}
117
118size_t
119encode_rfc1035(const char *src, uint8_t *dst)
120{
121        uint8_t *p;
122        uint8_t *lp;
123        size_t len;
124        uint8_t has_dot;
125
126        if (src == NULL || *src == '\0')
127                return 0;
128
129        if (dst) {
130                p = dst;
131                lp = p++;
132        }
133        /* Silence bogus GCC warnings */
134        else
135                p = lp = NULL;
136
137        len = 1;
138        has_dot = 0;
139        for (; *src; src++) {
140                if (*src == '\0')
141                        break;
142                if (*src == '.') {
143                        /* Skip the trailing . */
144                        if (src[1] == '\0')
145                                break;
146                        has_dot = 1;
147                        if (dst) {
148                                *lp = p - lp - 1;
149                                if (*lp == '\0')
150                                        return len;
151                                lp = p++;
152                        }
153                } else if (dst)
154                        *p++ = (uint8_t)*src;
155                len++;
156        }
157
158        if (dst) {
159                *lp = p - lp - 1;
160                if (has_dot)
161                        *p++ = '\0';
162        }
163
164        if (has_dot)
165                len++;
166
167        return len;
168}
169
170/* Decode an RFC3397 DNS search order option into a space
171 * separated string. Returns length of string (including
172 * terminating zero) or zero on error. out may be NULL
173 * to just determine output length. */
174ssize_t
175decode_rfc3397(char *out, ssize_t len, int pl, const uint8_t *p)
176{
177        const char *start;
178        ssize_t start_len;
179        const uint8_t *r, *q = p;
180        int count = 0, l, hops;
181        uint8_t ltype;
182
183        start = out;
184        start_len = len;
185        while (q - p < pl) {
186                r = NULL;
187                hops = 0;
188                /* Check we are inside our length again in-case
189                 * the name isn't fully qualified (ie, not terminated) */
190                while (q - p < pl && (l = *q++)) {
191                        ltype = l & 0xc0;
192                        if (ltype == 0x80 || ltype == 0x40)
193                                return 0;
194                        else if (ltype == 0xc0) { /* pointer */
195                                l = (l & 0x3f) << 8;
196                                l |= *q++;
197                                /* save source of first jump. */
198                                if (!r)
199                                        r = q;
200                                hops++;
201                                if (hops > 255)
202                                        return 0;
203                                q = p + l;
204                                if (q - p >= pl)
205                                        return 0;
206                        } else {
207                                /* straightforward name segment, add with '.' */
208                                count += l + 1;
209                                if (out) {
210                                        if ((ssize_t)l + 1 > len) {
211                                                errno = ENOBUFS;
212                                                return -1;
213                                        }
214                                        memcpy(out, q, l);
215                                        out += l;
216                                        *out++ = '.';
217                                        len -= l;
218                                        len--;
219                                }
220                                q += l;
221                        }
222                }
223                /* change last dot to space */
224                if (out && out != start)
225                        *(out - 1) = ' ';
226                if (r)
227                        q = r;
228        }
229
230        /* change last space to zero terminator */
231        if (out) {
232                if (out != start)
233                        *(out - 1) = '\0';
234                else if (start_len > 0)
235                        *out = '\0';
236        }
237
238        return count;
239}
240
241ssize_t
242print_string(char *s, ssize_t len, int dl, const uint8_t *data)
243{
244        uint8_t c;
245        const uint8_t *e, *p;
246        ssize_t bytes = 0;
247        ssize_t r;
248
249        e = data + dl;
250        while (data < e) {
251                c = *data++;
252                if (c == '\0') {
253                        /* If rest is all NULL, skip it. */
254                        for (p = data; p < e; p++)
255                                if (*p != '\0')
256                                        break;
257                        if (p == e)
258                                break;
259                }
260                if (!isascii(c) || !isprint(c)) {
261                        if (s) {
262                                if (len < 5) {
263                                        errno = ENOBUFS;
264                                        return -1;
265                                }
266                                r = snprintf(s, len, "\\%03o", c);
267                                len -= r;
268                                bytes += r;
269                                s += r;
270                        } else
271                                bytes += 4;
272                        continue;
273                }
274                switch (c) {
275                case '"':  /* FALLTHROUGH */
276                case '\'': /* FALLTHROUGH */
277                case '$':  /* FALLTHROUGH */
278                case '`':  /* FALLTHROUGH */
279                case '\\': /* FALLTHROUGH */
280                case '|':  /* FALLTHROUGH */
281                case '&':
282                        if (s) {
283                                if (len < 3) {
284                                        errno = ENOBUFS;
285                                        return -1;
286                                }
287                                *s++ = '\\';
288                                len--;
289                        }
290                        bytes++;
291                        break;
292                }
293                if (s) {
294                        *s++ = c;
295                        len--;
296                }
297                bytes++;
298        }
299
300        /* NULL */
301        if (s)
302                *s = '\0';
303        bytes++;
304        return bytes;
305}
306
307#define ADDRSZ          4
308#define ADDR6SZ         16
309static size_t
310dhcp_optlen(const struct dhcp_opt *opt, size_t dl)
311{
312        size_t sz;
313
314        if (dl == 0)
315                return 0;
316
317        if (opt->type == 0 ||
318            opt->type & (STRING | BINHEX | RFC3442 | RFC5969))
319        {
320                if (opt->len) {
321                        if ((size_t)opt->len > dl)
322                                return 0;
323                        return opt->len;
324                }
325                return dl;
326        }
327
328        if ((opt->type & (ADDRIPV4 | ARRAY)) == (ADDRIPV4 | ARRAY)) {
329                if (dl < ADDRSZ)
330                        return 0;
331                return dl - (dl % ADDRSZ);
332        }
333
334        if ((opt->type & (ADDRIPV6 | ARRAY)) == (ADDRIPV6 | ARRAY)) {
335                if (dl < ADDR6SZ)
336                        return 0;
337                return dl - (dl % ADDR6SZ);
338        }
339
340        sz = 0;
341        if (opt->type & (UINT32 | ADDRIPV4))
342                sz = sizeof(uint32_t);
343        else if (opt->type & UINT16)
344                sz = sizeof(uint16_t);
345        else if (opt->type & UINT8)
346                sz = sizeof(uint8_t);
347        else if (opt->type & ADDRIPV6)
348                sz = ADDR6SZ;
349        else
350                /* If we don't know the size, assume it's valid */
351                return dl;
352        return (dl < sz ? 0 : sz);
353}
354
355#ifdef INET6
356#define PO_IFNAME
357#else
358#define PO_IFNAME __unused
359#endif
360
361ssize_t
362print_option(char *s, ssize_t len, int type, int dl, const uint8_t *data,
363    PO_IFNAME const char *ifname)
364{
365        const uint8_t *e, *t;
366        uint16_t u16;
367        int16_t s16;
368        uint32_t u32;
369        int32_t s32;
370        struct in_addr addr;
371        ssize_t bytes = 0;
372        ssize_t l;
373        char *tmp;
374
375        if (type & RFC3397) {
376                l = decode_rfc3397(NULL, 0, dl, data);
377                if (l < 1)
378                        return l;
379                tmp = malloc(l);
380                if (tmp == NULL)
381                        return -1;
382                decode_rfc3397(tmp, l, dl, data);
383                l = print_string(s, len, l - 1, (uint8_t *)tmp);
384                free(tmp);
385                return l;
386        }
387
388#ifdef INET
389        if (type & RFC3361) {
390                if ((tmp = decode_rfc3361(dl, data)) == NULL)
391                        return -1;
392                l = strlen(tmp);
393                l = print_string(s, len, l, (uint8_t *)tmp);
394                free(tmp);
395                return l;
396        }
397
398        if (type & RFC3442)
399                return decode_rfc3442(s, len, dl, data);
400
401        if (type & RFC5969)
402                return decode_rfc5969(s, len, dl, data);
403#endif
404
405        if (type & STRING) {
406                /* Some DHCP servers return NULL strings */
407                if (*data == '\0')
408                        return 0;
409                return print_string(s, len, dl, data);
410        }
411
412        if (type & FLAG) {
413                if (s) {
414                        *s++ = '1';
415                        *s = '\0';
416                }
417                return 2;
418        }
419
420        if (!s) {
421                if (type & UINT8)
422                        l = 3;
423                else if (type & UINT16) {
424                        l = 5;
425                        dl /= 2;
426                } else if (type & SINT16) {
427                        l = 6;
428                        dl /= 2;
429                } else if (type & UINT32) {
430                        l = 10;
431                        dl /= 4;
432                } else if (type & SINT32) {
433                        l = 11;
434                        dl /= 4;
435                } else if (type & ADDRIPV4) {
436                        l = 16;
437                        dl /= 4;
438                }
439#ifdef INET6
440                else if (type & ADDRIPV6) {
441                        e = data + dl;
442                        l = 0;
443                        while (data < e) {
444                                if (l)
445                                        l++; /* space */
446                                dl = ipv6_printaddr(NULL, 0, data, ifname);
447                                if (dl != -1)
448                                        l += dl;
449                                data += 16;
450                        }
451                        return l + 1;
452                }
453#endif
454                else if (type & BINHEX) {
455                        l = 2;
456                } else {
457                        errno = EINVAL;
458                        return -1;
459                }
460                return (l + 1) * dl;
461        }
462
463        t = data;
464        e = data + dl;
465        while (data < e) {
466                if (data != t && type != BINHEX) {
467                        *s++ = ' ';
468                        bytes++;
469                        len--;
470                }
471                if (type & UINT8) {
472                        l = snprintf(s, len, "%u", *data);
473                        data++;
474                } else if (type & UINT16) {
475                        memcpy(&u16, data, sizeof(u16));
476                        u16 = ntohs(u16);
477                        l = snprintf(s, len, "%u", u16);
478                        data += sizeof(u16);
479                } else if (type & SINT16) {
480                        memcpy(&s16, data, sizeof(s16));
481                        s16 = ntohs(s16);
482                        l = snprintf(s, len, "%d", s16);
483                        data += sizeof(s16);
484                } else if (type & UINT32) {
485                        memcpy(&u32, data, sizeof(u32));
486                        u32 = ntohl(u32);
487                        l = snprintf(s, len, "%u", u32);
488                        data += sizeof(u32);
489                } else if (type & SINT32) {
490                        memcpy(&s32, data, sizeof(s32));
491                        s32 = ntohl(s32);
492                        l = snprintf(s, len, "%d", s32);
493                        data += sizeof(s32);
494                } else if (type & ADDRIPV4) {
495                        memcpy(&addr.s_addr, data, sizeof(addr.s_addr));
496                        l = snprintf(s, len, "%s", inet_ntoa(addr));
497                        data += sizeof(addr.s_addr);
498                }
499#ifdef INET6
500                else if (type & ADDRIPV6) {
501                        dl = ipv6_printaddr(s, len, data, ifname);
502                        if (dl != -1)
503                                l = dl;
504                        else
505                                l = 0;
506                        data += 16;
507                }
508#endif
509                else if (type & BINHEX) {
510                        l = snprintf(s, len, "%.2x", data[0]);
511                        data++;
512                } else
513                        l = 0;
514                len -= l;
515                bytes += l;
516                s += l;
517        }
518
519        return bytes;
520}
521
522static size_t
523dhcp_envoption1(char **env, const char *prefix,
524    const struct dhcp_opt *opt, int vname, const uint8_t *od, int ol,
525    const char *ifname)
526{
527        ssize_t len;
528        size_t e;
529        char *v, *val;
530
531        if (opt->len && opt->len < ol)
532                ol = opt->len;
533        len = print_option(NULL, 0, opt->type, ol, od, ifname);
534        if (len < 0)
535                return 0;
536        if (vname)
537                e = strlen(opt->var) + 1;
538        else
539                e = 0;
540        if (prefix)
541                e += strlen(prefix);
542        e += len + 4;
543        if (env == NULL)
544                return e;
545        v = val = *env = malloc(e);
546        if (v == NULL) {
547                syslog(LOG_ERR, "%s: %m", __func__);
548                return 0;
549        }
550        if (vname)
551                v += snprintf(val, e, "%s_%s=", prefix, opt->var);
552        else
553                v += snprintf(val, e, "%s=", prefix);
554        if (len != 0)
555                print_option(v, len, opt->type, ol, od, ifname);
556        return e;
557}
558
559ssize_t
560dhcp_envoption(char **env, const char *prefix,
561    const char *ifname, struct dhcp_opt *opt,
562    const uint8_t *(*dgetopt)(unsigned int *, unsigned int *, unsigned int *,
563    const uint8_t *, unsigned int, struct dhcp_opt **),
564    const uint8_t *od, int ol)
565{
566        ssize_t e, n;
567        size_t i;
568        unsigned int eoc, eos, eol;
569        const uint8_t *eod;
570        int ov;
571        struct dhcp_opt *eopt, *oopt;
572        char *pfx;
573
574        /* If no embedded or encapsulated options, it's easy */
575        if (opt->embopts_len == 0 && opt->encopts_len == 0) {
576                if (dhcp_envoption1(env == NULL ? NULL : &env[0],
577                    prefix, opt, 1, od, ol, ifname))
578                        return 1;
579                return 0;
580        }
581
582        /* Create a new prefix based on the option */
583        if (env) {
584                if (opt->type & INDEX) {
585                        if (opt->index > 999) {
586                                errno = ENOBUFS;
587                                syslog(LOG_ERR, "%s: %m", __func__);
588                                return 0;
589                        }
590                }
591                e = strlen(prefix) + strlen(opt->var) + 2 +
592                    (opt->type & INDEX ? 3 : 0);
593                pfx = malloc(e);
594                if (pfx == NULL) {
595                        syslog(LOG_ERR, "%s: %m", __func__);
596                        return 0;
597                }
598                if (opt->type & INDEX)
599                        snprintf(pfx, e, "%s_%s%d", prefix,
600                            opt->var, ++opt->index);
601                else
602                        snprintf(pfx, e, "%s_%s", prefix, opt->var);
603        } else
604                pfx = NULL;
605
606        /* Embedded options are always processed first as that
607         * is a fixed layout */
608        n = 0;
609        for (i = 0, eopt = opt->embopts; i < opt->embopts_len; i++, eopt++) {
610                e = dhcp_optlen(eopt, ol);
611                if (e == 0)
612                        /* Report error? */
613                        return 0;
614                /* Use the option prefix if the embedded option
615                 * name is different.
616                 * This avoids new_fqdn_fqdn which would be silly. */
617                ov = strcmp(opt->var, eopt->var);
618                if (dhcp_envoption1(env == NULL ? NULL : &env[n],
619                    pfx, eopt, ov, od, e, ifname))
620                        n++;
621                od += e;
622                ol -= e;
623        }
624
625        /* Enumerate our encapsulated options */
626        if (opt->encopts_len && ol > 0) {
627                /* Zero any option indexes
628                 * We assume that referenced encapsulated options are NEVER
629                 * recursive as the index order could break. */
630                for (i = 0, eopt = opt->encopts;
631                    i < opt->encopts_len;
632                    i++, eopt++)
633                {
634                        eoc = opt->option;
635                        if (eopt->type & OPTION) {
636                                dgetopt(NULL, &eoc, NULL, NULL, 0, &oopt);
637                                if (oopt)
638                                        oopt->index = 0;
639                        }
640                }
641
642                while ((eod = dgetopt(&eos, &eoc, &eol, od, ol, &oopt))) {
643                        for (i = 0, eopt = opt->encopts;
644                            i < opt->encopts_len;
645                            i++, eopt++)
646                        {
647                                if (eopt->option == eoc) {
648                                        if (eopt->type & OPTION) {
649                                                if (oopt == NULL)
650                                                        /* Report error? */
651                                                        continue;
652                                        }
653                                        n += dhcp_envoption(
654                                            env == NULL ? NULL : &env[n], pfx,
655                                            ifname,
656                                            eopt->type & OPTION ? oopt : eopt,
657                                            dgetopt, eod, eol);
658                                        break;
659                                }
660                        }
661                        od += eos + eol;
662                        ol -= eos + eol;
663                }
664        }
665
666        if (env)
667                free(pfx);
668
669        /* Return number of options found */
670        return n;
671}
672
673void
674dhcp_zero_index(struct dhcp_opt *opt)
675{
676        size_t i;
677        struct dhcp_opt *o;
678
679        opt->index = 0;
680        for (i = 0, o = opt->embopts; i < opt->embopts_len; i++, o++)
681                dhcp_zero_index(o);
682        for (i = 0, o = opt->encopts; i < opt->encopts_len; i++, o++)
683                dhcp_zero_index(o);
684}
Note: See TracBrowser for help on using the repository browser.