Current patch for DHCP discovery of WINS servers

Andrew Bartlett abartlet at pcug.org.au
Sun Jan 13 18:20:03 GMT 2002


Andrew Bartlett wrote:
> 
> Andrew Bartlett wrote:
> >
> > Tridge:
> > I've regenerated (bug not tested) this against current HEAD.  Probably
> > best to make it use the generic cache you were talking about on IRC.
> > I've #if 0'ed out the dhcp-discover version.  Its probably fine, but I
> > had reservations at the time I was looking at this.
> >
> > Shirish:
> > Have you updated this patch since you first proposed it?  Its starting
> > to get the serious attention it deserves, so it may yet see the light of
> > day :-)
> >
> > Andrew Bartlett
> 
> OK, so I forgot to include the new file in the patch :-)
> 
> Attached is the whole patch...

OK, so its really the whole patch this time...

Andrew Bartlett

-- 
Andrew Bartlett                                 abartlet at pcug.org.au
Manager, Authentication Subsystems, Samba Team  abartlet at samba.org
Student Network Administrator, Hawker College   abartlet at hawkerc.net
http://samba.org     http://build.samba.org     http://hawkerc.net
-------------- next part --------------
? include/nmbd_dhcp_for_wins.h
? nmbd/nmbd_dhcp_for_wins.c
? nsswitch/.libs
Index: Makefile.in
===================================================================
RCS file: /data/cvs/samba/source/Makefile.in,v
retrieving revision 1.433
diff -u -r1.433 Makefile.in
--- Makefile.in	12 Jan 2002 23:57:09 -0000	1.433
+++ Makefile.in	13 Jan 2002 13:19:18 -0000
@@ -249,7 +249,8 @@
             nmbd/nmbd_processlogon.o nmbd/nmbd_responserecordsdb.o \
             nmbd/nmbd_sendannounce.o nmbd/nmbd_serverlistdb.o \
             nmbd/nmbd_subnetdb.o nmbd/nmbd_winsproxy.o nmbd/nmbd_winsserver.o \
-            nmbd/nmbd_workgroupdb.o nmbd/nmbd_synclists.o
+            nmbd/nmbd_workgroupdb.o nmbd/nmbd_synclists.o \
+	    nmbd/nmbd_dhcp_for_wins.o
 
 NMBD_OBJ = $(NMBD_OBJ1) $(PARAM_OBJ) $(LIBSMB_OBJ) $(UBIQX_OBJ) \
            $(PROFILE_OBJ) $(LIB_OBJ)
Index: nmbd/nmbd.c
===================================================================
RCS file: /data/cvs/samba/source/nmbd/nmbd.c,v
retrieving revision 1.129
diff -u -r1.129 nmbd.c
--- nmbd/nmbd.c	30 Dec 2001 01:46:37 -0000	1.129
+++ nmbd/nmbd.c	13 Jan 2002 13:19:22 -0000
@@ -261,6 +261,8 @@
 static BOOL reload_nmbd_services(BOOL test)
 {
   BOOL ret;
+  struct in_addr wins_server;
+
   extern fstring remote_machine;
 
   fstrcpy( remote_machine, "nmbd" );
@@ -288,6 +290,17 @@
     reload_nmbd_services( True );
   }
 
+  /* Here, we discover wins servers using dhcp if asked to */
+  if (lp_dhcp_for_wins()) {
+	  if(discover_wins_server(&wins_server)) {
+		  set_wins_server(wins_server);
+		  DEBUG(3, ("reload_nmbd_services: Configured WINS server as %s using DHCP.\n", inet_ntoa(wins_server)) );
+
+	  } else {
+		  DEBUG(0, ("reload_nmbd_services: Failed to configure WINS server setting using DHCP.\n") );
+	  }
+  }
+
   /* Do a sanity check for a misconfigured nmbd */
   if( lp_wins_support() && wins_srv_count() )
     {
@@ -773,8 +786,21 @@
   DEBUG( 1, ( "Netbios nameserver version %s started.\n", VERSION ) );
   DEBUGADD( 1, ( "Copyright Andrew Tridgell 1994-1998\n" ) );
 
+  if (!is_daemon && !is_a_socket(0))
+  {
+    DEBUG(0,("standard input is not a socket, assuming -D option\n"));
+    is_daemon = True;
+  }
+  
+  if (is_daemon && !opt_interactive)
+  {
+    DEBUG( 2, ( "Becoming a daemon.\n" ) );
+    become_daemon();
+  }
+
   if ( !reload_nmbd_services(False) )
     return(-1);
+	return True;
 
   if(!init_structs())
     return -1;
@@ -790,18 +816,6 @@
   }
 
   set_samba_nb_type();
-
-  if (!is_daemon && !is_a_socket(0))
-  {
-    DEBUG(0,("standard input is not a socket, assuming -D option\n"));
-    is_daemon = True;
-  }
-  
-  if (is_daemon && !opt_interactive)
-  {
-    DEBUG( 2, ( "Becoming a daemon.\n" ) );
-    become_daemon();
-  }
 
 #if HAVE_SETPGID
   /*
Index: param/loadparm.c
===================================================================
RCS file: /data/cvs/samba/source/param/loadparm.c,v
retrieving revision 1.371
diff -u -r1.371 loadparm.c
--- param/loadparm.c	9 Jan 2002 04:17:24 -0000	1.371
+++ param/loadparm.c	13 Jan 2002 13:19:31 -0000
@@ -2525,15 +2525,46 @@
 /***************************************************************************
  Handle the WINS SERVER list
 ***************************************************************************/
+static struct in_addr wins_server = { 0 };
+static BOOL dhcp_for_wins_flag = False;
+
 static BOOL handle_wins_server_list( char *pszParmValue, char **ptr )
-  {
-  if( !wins_srv_load_list( pszParmValue ) )
-    return( False );  /* Parse failed. */
-
-  string_set( ptr, pszParmValue );
-  return( True );
-  }
+{
+	  if (strncmp(pszParmValue, "dhcp", 5)) {
+		  if( !wins_srv_load_list( pszParmValue ) )
+			  return( False );  /* Parse failed. */
+
+		  string_set( ptr, pszParmValue );
+		  return True;
+	  }
+
+	  /* Discover the WINS server using DHCP if we haven't done it
+	     one already 
+	  */
+	  if (wins_server.s_addr != 0) {
+		  string_set(ptr, inet_ntoa(wins_server));
+		  return True;
+	  }
+	  
+	  dhcp_for_wins_flag = True;
+
+	  string_set(ptr, "");
+	  return True;
+}
+
+BOOL lp_dhcp_for_wins()
+{
+	return dhcp_for_wins_flag;
+}
 
+void set_wins_server(struct in_addr srv)
+{
+	if (!wins_srv_load_list(inet_ntoa(srv)))
+		return False;
+	
+	wins_server.s_addr = srv.s_addr;
+	string_set(&Globals.szWINSserver, inet_ntoa(srv));
+}
 
 /***************************************************************************
  Handle the DEBUG level list
--- /dev/null	Thu Aug 24 19:00:32 2000
+++ nmbd/nmbd_dhcp_for_wins.c	Wed Aug 15 18:25:36 2001
@@ -0,0 +1,371 @@
+/* 
+   Unix SMB/Netbios implementation.
+   Version 1.0
+   Discover the network WINS server using DHCP.
+
+   Copyright (C) Shirish Kalele 2001.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2 of the License, or
+   (at your option) any later version.
+   
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+   
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+   
+*/
+#ident "@(#) $RCSfile$$Revision$$Date$"
+
+#include "includes.h"
+#include "nmbd_dhcp_for_wins.h"
+
+extern int DEBUGLEVEL;
+
+static unsigned int xid = 92830;
+
+int timed_out = 0;
+
+static void timeout_fn(int arg)
+{
+	timed_out = 1;
+}
+
+static int init_my_addr(struct sockaddr_in *my_addr)
+{
+	struct hostent *myhostentp;
+	char myhostname[256];
+
+	if (!my_addr) 
+		return 0;
+
+	bzero((unsigned char *) my_addr, sizeof(struct sockaddr_in));
+	my_addr->sin_family = AF_INET;
+	my_addr->sin_port = htons(DHCP_CLIPORT);
+
+	gethostname(myhostname, 256);
+	myhostentp = gethostbyname(myhostname);
+
+	/* Use the first address */
+	
+	memcpy(&my_addr->sin_addr.s_addr, *(myhostentp->h_addr_list), 
+	       sizeof(my_addr->sin_addr.s_addr));
+	
+	return 1;
+}
+	
+static int init_bcast_addr(struct sockaddr_in *bcast_addr)
+{
+	if (!bcast_addr)
+		return 0;
+
+	bzero((unsigned char *) bcast_addr, sizeof(struct sockaddr_in));
+	bcast_addr->sin_family = AF_INET;
+	bcast_addr->sin_port = htons(DHCP_SRVPORT);
+	bcast_addr->sin_addr.s_addr = htonl(INADDR_BROADCAST);
+	return 1;
+}
+
+static int creat_dhcp_clisock(struct sockaddr_in cli_addr)
+{
+	int bcast_flag = 1;
+	struct timeval tv;
+
+	int sock = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP);
+	if (sock == -1) {
+		DEBUG(3, ("creat_dhcp_clisock: socket open failed: %s\n",
+			  strerror(errno)) );
+		return -1;
+	}
+
+	/* These two options are needed for Linux */
+	setsockopt(sock, SOL_SOCKET, SO_BROADCAST, (char *) &bcast_flag,
+		   sizeof (bcast_flag));
+	 
+	tv.tv_sec = DHCP_WAIT_TIME+1;
+	tv.tv_usec = 0;
+	setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO, (char *) &tv,
+		   sizeof (tv));
+
+	if (bind(sock, (struct sockaddr *) &cli_addr, sizeof(cli_addr))
+	    < 0) {
+		DEBUG(3, ("creat_dhcp_clisock: bind udp socket to %s:%d failed: %s\n",
+			  inet_ntoa(cli_addr.sin_addr),
+			  ntohs(cli_addr.sin_port),
+			  strerror(errno)) );
+		return -1;
+	}
+
+	return sock;
+}
+
+static int do_dhcp_request(int sockd, unsigned char dhcp_type,
+		    struct sockaddr_in my_addr,
+		    struct sockaddr_in bcast_addr)
+{
+	struct dhcp_pkt_t *dhcp_reqp;
+	int dhcp_req_sz;
+
+	/* Our requests have two options: msg-type & param list req
+	   for NBNS
+	*/
+	dhcp_req_sz = sizeof(struct dhcp_pkt_t) + 7;
+
+	dhcp_reqp = (struct dhcp_pkt_t *) malloc(dhcp_req_sz);
+
+	if (dhcp_reqp == NULL) {
+		DEBUG(0, ("do_dhcp_request: malloc failed: %s",
+			  strerror(errno)) );
+		return -1;
+	}
+
+	bzero((char *) dhcp_reqp, dhcp_req_sz);
+	dhcp_reqp->op = 1;         /* BOOTP request */
+	dhcp_reqp->htype = 1;      /* 10Mb Ethernet */
+	dhcp_reqp->hlen = 6;       /* MAC address size */
+	dhcp_reqp->hops = 0;       
+	dhcp_reqp->xid = htonl(xid); 
+	dhcp_reqp->secs = 0;
+	dhcp_reqp->flags = 0;
+	dhcp_reqp->ciaddr = my_addr.sin_addr.s_addr;
+	dhcp_reqp->yiaddr = 0;
+	dhcp_reqp->siaddr = 0;
+	dhcp_reqp->giaddr = 0;
+	memset(dhcp_reqp->chaddr, 0, 16);
+	memset(dhcp_reqp->sname, 0, 64);
+	memset(dhcp_reqp->file, 0, 128);
+	dhcp_reqp->magic_cookie = inet_addr("99.130.83.99");
+	dhcp_reqp->option[0] = DHCPOP_MSG_TYPE;
+	dhcp_reqp->option[1] = 1;
+	dhcp_reqp->option[2] = dhcp_type;
+	   
+	dhcp_reqp->option[3] = DHCPOP_PARAM_LIST;
+	dhcp_reqp->option[4] = 1;
+	dhcp_reqp->option[5] = DHCPOP_NETBIOS_NAME_SERVERS;
+	dhcp_reqp->option[6] = DHCPOP_PAD;
+
+	dhcp_reqp->option[7] = DHCPOP_END;
+
+	if (sendto(sockd, (char *) dhcp_reqp, dhcp_req_sz, 0,
+		  (struct sockaddr *) &bcast_addr, sizeof(bcast_addr)) < 0) {
+		DEBUG(0,("do_dhcp_request: Broadcast of DHCP query failed.\n"));
+		return 0;
+	}
+
+	return 1;
+}
+
+static int get_dhcp_reply(int sockd, struct in_addr **pptr,
+		   int *cnt, struct in_addr *dhcp_server)
+{
+	struct sockaddr_in from_addr;
+	size_t from_len;
+	
+	unsigned char buf[1024];
+	int bufsz = sizeof (buf);
+	int nrecv;
+	struct dhcp_pkt_t *pkt = (struct dhcp_pkt_t *) buf;
+	
+	struct in_addr srv_id_addr = { 0 };
+	unsigned char *options;
+	int i = 0, j = 0, data_ndx = 0;
+
+	char srv_addr_str[20];
+
+	struct in_addr *nbns_servers = NULL;
+	int nbns_servers_cnt = 0;
+
+	from_len = sizeof(from_addr);
+
+	*pptr = NULL;
+	*cnt = 0;
+
+	do {
+		nrecv = recvfrom(sockd, (char *) buf, bufsz, 0, 
+				 (struct sockaddr *) &from_addr,
+				 (int *) &from_len);
+		if (nrecv < 0) {
+			if (errno == EINTR && timed_out) {
+				timed_out = 0;
+				signal(SIGALRM, timeout_fn);
+				xid++;
+				DEBUG(5, ("get_dhcp_reply: No reply to DHCP request. recvfrom interrupted by alarm.\n"));
+				return 0;
+			} else if (errno == EAGAIN) {
+				xid++;
+				DEBUG(5, ("get_dhcp_reply: No reply to DHCP request. recvfrom timed out..\n"));
+				return 0;
+			} else {
+				DEBUG(5, ("get_dhcp_reply: Error receiving: %s\n",
+					  strerror(errno)) );
+				return 0;
+			}
+		}
+
+		DEBUG(8,("get_dhcp_reply: got %d bytes from host %s:%d\n",
+			 nrecv, inet_ntoa(from_addr.sin_addr),
+			 ntohs(from_addr.sin_port)) );
+
+	} while (ntohl(pkt->xid) != xid ||
+		 ntohs(from_addr.sin_port) != DHCP_SRVPORT);
+
+	DEBUG(5, ("get_dhcp_reply: valid DHCP reply from server %s\n",
+		  inet_ntoa(from_addr.sin_addr)) );
+
+	strncpy(srv_addr_str, inet_ntoa(from_addr.sin_addr), 20);
+
+	/* Jump to the options */
+	options = &pkt->option[0];
+
+	/* Parse the options */
+	while (options[i] != DHCPOP_END && i < bufsz) {
+		
+		struct dhcp_option_t *opt = 
+			(struct dhcp_option_t *) &options[i];
+
+		switch (opt->type) {
+
+		case DHCPOP_PAD: 
+			i += 1;
+			break;
+
+		case DHCPOP_SERVER_IDENTIFIER: 
+			
+			if (opt->len != 4) {
+				DEBUG(8, ("get_dhcp_reply: Invalid data for Server Identifier option in DHCP reply from %s.\n",
+					  srv_addr_str) );
+				return 0;
+			}
+			
+			memcpy(&srv_id_addr.s_addr, &opt->data[0],
+			       sizeof(srv_id_addr.s_addr));
+			i += 6;
+			break;
+
+		case DHCPOP_NETBIOS_NAME_SERVERS:
+						
+			if (opt->len == 0 || opt->len % 4 != 0) {
+				DEBUG(8, ("get_dhcp_reply: Invalid data for Netbios Name Server option in DHCP reply from %s.\n",
+					  srv_addr_str) );
+				return 0;
+			}
+			
+			nbns_servers_cnt = opt->len / 4;
+
+			nbns_servers = (struct in_addr *) malloc(sizeof (struct in_addr) * nbns_servers_cnt);
+			
+			if (nbns_servers == NULL) {
+				DEBUG(0, ("get_dhcp_reply: malloc failed: %s\n",
+					  strerror(errno)) );
+				return 0;
+			}
+			
+			for (j = 0, data_ndx = 0; 
+			     j < nbns_servers_cnt && data_ndx < opt->len;
+			     j++, data_ndx += 4) {
+				memcpy(&nbns_servers[j].s_addr,
+				       &opt->data[data_ndx],
+				       sizeof (nbns_servers[0].s_addr));
+			}
+			
+			i += 2 + opt->len;
+			break;
+		default:
+			i += 2 + opt->len;
+		}
+	}
+
+	*pptr = nbns_servers;
+	*cnt = nbns_servers_cnt;
+	if (dhcp_server) 
+		dhcp_server->s_addr = from_addr.sin_addr.s_addr;
+
+	return 1;
+}
+
+int discover_wins_server(struct in_addr *wins_server)
+{
+	struct in_addr dhcp_server;
+	struct in_addr *nbns_servers;
+	int nbns_servers_cnt;
+
+	if (!get_wins_servers(&nbns_servers, &nbns_servers_cnt,
+			      &dhcp_server)) 
+		return 0;
+
+	if (nbns_servers != NULL) {
+		char dhcp_server_str[20];
+		strncpy(dhcp_server_str, inet_ntoa(dhcp_server), 20);
+
+		DEBUG(5, ("discover_wins_server: DHCP server %s returned WINS server: %s\n",
+			  dhcp_server_str,
+			  inet_ntoa(nbns_servers[0])) );
+		wins_server->s_addr = nbns_servers[0].s_addr;
+		return 1;
+	} else {
+		DEBUG(3,("discover_wins_server: The DHCP server (%s) returned no NetBIOS Name Servers in its response.\n",
+			 inet_ntoa(dhcp_server)));
+		return 0;
+	}
+}
+
+int get_wins_servers(struct in_addr **wins_servers, int *wins_servers_cnt,
+		     struct in_addr *dhcp_server)
+{
+	int sockd;
+	int ret = 0;
+	
+	struct sockaddr_in bcast_addr;
+	struct in_addr srv_addr;
+	struct sockaddr_in my_addr;
+	
+	init_my_addr(&my_addr);
+
+	if((sockd = creat_dhcp_clisock(my_addr)) < 0)
+		return 0;
+
+	init_bcast_addr(&bcast_addr);
+
+	signal(SIGALRM, timeout_fn);
+
+	DEBUG(5,("get_wins_servers: Broadcasting a DHCPinform request for list of WINS servers\n"));
+	if (do_dhcp_request(sockd, DHCPINFORM, my_addr, bcast_addr) < 0) {
+		ret = 0;
+		goto done;
+	}
+
+	alarm(DHCP_WAIT_TIME);
+
+	if (get_dhcp_reply(sockd, wins_servers, wins_servers_cnt,
+			   dhcp_server)) {
+		ret = 1;
+		goto done;
+	}
+
+#if 0
+	DEBUG(5,("get_wins_servers: No replies to DHCPinform. Broadcasting DHCPdiscover..\n"));
+	if (do_dhcp_request(sockd, DHCPDISCOVER, my_addr, bcast_addr) < 0) {
+		ret = 0;
+		goto done;
+	}
+
+	alarm(DHCP_WAIT_TIME);
+
+	if (get_dhcp_reply(sockd, wins_servers, wins_servers_cnt,
+			   dhcp_server)) {
+		ret = 1;
+		goto done;
+	}
+#endif
+
+	DEBUG(5,("get_wins_servers: No DHCP server replied to our pleas.\n"));
+ done:
+	sigrelse(SIGALRM);
+	close(sockd);
+	return ret;
+}
--- /dev/null	Thu Aug 24 19:00:32 2000
+++ include/nmbd_dhcp_for_wins.h	Wed Aug 15 18:24:52 2001
@@ -0,0 +1,86 @@
+/* 
+   Unix SMB/Netbios implementation.
+   Version 1.0
+   Discover the WINS server using DHCP.
+
+   Copyright (C) Shirish Kalele 2001.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2 of the License, or
+   (at your option) any later version.
+   
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+   
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+   
+*/
+
+#ident "@(#) $RCSfile$$Revision$$Date$"
+
+#ifndef _DHCP_DISCOVER_WINS_H
+
+#define _DHCP_DISCOVER_WINS_H
+
+#ifndef MAXHOSTNAMELEN
+#define MAXHOSTNAMELEN 256
+#endif
+
+#define DHCP_CLIPORT 68
+#define DHCP_SRVPORT 67
+
+/* DHCP message types */
+#define DHCPDISCOVER 1
+#define DHCPOFFER    2
+#define DHCPREQUEST  3
+#define DHCPDECLINE  4 
+#define DHCPACK      5
+#define DHCPNAK      6
+#define DHCPRELEASE  7
+#define DHCPINFORM   8
+
+/* DHCP option codes */
+#define DHCPOP_PAD                          0x00
+#define DHCPOP_NETBIOS_NAME_SERVERS         0x2C
+#define DHCPOP_MSG_TYPE                     0x35
+#define DHCPOP_SERVER_IDENTIFIER            0x36
+#define DHCPOP_PARAM_LIST                   0x37
+#define DHCPOP_END                          0xFF
+
+#define DHCP_WAIT_TIME    5 /* seconds */
+
+struct dhcp_pkt_t {
+	unsigned char op;
+	unsigned char htype;
+	unsigned char hlen;
+	unsigned char hops;
+
+	unsigned int xid;
+
+	unsigned short secs;
+	unsigned short flags;
+
+	unsigned int ciaddr;
+	unsigned int yiaddr;
+	unsigned int siaddr;
+	unsigned int giaddr;
+	unsigned char chaddr[16];
+	unsigned char sname[64];
+	unsigned char file[128];
+	unsigned int magic_cookie;
+	
+	unsigned char option[1];
+};
+
+struct dhcp_option_t {
+	unsigned char type;
+	unsigned char len;
+	unsigned char data[1];
+};
+
+#endif


More information about the samba-technical mailing list