[PATCH] pppd: added auto DNS configuration

Igor Pokrovsky ip at doom.homeunix.org
Tue Dec 27 08:52:19 PST 2005


Hello,

I've implemented DNS automatic negotiation and configuration in
pppd (RFC1877). Since it is not a standard thing, I made it an
optional feature of pppd. Some parts of the code were taken from ppp
implementation. I would be greatful for testing of this patch and
for any comments and suggestion.

Thanks,

-ip

-- 
If your condition seems to be getting better, it's
probably your doctor getting sick.
-------------- next part --------------
Index: pppd/Makefile
===================================================================
RCS file: /home/ncvs/src/usr.sbin/pppd/Makefile,v
retrieving revision 1.19.2.3
diff -u -r1.19.2.3 Makefile
--- pppd/Makefile	13 Dec 2004 13:50:02 -0000	1.19.2.3
+++ pppd/Makefile	25 Dec 2005 17:25:55 -0000
@@ -38,6 +38,9 @@
 DPADD+=	${LIBCRYPTO}
 .endif
 
+# DNS automatic configuration support
+CFLAGS+=-DNS_NEGOTIATE -DDNS_CONFIGURE
+
 .if defined(RELEASE_CRUNCH)
 # We must create these objects because crunchgen will link them,
 # and we don't want any unused symbols to spoil the final link.
Index: pppd/cbcp.c
===================================================================
RCS file: /home/ncvs/src/usr.sbin/pppd/cbcp.c,v
retrieving revision 1.4.2.2
diff -u -r1.4.2.2 cbcp.c
--- pppd/cbcp.c	11 Dec 2004 11:23:56 -0000	1.4.2.2
+++ pppd/cbcp.c	25 Dec 2005 14:43:18 -0000
@@ -26,6 +26,7 @@
 #include <string.h>
 #include <sys/types.h>
 #include <sys/time.h>
+#include <netinet/in.h>
 #include <syslog.h>
 
 #include "pppd.h"
Index: pppd/demand.c
===================================================================
RCS file: /home/ncvs/src/usr.sbin/pppd/demand.c,v
retrieving revision 1.5
diff -u -r1.5 demand.c
--- pppd/demand.c	28 Aug 1999 01:19:02 -0000	1.5
+++ pppd/demand.c	25 Dec 2005 14:38:28 -0000
@@ -28,6 +28,7 @@
 #include <fcntl.h>
 #include <syslog.h>
 #include <netdb.h>
+#include <netinet/in.h>
 #include <sys/param.h>
 #include <sys/types.h>
 #include <sys/wait.h>
Index: pppd/ipcp.c
===================================================================
RCS file: /home/ncvs/src/usr.sbin/pppd/ipcp.c,v
retrieving revision 1.12
diff -u -r1.12 ipcp.c
--- pppd/ipcp.c	28 Aug 1999 01:19:03 -0000	1.12
+++ pppd/ipcp.c	27 Dec 2005 16:47:00 -0000
@@ -28,11 +28,15 @@
 #include <stdio.h>
 #include <string.h>
 #include <syslog.h>
+#include <fcntl.h>
 #include <netdb.h>
 #include <sys/param.h>
+#include <sys/stat.h>
 #include <sys/types.h>
 #include <sys/socket.h>
 #include <netinet/in.h>
+#include <arpa/inet.h>
+#include <resolv.h>
 
 #include "pppd.h"
 #include "fsm.h"
@@ -49,6 +53,9 @@
 static int cis_received[NUM_PPP];	/* # Conf-Reqs received */
 static int default_route_set[NUM_PPP];	/* Have set up a default route */
 static int proxy_arp_set[NUM_PPP];	/* Have created proxy arp entry */
+#ifdef DNS_CONFIGURE
+static ns_t ns;				/* local storage for NS config */
+#endif
 
 /*
  * Callbacks for fsm code.  (CI = Configuration Information)
@@ -154,7 +161,162 @@
     return b;
 }
 
+/*
+ * loadDNS - load info from existing resolv.conf
+ * Adopted from ppp.
+ */
+static void
+loadDNS(ns)
+ns_t *ns;
+{
+    int fd;
+
+    ns->dns[0].s_addr = ns->dns[1].s_addr = INADDR_NONE;
+
+    if (ns->resolv != NULL) {
+	free(ns->resolv);
+	ns->resolv = NULL;
+    }
+  
+    if (ns->resolv_nons != NULL) {
+	free(ns->resolv_nons);
+	ns->resolv_nons = NULL;
+    }  
+    
+    if ((fd = open(_PATH_RESCONF, O_RDONLY)) != -1) {
+	struct stat st;
 
+	if (fstat(fd, &st) == 0) {
+	    ssize_t got;
+
+	    if ((ns->resolv_nons = (char *)malloc(st.st_size + 1)) == NULL)
+		IPCPDEBUG((LOG_ERROR, "Failed to malloc %lu for %s: %s\n",
+		    (unsigned long)st.st_size, _PATH_RESCONF, strerror(errno)));
+	    else if ((ns->resolv = (char *)malloc(st.st_size + 1)) == NULL) {
+   		IPCPDEBUG((LOG_ERROR, "Failed(2) to malloc %lu for %s: %s\n",
+   		    (unsigned long)st.st_size, _PATH_RESCONF, strerror(errno)));
+	    free(ns->resolv_nons);
+	    ns->resolv_nons = NULL;
+ 	} else if ((got = read(fd, ns->resolv, st.st_size)) != st.st_size) {
+	    if (got == -1)
+		IPCPDEBUG((LOG_ERROR, "Failed to read %s: %s\n",
+		    _PATH_RESCONF, strerror(errno)));
+	    else
+		IPCPDEBUG((LOG_ERROR, "Failed to read %s, got %lu not %lu\n",
+		    _PATH_RESCONF, (unsigned long)got, (unsigned long)st.st_size));
+	    free(ns->resolv_nons);
+	    ns->resolv_nons = NULL;
+	    free(ns->resolv);
+	    ns->resolv = NULL;
+	} else {
+
+#define issep(ch) ((ch) == ' ' || (ch) == '\t')
+#define isip(ch) (((ch) >= '0' && (ch) <= '9') || (ch) == '.')
+	
+	    char *cp, *cp_nons, *ncp, ch;
+	    int n;
+	    
+	    ns->resolv[st.st_size] = '\0';
+	    
+	    cp_nons = ns->resolv_nons;
+	    cp = ns->resolv;
+	    n = 0;
+	    
+	    while ((ncp = strstr(cp, "nameserver")) != NULL) {
+		if (ncp != cp) {
+		    memcpy(cp_nons, cp, ncp - cp);
+		    cp_nons += ncp - cp;
+		}
+		if ((ncp != cp && ncp[-1] != '\n') || !issep(ncp[10])) {
+		    memcpy(cp_nons, ncp, 9);
+		    cp_nons += 9;
+		    cp = ncp + 9;	/* Can't match "nameserver" at cp... */
+		    continue;
+		}
+
+		for (cp = ncp + 11; issep(*cp); cp++)	/* Skip whitespace */
+		    ;
+
+		for (ncp = cp; isip(*ncp); ncp++)	/* Jump over IP */
+		    ;
+		    
+		ch = *ncp;
+		*ncp = '\0';
+		if (n < 2 && inet_aton(cp, ns->dns))
+		    n++;
+		*ncp = ch;
+		
+		if ((cp = strchr(ncp, '\n')) == NULL)	/* Point at next line */
+		    cp = ncp + strlen(ncp);
+		else
+		    cp++;
+	    }	    
+	    strcpy(cp_nons, cp);	/* Copy the end - including the NUL */
+	    cp_nons += strlen(cp_nons) - 1;
+	    while (cp_nons >= ns->resolv_nons && *cp_nons == '\n')
+		*cp_nons-- = '\0';
+	    if (n == 2 && ns->dns[0].s_addr == INADDR_ANY) {
+		ns->dns[0].s_addr = ns->dns[1].s_addr;
+		ns->dns[1].s_addr = INADDR_ANY;
+	    }
+	}
+    } else
+	IPCPDEBUG((LOG_ERROR, "Failed to stat opened %s: %s\n",
+	    _PATH_RESCONF, strerror(errno)));
+	close(fd);
+  }               
+}
+
+/*
+ * writeDNS - update nameserver entries in resolv.conf
+ * Adopted from ppp.
+ */
+static int
+writeDNS(ns)
+ns_t *ns;
+{
+    char *paddr;
+    mode_t mask;
+    FILE *fp;
+  
+    if (ns->dns[0].s_addr == INADDR_ANY &&
+	ns->dns[1].s_addr == INADDR_ANY) {
+	IPCPDEBUG((LOG_INFO, "%s not modified: All nameservers NAKd\n",
+	    _PATH_RESCONF));
+	return 0;
+    }
+    
+    if (ns->dns[0].s_addr == INADDR_ANY) {
+	ns->dns[0].s_addr = ns->dns[1].s_addr;
+	ns->dns[1].s_addr = INADDR_ANY;
+    }
+
+    mask = umask(022);
+    if ((fp = fopen(_PATH_RESCONF, "w")) != NULL) {
+	umask(mask);
+	if (ns->resolv_nons)
+	    fputs(ns->resolv_nons, fp);
+	paddr = inet_ntoa(ns->dns[0]);
+	IPCPDEBUG((LOG_INFO, "Primary nameserver set to %s\n", paddr));
+	fprintf(fp, "\nnameserver %s\n", paddr);
+	if (ns->dns[1].s_addr != INADDR_ANY &&
+	    ns->dns[1].s_addr != INADDR_NONE &&
+	    ns->dns[1].s_addr != ns->dns[0].s_addr) {
+	    paddr = inet_ntoa(ns->dns[1]);
+	    IPCPDEBUG((LOG_INFO, "Secondary nameserver set to %s\n", paddr));
+	    fprintf(fp, "nameserver %s\n", paddr);
+	}
+	if (fclose(fp) == EOF) {
+	    IPCPDEBUG((LOG_INFO, "write(): Failed updating %s: %s\n",
+		_PATH_RESCONF, strerror(errno)));
+	    return 0;
+	}
+    } else
+	umask(mask);
+
+    return 1;
+}
+  
 /*
  * ipcp_init - Initialize IPCP.
  */
@@ -180,6 +342,18 @@
     wo->maxslotindex = MAX_STATES - 1; /* really max index */
     wo->cflag = 1;
 
+#ifdef NS_NEGOTIATE
+    wo->neg_dns1 = 1;
+    wo->neg_wins1 = 1;
+    wo->neg_dns2 = 1;
+    wo->neg_wins2 = 1;
+#endif
+#ifdef DNS_CONFIGURE
+    ns.resolv = NULL;
+    ns.resolv_nons = NULL;
+    loadDNS(&ns);
+#endif
+
     /* max slots and slot-id compression are currently hardwired in */
     /* ppp_if.c to 16 and 1, this needs to be changed (among other */
     /* things) gmc */
@@ -298,6 +472,7 @@
     ipcp_options *go = &ipcp_gotoptions[f->unit];
     ipcp_options *wo = &ipcp_wantoptions[f->unit];
     ipcp_options *ho = &ipcp_hisoptions[f->unit];
+    int len;
 
 #define LENCIVJ(neg, old)	(neg ? (old? CILEN_COMPRESS : CILEN_VJ) : 0)
 #define LENCIADDR(neg, old)	(neg ? (old? CILEN_ADDRS : CILEN_ADDR) : 0)
@@ -326,8 +501,17 @@
 	}
     }
 
-    return (LENCIADDR(go->neg_addr, go->old_addrs) +
+    len = (LENCIADDR(go->neg_addr, go->old_addrs) +
 	    LENCIVJ(go->neg_vj, go->old_vj));
+
+#ifdef NS_NEGOTIATE
+    len += LENCIADDR(go->neg_dns1, 0) +
+	    LENCIADDR(go->neg_wins1, 0) +
+	    LENCIADDR(go->neg_dns2, 0) +
+	    LENCIADDR(go->neg_wins2, 0);
+#endif
+
+    return (len);
 }
 
 
@@ -383,6 +567,13 @@
     ADDCIVJ(CI_COMPRESSTYPE, go->neg_vj, go->vj_protocol, go->old_vj,
 	    go->maxslotindex, go->cflag);
 
+#ifdef NS_NEGOTIATE
+    ADDCIADDR(CI_MS_DNS1, go->neg_dns1, 0, go->dnsaddr[0], 0);
+    ADDCIADDR(CI_MS_WINS1, go->neg_wins1, 0, go->winsaddr[0], 0);
+    ADDCIADDR(CI_MS_DNS2, go->neg_dns2, 0, go->dnsaddr[1], 0);
+    ADDCIADDR(CI_MS_WINS2, go->neg_wins2, 0, go->winsaddr[1], 0);
+#endif
+
     *lenp -= len;
 }
 
@@ -463,6 +654,13 @@
     ACKCIVJ(CI_COMPRESSTYPE, go->neg_vj, go->vj_protocol, go->old_vj,
 	    go->maxslotindex, go->cflag);
 
+#ifdef NS_NEGOTIATE
+    ACKCIADDR(CI_MS_DNS1, go->neg_dns1, 0, go->dnsaddr[0], 0);
+    ACKCIADDR(CI_MS_WINS1, go->neg_wins1, 0, go->winsaddr[0], 0);
+    ACKCIADDR(CI_MS_DNS2, go->neg_dns2, 0, go->dnsaddr[1], 0);
+    ACKCIADDR(CI_MS_WINS2, go->neg_wins2, 0, go->winsaddr[1], 0);
+#endif
+
     /*
      * If there are any remaining CIs, then this packet is bad.
      */
@@ -584,6 +782,25 @@
 	    );
 
     /*
+     * Accept the peer's idea of the DNS and WINS addresses
+     */
+#ifdef NS_NEGOTIATE
+    NAKCIADDR(CI_MS_DNS1, neg_dns1, 0, try.dnsaddr[0] = ciaddr1;);
+    NAKCIADDR(CI_MS_WINS1, neg_wins1, 0, try.winsaddr[0] = ciaddr1;);
+    NAKCIADDR(CI_MS_DNS2, neg_dns2, 0, try.dnsaddr[1] = ciaddr1;);
+    NAKCIADDR(CI_MS_WINS2, neg_wins2, 0, try.winsaddr[1] = ciaddr1;);
+#endif
+#ifdef DNS_CONFIGURE
+    /* update DNS info storage if needed */
+    if (try.dnsaddr[0] != 0) {
+	ns.dns[0].s_addr = try.dnsaddr[0];
+    }
+    if (try.dnsaddr[1] != 0) {
+        ns.dns[1].s_addr = try.dnsaddr[1];
+    }
+#endif
+
+    /*
      * There may be remaining CIs, if the peer is requesting negotiation
      * on an option that we didn't include in our request packet.
      * If they want to negotiate about IP addresses, we comply.
@@ -667,6 +884,9 @@
     u_short cishort;
     u_int32_t cilong;
     ipcp_options try;		/* options to request next time */
+#ifdef NS_NEGOTIATE
+    u_char citype, *next;
+#endif
 
     try = *go;
     /*
@@ -726,6 +946,35 @@
 	    go->maxslotindex, go->cflag);
 
     /*
+     * There may be remaining CIs, if the peer is unable to support
+     * DNS or WINS negotiation. If so, turn them off.
+     */
+#ifdef NS_NEGOTIATE
+    while (len > CILEN_VOID) {
+	GETCHAR(citype, p);
+	GETCHAR(cilen, p);
+	if( (len -= cilen) < 0 )
+	    goto bad;
+	next = p + cilen - 2;
+	switch (citype) {
+	case CI_MS_DNS1:
+	    try.neg_dns1 = 0;
+	    break;
+	case CI_MS_WINS1:
+	    try.neg_wins1 = 0;
+	    break;
+	case CI_MS_DNS2:
+	    try.neg_dns2 = 0;
+	    break;
+	case CI_MS_WINS2:
+	    try.neg_wins2 = 0;
+	    break;
+	}
+	p = next;
+    }
+#endif
+    
+    /*
      * If there are any remaining CIs, then this packet is bad.
      */
     if (len != 0)
@@ -1173,6 +1422,9 @@
     /* set tcp compression */
     sifvjcomp(f->unit, ho->neg_vj, ho->cflag, ho->maxslotindex);
 
+    /* configure DNS */
+    writeDNS(&ns);
+
     /*
      * If we are doing dial-on-demand, the interface is already
      * configured, so we put out any saved-up packets, then set the
Index: pppd/ipcp.h
===================================================================
RCS file: /home/ncvs/src/usr.sbin/pppd/ipcp.h,v
retrieving revision 1.10
diff -u -r1.10 ipcp.h
--- pppd/ipcp.h	28 Aug 1999 01:19:03 -0000	1.10
+++ pppd/ipcp.h	25 Dec 2005 16:45:47 -0000
@@ -52,6 +52,12 @@
     int old_vj : 1;		/* use old (short) form of VJ option? */
     int accept_local : 1;	/* accept peer's value for ouraddr */
     int accept_remote : 1;	/* accept peer's value for hisaddr */
+#ifdef NS_NEGOTIATE
+    int neg_dns1 : 1;		/* Negotiate primary domain name server */
+    int neg_wins1 : 1;		/* Negotiate primary WINS */
+    int neg_dns2 : 1;		/* Negotiate secondary domain name server */
+    int neg_wins2 : 1;		/* Negotiate secondary WINS */
+#endif
     u_short vj_protocol;	/* protocol value to use in VJ option */
     u_char maxslotindex, cflag;	/* values for RFC1332 VJ compression neg. */
     u_int32_t ouraddr, hisaddr;	/* Addresses in NETWORK BYTE ORDER */
@@ -59,6 +65,14 @@
     u_int32_t winsaddr[2];	/* Primary and secondary MS WINS entries */
 } ipcp_options;
 
+#ifdef DNS_CONFIGURE
+typedef struct ns {
+    struct in_addr dns[2];	/* Current DNS addresses */
+    char *resolv;		/* Contents of resolv.conf */
+    char *resolv_nons;		/* Contents of resolv.conf without ns */
+} ns_t;
+#endif
+
 extern fsm ipcp_fsm[];
 extern ipcp_options ipcp_wantoptions[];
 extern ipcp_options ipcp_gotoptions[];
Index: pppd/main.c
===================================================================
RCS file: /home/ncvs/src/usr.sbin/pppd/main.c,v
retrieving revision 1.19.2.1
diff -u -r1.19.2.1 main.c
--- pppd/main.c	30 Jul 2002 03:50:40 -0000	1.19.2.1
+++ pppd/main.c	25 Dec 2005 14:36:29 -0000
@@ -33,6 +33,7 @@
 #include <netdb.h>
 #include <utmp.h>
 #include <pwd.h>
+#include <netinet/in.h>
 #include <sys/param.h>
 #include <sys/types.h>
 #include <sys/wait.h>


More information about the freebsd-hackers mailing list