]>
Commit | Line | Data |
---|---|---|
114167a2 | 1 | /* SPDX-License-Identifier: BSD-2-Clause */ |
8cc47ba2 | 2 | /* |
294eff4d | 3 | * dhcpcd - DHCP client daemon |
94354c03 | 4 | * Copyright (c) 2006-2019 Roy Marples <roy@marples.name> |
294eff4d RM |
5 | * All rights reserved |
6 | ||
7 | * Redistribution and use in source and binary forms, with or without | |
8 | * modification, are permitted provided that the following conditions | |
9 | * are met: | |
10 | * 1. Redistributions of source code must retain the above copyright | |
11 | * notice, this list of conditions and the following disclaimer. | |
12 | * 2. Redistributions in binary form must reproduce the above copyright | |
13 | * notice, this list of conditions and the following disclaimer in the | |
14 | * documentation and/or other materials provided with the distribution. | |
15 | * | |
16 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND | |
17 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | |
18 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | |
19 | * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE | |
20 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL | |
21 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS | |
22 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) | |
23 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT | |
24 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY | |
25 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF | |
26 | * SUCH DAMAGE. | |
27 | */ | |
28 | ||
6c6c2328 | 29 | #include <sys/stat.h> |
d074b249 RM |
30 | #include <sys/utsname.h> |
31 | ||
294eff4d RM |
32 | #include <ctype.h> |
33 | #include <errno.h> | |
34 | #include <fcntl.h> | |
f94b4eab | 35 | #include <inttypes.h> |
294eff4d RM |
36 | #include <stdlib.h> |
37 | #include <string.h> | |
294eff4d RM |
38 | #include <unistd.h> |
39 | ||
40 | #include "config.h" | |
4f219984 | 41 | |
294eff4d RM |
42 | #include "common.h" |
43 | #include "dhcp-common.h" | |
44 | #include "dhcp.h" | |
1a7ff100 RM |
45 | #include "if.h" |
46 | #include "ipv6.h" | |
94d1ded9 | 47 | #include "logerr.h" |
c8521994 | 48 | #include "script.h" |
7374150c | 49 | |
d056e4b5 RM |
50 | const char * |
51 | dhcp_get_hostname(char *buf, size_t buf_len, const struct if_options *ifo) | |
52 | { | |
d056e4b5 RM |
53 | |
54 | if (ifo->hostname[0] == '\0') { | |
55 | if (gethostname(buf, buf_len) != 0) | |
56 | return NULL; | |
57 | buf[buf_len - 1] = '\0'; | |
58 | } else | |
b1128230 | 59 | strlcpy(buf, ifo->hostname, buf_len); |
d056e4b5 RM |
60 | |
61 | /* Deny sending of these local hostnames */ | |
02e24339 RM |
62 | if (buf[0] == '\0' || buf[0] == '.' || |
63 | strcmp(buf, "(none)") == 0 || | |
d056e4b5 | 64 | strcmp(buf, "localhost") == 0 || |
02e24339 | 65 | strncmp(buf, "localhost.", strlen("localhost.")) == 0) |
d056e4b5 RM |
66 | return NULL; |
67 | ||
68 | /* Shorten the hostname if required */ | |
69 | if (ifo->options & DHCPCD_HOSTNAME_SHORT) { | |
70 | char *hp; | |
71 | ||
72 | hp = strchr(buf, '.'); | |
73 | if (hp != NULL) | |
74 | *hp = '\0'; | |
75 | } | |
76 | ||
77 | return buf; | |
78 | } | |
79 | ||
8f008ca7 RM |
80 | void |
81 | dhcp_print_option_encoding(const struct dhcp_opt *opt, int cols) | |
82 | { | |
83 | ||
84 | while (cols < 40) { | |
85 | putchar(' '); | |
86 | cols++; | |
87 | } | |
88 | putchar('\t'); | |
41eefe94 | 89 | if (opt->type & OT_EMBED) |
8f008ca7 | 90 | printf(" embed"); |
41eefe94 | 91 | if (opt->type & OT_ENCAP) |
8f008ca7 | 92 | printf(" encap"); |
41eefe94 | 93 | if (opt->type & OT_INDEX) |
8f008ca7 | 94 | printf(" index"); |
41eefe94 | 95 | if (opt->type & OT_ARRAY) |
8f008ca7 | 96 | printf(" array"); |
41eefe94 | 97 | if (opt->type & OT_UINT8) |
c960d1e6 | 98 | printf(" uint8"); |
41eefe94 | 99 | else if (opt->type & OT_INT8) |
c960d1e6 | 100 | printf(" int8"); |
41eefe94 | 101 | else if (opt->type & OT_UINT16) |
8f008ca7 | 102 | printf(" uint16"); |
41eefe94 | 103 | else if (opt->type & OT_INT16) |
c960d1e6 | 104 | printf(" int16"); |
41eefe94 | 105 | else if (opt->type & OT_UINT32) |
8f008ca7 | 106 | printf(" uint32"); |
41eefe94 | 107 | else if (opt->type & OT_INT32) |
c960d1e6 | 108 | printf(" int32"); |
41eefe94 | 109 | else if (opt->type & OT_ADDRIPV4) |
8f008ca7 | 110 | printf(" ipaddress"); |
41eefe94 | 111 | else if (opt->type & OT_ADDRIPV6) |
8f008ca7 | 112 | printf(" ip6address"); |
41eefe94 | 113 | else if (opt->type & OT_FLAG) |
8f008ca7 | 114 | printf(" flag"); |
41eefe94 | 115 | else if (opt->type & OT_BITFLAG) |
cc71162d | 116 | printf(" bitflags"); |
41eefe94 | 117 | else if (opt->type & OT_RFC1035) |
8f008ca7 | 118 | printf(" domain"); |
41eefe94 | 119 | else if (opt->type & OT_DOMAIN) |
8f008ca7 | 120 | printf(" dname"); |
41eefe94 | 121 | else if (opt->type & OT_ASCII) |
8f008ca7 | 122 | printf(" ascii"); |
41eefe94 | 123 | else if (opt->type & OT_RAW) |
8f008ca7 | 124 | printf(" raw"); |
41eefe94 | 125 | else if (opt->type & OT_BINHEX) |
8f008ca7 | 126 | printf(" binhex"); |
41eefe94 | 127 | else if (opt->type & OT_STRING) |
8f008ca7 | 128 | printf(" string"); |
41eefe94 | 129 | if (opt->type & OT_RFC3361) |
8f008ca7 | 130 | printf(" rfc3361"); |
41eefe94 | 131 | if (opt->type & OT_RFC3442) |
8f008ca7 | 132 | printf(" rfc3442"); |
41eefe94 | 133 | if (opt->type & OT_REQUEST) |
8f008ca7 | 134 | printf(" request"); |
41eefe94 | 135 | if (opt->type & OT_NOREQ) |
8f008ca7 RM |
136 | printf(" norequest"); |
137 | putchar('\n'); | |
138 | } | |
139 | ||
7a911e57 | 140 | struct dhcp_opt * |
34457fe6 | 141 | vivso_find(uint32_t iana_en, const void *arg) |
7a911e57 | 142 | { |
04a19189 | 143 | const struct interface *ifp; |
7a911e57 RM |
144 | size_t i; |
145 | struct dhcp_opt *opt; | |
146 | ||
04a19189 RM |
147 | ifp = arg; |
148 | for (i = 0, opt = ifp->options->vivso_override; | |
149 | i < ifp->options->vivso_override_len; | |
150 | i++, opt++) | |
151 | if (opt->option == iana_en) | |
152 | return opt; | |
153 | for (i = 0, opt = ifp->ctx->vivso; | |
154 | i < ifp->ctx->vivso_len; | |
155 | i++, opt++) | |
7a911e57 RM |
156 | if (opt->option == iana_en) |
157 | return opt; | |
158 | return NULL; | |
159 | } | |
8e7d8c37 | 160 | |
fec195b5 | 161 | ssize_t |
34457fe6 | 162 | dhcp_vendor(char *str, size_t len) |
d074b249 RM |
163 | { |
164 | struct utsname utn; | |
165 | char *p; | |
34457fe6 | 166 | int l; |
d074b249 | 167 | |
7ac84761 | 168 | if (uname(&utn) == -1) |
fec195b5 | 169 | return (ssize_t)snprintf(str, len, "%s-%s", |
34457fe6 | 170 | PACKAGE, VERSION); |
d074b249 | 171 | p = str; |
34457fe6 | 172 | l = snprintf(p, len, |
d074b249 RM |
173 | "%s-%s:%s-%s:%s", PACKAGE, VERSION, |
174 | utn.sysname, utn.release, utn.machine); | |
fec195b5 RM |
175 | if (l == -1 || (size_t)(l + 1) > len) |
176 | return -1; | |
34457fe6 RM |
177 | p += l; |
178 | len -= (size_t)l; | |
1a7ff100 | 179 | l = if_machinearch(p, len); |
fec195b5 RM |
180 | if (l == -1 || (size_t)(l + 1) > len) |
181 | return -1; | |
a8c8e686 | 182 | p += l; |
fec195b5 | 183 | return p - str; |
d074b249 RM |
184 | } |
185 | ||
7a911e57 RM |
186 | int |
187 | make_option_mask(const struct dhcp_opt *dopts, size_t dopts_len, | |
49b749f0 | 188 | const struct dhcp_opt *odopts, size_t odopts_len, |
294eff4d RM |
189 | uint8_t *mask, const char *opts, int add) |
190 | { | |
f94b4eab | 191 | char *token, *o, *p; |
294eff4d | 192 | const struct dhcp_opt *opt; |
f94b4eab | 193 | int match, e; |
7a911e57 | 194 | unsigned int n; |
ae4e592f | 195 | size_t i; |
294eff4d | 196 | |
78369646 RM |
197 | if (opts == NULL) |
198 | return -1; | |
49b749f0 | 199 | o = p = strdup(opts); |
294eff4d RM |
200 | while ((token = strsep(&p, ", "))) { |
201 | if (*token == '\0') | |
202 | continue; | |
18aa94f8 RM |
203 | if (strncmp(token, "dhcp6_", 6) == 0) |
204 | token += 6; | |
825221f9 AC |
205 | if (strncmp(token, "nd_", 3) == 0) |
206 | token += 3; | |
49b749f0 RM |
207 | match = 0; |
208 | for (i = 0, opt = odopts; i < odopts_len; i++, opt++) { | |
04d965fe | 209 | if (opt->var == NULL || opt->option == 0) |
2b0e688c | 210 | continue; /* buggy dhcpcd-definitions.conf */ |
bc22d277 | 211 | if (strcmp(opt->var, token) == 0) |
294eff4d RM |
212 | match = 1; |
213 | else { | |
f94b4eab RM |
214 | n = (unsigned int)strtou(token, NULL, 0, |
215 | 0, UINT_MAX, &e); | |
216 | if (e == 0 && opt->option == n) | |
217 | match = 1; | |
294eff4d | 218 | } |
49b749f0 | 219 | if (match) |
294eff4d | 220 | break; |
49b749f0 RM |
221 | } |
222 | if (match == 0) { | |
223 | for (i = 0, opt = dopts; i < dopts_len; i++, opt++) { | |
224 | if (strcmp(opt->var, token) == 0) | |
225 | match = 1; | |
226 | else { | |
f94b4eab RM |
227 | n = (unsigned int)strtou(token, NULL, 0, |
228 | 0, UINT_MAX, &e); | |
229 | if (e == 0 && opt->option == n) | |
230 | match = 1; | |
49b749f0 RM |
231 | } |
232 | if (match) | |
233 | break; | |
294eff4d RM |
234 | } |
235 | } | |
49b749f0 | 236 | if (!match || !opt->option) { |
294eff4d RM |
237 | free(o); |
238 | errno = ENOENT; | |
239 | return -1; | |
240 | } | |
41eefe94 | 241 | if (add == 2 && !(opt->type & OT_ADDRIPV4)) { |
49b749f0 RM |
242 | free(o); |
243 | errno = EINVAL; | |
244 | return -1; | |
245 | } | |
246 | if (add == 1 || add == 2) | |
60ccc7df | 247 | add_option_mask(mask, opt->option); |
49b749f0 | 248 | else |
60ccc7df | 249 | del_option_mask(mask, opt->option); |
294eff4d RM |
250 | } |
251 | free(o); | |
252 | return 0; | |
253 | } | |
254 | ||
cc3c3560 RM |
255 | size_t |
256 | encode_rfc1035(const char *src, uint8_t *dst) | |
257 | { | |
258 | uint8_t *p; | |
259 | uint8_t *lp; | |
260 | size_t len; | |
261 | uint8_t has_dot; | |
262 | ||
263 | if (src == NULL || *src == '\0') | |
264 | return 0; | |
b340cfc9 | 265 | |
cc3c3560 RM |
266 | if (dst) { |
267 | p = dst; | |
268 | lp = p++; | |
269 | } | |
b340cfc9 RM |
270 | /* Silence bogus GCC warnings */ |
271 | else | |
272 | p = lp = NULL; | |
273 | ||
cc3c3560 RM |
274 | len = 1; |
275 | has_dot = 0; | |
276 | for (; *src; src++) { | |
277 | if (*src == '\0') | |
278 | break; | |
279 | if (*src == '.') { | |
280 | /* Skip the trailing . */ | |
281 | if (src[1] == '\0') | |
282 | break; | |
283 | has_dot = 1; | |
284 | if (dst) { | |
34457fe6 | 285 | *lp = (uint8_t)(p - lp - 1); |
cc3c3560 RM |
286 | if (*lp == '\0') |
287 | return len; | |
288 | lp = p++; | |
289 | } | |
290 | } else if (dst) | |
291 | *p++ = (uint8_t)*src; | |
292 | len++; | |
293 | } | |
b340cfc9 | 294 | |
cc3c3560 | 295 | if (dst) { |
34457fe6 | 296 | *lp = (uint8_t)(p - lp - 1); |
cc3c3560 RM |
297 | if (has_dot) |
298 | *p++ = '\0'; | |
299 | } | |
b340cfc9 | 300 | |
cc3c3560 RM |
301 | if (has_dot) |
302 | len++; | |
b340cfc9 | 303 | |
cc3c3560 RM |
304 | return len; |
305 | } | |
306 | ||
167b9b9b | 307 | /* Decode an RFC1035 DNS search order option into a space |
294eff4d RM |
308 | * separated string. Returns length of string (including |
309 | * terminating zero) or zero on error. out may be NULL | |
310 | * to just determine output length. */ | |
311 | ssize_t | |
167b9b9b | 312 | decode_rfc1035(char *out, size_t len, const uint8_t *p, size_t pl) |
294eff4d RM |
313 | { |
314 | const char *start; | |
77fbcbc8 | 315 | size_t start_len, l, d_len, o_len; |
34457fe6 | 316 | const uint8_t *r, *q = p, *e; |
f3ba5179 | 317 | int hops; |
294eff4d RM |
318 | uint8_t ltype; |
319 | ||
77fbcbc8 | 320 | o_len = 0; |
294eff4d RM |
321 | start = out; |
322 | start_len = len; | |
34457fe6 RM |
323 | q = p; |
324 | e = p + pl; | |
325 | while (q < e) { | |
294eff4d | 326 | r = NULL; |
77fbcbc8 | 327 | d_len = 0; |
294eff4d | 328 | hops = 0; |
1b77c520 RM |
329 | /* Check we are inside our length again in-case |
330 | * the name isn't fully qualified (ie, not terminated) */ | |
34457fe6 | 331 | while (q < e && (l = (size_t)*q++)) { |
294eff4d | 332 | ltype = l & 0xc0; |
3afee65d RM |
333 | if (ltype == 0x80 || ltype == 0x40) { |
334 | /* Currently reserved for future use as noted | |
335 | * in RFC1035 4.1.4 as the 10 and 01 | |
336 | * combinations. */ | |
337 | errno = ENOTSUP; | |
34457fe6 | 338 | return -1; |
3afee65d | 339 | } |
294eff4d | 340 | else if (ltype == 0xc0) { /* pointer */ |
8f38710f RM |
341 | if (q == e) { |
342 | errno = ERANGE; | |
343 | return -1; | |
344 | } | |
294eff4d RM |
345 | l = (l & 0x3f) << 8; |
346 | l |= *q++; | |
347 | /* save source of first jump. */ | |
348 | if (!r) | |
349 | r = q; | |
350 | hops++; | |
34457fe6 RM |
351 | if (hops > 255) { |
352 | errno = ERANGE; | |
353 | return -1; | |
354 | } | |
294eff4d | 355 | q = p + l; |
34457fe6 RM |
356 | if (q >= e) { |
357 | errno = ERANGE; | |
358 | return -1; | |
359 | } | |
294eff4d RM |
360 | } else { |
361 | /* straightforward name segment, add with '.' */ | |
8f38710f RM |
362 | if (q + l > e) { |
363 | errno = ERANGE; | |
364 | return -1; | |
365 | } | |
77fbcbc8 RM |
366 | if (l > NS_MAXLABEL) { |
367 | errno = EINVAL; | |
368 | return -1; | |
369 | } | |
370 | d_len += l + 1; | |
294eff4d | 371 | if (out) { |
34457fe6 | 372 | if (l + 1 > len) { |
294eff4d RM |
373 | errno = ENOBUFS; |
374 | return -1; | |
375 | } | |
376 | memcpy(out, q, l); | |
377 | out += l; | |
378 | *out++ = '.'; | |
379 | len -= l; | |
380 | len--; | |
381 | } | |
382 | q += l; | |
383 | } | |
384 | } | |
77fbcbc8 RM |
385 | |
386 | /* Don't count the trailing NUL */ | |
387 | if (d_len > NS_MAXDNAME + 1) { | |
388 | errno = E2BIG; | |
389 | return -1; | |
390 | } | |
391 | o_len += d_len; | |
392 | ||
294eff4d RM |
393 | /* change last dot to space */ |
394 | if (out && out != start) | |
395 | *(out - 1) = ' '; | |
396 | if (r) | |
397 | q = r; | |
398 | } | |
399 | ||
400 | /* change last space to zero terminator */ | |
401 | if (out) { | |
402 | if (out != start) | |
403 | *(out - 1) = '\0'; | |
404 | else if (start_len > 0) | |
405 | *out = '\0'; | |
406 | } | |
407 | ||
77fbcbc8 RM |
408 | /* Remove the trailing NUL */ |
409 | if (o_len != 0) | |
410 | o_len--; | |
411 | ||
412 | return (ssize_t)o_len; | |
294eff4d RM |
413 | } |
414 | ||
cb2d5677 | 415 | /* Check for a valid name as per RFC952 and RFC1123 section 2.1 */ |
8f008ca7 RM |
416 | static int |
417 | valid_domainname(char *lbl, int type) | |
418 | { | |
419 | char *slbl, *lst; | |
420 | unsigned char c; | |
421 | int start, len, errset; | |
422 | ||
423 | if (lbl == NULL || *lbl == '\0') { | |
424 | errno = EINVAL; | |
425 | return 0; | |
426 | } | |
427 | ||
428 | slbl = lbl; | |
429 | lst = NULL; | |
430 | start = 1; | |
431 | len = errset = 0; | |
432 | for (;;) { | |
433 | c = (unsigned char)*lbl++; | |
434 | if (c == '\0') | |
435 | return 1; | |
436 | if (c == ' ') { | |
437 | if (lbl - 1 == slbl) /* No space at start */ | |
438 | break; | |
41eefe94 | 439 | if (!(type & OT_ARRAY)) |
8f008ca7 RM |
440 | break; |
441 | /* Skip to the next label */ | |
442 | if (!start) { | |
443 | start = 1; | |
444 | lst = lbl - 1; | |
445 | } | |
446 | if (len) | |
447 | len = 0; | |
448 | continue; | |
449 | } | |
450 | if (c == '.') { | |
451 | if (*lbl == '.') | |
452 | break; | |
453 | len = 0; | |
454 | continue; | |
455 | } | |
456 | if (((c == '-' || c == '_') && | |
457 | !start && *lbl != ' ' && *lbl != '\0') || | |
458 | isalnum(c)) | |
459 | { | |
e3e434d4 | 460 | if (++len > NS_MAXLABEL) { |
8f008ca7 | 461 | errno = ERANGE; |
34a9f2d1 | 462 | errset = 1; |
8f008ca7 RM |
463 | break; |
464 | } | |
465 | } else | |
466 | break; | |
467 | if (start) | |
468 | start = 0; | |
469 | } | |
470 | ||
471 | if (!errset) | |
472 | errno = EINVAL; | |
473 | if (lst) { | |
474 | /* At least one valid domain, return it */ | |
475 | *lst = '\0'; | |
476 | return 1; | |
477 | } | |
478 | return 0; | |
479 | } | |
4f219984 RM |
480 | |
481 | /* | |
482 | * Prints a chunk of data to a string. | |
8f008ca7 RM |
483 | * PS_SHELL goes as it is these days, it's upto the target to validate it. |
484 | * PS_SAFE has all non ascii and non printables changes to escaped octal. | |
4f219984 | 485 | */ |
8f008ca7 | 486 | static const char hexchrs[] = "0123456789abcdef"; |
294eff4d | 487 | ssize_t |
8f008ca7 | 488 | print_string(char *dst, size_t len, int type, const uint8_t *data, size_t dl) |
294eff4d | 489 | { |
8f008ca7 | 490 | char *odst; |
294eff4d | 491 | uint8_t c; |
8f008ca7 | 492 | const uint8_t *e; |
4f219984 | 493 | size_t bytes; |
294eff4d | 494 | |
8f008ca7 | 495 | odst = dst; |
4f219984 | 496 | bytes = 0; |
294eff4d | 497 | e = data + dl; |
34a9f2d1 | 498 | |
294eff4d RM |
499 | while (data < e) { |
500 | c = *data++; | |
41eefe94 | 501 | if (type & OT_BINHEX) { |
8f008ca7 RM |
502 | if (dst) { |
503 | if (len == 0 || len == 1) { | |
61318a5b | 504 | errno = ENOBUFS; |
8f008ca7 RM |
505 | return -1; |
506 | } | |
507 | *dst++ = hexchrs[(c & 0xF0) >> 4]; | |
508 | *dst++ = hexchrs[(c & 0x0F)]; | |
509 | len -= 2; | |
510 | } | |
511 | bytes += 2; | |
512 | continue; | |
294eff4d | 513 | } |
41eefe94 | 514 | if (type & OT_ASCII && (!isascii(c))) { |
8f008ca7 RM |
515 | errno = EINVAL; |
516 | break; | |
294eff4d | 517 | } |
41eefe94 | 518 | if (!(type & (OT_ASCII | OT_RAW | OT_ESCSTRING | OT_ESCFILE)) && |
8f008ca7 RM |
519 | (!isascii(c) && !isprint(c))) |
520 | { | |
521 | errno = EINVAL; | |
522 | break; | |
523 | } | |
41eefe94 | 524 | if ((type & (OT_ESCSTRING | OT_ESCFILE) && |
937bc321 | 525 | (c == '\\' || !isascii(c) || !isprint(c))) || |
41eefe94 | 526 | (type & OT_ESCFILE && (c == '/' || c == ' '))) |
8f008ca7 RM |
527 | { |
528 | errno = EINVAL; | |
529 | if (c == '\\') { | |
8f008ca7 | 530 | if (dst) { |
2cb87923 | 531 | if (len == 0 || len == 1) { |
61318a5b | 532 | errno = ENOBUFS; |
2cb87923 RM |
533 | return -1; |
534 | } | |
8f008ca7 RM |
535 | *dst++ = '\\'; *dst++ = '\\'; |
536 | len -= 2; | |
537 | } | |
538 | bytes += 2; | |
539 | continue; | |
540 | } | |
541 | if (dst) { | |
542 | if (len < 5) { | |
61318a5b | 543 | errno = ENOBUFS; |
8f008ca7 RM |
544 | return -1; |
545 | } | |
546 | *dst++ = '\\'; | |
a8cbd6a0 RM |
547 | *dst++ = (char)(((c >> 6) & 03) + '0'); |
548 | *dst++ = (char)(((c >> 3) & 07) + '0'); | |
549 | *dst++ = (char)(( c & 07) + '0'); | |
8f008ca7 RM |
550 | len -= 4; |
551 | } | |
552 | bytes += 4; | |
553 | } else { | |
554 | if (dst) { | |
555 | if (len == 0) { | |
61318a5b | 556 | errno = ENOBUFS; |
8f008ca7 RM |
557 | return -1; |
558 | } | |
559 | *dst++ = (char)c; | |
560 | len--; | |
561 | } | |
562 | bytes++; | |
294eff4d | 563 | } |
294eff4d RM |
564 | } |
565 | ||
566 | /* NULL */ | |
8f008ca7 RM |
567 | if (dst) { |
568 | if (len == 0) { | |
61318a5b | 569 | errno = ENOBUFS; |
8f008ca7 RM |
570 | return -1; |
571 | } | |
572 | *dst = '\0'; | |
573 | ||
574 | /* Now we've printed it, validate the domain */ | |
41eefe94 | 575 | if (type & OT_DOMAIN && !valid_domainname(odst, type)) { |
8f008ca7 RM |
576 | *odst = '\0'; |
577 | return 1; | |
578 | } | |
579 | ||
580 | } | |
8f008ca7 | 581 | |
4f219984 | 582 | return (ssize_t)bytes; |
294eff4d RM |
583 | } |
584 | ||
03476881 | 585 | #define ADDR6SZ 16 |
4dd68670 | 586 | static ssize_t |
b21cd906 | 587 | dhcp_optlen(const struct dhcp_opt *opt, size_t dl) |
8e7d8c37 RM |
588 | { |
589 | size_t sz; | |
590 | ||
41eefe94 | 591 | if (opt->type & OT_ADDRIPV6) |
1475a702 | 592 | sz = ADDR6SZ; |
41eefe94 | 593 | else if (opt->type & (OT_INT32 | OT_UINT32 | OT_ADDRIPV4)) |
1475a702 | 594 | sz = sizeof(uint32_t); |
41eefe94 | 595 | else if (opt->type & (OT_INT16 | OT_UINT16)) |
1475a702 | 596 | sz = sizeof(uint16_t); |
41eefe94 | 597 | else if (opt->type & (OT_INT8 | OT_UINT8 | OT_BITFLAG)) |
1475a702 | 598 | sz = sizeof(uint8_t); |
41eefe94 | 599 | else if (opt->type & OT_FLAG) |
1475a702 RM |
600 | return 0; |
601 | else { | |
602 | /* All other types are variable length */ | |
b21cd906 | 603 | if (opt->len) { |
1475a702 | 604 | if ((size_t)opt->len > dl) { |
a0800a8a | 605 | errno = EOVERFLOW; |
4dd68670 | 606 | return -1; |
1475a702 | 607 | } |
4dd68670 | 608 | return (ssize_t)opt->len; |
b21cd906 | 609 | } |
4dd68670 | 610 | return (ssize_t)dl; |
b21cd906 | 611 | } |
1475a702 | 612 | if (dl < sz) { |
a0800a8a | 613 | errno = EOVERFLOW; |
1475a702 | 614 | return -1; |
8e7d8c37 RM |
615 | } |
616 | ||
1475a702 RM |
617 | /* Trim any extra data. |
618 | * Maybe we need a settng to reject DHCP options with extra data? */ | |
41eefe94 | 619 | if (opt->type & OT_ARRAY) |
1475a702 RM |
620 | return (ssize_t)(dl - (dl % sz)); |
621 | return (ssize_t)sz; | |
8e7d8c37 RM |
622 | } |
623 | ||
cc71162d | 624 | static ssize_t |
c8521994 RM |
625 | print_option(FILE *fp, const char *prefix, const struct dhcp_opt *opt, |
626 | int vname, | |
cc71162d | 627 | const uint8_t *data, size_t dl, const char *ifname) |
294eff4d | 628 | { |
2dae918b | 629 | fpos_t fp_pos; |
294eff4d RM |
630 | const uint8_t *e, *t; |
631 | uint16_t u16; | |
632 | int16_t s16; | |
633 | uint32_t u32; | |
634 | int32_t s32; | |
635 | struct in_addr addr; | |
2dae918b | 636 | ssize_t sl; |
34457fe6 | 637 | size_t l; |
c8521994 RM |
638 | |
639 | /* Ensure a valid length */ | |
640 | dl = (size_t)dhcp_optlen(opt, dl); | |
641 | if ((ssize_t)dl == -1) | |
642 | return 0; | |
643 | ||
2dae918b | 644 | if (fgetpos(fp, &fp_pos) == -1) |
c8521994 | 645 | return -1; |
2dae918b RM |
646 | if (fprintf(fp, "%s", prefix) == -1) |
647 | goto err; | |
859110a8 RM |
648 | |
649 | /* We printed something, so always goto err from now-on | |
650 | * to terminate the string. */ | |
c8521994 RM |
651 | if (vname) { |
652 | if (fprintf(fp, "_%s", opt->var) == -1) | |
859110a8 | 653 | goto err; |
c8521994 RM |
654 | } |
655 | if (fputc('=', fp) == EOF) | |
859110a8 | 656 | goto err; |
c8521994 | 657 | if (dl == 0) |
859110a8 | 658 | goto done; |
294eff4d | 659 | |
41eefe94 | 660 | if (opt->type & OT_RFC1035) { |
c8521994 RM |
661 | char domain[NS_MAXDNAME]; |
662 | ||
663 | sl = decode_rfc1035(domain, sizeof(domain), data, dl); | |
87def87d | 664 | if (sl == -1) |
859110a8 | 665 | goto err; |
87def87d | 666 | if (sl == 0) |
859110a8 | 667 | goto done; |
c8521994 | 668 | if (valid_domainname(domain, opt->type) == -1) |
859110a8 | 669 | goto err; |
c8521994 | 670 | return efprintf(fp, "%s", domain); |
294eff4d RM |
671 | } |
672 | ||
aae24feb | 673 | #ifdef INET |
c8521994 RM |
674 | if (opt->type & OT_RFC3361) |
675 | return print_rfc3361(fp, data, dl); | |
294eff4d | 676 | |
41eefe94 | 677 | if (opt->type & OT_RFC3442) |
c8521994 | 678 | return print_rfc3442(fp, data, dl); |
aae24feb | 679 | #endif |
294eff4d | 680 | |
c8521994 RM |
681 | if (opt->type & OT_STRING) { |
682 | char buf[1024]; | |
294eff4d | 683 | |
c8521994 | 684 | if (print_string(buf, sizeof(buf), opt->type, data, dl) == -1) |
859110a8 | 685 | goto err; |
c8521994 | 686 | return efprintf(fp, "%s", buf); |
6bb6f717 RM |
687 | } |
688 | ||
c8521994 RM |
689 | if (opt->type & OT_FLAG) |
690 | return efprintf(fp, "1"); | |
691 | ||
41eefe94 | 692 | if (opt->type & OT_BITFLAG) { |
cc71162d RM |
693 | /* bitflags are a string, MSB first, such as ABCDEFGH |
694 | * where A is 10000000, B is 01000000, etc. */ | |
cc71162d RM |
695 | for (l = 0, sl = sizeof(opt->bitflags) - 1; |
696 | l < sizeof(opt->bitflags); | |
697 | l++, sl--) | |
698 | { | |
699 | /* Don't print NULL or 0 flags */ | |
700 | if (opt->bitflags[l] != '\0' && | |
701 | opt->bitflags[l] != '0' && | |
702 | *data & (1 << sl)) | |
703 | { | |
c8521994 | 704 | if (fputc(opt->bitflags[l], fp) == EOF) |
859110a8 | 705 | goto err; |
cc71162d RM |
706 | } |
707 | } | |
859110a8 | 708 | goto done; |
294eff4d RM |
709 | } |
710 | ||
711 | t = data; | |
712 | e = data + dl; | |
713 | while (data < e) { | |
8f008ca7 | 714 | if (data != t) { |
c8521994 | 715 | if (fputc(' ', fp) == EOF) |
859110a8 | 716 | goto err; |
294eff4d | 717 | } |
41eefe94 | 718 | if (opt->type & OT_UINT8) { |
c8521994 | 719 | if (fprintf(fp, "%u", *data) == -1) |
859110a8 | 720 | goto err; |
294eff4d | 721 | data++; |
41eefe94 | 722 | } else if (opt->type & OT_INT8) { |
c8521994 | 723 | if (fprintf(fp, "%d", *data) == -1) |
859110a8 | 724 | goto err; |
c960d1e6 | 725 | data++; |
41eefe94 | 726 | } else if (opt->type & OT_UINT16) { |
294eff4d RM |
727 | memcpy(&u16, data, sizeof(u16)); |
728 | u16 = ntohs(u16); | |
c8521994 | 729 | if (fprintf(fp, "%u", u16) == -1) |
859110a8 | 730 | goto err; |
294eff4d | 731 | data += sizeof(u16); |
41eefe94 | 732 | } else if (opt->type & OT_INT16) { |
34457fe6 RM |
733 | memcpy(&u16, data, sizeof(u16)); |
734 | s16 = (int16_t)ntohs(u16); | |
c8521994 | 735 | if (fprintf(fp, "%d", s16) == -1) |
859110a8 | 736 | goto err; |
34457fe6 | 737 | data += sizeof(u16); |
41eefe94 | 738 | } else if (opt->type & OT_UINT32) { |
294eff4d RM |
739 | memcpy(&u32, data, sizeof(u32)); |
740 | u32 = ntohl(u32); | |
c8521994 | 741 | if (fprintf(fp, "%u", u32) == -1) |
859110a8 | 742 | goto err; |
294eff4d | 743 | data += sizeof(u32); |
41eefe94 | 744 | } else if (opt->type & OT_INT32) { |
34457fe6 RM |
745 | memcpy(&u32, data, sizeof(u32)); |
746 | s32 = (int32_t)ntohl(u32); | |
c8521994 | 747 | if (fprintf(fp, "%d", s32) == -1) |
859110a8 | 748 | goto err; |
34457fe6 | 749 | data += sizeof(u32); |
41eefe94 | 750 | } else if (opt->type & OT_ADDRIPV4) { |
294eff4d | 751 | memcpy(&addr.s_addr, data, sizeof(addr.s_addr)); |
c8521994 | 752 | if (fprintf(fp, "%s", inet_ntoa(addr)) == -1) |
859110a8 | 753 | goto err; |
294eff4d | 754 | data += sizeof(addr.s_addr); |
41eefe94 | 755 | } else if (opt->type & OT_ADDRIPV6) { |
c8521994 RM |
756 | char buf[INET6_ADDRSTRLEN]; |
757 | ||
758 | if (inet_ntop(AF_INET6, data, buf, sizeof(buf)) == NULL) | |
859110a8 | 759 | goto err; |
c8521994 | 760 | if (fprintf(fp, "%s", buf) == -1) |
859110a8 | 761 | goto err; |
c8521994 RM |
762 | if (data[0] == 0xfe && (data[1] & 0xc0) == 0x80) { |
763 | if (fprintf(fp,"%%%s", ifname) == -1) | |
859110a8 | 764 | goto err; |
c8521994 | 765 | } |
294eff4d | 766 | data += 16; |
93f3066b RM |
767 | } else { |
768 | errno = EINVAL; | |
859110a8 | 769 | goto err; |
aae24feb | 770 | } |
294eff4d RM |
771 | } |
772 | ||
859110a8 | 773 | done: |
c8521994 RM |
774 | if (fputc('\0', fp) == EOF) |
775 | return -1; | |
2dae918b RM |
776 | return 1; |
777 | ||
778 | err: | |
779 | (void)fsetpos(fp, &fp_pos); | |
780 | return -1; | |
294eff4d | 781 | } |
8e7d8c37 | 782 | |
cb1e1319 RM |
783 | int |
784 | dhcp_set_leasefile(char *leasefile, size_t len, int family, | |
1f7bfb39 | 785 | const struct interface *ifp) |
cb1e1319 | 786 | { |
28322203 | 787 | char ssid[1 + (IF_SSIDLEN * 4) + 1]; /* - prefix and NUL terminated. */ |
cb1e1319 | 788 | |
ff4df8e0 RM |
789 | if (ifp->name[0] == '\0') { |
790 | strlcpy(leasefile, ifp->ctx->pidfile, len); | |
791 | return 0; | |
792 | } | |
793 | ||
cb1e1319 RM |
794 | switch (family) { |
795 | case AF_INET: | |
796 | case AF_INET6: | |
797 | break; | |
798 | default: | |
799 | errno = EINVAL; | |
800 | return -1; | |
801 | } | |
802 | ||
803 | if (ifp->wireless) { | |
804 | ssid[0] = '-'; | |
805 | print_string(ssid + 1, sizeof(ssid) - 1, | |
41eefe94 | 806 | OT_ESCFILE, |
cb1e1319 RM |
807 | (const uint8_t *)ifp->ssid, ifp->ssid_len); |
808 | } else | |
809 | ssid[0] = '\0'; | |
810 | return snprintf(leasefile, len, | |
811 | family == AF_INET ? LEASEFILE : LEASEFILE6, | |
1f7bfb39 | 812 | ifp->name, ssid); |
cb1e1319 RM |
813 | } |
814 | ||
c8521994 RM |
815 | void |
816 | dhcp_envoption(struct dhcpcd_ctx *ctx, FILE *fp, const char *prefix, | |
03476881 | 817 | const char *ifname, struct dhcp_opt *opt, |
4eb7b489 | 818 | const uint8_t *(*dgetopt)(struct dhcpcd_ctx *, |
76bb4d03 RM |
819 | size_t *, unsigned int *, size_t *, |
820 | const uint8_t *, size_t, struct dhcp_opt **), | |
34457fe6 | 821 | const uint8_t *od, size_t ol) |
8e7d8c37 | 822 | { |
c8521994 | 823 | size_t i, eos, eol; |
4dd68670 | 824 | ssize_t eo; |
76bb4d03 | 825 | unsigned int eoc; |
03476881 | 826 | const uint8_t *eod; |
7a911e57 | 827 | int ov; |
03476881 RM |
828 | struct dhcp_opt *eopt, *oopt; |
829 | char *pfx; | |
8e7d8c37 RM |
830 | |
831 | /* If no embedded or encapsulated options, it's easy */ | |
832 | if (opt->embopts_len == 0 && opt->encopts_len == 0) { | |
c8521994 RM |
833 | if (opt->type & OT_RESERVED) |
834 | return; | |
835 | if (print_option(fp, prefix, opt, 1, od, ol, ifname) == -1) | |
836 | logerr("%s: %s %d", ifname, __func__, opt->option); | |
837 | return; | |
8e7d8c37 RM |
838 | } |
839 | ||
63bdd2c2 | 840 | /* Create a new prefix based on the option */ |
c8521994 RM |
841 | if (opt->type & OT_INDEX) { |
842 | if (asprintf(&pfx, "%s_%s%d", | |
843 | prefix, opt->var, ++opt->index) == -1) | |
844 | pfx = NULL; | |
845 | } else { | |
846 | if (asprintf(&pfx, "%s_%s", prefix, opt->var) == -1) | |
847 | pfx = NULL; | |
848 | } | |
849 | if (pfx == NULL) { | |
850 | logerr(__func__); | |
851 | return; | |
852 | } | |
63bdd2c2 | 853 | |
8e7d8c37 RM |
854 | /* Embedded options are always processed first as that |
855 | * is a fixed layout */ | |
03476881 | 856 | for (i = 0, eopt = opt->embopts; i < opt->embopts_len; i++, eopt++) { |
4dd68670 RM |
857 | eo = dhcp_optlen(eopt, ol); |
858 | if (eo == -1) { | |
c8521994 RM |
859 | logerrx("%s: %s %d.%d/%zu: " |
860 | "malformed embedded option", | |
861 | ifname, __func__, opt->option, | |
862 | eopt->option, i); | |
4dd68670 RM |
863 | goto out; |
864 | } | |
865 | if (eo == 0) { | |
866 | /* An option was expected, but there is no data | |
6b1f3b3e RM |
867 | * data for it. |
868 | * This may not be an error as some options like | |
869 | * DHCP FQDN in RFC4702 have a string as the last | |
ecdbb919 | 870 | * option which is optional. */ |
c8521994 RM |
871 | if (ol != 0 || !(eopt->type & OT_OPTIONAL)) |
872 | logerrx("%s: %s %d.%d/%zu: " | |
873 | "missing embedded option", | |
4dd68670 | 874 | ifname, __func__, opt->option, |
ecdbb919 | 875 | eopt->option, i); |
4dd68670 | 876 | goto out; |
6b1f3b3e | 877 | } |
2f8d2564 RM |
878 | /* Use the option prefix if the embedded option |
879 | * name is different. | |
880 | * This avoids new_fqdn_fqdn which would be silly. */ | |
41eefe94 | 881 | if (!(eopt->type & OT_RESERVED)) { |
cc71162d | 882 | ov = strcmp(opt->var, eopt->var); |
c8521994 RM |
883 | if (print_option(fp, pfx, eopt, ov, od, (size_t)eo, |
884 | ifname) == -1) | |
94d1ded9 | 885 | logerr("%s: %s %d.%d/%zu", |
e0a0ba22 RM |
886 | ifname, __func__, |
887 | opt->option, eopt->option, i); | |
cc71162d | 888 | } |
4dd68670 RM |
889 | od += (size_t)eo; |
890 | ol -= (size_t)eo; | |
8e7d8c37 RM |
891 | } |
892 | ||
03476881 RM |
893 | /* Enumerate our encapsulated options */ |
894 | if (opt->encopts_len && ol > 0) { | |
895 | /* Zero any option indexes | |
896 | * We assume that referenced encapsulated options are NEVER | |
897 | * recursive as the index order could break. */ | |
898 | for (i = 0, eopt = opt->encopts; | |
899 | i < opt->encopts_len; | |
900 | i++, eopt++) | |
901 | { | |
902 | eoc = opt->option; | |
41eefe94 | 903 | if (eopt->type & OT_OPTION) { |
4eb7b489 | 904 | dgetopt(ctx, NULL, &eoc, NULL, NULL, 0, &oopt); |
03476881 RM |
905 | if (oopt) |
906 | oopt->index = 0; | |
63bdd2c2 | 907 | } |
03476881 RM |
908 | } |
909 | ||
4eb7b489 | 910 | while ((eod = dgetopt(ctx, &eos, &eoc, &eol, od, ol, &oopt))) { |
03476881 RM |
911 | for (i = 0, eopt = opt->encopts; |
912 | i < opt->encopts_len; | |
913 | i++, eopt++) | |
914 | { | |
c8521994 RM |
915 | if (eopt->option != eoc) |
916 | continue; | |
917 | if (eopt->type & OT_OPTION) { | |
918 | if (oopt == NULL) | |
919 | /* Report error? */ | |
920 | continue; | |
03476881 | 921 | } |
c8521994 RM |
922 | dhcp_envoption(ctx, fp, pfx, ifname, |
923 | eopt->type & OT_OPTION ? oopt:eopt, | |
924 | dgetopt, eod, eol); | |
03476881 RM |
925 | } |
926 | od += eos + eol; | |
927 | ol -= eos + eol; | |
8e7d8c37 RM |
928 | } |
929 | } | |
930 | ||
6b1f3b3e | 931 | out: |
c8521994 | 932 | free(pfx); |
8e7d8c37 | 933 | } |
03476881 RM |
934 | |
935 | void | |
936 | dhcp_zero_index(struct dhcp_opt *opt) | |
937 | { | |
938 | size_t i; | |
939 | struct dhcp_opt *o; | |
940 | ||
941 | opt->index = 0; | |
22e299e6 | 942 | for (i = 0, o = opt->embopts; i < opt->embopts_len; i++, o++) |
03476881 | 943 | dhcp_zero_index(o); |
22e299e6 | 944 | for (i = 0, o = opt->encopts; i < opt->encopts_len; i++, o++) |
03476881 RM |
945 | dhcp_zero_index(o); |
946 | } | |
416a319e RM |
947 | |
948 | size_t | |
d4595ab1 | 949 | dhcp_read_lease_fd(int fd, void **lease) |
416a319e | 950 | { |
6c6c2328 RM |
951 | struct stat st; |
952 | size_t sz; | |
953 | void *buf; | |
954 | ssize_t len; | |
c8521994 | 955 | |
6c6c2328 RM |
956 | if (fstat(fd, &st) != 0) |
957 | goto out; | |
958 | if (!S_ISREG(st.st_mode)) { | |
959 | errno = EINVAL; | |
416a319e | 960 | goto out; |
6c6c2328 RM |
961 | } |
962 | if (st.st_size > UINT32_MAX) { | |
963 | errno = E2BIG; | |
964 | goto out; | |
965 | } | |
416a319e | 966 | |
6c6c2328 | 967 | sz = (size_t)st.st_size; |
268e1de9 RM |
968 | if (sz == 0) |
969 | goto out; | |
6c6c2328 RM |
970 | if ((buf = malloc(sz)) == NULL) |
971 | goto out; | |
972 | if ((len = read(fd, buf, sz)) == -1) { | |
973 | free(buf); | |
974 | goto out; | |
416a319e | 975 | } |
6c6c2328 RM |
976 | *lease = buf; |
977 | return (size_t)len; | |
416a319e | 978 | |
416a319e RM |
979 | out: |
980 | *lease = NULL; | |
981 | return 0; | |
982 | } |