This implements an option to change the host name sent by dhcpleased.

Being able to hide the real host name is useful for preserving some
shred of anonymity when connecting to sketchy open networks. That's
currently the only reason why I still use dhclient over dhcpleased.
Perhaps it has other uses for circumventing draconic DHCP servers like
the client id options, I don't know if there are such servers out there.

Reloading configuration after changing or (un)suppressing the host name
works as expected.

While here I slightly increased the gethostname buffer size to prevent
truncation, accounting for the DHCP option's prefix bytes and NUL
termination. dhclient doesn't truncate there.

Index: dhcpleased.c
===================================================================
RCS file: /cvs/src/sbin/dhcpleased/dhcpleased.c,v
retrieving revision 1.21
diff -u -p -r1.21 dhcpleased.c
--- dhcpleased.c        16 Sep 2021 13:36:52 -0000      1.21
+++ dhcpleased.c        17 Sep 2021 19:21:27 -0000
@@ -732,6 +732,10 @@ main_imsg_send_config(struct dhcpleased_
                    iface_conf->c_id, iface_conf->c_id_len);
                main_imsg_compose_engine(IMSG_RECONF_C_ID, -1,
                    iface_conf->c_id, iface_conf->c_id_len);
+               main_imsg_compose_frontend(IMSG_RECONF_H_NAME, -1,
+                   iface_conf->h_name, iface_conf->h_name_len);
+               main_imsg_compose_engine(IMSG_RECONF_H_NAME, -1,
+                   iface_conf->h_name, iface_conf->h_name_len);
        }
 
        /* Config is now complete. */
@@ -1227,6 +1231,7 @@ merge_config(struct dhcpleased_conf *con
                SIMPLEQ_REMOVE_HEAD(&conf->iface_list, entry);
                free(iface_conf->vc_id);
                free(iface_conf->c_id);
+               free(iface_conf->h_name);
                free(iface_conf);
        }
 
Index: dhcpleased.conf.5
===================================================================
RCS file: /cvs/src/sbin/dhcpleased/dhcpleased.conf.5,v
retrieving revision 1.5
diff -u -p -r1.5 dhcpleased.conf.5
--- dhcpleased.conf.5   12 Aug 2021 12:41:08 -0000      1.5
+++ dhcpleased.conf.5   17 Sep 2021 19:21:27 -0000
@@ -56,18 +56,22 @@ A list of interfaces to overwrite defaul
 .Pp
 .Ic interface
 options are as follows:
-.Bl -tag -width Ds
+.Pp
+.Bl -tag -width Ds -compact
 .It Ic ignore dns
 Ignore nameservers from leases on this interface.
 The default is to not ignore nameservers.
+.Pp
 .It Ic ignore routes
 Ignore routes from leases on this interface.
 The default is to not ignore routes.
+.Pp
 .It Ic ignore Ar server-ip
 Ignore leases from
 .Ar server-ip .
 This option can be listed multiple times.
 The default is to not ignore servers.
+.Pp
 .It Ic send client id Ar client-id
 Send the dhcp client identifier option with a value of
 .Ar client-id .
@@ -84,6 +88,15 @@ Otherwise the string
 .Ar client-id
 is sent verbatim with type zero.
 The default is to send the interface's MAC address as client identifier.
+.Pp
+.It Ic send host name Ar host-name
+.It Ic send no host name
+Send the dhcp client host name option with a value of
+.Ar host-name ,
+or suppress sending the option with
+.Ic send no host name .
+The default is to send the name of the current host.
+.Pp
 .It Ic send vendor class id Ar vendor-class-id
 Send the dhcp vendor class identifier option with a value of
 .Ar vendor-class-id .
Index: dhcpleased.h
===================================================================
RCS file: /cvs/src/sbin/dhcpleased/dhcpleased.h,v
retrieving revision 1.11
diff -u -p -r1.11 dhcpleased.h
--- dhcpleased.h        12 Aug 2021 12:41:08 -0000      1.11
+++ dhcpleased.h        17 Sep 2021 19:21:27 -0000
@@ -155,6 +155,9 @@
 #define        IGN_ROUTES      1
 #define        IGN_DNS         2
 
+/* Suppress sending parts of DHCP lease */
+#define SUP_HOST_NAME  1
+
 #define        MAX_SERVERS     16      /* max servers that can be ignored per 
if */
 
 #define        IMSG_DATA_SIZE(imsg)    ((imsg).hdr.len - IMSG_HEADER_SIZE)
@@ -203,6 +206,7 @@ enum imsg_type {
        IMSG_RECONF_IFACE,
        IMSG_RECONF_VC_ID,
        IMSG_RECONF_C_ID,
+       IMSG_RECONF_H_NAME,
        IMSG_RECONF_END,
 #endif /* SMALL */
        IMSG_SEND_DISCOVER,
@@ -253,6 +257,9 @@ struct iface_conf {
        int                              vc_id_len;
        uint8_t                         *c_id;
        int                              c_id_len;
+       uint8_t                         *h_name;
+       int                              h_name_len;
+       int                              suppress;
        int                              ignore;
        struct in_addr                   ignore_servers[MAX_SERVERS];
        int                              ignore_servers_len;
Index: engine.c
===================================================================
RCS file: /cvs/src/sbin/dhcpleased/engine.c,v
retrieving revision 1.27
diff -u -p -r1.27 engine.c
--- engine.c    15 Sep 2021 15:18:23 -0000      1.27
+++ engine.c    17 Sep 2021 19:21:27 -0000
@@ -478,6 +478,8 @@ engine_dispatch_main(int fd, short event
                        iface_conf->vc_id_len = 0;
                        iface_conf->c_id = NULL;
                        iface_conf->c_id_len = 0;
+                       iface_conf->h_name = NULL;
+                       iface_conf->h_name_len = 0;
                        SIMPLEQ_INSERT_TAIL(&nconf->iface_list,
                            iface_conf, entry);
                        break;
@@ -508,6 +510,20 @@ engine_dispatch_main(int fd, short event
                        memcpy(iface_conf->c_id, imsg.data,
                            IMSG_DATA_SIZE(imsg));
                        iface_conf->c_id_len = IMSG_DATA_SIZE(imsg);
+                       break;
+               case IMSG_RECONF_H_NAME:
+                       if (iface_conf == NULL)
+                               fatal("IMSG_RECONF_H_NAME without "
+                                   "IMSG_RECONF_IFACE");
+                       if (IMSG_DATA_SIZE(imsg) > 255 + 2)
+                               fatalx("%s: IMSG_RECONF_H_NAME wrong length: "
+                                   "%lu", __func__, IMSG_DATA_SIZE(imsg));
+                       if ((iface_conf->h_name = malloc(IMSG_DATA_SIZE(imsg)))
+                           == NULL)
+                               fatal(NULL);
+                       memcpy(iface_conf->h_name, imsg.data,
+                           IMSG_DATA_SIZE(imsg));
+                       iface_conf->h_name_len = IMSG_DATA_SIZE(imsg);
                        break;
                case IMSG_RECONF_END: {
                        struct dhcpleased_iface *iface;
Index: frontend.c
===================================================================
RCS file: /cvs/src/sbin/dhcpleased/frontend.c,v
retrieving revision 1.22
diff -u -p -r1.22 frontend.c
--- frontend.c  14 Sep 2021 07:51:51 -0000      1.22
+++ frontend.c  17 Sep 2021 19:21:27 -0000
@@ -360,6 +360,8 @@ frontend_dispatch_main(int fd, short eve
                        iface_conf->vc_id_len = 0;
                        iface_conf->c_id = NULL;
                        iface_conf->c_id_len = 0;
+                       iface_conf->h_name = NULL;
+                       iface_conf->h_name_len = 0;
                        SIMPLEQ_INSERT_TAIL(&nconf->iface_list,
                            iface_conf, entry);
                        break;
@@ -391,6 +393,20 @@ frontend_dispatch_main(int fd, short eve
                            IMSG_DATA_SIZE(imsg));
                        iface_conf->c_id_len = IMSG_DATA_SIZE(imsg);
                        break;
+               case IMSG_RECONF_H_NAME:
+                       if (iface_conf == NULL)
+                               fatal("IMSG_RECONF_H_NAME without "
+                                   "IMSG_RECONF_IFACE");
+                       if (IMSG_DATA_SIZE(imsg) > 255 + 2)
+                               fatalx("%s: IMSG_RECONF_H_NAME wrong length: "
+                                   "%lu", __func__, IMSG_DATA_SIZE(imsg));
+                       if ((iface_conf->h_name = malloc(IMSG_DATA_SIZE(imsg)))
+                           == NULL)
+                               fatal(NULL);
+                       memcpy(iface_conf->h_name, imsg.data,
+                           IMSG_DATA_SIZE(imsg));
+                       iface_conf->h_name_len = IMSG_DATA_SIZE(imsg);
+                       break;
                case IMSG_RECONF_END: {
                        int      i;
                        int     *ifaces;
@@ -892,7 +908,7 @@ build_packet(uint8_t message_type, char 
        static uint8_t   dhcp_cookie[] = DHCP_COOKIE;
        static uint8_t   dhcp_message_type[] = {DHO_DHCP_MESSAGE_TYPE, 1,
                DHCPDISCOVER};
-       static uint8_t   dhcp_hostname[255] = {DHO_HOST_NAME, 0 /*, ... */};
+       static uint8_t   dhcp_hostname[255 + 3] = {DHO_HOST_NAME, 0 /*, ... 
*/};
        static uint8_t   dhcp_client_id[] = {DHO_DHCP_CLIENT_IDENTIFIER, 7,
                HTYPE_ETHER, 0, 0, 0, 0, 0, 0};
        static uint8_t   dhcp_req_list[] = {DHO_DHCP_PARAMETER_REQUEST_LIST,
@@ -931,12 +947,26 @@ build_packet(uint8_t message_type, char 
        p += sizeof(dhcp_cookie);
        memcpy(p, dhcp_message_type, sizeof(dhcp_message_type));
        p += sizeof(dhcp_message_type);
-       if (gethostname(dhcp_hostname + 2, sizeof(dhcp_hostname) - 2) == 0) {
-               if ((c = strchr(dhcp_hostname + 2, '.')) != NULL)
-                       *c = '\0';
-               dhcp_hostname[1] = strlen(dhcp_hostname + 2);
-               memcpy(p, dhcp_hostname, dhcp_hostname[1] + 2);
-               p += dhcp_hostname[1] + 2;
+
+#ifndef SMALL
+       if (iface_conf != NULL && (iface_conf->h_name != NULL ||
+           iface_conf->suppress & SUP_HOST_NAME)) {
+               if (iface_conf->h_name_len > 0) {
+                       /* XXX check space */
+                       memcpy(p, iface_conf->h_name, iface_conf->h_name_len);
+                       p += iface_conf->h_name_len;
+               }
+       } else
+#endif /* SMALL */
+       {
+               if (gethostname(dhcp_hostname + 2,
+                   sizeof(dhcp_hostname) - 2) == 0) {
+                       if ((c = strchr(dhcp_hostname + 2, '.')) != NULL)
+                               *c = '\0';
+                       dhcp_hostname[1] = strlen(dhcp_hostname + 2);
+                       memcpy(p, dhcp_hostname, dhcp_hostname[1] + 2);
+                       p += dhcp_hostname[1] + 2;
+               }
        }
 
 #ifndef SMALL
@@ -1229,6 +1259,10 @@ iface_conf_cmp(struct iface_conf *a, str
        if (a->c_id_len != b->c_id_len)
                return 1;
        if (memcmp(a->c_id, b->c_id, a->c_id_len) != 0)
+               return 1;
+       if (a->h_name_len != b->h_name_len)
+               return 1;
+       if (memcmp(a->h_name, b->h_name, a->h_name_len) != 0)
                return 1;
        if (a->ignore != b->ignore)
                return 1;
Index: parse.y
===================================================================
RCS file: /cvs/src/sbin/dhcpleased/parse.y,v
retrieving revision 1.3
diff -u -p -r1.3 parse.y
--- parse.y     12 Aug 2021 12:41:08 -0000      1.3
+++ parse.y     17 Sep 2021 19:21:27 -0000
@@ -108,7 +108,8 @@ typedef struct {
 
 %}
 
-%token DHCP_IFACE ERROR SEND VENDOR CLASS ID CLIENT IGNORE DNS ROUTES
+%token DHCP_IFACE ERROR SEND VENDOR CLASS ID CLIENT NO HOST NAME IGNORE DNS
+%token ROUTES
 
 %token <v.string>      STRING
 %token <v.number>      NUMBER
@@ -275,6 +276,47 @@ ifaceoptsl : SEND VENDOR CLASS ID STRING
                        iface_conf->c_id[0] = DHO_DHCP_CLIENT_IDENTIFIER;
                        iface_conf->c_id[1] = iface_conf->c_id_len - 2;
                }
+               | SEND HOST NAME STRING {
+                       ssize_t len;
+                       char    buf[255];
+
+                       if (iface_conf->h_name != NULL ||
+                           iface_conf->suppress & SUP_HOST_NAME) {
+                               yyerror("host name already set");
+                               YYERROR;
+                       }
+
+                       len = strnunvis(buf, $4, sizeof(buf));
+                       free($4);
+
+                       if (len == -1 || len == 0) {
+                               yyerror("invalid host name");
+                               YYERROR;
+                       }
+                       if ((size_t)len >= sizeof(buf)) {
+                               yyerror("host name too long");
+                               YYERROR;
+                       }
+
+                       iface_conf->h_name_len = 2 + strlen(buf);
+                       iface_conf->h_name = malloc(iface_conf->h_name_len);
+                       if (iface_conf->h_name == NULL) {
+                               yyerror("malloc");
+                               YYERROR;
+                       }
+                       iface_conf->h_name[0] = DHO_HOST_NAME;
+                       iface_conf->h_name[1] = iface_conf->h_name_len - 2;
+                       memcpy(&iface_conf->h_name[2], buf,
+                           iface_conf->h_name_len - 2);
+               }
+               | SEND NO HOST NAME {
+                       if (iface_conf->h_name != NULL) {
+                               yyerror("host name already set");
+                               YYERROR;
+                       }
+
+                       iface_conf->suppress |= SUP_HOST_NAME;
+               }
                | IGNORE ROUTES {
                        iface_conf->ignore |= IGN_ROUTES;
                }
@@ -338,9 +380,12 @@ lookup(char *s)
                {"class",               CLASS},
                {"client",              CLIENT},
                {"dns",                 DNS},
+               {"host",                HOST},
                {"id",                  ID},
                {"ignore",              IGNORE},
                {"interface",           DHCP_IFACE},
+               {"name",                NAME},
+               {"no",                  NO},
                {"routes",              ROUTES},
                {"send",                SEND},
                {"vendor",              VENDOR},
Index: printconf.c
===================================================================
RCS file: /cvs/src/sbin/dhcpleased/printconf.c,v
retrieving revision 1.2
diff -u -p -r1.2 printconf.c
--- printconf.c 12 Aug 2021 12:41:08 -0000      1.2
+++ printconf.c 17 Sep 2021 19:21:27 -0000
@@ -95,6 +95,12 @@ print_dhcp_options(char *indent, uint8_t
                        p += dho_len - 1;
                        rem -= dho_len - 1;
                        break;
+               case DHO_HOST_NAME:
+                       strvisx(buf, p, dho_len, VIS_DQ | VIS_CSTYLE);
+                       p += dho_len;
+                       rem -= dho_len;
+                       printf("%ssend host name \"%s\"\n", indent, buf);
+                       break;
                default:
                        fatal("unknown dhcp option: %d [%d]", *p, rem);
                        break;
@@ -112,6 +118,12 @@ print_config(struct dhcpleased_conf *con
        SIMPLEQ_FOREACH(iface, &conf->iface_list, entry) {
                printf("interface %s {\n", iface->name);
                print_dhcp_options("\t", iface->c_id, iface->c_id_len);
+               if (iface->suppress & SUP_HOST_NAME)
+                       printf("\tsend no host name\n");
+               else {
+                       print_dhcp_options("\t", iface->h_name,
+                           iface->h_name_len);
+               }
                print_dhcp_options("\t", iface->vc_id, iface->vc_id_len);
                if (iface->ignore & IGN_DNS)
                        printf("\tignore dns\n");

Reply via email to