Package: pump
Version: 0.8.24-1
Severity: wishlist
Tags: patch

Hi!

I needed to register hostnames in different DNS zones
depending on domain requested by DHCP clients. DHCP
server daemon is dhcp3.

Using "interim" ddns-update style of dhcp3-server the
problem can't be solved with pump as client ("A"
records are never registered by dhcp3, only "PTR"
ones).

I studied this for some time, even hacked pump to send
"DN" option together with "HN", without success.

The reason for the strange dhcp3 vs. pump behaviour is
support of new DHCP option called "FQDN", which is
present in dhcp3 and absent in pump.

dhcp3 will _only_ register "A" DNS record for a client
when client sends DHCP request with "FQDN" option
present and "S" bit set in "flags" field.

You can find description of FQDN option at
http://www.ietf.org/internet-drafts/draft-ietf-dhc-fqdn-option-11.txt

In short:
>    The format of the Client FQDN option is:
> 
>         Code   Len    Flags  RCODE1 RCODE2   Domain Name
>        +------+------+------+------+------+------+--
>        |  81  |   n  |      |      |      |       ...
>        +------+------+------+------+------+------+--
...
>    The format of the 1-octet Flags field is:
> 
>         0 1 2 3 4 5 6 7
>        +-+-+-+-+-+-+-+-+
>        |  MBZ  |N|E|O|S|
>        +-+-+-+-+-+-+-+-+

relevant bits:
 S -- register "A" DNS RR in addition to "PTR"
 N -- don't register DNS resource records at all
 E -- encoding of FQDN (1==binary(DNS)/0==ASCII)

Generally, we have 5 possibilities:
* request A&PTR registration (S==1 && N==0)
* request PTR registration   (S==0 && N==0)
* request no dnsreg          (S==0 && N==1)
  // S==1 && N==1 is illegal
* don't use FQDN option at all (use HN)
* automatically request A&PTR registration when
  hostname contains dots

I made preliminary patch which adds support for all 5
modes (5th is made default) through cmdline options and
configfile directives.

Currently patch deals only with sending FQDN/HN, and in
ASCII encoding only. It does not account for server
replying us with FQDN instead of HN. I'll add support
for this later.

While hacking, I found some more problems with code, I
think we should discuss them before going further.

-- 
WBR,
xrgtn

-- System Information:
Debian Release: testing/unstable
  APT prefers testing
  APT policy: (990, 'testing'), (500, 'oldstable'), (500, 'unstable'), (500, 
'stable'), (1, 'experimental')
Architecture: i386 (i586)
Shell:  /bin/sh linked to /bin/bash
Kernel: Linux 2.4.32-grsec
Locale: LANG=en_GB.UTF-8, LC_CTYPE=en_GB.UTF-8 (charmap=UTF-8)

Versions of packages pump depends on:
ii  libc6                         2.3.5-8    GNU C Library: Shared libraries an
ii  libpopt0                      1.7-5      lib for parsing cmdline parameters

pump recommends no packages.

-- no debconf information
Index: config.c
===================================================================
RCS file: /var/lib/cvs/deb/pump/config.c,v
retrieving revision 1.1.1.1
retrieving revision 1.1.1.1.2.2
diff -u -r1.1.1.1 -r1.1.1.1.2.2
--- config.c    23 Dec 2005 13:17:46 -0000      1.1.1.1
+++ config.c    6 Jan 2006 23:07:47 -0000       1.1.1.1.2.2
@@ -225,6 +225,35 @@
                return 1;
            }
            override->flags |= OVERRIDE_FLAG_NORESOLVCONF;
+       } else if (!strcmp(start, "dnsreg")) {
+           if (*rest) {
+               parseError(*lineNum, "unexpected argument to dnsreg directive");
+               return 1;
+           }
+           override->fqdn_flags = DHCP_FQDN_DNSREG_BOTH;
+           override->fqdn_flags_explicit = 1;
+       } else if (!strcmp(start, "dnsregptr")) {
+           if (*rest) {
+               parseError(*lineNum, "unexpected argument to dnsregptr "
+                          "directive");
+               return 1;
+           }
+           override->fqdn_flags = DHCP_FQDN_DNSREG_PTR;
+           override->fqdn_flags_explicit = 1;
+       } else if (!strcmp(start, "nodnsreg")) {
+           if (*rest) {
+               parseError(*lineNum, "unexpected argument to nodnsreg "
+                          "directive");
+               return 1;
+           }
+           override->fqdn_flags = DHCP_FQDN_NO_DNSREG;
+           override->fqdn_flags_explicit = 1;
+       } else if (!strcmp(start, "nofqdn")) {
+           if (*rest) {
+               parseError(*lineNum, "unexpected argument to nofqdn directive");
+               return 1;
+           }
+           override->fqdn_flags_explicit = -1;
        } else if (!strcmp(start, "script")) {
            size_t len;
 
Index: dhcp.c
===================================================================
RCS file: /var/lib/cvs/deb/pump/dhcp.c,v
retrieving revision 1.1.1.1
retrieving revision 1.1.1.1.2.8
diff -u -r1.1.1.1 -r1.1.1.1.2.8
--- dhcp.c      23 Dec 2005 13:17:46 -0000      1.1.1.1
+++ dhcp.c      6 Jan 2006 23:23:01 -0000       1.1.1.1.2.8
@@ -83,6 +83,7 @@
 #define DHCP_OPTION_T1                 58
 #define DHCP_OPTION_CLASS_IDENTIFIER    60
 #define DHCP_OPTION_CLIENT_IDENTIFIER  61
+#define DHCP_OPTION_FQDN               81
 
 #define BOOTP_OPCODE_REQUEST   1
 #define BOOTP_OPCODE_REPLY     2
@@ -149,7 +150,8 @@
 static char * perrorstr(char * msg);
 static void addClientIdentifier(int flags, struct bootpRequest * req);
 static void buildRequest(struct bootpRequest * req, int flags, int type,
-                        char * reqHostname, char *class, int lease);
+                        char * reqHostname, char *class, int lease,
+                        struct pumpOverrideInfo * override);
 static void updateSecCount(struct bootpRequest * breq, time_t startTime);
 static void makeraw(struct ippkt *buf, const void *payload, size_t len);
 static uint32_t checksum(const void *, size_t, uint32_t);
@@ -537,6 +539,18 @@
                }
                break;
 
+           case DHCP_OPTION_FQDN:
+               if (length > 3) {
+                   unsigned char buf[1024];
+                   if (length - 3 < sizeof(buf)) {
+                       memcpy(buf, chptr + 3, length - 3);
+                       buf[length - 3] = '\0';
+                       syslog (LOG_DEBUG, "intf: FQDN (flags 0x%2.2X): %s",
+                               (unsigned)*chptr, buf);
+                   }
+               }
+               break;
+
            case BOOTP_OPTION_BOOTFILE:
                /* we ignore this right now */
                break;
@@ -1006,7 +1020,7 @@
 }
 
 static int getVendorCode(struct bootpRequest * bresp, unsigned char option,
-                         void * data, size_t maxsize) {
+                        void * data, size_t maxsize) {
     unsigned char * chptr;
     unsigned int length, theOption;
 
@@ -1060,13 +1074,72 @@
     return s;
 }
 
+/*
+ * Conditionally adds either BOOTP_OPTION_HOSTNAME or DHCP_OPTION_FQDN to
+ * DHCP request depending on interface's override settings.
+ * 
+ * If given hostname is NULL condAddFQDNVendorCode() tries to resolve it,
+ * but if that fails, neither DHCP option will be added.
+ */
+static void condAddFQDNVendorCode(struct bootpRequest * pbreq,
+       const struct pumpOverrideInfo * override, char * hostname) {
+    int use_fqdn_option = 0;
+    if (override) use_fqdn_option = override->fqdn_flags_explicit;
+    if (use_fqdn_option == 0/*auto*/ && hostname != NULL)
+       /* if not specified explicitly, use DHCP_OPTION_FQDN when requested
+        * hostname has domain part and BOOTP_OPTION_HOSTNAME otherwise */
+       use_fqdn_option = strchr(hostname, '.') ? 1 : -1;
+    if (hostname != NULL && use_fqdn_option < 0) {
+       /* hostname is given and FQDN option is disabled, thus simply
+        * go pass the hostname into BOOTP_OPTION_HOSTNAME.*/
+       addVendorCode(pbreq, BOOTP_OPTION_HOSTNAME,
+                     strlen(hostname) + 1, hostname);
+    } else {
+       /* We will need either to resolve hostname or to fill fqdn_opt,
+        * i.e. anyway we need to allocate a buffer for hostname.*/
+       struct {
+           u_int8_t flags;
+           u_int8_t rcode1;
+           u_int8_t rcode2;
+           char hn_buf[1024];
+       } fqdn_opt = {override->fqdn_flags, 0, 0};
+       size_t namelen = 0;
+       /* find out hostname to be requested */
+       if (hostname == NULL) {
+           gethostname(fqdn_opt.hn_buf, sizeof(fqdn_opt.hn_buf));
+           fqdn_opt.hn_buf[sizeof(fqdn_opt.hn_buf) - 1] = '\0';
+           if (!strcmp(fqdn_opt.hn_buf, "localhost") ||
+                   !strcmp(fqdn_opt.hn_buf, "localhost.localdomain")) {
+               fqdn_opt.hn_buf[0] = '\0';
+               /* XXX: don't ask for empty hostname when can't resolve */
+               return;
+           }
+           namelen = strlen(fqdn_opt.hn_buf);
+       } else {
+           /* copy hostname into the HN buffer for convenience */
+           namelen = strlen(hostname);
+           if (namelen >= sizeof(fqdn_opt.hn_buf))
+               namelen = sizeof(fqdn_opt.hn_buf) - 1;
+           memcpy(fqdn_opt.hn_buf, hostname, namelen);
+           fqdn_opt.hn_buf[namelen] = '\0';
+       }
+       /* ultimately choose between BOOTP_OPTION_HOSTNAME/DHCP_OPTION_FQDN */
+       if (use_fqdn_option == 0)
+           use_fqdn_option = strchr(fqdn_opt.hn_buf, '.') ? 1 : -1;
+       if (use_fqdn_option > 0)
+           addVendorCode(pbreq, DHCP_OPTION_FQDN, 3 + namelen + 1, &fqdn_opt);
+       else
+           addVendorCode(pbreq, BOOTP_OPTION_HOSTNAME, namelen + 1,
+                         fqdn_opt.hn_buf);
+    }
+}
+
 int pumpDhcpRelease(struct pumpNetIntf * intf) {
     struct bootpRequest breq, bresp;
     unsigned char messageType;
     struct sockaddr_in serverAddr;
     char * chptr;
     int s;
-    char hostname[1024];
 
     if (!(intf->set & PUMP_INTFINFO_HAS_LEASE)) {
        pumpDisableInterface(intf);
@@ -1090,18 +1163,8 @@
     memcpy(&breq.ciaddr, &intf->ip, sizeof(breq.ciaddr));
 
     /* Dynamic DHCP implementations need the hostname here. */
-    if (intf->set & PUMP_NETINFO_HAS_HOSTNAME) {
-       addVendorCode(&breq, BOOTP_OPTION_HOSTNAME,
-                     strlen(intf->hostname) + 1, intf->hostname);
-    } else {
-       gethostname(hostname, sizeof(hostname));
-       hostname[sizeof(hostname) - 1] = 0;
-       if (strcmp(hostname, "localhost") && 
-           strcmp(hostname, "localhost.localdomain")) {
-           addVendorCode(&breq, BOOTP_OPTION_HOSTNAME, 
-                         strlen(hostname) + 1, hostname);
-       }
-    }
+    condAddFQDNVendorCode(&breq, &intf->override,
+       (intf->set & PUMP_NETINFO_HAS_HOSTNAME) ? intf->hostname : NULL);
 
     serverAddr.sin_family = AF_INET;
     serverAddr.sin_port = bootp_server_port;   /* bootp server */
@@ -1122,7 +1185,7 @@
 
     return 0;
 }
-    
+
 /* This is somewhat broken. We try only to renew the lease. If we fail,
    we don't try to completely rebind. This doesn't follow the DHCP spec,
    but for the install it should be a reasonable compromise. */
@@ -1133,7 +1196,6 @@
     char * chptr;
     int s;
     int i;
-    char hostname[1024];
     time_t startTime = pumpUptime();
 
     s = createSocket(intf->device);
@@ -1150,19 +1212,8 @@
     addClientIdentifier(intf->flags, &breq);
 
     /* Dynamic DHCP implementations need the hostname here. */
-    if (intf->set & PUMP_NETINFO_HAS_HOSTNAME) {
-       addVendorCode(&breq, BOOTP_OPTION_HOSTNAME,
-                     strlen(intf->hostname) + 1,
-                     intf->hostname);
-    } else {
-       gethostname(hostname, sizeof(hostname));
-       hostname[sizeof(hostname) - 1] = 0;
-       if (strcmp(hostname, "localhost") && 
-           strcmp(hostname, "localhost.localdomain")) {
-           addVendorCode(&breq, BOOTP_OPTION_HOSTNAME, 
-                         strlen(hostname) + 1, hostname);
-       }
-    }
+    condAddFQDNVendorCode(&breq, &intf->override,
+       (intf->set & PUMP_NETINFO_HAS_HOSTNAME) ? intf->hostname : NULL);
 
     i = htonl(intf->reqLease);
     addVendorCode(&breq, DHCP_OPTION_LEASE, 4, &i);
@@ -1222,7 +1273,8 @@
 }
 
 static void buildRequest(struct bootpRequest * req, int flags, int type,
-                        char * reqHostname, char *class, int lease) {
+                        char * reqHostname, char *class, int lease,
+                        struct pumpOverrideInfo * override) {
     unsigned char messageType = type;
     short aShort;
     int anInt;
@@ -1255,19 +1307,7 @@
 
     req->xid += aCount;
 
-    if (!reqHostname) {
-       reqHostname = alloca(200);
-       gethostname(reqHostname, 200);
-       reqHostname[199] = 0;
-       if (!strcmp(reqHostname, "localhost") ||
-           !strcmp(reqHostname, "localhost.localdomain"))
-           reqHostname = NULL;
-    }
-
-    if (reqHostname) {
-       addVendorCode(req, BOOTP_OPTION_HOSTNAME,
-                     strlen(reqHostname) + 1, reqHostname);
-    }
+    condAddFQDNVendorCode(req, override, reqHostname);
     if (class) {
        addVendorCode(req, DHCP_OPTION_CLASS_IDENTIFIER,
                      strlen(class) + 1, class);
@@ -1335,8 +1375,7 @@
 
     if (reqHostname) {
        syslog(LOG_DEBUG, "HOSTNAME: requesting %s\n", reqHostname);
-       addVendorCode(&breq, BOOTP_OPTION_HOSTNAME,
-                     strlen(reqHostname) + 1, reqHostname);
+       condAddFQDNVendorCode(&breq, override, reqHostname);
     }
     if (class) {
        syslog(LOG_DEBUG, "CLASSID: sending %s\n", class);
@@ -1382,7 +1421,7 @@
 
        initVendorCodes(&breq);
        buildRequest(&breq, flags, DHCP_TYPE_DISCOVER, reqHostname, class,
-                    intf->reqLease);
+                    intf->reqLease, override);
 
        syslog (LOG_DEBUG, "PUMP: sending second discover");
 
@@ -1413,7 +1452,7 @@
 
        initVendorCodes(&breq);
        buildRequest(&breq, flags, DHCP_TYPE_REQUEST, reqHostname, class,
-                    intf->reqLease);
+                    intf->reqLease, override);
 
        addVendorCode(&breq, DHCP_OPTION_SERVER, 4, &serverAddr.sin_addr);
        addVendorCode(&breq, DHCP_OPTION_REQADDR, 4, &bresp.yiaddr);
@@ -1462,6 +1501,8 @@
     strcpy(override->device, "MASTER");
     override->timeout = DEFAULT_TIMEOUT;
     override->numRetries = DEFAULT_NUM_RETRIES;
+    override->fqdn_flags = DHCP_FQDN_DNSREG_BOTH;
+    override->fqdn_flags_explicit = 0;
 }
 
 /*
Index: pump.c
===================================================================
RCS file: /var/lib/cvs/deb/pump/pump.c,v
retrieving revision 1.1.1.1
retrieving revision 1.1.1.1.2.6
diff -u -r1.1.1.1 -r1.1.1.1.2.6
--- pump.c      23 Dec 2005 13:17:46 -0000      1.1.1.1
+++ pump.c      6 Jan 2006 23:23:01 -0000       1.1.1.1.2.6
@@ -933,10 +933,20 @@
     char * script = NULL;
     struct pumpOverrideInfo * overrides;
     int cont;
+    int fqdn_dnsreg_both = 0, fqdn_dnsreg_ptr = 0, fqdn_no_dnsreg = 0;
+    int no_fqdn = 0;
     struct poptOption options[] = {
+           { "dnsreg", 'A', POPT_ARG_NONE, &fqdn_dnsreg_both, 0,
+                       N_("Register A and PTR records using FQDN option"),
+                       NULL },
            { "config-file", 'c', POPT_ARG_STRING, &configFile, 0,
                        N_("Configuration file to use instead of "
                           "/etc/pump.conf") },
+           { "no-fqdn", 'f', POPT_ARG_NONE, &no_fqdn, 0,
+                       N_("Don't send FQDN DHCP option"), NULL },
+           { "no-dnsreg", 'F', POPT_ARG_NONE, &fqdn_no_dnsreg, 0,
+                       N_("Forbid A and PTR registration using FQDN option"),
+                       NULL },
             { "hostname", 'h', POPT_ARG_STRING, &hostname, 0, 
                        N_("Hostname to request"), N_("hostname") },
             { "interface", 'i', POPT_ARG_STRING, &device, 0, 
@@ -950,6 +960,8 @@
                        N_("Lease time to request (in seconds)"), N_("seconds") 
},
            { "lookup-hostname", '\0', POPT_ARG_NONE, &lookupHostname, 0,
                        N_("Force lookup of hostname") },
+           { "dnsreg-ptr", 'P', POPT_ARG_NONE, &fqdn_dnsreg_ptr, 0,
+                       N_("Register PTR record using FQDN option"), NULL },
            { "release", 'r', POPT_ARG_NONE, &release, 0,
                        N_("Release interface"), NULL },
            { "renew", 'R', POPT_ARG_NONE, &renew, 0,
@@ -1019,6 +1031,12 @@
        return 1;
     }
 
+    if (fqdn_dnsreg_both + fqdn_dnsreg_ptr + fqdn_no_dnsreg + no_fqdn > 1) {
+       fprintf(stderr, _("%s: -A, -P , -F and -f are mutually exclusive\n"),
+               PROGNAME);
+       return 1;
+    }
+
     /* make sure the config file is parseable before going on any further */
     if (readPumpConfig(configFile, &overrides)) return 1;
 
@@ -1079,6 +1097,18 @@
            o->flags |= OVERRIDE_FLAG_NORESOLVCONF;
        if (script)
            strcpy(o->script, script);
+       if (fqdn_dnsreg_both) {
+           o->fqdn_flags = DHCP_FQDN_DNSREG_BOTH;
+           o->fqdn_flags_explicit = 1;
+       } else if (fqdn_dnsreg_ptr) {
+           o->fqdn_flags = DHCP_FQDN_DNSREG_PTR;
+           o->fqdn_flags_explicit = 1;
+       } else if (fqdn_no_dnsreg) {
+           o->fqdn_flags = DHCP_FQDN_NO_DNSREG;
+           o->fqdn_flags_explicit = 1;
+       } else if (no_fqdn) {
+           o->fqdn_flags_explicit = -1;
+       }
  
        memcpy(&cmd.u.start.override, o, sizeof(*o));
     }
Index: pump.h
===================================================================
RCS file: /var/lib/cvs/deb/pump/pump.h,v
retrieving revision 1.1.1.1
retrieving revision 1.1.1.1.2.5
diff -u -r1.1.1.1 -r1.1.1.1.2.5
--- pump.h      23 Dec 2005 13:17:46 -0000      1.1.1.1
+++ pump.h      6 Jan 2006 23:25:13 -0000       1.1.1.1.2.5
@@ -5,6 +5,7 @@
 #include <netinet/ip.h>
 #include <arpa/inet.h>
 #include <sys/time.h>
+#include <sys/types.h>
 
 #define MAX_GATEWAYS           3
 #define MAX_DNS_SERVERS                3
@@ -56,6 +57,13 @@
 #define OVERRIDE_FLAG_NOSETUP          (1 << 4)
 #define OVERRIDE_FLAG_NORESOLVCONF     (1 << 5)
 
+#define DHCP_FQDN_DNSREG_PTR   0x00
+#define DHCP_FQDN_DNSREG_A     0x80
+#define DHCP_FQDN_DNSREG_BOTH  (DHCP_FQDN_DNSREG_PTR|DHCP_FQDN_DNSREG_A)
+#define DHCP_FQDN_NO_DNSREG    0x10
+#define DHCP_FQDN_BINARY       0x20
+#define DHCP_FQDN_OVERRIDEN    0x40
+
 struct pumpOverrideInfo {
     char device[10];
     char searchPath[1024];
@@ -63,6 +71,8 @@
     int numRetries;
     int timeout;
     char script[1024];
+    u_int8_t fqdn_flags;       /* raw value for DHCP_FQDN option flags */
+    int fqdn_flags_explicit;   /* 1==use FQDN, -1==don't use, 0==auto */
 };
 
 /* all of these in_addr things are in network byte order! */

Reply via email to