odhcpd: detect broken hostnames
authorHans Dedecker <dedeckeh@gmail.com>
Wed, 22 Aug 2018 09:45:53 +0000 (11:45 +0200)
committerHans Dedecker <hans.dedecker@technicolor.com>
Wed, 5 Sep 2018 08:35:42 +0000 (10:35 +0200)
Check hostnames contain valid characters as defined in RFC 952 and RFC 1123.
Invalid hostnames in uci configured host entries will result into a refusal
to create the static lease.
In case a client received hostname contains an invalid character no
<hostname> <IP address> entry will be added to the lease file.
In such case the leaseinfo description in the lease file will still contain
the hostname but preceded by the string broken\x20

Signed-off-by: Hans Dedecker <dedeckeh@gmail.com>
src/config.c
src/dhcpv4.c
src/dhcpv6-ia.c
src/odhcpd.c
src/odhcpd.h
src/ubus.c

index 6556eac0c376e531d474296ef3fdde093406c1fc..b36afb1285556cbe3d7b51bd4eab78e997473c96 100644 (file)
@@ -344,8 +344,11 @@ static int set_lease(struct uci_section *s)
        if (!lease)
                goto err;
 
-       if (hostlen > 1)
+       if (hostlen > 1) {
                memcpy(lease->hostname, blobmsg_get_string(c), hostlen);
+               if (!odhcpd_valid_hostname(lease->hostname))
+                       goto err;
+       }
 
        if ((c = tb[LEASE_ATTR_IP]))
                if (inet_pton(AF_INET, blobmsg_get_string(c), &lease->ipaddr) < 0)
index 3386abbeaa721e66de4154ae931c06ac8556d564..2cc627867b4aa62c5b27cd7a94fb93953d2bddce 100644 (file)
@@ -1095,6 +1095,11 @@ static struct dhcpv4_assignment* dhcpv4_lease(struct interface *iface,
                                        if (a->hostname) {
                                                memcpy(a->hostname, hostname, hostname_len);
                                                a->hostname[hostname_len] = 0;
+
+                                               if (odhcpd_valid_hostname(a->hostname))
+                                                       a->flags &= ~OAF_BROKEN_HOSTNAME;
+                                               else
+                                                       a->flags |= OAF_BROKEN_HOSTNAME;
                                        }
                                }
 
index 4ee6dd2639156bdc6310cb37efd5902913b24cc3..fb1c22860fe571e4c8e00f3c7d3810799d4af51e 100644 (file)
@@ -331,7 +331,8 @@ void dhcpv6_write_ia_addr(struct in6_addr *addr, int prefix, _unused uint32_t pr
 
        inet_ntop(AF_INET6, addr, ipbuf, sizeof(ipbuf) - 1);
 
-       if (ctxt->c->length == 128 && ctxt->c->hostname) {
+       if (ctxt->c->length == 128 && ctxt->c->hostname &&
+           !(ctxt->c->flags & OAF_BROKEN_HOSTNAME)) {
                fputs(ipbuf, ctxt->fp);
 
                char b[256];
@@ -394,8 +395,9 @@ void dhcpv6_write_statefile(void)
                                        odhcpd_hexlify(duidbuf, ctxt.c->clid_data, ctxt.c->clid_len);
 
                                        /* iface DUID iaid hostname lifetime assigned length [addrs...] */
-                                       ctxt.buf_idx = snprintf(ctxt.buf, ctxt.buf_len, "# %s %s %x %s %ld %x %u ",
+                                       ctxt.buf_idx = snprintf(ctxt.buf, ctxt.buf_len, "# %s %s %x %s%s %ld %x %u ",
                                                                ctxt.iface->ifname, duidbuf, ntohl(ctxt.c->iaid),
+                                                               (ctxt.c->flags & OAF_BROKEN_HOSTNAME) ? "broken\\x20" : "",
                                                                (ctxt.c->hostname ? ctxt.c->hostname : "-"),
                                                                (ctxt.c->valid_until > now ?
                                                                        (ctxt.c->valid_until - now + wall_time) :
@@ -423,8 +425,9 @@ void dhcpv6_write_statefile(void)
                                        odhcpd_hexlify(duidbuf, c->hwaddr, sizeof(c->hwaddr));
 
                                        /* iface DUID iaid hostname lifetime assigned length [addrs...] */
-                                       ctxt.buf_idx = snprintf(ctxt.buf, ctxt.buf_len, "# %s %s ipv4 %s %ld %x 32 ",
+                                       ctxt.buf_idx = snprintf(ctxt.buf, ctxt.buf_len, "# %s %s ipv4 %s%s %ld %x 32 ",
                                                                ctxt.iface->ifname, duidbuf,
+                                                               (c->flags & OAF_BROKEN_HOSTNAME) ? "broken\\x20" : "",
                                                                (c->hostname ? c->hostname : "-"),
                                                                (c->valid_until > now ?
                                                                        (c->valid_until - now + wall_time) :
@@ -434,7 +437,7 @@ void dhcpv6_write_statefile(void)
                                        struct in_addr addr = {.s_addr = c->addr};
                                        inet_ntop(AF_INET, &addr, ipbuf, sizeof(ipbuf) - 1);
 
-                                       if (c->hostname) {
+                                       if (c->hostname && !(c->flags & OAF_BROKEN_HOSTNAME)) {
                                                fputs(ipbuf, ctxt.fp);
 
                                                char b[256];
@@ -1343,6 +1346,11 @@ ssize_t dhcpv6_handle_ia(uint8_t *buf, size_t buflen, struct interface *iface,
                                        if (a->hostname) {
                                                memcpy(a->hostname, hostname, hostname_len);
                                                a->hostname[hostname_len] = 0;
+
+                                               if (odhcpd_valid_hostname(a->hostname))
+                                                       a->flags &= ~OAF_BROKEN_HOSTNAME;
+                                               else
+                                                       a->flags |= OAF_BROKEN_HOSTNAME;
                                        }
                                }
                                a->accept_reconf = accept_reconf;
index fe4dd732731bcc6405b5839fce77e59b87df5866..c1d51e7c717f5c1a1d655098add19b949915f667 100644 (file)
@@ -581,3 +581,38 @@ bool odhcpd_bitlen2netmask(bool inet6, unsigned int bits, void *mask)
 
        return true;
 }
+
+bool odhcpd_valid_hostname(const char *name)
+{
+#define MAX_LABEL      63
+       const char *c, *label, *label_end;
+       int label_sz = 0;
+
+       for (c = name, label_sz = 0, label = name, label_end = name + strcspn(name, ".") - 1;
+                       *c && label_sz <= MAX_LABEL; c++) {
+               if ((*c >= '0' && *c <= '9') ||
+                   (*c >= 'A' && *c <= 'Z') ||
+                   (*c >= 'a' && *c <= 'z')) {
+                       label_sz++;
+                       continue;
+               }
+
+               if ((*c == '_' || *c == '-') && c != label && c != label_end) {
+                       label_sz++;
+                       continue;
+               }
+
+               if (*c == '.') {
+                       if (*(c + 1)) {
+                               label = c + 1;
+                               label_end = label + strcspn(label, ".") - 1;
+                               label_sz = 0;
+                       }
+                       continue;
+               }
+
+               return false;
+       }
+
+       return (label_sz && label_sz <= MAX_LABEL ? true : false);
+}
index 91fdcbfe27ead984cebd2c69e3dbd44a7ec2a06c..ef94cfccf36a142e682f6ed21acd41ed30bd35a9 100644 (file)
@@ -129,9 +129,10 @@ enum odhcpd_mode {
 
 
 enum odhcpd_assignment_flags {
-       OAF_TENTATIVE   = (1 << 0),
-       OAF_BOUND       = (1 << 1),
-       OAF_STATIC      = (1 << 2),
+       OAF_TENTATIVE           = (1 << 0),
+       OAF_BOUND               = (1 << 1),
+       OAF_STATIC              = (1 << 2),
+       OAF_BROKEN_HOSTNAME     = (1 << 3),
 };
 
 struct config {
@@ -288,6 +289,7 @@ void odhcpd_bmemcpy(void *av, const void *bv, size_t bits);
 
 int odhcpd_netmask2bitlen(bool v6, void *mask);
 bool odhcpd_bitlen2netmask(bool v6, unsigned int bits, void *mask);
+bool odhcpd_valid_hostname(const char *name);
 
 int config_parse_interface(void *data, size_t len, const char *iname, bool overwrite);
 
index 5be6bbef41f60e44ab75ec9bfaf39e1637e43978..1aec590289b7b914924c613e55f8fc20e4ea5da1 100644 (file)
@@ -53,6 +53,9 @@ static int handle_dhcpv4_leases(struct ubus_context *ctx, _unused struct ubus_ob
 
                        if (c->flags & OAF_STATIC)
                                blobmsg_add_string(&b, NULL, "static");
+
+                       if (c->flags & OAF_BROKEN_HOSTNAME)
+                               blobmsg_add_string(&b, NULL, "broken-hostname");
                        blobmsg_close_array(&b, m);
 
                        buf = blobmsg_alloc_string_buffer(&b, "address", INET_ADDRSTRLEN);