Add DHPCv6 FQDN support (RFC4704)

Improve hostname support in general, so we are aware of hostname changes.
When setting the hostname, prefer the FQDN name or append a domain.
This commit is contained in:
Roy Marples 2013-06-05 16:23:24 +00:00
parent be2a8dac4e
commit cc3c3560f6
12 changed files with 157 additions and 68 deletions

View File

@ -60,6 +60,7 @@
# define _PATH_DEVNULL "/dev/null"
#endif
static char hostname_buffer[HOSTNAME_MAX_LEN];
int clock_monotonic;
static char *lbuf;
static size_t lbuf_len;
@ -134,6 +135,19 @@ set_nonblock(int fd)
return 0;
}
const char *
get_hostname(void)
{
gethostname(hostname_buffer, sizeof(hostname_buffer));
if (strcmp(hostname_buffer, "(none)") == 0 ||
strcmp(hostname_buffer, "localhost") == 0 ||
strncmp(hostname_buffer, "localhost.", strlen("localhost.")) == 0 ||
hostname_buffer[0] == '.')
return NULL;
return hostname_buffer;
}
/* Handy function to get the time.
* We only care about time advancements, not the actual time itself
* Which is why we use CLOCK_MONOTONIC, but it is not available on all

View File

@ -34,6 +34,10 @@
#include "config.h"
#include "defs.h"
#ifndef HOSTNAME_MAX_LEN
#define HOSTNAME_MAX_LEN 250 /* 255 - 3 (FQDN) - 2 (DNS enc) */
#endif
#define UNCONST(a) ((void *)(unsigned long)(const void *)(a))
#define timeval_to_double(tv) ((tv)->tv_sec * 1.0 + (tv)->tv_usec * 1.0e-6)
@ -96,6 +100,7 @@
int set_cloexec(int);
int set_nonblock(int);
char *get_line(FILE * __restrict);
const char *get_hostname(void);
extern int clock_monotonic;
int get_monotonic(struct timeval *);
ssize_t setvar(char ***, const char *, const char *, const char *);

View File

@ -89,6 +89,50 @@ int make_option_mask(const struct dhcp_opt *dopts,
return 0;
}
size_t
encode_rfc1035(const char *src, uint8_t *dst)
{
uint8_t *p;
uint8_t *lp;
size_t len;
uint8_t has_dot;
if (src == NULL || *src == '\0')
return 0;
if (dst) {
p = dst;
lp = p++;
}
len = 1;
has_dot = 0;
for (; *src; src++) {
if (*src == '\0')
break;
if (*src == '.') {
/* Skip the trailing . */
if (src[1] == '\0')
break;
has_dot = 1;
if (dst) {
*lp = p - lp - 1;
if (*lp == '\0')
return len;
lp = p++;
}
} else if (dst)
*p++ = (uint8_t)*src;
len++;
}
if (dst) {
*lp = p - lp - 1;
if (has_dot)
*p++ = '\0';
}
if (has_dot)
len++;
return len;
}
/* Decode an RFC3397 DNS search order option into a space
* separated string. Returns length of string (including
* terminating zero) or zero on error. out may be NULL

View File

@ -67,6 +67,7 @@ struct dhcp_opt {
#define del_option_mask(var, val) (var[val >> 3] &= ~(1 << (val & 7)))
#define has_option_mask(var, val) (var[val >> 3] & (1 << (val & 7)))
int make_option_mask(const struct dhcp_opt *,uint8_t *, const char *, int);
size_t encode_rfc1035(const char *src, uint8_t *dst);
ssize_t decode_rfc3397(char *, ssize_t, int, const uint8_t *);
ssize_t print_string(char *, ssize_t, int, const uint8_t *);
ssize_t print_option(char *, ssize_t, int, int, const uint8_t *, const char *);

57
dhcp.c
View File

@ -801,33 +801,6 @@ get_option_routes(struct interface *ifp, const struct dhcp_message *dhcp)
return routes;
}
static size_t
encode_rfc1035(const char *src, uint8_t *dst)
{
uint8_t *p = dst;
uint8_t *lp = p++;
if (*src == '\0')
return 0;
for (; *src; src++) {
if (*src == '\0')
break;
if (*src == '.') {
/* Skip the trailing . */
if (src[1] == '\0')
break;
*lp = p - lp - 1;
if (*lp == '\0')
return p - dst;
lp = p++;
} else
*p++ = (uint8_t)*src;
}
*lp = p - lp - 1;
*p++ = '\0';
return p - dst;
}
#define PUTADDR(_type, _val) \
{ \
*p++ = _type; \
@ -877,6 +850,7 @@ make_message(struct dhcp_message **message,
const struct dhcp_state *state = D_CSTATE(iface);
const struct dhcp_lease *lease = &state->lease;
time_t up = uptime() - state->start_uptime;
const char *hostname;
dhcp = calloc(1, sizeof (*dhcp));
if (dhcp == NULL)
@ -1006,18 +980,22 @@ make_message(struct dhcp_message **message,
* upto the first dot (the short hostname) as otherwise
* confuses some DHCP servers when updating DNS.
* The FQDN option should be used if a FQDN is required. */
if (ifo->options & DHCPCD_HOSTNAME && ifo->hostname[0]) {
if (ifo->hostname[0] == '\0')
hostname = get_hostname();
else
hostname = ifo->hostname;
if (ifo->options & DHCPCD_HOSTNAME && hostname) {
*p++ = DHO_HOSTNAME;
hp = strchr(ifo->hostname, '.');
hp = strchr(hostname, '.');
if (hp)
len = hp - ifo->hostname;
len = hp - hostname;
else
len = strlen(ifo->hostname);
len = strlen(hostname);
*p++ = len;
memcpy(p, ifo->hostname, len);
memcpy(p, hostname, len);
p += len;
}
if (ifo->fqdn != FQDN_DISABLE && ifo->hostname[0]) {
if (ifo->fqdn != FQDN_DISABLE) {
/* IETF DHC-FQDN option (81), RFC4702 */
*p++ = DHO_FQDN;
lp = p;
@ -1032,12 +1010,17 @@ make_message(struct dhcp_message **message,
* N: 1 => Client requests Server to not
* update DNS
*/
*p++ = (ifo->fqdn & 0x09) | 0x04;
if (hostname)
*p++ = (ifo->fqdn & 0x09) | 0x04;
else
*p++ = (FQDN_NONE & 0x09) | 0x04;
*p++ = 0; /* from server for PTR RR */
*p++ = 0; /* from server for A RR if S=1 */
ul = encode_rfc1035(ifo->hostname, p);
*lp += ul;
p += ul;
if (hostname) {
ul = encode_rfc1035(hostname, p);
*lp += ul;
p += ul;
}
}
/* vendor is already encoded correctly, so just add it */

37
dhcp6.c
View File

@ -126,6 +126,7 @@ const struct dhcp_opt dhcp6_opts[] = {
{ D6_OPTION_BCMS_SERVER_A, IPV6A, "bcms_server_a" },
{ D6_OPTION_POSIX_TIMEZONE, STRING, "posix_timezone" },
{ D6_OPTION_TZDB_TIMEZONE, STRING, "tzdb_timezone" },
{ D6_OPTION_FQDN, RFC3397, "fqdn" },
{ 0, 0, NULL }
};
@ -361,6 +362,7 @@ dhcp6_makemessage(struct interface *ifp)
uint8_t IA, *p;
uint32_t u32;
const struct ipv6_addr *ap;
const char *hostname;
state = D6_STATE(ifp);
if (state->send) {
@ -379,8 +381,16 @@ dhcp6_makemessage(struct interface *ifp)
len += sizeof(*u16);
}
if (len == 0)
len = sizeof(*u16) * 3;
len = sizeof(*u16) * 4;
len += sizeof(*o);
if (ifo->fqdn != FQDN_DISABLE) {
if (ifo->hostname[0] == '\0')
hostname = get_hostname();
else
hostname = ifo->hostname;
len += sizeof(*o) + 1 + encode_rfc1035(hostname, NULL);
}
}
len += sizeof(*state->send);
@ -568,6 +578,26 @@ dhcp6_makemessage(struct interface *ifp)
}
if (state->send->type != DHCP6_RELEASE) {
if (ifo->fqdn != FQDN_DISABLE) {
o = D6_NEXT_OPTION(o);
o->code = htons(D6_OPTION_FQDN);
p = D6_OPTION_DATA(o);
switch (ifo->fqdn) {
case FQDN_BOTH:
*p = 0x01;
break;
case FQDN_PTR:
*p = 0x00;
break;
default:
*p = 0x04;
break;
}
o->len = encode_rfc1035(hostname, p + 1);
if (o->len == 0)
*p = 0x04;
o->len = htons(++o->len);
}
o = D6_NEXT_OPTION(o);
o->code = htons(D6_OPTION_ORO);
o->len = 0;
@ -581,10 +611,11 @@ dhcp6_makemessage(struct interface *ifp)
}
}
if (o->len == 0) {
*u16++ = htons(D6_OPTION_UNICAST);
*u16++ = htons(D6_OPTION_DNS_SERVERS);
*u16++ = htons(D6_OPTION_DOMAIN_LIST);
*u16++ = htons(D6_OPTION_UNICAST);
o->len = sizeof(*u16) * 3;
*u16++ = htons(D6_OPTION_FQDN);
o->len = sizeof(*u16) * 4;
}
o->len = htons(o->len);
}

View File

@ -79,6 +79,7 @@
#define D6_OPTION_INFO_REFRESH_TIME 32
#define D6_OPTION_BCMS_SERVER_D 33
#define D6_OPTION_BCMS_SERVER_A 34
#define D6_OPTION_FQDN 39
#define D6_OPTION_POSIX_TIMEZONE 41
#define D6_OPTION_TZDB_TIMEZONE 42

View File

@ -65,15 +65,27 @@ try_hostname()
set_hostname()
{
if need_hostname; then
if [ -n "$new_host_name" ]; then
need_hostname || return
if [ -n "$new_fqdn_name" ]; then
try_hostname "$new_fqdn_name"
elif [ -n "$new_host_name" ]; then
if [ -n "$new_domain_name" ]; then
try_hostname "$new_host_name.$new_domain_name"
else
try_hostname "$new_host_name"
elif [ -n "$new_fqdn_name" ]; then
try_hostname "$new_fqdn_name"
fi
fi
}
# For ease of use, map DHCP6 names onto our DHCP4 names
case "$reason" in
BOUND6|RENEW6|REBIND6|REBOOT6|INFORM6)
new_fqdn_name="$new_dhcp6_fqdn"
;;
esac
if $if_up; then
set_hostname
fi

View File

@ -22,7 +22,7 @@
.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
.\" SUCH DAMAGE.
.\"
.Dd May 3, 2013
.Dd June 5, 2013
.Dt DHCPCD 8
.Os
.Sh NAME
@ -605,7 +605,7 @@ running on the
.Sh STANDARDS
RFC 951, RFC 1534, RFC 2131, RFC 2132, RFC 2855, RFC 3004, RFC 3315,RFC 3361,
RFC 3633, RFC 3396, RFC 3397, RFC 3442, RFC 3927, RFC 4361, RFC 4390, RFC 4702,
RFC 4861, RFC 4833, RFC 5227, RFC 5969, RFC 6106.
RFC 4704, RFC 4861, RFC 4833, RFC 5227, RFC 5969, RFC 6106.
.Sh AUTHORS
.An Roy Marples Aq roy@marples.name
.Sh BUGS

View File

@ -180,9 +180,11 @@ IPv6RS should be disabled globally when requesting a Prefix Delegation like so:
Only configure IPv4.
.It Ic ipv6only
Only confgiure IPv6.
.It Ic fqdn Op none | ptr | both
none disables FQDN encoding, ptr just asks the DHCP server to update the PTR
.It Ic fqdn Op disable | ptr | both
ptr just asks the DHCP server to update the PTR
record of the host in DNS whereas both also updates the A record.
disable will disable the FQDN option.
The default is both.
.Nm dhcpcd
itself never does any DNS updates.
.Nm dhcpcd

View File

@ -459,20 +459,20 @@ parse_option(struct if_options *ifo, int opt, const char *arg)
add_environ(ifo, arg, 1);
break;
case 'h':
if (arg) {
s = parse_string(ifo->hostname,
HOSTNAME_MAX_LEN, arg);
if (s == -1) {
syslog(LOG_ERR, "hostname: %m");
return -1;
}
if (s != 0 && ifo->hostname[0] == '.') {
syslog(LOG_ERR,
"hostname cannot begin with .");
return -1;
}
ifo->hostname[s] = '\0';
if (!arg) {
ifo->options |= DHCPCD_HOSTNAME;
break;
}
s = parse_string(ifo->hostname, HOSTNAME_MAX_LEN, arg);
if (s == -1) {
syslog(LOG_ERR, "hostname: %m");
return -1;
}
if (s != 0 && ifo->hostname[0] == '.') {
syslog(LOG_ERR, "hostname cannot begin with .");
return -1;
}
ifo->hostname[s] = '\0';
if (ifo->hostname[0] == '\0')
ifo->options &= ~DHCPCD_HOSTNAME;
else
@ -1131,12 +1131,6 @@ read_config(const char *file,
ifo->reboot = DEFAULT_REBOOT;
ifo->metric = -1;
strlcpy(ifo->script, SCRIPT, sizeof(ifo->script));
gethostname(ifo->hostname, HOSTNAME_MAX_LEN);
/* Ensure that the hostname is NULL terminated */
ifo->hostname[HOSTNAME_MAX_LEN] = '\0';
if (strcmp(ifo->hostname, "(none)") == 0 ||
strcmp(ifo->hostname, "localhost") == 0)
ifo->hostname[0] = '\0';
ifo->vendorclassid[0] = strlen(vendor);
memcpy(ifo->vendorclassid + 1, vendor, ifo->vendorclassid[0]);

View File

@ -45,7 +45,9 @@
#define DEFAULT_TIMEOUT 30
#define DEFAULT_REBOOT 5
#ifndef HOSTNAME_MAX_LEN
#define HOSTNAME_MAX_LEN 250 /* 255 - 3 (FQDN) - 2 (DNS enc) */
#endif
#define VENDORCLASSID_MAX_LEN 255
#define CLIENTID_MAX_LEN 48
#define USERCLASS_MAX_LEN 255