mirror of
https://github.com/systemd/systemd.git
synced 2024-10-28 03:25:31 +03:00
Merge pull request #1931 from bengal/dhcp-fqdn-v2
libsystemd-network: add support for "Client FQDN" DHCP option (v2)
This commit is contained in:
commit
dc9715d419
@ -72,7 +72,7 @@ static bool hostname_valid_char(char c) {
|
||||
* allow_trailing_dot is true and at least two components are present
|
||||
* in the name. Note that due to the restricted charset and length
|
||||
* this call is substantially more conservative than
|
||||
* dns_domain_is_valid().
|
||||
* dns_name_is_valid().
|
||||
*/
|
||||
bool hostname_is_valid(const char *s, bool allow_trailing_dot) {
|
||||
unsigned n_dots = 0;
|
||||
|
@ -137,6 +137,7 @@ enum {
|
||||
DHCP_OPTION_REBINDING_T2_TIME = 59,
|
||||
DHCP_OPTION_VENDOR_CLASS_IDENTIFIER = 60,
|
||||
DHCP_OPTION_CLIENT_IDENTIFIER = 61,
|
||||
DHCP_OPTION_FQDN = 81,
|
||||
DHCP_OPTION_NEW_POSIX_TIMEZONE = 100,
|
||||
DHCP_OPTION_NEW_TZDB_TIMEZONE = 101,
|
||||
DHCP_OPTION_CLASSLESS_STATIC_ROUTE = 121,
|
||||
@ -144,3 +145,12 @@ enum {
|
||||
DHCP_OPTION_PRIVATE_LAST = 254,
|
||||
DHCP_OPTION_END = 255,
|
||||
};
|
||||
|
||||
#define DHCP_MAX_FQDN_LENGTH 255
|
||||
|
||||
enum {
|
||||
DHCP_FQDN_FLAG_S = (1 << 0),
|
||||
DHCP_FQDN_FLAG_O = (1 << 1),
|
||||
DHCP_FQDN_FLAG_E = (1 << 2),
|
||||
DHCP_FQDN_FLAG_N = (1 << 3),
|
||||
};
|
||||
|
@ -34,6 +34,8 @@
|
||||
#include "dhcp-internal.h"
|
||||
#include "dhcp-lease-internal.h"
|
||||
#include "dhcp-protocol.h"
|
||||
#include "dns-domain.h"
|
||||
#include "hostname-util.h"
|
||||
#include "random-util.h"
|
||||
#include "string-util.h"
|
||||
#include "util.h"
|
||||
@ -298,6 +300,9 @@ int sd_dhcp_client_set_hostname(sd_dhcp_client *client,
|
||||
|
||||
assert_return(client, -EINVAL);
|
||||
|
||||
if (!hostname_is_valid(hostname, false) && !dns_name_is_valid(hostname))
|
||||
return -EINVAL;
|
||||
|
||||
if (streq_ptr(client->hostname, hostname))
|
||||
return 0;
|
||||
|
||||
@ -539,6 +544,24 @@ static int client_message_init(sd_dhcp_client *client, DHCPPacket **ret,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int client_append_fqdn_option(DHCPMessage *message, size_t optlen, size_t *optoffset,
|
||||
const char *fqdn) {
|
||||
uint8_t buffer[3 + DHCP_MAX_FQDN_LENGTH];
|
||||
int r;
|
||||
|
||||
buffer[0] = DHCP_FQDN_FLAG_S | /* Request server to perform A RR DNS updates */
|
||||
DHCP_FQDN_FLAG_E; /* Canonical wire format */
|
||||
buffer[1] = 0; /* RCODE1 (deprecated) */
|
||||
buffer[2] = 0; /* RCODE2 (deprecated) */
|
||||
|
||||
r = dns_name_to_wire_format(fqdn, buffer + 3, sizeof(buffer) - 3);
|
||||
if (r > 0)
|
||||
r = dhcp_option_append(message, optlen, optoffset, 0,
|
||||
DHCP_OPTION_FQDN, 3 + r, buffer);
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
static int dhcp_client_send_raw(sd_dhcp_client *client, DHCPPacket *packet,
|
||||
size_t len) {
|
||||
dhcp_packet_append_ip_headers(packet, INADDR_ANY, DHCP_PORT_CLIENT,
|
||||
@ -576,13 +599,21 @@ static int client_send_discover(sd_dhcp_client *client) {
|
||||
return r;
|
||||
}
|
||||
|
||||
/* it is unclear from RFC 2131 if client should send hostname in
|
||||
DHCPDISCOVER but dhclient does and so we do as well
|
||||
*/
|
||||
if (client->hostname) {
|
||||
r = dhcp_option_append(&discover->dhcp, optlen, &optoffset, 0,
|
||||
DHCP_OPTION_HOST_NAME,
|
||||
strlen(client->hostname), client->hostname);
|
||||
/* According to RFC 4702 "clients that send the Client FQDN option in
|
||||
their messages MUST NOT also send the Host Name option". Just send
|
||||
one of the two depending on the hostname type.
|
||||
*/
|
||||
if (dns_name_single_label(client->hostname)) {
|
||||
/* it is unclear from RFC 2131 if client should send hostname in
|
||||
DHCPDISCOVER but dhclient does and so we do as well
|
||||
*/
|
||||
r = dhcp_option_append(&discover->dhcp, optlen, &optoffset, 0,
|
||||
DHCP_OPTION_HOST_NAME,
|
||||
strlen(client->hostname), client->hostname);
|
||||
} else
|
||||
r = client_append_fqdn_option(&discover->dhcp, optlen, &optoffset,
|
||||
client->hostname);
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
@ -688,9 +719,13 @@ static int client_send_request(sd_dhcp_client *client) {
|
||||
}
|
||||
|
||||
if (client->hostname) {
|
||||
r = dhcp_option_append(&request->dhcp, optlen, &optoffset, 0,
|
||||
DHCP_OPTION_HOST_NAME,
|
||||
strlen(client->hostname), client->hostname);
|
||||
if (dns_name_single_label(client->hostname))
|
||||
r = dhcp_option_append(&request->dhcp, optlen, &optoffset, 0,
|
||||
DHCP_OPTION_HOST_NAME,
|
||||
strlen(client->hostname), client->hostname);
|
||||
else
|
||||
r = client_append_fqdn_option(&request->dhcp, optlen, &optoffset,
|
||||
client->hostname);
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
|
@ -715,3 +715,37 @@ int dns_name_single_label(const char *name) {
|
||||
|
||||
return r == 0 && *name == 0;
|
||||
}
|
||||
|
||||
/* Encode a domain name according to RFC 1035 Section 3.1 */
|
||||
int dns_name_to_wire_format(const char *domain, uint8_t *buffer, size_t len) {
|
||||
uint8_t *label_length;
|
||||
uint8_t *out;
|
||||
int r;
|
||||
|
||||
assert_return(buffer, -EINVAL);
|
||||
assert_return(domain, -EINVAL);
|
||||
assert_return(domain[0], -EINVAL);
|
||||
|
||||
out = buffer;
|
||||
|
||||
do {
|
||||
/* reserve a byte for label length */
|
||||
if (len == 0)
|
||||
return -ENOBUFS;
|
||||
len--;
|
||||
label_length = out;
|
||||
out++;
|
||||
|
||||
/* convert and copy a single label */
|
||||
r = dns_label_unescape(&domain, (char *) out, len);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
/* fill label length, move forward */
|
||||
*label_length = r;
|
||||
out += r;
|
||||
len -= r;
|
||||
} while (r != 0);
|
||||
|
||||
return out - buffer;
|
||||
}
|
||||
|
@ -67,3 +67,5 @@ int dns_name_address(const char *p, int *family, union in_addr_union *a);
|
||||
|
||||
int dns_name_root(const char *name);
|
||||
int dns_name_single_label(const char *name);
|
||||
|
||||
int dns_name_to_wire_format(const char *domain, uint8_t *buffer, size_t len);
|
||||
|
@ -52,6 +52,36 @@ static void test_dns_label_unescape(void) {
|
||||
test_dns_label_unescape_one("foobar.", "foobar", 20, 6);
|
||||
}
|
||||
|
||||
static void test_dns_name_to_wire_format_one(const char *what, const char *expect, size_t buffer_sz, int ret) {
|
||||
uint8_t buffer[buffer_sz];
|
||||
int r;
|
||||
|
||||
r = dns_name_to_wire_format(what, buffer, buffer_sz);
|
||||
assert_se(r == ret);
|
||||
|
||||
if (r < 0)
|
||||
return;
|
||||
|
||||
assert_se(!memcmp(buffer, expect, r));
|
||||
}
|
||||
|
||||
static void test_dns_name_to_wire_format(void) {
|
||||
const char out1[] = { 3, 'f', 'o', 'o', 0 };
|
||||
const char out2[] = { 5, 'h', 'a', 'l', 'l', 'o', 3, 'f', 'o', 'o', 3, 'b', 'a', 'r', 0 };
|
||||
const char out3[] = { 4, ' ', 'f', 'o', 'o', 3, 'b', 'a', 'r', 0 };
|
||||
|
||||
test_dns_name_to_wire_format_one("", NULL, 0, -EINVAL);
|
||||
|
||||
test_dns_name_to_wire_format_one("foo", out1, sizeof(out1), sizeof(out1));
|
||||
test_dns_name_to_wire_format_one("foo", out1, sizeof(out1) + 1, sizeof(out1));
|
||||
test_dns_name_to_wire_format_one("foo", out1, sizeof(out1) - 1, -ENOBUFS);
|
||||
|
||||
test_dns_name_to_wire_format_one("hallo.foo.bar", out2, sizeof(out2), sizeof(out2));
|
||||
test_dns_name_to_wire_format_one("hallo.foo..bar", NULL, 32, -EINVAL);
|
||||
|
||||
test_dns_name_to_wire_format_one("\\032foo.bar", out3, sizeof(out3), sizeof(out3));
|
||||
}
|
||||
|
||||
static void test_dns_label_unescape_suffix_one(const char *what, const char *expect1, const char *expect2, size_t buffer_sz, int ret1, int ret2) {
|
||||
char buffer[buffer_sz];
|
||||
const char *label;
|
||||
@ -300,6 +330,7 @@ int main(int argc, char *argv[]) {
|
||||
test_dns_name_reverse();
|
||||
test_dns_name_concat();
|
||||
test_dns_name_is_valid();
|
||||
test_dns_name_to_wire_format();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user