1 /** 2 * Copyright (C) 2012-2013 Steven Barth <steven@midlink.org> 3 * 4 * This program is free software; you can redistribute it and/or modify 5 * it under the terms of the GNU General Public License v2 as published by 6 * the Free Software Foundation. 7 * 8 * This program is distributed in the hope that it will be useful, 9 * but WITHOUT ANY WARRANTY; without even the implied warranty of 10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 * GNU General Public License for more details. 12 * 13 */ 14 15 #include <time.h> 16 #include <errno.h> 17 #include <fcntl.h> 18 #include <stdio.h> 19 #include <resolv.h> 20 #include <getopt.h> 21 #include <stddef.h> 22 #include <stdlib.h> 23 #include <string.h> 24 #include <unistd.h> 25 #include <signal.h> 26 #include <stdbool.h> 27 #include <syslog.h> 28 #include <alloca.h> 29 #include <inttypes.h> 30 31 #include <arpa/inet.h> 32 #include <net/if.h> 33 #include <netinet/ip6.h> 34 #include <netpacket/packet.h> 35 #include <linux/netlink.h> 36 37 #include <sys/socket.h> 38 #include <sys/ioctl.h> 39 #include <sys/epoll.h> 40 #include <sys/types.h> 41 #include <sys/wait.h> 42 #include <sys/syscall.h> 43 44 #include <libubox/uloop.h> 45 #include "odhcpd.h" 46 47 static int ioctl_sock = -1; 48 static int urandom_fd = -1; 49 50 static void sighandler(_unused int signal) 51 { 52 uloop_end(); 53 } 54 55 static void print_usage(const char *app) 56 { 57 printf("== %s Usage ==\n" 58 "Features: ra ndp dhcpv6" 59 #ifdef DHCPV4_SUPPORT 60 " dhcpv4" 61 #else 62 " no-dhcpv4" 63 #endif /* DHCPV4_SUPPORT */ 64 #ifdef WITH_UBUS 65 " ubus" 66 #else 67 " no-ubus" 68 #endif /* WITH_UBUS */ 69 #ifdef EXT_CER_ID 70 " cer" 71 #else 72 " no-cer" 73 #endif /* EXT_CER_ID */ 74 "\n" 75 "\n" 76 " -c <path> Use an alternative configuration file\n" 77 " -l <int> Specify log level 0..7 (default %d)\n" 78 " -h Print this help text and exit\n", 79 app, config.log_level); 80 } 81 82 static bool ipv6_enabled(void) 83 { 84 int fd = socket(AF_INET6, SOCK_DGRAM, 0); 85 86 if (fd < 0) 87 return false; 88 89 close(fd); 90 91 return true; 92 } 93 94 int main(int argc, char **argv) 95 { 96 openlog("odhcpd", LOG_PERROR | LOG_PID, LOG_DAEMON); 97 int opt; 98 99 while ((opt = getopt(argc, argv, "c:l:h")) != -1) { 100 switch (opt) { 101 case 'c': 102 config.uci_cfgfile = realpath(optarg, NULL); 103 fprintf(stderr, "Configuration will be read from %s\n", config.uci_cfgfile); 104 break; 105 case 'l': 106 config.log_level = (atoi(optarg) & LOG_PRIMASK); 107 fprintf(stderr, "Log level set to %d\n", config.log_level); 108 break; 109 case 'h': 110 print_usage(argv[0]); 111 return 0; 112 } 113 } 114 setlogmask(LOG_UPTO(config.log_level)); 115 uloop_init(); 116 117 if (getuid() != 0) { 118 syslog(LOG_ERR, "Must be run as root!"); 119 return 2; 120 } 121 122 ioctl_sock = socket(AF_INET, SOCK_DGRAM | SOCK_CLOEXEC, 0); 123 124 if (ioctl_sock < 0) 125 return 4; 126 127 if ((urandom_fd = open("/dev/urandom", O_RDONLY | O_CLOEXEC)) < 0) 128 return 4; 129 130 signal(SIGUSR1, SIG_IGN); 131 signal(SIGINT, sighandler); 132 signal(SIGTERM, sighandler); 133 134 if (netlink_init()) 135 return 4; 136 137 if (ipv6_enabled()) { 138 if (router_init()) 139 return 4; 140 141 if (dhcpv6_init()) 142 return 4; 143 144 if (ndp_init()) 145 return 4; 146 } 147 #ifndef DHCPV4_SUPPORT 148 else 149 return 4; 150 #else 151 if (dhcpv4_init()) 152 return 4; 153 #endif 154 155 odhcpd_run(); 156 return 0; 157 } 158 159 160 /* Read IPv6 MTU for interface */ 161 int odhcpd_get_interface_config(const char *ifname, const char *what) 162 { 163 char buf[64]; 164 165 snprintf(buf, sizeof(buf), "/proc/sys/net/ipv6/conf/%s/%s", ifname, what); 166 167 int fd = open(buf, O_RDONLY); 168 if (fd < 0) 169 return -1; 170 171 ssize_t len = read(fd, buf, sizeof(buf) - 1); 172 close(fd); 173 174 if (len < 0) 175 return -1; 176 177 buf[len] = 0; 178 return atoi(buf); 179 } 180 181 182 /* Read IPv6 MAC for interface */ 183 int odhcpd_get_mac(const struct interface *iface, uint8_t mac[6]) 184 { 185 struct ifreq ifr; 186 187 memset(&ifr, 0, sizeof(ifr)); 188 strncpy(ifr.ifr_name, iface->ifname, sizeof(ifr.ifr_name) - 1); 189 if (ioctl(ioctl_sock, SIOCGIFHWADDR, &ifr) < 0) 190 return -1; 191 192 memcpy(mac, ifr.ifr_hwaddr.sa_data, 6); 193 return 0; 194 } 195 196 int odhcpd_get_flags(const struct interface *iface) 197 { 198 struct ifreq ifr; 199 200 memset(&ifr, 0, sizeof(ifr)); 201 strncpy(ifr.ifr_name, iface->ifname, sizeof(ifr.ifr_name) - 1); 202 if (ioctl(ioctl_sock, SIOCGIFFLAGS, &ifr) < 0) 203 return -1; 204 205 return ifr.ifr_flags; 206 } 207 208 209 /* Forwards a packet on a specific interface with optional source address */ 210 ssize_t odhcpd_send_with_src(int socket, struct sockaddr_in6 *dest, 211 struct iovec *iov, size_t iov_len, 212 const struct interface *iface, const struct in6_addr *src_addr) 213 { 214 /* Construct headers */ 215 uint8_t cmsg_buf[CMSG_SPACE(sizeof(struct in6_pktinfo))] = {0}; 216 struct msghdr msg = { 217 .msg_name = (void *) dest, 218 .msg_namelen = sizeof(*dest), 219 .msg_iov = iov, 220 .msg_iovlen = iov_len, 221 .msg_control = cmsg_buf, 222 .msg_controllen = sizeof(cmsg_buf), 223 .msg_flags = 0 224 }; 225 226 /* Set control data (define destination interface) */ 227 struct cmsghdr *chdr = CMSG_FIRSTHDR(&msg); 228 chdr->cmsg_level = IPPROTO_IPV6; 229 chdr->cmsg_type = IPV6_PKTINFO; 230 chdr->cmsg_len = CMSG_LEN(sizeof(struct in6_pktinfo)); 231 struct in6_pktinfo *pktinfo = (struct in6_pktinfo*)CMSG_DATA(chdr); 232 pktinfo->ipi6_ifindex = iface->ifindex; 233 234 /* Set source address if provided */ 235 if (src_addr) 236 pktinfo->ipi6_addr = *src_addr; 237 238 /* Also set scope ID if link-local */ 239 if (IN6_IS_ADDR_LINKLOCAL(&dest->sin6_addr) 240 || IN6_IS_ADDR_MC_LINKLOCAL(&dest->sin6_addr)) 241 dest->sin6_scope_id = iface->ifindex; 242 243 char ipbuf[INET6_ADDRSTRLEN]; 244 inet_ntop(AF_INET6, &dest->sin6_addr, ipbuf, sizeof(ipbuf)); 245 246 ssize_t sent = sendmsg(socket, &msg, MSG_DONTWAIT); 247 if (sent < 0) 248 syslog(LOG_ERR, "Failed to send to %s%%%s@%s (%m)", 249 ipbuf, iface->name, iface->ifname); 250 else 251 syslog(LOG_DEBUG, "Sent %zd bytes to %s%%%s@%s", 252 sent, ipbuf, iface->name, iface->ifname); 253 return sent; 254 } 255 256 /* Forwards a packet on a specific interface */ 257 ssize_t odhcpd_send(int socket, struct sockaddr_in6 *dest, 258 struct iovec *iov, size_t iov_len, 259 const struct interface *iface) 260 { 261 return odhcpd_send_with_src(socket, dest, iov, iov_len, iface, NULL); 262 } 263 264 265 int odhcpd_get_interface_linklocal_addr(struct interface *iface, struct in6_addr *addr) 266 { 267 /* Return cached address if valid */ 268 if (iface->cached_linklocal_valid) { 269 *addr = iface->cached_linklocal_addr; 270 return 0; 271 } 272 273 /* First try to get link-local address from interface addresses */ 274 for (size_t i = 0; i < iface->addr6_len; ++i) { 275 if (IN6_IS_ADDR_LINKLOCAL(&iface->addr6[i].addr.in6)) { 276 *addr = iface->addr6[i].addr.in6; 277 /* Cache the result for future use */ 278 iface->cached_linklocal_addr = *addr; 279 iface->cached_linklocal_valid = true; 280 return 0; 281 } 282 } 283 284 /* Fallback to socket-based method */ 285 struct sockaddr_in6 sockaddr; 286 socklen_t alen = sizeof(sockaddr); 287 int sock = socket(AF_INET6, SOCK_RAW, IPPROTO_ICMPV6); 288 289 if (sock >= 0) { 290 memset(&sockaddr, 0, sizeof(sockaddr)); 291 sockaddr.sin6_family = AF_INET6; 292 inet_pton(AF_INET6, ALL_IPV6_ROUTERS, &sockaddr.sin6_addr); 293 sockaddr.sin6_scope_id = iface->ifindex; 294 295 if (!connect(sock, (struct sockaddr*)&sockaddr, sizeof(sockaddr)) && 296 !getsockname(sock, (struct sockaddr*)&sockaddr, &alen)) { 297 *addr = sockaddr.sin6_addr; 298 /* Cache the result for future use */ 299 iface->cached_linklocal_addr = *addr; 300 iface->cached_linklocal_valid = true; 301 close(sock); 302 return 0; 303 } 304 close(sock); 305 } 306 307 return -1; 308 } 309 310 /* Try to send with link-local source address for RFC 4861 compliance and macOS compatibility. 311 * RFC 4861, ยง4.2 mandates that Neighbor Advertisement source address MUST be 312 * the link-local address assigned to the interface from which this message is sent. */ 313 ssize_t odhcpd_try_send_with_src(int socket, struct sockaddr_in6 *dest, 314 struct iovec *iov, size_t iov_len, 315 struct interface *iface) 316 { 317 struct in6_addr src_addr; 318 319 if (iface->ndp_from_link_local && odhcpd_get_interface_linklocal_addr(iface, &src_addr) == 0) { 320 return odhcpd_send_with_src(socket, dest, iov, iov_len, iface, &src_addr); 321 } else { 322 /* Fall back to default behavior if no link-local address is available or flag is disabled */ 323 return odhcpd_send(socket, dest, iov, iov_len, iface); 324 } 325 } 326 327 /* 328 * DNS address selection criteria order : 329 * - use IPv6 address with valid lifetime if none is yet selected 330 * - use IPv6 address with a preferred lifetime if the already selected IPv6 address is deprecated 331 * - use an IPv6 ULA address if the already selected IPv6 address is not an ULA address 332 * - use the IPv6 address with the longest preferred lifetime 333 */ 334 int odhcpd_get_interface_dns_addr(struct interface *iface, struct in6_addr *addr) 335 { 336 time_t now = odhcpd_time(); 337 ssize_t m = -1; 338 339 if (!iface->dns_service) 340 return -1; 341 342 for (size_t i = 0; i < iface->addr6_len; ++i) { 343 if (iface->addr6[i].valid_lt <= (uint32_t)now) 344 continue; 345 346 if (m < 0) { 347 m = i; 348 continue; 349 } 350 351 if (iface->addr6[m].preferred_lt >= (uint32_t)now && 352 iface->addr6[i].preferred_lt < (uint32_t)now) 353 continue; 354 355 if (IN6_IS_ADDR_ULA(&iface->addr6[i].addr.in6)) { 356 if (!IN6_IS_ADDR_ULA(&iface->addr6[m].addr.in6)) { 357 m = i; 358 continue; 359 } 360 } else if (IN6_IS_ADDR_ULA(&iface->addr6[m].addr.in6)) 361 continue; 362 363 if (iface->addr6[i].preferred_lt > iface->addr6[m].preferred_lt) 364 m = i; 365 } 366 367 if (m >= 0) { 368 *addr = iface->addr6[m].addr.in6; 369 return 0; 370 } 371 372 return odhcpd_get_interface_linklocal_addr(iface, addr); 373 } 374 375 struct interface* odhcpd_get_interface_by_index(int ifindex) 376 { 377 struct interface *iface; 378 379 avl_for_each_element(&interfaces, iface, avl) { 380 if (iface->ifindex == ifindex) 381 return iface; 382 } 383 384 return NULL; 385 } 386 387 /* Convenience function to receive and do basic validation of packets */ 388 static void odhcpd_receive_packets(struct uloop_fd *u, _unused unsigned int events) 389 { 390 struct odhcpd_event *e = container_of(u, struct odhcpd_event, uloop); 391 392 uint8_t data_buf[8192], cmsg_buf[128]; 393 union { 394 struct sockaddr_in6 in6; 395 struct sockaddr_in in; 396 struct sockaddr_ll ll; 397 struct sockaddr_nl nl; 398 } addr; 399 400 if (u->error) { 401 int ret = -1; 402 socklen_t ret_len = sizeof(ret); 403 404 u->error = false; 405 if (e->handle_error && getsockopt(u->fd, SOL_SOCKET, SO_ERROR, &ret, &ret_len) == 0) 406 e->handle_error(e, ret); 407 } 408 409 if (e->recv_msgs) { 410 e->recv_msgs(e); 411 return; 412 } 413 414 while (true) { 415 struct iovec iov = {data_buf, sizeof(data_buf)}; 416 struct msghdr msg = { 417 .msg_name = (void *) &addr, 418 .msg_namelen = sizeof(addr), 419 .msg_iov = &iov, 420 .msg_iovlen = 1, 421 .msg_control = cmsg_buf, 422 .msg_controllen = sizeof(cmsg_buf), 423 .msg_flags = 0 424 }; 425 426 ssize_t len = recvmsg(u->fd, &msg, MSG_DONTWAIT); 427 if (len < 0) { 428 if (errno == EAGAIN) 429 break; 430 else 431 continue; 432 } 433 434 435 /* Extract destination interface */ 436 int destiface = 0; 437 int *hlim = NULL; 438 void *dest = NULL; 439 struct in6_pktinfo *pktinfo; 440 struct in_pktinfo *pkt4info; 441 for (struct cmsghdr *ch = CMSG_FIRSTHDR(&msg); ch != NULL; ch = CMSG_NXTHDR(&msg, ch)) { 442 if (ch->cmsg_level == IPPROTO_IPV6 && 443 ch->cmsg_type == IPV6_PKTINFO) { 444 pktinfo = (struct in6_pktinfo*)CMSG_DATA(ch); 445 destiface = pktinfo->ipi6_ifindex; 446 dest = &pktinfo->ipi6_addr; 447 } else if (ch->cmsg_level == IPPROTO_IP && 448 ch->cmsg_type == IP_PKTINFO) { 449 pkt4info = (struct in_pktinfo*)CMSG_DATA(ch); 450 destiface = pkt4info->ipi_ifindex; 451 dest = &pkt4info->ipi_addr; 452 } else if (ch->cmsg_level == IPPROTO_IPV6 && 453 ch->cmsg_type == IPV6_HOPLIMIT) { 454 hlim = (int*)CMSG_DATA(ch); 455 } 456 } 457 458 /* Check hoplimit if received */ 459 if (hlim && *hlim != 255) 460 continue; 461 462 /* Detect interface for packet sockets */ 463 if (addr.ll.sll_family == AF_PACKET) 464 destiface = addr.ll.sll_ifindex; 465 466 char ipbuf[INET6_ADDRSTRLEN] = "kernel"; 467 if (addr.ll.sll_family == AF_PACKET && 468 len >= (ssize_t)sizeof(struct ip6_hdr)) 469 inet_ntop(AF_INET6, &data_buf[8], ipbuf, sizeof(ipbuf)); 470 else if (addr.in6.sin6_family == AF_INET6) 471 inet_ntop(AF_INET6, &addr.in6.sin6_addr, ipbuf, sizeof(ipbuf)); 472 else if (addr.in.sin_family == AF_INET) 473 inet_ntop(AF_INET, &addr.in.sin_addr, ipbuf, sizeof(ipbuf)); 474 475 /* From netlink */ 476 if (addr.nl.nl_family == AF_NETLINK) { 477 syslog(LOG_DEBUG, "Received %zd Bytes from %s%%netlink", len, 478 ipbuf); 479 e->handle_dgram(&addr, data_buf, len, NULL, dest); 480 return; 481 } else if (destiface != 0) { 482 struct interface *iface; 483 484 avl_for_each_element(&interfaces, iface, avl) { 485 if (iface->ifindex != destiface) 486 continue; 487 488 syslog(LOG_DEBUG, "Received %zd Bytes from %s%%%s@%s", len, 489 ipbuf, iface->name, iface->ifname); 490 491 e->handle_dgram(&addr, data_buf, len, iface, dest); 492 } 493 } 494 495 496 } 497 } 498 499 /* Register events for the multiplexer */ 500 int odhcpd_register(struct odhcpd_event *event) 501 { 502 event->uloop.cb = odhcpd_receive_packets; 503 return uloop_fd_add(&event->uloop, ULOOP_READ | 504 ((event->handle_error) ? ULOOP_ERROR_CB : 0)); 505 } 506 507 int odhcpd_deregister(struct odhcpd_event *event) 508 { 509 event->uloop.cb = NULL; 510 return uloop_fd_delete(&event->uloop); 511 } 512 513 void odhcpd_process(struct odhcpd_event *event) 514 { 515 odhcpd_receive_packets(&event->uloop, 0); 516 } 517 518 int odhcpd_urandom(void *data, size_t len) 519 { 520 return read(urandom_fd, data, len); 521 } 522 523 524 time_t odhcpd_time(void) 525 { 526 struct timespec ts; 527 clock_gettime(CLOCK_MONOTONIC, &ts); 528 return ts.tv_sec; 529 } 530 531 532 static const char hexdigits[] = "0123456789abcdef"; 533 static const int8_t hexvals[] = { 534 -1, -1, -1, -1, -1, -1, -1, -1, -1, -2, -2, -1, -1, -2, -1, -1, 535 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 536 -2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 537 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, -1, -1, -1, -1, -1, -1, 538 -1, 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1, -1, -1, 539 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 540 -1, 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1, -1, -1, 541 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 542 }; 543 544 ssize_t odhcpd_unhexlify(uint8_t *dst, size_t len, const char *src) 545 { 546 size_t c; 547 for (c = 0; c < len && src[0] && src[1]; ++c) { 548 int8_t x = (int8_t)*src++; 549 int8_t y = (int8_t)*src++; 550 if (x < 0 || (x = hexvals[x]) < 0 551 || y < 0 || (y = hexvals[y]) < 0) 552 return -1; 553 dst[c] = x << 4 | y; 554 while (((int8_t)*src) < 0 || 555 (*src && hexvals[(uint8_t)*src] < 0)) 556 src++; 557 } 558 559 return c; 560 } 561 562 563 void odhcpd_hexlify(char *dst, const uint8_t *src, size_t len) 564 { 565 for (size_t i = 0; i < len; ++i) { 566 *dst++ = hexdigits[src[i] >> 4]; 567 *dst++ = hexdigits[src[i] & 0x0f]; 568 } 569 *dst = 0; 570 } 571 572 const char *odhcpd_print_mac(const uint8_t *mac, const size_t len) 573 { 574 static char buf[32]; 575 576 snprintf(buf, sizeof(buf), "%02x", mac[0]); 577 for (size_t i = 1, j = 2; i < len && j < sizeof(buf); i++, j += 3) 578 snprintf(buf + j, sizeof(buf) - j, ":%02x", mac[i]); 579 580 return buf; 581 } 582 583 int odhcpd_bmemcmp(const void *av, const void *bv, size_t bits) 584 { 585 const uint8_t *a = av, *b = bv; 586 size_t bytes = bits / 8; 587 bits %= 8; 588 589 int res = memcmp(a, b, bytes); 590 if (res == 0 && bits > 0) 591 res = (a[bytes] >> (8 - bits)) - (b[bytes] >> (8 - bits)); 592 593 return res; 594 } 595 596 597 void odhcpd_bmemcpy(void *av, const void *bv, size_t bits) 598 { 599 uint8_t *a = av; 600 const uint8_t *b = bv; 601 602 size_t bytes = bits / 8; 603 bits %= 8; 604 memcpy(a, b, bytes); 605 606 if (bits > 0) { 607 uint8_t mask = (1 << (8 - bits)) - 1; 608 a[bytes] = (a[bytes] & mask) | ((~mask) & b[bytes]); 609 } 610 } 611 612 613 int odhcpd_parse_addr6_prefix(const char *str, struct in6_addr *addr, uint8_t *prefix) 614 { 615 size_t len; 616 char *delim; 617 618 *prefix = 0; 619 if (!str) 620 return -1; 621 622 len = strlen(str); 623 624 char buf[len + 1]; 625 memcpy(buf, str, len); 626 buf[len] = '\0'; 627 628 delim = memchr(buf, '/', len); 629 if (!delim) 630 return -1; 631 632 *(delim++) = '\0'; 633 634 if (inet_pton(AF_INET6, buf, addr) != 1) 635 return -1; 636 637 if (sscanf(delim, "%" SCNu8, prefix) != 1 || *prefix > 128) { 638 *prefix = 0; 639 return -1; 640 } 641 642 return 0; 643 } 644 645 646 int odhcpd_netmask2bitlen(bool inet6, void *mask) 647 { 648 int bits; 649 struct in_addr *v4; 650 struct in6_addr *v6; 651 652 if (inet6) 653 for (bits = 0, v6 = mask; 654 bits < 128 && (v6->s6_addr[bits / 8] << (bits % 8)) & 128; 655 bits++); 656 else 657 for (bits = 0, v4 = mask; 658 bits < 32 && (ntohl(v4->s_addr) << bits) & 0x80000000; 659 bits++); 660 661 return bits; 662 } 663 664 bool odhcpd_bitlen2netmask(bool inet6, unsigned int bits, void *mask) 665 { 666 uint8_t b; 667 struct in_addr *v4; 668 struct in6_addr *v6; 669 670 if (inet6) 671 { 672 if (bits > 128) 673 return false; 674 675 v6 = mask; 676 677 for (unsigned int i = 0; i < sizeof(v6->s6_addr); i++) 678 { 679 b = (bits > 8) ? 8 : bits; 680 v6->s6_addr[i] = (uint8_t)(0xFF << (8 - b)); 681 bits -= b; 682 } 683 } 684 else 685 { 686 if (bits > 32) 687 return false; 688 689 v4 = mask; 690 v4->s_addr = bits ? htonl(~((1 << (32 - bits)) - 1)) : 0; 691 } 692 693 return true; 694 } 695 696 bool odhcpd_valid_hostname(const char *name) 697 { 698 #define MAX_LABEL 63 699 const char *c, *label, *label_end; 700 int label_sz = 0; 701 702 for (c = name, label_sz = 0, label = name, label_end = name + strcspn(name, ".") - 1; 703 *c && label_sz <= MAX_LABEL; c++) { 704 if ((*c >= '' && *c <= '9') || 705 (*c >= 'A' && *c <= 'Z') || 706 (*c >= 'a' && *c <= 'z')) { 707 label_sz++; 708 continue; 709 } 710 711 if ((*c == '_' || *c == '-') && c != label && c != label_end) { 712 label_sz++; 713 continue; 714 } 715 716 if (*c == '.') { 717 if (*(c + 1)) { 718 label = c + 1; 719 label_end = label + strcspn(label, ".") - 1; 720 label_sz = 0; 721 } 722 continue; 723 } 724 725 return false; 726 } 727 728 return (label_sz && label_sz <= MAX_LABEL ? true : false); 729 } 730
This page was automatically generated by LXR 0.3.1. • OpenWrt