3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
7 *
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
12 *
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 *
19 * CDDL HEADER END
20 */
21 /*
22 * Copyright (c) 1999, 2010, Oracle and/or its affiliates. All rights reserved.
23 */
24
25 #include <unistd.h>
26 #include <sys/types.h>
27 #include <sys/stat.h>
28 #include <stdlib.h>
29 #include <netinet/in.h> /* struct in_addr */
30 #include <netinet/dhcp.h>
31 #include <signal.h>
32 #include <sys/socket.h>
33 #include <net/route.h>
34 #include <net/if_arp.h>
35 #include <string.h>
36 #include <dhcpmsg.h>
37 #include <ctype.h>
38 #include <netdb.h>
39 #include <fcntl.h>
40 #include <stdio.h>
41 #include <dhcp_hostconf.h>
42
43 #include "states.h"
44 #include "agent.h"
45 #include "interface.h"
46 #include "util.h"
47 #include "packet.h"
48
49 /*
50 * this file contains utility functions that have no real better home
51 * of their own. they can largely be broken into six categories:
52 *
53 * o conversion functions -- functions to turn integers into strings,
54 * or to convert between units of a similar measure.
55 *
56 * o time and timer functions -- functions to handle time measurement
57 * and events.
58 *
59 * o ipc-related functions -- functions to simplify the generation of
60 * ipc messages to the agent's clients.
61 *
62 * o signal-related functions -- functions to clean up the agent when
63 * it receives a signal.
64 *
65 * o routing table manipulation functions
66 *
67 * o true miscellany -- anything else
68 */
69
70 /*
71 * pkt_type_to_string(): stringifies a packet type
72 *
73 * input: uchar_t: a DHCP packet type value, RFC 2131 or 3315
74 * boolean_t: B_TRUE if IPv6
75 * output: const char *: the stringified packet type
76 */
77
78 const char *
79 pkt_type_to_string(uchar_t type, boolean_t isv6)
80 {
81 /*
82 * note: the ordering in these arrays allows direct indexing of the
83 * table based on the RFC packet type value passed in.
84 */
85
86 static const char *v4types[] = {
87 "BOOTP", "DISCOVER", "OFFER", "REQUEST", "DECLINE",
88 "ACK", "NAK", "RELEASE", "INFORM"
89 };
449 boolean_t
450 bind_sock_v6(int fd, in_port_t port_hbo, const in6_addr_t *addr_nbo)
451 {
452 struct sockaddr_in6 sin6;
453 int on = 1;
454
455 (void) memset(&sin6, 0, sizeof (struct sockaddr_in6));
456 sin6.sin6_family = AF_INET6;
457 sin6.sin6_port = htons(port_hbo);
458 if (addr_nbo != NULL) {
459 (void) memcpy(&sin6.sin6_addr, addr_nbo,
460 sizeof (sin6.sin6_addr));
461 }
462
463 (void) setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof (int));
464
465 return (bind(fd, (struct sockaddr *)&sin6, sizeof (sin6)) == 0);
466 }
467
468 /*
469 * valid_hostname(): check whether a string is a valid hostname
470 *
471 * input: const char *: the string to verify as a hostname
472 * output: boolean_t: B_TRUE if the string is a valid hostname
473 *
474 * Note that we accept both host names beginning with a digit and
475 * those containing hyphens. Neither is strictly legal according
476 * to the RFCs, but both are in common practice, so we endeavour
477 * to not break what customers are using.
478 */
479
480 static boolean_t
481 valid_hostname(const char *hostname)
482 {
483 unsigned int i;
484
485 for (i = 0; hostname[i] != '\0'; i++) {
486
487 if (isalpha(hostname[i]) || isdigit(hostname[i]) ||
488 (((hostname[i] == '-') || (hostname[i] == '.')) && (i > 0)))
489 continue;
490
491 return (B_FALSE);
492 }
493
494 return (i > 0);
495 }
496
497 /*
498 * iffile_to_hostname(): return the hostname contained on a line of the form
499 *
500 * [ ^I]*inet[ ^I]+hostname[\n]*\0
501 *
502 * in the file located at the specified path
503 *
504 * input: const char *: the path of the file to look in for the hostname
505 * output: const char *: the hostname at that path, or NULL on failure
506 */
507
508 #define IFLINE_MAX 1024 /* maximum length of a hostname.<if> line */
509
510 const char *
511 iffile_to_hostname(const char *path)
512 {
513 FILE *fp;
514 static char ifline[IFLINE_MAX];
515
516 fp = fopen(path, "r");
517 if (fp == NULL)
539 if ((*p == '\n') || (*p == '\0')) {
540 (void) fclose(fp);
541 return (NULL);
542 }
543 if (isspace(*p)) {
544 char *nlptr;
545
546 /* no need to read more of the file */
547 (void) fclose(fp);
548
549 while (isspace(*p))
550 p++;
551 if ((nlptr = strrchr(p, '\n')) != NULL)
552 *nlptr = '\0';
553 if (strlen(p) > MAXHOSTNAMELEN) {
554 dhcpmsg(MSG_WARNING,
555 "iffile_to_hostname:"
556 " host name too long");
557 return (NULL);
558 }
559 if (valid_hostname(p)) {
560 return (p);
561 } else {
562 dhcpmsg(MSG_WARNING,
563 "iffile_to_hostname:"
564 " host name not valid");
565 return (NULL);
566 }
567 } else {
568 (void) fclose(fp);
569 return (NULL);
570 }
571 }
572 }
573
574 (void) fclose(fp);
575 return (NULL);
576 }
577
578 /*
579 * init_timer(): set up a DHCP timer
687 void
688 write_lease_to_hostconf(dhcp_smach_t *dsmp)
689 {
690 PKT_LIST *plp[2];
691 const char *hcfile;
692
693 hcfile = ifname_to_hostconf(dsmp->dsm_name, dsmp->dsm_isv6);
694 plp[0] = dsmp->dsm_ack;
695 plp[1] = dsmp->dsm_orig_ack;
696 if (write_hostconf(dsmp->dsm_name, plp, 2,
697 monosec_to_time(dsmp->dsm_curstart_monosec),
698 dsmp->dsm_isv6) != -1) {
699 dhcpmsg(MSG_DEBUG, "wrote lease to %s", hcfile);
700 } else if (errno == EROFS) {
701 dhcpmsg(MSG_DEBUG, "%s is on a read-only file "
702 "system; not saving lease", hcfile);
703 } else {
704 dhcpmsg(MSG_ERR, "cannot write %s (reboot will "
705 "not use cached configuration)", hcfile);
706 }
707 }
|
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
7 *
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
12 *
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 *
19 * CDDL HEADER END
20 */
21 /*
22 * Copyright (c) 1999, 2010, Oracle and/or its affiliates. All rights reserved.
23 * Copyright (c) 2016, Chris Fraire <cfraire@me.com>.
24 */
25
26 #include <unistd.h>
27 #include <sys/types.h>
28 #include <sys/stat.h>
29 #include <sys/utsname.h>
30 #include <stdlib.h>
31 #include <netinet/in.h> /* struct in_addr */
32 #include <netinet/dhcp.h>
33 #include <signal.h>
34 #include <sys/socket.h>
35 #include <net/route.h>
36 #include <net/if_arp.h>
37 #include <string.h>
38 #include <dhcpmsg.h>
39 #include <ctype.h>
40 #include <arpa/inet.h>
41 #include <arpa/nameser.h>
42 #include <resolv.h>
43 #include <netdb.h>
44 #include <fcntl.h>
45 #include <stdio.h>
46 #include <dhcp_hostconf.h>
47 #include <limits.h>
48 #include <strings.h>
49 #include <libipadm.h>
50
51 #include "states.h"
52 #include "agent.h"
53 #include "interface.h"
54 #include "util.h"
55 #include "packet.h"
56 #include "defaults.h"
57
58 /*
59 * this file contains utility functions that have no real better home
60 * of their own. they can largely be broken into six categories:
61 *
62 * o conversion functions -- functions to turn integers into strings,
63 * or to convert between units of a similar measure.
64 *
65 * o time and timer functions -- functions to handle time measurement
66 * and events.
67 *
68 * o ipc-related functions -- functions to simplify the generation of
69 * ipc messages to the agent's clients.
70 *
71 * o signal-related functions -- functions to clean up the agent when
72 * it receives a signal.
73 *
74 * o routing table manipulation functions
75 *
76 * o true miscellany -- anything else
77 */
78
79 #define ETCNODENAME "/etc/nodename"
80 #define ETCDEFAULTDOMAIN "/etc/defaultdomain"
81
82 static boolean_t is_fqdn(const char *);
83 static int dhcp_assemble_fqdn(dhcp_smach_t *dsmp);
84
85 /*
86 * pkt_type_to_string(): stringifies a packet type
87 *
88 * input: uchar_t: a DHCP packet type value, RFC 2131 or 3315
89 * boolean_t: B_TRUE if IPv6
90 * output: const char *: the stringified packet type
91 */
92
93 const char *
94 pkt_type_to_string(uchar_t type, boolean_t isv6)
95 {
96 /*
97 * note: the ordering in these arrays allows direct indexing of the
98 * table based on the RFC packet type value passed in.
99 */
100
101 static const char *v4types[] = {
102 "BOOTP", "DISCOVER", "OFFER", "REQUEST", "DECLINE",
103 "ACK", "NAK", "RELEASE", "INFORM"
104 };
464 boolean_t
465 bind_sock_v6(int fd, in_port_t port_hbo, const in6_addr_t *addr_nbo)
466 {
467 struct sockaddr_in6 sin6;
468 int on = 1;
469
470 (void) memset(&sin6, 0, sizeof (struct sockaddr_in6));
471 sin6.sin6_family = AF_INET6;
472 sin6.sin6_port = htons(port_hbo);
473 if (addr_nbo != NULL) {
474 (void) memcpy(&sin6.sin6_addr, addr_nbo,
475 sizeof (sin6.sin6_addr));
476 }
477
478 (void) setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof (int));
479
480 return (bind(fd, (struct sockaddr *)&sin6, sizeof (sin6)) == 0);
481 }
482
483 /*
484 * iffile_to_hostname(): return the hostname contained on a line of the form
485 *
486 * [ ^I]*inet[ ^I]+hostname[\n]*\0
487 *
488 * in the file located at the specified path
489 *
490 * input: const char *: the path of the file to look in for the hostname
491 * output: const char *: the hostname at that path, or NULL on failure
492 */
493
494 #define IFLINE_MAX 1024 /* maximum length of a hostname.<if> line */
495
496 const char *
497 iffile_to_hostname(const char *path)
498 {
499 FILE *fp;
500 static char ifline[IFLINE_MAX];
501
502 fp = fopen(path, "r");
503 if (fp == NULL)
525 if ((*p == '\n') || (*p == '\0')) {
526 (void) fclose(fp);
527 return (NULL);
528 }
529 if (isspace(*p)) {
530 char *nlptr;
531
532 /* no need to read more of the file */
533 (void) fclose(fp);
534
535 while (isspace(*p))
536 p++;
537 if ((nlptr = strrchr(p, '\n')) != NULL)
538 *nlptr = '\0';
539 if (strlen(p) > MAXHOSTNAMELEN) {
540 dhcpmsg(MSG_WARNING,
541 "iffile_to_hostname:"
542 " host name too long");
543 return (NULL);
544 }
545 if (ipadm_is_valid_hostname(p)) {
546 return (p);
547 } else {
548 dhcpmsg(MSG_WARNING,
549 "iffile_to_hostname:"
550 " host name not valid");
551 return (NULL);
552 }
553 } else {
554 (void) fclose(fp);
555 return (NULL);
556 }
557 }
558 }
559
560 (void) fclose(fp);
561 return (NULL);
562 }
563
564 /*
565 * init_timer(): set up a DHCP timer
673 void
674 write_lease_to_hostconf(dhcp_smach_t *dsmp)
675 {
676 PKT_LIST *plp[2];
677 const char *hcfile;
678
679 hcfile = ifname_to_hostconf(dsmp->dsm_name, dsmp->dsm_isv6);
680 plp[0] = dsmp->dsm_ack;
681 plp[1] = dsmp->dsm_orig_ack;
682 if (write_hostconf(dsmp->dsm_name, plp, 2,
683 monosec_to_time(dsmp->dsm_curstart_monosec),
684 dsmp->dsm_isv6) != -1) {
685 dhcpmsg(MSG_DEBUG, "wrote lease to %s", hcfile);
686 } else if (errno == EROFS) {
687 dhcpmsg(MSG_DEBUG, "%s is on a read-only file "
688 "system; not saving lease", hcfile);
689 } else {
690 dhcpmsg(MSG_ERR, "cannot write %s (reboot will "
691 "not use cached configuration)", hcfile);
692 }
693 }
694
695 /*
696 * Try to get a string from the first line of a file, up to but not
697 * including any space (0x20) or newline.
698 */
699
700 static int
701 dhcp_get_oneline(const char *filename, char *buf, size_t buflen)
702 {
703 char value[SYS_NMLN], *c;
704 int fd, i;
705
706 if ((fd = open(filename, O_RDONLY)) <= 0) {
707 dhcpmsg(MSG_DEBUG, "dhcp_get_oneline: could not open %s",
708 filename);
709 *buf = '\0';
710 } else {
711 if ((i = read(fd, value, SYS_NMLN - 1)) <= 0) {
712 dhcpmsg(MSG_WARNING, "dhcp_get_oneline: no line in %s",
713 filename);
714 *buf = '\0';
715 } else {
716 value[i] = '\0';
717 if ((c = strchr(value, '\n')) != NULL)
718 *c = '\0';
719 if ((c = strchr(value, ' ')) != NULL)
720 *c = '\0';
721
722 if (strlcpy(buf, value, buflen) >= buflen) {
723 dhcpmsg(MSG_WARNING, "dhcp_get_oneline: too long value, %s",
724 value);
725 *buf = '\0';
726 }
727 }
728 (void) close(fd);
729 }
730
731 return (*buf != '\0' ? 0 : -1);
732 }
733
734 /*
735 * Try to get the hostname from the /etc/nodename file. uname(2) cannot
736 * be used, because that is initialized after DHCP has solicited, in order
737 * to allow for the possibility that utsname.nodename can be set from
738 * DHCP Hostname. Here, though, we want to send a value specified
739 * advance of DHCP, so read /etc/nodename directly.
740 */
741
742 static int
743 dhcp_get_nodename(char *buf, size_t buflen)
744 {
745 return dhcp_get_oneline(ETCNODENAME, buf, buflen);
746 }
747
748 /*
749 * Try to get the value from the /etc/defaultdomain file. libnsl's
750 * domainname() cannot be used, because that is initialized after DHCP has
751 * solicited. Here, though, we want to send a value specified in advance
752 * of DHCP, so read /etc/defaultdomain directly.
753 */
754
755 static int
756 dhcp_get_defaultdomain(char *buf, size_t buflen)
757 {
758 return dhcp_get_oneline(ETCDEFAULTDOMAIN, buf, buflen);
759 }
760
761 /*
762 * dhcp_add_hostname_opt(): Set CD_HOSTNAME option if REQUEST_HOSTNAME is
763 * affirmative and if 1) dsm_msg_reqhost is available; or
764 * 2) hostname is read from an extant /etc/hostname.<ifname>
765 * file; or 3) interface is primary and nodename(4) is defined.
766 *
767 * input: dhcp_pkt_t *: pointer to DHCP message being constructed;
768 * dhcp_smach_t *: pointer to interface DHCP state machine;
769 */
770
771 int
772 dhcp_add_hostname_opt(dhcp_pkt_t *dpkt, dhcp_smach_t *dsmp)
773 {
774 const char *reqhost;
775 char nodename[MAXNAMELEN];
776
777 if (!df_get_bool(dsmp->dsm_name, dsmp->dsm_isv6, DF_REQUEST_HOSTNAME))
778 return (0);
779
780 dhcpmsg(MSG_DEBUG, "dhcp_add_hostname_opt: DF_REQUEST_HOSTNAME");
781
782 if (dsmp->dsm_msg_reqhost != NULL
783 && ipadm_is_valid_hostname(dsmp->dsm_msg_reqhost)) {
784 reqhost = dsmp->dsm_msg_reqhost;
785 } else {
786 char hostfile[PATH_MAX + 1];
787
788 (void) snprintf(hostfile, sizeof (hostfile),
789 "/etc/hostname.%s", dsmp->dsm_name);
790 reqhost = iffile_to_hostname(hostfile);
791 }
792
793 if (reqhost == NULL && (dsmp->dsm_dflags & DHCP_IF_PRIMARY) &&
794 dhcp_get_nodename(nodename, sizeof (nodename)) == 0) {
795 reqhost = nodename;
796 }
797
798 if (reqhost != NULL) {
799 free(dsmp->dsm_reqhost);
800 if ((dsmp->dsm_reqhost = strdup(reqhost)) == NULL)
801 dhcpmsg(MSG_WARNING, "dhcp_add_hostname_opt: cannot allocate "
802 "memory for host name option");
803 }
804
805 if (dsmp->dsm_reqhost != NULL) {
806 dhcpmsg(MSG_DEBUG, "dhcp_add_hostname_opt: host %s for %s",
807 dsmp->dsm_reqhost, dsmp->dsm_name);
808 (void) add_pkt_opt(dpkt, CD_HOSTNAME, dsmp->dsm_reqhost,
809 strlen(dsmp->dsm_reqhost));
810 return 1;
811 }
812 else {
813 dhcpmsg(MSG_DEBUG, "dhcp_add_hostname_opt: no hostname for %s",
814 dsmp->dsm_name);
815 }
816
817 return (0);
818 }
819
820 /*
821 * dhcp_add_fqdn_opt(): Set CD_CLIENTFQDN option if dsm_reqfqdn is not NULL
822 * or if dhcp_assemble_fqdn() initializes it. If dsm_reqfqdn
823 * cannot be set, no option will be added.
824 *
825 * input: dhcp_pkt_t *: pointer to DHCP message being constructed;
826 * dhcp_smach_t *: pointer to interface DHCP state machine;
827 * output: 0 if a CLIENT_FQDN was added; non-zero if otherwise;
828 */
829
830 int
831 dhcp_add_fqdn_opt(dhcp_pkt_t *dpkt, dhcp_smach_t *dsmp)
832 {
833 /*
834 * RFC 4702 section 2:
835 *
836 * The format of the Client FQDN option is:
837 *
838 * Code Len Flags RCODE1 RCODE2 Domain Name
839 * +------+------+------+------+------+------+--
840 * | 81 | n | | | | ...
841 * +------+------+------+------+------+------+--
842 *
843 * Code and Len are distinct, and the remainder is in a single buffer,
844 * opt81, for Flags + (unused) RCODE1 and RCODE2 (all octets) and a
845 * potentially maximum-length domain name.
846 *
847 * The format of the Flags field is:
848 *
849 * 0 1 2 3 4 5 6 7
850 * +-+-+-+-+-+-+-+-+
851 * | MBZ |N|E|O|S|
852 * +-+-+-+-+-+-+-+-+
853 *
854 * where MBZ is ignored and NEOS are:
855 *
856 * S = 1 to request that "the server SHOULD perform the A RR (FQDN-to-
857 * address) DNS updates;
858 *
859 * O = 0, for a server-only response bit;
860 *
861 * E = 1 to indicate the domain name is in "canonical wire format,
862 * without compression (i.e., ns_name_pton2) .... This encoding SHOULD
863 * be used by clients ....";
864 *
865 * N = 0 to request that "the server SHALL perform DNS updates [of the
866 * PTR RR]." (1 would request SHALL NOT update).
867 *
868 * The format of the DHCPv6 Client FQDN option is shown below:
869 *
870 * 0 1 2 3
871 * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
872 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
873 * | OPTION_FQDN | option-len |
874 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
875 * | flags | |
876 * +-+-+-+-+-+-+-+-+ |
877 * . .
878 * . domain-name .
879 * . .
880 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
881 *
882 * option-code OPTION_CLIENT_FQDN (39)
883 *
884 * option-len 1 + length of domain name
885 *
886 * flags flag bits used between client and server to
887 * negotiate who performs which updates
888 *
889 * domain-name the partial or fully qualified domain name
890 * (with length option-len - 1)
891 *
892 * The DHCPv6 format of the Flags field is:
893 *
894 * 0 1 2 3 4 5 6 7
895 * +-+-+-+-+-+-+-+-+
896 * | MBZ |N|O|S|
897 * +-+-+-+-+-+-+-+-+
898 */
899
900 const uint8_t S_BITPOS = 7 - 7;
901 const uint8_t E_BITPOS = 7 - 5;
902 const size_t OPT_FQDN_METALEN = 3, OPT_V6_FQDN_METALEN = 1;
903 uint_t fqdncode;
904 u_char enc_fqdnbuf[MAXNAMELEN];
905 uint8_t fqdnopt[MAXNAMELEN + OPT_FQDN_METALEN];
906 size_t len, metalen;
907
908 if (dsmp->dsm_reqfqdn == NULL && dhcp_assemble_fqdn(dsmp) != 0)
909 return (-1);
910
911 /* encode the FQDN in canonical wire format */
912
913 if (ns_name_pton2(dsmp->dsm_reqfqdn, enc_fqdnbuf, sizeof (enc_fqdnbuf),
914 &len) < 0) {
915 dhcpmsg(MSG_WARNING, "dhcp_add_fqdn_opt: error encoding domain"
916 " name %s", dsmp->dsm_reqfqdn);
917 return (-1);
918 }
919
920 dhcpmsg(MSG_DEBUG, "dhcp_add_fqdn_opt: interface FQDN is %s"
921 " for %s", dsmp->dsm_reqfqdn, dsmp->dsm_name);
922
923 bzero(fqdnopt, sizeof (fqdnopt));
924 if (dsmp->dsm_isv6) {
925 fqdncode = DHCPV6_OPT_CLIENT_FQDN;
926 metalen = OPT_V6_FQDN_METALEN;
927 *fqdnopt = (uint8_t)(1 << S_BITPOS);
928 } else {
929 fqdncode = CD_CLIENTFQDN;
930 metalen = OPT_FQDN_METALEN;
931 *fqdnopt = (uint8_t)((1 << S_BITPOS) | (1 << E_BITPOS));
932 }
933 (void) memcpy(fqdnopt + metalen, enc_fqdnbuf, len);
934 (void) add_pkt_opt(dpkt, fqdncode, fqdnopt, metalen + len);
935
936 return (0);
937 }
938
939 /*
940 * dhcp_assemble_fqdn(): Set dsm_reqfqdn if REQUEST_FQDN is set and
941 * either a host name was sent in the IPC message (e.g., from
942 * ipadm(1M) -h,--reqhost) or the interface is primary and a
943 * nodename(4) is defined. If the host name is not already fully
944 * qualified per is_fqdn(), then a value from
945 * dhcp_get_defaultdomain() or from resolv.conf(4)--if defined--
946 * is used to construct an FQDN.
947 *
948 * If no FQDN can be determined, dsm_reqfqdn will be NULL.
949 *
950 * input: dhcp_smach_t *: pointer to interface DHCP state machine;
951 * output: 0 if dsm_reqfqdn was assigned; non-zero if otherwise;
952 */
953
954 static int
955 dhcp_assemble_fqdn(dhcp_smach_t *dsmp)
956 {
957 char fqdnbuf[MAXNAMELEN], nodename[MAXNAMELEN], *reqhost;
958 size_t pos, len;
959
960 if (dsmp->dsm_reqfqdn != NULL) {
961 free(dsmp->dsm_reqfqdn);
962 dsmp->dsm_reqfqdn = NULL;
963 }
964
965 if (!df_get_bool(dsmp->dsm_name, dsmp->dsm_isv6, DF_REQUEST_FQDN))
966 return (-1);
967
968 dhcpmsg(MSG_DEBUG, "dhcp_assemble_fqdn: DF_REQUEST_FQDN");
969
970 bzero(fqdnbuf, sizeof (fqdnbuf));
971
972 reqhost = dsmp->dsm_msg_reqhost;
973 if (ipadm_is_nil_hostname(reqhost) &&
974 (dsmp->dsm_dflags & DHCP_IF_PRIMARY) &&
975 dhcp_get_nodename(nodename, sizeof (nodename)) == 0) {
976 reqhost = nodename;
977 }
978
979 if (ipadm_is_nil_hostname(reqhost)) {
980 dhcpmsg(MSG_DEBUG,
981 "dhcp_assemble_fqdn: no interface reqhost for %s",
982 dsmp->dsm_name);
983 return (-1);
984 }
985
986 if ((pos = strlcpy(fqdnbuf, reqhost, sizeof (fqdnbuf))) >=
987 sizeof (fqdnbuf)) {
988 dhcpmsg(MSG_WARNING, "dhcp_assemble_fqdn: too long reqhost %s"
989 " for %s", reqhost, dsmp->dsm_name);
990 return (-1);
991 }
992
993 if (!is_fqdn(reqhost)) {
994 char dnamebuf[MAXHOSTNAMELEN];
995 size_t needdots;
996 int lasterrno;
997 char *domainname = NULL;
998
999 /*
1000 * determine FQDN domain name, if possible
1001 */
1002
1003 if (dhcp_get_defaultdomain(dnamebuf, sizeof (dnamebuf)) == 0 &&
1004 !ipadm_is_nil_hostname(dnamebuf)) {
1005 domainname = dnamebuf;
1006 } else {
1007 /*
1008 * fall back to resolv's "default domain (deprecated)"
1009 */
1010
1011 struct __res_state res_state;
1012 bzero(&res_state, sizeof (struct __res_state));
1013
1014 /* initialize resolver or warn */
1015 if ((lasterrno = res_ninit(&res_state)) != 0) {
1016 dhcpmsg(MSG_WARNING, "dhcp_assemble_fqdn: error %d"
1017 " initializing resolver", lasterrno);
1018 }
1019 else {
1020 if (*res_state.defdname != '\0') {
1021 (void) strlcpy(dnamebuf, res_state.defdname,
1022 sizeof (dnamebuf));
1023 domainname = dnamebuf;
1024 }
1025 res_ndestroy(&res_state);
1026 }
1027 }
1028
1029 if (ipadm_is_nil_hostname(domainname)) {
1030 dhcpmsg(MSG_DEBUG, "dhcp_assemble_fqdn: no domain name for %s",
1031 dsmp->dsm_name);
1032 return (-1);
1033 }
1034
1035 /*
1036 * Finish constructing FQDN. Account for space needed to hold a
1037 * separator '.' and a terminating '.'.
1038 */
1039 len = strlen(domainname);
1040 needdots = 1 + (domainname[len - 1] != '.');
1041
1042 if (pos + len + needdots >= sizeof (fqdnbuf)) {
1043 dhcpmsg(MSG_WARNING, "dhcp_assemble_fqdn: too long FQDN %s.%s"
1044 " for %s", fqdnbuf, domainname, dsmp->dsm_name);
1045 return (-1);
1046 }
1047
1048 /* add separator and then domain name */
1049 fqdnbuf[pos++] = '.';
1050 (void) strlcpy(fqdnbuf + pos, domainname, sizeof (fqdnbuf) - pos);
1051 pos += len;
1052
1053 /* ensure the final character is '.' */
1054 if (needdots > 1)
1055 fqdnbuf[pos++] = '.'; /* following is already zeroed */
1056 }
1057
1058 if (!ipadm_is_valid_hostname(fqdnbuf)) {
1059 dhcpmsg(MSG_WARNING, "dhcp_assemble_fqdn: invalid FQDN %s for %s",
1060 fqdnbuf, dsmp->dsm_name);
1061 return (-1);
1062 }
1063
1064 free(dsmp->dsm_reqfqdn);
1065 if ((dsmp->dsm_reqfqdn = strdup(fqdnbuf)) == NULL) {
1066 dhcpmsg(MSG_WARNING, "dhcp_assemble_fqdn: cannot allocate memory");
1067 return (-1);
1068 }
1069 return (0);
1070 }
1071
1072 /*
1073 * is_fqdn() : Determine if the `hostname' can be considered as a Fully
1074 * Qualified Domain Name by being "rooted" (i.e., ending in '.')
1075 * or by containing at least three DNS labels (e.g.,
1076 * srv.example.com).
1077 *
1078 * input: const char *: the hostname to inspect;
1079 * output: boolean_t: B_TRUE if `hostname' is not NULL satisfies the
1080 * criteria above; otherwise, B_FALSE;
1081 */
1082
1083 boolean_t
1084 is_fqdn(const char *hostname)
1085 {
1086 const char *c;
1087 size_t i;
1088
1089 if (hostname == NULL)
1090 return (B_FALSE);
1091
1092 i = strlen(hostname);
1093 if (i > 0 && hostname[i - 1] == '.')
1094 return (B_TRUE);
1095
1096 c = hostname;
1097 i = 0;
1098 while ((c = strchr(c, '.')) != NULL) {
1099 ++i;
1100 ++c;
1101 }
1102
1103 /* at least two separators is inferred to be fully-qualified */
1104 return (i >= 2);
1105 }
|